list.lua

Faddle · faddle.git · Tickets

Branches > main > de7e2b0 > 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}