list.lua
Branches > main > 8432b65 > files / org / faddle / lang / list.lua
-- Faddle's linked lists
-- Immutable outside of rawset
local LIST_METATABLE = {}
local FIRST = {}
local REST = {}
local COUNT = {}
local NEWINDEX = function() error("Cannot mutate list!") end
local EMPTY
EMPTY = setmetatable({}, {__index = {[COUNT] = 0, [REST] = EMPTY}, __metatable = LIST_METATABLE, __newindex = NEWINDEX})
local function cons(x, lst)
if lst == nil then
return setmetatable({}, {__index = {[COUNT] = 1, [FIRST] = x, [REST] = EMPTY}, __metatable = LIST_METATABLE, __newindex = NEWINDEX})
elseif getmetatable(lst) ~= LIST_METATABLE then
error("Cannot cons onto type " .. type(lst), 2)
else
return setmetatable({}, {__index = {[COUNT] = lst[COUNT] + 1, [FIRST] = x, [REST] = lst}, __metatable = LIST_METATABLE, __newindex = NEWINDEX})
end
end
local function is_list(x)
return getmetatable(x) == LIST_METATABLE
end
local function first(lst)
if lst == nil then
return
elseif getmetatable(lst) == LIST_METATABLE then
return lst[FIRST]
else
error("Cannot call first on type " .. type(lst), 2)
end
end
local function count(lst)
if lst == nil then
return 0
elseif getmetatable(lst) == LIST_METATABLE then
return lst[COUNT]
else
error("Cannot get the count of a non list, type " .. type(lst), 2)
end
end
local function rest(lst)
if lst == nil then
return EMPTY
elseif getmetatable(lst) == LIST_METATABLE then
return lst[REST]
else
error("Cannot call rest on type " .. type(lst), 2)
end
end
-- Just builds up recursively with cons for now, but this could be much more efficient of course.
local function list(...)
local lst = EMPTY
local cnt = select("#", ...)
for i = 1, cnt do
-- The main branch of cons
lst = setmetatable({}, {__index = {[COUNT] = i, [FIRST] = select(cnt - i + 1, ...), [REST] = lst}, __metatable = LIST_METATABLE, __newindex = NEWINDEX})
end
return lst
end
-- local function iterator(lst) end
local function expand(lst)
if lst == EMPTY then return end
return lst[FIRST], expand(lst[REST])
end
local function apply(f, lst)
if getmetatable(lst) ~= LIST_METATABLE then error("Cannot apply to non-list") end
if lst[COUNT] >= 256 then error("List is too long to apply, I think maybe idk") end
return f(expand(lst))
end
return {is_list = is_list,
cons = cons,
first = first,
rest = rest,
count = count,
list = list,
empty = EMPTY,
apply = apply}