Files
nel/archive/nelli.lua

328 lines
8.6 KiB
Lua

require("interpret")
function B(scope,abstract,callback)
scope:insert(
Binding(
interpret(abstract),
callback
)
)
end
function R(scope,expression,chain,name)
name = name or "?"
assert_meta(Expression)(expression,"Arg 'expression' #2: %s")
assert_meta(Chain)(chain,"Arg 'chain' #3: %s")
local chain = Chain(expression:link(name)):from(chain)
return scope:run(expression,chain,scope)
end
NelliScope = Scope()
local ns = NelliScope
B(ns,"print (text)",function(s,e,c)
log(R(s,e.text,c,"print: text"))
end)
B(ns,"text (literal)",function(s,e,c)
return e.literal:text()
end)
-- Replace all :text() occurrences with ones in 'exp_map'
local function substitute(target,exp_map)
local text = target:text()
if text then -- Replace the parameter
local map = exp_map[text]
if map then
return Expression({map})
else
return target
end
else
local new = Expression()
for sub in iterate(target.items) do
local t = type_of(sub)
if t == "string" then
new:insert(sub)
elseif t == Expression then
new:insert(substitute(sub,exp_map))
end
end
return new
end
end
-- Iterate over all :text() occurrences
local function traverse(expression,callback)
for sub in iterate(expression.items) do
if type_of("sub") == Expression then
traverse(sub,callback)
else
callback(sub)
end
end
end
local function Bmeans(s,e,c)
local scope
if e.scope then
scope = R(s,e.scope,c,"means: using: scope: %s")
end
local bound = {}
for sub in iterate(e.abstract.items) do
local t = type_of(sub)
if t == Expression then
local text = sub:text()
if not text:match("'.+'") then
--[[error(string.format(
"nel: Abstract parameter \"%s\" not in 'quotes'.",
text
))]]
else
table.insert(bound,text)
end
end
end
return Binding(e.abstract,function(_s,_e,_c)
local map = {}
for binding in iterate(bound) do
map[binding] = _e[binding]
end
local expanded = substitute(e.expression,map)
return R(scope or _s,expanded,_c,"means: "..tostring(expanded))
end)
end
B(ns,"(abstract) means (expression) using (scope)",Bmeans)
local function Bdo(s,e,c)
local scope
if e.scope then
scope = R(s,e.scope,c,"do: scope")
else
scope = Scope()
scope.parent = s
end
local res
for item in iterate(e.expressions.items) do
if type_of(item) ~= Expression then
error(
string.format(
"Unexpected words '%s' in 'do' expression.",
tostring(item)
)
)
end
res = R(scope,item,c,"do: line")
end
return res
end
B(ns,"do (expressions)",Bdo)
B(ns,"do (expression) in (scope)",function(s,e,c)
local scope = R(s,e.scope,c,"do: scope")
assert_meta(Scope)(scope,"nel: (do: scope): %s")
R(scope,e.expression,c,"do: expression")
end)
B(ns,"error (text)",function(s,e,c)
error("nel: "..R(s,e.text,c,"error: message"))
end)
B(ns,"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(ns,"true",function(s,e,c) return true end)
B(ns,"false",function(s,e,c) return false end)
B(ns,"constant (constant) = (expression)",function(s,e,c)
local value = R(s,e.expression,c,"constant: expression")
e.constant:text()
s:insert(
Binding(e.constant,function(s,e,c)
return value
end)
)
end)
B(ns,"format (string) with (terms)",function(s,e,c)
local items = {}
for expression in iterate(e.terms.items) do
table.insert(items,R(s,expression),c,"format: term")
end
return string.format(
R(s,e.string,c,"format: string"),
table.unpack(items)
)
end)
B(ns,"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(ns,"repeat (expression) while (predicate)",function(s,e,c)
while true do
R(s,e.expression,c,"repeat: expression")
if not R(s,e.predicate,c,"repeat: predicate") then
break
end
end
end)
B(ns,"repeat (expression) until (predicate)",function(s,e,c)
while true do
R(s,e.expression,c,"repeat: expression")
if R(s,e.predicate,c,"repeat: predicate") then
break
end
end
end)
B(ns,"new table",function(s,e,c)
return {}
end)
B(ns,"(index) of (table)",function(s,e,c)
return (R(s,e.table,"of: index"))[R(s,e.index,c,"of: table")]
end)
B(ns,"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(ns,"remove (index) of (table)",function(s,e,c)
(R(s,e.table,"remove: table"))[R(s,e.index,c,"remove: index")] = nil
end)
B(ns,"nothing",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(ns,"here",function(s,e,c)
return s
end)
B(ns,"return (object)",function(s,e,c)
return R(s,e.object,c,"return: object")
end)
B(ns,"this scope",function(s,e,c)
return s
end)
B(ns,"in scope (scope) (expressions)",function(s,e,c)
return R(R(s,e.scope,c,"in scope: scope"),e.scope,c,"in scope: expression")
end)
local function Bdef(s,e,c)
if e.scope then
s = R(s,e.scope,c,"define: scope")
end
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,string.format(
"define: param: \"%s\"",
identifier
))
)
)
end
return R(scope,e.expression,_c,string.format(
"define: expression:\n\t\t%s\n\t",
table.concat(c:flatten(),"\n\t")
))
end))
end
B(ns,"define (abstract) as (expression)",Bdef)
B(ns,"define (abstract) as (expression) in (scope)",Bdef)
-- Create a constant binding
B(ns,"(abstract) is (expression)",function(s,e,c)
local result = R(s,e.expression,c,"be: expression")
return Binding(e.abstract,result)
end)
-- Insert a binding into the current scope
B(ns,"here (bindings)",function(s,e,c)
for meaning in e.binding:iterate() do
for sub in e.binding:iterate() do
local t = type_of(sub)
if t ~= Expression then
print(t,getmetatable(t))
error(string.format(
"nel: (here: bindings): %s%s\n\t%s",
"Expected subexpressions only got: ",
tostring(sub),
"Note: Are you using '{...}' style brackets?"
))
else
local bind = R(s,sub,c,"here: binding")
assert_meta(Binding)(bind,"nel: (here: binding): %s")
s:insert(bind)
end
end
end
end)
-- Insert a binding into a scope
B(ns,"in (scope) let (binding)",function(s,e,c)
assert_meta(Binding)(e.binding,"nel: (here: binding): %s")
assert_meta(Scope)(e.scope,"nel: (here: scope): %s")
R(s,e.scope,c,"let: scope"):insert(R(s,e.binding,c,"let: binding"))
end)
B(ns,[[
define (abstract)
as (expression)
in (scope)
using (parameter_scope)
]],Bdef)
local List = class("List")
function List:new(...)
self.items = {...}
end
B(ns,"new list",function(s,e,c)
return List()
end)
B(ns,"insert (item) into (list)",function(s,e,c)
table.insert(R(s,e.list).items,R(s,e.item),c,c)
end)
B(ns,"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(ns,"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(ns,"remove from (haystack) at (index)",function(s,e,c)
table.remove(R(s,e.list),R(s,e.index),c,c)
end)
-- Create a scope parented to the current scope as an object
B(ns,"new scope here",function(s,e,c)
local scope = Scope
scope.parent = s
return scope
end)
-- Create a scope with nothing in it as an object (weird)
B(ns,"new scope",function(s,e,c)
return Scope()
end)
-- Read a file in the current directly, more for compiling
B(ns,"read (path)",function(s,e,c)
return read(R(s,e.path,c,"read: path"))
end)
-- Take some text and interpret it into an expression
B(ns,"include (text)",function(s,e,c)
return R(s,interpret(R(s,e.text,c,"include: source")),c,"include: result")
end)
interpret([[
here {
(here ('name') is ('value'))
means
(here {
('name') is ('value')
});
(here ('abstract') means ('expression'))
means
(here {
('abstract') means ('expression')
});
(here ('abstract') means ('expression') using ('scope'))
means
(here {
('abstract') means ('expression')
} using ('scope'))
}
]])
return ns