Compare commits

...

6 Commits

Author SHA1 Message Date
7aa282b405 Sync 2026-03-29 11:46:50 +01:00
e6f3b5c77f Proto: meaning etc. 2026-03-08 18:59:45 +00:00
d7ca3739aa Renaming. 2026-03-03 14:22:18 +00:00
b89e015170 Organisation. 2026-02-27 19:05:01 +00:00
7edad0c5bb Removing archive. 2026-02-27 16:46:43 +00:00
d600fc7232 Moving nex out. 2026-02-22 17:51:11 +00:00
24 changed files with 501 additions and 1228 deletions

View File

@@ -1,3 +1,3 @@
# neli # neli
A silly language to help silly people understand silly things. A silly language to help silly people understand silly things.

View File

@@ -1,327 +0,0 @@
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

View File

@@ -1,13 +0,0 @@
I am thinking for the syntax in nel.
How can we have multiple arguments for something?
It feels rather stupid allowing this really.
It's not very clear, a list is a list in any case.
So maybe we really should just have another
substatement, and forget about powering up
the abstracts, for now? ...?
Well then... in that case.
We only need to make sure scoping works.
Which means, scopes inherit from eachother,
and applying congruent bindings causes an error.
I think.

View File

@@ -1,159 +0,0 @@
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

View File

@@ -1,19 +0,0 @@
// 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

@@ -1,196 +0,0 @@
require("parse")
-- TODO: This should really be a separate API and module
-- please break it out
-- Interpret some text
function interpret(content,uri,chain)
-- LOCALS
-- The pattern for words inbetween expressions
local expressionWordPattern = "(.*)"
-- The patterns to open and close expressions,
-- the words scattered inbetween them will be matches
local expressionOpenPattern = "%("
local expressionClosePattern = "%)"
local subExpressionOpenPattern = "%:"
local subExpressionClosePattern = "%,"
local blockOpenPattern = "%{"
local blockClosePattern = "%}"
local blockDelimiterPattern = "[;]+"
-- So that all functions have access to eachother
local consume,
consumeExpression,
processWords,
processClosure,
processOpening,
consumeBlock,
consumeText,
consumeNumber
local consumer = Consumer(content)
local function expression(...)
return Expression(...):locate(
string.format(
"%i:%i",
consumer.row,
consumer.col
),uri)
end
local baseExpression = expression()
-- FUNCTIONS
-- Consume an expression, with its sub expressions
-- given that an opening has just been consumed
local singleLineStringPattern = "\""
local function singleLineStringMeal(current,match)
return function(words,_)
current:insert(words,consumer:consume({
[match] = function(words,_)
return expression({"text",Expression({words})})
end
}))
end
end
local multiLineStringOpenPattern = "%[%["
local multiLineStringClosePattern = "%]%]"
local function multiLineStringMeal(current,match)
return function(words,_)
current:insert(words,consumer:consume({
["[\n\r]"] = function()
error("Incomplete string literal")
end,
[match] = function(words,_)
return expression({"text",Expression({words})})
end
}))
end
end
local uriOpenPattern = "<"
local uriClosePattern = ">"
local function URIMeal(current,match)
return function(words,_)
current:insert(words,consumer:consume({
["[\n\r]"] = function()
current:error("Incomplete URI literal")
end,
[match] = function(path)
return read(path)
end
}))
end
end
local function expressionMeal(current)
return function(words,_)
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] =
multiLineStringMeal(
current,multiLineStringClosePattern),
[singleLineStringPattern] =
singleLineStringMeal(
current,singleLineStringPattern),
[blockDelimiterPattern] = function(words,_)
current:insert(words)
if current:empty() then
--error("Extravenous semicolon.")
else
table.insert(expressions,current)
end
current = expression()
end,
[blockClosePattern] = function(words,_)
current:insert(words)
loop = false
end,
[blockOpenPattern] = function(words,_)
current:insert(
words,
consumeBlock()
)
end,
})
end
if #current.items ~= 0 then
table.insert(expressions,current)
end
return expression(expressions)
end
function consumeExpression()
local current = expression()
-- Loop, adding new expressions and words,
-- until closing that is
local loop = true
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] =
multiLineStringMeal(
current,multiLineStringClosePattern),
[singleLineStringPattern] =
singleLineStringMeal(
current,singleLineStringPattern),
[expressionOpenPattern] = expressionMeal(current),
[expressionClosePattern] = function(words,_)
current:insert(words)
loop = false
end,
[blockOpenPattern] = function(words,_)
current:insert(
words,
consumeBlock()
)
end,
["$"] = function(words,last)
current:insert(remaining)
loop = false
end
})
-- This single line below made me procrastinate life for a total of 4 hours on instagram reels; lock in.
-- if not expr then print("brk") break end -- Since now closing
end
return current
end
return consumeExpression()
-- TODO: check for later expressions please?
end
return _G

View File

@@ -1,23 +0,0 @@
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);
});
};
}

View File

@@ -1,14 +0,0 @@
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)
}

