Created
May 30, 2012 12:12
-
-
Save jmgunn87/2835892 to your computer and use it in GitHub Desktop.
lisp.c
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
/* lisp.c: high-speed LISP interpreter */ | |
/* | |
The storage required by this interpreter is 8 * 4 = 32 bytes times | |
the symbolic constant SIZE, which is 32 * 1,000,000 = | |
32 megabytes. To run this interpreter in small machines, | |
reduce the #define SIZE 1000000 below. | |
To compile, type | |
cc -O -olisp lisp.c | |
To run interactively, type | |
lisp | |
To run with output on screen, type | |
lisp <test.l | |
To run with output in file, type | |
lisp <test.l >test.r | |
Reference: Kernighan & Ritchie, | |
The C Programming Language, Second Edition, | |
Prentice-Hall, 1988. | |
*/ | |
#include <stdio.h> | |
#include <time.h> | |
#define SIZE 1000000 /* numbers of nodes of tree storage */ | |
#define nil 0 /* end of list marker */ | |
long car[SIZE], cdr[SIZE]; /* tree storage */ | |
short atom[SIZE]; /* is it an atom? */ | |
short numb[SIZE]; /* is it a number? */ | |
/* The following is only used for atoms */ | |
long vlst[SIZE]; /* bindings of each atom */ | |
long pname[SIZE]; /* print name of each atom = list of characters in reverse */ | |
/* The following is only used for atoms that are the names of primitive functions */ | |
short pf_numb[SIZE]; /* primitive function number (for interpreter switch) */ | |
short pf_args[SIZE]; /* number of arguments + 1 (for input parser) */ | |
long obj_lst; /* list of all atoms (& every other token read except numbers) */ | |
/* locations of atoms in tree storage */ | |
long wrd_nil, wrd_true, wrd_false, wrd_define, wrd_let, wrd_lambda, wrd_quote, wrd_if; | |
long wrd_car, wrd_cdr, wrd_cadr, wrd_caddr, wrd_eval, wrd_try; | |
long wrd_no_time_limit, wrd_out_of_time, wrd_out_of_data, wrd_success, wrd_failure; | |
long left_bracket, right_bracket, left_paren, right_paren, double_quote; | |
long wrd_zero, wrd_one; | |
long next = 0; /* next free node */ | |
long col = 0; /* column in each 50 character chunk of output | |
(preceeded by 12 char prefix) */ | |
time_t time1; /* clock at start of execution */ | |
time_t time2; /* clock at end of execution */ | |
long turing_machine_tapes; /* stack of binary data for try's */ | |
long display_enabled; /* stack of flags whether to capture displays or not */ | |
long captured_displays; /* stack of stubs to collect captured displays on */ | |
long q; /* for converting s-expressions into lists of bits */ | |
long buffer2; /* buffer for converting lists of bits into s-expressions */ | |
/* contains list of all the words in an input record */ | |
void initialize_atoms(void); /* initialize atoms */ | |
long mk_atom(long number, char *name, long args); /* make an atom */ | |
long mk_numb(long value); /* make an number */ | |
long mk_string(char *p); /* make list of characters */ | |
long eq_wrd(long x, long y); /* are two lists of characters equal ? */ | |
long lookup_word(long x); /* look up word in object list ? */ | |
long cons(long x, long y); /* get free node & stuff x & y in it */ | |
long out(char *x, long y); /* output expression */ | |
void out_lst(long x); /* output list */ | |
void out_atm(long x); /* output atom */ | |
void out_chr(long x); /* output character */ | |
long in_word2(void); /* read word */ | |
long in_word(void); /* read word - skip comments */ | |
long in(long mexp, long rparenokay); /* input m-exp */ | |
long only_digits(long x); /* check if list of characters are exclusively digits */ | |
long ev(long e); /* initialize and evaluate expression */ | |
long eval(long e, long d); /* evaluate expression */ | |
long evalst(long e, long d); /* evaluate list of expressions */ | |
void clean_env(void); /* clean environment */ | |
void restore_env(void); /* restore unclean environment */ | |
/* bind values of arguments to formal parameters */ | |
void bind(long vars, long args); | |
long append(long x, long y); /* append two lists */ | |
long eq(long x, long y); /* equal predicate */ | |
long length(long x); /* number of elements in list */ | |
long compare(long x, long y); /* compare two decimal numbers */ | |
long add1(long x); /* add 1 to decimal number */ | |
long sub1(long x); /* subtract 1 from decimal number */ | |
long nmb(long x); /* pick-up decimal number from atom & convert non-number to zero */ | |
long remove_leading_zeros(long x); /* from reversed list of digits of decimal number */ | |
long addition(long x, long y, long carry_in); /* add two decimal numbers */ | |
long multiplication(long x, long y); /* multiply two decimal numbers */ | |
long exponentiation(long base, long exponent); /* base raised to the power exponent */ | |
long subtraction(long x, long y, long borrow_in); /* x - y assumes x >= y */ | |
long base2_to_10(long x); /* convert bit string to decimal number */ | |
long halve(long x); /* used to convert decimal number to bit string */ | |
long base10_to_2(long x); /* convert decimal number to bit string */ | |
long size(long x); /* number of characters in print representation */ | |
long read_bit(void); /* read one square of Turing machine tape */ | |
void write_chr(long x); /* convert character into 8 bits */ | |
void write_atm(long x); /* convert atom into 8 bits per character */ | |
void write_lst(long x); /* convert s-exp into list of bits */ | |
long read_record(void); /* read record from Turing machine tape */ | |
long read_char(void); /* read one character from Turing machine tape */ | |
long read_word(void); /* read word from Turing machine tape */ | |
long read_expr(long rparenokay); /* read s-exp from Turing machine tape */ | |
main() /* lisp main program */ | |
{ | |
time1 = time(NULL); /* start timer */ | |
printf("LISP Interpreter Run\n"); | |
initialize_atoms(); | |
while (1) { | |
long e, f, name, def; | |
printf("\n"); | |
/* read lisp meta-expression, ) not okay */ | |
e = in(1,0); | |
printf("\n"); | |
f = car[e]; | |
name = car[cdr[e]]; | |
def = car[cdr[cdr[e]]]; | |
if (f == wrd_define) { | |
/* definition */ | |
if (atom[name]) { | |
/* variable definition, e.g., define x (a b c) */ | |
} /* end of variable definition */ | |
else { | |
/* function definition, e.g., define (F x y) cons x cons y nil */ | |
long var_list = cdr[name]; | |
name = car[name]; | |
def = cons(wrd_lambda,cons(var_list,cons(def,nil))); | |
} /* end of function definition */ | |
out("define",name); | |
out("value",def); | |
/* new binding replaces old */ | |
car[vlst[name]] = def; | |
continue; | |
} /* end of definition */ | |
/* write corresponding s-expression */ | |
e = out("expression",e); | |
/* evaluate expression */ | |
e = out("value",ev(e)); | |
} | |
} | |
void initialize_atoms(void) /* initialize atoms */ | |
{ | |
long i; | |
if ( nil != mk_atom(0,"()",0) ) { | |
printf("nil != 0\n"); | |
exit(0); /* terminate execution */ | |
} | |
wrd_nil = mk_atom(0,"nil",0); | |
car[vlst[wrd_nil]] = nil; /* so that value of nil is () */ | |
wrd_true = mk_atom(0,"true",0); | |
wrd_false = mk_atom(0,"false",0); | |
wrd_no_time_limit = mk_atom(0,"no-time-limit",0); | |
wrd_out_of_time = mk_atom(0,"out-of-time",0); | |
wrd_out_of_data = mk_atom(0,"out-of-data",0); | |
wrd_success = mk_atom(0,"success",0); | |
wrd_failure = mk_atom(0,"failure",0); | |
wrd_define = mk_atom(0,"define",3); | |
wrd_let = mk_atom(0,"let",4); | |
wrd_lambda = mk_atom(0,"lambda",3); | |
wrd_cadr = mk_atom(0,"cadr",2); | |
wrd_caddr = mk_atom(0,"caddr",2); | |
wrd_quote = mk_atom(0,"'",2); | |
wrd_if = mk_atom(0,"if",4); | |
wrd_car = mk_atom(1,"car",2); | |
wrd_cdr = mk_atom(2,"cdr",2); | |
i = mk_atom(3,"cons",3); | |
i = mk_atom(4,"atom",2); | |
i = mk_atom(5,"=",3); | |
i = mk_atom(6,"display",2); | |
i = mk_atom(7,"debug",2); | |
i = mk_atom(8,"append",3); | |
i = mk_atom(9,"length",2); | |
i = mk_atom(10,"<",3); | |
i = mk_atom(11,">",3); | |
i = mk_atom(12,"<=",3); | |
i = mk_atom(13,">=",3); | |
i = mk_atom(14,"+",3); | |
i = mk_atom(15,"*",3); | |
i = mk_atom(16,"^",3); | |
i = mk_atom(17,"-",3); | |
i = mk_atom(18,"base2-to-10",2); | |
i = mk_atom(19,"base10-to-2",2); | |
i = mk_atom(20,"size",2); | |
i = mk_atom(21,"read-bit",1); | |
i = mk_atom(22,"bits",2); | |
i = mk_atom(23,"read-exp",1); | |
wrd_eval = mk_atom(0,"eval",2); | |
wrd_try = mk_atom(0,"try",4); | |
left_bracket = mk_atom(0,"[",0); | |
right_bracket = mk_atom(0,"]",0); | |
left_paren = mk_atom(0,"(",0); | |
right_paren = mk_atom(0,")",0); | |
double_quote = mk_atom(0,"\"",0); | |
wrd_zero = mk_numb(nil); | |
wrd_one = mk_numb(cons('1',nil)); | |
} | |
long mk_atom(long number, char *name, long args) /* make an atom */ | |
{ | |
long a; | |
a = cons(nil,nil); /* get an empty node */ | |
car[a] = cdr[a] = a; /* so that car & cdr of atom = atom */ | |
atom[a] = 1; | |
numb[a] = 0; | |
pname[a] = mk_string(name); | |
pf_numb[a] = number; | |
pf_args[a] = args; | |
/* initially each atom evaluates to self */ | |
vlst[a] = cons(a,nil); | |
/* put on object list */ | |
obj_lst = cons(a,obj_lst); | |
return a; | |
} | |
long mk_numb(long value) /* make an number */ | |
{ /* digits are in reverse order, and 0 has empty list of digits */ | |
long a; | |
a = cons(nil,nil); /* get an empty node */ | |
car[a] = cdr[a] = a; /* so that car & cdr of atom = atom */ | |
atom[a] = 1; | |
numb[a] = 1; | |
pname[a] = value; /* must make 00099 into 99 and 000 into empty list of digits */ | |
/* if necessary before calling this routine (to avoid removing leading zeros unnecessarily) */ | |
pf_numb[a] = 0; | |
pf_args[a] = 0; | |
vlst[a] = 0; | |
/* do not put on object list ! */ | |
return a; | |
} | |
long mk_string(char *p) /* make list of characters */ | |
{ /* in reverse order */ | |
long v = nil; | |
while (*p != '\0') | |
v = cons(*p++,v); | |
return v; | |
} | |
long cons(long x, long y) /* get free node & stuff x & y in it */ | |
{ | |
long z; | |
/* if y is not a list, then cons is x */ | |
if ( y != nil && atom[y] ) return x; | |
if (next >= SIZE) { | |
printf("Storage overflow!\n"); | |
exit(0); | |
} | |
z = next++; | |
car[z] = x; | |
cdr[z] = y; | |
atom[z] = 0; | |
numb[z] = 0; | |
pname[z] = 0; | |
pf_numb[z] = 0; | |
pf_args[z] = 0; | |
vlst[z] = 0; | |
return z; | |
} | |
long out(char *x, long y) /* output expression */ | |
{ | |
printf("%-12s",x); | |
col = 0; /* so can insert \n and 12 blanks | |
every 50 characters of output */ | |
out_lst(y); | |
printf("\n"); | |
return y; | |
} | |
void out_lst(long x) /* output list */ | |
{ | |
if (numb[x] && pname[x] == nil) {out_chr('0'); return;} /* null list of digits means zero */ | |
if (atom[x]) {out_atm(pname[x]); return;} | |
out_chr('('); | |
while (!atom[x]) { | |
out_lst(car[x]); | |
x = cdr[x]; | |
if (!atom[x]) out_chr(' '); | |
} | |
out_chr(')'); | |
} | |
void out_atm(long x) /* output atom */ | |
{ | |
if (x == nil) return; | |
out_atm(cdr[x]); /* output characters in reverse order */ | |
out_chr(car[x]); | |
} | |
void out_chr(long x) /* output character */ | |
{ | |
if (col++ == 50) {printf("\n%-12s"," "); col = 1;} | |
putchar(x); | |
} | |
long eq_wrd(long x, long y) /* are two lists of characters equal ? */ | |
{ | |
if (x == nil) return y == nil; | |
if (y == nil) return 0; | |
if (car[x] != car[y]) return 0; | |
return eq_wrd(cdr[x],cdr[y]); | |
} | |
long lookup_word(long x) /* is word in object list ? */ | |
{ | |
long i = obj_lst; | |
while (!atom[i]) { | |
/* if word is already in object list, don't make a new atom */ | |
if (eq_wrd(pname[car[i]],x)) return car[i]; | |
i = cdr[i]; | |
} | |
/* if word isn't in object list, make new atom & add it to object list */ | |
i = mk_atom(0,"",0); /* adds word to object list */ | |
pname[i] = x; | |
return i; | |
} | |
long in_word2(void) { /* read word */ | |
static long buffer = nil; /* buffer with all the words in a line of input */ | |
long character, word, line, end_of_line, end_of_buffer; | |
while ( buffer == nil ) { /* read in a line */ | |
line = end_of_line = cons(nil,nil); /* stub */ | |
do { /* read characters until '\n' */ | |
character = getchar(); | |
if (character == EOF) { | |
time2 = time(NULL); | |
printf( | |
"End of LISP Run\n\nElapsed time is %.0f seconds.\n", | |
difftime(time2,time1) | |
/* on some systems, above line should instead be: */ | |
/* time2 - time1 */ | |
); | |
exit(0); /* terminate execution */ | |
} /* end of if (character == EOF) */ | |
putchar(character); | |
/* add character to end of line */ | |
end_of_line = cdr[end_of_line] = cons(character,nil); | |
} /* end of read characters until '\n' */ | |
while (character != '\n'); | |
line = cdr[line]; /* remove stub at beginning of line */ | |
/* break line into words at ( ) [ ] ' " characters */ | |
buffer = end_of_buffer = cons(nil,nil); /* stub */ | |
word = nil; | |
while ( line != nil ) { | |
character = car[line]; | |
line = cdr[line]; | |
/* look for characters that break words */ | |
if ( character == ' ' || character == '\n' || | |
character == '(' || character == ')' || | |
character == '[' || character == ']' || | |
character == '\'' || character == '\"' ) | |
{ /* add nonempty word to end of buffer */ | |
if ( word != nil ) | |
end_of_buffer = cdr[end_of_buffer] = cons(word,nil); | |
word = nil; | |
/* add break character to end of buffer */ | |
if ( character != ' ' && character != '\n' ) | |
end_of_buffer = cdr[end_of_buffer] = cons(cons(character,nil),nil); | |
} | |
else | |
{ /* add character to word (in reverse order) */ | |
/* keep only nonblank printable ASCII codes */ | |
if (32 < character && character < 127) | |
word = cons(character,word); | |
} | |
} /* end while ( line != nil ) */ | |
buffer = cdr[buffer]; /* remove stub at beginning of buffer */ | |
} /* end of do while ( buffer == nil ) */ | |
/* if buffer nonempty, return first word in buffer */ | |
word = car[buffer]; | |
buffer = cdr[buffer]; | |
/* first check if word consists only of digits */ | |
if (only_digits(word)) word = mk_numb(remove_leading_zeros(word)); | |
/* also makes 00099 into 99 and 0000 into null */ | |
else word = lookup_word(word); /* look up word in object list */ | |
/* also does mk_atom and adds it to object list if necessary */ | |
return word; | |
} | |
long only_digits(long x) /* check if list of characters are exclusively digits */ | |
{ | |
while (x != nil) { | |
long digit = car[x]; | |
if (digit < '0' || digit > '9') return 0; | |
x = cdr[x]; | |
} | |
return 1; | |
} | |
long in_word(void) /* read word - skip comments */ | |
{ | |
long w; | |
while (1) { | |
w = in_word2(); | |
if (w != left_bracket) return w; | |
while (in_word() != right_bracket) ; /* comments may be nested */ | |
} | |
} | |
long in(long mexp, long rparenokay) /* input m-exp */ | |
{ | |
long w = in_word(), first, last, next, name, def, body, var_lst, i ; | |
if (w == right_paren) if (rparenokay) return w; else return nil; | |
if (w == left_paren) { /* explicit list */ | |
first = last = cons(nil,nil); | |
while ((next = in(mexp,1)) != right_paren) | |
last = cdr[last] = cons(next,nil); | |
return cdr[first]; | |
} /* end if (w == left_paren) */ | |
if (!mexp) return w; /* atom */ | |
if (w == double_quote) return in(0,0); /* s-exp */ | |
if (w == wrd_cadr) /* expand cadr */ | |
return | |
cons(wrd_car, | |
cons(cons(wrd_cdr, | |
cons(in(1,0), | |
nil)), | |
nil)); | |
if (w == wrd_caddr) /* expand caddr */ | |
return | |
cons(wrd_car, | |
cons(cons(wrd_cdr, | |
cons(cons(wrd_cdr, | |
cons(in(1,0), | |
nil)), | |
nil)), | |
nil)); | |
if (w == wrd_let) { /* expand let name def body */ | |
name = in(1,0); | |
def = in(1,0); | |
body = in(1,0); | |
if (!atom[name]) { /* let (name var_lst) def body */ | |
var_lst = cdr[name]; | |
name = car[name]; | |
def = cons(wrd_quote, | |
cons(cons(wrd_lambda, | |
cons(var_lst, | |
cons(def, | |
nil))), | |
nil)); | |
} /* end if (!atom[name]) */ | |
return /* let name def body */ | |
cons(cons(wrd_quote, | |
cons(cons(wrd_lambda, | |
cons(cons(name, | |
nil), | |
cons(body, | |
nil))), | |
nil)), | |
cons(def, | |
nil)); | |
} /* end if (w == wrd_let) */ | |
i = pf_args[w]; | |
if (i == 0) return w; /* normal atom */ | |
/* atom is a primitive function with i-1 arguments */ | |
first = last = cons(w,nil); | |
while (--i > 0) | |
last = cdr[last] = cons(in(1,0),nil); | |
return first; | |
} | |
long ev(long e) /* initialize and evaluate expression */ | |
{ | |
long v; | |
turing_machine_tapes = cons(nil,nil); | |
display_enabled = cons(1,nil); | |
captured_displays = cons(nil,nil); | |
v = eval(e,wrd_no_time_limit); | |
return (v < 0 ? -v : v); | |
} | |
long eval(long e, long d) /* evaluate expression */ | |
{ | |
/* | |
e is expression to be evaluated | |
d is permitted depth - decimal integer, or wrd_no_time_limit | |
*/ | |
long f, v, args, x, y, z, vars, body, var; | |
if (numb[e]) return e; | |
/* find current binding of atomic expression */ | |
if (atom[e]) return car[vlst[e]]; | |
f = eval(car[e],d); /* evaluate function */ | |
e = cdr[e]; /* remove function from list of arguments */ | |
if (f < 0) return f; /* function = error value? */ | |
if (f == wrd_quote) return car[e]; /* quote */ | |
if (f == wrd_if) { /* if then else */ | |
v = eval(car[e],d); | |
e = cdr[e]; | |
if (v < 0) return v; /* error? */ | |
if (v == wrd_false) e = cdr[e]; | |
return eval(car[e],d); | |
} | |
args = evalst(e,d); /* evaluate list of arguments */ | |
if (args < 0) return args; /* error? */ | |
x = car[args]; /* pick up first argument */ | |
y = car[cdr[args]]; /* pick up second argument */ | |
z = car[cdr[cdr[args]]]; /* pick up third argument */ | |
switch (pf_numb[f]) { | |
case 1: return car[x]; | |
case 2: return cdr[x]; | |
case 3: return cons(x,y); | |
case 4: return (atom[x] ? wrd_true : wrd_false); | |
case 5: return (eq(x,y) ? wrd_true : wrd_false); | |
case 6: if (car[display_enabled]) return out("display",x); | |
else {long stub, old_end, new_end; | |
stub = car[captured_displays]; | |
old_end = car[stub]; | |
new_end = cons(x,nil); | |
cdr[old_end] = new_end; | |
car[stub] = new_end; | |
return x;} | |
case 7: return out("debug",x); | |
case 8: return append((atom[x]?nil:x),(atom[y]?nil:y)); | |
case 9: return mk_numb(length(x)); | |
case 10: return (compare(nmb(x),nmb(y)) == '<' ? wrd_true : wrd_false); | |
case 11: return (compare(nmb(x),nmb(y)) == '>' ? wrd_true : wrd_false); | |
case 12: return (compare(nmb(x),nmb(y)) != '>' ? wrd_true : wrd_false); /* <= */ | |
case 13: return (compare(nmb(x),nmb(y)) != '<' ? wrd_true : wrd_false); /* >= */ | |
case 14: return mk_numb(addition(nmb(x),nmb(y),0)); /* no carry in initially */ | |
case 15: return mk_numb(multiplication(nmb(x),nmb(y))); | |
case 16: return mk_numb(exponentiation(nmb(x),nmb(y))); | |
case 17: if (compare(nmb(x),nmb(y)) != '>') return mk_numb(nil); /* y too big to subtract from x */ | |
else return mk_numb(remove_leading_zeros(subtraction(nmb(x),nmb(y),0))); | |
/* no borrow in initially */ | |
case 18: return mk_numb(base2_to_10(x)); /* convert bit string to decimal number */ | |
case 19: return base10_to_2(nmb(x)); /* convert decimal number to bit string */ | |
case 20: return mk_numb(size(x)); /* size of print representation of x */ | |
case 21: return read_bit(); /* read one square of Turing machine tape */ | |
/* convert s-exp to list of bits */ | |
case 22: {v = q = cons(nil,nil); write_lst(x); write_chr('\n'); return cdr[v];} | |
/* read lisp s-expression from Turing machine tape, 8 bits per char */ | |
case 23: {v = read_record(); if (v < 0) return v; return read_expr(0);} | |
} /* end switch (pf_numb[f]) */ | |
if (d != wrd_no_time_limit) { | |
if (d == nil) return - wrd_out_of_time; /* depth exceeded -> error! */ | |
d = sub1(d); /* decrement depth */ | |
} | |
if (f == wrd_eval) { | |
clean_env(); /* clean environment */ | |
v = eval(x,d); | |
restore_env(); /* restore unclean environment */ | |
return v; | |
} | |
if (f == wrd_try) { | |
long stub, old_try_has_smaller_time_limit = 0; /* assume normal case, that x < d */ | |
if (x != wrd_no_time_limit) x = nmb(x); /* convert s-exp into number */ | |
if (x == wrd_no_time_limit || (d != wrd_no_time_limit && compare(x,d) != '<')) { | |
old_try_has_smaller_time_limit = 1; | |
x = d; /* continue to use older more constraining time limit */ | |
} | |
turing_machine_tapes = cons(z,turing_machine_tapes); | |
display_enabled = cons(0,display_enabled); | |
stub = cons(0,nil); /* stub to grow list on */ | |
car[stub] = stub; /* car of stub gives end of list */ | |
captured_displays = cons(stub,captured_displays); | |
clean_env(); | |
v = eval(y,x); | |
restore_env(); | |
turing_machine_tapes = cdr[turing_machine_tapes]; | |
display_enabled = cdr[display_enabled]; | |
stub = cdr[car[captured_displays]]; /* remove stub */ | |
captured_displays = cdr[captured_displays]; | |
if (old_try_has_smaller_time_limit && v == - wrd_out_of_time) return v; | |
if (v < 0) return cons(wrd_failure,cons(-v,cons(stub,nil))); | |
return cons(wrd_success,cons(v,cons(stub,nil))); | |
} | |
f = cdr[f]; | |
vars = car[f]; | |
f = cdr[f]; | |
body = car[f]; | |
bind(vars,args); | |
v = eval(body,d); | |
/* unbind */ | |
while (!atom[vars]) { | |
var = car[vars]; | |
if (atom[var]) | |
vlst[var] = cdr[vlst[var]]; | |
vars = cdr[vars]; | |
} | |
return v; | |
} | |
void clean_env(void) /* clean environment */ | |
{ | |
long o = obj_lst, var; | |
while (o != nil) { | |
var = car[o]; | |
vlst[var] = cons(var,vlst[var]); /* everything eval's to self */ | |
o = cdr[o]; | |
} | |
car[vlst[wrd_nil]] = nil; /* except that value of nil is () */ | |
} | |
void restore_env(void) /* restore unclean environment */ | |
{ | |
long o = obj_lst, var; | |
while (o != nil) { | |
var = car[o]; | |
if (cdr[vlst[var]] != nil) /* was token read in by read-exp within a try */ | |
vlst[var] = cdr[vlst[var]]; | |
o = cdr[o]; | |
} | |
} | |
/* bind values of arguments to formal parameters */ | |
void bind(long vars, long args) | |
{ | |
long var; | |
if (atom[vars]) return; | |
bind(cdr[vars],cdr[args]); | |
var = car[vars]; | |
if (atom[var]) | |
vlst[var] = cons(car[args],vlst[var]); | |
} | |
long evalst(long e, long d) /* evaluate list of expressions */ | |
{ | |
long x, y; | |
if (e == nil) return nil; | |
x = eval(car[e],d); | |
if (x < 0) return x; /* error? */ | |
y = evalst(cdr[e],d); | |
if (y < 0) return y; /* error? */ | |
return cons(x,y); | |
} | |
long append(long x, long y) /* append two lists */ | |
{ | |
if (x == nil) return y; | |
return cons(car[x],append(cdr[x],y)); | |
} | |
long eq(long x, long y) /* equal predicate */ | |
{ | |
if (x == y) return 1; | |
if (numb[x] && numb[y]) return eq_wrd(pname[x],pname[y]); | |
if (numb[x] || numb[y]) return 0; | |
if (atom[x] || atom[y]) return 0; | |
if (eq(car[x],car[y])) return eq(cdr[x],cdr[y]); | |
return 0; | |
} | |
long length(long x) /* number of elements in list */ | |
{ | |
if (atom[x]) return nil; /* is zero */ | |
return add1(length(cdr[x])); | |
} | |
long compare(long x, long y) /* compare two decimal numbers */ | |
{ | |
long already_decided, digit1, digit2; | |
if (x == nil && y == nil) return '='; | |
if (x == nil && y != nil) return '<'; | |
if (x != nil && y == nil) return '>'; | |
already_decided = compare(cdr[x],cdr[y]); | |
if (already_decided != '=') return already_decided; | |
digit1 = car[x]; | |
digit2 = car[y]; | |
if (digit1 == digit2) return '='; | |
if (digit1 < digit2) return '<'; | |
if (digit1 > digit2) return '>'; | |
} | |
long add1(long x) /* add 1 to decimal number */ | |
{ | |
long digit; | |
if (x == nil) return cons('1',nil); | |
digit = car[x]; | |
if (digit != '9') return cons(digit+1,cdr[x]); | |
return cons('0',add1(cdr[x])); | |
} | |
long sub1(long x) /* subtract 1 from decimal number */ | |
{ | |
long digit; | |
if (x == nil) return x; /* 0 - 1 = 0 */ | |
digit = car[x]; | |
if (digit == '1' && cdr[x] == nil) return nil; /* 1 - 1 = 0 */ | |
if (digit != '0') return cons(digit-1,cdr[x]); | |
return cons('9',sub1(cdr[x])); | |
} | |
long nmb(long x) /* pick-up decimal number from atom & convert non-number to zero */ | |
{ | |
if (numb[x]) return pname[x]; | |
return nil; | |
} | |
long remove_leading_zeros(long x) /* from reversed list of digits of decimal number */ | |
{ | |
long rest, digit; | |
if (x == nil) return nil; | |
digit = car[x]; | |
rest = remove_leading_zeros(cdr[x]); | |
if (rest == nil && digit == '0') return nil; | |
return cons(digit,rest); | |
} | |
long addition(long x, long y, long carry_in) | |
{ | |
long sum, digit1, digit2, rest1, rest2; | |
if (x == nil && !carry_in) return y; | |
if (y == nil && !carry_in) return x; | |
if (x != nil) {digit1 = car[x]; rest1 = cdr[x];} | |
else {digit1 = '0'; rest1 = nil;} | |
if (y != nil) {digit2 = car[y]; rest2 = cdr[y];} | |
else {digit2 = '0'; rest2 = nil;} | |
sum = digit1 + digit2 + carry_in - '0'; | |
if (sum <= '9') return cons(sum,addition(rest1,rest2,0)); | |
return cons(sum-10,addition(rest1,rest2,1)); | |
} | |
long subtraction(long x, long y, long borrow_in) /* x - y assumes x >= y */ | |
{ | |
long difference, digit1, digit2, rest1, rest2; | |
if (y == nil && !borrow_in) return x; | |
if (x != nil) {digit1 = car[x]; rest1 = cdr[x];} | |
else {digit1 = '0'; rest1 = nil;} | |
if (y != nil) {digit2 = car[y]; rest2 = cdr[y];} | |
else {digit2 = '0'; rest2 = nil;} | |
difference = digit1 - digit2 - borrow_in + '0'; | |
if (difference >= '0') return cons(difference,subtraction(rest1,rest2,0)); | |
return cons(difference+10,subtraction(rest1,rest2,1)); | |
} | |
long multiplication(long x, long y) /* goes faster if x is small */ | |
{ | |
long sum = nil; | |
if (y == nil) return nil; /* otherwise produces result 0000 */ | |
while (x != nil) { | |
long digit = car[x]; | |
while (digit-- > '0') sum = addition(sum,y,0); | |
x = cdr[x]; | |
y = cons('0',y); /* these are where bad decimal numbers are generated if y is zero */ | |
} | |
return sum; | |
} | |
long exponentiation(long base, long exponent) | |
{ | |
long product = cons('1',nil); | |
while (exponent != nil) { | |
product = multiplication(base,product); /* multiply faster if smaller comes first */ | |
exponent = sub1(exponent); | |
} | |
return product; | |
} | |
long base2_to_10(long x) /* convert bit string to decimal number */ | |
{ | |
long result = nil; | |
while (!atom[x]) { | |
long next_bit = car[x]; | |
x = cdr[x]; | |
if (!numb[next_bit] || pname[next_bit] != nil) next_bit = 1; else next_bit = 0; | |
result = addition(result,result,next_bit); | |
} | |
return result; | |
} | |
long halve(long x) /* used to convert decimal number to bit string */ | |
{ | |
long digit, next_digit, rest, halve_digit; | |
if (x == nil) return x; /* half of 0 is 0 */ | |
digit = car[x] - '0'; | |
x = cdr[x]; | |
rest = halve(x); | |
if (x == nil) next_digit = 0; else next_digit = car[x] - '0'; | |
next_digit = next_digit%2; /* remainder when divided by 2 */ | |
halve_digit = '0' + (digit/2) + (5*next_digit); | |
if (halve_digit != '0' || rest != nil) return cons(halve_digit,rest); | |
return nil; | |
} | |
long base10_to_2(long x) /* convert decimal number to bit string */ | |
{ | |
long bits = nil; | |
while (x != nil) { | |
long digit = car[x] - '0'; | |
bits = cons((digit%2 ? wrd_one : wrd_zero),bits); | |
x = halve(x); | |
} | |
return bits; | |
} | |
long size(long x) /* number of characters in print representation */ | |
{ | |
long sum = nil; | |
if (numb[x] && pname[x] == nil) return add1(nil); /* number zero */ | |
if (atom[x]) return length(pname[x]); | |
while (!atom[x]) { | |
sum = addition(sum,size(car[x]),0); | |
x = cdr[x]; | |
if (!atom[x]) sum = add1(sum); /* blank separator */ | |
} | |
return add1(add1(sum)); /* open & close paren */ | |
} | |
/* read one square of Turing machine tape */ | |
long read_bit(void) | |
{ | |
long x, tape = car[turing_machine_tapes]; | |
if (atom[tape]) return - wrd_out_of_data; /* tape finished ! */ | |
x = car[tape]; | |
car[turing_machine_tapes] = cdr[tape]; | |
if (!numb[x] || pname[x] != nil) return wrd_one; | |
return wrd_zero; | |
} | |
void write_chr(long x) /* convert character to list of 8 bits */ | |
{ | |
q = cdr[q] = cons(( x & 128 ? wrd_one : wrd_zero ), nil); | |
q = cdr[q] = cons(( x & 64 ? wrd_one : wrd_zero ), nil); | |
q = cdr[q] = cons(( x & 32 ? wrd_one : wrd_zero ), nil); | |
q = cdr[q] = cons(( x & 16 ? wrd_one : wrd_zero ), nil); | |
q = cdr[q] = cons(( x & 8 ? wrd_one : wrd_zero ), nil); | |
q = cdr[q] = cons(( x & 4 ? wrd_one : wrd_zero ), nil); | |
q = cdr[q] = cons(( x & 2 ? wrd_one : wrd_zero ), nil); | |
q = cdr[q] = cons(( x & 1 ? wrd_one : wrd_zero ), nil); | |
} | |
void write_lst(long x) /* convert s-exp to list of bits */ | |
{ | |
if (numb[x] && pname[x] == nil) {write_chr('0'); return;} /* null list of digits means zero */ | |
if (atom[x]) {write_atm(pname[x]); return;} | |
write_chr('('); | |
while (!atom[x]) { | |
write_lst(car[x]); | |
x = cdr[x]; | |
if (!atom[x]) write_chr(' '); | |
} | |
write_chr(')'); | |
} | |
void write_atm(long x) /* convert atom to 8 bits per character */ | |
{ | |
if (x == nil) return; | |
write_atm(cdr[x]); /* output characters in reverse order */ | |
write_chr(car[x]); | |
} | |
/* read one character from Turing machine tape */ | |
long read_char(void) | |
{ | |
long c, b, i = 8; | |
c = 0; | |
while (i-- > 0) { | |
b = read_bit(); | |
if (b < 0) return b; /* error? */ | |
if (pname[b] != nil) b = 1; else b = 0; | |
c = c + c + b; | |
} | |
return c; | |
} | |
long read_record(void) /* read record from Turing machine tape */ | |
{ /* fill buffer2 with all the words in an input record */ | |
long character, word, line, end_of_line, end_of_buffer; | |
line = end_of_line = cons(nil,nil); /* stub */ | |
do { /* read characters until '\n' */ | |
character = read_char(); | |
if (character < 0) return character; /* error? */; | |
/* add character to end of line */ | |
end_of_line = cdr[end_of_line] = cons(character,nil); | |
} /* end of read characters until '\n' */ | |
while (character != '\n'); | |
line = cdr[line]; /* remove stub at beginning of line */ | |
/* break line into words at ( ) characters */ | |
buffer2 = end_of_buffer = cons(nil,nil); /* stub */ | |
word = nil; | |
while ( line != nil ) { | |
character = car[line]; | |
line = cdr[line]; | |
/* look for characters that break words */ | |
if ( character == ' ' || character == '\n' || | |
character == '(' || character == ')' ) | |
{ /* add nonempty word to end of buffer */ | |
if ( word != nil ) | |
end_of_buffer = cdr[end_of_buffer] = cons(word,nil); | |
word = nil; | |
/* add break character to end of buffer */ | |
if ( character != ' ' && character != '\n' ) | |
end_of_buffer = cdr[end_of_buffer] = cons(cons(character,nil),nil); | |
} | |
else | |
{ /* add character to word (in reverse order) */ | |
/* keep only nonblank printable ASCII codes */ | |
if (32 < character && character < 127) | |
word = cons(character,word); | |
} | |
} /* end while ( line != nil ) */ | |
buffer2 = cdr[buffer2]; /* remove stub at beginning of buffer */ | |
return 0; /* indicates no error */ | |
} | |
long read_word(void) { /* read word from Turing machine tape */ | |
/* buffer2 has all the words in the input record */ | |
long word; | |
/* (if buffer empty, returns as many right parens as needed) */ | |
if (buffer2 == nil) return right_paren; | |
/* if buffer nonempty, return first word in buffer */ | |
word = car[buffer2]; | |
buffer2 = cdr[buffer2]; | |
/* first check if word consists only of digits */ | |
if (only_digits(word)) word = mk_numb(remove_leading_zeros(word)); | |
/* also makes 00099 into 99 and 0000 into null */ | |
else word = lookup_word(word); /* look up word in object list */ | |
/* also does mk_atom and adds it to object list if necessary */ | |
return word; | |
} | |
long read_expr(long rparenokay) /* read s-exp from Turing machine tape */ | |
{ | |
long w = read_word(), first, last, next; | |
if (w < 0) return w; /* error? */ | |
if (w == right_paren) if (rparenokay) return w; else return nil; | |
if (w == left_paren) { /* explicit list */ | |
first = last = cons(nil,nil); | |
while ((next = read_expr(1)) != right_paren) { | |
if (next < 0) return next; /* error? */ | |
last = cdr[last] = cons(next,nil); | |
} | |
return cdr[first]; | |
} /* end if (w == left_paren) */ | |
return w; /* normal atom */ | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment