about summary refs log tree commit diff stats
path: root/type.lua
diff options
context:
space:
mode:
authorCase Duckworth2024-03-10 21:39:53 -0500
committerCase Duckworth2024-03-10 21:39:53 -0500
commita72ff678da253fce46e8e4648f6e4cf5ce1ea9b4 (patch)
treea82501fca97f4adc272d05145548d10cabe3ea2a /type.lua
parentUgghhhh totally not working (diff)
downloadlam-a72ff678da253fce46e8e4648f6e4cf5ce1ea9b4.tar.gz
lam-a72ff678da253fce46e8e4648f6e4cf5ce1ea9b4.zip
uh new start
Diffstat (limited to 'type.lua')
-rw-r--r--type.lua137
1 files changed, 137 insertions, 0 deletions
diff --git a/type.lua b/type.lua new file mode 100644 index 0000000..945f4d1 --- /dev/null +++ b/type.lua
@@ -0,0 +1,137 @@
1--- lam.type
2-- lisp types
3
4local t = {}
5local util = require "util"
6local unpack = table.unpack or unpack
7
8--- Determining types
9
10t.luatype = type
11
12function t.lamtype (x)
13 if t.luatype(x) == "number" then
14 return "Number"
15 elseif t.luatype(x) == "string" then
16 return "Symbol"
17 elseif getmetatable(x) and getmetatable(x).__type then
18 return getmetatable(x).__type
19 else
20 return t.luatype(x)
21 end
22end
23
24-- isa is really only useful on basic types (i.e., not Lists)
25function t.isa (x, type)
26 return t.lamtype(x) == type
27end
28
29--- Creating types
30
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
33-- or whatever, but today is not that day
34t.Symbol = tostring
35t.Number = tonumber
36
37-- Strings are (lightly) wrapped
38function t.String (str)
39 local v = {
40 value = str,
41 escape =
42 function (self)
43 return self.gsub("[\\\"]", "\\%1")
44 end,
45 }
46 local mt = {
47 __type = "String",
48 __tostring =
49 function (self)
50 return string.format("\"%s\"", self:escape())
51 end,
52 }
53 return setmetatable(v, mt)
54end
55
56function t.totable (cons)
57 local out = {}
58 local car, cdr = cons.car, cons.cdr
59 while cdr do
60 table.insert(out, tostring(car))
61 if t.luatype(cdr) == "table" then
62 car = cdr.car
63 cdr = cdr.cdr
64 else
65 table.insert(out, cdr)
66 break
67 end
68 end
69 return out
70end
71
72-- Conses are Lisp's fundamental collection type
73function t.Cons (a, b)
74 local v = { a, b, }
75 local mt = {
76 __type = "Cons",
77 __index =
78 function (self, key)
79 if key == "car" then
80 return self[1]
81 elseif key == "cdr" then
82 return self[2]
83 end
84 end,
85 __tostring =
86 function (self)
87 local out = {}
88 local car, cdr = self.car, self.cdr
89 while cdr do
90 table.insert(out, tostring(car))
91 if t.luatype(cdr) == "table" then
92 car = cdr.car
93 cdr = cdr.cdr
94 else
95 table.insert(out, ".")
96 table.insert(out, cdr)
97 break
98 end
99 end
100 return "("..table.concat(out, " ")..")"
101 end,
102 }
103 return setmetatable(v, mt)
104end
105
106-- Null is the one value that is both an atom and a list
107t.Null = setmetatable({}, {
108 __type = "Null",
109 __tostring = function (self) return "()" end,
110})
111
112function t.isNull (x)
113 return x == t.Null
114end
115
116-- Lists are chained Conses ending in Null
117function t.List (items)
118 local function tolist (base, items)
119 if #items == 0 then return base end
120 return tolist(t.Cons(table.remove(items), base), items)
121 end
122 return tolist(t.Null, items)
123end
124
125function t.isList (x)
126 -- TODO: this does not detect circular lists yet
127 if t.isNull(x) then
128 return true
129 elseif t.isa(x, "Cons") then
130 return t.isList(x.cdr)
131 else
132 return false
133 end
134end
135
136---
137return t