View File

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

View File

@@ -1,14 +0,0 @@
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));
}

View File

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

View File

@@ -27,8 +27,15 @@ end
id = function(...) return ... end id = function(...) return ... end
function union(t0,t1) function union(t0,t1)
for index,item in pairs(t0) do for index,item in pairs(t1) do
t0[index] = t1 t0[index] = t1[index]
end
return t0
end
function union_all(t0,...)
for _,t1 in pairs({...}) do
union(t0,t1)
end end
return t0 return t0
end end
@@ -59,6 +66,12 @@ function indices(t)
return indices return indices
end end
function map(t,func)
for index,item in pairs(t) do
t[index] = func(index,item)
end
end
-- Ensures a table has a metatable -- Ensures a table has a metatable
function ensure_metatable(t) function ensure_metatable(t)
local meta = getmetatable(t) local meta = getmetatable(t)
@@ -193,7 +206,7 @@ function read(path)
local file = io.open(path) local file = io.open(path)
if not file then if not file then
error( error(
string.match("Could not open file '%s'.",path),3 string.format("Could not open file '%s'.",path),3
) )
end end
local content = file:read("a") local content = file:read("a")

8
nellie/helper.nel Normal file
View File

@@ -0,0 +1,8 @@
// single line comment
log "a";
/*
multi
line
comment
*/
log "b";

View File

@@ -1,27 +1,27 @@
require("nelli") local path = ...
require("nellie.proto")
local function main(args) function Run(...)
-- Take arguments as neli files to read and interpret -- Take arguments as neli files to read and interpret
local root = Chain( local root = Chain(
string.format( string.format(
"in cmd 'nelli {%s}'", "in cmd 'nelli {%s}'",
table.concat(args,";") table.concat({...},";")
) )
) )
for _,parameter in pairs(args) do for _,parameter in pairs({...}) do
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() root:call(function()
local root = interpret( local root = Parse(
read(parameter), read(parameter),
parameter, parameter,
chain chain
) )
NelliScope:run(root,chain) for _,expression in pairs(root.items) do
NelliScope:run(expression,Chain(expression:link("root: do")):from(chain))
end
end) end)
end end
end end
-- A list of arguments passed into this
main({...})

View File

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

336
nellie/parser.lua Normal file
View File

