Module:Calculated table

From CrawlWiki
Revision as of 04:07, 1 July 2015 by Edsrzf (talk | contribs) (Start calculating from 0; allow specifying inputs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

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