about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--eval.lua18
-rw-r--r--eval2.lua39
-rw-r--r--global.lua91
-rw-r--r--list.lua48
-rw-r--r--port.lua20
-rw-r--r--pp.lua22
-rw-r--r--read.lua93
-rw-r--r--repl.lua1
-rw-r--r--types.lua122
-rw-r--r--util.lua1
10 files changed, 350 insertions, 105 deletions
diff --git a/eval.lua b/eval.lua index 185268f..806148d 100644 --- a/eval.lua +++ b/eval.lua
@@ -5,12 +5,14 @@ local read = require "read"
5local util = require "util" 5local util = require "util"
6local pp = require "pp" 6local pp = require "pp"
7local global = require "global" 7local global = require "global"
8local types = require("types") 8local types = require "types"
9table.unpack = table.unpack or unpack
9 10
10if not table.unpack then table.unpack = unpack end 11--- Environments and Parameters
12-- these aren't in types.lua to avoid a circular dependency
11 13
12local function Env(inner, outer) 14local function Env(inner, outer)
13 return setmetatable(inner, { __type = "environment", __index = outer, }) 15 return setmetatable(inner, { __type = "Environment", __index = outer, })
14end 16end
15 17
16local function Proc(params, body, env) 18local function Proc(params, body, env)
@@ -20,7 +22,7 @@ local function Proc(params, body, env)
20 env = env, 22 env = env,
21 } 23 }
22 local mt = { 24 local mt = {
23 __type = "procedure", 25 __type = "Procedure",
24 __call = 26 __call =
25 function (self, ...) 27 function (self, ...)
26 local inner = {} 28 local inner = {}
@@ -37,23 +39,23 @@ end
37 39
38function eval.eval (x, e) 40function eval.eval (x, e)
39 e = e or global 41 e = e or global
40 if types.lamtype(x) == "symbol" then 42 if types.lamtype(x) == "Symbol" then
41 return e[x] 43 return e[x]
42 elseif types.luatype(x) ~= "table" then 44 elseif types.luatype(x) ~= "table" then
43 return x 45 return x
44 else 46 else
45 local op = util.car(x) 47 local op = util.car(x)
46 local args = util.cdr(x) 48 local args = util.cdr(x)
47 if op == "quote" then 49 if op == types.Symbol("quote") then
48 return args[1] 50 return args[1]
49 elseif op == "define" then 51 elseif op == types.Symbol("define") then
50 local sym, exp = table.unpack(args) 52 local sym, exp = table.unpack(args)
51 e[sym] = eval(exp, e) 53 e[sym] = eval(exp, e)
52 --[[ 54 --[[
53 elseif op == "set!" then 55 elseif op == "set!" then
54 local sym, exp = table.unpack(args) 56 local sym, exp = table.unpack(args)
55 e[sym] = eval(exp, e) --]] 57 e[sym] = eval(exp, e) --]]
56 elseif op == "lambda" then 58 elseif op == types.Symbol("lambda") then
57 local params = util.car(args) 59 local params = util.car(args)
58 local body = util.cdr(args) 60 local body = util.cdr(args)
59 table.insert(body, 1, "begin") 61 table.insert(body, 1, "begin")
diff --git a/eval2.lua b/eval2.lua new file mode 100644 index 0000000..02444b8 --- /dev/null +++ b/eval2.lua
@@ -0,0 +1,39 @@
1--- lam.eval
2
3local eval = {}
4local read = require "read"
5local util = require "util"
6local types = require "types"
7table.unpack = table.unpack or unpack
8
9local Environment =
10 function (inner, outer)
11 -- an Environment is really just a lua table between symbols and
12 -- values. They can be nested for uh, closure reasons or
13 -- something. TODO: figure out how this intersects with
14 -- Namespaces or Symboltables or whatever.
15 local mt = {
16 __type = "Environment",
17 __index = outer,
18 }
19 return setmetatable(inner, mt)
20 end
21
22local Procedure =
23 function (params, body, env)
24 local proc = {
25 params = params,
26 body = body,
27 env = env,
28 }
29 local mt = {
30 __type = "Procedure",
31 __call =
32 function (self, ...)
33 end,
34 }
35 return setmetatable(proc, mt)
36 end
37
38---
39return eval
diff --git a/global.lua b/global.lua index 3805912..1dea773 100644 --- a/global.lua +++ b/global.lua
@@ -1,40 +1,47 @@
1--- lam.environment 1--- lam.environment
2 2
3local util = require "util" 3local util = require "util"
4local types = require("types") 4local types = require "types"
5 5table.unpack = table.unpack or unpack
6if not table.unpack then table.unpack = unpack end
7 6
8local global = { 7local global = {
9 -- constants 8 -- constants ---- TODO this should be at the reader level
10 ["#t"] = true, 9 ["#t"] = true,
11 ["#f"] = false, 10 ["#f"] = false,
12} 11}
13 12
14--- Types --- 13--- Types ---
15 14
16for name, func in pairs(types) do 15global.luatype = type
17 if name == "lamtype" then 16global.type = types.lamtype
18 global.type = func 17
19 else 18global["number?"] = function (x) types.isa(x, "Number") end
20 global[name] = func 19global["string?"] = function (x) types.isa(x, "String") end
21 end 20global["symbol?"] = function (x) types.isa(x, "Symbol") end
22end 21global["pair?"] = function (x) types.isa(x, "Pair") end
22global["is-a?"] = function (x, t) types.isa(x, t) end
23 23
24--- Basic functions --- 24--- Basic functions ---
25 25
26global.begin = function(...) 26global.car = function (pair) return pair[1] end
27 local xs = {...} 27global.cdr = function (pair) return pair[2] end
28 return xs[#xs]
29end
30 28
31global.car = util.car 29-- global.list = types.List
32global.cdr = util.cdr
33 30
34global.list = function(...) return {...} end 31global["list?"] =
32 function (x)
33 -- TODO : detect circular lists
34 if type(x) == "table" then
35 if #x == 0 then return true end
36 if type(x[2]) ~= "table" then return false end
37 end
38 return global["list?"](x[2])
39 end
35 40
36--- Higher-order functions --- 41global["null?"] = function (x) return type(x) == "table" and #x == 0 end
37 42
43--- Higher-order functions ---
44--[[
38global.apply = function(fn, ...) 45global.apply = function(fn, ...)
39 local args = {...} 46 local args = {...}
40 local last = args[#args] 47 local last = args[#args]
@@ -49,7 +56,7 @@ end
49global.map = function(fn, list) 56global.map = function(fn, list)
50 return util.map(fn, list) 57 return util.map(fn, list)
51end 58end
52 59--]]
53--- Math --- 60--- Math ---
54-- NOTE: we do not have the full numeric tower yet! 61-- NOTE: we do not have the full numeric tower yet!
55 62
@@ -57,7 +64,15 @@ for name, func in pairs(math) do
57 global[name] = func 64 global[name] = func
58end 65end
59 66
60global["+"] = function (...) 67global.fold =
68 function (fn, lis)
69 local out = {}
70
71 return types.List(out)
72 end
73
74global["+"] = function (lis)
75 return
61 return util.reduce({...}, 0, function (a, b) return a + b end) 76 return util.reduce({...}, 0, function (a, b) return a + b end)
62end 77end
63 78
@@ -102,40 +117,46 @@ global["/"] = function (...)
102 end 117 end
103end 118end
104 119
105global["="] = function (...) 120--[[
106 for _, v in ipairs({...}) do 121global["="] =
107 if not a == b then return false end 122 function (...)
123 for _, v in ipairs({...}) do
124 if not a == b then return false end
125 end
126 return true
108 end 127 end
109 return true
110end
111 128
112global["<"] = function (...) 129global["<"] =
113 for _, v in ipairs({...}) do 130 function (...)
114 if not a < b then return false end 131 for _, v in ipairs({...}) do
132 if not a < b then return false end
133 end
134 return true
115 end 135 end
116 return true
117end
118 136
119global["<="] = function (...) 137global["<="] =
138 function (...)
120 for _, v in ipairs({...}) do 139 for _, v in ipairs({...}) do
121 if not a <= b then return false end 140 if not a <= b then return false end
122 end 141 end
123 return true 142 return true
124end 143end
125 144
126global[">"] = function (...) 145global[">"] =
146 function (...)
127 for _, v in ipairs({...}) do 147 for _, v in ipairs({...}) do
128 if not a > b then return false end 148 if not a > b then return false end
129 end 149 end
130 return true 150 return true
131end 151end
132 152
133global[">="] = function (...) 153global[">="] =
154 function (...)
134 for _, v in ipairs({...}) do 155 for _, v in ipairs({...}) do
135 if not a >= b then return false end 156 if not a >= b then return false end
136 end 157 end
137 return true 158 return true
138end 159end
139 160--]]
140--- 161---
141return global 162return global
diff --git a/list.lua b/list.lua new file mode 100644 index 0000000..1153c26 --- /dev/null +++ b/list.lua
@@ -0,0 +1,48 @@
1--- lam.list
2
3local list = {}
4local util = require "util"
5local types = require "types"
6table.unpack = table.unpack or unpack
7
8list.Null = setmetatable({}, {
9 __type = "Null",
10 __tostring = function(self) return "()" end,
11})
12
13list.isNull =
14 function (x)
15 return x == list.Null
16 end
17
18list.List =
19 function (tbl)
20 local function tolist (base, items)
21 if #items == 0 then return base end
22 return tolist (
23 types.Cons(table.remove(items), base),
24 items
25 )
26 end
27 return tolist(list.Null, tbl)
28 end
29
30list.isList =
31 function (x)
32 if list.isNull(x) then
33 return true
34 elseif types.isa(x, "Pair") then
35 return list.isList(x[2])
36 else
37 return false
38 end
39 end
40
41list.fold1 =
42 function (fn, seed, lis)
43 if list.isNull(lis) then return seed end
44 return list.fold1(fn, fn(seed, lis[1]), lis[2])
45 end
46
47---
48return list
diff --git a/port.lua b/port.lua new file mode 100644 index 0000000..c5763df --- /dev/null +++ b/port.lua
@@ -0,0 +1,20 @@
1--- lam.port
2
3local port = {}
4table.unpack = table.unpack or unpack
5
6function port.Input (file)
7 return {
8 file = file,
9 line = "",
10 }
11end
12
13port.tokenizer = "%s*(,@|[('`,)]|)"
14
15function port.Input:tokens () -- iterator
16
17end
18
19---
20return port
diff --git a/pp.lua b/pp.lua index 85d323e..9c1a6d0 100644 --- a/pp.lua +++ b/pp.lua
@@ -3,11 +3,27 @@
3local pp = {} 3local pp = {}
4table.unpack = table.unpack or unpack 4table.unpack = table.unpack or unpack
5 5
6pp.luadump =
7 function (x)
8 end
9
10pp.luapp = function (x) print(pp.luadump(x)) end
11
12pp.lamdump =
13 function (x)
14 end
15
16pp.lampp = function (x) print(pp.lamdump(x)) end
17
18-- The following should be at some point replaced by the preceding
19
6function pp.dump (x, lvl) 20function pp.dump (x, lvl)
7 lvl = lvl or 0 21 lvl = lvl or 0
8 local space = string.rep(" ", lvl) 22 local space = string.rep(" ", lvl)
9 local output = "" 23 local output = ""
10 if type(x) == "table" then 24 --[[if getmetatable(x) and getmetatable(x).__tostring then
25 output = output .. tostring(x)
26 else]]if type(x) == "table" then
11 local subo = "" 27 local subo = ""
12 for k,v in pairs(x) do 28 for k,v in pairs(x) do
13 if v == x then 29 if v == x then
@@ -20,10 +36,8 @@ function pp.dump (x, lvl)
20 end 36 end
21 output = output .. string.format("\n%s{%s\n%s}", 37 output = output .. string.format("\n%s{%s\n%s}",
22 space, subo, space) 38 space, subo, space)
23 elseif type(x) == "string" then
24 output = output .. string.format("'%s'", x)
25 else 39 else
26 output = output .. string.format("%s", x) 40 output = output .. tostring(x)
27 end 41 end
28 return output 42 return output
29end 43end
diff --git a/read.lua b/read.lua index c89261c..00a2d2a 100644 --- a/read.lua +++ b/read.lua
@@ -2,6 +2,7 @@
2 2
3local read = {} 3local read = {}
4local utf8 = require "utf8" 4local utf8 = require "utf8"
5local types = require "types"
5table.unpack = table.unpack or unpack 6table.unpack = table.unpack or unpack
6 7
7local string_to_table = 8local string_to_table =
@@ -13,7 +14,36 @@ local string_to_table =
13 return tbl 14 return tbl
14 end 15 end
15 16
16local bslash = { -- backslash characters 17local consume_whitespace =
18 function (chars)
19 local s = {"\\"} -- accumulator for if there's no \n
20 while chars[1]:match("[ \t]") do
21 table.insert(s, util.pop(chars))
22 end
23 if chars[1] ~= "\n" then
24 table.insert(s, chars[1])
25 return table.concat(s), chars
26 end
27 while chars[1]:match("%s") do
28 util.pop(chars)
29 end
30 return chars[1], chars
31 end
32
33local consume_hexvalue =
34 function (chars)
35 local u8ch = {}
36 repeat
37 local c = util.pop(chars)
38 table.insert(u8ch,c)
39 until c == ";"
40 table.remove(u8ch) -- remove semicolon
41 return
42 utf8.char(tonumber(table.concat(u8ch), 16)),
43 chars
44 end
45
46local string_bslash = { -- backslash characters
17 a = "\a", 47 a = "\a",
18 b = "\b", 48 b = "\b",
19 t = "\t", 49 t = "\t",
@@ -22,23 +52,12 @@ local bslash = { -- backslash characters
22 ["\""] = "\"", 52 ["\""] = "\"",
23 ["\\"] = "\\", 53 ["\\"] = "\\",
24 ["|"] = "|", 54 ["|"] = "|",
25 55 -- \<intraline ws>*<line ending> <intraline ws>* : nothing
26 -- TODO: whitespace 56 [" "] = consume_whitespace,
27 -- \<intraline whitespace>*<line ending> <intraline whitespace>* : 57 ["\t"] = consuem_whitespace,
28 -- nothing 58 ["\n"] = consume_whitespace,
29 59 -- \x<hex scalar value>; : specified character
30 x = -- \x<hex scalar value>; : specified character 60 x = consume_hexvalue,
31 function (chars)
32 local u8ch = {}
33 repeat
34 local c = util.pop(chars)
35 table.insert(u8ch,c)
36 until c == ";"
37 table.remove(u8ch) -- remove semicolon
38 return
39 utf8.char(tonumber(table.concat(u8ch), 16)),
40 chars
41 end,
42} 61}
43 62
44local consume_string = 63local consume_string =
@@ -47,17 +66,22 @@ local consume_string =
47 repeat 66 repeat
48 local c = util.pop(chars) 67 local c = util.pop(chars)
49 if c == "\\" then 68 if c == "\\" then
50 c = util.pop(chars) 69 c = chars[1]
51 if bslash[c] then 70 if string_bslash[c] then
52 if type(bslash[c]) == "function" then 71 if type(string_bslash[c]) == "function"
53 c, chars = bslash[c](chars) 72 then
73 c, chars =
74 string_bslash[c](chars)
54 table.insert(str, c) 75 table.insert(str, c)
55 else 76 else
56 table.insert(str, bslash[c]) 77 table.insert(
78 str,
79 string_bslash[c])
57 end 80 end
58 else 81 else
59 table.insert(str, "\\"..c) 82 table.insert(str, "\\"..c)
60 end 83 end
84 util.pop(chars)
61 elseif c == "\"" then 85 elseif c == "\"" then
62 break 86 break
63 else 87 else
@@ -114,18 +138,9 @@ read.tokenize =
114 end 138 end
115 139
116read.tokentable = { 140read.tokentable = {
117 string = 141 string = function (tok) return types.String(tok.value) end,
118 function (tok) 142 number = function (tok) return types.Number(tok.value) end,
119 return tok.value 143 symbol = function (tok) return types.Symbol(tok.value) end,
120 end,
121 number =
122 function (tok)
123 return tonumber(tok.value)
124 end,
125 symbol =
126 function (tok) -- TODO need to return a Symbol from types...
127 return tok.value
128 end,
129} 144}
130 145
131read.parse = 146read.parse =
@@ -138,7 +153,7 @@ read.parse =
138 table.insert(L, read.parse(tokens)) 153 table.insert(L, read.parse(tokens))
139 end 154 end
140 util.pop(tokens) -- remove ")" 155 util.pop(tokens) -- remove ")"
141 return L 156 return types.List(table.unpack(L))
142 elseif tok.value == ")" then 157 elseif tok.value == ")" then
143 error("Unexpected ')'") 158 error("Unexpected ')'")
144 elseif read.tokentable[tok.type] then 159 elseif read.tokentable[tok.type] then
@@ -152,7 +167,7 @@ read.read = function (program) return read.parse(read.tokenize(program)) end
152 167
153--- 168---
154return setmetatable(read, { __call = 169return setmetatable(read, { __call =
155 function(_, program) 170 function(_, program)
156 return read.read(program) 171 return read.read(program)
157 end, 172 end,
158}) 173})
diff --git a/repl.lua b/repl.lua index 3cdfe4e..a89fd2c 100644 --- a/repl.lua +++ b/repl.lua
@@ -4,6 +4,7 @@ local repl = {}
4local eval = require "eval" 4local eval = require "eval"
5local read = require "read" 5local read = require "read"
6local util = require "util" 6local util = require "util"
7table.unpack = table.unpack or unpack
7 8
8function schemestr(x) 9function schemestr(x)
9 if type(x) == "table" then 10 if type(x) == "table" then
diff --git a/types.lua b/types.lua index d4c8d14..e4813b2 100644 --- a/types.lua +++ b/types.lua
@@ -1,28 +1,112 @@
1--- lam.types 1--- lam.types
2 2
3local types = {} 3local types = {}
4local util = require "util"
5table.unpack = table.unpack or unpack
4 6
5types.luatype = type 7--- Converting between types
6 8
7function types.lamtype (x) 9types.globalns = {} -- namespace
8 if types.luatype(x) == "string" then 10
9 return "symbol" 11types.Symbol =
10 elseif types.luatype(x) == "number" then 12 function (name, ns, aliases)
11 return "number" 13 ns = ns or types.globalns
12 elseif getmetatable(x) and getmetatable(x).__type then 14 aliases = aliases or {}
13 return getmetatable(x).__type 15 if ns[name] then return ns[name] end
14 elseif types.luatype(x) == "table" then 16 local sym = { name = name, aliases = aliases }
15 return "list" 17 ns[name] = sym
16 else 18 for _,a in ipairs(aliases) do
17 return types.luatype(x) 19 ns[a] = sym
20 end
21 local mt = {
22 __type = "Symbol",
23 __tostring = function (self) return self.name end,
24 }
25 return setmetatable(sym, mt)
26 end
27
28types.Number = tonumber
29
30types.String =
31 function (str)
32 local s = {
33 value = str,
34 escape =
35 function(self)
36 return self:gsub("\"", "\\\"")
37 end,
38 }
39 local mt = {
40 __type = "String",
41 __tostring =
42 function (self)
43 return string.format(
44 "\"%s\"",
45 self:escape())
46 end,
47 }
48 return setmetatable(s, mt)
49 end
50
51types.Cons =
52 function (a, b)
53 assert(a ~= nil and b ~= nil,
54 "Need two non-nil arguments in a pair")
55 local s = { a, b }
56 local mt = {
57 __type = "Pair",
58 __tostring =
59 function (p)
60 local out = {}
61 local car, cdr = p[1], p[2]
62 while cdr do
63 table.insert(out, tostring(car))
64 if type(cdr) == "table" then
65 car = cdr[1]
66 cdr = cdr[2]
67 else
68 table.insert(out, ".")
69 table.insert(out, cdr)
70 break
71 end
72 end
73 return "("..table.concat(out, " ")..")"
74 end
75
76 }
77 return setmetatable(s, mt)
18 end 78 end
19end
20 79
21types["number?"] = function (x) return types.lamtype(x) == "number" end 80types.List =
22types["symbol?"] = function (x) return types.lamtype(x) == "symbol" end 81 function (tbl)
23types["list?"] = function (x) return types.lamtype(x) == "list" end 82 local function tolist(base, items)
24types["procedure?"] = function (x) return types.lamtype(x) == "procedure" end 83 if #items == 0 then return base end
25types["null?"] = function (x) return x == {} end 84 return tolist(
85 types.Cons(table.remove(items), base),
86 items)
87 end
88 return tolist({}, tbl)
89 end
90
91--- Determining types
92
93types.lamtype =
94 function (x)
95 if type(x) == "number" then
96 return "Number"
97 elseif getmetatable(x) and getmetatable(x).__type then
98 return getmetatable(x).__type
99 else
100 return type(x)
101 end
102 end
103
104--- Type predicates
105
106types.isa =
107 function (x, t)
108 return types.lamtype(x) == t
109 end
26 110
27--- 111---
28return types 112return types
diff --git a/util.lua b/util.lua index 98536a1..1059edf 100644 --- a/util.lua +++ b/util.lua
@@ -1,6 +1,7 @@
1--- lam.util 1--- lam.util
2 2
3local util = {} 3local util = {}
4table.unpack = table.unpack or unpack
4 5
5function util.table (x) 6function util.table (x)
6 if type(x) == "table" then 7 if type(x) == "table" then