summaryrefslogtreecommitdiff
path: root/tex/context/base/mkiv/mlib-scn.lmt
diff options
context:
space:
mode:
Diffstat (limited to 'tex/context/base/mkiv/mlib-scn.lmt')
-rw-r--r--tex/context/base/mkiv/mlib-scn.lmt795
1 files changed, 795 insertions, 0 deletions
diff --git a/tex/context/base/mkiv/mlib-scn.lmt b/tex/context/base/mkiv/mlib-scn.lmt
new file mode 100644
index 000000000..5655c507d
--- /dev/null
+++ b/tex/context/base/mkiv/mlib-scn.lmt
@@ -0,0 +1,795 @@
+if not modules then modules = { } end modules ['mlib-scn'] = {
+ version = 1.001,
+ comment = "companion to mlib-ctx.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files",
+}
+
+-- Very experimental, for Alan and me.
+
+-- for i = 1 upto 32000 : % 0.062
+-- ts := 5mm / 20;
+-- endfor ;
+--
+-- for i = 1 upto 32000 : % 0.219
+-- ts := (getparameter "axis" "sy") / 20;
+-- endfor ;
+--
+-- for i = 1 upto 32000 : % 0.266
+-- ts := (getparameterx "axis" "sy") / 20;
+-- endfor ;
+--
+-- pushparameters "axis";
+-- for i = 1 upto 32000 : % 0.250
+-- ts := (getparameterx "sy") / 20;
+-- endfor ;
+-- popparameters;
+
+local type, next, rawget, getmetatable = type, next, rawget, getmetatable
+local byte, gmatch = string.byte, string.gmatch
+local insert, remove = table.insert, table.remove
+
+local mplib = mplib
+local metapost = metapost
+
+local codes = mplib.codes()
+local types = mplib.types()
+
+table.hashed(codes)
+table.hashed(types)
+
+metapost.codes = codes
+metapost.types = types
+
+local setmetatableindex = table.setmetatableindex
+
+local scanners = mp.scan
+local injectors = mp.inject
+
+local scannext = scanners.next
+local scanexpression = scanners.expression
+local scantoken = scanners.token
+local scansymbol = scanners.symbol
+local scannumeric = scanners.numeric
+local scannumber = scanners.number
+local scaninteger = scanners.integer
+local scanboolean = scanners.boolean
+local scanstring = scanners.string
+local scanpair = scanners.pair
+local scancolor = scanners.color
+local scancmykcolor = scanners.cmykcolor
+local scantransform = scanners.transform
+local scanpath = scanners.path
+local scanpen = scanners.pen
+
+local mpprint = mp.print
+local injectnumeric = injectors.numeric
+local injectstring = injectors.string
+local injectboolean = injectors.boolean
+local injectpair = injectors.pair
+local injecttriplet = injectors.color
+local injectquadruple = injectors.cmykcolor
+local injecttransform = injectors.transform
+
+local report = logs.reporter("metapost")
+
+local semicolon_code = codes.semicolon
+local equals_code = codes.equals
+local comma_code = codes.comma
+local colon_code = codes.colon
+local leftbrace_code = codes.leftbrace
+local rightbrace_code = codes.rightbrace
+local leftbracket_code = codes.leftbracket
+local rightbracket_code = codes.rightbracket
+local leftdelimiter_code = codes.leftdelimiter
+local rightdelimiter_code = codes.rightdelimiter
+local numeric_code = codes.numeric
+local string_code = codes.string
+local capsule_code = codes.capsule
+local nullary_code = codes.nullary
+local tag_code = codes.tag
+local definedmacro_code = codes.definedmacro
+
+local typescanners = nil
+local tokenscanners = nil
+local scanset = nil
+local scanparameters = nil
+
+local injectpath = mp.path
+
+do -- todo: this will become an overload
+
+ local flush = mp.path
+ local inject = injectors.path -- work in progress
+
+ injectpath = function(p,connector,close)
+ if #p > 1 then
+ if connector == true or connector == ".." then
+ return inject(p,false,close or p.close)
+ elseif connector == "--" then
+ return inject(p,true,close or p.close)
+ end
+ end
+ return flush(p,connector,close)
+ end
+
+end
+
+scanset = function() -- can be optimized, we now read twice
+ scantoken()
+ if scantoken(true) == rightbrace_code then
+ scantoken()
+ return { }
+ else
+ local l = { }
+ local i = 0
+ while true do
+ i = i + 1
+ local s = scansymbol(true)
+ if s == "{" then
+ l[i] = scanset()
+ elseif s == "[" then
+ local d = { }
+ scansymbol()
+ while true do
+ local s = scansymbol()
+ if s == "]" then
+ break;
+ elseif s == "," then
+ -- continue
+ else
+ local t = scantoken(true)
+ if t == equals_code or t == colon_code then
+ scantoken()
+ end
+ d[s] = tokenscanners[scantoken(true)]()
+ end
+ end
+ l[i] = d
+ else
+ local e = scanexpression(true)
+ l[i] = (typescanners[e] or scanexpression)()
+ end
+ if scantoken() == rightbrace_code then
+ break
+ else
+ -- whatever
+ end
+ end
+ return l
+ end
+end
+
+local function scan_pair () return scanpair (true) end
+local function scan_color () return scancolor (true) end
+local function scan_cmykcolor() return scancmykcolor(true) end
+local function scan_transform() return scantransform(true) end
+
+tokenscanners = {
+ [leftbrace_code] = scanset,
+ [numeric_code] = scannumeric,
+ [string_code] = scanstring,
+ [nullary_code] = scanboolean, -- todo
+}
+
+typescanners = {
+ [types.known] = scannumeric,
+ [types.numeric] = scannumeric,
+ [types.string] = scanstring,
+ [types.boolean] = scanboolean,
+ [types.pair] = scan_pair,
+ [types.color] = scan_color,
+ [types.cmykcolor] = scan_cmykcolor,
+ [types.transform] = scan_transform,
+ [types.path] = scanpath,
+ [types.pen] = scanpen,
+}
+
+table.setmetatableindex(tokenscanners,function()
+ local e = scanexpression(true)
+ return typescanners[e] or scanexpression
+end)
+
+-- a key like 'color' has code 'declare'
+
+local function scanparameters(fenced)
+ local data = { }
+ local close = "]"
+ if not fenced then
+ close = ";"
+ elseif scansymbol(true) == "[" then
+ scansymbol()
+ else
+ return data
+ end
+ while true do
+ -- local s = scansymbol()
+ local s = scansymbol(false,false)
+ if s == close then
+ break;
+ elseif s == "," then
+ -- continue
+ else
+ local t = scantoken(true)
+ if t == equals_code or t == colon_code then
+ -- optional equal or :
+ scantoken()
+ end
+ local kind = scantoken(true)
+ if kind == leftdelimiter_code or kind == tag_code or kind == capsule_code then
+ kind = scanexpression(true)
+ data[s] = (typescanners[kind] or scanexpression)()
+ elseif kind == leftbracket_code then
+ data[s] = get_parameters(true)
+ else
+ data[s] = tokenscanners[kind]()
+ end
+ end
+ end
+ return data
+end
+
+local namespaces = { }
+local presets = { }
+local passed = { }
+
+local function get_parameters(nested)
+ local data = { }
+ if nested or scansymbol(true) == "[" then
+ scansymbol()
+ else
+ return data
+ end
+ while true do
+ local s = scansymbol(false,false)
+ if s == "]" then
+ break;
+ elseif s == "," then
+ -- continue
+ else
+ local t = scantoken(true)
+ if t == equals_code or t == colon_code then
+ -- optional equal or :
+ scantoken()
+ end
+ local kind = scantoken(true)
+ if kind == leftdelimiter_code or kind == tag_code or kind == capsule_code then
+ kind = scanexpression(true)
+ data[s] = (typescanners[kind] or scanexpression)()
+ elseif kind == leftbracket_code then
+ data[s] = get_parameters(true)
+ else
+ data[s] = tokenscanners[kind]()
+ end
+ end
+ end
+ return data
+end
+
+local function getparameters()
+ local namespace = scanstring()
+ -- same as below
+ local parameters = get_parameters()
+ local presets = presets[namespace]
+ local passed = passed[namespace]
+ if passed then
+ if presets then
+ setmetatableindex(passed,presets)
+ end
+ setmetatableindex(parameters,passed)
+ elseif presets then
+ setmetatableindex(parameters,presets)
+ end
+ namespaces[namespace] = parameters
+ --
+end
+
+local function applyparameters()
+ local saved = namespaces
+ local namespace = scanstring()
+ local action = scanstring() -- before we scan the parameters
+ -- same as above
+ local parameters = get_parameters()
+ local presets = presets[namespace]
+ local passed = passed[namespace]
+ if passed then
+ if presets then
+ setmetatableindex(passed,presets)
+ end
+ setmetatableindex(parameters,passed)
+ elseif presets then
+ setmetatableindex(parameters,presets)
+ end
+ namespaces[namespace] = parameters
+ -- till here
+ mpprint(action)
+ namespaces = saved
+end
+
+local function presetparameters()
+ local namespace = scanstring()
+ local parent = nil
+ local t = scantoken(true)
+ if t == string_code then
+ parent = presets[scanstring()]
+ end
+ local p = get_parameters()
+ if parent then
+ setmetatableindex(p,parent)
+ end
+ presets[namespace] = p
+end
+
+local function collectnames()
+ local l = { } -- can be reused but then we can't nest
+ local n = 0
+ while true do
+ local t = scantoken(true)
+ -- (1) not really needed
+ if t == numeric_code then
+ n = n + 1 l[n] = scaninteger(1)
+ elseif t == string_code then
+ n = n + 1 l[n] = scanstring(1)
+ elseif t == nullary_code then
+ n = n + 1 l[n] = scanboolean(1)
+ elseif t == leftdelimiter_code or t == tag_code or t == capsule_code then
+ t = scanexpression(true)
+ n = n + 1 l[n] = (typescanners[t] or scanexpression)()
+ else
+ break
+ end
+ end
+ return l, n
+end
+
+local function get(v)
+ local t = type(v)
+ if t == "number" then
+ return injectnumeric(v)
+ elseif t == "boolean" then
+ return injectboolean(v)
+ elseif t == "string" then
+ return injectstring(v)
+ elseif t == "table" then
+ local n = #v
+ if type(v[1]) == "table" then
+ return injectpath(v) -- cycle ?
+ elseif n == 2 then
+ return injectpair(v)
+ elseif n == 3 then
+ return injecttriplet(v)
+ elseif n == 4 then
+ return injectquadruple(v)
+ elseif n == 6 then
+ return injecttransform(v)
+ end
+ end
+ return injectnumeric(0)
+end
+
+local stack = { }
+
+local function pushparameters()
+ local l, n = collectnames()
+ insert(stack,namespaces)
+ for i=1,n do
+ local n = namespaces[l[i]]
+ if type(n) == "table" then
+ namespaces = n
+ else
+ break
+ end
+ end
+end
+
+local function popparameters()
+ local n = remove(stack)
+ if n then
+ namespaces = n
+ else
+ report("stack error")
+ end
+end
+
+-- todo:
+
+local function getparameter()
+ local list, n = collectnames()
+ local v = namespaces
+ for i=1,n do
+ local l = list[i]
+ local vl = v[l]
+ if vl == nil then
+ if type(l) == "number" then
+ vl = v[1]
+ if vl == nil then
+ return injectnumeric(0)
+ end
+ else
+ return injectnumeric(0)
+ end
+ end
+ v = vl
+ end
+ if v == nil then
+ return injectnumeric(0)
+ else
+ return get(v)
+ end
+end
+
+local function hasparameter()
+ local list, n = collectnames()
+ local v = namespaces
+ for i=1,n do
+ local l = list[i]
+ local vl = rawget(v,l)
+ if vl == nil then
+ if type(l) == "number" then
+ vl = rawget(v,1)
+ if vl == nil then
+ return injectboolean(false)
+ end
+ else
+ return injectboolean(false)
+ end
+ end
+ v = vl
+ end
+ if v == nil then
+ return injectboolean(false)
+ else
+ return injectboolean(true)
+ end
+end
+
+local function hasoption()
+ local list, n = collectnames()
+ if n > 1 then
+ local v = namespaces
+ if n > 2 then
+ for i=1,n-1 do
+ local l = list[i]
+ local vl = v[l]
+ if vl == nil then
+ return injectboolean(false)
+ end
+ v = vl
+ end
+ else
+ v = v[list[1]]
+ end
+ if type(v) == "string" then
+ -- no caching .. slow anyway
+ local o = list[n]
+ if v == o then
+ return injectboolean(true)
+ end
+ for vv in gmatch(v,"[^%s,]+") do
+ for oo in gmatch(o,"[^%s,]+") do
+ if vv == oo then
+ return injectboolean(true)
+ end
+ end
+ end
+ end
+ end
+ return injectboolean(false)
+end
+
+local function getparameterdefault()
+ local list, n = collectnames()
+ local v = namespaces
+ if n == 1 then
+ local l = list[1]
+ local vl = v[l]
+ if vl == nil then
+ -- maybe backtrack
+ local top = stack[#stack]
+ if top then
+ vl = top[l]
+ end
+ end
+ if vl == nil then
+ return injectnumeric(0)
+ else
+ return get(vl)
+ end
+ else
+ for i=1,n-1 do
+ local l = list[i]
+ local vl = v[l]
+ if vl == nil then
+ if type(l) == "number" then
+ vl = v[1]
+ if vl == nil then
+ return get(list[n])
+ end
+ else
+ local last = list[n]
+ if last == "*" then
+ -- so, only when not pushed
+ local m = getmetatable(namespaces[list[1]])
+ if n then
+ m = m.__index -- can also be a _m_
+ end
+ if m then
+ local v = m
+ for i=2,n-1 do
+ local l = list[i]
+ local vl = v[l]
+ if vl == nil then
+ return injectnumeric(0)
+ end
+ v = vl
+ end
+ if v == nil then
+ return injectnumeric(0)
+ else
+ return get(v)
+ end
+ end
+ return injectnumeric(0)
+ else
+ return get(last)
+ end
+ end
+ end
+ v = vl
+ end
+ if v == nil then
+ return get(list[n])
+ else
+ return get(v)
+ end
+ end
+end
+
+local function getparametercount()
+ local list, n = collectnames()
+ local v = namespaces
+ for i=1,n do
+ v = v[list[i]]
+ if not v then
+ break
+ end
+ end
+ return injectnumeric(type(v) == "table" and #v or 0)
+end
+
+local function getmaxparametercount()
+ local list, n = collectnames()
+ local v = namespaces
+ for i=1,n do
+ v = v[list[i]]
+ if not v then
+ break
+ end
+ end
+ local n = 0
+ if type(v) == "table" then
+ local v1 = v[1]
+ if type(v1) == "table" then
+ n = #v1
+ for i=2,#v do
+ local vi = v[i]
+ if type(vi) == "table" then
+ local vn = #vi
+ if vn > n then
+ n = vn
+ end
+ else
+ break;
+ end
+ end
+ end
+
+ end
+ return injectnumeric(n)
+end
+
+local validconnectors = {
+ [".."] = true,
+ ["..."] = true,
+ ["--"] = true,
+}
+
+local function getparameterpath()
+ local list, n = collectnames()
+ local close = list[n]
+ if type(close) == "boolean" then
+ n = n - 1
+ else
+ close = false
+ end
+ local connector = list[n]
+ if type(connector) == "string" and validconnectors[connector] then
+ n = n - 1
+ else
+ connector = "--"
+ end
+ local v = namespaces
+ for i=1,n do
+ v = v[list[i]]
+ if not v then
+ break
+ end
+ end
+ if type(v) == "table" then
+ return injectpath(v,connector,close)
+ else
+ return injectpair(0,0)
+ end
+end
+
+local function getparameterpen()
+ local list, n = collectnames()
+ local v = namespaces
+ for i=1,n do
+ v = v[list[i]]
+ if not v then
+ break
+ end
+ end
+ if type(v) == "table" then
+ return injectpath(v,"..",true)
+ else
+ return injectpair(0,0)
+ end
+end
+
+local function getparametertext()
+ local list, n = collectnames()
+ local strut = list[n]
+ if type(strut) == "boolean" then
+ n = n - 1
+ else
+ strut = false
+ end
+ local v = namespaces
+ for i=1,n do
+ v = v[list[i]]
+ if not v then
+ break
+ end
+ end
+ if type(v) == "string" then
+ return injectstring("\\strut " .. v)
+ else
+ return injectstring("")
+ end
+end
+
+-- local function getparameteroption()
+-- local list, n = collectnames()
+-- local last = list[n]
+-- if type(last) == "string" then
+-- n = n - 1
+-- else
+-- return false
+-- end
+-- local v = namespaces
+-- for i=1,n do
+-- v = v[list[i]]
+-- if not v then
+-- break
+-- end
+-- end
+-- if type(v) == "string" and v ~= "" then
+-- for s in gmatch(v,"[^ ,]+") do
+-- if s == last then
+-- return true
+-- end
+-- end
+-- end
+-- return false
+-- end
+
+function metapost.scanparameters()
+-- scantoken() -- we scan the semicolon
+ return get_parameters()
+end
+
+metapost.registerscript("getparameters", getparameters)
+metapost.registerscript("applyparameters", applyparameters)
+metapost.registerscript("presetparameters", presetparameters)
+metapost.registerscript("hasparameter", hasparameter)
+metapost.registerscript("hasoption", hasoption)
+metapost.registerscript("getparameter", getparameter)
+metapost.registerscript("getparameterdefault", getparameterdefault)
+metapost.registerscript("getparametercount", getparametercount)
+metapost.registerscript("getmaxparametercount",getmaxparametercount)
+metapost.registerscript("getparameterpath", getparameterpath)
+metapost.registerscript("getparameterpen", getparameterpen)
+metapost.registerscript("getparametertext", getparametertext)
+--------.registerscript("getparameteroption", getparameteroption)
+metapost.registerscript("pushparameters", pushparameters)
+metapost.registerscript("popparameters", popparameters)
+
+function metapost.getparameter(list)
+ local n = #list
+ local v = namespaces
+ for i=1,n do
+ local l = list[i]
+ local vl = v[l]
+ if vl == nil then
+ return
+ end
+ v = vl
+ end
+ return v
+end
+
+function metapost.getparameterset(namespace)
+ return namespace and namespaces[namespace] or namespaces
+end
+
+function metapost.setparameterset(namespace,t)
+ namespaces[namespace] = t
+end
+
+-- goodies
+
+metapost.registerscript("definecolor", function()
+ scantoken() -- we scan the semicolon
+ local s = get_parameters()
+ attributes.colors.defineprocesscolordirect(s)
+end)
+
+-- tex scanners
+
+local scanners = tokens.scanners
+local scanhash = scanners.hash
+local scanstring = scanners.string
+local scanvalue = scanners.value
+local scaninteger = scanners.integer
+local scanboolean = scanners.boolean
+local scanfloat = scanners.float
+local scandimension = scanners.dimension
+
+local definitions = { }
+
+local bpfactor = number.dimenfactors.bp
+local comma = byte(",")
+local close = byte("]")
+
+local scanrest = function() return scanvalue(comma,close) or "" end
+local scandimension = function() return scandimension() * bpfactor end
+
+local scanners = {
+ ["integer"] = scaninteger,
+ ["number"] = scanfloat,
+ ["numeric"] = scanfloat,
+ ["boolean"] = scanboolean,
+ ["string"] = scanrest,
+ ["dimension"] = scandimension,
+}
+
+interfaces.implement {
+ name = "lmt_parameters_define",
+ arguments = "string",
+ actions = function(namespace)
+ local d = scanhash()
+ for k, v in next, d do
+ d[k] = scanners[v] or scanrest
+ end
+ definitions[namespace] = d
+ end,
+}
+
+interfaces.implement {
+ name = "lmt_parameters_preset",
+ arguments = "string",
+ actions = function(namespace)
+ passed[namespace] = scanhash(definitions[namespace])
+ end,
+}
+
+interfaces.implement {
+ name = "lmt_parameters_reset",
+ arguments = "string",
+ actions = function(namespace)
+ passed[namespace] = nil
+ end,
+}