@@ -0,0 +1,336 @@
require("nellie.interpreter")
-- A stream of text (kind of)
-- with some helping methods
Reader = class("Reader")
-- Construct a reader from
function Reader:new(text)
assert_type("string")(text,"Arg 'text' #1: %s")
self.text = text
end
-- Get the substring of the readers text in a bounds
function Reader:get(first,final)
assert_type("number")(first,"Arg 'first' #1: %s")
assert_type("number","nil")(final,"Arg 'final' #2: %s")
return self.text:sub(first,final)
end
-- Match on the reader's text from an index
function Reader:find(pattern,init)
assert_type("string")(pattern,"Arg 'pattern' #1: %s")
assert_type("number","nil")(init,"Arg 'init' #2: %s")
return self.text:find(pattern,init)
end
function Reader:match(pattern,init)
assert_type("string")(pattern,"Arg 'pattern' #1: %s")
assert_type("number")(init,"Arg 'init' #2: %s")
return self.text:match(pattern,init)
end
-- A range of text being considered
-- like a cursor
Range = class("Range")
-- Construct a range of text from a reader and bounds
function Range:new(reader,first,final)
assert_meta(Reader)(reader)
assert_type("number","nil")(first,"Arg 'first' #1: %s")
assert_type("number","nil")(final,"Arg 'final' #2: %s")
self.reader = reader
self.first = first
self.final = final
end
function Range:text()
return self.reader:get(self.first or 1,self.final)
end
function Range:find(pattern)
return self.reader:find(pattern,self.first)
end
function Range:move(first,final)
assert_type("number","nil")(first,"Arg 'first' #1: %s")
assert_type("number","nil")(final,"Arg 'final' #2: %s")
self.first = first
self.final = final
-- note that this function returns itself
return self
end
--[[
A class which parses text using the 'consumer-meal'
model.
]]
Consumer = class("Consumer")
function Consumer:new(content)
self.range = Range(Reader(content))
self.col = 1
self.row = 1
end
function Consumer:remaining()
return self.range:text()
end
function Consumer:mark()
end
-- From a table of actions of patterns,
-- consume the earliest pattern (index)
-- and call the value
local NONE = {}
function Consumer:consumePatterns(patterns,state)
-- t: {string = function...}
assert_type("table")(patterns,"Arg 't' #1: %s")
if not next(patterns) then
error("The match table cannot be empty!",2)
end
for index,func in pairs(patterns) do
assert_type("string")(index,"Bad 't' index: %s")
assert_type("function")(func,"Bad 't' item: %s")
end
local origin = self.range.first or 1
-- Get the next earliest match
local callback = nil
local earliest = {}
--local findex,ffinal,fpattern,ffunc,fexcess -- TODO: make this not be a bunch of locals. New class?
for pattern,new_func in pairs(patterns) do
local new_index,new_final = self.range:find(pattern)
if new_index and
((not earliest.index) or (earliest.index > new_index))
then
if not new_index then return end -- (later:) what why?
earliest.index = new_index
earliest.final = new_final
earliest.pattern = pattern
callback = new_func
earliest.excess = self.range.reader:get(
self.range.first or 1,
earliest.index - 1
)
end
end
-- Pass into the func
if not callback then
if patterns[NONE] then
return patterns[NONE]()
end
return nil
end
assert(earliest.index) assert(earliest.final)
self.range:move(earliest.final+1) -- Move range to after the match
local fmatch = self.range.reader:match(earliest.pattern,earliest.final)
-- This seems to be broken:
local sum = self.range.reader:get(origin+1,self.range.first)
:gsub("\r\n","\n")
:gsub("\r","\n")
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 callback(earliest,state)
end
-- Interpret some text
function Parse(content,uri,chain)
-- LOCALS
-- So that all functions have access to eachother
local consume,
consumeExpression,
consumeBlock
local consumer = Consumer(content)
local function expression(...)
return Expression(...):locate(
string.format(
"%i:%i",
consumer.row,
consumer.col
),uri)
end
local baseExpression = expression()
local patterns = {}
patterns.contentClose = "$"
-- The pattern for words inbetween expressions
patterns.expressionWord = "(.*)"
-- The patterns to open and close expressions,
-- the words scattered inbetween them will be matches
patterns.expressionOpen = "%("
patterns.expressionClose = "%)"
patterns.subExpressionOpen = "%:"
patterns.subExpressionClose = "%,"
patterns.blockOpen = "%{"
patterns.blockClose = "%}"
patterns.blockDelimiter = "[;]+"
patterns.newLine = "[\n\r]+"
patterns.singleLineString = "\""
patterns.named = "'"
patterns.multiLineStringOpen = "%[%["
patterns.multiLineStringClose = "%]%]"
patterns.uriOpen = "<"
patterns.uriClose = ">"
patterns.singleLineComment = "//"
patterns.multiLineCommentOpen = "/%*"
patterns.multiLineCommentClose = "%*/"
patterns.colonSyntax = ":"
-- Give me: {patterns that take (match,state)}, the state, and a handler for comments
-- I will give you a function that will return the result of consumePatterns(patterns)
-- With escapes handled.
local function makeConsumerEscaping(patterns,state,handler)
return function()
handler = handler or function() end
local escaped = true
local function escape() escaped = true end
while escaped do
escaped = false
consumer:consumePatterns(union({
[patterns.singleLineComment] = function(...)
handler(...)
consumer:consumePatterns({
[patterns.newLine] = escape
})
end,
[patterns.multiLineCommentOpen] = function(...)
handler(...)
consumer:consumePatterns({
[NONE] = error,
[patterns.multiLineCommentClose] = escape
})
end,
},patterns),state)
end
end
end
-- Give me {patterns that take (match,current)} and current (expression)
local function makeConsumerAppending(patterns,state)
local appendingPatterns = map(patterns,function(func)
return function(match,current)
current:insert(match.excess,func(match,current))
end
end)
makeConsumerEscaping(appendingPatterns,function(match,current)
current:insert(match.excess)
end)
end
local function makeConsumerExpressive()
local function consumeSingleLineString()
return
end
local function consumeNamed()
end
local function consumeMultiLineString()
end
local function consumeURI()
end
return makeConsumerAppending({
[patterns.singleLineString] = function(match,current)
return consumer:consumePatterns({
["[\n\r]"] = function()
error("Incomplete string literal")
end,
[patterns.singleLineString] = function(words,_)
return expression({"text",Expression({words})})
end
})
end,
[patterns.named] = function(words,_,current)
current:insert(words,consumePatternsEscaping({
[patterns.named] = function(words,_)
return expression({"the",Expression({words})})
end
}))
end,
[patterns.multiLineStringOpen] = function(match,current)
return consumer:consumePatterns({
[patterns.multiLineStringClose] = function(words,_)
return expression({"text",Expression({words})})
end
})
end,
[patterns.multiLineStringOpen] = function(words,_,current)
current:insert(words,consumePatternsEscaping({
[patterns.multiLineStringClose] = function(words,_)
return expression({"text",Expression({words})})
end
}))
end,
[patterns.uriOpen] = function(words,_,current)
current:insert()
current:insert(words,consumePatternsEscaping({
["[\n\r]"] = function()
current:error("Incomplete URI literal")
end,
[patterns.uriClose] = function(path)
return read(path)
end
}))
end,
[patterns.expressionOpen] = function(match,current)
return consumeExpression()
end,
[patterns.colonSyntax] = function(match,current)
return consumeExpression()
end,
[patterns.blockOpen] = function(match,current)
return consumeBlock()
end,
})
end
local function consumePatternsExpressive(patterns,current)
return consumePatternsEscaping
end
-- Consume a {} block of code
function consumeBlock(closing_pattern)
closing_pattern = closing_pattern or patterns.blockClose
local expressions = {}
local current = expression()
local loop = true
while loop do
local remaining = consumer:remaining()
local expr = consumePatternsEscaping(union(expressionMeals,{
[patterns.blockDelimiter] = function(words,_)
current:insert(words)
if current:empty() then
--error("Extravenous semicolon.")
else
table.insert(expressions,current)
end
current = expression()
end,
[closing_pattern] = function(words,_)
current:insert(words)
loop = false
end,
}),current)
end
if #current.items ~= 0 then
table.insert(expressions,current)
end
return expression(expressions)
end
function consumeExpression()
local current = expression()
-- Loop, adding new expressions and words,
-- until closing that is
local loop = true
while loop do
local remaining = consumer:remaining()
local expr = consumePatternsEscaping(union(expressionMeals,{
[patterns.expressionClose] = function(words,_,_current)
current:insert(words)
loop = false
end
}),current)
-- This single line below made me procrastinate life for a total of 4 hours on instagram reels; lock in.
-- if not expr then print("brk") break end -- Since now closing
end
return current
end
return consumeBlock(patterns.contentClose)
-- TODO: check for later expressions please?
end

