123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375 |
- local _G=_G
- local ipairs=ipairs
- local select=select
- local pairs=pairs
- local type = type
- local unpack=unpack
- local logError = logError
- local DATA = DATA
- --/////////////////////////
- local function table_print (tt, done)
- done = done or {}
- if type(tt) == "table" then
- local sb = {}
- table.insert(sb, "{");
- for key, value in pairs (tt) do
- if type (value) == "table" and not done [value] then
- done [value] = true
- table.insert(sb, key .. ":{");
- table.insert(sb, table_print (value, done))
- table.insert(sb, "}");
- else
- table.insert(sb, string.format(
- "%s:\"%s\"", tostring (key), tostring(value)))
- end
- table.insert(sb, ",");
- end
- table.insert(sb, "}");
- m = getmetatable(tt);
- if m and m.__index then
- table.insert(sb, table_print (m.__index, done));
- end
- return table.concat(sb)
- else
- return tt .. ""
- end
- end
- local function to_string( tbl )
- if "nil" == type( tbl ) then
- return tostring(nil)
- elseif "table" == type( tbl ) then
- return table_print(tbl)
- elseif "string" == type( tbl ) then
- return tbl
- else
- return tostring(tbl)
- end
- end
- --/////////////////////////
- local VERM = {}
- local function createEnv(parent, current)
- return setmetatable(
- current or {},
- {
- __parent = parent,
- __index = parent,
- __newindex = function(t, k ,v)
- if type(k) ~= "string" then
- error("String key for env. table required, but got:"..to_string(k))
- end
- local function setOnFirstHit(t, k, v)
- local vv = rawget(t, k)
- if vv~= nil then rawset(t, k, v); return true end
- local m = getmetatable(t)
- if not m then return false end --assume top
- local p = m.__parent
- if not p then
- return false
- else
- return setOnFirstHit(p, k, v)
- end
- end
- if not setOnFirstHit(t, k, v) then
- rawset(t, k, v)
- end
- end
- }
- )
- end
- local function isNIL(v)
- return (type(v) == "table") and (next(v) == nil)
- end
- local function prognForm(e, ...)
- --eval each argument, return last result
- local argc = select('#',...)
- if argc == 0 then return {} end
- for n = 1, argc - 1 do
- VERM:eval(e, (select(n,...)))
- end
- return VERM:eval(e, (select(argc,...)))
- end
- local function lambdaOrMacro(e, isMacro, args, ...)
- --TODO: get rid of pack-unpack
- local body = {...}
- local oldEnv = e
- local ret = function(e, ...)
- -- we need a function that have parameters with names from `args` table
- -- pack parameters from '...' and bind to new environment
- local newEnv = createEnv(oldEnv, {})
- for i, v in ipairs(args) do
- local p = select(i,...)
- if isMacro then
- newEnv[v] = p
- else
- newEnv[v] = VERM:evalValue(e, p)
- end
- end
- if isMacro then
- local buffer = {}
- for _, v in ipairs(body) do
- table.insert(buffer, (VERM:eval(newEnv, v)))
- end
- return prognForm(newEnv, unpack(buffer))
- else
- return prognForm(newEnv, unpack(body))
- end
- end
- return ret
- end
- local function lambdaForm(e, args, ...)
- return lambdaOrMacro(e, false, args, ...)
- end
- local function defunForm(e, name, args, ...)
- local ret = lambdaOrMacro(e, false, args, ...)
- e[name] = ret
- return ret
- end
- local function defmacroForm(e, name, args, ...)
- local ret = lambdaOrMacro(e, true, args, ...)
- e[name] = ret
- return ret
- end
- local function backquoteEval(e, v)
- if isNIL(v) then
- return v
- elseif type(v) == "table" then
- local car = v[1]
- if car == "," then
- return VERM:evalValue(e, v[2])
- else
- local ret = {}
- for _, v in ipairs(v) do
- table.insert(ret, (backquoteEval(e, v)))
- end
- return ret
- end
- else
- return v
- end
- end
- local specialForms =
- {
- ["<"] = function(e, lhs, rhs)
- lhs = VERM:evalValue(e, lhs)
- rhs = VERM:evalValue(e, rhs)
- if lhs < rhs then
- return lhs
- else
- return {}
- end
- end,
- ["<="] = function(e, lhs, rhs)
- lhs = VERM:evalValue(e, lhs)
- rhs = VERM:evalValue(e, rhs)
- if lhs <= rhs then
- return lhs
- else
- return {}
- end
- end,
- [">"] = function(e, lhs, rhs)
- lhs = VERM:evalValue(e, lhs)
- rhs = VERM:evalValue(e, rhs)
- if lhs > rhs then
- return lhs
- else
- return {}
- end
- end,
- [">="] = function(e, lhs, rhs)
- lhs = VERM:evalValue(e, lhs)
- rhs = VERM:evalValue(e, rhs)
- if lhs >= rhs then
- return lhs
- else
- return {}
- end
- end,
- ["="] = function(e, lhs, rhs)
- lhs = VERM:evalValue(e, lhs)
- rhs = VERM:evalValue(e, rhs)
- if lhs == rhs then
- return lhs
- else
- return {}
- end
- end,
- ["+"] = function(e, ...)
- local ret = 0
- for n=1,select('#',...) do
- local v = VERM:evalValue(e, (select(n,...)))
- ret = ret + v
- end
- return ret
- end,
- ["*"] = function(e, ...)
- local ret = 1
- for n=1,select('#',...) do
- local v = VERM:evalValue(e, (select(n,...)))
- ret = ret * v
- end
- return ret
- end,
- ["-"] = function(e, lhs, rhs)
- lhs = VERM:evalValue(e, lhs)
- rhs = VERM:evalValue(e, rhs)
- return lhs - rhs
- end,
- ["/"] = function(e, lhs, rhs)
- lhs = VERM:evalValue(e, lhs)
- rhs = VERM:evalValue(e, rhs)
- return lhs / rhs
- end,
- ["%"] = function(e, lhs, rhs)
- lhs = VERM:evalValue(e, lhs)
- rhs = VERM:evalValue(e, rhs)
- return lhs % rhs
- end,
- -- ["comma-unlist"] = function(e, ...) end,
- ["`"] = backquoteEval,
- -- ["get-func"] = function(e, ...) end,
- ["'"] = function(e, v)
- return v
- end,
- ["if"] = function(e, cond, v1, v2)
- cond = VERM:evalValue(e, cond)
- if isNIL(cond) then
- return VERM:evalValue(e, v2)
- else
- return VERM:evalValue(e, v1)
- end
- end,
- -- ["set"] = function(e, ...) end,
- -- ["setf"] = function(e, ...) end,
- ["setq"] = function(e, name, value)
- e[name] = VERM:evalValue(e, value)
- end,
- ["lambda"] = lambdaForm,
- ["defun"] = defunForm,
- ["progn"] = prognForm,
- ["defmacro"] = defmacroForm,
- ["do"] = function(e, cond, body)
- local c = VERM:eval(e, cond)
- while not isNIL(c) do
- VERM:eval(e, body)
- c = VERM:eval(e, cond)
- end
- return {}
- end,
- ["car"] = function(e, list)
- list = VERM:eval(e, list)
- return list[1] or {}
- end,
- ["cdr"] = function(e, list)
- list = VERM:eval(e, list)
- local ret = {}
- for i, v in ipairs(list) do
- if i > 1 then
- table.insert(ret, v)
- end
- end
- return ret
- end,
- ["list"] = function(e, ...)
- local ret = {}
- for n=1,select('#',...) do
- local v = VERM:evalValue(e, (select(n,...)))
- table.insert(ret, v)
- end
- return ret
- end,
- ["setq-erm"] = function(e, var, varIndex, value)
- local v = VERM:evalValue(e, value)
- DATA.ERM[var][tostring(VERM:evalValue(e, varIndex))] = v
- return v
- end,
- }
- function VERM:evalValue(e, v)
- if isNIL(v) then
- return v
- elseif type(v) == "table" then
- return self:eval(e, v)
- elseif type(v) == "string" then
- return e[v]
- elseif type(v) == "function" then
- error("evalValue do not accept functions")
- else
- return v
- end
- end
- function VERM:eval(e, t)
- if type(t) ~= "table" then
- logError("Not valid form: ".. to_string(t))
- return {}
- end
- local car = t[1]
- local origCar = car
- if type(car) == "string" then
- car = e[car]
- end
- if type(car) == "table" then
- car = self:eval(e, car)
- end
- if type(car) == "function" then
- return car(e, unpack(t,2))
- else
- logError(to_string(t) .. " is not callable. Car()="..to_string(car))
- logError("Env:"..to_string(e))
- return {}
- end
- end
- function VERM:E(line)
- self:eval(self.topEnv, line)
- end
- VERM.topEnv = createEnv(specialForms)
- return VERM
|