216 lines
8.5 KiB
Lua
Executable File
216 lines
8.5 KiB
Lua
Executable File
--------------------------------------------------------------------------------
|
|
---------------------- ## ##### ##### ###### -----------------------
|
|
---------------------- ## ## ## ## ## ## ## -----------------------
|
|
---------------------- ## ## ## ## ## ###### -----------------------
|
|
---------------------- ## ## ## ## ## ## -----------------------
|
|
---------------------- ###### ##### ##### ## -----------------------
|
|
---------------------- -----------------------
|
|
----------------------- Lua Object-Oriented Programming ------------------------
|
|
--------------------------------------------------------------------------------
|
|
-- Project: LOOP Class Library --
|
|
-- Release: 2.3 beta --
|
|
-- Title : Simple Expression Parser --
|
|
-- Author : Renato Maia <maia@inf.puc-rio.br> --
|
|
--------------------------------------------------------------------------------
|
|
|
|
local luaerror = error
|
|
local pairs = pairs
|
|
local ipairs = ipairs
|
|
local unpack = unpack
|
|
local select = select
|
|
|
|
require "string"
|
|
local oo = require "loop.base"
|
|
|
|
--[[VERBOSE]] local verbose = require("loop.debug.Verbose"){
|
|
--[[VERBOSE]] groups = { expression = {"operator","value","parse"} }
|
|
--[[VERBOSE]] }
|
|
--[[VERBOSE]] verbose:flag("expression", false)
|
|
|
|
module("loop.compiler.Expression", oo.class)
|
|
|
|
pos = 1
|
|
count = 0
|
|
precedence = {}
|
|
|
|
local pattern = "^ *%s"
|
|
function __init(self, object)
|
|
self = oo.rawnew(self, object)
|
|
|
|
if not self.operands and self.values then
|
|
local operands = {}
|
|
for kind, spec in pairs(self.values) do
|
|
operands[kind] = pattern:format(spec)
|
|
end
|
|
self.operands = operands
|
|
end
|
|
|
|
if not self.format and self.operators then
|
|
local opformat = {}
|
|
for name, spec in pairs(self.operators) do
|
|
local format = {}
|
|
local pos = 1
|
|
while pos <= #spec do
|
|
if spec:find("^ ", pos) then
|
|
format[#format+1] = true
|
|
pos = pos + 1
|
|
else
|
|
local keyword = spec:match("^[^ ]+", pos)
|
|
format[#format+1] = keyword
|
|
pos = pos + #keyword
|
|
end
|
|
end
|
|
opformat[name] = format
|
|
end
|
|
self.format = opformat
|
|
end
|
|
|
|
self.values = self.values or {}
|
|
|
|
return self
|
|
end
|
|
|
|
function push(self, kind, value)
|
|
self[#self+1] = kind
|
|
if kind == true then
|
|
self.count = self.count + 1
|
|
self.values[self.count] = value
|
|
end
|
|
end
|
|
|
|
function pop(self)
|
|
local kind = self[#self]
|
|
self[#self] = nil
|
|
local value
|
|
if kind == true then
|
|
value = self.values[self.count]
|
|
self.count = self.count - 1
|
|
end
|
|
return kind, value
|
|
end
|
|
|
|
function get(self, count)
|
|
local nvals = 0
|
|
for _=1, count do
|
|
if self[#self] == true then
|
|
nvals = nvals + 1
|
|
end
|
|
self[#self] = nil
|
|
end
|
|
self.count = self.count - nvals
|
|
return unpack(self.values, self.count + 1, self.count + nvals)
|
|
end
|
|
|
|
local errmsg = "%s at position %d"
|
|
function error(self, msg)
|
|
return luaerror(errmsg:format(msg, self.pos))
|
|
end
|
|
|
|
function done(self)
|
|
return (self.text:match("^%s*$", self.pos))
|
|
end
|
|
|
|
function token(self, token)
|
|
local pos = select(2, self.text:find("^%s*[^%s]", self.pos))
|
|
if pos and (self.text:find(token, pos, true) == pos) then
|
|
self.pos = pos + #token
|
|
return true
|
|
end
|
|
end
|
|
|
|
function match(self, pattern)
|
|
local first, last, value = self.text:find(pattern, self.pos)
|
|
if first then
|
|
self.pos = last + 1
|
|
return value
|
|
end
|
|
end
|
|
|
|
function operator(self, name, level, start)
|
|
local format = self.format[name]
|
|
for index, kind in ipairs(format) do
|
|
local parsed = self[start + index]
|
|
if parsed then
|
|
if parsed ~= kind then --[[VERBOSE]] verbose:operator("parsed value mismatch, got '",parsed,"' ('",kind,"' expected)")
|
|
return false --[[VERBOSE]] else verbose:operator("parsed value matched, got '",parsed,"'")
|
|
end
|
|
else
|
|
if kind == true then --[[VERBOSE]] verbose:operator(true, "operand expected, parsing...")
|
|
if not self:parse(level + 1, #self) then --[[VERBOSE]] verbose:operator(false, "operand parsing failed")
|
|
return false
|
|
end --[[VERBOSE]] verbose:operator(false, "operand parsed successfully")
|
|
else --[[VERBOSE]] verbose:operator("token ",kind," expected")
|
|
if self:token(kind) then --[[VERBOSE]] verbose:operator("token found successfully")
|
|
self:push(kind)
|
|
else --[[VERBOSE]] verbose:operator("token not found")
|
|
return false
|
|
end
|
|
end
|
|
end
|
|
end --[[VERBOSE]] verbose:operator(true, "operator ",name," matched")
|
|
self:push(true, self[name](self, self:get(#format))) --[[VERBOSE]] verbose:operator(false, "operator ",name," callback called")
|
|
return true
|
|
end
|
|
|
|
function value(self)
|
|
if self:token("(") then --[[VERBOSE]] verbose:value(true, "'(' found at ",self.pos)
|
|
local start = #self
|
|
if not self:parse(1, start) then --[[VERBOSE]] verbose:value(false, "error in enclosed expression")
|
|
self:error("value expected")
|
|
elseif #self ~= start + 1 or self[#self] ~= true then --[[VERBOSE]] verbose:value(false, "enclosed expression incomplete (too many parsed values left)")
|
|
self:error("incomplete expression")
|
|
elseif not self:token(")") then --[[VERBOSE]] verbose:value(false, "')' not found")
|
|
self:error("')' expected")
|
|
end --[[VERBOSE]] verbose:value(false, "matching ')' found")
|
|
return true
|
|
else --[[VERBOSE]] verbose:value(true, "parsing value at ",self.pos)
|
|
for kind, pattern in pairs(self.operands) do --[[VERBOSE]] verbose:value("attempt to match value as ",kind)
|
|
local value = self:match(pattern)
|
|
if value then --[[VERBOSE]] verbose:value(true, "value found as ",kind)
|
|
self:push(true, self[kind](self, value)) --[[VERBOSE]] verbose:value(false, "value evaluated to ",self.values[self.count])
|
|
return true --[[VERBOSE]],verbose:value(false)
|
|
end
|
|
end --[[VERBOSE]] verbose:value(false, "no value found at ",self.pos)
|
|
return false
|
|
end
|
|
end
|
|
|
|
function parse(self, level, start)
|
|
if not self:done() then
|
|
local ops = self.precedence[level]
|
|
if ops then --[[VERBOSE]] verbose:parse(true, "parsing operators of level ",level)
|
|
local i = 1
|
|
while ops[i] do
|
|
local op = ops[i] --[[VERBOSE]] verbose:parse(true, "attempt to match operator ",op)
|
|
if self:operator(ops[i], level, start) then --[[VERBOSE]] verbose:parse(false, "operator ",op," successfully matched")
|
|
i = 1
|
|
else --[[VERBOSE]] verbose:parse(false, "operator ",op," not matched")
|
|
i = i + 1
|
|
end
|
|
end
|
|
if #self == start then --[[VERBOSE]] verbose:parse(false, "no value evaluated by operators of level ",level)
|
|
return self:parse(level + 1, start)
|
|
elseif self[start + 1] == true then --[[VERBOSE]] verbose:parse(false, "values evaluated by operators of level ",level)
|
|
return true
|
|
end
|
|
else --[[VERBOSE]] verbose:parse(true, "parsing value")
|
|
return self:value() --[[VERBOSE]] ,verbose:parse(false)
|
|
end
|
|
end
|
|
end
|
|
|
|
function evaluate(self, text, pos)
|
|
if text then
|
|
self.text = text
|
|
self.pos = pos
|
|
end
|
|
if not self:parse(1, 0) then
|
|
self:error("parsing failed")
|
|
elseif not self:done() then
|
|
self:error("malformed expression")
|
|
elseif #self ~= 1 or self[1] ~= true then
|
|
self:error("incomplete expression")
|
|
end
|
|
return self:get(1)
|
|
end
|