Module:Calculated table
Documentation for this module may be created at Module:Calculated table/doc
local p = {}
-- This is a simple Reverse Polish Notation evaluator.
-- It's not infix because edsrzf is lazy and didn't want to write an infix
-- parser.
-- If you'd like to write one and replace this one, please feel free.
local function evaluate(expr, env)
local stack = {}
local i = 1
while i <= #expr do
local c = expr:sub(i, i)
if c == '*' then
local x = table.remove(stack)
local y = table.remove(stack)
table.insert(stack, x*y)
elseif c == '/' then
local x = table.remove(stack)
local y = table.remove(stack)
table.insert(stack, x/y)
elseif c == '-' then
local x = table.remove(stack)
local y = table.remove(stack)
table.insert(stack, x-y)
elseif c == '+' then
local x = table.remove(stack)
local y = table.remove(stack)
table.insert(stack, x+y)
elseif tonumber(c) then
local match = expr:match("^%d+", i)
i = i + #match - 1 -- We'll add 1 more below
table.insert(stack, tonumber(match))
elseif c:match("%a") then
local match = expr:match("^%a+", i)
i = i + #match - 1 -- We'll add 1 more below
table.insert(stack, env[match])
end
i = i + 1
end
return table.remove(stack)
end
-- eval_func returns a function that evaluates the expression with a given environment.
local function eval_func(expr)
return function(env)
return evaluate(expr, env)
end
end
function p.table(frame)
local i = 4
while i <= #frame.args do
frame.args[i] = eval_func(frame.args[i])
i = i + 2
end
local inputs = {}
if frame.args.inputs then
for str in frame.args.inputs:gmatch("[^,]+") do
table.insert(inputs, str)
end
end
return p._table(frame.args[1], frame.args[2], {style = frame.args.style, format = frame.args.format, inputs = inputs}, unpack(frame.args, 3))
end
function p._table(base_heading, rows, opts, ...)
local headings = {base_heading}
local calculations = {}
for j = 1, rows do
calculations[j] = {}
end
local inputs = opts.inputs or {}
local i = 1
while i < arg.n do
table.insert(headings, arg[i])
local formula = arg[i+1]
for j, row in ipairs(calculations) do
row[(i+1)/2] = formula({x = inputs[j] or j-1})
end
i = i + 2
end
local result = {"{| "}
local style = opts.style or [[cellpadding="4" cellspacing="0" border="1" align="center" style="text-align:center"]]
table.insert(result, style)
table.insert(result, "\n|-\n| ")
for i, heading in ipairs(headings) do
if i > 1 then
table.insert(result, " || ")
end
table.insert(result, heading)
end
local format = function(s) return s end
if type(opts.format) == "function" then
format = opts.format
elseif type(opts.format) == "string" then
local formatstr = opts.format
format = function(s) return formatstr:format(s) end
end
for j, row in ipairs(calculations) do
table.insert(result, "\n|-\n| ")
table.insert(result, inputs[j] or j-1)
for i, col in ipairs(row) do
table.insert(result, " || ")
table.insert(result, format(col))
end
end
table.insert(result, "\n|}")
return table.concat(result)
end
return p