Created
February 17, 2023 12:42
-
-
Save swatson555/8cc36d8d022d7e5cc44a5edb2c4f7d0b to your computer and use it in GitHub Desktop.
Heap based scheme machine.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Heap based virtual machine described in section 3.4 of Three Implementation Models for Scheme, Dybvig | |
*/ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <ctype.h> | |
#include <assert.h> | |
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; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment