6

In the course of trying to answer this question, I now realize that I need to be able to construct a math list and then check if it consists of a single character atom.

What I would like to be able to do is to deconstruct the math list similar to "Dissecting paragraphs with \lastbox" in TeX by Topic.

As described in this answer, one can use \showlists to write a representation of the partial lists to the log file. That doesn't seem to help me unless I want to parse the log file. (I don't.)

TH.
  • 62,639

2 Answers2

6

I am pretty sure you can do something that extends the TeX by Topic with \lastbox and box unrolling after the paragraph has completed, but (besides being very tedious) that will not give you access to the actual math list, only to its converted format as a nested tree of \hbox and \vbox-es.

TeX82 does not offer access to the math list other than via \showlists (and for once I think a luatex answer would not really help).

Joseph Wright
  • 259,911
  • 34
  • 706
  • 1,036
Taco Hoekwater
  • 13,724
  • 43
  • 67
  • I'm not sure how to deal with the \mathon/\mathoff itemsssssss that appear in the boxes. For example, \hbox{$x$} gives \mathon, \teni x, and \mathoff in plain TeX. I guess I haven't played with box unrolling much to know if there's anything that can be done to see that this is just a \teni x, for example. If I could do that, I think that'd be sufficient. – TH. Oct 08 '10 at 10:35
  • To follow up on that, \hbox{$x$\setbox0\lastbox\showbox0} tells me that \box0 is void. This makes sense since \mathoff is not a box. I might just be out of luck here. – TH. Oct 08 '10 at 10:47
  • @TH Yes, I fear you are right. Parsing the log file it is, then – Taco Hoekwater Oct 08 '10 at 12:00
  • @TH: In Chapter 21 of the TeXbook it says that \lastbox is always void in math mode. – Hendrik Vogt Oct 08 '10 at 13:33
  • @TH: The following might be worth pointing out: It is not only that \mathoff is not a box, it is not even a TeX command. – Hendrik Vogt Oct 08 '10 at 14:55
  • @Hendrik Vogt: Yes, you're right on both counts. – TH. Oct 09 '10 at 13:20
  • @Taco Hoekwater: That's too bad. Parsing the log file is far, far more effort than I'm willing to go to here. If there were something similar to \meaning for \showlists, that'd be quite handy. Oh well. – TH. Oct 09 '10 at 13:22
6

It is only possible to dissect math lists when using LuaTeX. For instance, you can use the mlist_to_hlist callback. The code below emulates \showlists for math lists, to some limited extent: for instance \radical is not taken care of.

Note that I am very much a LuaTeX newbie, so the code is probably very far from optimal, and doesn't follow standard best practices.

\begingroup
\catcode`~=12
\catcode`#=12
\escapechar=-1
\edef\\{\string\\}
\directlua
  {%
    local my = {}
    my.node = {}
    function my.node.tostring(t, newline)
        if t == nil then
            return ''
        end
        if my[node.type(t.id)] ~= nil then
            return my[node.type(t.id)].tostring(t, newline)
        else
            local result = newline .. node.type(t.id)
            for _, v in ipairs(node.fields(t.id)) do
                if v == 'id' or t[v] == nil then else
                    local value = t[v]
                    if type(value) == "userdata" then
                        value = node.type(value.id)
                    end
                    result = result .. ', ' .. v .. ': ' .. value
                end
            end
            return result
        end
    end
    %
    my.noad = {}
    my.noad.subtypes = {'op', nil, nil, 'bin', 'rel', 'open',
      'close', 'punct', 'inner', [0] = 'ord'}
    function my.noad.subtype(subtype)
        if my.noad.subtypes[subtype] == nil then
            return '\\\\math?'
        else
            return '\\\\math' .. my.noad.subtypes[subtype]
        end
    end
    function my.noad.tostring(t, newline)
        local result = newline .. my.noad.subtype(t.subtype)
        if t.attr ~= nil then
            result = result .. t.attr
        end
        result = result
                 .. my.node.tostring(t.nucleus, newline .. '.')
                 .. my.node.tostring(t.sup, newline .. '^')
                 .. my.node.tostring(t.sub, newline .. '_')
        return result
    end
    %
    my.math_char = {}
    function my.math_char.tostring(t, newline)
        return newline .. '\\\\fam' .. t.fam .. ' ' .. string.char(t.char)
    end
    %
    my.fence = {}
    function my.fence.tostring(t, newline)
        fencename = ({'\\\\left', '\\\\middle', '\\\\right'})[t.subtype]
        return newline .. fencename .. my.node.tostring(t.delim, ' ')
    end
    %
    my.delim = {}
    function my.delim.tostring(t, newline)
        return
            newline
            .. 'small: \\\\fam'
            .. t.small_fam
            .. ' '
            .. (t.small_char == 0 and '^'..'^@' or string.char(t.small_char))
            .. ', large: \\\\fam'
            .. t.large_fam
            .. ' '
            .. (t.large_char == 0 and '^'..'^@' or string.char(t.large_char))
    end
    %
    % subtype and attr ignored for sub_mlist?
    my.sub_mlist = {}
    function my.sub_mlist.tostring(mlist, newline)
        return my.mlist.tostring(mlist.head, newline)
    end
    %
    my.mlist = {}
    function my.mlist.tostring(h, newline)
        local result = ''
        for t in node.traverse(h) do
            result = result .. my.node.tostring(t, newline)
        end
        return result
    end
    %
    callback.register
      (
        "mlist_to_hlist",
        function (h, d, p)
            local tab = {}
            texio.write_nl('\\n')
            my.mlist.tostring(h, '\\n'):gsub('([^\\n]+)', texio.write_nl)
            texio.write_nl('\\n')
            return node.mlist_to_hlist(h, d, p)
        end
      )
  }
\endgroup

abc $ \mathord{\mathord{a}^b}^b \sum + = (),\left(2\middle|3\right)\sqrt{a}$ f

\bye