From e2ad04b692cbb455d2613b279e513b2b61493659 Mon Sep 17 00:00:00 2001 From: Christian Lincoln Date: Sat, 14 Feb 2026 23:35:41 +0000 Subject: [PATCH] Still working on standards. --- README.md | 2 +- archive/nelli.lua | 327 ++++++++++++++++++++++++++++++++++++++ note => archive/note0.txt | 0 archive/note1.txt | 159 ++++++++++++++++++ archive/note2.txt | 19 +++ helper.lua | 2 +- interpret.lua | 42 ++++- main.lua | 222 ++------------------------ nel/helper.nel | 23 +++ nel/lc.nel | 14 ++ nel/translator/cxx.nel | 3 + nel/translator/main.nel | 14 ++ nel/ui.nel | 6 + nelli | 9 ++ nelli.lua | 299 ++++++++++++++++++++++++++++++++++ nex.nel | 6 + parse.lua | 129 +++++++++++++-- test.nel | 4 +- 18 files changed, 1046 insertions(+), 234 deletions(-) create mode 100644 archive/nelli.lua rename note => archive/note0.txt (100%) create mode 100644 archive/note1.txt create mode 100644 archive/note2.txt create mode 100644 nel/helper.nel create mode 100644 nel/lc.nel create mode 100644 nel/translator/cxx.nel create mode 100644 nel/translator/main.nel create mode 100644 nel/ui.nel create mode 100755 nelli create mode 100644 nelli.lua create mode 100644 nex.nel diff --git a/README.md b/README.md index 316c443..6c996ff 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ # neli -A silly language for silly people to understand smart things. \ No newline at end of file +A silly language to help silly people understand silly things. diff --git a/archive/nelli.lua b/archive/nelli.lua new file mode 100644 index 0000000..f326803 --- /dev/null +++ b/archive/nelli.lua @@ -0,0 +1,327 @@ +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 diff --git a/note b/archive/note0.txt similarity index 100% rename from note rename to archive/note0.txt diff --git a/archive/note1.txt b/archive/note1.txt new file mode 100644 index 0000000..5c63593 --- /dev/null +++ b/archive/note1.txt @@ -0,0 +1,159 @@ + +taking expressions and parsing them later down? +that seems slightly stupid. +really, an expression is sort of a tree that +hasn't been dealt with yet. +is it really wise to keep passing them down as +just that expression? +otherwise it would have to be evaluated in the later +scope. +it makes more sense to sort of evaluate things +as we go along. +at the very least, every expression will always end +up being 'something'. +it just means that we need to execute different +expressions in different contexts +i guess. +so 'meaning' becomes slightly more interesting here. +i guess this would be where the macros fit. +we could use a macro, which literally replaces +a particular expression with another one, somehow, or +maybe pass in some other sort of context to evaluate +these things under. but that would evaluate everything +that way. +it would be nice if we could somehow refer to an +expression as what it is expressed as, but also +what it will be. + +i mean: let (render (elements)) mean (... do stuff ...) +is great and all because i can now do: +render ( + box() +) +but then how about +let (view) mean (box()) +and then render(view) + +that clearly does not work, but absolutely should be +valid nel code. + +with equal pertinence, on the other hand we have: + +let (sum) mean (math (5 + 2)) +where math ()... + +hold on. i think i know why this is actually confusing +me. it also connects quite well with how source code +works. + +'mean' means 'copy the expression in place' +somehow preserving the scope. +but +'be' means 'the literal value we want' + +which is, definitely rather odd. very odd. +how is each one useful in their own right? + +i think, my issue is that im really struggling +to see the direct separation between what we have +defined in nel and what the compiled bits should be! + +code { + (number) means (int) + let (x) is a (number) + let (y) is a (number) + let (sum) is a (number), is (x + y) +} + +i mean its weird. very weird. +since the 'let' bits should really be interpreted by +the 'code' expression to represent some local variables +in source code! +but then the 'means' bit quite literally breaks that idea +in two because that isn't source code at all it's just +doing its own thing really. +we ideally want 'means' to be available everywhere? +are there any situations where it is bad? +and again, what about macros? +so maybe back to html as a weird example + +ui { + + box() +} + +i think i get it now. +when you use 'means' you are creating a new binding in +the scope that forces NEL to create a 'value' which +is more a wrapper (or even a trap!) that calls that +particular expression again in the parent scope. +this IS that macro, and i think it is quite deadly. +which makes me wonder, is this a good idea to have +around? well, i'm not sure! + +in a lot of cases, this is actually very nice, because +we can literally just functionally expand our ENTIRE +program out as we please without any function overhead. +note that this is bad for maintainability and code bloat. +we also still have the power of a general routine, so this +isn't all doom and gloom really. + +how weird. +i think i am sort of ok knowing this now, even though +i am now very much more confused than even before. + +however, this turns out to be very nice. + +so: +(doeth (code)) means (do (code)) + +right, crap. effectively here that entire idea doesn't +really hold water simply because i need the candid +expression to be able to execute the nellie effectively. +that's not entirely great is it now. + +'means' and 'do' operations themselves work over abstract +nel anyways. this sum foolery indeed. +so effectively, abstract nel doesnt like abstract nel, +yes yes yes that makes complete sense actually. +because with doubly abstract nel we cannot just say +(action) means (print (text (fart))) +and then literally +do { + action; + action; + action; +} +or even worse have some sort of multiplicative expression. +it's just not feasible you see! +however, i now think i have a sort of idea. +you see, with bindings, we can have values + +i have been thinking for a while and i am not sure if +i can easily find some sort of solution to all of this. +it's quite concerning really. + +do {} +represents a list of expressions, how can we define this, +just in pure NEL, to execute each statement. +because, and i need to keep saying it, NEL should compile +NEL, no? +if EVERYTHING is written in NEL, then i surely should be +allowed to write nel that can interpret expressions this +way. it just feels incredibly stupid to not be able to. +like what? +all while being simple to understand. completely insane. +COMPLETELY, insane. + +unless... just about unless. +all expressions were functions. +that promised to give back an expression. + +i realise now that my mind is very very weak. +there is something missing from it. it's not about all the +puzzles. it seems definition is trivial to everything. +but this algorithm seems to me like a knot. +its not a knot, i know there is a very clean solution, +i just cant see it right now. +i need to actually break things down into what i want first +so i can see things as clearly as i possibly can diff --git a/archive/note2.txt b/archive/note2.txt new file mode 100644 index 0000000..9d87690 --- /dev/null +++ b/archive/note2.txt @@ -0,0 +1,19 @@ +// My name is 'pal' +/* Some sorcery for a query? */ + +// There is an object which is me +// There is an object 'pal' (how pronounce?) +// I guess a 'written as' branch + +/* Notes: +Queries can create queries (are they called 'queries'?) +They specify a general scenario. +Can they have constants? Are those constants branches? +*/ + +// wtf ^ + +/* From DMLR +there was self referencing logic. +quantifiers over general objects +*/ diff --git a/helper.lua b/helper.lua index 1ea4e70..f93d9c3 100644 --- a/helper.lua +++ b/helper.lua @@ -35,7 +35,7 @@ end -- Iterate (ipairs without the index) function iterate(...) - local iterator,tab,i = pairs(...) + local iterator,tab,i = ipairs(...) return function(...) local index,item = iterator(tab,i) i = index diff --git a/interpret.lua b/interpret.lua index 7e26cca..5218aa3 100644 --- a/interpret.lua +++ b/interpret.lua @@ -84,12 +84,38 @@ function interpret(content,uri,chain) current:insert(words,consumeExpression()) end end + local singleLineCommentPattern = "//" + local function singleLineCommentMeal(current) + return function(words,_) + current:insert(words) -- Consume what was left + consumer:consume({ + ["[\n\r]"] = function() end + }) + end + end + local multiLineCommentPattern = "/%*" + local function multiLineCommentMeal(current) + return function(words,_) -- consume what was left + current:insert(words) + consumer:consume({ + ["%*/"] = function() end + }) + end + end + local colonSyntaxPattern = ":" + local function colonSyntaxMeal(current) + return function(words,_) + current:insert(words,consumeColonExpression()) + end + end function consumeBlock() local expressions = {} local current = expression() local loop = true while loop do local expr = consumer:consume({ + [multiLineCommentPattern] = multiLineCommentMeal(current), + [singleLineCommentPattern] = singleLineCommentMeal(current), [uriOpenPattern] = URIMeal(current,uriClosePattern), [expressionOpenPattern] = expressionMeal(current), [multiLineStringOpenPattern] = @@ -99,16 +125,24 @@ function interpret(content,uri,chain) singleLineStringMeal( current,singleLineStringPattern), [blockDelimiterPattern] = function(words,_) + current:insert(words) if current:empty() then - error("Extravenous semicolon.") + --error("Extravenous semicolon.") + else + table.insert(expressions,current) end - table.insert(expressions,current) current = expression() end, [blockClosePattern] = function(words,_) current:insert(words) loop = false - end + end, + [blockOpenPattern] = function(words,_) + current:insert( + words, + consumeBlock() + ) + end, }) end if #current.items ~= 0 then @@ -124,6 +158,8 @@ function interpret(content,uri,chain) while loop do local remaining = consumer:remaining() local expr = consumer:consume({ + [multiLineCommentPattern] = multiLineCommentMeal(current), + [singleLineCommentPattern] = singleLineCommentMeal(current), [uriOpenPattern] = URIMeal(current,uriClosePattern), [expressionOpenPattern] = expressionMeal(current), [multiLineStringOpenPattern] = diff --git a/main.lua b/main.lua index 6e7baae..e9e224f 100644 --- a/main.lua +++ b/main.lua @@ -1,225 +1,27 @@ -require("interpret") +require("nelli") -local nelli_scope = Scope() -local function B(abstract,callback) - nelli_scope:insert( - Binding( - interpret(abstract), - callback - ) - ) -end -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,c) - return e.literal:text() -end) -B("do (expressions)",function(s,e,c) - --assert_meta(Chain)(chain,"Arg 'chain' #3: %s") - scope = Scope() - scope.parent = s - 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(s,item,c,"do: line") - end - return res -end) -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,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,c) - return value - end) - ) -end) -B("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("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,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("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("new table",function(s,e,c) - return {} -end) -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,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("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 - return R(scope,e.expression,_c,"macro: expression") - end)) -end) -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(...) - self.items = {...} -end -B("new list",function(s,e,c) - return List() -end) -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,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,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,c) - table.remove(R(s,e.list),R(s,e.index),c,c) -end) - -local Variable = class("Variable") -function Variable:new() -end -function Variable:set(item) - self.data = item -end -function Variable:get() - return self.data -end -B("let (variable)",function(s,e,c) - local variable = Variable() - s:insert( - Binding(e.variable,function(s,e,c) - return variable - end) - ) - return variable -end) -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,c) - local var = R(s,e.variable,c) - return var:get(value) -end) - -local args = {...} -local function main() +local function main(args) -- Take arguments as neli files to read and interpret - local last local root = Chain( string.format( - "in cmd 'nel {%s}'", + "in cmd 'nelli {%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) + for _,parameter in pairs(args) do + local chain = Chain( + string.format("in 'call: root' @'%s'",parameter) + ):from(root) + root:call(function() 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") - )) + NelliScope:run(root,chain) + end) end end -main() +-- A list of arguments passed into this +main({...}) diff --git a/nel/helper.nel b/nel/helper.nel new file mode 100644 index 0000000..7fd556e --- /dev/null +++ b/nel/helper.nel @@ -0,0 +1,23 @@ +do { + let { + ( here (abstract) expands to (expression) ) + expands to + ( let { (abstract) expands to (expression) } in (this scope) ); + + (here (abstract) evaluates to (expression)) + expands to + (let {(abstract) evaluates to (expression)} in (this scope)); + + ( here (abstract) is equal to (expression) ) + expands to + ( let {(abstract) is equal to (expression)} in (this scope) ); + + (module (the path)) + expands to + (do { + here (the scope) is (a new scope); + evaluate (read file (the path)) in (the scope); + return (the scope); + }); + }; +} diff --git a/nel/lc.nel b/nel/lc.nel new file mode 100644 index 0000000..9b67ca4 --- /dev/null +++ b/nel/lc.nel @@ -0,0 +1,14 @@ +do { + // Scope for the language that we will write in NEL + here (I scope) is (new scope); // I -> NEL Intermediate Code + + // Compile (...) should return the compiled text + here (compile ('code')) + expands to + (do {'code'} in (I scope)); + + // Definitions for things in the I scope + let { + + } in (I scope) +} diff --git a/nel/translator/cxx.nel b/nel/translator/cxx.nel new file mode 100644 index 0000000..c2cc1d2 --- /dev/null +++ b/nel/translator/cxx.nel @@ -0,0 +1,3 @@ +do { + +} in ((module ("nel/translator/main.nel")) diff --git a/nel/translator/main.nel b/nel/translator/main.nel new file mode 100644 index 0000000..5123428 --- /dev/null +++ b/nel/translator/main.nel @@ -0,0 +1,14 @@ +do { + here (lang scope) is (new scope); + + let { + here ( call (identifier) (values) ) evaluates to ( + here (source) is + ) + here ( text (literal) ) evaluates to ( text (literal) ) + here ( print (values) ) expands to ( call (print) (values) ) + } in (lang scope) + here (compile (code)) + expands to + (do (code) in (lang scope)); +} diff --git a/nel/ui.nel b/nel/ui.nel new file mode 100644 index 0000000..efddfa6 --- /dev/null +++ b/nel/ui.nel @@ -0,0 +1,6 @@ +do { + here ((fart) means (print "fart")); + fart; + fart; + fart; +} diff --git a/nelli b/nelli new file mode 100755 index 0000000..3f7917e --- /dev/null +++ b/nelli @@ -0,0 +1,9 @@ +#/bin/sh +if ! command -v lua &> /dev/null +then + echo "lua could not be found" + exit 1 +else + lua main.lua "$@" +fi + diff --git a/nelli.lua b/nelli.lua new file mode 100644 index 0000000..80369bf --- /dev/null +++ b/nelli.lua @@ -0,0 +1,299 @@ +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 diff --git a/nex.nel b/nex.nel new file mode 100644 index 0000000..d7c4018 --- /dev/null +++ b/nex.nel @@ -0,0 +1,6 @@ +do { + register query { + /* Some sorcery for a query? */ + + } +} diff --git a/parse.lua b/parse.lua index f5ac7bb..a009f9d 100644 --- a/parse.lua +++ b/parse.lua @@ -64,10 +64,18 @@ function Expression:new(items) self:insert(item) end end +-- Return the iterator through the expression +function Expression:iterate() + return iterate(self.items) +end +-- Is the expression empty? function Expression:empty() return #self.items == 0 end --- insert a word or sub expression into this one +--[[ +Insert a word or sub expression into this one +Note: The expression has a .items = {} list. +]] function Expression:insert(...) -- closures for nvim highlighting local function insert_word(word) @@ -83,15 +91,19 @@ function Expression:insert(...) end for item in iterate({...}) do case_of({ - table = function(expr) + [Expression] = function(expr) insert_expression(expr) end, string = function(word) insert_word(word) end - })(type(item),item) + })(type_of(item),item) end end +--[[ +Converts an expression to meaningful text format. +This should be reparseable. +]] function Expression:string() local parts = {} for index,item in pairs(self.items) do @@ -99,6 +111,30 @@ function Expression:string() end return string.format("(%s)",table.concat(parts," ")) end +--[[ +Similarly to abstract, returns this expression +with subexpression:reduce(depth-1) +Useful for more informative debugging +]] +function Expression:reduce(depth) + local parts = {} + for index,item in pairs(self.items) do + parts[index] = case_of({ + [Expression] = function(item) + if depth == 0 then + return Expression({"..."}) + else + return item:reduce(depth - 1) + end + end, + string = id + })(type_of(item),item) + end +end +--[[ +Returns this expression with all sub expressions +simplified to text by :text() or '...' +]] function Expression:abstract(infer) local parts = {} local count = 0 @@ -122,6 +158,10 @@ function Expression:abstract(infer) end return Expression(parts) end +--[[ +Return the single 'words' part of this expression +if it is alone. Otherwise return nil. +]] function Expression:text() -- Get the text local text = self.items[#self.items] @@ -129,21 +169,32 @@ function Expression:text() if type(text) ~= "string" then return end return text end +--[[ +Give this expression a meaningful debug location +referring to some source code. +]] function Expression:locate(str,uri) self.location = str self.source = uri return self end +--[[ +Return the meaningful debug location as a string msg. +]] function Expression:link(msg) return string.format( "in '%s' @%s (%s)", msg, - tostring(self), + tostring(self:abstract()), tostring(self.location or "?"), tostring(self.source or "?.nel") ) end +--[[ +A class which parses text using the 'consumer-meal' +model. +]] Consumer = class("Consumer") function Consumer:new(content) self.range = Range(Reader(content)) @@ -226,13 +277,18 @@ function Binding:string() return string.format("Binding %s: %s",self.template,tostring(self.value or self.callback)) end function Binding:call(s,e,c) - assert_meta(Scope)(s) - assert_meta(Chain)(c) + for item in iterate(e) do + assert_meta(Expression)(item) + end + assert_meta(Scope)(s,"Arg 'scope' #1: %s") + assert_meta(Chain)(c,"Arg 'chain' #3: %s") return self.value or self.callback(s,e,c) end -function Binding:check(expression) +-- Expecting an abstract expression! +function Binding:check(expression,candid) assert_meta(Expression)(expression,"Arg 'expression' #1: %s") - local candid = expression:abstract() + candid = candid or expression:abstract() + assert_meta(Expression)(candid,"Arg 'abstract' #2: %s") local data = {} local index local loop = true @@ -249,7 +305,18 @@ function Binding:check(expression) if type_of(candid_item) ~= Expression then loop = false else - data[template_item:text()] = expression.items[index] + local text = template_item:text() + if not text then + error( + string.format( + [[There was a subexpression without + just text in this expression: %s]], + tostring(candid) + ) + ) + end + data[text] = expression.items[index] + assert_meta(Expression)(expression.items[index]) end end, string = function() @@ -291,6 +358,31 @@ function Chain:flatten(tab) table.insert(tab,self.data) return tab end +function Chain:pcall(func) + local success,result = xpcall(func,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,self) + if not success then + log(string.format( + "nel: %s\nNEL traceback:\n\t%s", + result, + table.concat(self:flatten(),"\n\t") + )) + end + return successs,result +end +function Chain:call(func) + local success,result = self:pcall(func) + if not success then error("Chain: Halt!") end + return result +end -- The scope of a neli expression under evaluation -- without exact faith to traditional language scopes @@ -308,7 +400,7 @@ function Scope:insert(binding) assert_meta(Binding)(binding,"Arg 'binding' #1: %s") for competitor in iterate(self.bindings) do if competitor:check(binding.template) then - nerror("Conflicting sibling bindings in scope.") + error(string.format("nel: Conflicting sibling bindings in scope for '%s'.",binding.template)) end end table.insert(self.bindings,binding) @@ -317,10 +409,12 @@ function Scope:run(expression,chain,original) original = original or self assert_meta(Expression)(expression,"Arg 'expression' #1: %s") assert_meta(Chain)(chain,"Arg 'chain' #2: %s") + assert_meta(Scope,original,"Arg 'scope' #3: %s") -- Could be multiple bindings so make a table local binds = {} + local abstract = expression:abstract() for binding in iterate(self.bindings) do - local bind = binding:check(expression) + local bind = binding:check(expression,abstract) -- Insert into table if bind then binds[binding] = bind end end @@ -328,21 +422,21 @@ function Scope:run(expression,chain,original) -- Check for multiple if not bind then if self.parent then - return self.parent:run(expression,chain,self) + return self.parent:run(expression,chain,original) else local extra = "" if #expression.items == 1 then local item = expression.items[1] if type_of(item) == Expression then - extra = extra .. "\nNote: This expression is formed (just bracketed) of another single expression. Maybe too many brackets were used here?" + extra = extra .. "\nNote: This expression is formed (just bracketed) of another single expression. Maybe too many brackets were used here? Are you using curly braces?" else - extra = extra .. "\n\tNote: This is a 'text' expression, it has no subexpressions. It is most likely that an expected object definition was not bound to the scope or there is a typo." + extra = extra .. "\n\tNote: This is a 'word only' expression, it has no subexpressions. It is most likely that an expected object definition was not bound to the scope or there is a typo." end end error( string.format( - "nel: No binding for '%s' in scope.", - tostring(expression:abstract()), + "nel: No binding for '%s' in scope.%s", + tostring(expression), extra ),3 ) @@ -361,6 +455,9 @@ function Scope:run(expression,chain,original) ) ,2) else + for index,item in pairs(binds) do + print("index:",index,"item:",item) + end return binding:call( original, bind, diff --git a/test.nel b/test.nel index c2156ab..6ed8941 100644 --- a/test.nel +++ b/test.nel @@ -1,5 +1,3 @@ do { - print "large balls"; - define (say (text)) as (print (text)); - say "shit"; + print "wsg"; }