summaryrefslogtreecommitdiff
path: root/tex/context/base/m-spreadsheet.lua
diff options
context:
space:
mode:
Diffstat (limited to 'tex/context/base/m-spreadsheet.lua')
-rw-r--r--tex/context/base/m-spreadsheet.lua153
1 files changed, 109 insertions, 44 deletions
diff --git a/tex/context/base/m-spreadsheet.lua b/tex/context/base/m-spreadsheet.lua
index 7d8fe9e07..30980684f 100644
--- a/tex/context/base/m-spreadsheet.lua
+++ b/tex/context/base/m-spreadsheet.lua
@@ -6,8 +6,9 @@ if not modules then modules = { } end modules ['m-spreadsheet'] = {
license = "see context related readme files"
}
-local byte, format, gsub = string.byte, string.format, string.gsub
-local R, P, C, V, Cs, Cc, Carg, lpegmatch = lpeg.R, lpeg.P, lpeg.C, lpeg.V, lpeg.Cs, lpeg.Cc, lpeg.Carg, lpeg.match
+local byte, format, gsub, find = string.byte, string.format, string.gsub, string.find
+local R, P, S, C, V, Cs, Cc, Ct, Cg, Cf, Carg = lpeg.R, lpeg.P, lpeg.S, lpeg.C, lpeg.V, lpeg.Cs, lpeg.Cc, lpeg.Ct, lpeg.Cg, lpeg.Cf, lpeg.Carg
+local lpegmatch, patterns = lpeg.match, lpeg.patterns
local setmetatable, loadstring, next, tostring, tonumber,rawget = setmetatable, loadstring, next, tostring, tonumber, rawget
local context = context
@@ -61,18 +62,27 @@ function spreadsheets.setup(t)
end
end
+local function emptydata(name,settings)
+ local data = { }
+ local specifications = { }
+ local settings = settings or { }
+ setmetatable(data,d_mt)
+ setmetatable(specifications,d_mt)
+ setmetatable(settings,s_mt)
+ return {
+ name = name,
+ data = data,
+ maxcol = 0,
+ maxrow = 0,
+ settings = settings,
+ temp = { }, -- for local usage
+ specifications = specifications,
+ }
+end
+
function spreadsheets.reset(name)
if not name or name == "" then name = defaultname end
- local d = { }
- local s = { }
- setmetatable(d,d_mt)
- setmetatable(s,s_mt)
- data[name] = {
- name = name,
- data = d,
- settings = s,
- temp = { }, -- for local usage
- }
+ data[name] = emptydata(name,data[name] and data[name].settings)
end
function spreadsheets.start(name,s)
@@ -88,15 +98,7 @@ function spreadsheets.start(name,s)
setmetatable(s,s_mt)
data[current].settings = s
else
- local d = { }
- setmetatable(d,d_mt)
- setmetatable(s,s_mt)
- data[current] = {
- name = name,
- data = d,
- settings = s,
- temp = { },
- }
+ data[current] = emptydata(name,s)
end
end
@@ -125,22 +127,54 @@ function datacell(a,b,...)
return format("dat[%s]",n)
end
-local cell = C(R("AZ"))^1 / datacell * (Cc("[") * (R("09")^1) * Cc("]") + #P(1))
-local pattern = Cs((cell + P(1))^0)
+local function checktemplate(s)
+ if find(s,"%%") then
+ -- normal template
+ return s
+ elseif find(s,"@") then
+ -- tex specific template
+ return gsub(s,"@","%%")
+ else
+ -- tex specific quick template
+ return "%" .. s
+ end
+end
+
+local quoted = Cs(patterns.unquoted)
+local spaces = patterns.whitespace^0
+local cell = C(R("AZ"))^1 / datacell * (Cc("[") * (R("09")^1) * Cc("]") + #P(1))
+
+-- A nasty aspect of lpeg: Cf ( spaces * Cc("") * { "start" ... this will create a table that will
+-- be reused, so we accumulate!
+
+local pattern = Cf ( spaces * Ct("") * { "start",
+ start = V("value") + V("set") + V("format") + V("string") + V("code"),
+ value = Cg(P([[=]]) * spaces * Cc("kind") * Cc("value")) * V("code"),
+ set = Cg(P([[!]]) * spaces * Cc("kind") * Cc("set")) * V("code"),
+ format = Cg(P([[@]]) * spaces * Cc("kind") * Cc("format")) * spaces * Cg(Cc("template") * Cs(quoted/checktemplate)) * V("code"),
+ string = Cg(#S([["']]) * Cc("kind") * Cc("string")) * Cg(Cc("content") * quoted),
+ code = spaces * Cg(Cc("code") * Cs((cell + P(1))^0)),
+}, rawset)
local functions = { }
spreadsheets.functions = functions
-function functions.sum(c,f,t)
- if f and t then
- local r = 0
- for i=f,t do
+function functions._s_(row,col,c,f,t)
+ local r = 0
+ if f and t then -- f..t
+ -- ok
+ elseif f then -- 1..f
+ f, t = 1, f
+ else
+ f, t = 1, row - 1
+ end
+ for i=f,t do
+ local ci = c[i]
+ if type(ci) == "number" then
r = r + c[i]
end
- return r
- else
- return 0
end
+ return r
end
functions.fmt= string.tformat
@@ -150,6 +184,9 @@ local template = [[
local dat = _m_.data['%s'].data
local tmp = _m_.temp
local fnc = _m_.functions
+ local row = %s
+ local col = %s
+ function fnc.sum(...) return fnc._s_(row,col,...) end
local sum = fnc.sum
local fmt = fnc.fmt
return %s
@@ -167,17 +204,45 @@ local function propername(name)
end
end
+-- if name == "" then name = current if name == "" then name = defaultname end end
+
local function execute(name,r,c,str)
- -- if name == "" then name = current if name == "" then name = defaultname end end
- str = lpegmatch(pattern,str,1,name)
- str = format(template,name,str)
- local result = loadstring(str) -- utilities.lua.strippedloadstring(str,true) -- when tracing
- result = result and result() or 0
- data[name].data[c][r] = result
- if type(result) == "function" then
- return result()
- else
- return result
+ if str ~= "" then
+ local d = data[name]
+ if c > d.maxcol then
+ d.maxcol = c
+ end
+ if r > d.maxrow then
+ d.maxrow = r
+ end
+ local specification = lpegmatch(pattern,str,1,name)
+ d.specifications[c][r] = specification
+ local kind = specification.kind
+ if kind == "string" then
+ return specification.content or ""
+ else
+ local code = specification.code
+ if code and code ~= "" then
+ code = format(template,name,r,c,code or "")
+ local result = loadstring(code) -- utilities.lua.strippedloadstring(code,true) -- when tracing
+ result = result and result()
+ if type(result) == "function" then
+ result = result()
+ end
+ if type(result) == "number" then
+ d.data[c][r] = result
+ end
+ if not result then
+ -- nothing
+ elseif kind == "set" then
+ -- no return
+ elseif kind == "format" then
+ return format(specification.template,result)
+ else
+ return result
+ end
+ end
+ end
end
end
@@ -196,10 +261,10 @@ function spreadsheets.get(name,r,c,str)
else
local result = execute(name,r,c,str)
if result then
- if type(result) == "number" then
- dname.data[c][r] = result
- result = tostring(result)
- end
+-- if type(result) == "number" then
+-- dname.data[c][r] = result
+-- result = tostring(result)
+-- end
local settings = dname.settings
local split = settings.split
local period = settings.period