Still working on standards.

This commit is contained in:
2026-02-14 23:35:41 +00:00
parent 5a3c126100
commit e2ad04b692
18 changed files with 1046 additions and 234 deletions

View File

@@ -1,3 +1,3 @@
# neli # 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
View 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
View 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
View 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
*/

View File

@@ -35,7 +35,7 @@ end
-- Iterate (ipairs without the index) -- Iterate (ipairs without the index)
function iterate(...) function iterate(...)
local iterator,tab,i = pairs(...) local iterator,tab,i = ipairs(...)
return function(...) return function(...)
local index,item = iterator(tab,i) local index,item = iterator(tab,i)
i = index i = index

View File

@@ -84,12 +84,38 @@ function interpret(content,uri,chain)
current:insert(words,consumeExpression()) current:insert(words,consumeExpression())
end end
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() function consumeBlock()
local expressions = {} local expressions = {}
local current = expression() local current = expression()
local loop = true local loop = true
while loop do while loop do
local expr = consumer:consume({ local expr = consumer:consume({
[multiLineCommentPattern] = multiLineCommentMeal(current),
[singleLineCommentPattern] = singleLineCommentMeal(current),
[uriOpenPattern] = URIMeal(current,uriClosePattern), [uriOpenPattern] = URIMeal(current,uriClosePattern),
[expressionOpenPattern] = expressionMeal(current), [expressionOpenPattern] = expressionMeal(current),
[multiLineStringOpenPattern] = [multiLineStringOpenPattern] =
@@ -99,16 +125,24 @@ function interpret(content,uri,chain)
singleLineStringMeal( singleLineStringMeal(
current,singleLineStringPattern), current,singleLineStringPattern),
[blockDelimiterPattern] = function(words,_) [blockDelimiterPattern] = function(words,_)
current:insert(words)
if current:empty() then if current:empty() then
error("Extravenous semicolon.") --error("Extravenous semicolon.")
end else
table.insert(expressions,current) table.insert(expressions,current)
end
current = expression() current = expression()
end, end,
[blockClosePattern] = function(words,_) [blockClosePattern] = function(words,_)
current:insert(words) current:insert(words)
loop = false loop = false
end end,
[blockOpenPattern] = function(words,_)
current:insert(
words,
consumeBlock()
)
end,
}) })
end end
if #current.items ~= 0 then if #current.items ~= 0 then
@@ -124,6 +158,8 @@ function interpret(content,uri,chain)
while loop do while loop do
local remaining = consumer:remaining() local remaining = consumer:remaining()
local expr = consumer:consume({ local expr = consumer:consume({
[multiLineCommentPattern] = multiLineCommentMeal(current),
[singleLineCommentPattern] = singleLineCommentMeal(current),
[uriOpenPattern] = URIMeal(current,uriClosePattern), [uriOpenPattern] = URIMeal(current,uriClosePattern),
[expressionOpenPattern] = expressionMeal(current), [expressionOpenPattern] = expressionMeal(current),
[multiLineStringOpenPattern] = [multiLineStringOpenPattern] =

212
main.lua
View File

@@ -1,225 +1,27 @@
require("interpret") require("nelli")
local nelli_scope = Scope() local function main(args)
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()
-- Take arguments as neli files to read and interpret -- Take arguments as neli files to read and interpret
local last
local root = Chain( local root = Chain(
string.format( string.format(
"in cmd 'nel {%s}'", "in cmd 'nelli {%s}'",
table.concat(args,";") table.concat(args,";")
) )
) )
local success,result = xpcall(function()
for _,parameter in pairs(args) do for _,parameter in pairs(args) do
last = parameter
local chain = Chain( local chain = Chain(
string.format("in 'call: root' @'%s'",parameter) string.format("in 'call: root' @'%s'",parameter)
):from(root) ):from(root)
root:call(function()
local root = interpret( local root = interpret(
read(parameter), read(parameter),
parameter, parameter,
chain chain
) )
assert_meta(Chain)(chain,"?") NelliScope:run(root,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) end)
if not success then
log(string.format(
"nel: %s\nNEL traceback:\n\t%s",
result,
table.concat(root:flatten(),"\n\t")
))
end end
end end
main() -- A list of arguments passed into this
main({...})

23
nel/helper.nel Normal file
View 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
View 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
View File

@@ -0,0 +1,3 @@
do {
} in ((module ("nel/translator/main.nel"))

14
nel/translator/main.nel Normal file
View 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
View File

@@ -0,0 +1,6 @@
do {
here ((fart) means (print "fart"));
fart;
fart;
fart;
}

9
nelli Executable file
View 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
View 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
View File

@@ -0,0 +1,6 @@
do {
register query {
/* Some sorcery for a query? */
}
}

129
parse.lua
View File

@@ -64,10 +64,18 @@ function Expression:new(items)
self:insert(item) self:insert(item)
end end
end end
-- Return the iterator through the expression
function Expression:iterate()
return iterate(self.items)
end
-- Is the expression empty?
function Expression:empty() function Expression:empty()
return #self.items == 0 return #self.items == 0
end 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(...) function Expression:insert(...)
-- closures for nvim highlighting -- closures for nvim highlighting
local function insert_word(word) local function insert_word(word)
@@ -83,15 +91,19 @@ function Expression:insert(...)
end end
for item in iterate({...}) do for item in iterate({...}) do
case_of({ case_of({
table = function(expr) [Expression] = function(expr)
insert_expression(expr) insert_expression(expr)
end, end,
string = function(word) string = function(word)
insert_word(word) insert_word(word)
end end
})(type(item),item) })(type_of(item),item)
end end
end end
--[[
Converts an expression to meaningful text format.
This should be reparseable.
]]
function Expression:string() function Expression:string()
local parts = {} local parts = {}
for index,item in pairs(self.items) do for index,item in pairs(self.items) do
@@ -99,6 +111,30 @@ function Expression:string()
end end
return string.format("(%s)",table.concat(parts," ")) return string.format("(%s)",table.concat(parts," "))
end 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) function Expression:abstract(infer)
local parts = {} local parts = {}
local count = 0 local count = 0
@@ -122,6 +158,10 @@ function Expression:abstract(infer)
end end
return Expression(parts) return Expression(parts)
end end
--[[
Return the single 'words' part of this expression
if it is alone. Otherwise return nil.
]]
function Expression:text() function Expression:text()
-- Get the text -- Get the text
local text = self.items[#self.items] local text = self.items[#self.items]
@@ -129,21 +169,32 @@ function Expression:text()
if type(text) ~= "string" then return end if type(text) ~= "string" then return end
return text return text
end end
--[[
Give this expression a meaningful debug location
referring to some source code.
]]
function Expression:locate(str,uri) function Expression:locate(str,uri)
self.location = str self.location = str
self.source = uri self.source = uri
return self return self
end end
--[[
Return the meaningful debug location as a string msg.
]]
function Expression:link(msg) function Expression:link(msg)
return string.format( return string.format(
"in '%s' @%s (%s)", "in '%s' @%s (%s)",
msg, msg,
tostring(self), tostring(self:abstract()),
tostring(self.location or "?"), tostring(self.location or "?"),
tostring(self.source or "?.nel") tostring(self.source or "?.nel")
) )
end end
--[[
A class which parses text using the 'consumer-meal'
model.
]]
Consumer = class("Consumer") Consumer = class("Consumer")
function Consumer:new(content) function Consumer:new(content)
self.range = Range(Reader(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)) return string.format("Binding %s: %s",self.template,tostring(self.value or self.callback))
end end
function Binding:call(s,e,c) function Binding:call(s,e,c)
assert_meta(Scope)(s) for item in iterate(e) do
assert_meta(Chain)(c) 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) return self.value or self.callback(s,e,c)
end end
function Binding:check(expression) -- Expecting an abstract expression!
function Binding:check(expression,candid)
assert_meta(Expression)(expression,"Arg 'expression' #1: %s") 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 data = {}
local index local index
local loop = true local loop = true
@@ -249,7 +305,18 @@ function Binding:check(expression)
if type_of(candid_item) ~= Expression then if type_of(candid_item) ~= Expression then
loop = false loop = false
else 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
end, end,
string = function() string = function()
@@ -291,6 +358,31 @@ function Chain:flatten(tab)
table.insert(tab,self.data) table.insert(tab,self.data)
return tab return tab
end 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 -- The scope of a neli expression under evaluation
-- without exact faith to traditional language scopes -- without exact faith to traditional language scopes
@@ -308,7 +400,7 @@ function Scope:insert(binding)
assert_meta(Binding)(binding,"Arg 'binding' #1: %s") assert_meta(Binding)(binding,"Arg 'binding' #1: %s")
for competitor in iterate(self.bindings) do for competitor in iterate(self.bindings) do
if competitor:check(binding.template) then 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
end end
table.insert(self.bindings,binding) table.insert(self.bindings,binding)
@@ -317,10 +409,12 @@ function Scope:run(expression,chain,original)
original = original or self original = original or self
assert_meta(Expression)(expression,"Arg 'expression' #1: %s") assert_meta(Expression)(expression,"Arg 'expression' #1: %s")
assert_meta(Chain)(chain,"Arg 'chain' #2: %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 -- Could be multiple bindings so make a table
local binds = {} local binds = {}
local abstract = expression:abstract()
for binding in iterate(self.bindings) do for binding in iterate(self.bindings) do
local bind = binding:check(expression) local bind = binding:check(expression,abstract)
-- Insert into table -- Insert into table
if bind then binds[binding] = bind end if bind then binds[binding] = bind end
end end
@@ -328,21 +422,21 @@ function Scope:run(expression,chain,original)
-- Check for multiple -- Check for multiple
if not bind then if not bind then
if self.parent then if self.parent then
return self.parent:run(expression,chain,self) return self.parent:run(expression,chain,original)
else else
local extra = "" local extra = ""
if #expression.items == 1 then if #expression.items == 1 then
local item = expression.items[1] local item = expression.items[1]
if type_of(item) == Expression then 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 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
end end
error( error(
string.format( string.format(
"nel: No binding for '%s' in scope.", "nel: No binding for '%s' in scope.%s",
tostring(expression:abstract()), tostring(expression),
extra extra
),3 ),3
) )
@@ -361,6 +455,9 @@ function Scope:run(expression,chain,original)
) )
,2) ,2)
else else
for index,item in pairs(binds) do
print("index:",index,"item:",item)
end
return binding:call( return binding:call(
original, original,
bind, bind,

View File

@@ -1,5 +1,3 @@
do { do {
print "large balls"; print "wsg";
define (say (text)) as (print (text));
say "shit";
} }