--- 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, last) local function tolist (base, items) if #items == 0 then return base end return tolist(t.Cons(table.remove(items), base), items) end return tolist(last or 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