View File

@@ -1,14 +1,16 @@
require("interpret") -- Prototype for nel
require("nellie.parser")
function B(scope,abstract,callback) function Bind(scope,abstract,callback)
scope:insert( for _,expression in pairs(Parse(abstract).items) do
Binding( assert_meta(Expression)(expression)
interpret(abstract), scope:insert(Binding(
expression,
callback callback
) ))
) end
end end
function R(scope,expression,chain,name) function Run(scope,expression,chain,name)
name = name or "?" name = name or "?"
assert_meta(Chain)(chain,"Arg 'chain' #3: %s") assert_meta(Chain)(chain,"Arg 'chain' #3: %s")
assert_meta(Scope)(scope,"Arg 'scope' #1: %s") assert_meta(Scope)(scope,"Arg 'scope' #1: %s")
@@ -16,9 +18,17 @@ function R(scope,expression,chain,name)
local chain = Chain(expression:link(name)):from(chain) local chain = Chain(expression:link(name)):from(chain)
return scope:run(expression,chain) return scope:run(expression,chain)
end end
function Do(scope,expression,chain,name)
local latest
for index,item in pairs(expression.items) do
latest = Run(scope,item,chain,"do: "..tostring(index))
end
return latest
end
NelliScope = Scope() NelliScope = Scope()
local ns = NelliScope local ns = NelliScope
--[[
-- Replace all :text() occurrences with ones in 'exp_map' -- Replace all :text() occurrences with ones in 'exp_map'
local function substitute(target,exp_map) local function substitute(target,exp_map)
local text = target:text() local text = target:text()
@@ -64,7 +74,7 @@ local function Bexpands(s,e,c)
--[[error(string.format( --[[error(string.format(
"nel: Abstract parameter \"%s\" not in 'quotes'.", "nel: Abstract parameter \"%s\" not in 'quotes'.",
text text
))]] ))]]--[[
else else
table.insert(bound,text) table.insert(bound,text)
end end
@@ -76,10 +86,10 @@ local function Bexpands(s,e,c)
map[binding] = _e[binding] map[binding] = _e[binding]
end end
local expanded = substitute(e.expression,map) local expanded = substitute(e.expression,map)
return R(_s,expanded,_c,"means: "..tostring(expanded)) return Run(_s,expanded,_c,"means: "..tostring(expanded))
end) end)
end end]]
B(ns,"(abstract) means (expression)",Bexpands) --[[Bind(ns,"(abstract) means (expression)",Bexpands)
local function Bevaluate(s,e,c) local function Bevaluate(s,e,c)
return Binding(e.abstract,function(_s,_e,_c) return Binding(e.abstract,function(_s,_e,_c)
local scope = Scope() local scope = Scope()
@@ -87,18 +97,18 @@ local function Bevaluate(s,e,c)
for name,expression in pairs(_e.items) do for name,expression in pairs(_e.items) do
scope:insert(Binding( scope:insert(Binding(
Expression({name}), Expression({name}),
R(_s,_e,_c,string.format("expands: %s:",name)) Run(_s,_e,_c,string.format("expands: %s:",name))
)) ))
end end
return R(scope,e,c,"evaluate: result") return Run(scope,e,c,"evaluate: result")
end) end)
end end
B(ns,"(abstract) becomes (expression)",Bevaluates) Bind(ns,"(abstract) becomes (expression)",Bevaluates)
B(ns,"(identifier) is (expression)",function(s,e,c) Bind(ns,"(identifier) is (expression)",function(s,e,c)
if not e.identifier:text(s,e,c) then if not e.identifier:text(s,e,c) then
error("nel: text-only expression expected.") error("nel: text-only expression expected.")
end end
return Binding(e.identifier,R()) return Binding(e.identifier,Run())
end) end)
local function doAll(s,e,c) local function doAll(s,e,c)
local res local res
@@ -111,7 +121,7 @@ local function doAll(s,e,c)
) )
) )
end end
res = R(s,item,c,"do: line") res = Run(s,item,c,"do: line")
end end
return res return res
end end
@@ -120,16 +130,16 @@ end
local function Bdo(s,e,c) local function Bdo(s,e,c)
local scope local scope
if e.scope then if e.scope then
scope = R(s,e.scope,c,"do: scope") scope = Run(s,e.scope,c,"do: scope")
else else
scope = Scope() scope = Scope()
scope.parent = s scope.parent = s
end end
return doAll(s,e.expression,c) return doAll(s,e.expression,c)
end end
B(ns,"do (expression) in (scope)",Bdo) Bind(ns,"do (expression) in (scope)",Bdo)
B(ns,"do (expression)",Bdo) Bind(ns,"do (expression)",Bdo)
B(ns,"(abstract) broken into (expressions) as do (results)",function(s,e,c) Bind(ns,"(abstract) broken into (expressions) as do (results)",function(s,e,c)
return Binding(e.abstract,function(_s,_e,_c) return Binding(e.abstract,function(_s,_e,_c)
local params = List() local params = List()
for index,item in pairs(_e) do for index,item in pairs(_e) do
@@ -157,10 +167,10 @@ local _list = {}
local function List() local function List()
return setmetatable(_list,{}) return setmetatable(_list,{})
end end
B(ns,"a new list",function(s,e,c) Bind(ns,"a new list",function(s,e,c)
return List() return List()
end) end)
B(ns,"for each (item) in (list) do (expressions)",function(s,e,c) Bind(ns,"for each (item) in (list) do (expressions)",function(s,e,c)
local item = e.item:text() local item = e.item:text()
if not property then if not property then
error( error(
@@ -170,7 +180,7 @@ B(ns,"for each (item) in (list) do (expressions)",function(s,e,c)
) )
) )
end end
local list = R(s,e.list,c,"for each: list") local list = Run(s,e.list,c,"for each: list")
assert_meta(List)(list,"for each: list: %s") assert_meta(List)(list,"for each: list: %s")
for index,item in ipairs(list) do for index,item in ipairs(list) do
local scope = Scope() local scope = Scope()
@@ -179,40 +189,40 @@ B(ns,"for each (item) in (list) do (expressions)",function(s,e,c)
doAll(s,e.expressions,c) doAll(s,e.expressions,c)
end end
end) end)
B(ns,"add (item) to (list)",function(s,e,c) Bind(ns,"add (item) to (list)",function(s,e,c)
local item = R(s,e.list,c,"") local item = Run(s,e.list,c,"")
local list = R(s,e.list,c,"add: list") local list = Run(s,e.list,c,"add: list")
assert_meta(List)(list,"add: list: %s") assert_meta(List)(list,"add: list: %s")
table.insert(list,item) table.insert(list,item)
end) end)
-- I even quite dislike these, they are prone to gumming things up -- I even quite dislike these, they are prone to gumming things up
local _table = {} local _table = {}
B(ns,"a new table",function(s,e,c) return {} end) Bind(ns,"a new table",function(s,e,c) return {} end)
B(ns,"set (index) of (table) to (value)",function(s,e,c) Bind(ns,"set (index) of (table) to (value)",function(s,e,c)
local index = e.index:text() local index = e.index:text()
if not index then if not index then
index = R(s,e.index,c,"set: index") index = Run(s,e.index,c,"set: index")
end end
local tab = R(s,e.table,c,"set: table") local tab = Run(s,e.table,c,"set: table")
local val = R(s,e.value,c,"set: value") local val = Run(s,e.value,c,"set: value")
tab[index] = val tab[index] = val
end) end)
B(ns,"get (index) of (table)",function(s,e,c) Bind(ns,"get (index) of (table)",function(s,e,c)
local index = e.index:text() local index = e.index:text()
if not index then if not index then
index = R(s,e.index,c,"get: index") index = Run(s,e.index,c,"get: index")
end end
local tab = R(s,e.table,c,"get: table") local tab = Run(s,e.table,c,"get: table")
return tab[index] return tab[index]
end) end)
local _object = {} local _object = {}
local function Object() local function Object()
return setmetatable(_object,{}) return setmetatable(_object,{})
end end
B(ns,"a new object",function(s,e,c) Bind(ns,"a new object",function(s,e,c)
return Object() return Object()
end) end)
B(ns,"(property) of (object) is (value)",function(s,e,c) Bind(ns,"(property) of (object) is (value)",function(s,e,c)
local property = e.property:text() local property = e.property:text()
if not property then if not property then
error( error(
@@ -222,12 +232,12 @@ B(ns,"(property) of (object) is (value)",function(s,e,c)
) )
) )
end end
local object = R(s,e.object,c,"property is: object") local object = Run(s,e.object,c,"property is: object")
assert_meta(Object)(object,"nel: property is: object: %s") assert_meta(Object)(object,"nel: property is: object: %s")
local value = R(s,e.value,c,"property is: value") local value = Run(s,e.value,c,"property is: value")
object[property] = value object[property] = value
end) end)
B(ns,"(property) of (object)",function(s,e,c) Bind(ns,"(property) of (object)",function(s,e,c)
local property = e.property:text() local property = e.property:text()
if not property then if not property then
error( error(
@@ -237,58 +247,77 @@ B(ns,"(property) of (object)",function(s,e,c)
) )
) )
end end
local object = R(s,e.object,c,"property is: object") local object = Run(s,e.object,c,"property is: object")
assert_meta(Object)(object,"nel: property is: object: %s") assert_meta(Object)(object,"nel: property is: object: %s")
return object[property] return object[property]
end) end)
B(ns,"error (text)",function(s,e,c) Bind(ns,"error (text)",function(s,e,c)
error("nel: "..R(s,e.text,c,"error: message")) error("nel: "..Run(s,e.text,c,"error: message"))
end) end)
B(ns,"this scope",function(s,e,c) Bind(ns,"this scope",function(s,e,c)
return s return s
end) end)
B(ns,"a new scope",function(s,e,c) Bind(ns,"a new scope",function(s,e,c)
return Scope() return Scope()
end) end)
-- Read a file in the current directly, more for compiling -- Read a file in the current directly, more for compiling
B(ns,"read file (path)",function(s,e,c) Bind(ns,"read file (path)",function(s,e,c)
return read(R(s,e.path,c,"read: path")) return read(Run(s,e.path,c,"read: path"))
end) end)
-- Take some text and interpret it into an expression -- Take some text and interpret it into an expression
B(ns,"evaluate (text) in (scope)",function(s,e,c) Bind(ns,"evaluate (text) in (scope)",function(s,e,c)
local scope = R(s,e.scope,c,"evaluate: scope") local scope = Run(s,e.scope,c,"evaluate: scope")
return R(s,interpret(R(scope,e.text,c,"include: source")),c,"include: result") return Run(s,interpret(Run(scope,e.text,c,"include: source")),c,"include: result")
end) end)
B(ns,"print (text)",function(s,e,c) Bind(ns,"text (literal)",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() return e.literal:text()
end) end)
B(ns,"return (expression)",function(s,e,c) Bind(ns,"return (expression)",function(s,e,c)
return R(s,e.expression,c,"return: result") return Run(s,e.expression,c,"return: result")
end) end)
B(ns,"(macro) means (expansion)",function(s,e,c) Bind(ns,"let (binding) in (scope)",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) end)
B(ns,"let (binding) in (scope)",function(s,e,c) Bind(ns,"text (literal)",function(s,e,c) return e.literal:text() end)
Bind(ns,"let ((named (name)) be (value))",function(s,e,c) end)
Bind(ns,"new table",function(s,e,c) return {} end)
]]
Bind(ns,"text (text)",function(s,e,c) return e.text:text() end)
Bind(ns,"(closed)",function(s,e,c) print(e.closed) return Run(s,e.closed,c,"(...)") end)
Bind(ns,"log (text)",function(s,e,c) log(Run(s,e.text,c,"print: text")) end)
Bind(ns,"this scope",function(s,e,c) return s end)
Bind(ns,"new scope",function(s,e,c) return Scope() end) -- TODO: chains?!!?
Bind(ns,"index (table) with (key)",function(s,e,c) return Run(s,e,c,"") end)
Bind(ns,[[
(input) means (output);
(input) in (input_scope) means (output);
(input) means do (output) in (output_scope);
(input) in (input_scope) means (output) in (output_scope)
]],function(s1,e1,c1) -- A substitution
local input_scope = s1
if e1.input_scope then input_scope = Run(s1,e1.input_scope,c1,"means: input scope") end
local output_scope = s1
if e1.output_scope then output_scope = Run(s1,e1.output_scope,c1,"means: output scope") end
Bind(input_scope,e1.input,function(s2,e2,c2)
return Do(output_scope,e1.output,c2) -- TODO: chains?!
end)
end) end)
Bind(ns,"do (stuff) in (scope); do (stuff)",function(s,e,c)
s = s or Scope()
return Do(s,e.stuff,c)
end)
Bind(ns,"new table",function(s,e,c) return {} end)
local Hpath = "nel/helper.nel" local Hpath = "nellie/helper.nel"
local Hchain = Chain("in internal: "..Hpath) local Hchain = Chain("in internal: "..Hpath)
local success,result = Hchain:call(function(chain) local success,result = Hchain:call(function(chain)
R(NelliScope,interpret(read(Hpath),Hpath,chain),chain) print(Parse(read(Hpath),Hpath,chain))
Do(NelliScope,Parse(read(Hpath),Hpath,chain),chain)
end) end)
--[[ Documentation: --[[ Documentation:
(log (text)) -> Log text to the output stream (print?)
(a new scope) -> Becomes a created new scope (a new scope) -> Becomes a created new scope
(this scope) -> Becomes the current scope (this scope) -> Becomes the current scope
(macro) expands to (expansion) -> Becomes a binding which makes (macro) expands to (expansion) -> Becomes a binding which makes
@@ -296,4 +325,4 @@ end)
(expression) (expression)
--]] --]]
return ns return ns

