Skip to content

Instantly share code, notes, and snippets.

@kindy
Created August 28, 2013 03:59
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kindy/6362022 to your computer and use it in GitHub Desktop.
Save kindy/6362022 to your computer and use it in GitHub Desktop.
/* lisp.c: high-speed LISP interpreter */
/* src: http://www.cs.auckland.ac.nz/~chaitin/lisp.c */
/*
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 */
}
################ Lispy: Scheme Interpreter in Python
## src: http://norvig.com/lispy.html
## ref: http://howtowriteaprogram.blogspot.com/2010/11/lisp-interpreter-in-90-lines-of-c.html
## ref: http://gist.github.com/240957
## ref: http://probablyprogramming.com/2009/11/23/a-simple-lisp-parser-in-python
## (c) Peter Norvig, 2010; See http://norvig.com/lispy.html
################ Symbol, Env classes
from __future__ import division
Symbol = str
class Env(dict):
"An environment: a dict of {'var':val} pairs, with an outer Env."
def __init__(self, parms=(), args=(), outer=None):
self.update(zip(parms,args))
self.outer = outer
def find(self, var):
"Find the innermost Env where var appears."
return self if var in self else self.outer.find(var)
def add_globals(env):
"Add some Scheme standard procedures to an environment."
import math, operator as op
env.update(vars(math)) # sin, sqrt, ...
env.update(
{'+':op.add, '-':op.sub, '*':op.mul, '/':op.div, 'not':op.not_,
'>':op.gt, '<':op.lt, '>=':op.ge, '<=':op.le, '=':op.eq,
'equal?':op.eq, 'eq?':op.is_, 'length':len, 'cons':lambda x,y:[x]+y,
'car':lambda x:x[0],'cdr':lambda x:x[1:], 'append':op.add,
'list':lambda *x:list(x), 'list?': lambda x:isa(x,list),
'null?':lambda x:x==[], 'symbol?':lambda x: isa(x, Symbol)})
return env
global_env = add_globals(Env())
isa = isinstance
################ eval
def eval(x, env=global_env):
"Evaluate an expression in an environment."
if isa(x, Symbol): # variable reference
return env.find(x)[x]
elif not isa(x, list): # constant literal
return x
elif x[0] == 'quote': # (quote exp)
(_, exp) = x
return exp
elif x[0] == 'if': # (if test conseq alt)
(_, test, conseq, alt) = x
return eval((conseq if eval(test, env) else alt), env)
elif x[0] == 'set!': # (set! var exp)
(_, var, exp) = x
env.find(var)[var] = eval(exp, env)
elif x[0] == 'define': # (define var exp)
(_, var, exp) = x
env[var] = eval(exp, env)
elif x[0] == 'lambda': # (lambda (var*) exp)
(_, vars, exp) = x
return lambda *args: eval(exp, Env(vars, args, env))
elif x[0] == 'begin': # (begin exp*)
for exp in x[1:]:
val = eval(exp, env)
return val
else: # (proc exp*)
exps = [eval(exp, env) for exp in x]
proc = exps.pop(0)
return proc(*exps)
################ parse, read, and user interaction
def read(s):
"Read a Scheme expression from a string."
return read_from(tokenize(s))
parse = read
def tokenize(s):
"Convert a string into a list of tokens."
return s.replace('(',' ( ').replace(')',' ) ').split()
def read_from(tokens):
"Read an expression from a sequence of tokens."
if len(tokens) == 0:
raise SyntaxError('unexpected EOF while reading')
token = tokens.pop(0)
if '(' == token:
L = []
while tokens[0] != ')':
L.append(read_from(tokens))
tokens.pop(0) # pop off ')'
return L
elif ')' == token:
raise SyntaxError('unexpected )')
else:
return atom(token)
def atom(token):
"Numbers become numbers; every other token is a symbol."
try: return int(token)
except ValueError:
try: return float(token)
except ValueError:
return Symbol(token)
def to_string(exp):
"Convert a Python object back into a Lisp-readable string."
return '('+' '.join(map(to_string, exp))+')' if isa(exp, list) else str(exp)
def repl(prompt='lis.py> '):
"A prompt-read-eval-print loop."
while True:
val = eval(parse(raw_input(prompt)))
if val is not None: print to_string(val)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment