From 7aa282b4057457f7f74ba3a0deb536a97ef7431f Mon Sep 17 00:00:00 2001 From: Christian Lincoln Date: Sun, 29 Mar 2026 11:46:50 +0100 Subject: [PATCH] Sync --- nellie/helper.lua | 6 ++ nellie/helper.nel | 11 ++- nellie/init.lua | 6 +- nellie/interpreter.lua | 3 - nellie/parser.lua | 213 ++++++++++++++++++++++++++--------------- nellie/proto.lua | 30 +++--- run.sh | 2 +- tests/test.lua | 6 +- 8 files changed, 175 insertions(+), 102 deletions(-) diff --git a/nellie/helper.lua b/nellie/helper.lua index a3376e1..ac81f53 100644 --- a/nellie/helper.lua +++ b/nellie/helper.lua @@ -66,6 +66,12 @@ function indices(t) return indices end +function map(t,func) + for index,item in pairs(t) do + t[index] = func(index,item) + end +end + -- Ensures a table has a metatable function ensure_metatable(t) local meta = getmetatable(t) diff --git a/nellie/helper.nel b/nellie/helper.nel index eacd290..1372552 100644 --- a/nellie/helper.nel +++ b/nellie/helper.nel @@ -1,3 +1,8 @@ -out "huh?"; -// proto: -(FUCK!) \ No newline at end of file +// single line comment +log "a"; +/* +multi +line +comment +*/ +log "b"; \ No newline at end of file diff --git a/nellie/init.lua b/nellie/init.lua index 03ef478..4cdce20 100644 --- a/nellie/init.lua +++ b/nellie/init.lua @@ -1,15 +1,15 @@ local path = ... require("nellie.proto") -function Run(args) +function Run(...) -- Take arguments as neli files to read and interpret local root = Chain( string.format( "in cmd 'nelli {%s}'", - table.concat(args,";") + table.concat({...},";") ) ) - for _,parameter in pairs(args) do + for _,parameter in pairs({...}) do local chain = Chain( string.format("in 'call: root' @'%s'",parameter) ):from(root) diff --git a/nellie/interpreter.lua b/nellie/interpreter.lua index 7dc5788..9c8c43a 100644 --- a/nellie/interpreter.lua +++ b/nellie/interpreter.lua @@ -339,9 +339,6 @@ function Scope:run(expression,chain,original) ) ,2) else - for index,item in pairs(binds) do - print("index:",index,"item:",item) - end if not binding then error("No binding") end -- todo: improve error return binding:call( original, diff --git a/nellie/parser.lua b/nellie/parser.lua index 2afa862..d00c90c 100644 --- a/nellie/parser.lua +++ b/nellie/parser.lua @@ -72,6 +72,7 @@ end -- From a table of actions of patterns, -- consume the earliest pattern (index) -- and call the value +local NONE = {} function Consumer:consumePatterns(patterns,state) -- t: {string = function...} assert_type("table")(patterns,"Arg 't' #1: %s") @@ -84,31 +85,37 @@ function Consumer:consumePatterns(patterns,state) end local origin = self.range.first or 1 -- Get the next earliest match - local findex,ffinal,fpattern,ffunc,fexcess -- TODO: make this not be a bunch of locals. New class? + local callback = nil + local earliest = {} + --local findex,ffinal,fpattern,ffunc,fexcess -- TODO: make this not be a bunch of locals. New class? for pattern,new_func in pairs(patterns) do local new_index,new_final = self.range:find(pattern) if new_index and - ((not findex) or (findex > new_index)) + ((not earliest.index) or (earliest.index > new_index)) then - if not new_index then return end - findex = new_index - ffinal = new_final - fpattern = pattern - ffunc = new_func - fexcess = self.range.reader:get( + if not new_index then return end -- (later:) what why? + earliest.index = new_index + earliest.final = new_final + earliest.pattern = pattern + callback = new_func + earliest.excess = self.range.reader:get( self.range.first or 1, - new_final - 1 + earliest.index - 1 ) end end -- Pass into the func - if not ffunc then + if not callback then + if patterns[NONE] then + return patterns[NONE]() + end return nil end - assert(findex) assert(ffinal) - self.range:move(ffinal+1) -- Move range to after the match - local fmatch = self.range.reader:match(fpattern,findex) - -- Pass back consumed result to + assert(earliest.index) assert(earliest.final) + self.range:move(earliest.final+1) -- Move range to after the match + local fmatch = self.range.reader:match(earliest.pattern,earliest.final) + + -- This seems to be broken: local sum = self.range.reader:get(origin+1,self.range.first) :gsub("\r\n","\n") :gsub("\r","\n") @@ -120,7 +127,7 @@ function Consumer:consumePatterns(patterns,state) self.col = self.col + 1 end end - return ffunc(fexcess,fmatch,state) + return callback(earliest,state) end -- Interpret some text @@ -164,67 +171,119 @@ function Parse(content,uri,chain) patterns.uriClose = ">" patterns.singleLineComment = "//" patterns.multiLineCommentOpen = "/%*" - patterns.multiLineCommentOpen = "%*/" + patterns.multiLineCommentClose = "%*/" patterns.colonSyntax = ":" - -- TODO: I don't like that this structure does: open(stuff;close) instead of open(stuff)close - -- TODO: current:insert() is repeated in EVERY meal. I wonder if this should be fixed -__- - local expressionMeals = { - [patterns.singleLineString] = function(words,_,current) - current:insert(words,consumer:consumePatterns({ - [patterns.singleLineString] = function(words,_) - return expression({"text",Expression({words})}) - end - })) - end, - [patterns.named] = function(words,_,current) - current:insert(words,consumer:consumePatterns({ - [patterns.named] = function(words,_) - return expression({"the",Expression({words})}) - end - })) - end, - [patterns.multiLineStringOpen] = function(words,_,current) - current:insert(words,consumer:consumePatterns({ - ["[\n\r]"] = function() - error("Incomplete string literal") - end, - [patterns.multiLineStringClose] = function(words,_) - return expression({"text",Expression({words})}) - end - })) - end, - [patterns.uriOpen] = function(words,_,current) - current:insert(words,consumer:consumePatterns({ - ["[\n\r]"] = function() - current:error("Incomplete URI literal") - end, - [patterns.uriClose] = function(path) - return read(path) - end - })) - end, - [patterns.expressionOpen] = function(words,_,current) - current:insert(words,consumeExpression()) - end, - [patterns.singleLineComment] = function(words,_,current) - --current:insert(words) -- Consume what was left - consumer:consumePatterns({ - [patterns.newLine] = function() end - }) - end, - [patterns.multiLineCommentOpen] = function(words,_,current) - current:insert(words) - consumer:consumePatterns({ - [patterns.multiLineCommentClose] = function() end - }) - end, - [patterns.colonSyntax] = function(words,_,current) - current:insert(words,consumeExpression()) - end, - [patterns.blockOpen] = function(words,_,current) - current:insert(words,consumeBlock()) - end, - } + + -- Give me: {patterns that take (match,state)}, the state, and a handler for comments + -- I will give you a function that will return the result of consumePatterns(patterns) + -- With escapes handled. + local function makeConsumerEscaping(patterns,state,handler) + return function() + handler = handler or function() end + local escaped = true + local function escape() escaped = true end + while escaped do + escaped = false + consumer:consumePatterns(union({ + [patterns.singleLineComment] = function(...) + handler(...) + consumer:consumePatterns({ + [patterns.newLine] = escape + }) + end, + [patterns.multiLineCommentOpen] = function(...) + handler(...) + consumer:consumePatterns({ + [NONE] = error, + [patterns.multiLineCommentClose] = escape + }) + end, + },patterns),state) + end + end + end + -- Give me {patterns that take (match,current)} and current (expression) + local function makeConsumerAppending(patterns,state) + local appendingPatterns = map(patterns,function(func) + return function(match,current) + current:insert(match.excess,func(match,current)) + end + end) + makeConsumerEscaping(appendingPatterns,function(match,current) + current:insert(match.excess) + end) + end + local function makeConsumerExpressive() + local function consumeSingleLineString() + return + end + local function consumeNamed() + + end + local function consumeMultiLineString() + + end + local function consumeURI() + + end + return makeConsumerAppending({ + [patterns.singleLineString] = function(match,current) + return consumer:consumePatterns({ + ["[\n\r]"] = function() + error("Incomplete string literal") + end, + [patterns.singleLineString] = function(words,_) + return expression({"text",Expression({words})}) + end + }) + end, + [patterns.named] = function(words,_,current) + current:insert(words,consumePatternsEscaping({ + [patterns.named] = function(words,_) + return expression({"the",Expression({words})}) + end + })) + end, + [patterns.multiLineStringOpen] = function(match,current) + return consumer:consumePatterns({ + [patterns.multiLineStringClose] = function(words,_) + return expression({"text",Expression({words})}) + end + }) + end, + [patterns.multiLineStringOpen] = function(words,_,current) + current:insert(words,consumePatternsEscaping({ + [patterns.multiLineStringClose] = function(words,_) + return expression({"text",Expression({words})}) + end + })) + end, + [patterns.uriOpen] = function(words,_,current) + current:insert() + current:insert(words,consumePatternsEscaping({ + ["[\n\r]"] = function() + current:error("Incomplete URI literal") + end, + [patterns.uriClose] = function(path) + return read(path) + end + })) + end, + [patterns.expressionOpen] = function(match,current) + return consumeExpression() + end, + [patterns.colonSyntax] = function(match,current) + return consumeExpression() + end, + [patterns.blockOpen] = function(match,current) + return consumeBlock() + end, + }) + end + local function consumePatternsExpressive(patterns,current) + + return consumePatternsEscaping + end -- Consume a {} block of code function consumeBlock(closing_pattern) closing_pattern = closing_pattern or patterns.blockClose @@ -233,7 +292,7 @@ function Parse(content,uri,chain) local loop = true while loop do local remaining = consumer:remaining() - local expr = consumer:consumePatterns(union(expressionMeals,{ + local expr = consumePatternsEscaping(union(expressionMeals,{ [patterns.blockDelimiter] = function(words,_) current:insert(words) if current:empty() then @@ -261,7 +320,7 @@ function Parse(content,uri,chain) local loop = true while loop do local remaining = consumer:remaining() - local expr = consumer:consumePatterns(union(expressionMeals,{ + local expr = consumePatternsEscaping(union(expressionMeals,{ [patterns.expressionClose] = function(words,_,_current) current:insert(words) loop = false diff --git a/nellie/proto.lua b/nellie/proto.lua index 9ca0502..3a4b26f 100644 --- a/nellie/proto.lua +++ b/nellie/proto.lua @@ -2,7 +2,7 @@ require("nellie.parser") function Bind(scope,abstract,callback) - for _,expression in pairs(Parse(abstract)) do + for _,expression in pairs(Parse(abstract).items) do assert_meta(Expression)(expression) scope:insert(Binding( expression, @@ -21,7 +21,7 @@ end function Do(scope,expression,chain,name) local latest for index,item in pairs(expression.items) do - latest = Run(scope,item,chain,"do: "+tostring(index)) + latest = Run(scope,item,chain,"do: "..tostring(index)) end return latest end @@ -283,23 +283,24 @@ Bind(ns,"let ((named (name)) be (value))",function(s,e,c) end) Bind(ns,"new table",function(s,e,c) return {} end) ]] -Bind(ns,"(closed)",function(s,e,c) return Run(s,e.closed,c,"(...)") end) +Bind(ns,"text (text)",function(s,e,c) return e.text:text() end) +Bind(ns,"(closed)",function(s,e,c) print(e.closed) return Run(s,e.closed,c,"(...)") end) Bind(ns,"log (text)",function(s,e,c) log(Run(s,e.text,c,"print: text")) end) Bind(ns,"this scope",function(s,e,c) return s end) Bind(ns,"new scope",function(s,e,c) return Scope() end) -- TODO: chains?!!? Bind(ns,"index (table) with (key)",function(s,e,c) return Run(s,e,c,"") end) Bind(ns,[[ -(input) means do (output); -(input) in (input_scope) means do (output); +(input) means (output); +(input) in (input_scope) means (output); (input) means do (output) in (output_scope); -(input) in (input_scope) means do (output) in (output_scope) -]],function(s,e,c1) -- A substitution - local input_scope = s - if e.input_scope then input_scope = Run(s,e.input_scope,c1,"means: input scope") end - local output_scope = s - if e.output_scope then output_scope = Run(s,e.output_scope,c2,"means: output scope") end - Bind(e.input_scope,e.input,function(s,e,c2) - return Do(e.output_scope,e.output,c2) -- TODO: chains?! +(input) in (input_scope) means (output) in (output_scope) +]],function(s1,e1,c1) -- A substitution + local input_scope = s1 + if e1.input_scope then input_scope = Run(s1,e1.input_scope,c1,"means: input scope") end + local output_scope = s1 + if e1.output_scope then output_scope = Run(s1,e1.output_scope,c1,"means: output scope") end + Bind(input_scope,e1.input,function(s2,e2,c2) + return Do(output_scope,e1.output,c2) -- TODO: chains?! end) end) Bind(ns,"do (stuff) in (scope); do (stuff)",function(s,e,c) @@ -311,7 +312,8 @@ Bind(ns,"new table",function(s,e,c) return {} end) local Hpath = "nellie/helper.nel" local Hchain = Chain("in internal: "..Hpath) local success,result = Hchain:call(function(chain) - Run(NelliScope,Parse(read(Hpath),Hpath,chain),chain) + print(Parse(read(Hpath),Hpath,chain)) + Do(NelliScope,Parse(read(Hpath),Hpath,chain),chain) end) --[[ Documentation: diff --git a/run.sh b/run.sh index 09d8f2f..d49fa2b 100755 --- a/run.sh +++ b/run.sh @@ -4,5 +4,5 @@ then echo "lua could not be found" exit 1 else - lua -i -v -W -l nellie -e Run "$@" + lua -l nellie -e "Run'$@'" fi \ No newline at end of file diff --git a/tests/test.lua b/tests/test.lua index fddafdf..44d9319 100644 --- a/tests/test.lua +++ b/tests/test.lua @@ -2,4 +2,8 @@ print("doing some tests") local function test(file,result) end -test("print.nel",[[hello world!]]) \ No newline at end of file +test("print.nel",[[hello world!]]) + +do + +end \ No newline at end of file