forked from endernon/PGRData
254 lines
5.7 KiB
Lua
254 lines
5.7 KiB
Lua
|
-- require("XCommon/XClass")
|
||
|
-- require("XCommon/XGlobalFunc")
|
||
|
|
||
|
local function split(input, delimiter)
|
||
|
input = tostring(input)
|
||
|
delimiter = tostring(delimiter)
|
||
|
if (delimiter=='') then return false end
|
||
|
local pos,arr = 0, {}
|
||
|
for st,sp in function() return string.find(input, delimiter, pos, true) end do
|
||
|
table.insert(arr, string.sub(input, pos, st - 1))
|
||
|
pos = sp + 1
|
||
|
end
|
||
|
table.insert(arr, string.sub(input, pos))
|
||
|
return arr
|
||
|
end
|
||
|
|
||
|
---------------------------------------------------------------
|
||
|
-- @class module
|
||
|
-- @name Stack
|
||
|
-- @author liqiang
|
||
|
local Stack = XClass(nil, "Stack")
|
||
|
|
||
|
--[[--
|
||
|
得到栈内元素数量
|
||
|
@treturn number size 元素个数
|
||
|
]]
|
||
|
function Stack:size()
|
||
|
return #self
|
||
|
end
|
||
|
|
||
|
--[[--
|
||
|
栈是否为空
|
||
|
@treturn boolean b true为空,否则为不空
|
||
|
]]
|
||
|
function Stack:empty()
|
||
|
return (self:size() == 0)
|
||
|
end
|
||
|
|
||
|
--[[--
|
||
|
出栈
|
||
|
@treturn void value 返回删除的元素,空返回nil
|
||
|
]]
|
||
|
function Stack:pop()
|
||
|
local tmp = self:top()
|
||
|
table.remove(self)
|
||
|
return tmp
|
||
|
end
|
||
|
|
||
|
--[[--
|
||
|
入栈
|
||
|
@tparam void value 入栈的元素
|
||
|
@treturn number size 入栈后元素个数
|
||
|
]]
|
||
|
function Stack:push(value)
|
||
|
table.insert(self, value)
|
||
|
return self:size()
|
||
|
end
|
||
|
|
||
|
--[[--
|
||
|
得到栈顶元素
|
||
|
@treturn void value 栈顶元素,空返回nil
|
||
|
]]
|
||
|
function Stack:top()
|
||
|
local size = self:size()
|
||
|
if size == 0 then
|
||
|
return nil
|
||
|
end
|
||
|
return self[size]
|
||
|
end
|
||
|
|
||
|
local XArithmetic = XClass(nil, "XArithmetic")
|
||
|
|
||
|
local defaultOperatorLevel = {
|
||
|
["+"] = 0,
|
||
|
["-"] = 0,
|
||
|
["("] = 10,
|
||
|
["*"] = 1,
|
||
|
["/"] = 1,
|
||
|
[")"] = 0,
|
||
|
["<"] = 1,
|
||
|
[">"] = 1,
|
||
|
["^"] = 2,
|
||
|
}
|
||
|
|
||
|
local defaultOperatorPattern = {
|
||
|
["%+"] = "+",
|
||
|
["%-"] = "-",
|
||
|
["%("] = "(",
|
||
|
["%*"] = "*",
|
||
|
["/"] = "/",
|
||
|
["%)"] = ")",
|
||
|
["<"] = "<",
|
||
|
[">"] = ">",
|
||
|
["%^"] = "^",
|
||
|
}
|
||
|
|
||
|
local charsReg = "^%a+%d*$"
|
||
|
|
||
|
function XArithmetic:Ctor()
|
||
|
self.OperatorLevel = defaultOperatorLevel
|
||
|
self.OperatorPattern = defaultOperatorPattern
|
||
|
self.GetVariableDelegate = nil
|
||
|
end
|
||
|
|
||
|
function XArithmetic:SetTextValueHandler(handle)
|
||
|
self.GetVariableDelegate = handle
|
||
|
end
|
||
|
|
||
|
function XArithmetic:Calculate(expression)
|
||
|
local rpnExperssion = self:ConvertToRPN(self:InsertBlank(expression))
|
||
|
return self:GetResultByExperssion(rpnExperssion)
|
||
|
end
|
||
|
|
||
|
function XArithmetic:GetValue(left, right, operator)
|
||
|
if "+" == operator then
|
||
|
return left + right
|
||
|
elseif "-" == operator then
|
||
|
return left - right
|
||
|
elseif "*" == operator then
|
||
|
return left * right
|
||
|
elseif "/" == operator then
|
||
|
return left / right
|
||
|
elseif "<" == operator then
|
||
|
return math.min(left, right)
|
||
|
elseif ">" == operator then
|
||
|
return math.max(left, right)
|
||
|
elseif "^" == operator then
|
||
|
return math.pow(left, right)
|
||
|
end
|
||
|
XLog.Warning("XArithmetic:GetValue error! operator: %s", operator)
|
||
|
return 0
|
||
|
end
|
||
|
|
||
|
function XArithmetic:GetResultByExperssion(source)
|
||
|
local operatorLevel = self.OperatorLevel
|
||
|
local stack = Stack.New()
|
||
|
local list = split(source, " ")
|
||
|
for i, current in ipairs(list) do
|
||
|
if tonumber(current) then
|
||
|
stack:push(tonumber(current))
|
||
|
elseif string.match(current, charsReg) then
|
||
|
stack:push(current)
|
||
|
elseif operatorLevel[current] then
|
||
|
local right = self:GetValueByText(stack:pop())
|
||
|
local left = self:GetValueByText(stack:pop())
|
||
|
stack:push(self:GetValue(left, right, string.sub(current, 1, 1)))
|
||
|
end
|
||
|
end
|
||
|
return stack:pop(), #stack
|
||
|
end
|
||
|
|
||
|
function XArithmetic:GetValueByText(text)
|
||
|
if self.GetVariableDelegate then
|
||
|
return self.GetVariableDelegate(text)
|
||
|
end
|
||
|
if tonumber(text) then
|
||
|
return tonumber(text)
|
||
|
end
|
||
|
XLog.Warning("XArithmetic.GetVariableDelegate == nil")
|
||
|
return 1
|
||
|
end
|
||
|
|
||
|
function XArithmetic:ConvertToRPN(source)
|
||
|
local operatorLevel = self.OperatorLevel
|
||
|
local result = ""
|
||
|
local stack = Stack.New()
|
||
|
local list = split(source, " ")
|
||
|
for i, current in ipairs(list) do
|
||
|
-- log("current %s", current)
|
||
|
if tonumber(current) then
|
||
|
result = result..current.." "
|
||
|
elseif string.match(current, charsReg) then
|
||
|
result = result..current.." "
|
||
|
elseif operatorLevel[current] then
|
||
|
if #stack > 0 then
|
||
|
local prev = stack:top()
|
||
|
-- log("prev %s", prev)
|
||
|
if prev == "(" then
|
||
|
stack:push(current)
|
||
|
elseif current == "(" then
|
||
|
stack:push(current)
|
||
|
elseif current == ")" then
|
||
|
while #stack > 0 and stack:top() ~= "(" do
|
||
|
result = result..stack:pop().." "
|
||
|
end
|
||
|
--Pop the "("
|
||
|
if #stack > 0 and stack:top() == "(" then
|
||
|
stack:pop()
|
||
|
end
|
||
|
elseif operatorLevel[current] <= operatorLevel[prev] then
|
||
|
while #stack > 0 do
|
||
|
local top = stack:pop()
|
||
|
if top ~= "(" and top ~= ")" and operatorLevel[current] <= operatorLevel[top] then
|
||
|
result = result..top.." "
|
||
|
else
|
||
|
-- break
|
||
|
stack:push(top)
|
||
|
break
|
||
|
end
|
||
|
end
|
||
|
stack:push(current)
|
||
|
else
|
||
|
stack:push(current)
|
||
|
end
|
||
|
else
|
||
|
stack:push(current)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local text = ""
|
||
|
local i = 1
|
||
|
while stack[i] ~= nil do
|
||
|
text = text.." "..stack[i]
|
||
|
i = i + 1
|
||
|
end
|
||
|
-- log(" "..text)
|
||
|
end
|
||
|
if #stack > 0 then
|
||
|
while #stack > 0 do
|
||
|
local top = stack:pop()
|
||
|
if top ~= "(" and top ~= ")" then
|
||
|
result = result..top.." "
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
-- log(result)
|
||
|
return result
|
||
|
end
|
||
|
|
||
|
function XArithmetic:InsertBlank(source)
|
||
|
for p, v in pairs(self.OperatorPattern) do
|
||
|
source = string.gsub(source, p, " "..v.." ")
|
||
|
end
|
||
|
return source
|
||
|
end
|
||
|
|
||
|
return XArithmetic
|
||
|
|
||
|
-- local function Test(expression, result)
|
||
|
-- local realyResult = XArithmetic:Calculate(expression);
|
||
|
-- XLog.Warning(realyResult, result)
|
||
|
-- if realyResult ~= result then
|
||
|
-- XLog.Warning("Formula.Calculate(\"%s\") != %d, realy result = %d", expression, result, realyResult)
|
||
|
-- else
|
||
|
-- XLog.Debug("XArithmetic test ok")
|
||
|
-- end
|
||
|
-- end
|
||
|
|
||
|
-- XLog.Warning("===========asdasd")
|
||
|
-- Test("4-2-1+3", 4)
|
||
|
-- Test("1+1*(2+1)-3.8/2 + 1 * (2+1)", 5.1)
|
||
|
-- Test("1000+0+0+0+0-0-0-0+0-100-0-0-0", 900)
|
||
|
-- Test("1000-0-100", 900)
|
||
|
-- Test("0-2*(3+4*6/5)", -15.6)
|