1

I've been trying to get a macro to work without any kind of success.

I want a function \graf{} to take a list of arguments separated by a semicolon. These arguments need to be passed to the function \xygraph, which has a particular syntax.

I'm using xparse, which strips brackets, which are needed for syntax, so I did the following

\NewDocumentCommand{\graf}{ >{ \SplitList{;} } m }{%
    {%
        \NewDocumentCommand{\func}{ m }{%
            !\{##1\}*+\{\thenewcounter \}="\thenewcounter"%
            \addtocounter{newcounter}{1}

            }% 

            \ProcessList{#1}{\func}%

    }%
}

the output of \graf{(0,0);(1,1)} is perfect: it's processed as

!{(0,0)}*+{0}="0"
!{(1,1)}*+{1}="1"

Unfortunately, as soon as I try to pass all of this to \xygraph, things don't go well, and I get the error "argument of \graf has an extra }".

\NewDocumentCommand{\graf}{ >{ \SplitList{;} } m }{%
{%
    \NewDocumentCommand{\func}{ m }{%
        !\{##1\}*+\{\thenewcounter \}="\thenewcounter"%
        \addtocounter{newcounter}{1}

        }% 
    \xygraph{!{<0cm,0cm>;<1cm,0cm>:<0cm,1cm>::}
        \ProcessList{#1}{\func}%
        }
}


}

How do I solve this?

P.S.: it does work if I don't want to do any processing at all

\DeclareDocumentCommand \gra {m}{
\xygraph{%
!{<0cm,0cm>;<1cm,0cm>:<0cm,1cm>::}%
#1 }

But this defeats the purpose of simplifying syntax.

EDIT:

There is a need to add a list of edges for the graph too. Which should look like

\graf{(0,0);(1,1);(1,2)}{0-1;0-2;1-2}

which should translate to

\xygraph{ !{<0cm,0cm>;<1cm,0cm>:<0cm,1cm>::} !{(0,0)}*+{0}="0" !{(1,1)}*+{1}="1" !{(1,2)}*+{2}="2" "0"-"1" "0"-"2" "1"-"2"}

But I'm not good enough with expl3 to make changes on my own.

1 Answer 1

3

You shouldn't be defining \func inside the definition of \graf. On the other hand, the problem is a bit more complex, because \xygraph wants to see its argument already formed.

Here's a way with expl3.

\documentclass{article}
\usepackage{xparse}
\usepackage[all,cmtip]{xy}

\ExplSyntaxOn
\NewDocumentCommand{\graf}{m}
 {
  \andy_graf_input:n { #1 }
 }

\seq_new:N \l_andy_graf_input_seq
\tl_new:N \l_andy_graf_output_tl

\cs_new_protected:Nn \andy_graf_input:n
 {
  \seq_set_split:Nnn \l_andy_graf_input_seq { ; } { #1 }
  \tl_clear:N \l_andy_graf_output_tl
  \int_step_inline:nnnn { 0 } { 1 } { \seq_count:N \l_andy_graf_input_seq - 1 }
   {
    \tl_put_right:Nx \l_andy_graf_output_tl
     {
      !{\seq_item:Nn \l_andy_graf_input_seq { ##1 + 1 }}*+{##1}="##1"
     }
   }
  \andy_graf_make:V \l_andy_graf_output_tl
 }

\cs_new_protected:Nn \andy_graf_make:n
 {% some trickery because of the colons
  \exp_args:Nx \xygraph { !{ \tl_to_str:n { <0cm,0cm>;<1cm,0cm>:<0cm,1cm>:: } \exp_not:n { #1 } } }
 }
\cs_generate_variant:Nn \andy_graf_make:n { V }
\ExplSyntaxOff

\begin{document}

With \texttt{\string\xygraph}: \xygraph{!{<0cm,0cm>;<1cm,0cm>:<0cm,1cm>::!{(0,0)}*+{0}="0"!{(1,1)}*+{1}="1"}}

With \texttt{\string\graf}: \graf{(0,0);(1,1)}

With \texttt{\string\graf}: \graf{(0,0);(1,0);(1,1)}

\end{document}

First the input is split at semicolons, then the required list is prepared and then passed to \xygraph.

There is a small catch, because colons after \ExplSyntaxOn are not the usual colons (they're considered as letters because they must be part of function names.

enter image description here

A new version with the specification for the edges:

\documentclass{article}
\usepackage{xparse}
\usepackage[all,cmtip]{xy}

\ExplSyntaxOn
\NewDocumentCommand{\graf}{mm}
 {
  \andy_graf_input:nn { #1 } { #2 }
 }

\seq_new:N \l_andy_graf_vertices_seq
\seq_new:N \l_andy_graf_edges_seq
\tl_new:N \l_andy_graf_vertices_tl
\tl_new:N \l_andy_graf_edges_tl
\tl_const:Nx \c_andy_graf_start_tl { !{ \tl_to_str:n { <0cm,0cm>;<1cm,0cm>:<0cm,1cm>:: } } }

\cs_new_protected:Nn \andy_graf_input:nn
 {
  % the vertices
  \seq_set_split:Nnn \l_andy_graf_vertices_seq { ; } { #1 }
  \tl_clear:N \l_andy_graf_vertices_tl
  \int_step_inline:nnnn { 0 } { 1 } { \seq_count:N \l_andy_graf_vertices_seq - 1 }
   {
    \tl_put_right:Nx \l_andy_graf_vertices_tl
     {~ % a space
      !{\seq_item:Nn \l_andy_graf_vertices_seq { ##1 + 1 }}*+{##1}="##1"
     }
   }
  % the edges
  \seq_set_split:Nnn \l_andy_graf_edges_seq { ; } { #2 }
  \tl_clear:N \l_andy_graf_edges_tl
  \seq_map_function:NN \l_andy_graf_edges_seq \__andy_graf_edge:n
  % make the graph
  \andy_graf_make:VVV \c_andy_graf_start_tl \l_andy_graf_vertices_tl \l_andy_graf_edges_tl
 }

\cs_new_protected:Nn \andy_graf_make:nnn
 {% some trickery because of the colons
  \xygraph
   {
    #1 % the start
    #2 % the vertices
    #3 % the edges
   }
 }
\cs_generate_variant:Nn \andy_graf_make:nnn { VVV }

\cs_new_protected:Nn \__andy_graf_edge:n
 {
  \tl_set:Nn \l__andy_graf_edge_tl { #1 }
  \regex_replace_all:nnN { ([^\-]*) - (.*) \Z } { \ "\1"-"\2" } \l__andy_graf_edge_tl
  \tl_put_right:NV \l_andy_graf_edges_tl \l__andy_graf_edge_tl
 }
\ExplSyntaxOff

\begin{document}

With \texttt{\string\xygraph}:
  \xygraph{
    !{<0cm,0cm>;<1cm,0cm>:<0cm,1cm>::}
    !{(0,0)}*+{0}="0"
    !{(1,1)}*+{1}="1"
    "0"-"1"
  }

With \texttt{\string\graf}: \graf{(0,0);(1,1)}{0-1}

With \texttt{\string\graf}: \graf{(0,0);(1,0);(1,1)}{0-1;1-2;2-0}

\end{document}

enter image description here

8
  • Works great, thanks! Just an addendum because I have a hard time with expl3: if I wanted \graf to have another mandatory argument (for edges), that looks like \graph{(0,0);(1,1)}{"0"-"1"; ....}, what kind of changes should I have to make? Do you have a reference where I can learn more expl3? Thanks again
    – Andy
    May 8, 2018 at 11:31
  • @Andy I'm not expert with xygraph. Can you please add an example with the proposed syntax and the required full translation in xygraph language?
    – egreg
    May 8, 2018 at 11:41
  • Thank you for you time. Proposed syntax is: \graf{(0,0);(1,1)}{0-1;1-0}, which should translate to \xygraph{ !{<0cm,0cm>;<1cm,0cm>:<0cm,1cm>::} !{(0,0)}*+{0}="0" !{(1,1)}*+{1}="1" "0"-"1" "1"-"0"} Where the new arguments appended at the end of xygraph with quotes.
    – Andy
    May 8, 2018 at 11:44
  • 1
    @Andy Added; enjoy!
    – egreg
    May 8, 2018 at 12:19
  • 1
    @Andy Apparently Overleaf has an outdated version of expl3.
    – egreg
    May 8, 2018 at 15:19

You must log in to answer this question.

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