mirror of
https://github.com/kanaka/mal.git
synced 2024-11-11 00:52:44 +03:00
a68c26aff8
- Update to support the new behavior of varargs and table.pack/unpack in lua 5.2 - Bump up the stack overflow test since lua 5.2 seems to support much larger stacks before overflow. - Fix a latent issue in if forms where false in the 4th position would result in nil returned. Add a new test to catch this in the future.
230 lines
4.7 KiB
Lua
230 lines
4.7 KiB
Lua
local utils = require('utils')
|
|
|
|
local M = {}
|
|
|
|
-- type functions
|
|
|
|
function M._sequential_Q(obj)
|
|
return M._list_Q(obj) or M._vector_Q(obj)
|
|
end
|
|
|
|
function M._equal_Q(a,b)
|
|
if M._symbol_Q(a) and M._symbol_Q(b) then
|
|
return a.val == b.val
|
|
elseif M._sequential_Q(a) and M._sequential_Q(b) then
|
|
if #a ~= #b then return false end
|
|
for i, v in ipairs(a) do
|
|
if not M._equal_Q(v,b[i]) then return false end
|
|
end
|
|
return true
|
|
elseif M._hash_map_Q(a) and M._hash_map_Q(b) then
|
|
if #a ~= #b then return false end
|
|
for k, v in pairs(a) do
|
|
if not M._equal_Q(v,b[k]) then return false end
|
|
end
|
|
return true
|
|
else
|
|
return a == b
|
|
end
|
|
end
|
|
|
|
function M.copy(obj)
|
|
if type(obj) == "function" then
|
|
return M.FunctionRef:new(obj)
|
|
end
|
|
if type(obj) ~= "table" then return obj end
|
|
|
|
-- copy object data
|
|
local new_obj = {}
|
|
for k,v in pairs(obj) do
|
|
new_obj[k] = v
|
|
end
|
|
|
|
-- copy metatable and link to original
|
|
local old_mt = getmetatable(obj)
|
|
if old_mt ~= nil then
|
|
local new_mt = {}
|
|
for k,v in pairs(old_mt) do
|
|
new_mt[k] = v
|
|
end
|
|
setmetatable(new_mt, old_mt)
|
|
setmetatable(new_obj, new_mt)
|
|
end
|
|
|
|
return new_obj
|
|
end
|
|
|
|
function M.slice(lst, start, last)
|
|
if last == nil then last = #lst end
|
|
local new_lst = {}
|
|
if start <= last then
|
|
for i = start, last do
|
|
new_lst[#new_lst+1] = lst[i]
|
|
end
|
|
end
|
|
return new_lst
|
|
end
|
|
|
|
-- Error/exceptions
|
|
|
|
M.MalException = {}
|
|
function M.MalException:new(val)
|
|
local newObj = {val = val}
|
|
self.__index = self
|
|
return setmetatable(newObj, self)
|
|
end
|
|
function M._malexception_Q(obj)
|
|
return utils.instanceOf(obj, M.MalException)
|
|
end
|
|
|
|
function M.throw(val)
|
|
error(M.MalException:new(val))
|
|
end
|
|
|
|
-- Nil
|
|
|
|
local NilType = {}
|
|
function NilType:new(val)
|
|
local newObj = {}
|
|
self.__index = self
|
|
return setmetatable(newObj, self)
|
|
end
|
|
M.Nil = NilType:new()
|
|
function M._nil_Q(obj)
|
|
return obj == Nil
|
|
end
|
|
|
|
-- Numbers
|
|
function M._number_Q(obj)
|
|
return type(obj) == "number"
|
|
end
|
|
|
|
-- Strings
|
|
function M._string_Q(obj)
|
|
return type(obj) == "string"
|
|
end
|
|
|
|
-- Symbols
|
|
|
|
M.Symbol = {}
|
|
function M.Symbol:new(val)
|
|
local newObj = {val = val}
|
|
self.__index = self
|
|
return setmetatable(newObj, self)
|
|
end
|
|
function M._symbol_Q(obj)
|
|
return utils.instanceOf(obj, M.Symbol)
|
|
end
|
|
|
|
-- Keywords
|
|
function M._keyword_Q(obj)
|
|
return M._string_Q(obj) and "\177" == string.sub(obj,1,1)
|
|
end
|
|
|
|
|
|
-- Lists
|
|
|
|
M.List = {}
|
|
function M.List:new(lst)
|
|
local newObj = lst and lst or {}
|
|
self.__index = self
|
|
return setmetatable(newObj, self)
|
|
end
|
|
function M._list_Q(obj)
|
|
return utils.instanceOf(obj, M.List)
|
|
end
|
|
function M.List:slice(start,last)
|
|
return M.List:new(M.slice(self,start,last))
|
|
end
|
|
|
|
-- Vectors
|
|
|
|
M.Vector = {}
|
|
function M.Vector:new(lst)
|
|
local newObj = lst and lst or {}
|
|
self.__index = self
|
|
return setmetatable(newObj, self)
|
|
end
|
|
function M._vector_Q(obj)
|
|
return utils.instanceOf(obj, M.Vector)
|
|
end
|
|
function M.Vector:slice(start,last)
|
|
return M.Vector:new(M.slice(self,start,last))
|
|
end
|
|
|
|
-- Hash Maps
|
|
--
|
|
M.HashMap = {}
|
|
function M.HashMap:new(val)
|
|
local newObj = val and val or {}
|
|
self.__index = self
|
|
return setmetatable(newObj, self)
|
|
end
|
|
function M.hash_map(...)
|
|
return M._assoc_BANG(M.HashMap:new(), ...)
|
|
end
|
|
function M._hash_map_Q(obj)
|
|
return utils.instanceOf(obj, M.HashMap)
|
|
end
|
|
function M._assoc_BANG(hm, ...)
|
|
local arg = table.pack(...)
|
|
for i = 1, #arg, 2 do
|
|
hm[arg[i]] = arg[i+1]
|
|
end
|
|
return hm
|
|
end
|
|
function M._dissoc_BANG(hm, ...)
|
|
local arg = table.pack(...)
|
|
for i = 1, #arg do
|
|
hm[arg[i]] = nil
|
|
end
|
|
return hm
|
|
end
|
|
|
|
-- Functions
|
|
|
|
M.MalFunc = {}
|
|
function M.MalFunc:new(fn, ast, env, params)
|
|
local newObj = {fn = fn, ast = ast, env = env,
|
|
params = params, ismacro = false}
|
|
self.__index = self
|
|
return setmetatable(newObj, self)
|
|
end
|
|
function M._malfunc_Q(obj)
|
|
return utils.instanceOf(obj, M.MalFunc)
|
|
end
|
|
function M._fn_Q(obj)
|
|
return type(obj) == "function" or (M._malfunc_Q(obj) and not obj.ismacro)
|
|
end
|
|
function M._macro_Q(obj)
|
|
return M._malfunc_Q(obj) and obj.ismacro
|
|
end
|
|
|
|
-- Atoms
|
|
|
|
M.Atom = {}
|
|
function M.Atom:new(val)
|
|
local newObj = {val = val}
|
|
self.__index = self
|
|
return setmetatable(newObj, self)
|
|
end
|
|
function M._atom_Q(obj)
|
|
return utils.instanceOf(obj, M.Atom)
|
|
end
|
|
|
|
-- FunctionRefs
|
|
|
|
M.FunctionRef = {}
|
|
function M.FunctionRef:new(fn)
|
|
local newObj = {fn = fn}
|
|
return setmetatable(newObj, self)
|
|
end
|
|
function M._functionref_Q(obj)
|
|
return utils.instanceOf(obj, M.FunctionRef)
|
|
end
|
|
function M.FunctionRef:__call(...)
|
|
return self.fn(...)
|
|
end
|
|
|
|
return M
|