4

A comment below has clarified my problem, it has nothing to do with lua. In the code snippet below:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\cs_new_protected:Npn \sops_format_set_expr:n #1 {
    \tl_set:Nn \l_tmpa_tl {A+\overline{(B+A)}}
    \tl_replace_all:Nnn \l_tmpa_tl {+}{\cup}
    \tl_use:N \l_tmpa_tl
}

\NewDocumentCommand{\seval}{ m }{
    $\sops_format_set_expr:n {#1}$
}
\ExplSyntaxOff

\begin{document}

\seval{A+-(B+A)}

\end{document}

Is there a better way to perform the replacement + to \cup, than (for example) iterating over the string A+\overline{(B+A)} with \tl_head, checking for head=group along the way, and then piecing it back together?



Background: I'm making a little package to evaluate and format some set expressions. I have code to do the evaluation, however printing is an issue. I have code in lua that will evaluate an expression of the form A+(B*C) (with + interpreted as set union, and * interpreted as intersection) and print the resulting set. Finding an appropriate way to handle complements, resulted in (what I felt was) the need to use pattern matching in lua to handle balanced delimiters properly. As a result, part of the code does some string manipulation in lua, and spits it back to tex to be formatted.

Question: The code below attempts to take a string of the form -(<stuff>), and replace it with \overline{(<stuff>)} in tex. See an earlier question of mine for reference.

\begingroup
  \catcode`\%=12\relax
  \gdef\patmatch{"%-(%b())","\noexpand\\overline{%1}"}
\endgroup

\def\setcomp#1{%
  \directlua{
    local s, _ = string.gsub("\luatexluaescapestring{#1}",\patmatch)
    tex.sprint(s)
    %tex.sprint(-2,s) 
  }
}

I would like to have tex parse the resulting string (?) to replace * with \cap and + with \cup. The issue is that in the replacement, tex does not recognize any + that is handled by lua in the string substitution above. The code below "accomplishes" the substitution:

\cs_new_protected:Npn \sops_format_set_expr:n #1 {
    \tl_set:Nx \l_tmpa_tl {\setcomp{#1}}
    \tl_replace_all:Nnn \l_tmpa_tl {+}{\cup}
    \tl_use:N \l_tmpa_tl
}

\NewDocumentCommand{\seval}{ m }{
    $\sops_format_set_expr:n {#1}$
}

When \seval is called on something like \seval{A+-(B+A)}, the -(B+A) is first handled by the string substitution and replaced with \overline{(B+A)}, when we have A+\overline{(B+A)} to be scanned for replacement of + with \cup. The problem is that the first occurrence of + (the one not involved with the string substitution) is recognized, while the second one is not. I blindly attempted to use string.sprint(-2,s) as commented in the first code block above, in which case the second + is recognized, but the macro is not.

As usual, my puny brain is unable to handle the intricacies of tex/lua catcode interaction. Could someone please explain what the problem is, and how to fix it? MWE below.

\documentclass{article}
\usepackage{xparse}

\begingroup
  \catcode`\%=12\relax
  \gdef\patmatch{"%-(%b())","\noexpand\\overline{%1}"}
\endgroup

\def\setcomp#1{%
  \directlua{
    local s, _ = string.gsub("\luatexluaescapestring{#1}",\patmatch)
    tex.sprint(s)
  }
}

\ExplSyntaxOn

\cs_new_protected:Npn \sops_format_set_expr:n #1 {
    \tl_set:Nx \l_tmpa_tl {\setcomp{#1}}
    \tl_replace_all:Nnn \l_tmpa_tl {+}{\cup}
    \tl_use:N \l_tmpa_tl
}

\NewDocumentCommand{\seval}{ m }{
    $\sops_format_set_expr:n {#1}$
}
\ExplSyntaxOff


\begin{document}

\seval{A+-(B+A)}

\end{document}
3
  • 2
    \tl_replace works on a “item” basis, not on a token basis. So if you have {+} it's seen not as {, + and } (three tokens) but as {+} (one item), and you can't reach the + inside a group {}.
    – Manuel
    Sep 18, 2014 at 22:43
  • @Manuel Oh wow, I feel a little dumb now. My whole question could have been about 5 lines long haha.
    – Scott H.
    Sep 18, 2014 at 22:56
  • @Manuel Thanks btw. Can you think of a better solution than iterating with \tl_head, handling a group as the head separately, and piecing back together?
    – Scott H.
    Sep 18, 2014 at 23:01

1 Answer 1

2

The only problem is replacing, that \tl_replace_… doesn't work as you expect (everything inside {} is hid and can't be found). So the problematic line is only

\tl_replace_all:Nnn \l_tmpa_tl { + } { \cup }

Option 1

Load etoolbox and use

\patchcmd \l_tmpa_tl { + } { \cup } { } { }

instead. The problem is that this doesn't work like \tl_replace_all but \tl_replace_once.

Option 2

Load l3regex and substitute that line by

\regex_replace_all:nnN { \+ } { \c{cup} } \l_tmpa_tl
1
  • 1
    regexpatch offers \xpatchcmd* for that precise purpose. But since it's based on l3regex, it's simpler using it directly.
    – egreg
    Sep 18, 2014 at 23:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.