diff options
author | Case Duckworth | 2024-04-09 21:04:17 -0500 |
---|---|---|
committer | Case Duckworth | 2024-04-09 21:04:29 -0500 |
commit | 8ce2915e3c54598c2fda4fec0980ebfc2a3adf6e (patch) | |
tree | 124ef31663ed570bed358dffd9c861d10fabce7b /eval.lua | |
parent | Uh (diff) | |
download | lam-8ce2915e3c54598c2fda4fec0980ebfc2a3adf6e.tar.gz lam-8ce2915e3c54598c2fda4fec0980ebfc2a3adf6e.zip |
Reorganization
Diffstat (limited to 'eval.lua')
-rw-r--r-- | eval.lua | 171 |
1 files changed, 59 insertions, 112 deletions
diff --git a/eval.lua b/eval.lua index 867a704..4b8f782 100644 --- a/eval.lua +++ b/eval.lua | |||
@@ -1,150 +1,97 @@ | |||
1 | --- lam.eval | 1 | --- lam.eval |
2 | 2 | ||
3 | local m = {} | 3 | local m = {} |
4 | local type = require "type" | 4 | local type = require("type") |
5 | local assert_arity = require("util").assert_arity | 5 | local assert_arity = type.assert_arity |
6 | local util = require("util") | ||
7 | local error = util.error | ||
6 | 8 | ||
7 | function m.environ (inner, outer) | 9 | m.special_forms = { |
8 | local mt = { | 10 | quote = |
9 | __type = "environment", | 11 | function (r, _) |
10 | __index = outer, | 12 | assert_arity(r,1,1) |
11 | __newindex = | 13 | return r[1] |
12 | function (self, key, val) | 14 | end, |
13 | if rawget(self, key) then | 15 | quasiquote = |
14 | rawset(self, key, val) | 16 | function (r, e) |
17 | assert_arity(r,1,1) | ||
18 | local x = r[1] | ||
19 | if not type.listp(x) or type.nullp(x) then | ||
20 | return x | ||
21 | end | ||
22 | local QQ, fin = {}, nil | ||
23 | while x[2] do | ||
24 | if type.listp(x[1]) then | ||
25 | if x[1][1] == "unquote" then | ||
26 | table.insert(QQ, | ||
27 | m.eval(x[1][2][1], e)) | ||
28 | elseif x[1][1] == "unquote-splicing" | ||
29 | then | ||
30 | local y = m.eval(x[1][2][1], e) | ||
31 | if not type.listp(y) then | ||
32 | fin = y | ||
33 | break | ||
34 | end | ||
35 | while y[2] do | ||
36 | table.insert(QQ, y[1]) | ||
37 | y = y[2] | ||
38 | end | ||
39 | end | ||
15 | else | 40 | else |
16 | getmetatable(self).__index[key] = val | 41 | table.insert(QQ, x[1]) |
17 | end | ||
18 | end, | ||
19 | } | ||
20 | return setmetatable(inner, mt) | ||
21 | end | ||
22 | |||
23 | local function procedure_call (proc, r) | ||
24 | local function doargs (p, r, e) | ||
25 | if p == type.null and r == type.null then return e end | ||
26 | if type.isa(p, "symbol") then | ||
27 | e[p] = r | ||
28 | return e | ||
29 | end | ||
30 | if p[1] == nil then error("Too many arguments") end | ||
31 | if r[1] == nil then error("Too few arguments") end | ||
32 | e[p[1]] = r[1] | ||
33 | doargs(p[2], r[2], e) | ||
34 | end | ||
35 | |||
36 | local e = doargs(proc.params, r, m.environ({}, proc.env)) | ||
37 | local b = proc.body | ||
38 | while b[2] ~= type.null do | ||
39 | m.eval(b[1], e) | ||
40 | b = b[2] | ||
41 | end | ||
42 | return m.eval(b[1], e) | ||
43 | end | ||
44 | |||
45 | function m.procedure (params, body, env) | ||
46 | local t = { | ||
47 | params = params, | ||
48 | body = body, | ||
49 | env = env, | ||
50 | } | ||
51 | local mt = { | ||
52 | __type = "procedure", | ||
53 | __call = procedure_call, | ||
54 | } | ||
55 | return setmetatable(t, mt) | ||
56 | end | ||
57 | |||
58 | local function handle_quasiquote (r, e) | ||
59 | assert_arity(r, 1, 1) | ||
60 | local x = r[1] | ||
61 | if not type.islist(x) or x == type.null then | ||
62 | return x | ||
63 | end | ||
64 | local QQ, fin = {}, nil | ||
65 | local car, cdr = x[1], x[2] | ||
66 | while cdr do | ||
67 | if type.islist(car) then | ||
68 | if car[1] == "unquote" then | ||
69 | table.insert(QQ, m.eval(car[2][1], e)) | ||
70 | elseif car[1] == "unquote-splicing" then | ||
71 | local usl = m.eval(car[2][1], e) | ||
72 | if not type.islist(usl) then | ||
73 | fin = usl | ||
74 | break | ||
75 | end | ||
76 | while usl[2] do | ||
77 | table.insert(QQ, usl[1]) | ||
78 | usl = usl[2] | ||
79 | end | 42 | end |
43 | x = x[2] | ||
80 | end | 44 | end |
81 | else | 45 | return type.list(QQ, fin) |
82 | table.insert(QQ, car) | 46 | end, |
83 | end | 47 | unquote = |
84 | car, cdr = cdr[1], cdr[2] | 48 | function (_, _) |
85 | end | 49 | error("unexpected", ",") |
86 | return type.list(QQ, fin) | ||
87 | end | ||
88 | |||
89 | m.specials = { | ||
90 | -- each of these takes R (a list of args) and E (an environment) | ||
91 | quote = | ||
92 | function (r, e) | ||
93 | assert_arity(r, 1, 1) | ||
94 | return r[1] | ||
95 | end, | 50 | end, |
96 | quasiquote = handle_quasiquote, | ||
97 | -- if not inside quasiquote, unquote and unquote-splicing are errors | ||
98 | unquote = function () error("Unexpected unquote") end, | ||
99 | ["unquote-splicing"] = | 51 | ["unquote-splicing"] = |
100 | function () error("Unexpected unquote-splicing") end, | 52 | function (_, _) |
101 | -- define variables | 53 | error("unexpected", ",@") |
54 | end, | ||
102 | define = | 55 | define = |
103 | function (r, e) | 56 | function (r, e) |
104 | assert_arity(r, 2, 2) | 57 | assert_arity(r,2,2) |
105 | rawset(e, r[1], m.eval(r[2][1], e)) | 58 | rawset(e, r[1], m.eval(r[2][1], e)) |
106 | end, | 59 | end, |
107 | ["set!"] = | 60 | ["set!"] = |
108 | function (r, e) | 61 | function (r, e) |
109 | assert_arity(r, 2, 2) | 62 | assert_arity(r,2,2) |
110 | e[r[1]] = m.eval(r[2][1], e) | 63 | e[r[1]] = m.eval(r[2][1], e) |
111 | end, | 64 | end, |
112 | -- y'know, ... lambda | ||
113 | lambda = | 65 | lambda = |
114 | function (r, e) | 66 | function (r, e) |
115 | assert_arity(r, 2) | 67 | assert_arity(r,2) |
116 | return m.procedure(r[1], r[2], e) | 68 | return type.procedure(r[1], r[2], e, m.eval) |
117 | end, | 69 | end, |
118 | -- control flow | ||
119 | ["if"] = | 70 | ["if"] = |
120 | function (r, e) | 71 | function (r, e) |
121 | assert_arity(r, 3, 3) | 72 | assert_arity(r,3,3) |
122 | local test, conseq, alt = | 73 | local test, conseq, alt = r[1], r[2][1], r[2][2][1] |
123 | r[1], r[2][1], r[2][2][1] | ||
124 | if m.eval(test, e) | 74 | if m.eval(test, e) |
125 | then return m.eval(conseq, e) | 75 | then return m.eval(conseq, e) |
126 | else return m.eval(alt, e) | 76 | else return m.eval(alt, e) |
127 | end | 77 | end |
128 | end, | 78 | end, |
129 | -- TODO: include, import, define-syntax, ... | 79 | -- TODO: include, import, define-syntax ... |
130 | } | 80 | } |
131 | -- Aliases | ||
132 | m.specials.lam = m.specials.lambda | ||
133 | m.specials.def = m.specials.define | ||
134 | 81 | ||
135 | function m.eval (x, env) -- TODO: specify ENV on all calls | 82 | function m.eval (x, env) |
136 | if type.isa(x, "symbol") then | 83 | if type.isp(x, "symbol") then |
137 | if env[x] == nil then | 84 | if env[x] == nil then |
138 | error(string.format("Unbound variable: %s", x)) | 85 | error("unbound symbol", x) |
139 | end | 86 | end |
140 | return env[x] | 87 | return env[x] |
141 | elseif not type.islist(x) then | 88 | elseif not type.listp(x) then |
142 | return x | 89 | return x |
143 | else | 90 | else |
144 | local op, args = x[1], x[2] | 91 | local op, args = x[1], x[2] |
145 | if m.specials[op] then | 92 | if m.special_forms[op] then |
146 | return m.specials[op](args, env) | 93 | return m.special_forms[op](args, env) |
147 | else -- procedure call | 94 | else -- procedure application |
148 | local fn = m.eval(op, env) | 95 | local fn = m.eval(op, env) |
149 | local params = {} | 96 | local params = {} |
150 | local r = args | 97 | local r = args |