about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--Makefile19
-rw-r--r--README.md6
-rw-r--r--base.lua37
-rw-r--r--eval.lua39
-rw-r--r--read.lua2
-rw-r--r--repl.lua21
-rw-r--r--test.lua43
-rw-r--r--type.lua1
8 files changed, 109 insertions, 59 deletions
diff --git a/Makefile b/Makefile index 54c4f2c..f08ae06 100644 --- a/Makefile +++ b/Makefile
@@ -1,4 +1,12 @@
1LUA ?= rlwrap luajit 1LUA ?= rlwrap luajit \
2 -e 'pp = require "pp"' \
3 -e 'eval = require "eval"' \
4 -e 'read = require "read"' \
5 -e 'type = require "type"' \
6 -e 'utf8 = require "utf8"' \
7 -e 'util = require "util"' \
8 -e 'test = require "test"'
9
2 10
3.PHONY: repl 11.PHONY: repl
4repl: 12repl:
@@ -6,14 +14,7 @@ repl:
6 14
7.PHONY: test 15.PHONY: test
8test: 16test:
9 $(LUA) -i \ 17 $(LUA) -e 'test.runtests()'
10 -e 'pp = require "pp"' \
11 -e 'eval = require "eval"' \
12 -e 'read = require "read"' \
13 -e 'type = require "type"' \
14 -e 'utf8 = require "utf8"' \
15 -e 'util = require "util"' \
16 -e 'test = require "test"'
17 18
18.PHONY: check 19.PHONY: check
19check: 20check:
diff --git a/README.md b/README.md index 3728ba6..1dcd392 100644 --- a/README.md +++ b/README.md
@@ -36,6 +36,12 @@ see COPYING for details.
36 36
37[email me]: mailto:acdw@acdw.net 37[email me]: mailto:acdw@acdw.net
38 38
39## references
40
41- [Lua 5.1 reference](https://www.lua.org/manual/5.1/manual.html)
42- [Luajit](https://luajit.org/)
43- [R7RS](https://standards.scheme.org/corrected-r7rs/r7rs.html)
44
39## thanks 45## thanks
40 46
41thanks to peter norvig's [series on lisp interpreters][norvig] 47thanks to peter norvig's [series on lisp interpreters][norvig]
diff --git a/base.lua b/base.lua new file mode 100644 index 0000000..9c5b5b7 --- /dev/null +++ b/base.lua
@@ -0,0 +1,37 @@
1--- lam.base --- base environment
2
3local base = {}
4local type = require "type"
5local isNull = type.isNull
6
7base.env = {
8 begin =
9 function (r)
10 local r = r
11 while not isNull(r.cdr) do
12 r = r.cdr
13 end
14 return r.car
15 end,
16 ["+"] =
17 function (r)
18 local r, a = r, 0
19 while r.cdr do
20 r, a = r.cdr, a + r.car
21 end
22 return a
23 end,
24 ["-"] =
25 function (r)
26 if isNull(r) then return -1 end
27 if isNull(r.cdr) then return (- r.car) end
28 local r, a = r.cdr, r.car
29 while r.cdr do
30 r, a = r.cdr, a - r.car
31 end
32 return a
33 end,
34}
35
36---
37return base
diff --git a/eval.lua b/eval.lua index 5e897e2..e926bc4 100644 --- a/eval.lua +++ b/eval.lua
@@ -1,6 +1,7 @@
1--- lam.eval 1--- lam.eval
2 2
3local eval = {} 3local eval = {}
4local base = require "base"
4local type = require "type" 5local type = require "type"
5local isNull, isList, isa, List, Cons = 6local isNull, isList, isa, List, Cons =
6 type.isNull, type.isList, type.isa, type.List, type.Cons 7 type.isNull, type.isList, type.isa, type.List, type.Cons
@@ -30,6 +31,7 @@ function eval.Proc (params, body, env)
30 inner[p.car] = a.car 31 inner[p.car] = a.car
31 p, a = p.cdr, a.cdr 32 p, a = p.cdr, a.cdr
32 end 33 end
34 pp.pp(self.body)
33 return eval.eval( 35 return eval.eval(
34 self.body, 36 self.body,
35 eval.Env(inner, self.env)) 37 eval.Env(inner, self.env))
@@ -38,37 +40,8 @@ function eval.Proc (params, body, env)
38 return setmetatable(v, mt) 40 return setmetatable(v, mt)
39end 41end
40 42
41local global = {
42 begin =
43 function (r)
44 local r = r
45 while not isNull(r.cdr) do
46 r = r.cdr
47 end
48 return r.car
49 end,
50 ["+"] =
51 function (r)
52 local r, a = r, 0
53 while r.cdr do
54 r, a = r.cdr, a + r.car
55 end
56 return a
57 end,
58 ["-"] =
59 function (r)
60 if isNull(r) then return -1 end
61 if isNull(r.cdr) then return (- r.car) end
62 local r, a = r.cdr, r.car
63 while r.cdr do
64 r, a = r.cdr, a - r.car
65 end
66 return a
67 end,
68}
69
70function eval.eval (x, env) 43function eval.eval (x, env)
71 env = env or global 44 env = env or base.env
72 if isa(x, "Symbol") then 45 if isa(x, "Symbol") then
73 return env[x] 46 return env[x]
74 elseif not isList(x) then 47 elseif not isList(x) then
@@ -77,13 +50,13 @@ function eval.eval (x, env)
77 local op, args = x.car, x.cdr 50 local op, args = x.car, x.cdr
78 if op == "quote" then 51 if op == "quote" then
79 return args.car 52 return args.car
80 elseif op == "define" then 53 elseif op == "define" or op == "def" then
81 env[args.car] = eval.eval(args.cdr.car, env) 54 env[args.car] = eval.eval(args.cdr.car, env)
82 return nil 55 return nil
83 elseif op == "lambda" then 56 elseif op == "lambda" or op == "lam" then
84 return eval.Proc( 57 return eval.Proc(
85 args.car, 58 args.car,
86 Cons("begin", args.cdr), 59 Cons("begin", args.cdr), -- i don't like this
87 env) 60 env)
88 elseif op == "if" then 61 elseif op == "if" then
89 assert(not isNull(args.cdr), "Malformed 'if'") 62 assert(not isNull(args.cdr), "Malformed 'if'")
diff --git a/read.lua b/read.lua index c5dacc0..d21e4cb 100644 --- a/read.lua +++ b/read.lua
@@ -7,7 +7,7 @@ local util = require "util"
7local pop = util.pop 7local pop = util.pop
8local unpack = table.unpack or unpack 8local unpack = table.unpack or unpack
9 9
10function program_characters (program) 10local function program_characters (program)
11 local chars = {} 11 local chars = {}
12 for pos, code in utf8.codes(program) do 12 for pos, code in utf8.codes(program) do
13 table.insert(chars, code) 13 table.insert(chars, code)
diff --git a/repl.lua b/repl.lua new file mode 100644 index 0000000..34e4c94 --- /dev/null +++ b/repl.lua
@@ -0,0 +1,21 @@
1--- lam.repl
2
3local repl = {}
4local eval = require("eval").eval
5local read = require("read").read
6
7function repl.repl (prompt)
8 if not prompt then prompt = "lam> " end
9 io.input():setvbuf("line")
10 repeat
11 io.write(prompt)
12 io.output():flush()
13 local input = io.read()
14 if input == nil then break end
15 local value = eval(read(input))
16 if value then print(value) end
17 until false
18end
19
20---
21return repl
diff --git a/test.lua b/test.lua index ce8c034..1d90df2 100644 --- a/test.lua +++ b/test.lua
@@ -1,28 +1,39 @@
1--- lam.test 1--- lam.test
2-- testing helpers
3 2
4local test = {} 3local test = {}
5local eval = require("eval").eval 4local eval = require("eval").eval
6local read = require("read").read 5local read = require("read").read
6local luatype = require("type").luatype
7 7
8function test.lambda () 8function test.test (form, expected)
9 local ls = { 9 local diag = string.format("%s == %s", form, expected)
10 [ [[((lambda (x) (+ x x)) 3)]] ] = 6, 10 local value = eval(read(form))
11 [ [[((lambda () 100))]] ] = 100, 11 if value == expected then
12 [ [[((lambda (x) 1 2 3) 4)]] ] = 3, 12 print(string.format("ok: %s", diag))
13 [ [[((lambda () 1 2 3))]] ] = 3, 13 else
14 [ [[((lambda (x) x (+ x x) (+ x x x)) 9)]] ] = 27, 14 print(string.format("not ok: %s != %s", diag, value))
15 } 15 end
16 for l, target in pairs(ls) do 16end
17 io.write(string.format("%s == %s\n\t", l, target)) 17
18 local value = eval(read(l)) 18function test.runtests ()
19 if value == target then 19 for name, fn in pairs(test) do
20 print "ok" 20 if luatype(fn) == "function"
21 else 21 and name ~= "test"
22 print(string.format("not ok : %s", value)) 22 and name ~= "runtests"
23 then
24 print(">>>", name)
25 fn()
23 end 26 end
24 end 27 end
25end 28end
26 29
30function test.lambda ()
31 test.test([[((lambda (x) (+ x x)) 3)]], 6)
32 test.test([[((lambda () 100))]], 100)
33 test.test([[((lambda (x) 1 2 3) 4)]], 3)
34 test.test([[((lambda () 1 2 3))]], 3)
35 test.test([[((lambda (x) x (+ x x) (+ x x x)) 9)]], 27)
36end
37
27--- 38---
28return test 39return test
diff --git a/type.lua b/type.lua index 945f4d1..0d3b776 100644 --- a/type.lua +++ b/type.lua
@@ -31,6 +31,7 @@ end
31-- Symbols and Numbers are strings and numbers, respectively. At some point 31-- Symbols and Numbers are strings and numbers, respectively. At some point
32-- I'll want to implement a full numeric tower and symbol tables or namespaces 32-- I'll want to implement a full numeric tower and symbol tables or namespaces
33-- or whatever, but today is not that day 33-- or whatever, but today is not that day
34
34t.Symbol = tostring 35t.Symbol = tostring
35t.Number = tonumber 36t.Number = tonumber
36 37