wiremod-svn-archive/wire/lua/entities/gmod_wire_expression/parser.lua

554 lines
14 KiB
Lua
Raw Normal View History

2007-04-19 16:53:15 +00:00
WireGateExpressionParser = {}
WireGateExpressionParser.toktable = {
["add"] = "+",
["sub"] = "-",
["mul"] = "*",
["div"] = "/",
["mod"] = "%",
["exp"] = "^",
["aadd"] = "+=",
["asub"] = "-=",
["amul"] = "*=",
["adiv"] = "/=",
["amod"] = "%=",
["aexp"] = "^=",
["imp"] = "->",
["ass"] = "=",
["not"] = "!",
["gth"] = ">",
["lth"] = "<",
["eq"] = "==",
["neq"] = "!=",
["geq"] = ">=",
["leq"] = "<=",
["and"] = "&",
["or"] = "|",
["qst"] = "?",
["col"] = ":",
["sem"] = ";",
["com"] = ",",
["lpa"] = "(",
["rpa"] = ")",
["trg"] = "~",
["dlt"] = "$",
}
WireGateExpressionParser.optable = {
["+"] = {"add", {["="] = {"aadd"}}},
["-"] = {"sub", {["="] = {"asub"}, [">"] = {"imp"}}},
["*"] = {"mul", {["="] = {"amul"}}},
["/"] = {"div", {["="] = {"adiv"}}},
["%"] = {"mod", {["="] = {"amod"}}},
["^"] = {"exp", {["="] = {"aexp"}}},
["="] = {"ass", {["="] = {"eq"}}},
["!"] = {"not", {["="] = {"neq"}}},
[">"] = {"gth", {["="] = {"geq"}}},
["<"] = {"lth", {["="] = {"leq"}}},
["&"] = {"and"},
["|"] = {"or"},
["?"] = {"qst"},
[":"] = {"col"},
[";"] = {"sem"},
[","] = {"com"},
["("] = {"lpa"},
[")"] = {"rpa"},
["~"] = {"trg"},
["$"] = {"dlt"},
}
function WireGateExpressionParser:New(code, inputs, outputs)
local object = {
symtok = nil,
symarg = nil,
locals = {},
symbols = {},
symbindex = 1,
line = 1,
}
self.__index = self
self = setmetatable(object, self)
self:ParseSymbols(code)
self:NextSymbol()
self.instructions = self:expr1()
self.inputs = self:ParsePorts(inputs)
self.outputs = self:ParsePorts(outputs)
if #self.outputs == 0 then
self.outputs = self.inputs
end
if #self.outputs == 0 then
self:Error('No outputs defined')
end
local inputkeys = self.GetIndexTable(self.inputs)
local outputkeys = self.GetIndexTable(self.outputs)
local _locals = self.locals self.locals = {}
for key,_ in pairs(_locals) do
if not inputkeys[key] and not outputkeys[key] then
table.insert(self.locals, key)
end
end
return self
end
function WireGateExpressionParser:GetError()
return self.error
end
function WireGateExpressionParser:GetInstructions()
if self.error then return nil end
return self.instructions
end
function WireGateExpressionParser:GetLocals()
if self.error then return nil end
return self.locals
end
function WireGateExpressionParser:GetInputs()
if self.error then return nil end
return self.inputs
end
function WireGateExpressionParser:GetOutputs()
if self.error then return nil end
return self.outputs
end
function WireGateExpressionParser.GetIndexTable(tbl)
local keys = {}
for _,key in ipairs(tbl) do keys[key] = true end
return keys
end
function WireGateExpressionParser:Error(str)
if not self.error then self.error = str end
end
function WireGateExpressionParser:NextCharacter()
if self.position <= self.length then
self.character = string.sub(self.expression, self.position, self.position)
self.charvalue = string.byte(self.character)
self.position = self.position + 1
else
self.character = nil
self.charvalue = nil
end
end
function WireGateExpressionParser:ReadCharacter()
self.buffer = self.buffer .. self.character
self:NextCharacter()
end
function WireGateExpressionParser:ParseSymbols(str)
--[[
97 = a, 122 = z
65 = A, 90 = Z
--]]
if str == "" then str = "0" end
self.expression = str
self.position = 1
self.length = string.len(str)
self.buffer = ""
self:NextCharacter()
while true do
if self.character then
while self.character == " " or self.character == "\t" or self.character == "\n" or self.character == "\r" do
if self.character == "\n" then self.line = self.line + 1 end
self:NextCharacter()
end
if not self.character then break end
self.symarg = ""
self.buffer = ""
if self.character >= "0" and self.character <= "9" then
while self.character and self.character >= "0" and self.character <= "9" do self:ReadCharacter() end
if self.character and self.character == "." then
self:ReadCharacter()
-- error otherwise self:Error("Improper number layout")
while self.character and self.character >= "0" and self.character <= "9" do self:ReadCharacter() end
end
self.symtok = "num";
elseif self.charvalue >= 97 and self.charvalue <= 122 then
while self.character and (self.charvalue >= 97 and self.charvalue <= 122 or self.charvalue >= 65 and self.charvalue <= 90 or self.character >= "0" and self.character <= "9" or self.character == "_") do self:ReadCharacter() end
self.symtok = "fun"
elseif self.charvalue >= 65 and self.charvalue <= 90 or self.character == "_" then
while self.character and (self.charvalue >= 65 and self.charvalue <= 90 or self.charvalue >= 97 and self.charvalue <= 122 or self.character >= "0" and self.character <= "9" or self.character == "_") do self:ReadCharacter() end
self.symtok = "var"
elseif self.character == "" then
break
else
if !self:ParseOperator() then
self:Error("Unexpected character (" .. self.character .. ") at line " .. self.line)
self:NextCharacter()
end
end
self.symarg = self.buffer
else
break
end
table.insert(self.symbols, { self.symtok, self.symarg, self.line })
end
end
function WireGateExpressionParser:ParseOperator()
-- should be extended to automatically backtrack if failed (and is at nil, but passed a token that wasn't nil)
-- this is not entirely correct either
local op = self.optable
if op[self.character] then
while true do
op = op[self.character]
self:ReadCharacter()
if self.character then
if op[2] then
if op[2][self.character] then
op = op[2]
else
self.symtok = op[1]
self.symarg = self.buffer
return true
end
else
self.symtok = op[1]
self.symarg = self.buffer
return true
end
else
if op[1] then
self.symtok = op[1]
self.symarg = self.buffer
else
return false
end
end
end
else
return false
end
end
function WireGateExpressionParser:ParsePorts(ports)
local vals = {}
local keys = {}
ports = string.Explode(" ", string.Trim(ports))
for _,key in ipairs(ports) do
key = string.Trim(key)
if key ~= "" then
character = string.sub(key, 1, 1)
charvalue = string.byte(character)
if charvalue >= 65 and charvalue <= 90 or character == "_" then
for i=2,string.len(key) do
character = string.sub(key, i, i)
charvalue = string.byte(character)
if character and charvalue >= 65 and charvalue <= 90 or charvalue >= 97 and charvalue <= 122 or character >= "0" and character <= "9" or character == "_" then
else
self:Error("Invalid port name: " .. key)
end
end
else
self:Error("Invalid port name: " .. key)
end
if keys[key] then
self:Error("Duplicate port: " .. key)
else
keys[key] = true
table.insert(vals, key)
end
end
end
return vals
end
function WireGateExpressionParser:Accept(token)
if self.symtok == token then
self:NextSymbol()
return true;
else
return false;
end
end
function WireGateExpressionParser:Expect(token) -- outputs bad errors (lpa) etc
if self:Accept(token) then
return true;
else
if self.symtok then
if self.symtok == "fun" then
self:Error("Expected symbol (" .. self.toktable[token] .. ") near function (" .. self.symarg .. ") at line " .. self.line);
elseif self.symtok == "var" then
self:Error("Expected symbol (" .. self.toktable[token] .. ") near variable (" .. self.symarg .. ") at line " .. self.line);
elseif self.symtok == "num" then
self:Error("Expected symbol (" .. self.toktable[token] .. ") near number (" .. self.symarg .. ") at line " .. self.line);
else
self:Error("Expected symbol (" .. self.toktable[token] .. ") near (" .. self.toktable[self.symtok] .. ") at line " .. self.line);
end
else
self:Error("Expected symbol (" .. self.toktable[token] .. ") at line " .. self.line);
end
return false;
end
end
function WireGateExpressionParser:NextSymbol()
if self.symbindex <= #self.symbols then
self.symtok = self.symbols[self.symbindex][1]
self.symarg = self.symbols[self.symbindex][2]
self.line = self.symbols[self.symbindex][3]
self.symbindex = self.symbindex + 1
else
self.symtok = nil
self.symarg = nil
end
end
function WireGateExpressionParser:RecurseLeft(expr, tbl)
local expression = expr(self)
local ins = false
while true do
for key,value in pairs(tbl) do
if self:Accept(key) then
ins = true
expression = {value, expression, expr(self)}
break
end
end
if !ins then break end
ins = false
end
return expression
end
function WireGateExpressionParser:RecurseRight(expr, tbl)
local expression = expr(self)
for key,value in pairs(tbl) do
if self:Accept(key) then
return {value, expression, self:RecurseRight(expr, tbl)}
end
end
return expression
end
--[[ 1 : exp2 , exp2 exp1 ]]
function WireGateExpressionParser:expr1()
local expression = self:expr2()
if self:Accept("com") or self.symtok then
return {"seq", expression, self:expr1()}
else
return expression
end
end
--[[ 2 : exp4 , exp4->exp3; ]]
function WireGateExpressionParser:expr2()
local expression = self:expr4()
if self:Accept("imp") then
local expression = {"imp", expression, self:expr3()}
self:Expect("sem")
return expression
else
return expression
end
end
--[[ 3 : exp2 , exp2 exp3 ]]
function WireGateExpressionParser:expr3()
if self.symtok and self.symtok == "fun" and self.symarg == "end" then
self:NextSymbol()
return {"end"}
end
local expression = self:expr2()
if self:Accept("com") or self.symtok and self.symtok != "sem" then
return {"seq", expression, self:expr3()}
else
return expression
end
end
--[[ 4 : exp5 , var=exp4 , var+=exp4 ]] -- force for first level!
function WireGateExpressionParser:expr4()
local expression = self:expr5()
if expression and expression[1] == "var" then
local arg = expression
if self:Accept("ass") then
return {"ass", arg, self:expr4()}
elseif self:Accept("aadd") then
return {"aadd", arg, self:expr4()}
elseif self:Accept("asub") then
return {"asub", arg, self:expr4()}
elseif self:Accept("amul") then
return {"amul", arg, self:expr4()}
elseif self:Accept("adiv") then
return {"adiv", arg, self:expr4()}
elseif self:Accept("amod") then
return {"amod", arg, self:expr4()}
elseif self:Accept("aexp") then
return {"aexp", arg, self:expr4()}
end
end
return expression
end
--[[ 5 : exp6 , exp6?exp4:exp4 ]]
function WireGateExpressionParser:expr5()
local expression = self:expr6()
if self:Accept("qst") then
local exprtrue = self:expr4()
self:Expect("col")
return {"cnd", expression, exprtrue, self:expr4()}
else
return expression
end
end
--[[ 6 : exp7 , exp7|exp6 ]]
function WireGateExpressionParser:expr6()
return self:RecurseLeft(self.expr7, {["or"] = "or"})
end
--[[ 7 : exp8 , exp7&exp8 ]]
function WireGateExpressionParser:expr7()
return self:RecurseLeft(self.expr8, {["and"] = "and"})
end
--[[ 8 : exp9 , exp8==exp9 , exp8!=exp9 ]]
function WireGateExpressionParser:expr8()
return self:RecurseLeft(self.expr9, {["eq"] = "eq", ["neq"] = "neq"})
end
--[[ 9 : exp10, exp9>=exp10 , exp9<=exp10 , exp9>exp10 , exp9<exp10 ]]
function WireGateExpressionParser:expr9()
return self:RecurseLeft(self.expr10, {["geq"] = "geq", ["leq"] = "leq", ["gth"] = "gth", ["lth"] = "lth"})
end
--[[ 10: exp11, exp10+exp11 , exp10-exp11 ]]
function WireGateExpressionParser:expr10()
return self:RecurseLeft(self.expr11, {["add"] = "add", ["sub"] = "sub"})
end
--[[ 11: exp12, exp11*exp12 , exp11/exp12 , exp11%exp12 ]]
function WireGateExpressionParser:expr11()
return self:RecurseLeft(self.expr12, {["mul"] = "mul", ["div"] = "div", ["mod"] = "mod"})
end
--[[ 12: exp13, exp13^exp12 ]] -- left or right? (right is lua, left is normal calcs)
function WireGateExpressionParser:expr12()
return self:RecurseRight(self.expr13, {["exp"] = "exp"})
end
--[[ 13: exp14, -exp14, !exp14 ]]
function WireGateExpressionParser:expr13()
if self:Accept("sub") then
return {"neg", self:expr14()}
elseif self:Accept("not") then
return {"not", self:expr14()}
else
return self:expr14()
end
end
--[[ 14: exp15, (exp4), function(exp16) ]]
function WireGateExpressionParser:expr14()
local arg = self.symarg
if self:Accept("lpa") then
local expression = self:expr4()
self:Expect("rpa")
return expression
elseif self:Accept("fun") then
self:Expect("lpa")
if self:Accept("rpa") then
return {"fun", arg, {"nil"}}
else
local expression = self:expr16()
self:Expect("rpa")
return {"fun", arg, expression}
end
else
return self:expr15()
end
end
--[[ 15: number, variable ]]
function WireGateExpressionParser:expr15()
local arg = self.symarg
if self:Accept("num") then
return {"num", tonumber(arg)}
elseif self:Accept("var") then
self.locals[arg] = true
return {"var", arg}
elseif self:Accept("trg") then
arg = self.symarg
if self:Accept("var") then
return {"trg", arg}
else
self:Error("Expected variable near trigger (~) at line " .. self.line)
end
elseif self:Accept("dlt") then
arg = self.symarg
if self:Accept("var") then
return {"dlt", arg}
else
self:Error("Expected variable near delta ($) at line " .. self.line)
end
else
if self.symtok then
self:Error("Unexpected symbol (" .. self.symarg .. ") at line " .. self.line)
self.symbindex = -1 -- make sure there are no more
self.symtok = nil
self.symarg = nil
else
self:Error("Expected further input at line " .. self.line)
end
end
end
--[[ 16 : nil , exp4,exp16 ]]
function WireGateExpressionParser:expr16()
local parameters = {"prm", {"nil"}, self:expr4()}
while self:Accept("com") do
parameters = {"prm", parameters, self:expr4()}
end
return parameters
end