-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathlisp-macro.js
61 lines (52 loc) · 1.7 KB
/
lisp-macro.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// Perform one step of macroexpansion for given source term
// (source code).
// hasHead - if false, do not append to invoke a macro in the
// head of the term
// quasiLevel - if >0, we're inside a quasiquote
lisp.macroExpandOne = function(term, hasHead, quasiLevel) {
if (term.type != 'cons')
return null;
// first, try expanding the head
var car = lisp.macroExpandOne(term.car, true, quasiLevel);
if (car != null)
return new lisp.Cons(car, term.cdr);
// now, maybe the term itself is a macro invocation
if (hasHead &&
quasiLevel == 0 &&
term.car.type == 'symbol')
{
var func = lisp.env.get('macro:' + term.car.s);
if (func != null) {
var args = lisp.termToList(term.cdr);
lisp.stackTrace.push('macroexpand: ' + term.print());
var result = func.run(args);
lisp.stackTrace.pop();
return result;
}
}
if (quasiLevel == 0 &&
term.car.type == 'symbol' &&
term.car.s == 'quote')
// we don't expand quoted terms
return null;
if (hasHead && term.car.type == 'symbol') {
if (term.car.s == 'quasiquote')
quasiLevel++;
if (term.car.s == 'unquote')
quasiLevel--;
}
// lastly, try expanding the rest of list
var cdr = lisp.macroExpandOne(term.cdr, false, quasiLevel);
if (cdr != null)
return new lisp.Cons(term.car, cdr);
return null;
};
lisp.macroExpand = function(term) {
var newTerm;
while ((newTerm = lisp.macroExpandOne(term, true, 0)) != null)
term = newTerm;
return term;
};
lisp.addMacro = function(name, func) {
lisp.env.vars['macro:' + name] = func;
};