Compare commits
6 Commits
fe581affb7
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 7aa282b405 | |||
| e6f3b5c77f | |||
| d7ca3739aa | |||
| b89e015170 | |||
| 7edad0c5bb | |||
| d600fc7232 |
@@ -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
|
|
||||||
@@ -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.
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
*/
|
|
||||||
196
interpret.lua
196
interpret.lua
@@ -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
|
|
||||||
@@ -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);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
14
nel/lc.nel
14
nel/lc.nel
@@ -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)
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
do {
|
|
||||||
|
|
||||||
} in ((module ("nel/translator/main.nel"))
|
|
||||||
@@ -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));
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
do {
|
|
||||||
here ((fart) means (print "fart"));
|
|
||||||
fart;
|
|
||||||
fart;
|
|
||||||
fart;
|
|
||||||
}
|
|
||||||
@@ -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
8
nellie/helper.nel
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
// single line comment
|
||||||
|
log "a";
|
||||||
|
/*
|
||||||
|
multi
|
||||||
|
line
|
||||||
|
comment
|
||||||
|
*/
|
||||||
|
log "b";
|
||||||
@@ -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({...})
|
|
||||||
@@ -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,9 +339,7 @@ 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,
|
||||||
@@ -465,4 +347,3 @@ function Scope:run(expression,chain,original)
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return _G
|
|
||||||
336
nellie/parser.lua
Normal file
336
nellie/parser.lua
Normal 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
|
||||||
@@ -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)
|
end)
|
||||||
B(ns,"(identifier) is (value)",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)
|
||||||
B(ns,"(expression) becomes (result)",function(s,e,c)
|
Bind(ns,"do (stuff) in (scope); do (stuff)",function(s,e,c)
|
||||||
|
s = s or Scope()
|
||||||
|
return Do(s,e.stuff,c)
|
||||||
end)
|
end)
|
||||||
B(ns,"let (binding) in (scope)",function(s,e,c)
|
Bind(ns,"new table",function(s,e,c) return {} end)
|
||||||
|
|
||||||
end)
|
local Hpath = "nellie/helper.nel"
|
||||||
|
|
||||||
local Hpath = "nel/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
|
||||||
28
nex.nel
28
nex.nel
@@ -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' };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
||||||
|
|
||||||
205
session.vim
205
session.vim
@@ -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 :
|
|
||||||
8
test.sh
Executable file
8
test.sh
Executable 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
0
tests/print.nel
Normal file
9
tests/test.lua
Normal file
9
tests/test.lua
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
print("doing some tests")
|
||||||
|
local function test(file,result)
|
||||||
|
|
||||||
|
end
|
||||||
|
test("print.nel",[[hello world!]])
|
||||||
|
|
||||||
|
do
|
||||||
|
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user