about summary refs log tree commit diff stats
path: root/type.lua
diff options
context:
space:
mode:
authorCase Duckworth2024-03-30 22:20:36 -0500
committerCase Duckworth2024-03-30 22:20:36 -0500
commitab8a02fd30451207578927c7e69aa397ad596459 (patch)
tree24803910776ed692f1610f44e35d0f23b9712ca3 /type.lua
parentSpecial-case '.', '...', '+', '-' (diff)
downloadlam-ab8a02fd30451207578927c7e69aa397ad596459.tar.gz
lam-ab8a02fd30451207578927c7e69aa397ad596459.zip
Read from ports now
Diffstat (limited to 'type.lua')
-rw-r--r--type.lua202
1 files changed, 113 insertions, 89 deletions
diff --git a/type.lua b/type.lua index 0a0c62d..3c26188 100644 --- a/type.lua +++ b/type.lua
@@ -1,138 +1,162 @@
1--- lam.type 1--- lam.type
2-- lisp types
3 2
4local t = {} 3local m = {}
5local util = require "util" 4local utf8 = require "utf8"
6local unpack = table.unpack or unpack 5utf_char, utf_codepoint = utf8.char, utf8.codepoint
7 6
8--- Determining types 7--- atomic types
9 8
10t.luatype = type 9-- true, false and nil are just ... true, false, and nil
11 10
12function t.lamtype (x) 11-- Characters contain both their string reputations and their codepoints
13 if t.luatype(x) == "number" then 12function m.character (x)
14 return "Number" 13 -- is storing a character with its string and numerical representation
15 elseif t.luatype(x) == "string" then 14 -- overkill? ... maybe.
16 return "Symbol" 15 local s = tostring(x)
17 elseif getmetatable(x) and getmetatable(x).__type then 16 local uc = utf_codepoint(s)
18 return getmetatable(x).__type 17 local t = { -- String representation of the character
19 else 18 v = utf_char(uc),
20 return t.luatype(x) 19 u = uc,
21 end 20 }
22end 21 local mt = {
23 22 __type = "character",
24-- isa is really only useful on basic types (i.e., not Lists) 23 __eq = function (self) return self.v end,
25function t.isa (x, type) 24 __lt = function (a, b) return a.u < b.u end,
26 return t.lamtype(x) == type 25 __tostring =
26 function (self)
27 local v = self.v
28 if v == "\n" then
29 return "#\\newline"
30 elseif v == " " then
31 return "#\\space"
32 else
33 return "#\\" .. v
34 end
35 end,
36 }
37 return setmetatable(t, mt)
27end 38end
28 39
29--- Creating types 40-- a symbol is just a string, unadorned. I was going to have a character be
30 41-- represented by a one-character string, but then it would be indistinguishable
31-- Symbols and Numbers are strings and numbers, respectively. At some point 42-- from a one-character symbol internally.
32-- I'll want to implement a full numeric tower and symbol tables or namespaces 43m.symbol = tostring
33-- or whatever, but today is not that day
34 44
35t.Symbol = tostring 45-- for now, number will just be lua's number. At *some* point, it will be the
36t.Number = tonumber 46-- whole numeric tower, yaaayyy
47m.number = tonumber
37 48
38-- Strings are (lightly) wrapped 49-- strings are wrapped strings
39function t.String (str) 50function m.string (x)
40 local v = { 51 local x = tostring(x)
41 value = str, 52 local t = {
53 v = x,
42 escape = 54 escape =
43 function (self) 55 function (self)
44 return self.gsub("[\\\"]", "\\%1") 56 return self.v:gsub("[\\\"]", "\\%1")
45 end, 57 end,
46 } 58 }
47 local mt = { 59 local mt = {
48 __type = "String", 60 __type = "string",
49 __tostring = 61 __tostring =
50 function (self) 62 function (self)
51 return string.format("\"%s\"", self:escape()) 63 return "\"" .. self:escape() .. "\""
52 end, 64 end,
53 } 65 }
54 return setmetatable(v, mt) 66 return setmetatable(t, mt)
55end 67end
56 68
57function t.totable (cons) 69-- null () is both an atom and a list (yay)
58 local out = {} 70-- this one is NOT a function
59 local car, cdr = cons.car, cons.cdr 71m.null = setmetatable({}, {
60 while cdr do 72 __type = "null",
61 table.insert(out, tostring(car)) 73 __tostring = function (self) return "()" end,
62 if t.luatype(cdr) == "table" then 74})
63 car = cdr.car 75
64 cdr = cdr.cdr 76--- collection types
65 else
66 table.insert(out, cdr)
67 break
68 end
69 end
70 return out
71end
72 77
73-- Conses are Lisp's fundamental collection type 78-- cons are lisp's fundamental collection type
74function t.Cons (a, b) 79function m.cons (a, b)
75 local v = { a, b, } 80 local t = { a, b, }
76 local mt = { 81 local mt = {
77 __type = "Cons", 82 __type = "cons",
78 __index =
79 function (self, key)
80 if key == "car" then
81 return self[1]
82 elseif key == "cdr" then
83 return self[2]
84 end
85 end,
86 __tostring = 83 __tostring =
87 function (self) 84 function (self)
88 local out = {} 85 local out = {}
89 local car, cdr = self.car, self.cdr 86 local car, cdr = self[1], self[2]
90 while cdr do 87 while cdr do
91 table.insert(out, tostring(car)) 88 table.insert(out, tostring(car))
92 if t.luatype(cdr) == "table" then 89 if m.luatype(cdr) == "table" then
93 car = cdr.car 90 car = cdr[1]
94 cdr = cdr.cdr 91 cdr = cdr[2]
95 else 92 else
96 table.insert(out, ".") 93 table.insert(out, ".")
97 table.insert(out, cdr) 94 table.insert(out, cdr)
98 break 95 break
99 end 96 end
100 end 97 end
101 return "("..table.concat(out, " ")..")" 98 return "(" .. table.concat(out, " ") .. ")"
102 end, 99 end,
103 } 100 }
104 return setmetatable(v, mt) 101 return setmetatable(t, mt)
105end 102end
106 103
107-- Null is the one value that is both an atom and a list 104-- lists are singly-linked cons cells
108t.Null = setmetatable({}, { 105function m.list (items, last)
109 __type = "Null", 106 -- ITEMS is a table and LAST is an optional final cdr. If it's nil, the
110 __tostring = function (self) return "()" end, 107 -- list is a "proper" list; that is, it ends in ().
111}) 108 local function tolist (base, items)
109 if #items == 0 then return base end
110 return tolist(m.cons(table.remove(items), base), items)
111 end
112 return tolist(last or m.null, items)
113end
112 114
113function t.isNull (x) 115-- convert a list to a lua table
114 return x == t.Null 116function m.totable (cons)
117 local t = {}
118 local car, cdr = cons[1], cons[2]
119 while cdr do
120 table.insert(t, car)
121 if m.luatype(cdr) == "table" then
122 car = cdr[1]
123 cdr = cdr[2]
124 else
125 table.insert(t, cdr)
126 end
127 end
128 return t
115end 129end
116 130
117-- Lists are chained Conses ending in Null 131-- testing types
118function t.List (items, last) 132
119 local function tolist (base, items) 133-- we love name collisions
120 if #items == 0 then return base end 134m.luatype = type
121 return tolist(t.Cons(table.remove(items), base), items) 135
136function m.lamtype (x)
137 if m.luatype(x) == "string" then
138 return "symbol"
139 elseif getmetatable(x) and getmetatable(x).__type then
140 return getmetatable(x).__type
141 else
142 return m.luatype(x)
122 end 143 end
123 return tolist(last or t.Null, items)
124end 144end
125 145
126function t.isList (x) 146function m.isa (x, t)
127 -- TODO: this does not detect circular lists yet 147 return m.lamtype(x) == t
128 if t.isNull(x) then 148end
149
150function m.islist (x)
151 -- TODO: detect circular lists
152 if x == m.null then
129 return true 153 return true
130 elseif t.isa(x, "Cons") then 154 elseif m.isa(x, "cons") then
131 return t.isList(x.cdr) 155 return m.islist(x[2])
132 else 156 else
133 return false 157 return false
134 end 158 end
135end 159end
136 160
137--- 161--------
138return t 162return m