Proto: meaning etc.
This commit is contained in:
@@ -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
|
||||||
@@ -193,7 +200,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")
|
||||||
|
|||||||
3
nellie/helper.nel
Normal file
3
nellie/helper.nel
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
out "huh?";
|
||||||
|
// proto:
|
||||||
|
(FUCK!)
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
require("nelli")
|
local path = ...
|
||||||
|
require("nellie.proto")
|
||||||
|
|
||||||
local function main(args)
|
function Run(args)
|
||||||
-- 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(
|
||||||
@@ -13,15 +14,14 @@ local function main(args)
|
|||||||
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
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -1,31 +1,135 @@
|
|||||||
require("interpreter")
|
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
|
||||||
|
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 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 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,state)
|
||||||
|
end
|
||||||
|
|
||||||
-- TODO: This should really be a separate API and module
|
|
||||||
-- please break it out
|
|
||||||
-- Interpret some text
|
-- Interpret some text
|
||||||
function Parse(content,uri,chain)
|
function Parse(content,uri,chain)
|
||||||
-- LOCALS
|
-- 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
|
-- So that all functions have access to eachother
|
||||||
local consume,
|
local consume,
|
||||||
consumeExpression,
|
consumeExpression,
|
||||||
processWords,
|
consumeBlock
|
||||||
processClosure,
|
|
||||||
processOpening,
|
|
||||||
consumeBlock,
|
|
||||||
consumeText,
|
|
||||||
consumeNumber
|
|
||||||
|
|
||||||
|
|
||||||
local consumer = Consumer(content)
|
local consumer = Consumer(content)
|
||||||
local function expression(...)
|
local function expression(...)
|
||||||
@@ -38,93 +142,99 @@ function Parse(content,uri,chain)
|
|||||||
end
|
end
|
||||||
local baseExpression = expression()
|
local baseExpression = expression()
|
||||||
|
|
||||||
-- FUNCTIONS
|
local patterns = {}
|
||||||
-- Consume an expression, with its sub expressions
|
patterns.contentClose = "$"
|
||||||
-- given that an opening has just been consumed
|
-- The pattern for words inbetween expressions
|
||||||
local singleLineStringPattern = "\""
|
patterns.expressionWord = "(.*)"
|
||||||
local function singleLineStringMeal(current,match)
|
-- The patterns to open and close expressions,
|
||||||
return function(words,_)
|
-- the words scattered inbetween them will be matches
|
||||||
current:insert(words,consumer:consume({
|
patterns.expressionOpen = "%("
|
||||||
[match] = function(words,_)
|
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.multiLineCommentOpen = "%*/"
|
||||||
|
patterns.colonSyntax = ":"
|
||||||
|
-- TODO: I don't like that this structure does: open(stuff;close) instead of open(stuff)close
|
||||||
|
-- TODO: current:insert() is repeated in EVERY meal. I wonder if this should be fixed -__-
|
||||||
|
local expressionMeals = {
|
||||||
|
[patterns.singleLineString] = function(words,_,current)
|
||||||
|
current:insert(words,consumer:consumePatterns({
|
||||||
|
[patterns.singleLineString] = function(words,_)
|
||||||
return expression({"text",Expression({words})})
|
return expression({"text",Expression({words})})
|
||||||
end
|
end
|
||||||
}))
|
}))
|
||||||
|
end,
|
||||||
|
[patterns.named] = function(words,_,current)
|
||||||
|
current:insert(words,consumer:consumePatterns({
|
||||||
|
[patterns.named] = function(words,_)
|
||||||
|
return expression({"the",Expression({words})})
|
||||||
end
|
end
|
||||||
end
|
}))
|
||||||
local multiLineStringOpenPattern = "%[%["
|
end,
|
||||||
local multiLineStringClosePattern = "%]%]"
|
[patterns.multiLineStringOpen] = function(words,_,current)
|
||||||
local function multiLineStringMeal(current,match)
|
current:insert(words,consumer:consumePatterns({
|
||||||
return function(words,_)
|
|
||||||
current:insert(words,consumer:consume({
|
|
||||||
["[\n\r]"] = function()
|
["[\n\r]"] = function()
|
||||||
error("Incomplete string literal")
|
error("Incomplete string literal")
|
||||||
end,
|
end,
|
||||||
[match] = function(words,_)
|
[patterns.multiLineStringClose] = function(words,_)
|
||||||
return expression({"text",Expression({words})})
|
return expression({"text",Expression({words})})
|
||||||
end
|
end
|
||||||
}))
|
}))
|
||||||
end
|
end,
|
||||||
end
|
[patterns.uriOpen] = function(words,_,current)
|
||||||
local uriOpenPattern = "<"
|
current:insert(words,consumer:consumePatterns({
|
||||||
local uriClosePattern = ">"
|
|
||||||
local function URIMeal(current,match)
|
|
||||||
return function(words,_)
|
|
||||||
current:insert(words,consumer:consume({
|
|
||||||
["[\n\r]"] = function()
|
["[\n\r]"] = function()
|
||||||
current:error("Incomplete URI literal")
|
current:error("Incomplete URI literal")
|
||||||
end,
|
end,
|
||||||
[match] = function(path)
|
[patterns.uriClose] = function(path)
|
||||||
return read(path)
|
return read(path)
|
||||||
end
|
end
|
||||||
}))
|
}))
|
||||||
end
|
end,
|
||||||
end
|
[patterns.expressionOpen] = function(words,_,current)
|
||||||
local function expressionMeal(current)
|
|
||||||
return function(words,_)
|
|
||||||
current:insert(words,consumeExpression())
|
current:insert(words,consumeExpression())
|
||||||
end
|
end,
|
||||||
end
|
[patterns.singleLineComment] = function(words,_,current)
|
||||||
local singleLineCommentPattern = "//"
|
--current:insert(words) -- Consume what was left
|
||||||
local function singleLineCommentMeal(current)
|
consumer:consumePatterns({
|
||||||
return function(words,_)
|
[patterns.newLine] = function() end
|
||||||
current:insert(words) -- Consume what was left
|
|
||||||
consumer:consume({
|
|
||||||
["[\n\r]"] = function() end
|
|
||||||
})
|
})
|
||||||
end
|
end,
|
||||||
end
|
[patterns.multiLineCommentOpen] = function(words,_,current)
|
||||||
local multiLineCommentPattern = "/%*"
|
|
||||||
local function multiLineCommentMeal(current)
|
|
||||||
return function(words,_) -- consume what was left
|
|
||||||
current:insert(words)
|
current:insert(words)
|
||||||
consumer:consume({
|
consumer:consumePatterns({
|
||||||
["%*/"] = function() end
|
[patterns.multiLineCommentClose] = function() end
|
||||||
})
|
})
|
||||||
end
|
end,
|
||||||
end
|
[patterns.colonSyntax] = function(words,_,current)
|
||||||
local colonSyntaxPattern = ":"
|
current:insert(words,consumeExpression())
|
||||||
local function colonSyntaxMeal(current)
|
end,
|
||||||
return function(words,_)
|
[patterns.blockOpen] = function(words,_,current)
|
||||||
current:insert(words,consumeColonExpression())
|
current:insert(words,consumeBlock())
|
||||||
end
|
end,
|
||||||
end
|
}
|
||||||
function consumeBlock()
|
-- Consume a {} block of code
|
||||||
|
function consumeBlock(closing_pattern)
|
||||||
|
closing_pattern = closing_pattern or patterns.blockClose
|
||||||
local expressions = {}
|
local expressions = {}
|
||||||
local current = expression()
|
local current = expression()
|
||||||
local loop = true
|
local loop = true
|
||||||
while loop do
|
while loop do
|
||||||
local expr = consumer:consume({
|
local remaining = consumer:remaining()
|
||||||
[multiLineCommentPattern] = multiLineCommentMeal(current),
|
local expr = consumer:consumePatterns(union(expressionMeals,{
|
||||||
[singleLineCommentPattern] = singleLineCommentMeal(current),
|
[patterns.blockDelimiter] = function(words,_)
|
||||||
[uriOpenPattern] = URIMeal(current,uriClosePattern),
|
|
||||||
[expressionOpenPattern] = expressionMeal(current),
|
|
||||||
[multiLineStringOpenPattern] =
|
|
||||||
multiLineStringMeal(
|
|
||||||
current,multiLineStringClosePattern),
|
|
||||||
[singleLineStringPattern] =
|
|
||||||
singleLineStringMeal(
|
|
||||||
current,singleLineStringPattern),
|
|
||||||
[blockDelimiterPattern] = function(words,_)
|
|
||||||
current:insert(words)
|
current:insert(words)
|
||||||
if current:empty() then
|
if current:empty() then
|
||||||
--error("Extravenous semicolon.")
|
--error("Extravenous semicolon.")
|
||||||
@@ -133,17 +243,11 @@ function Parse(content,uri,chain)
|
|||||||
end
|
end
|
||||||
current = expression()
|
current = expression()
|
||||||
end,
|
end,
|
||||||
[blockClosePattern] = function(words,_)
|
[closing_pattern] = function(words,_)
|
||||||
current:insert(words)
|
current:insert(words)
|
||||||
loop = false
|
loop = false
|
||||||
end,
|
end,
|
||||||
[blockOpenPattern] = function(words,_)
|
}),current)
|
||||||
current:insert(
|
|
||||||
words,
|
|
||||||
consumeBlock()
|
|
||||||
)
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
end
|
end
|
||||||
if #current.items ~= 0 then
|
if #current.items ~= 0 then
|
||||||
table.insert(expressions,current)
|
table.insert(expressions,current)
|
||||||
@@ -157,38 +261,17 @@ function Parse(content,uri,chain)
|
|||||||
local loop = true
|
local loop = true
|
||||||
while loop do
|
while loop do
|
||||||
local remaining = consumer:remaining()
|
local remaining = consumer:remaining()
|
||||||
local expr = consumer:consume({
|
local expr = consumer:consumePatterns(union(expressionMeals,{
|
||||||
[multiLineCommentPattern] = multiLineCommentMeal(current),
|
[patterns.expressionClose] = function(words,_,_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)
|
current:insert(words)
|
||||||
loop = false
|
loop = false
|
||||||
end,
|
|
||||||
[blockOpenPattern] = function(words,_)
|
|
||||||
current:insert(
|
|
||||||
words,
|
|
||||||
consumeBlock()
|
|
||||||
)
|
|
||||||
end,
|
|
||||||
["$"] = function(words,last)
|
|
||||||
current:insert(remaining)
|
|
||||||
loop = false
|
|
||||||
end
|
end
|
||||||
})
|
}),current)
|
||||||
-- This single line below made me procrastinate life for a total of 4 hours on instagram reels; lock in.
|
-- 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
|
-- if not expr then print("brk") break end -- Since now closing
|
||||||
end
|
end
|
||||||
return current
|
return current
|
||||||
end
|
end
|
||||||
return consumeExpression()
|
return consumeBlock(patterns.contentClose)
|
||||||
-- TODO: check for later expressions please?
|
-- TODO: check for later expressions please?
|
||||||
end
|
end
|
||||||
@@ -1,13 +1,14 @@
|
|||||||
-- Prototype for nel
|
-- Prototype for nel
|
||||||
require("interpreter")
|
require("nellie.parser")
|
||||||
|
|
||||||
function Bind(scope,abstract,callback)
|
function Bind(scope,abstract,callback)
|
||||||
scope:insert(
|
for _,expression in pairs(Parse(abstract)) do
|
||||||
Binding(
|
assert_meta(Expression)(expression)
|
||||||
Parse(abstract),
|
scope:insert(Binding(
|
||||||
|
expression,
|
||||||
callback
|
callback
|
||||||
)
|
))
|
||||||
)
|
end
|
||||||
end
|
end
|
||||||
function Run(scope,expression,chain,name)
|
function Run(scope,expression,chain,name)
|
||||||
name = name or "?"
|
name = name or "?"
|
||||||
@@ -17,6 +18,13 @@ function Run(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
|
||||||
@@ -270,13 +278,37 @@ end)
|
|||||||
Bind(ns,"let (binding) in (scope)",function(s,e,c)
|
Bind(ns,"let (binding) in (scope)",function(s,e,c)
|
||||||
|
|
||||||
end)
|
end)
|
||||||
|
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,"print (text)",function(s,e,c)
|
Bind(ns,"(closed)",function(s,e,c) return Run(s,e.closed,c,"(...)") end)
|
||||||
log(Run(s,e.text,c,"print: text"))
|
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 do (output);
|
||||||
|
(input) in (input_scope) means do (output);
|
||||||
|
(input) means do (output) in (output_scope);
|
||||||
|
(input) in (input_scope) means do (output) in (output_scope)
|
||||||
|
]],function(s,e,c1) -- A substitution
|
||||||
|
local input_scope = s
|
||||||
|
if e.input_scope then input_scope = Run(s,e.input_scope,c1,"means: input scope") end
|
||||||
|
local output_scope = s
|
||||||
|
if e.output_scope then output_scope = Run(s,e.output_scope,c2,"means: output scope") end
|
||||||
|
Bind(e.input_scope,e.input,function(s,e,c2)
|
||||||
|
return Do(e.output_scope,e.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)
|
||||||
Run(NelliScope,Parse(read(Hpath),Hpath,chain),chain)
|
Run(NelliScope,Parse(read(Hpath),Hpath,chain),chain)
|
||||||
@@ -284,8 +316,6 @@ end)
|
|||||||
|
|
||||||
--[[ Documentation:
|
--[[ Documentation:
|
||||||
(log (text)) -> Log text to the output stream (print?)
|
(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
|
||||||
|
|||||||
@@ -4,5 +4,5 @@ then
|
|||||||
echo "lua could not be found"
|
echo "lua could not be found"
|
||||||
exit 1
|
exit 1
|
||||||
else
|
else
|
||||||
lua index.lua "$@"
|
lua -i -v -W -l nellie -e Run "$@"
|
||||||
fi
|
fi
|
||||||
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
|
||||||
@@ -1 +1,5 @@
|
|||||||
require("")
|
print("doing some tests")
|
||||||
|
local function test(file,result)
|
||||||
|
|
||||||
|
end
|
||||||
|
test("print.nel",[[hello world!]])
|
||||||
Reference in New Issue
Block a user