text/plain
•
10.59 KB
•
388 lines
/*
* Wolkenwelten - Copyright (C) 2020-2021 - Benjamin Vincent Schulenburg
*
* This project uses the MIT license, a copy should be included under /LICENSE
*/
#include "nujel.h"
#include "allocator/garbage-collection.h"
#include "allocator/roots.h"
#include "misc/random-number-generator.h"
#include "s-expression/reader.h"
#include "s-expression/writer.h"
#include "type-system.h"
#include "types/array.h"
#include "types/closure.h"
#include "types/list.h"
#include "types/native-function.h"
#include "types/string.h"
#include "types/symbol.h"
#include "types/val.h"
#include "types/vec.h"
#include "operator/arithmetic.h"
#include "operator/array.h"
#include "operator/binary.h"
#include "operator/closure.h"
#include "operator/special.h"
#include "operator/list.h"
#include "operator/predicates.h"
#include "operator/random.h"
#include "operator/string.h"
#include "operator/time.h"
#include "operator/vec.h"
#ifndef COSMOPOLITAN_H_
#include <ctype.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#endif
extern u8 stdlib_nuj_data[];
char dispWriteBuf[1<<18];
/* Initialize the allocator and symbol table, needs to be called before any other call.*/
void lInit(){
lInitArray();
lInitClosure();
lInitNativeFunctions();
lInitStr();
lInitVal();
lInitVec();
lInitSymbol();
}
/* Display v on the default channel, most likely stdout */
void lDisplayVal(lVal *v){
lSWriteVal(v,dispWriteBuf,&dispWriteBuf[sizeof(dispWriteBuf)],0,true);
printf("%s",dispWriteBuf);
}
/* Display v on the error channel, most likely stderr */
void lDisplayErrorVal(lVal *v){
lSWriteVal(v,dispWriteBuf,&dispWriteBuf[sizeof(dispWriteBuf)],0,true);
fprintf(stderr,"%s",dispWriteBuf);
}
/* Write a machine-readable presentation of v to stdout */
void lWriteVal(lVal *v){
lSWriteVal(v,dispWriteBuf,&dispWriteBuf[sizeof(dispWriteBuf)],0,false);
printf("%s\n",dispWriteBuf);
}
/* Handler for [λ [...args] ...body] */
static lVal *lnfLambda(lClosure *c, lVal *v){
if((v == NULL) || (lCar(v) == NULL) || (lCdr(v) == NULL)){
return NULL;
}
lVal *ret = lRootsValPush(lValAlloc());
ret->type = ltLambda;
ret->vClosure = lClosureNew(c);
ret->vClosure->doc = lCons(lCar(v),lCadr(v));
ret->vClosure->text = lWrap(lCdr(v));
forEach(n,lCar(v)){
lVal *car = lCar(n);
if((car == NULL) || (car->type != ltSymbol)){continue;}
lVal *t = lDefineClosureSym(ret->vClosure,lGetSymbol(car));
t->vList.car = NULL;
(void)t;
}
return lRootsValPop();
}
/* Handler for [λ* [..args] docstring body] */
static lVal *lnfLambdaRaw(lClosure *c, lVal *v){
lVal *ret = lValAlloc();
lRootsValPush(ret);
ret->type = ltLambda;
ret->vClosure = lClosureNew(c);
ret->vClosure->doc = lCons(lCar(v),lCadr(v));
ret->vClosure->text = lCaddr(v);
forEach(n,lCar(v)){
lVal *car = lCar(n);
if((car == NULL) || (car->type != ltSymbol)){continue;}
lVal *t = lDefineClosureSym(ret->vClosure,lGetSymbol(car));
t->vList.car = NULL;
(void)t;
}
return lRootsValPop();
}
/* Handler for [δ [...args] ...body] */
static lVal *lnfDynamic(lClosure *c, lVal *v){
lVal *ret = lnfLambda(c,v);
if(ret == NULL){return NULL;}
ret->type = ltDynamic;
return ret;
}
/* Handler for [ω ...body] */
static lVal *lnfObject(lClosure *c, lVal *v){
lVal *ret = lRootsValPush(lValAlloc());
ret->type = ltObject;
ret->vClosure = lClosureNew(c);
lnfDo(ret->vClosure,v);
return lRootsValPop();
}
/* Handler for [memory-info] */
static lVal *lnfMemInfo(lClosure *c, lVal *v){
(void)c; (void)v;
lVal *ret = lRootsValPush(lCons(NULL,NULL));
lVal *l = ret;
l->vList.car = lValSym(":value");
l->vList.cdr = lCons(NULL,NULL);
l = l->vList.cdr;
l->vList.car = lValInt(lValActive);
l->vList.cdr = lCons(NULL,NULL);
l = l->vList.cdr;
l->vList.car = lValSym(":closure");
l->vList.cdr = lCons(NULL,NULL);
l = l->vList.cdr;
l->vList.car = lValInt(lClosureActive);
l->vList.cdr = lCons(NULL,NULL);
l = l->vList.cdr;
l->vList.car = lValSym(":array");
l->vList.cdr = lCons(NULL,NULL);
l = l->vList.cdr;
l->vList.car = lValInt(lArrayActive);
l->vList.cdr = lCons(NULL,NULL);
l = l->vList.cdr;
l->vList.car = lValSym(":string");
l->vList.cdr = lCons(NULL,NULL);
l = l->vList.cdr;
l->vList.car = lValInt(lStringActive);
l->vList.cdr = lCons(NULL,NULL);
l = l->vList.cdr;
l->vList.car = lValSym(":symbol");
l->vList.cdr = lCons(NULL,NULL);
l = l->vList.cdr;
l->vList.car = lValInt(lSymbolMax);
l->vList.cdr = lCons(NULL,NULL);
l = l->vList.cdr;
return ret;
}
/* Evaluate the Nujel Lambda expression and return the results */
static lVal *lLambda(lClosure *c,lVal *args, lVal *lambda){
if(lambda == NULL){
lPrintError("lLambda: NULL\n");
return NULL;
}
if(lambda->type == ltObject){
return lnfDo(lambda->vClosure,args);
}
lVal *vn = args;
lClosure *tmpc = (lambda->type == ltDynamic
? lClosureNew(c)
: lClosureNew(lambda->vClosure));
lRootsClosurePush(tmpc);
tmpc->text = lambda->vClosure->text;
forEach(n,lambda->vClosure->data){
if(vn == NULL){break;}
lVal *car = lCaar(n);
if((car == NULL) || (car->type != ltSymbol)){continue;}
lSymbol *csym = lGetSymbol(car);
lVal *lv = lDefineClosureSym(tmpc,csym);
if(lSymVariadic(csym)){
lVal *t = lSymNoEval(csym) ? vn : lMap(c,vn,lEval);
if((lv != NULL) && (lv->type == ltPair)){ lv->vList.car = t;}
break;
}else{
lVal *t = lSymNoEval(csym) ? lCar(vn) : lEval(c,lCar(vn));
if((lv != NULL) && (lv->type == ltPair)){ lv->vList.car = t;}
if(vn != NULL){vn = lCdr(vn);}
}
}
lVal *ret = lEval(tmpc,lambda->vClosure->text);
lRootsClosurePop();
return ret;
}
/* Evaluate a single value, v, and return the result */
lVal *lEval(lClosure *c, lVal *v){
if((c == NULL) || (v == NULL)){return NULL;}
lRootsValPush(v);
lRootsClosurePush(c);
if(v->type == ltSymbol){
v = lResolveSym(c,v);
}else if(v->type == ltPair){
lVal *ret = lRootsValPush(lEval(c,lCar(v)));
if(ret == NULL){
v = NULL;
}else if(ret->type == ltSpecialForm){
v = ret->vNFunc->fp(c,lCdr(v));
}else if((ret->type == ltLambda) || (ret->type == ltDynamic) || (ret->type == ltObject)){
v = lLambda(c,lCdr(v),ret);
}else{
lVal *args = lMap(c,lCdr(v),lEval);
lRootsValPush(args);
if(ret->type == ltNativeFunc){
v = ret->vNFunc->fp(c,args);
}else{
lVal *nv = lCons(ret,args);
lRootsValPush(nv);
switch(ret->type){
case ltPair:
v = lEval(c,nv);
break;
case ltString:
v = lnfCat(c,nv);
break;
case ltInt:
case ltFloat:
case ltVec:
v = (v->vList.cdr == NULL) ? ret : lnfInfix(c,nv);
break;
case ltArray:
v = (v->vList.cdr == NULL) ? ret : lnfArrRef(c,nv);
break;
}
lRootsValPop();
}
lRootsValPop();
}
lRootsValPop();
}
lRootsClosurePop();
lRootsValPop();
return v;
}
/* Evaluate func for every entry in list v and return a list containing the results */
lVal *lMap(lClosure *c, lVal *v, lVal *(*func)(lClosure *,lVal *)){
if((c == NULL) || (v == NULL)){return NULL;}
lVal *car = func(c,lCar(v));
lRootsValPush(car);
lVal *cc = lCons(car,NULL);
lRootsValPop();
lRootsValPush(cc);
forEach(t,lCdr(v)){
cc->vList.cdr = lCons(NULL,NULL);
cc = cc->vList.cdr;
cc->vList.car = func(c,lCar(t));
}
return lRootsValPop();
}
/* Handler for [apply fn list] */
static lVal *lnfApply(lClosure *c, lVal *v){
lVal *func = lCar(v);
if(func == NULL){return NULL;}
if(func->type == ltSymbol){func = lResolveSym(c,func);}
switch(func->type){
case ltSpecialForm:
return func->vNFunc->fp(c,lCadr(v));
case ltNativeFunc:
return func->vNFunc->fp(c,lCadr(v));
case ltDynamic:
case ltObject:
case ltLambda: {
lVal *t = lCadr(v);
if((t == NULL) || (t->type != ltPair)){t = lCons(t,NULL);}
return lLambda(c,t,func);}
default:
return v;
}
}
/* Add all the platform specific constants to c */
static void lAddPlatformVars(lClosure *c){
#if defined(__HAIKU__)
lDefineVal(c, "OS", lValString("Haiku"));
#elif defined(__APPLE__)
lDefineVal(c, "OS", lValString("MacOS"));
#elif defined(__EMSCRIPTEN__)
lDefineVal(c, "OS", lValString("Emscripten"));
#elif defined(__MINGW32__)
lDefineVal(c, "OS", lValString("Windows"));
#elif defined(__linux__)
lDefineVal(c, "OS", lValString("Linux"));
#else
lDefineVal(c, "OS", lValString("*nix"));
#endif
#if defined(__arm__)
lDefineVal(c, "ARCH", lValString("armv7l"));
#elif defined(__aarch64__)
lDefineVal(c, "ARCH", lValString("aarch64"));
#elif defined(__x86_64__)
lDefineVal(c, "ARCH", lValString("x86_64"));
#elif defined(__EMSCRIPTEN__)
lDefineVal(c, "ARCH", lValString("wasm"));
#else
lDefineVal(c, "ARCH", lValString("unknown"));
#endif
}
/* Handler for [eval s-expr] */
static lVal *lnfEval(lClosure *c, lVal *v){
return lEval(c,lCar(v));
}
/* Add all the core native functions to c, without IO or stdlib */
static void lAddCoreFuncs(lClosure *c){
lOperationsArithmetic(c);
lOperationsArray(c);
lOperationsBinary(c);
lOperationsTypeSystem(c);
lOperationsClosure(c);
lOperationsSpecial(c);
lOperationsList(c);
lOperationsPredicate(c);
lOperationsRandom(c);
lOperationsReader(c);
lOperationsString(c);
lOperationsTime(c);
lOperationsVector(c);
lAddNativeFunc(c,"apply", "[func list]", "Evaluate FUNC with LIST as arguments", lnfApply);
lAddNativeFunc(c,"eval", "[expr]", "Evaluate EXPR", lnfEval);
lAddNativeFunc(c,"memory-info", "[]", "Return memory usage data", lnfMemInfo);
lAddSpecialForm(c,"λ*", "[args source body]", "Create a new, raw, lambda", lnfLambdaRaw);
lAddSpecialForm(c,"lambda lam λ \\","[args ...body]", "Create a new lambda", lnfLambda);
lAddSpecialForm(c,"dynamic dyn δ", "[args ...body]", "New Dynamic scoped lambda", lnfDynamic);
lAddSpecialForm(c,"object obj ω", "[args ...body]", "Create a new object", lnfObject);
}
/* Create a new root closure WITHTOUT loading the nujel stdlib, mostly of interest when testing a different stdlib than the one included */
lClosure *lClosureNewRootNoStdLib(){
lClosure *c = lClosureAlloc();
c->parent = NULL;
lRootsClosurePush(c);
lAddCoreFuncs(c);
lAddPlatformVars(c);
return c;
}
/* Create a new root closure with the default included stdlib */
lClosure *lClosureNewRoot(){
lClosure *c = lClosureNewRootNoStdLib();
c->text = lRead((const char *)stdlib_nuj_data);
c->text = lWrap(c->text);
lEval(c,c->text);
c->text = NULL;
return c;
}
/* Append a do to the beginning of v, useful when evaluating user input via a repl, since otherwise we could only accept a single expression. */
lVal *lWrap(lVal *v){
lVal *r = lRootsValPush(lCons(NULL,NULL));
r->vList.cdr = v;
r->vList.car = lValSymS(symDo);
return lRootsValPop();
}