5

I have some complicated expressions with loads of nested commands which are difficult to work with. To solve this I wrote a parser using pgfparser which creates the expressions for me. I can then copy the output back in to LaTeX and it outputs what I need.

I was wondering if I could automate this. I can store the text (a sequence of commands) in a variable but then I can't find a way to run the commands in the variable

The parser itself is not of much interest but as a mwe this is a simplified version of what I have:

\documentclass{standalone}
\usepackage[T1]{fontenc}
\usepackage{pgf}
\usepgfmodule{parser}

\pgfparserdef{mweparser}{initial}{\meaning [}{\textbackslash overline\{}
\pgfparserdef{mweparser}{initial}{\meaning ]}{\}}
\pgfparserdef{mweparser}{initial}{\meaning A}{A}
\pgfparserdef{mweparser}{initial}{subscript character _}{\_\{\pgfparserswitch{subscript}}
\pgfparserdef{mweparser}{subscript}{\meaning +}{\textbackslash hat\{\pgfparserswitch{edgename}}
\pgfparserdef{mweparser}{subscript}{\meaning -}{\textbackslash check\{\pgfparserswitch{edgename}}
\pgfparserdef{mweparser}{edgename}{\meaning a}{a\}\pgfparserswitch{subscript}}
\pgfparserdef{mweparser}{edgename}{\meaning b}{b\}\pgfparserswitch{subscript}}
\pgfparserdef{mweparser}{subscript}{subscript character _}{\}\pgfparserswitch{initial}}
\pgfparserdef{mweparser}{all}{the character ;}{\pgfparserswitch{final}}

\begin{document}
    \pgfparserparse{mweparser}[A_+a-b-a_[A_+a+b-a_]];%
\end{document}

This will output:

\overline{A_{\hat{a}\check{b}\check{a}}\overline{A_{\hat{a}\hat{b}\check{a}}}}

Which is what I would like to run (in math mode). I can store this text in a variable but can't see how I can run it.

I am new to this stuff so I apologise if I have done anything wrong.

DQuick
  • 53
  • Welcome to TeX.SX! Please make your code compilable (if possible), or at least complete it with \documentclass{...}, the required \usepackage's, \begin{document}, and \end{document}. That may seem tedious to you, but think of the extra work it represents for TeX.SX users willing to give you a hand. Help them help you: remove that one hurdle between you and a solution to your problem. – jub0bs Mar 25 '14 at 17:55
  • Sorry it seems to compile fine to me. I realise my example is quite long. I could create a shorter version. Thanks for replying so fast and for the help. – DQuick Mar 25 '14 at 18:03
  • 1
    Sorry about my last edit (I misinterpreted some of your text as code). Yes, you should reduce your code to the bare minimum (but it should still be compilable) for explaining your problem; you stand a greater chance of getting a good answer quickly if you do that. – jub0bs Mar 25 '14 at 18:08
  • Thanks, I've tried to minimise the parser as much as possible, still presenting the problem I have. Do you have any ideas for other tags I should add? I don't really know how to refer to the problem I have. – DQuick Mar 25 '14 at 18:33

1 Answers1

5

I don't really know how the PGF parser works, but what you get with your code is a string which is not what you want: \textbackslash is not a backslash but a command to print one.

Here's an implementation with l3regex, the regular expression parser of LaTeX3; the wanted result has been added for checking against what the macros do.

\documentclass[border=3,varwidth]{standalone}
\usepackage[T1]{fontenc}
\usepackage{xparse,l3regex}

