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" } -- The way we do math in \CONTEXT\ \MKIV\ differs from other macro packages so you -- should not mix the somewhat unique (and bit weird) approach here with the more -- traditional (\TEX) approach. Also, we use char-def.lua as starting point and that -- file is quite \CONTEXT\ specific. When we added math to that there was no -- interest (quite the contrary) so we didn't went generic there which in retrospect -- also gives us the freedom to add more information, something that happens -- occasionally. Because that file is shared between \MKIV\ and \LMTX\ some -- information is only used by \LMTX. We also have quite some runs over the math -- list but that has been so since we started and performance will not degrade much -- by it; after all math is not that demanding. More details can be found in the -- manuals that discuss math. Most code (and concepts) date from 2005 so maybe I -- will upgrade the lot some day, although it has been adapted on the way to the -- changes in the engine(s). local next, type = next, type local formatters, find, nospaces = string.formatters, string.find, string.nospaces local utfchar, utfbyte = utf.char, utf.byte local sortedhash = table.sortedhash local toboolean = toboolean local context = context local implement = interfaces.implement 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 = fonts.privateoffsets.mathextrabase -- here we push some virtuals mathematics.privatebase = fonts.privateoffsets.mathbase -- 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 texintegerdef = tex.integerdef local getfontoffamily = tex.getfontoffamily local fontchardata = fonts.hashes.characters setdelcode = function() end -- These are different from mkiv with luatex. local classes = allocate { unset = 64 } -- or -1 local classnames = allocate { } local maxengineclass = 63 local lastengineclass = 0 local lastprivateclass = maxengineclass for k, v in next, nodes.noadcodes do if type(k) == "string" then classes[k] = v -- local n = classnames[v] -- if not n or #k < #n then -- classnames[v] = k -- end elseif k > lastengineclass then lastengineclass = k end end local ordinary_class = classes.ordinary local operator_class = classes.operator local binary_class = classes.binary local relation_class = classes.relation local open_class = classes.open local close_class = classes.close local punctuation_class = classes.punctuation local middle_class = classes.middle local accent_class = classes.accent local radical_class = classes.radical local fraction_class = classes.fraction local under_class = classes.under local over_class = classes.over local fenced_class = classes.fenced local ghost_class = classes.ghost -- these will go classes.ord = ordinary_class classes.op = operator_class classes.bin = binary_class classes.rel = relation_class classes.opening = open_class -- will go classes.closing = close_class -- will go classes.punct = punctuation_class classes.frac = fraction_class classes.rad = radical_class classes.fen = fenced_class classes.gst = ghost_class -- these will go too classes.limop = operator_class classes.limoperator = operator_class classes.nolop = operator_class classes.nolimoperator = operator_class classes.large = operator_class classes.largeoperator = operator_class -- special in the engine : variable active inner vcenter local function registerengineclass(name,short) local class = classes[name] if not class then if lastengineclass < maxengineclass then lastengineclass = lastengineclass + 1 class = lastengineclass classnames[class] = short or name else class = ordinary_class end else classnames[class] = short or name end classes[class] = name classes[name] = class return class end -- predefined classes registerengineclass("ordinary", "ord") registerengineclass("operator", "ope") registerengineclass("binary", "bin") registerengineclass("relation", "rel") registerengineclass("open", "ope") registerengineclass("close", "clo") registerengineclass("punctuation", "pun") registerengineclass("variable", "var") -- not used registerengineclass("active", "act") -- not used registerengineclass("inner", "inn") -- not used registerengineclass("middle", "mid") registerengineclass("accent", "acc") registerengineclass("radical", "rad") registerengineclass("fraction", "fra") registerengineclass("under", "und") registerengineclass("over", "ove") registerengineclass("fenced", "fen") registerengineclass("ghost", "gho") registerengineclass("vcenter", "vce") -- not used -- additional classes registerengineclass("explicit", "xpl") registerengineclass("imaginary", "img") registerengineclass("differential", "dif") registerengineclass("exponential", "exp") registerengineclass("integral", "int") registerengineclass("ellipsis", "ell") registerengineclass("function", "fnc") registerengineclass("digit", "dig") local division_class = registerengineclass("division", "div") registerengineclass("factorial", "fac") registerengineclass("wrapped", "wra") registerengineclass("construct", "con") registerengineclass("dimension", "dim") registerengineclass("unary", "una") registerengineclass("textpunctuation", "tpu") registerengineclass("unspaced", "uns") registerengineclass("experimental", "exp") registerengineclass("fake", "fak") registerengineclass("numbergroup", "ngr") registerengineclass("maybeordinary", "mor") registerengineclass("mayberelation", "mre") registerengineclass("maybebinary", "mbi") registerengineclass("chemicalbond", "chb") registerengineclass("implication", "imp") local specialclasses = tex.specialmathclasscodes classes["all"] = specialclasses["all"] classnames[specialclasses["all"] ] = "all" classes["begin"] = specialclasses["begin"] classnames[specialclasses["begin"]] = "beg" classes["end"] = specialclasses["end"] classnames[specialclasses["end"] ] = "end" callbacks.register("get_noad_class", function(n) return classnames[n] end,"provide math class name") local function registerprivateclass(name,parent) local class = parent and classes[parent] or classes[name] if not class then lastprivateclass = lastprivateclass + 1 class = lastprivateclass classes[name] = class -- also setup end return class end local function toengineclass(class) if type(class) == "string" then return classes[class] or ordinary_class elseif class > lastengineclass then return ordinary_class else return class end end implement { name = "registerengineclass", public = true, protected = true, arguments = { "optional", "optional" }, actions = registerengineclass, } local topaccent_class = registerprivateclass("topaccent") local bottomaccent_class = registerprivateclass("bottomaccent") local delimiter_class = registerprivateclass("delimiter") local root_class = registerprivateclass("root") local prime_class = registerprivateclass("prime") registerprivateclass("botaccent","bottomaccent") local accents = allocate { accent = true, -- some can be both topaccent = true, [topaccent_class] = true, bottomaccent = true, [bottomaccent_class] = true, botaccent = true, under = true, [under_class] = true, over = true, [over_class] = true, unknown = false, } local integer_value = tokens.values.integer implement { name = "mathclassvalue", -- usage = "value", public = true, arguments = "string", actions = function(name) -- return integer_value, classes[name] or ordinary_class context(tostring(classes[name] or ordinary_class)) end } -- used in math-tag: so there we need to make things ord etc to fit within -- mathml local codes = allocate { ordinary = ordinary_class, [ordinary_class] = "ordinary", largeoperator = operator_class, [operator_class] = "largeoperator", binaryoperator = binary_class, [binary_class] = "binaryoperator", relation = relation_class, [relation_class] = "relation", openingsymbol = open_class, [open_class] = "openingsymbol", closingsymbol = close_class, [close_class] = "closingsymbol", punctuation = punctuation_class, [punctuation_class] = "punctuation", middlesymbol = middle_class, [middle_class] = "middlesymbol", } 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) local virtualized = allocate { } function mathematics.virtualize(unicode,virtual) local function virtualize(k,v) local c = virtualized[k] if c == v then report_math("character %C is already virtualized to %C",k,v) elseif c then report_math("character %C is already virtualized to %C, ignoring mapping to %C",k,c,v) else virtualized[k] = v end end if type(unicode) == "table" then for k, v in next, unicode do virtualize(k,v) end elseif type(unicode) == "number" and type(virtual) == "number" then virtualize(unicode,virtual) -- else -- error end end mathematics.extensibles = extensibles mathematics.classes = classes mathematics.toengineclass = toengineclass mathematics.classnames = classnames mathematics.codes = codes -----------.accents = codes mathematics.virtualized = virtualized -- This is relatively new and experimental: do local dictionaries = mathematics.dictionaries or { } mathematics.dictionaries = dictionaries local names = dictionaries.names or utilities.storage.allocate() local groups = dictionaries.groups or utilities.storage.allocate() local data = dictionaries.data or utilities.storage.allocate() local sets = dictionaries.sets or utilities.storage.allocate() local variants = dictionaries.variants or utilities.storage.allocate() -- todo: get from char-def local defaults = dictionaries.defaults or utilities.storage.allocate() -- todo: get from char-def storage.register("mathematics/dictionaries/names", names, "mathematics.dictionaries.names") storage.register("mathematics/dictionaries/groups", groups, "mathematics.dictionaries.groups") storage.register("mathematics/dictionaries/data", data, "mathematics.dictionaries.data") storage.register("mathematics/dictionaries/sets", sets, "mathematics.dictionaries.sets") storage.register("mathematics/dictionaries/variants", variants, "mathematics.dictionaries.variants") storage.register("mathematics/dictionaries/defaults", defaults, "mathematics.dictionaries.defaults") dictionaries.names = dictionaries.names or names dictionaries.groups = dictionaries.groups or groups dictionaries.data = dictionaries.data or data dictionaries.sets = dictionaries.sets or sets dictionaries.variants = dictionaries.variants or variants dictionaries.defaults = dictionaries.defaults or defaults if not sets.n then sets.n = 0 end function dictionaries.registergroup(name) local group = rawget(names,name) if not group then group = #groups + 1 names[name] = group names[group] = group groups[group] = name data[group] = { } local csname = "math" .. nospaces(name) .. "dictionary" texintegerdef(csname,group,"immutable") end return group end function dictionaries.registergroupset(name,set) local s = sets[name] if not s then local d = dictionaries.registergroup(name) local n = sets.n + 1 local l = utilities.parsers.settings_to_array(set) local g = { } for i=1,#l do local n = names[l[i]] if n then g[#g+1] = n -- ordered end end s = { names = l, groups = g, group = d, } sets[name] = s sets[d] = s end end function dictionaries.groupset(name) return sets[name] or { } end function dictionaries.groupsetgroup(name) local s = sets[name] if s then return s.group else return names[name] or 0 end end function dictionaries.registercharacter(group,index,description,class) local d = names[group] -- can be number or string if d then data[d][index] = description or true local v = variants[index] if type(class) == "string" then class = classes[class] end if not class then class = true end if v then v[d] = class else variants[index] = { [d] = class } end if not defaults[index] then defaults[index] = d end end end implement { name = "registergroupset", arguments = "2 strings", actions = dictionaries.registergroupset, } implement { name = "groupsetgroup", arguments = "string", actions = { dictionaries.groupsetgroup, context }, } local f_dictionary = false local whatdetail = "all" local function trace(n,properties,group,index,font,char) -- local properties, group, index, font, char = nodes.nuts.getchardict(nodes.nuts.tonut(n)) if whatdetail and (properties ~= 0 or group ~= 0 or index ~= 0) then local char = fontchardata[font][char] if char or whatdetail == "all" then local unicode = char and char.unicode if unicode then local groupname = groups[group] local indexname = false if groupname then indexname = data[group][index] -- dictionaries.data else groupname = "unknown" end if not indexname or indexname == true then indexname = chardata[unicode] indexname = indexname and indexname.description or "unknown" end if not f_dictionary then f_dictionary = formatters["properties [%04X:%04X:%04X] [%s] %U : %s"] end return f_dictionary(properties,group,index,groupname,unicode,indexname) end end end end trackers.register("math.dictionaries",function(v) whatdetail = v end) callbacks.register("get_math_dictionary",trace,"provide math dictionary details") -- This is experimental and a prelude to the long pending "relate math rendering to -- some field" wish. In TeX characters and symbols are grouped by class but that is -- mostly related to spacing etc. while we actually want to group by meaning. A -- reasonable but incomplete starting point is: -- -- https://www.w3.org/TR/MathML3/appendixa.html#parsing_DefEncAtt -- -- But it has some weird short names mixed with long ones (and a strange suddenly -- uppercase: Differential-Operator) but we are not bound to that at all. We will -- probably remove and add categories anyway. This openmath stuff looks a bit -- abandoned but we can use it as a start and playground anyway. -- -- The char-def.lua file will have mathgroup entries reflecting this. -- -- This is a good one (with nice roll-overs too): -- -- https://en.wikipedia.org/wiki/List_of_mathematical_symbols_by_subject if environment.initex then local registergroup = mathematics.dictionaries.registergroup registergroup("default") registergroup("binary arithmetic") registergroup("binary linear algebra") registergroup("binary logical") registergroup("binary relation") registergroup("binary set") registergroup("constant arithmetic") registergroup("constant set") registergroup("differential") registergroup("integral") registergroup("interval") registergroup("lambda") registergroup("limit") registergroup("nary arithmetic") registergroup("nary constructor") registergroup("nary functional") registergroup("nary linear algebra") registergroup("nary logical") registergroup("nary minmax") registergroup("nary relation") registergroup("nary set list") registergroup("nary set relation") registergroup("nary set") registergroup("nary statistics") registergroup("partial") -- partial differential registergroup("product") registergroup("quantifier") registergroup("unary arithmetic") registergroup("unary elementary") registergroup("unary functional") registergroup("unary linear algebra") registergroup("unary logical") registergroup("unary set") registergroup("unary vector") end -- \Umathdictdef\vdash 1 \mathbinarylogicaldictionary "22A2 \mathrelationcode 0 "22A2 -- -- \startluacode -- mathematics.dictionaries.registercharacter("binary logical",0x22A2,"implies") -- \stopluacode end do local skip = { [accent_class] = true, [topaccent_class] = true, [bottomaccent_class] = true, [over_class] = true, [under_class] = true, [radical_class] = true, [root_class] = true, } local registercharacter = mathematics.dictionaries.registercharacter local groupnames = mathematics.dictionaries.names local setmathcharacter = function(class,family,slot,unicode,mset,dset,group) if mset and class ~= ordinary_class then setmathcode("global",slot,class,family,unicode) mset = false end -- if dset and (class == open_class or class == close_class or class == middle_class or class == division_class) then -- setdelcode("global",slot,family,unicode,0,0) -- dset = false -- end if group then group = groupnames[group] or 0 if group ~= 0 then -- which one registercharacter(group,unicode,nil,class) -- registercharacter(group,slot,nil,class) end end return mset, dset end local function report(class,family,unicode,name) local nametype = type(name) if nametype == "string" then report_math("class %a, family %a, char %C, name %a",class,family,unicode,name) elseif nametype == "number" then report_math("class %a, family %a, char %C, number %U",class,family,unicode,name) else report_math("class %a, family %a, char %C",class,family,unicode) end end local texmathchardef = tex.mathchardef -- local setmathsymbol = function(name,class,family,slot,stretch,group) -- hex is nicer for tracing -- if skip[class] then -- return -- only in mkiv -- elseif class == open_class or class == close_class or class == middle_class then -- setdelcode("global",slot,family,slot,0,0) -- can go -- elseif class == delimiter_class then -- open close or middle (bars) -- setdelcode("global",slot,family,slot,0,0) -- can go -- class = 0 -- else -- if group then -- group = groupnames[group] or 0 -- if group ~= 0 then -- texmathchardef(name,class,family,slot,"permanent",0x1,group,slot) -- return -- end -- end -- end -- texmathchardef(name,class,family,slot,"permanent") -- end local setmathsymbol = function(name,class,family,slot,stretch,group) -- hex is nicer for tracing if skip[class] then return -- only in mkiv -- elseif class == open_class or class == close_class or class == middle_class then else if class == delimiter_class then -- open close or middle (bars) class = ordinary_class end if group then group = groupnames[group] or 0 if group ~= 0 then texmathchardef(name,class,family,slot,"permanent",0x1,group,slot) return end end texmathchardef(name,class,family,slot,"permanent") end end function mathematics.define() if trace_defining then logs.startfilelogging(report_math,"math defined from character definitions") end local family = 0 local data = characters.data -- local function remap(first,last) for unicode=utfbyte(first),utfbyte(last) do setmathcode("global",unicode,ordinary_class,family,unicode) end end remap("0","9") remap("A","Z") remap("a","z") -- setdelcode("global",0x2E,0,0,0,0) -- period is special -- for unicode, character in sortedhash(data) do local symbol = character.mathsymbol local mset = true local dset = true if symbol then local other = data[symbol] local class = other.mathclass if class then local engine = toengineclass(class) if trace_defining then report(engine,family,unicode,symbol) end mset, dset = setmathcharacter(engine,family,unicode,symbol,mset,dset,group) end local spec = other.mathspec if spec then for i=1,#spec do local m = spec[i] local class = m.class if class then local engine = toengineclass(class) -- todo: trace mset, dset = setmathcharacter(engine,family,unicode,symbol,mset,dset,group) end end end end local class = character.mathclass local spec = character.mathspec local name = character.mathname local stretch = character.mathstretch local group = character.mathgroup if spec then local done = false if class then if name then report_math("fatal error, conflicting mathclass and mathspec for %C",unicode) os.exit() else class = classes[class] or ordinary_class local engine = toengineclass(class) if trace_defining then report(engine,family,unicode) end mset, dset = setmathcharacter(engine,family,unicode,unicode,mset,dset,group) done = true end end for i=1,#spec do local m = spec[i] local name = m.name local class = m.class or class local group = m.group or group local stretch = m.stretch or stretch if class then class = classes[class] or ordinary_class else class = ordinary_class end if class then local engine = toengineclass(class) if name then if trace_defining then report(engine,family,unicode,name) end setmathsymbol(name,engine,family,unicode,stretch,group) else name = (class == classes.ordinary or class == classes.digit) and character.adobename -- bad if name and trace_defining then report(engine,family,unicode,name) end end if not done then mset, dset = setmathcharacter(engine,family,unicode,m.unicode or unicode,mset,dset,group) -- see solidus done = true end end end else if class then class = classes[class] or ordinary_class else class = ordinary_class end if name ~= nil then local engine = toengineclass(class) if name == false then if trace_defining then report(engine,family,unicode,name) end mset, dset = setmathcharacter(engine,family,unicode,unicode,mset,dset,group) else -- if not name then -- name = character.contextname -- too dangerous, we loose textslash and a few more -- end if name then if trace_defining then report(engine,family,unicode,name) end setmathsymbol(name,engine,family,unicode,stretch,group) else if trace_defining then report(engine,family,unicode,character.adobename) end end mset, dset = setmathcharacter(engine,family,unicode,unicode,mset,dset,group) end elseif class ~= ordinary_class then local engine = toengineclass(class) if trace_defining then report(engine,family,unicode,character.adobename) end mset, dset = setmathcharacter(engine,family,unicode,unicode,mset,dset,group) end end end -- if trace_defining then logs.stopfilelogging() end end end -- needed for mathml analysis -- string with # > 1 are invalid -- we could cache do 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(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 utfmathlimop(chr) local cd = somechar[chr] return cd and (cd.mathclass == "operator" or cd.mathclass == "integral") or false 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", public = true, actions = { utfmathclass, context }, arguments = "argument" } implement { name = "utfmathstretch", public = true, actions = { utfmathstretch, context }, arguments = "argument" } implement { name = "utfmathcommand", public = true, actions = { utfmathcommand, context }, arguments = "argument" } implement { name = "utfmathfiller", public = true, actions = { utfmathfiller, context }, arguments = "argument" } implement { name = "utfmathcommandabove", public = true, actions = { utfmathcommand, context }, arguments = { "argument", false, "'topaccent'","'over'" } } implement { name = "utfmathcommandbelow", public = true, actions = { utfmathcommand, context }, arguments = { "argument", false, "'bottomaccent'","'under'" } } implement { name = "utfmathcommandfiller", public = true, actions = { utfmathfiller, context }, arguments = "argument" } -- todo: make this a helper: implement { name = "doifelseutfmathabove", public = true, actions = { utfmathaccent, ctx_doifelsesomething }, arguments = { "argument", false, "'topaccent'", "'over'" } } implement { name = "doifelseutfmathbelow", public = true, actions = { utfmathaccent, ctx_doifelsesomething }, arguments = { "argument", false, "'bottomaccent'", "'under'" } } implement { name = "doifelseutfmathaccent", public = true, actions = { utfmathaccent, ctx_doifelsesomething }, arguments = "argument", } implement { name = "doifelseutfmathfiller", public = true, actions = { utfmathfiller, ctx_doifelsesomething }, arguments = "argument", } implement { name = "doifelseutfmathlimop", public = true, actions = { utfmathlimop, ctx_doifelsesomething }, arguments = "argument" } end -- helpers -- -- 1: step 1 -- 2: step 2 -- 3: htdp * 1.33^n -- 4: size * 1.33^n -- 5: use lfg function mathematics.big(tfmdata,unicode,n,method) local t = tfmdata.characters local c = t[unicode] if c and n > 0 then if method == 1 or method == 2 or method == 5 then if method == 5 then local b = tfmdata.bigslots if b then n = (n > #b and b[#b]) or b[n] or n end elseif 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 elseif method >= 3 then 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 do -- experimental local categories = { } mathematics.categories = categories local a_mathcategory = attributes.private("mathcategory") local functions = storage.allocate() categories.functions = functions local noffunctions = 1000 -- offset 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 } end do 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 end implement { name = "resetmathattributes", public = true, protected = true, actions = mathematics.resetattributes } -- weird to do this here but it's a side affect of math anyway implement { name = "enableasciimode", onlyonce = true, actions = resolvers.macros.enablecomment, } implement { name = "nofmathvariants", public = true, usage = "value", arguments = "integer", actions = function(n) local char = fontchardata[getfontoffamily(0)] local data = char[n] local size = 1 while data do local next = data.next if next then size = size + 1 data = char[next] else break end end return integer_value, size end, } implement { name = "getmathvariant", public = true, usage = "value", arguments = "2 integers", actions = function(m,n) local char = fontchardata[getfontoffamily(0)] local data = char[n] local slot = n if data then while data and m > 1 do local next = data.next if next then m = m - 1 data = char[next] slot = next else break end end end return integer_value, slot end, } implement { name = "getmathcharone", public = true, usage = "value", arguments = "integer", actions = function(n) local char = fontchardata[getfontoffamily(0)] local data = char[n] return integer_value, data and data.smaller or n end, } implement { name = "getmathchartwo", public = true, usage = "value", arguments = "integer", actions = function(n) local char = fontchardata[getfontoffamily(0)] local data = char[n] local slot = data and data.smaller if slot then data = char[slot] n = slot end return integer_value, data and data.smaller or n end, }