Index: cortav.lua ================================================================== --- cortav.lua +++ cortav.lua @@ -451,11 +451,11 @@ -- generate a branch job linked to this job local branch = getmetatable(me)(name, me.doc, pred, ...) branch.parent = me return branch end; - delegate = function(me, ext) -- creates a delegate for state access + delegate = function(me, ext) -- creates a delegate for hierarchical state access local submethods = { unwind = function(self, n) local function climb(dlg, job, n) if n == 0 then @@ -487,15 +487,15 @@ __newindex = function(self, key, value) local D = self._delegate_state if key == 'state' then D.target.states[D.extension] = value else - D.target[D.extension] = value + D.target[D.extension] = value -- FIXME?? is this right??? end end; - }); - return d; + }) + return d end; each = function(me, ...) local ek local path = {...} return function() @@ -613,11 +613,11 @@ -- sections. to handle this, we provide a "namespace" mechanism, -- where some lua table (really its address in memory) is used -- as a handle for the object and a unique ID is attached to it. -- if the object has an ID of its own, it is guaranteed to be -- unique and returned; otherwise, a generic id of the form --- `x-%u` is generated, where %u is an integer that increments +-- `x-%u` is generated, where %u is an integer that incrementsfile:///home/lexi/dev/cortav/build/cortav.html -- for every new object local ids = {} local canonicalID = {} return function(obj,pfx) pfx = pfx or '' @@ -756,10 +756,11 @@ end ct.spanctls = { {seq = '!', parse = formatter 'emph'}; {seq = '*', parse = formatter 'strong'}; {seq = '~', parse = formatter 'strike'}; + {seq = '_', parse = formatter 'underline'}; {seq = '+', parse = formatter 'insert'}; {seq = '\\', parse = function(s, c) -- raw return { kind = 'raw'; spans = {s}; @@ -767,10 +768,12 @@ } end}; {seq = '`', parse = formatter 'literal'}; {seq = '"', parse = rawcode}; {seq = '$', parse = formatter 'variable'}; + {seq = "'", parse = formatter 'super'}; + {seq = ',', parse = formatter 'sub'}; {seq = '^', parse = function(s, c) -- TODO support for footnote sections local fn, t = s:match '^([^%s]+)%s*(.-)$' return { kind = 'footnote'; @@ -778,23 +781,25 @@ ref = fn; origin = c:clone(); } end}; {seq = '=', parse = function(s,c) --math mode - local tx = { - ['%*'] = '×'; - ['/'] = '÷'; - } - for k,v in pairs(tx) do s = s:gsub(k,v) end - s=s:gsub('%^([0-9]+)', function(num) - local sup = {'⁰','¹','²','³','⁴','⁵','⁶','⁷','⁸','⁹'}; - local r = '' - for i=1,#num do - r = r .. sup[1 + (num:byte(i) - 0x30)] + if c.doc.enc ~= ss.str.enc.ascii then + for _,v in pairs(ss.compseq.math) do + local seq, utf8, html, cp = table.unpack(v) + seq = seq:gsub('[-+.*?[%]%%]', '%%%0') -- >_< + s = s:gsub(seq,c.doc.enc.encodeUCS(utf8)) end - return r - end) + end +-- s=s:gsub('%^([0-9]+)', function(num) +-- local sup = {'⁰','¹','²','³','⁴','⁵','⁶','⁷','⁸','⁹'}; +-- local r = '' +-- for i=1,#num do +-- r = r .. sup[1 + (num:byte(i) - 0x30)] +-- end +-- return r +-- end) local m = {s} --TODO return { kind = 'math'; original = s; spans = {s}; Index: ext/transmogrify.lua ================================================================== --- ext/transmogrify.lua +++ ext/transmogrify.lua @@ -22,10 +22,12 @@ ['<--'] = '←'; ['==>'] = '⇒'; ['<=>'] = '⇔'; ['<=='] = '⇐'; ['=/='] = '≠'; + ['::='] = '⩴'; + [':='] = '≔'; ['---'] = '⸺'; }; { ['-:-'] = '÷'; @@ -140,10 +142,13 @@ version = ss.version {0,1; 'devel'}; contributors = {{name='lexi hale', handle='velartrill', mail='lexi@hale.su', homepage='https://hale.su'}}; default = true; -- on unless inhibited slow = true; hook = { + doc_macro_expand_span = function(job, ir, block) + enterspan(block.origin, ir) + end; doc_meddle_ast = function(job) for n, sec in pairs(job.doc.secorder) do if sec.kind=='ordinary' or sec.kind=='quote' or sec.kind=='footnote' then for i, block in pairs(sec.blocks) do Index: render/groff.lua ================================================================== --- render/groff.lua +++ render/groff.lua @@ -394,11 +394,18 @@ if type(r) ~= 'string' then b.origin:fail('%s is an object, not a reference', t.ref) end local mctx = b.origin:clone() mctx.invocation = m - rs.renderSpans(rc, ct.parse_span(r, mctx)) + + local ir = ct.parse_span(r, mctx) + local j = b.origin.doc.docjob + for fn, ext, state in j:each('hook', 'doc_macro_expand_span') do + local r = fn(j:delegate(ext), ir, b) + if r then ir = r end + end + rs.renderSpans(rc, ir) end function rs.renderSpans(rc, sp, b, sec) rc = rc or mkrc(b.origin) for i, v in ipairs(sp) do Index: render/html.lua ================================================================== --- render/html.lua +++ render/html.lua @@ -686,11 +686,21 @@ local span_renderers = {} local plainrdr = getBaseRenderers(tagproc.toTXT, span_renderers) local htmlSpan = getBaseRenderers(procs, span_renderers).htmlSpan function span_renderers.format(sp,...) - local tags = { strong = 'strong', emph = 'em', strike = 'del', insert = 'ins', literal = 'code', variable = 'var'} + local tags = { + strong = 'strong'; + emph = 'em'; + strike = 'del'; + insert = 'ins'; + literal = 'code'; + variable = 'var'; + super = 'sup'; + sub = 'sub'; + underline = 'u'; + } if sp.style == 'literal' and not opts['fossil-uv'] then addStyle 'code' elseif sp.style == 'strike' or sp.style == 'insert' then addStyle 'editors_markup' elseif sp.style == 'variable' then @@ -766,11 +776,19 @@ if type(r) ~= 'string' then b.origin:fail('%s is an object, not a reference', r.id) end local mctx = b.origin:clone() mctx.invocation = m - return htmlSpan(ct.parse_span(r, mctx),b,s) + local ir = ct.parse_span(r, mctx) + -- even though this happens at render time, it really shouldn't; + -- we pretend this is happening as part of the document job + local j = b.origin.doc.docjob + for fn, ext, state in j:each('hook', 'doc_macro_expand_span') do + local r = fn(j:delegate(ext), ir, b) + if r then ir = r end + end + return htmlSpan(ir, b, s) end function span_renderers.math(m,b,s) addStyle 'math' local spans = {} local function fmt(sp, target) Index: sirsem.lua ================================================================== --- sirsem.lua +++ sirsem.lua @@ -1641,5 +1641,47 @@ return c == pc end; }; } ss.mime.exn = ss.exnkind 'MIME error' + + +-- a composition table maps from a compose sequence to the tuple +-- {UTF8, htmlentity, UCS codepoint} +ss.compseq = { + math = { + {'*', '×', 'times', 0x2A2F}; + {'/', '÷', 'divide', 0x00F7}; + {'-', '−', 'minus', 0x2212}; + {'+-', '±', 'plusmn', 0x00B1}; + {'&&', '∧', 'and', 0x2227}; + {'||', '∨', 'or', 0x2228}; + {'&', '⋏', nil, 0x22CF}; + {'|', '⋎', nil, 0x22CE}; + {'~', '¬', 'not', 0x00AC}; + {'~=', '≠', 'ne', 0x2260}; + {'^=', '≜', 'trie', 0x225C}; + {':=', '≔', 'coloneq', 0x2254}; + {'::=', '⩴', nil, 0x2A74}; + {'==', '≡', 'equiv', 0x2261}; + {'===', '≣', nil, 0x2263}; + {'<=', '≤', 'le', 0x2264}; + {'>=', '≥', 'ge', 0x2265}; + {'?=', '≟', 'questeq', 0x225F}; + {'@<', '∝', 'prop', 0x221D}; + {'<>', '⋄', nil, 0x22C4}; + {'~~', '≈', 'asymp', 0x2248}; + {'<==>', '⟺', 'Longleftrightarrow', 0x27FA}; + {'<=>', '⇔', 'hArr', 0x21D4}; + {'==>', '⟹', 'DoubleLongRightArrow', 0x27F9}; + {'=>', '⇒', 'rArr', 0x21D2}; + {'<->', '↔', 'harr', 0x2194}; + {'->', '→', 'rarr', 0x2192}; + {'<-', '←', 'ShortLeftArrow', 0x2190}; + {'~|', '⊕', 'oplus', 0x2295}; + {'@A', '∀', 'forall', 0x2200}; + {'~@E', '∄', 'NotExists', 0x2204}; + {'@E', '∃', 'exist', 0x2203}; + {'.*.', '∴', 'therefore', 0x2234}; + {'*.*', '∵', 'because', 0x2235}; + }; +};