\ExplSyntaxOn
\NewDocumentCommand{\term}{m}
 {
  \dquick_term:n { #1 }
 }

\tl_new:N \l__dquick_input_tl

\cs_new_protected:Npn \dquick_term:n #1
 {
  \tl_set:Nn \l__dquick_input_tl { #1 }
  %% subscripts (works so long you don't have nested subscripts)
  \regex_replace_all:nnN { \_(.*?)\_ } { \cD\_\cB\{\1\cE\} } \l__dquick_input_tl
  %% [ becomes \overline\bgroup (nesting is allowed)
  \regex_replace_all:nnN { \[ } { \c{overline}\c{bgroup} } \l__dquick_input_tl
  %% ] becomes \egroup
  \regex_replace_all:nnN { \] } { \c{egroup} } \l__dquick_input_tl
  %% +<char> becomes \hat{<char>}
  \regex_replace_all:nnN { \+(.) } { \c{hat}\cB\{\1\cE\} } \l__dquick_input_tl
  %% -<char> becomes \check{<char>}
  \regex_replace_all:nnN { \-(.) } { \c{check}\cB\{\1\cE\} } \l__dquick_input_tl
  $\tl_use:N \l__dquick_input_tl$
 }
\ExplSyntaxOff

\begin{document}
\term{[G_+a-b-c_[H_+a+a-c_]]}

$\overline{G_{\hat{a}\check{b}\check{c}}\overline{H_{\hat{a}\hat{a}\check{c}}}}$
\end{document}

enter image description here


A slightly different version that allows specifying delimiter characters (they must be different) for nested macro, here shown with \overline.

\documentclass[border=3,varwidth]{standalone}
\usepackage[T1]{fontenc}
\usepackage{xparse,l3regex}

\ExplSyntaxOn
\NewDocumentCommand{\term}{m}
 {
  \dquick_term:n { #1 }
 }

\tl_new:N \l__dquick_input_tl

\cs_new_protected:Npn \dquick_term:n #1
 {
  \tl_set:Nn \l__dquick_input_tl { #1 }
  %% subscripts (works so long you don't have nested subscripts)
  \regex_replace_all:nnN { \_(.*?)\_ } { \cD\_\cB\{\1\cE\} } \l__dquick_input_tl
  %% brackets can be nested, this requires a slower routine
  \dquick_replace_nested:nnn { \[ } { \] } { overline }
  %% +<char> becomes \hat{<char>}
  \regex_replace_all:nnN { \+(.) } { \c{hat}\cB\{\1\cE\} } \l__dquick_input_tl
  %% -<char> becomes \check{<char>}
  \regex_replace_all:nnN { \-(.) } { \c{check}\cB\{\1\cE\} } \l__dquick_input_tl
  \tl_use:N \l__dquick_input_tl
 }

\cs_new_protected:Npn \dquick_replace_nested:nnn #1 #2 #3
 {% #1 is the left delimiter
  % #2 is the right delimiter
  % #3 is the macro name for replacement
  \regex_match:nVT { #1 ( [^#1#2]*? ) #2 } \l__dquick_input_tl
   {
    \regex_replace_all:nnN { #1 ( [^#1#2]*? ) #2 } { \c{#3}\cB\{\1\cE\} } \l__dquick_input_tl
    \dquick_replace_nested:nnn { #1 } { #2 } { #3 }
   }
 }
\cs_generate_variant:Nn \regex_match:nnT { nV }

\ExplSyntaxOff

\begin{document}
$\term{[G_+a-b-c_[H_+a+a-c_]]}$

$\overline{G_{\hat{a}\check{b}\check{c}}\overline{H_{\hat{a}\hat{a}\check{c}}}}$
\end{document}
egreg
  • 1,121,712
  • 1
    Incredible answer. I'll have to make sure I understand what is going on and that I can apply it to my more complicated example but it looks perfect. Thanks for putting in the time and effort. – DQuick Mar 25 '14 at 19:06
  • I have one issue still which can be seen by replacing overline in your example with test defined as \newcommand{\test}[1]{l #1 r} then \term{[a]} becomes lra instead of lar. – DQuick Mar 25 '14 at 23:33
  • @DQuick I used the fact that the argument of \overline can be delimited by \bgroup and \egroup. Nested things like those can be tricky with regular expressions. Can you be more precise about your need? – egreg Mar 25 '14 at 23:41
  • I have a few commands defined like \newcommand{\box}[2]=\left[ #1 \right]^{ #2 }. I wish to use these instead of overline above. My plan was to replace with \c{box}\c{bgroup} and with \c{egroup}\cB{\1\cE}. But I guess as you said this is not what bgroup and cgroup do. – DQuick Mar 26 '14 at 00:11
  • @DQuick No, you can't use \bgroup and \egroup for delimiting macro arguments. If those commands are not nested there should be no problem in doing like for _. Oh, and don't redefine \box! – egreg Mar 26 '14 at 00:14
  • The commands are nested which is the problem. I can get around it by using the regex [([^\[\]]*?)](.) which matches the innermost brackets correctly. Then I have to repeat that command n times to allow nesting up to n levels. Is there a command to repeat until it stops finding matches? – DQuick Mar 26 '14 at 00:25
  • Yes, with \regex_match:nnTF you can check whether there's a match and do a recursion; I'll try suggesting something tomorrow. – egreg Mar 26 '14 at 00:29
  • Great thanks. I'm also wondering if there is some way I can have a seperate regex function for my subscripts (I have already written this) and then have ... replaced by \subscriptregex(...). i.e \regex_replace_all:nnN { _(.*?)_ } { \subscriptregex{\1} }. How do I actually apply subscriptregex instead of writing it out? – DQuick Mar 26 '14 at 00:31
  • I didn't see your edit until an hour ago. Everything works as planned now. Thank you so much. – DQuick Mar 28 '14 at 03:20
  • @DQuick I had promised an update, didn't I? ;-) – egreg Mar 28 '14 at 09:38