28
nex.nel
View File

@@ -1,28 +0,0 @@
// NEX 4
here 'nex language' is [do {
here 'language' is [create new language]
return 'language'
}]
here (nex (expressions)) means [
do (expressions) in 'nex language'
]
(interpret 'input') means {
here: 'previous character' is [nothing]
iterate over [pattern match all "." in 'input' as {'this character'}] {
if ['previous character' is something] {
}
now: 'previous character' is 'this character';
}
}
loop (until 'termination') {
choose from [read console] as 'input' {
('input' matches "end") does { now: termination };
everything else does { interpret 'input' };
}
}

View File

@@ -4,6 +4,5 @@ then
echo "lua could not be found" echo "lua could not be found"
exit 1 exit 1
else else
lua main.lua "$@" lua -l nellie -e "Run'$@'"
fi fi

View File

@@ -1,205 +0,0 @@
let SessionLoad = 1
let s:so_save = &g:so | let s:siso_save = &g:siso | setg so=0 siso=0 | setl so=-1 siso=-1
let v:this_session=expand("<sfile>:p")
silent only
silent tabonly
cd ~/Programming/nel/1
if expand('%') == '' && !&modified && line('$') <= 1 && getline(1) == ''
let s:wipebuf = bufnr('%')
endif
let s:shortmess_save = &shortmess
if &shortmess =~ 'A'
set shortmess=aoOA
else
set shortmess=aoO
endif
badd +167 main.lua
badd +27 parse.lua
badd +0 term://~/Programming/nel/1//1946:/usr/bin/zsh
badd +0 interpret.lua
badd +0 test.nel
badd +191 helper.lua
argglobal
%argdel
$argadd ~/Programming/nel/1
edit parse.lua
let s:save_splitbelow = &splitbelow
let s:save_splitright = &splitright
set splitbelow splitright
wincmd _ | wincmd |
vsplit
1wincmd h
wincmd _ | wincmd |
split
1wincmd k
wincmd _ | wincmd |
vsplit
1wincmd h
wincmd w
wincmd w
wincmd w
wincmd _ | wincmd |
split
1wincmd k
wincmd w
let &splitbelow = s:save_splitbelow
let &splitright = s:save_splitright
wincmd t
let s:save_winminheight = &winminheight
let s:save_winminwidth = &winminwidth
set winminheight=0
set winheight=1
set winminwidth=0
set winwidth=1
exe '1resize ' . ((&lines * 24 + 19) / 39)
exe 'vert 1resize ' . ((&columns * 60 + 90) / 181)
exe '2resize ' . ((&lines * 24 + 19) / 39)
exe 'vert 2resize ' . ((&columns * 60 + 90) / 181)
exe '3resize ' . ((&lines * 12 + 19) / 39)
exe 'vert 3resize ' . ((&columns * 121 + 90) / 181)
exe '4resize ' . ((&lines * 24 + 19) / 39)
exe 'vert 4resize ' . ((&columns * 59 + 90) / 181)
exe '5resize ' . ((&lines * 12 + 19) / 39)
exe 'vert 5resize ' . ((&columns * 59 + 90) / 181)
argglobal
setlocal foldmethod=manual
setlocal foldexpr=v:lua.vim.treesitter.foldexpr()
setlocal foldmarker={{{,}}}
setlocal foldignore=#
setlocal foldlevel=0
setlocal foldminlines=1
setlocal foldnestmax=20
setlocal foldenable
silent! normal! zE
let &fdl = &fdl
let s:l = 267 - ((8 * winheight(0) + 12) / 24)
if s:l < 1 | let s:l = 1 | endif
keepjumps exe s:l
normal! zt
keepjumps 267
normal! 019|
lcd ~/Programming/nel/1
wincmd w
argglobal
if bufexists(fnamemodify("~/Programming/nel/1/interpret.lua", ":p")) | buffer ~/Programming/nel/1/interpret.lua | else | edit ~/Programming/nel/1/interpret.lua | endif
if &buftype ==# 'terminal'
silent file ~/Programming/nel/1/interpret.lua
endif
balt ~/Programming/nel/1/parse.lua
setlocal foldmethod=manual
setlocal foldexpr=v:lua.vim.treesitter.foldexpr()
setlocal foldmarker={{{,}}}
setlocal foldignore=#
setlocal foldlevel=0
setlocal foldminlines=1
setlocal foldnestmax=20
setlocal foldenable
silent! normal! zE
let &fdl = &fdl
let s:l = 58 - ((15 * winheight(0) + 12) / 24)
if s:l < 1 | let s:l = 1 | endif
keepjumps exe s:l
normal! zt
keepjumps 58
normal! 034|
lcd ~/Programming/nel/1
wincmd w
argglobal
if bufexists(fnamemodify("term://~/Programming/nel/1//1946:/usr/bin/zsh", ":p")) | buffer term://~/Programming/nel/1//1946:/usr/bin/zsh | else | edit term://~/Programming/nel/1//1946:/usr/bin/zsh | endif
if &buftype ==# 'terminal'
silent file term://~/Programming/nel/1//1946:/usr/bin/zsh
endif
balt ~/Programming/nel/1/parse.lua
setlocal foldmethod=manual
setlocal foldexpr=0
setlocal foldmarker={{{,}}}
setlocal foldignore=#
setlocal foldlevel=0
setlocal foldminlines=1
setlocal foldnestmax=20
setlocal foldenable
let s:l = 947 - ((11 * winheight(0) + 6) / 12)
if s:l < 1 | let s:l = 1 | endif
keepjumps exe s:l
normal! zt
keepjumps 947
normal! 040|
lcd ~/Programming/nel/1
wincmd w
argglobal
if bufexists(fnamemodify("~/Programming/nel/1/main.lua", ":p")) | buffer ~/Programming/nel/1/main.lua | else | edit ~/Programming/nel/1/main.lua | endif
if &buftype ==# 'terminal'
silent file ~/Programming/nel/1/main.lua
endif
balt ~/Programming/nel/1/helper.lua
setlocal foldmethod=manual
setlocal foldexpr=v:lua.vim.treesitter.foldexpr()
setlocal foldmarker={{{,}}}
setlocal foldignore=#
setlocal foldlevel=0
setlocal foldminlines=1
setlocal foldnestmax=20
setlocal foldenable
silent! normal! zE
let &fdl = &fdl
let s:l = 178 - ((21 * winheight(0) + 12) / 24)
if s:l < 1 | let s:l = 1 | endif
keepjumps exe s:l
normal! zt
keepjumps 178
normal! 012|
lcd ~/Programming/nel/1
wincmd w
argglobal
if bufexists(fnamemodify("~/Programming/nel/1/test.nel", ":p")) | buffer ~/Programming/nel/1/test.nel | else | edit ~/Programming/nel/1/test.nel | endif
if &buftype ==# 'terminal'
silent file ~/Programming/nel/1/test.nel
endif
balt ~/Programming/nel/1/main.lua
setlocal foldmethod=manual
setlocal foldexpr=0
setlocal foldmarker={{{,}}}
setlocal foldignore=#
setlocal foldlevel=0
setlocal foldminlines=1
setlocal foldnestmax=20
setlocal foldenable
silent! normal! zE
let &fdl = &fdl
let s:l = 5 - ((4 * winheight(0) + 6) / 12)
if s:l < 1 | let s:l = 1 | endif
keepjumps exe s:l
normal! zt
keepjumps 5
normal! 0
lcd ~/Programming/nel/1
wincmd w
3wincmd w
exe '1resize ' . ((&lines * 24 + 19) / 39)
exe 'vert 1resize ' . ((&columns * 60 + 90) / 181)
exe '2resize ' . ((&lines * 24 + 19) / 39)
exe 'vert 2resize ' . ((&columns * 60 + 90) / 181)
exe '3resize ' . ((&lines * 12 + 19) / 39)
exe 'vert 3resize ' . ((&columns * 121 + 90) / 181)
exe '4resize ' . ((&lines * 24 + 19) / 39)
exe 'vert 4resize ' . ((&columns * 59 + 90) / 181)
exe '5resize ' . ((&lines * 12 + 19) / 39)
exe 'vert 5resize ' . ((&columns * 59 + 90) / 181)
tabnext 1
if exists('s:wipebuf') && len(win_findbuf(s:wipebuf)) == 0 && getbufvar(s:wipebuf, '&buftype') isnot# 'terminal'
silent exe 'bwipe ' . s:wipebuf
endif
unlet! s:wipebuf
set winheight=1 winwidth=20
let &shortmess = s:shortmess_save
let &winminheight = s:save_winminheight
let &winminwidth = s:save_winminwidth
let s:sx = expand("<sfile>:p:r")."x.vim"
if filereadable(s:sx)
exe "source " . fnameescape(s:sx)
endif
let &g:so = s:so_save | let &g:siso = s:siso_save
set hlsearch
doautoall SessionLoadPost
unlet SessionLoad
" vim: set ft=vim :

View File

@@ -1,3 +0,0 @@
do {
print "wsg";
}

8
test.sh Executable file
View File

@@ -0,0 +1,8 @@
#/bin/sh
if ! command -v lua &> /dev/null
then
echo "lua could not be found"
exit 1
else
lua -i -v -W -l nellie tests/test.lua
fi

0
tests/print.nel Normal file
View File

9
tests/test.lua Normal file
View File

@@ -0,0 +1,9 @@
print("doing some tests")
local function test(file,result)
end
test("print.nel",[[hello world!]])
do
end