Login
7 branches 0 tags
Ben (X13/Arch) Fixed performance report Nujel average e2211fb 2 years ago 1058 Commits
nujel / lib / nujel.c
/* Nujel - Copyright (C) 2020-2022 - Benjamin Vincent Schulenburg
 * This project uses the MIT license, a copy should be included under /LICENSE */
#ifndef NUJEL_AMALGAMATION
#include "nujel-private.h"
#endif

#include <stdlib.h>

extern u8 stdlib_no_data[];

lVal NIL;

/* Initialize the allocator and symbol table, needs to be called before as
 * soon as possible, since most procedures depend on it.*/
void lInit(){
	lSymbolInit();
	lTypesInit();
}

void simplePrintTree(lTree *t){
	if(!t){return;}
	simplePrintTree(t->left);
	if(t->key){
		fprintf(stderr, ":%s ", t->key->c);
		simplePrintVal(t->value);
	}
	simplePrintTree(t->right);
}

/* Super simple printer, not meant for production use, but only as a tool of last resort, for example when
 * we throw past the root exception handler.
 */
void simplePrintVal(lVal v){
	switch(v.type){
	default:
		fprintf(stderr, "#<not-printable-from-c %i> ", v.type);
		break;
	case ltEnvironment:
		fprintf(stderr, "#<env>");
		break;
	case ltBytecodeArr:
		fprintf(stderr, "#<bc-arr>");
		break;
	case ltLambda:
		fprintf(stderr, "#<fn>");
		break;
	case ltNativeFunc:
		fprintf(stderr, "#<NFn>");
		break;
	case ltTree:
		fprintf(stderr, "{");
		simplePrintTree(v.vTree->root);
		fprintf(stderr, "}");
		break;
	case ltNil:
		fprintf(stderr, "#nil");
		break;
	case ltBool:
		fprintf(stderr, "%s", v.vBool ? "#t" : "#f");
		break;
	case ltInt:
		fprintf(stderr, "%lli", (long long int)v.vInt);
		break;
	case ltFloat:
		fprintf(stderr, "%f", v.vFloat);
		break;
	case ltBuffer:
		fprintf(stderr, "#<buf>");
		break;
	case ltString:
		fprintf(stderr, "\"%s\"", (const char *)lBufferData(v.vString));
		break;
	case ltKeyword:
		fprintf(stderr, ":"); // fall-through
	case ltSymbol:
		fprintf(stderr, "%s", v.vSymbol->c);
		break;
	case ltPair: {
		fprintf(stderr, "(");
		lVal t = v;
		bool first = true;
		for(; t.type == ltPair; t = lCdr(t)){
			if(!first){
				fprintf(stderr, " ");
			}
			first = false;
			simplePrintVal(t.vList->car);
		}
		if(t.type != ltNil){
			fprintf(stderr, ". ");
			simplePrintVal(t);
		}
		fprintf(stderr, ")");
		break; }
	case ltArray:
		fprintf(stderr, "##(");
		for(int i=0;i<v.vArray->length;i++){
			simplePrintVal(v.vArray->data[i]);
		}
		fprintf(stderr, ") ");
	}
}

lVal lValException(const lSymbol *symbol, const char *error, lVal v) {
	lVal l = lCons(v, NIL);
	l = lCons(lValString(error),l);
	l = lCons(lValKeywordS(symbol),l);
	l.type = ltException;
	return l;
}

/* Run fun with args  */
lVal lApply(lVal fun, lVal args){
	if(unlikely(fun.type != ltLambda)){
		return lValException(lSymTypeError, "Can't apply to following val", fun);
	}
	return lBytecodeEval(lClosureNewFunCall(args, fun), fun.vClosure->text);
}

/* Reads EXPR which should contain bytecode arrays and then evaluate them in C.
 * Mainly used for bootstrapping the stdlib and compiler out of precompiled .no
 * files. */
lClosure *lLoad(lClosure *c, const char *expr){
	lVal v = lRead(expr);
	const int RSP = lRootsGet();
	for(lVal n=v; n.type == ltPair; n = n.vList->cdr){
		c->args = n; // We need a reference to make sure that n won't be collected by the GC
		lVal car = n.vList->car;
		if(likely(car.type == ltBytecodeArr)){
			lBytecodeEval(c, car.vBytecodeArr);
		}
	}
	c->args = NIL;
	lRootsRet(RSP);
	return c;
}

void lOperationsBase(lClosure *c){
	lOperationsArithmetic(c);
	lOperationsBuffer(c);
	lOperationsArray(c);
	lOperationsBytecode(c);
	lOperationsCore(c);
	lOperationsSpecial(c);
	lOperationsString(c);
	lOperationsTree(c);
}

/* Create a new root closure with the stdlib */
lClosure *lNewRoot(){
	lClosure *c = lClosureAllocRaw();
	c->type = closureRoot;
	lOperationsBase(c);
	lAddPlatformVars(c);
	lDefineVal(c,"exports",  lValTree(NULL));
	lDefineVal(c,"*module*", lValKeyword("core"));
	return lLoad(c, (const char *)stdlib_no_data);
}