From a72ff678da253fce46e8e4648f6e4cf5ce1ea9b4 Mon Sep 17 00:00:00 2001 From: Case Duckworth Date: Sun, 10 Mar 2024 21:39:53 -0500 Subject: uh new start --- type.lua | 137 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 type.lua (limited to 'type.lua') diff --git a/type.lua b/type.lua new file mode 100644 index 0000000..945f4d1 --- /dev/null +++ b/type.lua @@ -0,0 +1,137 @@ +--- 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 -- cgit 1.4.1-21-gabe81