summaryrefslogtreecommitdiff
path: root/tex/context/base/mkiv/math-ini.lua
diff options
context:
space:
mode:
Diffstat (limited to 'tex/context/base/mkiv/math-ini.lua')
-rw-r--r--tex/context/base/mkiv/math-ini.lua712
1 files changed, 712 insertions, 0 deletions
diff --git a/tex/context/base/mkiv/math-ini.lua b/tex/context/base/mkiv/math-ini.lua
new file mode 100644
index 000000000..e6a35c39e
--- /dev/null
+++ b/tex/context/base/mkiv/math-ini.lua
@@ -0,0 +1,712 @@
+if not modules then modules = { } end modules ['math-ini'] = {
+ version = 1.001,
+ comment = "companion to math-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- if needed we can use the info here to set up xetex definition files
+-- the "8000 hackery influences direct characters (utf) as indirect \char's
+--
+-- isn't characters.data loaded already ... shortcut it here
+--
+-- replace code 7 by 0 as we don't use it anyway (chars with code 7 will adapt to
+-- to the fam when set ... we use other means .. ok, we could use it for spacing but
+-- then we also have to set the other characters (only a subset done now)
+
+local formatters, find = string.formatters, string.find
+local utfchar, utfbyte, utflength = utf.char, utf.byte, utf.length
+local floor = math.floor
+local toboolean = toboolean
+
+local context = context
+local commands = commands
+local implement = interfaces.implement
+
+local context_sprint = context.sprint
+----- context_fprint = context.fprint -- a bit inefficient
+local ctx_doifelsesomething = commands.doifelsesomething
+
+local trace_defining = false trackers.register("math.defining", function(v) trace_defining = v end)
+
+local report_math = logs.reporter("mathematics","initializing")
+
+mathematics = mathematics or { }
+local mathematics = mathematics
+
+mathematics.extrabase = 0xFE000 -- here we push some virtuals
+mathematics.privatebase = 0xFF000 -- here we push the ex
+
+local unsetvalue = attributes.unsetvalue
+local allocate = utilities.storage.allocate
+local chardata = characters.data
+
+local texsetattribute = tex.setattribute
+local setmathcode = tex.setmathcode
+local setdelcode = tex.setdelcode
+
+local families = allocate {
+ mr = 0,
+ mb = 1,
+}
+
+--- to be checked .. afew defaults in char-def that should be alpha
+
+local classes = allocate {
+ ord = 0, -- mathordcomm mathord
+ op = 1, -- mathopcomm mathop
+ bin = 2, -- mathbincomm mathbin
+ rel = 3, -- mathrelcomm mathrel
+ open = 4, -- mathopencomm mathopen
+ middle = 4,
+ close = 5, -- mathclosecomm mathclose
+ punct = 6, -- mathpunctcomm mathpunct
+ alpha = 7, -- mathalphacomm firstofoneargument
+ accent = 8, -- class 0
+ radical = 9,
+ xaccent = 10, -- class 3
+ topaccent = 11, -- class 0
+ botaccent = 12, -- class 0
+ under = 13,
+ over = 14,
+ delimiter = 15,
+ inner = 0, -- mathinnercomm mathinner
+ nothing = 0, -- mathnothingcomm firstofoneargument
+ choice = 0, -- mathchoicecomm @@mathchoicecomm
+ box = 0, -- mathboxcomm @@mathboxcomm
+ limop = 1, -- mathlimopcomm @@mathlimopcomm
+ nolop = 1, -- mathnolopcomm @@mathnolopcomm
+ --
+ ordinary = 0, -- ord
+ alphabetic = 7, -- alpha
+ unknown = 0, -- nothing
+ default = 0, -- nothing
+ punctuation = 6, -- punct
+ normal = 0, -- nothing
+ opening = 4, -- open
+ closing = 5, -- close
+ binary = 2, -- bin
+ relation = 3, -- rel
+ fence = 0, -- unknown
+ diacritic = 8, -- accent
+ large = 1, -- op
+ variable = 7, -- alphabetic
+ number = 7, -- alphabetic
+ root = 16, -- a private one
+}
+
+local open_class = 4
+local middle_class = 4
+local close_class = 5
+
+local accents = allocate {
+ accent = true, -- some can be both
+ topaccent = true, [11] = true,
+ botaccent = true, [12] = true,
+ under = true, [13] = true,
+ over = true, [14] = true,
+ unknown = false,
+}
+
+local codes = allocate {
+ ordinary = 0, [0] = "ordinary",
+ largeoperator = 1, [1] = "largeoperator",
+ binaryoperator = 2, [2] = "binaryoperator",
+ relation = 3, [3] = "relation",
+ openingsymbol = 4, [4] = "openingsymbol",
+ closingsymbol = 5, [5] = "closingsymbol",
+ punctuation = 6, [6] = "punctuation",
+ variable = 7, [7] = "variable",
+}
+
+local extensibles = allocate {
+ unknown = 0,
+ l = 1, left = 1,
+ r = 2, right = 2,
+ h = 3, horizontal = 3,-- lr or rl
+ u = 5, up = 4,
+ d = 5, down = 5,
+ v = 6, vertical = 6,-- ud or du
+ m = 7, mixed = 7,
+}
+
+table.setmetatableindex(extensibles,function(t,k) t[k] = 0 return 0 end)
+
+mathematics.extensibles = extensibles
+mathematics.classes = classes
+mathematics.codes = codes
+-----------.accents = codes
+mathematics.families = families
+
+-- there will be proper functions soon (and we will move this code in-line)
+-- no need for " in class and family (saves space)
+
+local function mathchar(class,family,slot)
+ return formatters['\\Umathchar "%X "%X "%X '](class,family,slot)
+end
+
+local function mathaccent(class,family,slot)
+ return formatters['\\Umathaccent "%X "%X "%X '](0,family,slot) -- no class
+end
+
+local function delimiter(class,family,slot)
+ return formatters['\\Udelimiter "%X "%X "%X '](class,family,slot)
+end
+
+local function radical(family,slot)
+ return formatters['\\Uradical "%X "%X '](family,slot)
+end
+
+local function root(family,slot)
+ return formatters['\\Uroot "%X "%X '](family,slot)
+end
+
+local function mathchardef(name,class,family,slot)
+ return formatters['\\Umathchardef\\%s "%X "%X "%X '](name,class,family,slot)
+end
+
+local function mathcode(target,class,family,slot)
+ return formatters['\\Umathcode%s="%X "%X "%X '](target,class,family,slot)
+end
+
+local function mathtopaccent(class,family,slot)
+ return formatters['\\Umathaccent "%X "%X "%X '](0,family,slot) -- no class
+end
+
+local function mathbotaccent(class,family,slot)
+ return formatters['\\Umathaccent bottom "%X "%X "%X '](0,family,slot) -- no class
+end
+
+local function mathtopdelimiter(class,family,slot)
+ return formatters['\\Udelimiterover "%X "%X '](family,slot) -- no class
+end
+
+local function mathbotdelimiter(class,family,slot)
+ return formatters['\\Udelimiterunder "%X "%X '](family,slot) -- no class
+end
+
+local escapes = characters.filters.utf.private.escapes
+
+-- not that many so no need to reuse tables
+
+local setmathcharacter = function(class,family,slot,unicode,mset,dset)
+ if mset and codes[class] then -- regular codes < 7
+ setmathcode("global",slot,{class,family,unicode})
+ mset = false
+ end
+ if dset and class == open_class or class == close_class or class == middle_class then
+ setdelcode("global",slot,{family,unicode,0,0})
+ dset = false
+ end
+ return mset, dset
+end
+
+local f_accent = formatters[ [[\ugdef\%s{\Umathaccent 0 "%X "%X }]] ]
+local f_topaccent = formatters[ [[\ugdef\%s{\Umathaccent 0 "%X "%X }]] ]
+local f_botaccent = formatters[ [[\ugdef\%s{\Umathbotaccent 0 "%X "%X }]] ]
+local f_over = formatters[ [[\ugdef\%s{\Udelimiterover "%X "%X }]] ]
+local f_under = formatters[ [[\ugdef\%s{\Udelimiterunder "%X "%X }]] ]
+local f_fence = formatters[ [[\ugdef\%s{\Udelimiter "%X "%X "%X }]] ]
+local f_delimiter = formatters[ [[\ugdef\%s{\Udelimiter 0 "%X "%X }]] ]
+local f_radical = formatters[ [[\ugdef\%s{\Uradical "%X "%X }]] ]
+local f_root = formatters[ [[\ugdef\%s{\Uroot "%X "%X }]] ]
+----- f_char = formatters[ [[\ugdef\%s{\Umathchar "%X "%X "%X }]]
+local f_char = formatters[ [[\Umathchardef\%s "%X "%X "%X ]] ]
+
+local setmathsymbol = function(name,class,family,slot) -- hex is nicer for tracing
+ if class == classes.accent then
+ context_sprint(f_accent(name,family,slot))
+ elseif class == classes.topaccent then
+ context_sprint(f_topaccent(name,family,slot))
+ elseif class == classes.botaccent then
+ context_sprint(f_botaccent(name,family,slot))
+ elseif class == classes.over then
+ context_sprint(f_over(name,family,slot))
+ elseif class == classes.under then
+ context_sprint(f_under(name,family,slot))
+ elseif class == open_class or class == close_class or class == middle_class then
+ setdelcode("global",slot,{family,slot,0,0})
+ context_sprint(f_fence(name,class,family,slot))
+ elseif class == classes.delimiter then
+ setdelcode("global",slot,{family,slot,0,0})
+ context_sprint(f_delimiter(name,family,slot))
+ elseif class == classes.radical then
+ context_sprint(f_radical(name,family,slot))
+ elseif class == classes.root then
+ context_sprint(f_root(name,family,slot))
+ else
+ -- beware, open/close and other specials should not end up here
+ context_sprint(f_char(name,class,family,slot))
+ end
+end
+
+local function report(class,family,unicode,name)
+ local nametype = type(name)
+ if nametype == "string" then
+ report_math("class name %a, class %a, family %a, char %C, name %a",classname,class,family,unicode,name)
+ elseif nametype == "number" then
+ report_math("class name %a, class %a, family %a, char %C, number %U",classname,class,family,unicode,name)
+ else
+ report_math("class name %a, class %a, family %a, char %C", classname,class,family,unicode)
+ end
+end
+
+-- there will be a combined \(math)chardef (tracker)
+
+function mathematics.define(family)
+ family = family or 0
+ family = families[family] or family
+ local data = characters.data
+ for unicode, character in next, data do
+ local symbol = character.mathsymbol
+ local mset, dset = true, true
+ if symbol then
+ local other = data[symbol]
+ local class = other.mathclass
+ if class then
+ class = classes[class] or class -- no real checks needed
+ if trace_defining then
+ report(class,family,unicode,symbol)
+ end
+ mset, dset = setmathcharacter(class,family,unicode,symbol,mset,dset)
+ end
+ local spec = other.mathspec
+ if spec then
+ for i, m in next, spec do
+ local class = m.class
+ if class then
+ class = classes[class] or class -- no real checks needed
+ mset, dset = setmathcharacter(class,family,unicode,symbol,mset,dset)
+ end
+ end
+ end
+ end
+ local mathclass = character.mathclass
+ local mathspec = character.mathspec
+ if mathspec then
+ for i, m in next, mathspec do
+ local name = m.name
+ local class = m.class
+ if not class then
+ class = mathclass
+ elseif not mathclass then
+ mathclass = class
+ end
+ if class then
+ class = classes[class] or class -- no real checks needed
+ if name then
+ if trace_defining then
+ report(class,family,unicode,name)
+ end
+ setmathsymbol(name,class,family,unicode)
+ else
+ name = class == classes.variable or class == classes.number and character.adobename
+ if name and trace_defining then
+ report(class,family,unicode,name)
+ end
+ end
+ mset, dset = setmathcharacter(class,family,unicode,m.unicode or unicode,mset,dset) -- see solidus
+ end
+ end
+ end
+ if mathclass then
+ local name = character.mathname
+ local class = classes[mathclass] or mathclass -- no real checks needed
+ if name == false then
+ if trace_defining then
+ report(class,family,unicode,name)
+ end
+ mset, dset = setmathcharacter(class,family,unicode,mset,dset)
+ else
+ name = name or character.contextname
+ if name then
+ if trace_defining then
+ report(class,family,unicode,name)
+ end
+ setmathsymbol(name,class,family,unicode)
+ else
+ if trace_defining then
+ report(class,family,unicode,character.adobename)
+ end
+ end
+ mset, dset = setmathcharacter(class,family,unicode,unicode,mset,dset)
+ end
+ end
+ end
+end
+
+-- needed for mathml analysis
+-- string with # > 1 are invalid
+-- we could cache
+
+local lpegmatch = lpeg.match
+
+local utf8byte = lpeg.patterns.utf8byte * lpeg.P(-1)
+
+-- function somechar(c)
+-- local b = lpegmatch(utf8byte,c)
+-- return b and chardata[b]
+-- end
+
+
+local somechar = { }
+
+table.setmetatableindex(somechar,function(t,k)
+ if k then
+ local b = lpegmatch(utf8byte,k)
+ local v = b and chardata[b] or false
+ t[k] = v
+ return v
+ end
+end)
+
+local function utfmathclass(chr, default)
+ local cd = somechar[chr]
+ return cd and cd.mathclass or default or "unknown"
+end
+
+local function utfmathaccent(chr,default,asked1,asked2)
+ local cd = somechar[chr]
+ if not cd then
+ return default or false
+ end
+ if asked1 and asked1 ~= "" then
+ local mc = cd.mathclass
+ if mc and (mc == asked1 or mc == asked2) then
+ return true
+ end
+ local ms = cd.mathspec
+ if not ms then
+ local mp = cd.mathparent
+ if mp then
+ ms = chardata[mp].mathspec
+ end
+ end
+ if ms then
+ for i=1,#ms do
+ local msi = ms[i]
+ local mc = msi.class
+ if mc and (mc == asked1 or mc == asked2) then
+ return true
+ end
+ end
+ end
+ else
+ local mc = cd.mathclass
+ if mc then
+ return accents[mc] or default or false
+ end
+ local ms = cd.mathspec
+ if ms then
+ for i=1,#ms do
+ local msi = ms[i]
+ local mc = msi.class
+ if mc then
+ return accents[mc] or default or false
+ end
+ end
+ end
+ end
+ return default or false
+end
+
+local function utfmathstretch(chr,default) -- "h", "v", "b", ""
+ local cd = somechar[chr]
+ return cd and cd.mathstretch or default or ""
+end
+
+local function utfmathcommand(chr,default,asked1,asked2)
+ local cd = somechar[chr]
+ if not cd then
+ return default or ""
+ end
+ if asked1 then
+ local mn = cd.mathname
+ local mc = cd.mathclass
+ if mn and mc and (mc == asked1 or mc == asked2) then
+ return mn
+ end
+ local ms = cd.mathspec
+ if not ms then
+ local mp = cd.mathparent
+ if mp then
+ ms = chardata[mp].mathspec
+ end
+ end
+ if ms then
+ for i=1,#ms do
+ local msi = ms[i]
+ local mn = msi.name
+ if mn then
+ local mc = msi.class
+ if mc == asked1 or mc == asked2 then
+ return mn
+ end
+ end
+ end
+ end
+ else
+ local mn = cd.mathname
+ if mn then
+ return mn
+ end
+ local ms = cd.mathspec
+ if ms then
+ for i=1,#ms do
+ local msi = ms[i]
+ local mn = msi.name
+ if mn then
+ return mn
+ end
+ end
+ end
+ end
+ return default or ""
+end
+
+local function utfmathfiller(chr, default)
+ local cd = somechar[chr]
+ local cmd = cd and cd.mathfiller -- or cd.mathname
+ return cmd or default or ""
+end
+
+mathematics.utfmathclass = utfmathclass
+mathematics.utfmathstretch = utfmathstretch
+mathematics.utfmathcommand = utfmathcommand
+mathematics.utfmathfiller = utfmathfiller
+mathematics.utfmathaccent = utfmathaccent
+
+-- interfaced
+
+implement {
+ name = "utfmathclass",
+ actions = { utfmathclass, context },
+ arguments = "string"
+}
+
+implement {
+ name = "utfmathstretch",
+ actions = { utfmathstretch, context },
+ arguments = "string"
+}
+
+implement {
+ name = "utfmathcommand",
+ actions = { utfmathcommand, context },
+ arguments = "string"
+}
+
+implement {
+ name = "utfmathfiller",
+ actions = { utfmathfiller, context },
+ arguments = "string"
+}
+
+implement {
+ name = "utfmathcommandabove",
+ actions = { utfmathcommand, context },
+ arguments = { "string", false, "'topaccent'","'over'" }
+}
+
+implement {
+ name = "utfmathcommandbelow",
+ actions = { utfmathcommand, context },
+ arguments = { "string", false, "'botaccent'","'under'" }
+}
+implement {
+ name = "utfmathcommandfiller",
+ actions = { utfmathfiller, context },
+ arguments = "string"
+}
+
+-- todo: make this a helper:
+
+implement {
+ name = "doifelseutfmathabove",
+ actions = { utfmathaccent, ctx_doifelsesomething },
+ arguments = { "string", false, "'topaccent'", "'over'" }
+}
+
+implement {
+ name = "doifelseutfmathbelow",
+ actions = { utfmathaccent, ctx_doifelsesomething },
+ arguments = { "string", false, "'botaccent'", "'under'" }
+}
+
+implement {
+ name = "doifelseutfmathaccent",
+ actions = { utfmathaccent, ctx_doifelsesomething },
+ arguments = "string",
+}
+
+implement {
+ name = "doifelseutfmathfiller",
+ actions = { utfmathfiller, ctx_doifelsesomething },
+ arguments = "string",
+}
+
+-- helpers
+--
+-- 1: step 1
+-- 2: step 2
+-- 3: htdp * 1.33^n
+-- 4: size * 1.33^n
+
+function mathematics.big(tfmdata,unicode,n,method)
+ local t = tfmdata.characters
+ local c = t[unicode]
+ if c and n > 0 then
+ local vv = c.vert_variants or c.next and t[c.next].vert_variants
+ if vv then
+ local vvn = vv[n]
+ return vvn and vvn.glyph or vv[#vv].glyph or unicode
+ elseif method == 1 or method == 2 then
+ if method == 2 then -- large steps
+ n = n * 2
+ end
+ local next = c.next
+ while next do
+ if n <= 1 then
+ return next
+ else
+ n = n - 1
+ local tn = t[next].next
+ if tn then
+ next = tn
+ else
+ return next
+ end
+ end
+ end
+ else
+ local size = 1.33^n
+ if method == 4 then
+ size = tfmdata.parameters.size * size
+ else -- if method == 3 then
+ size = (c.height + c.depth) * size
+ end
+ local next = c.next
+ while next do
+ local cn = t[next]
+ if (cn.height + cn.depth) >= size then
+ return next
+ else
+ local tn = cn.next
+ if tn then
+ next = tn
+ else
+ return next
+ end
+ end
+ end
+ end
+ end
+ return unicode
+end
+
+-- experimental
+
+-- local categories = { } -- indexed + hashed
+--
+-- local a_mathcategory = attributes.private("mathcategory")
+--
+-- local function registercategory(category,tag,data) -- always same data for tag
+-- local c = categories[category]
+-- if not c then
+-- c = { }
+-- categories[category] = c
+-- end
+-- local n = c[tag]
+-- if not n then
+-- n = #c + 1
+-- c[n] = data
+-- n = n * 1000 + category
+-- c[tag] = n
+-- end
+-- return n
+-- end
+--
+-- function mathematics.getcategory(n)
+-- local category = n % 1000
+-- return category, categories[category][floor(n/1000)]
+-- end
+--
+-- mathematics.registercategory = registercategory
+--
+-- function commands.taggedmathfunction(tag,label)
+-- if label then
+-- texsetattribute(a_mathcategory,registercategory(1,tag,tag))
+-- context.mathlabeltext(tag)
+-- else
+-- texsetattribute(a_mathcategory,1)
+-- context(tag)
+-- end
+-- end
+
+local categories = { }
+mathematics.categories = categories
+
+local a_mathcategory = attributes.private("mathcategory")
+
+local functions = storage.allocate()
+local noffunctions = 1000 -- offset
+
+categories.functions = functions
+
+implement {
+ name = "tagmfunctiontxt",
+ arguments = { "string", "conditional" },
+ actions = function(tag,apply)
+ local delta = apply and 1000 or 0
+ texsetattribute(a_mathcategory,1000 + delta)
+ end
+}
+
+implement {
+ name = "tagmfunctionlab",
+ arguments = { "string", "conditional" },
+ actions = function(tag,apply)
+ local delta = apply and 1000 or 0
+ local n = functions[tag]
+ if not n then
+ noffunctions = noffunctions + 1
+ functions[noffunctions] = tag
+ functions[tag] = noffunctions
+ texsetattribute(a_mathcategory,noffunctions + delta)
+ else
+ texsetattribute(a_mathcategory,n + delta)
+ end
+ end
+}
+
+--
+
+local list
+
+function mathematics.resetattributes()
+ if not list then
+ list = { }
+ for k, v in next, attributes.numbers do
+ if find(k,"^math") then
+ list[#list+1] = v
+ end
+ end
+ end
+ for i=1,#list do
+ texsetattribute(list[i],unsetvalue)
+ end
+end
+
+implement {
+ name = "resetmathattributes",
+ actions = mathematics.resetattributes
+}
+
+-- weird to do this here but it's a side affect of math anyway
+
+interfaces.implement {
+ name = "enableasciimode",
+ onlyonce = true,
+ actions = resolvers.macros.enablecomment,
+}