about summary refs log tree commit diff stats
path: root/eval.lua
diff options
context:
space:
mode:
authorCase Duckworth2024-04-09 21:04:17 -0500
committerCase Duckworth2024-04-09 21:04:29 -0500
commit8ce2915e3c54598c2fda4fec0980ebfc2a3adf6e (patch)
tree124ef31663ed570bed358dffd9c861d10fabce7b /eval.lua
parentUh (diff)
downloadlam-8ce2915e3c54598c2fda4fec0980ebfc2a3adf6e.tar.gz
lam-8ce2915e3c54598c2fda4fec0980ebfc2a3adf6e.zip
Reorganization
Diffstat (limited to 'eval.lua')
-rw-r--r--eval.lua171
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
3local m = {} 3local m = {}
4local type = require "type" 4local type = require("type")
5local assert_arity = require("util").assert_arity 5local assert_arity = type.assert_arity
6local util = require("util")
7local error = util.error
6 8
7function m.environ (inner, outer) 9m.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)
21end
22
23local 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)
43end
44
45function 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)
56end
57
58local 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)
87end
88
89m.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
132m.specials.lam = m.specials.lambda
133m.specials.def = m.specials.define
134 81
135function m.eval (x, env) -- TODO: specify ENV on all calls 82function 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