// sexp.c - an integer-coded tiny lisp. // comments at end #include #include #include #include #include #include #include #include #include"ppnarg.h" /*https://github.com/luser-dr00g/sexp.c/blob/master/ppnarg.h*/ #ifdef DEBUGMODE #define CHECK_DEBUG_LEVEL(LVL) (LVL<=DEBUGMODE) #define DEBUG(LVL,...) ( CHECK_DEBUG_LEVEL(LVL) ? fprintf(stderr, __VA_ARGS__) : 0 ) #define IFDEBUG(LVL,...) ( CHECK_DEBUG_LEVEL(LVL) ? __VA_ARGS__ : 0 ) #else #define DEBUG(...) 0 #define IFDEBUG(...) 0 #endif #define nil (0) #define LPAR "(" #define RPAR ")" #define ATOMBUFSZ 10 #define defun(NAME,ARGS,...) \ int NAME ARGS { IFDEBUG(2, fprintf(stderr, "%s ", __func__)); return __VA_ARGS__; } struct state { int*m,*n,msz, /*memory next mem-size*/ env, /* global environment for REPL, modified by SET, SETQ and DEFUN */ atoms; /* head of atom list */ char linebuf[BUFSIZ]; char *inputptr; } global = { .linebuf = { 0 }, .inputptr = global.linebuf }; #define INIT_ALL INIT_MEMORY, INIT_ATOM_LIST, INIT_ENVIRONMENT #define INIT_MEMORY global.n=16+(global.m=calloc(global.msz=getpagesize(),sizeof(int))) #define ATOM_PROPS(x) list(TO_STRING(x)) #define INIT_ATOM_LIST global.atoms = list(ATOMSEEDS(ATOM_PROPS)) #define INIT_ENVIRONMENT global.env = list( \ list(T, T), \ list(NIL, nil), \ SUBR_LIST(make_subr), \ SUBR2_LIST(make_subr2), \ FSUBR_LIST(make_fsubr1), \ FSUBR2_LIST(make_fsubr2) \ ) #define make_subr(X,Y) list(atom(#X),subr1(Y)) #define make_subr2(X,Y) list(atom(#X),subr2(Y)) #define make_fsubr1(X,Y) list(atom(#X),fsubr1(Y)) #define make_fsubr2(X,Y) list(atom(#X),fsubr2(Y)) #define SUBR_LIST(X) \ X(CAAR,caar), X(CDAR,cdar), X(CADR,cadr), X(CDDR,cddr), \ X(CAAAR,caaar), X(CDAAR,cdaar), X(CADAR,cadar), X(CDDAR,cddar), \ X(CAADR,caadr), X(CDADR,cdadr), X(CADDR,caddr), X(CDDDR,cdddr), \ X(LENGTH,length), X(PRNC,prnc) #define SUBR2_LIST(X) X(SET,set) #define FSUBR_LIST(X) X(READ,read_), X(READCH,readch) #define FSUBR2_LIST(X) X(SETQ,set) enum { TAGCONS, TAGATOM, TAGOBJ, TAGNUM, TAGBITS = 2, TAGMASK = (1U<>TAGBITS) defun( tag, (x),x&TAGMASK) defun(number, (x),x<s.stroffset),s)?i: cdr(slist)?findstr(s,cdr(slist),i+1):(rplacd(slist,list(list(string(s)))),i+1)) defun(encstr, (char*s),findstr(s,global.atoms,0)) defun(atom, (char*s),encstr(s)<tag==STRING) defun(cons, (x,y),*global.n++=x,*global.n++=y,(global.n-global.m)-2<s.stroffset))) defun(prnatomx, (x,atoms,f)FILE*f;, (!f?f=stdout:0),x?prnatomx(x-1,cdr(atoms),f):fprintf(f,"%s ", (char*)(global.m+ptrobj(caar(atoms))->s.stroffset))) defun(prnatom0, (x,f)FILE*f;,(!f?f=stdout:0),prnatomx(x,global.atoms,f)) defun(prnatom, (unsigned x,FILE*f),(!f?f=stdout:0),prnatom0(x>>TAGBITS,f)) defun(prnlstn, (x,f)FILE*f;,(!f?f=stdout:0),!listp(x)?prn(x,f): ((car(x)?prnlst(car(x),f):0),(cdr(x)?prnlstn(cdr(x),f):0))) defun(prnlst, (x,f)FILE*f;,(!f?f=stdout:0),!listp(x)?prn(x,f): (fprintf(f,LPAR),(car(x)?prnlst (car(x),f):0), (cdr(x)?prnlstn(cdr(x),f):0),fprintf(f,RPAR))) defun(prnc, (x),printf("%c",val(x))) char*adjust_case(char*buf){ for(char*p=buf;*p;p++)*p=toupper(*p); return buf; } char*rdatom(char**p,char*buf,int i){return memcpy(buf,*p,i),buf[i]=0,(*p)+=i,adjust_case(buf);} defun(rdlist,(p,z,u)char**p;,u==atom(RPAR)?z:append(cons(u,nil),rdlist(p,z,rd(p)))) defun(rdnum, (p,v)char**p;,*++*p>='0'&&**p<='9'?rdnum(p,v*10+**p-'0'):v) defun(rdbuf, (char**p,char*buf,char c),c?(c==' ' ?(++(*p),rd(p) ): c==*RPAR ?(++(*p),atom(RPAR) ): c==*LPAR ?(++(*p),rdlist(p,nil,rd(p)) ): c>='0'&&c<='9'? number(rdnum(p,c-'0')): c=='-'&&(*p)[1]>='0'&&(*p)[1]<='9'? number(-rdnum(p,0)): atom(rdatom(p,buf,strcspn(*p,"() \t"))) ):0) defun(rd, (char**p),rdbuf(p,(char[ATOMBUFSZ]){""},**p)) defun(check_input,(),!*global.inputptr?global_readline():1) defun(readch,(),check_input()?number(*global.inputptr++):QUIT) defun(read_,(),check_input()?rd(&global.inputptr):QUIT) defun(prompt,(),printf(">"), fflush(0)) defun(readline,(char *s,size_t sz), (prompt(),fgets(s,sz,stdin)&&((s[strlen(s)-1]=0),1))) defun(global_readline,(), global.inputptr=global.linebuf,readline(global.linebuf,sizeof(global.linebuf))) defun(repl,(x), (x=read_())==QUIT?0: (IFDEBUG(0,prn(x,stdout),fprintf(stdout,"\n"), prnlst(x,stdout),fprintf(stdout,"\n")), x=eval(x,global.env), IFDEBUG(0,dump(x,stdout)), prnlst(x,stdout),printf("\n"), repl())) defun(debug_global,(), prnlst(global.atoms,stderr), prnlst(global.env,stderr), fflush(stderr) ) defun(init,(), INIT_ALL, IFDEBUG(2, debug_global()), repl()) int main(){ assert((-1 & 3) == 3); /* that ints are 2's complement */ assert((-1 >> 1) < 0); /* that right shift keeps sign */ int r = init(); dumpmem(); return r; } int dumpmem(){ FILE *f = fopen( "mem.dump","w" ); fwrite( global.m, sizeof *global.m, global.n-global.m, f ); fclose( f ); debug_global(); } int dump(int x,FILE*f){ IFDEBUG(1,fprintf(stderr,"env:\n"), prnlst(global.env,stderr), fprintf(stderr,"\n")); fprintf(f,"x: %d\n", x), fprintf(f,"0: %o\n", x), fprintf(f,"0x: %x\n", x), fprintf(f,"tag(x): %d\n", tag(x)), fprintf(f,"val(x): %d\n", val(x)), fprintf(f,"car(x): %d\n", car(x)), fprintf(f,"cdr(x): %d\n", cdr(x)), prn(x,f), fprintf(f,"\n"); } /* sexp.c - an integer-coded tiny lisp. $ make test $ make test cflags=-DDEBUGMODE=1 cf. http://www.ioccc.org/1989/jar.2.c <-- memory 'cursors' http://leon.bottou.org/projects/minilisp <-- compact 'C'-able cell encoding http://www.jsoftware.com/jwiki/Essays/Incunabulum <-- tiny APL interpreter http://www-formal.stanford.edu/jmc/recursive/recursive.html <-- original lisp paper http://www.paulgraham.com/rootsoflisp.html <-- alternate presentation of core (with bugfix) http://www.cse.sc.edu/~mgv/csce330f13/micromanualLISP.pdf <-- original micro-manual for lisp http://codegolf.stackexchange.com/questions/284/write-an-interpreter-for-the-untyped-lambda-calculus/3290#3290 http://stackoverflow.com/questions/18096456/why-wont-my-little-lisp-quote <-- earlier version of this program http://www.nhplace.com/kent/Papers/Special-Forms.html <-- FEXPRs NLAMBDAs and MACROs, oh my! https://web.archive.org/web/20070317222311/http://www.modeemi.fi/~chery/lisp500/lisp500.c <-- similar idea defun macro thanks to Kaz Kylheku: https://groups.google.com/d/msg/comp.lang.c/FiC6hbH1azw/-Tiuw2oQoyAJ better asserts thx to Tim Rentsch https://groups.google.com/d/msg/comp.lang.c/FZldZaPpTT4/5g4bWdsxAwAJ bias the atom encoding for the code for T, [