1 /* A minimal Lisp interpreter
2 Copyright 2004 Andru Luvisi
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License , or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, write to the Free Software
16 Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
26 #define error(X) do { fprintf(stderr, "%s\n", X); exit(1); } while (0)
28 /*** List Structured Memory ***/
29 enum otype
{ INT
, SYM
, CONS
, PROC
, PRIMOP
};
34 typedef obj
* (*primop
)(obj
*);
35 obj
*all_symbols
, *top_env
, *nil
, *tee
, *quote
,
36 *s_if
, *s_lambda
, *s_define
, *s_setb
;
38 #define cons(X, Y) omake(CONS, 2, (X), (Y))
39 #define car(X) ((X)->p[0])
40 #define cdr(X) ((X)->p[1])
41 #define setcar(X,Y) (((X)->p[0]) = (Y))
42 #define setcdr(X,Y) (((X)->p[1]) = (Y))
43 #define mkint(X) omake(INT, 1, (obj *)(X))
44 #define intval(X) ((int)((X)->p[0]))
45 #define mksym(X) omake(SYM, 1, (obj *)(X))
46 #define symname(X) ((char *)((X)->p[0]))
47 #define mkprimop(X) omake(PRIMOP, 1, (obj *)(X))
48 #define primopval(X) ((primop)(X)->p[0])
49 #define mkproc(X,Y,Z) omake(PROC, 3, (X), (Y), (Z))
50 #define procargs(X) ((X)->p[0])
51 #define proccode(X) ((X)->p[1])
52 #define procenv(X) ((X)->p[2])
53 #define isnil(X) ((X) == nil)
55 obj
*omake(enum otype type
, int count
, ...) {
60 ret
= (obj
*) malloc(sizeof(obj
) + (count
- 1)*sizeof(obj
*));
62 for(i
= 0; i
< count
; i
++) ret
->p
[i
] = va_arg(ap
, obj
*);
67 obj
*findsym(char *name
) {
69 for(symlist
= all_symbols
; !isnil(symlist
); symlist
= cdr(symlist
))
70 if(!strcmp(name
, symname(car(symlist
))))
75 obj
*intern(char *name
) {
76 obj
*op
= findsym(name
);
77 if(!isnil(op
)) return car(op
);
79 all_symbols
= cons(op
, all_symbols
);
84 #define extend(ENV, SYM, VAL) (cons(cons((SYM), (VAL)), (ENV)))
86 obj
*multiple_extend(obj
*env
, obj
*syms
, obj
*vals
) {
89 multiple_extend(extend(env
, car(syms
), car(vals
)),
90 cdr(syms
), cdr(vals
));
93 obj
*extend_top(obj
*sym
, obj
*val
) {
94 setcdr(top_env
, cons(cons(sym
, val
), cdr(top_env
)));
98 obj
*assoc(obj
*key
, obj
*alist
) {
99 if(isnil(alist
)) return nil
;
100 if(car(car(alist
)) == key
) return car(alist
);
101 return assoc(key
, cdr(alist
));
104 /*** Input/Output ***/
112 void add_to_buf(char ch
) { if(bufused
< MAXLEN
- 1) buf
[bufused
++] = ch
; }
113 char *buf2str() { buf
[bufused
++] = '\0'; return strdup(buf
); }
114 void setinput(FILE *fp
) { ifp
= fp
; }
115 void putback_token(char *token
) { token_la
= token
; la_valid
= 1; }
121 if(la_valid
) { la_valid
= 0; return token_la
; }
123 if((ch
= getc(ifp
)) == EOF
) exit(0);
124 } while(isspace(ch
));
126 if(strchr("()\'", ch
)) return buf2str();
128 if((ch
= getc(ifp
)) == EOF
) exit(0);
129 if(strchr("()\'", ch
) || isspace(ch
)) {
142 if(!strcmp(token
, "(")) return readlist();
143 if(!strcmp(token
, "\'")) return cons(quote
, cons(readobj(), nil
));
144 if(token
[strspn(token
, "0123456789")] == '\0') return mkint(atoi(token
));
145 return intern(token
);
149 char *token
= gettoken();
151 if(!strcmp(token
, ")")) return nil
;
152 if(!strcmp(token
, ".")) {
154 if(strcmp(gettoken(), ")")) exit(1);
157 putback_token(token
);
158 tmp
= readobj(); /* Must force evaluation order */
159 return cons(tmp
, readlist());
162 void writeobj(FILE *ofp
, obj
*op
) {
164 case INT
: fprintf(ofp
, "%d", intval(op
)); break;
168 writeobj(ofp
, car(op
));
174 if(op
->type
!= CONS
) {
184 if(isnil(op
)) fprintf(ofp
, "()");
185 else fprintf(ofp
, "%s", symname(op
));
187 case PRIMOP
: fprintf(ofp
, "#<PRIMOP>"); break;
188 case PROC
: fprintf(ofp
, "#<PROC>"); break;
193 /*** Evaluator (Eval/Apply) ***/
194 obj
*evlis(obj
*exps
, obj
*env
);
195 obj
*progn(obj
*exps
, obj
*env
);
196 obj
*apply(obj
*proc
, obj
*vals
, obj
*env
);
198 obj
*eval(obj
*exp
, obj
*env
) {
201 if(exp
== nil
) return nil
;
204 case INT
: return exp
;
205 case SYM
: tmp
= assoc(exp
, env
);
206 if(tmp
== nil
) error("Unbound symbol");
208 case CONS
: if(car(exp
) == s_if
) {
209 if(eval(car(cdr(exp
)), env
) != nil
)
210 return eval(car(cdr(cdr(exp
))), env
);
212 return eval(car(cdr(cdr(cdr(exp
)))), env
);
214 if(car(exp
) == s_lambda
)
215 return mkproc(car(cdr(exp
)), cdr(cdr(exp
)), env
);
216 if(car(exp
) == quote
)
217 return car(cdr(exp
));
218 if(car(exp
) == s_define
)
219 return(extend_top(car(cdr(exp
)),
220 eval(car(cdr(cdr(exp
))), env
)));
221 if(car(exp
) == s_setb
) {
222 obj
*pair
= assoc(car(cdr(exp
)), env
);
223 obj
*newval
= eval(car(cdr(cdr(exp
))), env
);
224 setcdr(pair
, newval
);
227 return apply(eval(car(exp
), env
), evlis(cdr(exp
), env
), env
);
228 case PRIMOP
: return exp
;
229 case PROC
: return exp
;
235 obj
*evlis(obj
*exps
, obj
*env
) {
236 if(exps
== nil
) return nil
;
237 return cons(eval(car(exps
), env
),
238 evlis(cdr(exps
), env
));
241 obj
*progn(obj
*exps
, obj
*env
) {
242 if(exps
== nil
) return nil
;
245 return eval(car(exps
), env
);
246 eval(car(exps
), env
);
251 obj
*apply(obj
*proc
, obj
*vals
, obj
*env
) {
252 if(proc
->type
== PRIMOP
)
253 return (*primopval(proc
))(vals
);
254 if(proc
->type
== PROC
) {
255 /* For dynamic scope, use env instead of procenv(proc) */
256 return progn(proccode(proc
),
257 multiple_extend(procenv(proc
), procargs(proc
), vals
));
259 error("Bad argument to apply");
265 obj
*prim_sum(obj
*args
) {
267 for(sum
= 0; !isnil(args
); sum
+= intval(car(args
)), args
= cdr(args
));
271 obj
*prim_sub(obj
*args
) {
273 for(sum
= intval(car(args
)), args
= cdr(args
);
275 sum
-= intval(car(args
)), args
= cdr(args
));
279 obj
*prim_prod(obj
*args
) {
281 for(prod
= 1; !isnil(args
); prod
*= intval(car(args
)), args
= cdr(args
));
285 obj
*prim_numeq(obj
*args
) {
286 return intval(car(args
)) == intval(car(cdr(args
))) ? tee
: nil
;
289 obj
*prim_cons(obj
*args
) { return cons(car(args
), car(cdr(args
))); }
290 obj
*prim_car(obj
*args
) { return car(car(args
)); }
291 obj
*prim_cdr(obj
*args
) { return cdr(car(args
)); }
293 /*** Initialization ***/
296 all_symbols
= cons(nil
, nil
);
297 top_env
= cons(cons(nil
, nil
), nil
);
299 extend_top(tee
, tee
);
300 quote
= intern("quote");
302 s_lambda
= intern("lambda");
303 s_define
= intern("define");
304 s_setb
= intern("set!");
305 extend_top(intern("+"), mkprimop(prim_sum
));
306 extend_top(intern("-"), mkprimop(prim_sub
));
307 extend_top(intern("*"), mkprimop(prim_prod
));
308 extend_top(intern("="), mkprimop(prim_numeq
));
309 extend_top(intern("cons"), mkprimop(prim_cons
));
310 extend_top(intern("car"), mkprimop(prim_car
));
311 extend_top(intern("cdr"), mkprimop(prim_cdr
));
314 /*** Main Driver ***/
319 writeobj(stdout
, eval(readobj(), top_env
));