Added more bindings and error tracebacks.
This commit is contained in:
117
parse.lua
117
parse.lua
@@ -1,4 +1,5 @@
|
||||
require("helper")
|
||||
|
||||
-- CLASS DEFINITIONS
|
||||
-- A stream of text (kind of)
|
||||
-- with some helping methods
|
||||
@@ -128,13 +129,32 @@ function Expression:text()
|
||||
if type(text) ~= "string" then return end
|
||||
return text
|
||||
end
|
||||
function Expression:locate(str,uri)
|
||||
self.location = str
|
||||
self.source = uri
|
||||
return self
|
||||
end
|
||||
function Expression:link(msg)
|
||||
return string.format(
|
||||
"in '%s' @%s (%s)",
|
||||
msg,
|
||||
tostring(self),
|
||||
tostring(self.location or "?"),
|
||||
tostring(self.source or "?.nel")
|
||||
)
|
||||
end
|
||||
|
||||
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)
|
||||
@@ -149,6 +169,7 @@ function Consumer:consume(t)
|
||||
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
|
||||
@@ -173,8 +194,20 @@ function Consumer:consume(t)
|
||||
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
|
||||
return ffunc(fexcess,self.range.reader:match(fpattern,findex))
|
||||
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")
|
||||
@@ -182,12 +215,20 @@ Binding = class("Binding")
|
||||
-- note that you must wrap this definition in "(<def>)"!
|
||||
function Binding:new(expression,callback)
|
||||
assert_meta(Expression)(expression,"Arg 'expression' #1: %s")
|
||||
assert_type("function")(callback,"Arg 'callback' #2: %s")
|
||||
if type_of(callback) == "function" then
|
||||
self.callback = callback
|
||||
else
|
||||
self.value = callback
|
||||
end
|
||||
self.template = expression:abstract(true)
|
||||
self.callback = callback
|
||||
end
|
||||
function Binding:call(scope,binds)
|
||||
return self.callback(scope,binds)
|
||||
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)
|
||||
return self.value or self.callback(s,e,c)
|
||||
end
|
||||
function Binding:check(expression)
|
||||
assert_meta(Expression)(expression,"Arg 'expression' #1: %s")
|
||||
@@ -228,6 +269,29 @@ function Binding:check(expression)
|
||||
end
|
||||
end
|
||||
|
||||
Chain = class("Chain")
|
||||
function Chain:new(data)
|
||||
assert_type("string")(data,"Arg 'data' #1: %s")
|
||||
self.data = data
|
||||
end
|
||||
function Chain:from(previous)
|
||||
assert_meta(Chain)(previous,"Arg 'previous' #1: %s")
|
||||
previous.next = self
|
||||
self.previous = previous
|
||||
return self
|
||||
end
|
||||
function Chain:string()
|
||||
return string.format("Chain:%s",table.concat(self:flatten(),", "))
|
||||
end
|
||||
function Chain:flatten(tab)
|
||||
tab = tab or {}
|
||||
if self.next then
|
||||
self.next:flatten(tab)
|
||||
end
|
||||
table.insert(tab,self.data)
|
||||
return tab
|
||||
end
|
||||
|
||||
-- The scope of a neli expression under evaluation
|
||||
-- without exact faith to traditional language scopes
|
||||
Scope = class("Scope")
|
||||
@@ -237,13 +301,22 @@ function Scope:new(bindings)
|
||||
self:insert(binding)
|
||||
end
|
||||
end
|
||||
function Scope:string()
|
||||
return string.format("Scope: {\n\t%s\n}",table.concat(map(self.bindings,tostring),"\n\t"))
|
||||
end
|
||||
function Scope:insert(binding)
|
||||
assert_meta(binding,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.")
|
||||
end
|
||||
end
|
||||
table.insert(self.bindings,binding)
|
||||
end
|
||||
function Scope:evaluate(expression)
|
||||
function Scope:run(expression,chain,original)
|
||||
original = original or self
|
||||
assert_meta(Expression)(expression,"Arg 'expression' #1: %s")
|
||||
local parent = self.parent
|
||||
assert_meta(Chain)(chain,"Arg 'chain' #2: %s")
|
||||
-- Could be multiple bindings so make a table
|
||||
local binds = {}
|
||||
for binding in iterate(self.bindings) do
|
||||
@@ -254,13 +327,23 @@ function Scope:evaluate(expression)
|
||||
local binding,bind = next(binds)
|
||||
-- Check for multiple
|
||||
if not bind then
|
||||
if parent then
|
||||
parent:evaluate(expression)
|
||||
if self.parent then
|
||||
return self.parent:run(expression,chain,self)
|
||||
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?"
|
||||
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."
|
||||
end
|
||||
end
|
||||
error(
|
||||
string.format(
|
||||
"No binding for '%s' in transitive scope.",
|
||||
tostring(expression)
|
||||
"nel: No binding for '%s' in scope.",
|
||||
tostring(expression:abstract()),
|
||||
extra
|
||||
),3
|
||||
)
|
||||
end
|
||||
@@ -272,13 +355,17 @@ function Scope:evaluate(expression)
|
||||
end
|
||||
error(
|
||||
string.format(
|
||||
"Ambiguous bindings in scope for '%s': %s",
|
||||
"nel: Ambiguous bindings in scope for '%s': %s",
|
||||
tostring(expression:abstract()),
|
||||
table.concat(candidates)
|
||||
)
|
||||
,2)
|
||||
else
|
||||
return binding:call(self,bind)
|
||||
end
|
||||
return binding:call(
|
||||
original,
|
||||
bind,
|
||||
chain
|
||||
)
|
||||
end
|
||||
end
|
||||
return _G
|
||||
|
||||
Reference in New Issue
Block a user