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(Chain)(chain,"Arg 'chain' #3: %s") assert_meta(Scope)(scope,"Arg 'scope' #1: %s") assert_meta(Expression)(expression,"Arg 'expression' #2: %s") local chain = Chain(expression:link(name)):from(chain) return scope:run(expression,chain) end NelliScope = Scope() local ns = NelliScope -- 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 Bexpands(s,e,c) 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(_s,expanded,_c,"means: "..tostring(expanded)) end) end B(ns,"(abstract) means (expression)",Bexpands) local function Bevaluate(s,e,c) return Binding(e.abstract,function(_s,_e,_c) local scope = Scope() scope.parent = s for name,expression in pairs(_e.items) do scope:insert(Binding( Expression({name}), R(_s,_e,_c,string.format("expands: %s:",name)) )) end return R(scope,e,c,"evaluate: result") end) end B(ns,"(abstract) becomes (expression)",Bevaluates) B(ns,"(identifier) is (expression)",function(s,e,c) if not e.identifier:text(s,e,c) then error("nel: text-only expression expected.") end return Binding(e.identifier,R()) end) local function doAll(s,e,c) local res for item in iterate(e.items) do if type_of(item) ~= Expression then error( string.format( "Unexpected words '%s' in 'do' expression.", tostring(item) ) ) end res = R(s,item,c,"do: line") end return res end -- Note that do actually runs stuff in the same scope it was called -- Otherwise we can't have easily multiple mutations to the parent scope 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 return doAll(s,e.expression,c) end B(ns,"do (expression) in (scope)",Bdo) B(ns,"do (expression)",Bdo) B(ns,"(abstract) broken into (expressions) as do (results)",function(s,e,c) return Binding(e.abstract,function(_s,_e,_c) local params = List() for index,item in pairs(_e) do local expr = Object() expr.identifier = index expr.expression = item table.insert(params,expr) end local expressions = e.expressions:text() if not expressions then error( string.format( "nel: binding: sub expressions: %s not text-only!", e.expressions ) ) end local scope = Scope() scope.parent = s scope:insert(Binding(Expression{expressions},params)) return doAll(s,e.expressions,c) end) end) local _list = {} local function List() return setmetatable(_list,{}) end B(ns,"a new list",function(s,e,c) return List() end) B(ns,"for each (item) in (list) do (expressions)",function(s,e,c) local item = e.item:text() if not property then error( string.format( "nel: for each: item: '%s' not text only!", e.property ) ) end local list = R(s,e.list,c,"for each: list") assert_meta(List)(list,"for each: list: %s") for index,item in ipairs(list) do local scope = Scope() scope.parent = s scope:insert(Binding(Expression{item})) doAll(s,e.expressions,c) end end) B(ns,"add (item) to (list)",function(s,e,c) local item = R(s,e.list,c,"") local list = R(s,e.list,c,"add: list") assert_meta(List)(list,"add: list: %s") table.insert(list,item) end) -- I even quite dislike these, they are prone to gumming things up local _table = {} B(ns,"a new table",function(s,e,c) return {} end) B(ns,"set (index) of (table) to (value)",function(s,e,c) local index = e.index:text() if not index then index = R(s,e.index,c,"set: index") end local tab = R(s,e.table,c,"set: table") local val = R(s,e.value,c,"set: value") tab[index] = val end) B(ns,"get (index) of (table)",function(s,e,c) local index = e.index:text() if not index then index = R(s,e.index,c,"get: index") end local tab = R(s,e.table,c,"get: table") return tab[index] end) local _object = {} local function Object() return setmetatable(_object,{}) end B(ns,"a new object",function(s,e,c) return Object() end) B(ns,"(property) of (object) is (value)",function(s,e,c) local property = e.property:text() if not property then error( string.format( "nel: property is: property: '%s' not text only!", e.property ) ) end local object = R(s,e.object,c,"property is: object") assert_meta(Object)(object,"nel: property is: object: %s") local value = R(s,e.value,c,"property is: value") object[property] = value end) B(ns,"(property) of (object)",function(s,e,c) local property = e.property:text() if not property then error( string.format( "nel: property is: property: '%s' not text only!", e.property ) ) end local object = R(s,e.object,c,"property is: object") assert_meta(Object)(object,"nel: property is: object: %s") return object[property] end) B(ns,"error (text)",function(s,e,c) error("nel: "..R(s,e.text,c,"error: message")) end) B(ns,"this scope",function(s,e,c) return s end) B(ns,"a new scope",function(s,e,c) return Scope() end) -- Read a file in the current directly, more for compiling B(ns,"read file (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,"evaluate (text) in (scope)",function(s,e,c) local scope = R(s,e.scope,c,"evaluate: scope") return R(s,interpret(R(scope,e.text,c,"include: source")),c,"include: result") end) 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) B(ns,"return (expression)",function(s,e,c) return R(s,e.expression,c,"return: result") end) B(ns,"(macro) means (expansion)",function(s,e,c) end) B(ns,"(identifier) is (value)",function(s,e,c) end) B(ns,"(expression) becomes (result)",function(s,e,c) end) B(ns,"let (binding) in (scope)",function(s,e,c) end) local Hpath = "nel/helper.nel" local Hchain = Chain("in internal: "..Hpath) local success,result = Hchain:call(function(chain) R(NelliScope,interpret(read(Hpath),Hpath,chain),chain) end) --[[ Documentation: (a new scope) -> Becomes a created new scope (this scope) -> Becomes the current scope (macro) expands to (expansion) -> Becomes a binding which makes () become whatever (result) will become in place (expression) --]] return ns