diff options
author | Case Duckworth | 2024-03-04 21:01:27 -0600 |
---|---|---|
committer | Case Duckworth | 2024-03-04 21:01:27 -0600 |
commit | bbaff0e0c204c2fab216f6501dc8c11b4425b4bc (patch) | |
tree | b2a06edc7b1c8e6f86839bff8c16e06297080674 | |
parent | Add copying (diff) | |
download | lam-first-try.tar.gz lam-first-try.zip |
Ugghhhh totally not working first-try
-rw-r--r-- | eval.lua | 18 | ||||
-rw-r--r-- | eval2.lua | 39 | ||||
-rw-r--r-- | global.lua | 91 | ||||
-rw-r--r-- | list.lua | 48 | ||||
-rw-r--r-- | port.lua | 20 | ||||
-rw-r--r-- | pp.lua | 22 | ||||
-rw-r--r-- | read.lua | 93 | ||||
-rw-r--r-- | repl.lua | 1 | ||||
-rw-r--r-- | types.lua | 122 | ||||
-rw-r--r-- | util.lua | 1 |
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" | |||
5 | local util = require "util" | 5 | local util = require "util" |
6 | local pp = require "pp" | 6 | local pp = require "pp" |
7 | local global = require "global" | 7 | local global = require "global" |
8 | local types = require("types") | 8 | local types = require "types" |
9 | table.unpack = table.unpack or unpack | ||
9 | 10 | ||
10 | if 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 | ||
12 | local function Env(inner, outer) | 14 | local function Env(inner, outer) |
13 | return setmetatable(inner, { __type = "environment", __index = outer, }) | 15 | return setmetatable(inner, { __type = "Environment", __index = outer, }) |
14 | end | 16 | end |
15 | 17 | ||
16 | local function Proc(params, body, env) | 18 | local 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 | ||
38 | function eval.eval (x, e) | 40 | function 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 | |||
3 | local eval = {} | ||
4 | local read = require "read" | ||
5 | local util = require "util" | ||
6 | local types = require "types" | ||
7 | table.unpack = table.unpack or unpack | ||
8 | |||
9 | local 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 | |||
22 | local 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 | --- | ||
39 | return 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 | ||
3 | local util = require "util" | 3 | local util = require "util" |
4 | local types = require("types") | 4 | local types = require "types" |
5 | 5 | table.unpack = table.unpack or unpack | |
6 | if not table.unpack then table.unpack = unpack end | ||
7 | 6 | ||
8 | local global = { | 7 | local 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 | ||
16 | for name, func in pairs(types) do | 15 | global.luatype = type |
17 | if name == "lamtype" then | 16 | global.type = types.lamtype |
18 | global.type = func | 17 | |
19 | else | 18 | global["number?"] = function (x) types.isa(x, "Number") end |
20 | global[name] = func | 19 | global["string?"] = function (x) types.isa(x, "String") end |
21 | end | 20 | global["symbol?"] = function (x) types.isa(x, "Symbol") end |
22 | end | 21 | global["pair?"] = function (x) types.isa(x, "Pair") end |
22 | global["is-a?"] = function (x, t) types.isa(x, t) end | ||
23 | 23 | ||
24 | --- Basic functions --- | 24 | --- Basic functions --- |
25 | 25 | ||
26 | global.begin = function(...) | 26 | global.car = function (pair) return pair[1] end |
27 | local xs = {...} | 27 | global.cdr = function (pair) return pair[2] end |
28 | return xs[#xs] | ||
29 | end | ||
30 | 28 | ||
31 | global.car = util.car | 29 | -- global.list = types.List |
32 | global.cdr = util.cdr | ||
33 | 30 | ||
34 | global.list = function(...) return {...} end | 31 | global["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 --- | 41 | global["null?"] = function (x) return type(x) == "table" and #x == 0 end |
37 | 42 | ||
43 | --- Higher-order functions --- | ||
44 | --[[ | ||
38 | global.apply = function(fn, ...) | 45 | global.apply = function(fn, ...) |
39 | local args = {...} | 46 | local args = {...} |
40 | local last = args[#args] | 47 | local last = args[#args] |
@@ -49,7 +56,7 @@ end | |||
49 | global.map = function(fn, list) | 56 | global.map = function(fn, list) |
50 | return util.map(fn, list) | 57 | return util.map(fn, list) |
51 | end | 58 | end |
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 |
58 | end | 65 | end |
59 | 66 | ||
60 | global["+"] = function (...) | 67 | global.fold = |
68 | function (fn, lis) | ||
69 | local out = {} | ||
70 | |||
71 | return types.List(out) | ||
72 | end | ||
73 | |||
74 | global["+"] = 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) |
62 | end | 77 | end |
63 | 78 | ||
@@ -102,40 +117,46 @@ global["/"] = function (...) | |||
102 | end | 117 | end |
103 | end | 118 | end |
104 | 119 | ||
105 | global["="] = function (...) | 120 | --[[ |
106 | for _, v in ipairs({...}) do | 121 | global["="] = |
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 | ||
110 | end | ||
111 | 128 | ||
112 | global["<"] = function (...) | 129 | global["<"] = |
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 | ||
117 | end | ||
118 | 136 | ||
119 | global["<="] = function (...) | 137 | global["<="] = |
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 |
124 | end | 143 | end |
125 | 144 | ||
126 | global[">"] = function (...) | 145 | global[">"] = |
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 |
131 | end | 151 | end |
132 | 152 | ||
133 | global[">="] = function (...) | 153 | global[">="] = |
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 |
138 | end | 159 | end |
139 | 160 | --]] | |
140 | --- | 161 | --- |
141 | return global | 162 | return 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 | |||
3 | local list = {} | ||
4 | local util = require "util" | ||
5 | local types = require "types" | ||
6 | table.unpack = table.unpack or unpack | ||
7 | |||
8 | list.Null = setmetatable({}, { | ||
9 | __type = "Null", | ||
10 | __tostring = function(self) return "()" end, | ||
11 | }) | ||
12 | |||
13 | list.isNull = | ||
14 | function (x) | ||
15 | return x == list.Null | ||
16 | end | ||
17 | |||
18 | list.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 | |||
30 | list.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 | |||
41 | list.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 | --- | ||
48 | return 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 | |||
3 | local port = {} | ||
4 | table.unpack = table.unpack or unpack | ||
5 | |||
6 | function port.Input (file) | ||
7 | return { | ||
8 | file = file, | ||
9 | line = "", | ||
10 | } | ||
11 | end | ||
12 | |||
13 | port.tokenizer = "%s*(,@|[('`,)]|)" | ||
14 | |||
15 | function port.Input:tokens () -- iterator | ||
16 | |||
17 | end | ||
18 | |||
19 | --- | ||
20 | return port | ||
diff --git a/pp.lua b/pp.lua index 85d323e..9c1a6d0 100644 --- a/pp.lua +++ b/pp.lua | |||
@@ -3,11 +3,27 @@ | |||
3 | local pp = {} | 3 | local pp = {} |
4 | table.unpack = table.unpack or unpack | 4 | table.unpack = table.unpack or unpack |
5 | 5 | ||
6 | pp.luadump = | ||
7 | function (x) | ||
8 | end | ||
9 | |||
10 | pp.luapp = function (x) print(pp.luadump(x)) end | ||
11 | |||
12 | pp.lamdump = | ||
13 | function (x) | ||
14 | end | ||
15 | |||
16 | pp.lampp = function (x) print(pp.lamdump(x)) end | ||
17 | |||
18 | -- The following should be at some point replaced by the preceding | ||
19 | |||
6 | function pp.dump (x, lvl) | 20 | function 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 |
29 | end | 43 | end |
diff --git a/read.lua b/read.lua index c89261c..00a2d2a 100644 --- a/read.lua +++ b/read.lua | |||
@@ -2,6 +2,7 @@ | |||
2 | 2 | ||
3 | local read = {} | 3 | local read = {} |
4 | local utf8 = require "utf8" | 4 | local utf8 = require "utf8" |
5 | local types = require "types" | ||
5 | table.unpack = table.unpack or unpack | 6 | table.unpack = table.unpack or unpack |
6 | 7 | ||
7 | local string_to_table = | 8 | local string_to_table = |
@@ -13,7 +14,36 @@ local string_to_table = | |||
13 | return tbl | 14 | return tbl |
14 | end | 15 | end |
15 | 16 | ||
16 | local bslash = { -- backslash characters | 17 | local 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 | |||
33 | local 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 | |||
46 | local 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 | ||
44 | local consume_string = | 63 | local 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 | ||
116 | read.tokentable = { | 140 | read.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 | ||
131 | read.parse = | 146 | read.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 | --- |
154 | return setmetatable(read, { __call = | 169 | return 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 = {} | |||
4 | local eval = require "eval" | 4 | local eval = require "eval" |
5 | local read = require "read" | 5 | local read = require "read" |
6 | local util = require "util" | 6 | local util = require "util" |
7 | table.unpack = table.unpack or unpack | ||
7 | 8 | ||
8 | function schemestr(x) | 9 | function 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 | ||
3 | local types = {} | 3 | local types = {} |
4 | local util = require "util" | ||
5 | table.unpack = table.unpack or unpack | ||
4 | 6 | ||
5 | types.luatype = type | 7 | --- Converting between types |
6 | 8 | ||
7 | function types.lamtype (x) | 9 | types.globalns = {} -- namespace |
8 | if types.luatype(x) == "string" then | 10 | |
9 | return "symbol" | 11 | types.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 | |||
28 | types.Number = tonumber | ||
29 | |||
30 | types.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 | |||
51 | types.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 |
19 | end | ||
20 | 79 | ||
21 | types["number?"] = function (x) return types.lamtype(x) == "number" end | 80 | types.List = |
22 | types["symbol?"] = function (x) return types.lamtype(x) == "symbol" end | 81 | function (tbl) |
23 | types["list?"] = function (x) return types.lamtype(x) == "list" end | 82 | local function tolist(base, items) |
24 | types["procedure?"] = function (x) return types.lamtype(x) == "procedure" end | 83 | if #items == 0 then return base end |
25 | types["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 | |||
93 | types.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 | |||
106 | types.isa = | ||
107 | function (x, t) | ||
108 | return types.lamtype(x) == t | ||
109 | end | ||
26 | 110 | ||
27 | --- | 111 | --- |
28 | return types | 112 | return 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 | ||
3 | local util = {} | 3 | local util = {} |
4 | table.unpack = table.unpack or unpack | ||
4 | 5 | ||
5 | function util.table (x) | 6 | function util.table (x) |
6 | if type(x) == "table" then | 7 | if type(x) == "table" then |