Still working on standards.
This commit is contained in:
@@ -1,3 +1,3 @@
|
||||
# neli
|
||||
|
||||
A silly language for silly people to understand smart things.
|
||||
A silly language to help silly people understand silly things.
|
||||
|
||||
327
archive/nelli.lua
Normal file
327
archive/nelli.lua
Normal file
@@ -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
|
||||
159
archive/note1.txt
Normal file
159
archive/note1.txt
Normal file
@@ -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
|
||||
19
archive/note2.txt
Normal file
19
archive/note2.txt
Normal file
@@ -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
|
||||
*/
|
||||
@@ -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
|
||||
|
||||
@@ -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] =
|
||||
|
||||
222
main.lua
222
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({...})
|
||||
|
||||
23
nel/helper.nel
Normal file
23
nel/helper.nel
Normal file
@@ -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);
|
||||
});
|
||||
};
|
||||
}
|
||||
14
nel/lc.nel
Normal file
14
nel/lc.nel
Normal file
@@ -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)
|
||||
}
|
||||
3
nel/translator/cxx.nel
Normal file
3
nel/translator/cxx.nel
Normal file
@@ -0,0 +1,3 @@
|
||||
do {
|
||||
|
||||
} in ((module ("nel/translator/main.nel"))
|
||||
14
nel/translator/main.nel
Normal file
14
nel/translator/main.nel
Normal file
@@ -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));
|
||||
}
|
||||
6
nel/ui.nel
Normal file
6
nel/ui.nel
Normal file
@@ -0,0 +1,6 @@
|
||||
do {
|
||||
here ((fart) means (print "fart"));
|
||||
fart;
|
||||
fart;
|
||||
fart;
|
||||
}
|
||||
9
nelli
Executable file
9
nelli
Executable file
@@ -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
|
||||
|
||||
299
nelli.lua
Normal file
299
nelli.lua
Normal file
@@ -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
|
||||
(<macro>) become whatever (result) will become in place
|
||||
(expression)
|
||||
--]]
|
||||
|
||||
return ns
|
||||
6
nex.nel
Normal file
6
nex.nel
Normal file
@@ -0,0 +1,6 @@
|
||||
do {
|
||||
register query {
|
||||
/* Some sorcery for a query? */
|
||||
|
||||
}
|
||||
}
|
||||
129
parse.lua
129
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,
|
||||
|
||||
Reference in New Issue
Block a user