1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
|
--- lam.type
-- lisp types
local t = {}
local util = require "util"
local unpack = table.unpack or unpack
--- Determining types
t.luatype = type
function t.lamtype (x)
if t.luatype(x) == "number" then
return "Number"
elseif t.luatype(x) == "string" then
return "Symbol"
elseif getmetatable(x) and getmetatable(x).__type then
return getmetatable(x).__type
else
return t.luatype(x)
end
end
-- isa is really only useful on basic types (i.e., not Lists)
function t.isa (x, type)
return t.lamtype(x) == type
end
--- Creating types
-- Symbols and Numbers are strings and numbers, respectively. At some point
-- I'll want to implement a full numeric tower and symbol tables or namespaces
-- or whatever, but today is not that day
t.Symbol = tostring
t.Number = tonumber
-- Strings are (lightly) wrapped
function t.String (str)
local v = {
value = str,
escape =
function (self)
return self.gsub("[\\\"]", "\\%1")
end,
}
local mt = {
__type = "String",
__tostring =
function (self)
return string.format("\"%s\"", self:escape())
end,
}
return setmetatable(v, mt)
end
function t.totable (cons)
local out = {}
local car, cdr = cons.car, cons.cdr
while cdr do
table.insert(out, tostring(car))
if t.luatype(cdr) == "table" then
car = cdr.car
cdr = cdr.cdr
else
table.insert(out, cdr)
break
end
end
return out
end
-- Conses are Lisp's fundamental collection type
function t.Cons (a, b)
local v = { a, b, }
local mt = {
__type = "Cons",
__index =
function (self, key)
if key == "car" then
return self[1]
elseif key == "cdr" then
return self[2]
end
end,
__tostring =
function (self)
local out = {}
local car, cdr = self.car, self.cdr
while cdr do
table.insert(out, tostring(car))
if t.luatype(cdr) == "table" then
car = cdr.car
cdr = cdr.cdr
else
table.insert(out, ".")
table.insert(out, cdr)
break
end
end
return "("..table.concat(out, " ")..")"
end,
}
return setmetatable(v, mt)
end
-- Null is the one value that is both an atom and a list
t.Null = setmetatable({}, {
__type = "Null",
__tostring = function (self) return "()" end,
})
function t.isNull (x)
return x == t.Null
end
-- Lists are chained Conses ending in Null
function t.List (items)
local function tolist (base, items)
if #items == 0 then return base end
return tolist(t.Cons(table.remove(items), base), items)
end
return tolist(t.Null, items)
end
function t.isList (x)
-- TODO: this does not detect circular lists yet
if t.isNull(x) then
return true
elseif t.isa(x, "Cons") then
return t.isList(x.cdr)
else
return false
end
end
---
return t
|