% Copyright 2019 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% {% \pgfqkeys{/pgfparser}{#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@h 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 {% % check the problematic categories based on the meaning of the token \edef\pgfparser@tokencategory{\pgfparser@extractmeaning\pgfparsertoken}% \pgfparser@getnexttoken@ifcategory\pgfparser@category@i\bgroup {% \pgfparser@getnexttoken@ifcategory\pgfparser@category@ii\egroup {% \pgfparser@getnexttoken@ifcategory\pgfparser@category@x{ }% {% \pgfparser@getnexttoken@ifcategory\pgfparser@category@vi{####}% {\pgfparser@getnexttoken@defchar}% }% }% }% } \begingroup \def\pgfparser@extractmeaning#1% {% \endgroup \def\pgfparser@extractmeaning##1% {\expandafter\pgfparser@extractmeaning@\meaning##1#1#1\pgfparser@mark}% \def\pgfparser@extractmeaning@##1#1##2#1##3\pgfparser@mark{##1#1##2}% } \catcode`\ =12\pgfparser@extractmeaning{ } % ends the group \edef\pgfparser@category@i{\pgfparser@extractmeaning\bgroup} \edef\pgfparser@category@ii{\pgfparser@extractmeaning\egroup} \edef\pgfparser@category@vi{\pgfparser@extractmeaning#} \edef\pgfparser@category@x{\pgfparser@extractmeaning\pgfutil@sptoken} \pgfutil@protected\def\pgfparser@getnexttoken@ifcategory#1#2% {% \ifx\pgfparser@tokencategory#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