This commit is contained in:
2026-03-29 11:46:50 +01:00
parent e6f3b5c77f
commit 7aa282b405
8 changed files with 175 additions and 102 deletions

View File

@@ -72,6 +72,7 @@ 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")
@@ -84,31 +85,37 @@ function Consumer:consumePatterns(patterns,state)
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?
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 findex) or (findex > new_index))
((not earliest.index) or (earliest.index > 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(
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,
new_final - 1
earliest.index - 1
)
end
end
-- Pass into the func
if not ffunc then
if not callback then
if patterns[NONE] then
return patterns[NONE]()
end
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
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")
@@ -120,7 +127,7 @@ function Consumer:consumePatterns(patterns,state)
self.col = self.col + 1
end
end
return ffunc(fexcess,fmatch,state)
return callback(earliest,state)
end
-- Interpret some text
@@ -164,67 +171,119 @@ function Parse(content,uri,chain)
patterns.uriClose = ">"
patterns.singleLineComment = "//"
patterns.multiLineCommentOpen = "/%*"
patterns.multiLineCommentOpen = "%*/"
patterns.multiLineCommentClose = "%*/"
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})})
end
}))
end,
[patterns.named] = function(words,_,current)
current:insert(words,consumer:consumePatterns({
[patterns.named] = function(words,_)
return expression({"the",Expression({words})})
end
}))
end,
[patterns.multiLineStringOpen] = function(words,_,current)
current:insert(words,consumer:consumePatterns({
["[\n\r]"] = function()
error("Incomplete string literal")
end,
[patterns.multiLineStringClose] = function(words,_)
return expression({"text",Expression({words})})
end
}))
end,
[patterns.uriOpen] = function(words,_,current)
current:insert(words,consumer:consumePatterns({
["[\n\r]"] = function()
current:error("Incomplete URI literal")
end,
[patterns.uriClose] = function(path)
return read(path)
end
}))
end,
[patterns.expressionOpen] = function(words,_,current)
current:insert(words,consumeExpression())
end,
[patterns.singleLineComment] = function(words,_,current)
--current:insert(words) -- Consume what was left
consumer:consumePatterns({
[patterns.newLine] = function() end
})
end,
[patterns.multiLineCommentOpen] = function(words,_,current)
current:insert(words)
consumer:consumePatterns({
[patterns.multiLineCommentClose] = function() end
})
end,
[patterns.colonSyntax] = function(words,_,current)
current:insert(words,consumeExpression())
end,
[patterns.blockOpen] = function(words,_,current)
current:insert(words,consumeBlock())
end,
}
-- 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
@@ -233,7 +292,7 @@ function Parse(content,uri,chain)
local loop = true
while loop do
local remaining = consumer:remaining()
local expr = consumer:consumePatterns(union(expressionMeals,{
local expr = consumePatternsEscaping(union(expressionMeals,{
[patterns.blockDelimiter] = function(words,_)
current:insert(words)
if current:empty() then
@@ -261,7 +320,7 @@ function Parse(content,uri,chain)
local loop = true
while loop do
local remaining = consumer:remaining()
local expr = consumer:consumePatterns(union(expressionMeals,{
local expr = consumePatternsEscaping(union(expressionMeals,{
[patterns.expressionClose] = function(words,_,_current)
current:insert(words)
loop = false