#include "scheme.h" static vlong peekbuf[10];static int peekind;static Rune stringbuf[512];static int bufind; #define EOF ~0 static intisdelim(Rune r){ return isspacerune(r) || runestrchr(L"()\"", r);} /* For ALL Biobufs */static voidungetrune(Rune r){ peekbuf[peekind++] = r;} static Runenextrunesp(Biobuf *file){ Rune r; if(peekind == 0){ vlong x; x = Bgetrune(file); r = x; if(x < 0) r = EOF; }else r = peekbuf[--peekind]; if(r == L';') while(r = Bgetrune(file), r != L'\n'); if(isspacerune(r)) r = L' '; return r;} static Runenextrune(Biobuf *file){ Rune r; while(r = nextrunesp(file), r == L' '); return r;} static Sexp*readlist(Biobuf *file){ Rune r; Sexp *first, *second; if(r = nextrune(file), r == L')') return empty; ungetrune(r); first = readsexp(file); pushroot(&first); if(r = nextrune(file), r == L'.'){ // TODO: expect delimiter second = readsexp(file); poproot(); if(second == nil){ Bprint(&perr, "error: invalid cdr\n"); return nil; } if(r = nextrune(file), r != L')'){ Bprint(&perr, "error: ')' expected\n"); return empty; } return cons(first, second); }else{ ungetrune(r); second = readlist(file); poproot(); return cons(first, second); }} static Sexp*readproperlist(Biobuf *file){ Rune r; Sexp *first, *second; if(r = nextrune(file), r == L')') return empty; ungetrune(r); first = readsexp(file); pushroot(&first); second = readproperlist(file); poproot(); return cons(first, second);} static intgetdigit(Rune r, int base){ int d; if(r >= L'0' && r <= L'9') d = r-L'0'; else if(r >= L'A' && r <= L'F') d = r-L'A' + 10; else if(r >= L'a' && r <= L'f') d = r-L'a' + 10; else return -1; if(d >= base) return -1; return d;} /* TODO */static Sexp*readnumber(Biobuf *file){ Sexp *e; Rune r, *sp; char *s; int d, i; double f, div; int exp, esign; int exactness; int base; int canprefix, signstate, digitstate; int sign; int numtype; bufind = 0; while(r = nextrunesp(file), !isdelim(r)) stringbuf[bufind++] = r; ungetrune(r); stringbuf[bufind++] = 0; canprefix = 1; signstate = 0; /* 0 can't read sign 1 can read sign 2 can read esign */ digitstate = 0; /* 0 reading integer part 1 reading fractional part 2 reading exponent */ base = 10; exactness = 0; sign = 1; i = 0; /* integer part */ f = 0.0; /* fractional part */ div = base; exp = 0; esign = 1; numtype = Integer; for(sp = stringbuf; *sp; sp++){ if(canprefix && *sp == L'#'){ sp++; switch(*sp){ case L'b': div = base = 2; break; case L'o': div = base = 8; break; case L'd': div = base = 10; break; case L'x': div = base = 16; break; case L'i': exactness = Inexact; break; case L'e': exactness = 0; break; default: goto symbol; } continue; } canprefix = 0; if(signstate && (*sp == '-' || *sp == '+')){ if(signstate == 1) sign = (*sp == '-') ? -1 : 1; else esign = (*sp == '-') ? -1 : 1; signstate = 0; continue; } signstate = 0; if((d = getdigit(*sp, base)) >= 0){ if(digitstate == 0) i = i*base + d; else if(digitstate == 1){ f += d/div; div *= base; }else exp = exp*base + d; continue; } if(*sp == L'.'){ if(digitstate == 1) goto symbol; digitstate = 1; numtype = Real; continue; } if(*sp == L'e'){ numtype = Real; signstate = digitstate = 2; continue; } goto symbol; } i *= sign; if(numtype == Integer){ return makeinteger(i); }else{ f += i; if(esign < 0) while(exp--) f /= 10.0f; // TODO: or base? else while(exp--) f *= 10.0f; return makereal(f); } symbol: s = smprint("%S", stringbuf); e = getsymbol(s); free(s); return e;} static Sexp*readsymbol(Biobuf *file){ Rune r; char *s; Sexp *e; bufind = 0; while(r = nextrunesp(file), !isdelim(r)) stringbuf[bufind++] = r; ungetrune(r); stringbuf[bufind++] = 0; s = smprint("%S", stringbuf); e = getsymbol(s); free(s); return e;} Sexp*readsexp(Biobuf *file){ Rune r, rr; Sexp *lst, *vec; int len, i; if(r = nextrune(file), r == EOF) return nil; switch(r){ /* List */ case L'(': return readlist(file); case L')': Bprint(&perr, "error: s-expression can't start with ')'\n"); Bflush(&perr); return nil; /* String */ case L'"': bufind = 0; if(peekind != 0){ Bprint(&perr, "peekind shouldn't be 0 when reading string\n"); Bflush(&perr); } while(r = Bgetrune(file), r != L'"'){ if(r == L'\\'){ r = Bgetrune(file); switch(r){ case L'n': r = '\n'; break; case L't': r = '\t'; break; default: break; } } stringbuf[bufind++] = r; } stringbuf[bufind++] = 0; return consatom(String, 0, (uintptr)smprint("%S", stringbuf)); case L'#': r = nextrunesp(file); /* Booleans */ if(r == L'f') return false; if(r == L't') return true; /* Vector */ if(r == L'('){ lst = readproperlist(file); pushroot(&lst); len = listlen(lst); vec = makevector(len, empty); for(i = 0; i < len; i++, lst = lst->cdr) vectorset(vec, i, lst->car); poproot(); return vec; } /* Character */ if(r == L'\\'){ bufind = 0; while(r = nextrunesp(file), !isdelim(r)) stringbuf[bufind++] = r; ungetrune(r); stringbuf[bufind++] = 0; if(bufind == 2) return consatom(Character, 0, (uintptr)stringbuf[0]); if(runestrcmp(stringbuf, L"newline") == 0) return consatom(Character, 0, (uintptr)L'\n'); if(runestrcmp(stringbuf, L"space") == 0) return consatom(Character, 0, (uintptr)L' '); Bprint(&perr, "unknown character: %S\n", stringbuf); Bflush(&perr); return nil; } if(runestrchr(L"iebodx", r)){ ungetrune(r); ungetrune(L'#'); return readnumber(file); } Bprint(&perr, "can't read after hash\n"); Bflush(&perr); return nil; case L'.': r = nextrunesp(file); if(runestrchr(L"0123456789", r)){ ungetrune(r); ungetrune(L'.'); return readnumber(file); } if(r == ' '){ Bprint(&perr, "error: dot not allowed here\n"); Bflush(&perr); return nil; } ungetrune(r); ungetrune(L'.'); return readsymbol(file); case L'+': case L'-': rr = r; r = nextrunesp(file); if(runestrchr(L"i0123456789abcdefABCDEF", r)){ ungetrune(r); ungetrune(rr); /* can also return symbol */ return readnumber(file); } ungetrune(r); ungetrune(rr); return readsymbol(file); case L'\'': return cons(quote, cons(readsexp(file), empty)); case L'`': return cons(quasiquote, cons(readsexp(file), empty)); case L',': r = nextrunesp(file); if(r == L'@') return cons(unquotesplicing, cons(readsexp(file), empty)); ungetrune(r); return cons(unquote, cons(readsexp(file), empty)); default: ungetrune(r); if(runestrchr(L"0123456789", r)) return readnumber(file); return readsymbol(file); }} voidprintsexpnl(Sexp *e, Biobuf *file){ printsexp(e, file); Bprint(file, "\n"); Bflush(file);} voidprintsexp(Sexp *o, Biobuf *file){ int len, i; Rune r; if(ispair(o)){ /* HACK: */ if(o->car == beta){ Bprint(file, "β-closure"); return; } Bprint(file, "("); for(; o != empty && ispair(o); o = o->cdr){ printsexp(o->car, file); if(o->cdr != empty){ Bprint(file, " "); if(!ispair(o->cdr)){ Bprint(file, ". "); printsexp(o->cdr, file); } } } Bprint(file, ")"); }else switch(gettype(o)){ case Symbol: Bprint(file, "%s", (char*)o->cdr); break; case Vector: Bprint(file, "#("); len = vectorlen(o); for(i = 0; i < len; i++){ printsexp(vectorref(o, i), file); if(i+1 != len) Bprint(file, " "); } Bprint(file, ")"); break;/* case Lambda: Bprint(file, "λ"); break;*/ case Primitive: Bprint(file, "#<primitive %p>", o->cdr); break; case Number: if((getnumtype(o) & 0x3) == Integer) Bprint(file, "%d", o->integ); else Bprint(file, "%f", o->real); break; case Character: r = (Rune)(uintptr)o->cdr; if(r == L'\n') Bprint(file, "#\\newline"); else if(r == L' ') Bprint(file, "#\\space"); else Bprint(file, "#\\%C", r); break; case String: Bprint(file, "\"%s\"", (char*)o->cdr); break; case Port: Bprint(file, "port"); break; case Special: if(o == empty) Bprint(file, "()"); else if(o == true) Bprint(file, "#t"); else if(o == false) Bprint(file, "#f"); else Bprint(file, "special"); break; case Free: Bprint(file, "free"); break; }}
#include "scheme.h"
static vlong peekbuf[10];
static int peekind;
static Rune stringbuf[512];
static int bufind;
#define EOF ~0
static int
isdelim(Rune r)
{
return isspacerune(r) || runestrchr(L"()\"", r);
}
/* For ALL Biobufs */
static void
ungetrune(Rune r)
{
peekbuf[peekind++] = r;
}
static Rune
nextrunesp(Biobuf *file)
{
Rune r;
if(peekind == 0){
vlong x;
x = Bgetrune(file);
r = x;
if(x < 0)
r = EOF;
}else
r = peekbuf[--peekind];
if(r == L';')
while(r = Bgetrune(file), r != L'\n');
if(isspacerune(r))
r = L' ';
return r;
}
static Rune
nextrune(Biobuf *file)
{
Rune r;
while(r = nextrunesp(file), r == L' ');
return r;
}
static Sexp*
readlist(Biobuf *file)
{
Rune r;
Sexp *first, *second;
if(r = nextrune(file), r == L')')
return empty;
ungetrune(r);
first = readsexp(file);
pushroot(&first);
if(r = nextrune(file), r == L'.'){
// TODO: expect delimiter
second = readsexp(file);
poproot();
if(second == nil){
Bprint(&perr, "error: invalid cdr\n");
return nil;
}
if(r = nextrune(file), r != L')'){
Bprint(&perr, "error: ')' expected\n");
return empty;
}
return cons(first, second);
}else{
ungetrune(r);
second = readlist(file);
poproot();
return cons(first, second);
}
}
static Sexp*
readproperlist(Biobuf *file)
{
Rune r;
Sexp *first, *second;
if(r = nextrune(file), r == L')')
return empty;
ungetrune(r);
first = readsexp(file);
pushroot(&first);
second = readproperlist(file);
poproot();
return cons(first, second);
}
static int
getdigit(Rune r, int base)
{
int d;
if(r >= L'0' && r <= L'9')
d = r-L'0';
else if(r >= L'A' && r <= L'F')
d = r-L'A' + 10;
else if(r >= L'a' && r <= L'f')
d = r-L'a' + 10;
else
return -1;
if(d >= base)
return -1;
return d;
}
/* TODO */
static Sexp*
readnumber(Biobuf *file)
{
Sexp *e;
Rune r, *sp;
char *s;
int d, i;
double f, div;
int exp, esign;
int exactness;
int base;
int canprefix, signstate, digitstate;
int sign;
int numtype;
bufind = 0;
while(r = nextrunesp(file), !isdelim(r))
stringbuf[bufind++] = r;
ungetrune(r);
stringbuf[bufind++] = 0;
canprefix = 1;
signstate = 0; /* 0 can't read sign
1 can read sign
2 can read esign */
digitstate = 0; /* 0 reading integer part
1 reading fractional part
2 reading exponent */
base = 10;
exactness = 0;
sign = 1;
i = 0; /* integer part */
f = 0.0; /* fractional part */
div = base;
exp = 0;
esign = 1;
numtype = Integer;
for(sp = stringbuf; *sp; sp++){
if(canprefix && *sp == L'#'){
sp++;
switch(*sp){
case L'b':
div = base = 2;
break;
case L'o':
div = base = 8;
break;
case L'd':
div = base = 10;
break;
case L'x':
div = base = 16;
break;
case L'i':
exactness = Inexact;
break;
case L'e':
exactness = 0;
break;
default:
goto symbol;
}
continue;
}
canprefix = 0;
if(signstate && (*sp == '-' || *sp == '+')){
if(signstate == 1)
sign = (*sp == '-') ? -1 : 1;
else
esign = (*sp == '-') ? -1 : 1;
signstate = 0;
continue;
}
signstate = 0;
if((d = getdigit(*sp, base)) >= 0){
if(digitstate == 0)
i = i*base + d;
else if(digitstate == 1){
f += d/div;
div *= base;
}else
exp = exp*base + d;
continue;
}
if(*sp == L'.'){
if(digitstate == 1)
goto symbol;
digitstate = 1;
numtype = Real;
continue;
}
if(*sp == L'e'){
numtype = Real;
signstate = digitstate = 2;
continue;
}
goto symbol;
}
i *= sign;
if(numtype == Integer){
return makeinteger(i);
}else{
f += i;
if(esign < 0)
while(exp--)
f /= 10.0f; // TODO: or base?
else
while(exp--)
f *= 10.0f;
return makereal(f);
}
symbol:
s = smprint("%S", stringbuf);
e = getsymbol(s);
free(s);
return e;
}
static Sexp*
readsymbol(Biobuf *file)
{
Rune r;
char *s;
Sexp *e;
bufind = 0;
while(r = nextrunesp(file), !isdelim(r))
stringbuf[bufind++] = r;
ungetrune(r);
stringbuf[bufind++] = 0;
s = smprint("%S", stringbuf);
e = getsymbol(s);
free(s);
return e;
}
Sexp*
readsexp(Biobuf *file)
{
Rune r, rr;
Sexp *lst, *vec;
int len, i;
if(r = nextrune(file), r == EOF)
return nil;
switch(r){
/* List */
case L'(':
return readlist(file);
case L')':
Bprint(&perr, "error: s-expression can't start with ')'\n");
Bflush(&perr);
return nil;
/* String */
case L'"':
bufind = 0;
if(peekind != 0){
Bprint(&perr, "peekind shouldn't be 0 when reading string\n"); Bflush(&perr);
}
while(r = Bgetrune(file), r != L'"'){
if(r == L'\\'){
r = Bgetrune(file);
switch(r){
case L'n':
r = '\n';
break;
case L't':
r = '\t';
break;
default:
break;
}
}
stringbuf[bufind++] = r;
}
stringbuf[bufind++] = 0;
return consatom(String, 0, (uintptr)smprint("%S", stringbuf));
case L'#':
r = nextrunesp(file);
/* Booleans */
if(r == L'f')
return false;
if(r == L't')
return true;
/* Vector */
if(r == L'('){
lst = readproperlist(file);
pushroot(&lst);
len = listlen(lst);
vec = makevector(len, empty);
for(i = 0; i < len; i++, lst = lst->cdr)
vectorset(vec, i, lst->car);
poproot();
return vec;
}
/* Character */
if(r == L'\\'){
bufind = 0;
while(r = nextrunesp(file), !isdelim(r))
stringbuf[bufind++] = r;
ungetrune(r);
stringbuf[bufind++] = 0;
if(bufind == 2)
return consatom(Character, 0, (uintptr)stringbuf[0]);
if(runestrcmp(stringbuf, L"newline") == 0)
return consatom(Character, 0, (uintptr)L'\n');
if(runestrcmp(stringbuf, L"space") == 0)
return consatom(Character, 0, (uintptr)L' ');
Bprint(&perr, "unknown character: %S\n", stringbuf);
Bflush(&perr);
return nil;
}
if(runestrchr(L"iebodx", r)){
ungetrune(r);
ungetrune(L'#');
return readnumber(file);
}
Bprint(&perr, "can't read after hash\n");
Bflush(&perr);
return nil;
case L'.':
r = nextrunesp(file);
if(runestrchr(L"0123456789", r)){
ungetrune(r);
ungetrune(L'.');
return readnumber(file);
}
if(r == ' '){
Bprint(&perr, "error: dot not allowed here\n");
Bflush(&perr);
return nil;
}
ungetrune(r);
ungetrune(L'.');
return readsymbol(file);
case L'+':
case L'-':
rr = r;
r = nextrunesp(file);
if(runestrchr(L"i0123456789abcdefABCDEF", r)){
ungetrune(r);
ungetrune(rr);
/* can also return symbol */
return readnumber(file);
}
ungetrune(r);
ungetrune(rr);
return readsymbol(file);
case L'\'':
return cons(quote, cons(readsexp(file), empty));
case L'`':
return cons(quasiquote, cons(readsexp(file), empty));
case L',':
r = nextrunesp(file);
if(r == L'@')
return cons(unquotesplicing, cons(readsexp(file), empty));
ungetrune(r);
return cons(unquote, cons(readsexp(file), empty));
default:
ungetrune(r);
if(runestrchr(L"0123456789", r))
return readnumber(file);
return readsymbol(file);
}
}
void
printsexpnl(Sexp *e, Biobuf *file)
{
printsexp(e, file);
Bprint(file, "\n");
Bflush(file);
}
void
printsexp(Sexp *o, Biobuf *file)
{
int len, i;
Rune r;
if(ispair(o)){
/* HACK: */
if(o->car == beta){
Bprint(file, "β-closure");
return;
}
Bprint(file, "(");
for(; o != empty && ispair(o); o = o->cdr){
printsexp(o->car, file);
if(o->cdr != empty){
Bprint(file, " ");
if(!ispair(o->cdr)){
Bprint(file, ". ");
printsexp(o->cdr, file);
}
}
}
Bprint(file, ")");
}else switch(gettype(o)){
case Symbol:
Bprint(file, "%s", (char*)o->cdr);
break;
case Vector:
Bprint(file, "#(");
len = vectorlen(o);
for(i = 0; i < len; i++){
printsexp(vectorref(o, i), file);
if(i+1 != len)
Bprint(file, " ");
}
Bprint(file, ")");
break;
/*
case Lambda:
Bprint(file, "λ");
break;
*/
case Primitive:
Bprint(file, "#<primitive %p>", o->cdr);
break;
case Number:
if((getnumtype(o) & 0x3) == Integer)
Bprint(file, "%d", o->integ);
else
Bprint(file, "%f", o->real);
break;
case Character:
r = (Rune)(uintptr)o->cdr;
if(r == L'\n')
Bprint(file, "#\\newline");
else if(r == L' ')
Bprint(file, "#\\space");
else
Bprint(file, "#\\%C", r);
break;
case String:
Bprint(file, "\"%s\"", (char*)o->cdr);
break;
case Port:
Bprint(file, "port");
break;
case Special:
if(o == empty)
Bprint(file, "()");
else if(o == true)
Bprint(file, "#t");
else if(o == false)
Bprint(file, "#f");
else
Bprint(file, "special");
break;
case Free:
Bprint(file, "free");
break;
}
}