r3tex

custom LuaTeX format
git clone git://git.rr3.xyz/r3tex
Log | Files | Refs | README | LICENSE

commit 4dbb21aa0ed477a91422b43595d2d1bbc380a265
parent 3aa814ab800721f56bf67c01fa698184cc1ab57d
Author: Robert Russell <robertrussell.72001@gmail.com>
Date:   Sun, 16 Apr 2023 16:27:21 -0700

Add macros for defining "parsers"

See in-source documentation.

Diffstat:
Mutil.tex | 98+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 94 insertions(+), 4 deletions(-)

diff --git a/util.tex b/util.tex @@ -5,6 +5,12 @@ \newskip\centering \centering=0pt plus1000pt minus1000pt \newbox\voidbox +% These macros help cope with TeX's bad behaviour regarding \afterassignment +% and \setbox with a literal box (e.g., \hbox{...}). +\newcount\@grouplevel +\def\@checkgrouplevel{\ifnum\currentgrouplevel>\@grouplevel \ea\aftergroup \fi} +\def\@savegrouplevel{\@grouplevel\currentgrouplevel} + % \defevent defines an "event". Hooks can be attached to events with \addhook, % and all hooks are run in order when the event is triggered with \event. % Events can have parameters like macros, and hooks have access to these @@ -20,7 +26,7 @@ \protected\def\defevent#1[#2]{% \toksA={#1}% Double hashes \xcsdef{event:#2}{\def\nx\@hooks\the\toksA}% - \ea\newtoks\begcs hooks:#2\endcs \cs{hooks:#2}={}% + \ea\gnewtoks\begcs hooks:#2\endcs \cs{hooks:#2}={}% } \protected\def\event[#1]{% \relax % Events can appear in weird places (e.g., the standard \par event) @@ -29,6 +35,93 @@ } \protected\def\addhook[#1]#2{\cs{hooks:#1}\ea{\the\cs{hooks:#1}#2}} +% \begparser...\endparser defines a "parser". Usage: +% \begparser\x +% \stage<param 0>{<body 0>}<assign 0>... +% \stage<param 1>{<body 1>}<assign 1>... +% etc... +% \stage<param n-1>{<body n-1>}<assign n-1>... +% \stage<param n>{<body n>}... +% \endparser +% +% This defines a series of macros of form +% \csdef{x:0}<param 0>{<body 0> \afterassignment\x:1 <assign 0>} +% \csdef{x:1}<param 1>{<body 1> \afterassignment\x:2 <assign 1>} +% etc... +% \csdef{x:n-1}<param n-1>{<body n-1> \afterassignment\x:n <assign n-1>} +% \csdef{x:n}<param n>{<body n>} +% +% Any of the <assign i>'s may be empty, in which case the corresponding +% \afterassignment is omitted. Also, any of the \stage's may be replaced by a +% \boxstage, in which case an intermediate stage is inserted to deal with the +% behaviour of \afterassignment on box assignments with literal boxes +% (e.g., \hbox{...}). +% +% Note that the text between \begparser\x and \endparser is essentially +% arbitrary, so one can employ tricks such as +% \toksA={ } +% \ea\stage\the\toksA{<body>}<assign>... +% +% Parsers are always defined locally. +% TODO: Add version of \begparser that defines each stage globally. +\newcount\@parsern +\newif\if@boxstage +\newtoks\@stageparam +\def\@parsercs{\@parsername:\number\numexpr\@parsern} +\protected\def\begparser#1{% + \@parsern=0 + \edef\@parsername{\csstring#1}% + \def\@prevstage##1{}% +} +\protected\def\boxstage{\@boxstagetrue \stage} +\protected\def\stage#1#{\@stageparam={#1}\@stage} +\def\@stage#1#2...{% + \@prevstage\iftrue % Define the prev stage with linkage to the curr stage. + \def\@stageasn{#2}% + \if@boxstage + \ifx\@stageasn\empty \else + \ecsdef{\@parsercs!}{% + \nx\@checkgrouplevel \ea\nx\begcs\@parsercs+1\endcs + }% + \fi + \fi + \edef\@stagedef{\csdef{\@parsercs}\the\@stageparam}% + \edef\@stagelinkage{% + \ifx\@stageasn\empty % Omit \afterassignment? + \ea\nx\begcs\@parsercs+1\endcs + \else + \if@boxstage + \nx\@savegrouplevel + \afterassignment\ea\nx\begcs\@parsercs!\endcs + \else + \afterassignment\ea\nx\begcs\@parsercs+1\endcs + \fi + \unexpanded{#2}% + \fi + }% + \def\@prevstage##1{% + \edef\@stagebody{{% + \unexpanded{#1}% + ##1% Include linkage? + \unexpanded\ea{\@stagelinkage}% + \fi + }}% + \ea\@stagedef\@stagebody + }% + \incr\@parsern + \@boxstagefalse +} +\protected\def\endparser{% + \ifnum\@parsern=0 + \errmessage{parser misuse: no stages}% + \fi + \ifx\@stageasn\empty \else + \errmessage{parser misuse: last stage must have no assignment}% + \fi + \@prevstage\iffalse % Define last stage with no linkage. + \csletcs\@parsername{\@parsername:0}% +} + % White space control \def\^^M{\ } % control <return> = control <space> \def\^^I{\ } % control <tab> = control <space> @@ -110,9 +203,6 @@ % assignment (like \afterassignment, but with multiple tokens). For box % register assignments, the tokens are placed after the entire assignment, % unlike with \afterassignment. \deferasn does not use any temporary registers. -\newcount\@grouplevel -\def\@checkgrouplevel{\ifnum\currentgrouplevel>\@grouplevel \ea\aftergroup \fi} -\def\@savegrouplevel{\@grouplevel\currentgrouplevel} \def\@deferasnA{\@checkgrouplevel\@deferasnB} \protected\def\deferasn#1{% \@savegrouplevel