Added more bindings and error tracebacks.

This commit is contained in:
2026-01-14 00:29:34 +00:00
parent 80ac2b7b37
commit 5a3c126100
6 changed files with 464 additions and 99 deletions

191
main.lua
View File

@@ -9,17 +9,23 @@ local function B(abstract,callback)
)
)
end
B("print (text)",function(s,e)
log(s:evaluate(e.text))
local function R(scope,expression,chain,name)
name = name or "?"
assert_meta(Chain)(chain,"Arg 'chain' #1: %s")
local chain = Chain(expression:link(name)):from(chain)
return scope:run(expression,chain)
end
B("print (text)",function(s,e,c)
log(R(s,e.text,c,"print: text"))
end)
B("text (literal)",function(s,e)
B("text (literal)",function(s,e,c)
return e.literal:text()
end)
B("do (expressions)",function(s,e)
B("do (expressions)",function(s,e,c)
--assert_meta(Chain)(chain,"Arg 'chain' #3: %s")
scope = Scope()
scope.parent = s
local res
print(e.expressions)
for item in iterate(e.expressions.items) do
if type_of(item) ~= Expression then
error(
@@ -29,99 +35,122 @@ B("do (expressions)",function(s,e)
)
)
end
res = scope:evaluate(item)
res = R(s,item,c,"do: line")
end
return res
end)
B("if (predicate) then (expression)",function(s,e)
if s:evaluate(e.predicate) then
return s:evaluate(e.expression)
B("error (text)",function(s,e,c)
error("nel: "..R(s,e.text,c,"error: message"))
end)
B("if (predicate) then (expression)",function(s,e,c)
if R(s,e.predicate,c,"if: predicate") then
return R(s,e.expression,c,"if: result")
end
end)
B("true",function(s,e) return true end)
B("false",function(s,e) return false end)
B("(constant) = (expression)",function(s,e)
local value = s:evaluate(e.expression)
B("true",function(s,e,c) return true end)
B("false",function(s,e,c) return false end)
B("(constant) = (expression)",function(s,e,c)
local value = R(s,e.expression,c,"constant: expression")
s:insert(
Binding(e.constant:text(),function(s,e)
Binding(e.constant:text(),function(s,e,c)
return value
end)
)
end)
B("format (string) with (terms)",function(s,e)
B("format (string) with (terms)",function(s,e,c)
local items = {}
for expression in iterate(e.terms.items) do
table.insert(items,s:evaluate(expression))
table.insert(items,R(s,expression),c,"format: term")
end
return string.format(
s:evaluate(e.string),
R(s,e.string,c,"format: string"),
table.unpack(items)
)
end)
B("while (predicate) (expression)",function(s,e)
while s:evaluate(e.predicate) do
s:evaluate(e.expression)
B("while (predicate) (expression)",function(s,e,c)
while R(s,e.predicate,c,"while: predicate") do
R(s,e.expression,c,"while: loop")
end
end)
B("repeat (expression) while (predicate)",function(s,e)
B("repeat (expression) while (predicate)",function(s,e,c)
while true do
s:evaluate(e.expression)
if not s:evaluate(e.predicate) then
R(s,e.expression,c,"repeat: expression")
if not R(s,e.predicate,c,"repeat: predicate") then
break
end
end
end
B("repeat (expression) until (predicate)",function(s,e)
end)
B("repeat (expression) until (predicate)",function(s,e,c)
while true do
s:evaluate(e.expression)
if s:evaluate(e.predicate) then
R(s,e.expression,c,"repeat: expression")
if R(s,e.predicate,c,"repeat: predicate") then
break
end
end
end
B("new table",function(s,e)
end)
B("new table",function(s,e,c)
return {}
end)
B("(index) of (table)",function(s,e)
return s:evaluate(e.table)[s:evaluate(e.index)]
B("(index) of (table)",function(s,e,c)
return (R(s,e.table,"of: index"))[R(s,e.index,c,"of: table")]
end)
B("set (index) of (table) to (item)",function(s,e)
s:evaluate(e.table)[s:evaluate(e.index)]
B("set (index) of (table) to (item)",function(s,e,c)
R(s,e.table,c,"of-to: index")[R(s,e.index,c,"of-to: table")] = s:run(e.item,c,"of-to: item")
end)
B("macro (macro) expands to (expression)",function(s,e)
s:insert(Binding(e.abstract,function(_s,_e)
B("remove (index) of (table)",function(s,e,c)
(R(s,e.table,"remove: table"))[R(s,e.index,c,"remove: index")] = nil
end)
B("nil",function(s,e,c)
return nil
end)
--[[ Note
A macro executes in the same scope of the callee.
A defintion executes in the scope it was defined.
I would argue that macros are more unpredictable!
However, they allow for some very clean code.
--]]
B("macro (macro) expands to (expression)",function(s,e,c)
s:insert(Binding(e.abstract,function(_s,_e,_c)
local scope = Scope()
scope.parent = _s
for identifier,expression in pairs(_e) do
scope:insert(Binding(Expression({identifier}),R(_s,expression,_c)))
end
_s:evaluate(e.expression)
end)
return R(scope,e.expression,_c,"macro: expression")
end))
end)
B("define (abstract) as (expression)",function(s,e)
s:insert(Binding(e.abstract,function(_s,_e)
return s:evaluate(e.expression)
B("define (abstract) as (expression)",function(s,e,c)
s:insert(Binding(e.abstract,function(_s,_e,_c)
local scope = Scope()
scope.parent = s
for identifier,expression in pairs(_e) do
scope:insert(Binding(Expression({identifier}),R(_s,expression,_c)))
end
return R(scope,e.expression,_c,"define: expression")
end))
end)
local List = class("List")
function List:new(...) do
function List:new(...)
self.items = {...}
end
B("new list",function(s,e)
B("new list",function(s,e,c)
return List()
end)
B("insert (item) into (list)",function(s,e)
B("insert (item) into (list)",function(s,e,c)
table.insert(R(s,e.list).items,R(s,e.item),c,c)
end)
B("insert (item) into (list) at (index)",function(s,e)
B("insert (item) into (list) at (index)",function(s,e,c)
table.insert(s:evaulate(e.list).items,R(s,e.index),R(s,item),c,c)
end)
B("remove (needle) from (haystack)",function(s,e)
B("remove (needle) from (haystack)",function(s,e,c)
local tab = R(s,e.haystack,c)
table.remove(tab,table.find(tab,R(s,e.needle)),c)
end)
B("remove from (haystack) at (index)",function(s,e)
s:evaluate(e.haystack)
B("remove from (haystack) at (index)",function(s,e,c)
table.remove(R(s,e.list),R(s,e.index),c,c)
end)
local Variable = class()
local Variable = class("Variable")
function Variable:new()
end
function Variable:set(item)
@@ -130,36 +159,66 @@ end
function Variable:get()
return self.data
end
B("let (variable)",function(s,e)
B("let (variable)",function(s,e,c)
local variable = Variable()
s:insert(
Binding(e.variable,function(s,e)
Binding(e.variable,function(s,e,c)
return variable
end)
)
return variable
end)
B("set (variable) to (expression)",function(s,e)
local var = s:evaluate(e.variable)
local value = s:evaluate(e.expression)
B("set (variable) to (expression)",function(s,e,c)
local var = R(s,e.variable,c,"set: identifier")
local value = R(s,e.expression,c,"set: expression")
assert_meta(Variable)(var)
var:set(value)
end)
B("get (variable)",function(s,e)
local var = s:evaluate(e.variable)
assert_meta(Variable)(var)
B("get (variable)",function(s,e,c)
local var = R(s,e.variable,c)
return var:get(value)
end)
for item in iterate(nelli_scope.bindings) do
print(item.template)
end
local args = {...}
local function main()
-- Take arguments as neli files to read and interpret
for _,parameter in pairs(args) do
local root = interpret(read(parameter))
nelli_scope:evaluate(root)
local last
local root = Chain(
string.format(
"in cmd 'nel {%s}'",
table.concat(args,";")
)
)
local success,result = xpcall(function()
for _,parameter in pairs(args) do
last = parameter
local chain = Chain(
string.format("in 'call: root' @'%s'",parameter)
):from(root)
local root = interpret(
read(parameter),
parameter,
chain
)
assert_meta(Chain)(chain,"?")
nelli_scope:run(root,chain)
end
end,function(msg)
-- If this is a 'nel:' error, strip the line responsible
-- Otherwise just return the whole traceback since something went really bad
local match = msg:match("nel:%s(.*)")
if match then
return match
else
return "internal: "..debug.traceback(msg,2)
end
end)
if not success then
log(string.format(
"nel: %s\nNEL traceback:\n\t%s",
result,
table.concat(root:flatten(),"\n\t")
))
end
end