Proto: meaning etc.

This commit is contained in:
2026-03-08 18:59:45 +00:00
parent d7ca3739aa
commit e6f3b5c77f
11 changed files with 287 additions and 268 deletions

View File

@@ -1,59 +1,6 @@
require("helper")
require("nellie.helper")
-- CLASS DEFINITIONS
-- A stream of text (kind of)
-- with some helping methods
Reader = class("Reader")
-- Construct a reader from
function Reader:new(text)
assert_type("string")(text,"Arg 'text' #1: %s")
self.text = text
end
-- Get the substring of the readers text in a bounds
function Reader:get(first,final)
assert_type("number")(first,"Arg 'first' #1: %s")
assert_type("number","nil")(final,"Arg 'final' #2: %s")
return self.text:sub(first,final)
end
-- Match on the reader's text from an index
function Reader:find(pattern,init)
assert_type("string")(pattern,"Arg 'pattern' #1: %s")
assert_type("number","nil")(init,"Arg 'init' #2: %s")
return self.text:find(pattern,init)
end
function Reader:match(pattern,init)
assert_type("string")(pattern,"Arg 'pattern' #1: %s")
assert_type("number")(init,"Arg 'init' #2: %s")
return self.text:match(pattern,init)
end
-- A range of text being considered
-- like a cursor
Range = class("Range")
-- Construct a range of text from a reader and bounds
function Range:new(reader,first,final)
assert_meta(Reader)(reader)
assert_type("number","nil")(first,"Arg 'first' #1: %s")
assert_type("number","nil")(final,"Arg 'final' #2: %s")
self.reader = reader
self.first = first
self.final = final
end
function Range:text()
return self.reader:get(self.first or 1,self.final)
end
function Range:find(pattern)
return self.reader:find(pattern,self.first)
end
function Range:move(first,final)
assert_type("number","nil")(first,"Arg 'first' #1: %s")
assert_type("number","nil")(final,"Arg 'final' #2: %s")
self.first = first
self.final = final
-- note that this function returns itself
return self
end
Expression = class("Expression")
-- Construct an expression from items (or not)
function Expression:new(items)
@@ -191,76 +138,6 @@ function Expression:link(msg)
)
end
--[[
A class which parses text using the 'consumer-meal'
model.
]]
Consumer = class("Consumer")
function Consumer:new(content)
self.range = Range(Reader(content))
self.col = 1
self.row = 1
end
function Consumer:remaining()
return self.range:text()
end
function Consumer:mark()
end
-- From a table of actions of patterns,
-- consume the earliest pattern (index)
-- and call the value
function Consumer:consume(t)
-- t: {string = function...}
assert_type("table")(t,"Arg 't' #1: %s")
if not next(t) then
error("The match table cannot be empty!",2)
end
for index,func in pairs(t) do
assert_type("string")(index,"Bad 't' index: %s")
assert_type("function")(func,"Bad 't' item: %s")
end
local origin = self.range.first or 1
-- Get the next earliest match
local findex,ffinal,fpattern,ffunc,fexcess
for pattern,new_func in pairs(t) do
local new_index,new_final = self.range:find(pattern)
if new_index and
((not findex) or (findex > 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(
self.range.first or 1,
new_final - 1
)
end
end
-- Pass into the func
if not ffunc then
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
local sum = self.range.reader:get(origin+1,self.range.first)
:gsub("\r\n","\n")
:gsub("\r","\n")
for char in sum:gmatch(".") do
if char == "\n" then
self.row = self.row + 1
self.col = 1
else
self.col = self.col + 1
end
end
return ffunc(fexcess,fmatch)
end
Binding = class("Binding")
-- Construct from a neli string to infer a binding
-- note that you must wrap this definition in "(<def>)"!
@@ -362,6 +239,7 @@ 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
if not msg then msg = "no message provided" end
local match = msg:match("nel:%s(.*)")
if match then
return match
@@ -400,7 +278,13 @@ 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
error(string.format("nel: Conflicting sibling bindings in scope for '%s'.",binding.template))
error(
string.format(
"nel: Conflicting sibling binding in scope for '%s': '%s'.",
binding.template,
competitor.template
)
)
end
end
table.insert(self.bindings,binding)