|
/* Heap based virtual machine described in section 3.4 of Three Implementation Models for Scheme, Dybvig |
|
*/ |
|
|
|
#include |
|
#include |
|
#include |
|
#include |
|
#include |
|
|
|
char token[128][32]; |
|
|
|
int lexer(char* input) { |
|
int ii = 0; // input index |
|
int ti = 0; // token index |
|
|
|
while(input[ii] != ‘\0’) |
|
switch(input[ii]) { |
|
// Ignore whitespace and newlines |
|
case ‘ ‘: |
|
case ‘\n’: |
|
++ii; |
|
break; |
|
|
|
// Turn a left parenthesis into a token. |
|
case ‘(‘: |
|
token[ti][0] = ‘(‘; |
|
token[ti][1] = ‘\0’; |
|
++ii; |
|
++ti; |
|
break; |
|
|
|
// Turn a right parenthesis into a token. |
|
case ‘)’: |
|
token[ti][0] = ‘)’; |
|
token[ti][1] = ‘\0’; |
|
++ii; |
|
++ti; |
|
break; |
|
|
|
// Turn an apostrophe into a token. |
|
case ‘\”: |
|
token[ti][0] = ‘\”; |
|
token[ti][1] = ‘\0’; |
|
++ii; |
|
++ti; |
|
break; |
|
|
|
// Anything else is a symbol |
|
default: |
|
for(int i = 0;; ++i) { |
|
if(input[ii] != ‘ ‘ && |
|
input[ii] != ‘)’ && |
|
input[ii] != ‘(‘ && |
|
input[ii] != ‘\n’ && |
|
input[ii] != ‘\0’) { |
|
token[ti][i] = input[ii++]; |
|
} |
|
else { |
|
token[ti][i] = ‘\0’; |
|
break; |
|
} |
|
} |
|
++ti; |
|
break; |
|
} |
|
return ti; |
|
} |
|
|
|
int curtok; |
|
|
|
char* nexttok() { |
|
return token[curtok++]; |
|
} |
|
|
|
char* peektok() { |
|
return token[curtok]; |
|
} |
|
|
|
|
|
typedef struct Pair { |
|
void* car; |
|
void* cdr; |
|
} Pair; |
|
|
|
typedef struct Text { |
|
char* car; |
|
struct Text* cdr; |
|
} Text; |
|
|
|
Pair text[1280]; |
|
Pair* textptr; |
|
|
|
int istext(void* x) { |
|
return x >= (void*)&text && |
|
x (void*)&text[1280]; |
|
} |
|
|
|
Pair* cons(void* x, void* y) { |
|
assert(istext(textptr)); |
|
textptr->car = x; |
|
textptr->cdr = y; |
|
return textptr++; |
|
} |
|
|
|
void* read(char* ln); |
|
void* read_exp(); |
|
void* read_list(); |
|
|
|
void* read(char* ln) { |
|
// Initialize the lexer and list memory. |
|
curtok = 0; |
|
textptr = text; |
|
|
|
lexer(ln); |
|
return read_exp(); |
|
} |
|
|
|
void* read_exp() { |
|
char* tok = nexttok(); |
|
if (tok[0] == ‘(‘ && peektok()[0] == ‘)’) { |
|
nexttok(); |
|
return NULL; |
|
} |
|
else if (tok[0] == ‘\”) |
|
return cons(“quote”, cons(read_exp(), NULL)); |
|
else if (tok[0] == ‘(‘) |
|
return read_list(); |
|
else |
|
return tok; |
|
} |
|
|
|
void* read_list() { |
|
char* tok = peektok(); |
|
if(tok[0] == ‘)’) { |
|
nexttok(); |
|
return NULL; |
|
} |
|
else if(tok[0] == ‘.’) { |
|
nexttok(); |
|
tok = read_exp(); |
|
nexttok(); |
|
return tok; |
|
} |
|
else { |
|
void* fst = read_exp(); |
|
void* snd = read_list(); |
|
return cons(fst, snd); |
|
} |
|
} |
|
|
|
void print(void* exp); |
|
void print_exp(void* exp); |
|
void print_list(Pair* list); |
|
void print_cons(Pair* pair); |
|
|
|
void print(void* exp) { |
|
print_exp(exp); |
|
printf(“\n”); |
|
} |
|
|
|
void print_exp(void* exp) { |
|
if (istext(exp)) { |
|
Pair* pair = exp; |
|
if(!istext(pair->cdr) && pair->cdr != NULL) { |
|
printf(“(“); |
|
print_cons(exp); |
|
printf(“)”); |
|
} |
|
else { |
|
printf(“(“); |
|
print_list(exp); |
|
} |
|
} |
|
else |
|
printf(“%s”, exp ? (char*)exp : “()”); |
|
} |
|
|
|
void print_list(Pair* list) { |
|
if (list->cdr == NULL) { |
|
print_exp(list->car); |
|
printf(“)”); |
|
} |
|
else { |
|
if(!istext(list->cdr) && list->cdr != NULL) { |
|
print_cons(list); |
|
printf(“)”); |
|
} |
|
else { |
|
print_exp(list->car); |
|
printf(” “); |
|
print_list(list->cdr); |
|
} |
|
} |
|
} |
|
|
|
void print_cons(Pair* pair) { |
|
print_exp(pair->car); |
|
printf(” . “); |
|
print_exp(pair->cdr); |
|
} |
|
|
|
|
|
Pair* compile(void* exp, void* next) { |
|
if (istext(exp)) { |
|
Text* p = exp; |
|
if (strcmp(p->car, “quote”) == 0) { |
|
return cons(“constant”, cons(p->cdr->car, cons(next, NULL))); |
|
} |
|
else if (strcmp(p->car, “lambda”) == 0) { |
|
return cons(“close”, cons(p->cdr->car, cons(compile(p->cdr->cdr->car, cons(“return”, NULL)), cons(next, NULL)))); |
|
} |
|
else if (strcmp(p->car, “if”) == 0) { |
|
return compile(p->cdr->car, cons(“test”, cons(compile(p->cdr->cdr->car, next), |
|
cons(compile(p->cdr->cdr->cdr->car, next), |
|
NULL)))); |
|
} |
|
else if (strcmp(p->car, “set!”) == 0) { |
|
return compile(p->cdr->cdr->car, cons(“assign”, cons(p->cdr->car, cons(next, NULL)))); |
|
} |
|
else if (strcmp(p->car, “call/cc”) == 0) { |
|
void* c = cons(“conti”, cons(cons(“argument”, cons(compile(p->cdr->car, cons(“apply”, NULL)), NULL)), NULL)); |
|
Text* n = next; |
|
if (strcmp(n->car, “return”) == 0) |
|
return c; |
|
else |
|
return cons(“frame”, cons(next, cons(c, NULL))); |
|
} |
|
else { |
|
Pair* args = (Pair*)p->cdr; |
|
void* c = compile(p->car, cons(“apply”, NULL)); |
|
while (args) { |
|
c = compile(args->car, cons(“argument”, cons(c, NULL))); |
|
args = args->cdr; |
|
} |
|
Text* n = next; |
|
if (strcmp(n->car, “return”) == 0) |
|
return c; |
|
else |
|
return cons(“frame”, cons(next, cons(c, NULL))); |
|
} |
|
} |
|
else if(isdigit(*((char*)exp))) { // a number |
|
return cons(“constant”, cons(exp, cons(next, NULL))); |
|
} |
|
else if(strcmp(exp, “#t”) == 0) { // a boolean |
|
return cons(“constant”, cons(exp, cons(next, NULL))); |
|
} |
|
else if(strcmp(exp, “#f”) == 0) { // a boolean |
|
return cons(“constant”, cons(exp, cons(next, NULL))); |
|
} |
|
else { // a symbol |
|
return cons(“refer”, cons(exp, cons(next, NULL))); |
|
} |
|
} |
|
|
|
void* get(void* env, char* var) { |
|
Pair* e = env; |
|
while(env) { |
|
Pair* cur = e->car; |
|
Pair* vars = cur->car; |
|
Pair* vals = cur->cdr; |
|
while (vars && vals) { |
|
if (strcmp(vars->car, var) == 0) |
|
return vals->car; |
|
vars = vars->cdr; |
|
vals = vals->cdr; |
|
} |
|
e = e->cdr; |
|
} |
|
fprintf(stderr, “No definition in environment for %s.\n”, var); |
|
assert(0); |
|
} |
|
|
|
void set(void* env, char* var, char* val) { |
|
void* ref = get(env, var); |
|
ref = val; |
|
} |
|
|
|
void* extend(void* env, void* vars, void* vals) { |
|
return cons(cons(vars, vals), env); |
|
} |
|
|
|
void* callframe(void* next, void* env, void* rib, void* stack) { |
|
return cons(next, cons(env, cons(rib, cons(stack, NULL)))); |
|
} |
|
|
|
void* closure(void* body, void* env, void* vars) { |
|
return cons(body, cons(env, cons(vars, NULL))); |
|
} |
|
|
|
void* continuation(void* stack) { |
|
return closure(cons(“nuate”, cons(stack, cons(“v”, NULL))), NULL, cons(“v”, NULL)); |
|
} |
|
|
|
|
|
void* accum; |
|
void* next; |
|
void* env; |
|
void* rib; |
|
void* stack; |
|
|
|
void virtmach() { |
|
Text* n = next; |
|
if (strcmp(n->car, “halt”) == 0) { |
|
} |
|
else if (strcmp(n->car, “refer”) == 0) { |
|
accum = get(env, n->cdr->car); |
|
next = n->cdr->cdr->car; |
|
return virtmach(); |
|
} |
|
else if (strcmp(n->car, “constant”) == 0) { |
|
accum = n->cdr->car; |
|
next = n->cdr->cdr->car; |
|
return virtmach(); |
|
} |
|
else if (strcmp(n->car, “close”) == 0) { |
|
void* vars = n->cdr->car; |
|
void* body = n->cdr->cdr->car; |
|
void* x = n->cdr->cdr->cdr->car; |
|
accum = closure(body, env, vars); |
|
next = x; |
|
return virtmach(); |
|
} |
|
else if (strcmp(n->car, “test”) == 0) { |
|
void* consequent = n->cdr->car; |
|
void* alternate = n->cdr->cdr->car; |
|
next = strcmp(accum, “#f”) == 0 ? alternate : consequent; |
|
return virtmach(); |
|
} |
|
else if (strcmp(n->car, “assign”) == 0) { |
|
set(env, n->cdr->car, accum); |
|
next = n->cdr->cdr->car; |
|
return virtmach(); |
|
} |
|
else if (strcmp(n->car, “conti”) == 0) { |
|
accum = continuation(stack); |
|
next = n->cdr->car; |
|
return virtmach(); |
|
} |
|
else if (strcmp(n->car, “nuate”) == 0) { |
|
stack = n->cdr->car; |
|
accum = get(env, n->cdr->cdr->car); |
|
next = cons(“return”, NULL); |
|
return virtmach(); |
|
} |
|
else if (strcmp(n->car, “frame”) == 0) { |
|
stack = callframe(n->cdr->car, env, rib, stack); |
|
rib = NULL; |
|
next = n->cdr->cdr->car; |
|
return virtmach(); |
|
} |
|
else if (strcmp(n->car, “argument”) == 0) { |
|
rib = cons(accum, rib); |
|
next = n->cdr->car; |
|
return virtmach(); |
|
} |
|
else if (strcmp(n->car, “apply”) == 0) { |
|
Text* a = accum; |
|
void* body = a->car; |
|
void* clos = a->cdr->car; |
|
void* vars = a->cdr->cdr->car; |
|
env = extend(env, vars, rib); |
|
rib = NULL; |
|
next = body; |
|
return virtmach(); |
|
} |
|
else if (strcmp(n->car, “return”) == 0) { |
|
Text* s = stack; |
|
next = s->car; |
|
env = s->cdr->car; |
|
rib = s->cdr->cdr->car; |
|
stack = s->cdr->cdr->cdr->car; |
|
return virtmach(); |
|
} |
|
else { |
|
fprintf(stderr, “Unhandled operation.\n”); |
|
assert(0); |
|
} |
|
} |
|
|
|
|
|
int main(int argc, char** argv) { |
|
// note! repl implies there’s a top-level but there isn’t… |
|
printf(“Lisp REPL\n\n”); |
|
printf(“>> “); |
|
|
|
char buffer[256]; |
|
while (fgets(buffer, 256, stdin)) { |
|
next = compile(read(buffer), cons(“halt”, NULL)); |
|
virtmach(); |
|
print(accum); |
|
printf(“>> “); |
|
} |
|
return 0; |
|
} |