7

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.)

2 Answers 2

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).

7
  • 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.
    Commented Oct 8, 2010 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.
    Commented Oct 8, 2010 at 10:47
  • @TH Yes, I fear you are right. Parsing the log file it is, then Commented Oct 8, 2010 at 12:00
  • @TH: In Chapter 21 of the TeXbook it says that \lastbox is always void in math mode. Commented Oct 8, 2010 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. Commented Oct 8, 2010 at 14:55
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

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .