% Copyright 2009 by Till Tantau
% Copyright 2019 by Jonathan P. Spratte
%
% This file may be distributed and/or modified
%
% 1. under the LaTeX Project Public License and/or
% 2. under the GNU Public License.
%
% See the file doc/generic/pgf/licenses/LICENSE for more details.

\ProvidesFileRCS{pgfmoduleparser.code.tex}

%
% This file defines commands for defining a letter-by-letter parser
% based on a dfa. The idea is inspired by the pgf module parser, but extends it
% and eventually replaced it.
%

% options
\long\def\pgfparserset#1%
  {%
    \pgfset{/pgfparser/.cd,#1}%
  }
\pgfparserset
  {%
    ,silent/.is if=\pgfparser@ifname{every parser silent}%
    ,status/.is if=\pgfparser@ifname{pgfparser status}%
  }

% logic helpers
\long\def\pgfparser@fi@Bn\fi#1{\fi}
\long\def\pgfparser@fi@Bg\fi#1#2{\fi}
\long\def\pgfparser@fi@BTb\fi#1#2#3{\fi#2}
\long\def\pgfparser@fi@BTc\fi#1#2#3{\fi#3}

% opt arg parsing
\long\def\pgfparserifmark#1%
  {%
    \ifx\pgfparser@mark#1%
      \pgfparser@fi@BTb
    \fi
    \pgfutil@secondoftwo
  }
\pgfutil@protected\long\def\pgfparser@oarg@oarg#1%
  {%
    \pgfutil@ifnextchar[{\pgfparser@oarg@oarg@{#1}}{#1{\pgfparser@mark}}%
  }
\long\def\pgfparser@oarg@oarg@#1[#2]%
  {%
    #1{#2}%
  }
\pgfutil@protected\long\def\pgfparser@oarg@Oarg#1#2%
  {%
    \pgfutil@ifnextchar[{\pgfparser@oarg@oarg@{#2}}{#2{#1}}%
  }
\pgfutil@protected\long\def\pgfparser@oarg@darg#1#2#3%
  {%
    \long\def\pgfparser@oarg@darg@##1#1##2#2{##1{##2}}%
    \pgfutil@ifnextchar#1{\pgfparser@oarg@darg@{#3}}{#3{\pgfparser@mark}}%
  }
\pgfutil@protected\long\def\pgfparser@oarg@Darg#1#2#3#4%
  {%
    \long\def\pgfparser@oarg@darg@##1#1##2#2{##1{##2}}%
    \pgfutil@ifnextchar#1{\pgfparser@oarg@darg@{#4}}{#4{#3}}%
  }
\pgfutil@protected\long\def\pgfparser@oarg@targ@#1%
  {%
    \def\pgfparser@oarg@targ@next{#1}%
    \afterassignment\pgfparser@oarg@targ@next
    \let\pgfparser@ignored@token=%
  }
\pgfutil@protected\long\def\pgfparser@oarg@targ#1#2%
  {%
    \pgfutil@ifnextchar#1%
      {\pgfparser@oarg@targ@{#2{\pgfparser@mark}}}{#2{\pgfparser@stop}}%
  }

% status messages
\pgfutil@protected\def\pgfparser@msg#1%
  {%
    \immediate\write0{(pgfparser) #1}%
  }
\pgfutil@protected\def\pgfparser@status@start#1%
  {%
    \pgfparser@if{pgfparser status}{\pgfparser@msg{parser '#1' started}}{}%
  }
\pgfutil@protected\def\pgfparser@status@state#1%
  {%
    \pgfparser@if{pgfparser status}
      {\pgfparser@msg{|= switched to state '#1'}}
      {}%
  }
\pgfutil@protected\def\pgfparser@status@rule
  {%
    \pgfparser@if{pgfparser status}
      {\pgfparser@msg{| \space|= rule '\meaning\pgfparsertoken' executed}}
      {}%
  }
\pgfutil@protected\def\pgfparser@status@rule@all
  {%
    \pgfparser@if{pgfparser status}
      {%
        \pgfparser@msg
          {| \space|= (all) rule '\meaning\pgfparsertoken' executed}%
      }
      {}%
  }
\pgfutil@protected\def\pgfparser@status@rule@undefined
  {%
    \pgfparser@if{pgfparser status}
      {%
        \pgfparser@msg
          {| \space|= unknown for '\meaning\pgfparsertoken' executed}%
      }
      {}%
  }
\pgfutil@protected\def\pgfparser@status@rule@undefined@all
  {%
    \pgfparser@if{pgfparser status}
      {%
        \pgfparser@msg
          {| \space|= (all) unknown for '\meaning\pgfparsertoken' executed}%
      }
      {}%
  }

% macros for string comparison
\begingroup
\lccode`\P=`\P   \lccode`\G=`\G   \lccode`\F=`\F
\lccode`\:=`\X   \lccode`\;=`\r   \lccode`\A=`\-
\lowercase
  {%
    \endgroup
    \def\pgfparser@mark{APGFparse;:mark-}%
    \def\pgfparser@stop{APGFparse;:stop-}%
  }%
\def\pgfparser@final{final}
\def\pgfparser@noarg{noarg}
\def\pgfparser@blankspace{blank space}

% counters
\newcount\pgfparserdef@arg@count

% shortcuts for special names
\def\pgfparser@name#1%
  {%
    pgfparser #1%
  }
\def\pgfparser@rule#1%
  {%
    \pgfparser@current\space #1\space \meaning\pgfparsertoken
  }
\def\pgfparser@type#1%
  {%
    \pgfparser@rule{#1} type%
  }
\def\pgfparser@code#1%
  {%
    \pgfparser@rule{#1} code%
  }
\def\pgfparser@undefined#1%
  {%
    \pgfparser@current\space #1\space unknown%
  }
\def\pgfparser@ifname#1%
  {%
    pgfparserconditional #1%
  }

% conditionals
\def\pgfparser@if#1%
  {%
    \pgfutil@ifundefined{if\pgfparser@ifname{#1}}
      {%
        \pgferror{Undefined conditional '#1'. Falling back to false}%
        \iffalse
      }
      {\csname if\pgfparser@ifname{#1}\endcsname}%
      \pgfparser@fi@BTb
    \fi
    \pgfutil@secondoftwo
  }
\def\pgfparser@newif#1%
  {%
    \csname newif\expandafter\endcsname
    \csname if\pgfparser@ifname{#1}\endcsname
  }
\pgfparser@newif{every parser silent}
\pgfparser@newif{pgfparserdef abort}
\pgfparser@newif{pgfparser status}

% pgfparserdef
\pgfutil@protected\def\pgfparserdef#1#2%
  {%
    \def\pgfparserdef@twoargs{{#1}{#2}}%
    \pgfparserdef@a
  }
\pgfutil@protected\def\pgfparserdef@a
  {%
    \futurelet\pgfparserdef@arg\pgfparserdef@b
  }
\def\pgfparserdef@b
  {%
    \ifx\pgfparserdef@arg\pgfutil@sptoken
      % will also gobble the \pgfparserdef@c after \fi
      \pgfparserdef@gobble@space
    \fi
    \pgfparserdef@c
  }
\pgfutil@protected\def\pgfparserdef@gobble@space\fi#1%
  {%
    \fi
    \afterassignment\pgfparserdef@a
    \let\pgfparser@ignored@token= % space after = to get spaces, too
  }
\pgfutil@protected\def\pgfparserdef@c#1%
  {%
    \ifx\pgfparserdef@arg\bgroup
      \pgfparser@fi@BTb
    \fi
    \pgfutil@secondoftwo
    {%
      \def\pgfparserdef@arg{#1}%
      \ifx\pgfparser@blankspace\pgfparserdef@arg
        \pgfparser@fi@BTb
      \fi
      \pgfutil@secondoftwo
      {\expandafter\pgfparserdef@d\pgfparserdef@twoargs{blank space \space}}
      {\expandafter\pgfparserdef@d\pgfparserdef@twoargs{#1}}%
    }
    {%
      \expandafter\pgfparserdef@d\pgfparserdef@twoargs
        {\meaning\pgfparserdef@arg}%
    }%
  }
\long\def\pgfparserdef@d#1#2#3%
  {%
    \pgfparser@oarg@Oarg{}{\pgfparserdef@e{#1}{#2}{#3}}%
  }
\pgfutil@protected\long\def\pgfparserdef@e#1#2#3#4#5%
  {%
    \pgfparser@initiate@parser{#1}%
    \csname\pgfparser@ifname{pgfparserdef abort}false\endcsname
    % check how many arguments are used, also parses whether the argument string
    % is valid
    \pgfparserdef@arg@count=0
    \pgfparserdef@argcount#4\pgfparser@mark
    \ifnum\pgfparserdef@arg@count>9
      \pgferror
        {%
          Too many arguments for parser rule. A maximum of 9 parameters is
          supported.%
        }%
      \csname\pgfparser@ifname{pgfparserdef abort}true\endcsname
    \fi
    % define what the code does
    \pgfparser@if{pgfparserdef abort}{}
      {%
        % use a macro as flag that there is a rule for the specified state and
        % hold information how to parse it
        \ifnum\pgfparserdef@arg@count=0
          \pgfutil@namedef{\pgfparser@name{#1} #2 #3 type}{noarg}%
          \pgfutil@namedef{\pgfparser@name{#1} #2 #3 code}%
            {#5\pgfparser@getnexttoken}%
        \else
          \pgfutil@namedef{\pgfparser@name{#1} #2 #3 type}{#4}%
          \expandafter\pgfparserdef@argstring@def\the\pgfparserdef@arg@count
          \pgfutil@namedef{\pgfparser@name{#1} #2 #3 code\expandafter}%
            \pgfparserdef@argstring##1##2##3##4##5##6##7##8##9%
            {#5\pgfparser@getnexttoken}%
        \fi
      }%
  }
\def\pgfparserdef@argstring@def#1%
  {%
    \def\pgfparserdef@argstring##1#1##2##{##1#1}%
  }
\def\pgfparserdef@argcount#1%
  {%
    \ifx\pgfparser@mark#1%
      \pgfparser@fi@Bg
    \fi
    \pgfutil@firstofone
    {%
      \pgfutil@IfUndefined{pgfparserdef@argcount@#1}
        {%
          \pgferror{Unknown argument type '#1'. Aborting rule definition}%
          \pgfparserdef@argcount@abort
        }
        {\csname pgfparserdef@argcount@#1\endcsname}%
    }%
  }
\pgfutil@protected\long\def\pgfparserdef@argcount@abort#1\pgfparser@mark
  {%
    \csname\pgfparser@ifname{pgfparserdef abort}true\endcsname
  }
\def\pgfparserdef@argcount@m
  {%
    \advance\pgfparserdef@arg@count1
    \pgfparserdef@argcount
  }
\let\pgfparserdef@argcount@o\pgfparserdef@argcount@m
\def\pgfparserdef@argcount@O#1%
  {%
    \advance\pgfparserdef@arg@count1
    \pgfparserdef@argcount
  }
\let\pgfparserdef@argcount@r\pgfparserdef@argcount@O
\let\pgfparserdef@argcount@t\pgfparserdef@argcount@O
\def\pgfparserdef@argcount@d#1#2%
  {%
    \advance\pgfparserdef@arg@count1
    \pgfparserdef@argcount
  }
\def\pgfparserdef@argcount@D#1#2#3%
  {%
    \advance\pgfparserdef@arg@count1
    \pgfparserdef@argcount
  }

\pgfutil@protected\def\pgfparser@initiate@parser#1%
  {%
    % if that is the first rule for the parser
    \pgfutil@IfUndefined{if\pgfparser@ifname{#1 silent}}%
      {%
        % define space to be a no-op
        \pgfutil@namedef
          {\pgfparser@name{#1} all blank space \space\space type}{noarg}%
        \pgfutil@namedef
          {\pgfparser@name{#1} all blank space \space\space code}%
          {\pgfparser@getnexttoken}%
        % define a new conditional whether the parser silently ignores unknown
        % tokens
        \pgfparser@newif{#1 silent}%
        \pgfparserset
          {%
            #1/silent/.is if=\pgfparser@ifname{#1 silent}%
          }%
      }
      {}%
  }

% pgfparserlet
\pgfutil@protected\def\pgfparserlet#1#2%
  {%
    \def\pgfparserlet@parser@a{#1}%
    \def\pgfparserlet@state@a{#2}%
    \pgfparserlet@a
  }
\pgfutil@protected\def\pgfparserlet@a
  {%
    \futurelet\pgfparserlet@arga\pgfparserlet@b
  }
\def\pgfparserlet@b
  {%
    \ifx\pgfparserlet@arga\pgfutil@sptoken
      % will also gobble the \pgfparserlet@c after \fi
      \pgfparserlet@gobble@space\pgfparserlet@a
    \fi
    \pgfparserlet@c
  }
\pgfutil@protected\def\pgfparserlet@gobble@space#1\fi#2%
  {%
    \fi
    \afterassignment#1%
    \let\pgfparser@ignored@token= % space after = to get spaces, too
  }
\pgfutil@protected\def\pgfparserlet@c#1%
  {%
    \ifx\pgfparserlet@arga\bgroup
      \pgfparser@fi@BTb
    \fi
    \pgfutil@secondoftwo
    {%
      \def\pgfparserlet@arga{#1}%
      \ifx\pgfparser@blankspace\pgfparserlet@arga
        \def\pgfparserlet@arga{blank space \space}%
      \fi
    }
    {\edef\pgfparserlet@arga{\meaning\pgfparserlet@arga}}%
    \pgfparser@oarg@oarg\pgfparserlet@d
  }
\pgfutil@protected\def\pgfparserlet@d#1%
  {%
    \pgfparserifmark{#1}
      {%
        \let\pgfparserlet@parser@b\pgfparserlet@parser@a
        \let\pgfparserlet@state@b\pgfparserlet@state@a
        \pgfparserlet@f
      }
      {\pgfparser@oarg@oarg{\pgfparserlet@e{#1}}}%
  }
\pgfutil@protected\def\pgfparserlet@e#1#2%
  {%
    \pgfparserifmark{#2}
      {%
        \let\pgfparserlet@parser@b\pgfparserlet@parser@a
        \def\pgfparserlet@state@b{#1}%
      }
      {%
        \def\pgfparserlet@parser@b{#1}%
        \def\pgfparserlet@state@b{#2}%
      }
    \pgfparserlet@f
  }
\pgfutil@protected\def\pgfparserlet@f
  {%
    \futurelet\pgfparserlet@argb\pgfparserlet@g
  }
\def\pgfparserlet@g
  {%
    \ifx\pgfparserlet@argb\pgfutil@sptoken
      % will also gobble the \pgfparserlet@c after \fi
      \pgfparserlet@gobble@space\pgfparserlet@f
    \fi
    \pgfparserlet@h
  }
\pgfutil@protected\def\pgfparserlet@h#1%
  {%
    \ifx\pgfparserlet@argb\bgroup
      \pgfparser@fi@BTb
    \fi
    \pgfutil@secondoftwo
    {%
      \def\pgfparserlet@argb{#1}%
      \ifx\pgfparser@blankspace\pgfparserlet@argb
        \def\pgfparserlet@argb{blank space \space}%
      \fi
    }
    {\edef\pgfparserlet@argb{\meaning\pgfparserlet@argb}}%
    \pgfparserlet@i
      \pgfparserlet@parser@a
      \pgfparserlet@state@a
      \pgfparserlet@arga
      \pgfparserlet@parser@b
      \pgfparserlet@state@b
      \pgfparserlet@argb
  }
\def\pgfparserlet@i#1#2#3#4#5#6%
  {%
    \pgfutil@IfUndefined{\pgfparser@name{#4} #5 #6 type}
      {%
        \pgferror
          {%
            There is no rule defined for parser '#4' in state '#5' for '#6'.
            Aborting \string\pgfparserlet
          }%
      }
      {%
        \pgfparser@initiate@parser{#1}%
        \pgfparserlet@let{#1}{#2}{#3}{#4}{#5}{#6}{type}%
        \pgfparserlet@let{#1}{#2}{#3}{#4}{#5}{#6}{code}%
      }%
  }
\def\pgfparserlet@let#1#2#3#4#5#6#7%
  {%
    \expandafter\let\csname\pgfparser@name{#1} #2 #3 #7\expandafter\endcsname
                    \csname\pgfparser@name{#4} #5 #6 #7\endcsname
  }

\pgfutil@protected\long\def\pgfparserdefunknown#1#2#3%
  {%
    \pgfparser@initiate@parser{#1}%
    \pgfutil@namedef{\pgfparser@name{#1} #2 unknown}{#3}%
  }
\pgfutil@protected\long\def\pgfparserdeffinal#1#2%
  {%
    \pgfutil@namedef{\pgfparser@name{#1} final action}{#2}%
  }
\pgfutil@protected\def\pgfparserswitch#1%
  {%
    \pgfparser@status@state{#1}%
    \def\pgfparserstate{#1}%
  }
\pgfutil@protected\def\pgfparserparse#1%
  {%
    \pgfutil@IfUndefined{if\pgfparser@ifname{#1 silent}}
      {\pgferror{No parser named '#1' defined}}
      {%
        \edef\pgfparser@current{\pgfparser@name{#1}}%
        \def\pgfparser@usersname{#1}%
        \pgfparser@status@start{#1}%
        \pgfparserswitch{initial}%
        \pgfparser@getnexttoken
      }%
  }
% get next token
\pgfutil@protected\def\pgfparser@getnexttoken
  {%
    \ifx\pgfparserstate\pgfparser@final
      \pgfparser@fi@BTb
    \fi
    \pgfutil@secondoftwo
    {%
      \pgfutil@IfUndefined{\pgfparser@current\space final action}
        {}
        {\csname \pgfparser@current\space final action\endcsname}%
    }
    {%
      \futurelet\pgfparsertoken\pgfparser@getnexttoken@a
    }%
  }
\pgfutil@protected\def\pgfparser@getnexttoken@a
  {%
    \pgfparser@getnexttoken@iftokengobble\bgroup\bgroup
      {%
        \pgfparser@getnexttoken@iftokengobble\egroup\egroup
          {%
            \pgfparser@getnexttoken@iftokengobble\pgfutil@sptoken{ }%
              {\pgfparser@getnexttoken@defchar}%
          }%
      }%
  }
\pgfutil@protected\def\pgfparser@getnexttoken@iftokengobble#1#2%
  {%
    \ifx\pgfparsertoken#1%
      \def\pgfparserletter{#2}%
      \pgfparser@getnexttoken@gobble
    \fi
    \pgfutil@firstofone
  }
\pgfutil@protected\def\pgfparser@getnexttoken@gobble\fi#1#2%
  {%
    \fi
    \afterassignment\pgfparser@getnexttoken@b
    \let\pgfparser@ignored@token= % gobble should gobble a space
  }
\pgfutil@protected\long\def\pgfparser@getnexttoken@defchar#1%
  {%
    \def\pgfparserletter{#1}%
    \pgfparser@getnexttoken@b
  }
\pgfutil@protected\def\pgfparser@getnexttoken@b
  {%
    \pgfutil@IfUndefined{\pgfparser@type{\pgfparserstate}}
      {%
        \pgfutil@IfUndefined{\pgfparser@type{all}}
          {%
            \pgfparser@if{every parser silent}{}
              {%
                \pgfparser@if{\pgfparser@usersname\space silent}{}
                  {%
                    \pgferror
                      {%
                        No rule for parser '\pgfparser@usersname' in state
                        '\pgfparserstate' for '\meaning\pgfparsertoken'.
                        Ignoring token%
                      }%
                  }%
              }%
            \pgfutil@IfUndefined{\pgfparser@undefined{\pgfparserstate}}
              {%
                \pgfutil@IfUndefined{\pgfparser@undefined{all}}
                  {}
                  {%
                    \pgfparser@status@rule@undefined@all
                    \csname\pgfparser@undefined{all}\endcsname
                  }%
              }
              {%
                \pgfparser@status@rule@undefined
                \csname\pgfparser@undefined{\pgfparserstate}\endcsname
              }%
            \pgfparser@getnexttoken
          }
          {%
            \pgfparser@status@rule@all
            \pgfparser@handle{all}%
          }%
      }
      {%
        \pgfparser@status@rule
        \pgfparser@handle{\pgfparserstate}%
      }%
  }

% handle arguments
\pgfutil@protected\def\pgfparser@handle#1%
  {%
    \expandafter\ifx\csname\pgfparser@type{#1}\endcsname\pgfparser@noarg
      \pgfparser@fi@BTb
    \fi
    \pgfutil@secondoftwo
    {\csname\pgfparser@code{#1}\endcsname}
    {\pgfparser@handle@args{#1}}%
  }
\pgfutil@protected\def\pgfparser@handle@args#1%
  {%
    \def\pgfparser@stateorall{#1}%
    \expandafter\expandafter\expandafter\pgfparser@handle@args@a
    \csname\pgfparser@type{#1}\endcsname\pgfparser@mark\pgfparser@stop
  }
\def\pgfparser@handle@args@a
  {%
    \pgfparser@handle@args@b{}%
  }
\pgfutil@protected\long\def\pgfparser@handle@args@b#1#2#3\pgfparser@stop
  {%
    \pgfparserifmark{#2}
      {\csname\pgfparser@code{\pgfparser@stateorall}\endcsname#1}
      {%
        \pgfutil@IfUndefined{pgfparser@handle@args@#2}
          {\pgferror{Unknown argument type '#2'}}
          {\csname pgfparser@handle@args@#2\endcsname{#1}{#3}}%
      }%
  }
\long\def\pgfparser@handle@args@c#1#2%
  {%
    \pgfparserifmark{#2}
      {\csname\pgfparser@code{\pgfparser@stateorall}\endcsname#1}
      {\pgfparser@handle@args@b{#1}#2\pgfparser@stop}%
  }
\long\def\pgfparser@handle@args@m#1#2#3%
  {%
    \pgfparser@handle@args@c{#1{#3}}{#2}%
  }
\pgfutil@protected\long\def\pgfparser@handle@args@o#1#2%
  {%
    \pgfparser@oarg@oarg{\pgfparser@handle@args@m{#1}{#2}}%
  }
\long\def\pgfparser@handle@args@O#1#2%
  {%
    \pgfparser@handle@args@O@{#1}#2\pgfparser@stop
  }
\pgfutil@protected\long\def\pgfparser@handle@args@O@#1#2#3\pgfparser@stop
  {%
    \pgfparser@oarg@Oarg{#2}{\pgfparser@handle@args@m{#1}{#3}}%
  }
\long\def\pgfparser@handle@args@d#1#2%
  {%
    \pgfparser@handle@args@d@{#1}#2\pgfparser@stop
  }
\pgfutil@protected\long\def\pgfparser@handle@args@d@#1#2#3#4\pgfparser@stop
  {%
    \pgfparser@oarg@darg{#2}{#3}{\pgfparser@handle@args@m{#1}{#4}}%
  }
\long\def\pgfparser@handle@args@D#1#2%
  {%
    \pgfparser@handle@args@D@{#1}#2\pgfparser@stop
  }
\pgfutil@protected\long\def\pgfparser@handle@args@D@#1#2#3#4#5\pgfparser@stop
  {%
    \pgfparser@oarg@Darg{#2}{#3}{#4}{\pgfparser@handle@args@m{#1}{#5}}%
  }
\long\def\pgfparser@handle@args@r#1#2%
  {%
    \pgfparser@handle@args@r@a{#1}#2\pgfparser@stop
  }
\pgfutil@protected\long\def\pgfparser@handle@args@r@a#1#2#3\pgfparser@stop
  {%
    \def\pgfparser@handle@args@r@b##1#2%
      {\pgfparser@handle@args@c{#1{##1}}{#3}}%
    \pgfparser@handle@args@r@b
  }
\long\def\pgfparser@handle@args@t#1#2%
  {%
    \pgfparser@handle@args@t@{#1}#2\pgfparser@stop
  }
\pgfutil@protected\long\def\pgfparser@handle@args@t@#1#2#3\pgfparser@stop
  {%
    \pgfparser@oarg@targ{#2}{\pgfparser@handle@args@m{#1}{#3}}%
  }

\pgfutil@protected\long\def\pgfparserreinsert#1%
  {%
    \ifx\pgfparser@getnexttoken#1%
      \pgfparser@fi@BTb
    \fi
    \@secondoftwo
    {\expandafter\pgfparser@getnexttoken\pgfparserletter}
    {%
      \pgferror
        {%
          Misuse of \string\pgfparserreinsert\space detected. You should only
          use it at the the end of an action code%
        }%
      #1%
    }%
  }

\endinput
