From 8fdc0d5c9ee743261c162302bdafbb83f9cfb542 Mon Sep 17 00:00:00 2001 From: Karl Berry Date: Mon, 10 Oct 2011 21:53:20 +0000 Subject: l3experimental 2900 (10oct11) git-svn-id: svn://tug.org/texlive/trunk@24258 c570f23f-e606-0410-a88d-b1316a301751 --- Master/texmf-dist/doc/latex/l3experimental/README | 23 +- .../doc/latex/l3experimental/l3str/l3regex.pdf | Bin 0 -> 640244 bytes .../doc/latex/l3experimental/l3str/l3str.pdf | Bin 0 -> 628821 bytes .../l3experimental/xcoffins/xcoffins-test.tex | 380 --- .../xcoffins-tschichold-example-keyval.tex | 281 -- .../xcoffins/xcoffins-tschichold-example.tex | 185 - .../doc/latex/l3experimental/xcoffins/xcoffins.pdf | Bin 478394 -> 478180 bytes .../doc/latex/l3experimental/xgalley/l3galley.pdf | Bin 0 -> 622455 bytes .../doc/latex/l3experimental/xgalley/xgalley.pdf | Bin 0 -> 570935 bytes .../source/latex/l3experimental/l3str/l3regex.dtx | 3545 ++++++++++++++++++++ .../source/latex/l3experimental/l3str/l3str.dtx | 1966 +++++++++++ .../source/latex/l3experimental/l3str/l3str.ins | 43 + .../latex/l3experimental/xcoffins/xcoffins.dtx | 12 +- .../latex/l3experimental/xgalley/l3galley.dtx | 2140 ++++++++++++ .../latex/l3experimental/xgalley/xgalley.dtx | 1602 +++++++++ .../latex/l3experimental/xgalley/xgalley.ins | 41 + .../latex/l3experimental/l3str/l3regex-trace.sty | 1637 +++++++++ .../tex/latex/l3experimental/l3str/l3regex.sty | 1637 +++++++++ .../tex/latex/l3experimental/l3str/l3str.sty | 970 ++++++ .../tex/latex/l3experimental/xcoffins/xcoffins.sty | 2 +- .../tex/latex/l3experimental/xgalley/l3galley.sty | 764 +++++ .../tex/latex/l3experimental/xgalley/xgalley.sty | 616 ++++ 22 files changed, 14982 insertions(+), 862 deletions(-) create mode 100644 Master/texmf-dist/doc/latex/l3experimental/l3str/l3regex.pdf create mode 100644 Master/texmf-dist/doc/latex/l3experimental/l3str/l3str.pdf delete mode 100644 Master/texmf-dist/doc/latex/l3experimental/xcoffins/xcoffins-test.tex delete mode 100644 Master/texmf-dist/doc/latex/l3experimental/xcoffins/xcoffins-tschichold-example-keyval.tex delete mode 100644 Master/texmf-dist/doc/latex/l3experimental/xcoffins/xcoffins-tschichold-example.tex create mode 100644 Master/texmf-dist/doc/latex/l3experimental/xgalley/l3galley.pdf create mode 100644 Master/texmf-dist/doc/latex/l3experimental/xgalley/xgalley.pdf create mode 100644 Master/texmf-dist/source/latex/l3experimental/l3str/l3regex.dtx create mode 100644 Master/texmf-dist/source/latex/l3experimental/l3str/l3str.dtx create mode 100644 Master/texmf-dist/source/latex/l3experimental/l3str/l3str.ins create mode 100644 Master/texmf-dist/source/latex/l3experimental/xgalley/l3galley.dtx create mode 100644 Master/texmf-dist/source/latex/l3experimental/xgalley/xgalley.dtx create mode 100644 Master/texmf-dist/source/latex/l3experimental/xgalley/xgalley.ins create mode 100644 Master/texmf-dist/tex/latex/l3experimental/l3str/l3regex-trace.sty create mode 100644 Master/texmf-dist/tex/latex/l3experimental/l3str/l3regex.sty create mode 100644 Master/texmf-dist/tex/latex/l3experimental/l3str/l3str.sty create mode 100644 Master/texmf-dist/tex/latex/l3experimental/xgalley/l3galley.sty create mode 100644 Master/texmf-dist/tex/latex/l3experimental/xgalley/xgalley.sty diff --git a/Master/texmf-dist/doc/latex/l3experimental/README b/Master/texmf-dist/doc/latex/l3experimental/README index 83c8236aeba..9acac689ec6 100644 --- a/Master/texmf-dist/doc/latex/l3experimental/README +++ b/Master/texmf-dist/doc/latex/l3experimental/README @@ -28,7 +28,21 @@ are under active development and the interfaces may change. Currently included in the CTAN release of l3experimental are the following bundles: + * l3str * xcoffins + * xgalley + +l3str +----- + +A 'string' in TeX terms is a token list in which all of the tokens have +category code 12 ('other'), with the exception of spaces which have the +category code 10 ('space'). The l3str bundle consists of two parts. The +first is l3str itself. This is a collection of functions to act on strings, +including for manipulations such as UTF8 mappings in pdfTeX. The second +part of the bundle is l3regex, a regular expression search-and-replace +implementation written in TeX primitives. The regex module works on a string +basis, ignoring category codes. xcoffins -------- @@ -51,15 +65,6 @@ there are ways to control or change its behaviour. The xgalley module provides a mechanism for filling galleys and controlling the spacing, hyphenation and justification within them. -xfss ----- - -This package is a translation of LaTeX's font selection scheme into expl3. -Its primary utility is to provide font support when LaTeX3 is built as a -format, but in time features will be added that will make it useful in 2e -as well. - - DISCUSSION ---------- diff --git a/Master/texmf-dist/doc/latex/l3experimental/l3str/l3regex.pdf b/Master/texmf-dist/doc/latex/l3experimental/l3str/l3regex.pdf new file mode 100644 index 00000000000..9ee837a7c92 Binary files /dev/null and b/Master/texmf-dist/doc/latex/l3experimental/l3str/l3regex.pdf differ diff --git a/Master/texmf-dist/doc/latex/l3experimental/l3str/l3str.pdf b/Master/texmf-dist/doc/latex/l3experimental/l3str/l3str.pdf new file mode 100644 index 00000000000..f4b06528802 Binary files /dev/null and b/Master/texmf-dist/doc/latex/l3experimental/l3str/l3str.pdf differ diff --git a/Master/texmf-dist/doc/latex/l3experimental/xcoffins/xcoffins-test.tex b/Master/texmf-dist/doc/latex/l3experimental/xcoffins/xcoffins-test.tex deleted file mode 100644 index 1ede1770c08..00000000000 --- a/Master/texmf-dist/doc/latex/l3experimental/xcoffins/xcoffins-test.tex +++ /dev/null @@ -1,380 +0,0 @@ - -\documentclass{article} - -\usepackage[T1]{fontenc} -\usepackage{times,multicol} -\usepackage{xcoffins} - -\usepackage{color} -\newcommand\cbox[2][.8]{{\setlength\fboxsep{0pt}\colorbox[gray]{#1}{#2}}} - -\newcommand\hrulebox [2]{\setbox#1\hbox to#2{{\scriptsize\itshape - \color{blue}\hrulefill #2\hrulefill}}} -\newcommand\vrulebox [2]{\setbox#1\vbox to#2{{\hsize 1pt\centering\scriptsize - \itshape\color{blue}\leaders\vrule\vfill - \hbox to0pt{\hss #2\hss}\leaders\vrule\vfill\par}}} - - -\addtolength\textwidth{10pt} - -\showboxdepth 9999 -\showboxbreadth 9999 -\tracingonline 1 - - -\scrollmode - -\newbox\zzz - - -\begin{document} - - -\title{Test file for coffins} -\author{FMi} -\maketitle - -First we add a few test coffins: -\begin{verbatim} - \newcoffin \aaa - \newcoffin \bbb - \newcoffin \ccc - \newcoffin \ddd -\end{verbatim} - \newcoffin \aaa - \newcoffin \bbb - \newcoffin \ccc - \newcoffin \ddd -and some boxes not set up as ordinary boxes (without extra poles): -\begin{verbatim} - \newbox \xxx - \newbox \yyy -\end{verbatim} - \newbox \xxx - \newbox \yyy - - -If a coffin receives data a set of ``natural'' default poles are automatically added. -\begin{verbatim} -\sbox\aaa{\fbox{\begin{tabular}[b]{l}123\\4\\5\end{tabular}}} -\showcoffindata \aaa -\end{verbatim} -\sbox\aaa{\fbox{\begin{tabular}[b]{l}123\\4\\5\end{tabular}}} -\showcoffindata \aaa - - -In contrast, boxes not declared as coffins have no poles defined. However, if -used as coffins at least the natural default poles can be used without -further problems, i.e.,they are changed to coffins automatically on use. -\begin{verbatim} -\sbox\xxx{\fbox{Some very looooonggg Caption Text}} -\showpoles \xxx -\end{verbatim} -\sbox\xxx{\fbox{Some very looooonggg Caption Text}} -\showcoffindata \xxx - - -Using \verb|\setvcoffin| instead of \verb|\sbox| gives us the codes with -\texttt{T} and \texttt{B}. Similar to \verb|\parbox| this command requires to -specify a target width of the box: -\begin{verbatim} -\setvcoffin \aaa {1.5cm} {\centering - \fbox{\begin{tabular}[b]{l}123\\4\\5\end{tabular}}} -\showcoffindata \aaa -\end{verbatim} -\setvcoffin \aaa {1.5cm} {\centering - \fbox{\begin{tabular}[b]{l}123\\4\\5\end{tabular}}} -\showcoffindata \aaa - -There are a dozen natural poles per box (some with multiple names); you can -move each of the poles to someplace else (either to an absolute position in -``box space'' or relative to its previous position). The coordinates can -refer to box dimensions (using \verb|\height|, \verb|\depth|, - and \verb|width|). First we move some pole and add one -additional one: -\begin{verbatim} -\setcoffinpole \aaa [h]{t}(\height -3pt) -\setcoffinpole \aaa [v]{l}(0pt) -\setcoffinpole \aaa [h]{mybottom}(\depth +2pt) -\showcoffindata \aaa -\end{verbatim} -\setcoffinpole \aaa [h]{t}(\height -3pt) -\setcoffinpole \aaa [v]{l}(0pt) -\setcoffinpole \aaa [h]{mybottom}(\depth +2pt) -\showcoffindata \aaa - - -More interesting in many cases is the relative move of poles. For this the -poles better exist or one gets an error and \texttt{0pt,0pt} is used: -\begin{verbatim} -\adjustcoffinpole \aaa [h]{H}(1pt) -\adjustcoffinpole \aaa [v]{l}(1pt) -\adjustcoffinpole \aaa [h]{undefined}(1pt) -\showcoffindata \aaa -\end{verbatim} -\adjustcoffinpole \aaa [h]{H}(1pt) -\adjustcoffinpole \aaa [v]{l}(1pt) -\adjustcoffinpole \aaa [h]{undefined}(1pt) -\showcoffindata \aaa - - - -\sbox \aaa {\fontsize{14.4}{5.5pc minus .5pc}\sffamily C\,H\,A\,P\,T\,E\,R - \fontsize{36}{40pt}\selectfont 2} - -\setvcoffin \bbb {11cm} - {\raggedleft\normalfont\fontsize{36}{38pt}\bfseries - The Structure of a \LaTeX{} Document} - -\setvcoffin \ccc {13cm} - {Some sample blind text to produce paragraph data after a heading. - Some sample blind text to produce paragraph data after a heading. - Some sample blind text to produce paragraph data after a heading. - Some sample blind text to produce paragraph data after a heading. - \endgraf - Some more sample blind text to produce paragraph data after a heading. - Some more sample blind text to produce paragraph data after a heading. -} - - -Suppose you have the following blocks of text: - -\bigskip - -\noindent\cbox{\usebox\aaa} - -\begin{multicols}{2} -\ttfamily\tiny -\ExplSyntaxOn - \noindent \coffin_print_pole_values:Nn \aaa \\ -\ExplSyntaxOff -\end{multicols} - -\medskip - -\noindent\cbox{\usebox\bbb} - -\medskip - -\noindent\cbox{\usebox\ccc} - -\bigskip - -produced by: -\begin{verbatim} -\sbox \aaa {\fontsize{14.4}{5.5pc minus .5pc}\sffamily - C\,H\,A\,P\,T\,E\,R - \fontsize{36}{40pt}\selectfont 2} -\setvcoffin \bbb {11cm} - {\raggedleft\normalfont\fontsize{36}{38pt}\bfseries - The Structure of a \LaTeX{} Document} -\setvcoffin \ccc {13cm} - {Some sample blind text to produce paragraph - data after a heading. ... } -\end{verbatim} - -How do you turn this into a heading of TLC2? - - -\newbox\RBi -\newbox\RBii -\newbox\RBiii - -\hrulebox\RBi{62pt} -\vrulebox\RBii{90pt} -\vrulebox\RBiii{60pt} - -\aligncoffins \bbb [T,r] \aaa [H,r](-62pt,60pt) -\aligncoffins \ccc[T,r] \bbb[B,r](62pt,90pt) - - \aligncoffins * \ccc[\bbb-T,\bbb-hc] \RBiii[b,hc](28pt,0pt) - \aligncoffins * \ccc[\bbb-b,\bbb-r] \RBi[H,r](0pt,-5pt) - \aligncoffins * \ccc[\bbb-B,\bbb-hc] \RBii[t,r](14pt,0pt) - -\newpage - -How do you best define/describe the following design? - -\medskip - -\noindent\cbox{\usebox\ccc} - - - - - - -\newpage \pagestyle{empty} - -\newcoffin \eee -\newcoffin \fff -\newcoffin \ggg - -\sbox \aaa {\small\scshape les vases communicants} -\sbox \bbb {\scshape comunicating} -\sbox \ccc {\fontsize{70pt}{60pt} \bfseries Ve\S els} -\sbox \ddd {\scshape andr\'e breton} -\setvcoffin \eee {4.7cm}{\noindent Translated by Mary Ann Caws \&\break - Geoffrey T.\,Harris, with notes \&\break - introduction by Mary Ann Caws\parfillskip=0pt\relax - } -\sbox \fff {University of Nebraska Press: Lincoln \& London} - -\setvcoffin \ggg {100mm}{\noindent\fbox{\parbox{97mm}{\leavevmode\vspace*{228mm}}}} - - -\aligncoffins \ccc [H,r] \ddd [H,r](0pt,-12pt) -\aligncoffins \ccc [t,l] \bbb (0pt,6pt) -\aligncoffins \ccc [H,r] \aaa [H,r](0pt,138pt) -\aligncoffins \eee [T,l] \ccc (0pt,22pc) -\aligncoffins \fff \eee [B,l](0pt,4pc) -\aligncoffins \ggg [b,l] \fff [B,l](54pt,11pc) - - -\vspace*{-2cm} -\noindent\cbox{\usebox\ggg} - - -\newpage - -\section{Rotation} - -\sbox \aaa {\tabcolsep0pt\begin{tabular}[c]{|c|}\hline a\\b\\cccccccccccccc\\d - \\e\\e\\e\\e\\\hline\end{tabular}} -\sbox \bbb {\fbox{A sample Text}} - -% just for the sake of it: -\adjustcoffinpole \aaa {T}(24pt) -\displaycoffinpoles \aaa {black} -x\cbox{\usebox \aaa }x -% -\rotatecoffin \aaa {45} -\displaycoffinhandle \aaa {b}{hc} {blue} -\displaycoffinhandle \aaa {b}{l} {blue} -\displaycoffinhandle \aaa {t}{r} {blue} -\displaycoffinhandle \aaa {vc}{r} {blue} -\displaycoffinhandle \aaa {vc}{l} {blue} -\quad -x\cbox{\usebox \aaa }x -\rotatecoffin \aaa {45} -\displaycoffinhandle \aaa {b}{r} {red} -\displaycoffinhandle \aaa {vc}{l} {red} -\quad -x\cbox{\usebox \aaa }x - -\vspace{1cm} - -\rotatecoffin \aaa {45} -\displaycoffinhandle \aaa {b}{l} {yellow} -% -x\cbox{\usebox \aaa }x -% - -\vspace{1cm} - -x\cbox{\usebox \bbb }x -% -\rotatecoffin \bbb {45} -\displaycoffinhandle \bbb {B}{hc} {yellow} -\quad -x\cbox{\usebox \bbb }x - - -\section{Rotation + alignment} - -\aligncoffins \aaa [b,l] \bbb[B,hc](30pt,0pt) -x\cbox{\usebox \aaa }x - - -After we have aligned a roated box with some other box we need to decide about -the bounding box of the new box. Right now this become the enclosing box and -we do not maintain information about the inner boxes. So when we rotate that -new box there seems to be unnecessary space in the enclosing bounding box, -even though that is correct if you think of the aligned box being unrotated. - -There is at least the possibility to refer to the handles of the inner boxes -as one can see by two of the green handles - -\medskip - -\rotatecoffin \aaa {45} -\displaycoffinhandle \aaa {vc}{hc} {green} -\displaycoffinhandle \aaa {H}{l} {green} -\displaycoffinhandle \aaa {\aaa-T}{\aaa-r} {green} -\displaycoffinhandle \aaa {\bbb-H}{\bbb-r} {green} -%\quad -x\cbox{\usebox \aaa }x - -We could do better, if we want to, by actually checking for max and -min of all inner bounding box corners and construct the result BB box from -that---but is it worth it? - -Perhaps it is! After all, the current implementation shows different results -depending on when you align boxes and when you rotate, e.g., aligning first -gives totally different bounding box results. - -\newpage - -Aligning first and then 135 + 45 rotation gives this: - -\sbox \aaa {\tabcolsep0pt\begin{tabular}[c]{|c|}\hline a\\b\\cccccccccccccc\\d - \\e\\e\\e\\e\\\hline\end{tabular}} -\sbox \bbb {\fbox{A sample Text}} -\rotatecoffin \bbb {90} - -\aligncoffins \aaa [b,l] \bbb[B,hc](-30pt,0pt) -x\cbox{\usebox \aaa }x -\qquad -\rotatecoffin \aaa {135} -x\cbox{\usebox \aaa }x -\qquad -\rotatecoffin \aaa {45} -x\cbox{\usebox \aaa }x - - - - -\newpage - -\section{Rotation by small amounts} - -\sbox \aaa {\tabcolsep0pt\begin{tabular}[c]{|c|}\hline a\\b\\ccccccccc\\d - \\e\\e\\e\\\hline\end{tabular}} - -\subsection{30 + 30 +30 } -\rotatecoffin \aaa {30} -x\cbox{\usebox \aaa }x -\rotatecoffin \aaa {30} -x\cbox{\usebox \aaa }x -\rotatecoffin \aaa {30} -x\cbox{\usebox \aaa }x - -\subsection{6 * 10 + 45 + 45 + 30} - -\rotatecoffin \aaa {10} -x\cbox{\usebox \aaa }x -\rotatecoffin \aaa {10} -x\cbox{\usebox \aaa }x -\rotatecoffin \aaa {10} -x\cbox{\usebox \aaa }x - -\vspace{1cm} -\rotatecoffin \aaa {10} -x\cbox{\usebox \aaa }x -\rotatecoffin \aaa {10} -x\cbox{\usebox \aaa }x -\rotatecoffin \aaa {10} -x\cbox{\usebox \aaa }x - -\vspace{1cm} -\rotatecoffin \aaa {45} -x\cbox{\usebox \aaa }x -\rotatecoffin \aaa {45} -x\cbox{\usebox \aaa }x -\rotatecoffin \aaa {30} -x\cbox{\usebox \aaa }x - - -\end{document} - - diff --git a/Master/texmf-dist/doc/latex/l3experimental/xcoffins/xcoffins-tschichold-example-keyval.tex b/Master/texmf-dist/doc/latex/l3experimental/xcoffins/xcoffins-tschichold-example-keyval.tex deleted file mode 100644 index e83c0b490b6..00000000000 --- a/Master/texmf-dist/doc/latex/l3experimental/xcoffins/xcoffins-tschichold-example-keyval.tex +++ /dev/null @@ -1,281 +0,0 @@ - -\documentclass{article} - -\usepackage[a4paper,margin=5pt]{geometry} - -\usepackage[T1]{fontenc} -\usepackage{times,multicol,graphicx} -\usepackage{xcoffins} - -\usepackage{times,color} -\newcommand\cbox[2][.8]{{\setlength\fboxsep{0pt}\colorbox[gray]{#1}{#2}}} - -\ExplSyntaxOn - -\ExplSyntaxOff - - - -%\scrollmode - -\pagestyle{empty} - -\begin{document} - - \NewCoffin \result - \NewCoffin \aaa - \NewCoffin \bbb - \NewCoffin \ccc - \NewCoffin \ddd - \NewCoffin \eee - \NewCoffin \fff - \NewCoffin \rulei - \NewCoffin \ruleii - \NewCoffin \ruleiii - -\SetHorizontalCoffin \result {} -\SetHorizontalCoffin \aaa {\fontsize{52}{50}\sffamily\bfseries mitteilungen} -\SetHorizontalCoffin \bbb {\fontsize{52}{50}\sffamily\bfseries typographische} -\SetHorizontalCoffin \ccc {\fontsize{12}{10}\sffamily - \quad zeitschrift des bildungsverbandes der - deutschen buchdrucker leipzig - \textbullet{} oktoberheft 1925} -\SetHorizontalCoffin \ddd {\fontsize{28}{20}\sffamily sonderheft} -\SetVerticalCoffin \eee {180pt} - {\raggedleft\fontsize{31}{36}\sffamily\bfseries - elementare\\ - typographie} -\SetVerticalCoffin \fff {140pt} - {\raggedright \fontsize{13}{14}\sffamily\bfseries - natan altman \\ - otto baumberger \\ - herbert mayer \\ - max burchartz \\ - el lissitzky \\ - ladislaus moholy-nagy \\ - moln\'ar f.~farkas \\ - johannes molzahn \\ - kurt schwitters \\ - mart stam \\ - ivan tschichold} - -\RotateCoffin \bbb {90} -\RotateCoffin \ccc {270} - -\SetHorizontalCoffin \rulei {\color{red}\rule{6.5in}{1pc}} -\SetHorizontalCoffin \ruleii {\color{red}\rule{1pc}{23.5cm}} -\SetHorizontalCoffin \ruleiii{\color{black}\rule{10pt}{152pt}} - - -\JoinCoffins \result \aaa -\JoinCoffins [ - coffin1-hpole = \aaa-t , - coffin1-vpole = \aaa-r , - coffin2-hpole = b , - coffin2-vpole = r , - voffset = 2 mm , -] \result \rulei -\JoinCoffins [ - coffin1-hpole = \aaa-b , - coffin1-vpole = \aaa-l , - coffin2-hpole = B , - coffin2-vpole = r , - hoffset = 2 pt , -] \result \bbb -\JoinCoffins [ - coffin1-hpole = \bbb-t , - coffin1-vpole = \bbb-r , - coffin2-hpole = t , - coffin2-vpole = r , - hoffset = -2 mm , -] \result \ruleii -\JoinCoffins [ - coffin1-hpole = \aaa-B , - coffin1-vpole = \aaa-r , - coffin2-hpole = B , - coffin2-vpole = l , - hoffset = 66 pt , - voffset = 14 pc , -] \result \ccc -\JoinCoffins [ - coffin1-hpole = \bbb-l , - coffin1-vpole = \ccc-B , - coffin2-hpole = t , - coffin2-vpole = r , - hoffset = -2 mm , -] \result \fff -\JoinCoffins [ - coffin1-hpole = \fff-b , - coffin1-vpole = \fff-r , - coffin2-hpole = b , - coffin2-vpole = l , - hoffset = 2 mm , -] \result \ruleiii -\JoinCoffins [ - coffin1-hpole = \ccc-r , - coffin1-vpole = \fff-l , - coffin2-hpole = B , - coffin2-vpole = r , -] \result \eee -\JoinCoffins [ - coffin1-hpole = \eee-T , - coffin1-vpole = \eee-r , - coffin2-hpole = B , - coffin2-vpole = r , - voffset = 4 pc , -] \result \ddd - - -\vspace*{3cm} -\begin{center} - {\Large Title page of ``elementare typographie'' by Ivan Tschichold\par} - -\large -\vspace*{1cm} - - 1. first the scanned original from 1925 - -\vspace*{6mm} - - 2. then the recreated \TeX{} version from 2010 using coffins---not - attempting\\ to match the fonts and size but the structure - -\vspace*{6mm} - - 3. and finally the source code used. - - This document uses the new - implementation by Joseph in \texttt{l3coffins-new}. - -\end{center} - -\newpage - - -\begin{center} - \null - \fbox{\includegraphics[scale=.95]{elementare-typographie-title.jpg}} -\end{center} - -\newpage - -\TypesetCoffin \result - -\newpage - -\vspace*{3cm} -\begin{center} - {\Large Code used: \par} -\vspace*{1cm} - - -\begin{minipage}{14cm} -\begin{verbatim} -\SetHorizontalCoffin\result{} -\SetHorizontalCoffin \aaa {\fontsize{52}{50}\sffamily\bfseries mitteilungen} -\SetHorizontalCoffin \bbb {\fontsize{52}{50}\sffamily\bfseries typographische} -\SetHorizontalCoffin \ccc {\fontsize{12}{10}\sffamily - \quad zeitschrift des bildungsverbandes der - deutschen buchdrucker leipzig - \textbullet{} oktoberheft 1925} -\SetHorizontalCoffin \ddd {\fontsize{28}{20}\sffamily sonderheft} -\SetVerticalCoffin \eee {180pt} - {\raggedleft\fontsize{31}{36}\sffamily\bfseries - elementare\\ - typographie} -\SetVerticalCoffin \fff {140pt} - {\raggedright \fontsize{13}{14}\sffamily\bfseries - natan altman \\ - otto baumberger \\ - herbert mayer \\ - max burchartz \\ - el lissitzky \\ - ladislaus moholy-nagy \\ - moln\'ar f.~farkas \\ - jahannes molzahn \\ - kurt schwitters \\ - mart stam \\ - ivan tschichold} - -\RotateCoffin \bbb {90} -\RotateCoffin \ccc {270} - -\SetHorizontalCoffin \rulei {\color{red}\rule{6.5in}{1pc}} -\SetHorizontalCoffin \ruleii {\color{red}\rule{1pc}{23.5cm}} -\SetHorizontalCoffin \ruleiii{\color{black}\rule{10pt}{152pt}} - -\JoinCoffins \result \aaa -\JoinCoffins [ - coffin1-hpole = \aaa-t , - coffin1-vpole = \aaa-r , - coffin2-hpole = b , - coffin2-vpole = r , - voffset = 2 mm , -] \result \rulei -\JoinCoffins [ - coffin1-hpole = \aaa-b , - coffin1-vpole = \aaa-l , - coffin2-hpole = B , - coffin2-vpole = r , - hoffset = 2 pt , -] \result \bbb -\JoinCoffins [ - coffin1-hpole = \bbb-t , - coffin1-vpole = \bbb-r , - coffin2-hpole = t , - coffin2-vpole = r , - hoffset = -2 mm , -] \result \ruleii -\JoinCoffins [ - coffin1-hpole = \aaa-B , - coffin1-vpole = \aaa-r , - coffin2-hpole = B , - coffin2-vpole = l , - hoffset = 66 pt , - voffset = 14 pc , -] \result \ccc -\JoinCoffins [ - coffin1-hpole = \bbb-l , - coffin1-vpole = \ccc-B , - coffin2-hpole = t , - coffin2-vpole = r , - hoffset = -2 mm , -] \result \fff -\JoinCoffins [ - coffin1-hpole = \fff-b , - coffin1-vpole = \fff-r , - coffin2-hpole = b , - coffin2-vpole = l , - hoffset = 2 mm , -] \result \ruleiii -\JoinCoffins [ - coffin1-hpole = \ccc-r , - coffin1-vpole = \fff-l , - coffin2-hpole = B , - coffin2-vpole = r , -] \result \eee -\JoinCoffins [ - coffin1-hpole = \eee-T , - coffin1-vpole = \eee-r , - coffin2-hpole = B , - coffin2-vpole = r , - voffset = 4 pc , -] \result \ddd -\SetHorizontalCoffin\result{} -\SetHorizontalCoffin \aaa {\fontsize{52}{50}\sffamily\bfseries - mitteilungen} -\SetHorizontalCoffin \bbb {\fontsize{52}{50}\rotatebox{90}{\sffamily\bfseries - typographische}} - -\TypesetCoffin \result -\end{verbatim} - -This is not necessarily the final syntax but for now it does its job. For -example, flexible support for adding ornaments (lines, \ldots) is still -missing, so above the rules got added as predefined individual coffins. - -\end{minipage} -\end{center} - -\end{document} - diff --git a/Master/texmf-dist/doc/latex/l3experimental/xcoffins/xcoffins-tschichold-example.tex b/Master/texmf-dist/doc/latex/l3experimental/xcoffins/xcoffins-tschichold-example.tex deleted file mode 100644 index 343c9d7e29f..00000000000 --- a/Master/texmf-dist/doc/latex/l3experimental/xcoffins/xcoffins-tschichold-example.tex +++ /dev/null @@ -1,185 +0,0 @@ - -\documentclass{article} - -\usepackage[a4paper,margin=5pt]{geometry} - -\usepackage[T1]{fontenc} -\usepackage{times,multicol,graphicx} -\usepackage{xcoffins} - -\usepackage{times,color} -\newcommand\cbox[2][.8]{{\setlength\fboxsep{0pt}\colorbox[gray]{#1}{#2}}} - -\ExplSyntaxOn - -\ExplSyntaxOff - - - -%\scrollmode - -\pagestyle{empty} - -\begin{document} - - \NewCoffin \result - \NewCoffin \aaa - \NewCoffin \bbb - \NewCoffin \ccc - \NewCoffin \ddd - \NewCoffin \eee - \NewCoffin \fff - \NewCoffin \rulei - \NewCoffin \ruleii - \NewCoffin \ruleiii - -\SetHorizontalCoffin \result {} -\SetHorizontalCoffin \aaa {\fontsize{52}{50}\sffamily\bfseries mitteilungen} -\SetHorizontalCoffin \bbb {\fontsize{52}{50}\sffamily\bfseries typographische} -\SetHorizontalCoffin \ccc {\fontsize{12}{10}\sffamily - \quad zeitschrift des bildungsverbandes der - deutschen buchdrucker leipzig - \textbullet{} oktoberheft 1925} -\SetHorizontalCoffin \ddd {\fontsize{28}{20}\sffamily sonderheft} -\SetVerticalCoffin \eee {180pt} - {\raggedleft\fontsize{31}{36}\sffamily\bfseries - elementare\\ - typographie} -\SetVerticalCoffin \fff {140pt} - {\raggedright \fontsize{13}{14}\sffamily\bfseries - natan altman \\ - otto baumberger \\ - herbert mayer \\ - max burchartz \\ - el lissitzky \\ - ladislaus moholy-nagy \\ - moln\'ar f.~farkas \\ - johannes molzahn \\ - kurt schwitters \\ - mart stam \\ - ivan tschichold} - -\RotateCoffin \bbb {90} -\RotateCoffin \ccc {270} - -\SetHorizontalCoffin \rulei {\color{red}\rule{6.5in}{1pc}} -\SetHorizontalCoffin \ruleii {\color{red}\rule{1pc}{23.5cm}} -\SetHorizontalCoffin \ruleiii{\color{black}\rule{10pt}{152pt}} - - -\JoinCoffins \result \aaa -\JoinCoffins \result[\aaa-t,\aaa-r] \rulei [b,r](0pt,2mm) -\JoinCoffins \result[\aaa-b,\aaa-l] \bbb [B,r](2pt,0pt) -\JoinCoffins \result[\bbb-t,\bbb-r] \ruleii [t,r](-2mm,0pt) -\JoinCoffins \result[\aaa-B,\aaa-r] \ccc [B,l](66pt,14pc) -\JoinCoffins \result[\bbb-l,\ccc-B] \fff [t,r](-2mm,0pt) -\JoinCoffins \result[\fff-b,\fff-r] \ruleiii [b,l](2mm,0pt) -\JoinCoffins \result[\ccc-r,\fff-l] \eee [B,r] -\JoinCoffins \result[\eee-T,\eee-r] \ddd [B,r](0pt,4pc) - - -\vspace*{3cm} -\begin{center} - {\Large Title page of ``elementare typographie'' by Ivan Tschichold\par} - -\large -\vspace*{1cm} - - 1. first the scanned original from 1925 - -\vspace*{6mm} - - 2. then the recreated \TeX{} version from 2010 using coffins---not - attempting\\ to match the fonts and size but the structure - -\vspace*{6mm} - - 3. and finally the source code used. - - This document uses the new - implementation by Joseph in \texttt{l3coffins-new}. - -\end{center} - -\newpage - - -\begin{center} - \null - \fbox{\includegraphics[scale=.95]{elementare-typographie-title.jpg}} -\end{center} - -\newpage - -\TypesetCoffin \result - -\newpage - -\vspace*{3cm} -\begin{center} - {\Large Code used: \par} -\vspace*{1cm} - - -\begin{minipage}{14cm} -\begin{verbatim} -\SetHorizontalCoffin\result{} -\SetHorizontalCoffin \aaa {\fontsize{52}{50}\sffamily\bfseries mitteilungen} -\SetHorizontalCoffin \bbb {\fontsize{52}{50}\sffamily\bfseries typographische} -\SetHorizontalCoffin \ccc {\fontsize{12}{10}\sffamily - \quad zeitschrift des bildungsverbandes der - deutschen buchdrucker leipzig - \textbullet{} oktoberheft 1925} -\SetHorizontalCoffin \ddd {\fontsize{28}{20}\sffamily sonderheft} -\SetVerticalCoffin \eee {180pt} - {\raggedleft\fontsize{31}{36}\sffamily\bfseries - elementare\\ - typographie} -\SetVerticalCoffin \fff {140pt} - {\raggedright \fontsize{13}{14}\sffamily\bfseries - natan altman \\ - otto baumberger \\ - herbert mayer \\ - max burchartz \\ - el lissitzky \\ - ladislaus moholy-nagy \\ - moln\'ar f.~farkas \\ - jahannes molzahn \\ - kurt schwitters \\ - mart stam \\ - ivan tschichold} - -\RotateCoffin \bbb {90} -\RotateCoffin \ccc {270} - -\SetHorizontalCoffin \rulei {\color{red}\rule{6.5in}{1pc}} -\SetHorizontalCoffin \ruleii {\color{red}\rule{1pc}{23.5cm}} -\SetHorizontalCoffin \ruleiii{\color{black}\rule{10pt}{152pt}} - -\JoinCoffins \result \aaa -\JoinCoffins \result[\aaa-t,\aaa-r] \rulei [b,r](0pt,2mm) -\JoinCoffins \result[\aaa-b,\aaa-l] \bbb [B,r](2pt,0pt) -\JoinCoffins \result[\bbb-t,\bbb-r] \ruleii [t,r](-2mm,0pt) -\JoinCoffins \result[\aaa-B,\aaa-r] \ccc [B,l](66pt,14pc) -\JoinCoffins \result[\bbb-l,\ccc-B] \fff [t,r](-2mm,0pt) -\JoinCoffins \result[\fff-b,\fff-r] \ruleiii [b,l](2mm,0pt) -\JoinCoffins \result[\ccc-r,\fff-l] \eee [B,r] -\JoinCoffins \result[\eee-T,\eee-r] \ddd [B,r](0pt,4pc) -\SetHorizontalCoffin\result{} -\SetHorizontalCoffin \aaa {\fontsize{52}{50}\sffamily\bfseries - mitteilungen} -\SetHorizontalCoffin \bbb {\fontsize{52}{50}\rotatebox{90}{\sffamily\bfseries - typographische}} - -\TypesetCoffin \result -\end{verbatim} - -This is not necessarily the final syntax but for now it does its job. For -example, flexible support for adding ornaments (lines, \ldots) is still -missing, so above the rules got added as predefined individual coffins. - -\end{minipage} -\end{center} - -\end{document} - diff --git a/Master/texmf-dist/doc/latex/l3experimental/xcoffins/xcoffins.pdf b/Master/texmf-dist/doc/latex/l3experimental/xcoffins/xcoffins.pdf index b9f4e0ae361..b20f638d514 100644 Binary files a/Master/texmf-dist/doc/latex/l3experimental/xcoffins/xcoffins.pdf and b/Master/texmf-dist/doc/latex/l3experimental/xcoffins/xcoffins.pdf differ diff --git a/Master/texmf-dist/doc/latex/l3experimental/xgalley/l3galley.pdf b/Master/texmf-dist/doc/latex/l3experimental/xgalley/l3galley.pdf new file mode 100644 index 00000000000..3cad13a681f Binary files /dev/null and b/Master/texmf-dist/doc/latex/l3experimental/xgalley/l3galley.pdf differ diff --git a/Master/texmf-dist/doc/latex/l3experimental/xgalley/xgalley.pdf b/Master/texmf-dist/doc/latex/l3experimental/xgalley/xgalley.pdf new file mode 100644 index 00000000000..38d903ad9fb Binary files /dev/null and b/Master/texmf-dist/doc/latex/l3experimental/xgalley/xgalley.pdf differ diff --git a/Master/texmf-dist/source/latex/l3experimental/l3str/l3regex.dtx b/Master/texmf-dist/source/latex/l3experimental/l3str/l3regex.dtx new file mode 100644 index 00000000000..e2977e2b249 --- /dev/null +++ b/Master/texmf-dist/source/latex/l3experimental/l3str/l3regex.dtx @@ -0,0 +1,3545 @@ +% \iffalse meta-comment +% +%% File: l3regex.dtx Copyright (C) 2011 The LaTeX3 Project +%% +%% It may be distributed and/or modified under the conditions of the +%% LaTeX Project Public License (LPPL), either version 1.3c of this +%% license or (at your option) any later version. The latest version +%% of this license is in the file +%% +%% http://www.latex-project.org/lppl.txt +%% +%% This file is part of the "l3experimental bundle" (The Work in LPPL) +%% and all files in that bundle must be distributed together. +%% +%% The released version of this bundle is available from CTAN. +%% +%% ----------------------------------------------------------------------- +%% +%% The development version of the bundle can be found at +%% +%% http://www.latex-project.org/svnroot/experimental/trunk/ +%% +%% for those people who are interested. +%% +%%%%%%%%%%% +%% NOTE: %% +%%%%%%%%%%% +%% +%% Snapshots taken from the repository represent work in progress and may +%% not work or may contain conflicting material! We therefore ask +%% people _not_ to put them into distributions, archives, etc. without +%% prior consultation with the LaTeX3 Project. +%% +%% ----------------------------------------------------------------------- +% +%<*driver|package> +\RequirePackage{expl3} +\GetIdInfo$Id: l3regex.dtx 2895 2011-10-09 15:58:19Z joseph $ + {L3 Experimental Regular Expressions} +% +%<*driver> +\documentclass[full]{l3doc} +\usepackage{amsmath} +\begin{document} + \DocInput{\jobname.dtx} +\end{document} +% +% \fi +% +% \title{^^A +% The \textsf{l3regex} package: regular expressions in \TeX{}^^A +% \thanks{This file describes v\ExplFileVersion, +% last revised \ExplFileDate.}^^A +% } +% +% \author{^^A +% The \LaTeX3 Project\thanks +% {^^A +% E-mail: +% \href{mailto:latex-team@latex-project.org} +% {latex-team@latex-project.org}^^A +% }^^A +% } +% +% \date{Released \ExplFileDate} +% +% \maketitle +% +% \begin{documentation} +% \newenvironment{l3regex-syntax} +% {\begin{itemize}\def\makelabel##1{\hss\llap{\ttfamily\string##1}}} +% {\end{itemize}} +% +% \section{\pkg{l3regex} documentation} +% +% The \pkg{l3regex} package provides regular expression testing, +% extraction of submatches, splitting, and replacement, all acting on strings +% of characters. The syntax of regular expressions is mostly a subset +% of the PCRE syntax (and very close to POSIX). For performance +% reasons, only a limited set of features are implemented. Notably, +% back-references are not supported. +% +% Let us give a few examples. After +% \begin{verbatim} +% \str_set:Nn \l_my_str { That~cat. } +% \regex_replace_once:nnN { at } { is } \l_my_str +% \end{verbatim} +% the string variable \cs{l_my_str} holds the text +% \enquote{\texttt{This cat.}}, where the first +% occurrence of \enquote{\texttt{at}} was replaced +% by \enquote{\texttt{is}}. A more complicated example is +% a pattern to add a comma at the end of each word: +% \begin{verbatim} +% \regex_replace_all:nnN { (\w+) } { \1 , } \l_my_str +% \end{verbatim} +% The |\w| sequence represents any \enquote{word} character, +% and |*| indicates that the |\w| sequence should be repeated +% as many times as possible, hence matching a word in the +% input string. The parentheses \enquote{capture} what their +% contents matched in the input string, and this can be used +% in the replacement text as |\1| (and higher numbers if several +% groups are used in the regular expression). +% +% \subsection{Syntax of regular expressions} +% +% Most characters match exactly themselves. Some characters are +% special and must be escaped with a backslash (\emph{e.g.}, |\*| +% matches an explicit star character). Some escape sequences of +% the form backslash--letter also have a special meaning +% (for instance |\d| matches any digit). As a rule, +% \begin{itemize} +% \item every alphanumeric character (\texttt{A}--\texttt{Z}, +% \texttt{a}--\texttt{z}, \texttt{0}--\texttt{9}) matches +% exactly itself, none of them should be escaped, because +% most of those escape sequences have special meanings; +% \item non-alphanumeric printable ascii characters can always +% be safely escaped, and for highest portability, they should +% always be escaped (this avoids problems if some currently +% \enquote{normal} characters is given a meaning in later +% releases); +% \item spaces should always be escaped (even in character +% classes); +% \item any other character may be escaped or not, without any +% effect: both versions will match exactly that character. +% \end{itemize} +% Note that these rules play nicely with the fact that many +% non-alphanumeric characters are difficult to input into \TeX{} +% under normal category codes. For instance, |\$\%\^\\abc\#| +% matches the literal string |$%^\abc#|. +% \begin{texnote} +% When converting the regular expression to a string, +% the value of the escape character is set to be a backslash. +% \end{texnote} +% +% Any special character which appears at a place where its special +% behaviour cannot apply matches itself instead (for instance, +% a quantifier appearing at the beginning of a string). +% +% Characters. +% \begin{l3regex-syntax} +% \item[\x\{hh\ldots{}\}] +% Character with hex code \texttt{hh\ldots{}} +% \item[\xhh] +% Character with hex code \texttt{hh}. +% \item[\a] Alarm (hex 07). +% \item[\e] Escape (hex 1B). +% \item[\f] Form-feed (hex 0C). +% \item[\n] New line (hex 0A). +% \item[\r] Carriage return (hex 0D). +% \item[\t] Horizontal tab (hex 09). +% \end{l3regex-syntax} +% +% Character types. +% \begin{l3regex-syntax} +% \item[.] A single period matches any character, +% including newlines.\footnote{Should that be changed?} +% \item[\N] A character that is not +% the |\n| character (hex 0A).\footnote{Is that right?} +% \item[\d] Any decimal digit. +% \item[\h] Any horizontal space character, +% equivalent to |[\^^I\ ]|: space and tab. +% \item[\s] Any space character, +% equivalent to |[\^^I\^^J\^^L\^^M\ ]|. +% \item[\v] Any vertical space character, +% equivalent to |[\^^I-\^^M]|. Note that |\^^K| is a vertical space, +% but not a space, for compatibility with perl. +% \item[\w] Any word character, \emph{i.e.}, +% alpha-numerics and underscore, equivalent to |[A-Za-z0-9\_]|. +% \item[\D] Any character not matched by |\d|. +% \item[\H] Any character not matched by |\h|. +% \item[\S] Any character not matched by |\s|. +% \item[\V] Any character not matched by |\v|. +% \item[\W] Any character not matched by |\w|. +% \end{l3regex-syntax} +% +% Character classes match exactly one character in the subject string. +% \begin{l3regex-syntax} +% \item[{[...]}] Positive character class. +% \item[{[\string^...]}] Negative character class. +% \item[{[x-y]}] Range (can be used with escaped characters). +% \end{l3regex-syntax} +% +% Quantifiers. +% \begin{l3regex-syntax} +% \item[?] $0$ or $1$, greedy. +% \item[??] $0$ or $1$, lazy. +% \item[*] $0$ or more, greedy. +% \item[*?] $0$ or more, lazy. +% \item[+] $1$ or more, greedy. +% \item[+?] $1$ or more, lazy. +% \end{l3regex-syntax} +% Not implemented yet: ^^A ! +% \begin{l3regex-syntax}\def\makelabel#1{\hss\llap{\ttfamily#1}} +% \item[\{$n$\}] Exactly $n$. +% \item[\{$n,$\}] $n$ or more, greedy. +% \item[\{$n,$\}?] $n$ or more, lazy. +% \item[\{$n,m$\}] At least $n$, no more than $m$, greedy. +% \item[\{$n,m$\}?] At least $n$, no more than $m$, lazy. +% \end{l3regex-syntax} +% +% Anchors and simple assertions. +% \begin{l3regex-syntax} +% \item[\b] Word boundary. +% \item[\B] Not a word boundary. +% \item[^\string\A] Start of the subject string.\footnote{The +% multiline mode is not implemented yet, so those are currently +% identical.} +% \item[$\string\Z\string\z] End of the subject +% string.\footnotemark[\thefootnote] +% \item[\G] Start of the current match. This is only different from |^| +% in the case of multiple matches: for instance +% |\regex_count:nnN { \G a } { aaba } \l_tmpa_int| yields $2$, but +% replacing |\G| by |^| would result in \cs{l_tmpa_int} holding the +% value $1$. +% \end{l3regex-syntax} +% +% Alternation and capturing groups. +% \begin{l3regex-syntax} +% \item[A\string|B\string|C] Either one of \texttt{A}, \texttt{B}, +% or \texttt{C}. +% \item[(\ldots{})] Capturing group. +% \item[(?:\ldots{})] Non-capturing group. +% \end{l3regex-syntax} +% +% In character classes, only |^|, |-|, |]|, |\| and spaces are special, +% and should be escaped. Other non-alphanumeric characters can +% still be escaped without harm. The escape sequences |\d|, +% |\D|, |\w|, |\W| are also supported in character classes. +% If the first character is |^|, then the meaning of the character +% class is inverted. Ranges of characters can be expressed using +% |-|, for instance, |[\D 0-5]| is equivalent to |[^6-9]|. +% +% Capturing groups are a means of extracting information about the +% match. Parenthesized groups are labelled in the order of their +% opening parenthesis, starting at $1$. The contents of those groups +% corresponding to the \enquote{best} match (leftmost longest) +% can be extracted and stored in a sequence of strings using for +% instance \cs{regex_extract:nnNTF}. +% +% \subsection{Syntax of in the replacement text} +% +% Most of the features described in regular expressions do not make sense +% within the replacement text. Escaped characters are supported as inside +% regular expressions. The various submatches are accessed with |\1|, |\2|, +% \emph{etc.}, and the whole match is accessed using |\0|. +% +% For instance, +% \begin{verbatim} +% \str_set:Nn \l_my_str { Hello,~world! } +% \regex_replace_all:nnN { ([er]?l|o) . } { \(\0\-\-\1\) } \l_my_str +% \end{verbatim} +% results in \cs{l_my_str} holding |H(ell--el)(o,--o) w(or--o)(ld--l)!| +% +% Submatches with numbers higher than $10$ are accessed in the same way, +% namely |\10|, |\11|, \emph{etc}. To insert in the replacement text +% a submatch followed by a digit, the digit must be entered using the +% |\x| escape sequence: for instance, to get the first submatch followed +% by the digit $7$, use |\1\x37|, because $7$ has character code |37| +% (in hexadecimal). +% +% \subsection{Precompiling regular expressions} +% +% If a regular expression is to be used several times, +% it is better to compile it once rather than doing it +% each time the regular expression is used. The precompiled +% regular expression is stored as a token list variable. All +% of the \pkg{l3regex} module's functions can be given their +% regular expression argument either as an explicit string +% or as a precompiled regular expression. +% +% \begin{function}{\regex_set:Nn} +% \begin{function}{\regex_gset:Nn} +% \begin{syntax} +% \cs{regex_set:Nn} \meta{tl var} \Arg{regex} +% \end{syntax} +% Stores a precompiled version of the \meta{regular expression} +% in the \meta{tl var}. For instance, this function can be used +% as +% \begin{verbatim} +% \tl_new:N \l_my_regex_tl +% \regex_set:Nn \l_my_regex_tl { my\ (simple\ )? reg(ex|ular\ expression) } +% \end{verbatim} +% The assignment is local for \cs{regex_set:Nn} +% and global for \cs{regex_gset:Nn}. +% \begin{texnote} +% Precompiled regular expressions can be safely written to a file +% and read when the \LaTeX3 syntax is active (as triggered by +% \cs{ExplSyntaxOn}). +% \end{texnote} +% \end{function} +% \end{function} +% +% \subsection{String matching} +% +% \begin{function}[TF]{\regex_match:nn} +% \begin{function}[TF]{\regex_match:Nn} +% \begin{syntax} +% \cs{regex_match:nnTF} \Arg{regex} \Arg{string} \Arg{true code} \Arg{false code} +% \end{syntax} +% Tests whether the \meta{regular expression} matches any substring +% of \meta{string}. For instance, +% \begin{verbatim} +% \regex_match:nnTF { b [cde]* } { abecdcx } { TRUE } { FALSE } +% \regex_match:nnTF { [b-dq-w] } { example } { TRUE } { FALSE } +% \end{verbatim} +% leaves \texttt{TRUE FALSE} in the input stream. +% \end{function} +% \end{function} +% +% \begin{function}{\regex_count:nnN} +% \begin{function}{\regex_count:NnN} +% \begin{syntax} +% \cs{regex_count:nnN} \Arg{regex} \Arg{string} \meta{int var} +% \end{syntax} +% Sets \meta{int var} equal to the number of times +% \meta{regular expression} appears in \meta{string}. +% The search starts by finding the left-most longest match, +% respecting greedy and ungreedy operators. Then the search +% starts again from the character following the last character +% of the previous match, until reaching the end of the string. +% For instance, +% \begin{verbatim} +% \int_new:N \l_foo_int +% \regex_count:nnN { (b+|c) } { abbababcbb } \l_foo_int +% \end{verbatim} +% results in \cs{l_foo_int} taking the value $5$. +% \end{function} +% \end{function} +% +% \subsection{Submatch extraction} +% +% \begin{function}{\regex_extract:nnN} +% \begin{function}{\regex_extract:NnN} +% \begin{function}[TF]{\regex_extract:nnN} +% \begin{function}[TF]{\regex_extract:NnN} +% \begin{syntax} +% \cs{regex_extract:nnNTF} \Arg{regex} \Arg{string} \meta{seq~var} \Arg{true code} \Arg{false code} +% \end{syntax} +% Finds the first match of the \meta{regular expression} +% in the \meta{string}. If it exists, the match is stored +% as the zeroeth item of the \meta{seq~var}, and further +% items are the contents of capturing groups, in the order +% of their opening left parenthesis. The \meta{seq~var} +% is assigned locally. If there is no match, +% the \meta{seq~var} is not altered. +% The testing versions return \meta{true} if a match was found, +% and \meta{false} otherwise. +% For instance, assume that you type +% \begin{verbatim} +% \regex_extract:nnNTF { ^(La)?TeX(!*)$ } { LaTeX!!! } +% \l_foo_seq { true } { false } +% \end{verbatim} +% Then the regular expression (anchored at the start with |^| and +% at the end with |$|) will match the whole string. The first +% capturing group, |(La)?|, matches |La|, and the second capturing +% group, |(!*)|, matches |!!!|. Thus, |\l_foo_seq| will contain +% the items |{LaTeX!!!}|, |{La}|, and |{!!!}|, and the \texttt{true} +% branch is left in the input stream. +% \end{function} +% \end{function} +% \end{function} +% \end{function} +% +% \subsection{String splitting} +% +% \begin{function}{\regex_split:nnN} +% \begin{function}{\regex_split:NnN} +% \begin{syntax} +% \cs{regex_split:nnN} \Arg{regular expression} \meta{string} \meta{seq~var} +% \end{syntax} +% Searches the \meta{string} into a sequence of substrings, delimited by +% matches of the \meta{regular expression}. If the \meta{regular expression} +% has capturing groups, then the substrings that they match are stored as +% items of the sequence as well. The assignment to \meta{seq~var} is local. +% If no match is found the resulting \meta{seq~var} has the \meta{string} as +% its sole item. If the \meta{regular expression} matches the empty string, +% then the \meta{string} is split into single character substrings. +% \end{function} +% \end{function} +% +% \subsection{String replacement} +% +% \begin{function}{\regex_replace_once:nnN} +% \begin{function}{\regex_replace_once:NnN} +% \begin{function}[TF]{\regex_replace_once:nnN} +% \begin{function}[TF]{\regex_replace_once:NnN} +% \begin{syntax} +% \cs{regex_replace_once:nnN} \Arg{regular expression} \Arg{replacement} \meta{str~var} +% \end{syntax} +% Searches for the \meta{regular expression} in the \meta{string} +% and replaces the matching part with the \meta{replacement}. The result +% is assigned locally to \meta{str~var}. In the \meta{replacement}, +% |\0| represents the full match, |\1| represent the contents +% of the first capturing group, |\2| of the second, \emph{etc.} +% \end{function} +% \end{function} +% \end{function} +% \end{function} +% +% \begin{function}{\regex_replace_all:nnN} +% \begin{function}{\regex_replace_all:NnN} +% \begin{syntax} +% \cs{regex_replace_all:nnN} \Arg{regular expression} \Arg{replacement} \meta{str~var} +% \end{syntax} +% Replaces all occurrences of the \cs{regular expression} +% in the \meta{string} by the \meta{replacement}, where +% |\0| represents the full match, |\1| +% represent the contents of the first capturing group, +% |\2| of the second, \emph{etc.} Every match +% is treated independently. The result is assigned +% locally to \meta{str~var}. +% \end{function} +% \end{function} +% +% \subsection{Bugs, misfeatures, future work, and other possibilities} +% +% The \texttt{\{\}} quantifiers are not implemented. +% +% The following need discussion. +% \begin{itemize} +% \item Newline conventions are not done. +% In particular, |.| should not match newlines. +% Also, |\A| should differ from |^|, and |\Z|, |\z| and |$| should +% differ. +% \item Caseless matching and more generally +% |(*..)| and |(?..)| sequences to set some options. +% \item General look-ahead/behind assertions. +% \item Idea of |#| as a synonym of |.*?|. +% \item Do we need a facility for balanced groups? (That's non-regular.) +% \end{itemize} +% +% The following features are likely to be implemented at some point +% in the future. +% \begin{itemize} +% \item Optimize simple strings: use less states +% (|abcade| should give two states, for |abc| and |ade|). +% \item Optimize groups with no alternative. +% \item Optimize the use of \cs{prg_stepwise_...} functions. +% \item Implement regex matching on external files. +% \item Conditional subpatterns with look ahead/behind: \enquote{if +% what follows is [\ldots{}], then [\ldots{}]}. +% \item "(?|..|..)" to reset the capturing group number at the start +% of each alternative. +% \item Todo: check space's catcode in the result of a replacement. +% \end{itemize} +% +% The following features not present in PCRE nor perl (because they can +% be done in other ways in those languages) might be added. +% \begin{itemize} +% \item |#| as a synonym of |.*?|, in a way similar to macro parameters. +% \item Facility to match balanced groups: this is non-regular, but quite +% useful, and it can be implemented without too much performance loss. +% (\emph{cf.} callout?) +% \end{itemize} +% +% The following features of PCRE or perl will probably not be implemented. +% \begin{itemize} +% \item Other forms of escapes, and character classes. +% |\cx|$\simeq$|\^^x|, |\ddd| (octal \texttt{ddd}). +% POSIX character classes. +% |\p{..}| and |\P{..}| for having/not having a Unicode property. +% |\X| for \enquote{extended} Unicode sequence. +% \item Callout with |(?C...)|. +% \item Conditional subpatterns (other than with a look-ahead +% or look-behind condition): this is non-regular, isn't it? +% \end{itemize} +% +% The following features of PCRE or perl will not be implemented. +% \begin{itemize} +% \item Comments: \TeX{} already has its own system for comments. +% \item Named subpatterns: \TeX{} programmers have lived so far without +% any need for named macro parameters. +% \item |\Q...\E| escaping: this would require to read the argument +% verbatim, which is not in the scope of this module. +% \item Atomic grouping, possessive quantifiers: those tools, mostly +% meant to fix catastrophic backtracking, are unnecessary in a +% non-backtracking algorithm, and difficult to implement. +% \item Subroutine calls: this syntactic sugar is difficult to +% include in a non-backtracking algorithm, in particular because +% the corresponding group should be treated as atomic. +% \item Recursion: this is a non-regular feature. +% \item Back-references: non-regular feature, this requires backtracking, +% which is prohibitively slow. +% \item Backtracking control verbs: intrinsically tied to backtracking. +% \item |\K| for resetting the beginning of the match: tied to backtracking. +% \item |\C| single byte in UTF-8 mode: well, UTF-8 mode is not +% implemented, and that seems like a very rarely useful feature anyways. +% \end{itemize} +% +% \end{documentation} +% +% \begin{implementation} +% +% \section{\pkg{l3regex} implementation} +% +%<*package> +% \begin{macrocode} +\ProvidesExplPackage + {\ExplFileName}{\ExplFileDate}{\ExplFileVersion}{\ExplFileDescription} +\RequirePackage{l3str} +% \end{macrocode} +% +% Most regex engines use backtracking. This allows to provide very +% powerful features (back-references come to mind first), but it is +% costly. Since \TeX{} is not first and foremost a programming language, +% complicated code tends to run slowly, and we must use faster, albeit +% slightly more restrictive, techniques, coming from automata theory. +% +% Given a regular expression of $n$ characters, we build a +% non-deterministic finite automaton (NFA) with roughly $n$ states, +% which accepts precisely those strings matching that regular expression. +% We then run the string through the NFA, and check the return value. +% +% The code is structured as follows. Various helper functions are +% introduced in the next subsection, to limit the clutter in later +% parts. Then functions pertaining to parsing the regular expression +% are introduced: that part is rather long because of the many bells +% and whistles that we need to cater for. The next subsection takes +% care of running the NFA, and describes how the various \TeX{} +% registers are (ab)used in this module. Finally, user functions. +% +% \subsection{Constants and variables} +% +% \begin{macro}{\regex_tmp:w} +% \begin{variable}{\g_regex_tmpa_tl,\l_regex_tmpa_tl,\l_regex_tmpb_tl} +% \begin{variable}{\l_regex_tmpa_int} +% Temporary variables. +% \begin{macrocode} +\cs_new:Npn \regex_tmp:w { } +\tl_new:N \g_regex_tmpa_tl +\tl_new:N \l_regex_tmpa_tl +\tl_new:N \l_regex_tmpb_tl +\int_new:N \l_regex_tmpa_int +% \end{macrocode} +% \end{variable} +% \end{variable} +% \end{macro} +% +% \subsubsection{Variables used while building} +% +% \begin{variable}{\l_regex_pattern_str} +% The \meta{pattern} is stored in a string variable. +% \begin{macrocode} +\tl_new:N \l_regex_pattern_str +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_regex_max_state_int} +% \begin{variable}{\l_regex_left_state_int,\l_regex_right_state_int} +% \begin{variable}{\l_regex_left_state_seq,\l_regex_right_state_seq} +% The last state that was allocated is stored in \cs{l_regex_max_state_int}, +% and \cs{l_regex_left/right_state_int} point to both end-points of the +% last group (which any quantifier would repeat). For simple strings of +% characters, the left and right pointers only differ by one. +% \begin{macrocode} +\int_new:N \l_regex_max_state_int +\int_new:N \l_regex_left_state_int +\int_new:N \l_regex_right_state_int +\seq_new:N \l_regex_left_state_seq +\seq_new:N \l_regex_right_state_seq +% \end{macrocode} +% \end{variable} +% \end{variable} +% \end{variable} +% +% \begin{variable}{\l_regex_capturing_group_int} +% \begin{variable}{\l_regex_capturing_group_seq} +% \cs{l_regex_capturing_group_int} is the id number of the current +% capturing group, starting at $0$ for a group enclosing the full +% regular expression, and counting in the order of their left parenthesis. +% This number is used when a branch of the alternation ends. +% Capturing groups can be arbitrarily nested, and we keep track of +% the stack of id numbers in \cs{l_regex_capturing_group_seq}. +% \begin{macrocode} +\int_new:N \l_regex_capturing_group_int +\seq_new:N \l_regex_capturing_group_seq +% \end{macrocode} +% \end{variable} +% \end{variable} +% +% \begin{variable}{\l_regex_one_or_group_tl} +% When looking for quantifiers, this variable holds either +% \enquote{one} or \enquote{group} depending on whether the +% object to which the quantifier applies matches one character +% (\emph{i.e.}, is a character or character class), or is a group. +% \begin{macrocode} +\tl_new:N \l_regex_one_or_group_tl +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{l_regex_repetition_int} +% Used when to distinguish threads at the same state but with +% different repetitions of some quantifiers. +% \begin{macrocode} +\int_new:N \l_regex_repetition_int +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_regex_look_behind_bool} +% \begin{variable}{\l_regex_look_behind_str} +% The boolean \cs{l_regex_look_behind_bool} indicates whether +% a look-behind assertion appears within the regular expression +% (currently only |\b| or |\B|). When matching, we will keep +% track of the part of the string before the current character +% in \cs{l_regex_look_behind_str}, stored backwards. +% \begin{macrocode} +\bool_new:N \l_regex_look_behind_bool +\tl_new:N \l_regex_look_behind_str +% \end{macrocode} +% \end{variable} +% \end{variable} +% +% \subsubsection{Character classes} +% +% \begin{macro}{\regex_build_tmp_class:n} +% \begin{variable}{\l_regex_class_bool,\l_regex_class_tl} +% Used when building character classes. +% \begin{macrocode} +\cs_new_eq:NN \regex_build_tmp_class:n \use_none:n +\bool_new:N \l_regex_class_bool +\tl_new:N \l_regex_class_tl +% \end{macrocode} +% \end{variable} +% \end{macro} +% +% \begin{variable}{\c_regex_d_tl,\c_regex_D_tl} +% \begin{variable}{\c_regex_h_tl,\c_regex_H_tl} +% \begin{variable}{\c_regex_s_tl,\c_regex_S_tl} +% \begin{variable}{\c_regex_v_tl,\c_regex_V_tl} +% \begin{variable}{\c_regex_w_tl,\c_regex_W_tl} +% \begin{variable}{\c_regex_N_tl} +% These constant token lists encode which characters +% are recognized by |\d|, |\D|, |\w|, \emph{etc.} +% in regular expressions. Namely, |\d=[0-9]|, +% |\w=[0-9A-Z_a-z]|, |\s=[\ \^^I\^^J\^^L\^^M]|, +% |\h=[\ \^^I]|, |\v=[\^^J-\^^M]|, and the upper-case +% counterparts match anything that the lowercase +% does not match. +% The order in which the various ranges appear is +% optimized for usual mostly lowercase letter text. +% \begin{macrocode} +\tl_const:Nn \c_regex_d_tl + { + \regex_item_range:nn {48} {57} % 0--9 + } +\tl_const:Nn \c_regex_D_tl + { + \regex_item_more:n {57} % 9 + \regex_item_range:nn {0} {47} % 0 + } +\tl_const:Nn \c_regex_h_tl + { + \regex_item_equal:n {32} % space + \regex_item_equal:n {9} % tab + } +\tl_const:Nn \c_regex_H_tl + { + \regex_item_neq:n {32} % space + \regex_item_neq:n {9} % tab + \regex_break_true:w + } +\tl_const:Nn \c_regex_s_tl + { + \regex_item_equal:n {32} % space + \regex_item_neq:n {11} % vtab + \regex_item_range:nn {9} {13} % tab, lf, vtab, ff, cr + } +\tl_const:Nn \c_regex_S_tl + { + \regex_item_more:n {32} % > space + \regex_item_range:nn {14} {31} % tab < ... < space + \regex_item_range:nn {0} {8} % < tab + \regex_item_equal:n {11} % vtab + } +\tl_const:Nn \c_regex_v_tl + { + \regex_item_range:nn {10} {13} % lf, vtab, ff, cr + } +\tl_const:Nn \c_regex_V_tl + { + \regex_item_more:n {13} % cr + \regex_item_range:nn {0} {9} % < lf + } +\tl_const:Nn \c_regex_w_tl + { + \regex_item_range:nn {97} {122} % a--z + \regex_item_range:nn {65} {90} % A--Z + \regex_item_range:nn {48} {57} % 0--9 + \regex_item_equal:n {95} % _ + } +\tl_const:Nn \c_regex_W_tl + { + \regex_item_range:nn {0} {47} % <`0 + \regex_item_range:nn {58} {64} % (`9+1)--(`A-1) + \regex_item_range:nn {91} {94} % (`Z+1)--(`_-1) + \regex_item_equal:n {96} % ` + \regex_item_more:n {122} % z + } +\tl_const:Nn \c_regex_N_tl + { + \regex_item_neq:n {10} % lf + \regex_break_true:w + } +% \end{macrocode} +% \end{variable} +% \end{variable} +% \end{variable} +% \end{variable} +% \end{variable} +% \end{variable} +% +% \subsubsection{Variables used when matching} +% +% \begin{variable}{\l_regex_query_str} +% The string that is being matched. +% \begin{macrocode} +\tl_new:N \l_regex_query_str +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_regex_start_step_int} +% In the case of multiple matches, \cs{l_regex_start_step_int} +% is equal to the position where the current match +% attempt began. +% \begin{macrocode} +\int_new:N \l_regex_start_step_int +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_regex_current_char_int} +% \begin{variable}{\l_regex_current_step_int} +% The character at the current position in the string, +% and the current position. +% \begin{macrocode} +\int_new:N \l_regex_current_char_int +\int_new:N \l_regex_current_step_int +% \end{macrocode} +% \end{variable} +% \end{variable} +% +% \begin{variable}{\l_regex_unique_step_int} +% This gives a unique identifier to every step in the loop over +% characters in the string. In the case of a single match, it is +% equal to \cs{l_regex_current_step_int}, but for multiple matches, +% it is not reset to the start of the next match, but incremented. +% A unique identifier is handy when considering whether a state of +% the automaton has been visited at that step or not, and when +% tracking submatch information. +% \begin{macrocode} +\int_new:N \l_regex_unique_step_int +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_regex_current_state_int} +% For every character in the string, each of the active states is +% considered in turn. +% The variable \cs{l_regex_current_state_int} holds the state +% of the NFA which is currently considered. +% \begin{macrocode} +\int_new:N \l_regex_current_state_int +\prop_new:N \l_regex_current_submatches_prop +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_regex_max_index_int} +% All the currently active states are kept in order of precedence +% in the \tn{skip} registers, which for our purpose serve as an array: +% the $i$-th item of the array is \tn{skip}$i$. The largest index used +% after treating the previous character is \cs{l_regex_max_index_int}. +% At the start of every step, the whole array is unpacked, so that the +% space can immediately be reused, and \cs{l_regex_max_index_int} reset +% to zero, effectively clearing the array. +% \begin{macrocode} +\int_new:N \l_regex_max_index_int +% \end{macrocode} +% \end{variable} +% +% \begin{macro}[int]{\l_regex_every_match_tl} +% Every time a match is found, this token list is used. +% For single matching, the token list is set to removing +% the remainder of the query string. For multiple matching, +% the token list is set to repeat the matching. +% \begin{macrocode} +\tl_new:N \l_regex_every_match_tl +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\regex_last_match_empty:F} +% This function is most often \cs{use:n}, unless the current +% regular expression can match an empty string at the current +% position, and has already done so at the previous match attempt. +% The goal is to break infinite loops. +% \begin{macrocode} +\cs_new_protected:Npn \regex_last_match_empty_no:F #1 {#1} +\cs_new_protected:Npn \regex_last_match_empty_yes:F + { \int_compare:nNnF \l_regex_start_step_int = \l_regex_current_step_int } +\cs_new_eq:NN \regex_last_match_empty:F \regex_last_match_empty_no:F +% \end{macrocode} +% \end{macro} +% +% \begin{variable}{\l_regex_success_bool} +% \begin{variable}{\l_regex_success_step_int} +% \begin{variable}{\l_regex_success_submatches_prop} +% \begin{variable}{\l_regex_success_empty_bool} +% The booleans \cs{l_regex_success_bool} and +% \cs{l_regex_success_empty_bool} are true if the current match +% attempt was successful, and if the match was empty, respectively. +% The other two variables hold the step number at which the match +% was successful and a property list of submatches. +% \begin{macrocode} +\bool_new:N \l_regex_success_bool +\bool_new:N \l_regex_success_empty_bool +\int_new:N \l_regex_success_step_int +\prop_new:N \l_regex_success_submatches_prop +% \end{macrocode} +% \end{variable} +% \end{variable} +% \end{variable} +% \end{variable} +% +% \begin{variable}{\l_regex_fresh_thread_bool} +% This boolean marks when the current thread has started from +% the beginning of the regular expression at this character, +% in other words, it is true if the current thread has matched +% an empty string so far (and we only care about this boolean +% when a thread succeeds, hence matching an empty string overall). +% \begin{macrocode} +\bool_new:N \l_regex_fresh_thread_bool +% \end{macrocode} +% \end{variable} +% +% \subsubsection{Variables used for user functions} +% +% \begin{variable}{\g_regex_submatches_seq} +% This holds temporarily a sequence of submatches, +% global so that it exits the group. +% \begin{macrocode} +\seq_new:N \g_regex_submatches_seq +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\g_regex_match_count_int} +% The number of matches found so far is stored +% in \cs{g_regex_match_count_int}. This is only used +% in the \cs{regex_count:nnN} functions. +% \begin{macrocode} +\int_new:N \g_regex_match_count_int +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\g_regex_split_seq} +% The \cs{regex_split:nnN} function stores its result +% in that sequence variable before assigning it to the +% variable provided by the user. +% \begin{macrocode} +\seq_new:N \g_regex_split_seq +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_regex_replacement_tl} +% \begin{variable}{\g_regex_replaced_str} +% The replacement code stores a processed version +% of the user's argument in \cs{l_regex_replacement_tl}. +% The result of the replacement is stored in \cs{g_regex_replaced_str}, +% global to exit the group. +% \begin{macrocode} +\tl_new:N \l_regex_replacement_tl +\tl_new:N \g_regex_replaced_str +% \end{macrocode} +% \end{variable} +% \end{variable} +% +% \subsection{Helpers} +% +% \subsubsection{Toks} +% +% \begin{macro}[int]{\s_regex_toks} +% When performing the matching, the \tn{toks} registers hold submatch +% information, followed by the instruction for a given state of the NFA. +% The two parts are separated by \cs{s_regex_toks}. +% \begin{macrocode} +\cs_new_eq:NN \s_regex_toks \scan_stop: +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[int]{\regex_toks_put_left:Nx} +% \begin{macro}[int]{\regex_toks_put_right:Nx} +% During the building phase, every \tn{toks} register starts with +% \cs{s_regex_toks}, and we wish to add \texttt{x}-expanded material +% to those registers. The expansion is done \enquote{by hand} for +% optimization (these operations are used quite a lot). When adding +% material to the left, we define \cs{regex_tmp:w} to remove the +% \cs{s_regex_toks} marker and put it back to the left of the new +% material. +% \begin{macrocode} +\cs_new_protected:Npn \regex_toks_put_left:Nx #1#2 + { + \cs_set_nopar:Npx \regex_tmp:w \s_regex_toks { \s_regex_toks #2 } + \tex_toks:D #1 \exp_after:wN \exp_after:wN \exp_after:wN + { \exp_after:wN \regex_tmp:w \tex_the:D \tex_toks:D #1 } + } +\cs_new_protected:Npn \regex_toks_put_right:Nx #1#2 + { + \cs_set_nopar:Npx \regex_tmp:w {#2} + \tex_toks:D #1 \exp_after:wN + { \tex_the:D \tex_toks:D \exp_after:wN #1 \regex_tmp:w } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \subsubsection{Interrupting recursions} +% +% \begin{macro}[int]{\regex_if_tail_stop:N} +% \begin{macro}[int]{\regex_if_tail_error:Nn} +% Test for the end of the \meta{string}, and either stop +% or cause an error if it is reached. +% \begin{macrocode} +\cs_new_eq:NN \regex_if_tail_stop:N \quark_if_recursion_tail_stop:N +\cs_new_protected_nopar:Npn \regex_if_tail_error:Nn #1#2 + { \quark_if_recursion_tail_stop_do:Nn #1 { \regex_build_error:n {#2} } } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[int]{\regex_build_error:n} +% This macro is called if anything goes wrong when building +% the NFA corresponding to a given regular expression +% Negative codes are specific to the \LaTeX3 implementation. +% Other error codes match with the PCRE codes (not all of +% the PCRE errors can occur, since some constructions are +% not supported). +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_build_error:n #1 + { + \msg_error:nnxx { regex } { build-error } {\int_eval:n{#1}} + { + \prg_case_int:nnn {#1} + { + {-999} {File~not~found} + {-998} {Unsupported~construct} + {-997} {The~regular~expression~is~too~large~(32768~states).} + {1} {\iow_char:N\\~at~end~of~pattern } +% {2} {\iow_char:N\\c~at~end~of~pattern } + {4} {Numbers~out~of~order~in~\iow_char:N\{\iow_char\}~quantifier.} + {6} {Missing~terminating~\iow_char:N\]~for~character~class } + {7} {Invalid~escape~sequence~in~character~class} + {8} {Range~out~of~order~in~character~class} + {22} {Mismatched~parentheses} + {34} {Character~value~in~\iow_char:N\\x{...}~sequence~is~too~large} +% {44} {Invalid~UTF-8~string} +% {46} {Malformed~\iow_char:N\\P~or\iow_char:N\\p~sequence} +% {47} {Unknown~property~after~\iow_char:N\\P~or\iow_char:N\\p} +% {68} {\iow_char:N\\c~must~be~followed~by~an~ASCII~character} + } + { Internal~bug. } + } + } +\msg_new:nnn { regex } { build-error } { (error~#1): ~ #2 } +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Testing characters} +% +% \begin{macro}[int]{\regex_break_point:TF} +% \begin{macro}[int]{\regex_break_true:w,\regex_break_false:w} +% When testing whether a character of the query string matches +% a given character class in the regular expression, we often +% have to test it against several ranges of characters, checking +% if any one of those matches. This is done with a structure like +% \begin{quote} +% \meta{test1} \ldots{} \meta{test$\sb{n}$} \\ +% \cs{regex_break_point:TF} \Arg{true code} \Arg{false code} +% \end{quote} +% If any of the tests succeeds, it calls \cs{regex_break_true:w}, +% which cleans up and leaves \meta{true code} in the input stream. +% Otherwise, \cs{regex_break_point:TF} leaves the \meta{false code} +% in the input stream. +% \begin{macrocode} +\cs_new_nopar:Npn \regex_break_true:w #1 \regex_break_point:TF #2 #3 {#2} +\cs_new_nopar:Npn \regex_break_false:w #1 \regex_break_point:TF #2 #3 {#3} +\cs_new_eq:NN \regex_break_point:TF \use_ii:nn +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[int]{\regex_item_equal:n,\regex_item_range:nn} +% \begin{macro}[int]{\regex_item_less:n,\regex_item_more:n} +% \begin{macro}[int]{\regex_item_neq:n} +% Simple comparisons triggering \cs{regex_break_true:w} when true. +% \begin{macrocode} +\cs_new_nopar:Npn \regex_item_equal:n #1 + { + \if_num:w #1 = \l_regex_current_char_int + \exp_after:wN \regex_break_true:w + \fi: + } +\cs_new_nopar:Npn \regex_item_range:nn #1 #2 + { + \reverse_if:N \if_num:w #1 > \l_regex_current_char_int + \reverse_if:N \if_num:w #2 < \l_regex_current_char_int + \exp_after:wN \exp_after:wN \exp_after:wN \regex_break_true:w + \fi: + \fi: + } +\cs_new_nopar:Npn \regex_item_less:n #1 + { + \if_num:w #1 > \l_regex_current_char_int + \exp_after:wN \regex_break_true:w + \fi: + } +\cs_new_nopar:Npn \regex_item_more:n #1 + { + \if_num:w #1 < \l_regex_current_char_int + \exp_after:wN \regex_break_true:w + \fi: + } +\cs_new_nopar:Npn \regex_item_neq:n #1 + { + \if_num:w #1 = \l_regex_current_char_int + \exp_after:wN \regex_break_false:w + \fi: + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \subsubsection{Grabbing digits} +% +% \begin{macro}[int]{\regex_get_digits:nw} +% \begin{macro}[aux]{\regex_get_digits_loop:N,\regex_get_digits_end:w} +% Grabs digits (of category code other), skipping any intervening +% space, until encountering a non-digit, and places the result +% in a brace group after |#1|. This is used when parsing the \texttt{\{} +% quantifier. +% \begin{macrocode} +\cs_new_protected:Npn \regex_get_digits:nw #1 + { + \tex_afterassignment:D \regex_tmp:w + \cs_set_nopar:Npx \regex_tmp:w + { + \exp_not:n {#1} + { \if_false: } } \fi: + \regex_get_digits_aux:N + } +\cs_new_nopar:Npn \regex_get_digits_aux:N #1 + { + \if_num:w 9 < 1 \exp_not:N #1 \exp_stop_f: + \else: + \if_charcode:w \c_space_token \exp_not:N #1 + \else: + \regex_get_digits_end:w + \fi: + \fi: + #1 \regex_get_digits_aux:N + } +\cs_new_nopar:Npn \regex_get_digits_end:w + \fi: \fi: #1 \regex_get_digits_aux:N + { + \fi: \fi: + \if_false: { { \fi: } } + #1 + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \subsubsection{More char testing} +% +% \begin{macro}[EXP,aux]{\regex_aux_char_if_alphanumeric:NTF} +% \begin{macro}[EXP,aux]{\regex_aux_char_if_special:NTF} +% These two tests are used in the first pass when parsing a +% regular expression. That pass is responsible for finding +% escaped and non-escaped characters, and recognizing which +% ones have special meanings and which should be interpreted +% as \enquote{raw} characters. Namely, +% \begin{itemize} +% \item alphanumerics are \enquote{raw} if they are not escaped, +% and may have a special meaning when escaped; +% \item non-alphanumeric printable ascii characters are \enquote{raw} +% if they are escaped, and may have a special meaning when not escaped; +% \item characters other than printable ascii are always \enquote{raw}. +% \end{itemize} +% The code is ugly, and highly based on magic numbers and the ascii +% codes of characters. This is mostly unavoidable for performance +% reasons: testing for instance with \cs{str_if_contains_char:nN} +% would be much slower. Maybe the tests can be optimized a little +% bit more. +% Here, \enquote{alphanumeric} means \texttt{0}--\texttt{9}, +% \texttt{A}--\texttt{Z}, \texttt{a}--\texttt{z}; +% \enquote{special} character means non-alphanumeric +% but printable ascii, from space (hex \texttt{20}) to +% \texttt{del} (hex \texttt{7E}). +% \begin{macrocode} +\prg_new_conditional:Npnn \regex_aux_char_if_special:N #1 { TF } + { + \if_num:w `#1 < 97 \exp_stop_f: + \if_num:w `#1 < 58 \exp_stop_f: + \if_num:w \int_eval:w (`#1 - \c_eight)/\c_sixteen = \c_two + \prg_return_true: + \else: + \prg_return_false: + \fi: + \else: + \if_num:w \int_eval:w `#1 / 26 = \c_three + \prg_return_false: + \else: + \prg_return_true: + \fi: + \fi: + \else: + \if_num:w \int_eval:w `#1 / \c_five = 25 \exp_stop_f: + \prg_return_true: + \else: + \prg_return_false: + \fi: + \fi: + } +\prg_new_conditional:Npnn \regex_aux_char_if_alphanumeric:N #1 { TF } + { + \if_num:w `#1 < 91 \exp_stop_f: + \if_num:w `#1 < 65 \exp_stop_f: + \if_num:w \c_nine < 1 #1 \exp_stop_f: + \prg_return_true: + \else: + \prg_return_false: + \fi: + \else: + \prg_return_true: + \fi: + \else: + \if_num:w \int_eval:w (`#1-\c_six)/26 = \c_four + \prg_return_true: + \else: + \prg_return_false: + \fi: + \fi: + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \subsection{Building} +% +% \subsubsection{Helpers for building an NFA} +% +% \begin{macro}[int]{\regex_build_new_state:} +% Here, we add a new state to the NFA. At the end of the building +% phase, we want every \tn{toks} register to start with +% \cs{s_regex_toks}, hence initialize the new register appropriately. +% Then set \cs{l_regex_left/right_state_int} to their new values. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_build_new_state: + { + \int_compare:nNnTF \l_regex_max_state_int > {32766} + { \regex_build_error:n {-997} } + { + \int_incr:N \l_regex_max_state_int + \tex_toks:D \l_regex_max_state_int { \s_regex_toks } + } + \int_set_eq:NN \l_regex_left_state_int \l_regex_right_state_int + \int_set_eq:NN \l_regex_right_state_int \l_regex_max_state_int + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux]{\regex_build_transition_aux:NN} +% \begin{macro}[aux]{\regex_build_transitions_aux:NNNN} +% These functions create a new state, and put one or two transitions +% starting from the old current state. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_build_transition_aux:NN #1#2 + { + \regex_build_new_state: + \regex_toks_put_right:Nx \l_regex_left_state_int + { #1 { \int_use:N #2 } } + } +\cs_new_protected_nopar:Npn \regex_build_transitions_aux:NNNN #1#2#3#4 + { + \regex_build_new_state: + \regex_toks_put_right:Nx \l_regex_left_state_int + { + #1 { \int_use:N #2 } + #3 { \int_use:N #4 } + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \subsubsection{From regex to NFA: framework} +% +% In order for the construction \texttt{ab\string|cd} to work, we enclose +% the whole pattern within parentheses. These have the added benefit +% to form a capturing group: hence we get the data of the whole match +% for free. +% +% \begin{macro}[int]{\regex_build:n} +% First, reset a few variables. Then use the generic framework defined +% in \pkg{l3str} to parse the regular expression once, recognizing +% which characters are raw characters, and which have special meanings. +% The result is stored in \cs{g_str_result_tl}, and can be run directly. +% The trailing \cs{prg_do_nothing:} ensure that the look-ahead done by +% some of the operations is harmless. +% Finally, \cs{regex_build_end:} adds the finishing code +% (checking that parentheses are properly nested, for instance). +% \begin{macrocode} +\cs_new_protected:Npn \regex_build:n #1 + { + \regex_build_setup: + \str_aux_escape:NNNn + \regex_build_i_unescaped:N + \regex_build_i_escaped:N + \regex_build_i_raw:N + { #1 } + \regex_build_open_aux: + \g_str_result_tl \prg_do_nothing: \prg_do_nothing: + \seq_push:Nn \l_regex_capturing_group_seq { 0 } + \regex_build_close_aux: \regex_build_group_: + \regex_build_end: + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux]{\regex_build_i_unescaped:N} +% \begin{macro}[aux]{\regex_build_i_escaped:N} +% \begin{macro}[aux]{\regex_build_i_raw:N} +% The \pkg{l3str} function \cs{str_aux_escape:NNNn} goes through +% the regular expression and finds the |\a|, |\e|, |\f|, |\n|, |\r|, +% |\t|, and |\x| escape sequences, then distinguishes three cases: +% non-escaped characters, escaped characters, and \enquote{raw} +% characters coming from one of the escape sequences. +% In the particular case of regular expressions, escaped alphanumerics +% and non-escaped non-alphanumeric printable ascii characters may have +% special meanings, while everything else should be treated as a raw +% character. +% \begin{macrocode} +\cs_new_nopar:Npn \regex_build_i_unescaped:N #1 + { + \regex_aux_char_if_special:NTF #1 + { \exp_not:N \regex_build_control:N #1 } + { \exp_not:N \regex_build_raw:N #1 } + } +\cs_new_nopar:Npn \regex_build_i_escaped:N #1 + { + \regex_aux_char_if_alphanumeric:NTF #1 + { \exp_not:N \regex_build_control:N #1 } + { \exp_not:N \regex_build_raw:N #1 } + } +\cs_new_nopar:Npn \regex_build_i_raw:N #1 + { \exp_not:N \regex_build_raw:N #1 } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[aux]{\regex_build_default_control:N} +% If the control character has a particular meaning in regular expressions, +% the corresponding function is used. Otherwise, it is interpreted as a raw +% character. The \cs{regex_build_default_raw:N} function is defined later. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_build_default_control:N #1 + { + \cs_if_exist_use:cF { regex_build_#1: } + { \regex_build_default_raw:N #1 } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[int]{\regex_build_setup:} +% Hopefully, we didn't forget to initialize anything here. +% The search is not anchored: to acheive that, we insert state(s) +% responsible for repeating the match attempt on every character +% of the string: the first state has a free transition to the second +% state, where the regular expression really begins, and a costly +% transition to itself, to try again at the next character. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_build_setup: + { + \cs_set_eq:NN \regex_build_control:N \regex_build_default_control:N + \cs_set_eq:NN \regex_build_raw:N \regex_build_default_raw:N + \int_set_eq:NN \l_regex_capturing_group_int \c_zero + \int_zero:N \l_regex_left_state_int + \int_zero:N \l_regex_right_state_int + \int_zero:N \l_regex_max_state_int + \regex_build_new_state: + \regex_build_new_state: + \regex_toks_put_right:Nx \l_regex_left_state_int + { + \regex_action_start_wildcard:nn + { \int_use:N \l_regex_left_state_int } + { \int_use:N \l_regex_right_state_int } + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[int]{\regex_build_end:} +% If parentheses are not nested properly, an error is raised, +% and the correct number of parentheses is closed. +% After that, we insert an instruction for the match to succeed. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_build_end: + { + \seq_if_empty:NF \l_regex_capturing_group_seq + { + \regex_build_error:n {22} + \prg_replicate:nn + { \seq_length:N \l_regex_capturing_group_seq } % ( + { \regex_build_close_aux: \regex_build_group_: } + } + \regex_toks_put_right:Nx \l_regex_right_state_int + { \regex_action_success: } + } +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Anchoring and simple assertions} +% +% \begin{macro}[int]{\regex_build_A:} +% \begin{macro}[int]+\regex_build_^:+ +% \begin{macro}[int]{\regex_build_G:} +% \begin{macro}[aux]{\regex_build_anchor_start:N} +% Anchoring at the start corresponds to checking that the current +% character is the first in the string. Anchoring to the beginning +% of the match attempt uses \cs{l_regex_start_step_int} instead of +% \cs{c_zero}. +% \begin{macrocode} +\cs_new_protected_nopar:cpn { regex_build_^: } + { \regex_build_anchor_start:N \c_zero } +\cs_new_protected_nopar:Npn \regex_build_A: + { \regex_build_anchor_start:N \c_zero } +\cs_new_protected_nopar:Npn \regex_build_G: + { \regex_build_anchor_start:N \l_regex_start_step_int } +\cs_new_protected_nopar:Npn \regex_build_anchor_start:N #1 + { + \regex_build_new_state: + \regex_toks_put_right:Nx \l_regex_left_state_int + { + \exp_not:N \int_compare:nNnT {#1} = \l_regex_current_step_int + { \regex_action_free:n { \int_use:N \l_regex_right_state_int } } + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[aux]{\regex_build_Z:} +% \begin{macro}[aux]{\regex_build_z:} +% \begin{macro}[aux]+\regex_build_$:+ +% This matches the end of the string, marked by a character code of $-1$. +% \begin{macrocode} +\cs_new_protected_nopar:cpn { regex_build_$: } % $ + { + \regex_build_new_state: + \regex_toks_put_right:Nx \l_regex_left_state_int + { + \exp_not:N \int_compare:nNnT + \c_minus_one = \l_regex_current_char_int + { \regex_action_free:n { \int_use:N \l_regex_right_state_int } } + } + } +\cs_new_eq:Nc \regex_build_Z: { regex_build_$: } %$ +\cs_new_eq:Nc \regex_build_z: { regex_build_$: } %$ +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[int]{\regex_build_b:} +% \begin{macro}[int]{\regex_build_B:} +% \begin{macro}[aux]{\regex_if_word_boundary:TF} +% Contrarily to |^| and |$|, which could be implemented without +% really knowing what preceeds in the string, this requires +% more information. We request it by setting +% \cs{l_regex_look_behind_bool}. Then the matching code will +% keep store the characters that were already read, backwards, +% in \cs{l_regex_look_behind_str}, and we can analyse the first +% character of that string. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_build_b: + { + \bool_set_true:N \l_regex_look_behind_bool + \regex_build_new_state: + \regex_toks_put_right:Nx \l_regex_left_state_int + { + \exp_not:N \regex_if_word_boundary:TF + { \regex_action_free:n { \int_use:N \l_regex_right_state_int } } + { } + } + } +\cs_new_protected_nopar:Npn \regex_build_B: + { + \bool_set_true:N \l_regex_look_behind_bool + \regex_build_new_state: + \regex_toks_put_right:Nx \l_regex_left_state_int + { + \exp_not:N \regex_if_word_boundary:TF + { } + { \regex_action_free:n { \int_use:N \l_regex_right_state_int } } + } + } +\cs_new_protected_nopar:Npn \regex_if_word_boundary:TF + { + \tl_if_empty:NTF \l_regex_look_behind_str + { \c_regex_w_tl } + { + \group_begin: + \cs_set_nopar:Npx \regex_tmp:w + { + \int_set:Nn \l_regex_current_char_int + { ` \str_head:N \l_regex_look_behind_str } + } + \regex_tmp:w + \c_regex_w_tl + \regex_break_point:TF + { \group_end: \c_regex_W_tl } + { \group_end: \c_regex_w_tl } + } + \regex_break_point:TF + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \subsubsection{Normal character, and simple character classes} +% +% \begin{macro}[aux]{\regex_build_default_raw:N} +% A normal alphanumeric or an escaped non-alphanumeric +% (actually, any unknown combination) will match itself +% and the thread will fail otherwise. We prepare +% \cs{regex_build_tmp_class:n} with the relevant test and +% commands. The program steps to be inserted in those +% commands will come as |##1| and |##2|: we don't know +% yet what those will be before checking for quantifiers. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_build_default_raw:N #1 + { + \cs_set:Npx \regex_build_tmp_class:n ##1 + { + \exp_not:n { \exp_not:N \if_num:w } + \int_value:w `#1 = \l_regex_current_char_int + \regex_action_cost:n { ##1 } + \exp_not:n { \exp_not:N \fi: } + } + \regex_build_one_quantifier: + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux]{\regex_build_.:} +% Similar to \cs{regex_build_default_raw:N} but accepts any character, +% and refuses $-1$, which marks the end of the string. +% \begin{macrocode} +\cs_new_protected_nopar:cpn { regex_build_.: } + { + \cs_set:Npn \regex_build_tmp_class:n ##1 + { + \exp_not:N \if_num:w \c_minus_one < \l_regex_current_char_int + \regex_action_cost:n {##1} + \exp_not:N \fi: + } + \regex_build_one_quantifier: + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux]{\regex_build_d:,\regex_build_D} +% \begin{macro}[aux]{\regex_build_h:,\regex_build_H} +% \begin{macro}[aux]{\regex_build_s:,\regex_build_S} +% \begin{macro}[aux]{\regex_build_v:,\regex_build_V} +% \begin{macro}[aux]{\regex_build_w:,\regex_build_W} +% \begin{macro}[aux]{\regex_build_N:} +% \begin{macro}[aux]{\regex_build_char_type:N} +% The constants \cs{c_regex_d_tl}, \emph{etc.} hold +% a list of tests which match the corresponding character +% class, and jump to the \cs{regex_break_point:TF} marker. +% As for a normal character, we check for quantifiers. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_build_char_type:N #1 + { + \cs_set:Npn \regex_build_tmp_class:n ##1 + { + \exp_not:N #1 + \exp_not:N \regex_break_point:TF + { \regex_action_cost:n {##1} } + { } + } + \regex_build_one_quantifier: + } +\tl_map_inline:nn { dDhHsSvVwWN } + { + \cs_new_protected_nopar:cpx { regex_build_#1: } + { + \exp_not:N \regex_build_char_type:N + \exp_not:c { c_regex_#1_tl } + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \subsubsection{Character classes} +% +% \begin{macro}[aux]{\regex_build_[:} +% This starts a class. The code for the class is collected +% in \cs{l_regex_class_tl}. The first character is special. +% \begin{macrocode} +\cs_new_protected_nopar:cpn { regex_build_[: } + { + \tl_clear:N \l_regex_class_tl + \cs_set_eq:NN \regex_build_control:N \regex_class_control:N + \cs_set_eq:NN \regex_build_raw:N \regex_class_raw:N + \regex_class_first:NN + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux]{\regex_class_control:N} +% This function is similar to \cs{regex_build_control:N}. If the control +% character has a meaning in character classes, call the corresponding +% function, otherwise, treat it as a raw character, with the +% \cs{regex_class_raw:N} function, defined later. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_class_control:N #1 + { + \cs_if_exist_use:cF { regex_class_#1: } + { \regex_class_raw:N #1 } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux]{\regex_class_]:} +% If \texttt{]} appears as the first item of a class, then +% it doesn't end the class. Otherwise, it's the end, +% act just as for a single character, but with a more +% complicated test. And restore \cs{regex_build_control:N} +% and \cs{regex_build_raw:N}. +% \begin{macrocode} +\cs_new_protected_nopar:cpn { regex_class_]: } + { + \tl_if_empty:NTF \l_regex_class_tl %[ + { \regex_class_raw:N ] } + { + \cs_set_eq:NN \regex_build_control:N \regex_build_default_control:N + \cs_set_eq:NN \regex_build_raw:N \regex_build_default_raw:N + \cs_set:Npn \regex_build_tmp_class:n ##1 + { + \exp_not:o \l_regex_class_tl + \bool_if:NTF \l_regex_class_bool + { + \exp_not:N \regex_break_point:TF + { \regex_action_cost:n {##1} } + { } + } + { + \exp_not:N \regex_break_point:TF + { } + { \regex_action_cost:n {##1} } + } + } + \regex_build_one_quantifier: + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux]{\regex_class_first:NN} +% If the first non-space character is |^|, then the class is inverted. +% We keep track of this in \cs{l_regex_class_bool}. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_class_first:NN #1#2 + { + \str_if_eq:nnTF {#1#2} { \regex_build_control:N ^ } + { \bool_set_false:N \l_regex_class_bool } + { + \bool_set_true:N \l_regex_class_bool + #1 #2 + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux]{\regex_class_raw:N} +% \begin{macro}[aux]{\regex_class_single:N} +% Most characters are treated here. We look ahead for an unescaped dash. +% If there is none, then the character matches itself. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_class_raw:N #1#2#3 + { + \str_if_eq:nnTF {#2#3} { \regex_build_control:N - } + { \regex_class_range:Nw #1 } + { + \regex_class_single:N #1 + #2 #3 + } + } +\cs_new_protected_nopar:Npn \regex_class_single:N #1 + { + \tl_put_right:Nx \l_regex_class_tl + { \exp_not:N \regex_item_equal:n { \int_value:w `#1 } } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[aux]{\regex_class_range:Nw} +% \begin{macro}[aux]{\regex_class_range_put:NN} +% If the character is followed by a dash, we look for +% the end-point of the range. For \enquote{raw} characters, +% that's simply |#3|. Most \enquote{control} characters also +% have no meaning, and can serve as an end-point, but those +% with a meaning interrupt the range. +% In the case of a true range, check whether the end-points +% are in the right order, and optimize in the case of equal +% end-points. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_class_range:Nw #1#2#3 + { + \token_if_eq_meaning:NNTF #2 \regex_build_control:N + { + \cs_if_exist:cTF { regex_class_#3: } + { + \regex_class_single:N #1 + \regex_class_single:N - + #2#3 + } + { \regex_class_range_put:NN #1#3 } + } + { \regex_class_range_put:NN #1#3 } + } +\cs_new_protected_nopar:Npn \regex_class_range_put:NN #1#2 + { + \if_num:w `#1 > `#2 \exp_stop_f: + \regex_build_error:n {8} + \else: + \tl_put_right:Nx \l_regex_class_tl + { + \if_num:w `#1 = `#2 \exp_stop_f: + \exp_not:N \regex_item_equal:n + \else: + \exp_not:N \regex_item_range:nn { \int_value:w `#1 } + \fi: + { \int_value:w `#2 } + } + \fi: + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[aux]{\regex_class_d:,\regex_class_D:} +% \begin{macro}[aux]{\regex_class_h:,\regex_class_H:} +% \begin{macro}[aux]{\regex_class_s:,\regex_class_S:} +% \begin{macro}[aux]{\regex_class_v:,\regex_class_V:} +% \begin{macro}[aux]{\regex_class_w:,\regex_class_W:} +% Similar to \cs{regex_class_single:N}, adding the appropriate +% ranges of characters to the class. The token lists are not +% expanded because it is more memory efficient, with a tiny +% overhead on execution. +% \begin{macrocode} +\tl_map_inline:nn { dDhHsSvVwWN } + { + \cs_new_protected_nopar:cpx { regex_class_#1: } + { + \tl_put_right:Nn \exp_not:N \l_regex_class_tl + { \exp_not:c { c_regex_#1_tl } } + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \subsubsection{Quantifiers} +% +% \begin{macro}[int]{\regex_build_quantifier:w} +% This looks ahead and finds any quantifier (control character +% equal to either of |?+*{|). ^^A} +% When all characters for the quantifier are found, the corresponding +% function is called. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_build_quantifier:w #1#2 + { + \token_if_eq_meaning:NNTF #1 \regex_build_control:N + { + \cs_if_exist_use:cF { regex_build_quantifier_#2:w } + { + \regex_build_quantifier_end:n { } + #1 #2 + } + } + { + \regex_build_quantifier_end:n { } + #1 #2 + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux]{\regex_build_quantifier_?:w} +% \begin{macro}[aux]{\regex_build_quantifier_*:w} +% \begin{macro}[aux]{\regex_build_quantifier_+:w} +% For each \enquote{basic} quantifier, |?|, |*|, |+|, feed the correct +% arguments to \cs{regex_build_quantifier_aux:nnNN}. +% \begin{macrocode} +\cs_new_protected_nopar:cpn { regex_build_quantifier_?:w } + { \regex_build_quantifier_aux:nnNN { } { ? } } +\cs_new_protected_nopar:cpn { regex_build_quantifier_*:w } + { \regex_build_quantifier_aux:nnNN { } { * } } +\cs_new_protected_nopar:cpn { regex_build_quantifier_+:w } + { \regex_build_quantifier_aux:nnNN { } { + } } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[aux]{\regex_build_quantifier_aux:nnNN} +% Once the \enquote{main} quantifier (\texttt{?}, \texttt{*}, +% \texttt{+} or a braced construction) is found, we check +% whether it is lazy (followed by a question mark), +% and calls the appropriate function. Here |#1| holds some extra +% arguments that the final function needs in the case of braced +% constructions, and is empty otherwise. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_build_quantifier_aux:nnNN #1#2#3#4 + { + \str_if_eq:nnTF { #3 #4 } { \regex_build_control:N ? } + { \regex_build_quantifier_end:n { #2 #4 } #1 } + { + \regex_build_quantifier_end:n { #2 } #1 + #3 #4 + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux]+\regex_build_quantifier_{:w+ ^^A} +% \begin{macro}[aux]{\regex_build_quantifier_lbrace:n} +% \begin{macro}[aux]{\regex_build_quantifier_lbrace:nw} +% \begin{macro}[aux]{\regex_build_quantifier_lbrace:nnw} +% Three possible syntaxes: \texttt{\{\meta{int}\}}, +% \texttt{\{\meta{int},\}}, or \texttt{\{\meta{int},\meta{int}\}}. +% \begin{macrocode} +\cs_new_protected_nopar:cpn { regex_build_quantifier_ \c_lbrace_str :w } + { \regex_get_digits:nw { \regex_build_quantifier_lbrace:n } } +\cs_new_protected_nopar:Npn \regex_build_quantifier_lbrace:n #1 + { + \tl_if_empty:nTF {#1} + { + \regex_build_quantifier_end:n { } + \exp_after:wN \regex_build_raw:N \c_lbrace_str + } + { \regex_build_quantifier_lbrace:nw {#1} } + } +\cs_new_protected_nopar:Npx \regex_build_quantifier_lbrace:nw #1#2#3 + { + \exp_not:N \prg_case_str:nnn { #2 #3 } + { + { \exp_not:N \regex_build_control:N , } + { + \exp_not:N \regex_get_digits:nw + { \exp_not:N \regex_build_quantifier_lbrace:nnw {#1} } + } + { \exp_not:N \regex_build_control:N \c_rbrace_str } + { \exp_not:N \regex_build_quantifier_end:n {n} {#1} } + } + { + \exp_not:N \regex_build_quantifier_end:n { } + \exp_not:N \regex_build_raw:N \c_lbrace_str #1#2 + } + } +\cs_new_protected_nopar:Npn \regex_build_quantifier_lbrace:nnw #1#2#3 + { + \str_if_eq:xxTF { \exp_not:N #3 } { \c_rbrace_str } + { + \tl_if_empty:nTF {#2} + { \regex_build_quantifier_aux:nnN { {#1} {\c_max_int} } {nn} } + { + \int_compare:nNnT {#1} > {#2} + { \regex_build_error:n {4} } + \regex_build_quantifier_aux:nnN { {#1} {#2} } {nn} + } + } + { + \regex_build_quantifier_end:n { } + \use:x + { + \exp_not:n { \exp_args:No \tl_map_function:nN } + { \c_lbrace_str #1 #2 , } + \regex_build_raw:N + } + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[aux]{\regex_build_quantifier_end:n} +% When all quantifiers are found, we will call the relevant +% \cs{regex_build_one/group_\meta{quantifiers}:} function. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_build_quantifier_end:n #1 + { \use:c { regex_build_ \l_regex_one_or_group_tl _ #1 : } } +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Quantifiers for one character or character class} +% +% \begin{macro}[aux]{\regex_build_one_quantifier:} +% Used for one single character, or a character class. +% Contrarily to \cs{regex_build_group_quantifier:}, +% we don't need to keep track of submatches, and no thread +% can be created within one repetition, so things are relatively easy. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_build_one_quantifier: + { + \tl_set:Nx \l_regex_one_or_group_tl { one } + \regex_build_quantifier:w + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux]{\regex_build_one_:} +% If no quantifier is found, then the character or character class +% should just be built into a transition from the current +% \enquote{right} state to a new state. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_build_one_: + { + \regex_build_transition_aux:NN + \regex_build_tmp_class:n \l_regex_right_state_int + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux]{\regex_build_one_?:} +% \begin{macro}[aux]{\regex_build_one_??:} +% The two transitions are a costly transition controlled by +% the character class, and a free transition, both going to +% a common new state. The only difference between the greedy +% and lazy operators is the order of transitions. +% \begin{macrocode} +\cs_new_protected_nopar:cpn { regex_build_one_?: } + { + \regex_build_transitions_aux:NNNN + \regex_build_tmp_class:n \l_regex_right_state_int + \regex_action_free:n \l_regex_right_state_int + } +\cs_new_protected_nopar:cpn { regex_build_one_??: } + { + \regex_build_transitions_aux:NNNN + \regex_action_free:n \l_regex_right_state_int + \regex_build_tmp_class:n \l_regex_right_state_int + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[aux]{\regex_build_one_*:} +% \begin{macro}[aux]{\regex_build_one_*?:} +% Build a costly transition going from the current state to itself, +% and a free transition moving to a new state. +% \begin{macrocode} +\cs_new_protected_nopar:cpn { regex_build_one_*: } + { + \regex_build_transitions_aux:NNNN + \regex_build_tmp_class:n \l_regex_left_state_int + \regex_action_free:n \l_regex_right_state_int + } +\cs_new_protected_nopar:cpn { regex_build_one_*?: } + { + \regex_build_transitions_aux:NNNN + \regex_action_free:n \l_regex_right_state_int + \regex_build_tmp_class:n \l_regex_left_state_int + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[aux]{\regex_build_one_+:} +% \begin{macro}[aux]{\regex_build_one_+?:} +% Build a transition from the current state to a new state, +% controlled by the character class, then build two transitions +% from this new state to the original state (for repetition) +% and to another new state (to move on to the rest of the pattern). +% \begin{macrocode} +\cs_new_protected_nopar:cpn { regex_build_one_+: } + { + \regex_build_one_: + \int_set_eq:NN \l_regex_tmpa_int \l_regex_left_state_int + \regex_build_transitions_aux:NNNN + \regex_action_free:n \l_regex_tmpa_int + \regex_action_free:n \l_regex_right_state_int + } +\cs_new_protected_nopar:cpn { regex_build_one_+?: } + { + \regex_build_one_: + \int_set_eq:NN \l_regex_tmpa_int \l_regex_left_state_int + \regex_build_transitions_aux:NNNN + \regex_action_free:n \l_regex_right_state_int + \regex_action_free:n \l_regex_tmpa_int + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[aux]{\regex_build_one_n:} +% \begin{macro}[aux]{\regex_build_one_n?:} +% This function is called in case the syntax is +% \texttt{\{\meta{int}\}}. Greedy and lazy operators +% are identical, since the number of repetitions is fixed. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_build_one_n: #1 + { + \int_set_eq:NN \l_regex_tmpa_int \l_regex_right_state_int + \regex_build_new_state: + \regex_build_new_state: + \regex_toks_put_right:Nx \l_regex_tmpa_int + { %^^A safe \if nesting? + \exp_not:N \if_num:w #1 > \l_regex_repetition_int + \regex_action_repeat_move:n + { \int_use:N \l_regex_left_state_int } + \exp_not:N \else: + \regex_action_no_repeat_move:n + { \int_use:N \l_regex_right_state_int } + \exp_not:N \fi: + } + \regex_toks_put_right:Nx \l_regex_left_state_int + { + \regex_build_tmp_class:n + { \int_use:N \l_regex_tmpa_int } + } + } +\cs_new_eq:cN { regex_build_one_n?: } \regex_build_one_n: +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[aux]{\regex_build_one_nn:} +% \begin{macro}[aux]{\regex_build_one_nn?:} +% \begin{macro}[aux]{\regex_build_one_nn_aux:nn} +% This function is called when the syntax is either +% \texttt{\{\meta{int},\}} or \texttt{\{\meta{int},\meta{int}\}}. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_build_one_nn: #1#2 + { + \regex_build_one_nn_aux:nn {#1} + { + \exp_not:N \if_num:w #2 > \l_regex_repetition_int + \regex_action_repeat_copy:n + { \int_use:N \l_regex_left_state_int } + \exp_not:N \fi: + \regex_action_no_repeat_move:n + { \int_use:N \l_regex_right_state_int } + } + } +\cs_new_protected_nopar:cpn { regex_build_one_nn?: } #1#2 + { + \regex_build_one_nn_aux:nn {#1} + { + \regex_action_no_repeat_copy:n + { \int_use:N \l_regex_right_state_int } + \exp_not:N \if_num:w #2 > \l_regex_repetition_int + \regex_action_repeat_move:n + { \int_use:N \l_regex_left_state_int } + \exp_not:N \fi: + } + } +\cs_new_protected_nopar:Npn \regex_build_one_nn_aux:nn #1#2 + { + \int_set_eq:NN \l_regex_tmpa_int \l_regex_right_state_int + \regex_build_new_state: + \regex_build_new_state: + \regex_toks_put_right:Nx \l_regex_tmpa_int + { + \exp_not:N \if_num:w #1 > \l_regex_repetition_int + \regex_action_repeat_move:n + { \int_use:N \l_regex_left_state_int } + \exp_not:N \else: + #2 + \exp_not:N \fi: + } + \regex_toks_put_right:Nx \l_regex_left_state_int + { + \regex_build_tmp_class:n + { \int_use:N \l_regex_tmpa_int } + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \subsubsection{Groups and alternation} +% +% We support the syntax \texttt{(\meta{expr1}|\ldots{}%^^A +% |\meta{expr$\sb{n}$})\meta{quantifier}} for alternations. +% +% \begin{macro}[aux]{\regex_build_(:, \regex_build_):} +% \begin{macro}[aux]{\regex_build_open_aux:} +% \begin{macro}[aux]+\regex_build_|:+ +% \begin{macro}[aux]{\regex_build_begin_alternation:, +% \regex_build_end_alternation:} +% Grouping and alternation go together. +% \begin{itemize} +% \item Allocate the next available number for the end vertex +% of the alternation/group and store it on a stack (so that nested +% alternations work). +% \item Put free transitions to separate all cases of the alternation. +% \item Build each branch separately, and merge them to the common +% end-node. +% \item Test for a quantifier, and if needed, transfer the initial +% vertex to a new vertex. +% \end{itemize} +% \begin{macrocode} +\cs_new_protected_nopar:cpn { regex_build_(: } #1#2 + { + \str_if_eq:nnTF { #1 #2 } { \regex_build_control:N ? } + { \regex_build_special_group:NN } + { + \int_incr:N \l_regex_capturing_group_int + \seq_push:Nx \l_regex_capturing_group_seq + { \int_use:N \l_regex_capturing_group_int } + \regex_build_open_aux: + #1 #2 + } + } +\cs_new_protected_nopar:Npn \regex_build_open_aux: + { + \regex_build_new_state: + \seq_push:Nx \l_regex_left_state_seq + { \int_use:N \l_regex_left_state_int } + \seq_push:Nx \l_regex_right_state_seq + { \int_use:N \l_regex_right_state_int } + \regex_build_begin_alternation: + } +\cs_new_protected_nopar:cpn { regex_build_|: } + { + \regex_build_end_alternation: + \regex_build_begin_alternation: + } +\cs_new_protected_nopar:cpn { regex_build_): } + { + \seq_if_empty:NTF \l_regex_capturing_group_seq + { \regex_build_error:n {22} } + { + \regex_build_close_aux: + \regex_build_group_quantifier: + } + } +\cs_new_protected_nopar:Npn \regex_build_close_aux: + { + \regex_build_end_alternation: + \seq_pop:NN \l_regex_left_state_seq \l_regex_tmpa_tl + \int_set:Nn \l_regex_left_state_int \l_regex_tmpa_tl + \seq_pop:NN \l_regex_right_state_seq \l_regex_tmpa_tl + \int_set:Nn \l_regex_right_state_int \l_regex_tmpa_tl + } +% \end{macrocode} +% Building each branch. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_build_begin_alternation: + { + \regex_build_new_state: + \seq_get:NN \l_regex_left_state_seq \l_regex_tmpa_tl + \int_set:Nn \l_regex_left_state_int \l_regex_tmpa_tl + \regex_toks_put_right:Nx \l_regex_left_state_int + { \regex_action_free:n { \int_use:N \l_regex_right_state_int } } + } +\cs_new_protected_nopar:Npn \regex_build_end_alternation: + { + \seq_get:NN \l_regex_right_state_seq \l_regex_tmpa_tl + \regex_toks_put_right:Nx \l_regex_right_state_int + { \regex_action_free:n { \l_regex_tmpa_tl } } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\regex_build_special_group:NN} +% Same method as elsewhere: if the combination |(?#1| ^^A ) +% is known, then use that. Otherwise, treat the question mark +% as if it had been escaped. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_build_special_group:NN #1#2 + { + \cs_if_exist_use:cF { regex_build_special_group_\token_to_str:N #2 : } + { + \regex_build_error:n { -998 } + \regex_build_control:N ( % ) + \regex_build_raw:N ? + #1 #2 + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\regex_build_special_group_::} +% Non-capturing groups are like capturing groups, except that +% we set the group id to \texttt{*}, which will then inhibit +% submatching in \cs{regex_build_group_submatches:NN}. +% The group number is not increased. +% \begin{macrocode} +\cs_new_protected_nopar:cpn { regex_build_special_group_:: } + { + \seq_push:Nx \l_regex_capturing_group_seq { * } + \regex_build_open_aux: + } +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Quantifiers for groups} +% +% \begin{macro}[aux]{\regex_build_group_quantifier:} +% Used for one group. We need to keep track of submatches, +% threads can be created within one repetition, so things are hard. +% The code for the group that was just built starts +% at \cs{l_regex_left_state_int} and ends at +% \cs{l_regex_right_state_int}. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_build_group_quantifier: + { + \tl_set:Nn \l_regex_one_or_group_tl { group } + \regex_build_quantifier:w + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux]{\regex_build_group_submatches:NN} +% Once the quantifier is found by \cs{regex_build_quantifier:w}, +% we insert the code for tracking submatches. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_build_group_submatches:NN #1#2 + { + \seq_pop:NN \l_regex_capturing_group_seq \l_regex_tmpa_tl + \str_if_eq:xxF { \l_regex_tmpa_tl } { * } + { + \regex_toks_put_left:Nx #1 + { \regex_action_submatch:n { \l_regex_tmpa_tl < } } + \regex_toks_put_left:Nx #2 + { \regex_action_submatch:n { \l_regex_tmpa_tl > } } + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux]{\regex_build_group_:} +% When there is no quantifier, the group is simply inserted as is, +% and we only need to track submatches. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_build_group_: + { + \regex_build_group_submatches:NN + \l_regex_left_state_int \l_regex_right_state_int + \regex_build_transition_aux:NN + \regex_action_free:n \l_regex_right_state_int + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux]{\regex_build_group_shift:N} +% Most quantifiers require to add an extra state before the group. +% This is done by shifting the current contents of the \cs{tex_toks:D} +% \cs{l_regex_tmpa_int} to a new state. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_build_group_shift:N #1 + { + \int_set_eq:NN \l_regex_tmpa_int \l_regex_left_state_int + \regex_build_new_state: + \tex_toks:D \l_regex_right_state_int = \tex_toks:D \l_regex_tmpa_int + \use:x + { + \tex_toks:D \l_regex_tmpa_int + { \s_regex_toks #1 { \int_use:N \l_regex_right_state_int } } + } + \regex_build_group_submatches:NN + \l_regex_right_state_int \l_regex_left_state_int + \int_set_eq:NN \l_regex_right_state_int \l_regex_left_state_int + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux]{\regex_build_group_qs_aux:NN} +% \begin{macro}[aux]{\regex_build_group_?:} +% \begin{macro}[aux]{\regex_build_group_??:} +% \begin{macro}[aux]{\regex_build_group_*:} +% \begin{macro}[aux]{\regex_build_group_*?:} +% Shift the state at which the group begins using +% \cs{regex_build_group_shift:N}, then add two transitions. +% The first transition is taken once the group has been +% traversed: in the case of \texttt{?} and \texttt{??}, +% we should exit by going to \cs{l_regex_right_state_int}, +% while for \texttt{*} and \texttt{*?} we loop by going to +% \cs{l_regex_tmpa_int}. +% The second transition corresponds to skipping the group; +% it has lower priority (\texttt{put_right}) for greedy +% operators, and higher priority (\texttt{put_left}) for +% lazy operators. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_build_group_qs_aux:NN #1#2 + { + \regex_build_group_shift:N \regex_action_free:n + \regex_build_transition_aux:NN \regex_action_free:n #1 + #2 \l_regex_tmpa_int + { \regex_action_free:n { \int_use:N \l_regex_right_state_int } } + } +\cs_new_protected_nopar:cpn { regex_build_group_?: } + { + \regex_build_group_qs_aux:NN + \l_regex_right_state_int \regex_toks_put_right:Nx + } +\cs_new_protected_nopar:cpn { regex_build_group_??: } + { + \regex_build_group_qs_aux:NN + \l_regex_right_state_int \regex_toks_put_left:Nx + } +\cs_new_protected_nopar:cpn { regex_build_group_*: } + { + \regex_build_group_qs_aux:NN + \l_regex_tmpa_int \regex_toks_put_right:Nx + } +\cs_new_protected_nopar:cpn { regex_build_group_*?: } + { + \regex_build_group_qs_aux:NN + \l_regex_tmpa_int \regex_toks_put_left:Nx + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[aux]{\regex_build_group_+:} +% \begin{macro}[aux]{\regex_build_group_+?:} +% Insert the submatch tracking code, then add two transitions +% from the current state to the left end of the group (repeating the group), +% and to a new state (to carry on with the rest of the regular expression). +% \begin{macrocode} +\cs_new_protected_nopar:cpn { regex_build_group_+: } + { + \regex_build_group_submatches:NN + \l_regex_left_state_int \l_regex_right_state_int + \int_set_eq:NN \l_regex_tmpa_int \l_regex_left_state_int + \regex_build_transitions_aux:NNNN + \regex_action_free:n \l_regex_tmpa_int + \regex_action_free:n \l_regex_right_state_int + } +\cs_new_protected_nopar:cpn { regex_build_group_+?: } + { + \regex_build_group_submatches:NN + \l_regex_left_state_int \l_regex_right_state_int + \int_set_eq:NN \l_regex_tmpa_int \l_regex_left_state_int + \regex_build_transitions_aux:NNNN + \regex_action_free:n \l_regex_right_state_int + \regex_action_free:n \l_regex_tmpa_int + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[aux]{\regex_build_group_n:} +% \begin{macro}[aux]{\regex_build_group_n?:} +% These functions are called in case the syntax is +% \texttt{\{\meta{int}\}}. Greedy and lazy operators +% are identical, since the number of repetitions is fixed. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_build_group_n: #1 + { + \regex_build_group_shift:N \regex_action_repeat_move:n + \regex_build_transition_aux:NN + \regex_action_free:n \l_regex_tmpa_int + \use:x + { + \tex_toks:D \l_regex_tmpa_int + { + \s_regex_toks + \exp_not:N \if_num:w #1 > \l_regex_repetition_int + \tex_the:D \tex_toks:D \l_regex_tmpa_int + \exp_not:N \else: + \regex_action_no_repeat_move:n + { \int_use:N \l_regex_right_state_int } + \exp_not:N \fi: + } + } + } +\cs_new_eq:cN { regex_build_group_n?: } \regex_build_group_n: +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[aux]{\regex_build_group_nn:} +% \begin{macro}[aux]{\regex_build_group_nn?:} +% These functions are called when the syntax is either +% \texttt{\{\meta{int},\}} or \texttt{\{\meta{int},\meta{int}\}}. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_build_group_nn: #1#2 + { + \regex_build_group_shift:N \regex_action_repeat_move:n + \regex_build_transition_aux:NN + \regex_action_free:n \l_regex_tmpa_int + \use:x + { + \tex_toks:D \l_regex_tmpa_int + { + \s_regex_toks + \exp_not:N \if_num:w #1 > \l_regex_repetition_int + \tex_the:D \tex_toks:D \l_regex_tmpa_int + \exp_not:N \else: + \exp_not:N \if_num:w #2 > \l_regex_repetition_int + \tex_the:D \tex_toks:D \l_regex_tmpa_int + \exp_not:N \fi: + \regex_action_no_repeat_copy:n + { \int_use:N \l_regex_right_state_int } + \exp_not:N \fi: + } + } + } +\cs_new_protected_nopar:cpn { regex_build_group_nn?: } #1#2 + { + \regex_build_group_shift:N \regex_action_repeat_move:n + \regex_build_transition_aux:NN + \regex_action_free:n \l_regex_tmpa_int + \use:x + { + \tex_toks:D \l_regex_tmpa_int + { + \s_regex_toks + \exp_not:N \if_num:w #1 > \l_regex_repetition_int + \tex_the:D \tex_toks:D \l_regex_tmpa_int + \exp_not:N \else: + \regex_action_no_repeat_copy:n + { \int_use:N \l_regex_right_state_int } + \exp_not:N \if_num:w #2 > \l_regex_repetition_int + \tex_the:D \tex_toks:D \l_regex_tmpa_int + \exp_not:N \fi: + \exp_not:N \fi: + } + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \subsection{Matching} +% +% \subsubsection{Use of \TeX{} registers when matching} +% +% The first step in matching a regular expression is to build +% the corresponding NFA and store its states in the \tn{toks} +% registers. Then loop through the query string one character +% (one \enquote{step}) at a time, exploring in parallel every +% possible path through the NFA. We keep track of an array of +% the states currently \enquote{active}. More precisely, +% \tn{skip} registers hold the state numbers to be considered +% when the next character of the string is read. +% +% At every step, we unpack that array of active states and +% empty it. Then loop over all active states, and perform the +% instruction at that state of the NFA. This can involve +% \enquote{free} transitions to other states, or transitions +% which \enquote{consume} the current character. For free +% transitions, the instruction at the new state of the NFA is +% performed. When a transition consumes a character, the new +% state is put in the array of \tn{skip} registers: it will +% be active again when the next character is read. +% +% If two paths through the NFA \enquote{collide} in the sense +% that they reach the same state when reading a given +% character, then any future execution will be identical for +% both. Hence, it is indeed enough to keep track of which +% states are active. [In the presence of back-references, the +% future execution is affected by how the previous match took +% place; this is why we cannot support those non-regular +% features.] +% +% Many of the functions require extracting the submatches for +% the \enquote{best} match. Execution paths through the NFA +% are ordered by precedence: for instance, the regular +% expression \texttt{a?} creates two paths, matching either +% an empty string or a single \texttt{a}; the path matching +% an \texttt{a} has higher precedence. When two paths collide, +% the path with the highest precedence is kept, and the other +% one is discarded. The submatch information for a given path +% is stored at the start of the \tn{toks} register which +% holds the state at which that path currently is. +% +% Deciding to store the submatch information in \tn{toks} +% registers alongside with states of the NFA unfortunately +% implies some shuffling around. The two other options are to +% store the submatch information in one control sequence per +% path, which wastes csnames, or to store all of the submatch +% information in one property list, which turns out to be too +% slow. A tricky aspect of submatch tracking is to know when +% to get rid of submatch information. This naturally happens +% when submatch information is stored in \tn{toks} registers: +% if the information is not moved, it will be overwritten +% later. +% +% The presence of $\epsilon$-transitions (transitions which +% consume no character) leads to potential infinite loops; +% for instance the regular expression |(a??)*| could lead to +% an infinite recursion, where |a??| matches no character, |*| +% loops back to the start of the group, and |a??| matches no +% character again. Therefore, we need to keep track of the +% states of the NFA visited at the current step. More +% precisely, a state is marked as \enquote{visited} if the +% instructions for that state have been inserted in the input +% stream, by setting the corresponding \tn{dimen} register to +% a value which uniquely identifies at which step it was last +% inserted. +% +% The current approach means that stretch and shrink components +% of \tn{skip} registers, +% as well as all \tn{muskip} registers are unused. It could seem that +% \tn{count} registers are also free for use, but we still want to be +% able to safely use integers, which are implemented as \tn{count} +% registers. +% +% \subsubsection{Helpers for running the NFA} +% +% \begin{macro}[aux]{\regex_if_state_free:nT} +% A state is free if it is not marker as taken, namely +% if the corresponding \tn{dimen} register is $0\mathtt{sp}$ +% rather than $1\mathtt{sp}$. +% \begin{macrocode} +\cs_new_protected:Npn \regex_if_state_free:nT #1 + { + \if_num:w \tex_dimen:D #1 = \l_regex_unique_step_int + \exp_after:wN \use_none:n + \else: + \exp_after:wN \use:n + \fi: + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux]{\regex_store_state:n} +% Put the given state in the array of \tn{skip} registers. +% This is done by increasing the pointer +% \cs{l_regex_max_index_int}, and converting the integer +% to a dimension (suitable for a \tn{skip} assignment) in +% scaled points. +% \begin{macrocode} +\cs_new_protected:Npn \regex_store_state:n #1 + { + \int_incr:N \l_regex_max_index_int + \tex_skip:D \l_regex_max_index_int #1 sp \scan_stop: + \regex_store_submatches:n {#1} + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[int]{\regex_state_use:} +% \begin{macro}[int]{\regex_state_use_submatches:} +% \begin{macro}[aux]{\regex_state_use_aux:n} +% \begin{macro}[aux]{\regex_state_use_aux_ii:w} +% Use a given program instruction, unless it has already been +% executed at this step. The \tn{toks} registers begin with +% some submatch information, ignored by \cs{regex_state_use:}, +% but not by \cs{regex_state_use_submatches:}. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_state_use_submatches: + { \regex_state_use_aux:n { } } +\cs_new_protected_nopar:Npn \regex_state_use: + { \regex_state_use_aux:n { \exp_after:wN \regex_state_use_aux_ii:w } } +\cs_new:Npn \regex_state_use_aux_ii:w #1 \s_regex_toks { } +\cs_new_protected:Npn \regex_state_use_aux:n #1 + { + \regex_if_state_free:nT { \l_regex_current_state_int } + { + \tex_dimen:D \l_regex_current_state_int + = \l_regex_unique_step_int sp \scan_stop: + #1 \tex_the:D \tex_toks:D \l_regex_current_state_int \scan_stop: + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \subsubsection{Submatch tracking when running the NFA} +% +% \begin{macro}[int]{\regex_disable_submatches:} +% Some user functions don't require tracking submatches. +% We get a performance improvement by simply defining the +% relevant functions to remove their argument and do nothing +% with it. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_disable_submatches: + { + \cs_set_eq:NN \regex_state_use_submatches: \regex_state_use: + \cs_set_eq:NN \regex_store_submatches:n + \regex_protected_use_none:n + \cs_set_eq:NN \regex_action_submatches:n + \regex_protected_use_none:n + } +\cs_new_protected:Npn \regex_protected_use_none:n #1 { } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[int]{\regex_store_submatches:n} +% \begin{macro}[aux]{\regex_store_submatches_aux:Nw} +% \begin{macro}[aux]{\regex_store_submatches_aux_ii:wnnwwn} +% The submatch information pertaining to one given thread is moved +% from state to state as we execute the NFA. +% \begin{macrocode} +\cs_new_protected:Npn \regex_store_submatches:n #1 + { + \exp_after:wN \regex_store_submatches_aux:Nw + \exp_after:wN \l_regex_current_submatches_prop + \tex_the:D \tex_toks:D #1 \scan_stop: \q_stop + { #1 } + } +\cs_new_protected:Npn \regex_store_submatches_aux:Nw #1 #2 \s_regex_toks + { + \exp_args:No \regex_store_submatches_aux_ii:wnnwwn { #1 } + #2 + \regex_state_submatches:nn { \c_minus_one } { \q_prop } + \s_regex_toks + } +\cs_new_protected:Npn \regex_store_submatches_aux_ii:wnnwwn + #1 \regex_state_submatches:nn #2#3 #4 \s_regex_toks #5 \q_stop #6 + { + \tex_toks:D #6 \exp_after:wN + { + \exp_after:wN \regex_state_submatches:nn \exp_after:wN + { \int_value:w \int_eval:w \l_regex_unique_step_int + \c_one } + { #1 } + \regex_state_submatches:nn {#2} {#3} + \s_regex_toks + #5 + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[aux]{\regex_state_submatches:nn} +% This function is inserted by \cs{regex_store_submatches:n} +% in the \tn{toks} register holding a given state, and it is +% performed when the state is used. +% \begin{macrocode} +\cs_new_protected:Npn \regex_state_submatches:nn #1#2 + { + \if_num:w #1 = \l_regex_unique_step_int + \tl_set:Nn \l_regex_current_submatches_prop {#2} + \fi: + } +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Matching: framework} +% +% \begin{macro}[int]{\regex_match:n} +% Store the query string in \cs{l_regex_query_str}. +% Then reset a few variables which should be set only once, +% before the first match, even in the case of multiple matches. +% Then run the NFA (\cs{regex_match_once:} matches multiple times +% when appropriate). +% \begin{macrocode} +\cs_new_protected:Npn \regex_match:n #1 + { + \tl_set:Nx \l_regex_query_str { \tl_to_other_str:n {#1} } + \regex_match_initial_setup: + \regex_match_once: + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[int]{\regex_match_once:} +% \begin{macro}[aux]{\regex_match_once_aux:} +% After setting up more variables in \cs{regex_match_setup:}, +% skip the \cs{l_regex_start_step_int} first characters of the +% query string, and loop over it. +% If there was a match, use the token list \cs{l_regex_every_match_tl}, +% which may call \cs{regex_match_once:} to achieve multiple matches. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_match_once: + { + \regex_match_setup: + \exp_after:wN \regex_match_once_aux: \l_regex_query_str + \q_recursion_tail \q_recursion_stop + \bool_if:NT \l_regex_success_bool { \l_regex_every_match_tl } + } +\cs_new_protected_nopar:Npn \regex_match_once_aux: + { \str_skip_do:nn { \l_regex_start_step_int } { \regex_match_loop:N } } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[aux]{\regex_match_initial_setup:} +% This function holds the setup that should be done +% only once for one given pattern matching on a given +% string. It is called only once for the whole string. +% On the other hand, \cs{regex_match_setup:} +% is called for every match in the string in case of +% repeated matches, and \cs{regex_match_loop_setup:} +% is called at every step. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_match_initial_setup: + { + \tl_clear:N \l_regex_look_behind_str + \prg_stepwise_inline:nnnn {1} {1} { \l_regex_max_state_int } + { \tex_dimen:D ##1 \c_minus_one sp \scan_stop: } + \int_set_eq:NN \l_regex_unique_step_int \c_minus_one + \int_set_eq:NN \l_regex_start_step_int \c_minus_one + \int_set_eq:NN \l_regex_current_step_int \c_zero + \int_set_eq:NN \l_regex_success_step_int \c_zero + \bool_set_false:N \l_regex_success_empty_bool + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux]{\regex_match_setup:} +% Every time a match starts, \cs{regex_match_setup:} resets +% a few variables. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_match_setup: + { + \prop_clear:N \l_regex_current_submatches_prop + \bool_if:NTF \l_regex_success_empty_bool + { \cs_set_eq:NN \regex_last_match_empty:F \regex_last_match_empty_yes:F } + { \cs_set_eq:NN \regex_last_match_empty:F \regex_last_match_empty_no:F } + \int_set_eq:NN \l_regex_start_step_int \l_regex_success_step_int + \int_set_eq:NN \l_regex_current_step_int \l_regex_start_step_int + \int_decr:N \l_regex_current_step_int + \bool_set_false:N \l_regex_success_bool + \int_zero:N \l_regex_max_index_int + \regex_store_state:n {1} + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux]{\regex_match_loop_setup:} +% This is called for every character in the string. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_match_loop_setup: + { + \int_incr:N \l_regex_current_step_int + \int_incr:N \l_regex_unique_step_int + \bool_set_false:N \l_regex_fresh_thread_bool + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[int]{\s_regex_step} +% \begin{macro}[aux]{\regex_break_step:w} +% When a thread succeeds, all threads with lower precedence +% can be ignored, and the next character should be read. +% This is done by skipping to \cs{s_regex_step}. This marker +% does nothing if no thread succeeded at this step. +% \begin{macrocode} +\cs_new_eq:NN \s_regex_step \scan_stop: +\cs_new:Npn \regex_break_step:w #1 \s_regex_step { } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[aux]{\regex_match_loop:N} +% \begin{macro}[aux]{\regex_match_one_index:n} +% \begin{macro}[aux]{\regex_match_one_index_aux:nn} +% Setup what needs to be reset at every character, +% then set \cs{l_regex_current_char_int} to the +% character code of the character that is read +% (and $-1$ for the end of the string), and loop +% over the elements of the \tn{skip} array. Then repeat. +% There are a couple of tests to stop reading the string +% when no active state is left, or when the end is reached. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_match_loop:N #1 + { + \regex_match_loop_setup: + \token_if_eq_meaning:NNTF #1 \q_recursion_tail + { \int_set_eq:NN \l_regex_current_char_int \c_minus_one } + { \int_set:Nn \l_regex_current_char_int {`#1} } + \cs_set_nopar:Npx \regex_tmp:w + { + \int_zero:N \l_regex_max_index_int + \prg_stepwise_function:nnnN + {1} {1} { \l_regex_max_index_int } + \regex_match_one_index:n + } + \regex_tmp:w + \s_regex_step + \if_num:w \l_regex_max_index_int = \c_zero + \exp_after:wN \use_none_delimit_by_q_recursion_stop:w + \fi: + \quark_if_recursion_tail_stop:N #1 + \bool_if:NT \l_regex_look_behind_bool + { \tl_put_left:Nx \l_regex_look_behind_str {#1} } + \regex_match_loop:N + } +\cs_new_nopar:Npn \regex_match_one_index:n #1 + { + \regex_match_one_index_aux:n { \int_value:w \tex_skip:D #1 } + } +\cs_new_protected_nopar:Npn \regex_match_one_index_aux:n #1 + { + \int_set:Nn \l_regex_current_state_int {#1} + \prop_clear:N \l_regex_current_submatches_prop + \regex_state_use_submatches: + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \subsubsection{Actions when matching} +% +% \begin{macro}[aux]{\regex_action_start_wildcard:nn} +% The search is made unanchored at the start by putting +% a free transition to the real start of the NFA, and a +% costly transition to the same state, waiting for the +% next character in the query string. This combination +% could be reused (with some changes). We sometimes need +% to know that the match for a given thread starts at +% this character. For that, we use the boolean +% \cs{l_regex_fresh_thread_bool}. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_action_start_wildcard:nn #1#2 + { + \bool_set_true:N \l_regex_fresh_thread_bool + \regex_action_free:n {#2} + \bool_set_false:N \l_regex_fresh_thread_bool + \regex_action_cost:n {#1} + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux]{\regex_action_cost:n} +% A transition which consumes the current character and moves +% to state |#1|. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_action_cost:n #1 + { \regex_store_state:n {#1} } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux]{\regex_action_success:} +% There is a successful match when an execution path reaches +% the end of the regular expression. Then store the current +% step and submatches. The current step is then interrupted, +% and only paths with higher precedence are pursued further. +% The values stored here may be overwritten by a later success +% of a path with higher precedence. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_action_success: + { + \regex_last_match_empty:F + { + \bool_set_true:N \l_regex_success_bool + \bool_set_eq:NN \l_regex_success_empty_bool + \l_regex_fresh_thread_bool + \int_set_eq:NN \l_regex_success_step_int + \l_regex_current_step_int + \prop_set_eq:NN \l_regex_success_submatches_prop + \l_regex_current_submatches_prop + \regex_break_step:w + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux]{\regex_action_free:n} +% To copy a thread, check whether the program state has already +% been used at this character. If not, store submatches in the +% new state, and insert the instructions for that state in the +% input stream. +% Then restore the old value of \cs{l_regex_current_state_int} +% and of the current submatches. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_action_free:n #1 + { + \regex_if_state_free:nT {#1} + { + \use:x + { + \int_set:Nn \l_regex_current_state_int {#1} + \regex_store_submatches:n { \l_regex_current_state_int } + \regex_state_use: + \int_set:Nn \l_regex_current_state_int + { \int_use:N \l_regex_current_state_int } + \tl_set:Nn \exp_not:N \l_regex_current_submatches_prop + { \exp_not:o \l_regex_current_submatches_prop } + } + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux]{\regex_action_submatch:n} +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_action_submatch:n #1 + { + \prop_put:Nno \l_regex_current_submatches_prop {#1} + { \int_use:N \l_regex_current_step_int } + \regex_store_submatches:n { \l_regex_current_state_int } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_action_repeat_copy:n { \ERROR } +\cs_new_protected_nopar:Npn \regex_action_repeat_move:n { \ERROR } +\cs_new_protected_nopar:Npn \regex_action_no_repeat_copy:n { \ERROR } +\cs_new_protected_nopar:Npn \regex_action_no_repeat_move:n { \ERROR } +% \end{macrocode} +% +% \subsection{Submatches, once the correct match is found} +% +% \begin{macro}[int]{\regex_extract:} +% \begin{macro}[aux]{\regex_extract_aux:nTF} +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_extract: + { + \seq_gclear:N \g_regex_submatches_seq + \prg_stepwise_inline:nnnn + {0} {1} { \l_regex_capturing_group_int } + { + \regex_extract_aux:nTF { ##1 } + { + \seq_gput_right:Nx \g_regex_submatches_seq + { + \str_from_to:Nnn \l_regex_query_str + { \l_regex_tmpa_tl } + { \l_regex_tmpb_tl } + } + } + { \seq_gput_right:Nn \g_regex_submatches_seq { } } + } + } +\cs_new_protected_nopar:Npn \regex_extract_aux:nTF #1#2#3 + { + \prop_get:NnNTF \l_regex_success_submatches_prop + { #1 < } \l_regex_tmpa_tl + { + \prop_get:NnNTF \l_regex_success_submatches_prop + { #1 > } \l_regex_tmpb_tl + {#2} + {#3} + } + {#3} + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \subsection{User commands} +% +% \subsubsection{Precompiled pattern} +% +% A given pattern is often reused to match many different strings. +% We thus give a means of storing the NFA corresponding to a given +% pattern in a token list variable of the form +% \begin{quote} +% \cs{regex_nfa:Nw} \meta{variable~name} \\ +% \meta{assignments} \\ +% \cs{tex_toks:D} 0 \{ \meta{instruction0} \} \\ +% \ldots{} \\ +% \cs{tex_toks:D} $n$ \{ \meta{instruction$\sb{n}$} \} \\ +% \cs{regex_nfa_end:} +% \end{quote} +% where $n$ is the number of states in the NFA, +% and the various \meta{instruction$\sb{i}$} control +% how the NFA behaves in state $i$. The \cs{regex_nfa:Nw} +% function removes the whole NFA from the input stream +% and produces an error: the \meta{nfa var} should only be +% accessed through dedicated functions. This rather drastic +% approach is taken because assignments triggered by the +% contents of \meta{nfa var} may overwrite data which is used +% elsewhere, unless everything is done carefully in a group. +% +% \begin{macro}{\regex_set:Nn} +% \begin{macro}{\regex_gset:Nn} +% \begin{macro}[aux]{\regex_set_aux:NNn} +% \begin{macro}[aux]{\regex_set_aux:n} +% \begin{macro}[aux]{\regex_nfa:Nw} +% \begin{macro}[aux]{\regex_nfa_end:} +% Within a group, build the NFA corresponding to the given regular +% expression, with submatch tracking. Then save the contents of all +% relevant \tn{toks} registers into \cs{g_regex_tmpa_tl}, then +% transferred to the user's tl variable. +% The auxiliary \cs{regex_nfa:Nw} is not protected: this ensures that +% the NFA will properly be replaced by an error message in expansion +% contexts. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_set:Nn + { \regex_set_aux:NNn \tl_set_eq:NN } +\cs_new_protected_nopar:Npn \regex_gset:Nn + { \regex_set_aux:NNn \tl_gset_eq:NN } +\cs_new_protected:Npn \regex_set_aux:NNn #1#2#3 + { + \group_begin: + \regex_build:n {#3} + \tl_gset:Nx \g_regex_tmpa_tl + { + \exp_not:N \regex_nfa:Nw \exp_not:N #2 + \l_regex_max_state_int + = \int_use:N \l_regex_max_state_int + \l_regex_capturing_group_int + = \int_use:N \l_regex_capturing_group_int + \bool_if:NTF \l_regex_look_behind_bool + { \bool_set_true:N \l_regex_look_behind_bool } + { \bool_set_false:N \l_regex_look_behind_bool } + \prg_stepwise_function:nnnN + {1} {1} {\l_regex_max_state_int} + \regex_set_aux:n + \regex_nfa_end: + } + \group_end: + #1 #2 \g_regex_tmpa_tl + } +\cs_new_nopar:Npn \regex_set_aux:n #1 + { \tex_toks:D #1 { \tex_the:D \tex_toks:D #1 } } +\cs_new:Npn \regex_nfa:Nw #1 #2 \regex_nfa_end: + { \msg_expandable_error:n { Automaton~#1 used~incorrectly. } } +\cs_new_eq:NN \regex_nfa_end: \scan_stop: +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[int,TF]{\regex_check_nfa:N} +% If a token list variable starts with \cs{regex_nfa:Nw}, +% then it most likely holds the data for a precompiled pattern. +% \begin{macrocode} +\prg_new_protected_conditional:Npnn \regex_check_nfa:N #1 { TF } + { \exp_after:wN \regex_check_nfa_aux:Nw #1 \q_stop } +\cs_new:Npn \regex_check_nfa_aux:Nw #1 #2 \q_stop + { + \if_meaning:w \regex_nfa:Nw #1 + \prg_return_true: + \else: + \msg_error:nnx { regex } { not-nfa } { \token_to_str:N #1 } + \prg_return_false: + \fi: + } +\msg_new:nnn { regex } { not-nfa } + { + I~was~expecting~a~regular~expression~variable.\\ + Instead,~I~got~#1. + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[int]{\regex_use:N} +% No error-checking. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_use:N #1 + { \exp_after:wN \use_none:nn #1 } +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Generic auxiliary functions} +% +% \begin{macro}[aux]{\regex_user_aux:n} +% This is an auxiliary used by most user functions. +% The first and second arguments control whether we should track +% submatches, and whether we track one or multiple submatches. +% Everything is done within a group, so that |#1| can perform +% \enquote{unsafe} assignments. Most user functions return a +% result using \cs{group_insert_after:N}. +% \begin{macrocode} +\cs_new_protected:Npn \regex_user_aux:n #1 + { + \group_begin: + \tl_clear:N \l_regex_every_match_tl + #1 + \group_end: + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux]{\regex_return_after_group:} +% Most of \pkg{l3regex}'s work is done within a group. +% This function triggers either \cs{prg_return_false:} +% or \cs{prg_return_true:} as appropriate to whether a +% match was found or not. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_return_after_group: + { + \if_bool:N \l_regex_success_bool + \group_insert_after:N \prg_return_true: + \else: + \group_insert_after:N \prg_return_false: + \fi: + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux]{\regex_extract_after_group:N} +% Extract submatches, and store them in the user-given variable +% after the group has ended. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_extract_after_group:N #1 + { + \if_bool:N \l_regex_success_bool + \regex_extract: + \group_insert_after:N \seq_set_eq:NN + \group_insert_after:N #1 + \group_insert_after:N \g_regex_submatches_seq + \fi: + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux]{\regex_count_after_group:N} +% Same procedure as \cs{regex_extract_after_group:N}, but simpler +% since the match counting has already taken place. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_count_after_group:N #1 + { + \group_insert_after:N \int_set_eq:NN + \group_insert_after:N #1 + \group_insert_after:N \g_regex_match_count_int + } +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Matching} +% +% \begin{macro}[TF]{\regex_match:nn} +% \begin{macro}[TF]{\regex_match:Nn} +% We don't track submatches. Then either build the NFA corresponding +% to the regular expression, or use a precompiled pattern. Then match, +% using the internal \cs{regex_match:n}. Finally return the result +% after closing the group. +% \begin{macrocode} +\prg_new_protected_conditional:Npnn \regex_match:nn #1#2 { T , F , TF } + { + \regex_user_aux:n + { + \regex_disable_submatches: + \regex_build:n {#1} + \regex_match:n {#2} + \regex_return_after_group: + } + } +\prg_new_protected_conditional:Npnn \regex_match:Nn #1#2 { T , F , TF } + { + \regex_check_nfa:NTF #1 + { + \regex_user_aux:n + { + \regex_disable_submatches: + \regex_use:N #1 + \regex_match:n {#2} + \regex_return_after_group: + } + } + { \prg_return_false: } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\regex_count:nnN} +% \begin{macro}{\regex_count:NnN} +% Instead of aborting once the first \enquote{best match} is found, +% we repeat the search. The code is such that the search will not +% start on the same character, hence avoiding infinite loops. +% \begin{macrocode} +\cs_new_protected:Npn \regex_count:nnN #1#2#3 + { + \regex_user_aux:n + { + \regex_disable_submatches: + \int_gzero:N \g_regex_match_count_int + \tl_set:Nn \l_regex_every_match_tl + { + \int_gincr:N \g_regex_match_count_int + \regex_match_once: + } + \regex_build:n {#1} + \regex_match:n {#2} + \regex_count_after_group:N #3 + } + } +\cs_new_protected:Npn \regex_count:NnN #1#2#3 + { + \regex_check_nfa:NTF #1 + { + \regex_user_aux:n + { + \regex_disable_submatches: + \int_gzero:N \g_regex_match_count_int + \tl_set:Nn \l_regex_every_match_tl + { + \int_gincr:N \g_regex_match_count_int + \regex_match_once: + } + \regex_use:N #1 + \regex_match:n {#2} + \regex_count_after_group:N #3 + } + } + { } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \subsubsection{Submatch extraction} +% +% \begin{macro}{\regex_extract:nnN} +% \begin{macro}{\regex_extract:NnN} +% \begin{macro}[TF]{\regex_extract:nnN} +% \begin{macro}[TF]{\regex_extract:NnN} +% \begin{macrocode} +\cs_new_protected:Npn \regex_extract:nnN #1#2#3 + { + \regex_user_aux:n + { + \regex_build:n {#1} + \regex_match:n {#2} + \regex_extract_after_group:N #3 + } + } +\prg_new_protected_conditional:Npnn \regex_extract:nnN #1#2#3 { T , F , TF } + { + \regex_user_aux:n + { + \regex_build:n {#1} + \regex_match:n {#2} + \regex_extract_after_group:N #3 + \regex_return_after_group: + } + } +\cs_new_protected:Npn \regex_extract:NnN #1#2#3 + { + \regex_check_nfa:NTF #1 + { + \regex_user_aux:n + { + \regex_use:N #1 + \regex_match:n {#2} + \regex_extract_after_group:N #3 + } + } + { } + } +\prg_new_protected_conditional:Npnn \regex_extract:NnN #1#2#3 { T , F , TF } + { + \regex_check_nfa:NTF #1 + { + \regex_user_aux:n + { + \regex_use:N #1 + \regex_match:n {#2} + \regex_extract_after_group:N #3 + \regex_return_after_group: + } + } + { \prg_return_false: } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \subsubsection{Splitting a string by matches of a regex} +% +% \begin{macro}{\regex_split:nnN} +% \begin{macro}{\regex_split:NnN} +% \begin{macro}[aux]{\regex_split_aux:} +% \begin{macro}[aux]{\regex_split_after_group:N} +% Similarly to \cs{regex_count:nnN} functions, +% recurse through matches of the pattern. Then we do +% something slightly different, extracting submatches. +% Submatches are not extracted if the pattern matched +% an empty string at the start of the match attempt +% (to avoid adding spurious empty items to the resulting +% sequence). +% \begin{macrocode} +\cs_new_protected:Npn \regex_split:nnN #1#2#3 + { + \regex_user_aux:n + { + \seq_gclear:N \g_regex_split_seq + \tl_set:Nn \l_regex_every_match_tl { \regex_split_aux: } + \regex_build:n {#1} + \regex_match:n {#2} + \regex_split_after_group:N #3 + } + } +\cs_new_protected:Npn \regex_split:NnN #1#2#3 + { + \regex_check_nfa:NTF #1 + { + \regex_user_aux:n + { + \seq_gclear:N \g_regex_split_seq + \tl_set:Nn \l_regex_every_match_tl { \regex_split_aux: } + \regex_use:N #1 + \regex_match:n {#2} + \regex_split_after_group:N #3 + } + } + { } + } +\cs_new_protected_nopar:Npn \regex_split_aux: + { + \int_compare:nNnF \l_regex_start_step_int = \l_regex_success_step_int + { + \regex_extract: + \seq_pop:NN \g_regex_submatches_seq \l_regex_tmpa_tl + \regex_extract_aux:nTF {0} + { + \seq_gput_left:Nx \g_regex_submatches_seq + { + \str_from_to:Nnn \l_regex_query_str + { \int_use:N \l_regex_start_step_int } + { \l_regex_tmpa_tl } + } + } + { \msg_error:nn { regex } { internal } } + \seq_gconcat:NNN \g_regex_split_seq + \g_regex_split_seq \g_regex_submatches_seq + } + \regex_match_once: + } +\cs_new_protected_nopar:Npn \regex_split_after_group:N #1 + { + \int_compare:nNnTF \l_regex_start_step_int = \l_regex_current_step_int + { + \bool_if:NF \l_regex_success_empty_bool + { \seq_gput_right:Nn \g_regex_split_seq { } } + } + { + \seq_gput_right:Nx \g_regex_split_seq + { + \str_from_to:Nnn \l_regex_query_str + { \int_use:N \l_regex_start_step_int } + { \int_use:N \l_regex_current_step_int } + } + } + \group_insert_after:N \seq_set_eq:NN + \group_insert_after:N #1 + \group_insert_after:N \g_regex_split_seq + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \subsubsection{String replacement} +% +% \begin{macro}[int]{\regex_replacement:n} +% \begin{macro}[aux]{\regex_replacement_loop:N} +% \begin{macro}[aux]{\regex_replacement_normal:N} +% \begin{macrocode} +\cs_new_protected:Npn \regex_replacement:n #1 + { + \str_aux_escape:NNNn + \prg_do_nothing: + \regex_replacement_escaped:N + \regex_replacement_raw:N + {#1} + \tl_set_eq:NN \l_regex_replacement_tl \g_str_result_tl + \tl_set:Nx \l_regex_replacement_tl + { \l_regex_replacement_tl \prg_do_nothing: } + } +\cs_new_nopar:Npn \regex_replacement_raw:N { \exp_not:N \prg_do_nothing: } +\cs_new_nopar:Npn \regex_replacement_escaped:N #1 + { + \if_num:w 9 < 1 #1 \exp_stop_f: + \exp_not:N \regex_replacement_escaped_ii:nN {#1} + \else: + #1 + \fi: + } +\cs_new_nopar:Npn \regex_replacement_escaped_ii:nN #1#2 + { + \regex_token_if_other_digit:NTF #2 + { \regex_replacement_escaped_ii:nN {#1#2} } + { \exp_not:N \seq_item:Nn \exp_not:N \g_regex_submatches_seq {#1} #2 } + } +\prg_new_conditional:Npnn \regex_token_if_other_digit:N #1 { TF } + { + \if_num:w 9 < 1 \exp_not:N #1 \exp_stop_f: + \prg_return_true: + \else: + \prg_return_false: + \fi: + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[aux]{\regex_replace_after_group:N} +% \begin{macrocode} +\cs_new_protected_nopar:Npn \regex_replace_after_group:N #1 + { + \group_insert_after:N \tl_set_eq:NN + \group_insert_after:N #1 + \group_insert_after:N \g_regex_replaced_str + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\regex_replace_once:nnN} +% \begin{macro}{\regex_replace_once:NnN} +% \begin{macro}[TF]{\regex_replace_once:nnN} +% \begin{macro}[TF]{\regex_replace_once:NnN} +% \begin{macro}[aux]{\regex_replace_once_aux:Nn} +% \begin{macrocode} +\cs_new_protected:Npn \regex_replace_once_aux:Nn #1#2 + { + \group_begin: + \regex_replace_after_group:N #1 + \tl_clear:N \l_regex_every_match_tl + #2 + \exp_args:No \regex_match:n {#1} + \regex_extract: + \regex_extract_aux:nTF {0} + { + \tl_gset:Nx \g_regex_replaced_str + { + \str_from_to:Nnn \l_regex_query_str {0} { \l_regex_tmpa_tl } + \l_regex_replacement_tl + \str_from_to:Nnn \l_regex_query_str + { \l_regex_tmpb_tl } { \c_max_int } + } + } + { \tl_gset_eq:NN \g_regex_replaced_str \l_regex_query_str } + \group_end: + } +\cs_new_protected:Npn \regex_replace_once:nnN #1#2#3 + { + \regex_replace_once_aux:Nn #3 + { + \regex_build:n {#1} + \regex_replacement:n {#2} + } + } +\cs_new_protected:Npn \regex_replace_once:NnN #1#2#3 + { + \regex_replace_once_aux:Nn #3 + { + \regex_use:N #1 + \regex_replacement:n {#2} + } + } +\prg_new_protected_conditional:Npnn \regex_replace_once:nnN #1#2#3 {T,F,TF} + { + \regex_replace_once_aux:Nn #3 + { + \regex_build:n {#1} + \regex_replacement:n {#2} + \regex_return_after_group: + } + } +\prg_new_protected_conditional:Npnn \regex_replace_once:NnN #1#2#3 {T,F,TF} + { + \regex_replace_once_aux:Nn #3 + { + \regex_use:N #1 + \regex_replacement:n {#2} + \regex_return_after_group: + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\regex_replace_all:nnN} +% \begin{macro}{\regex_replace_all:NnN} +% \begin{macro}[aux]{\regex_replace_all_aux:Nn} +% \begin{macro}[aux]{\regex_replace_all_aux:} +% \begin{macrocode} +\cs_new_protected:Npn \regex_replace_all_aux:Nn #1#2 + { + \group_begin: + \regex_replace_after_group:N #1 + \tl_set:Nn \l_regex_every_match_tl { \regex_replace_all_aux: } + \tl_gclear:N \g_regex_replaced_str + #2 + \exp_args:No \regex_match:n {#1} + \tl_gput_right:Nx \g_regex_replaced_str + { + \str_from_to:Nnn \l_regex_query_str + { \int_use:N \l_regex_start_step_int } + { \c_max_int } + } + \group_end: + } +\cs_new_protected_nopar:Npn \regex_replace_all_aux: + { + \regex_extract: + \regex_extract_aux:nTF {0} + { + \tl_gput_right:Nx \g_regex_replaced_str + { + \str_from_to:Nnn \l_regex_query_str + { \int_use:N \l_regex_start_step_int } + { \l_regex_tmpa_tl } + \l_regex_replacement_tl + } + } + { \msg_error:nn { regex } { internal } } + \regex_match_once: + } +\cs_new_protected:Npn \regex_replace_all:nnN #1#2#3 + { + \regex_replace_all_aux:Nn #3 + { + \regex_build:n {#1} + \regex_replacement:n {#2} + } + } +\cs_new_protected:Npn \regex_replace_all:NnN #1#2#3 + { + \regex_replace_all_aux:Nn #3 + { + \regex_use:N #1 + \regex_replacement:n {#2} + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macrocode} +% +% \end{macrocode} +% +% \end{implementation} +% +% \endinput +%^^A NOT IMPLEMENTED +%^^A \cx "control-x", where x is any ASCII character +%^^A \C one byte, even in UTF-8 mode (best avoided) +%^^A \p{xx} a character with the xx property +%^^A \P{xx} a character without the xx property +%^^A \R a newline sequence +%^^A \X an extended Unicode sequence +%^^A [[:xxx:]] positive POSIX named set +%^^A [[:^xxx:]] negative POSIX named set +%^^A ?+ 0 or 1, possessive +%^^A *+ 0 or more, possessive +%^^A ++ 1 or more, possessive +%^^A {n,m}+ at least n, no more than m, possessive +%^^A {n,}+ n or more, possessive +%^^A \K reset start of match +%^^A (?...) named capturing group (Perl) +%^^A (?'name'...) named capturing group (Perl) +%^^A (?P...) named capturing group (Python) +%^^A (?:...) non-capturing group +%^^A (?|...) non-capturing group; reset group numbers for +%^^A capturing groups in each alternative +%^^A (?>...) atomic, non-capturing group +%^^A (?#....) comment (not nestable) +%^^A (?i) caseless +%^^A (?J) allow duplicate names +%^^A (?m) multiline +%^^A (?s) single line (dotall) +%^^A (?U) default ungreedy (lazy) +%^^A (?x) extended (ignore white space) +%^^A (?-...) unset option(s) +%^^A (*NO_START_OPT) no start-match optimization (PCRE_NO_START_OPTIMIZE) +%^^A (*UTF8) set UTF-8 mode (PCRE_UTF8) +%^^A (*UCP) set PCRE_UCP (use Unicode properties for \d etc) +%^^A (?=...) positive look ahead +%^^A (?!...) negative look ahead +%^^A (?<=...) positive look behind +%^^A (? reference by name (Perl) +%^^A \k'name' reference by name (Perl) +%^^A \g{name} reference by name (Perl) +%^^A \k{name} reference by name (.NET) +%^^A (?P=name) reference by name (Python) +%^^A (?R) recurse whole pattern +%^^A (?n) call subpattern by absolute number +%^^A (?+n) call subpattern by relative number +%^^A (?-n) call subpattern by relative number +%^^A (?&name) call subpattern by name (Perl) +%^^A (?P>name) call subpattern by name (Python) +%^^A \g call subpattern by name (Oniguruma) +%^^A \g'name' call subpattern by name (Oniguruma) +%^^A \g call subpattern by absolute number (Oniguruma) +%^^A \g'n' call subpattern by absolute number (Oniguruma) +%^^A \g<+n> call subpattern by relative number (PCRE extension) +%^^A \g'+n' call subpattern by relative number (PCRE extension) +%^^A \g<-n> call subpattern by relative number (PCRE extension) +%^^A \g'-n' call subpattern by relative number (PCRE extension) +%^^A (?(n)... absolute reference condition +%^^A (?(+n)... relative reference condition +%^^A (?(-n)... relative reference condition +%^^A (?()... named reference condition (Perl) +%^^A (?('name')... named reference condition (Perl) +%^^A (?(name)... named reference condition (PCRE) +%^^A (?(R)... overall recursion condition +%^^A (?(Rn)... specific group recursion condition +%^^A (?(R&name)... specific recursion condition +%^^A (?(DEFINE)... define subpattern for reference +%^^A (?(assert)... assertion condition +%^^A (*ACCEPT) force successful match +%^^A (*FAIL) force backtrack; synonym (*F) +%^^A (*COMMIT) overall failure, no advance of starting point +%^^A (*PRUNE) advance to next starting character +%^^A (*SKIP) advance start to current matching position +%^^A (*THEN) local failure, backtrack to next alternation +%^^A (*CR) carriage return only +%^^A (*LF) linefeed only +%^^A (*CRLF) carriage return followed by linefeed +%^^A (*ANYCRLF) all three of the above +%^^A (*ANY) any Unicode newline sequence +%^^A (*BSR_ANYCRLF) CR, LF, or CRLF +%^^A (*BSR_UNICODE) any Unicode newline sequence +%^^A (?C) callout +%^^A (?Cn) callout with data n \ No newline at end of file diff --git a/Master/texmf-dist/source/latex/l3experimental/l3str/l3str.dtx b/Master/texmf-dist/source/latex/l3experimental/l3str/l3str.dtx new file mode 100644 index 00000000000..a718f986ce7 --- /dev/null +++ b/Master/texmf-dist/source/latex/l3experimental/l3str/l3str.dtx @@ -0,0 +1,1966 @@ +% \iffalse meta-comment +% +%% File: l3str.dtx Copyright (C) 2011 The LaTeX3 Project +%% +%% It may be distributed and/or modified under the conditions of the +%% LaTeX Project Public License (LPPL), either version 1.3c of this +%% license or (at your option) any later version. The latest version +%% of this license is in the file +%% +%% http://www.latex-project.org/lppl.txt +%% +%% This file is part of the "l3experimental bundle" (The Work in LPPL) +%% and all files in that bundle must be distributed together. +%% +%% The released version of this bundle is available from CTAN. +%% +%% ----------------------------------------------------------------------- +%% +%% The development version of the bundle can be found at +%% +%% http://www.latex-project.org/svnroot/experimental/trunk/ +%% +%% for those people who are interested. +%% +%%%%%%%%%%% +%% NOTE: %% +%%%%%%%%%%% +%% +%% Snapshots taken from the repository represent work in progress and may +%% not work or may contain conflicting material! We therefore ask +%% people _not_ to put them into distributions, archives, etc. without +%% prior consultation with the LaTeX3 Project. +%% +%% ----------------------------------------------------------------------- +% +%<*driver|package> +\RequirePackage{expl3} +\GetIdInfo$Id: l3str.dtx 2895 2011-10-09 15:58:19Z joseph $ + {L3 Experimental Strings} +% +%<*driver> +\documentclass[full]{l3doc} +\usepackage{amsmath} +\begin{document} + \DocInput{\jobname.dtx} +\end{document} +% +% \fi +% +% +% \title{^^A +% The \textsf{l3str} package: manipulating strings of characters^^A +% \thanks{This file describes v\ExplFileVersion, +% last revised \ExplFileDate.}^^A +% } +% +% \author{^^A +% The \LaTeX3 Project\thanks +% {^^A +% E-mail: +% \href{mailto:latex-team@latex-project.org} +% {latex-team@latex-project.org}^^A +% }^^A +% } +% +% \date{Released \ExplFileDate} +% +% \maketitle +% +% \begin{documentation} +% +% \LaTeX3 provides a set of functions to manipulate token lists +% as strings of characters, ignoring the category codes of those +% characters. +% +% There is no separate string data type: string variables are simply +% specialized token lists, conventionally made out only of characters +% with category code $12$ (other), except spaces, which have category +% code $10$ (blank space). All the \enquote{safe} functions in this +% module first convert their argument to a string for internal processing, +% and will not treat a token list or the corresponding string representation +% differently. However, it is often better to name token list variables +% meant to contain string data with a name such as \cs{l_\ldots{}_str}. +% Those are then manipulated mostly with tools from \pkg{l3tl}. +% +% Most functions in this module come in three flavours: +% \begin{itemize} +% \item \cs{str_...:N...}, which expect a token list +% variable as their argument; +% \item \cs{str_...:n...}, taking any token list (or string) as an argument; +% \item \cs{str_..._ignore_spaces:n...}, which ignores any space encountered +% during the operation: these functions are slightly faster than those +% which take care of escaping spaces appropriately; +% \end{itemize} +% When performance is important, the internal \cs{str_..._unsafe:n...} +% functions, which expect a \enquote{safe} string in which spaces +% have category code $12$ instead of $10$, might be useful. +% +% \subsection{Conversion and input of strings} +% +% \begin{variable}{\c_backslash_str, +% \c_lbrace_str,\c_rbrace_str,\c_hash_str,\c_percent_str} +% Constant strings, containing a single character, with category code $12$. +% \end{variable} +% +% \begin{function}[EXP]{\tl_to_str:N,\tl_to_str:n} +% \begin{syntax} +% \cs{tl_to_str:N} \meta{tl var} +% \cs{tl_to_str:n} \Arg{token list} +% \end{syntax} +% Converts the \meta{token list} to a \meta{string}. +% \end{function} +% +% \begin{function} +% { +% \str_set:Nn, \str_set:Nx, +% \str_set:cn, \str_set:cx, +% \str_gset:Nn, \str_gset:Nx, +% \str_gset:cn, \str_gset:cx, +% } +% \begin{function} +% { +% \str_put_left:Nn, \str_put_left:Nx, +% \str_put_left:cn, \str_put_left:cx, +% \str_gput_left:Nn, \str_gput_left:Nx, +% \str_gput_left:cn, \str_gput_left:cx, +% } +% \begin{function} +% { +% \str_put_right:Nn, \str_put_right:Nx, +% \str_put_right:cn, \str_put_right:cx, +% \str_gput_right:Nn, \str_gput_right:Nx, +% \str_gput_right:cn, \str_gput_right:cx, +% } +% \begin{syntax} +% \cs{str_set:Nn} \meta{str var} \Arg{token list} +% \end{syntax} +% Converts the \meta{token list} to a \meta{string}, +% and saves the result in \meta{str var}. +% \end{function} +% \end{function} +% \end{function} +% +% \begin{function}{\str_input:Nn,\str_ginput:Nn} +% \begin{syntax} +% \cs{str_input:Nn} \meta{str var} \Arg{token list} +% \end{syntax} +% Converts the \meta{token list} into a \meta{string}, and stores +% it in the \meta{str var}. Special characters can be input by +% escaping them with a backslash. +% \begin{itemize} +% \item Spaces are ignored unless escaped with a backslash. +% \item |\xhh| produces the character with code \texttt{hh} +% in hexadecimal: when |\x| is encountered, up to two hexadecimal digits +% (\texttt{0}--\texttt{9}, \texttt{a}--\texttt{f}, \texttt{A}--\texttt{F}) +% are read to give a number between $0$ and $255$. +% \item |\x{hh...}| produces the character with code \texttt{hh...} +% (an aribtrary number of hexadecimal digits are read): +% this is mostly useful for Lua\TeX{} and Xe\TeX{}. +% \item |\a|, |\e|, |\f|, |\n|, |\r|, |\t| stand for specific characters: +% \begin{center} +% \begin{tabular}{cccc} +% |\a| & |\^^G| & alarm & hex \texttt{07} \\ +% |\e| & |\^^[| & escape & hex \texttt{1B} \\ +% |\f| & |\^^L| & form feed & hex \texttt{0C} \\ +% |\n| & |\^^J| & new line & hex \texttt{0A} \\ +% |\r| & |\^^M| & carriage return & hex \texttt{0D} \\ +% |\t| & |\^^I| & horizontal tab & hex \texttt{09} \\ +% \end{tabular} +% \end{center} +% \end{itemize} +% For instance, +% \begin{quote} +% \cs{tl_new:N} \cs{l_my_str} \\ +% \cs{str_input:Nn} \cs{l_my_str} |{\x3C \\ \# abc\ def\^}| +% \end{quote} +% results in \cs{l_my_str} containing the characters |\<#abc def^|, +% since |<| has ascii code \texttt{3C} (in hexadecimal). +% \end{function} +% +% \subsection{Characters given by their position} +% +% \begin{function}[EXP]{\str_length:N} +% \begin{function}[EXP]{\str_length:n} +% \begin{function}[EXP]{\str_length_ignore_spaces:n} +% \begin{syntax} +% \cs{str_length:n} \Arg{token list} +% \end{syntax} +% Leaves the length of the string representation of \meta{token list} +% in the input stream. The functions differ in their treatment of spaces. +% In the case of \cs{str_length:N} and \cs{str_length:n}, +% all characters including spaces are counted. +% The \cs{str_length_ignore_spaces:n} returns the number of non-space +% characters. +% \begin{texnote} +% The \cs{str_length:n} of a given token list may depend +% on the category codes in effect when it is measured, +% and the value of the \tn{escapechar}: for instance +% |\str_length:n {\a}| may return $1$, $2$ or $3$ depending +% on the escape character, and the category code of \texttt{a}. +% \end{texnote} +% \end{function} +% \end{function} +% \end{function} +% +% \begin{function}[EXP]{\str_head:N} +% \begin{function}[EXP]{\str_head:n} +% \begin{function}[EXP]{\str_head_ignore_spaces:n} +% \begin{syntax} +% \cs{str_head:n} \Arg{token list} +% \end{syntax} +% Converts the \meta{token list} into a \meta{string}. +% The first character in the \meta{string} is then +% left in the input stream, with category code \enquote{other}. +% The functions differ in their treatment of spaces. +% In the case of \cs{str_head:N} and \cs{str_head:n}, +% a leading space is returned with category code $10$ (blank space). +% The \cs{str_head_ignore_spaces:n} function returns the first +% non-space character. +% If the \meta{token list} is empty (or blank in the case of the +% \texttt{_ignore_spaces} variant), then nothing is left on the +% input stream. +% \end{function} +% \end{function} +% \end{function} +% +% \begin{function}[EXP]{\str_tail:N} +% \begin{function}[EXP]{\str_tail:n} +% \begin{function}[EXP]{\str_tail_ignore_spaces:n} +% \begin{syntax} +% \cs{str_tail:n} \Arg{token list} +% \end{syntax} +% Converts the \meta{token list} to a \meta{string}, +% removes the first character, and leaves +% the remaining characters (if any) in the input stream, +% with category codes $12$ and $10$ (for spaces). +% The functions differ in the case where the first character +% is a space: \cs{str_tail:N} and \cs{str_tail:n} will trim +% only that space, while \cs{str_tail_ignore_spaces:n} trims +% the first non-space character. +% \end{function} +% \end{function} +% \end{function} +% +% \begin{function}[EXP]{\str_item:Nn} +% \begin{function}[EXP]{\str_item:nn} +% \begin{function}[EXP]{\str_item_ignore_spaces:nn} +% \begin{syntax} +% \cs{str_item:nn} \Arg{token list} \Arg{integer expression} +% \end{syntax} +% Converts the \meta{token list} to a \meta{string}, and +% leaves in the input stream the character in position +% \meta{integer expression} of the \meta{string}. +% In the case of \cs{str_item:Nn} and \cs{str_item:nn}, +% all characters including spaces are taken into account. +% The \cs{str_item_ignore_spaces:nn} function skips spaces +% in its argument. +% If the \meta{integer expression} is negative, characters +% are counted from the end of the \meta{string}. Hence, $-1$ is +% the right-most character, \emph{etc.}, while $0$ is the first +% (left-most) character. +% \end{function} +% \end{function} +% \end{function} +% +% \begin{function}[EXP]{\str_from_to:Nnn} +% \begin{function}[EXP]{\str_from_to:nnn} +% \begin{function}[EXP]{\str_from_to_ignore_spaces:nnn} +% \begin{syntax} +% \cs{str_from_to:nnn} \Arg{token list} \Arg{start index} \Arg{end index} +% \end{syntax} +% Converts the \meta{token list} to a \meta{string}, +% and leaves in the input stream the characters +% between \meta{start index} (inclusive) and \meta{end index} (exclusive). +% If either of \meta{start index} or \meta{end index} is negative, +% the it is incremented by the length of the list. +% \end{function} +% \end{function} +% \end{function} +% +% \subsection{String conditionals} +% +% \begin{function}[EXP,pTF]{\str_if_eq:NN} +% \begin{function}[EXP,pTF] +% { +% \str_if_eq:nn, \str_if_eq:Vn, \str_if_eq:on, \str_if_eq:no, +% \str_if_eq:nV, \str_if_eq:VV, \str_if_eq:xx +% } +% \begin{syntax} +% \cs{str_if_eq_p:nn} \Arg{tl1} \Arg{tl2} +% \cs{str_if_eq:nnTF} \Arg{tl1} \Arg{tl2} \Arg{true code} \Arg{false code} +% \end{syntax} +% Compares the two \meta{token lists} on a character by character +% basis, and is \texttt{true} if the two lists contain the same +% characters in the same order. Thus for example +% \begin{verbatim} +% \str_if_eq_p:xx { abc } { \tl_to_str:n { abc } } +% \end{verbatim} +% is logically \texttt{true}. All versions of these functions are fully +% expandable (including those involving an \texttt{x}-type +% expansion). +% \end{function} +% \end{function} +% +% \begin{function}[EXP,pTF]{\str_if_contains_char:NN} +% \begin{function}[EXP,pTF]{\str_if_contains_char:nN} +% \begin{syntax} +% \cs{str_if_contains_char:nN} \Arg{token list} \meta{char} +% \end{syntax} +% Converts the \meta{token list} to a \meta{string} +% and tests whether the \meta{char} is present in the \meta{string}. +% The \meta{char} can be given either directly, or as a one +% letter control sequence. +% \end{function} +% \end{function} +% +% \subsection{Bytes and encoding} +% +% \subsubsection{Conditionals} +% +% \begin{function}[EXP,pTF]{\str_if_bytes:N} +% \begin{syntax} +% \cs{str_if_bytes:NTF} \meta{str var} \Arg{true code} \Arg{false code} +% \end{syntax} +% Tests whether the \meta{str var} only contains characters +% in the range $0$--$255$. +% \end{function} +% +% \begin{function}[EXP,pTF]{\str_if_UTF_viii:N} +% \begin{syntax} +% \cs{str_if_UTF_viii:N} \meta{str var} \Arg{true code} \Arg{false code} +% \end{syntax} +% Tests whether the \meta{str var} only contains characters +% in the range $0$--$255$, and forms a valid \texttt{UTF8} string. +% \textbf{Missing!} ^^A Missing. +% \end{function} +% +% \subsubsection{Conversion} +% +% \begin{function}{\str_native_from_UTF_viii:NN} +% \begin{syntax} +% \cs{str_native_from_UTF_viii:NN} \meta{str~var1} \meta{str~var2} +% \end{syntax} +% Reads the contents of the \meta{str~var2} as an \texttt{UTF8}-encoded +% sequence of bytes, and stores the resulting characters (which can +% now have any character code) into \meta{str~var1}. +% This function raises an error if the \meta{str~var2} is not +% a valid sequence of bytes in the \texttt{UTF8} encoding. +% In the \pdfTeX{} engine, this function raises an error if any of the +% resulting characters is outside the range $0$--$255$ (in other words, +% don't use this function with \pdfTeX{}). +% \end{function} +% +% \begin{function}{\str_UTF_viii_from_native:NN} +% \begin{syntax} +% \cs{str_UTF_viii_from_native:NN} \meta{str~var1} \meta{str~var2} +% \end{syntax} +% Converts each character of the \meta{str~var2} into +% a sequence of bytes, as defined by the \texttt{UTF8} +% encoding, and stores the result in \meta{str~var1}. +% In the \pdfTeX{} engine, this function is of course +% of very little use, but can be used without harm: +% characters in the range $0$--$127$ are left unchanged, +% and characters in the range $128$--$255$ become two-byte +% sequences, as per the definition of \texttt{UTF8}. +% \end{function} +% +% \begin{function}{\str_bytes_escape_hexadecimal:NN} +% \begin{function}{\str_bytes_unescape_hexadecimal:NN} +% \begin{syntax} +% \cs{str_bytes_escape_hexadecimal:NN} \meta{str var1} \meta{str var2} +% \cs{str_bytes_unescape_hexadecimal:NN} \meta{str var1} \meta{str var2} +% \end{syntax} +% See \tn{pdfescapehex}. ^^A missing doc +% \end{function} +% \end{function} +% +% \begin{function}{\str_bytes_escape_name:NN} +% \begin{function}{\str_bytes_unescape_name:NN} +% \begin{syntax} +% \cs{str_bytes_escape_name:NN} \meta{str var1} \meta{str var2} +% \cs{str_bytes_unescape_name:NN} \meta{str var1} \meta{str var2} +% \end{syntax} +% See \tn{pdfescapename}. ^^A missing doc +% \end{function} +% \end{function} +% +% \begin{function}{\str_bytes_escape_string:NN} +% \begin{function}{\str_bytes_unescape_string:NN} +% \begin{syntax} +% \cs{str_bytes_escape_string:NN} \meta{str var1} \meta{str var2} +% \cs{str_bytes_unescape_string:NN} \meta{str var1} \meta{str var2} +% \end{syntax} +% See \tn{pdfescapestring}. ^^A missing doc +% \end{function} +% \end{function} +% +% \begin{function}{\str_bytes_percent_encode:NN} +% \begin{function}{\str_bytes_percent_decode:NN} +% \begin{syntax} +% \cs{str_bytes_percent_encode:NN} meta{str var1} \meta{str var2} +% \cs{str_bytes_percent_decode:NN} meta{str var1} \meta{str var2} +% \end{syntax} +% Used for urls. ^^A missing doc +% \end{function} +% \end{function} +% +% \subsection{Internal string functions} +% +% \begin{function}[EXP]{\tl_to_other_str:N,\tl_to_other_str:n} +% \begin{syntax} +% \cs{tl_to_other_str:n} \Arg{token list} +% \end{syntax} +% Converts the \meta{token list} to an \meta{other string}, where +% spaces have category code \enquote{other}. These functions create +% \enquote{safe} strings. +% \begin{texnote} +% These functions can be \texttt{f}-expanded without fear of losing +% a leading space, since spaces do not have category code $10$ in +% their result. +% \end{texnote} +% \end{function} +% +% \begin{function}[EXP]{\str_sanitize_args:Nn,\str_sanitize_args:Nnn} +% \begin{syntax} +% \cs{str_sanitize_args:Nnn} \meta{function} +% ~~\Arg{token list1} \Arg{token list2} +% \end{syntax} +% Converts the \meta{token lists} to \enquote{safe} strings +% (where spaces have category code \enquote{other}), +% and hands-in the result as arguments to \meta{function}. +% \end{function} +% +% \begin{function}[rEXP]{\str_map_tokens:Nn} +% \begin{syntax} +% \cs{str_map_tokens:Nn} \meta{str var} \meta{tokens} +% \end{syntax} +% Maps the \meta{tokens} over every character in the \meta{str var}. +% \end{function} +% +% \begin{function}{\str_aux_escape:NNNn} +% \begin{syntax} +% \cs{str_aux_escape:NNNn} \meta{fn1} \meta{fn2} \meta{fn3} \Arg{token list} +% \end{syntax} +% The \meta{token list} is converted to a string, then read from +% left to right, interpreting backslashes as escaping the next character. +% Unescaped characters are fed to the function \meta{fn1}, +% and escaped characters are fed to the function \meta{fn2} +% within an \texttt{x}-expansion context (typically those functions +% perform some tests on their argument to decide how to output them). +% The escape sequences |\a|, |\e|, |\f|, |\n|, |\r|, |\t| and |\x| +% are recognized as described for \cs{str_input:Nn}, and those +% are replaced by the corresponding character, then fed to +% \meta{fn3}. The result is assigned globally to \cs{g_str_result_tl}. +% \end{function} +% +% \subsection{Possibilities} +% +% \begin{itemize} +% \item More encodings (see Heiko's \pkg{stringenc}). +% In particular, what is needed for pdf: \texttt{UTF-16}? +% \item \cs{str_between:nnn} \Arg{str} +% \Arg{begin delimiter} \Arg{end delimiter} giving the piece of +% \meta{str} between \meta{begin} and \meta{end}; could be used +% with empty \meta{begin} or \meta{end} to indicate that we +% want everything until \meta{end} or starting from \meta{begin}, +% respectively; +% \item \cs{str_count_in:nn} \Arg{str} \Arg{substr} giving the number +% of occurrences of \meta{substr} in \meta{str}; +% \item \cs{str_if_head_eq:nN} alias of \cs{tl_if_head_eq_charcode:nN} +% \item \cs{str_if_numeric/decimal/integer:n}, perhaps in \pkg{l3fp}? +% \end{itemize} +% Some functionalities of \pkg{stringstrings} and \pkg{xstring} as well. +% +%^^A Should "str_item" be "str_char"? +%^^A Should "str_from_to" be "str_substr"? +% +% \end{documentation} +% +% \begin{implementation} +% +% \section{\pkg{l3str} implementation} +% +% \begin{macrocode} +%<*package> +% \end{macrocode} +% +% \begin{macrocode} +\ProvidesExplPackage + {\ExplFileName}{\ExplFileDate}{\ExplFileVersion}{\ExplFileDescription} +% \end{macrocode} +% +% Those string-related functions are defined in \pkg{l3kernel}. +% \begin{itemize} +% \item \cs{str_if_eq:nn}[pTF] and variants, +% \item \cs{str_if_eq_return:on}, +% \item \cs{tl_to_str:n}, \cs{tl_to_str:N}, \cs{tl_to_str:c}, +% \item \cs{token_to_str:N}, \cs{cs_to_str:N} +% \item \cs{str_head:n}, \cs{str_head_aux:w}, (changed behaviour slightly) +% \item \cs{str_tail:n}, \cs{str_tail_aux:w}, (changed behaviour slightly) +% \item \cs{str_length_skip_spaces} (deprecated) +% \item \cs{str_length_loop:NNNNNNNNN} (unchanged) +% \end{itemize} +% +% \subsection{General functions} +% +% \begin{macro}[EXP,aux]{\cs_if_exist_use:cF} +% This function could be moved to a different module in \pkg{l3kernel}. +% If the control sequence exists, use it, +% otherwise fall back to a default behaviour. +% \begin{macrocode} +\cs_if_exist:NF \cs_if_exist_use:cF + { + \cs_new:Npn \cs_if_exist_use:cF #1 + { \cs_if_exist:cTF {#1} { \use:c {#1} } } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[EXP,aux]{\use_i:nnnnnnnn} +% A function which may already be defined elsewhere. +% \begin{macrocode} +\cs_if_exist:NF \use_i:nnnnnnnn + { \cs_new:Npn \use_i:nnnnnnnn #1#2#3#4#5#6#7#8 {#1} } +% \end{macrocode} +% \end{macro} +% +% \begin{macro} +% { +% \str_set:Nn, \str_set:Nx, +% \str_set:cn, \str_set:cx, +% \str_gset:Nn, \str_gset:Nx, +% \str_gset:cn, \str_gset:cx, +% \str_put_left:Nn, \str_put_left:Nx, +% \str_put_left:cn, \str_put_left:cx, +% \str_gput_left:Nn, \str_gput_left:Nx, +% \str_gput_left:cn, \str_gput_left:cx, +% \str_put_right:Nn, \str_put_right:Nx, +% \str_put_right:cn, \str_put_right:cx, +% \str_gput_right:Nn, \str_gput_right:Nx, +% \str_gput_right:cn, \str_gput_right:cx, +% } +% Simply convert the token list inputs to \meta{strings}. +% \begin{macrocode} +\cs_set:Npn \str_tmp:w #1 + { + \cs_new_protected:cpx { str_#1:Nn } ##1##2 + { \exp_not:c { tl_#1:Nx } ##1 { \exp_not:N \tl_to_str:n {##2} } } + \exp_args:Nc \cs_generate_variant:Nn { str_#1:Nn } { Nx , cn , cx } + } +\str_tmp:w {set} +\str_tmp:w {gset} +\str_tmp:w {put_left} +\str_tmp:w {gput_left} +\str_tmp:w {put_right} +\str_tmp:w {gput_right} +% \end{macrocode} +% \end{macro} +% +% \subsection{Variables and constants} +% +% \begin{macro}{\str_tmp:w} +% \begin{variable}{\g_str_tmpa_tl} +% Internal scratch space for some functions. +% \begin{macrocode} +\cs_set_protected_nopar:Npn \str_tmp:w { } +\tl_new:N \g_str_tmpa_tl +% \end{macrocode} +% \end{variable} +% \end{macro} +% +% \begin{variable}{\g_str_result_tl} +% The \cs{g_str_result_tl} variable is used to hold the result +% of various internal string operations which are typically +% performed in a group. The variable is global so that it remains +% defined outside the group, to be assigned to a user provided variable. +% \begin{macrocode} +\tl_new:N \g_str_result_tl +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_str_char_int,\l_str_bytes_int} +% When converting from various escaped forms to raw characters, +% we often need to read several digits (hexadecimal or octal depending +% on the case) and keep track of the corresponding character code +% in \cs{l_str_char_int}. For UTF-8 support, the number of bytes +% of for the current character is stored in \cs{l_str_bytes_int}. +% \begin{macrocode} +\int_new:N \l_str_char_int +\int_new:N \l_str_bytes_int +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\c_backslash_str, +% \c_lbrace_str,\c_rbrace_str,\c_hash_str,\c_percent_str} +% For all of those strings, \cs{cs_to_str:N} produce characters +% with the correct category code. +% \begin{macrocode} +\tl_const:Nx \c_backslash_str { \cs_to_str:N \\ } +\tl_const:Nx \c_lbrace_str { \cs_to_str:N \{ } +\tl_const:Nx \c_rbrace_str { \cs_to_str:N \} } +\tl_const:Nx \c_hash_str { \cs_to_str:N \# } +\tl_const:Nx \c_percent_str { \cs_to_str:N \% } +% \end{macrocode} +% \end{variable} +% +% \subsection{Escaping spaces} +% +% \begin{macro}[EXP]{\tl_to_other_str:N,\tl_to_other_str:n} +% \begin{macro}[EXP,aux]{\tl_to_other_str_loop:w,\tl_to_other_str_end:w} +% Replaces all spaces by \enquote{other} spaces, after converting +% the token list to a string via \cs{tl_to_str:n}. +% \begin{macrocode} +\group_begin: +\char_set_lccode:nn {`\*}{`\ } +\char_set_lccode:nn {`\A}{`\A} +\tl_to_lowercase:n { + \group_end: + \cs_new:Npn \tl_to_other_str:n #1 + { + \exp_after:wN \tl_to_other_str_loop:w \tl_to_str:n {#1} ~ % + A ~ A ~ A ~ A ~ A ~ A ~ A ~ A ~ \q_mark \q_stop + } + \cs_new_nopar:Npn \tl_to_other_str_loop:w + #1 ~ #2 ~ #3 ~ #4 ~ #5 ~ #6 ~ #7 ~ #8 ~ #9 \q_stop + { + \if_meaning:w A #8 + \tl_to_other_str_end:w + \fi: + \tl_to_other_str_loop:w + #9 #1 * #2 * #3 * #4 * #5 * #6 * #7 * #8 * \q_stop + } + \cs_new_nopar:Npn \tl_to_other_str_end:w \fi: #1 \q_mark #2 * A #3 \q_stop + { \fi: #2 } +} +\cs_new_nopar:Npn \tl_to_other_str:N + { \exp_args:No \tl_to_other_str:n } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP]{\str_sanitize_args:Nn,\str_sanitize_args:Nnn} +% Here, \texttt{f}-expansion does not lose leading spaces, +% since they have catcode \enquote{other} after \cs{str_sanitize:n}. +% \begin{macrocode} +\cs_new:Npn \str_sanitize_args:Nn #1 #2 + { + \exp_args:Nf #1 + { \tl_to_other_str:n {#2} } + } +\cs_new:Npn \str_sanitize_args:Nnn #1#2#3 + { + \exp_args:Nff #1 + { \tl_to_other_str:n {#2} } + { \tl_to_other_str:n {#3} } + } +% \end{macrocode} +% \end{macro} +% +% \subsection{Characters given by their position} +% +% \begin{macro}[EXP]{\str_length:N} +% \begin{macro}[EXP]{\str_length:n} +% \begin{macro}[EXP]{\str_length_ignore_spaces:n} +% \begin{macro}[EXP]{\str_length_unsafe:n} +% \begin{macro}[EXP,aux]{\str_length_aux:n,\str_length_loop:NNNNNNNNN} +% The length of a string is found by first changing all spaces +% to other spaces using \cs{tl_to_other_str:n}, then counting +% characters $9$ at a time. When the end is reached, |#9| +% has the form |X|\meta{digit}, the catcode test is true, +% the digit gets added to the sum, and the loop is terminated +% by \cs{use_none_delimit_by_q_stop:w}. +% \begin{macrocode} +\cs_new_nopar:Npn \str_length:N { \exp_args:No \str_length:n } +\cs_new:Npn \str_length:n { \str_sanitize_args:Nn \str_length_unsafe:n } +\cs_new_nopar:Npn \str_length_unsafe:n #1 + { \str_length_aux:n { \str_length_loop:NNNNNNNNN #1 } } +\cs_new:Npn \str_length_ignore_spaces:n #1 + { + \str_length_aux:n + { \exp_after:wN \str_length_loop:NNNNNNNNN \tl_to_str:n {#1} } + } +\cs_new:Npn \str_length_aux:n #1 + { + \int_eval:n + { + #1 + { X \c_eight } { X \c_seven } { X \c_six } + { X \c_five } { X \c_four } { X \c_three } + { X \c_two } { X \c_one } { X \c_zero } + \q_stop + } + } +\cs_set:Npn \str_length_loop:NNNNNNNNN #1#2#3#4#5#6#7#8#9 + { + \if_catcode:w X #9 + \exp_after:wN \use_none_delimit_by_q_stop:w + \fi: + \c_nine + \str_length_loop:NNNNNNNNN + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP]{\str_head:N} +% \begin{macro}[EXP]{\str_head:n} +% \begin{macro}[EXP]{\str_head_ignore_spaces:n} +% \begin{macro}[EXP]{\str_head_unsafe:n} +% \begin{macro}[EXP,aux]{\str_head_aux:w} +% The cases of \cs{str_head_ignore_spaces:n} and +% \cs{str_head_unsafe:n} are mostly identical to +% \cs{tl_head:n}. +% As usual, \cs{str_head:N} expands its argument and hands it to +% \cs{str_head:n}. To circumvent the fact that \TeX{} skips spaces +% when grabbing undelimited macro parameters, \cs{str_head_aux:w} +% takes an argument delimited by a space. If |#1| starts with a +% non-space character, \cs{use_i_delimit_by_q_stop:nw} leaves that +% in the input stream. On the other hand, if |#1| starts with a +% space, the \cs{str_head_aux:w} takes an empty argument, and +% the single (braced) space in the definition of \cs{str_head_aux:w} +% makes its way to the output. Finally, for an empty argument, +% the (braced) empty brace group in the definition of \cs{str_head:n} +% gives an empty result after passing through +% \cs{use_i_delimit_by_q_stop:nw}. +% \begin{macrocode} +\cs_set_nopar:Npn \str_head:N { \exp_args:No \str_head:n } +\cs_set:Npn \str_head:n #1 + { + \exp_after:wN \str_head_aux:w + \tl_to_str:n {#1} + { { } } ~ \q_stop + } +\cs_set_nopar:Npx \str_head_aux:w #1 ~ % + { \exp_not:N \use_i_delimit_by_q_stop:nw #1 { ~ } } +\cs_new:Npn \str_head_ignore_spaces:n #1 + { \exp_after:wN \use_i_delimit_by_q_stop:nw \tl_to_str:n {#1} { } \q_stop } +\cs_new_nopar:Npn \str_head_unsafe:n #1 + { \use_i_delimit_by_q_stop:nw #1 { } \q_stop } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP]{\str_tail:N} +% \begin{macro}[EXP]{\str_tail:n} +% \begin{macro}[EXP]{\str_tail_ignore_spaces:n} +% \begin{macro}[EXP]{\str_tail_unsafe:n} +% \begin{macro}[EXP,aux]{\str_tail_aux:w} +% \begin{macro}[EXP,aux]{\str_tail_aux_ii:w} +% As when fetching the head of a string, the cases +% \enquote{\texttt{ignore_spaces:n}} and \enquote{\texttt{unsafe:n}} +% are similar to \cs{tl_tail:n}. +% The more commonly used \cs{str_tail:n} function is a little bit +% more convoluted: hitting the front of the string with +% \cs{reverse_if:N} \cs{if_charcode:w} \cs{scan_stop:} +% removes the first character (which necessarily makes the test true, +% since it cannot match \cs{scan_stop:}). The auxiliary function inserts +% the required \cs{fi:} to close the conditional, and leaves the tail +% of the string in the input string. The details are such that an empty +% string has an empty tail. +% \begin{macrocode} +\cs_new_nopar:Npn \str_tail:N { \exp_args:No \str_tail:n } +\cs_set:Npn \str_tail:n #1 + { + \exp_after:wN \str_tail_aux:w + \reverse_if:N \if_charcode:w + \scan_stop: \tl_to_str:n {#1} X X \q_stop + } +\cs_set_nopar:Npn \str_tail_aux:w #1 X #2 \q_stop { \fi: #1 } +\cs_new:Npn \str_tail_ignore_spaces:n #1 + { + \exp_after:wN \str_tail_aux_ii:w + \tl_to_str:n {#1} X { } X \q_stop + } +\cs_new_nopar:Npn \str_tail_unsafe:n #1 + { \str_tail_aux_ii:w #1 X { } X \q_stop } +\cs_set_nopar:Npn \str_tail_aux_ii:w #1 #2 X #3 \q_stop { #2 } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP,int]{\str_skip_do:nn} +% \begin{macro}[EXP,aux]{\str_skip_aux:nnnnnnnnn} +% \begin{macro}[EXP,aux]{\str_skip_end:nn} +% \begin{macro}[EXP,aux]{\str_skip_end_ii:nwn} +% Removes |max(#1,0)| characters then leaves |#2| in the input stream. +% We remove characters $7$ at a time. When the number of characters +% to remove is not a multiple of $7$, we need to remove less than +% $7$ characters in the last step. This is done by inserting a number +% of \texttt{X}, which are discarded as if they were part of the string. +% \begin{macrocode} +\cs_new:Npn \str_skip_do:nn #1 + { + \int_compare:nNnTF {#1} > \c_seven + { \str_skip_aux:nnnnnnnnn } + { \str_skip_end:n } + {#1} + } +\cs_new:Npn \str_skip_aux:nnnnnnnnn #1#2 #3#4#5#6#7#8#9 + { \exp_args:Nf \str_skip_do:nn { \int_eval:n { #1 - \c_seven } } {#2} } +\cs_new:Npn \str_skip_end:n #1 + { + \if_case:w \int_eval:w #1 \int_eval_end: + \str_skip_end_ii:nwn { XXXXXXX } + \or: \str_skip_end_ii:nwn { XXXXXX } + \or: \str_skip_end_ii:nwn { XXXXX } + \or: \str_skip_end_ii:nwn { XXXX } + \or: \str_skip_end_ii:nwn { XXX } + \or: \str_skip_end_ii:nwn { XX } + \or: \str_skip_end_ii:nwn { X } + \or: \str_skip_end_ii:nwn { } + \else: \str_skip_end_ii:nwn { XXXXXXX } + \fi: + } +\cs_new:Npn \str_skip_end_ii:nwn #1 #2 \fi: #3 + { \fi: \use_i:nnnnnnnn {#3} #1 } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP,int]{\str_collect_do:nn} +% \begin{macro}[EXP,aux]{\str_collect_aux:n,\str_collect_aux:nnNNNNNNN} +% \begin{macro}[EXP,aux] +% { +% \str_collect_end:nn, +% \str_collect_end_ii:nwn, +% \str_collect_end_iii:nwNNNNNNN +% } +% Collects |max(#1,0)| characters, and feeds them as an argument to |#2|. +% Again, we grab $7$ characters at a time. Instead of inserting +% a string of \texttt{X} to fill in to a multiple of $7$, we insert +% empty groups, so that they disappear in this context where arguments +% are accumulated. +% \begin{macrocode} +\cs_new:Npn \str_collect_do:nn #1#2 + { \str_collect_aux:n {#1} { \str_collect_end_iii:nwNNNNNNN {#2} } } +\cs_new:Npn \str_collect_aux:n #1 + { + \int_compare:nNnTF {#1} > \c_seven + { \str_collect_aux:nnNNNNNNN } + { \str_collect_end:n } + {#1} + } +\cs_new:Npn \str_collect_aux:nnNNNNNNN #1#2 #3#4#5#6#7#8#9 + { + \exp_args:Nf \str_collect_aux:n + { \int_eval:n { #1 - \c_seven } } + { #2 #3#4#5#6#7#8#9 } + } +\cs_new:Npn \str_collect_end:n #1 + { + \if_case:w \int_eval:w #1 \int_eval_end: + \str_collect_end_ii:nwn { {}{}{}{}{}{}{} } + \or: \str_collect_end_ii:nwn { {}{}{}{}{}{} } + \or: \str_collect_end_ii:nwn { {}{}{}{}{} } + \or: \str_collect_end_ii:nwn { {}{}{}{} } + \or: \str_collect_end_ii:nwn { {}{}{} } + \or: \str_collect_end_ii:nwn { {}{} } + \or: \str_collect_end_ii:nwn { {} } + \or: \str_collect_end_ii:nwn { } + \else: \str_collect_end_ii:nwn { {}{}{}{}{}{}{} } + \fi: + } +\cs_new:Npn \str_collect_end_ii:nwn #1 #2 \fi: #3 + { \fi: #3 \q_stop #1 } +\cs_new:Npn \str_collect_end_iii:nwNNNNNNN #1 #2 \q_stop #3#4#5#6#7#8#9 + { #1 {#2#3#4#5#6#7#8#9} } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP]{\str_item:Nn} +% \begin{macro}[EXP]{\str_item:nn} +% \begin{macro}[EXP]{\str_item_ignore_spaces:nn} +% \begin{macro}[EXP]{\str_item_unsafe:nn} +% \begin{macro}[EXP,aux]{\str_item_aux:nn} +% This is mostly shuffling arguments around to avoid measuring +% the length of the string more than once, and make sure that +% the parameters given to \cs{str_skip_do:nn} are necessarily +% within the bounds of the length of the string. +% The \cs{str_item_ignore_spaces:nn} function cheats a little bit +% in that it doesn't hand to \cs{str_item_unsafe:nn} a truly +% \enquote{safe} string. This is alright, as everything else +% is done with undelimited arguments. +% \begin{macrocode} +\cs_new_nopar:Npn \str_item:Nn { \exp_args:No \str_item:nn } +\cs_new:Npn \str_item:nn #1#2 + { + \exp_args:Nf \tl_to_str:n + { \str_sanitize_args:Nn \str_item_unsafe:nn {#1} {#2} } + } +\cs_new:Npn \str_item_ignore_spaces:nn #1 + { \exp_args:No \str_item_unsafe:nn { \tl_to_str:n {#1} } } +\cs_new_nopar:Npn \str_item_unsafe:nn #1#2 + { + \exp_args:Nff \str_item_aux:nn + { \int_eval:n {#2} } + { \str_length_unsafe:n {#1} } + #1 + \q_stop + } +\cs_new_nopar:Npn \str_item_aux:nn #1#2 + { + \int_compare:nNnTF {#1} < \c_zero + { + \int_compare:nNnTF {#1} < {-#2} + { \use_none_delimit_by_q_stop:w } + { + \str_skip_do:nn { #1 + #2 } + { \use_i_delimit_by_q_stop:nw } + } + } + { + \int_compare:nNnTF {#1} < {#2} + { + \str_skip_do:nn {#1} + { \use_i_delimit_by_q_stop:nw } + } + { \use_none_delimit_by_q_stop:w } + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP]{\str_from_to:Nnn} +% \begin{macro}[EXP]{\str_from_to:nnn} +% \begin{macro}[EXP]{\str_from_to_ignore_spaces:nnn} +% \begin{macro}[EXP]{\str_from_to_unsafe:nnn} +% \begin{macro}[EXP,aux]{\str_from_to_aux:nnnw} +% \begin{macro}[EXP,aux]{\str_from_to_aux_ii:nnw} +% \begin{macro}[EXP,aux]{\str_aux_eval_args:Nnnn} +% \begin{macro}[EXP,aux]{\str_aux_normalize_range:nn} +% Sanitize the string, then limit the second and third arguments +% to be at most the length of the string (this avoids needing +% to check for the end of the string when grabbing characters). +% Afterwards, skip characters, then keep some more, and finally +% drop the end of the string. +% \begin{macrocode} +\cs_new_nopar:Npn \str_from_to:Nnn { \exp_args:No \str_from_to:nnn } +\cs_new:Npn \str_from_to:nnn #1#2#3 + { + \exp_args:Nf \tl_to_str:n + { \str_sanitize_args:Nn \str_from_to_unsafe:nnn {#1}{#2}{#3} } + } +\cs_new:Npn \str_from_to_ignore_spaces:nnn #1 + { \exp_args:No \str_from_to_unsafe:nnn { \tl_to_str:n {#1} } } +\cs_new:Npn \str_from_to_unsafe:nnn #1#2#3 % + { + \str_aux_eval_args:Nnnn \str_from_to_aux:nnnw + { \str_length_unsafe:n {#1} } + {#2} + {#3} + #1 + \q_stop + } +\cs_new:Npn \str_from_to_aux:nnnw #1#2#3 % + { + \exp_args:Nf \str_from_to_aux_ii:nnw + { \str_aux_normalize_range:nn {#2} {#1} } + { \str_aux_normalize_range:nn {#3} {#1} } + } +\cs_new:Npn \str_from_to_aux_ii:nnw #1#2 + { + \str_skip_do:nn {#1} + { + \exp_args:Nf \str_collect_do:nn + { \int_eval:n { #2 - #1 } } + { \use_i_delimit_by_q_stop:nw } + } + } +\cs_new:Npn \str_aux_eval_args:Nnnn #1#2#3#4 + { + \exp_after:wN #1 + \exp_after:wN { \int_value:w \int_eval:w #2 \exp_after:wN } + \exp_after:wN { \int_value:w \int_eval:w #3 \exp_after:wN } + \exp_after:wN { \int_value:w \int_eval:w #4 } + } +\cs_new:Npn \str_aux_normalize_range:nn #1#2 + { + \int_eval:n + { + \if_num:w #1 < \c_zero + \if_num:w #1 < - #2 \exp_stop_f: + \c_zero + \else: + #1 + #2 + \fi: + \else: + \if_num:w #1 < #2 \exp_stop_f: + #1 + \else: + #2 + \fi: + \fi: + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \subsection{Internal mapping function} +% +% \begin{macro}[rEXP,int]{\str_map_tokens:nn} +% \begin{macro}[rEXP,int]{\str_map_tokens:Nn} +% \begin{macro}[rEXP,aux]{\str_map_tokens_aux:nn} +% \begin{macro}[rEXP,aux]{\str_map_tokens_loop:nN} +% \begin{macro}[rEXP,int]{\str_map_break_do:n} +% We simply need to be careful with spaces. +% Spaces are fed to the mapped tokens with +% category code $12$, not $10$. +% \begin{macrocode} +\cs_new_nopar:Npn \str_map_tokens:Nn + { \exp_args:No \str_map_tokens:nn } +\cs_new_nopar:Npn \str_map_tokens:nn + { \str_sanitize_args:Nn \str_map_tokens_aux:nn } +\cs_new:Npn \str_map_tokens_aux:nn #1#2 + { + \str_map_tokens_loop:nN {#2} #1 + { ? \use_none_delimit_by_q_recursion_stop:w } \q_recursion_stop + } +\cs_new_nopar:Npn \str_map_tokens_loop:nN #1#2 + { + \use_none:n #2 + #1 #2 + \str_map_tokens_loop:nN {#1} + } +\cs_new_eq:NN \str_map_break_do:n \use_i_delimit_by_q_recursion_stop:nw +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \subsection{String conditionals} +% +% \begin{macro}[EXP,pTF]{\str_if_eq:NN} +% \begin{macro}[EXP,pTF]{\str_if_eq:nn,\str_if_eq:xx} +% The \texttt{nn} and \texttt{xx} variants are already +% defined in \pkg{l3basics}. +% \begin{macrocode} +\prg_new_conditional:Npnn \str_if_eq:NN #1#2 { p , TF , T , F } + { + \if_int_compare:w \pdftex_strcmp:D { \tl_to_str:N #1 } { \tl_to_str:N #2 } + = \c_zero \prg_return_true: \else: \prg_return_false: \fi: + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP,pTF]{\str_if_contains_char:NN} +% \begin{macro}[EXP,pTF]{\str_if_contains_char:nN} +% \begin{macro}[EXP,aux]{\str_if_contains_char_aux:NN} +% \begin{macro}[EXP,aux]{\str_if_contains_char_end:w} +% Loop over the characters of the string, comparing character codes. +% We allow |#2| to be a single-character control sequence, hence the +% use of \cs{int_compare:nNnT} rather than \cs{token_if_eq_charcode:NNT}. +% The loop is broken if character codes match. Otherwise we return +% \enquote{false}. +% \begin{macrocode} +\prg_new_conditional:Npnn \str_if_contains_char:NN #1#2 { p , T , F , TF } + { + \str_map_tokens:Nn #1 { \str_if_contains_char_aux:NN #2 } + \prg_return_false: + } +\prg_new_conditional:Npnn \str_if_contains_char:nN #1#2 { p , T , F , TF } + { + \str_map_tokens:nn {#1} { \str_if_contains_char_aux:NN #2 } + \prg_return_false: + } +\cs_new_nopar:Npn \str_if_contains_char_aux:NN #1#2 + { + \if_num:w `#1 = `#2 \exp_stop_f: + \str_if_contains_char_end:w + \fi: + } +\cs_new_nopar:Npn \str_if_contains_char_end:w \fi: #1 \prg_return_false: + { \fi: \prg_return_true: } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP,pTF]{\str_if_bytes:N} +% \begin{macro}[EXP,aux]{\str_if_bytes_aux:N} +% Loop over the string, checking if every character code is less than $256$. +% \begin{macrocode} +\prg_new_conditional:Npnn \str_if_bytes:N #1 { p , T , F , TF } + { + \str_map_tokens:Nn #1 { \str_if_bytes_aux:N } + \prg_return_true: + } +\cs_new_nopar:Npn \str_if_bytes_aux:N #1 + { + \int_compare:nNnF {`#1} < \c_two_hundred_fifty_six + { + \use_i_delimit_by_q_recursion_stop:nw + { \prg_return_false: \use_none:n } + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \subsection{Internal conditionals} +% +% \begin{macro}[aux]{\str_aux_hexadecimal_test:N} +% This test is used when reading hexadecimal digits, for the |\x| escape +% sequence, and some conversion functions. It returns \meta{true} if the +% token is a hexadecimal digit, and \meta{false} otherwise. It has the +% additional side-effect of updating the value of +% \cs{l_str_char_int} (number formed in base $16$ from the digits +% read so far). +% \begin{macrocode} +\prg_new_protected_conditional:Npnn \str_aux_hexadecimal_test:N #1 { TF } + { + \tl_if_in:onTF { \tl_to_str:n {abcdef} } {#1} + { + \int_set:Nn \l_str_char_int + { \c_sixteen * \l_str_char_int + `#1 - 87 } + \prg_return_true: + } + { + \if_num:w \c_fifteen < "1 \exp_not:N #1 \exp_stop_f: + \int_set:Nn \l_str_char_int + { \c_sixteen * \l_str_char_int + "#1 } + \prg_return_true: + \else: + \prg_return_false: + \fi: + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux]{\str_aux_octal_test:N} +% This test is used when reading octal digits, for +% some conversion functions. It returns \meta{true} if the +% token is an octal digit, and \meta{false} otherwise. +% It has the additional side-effect of updating the value of +% \cs{l_str_char_int} (number formed in base $8$ from the digits +% read so far). +% \begin{macrocode} +\prg_new_protected_conditional:Npnn \str_aux_octal_test:N #1 { TF } + { + \if_num:w \c_seven < '1 \exp_not:N #1 \exp_stop_f: + \int_set:Nn \l_str_char_int + { \c_eight * \l_str_char_int + '#1 } + \prg_return_true: + \else: + \prg_return_false: + \fi: + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\str_aux_char_if_octal_digit:NTF} +% ^^A to be removed. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \str_aux_char_if_octal_digit:NTF #1 + { \tl_if_in:nnTF { 01234567 } {#1} } +% \end{macrocode} +% \end{macro} +% +% \subsection{Conversions} +% +% \subsubsection{Simple unescaping} +% +% The code of this section is used both here for \cs{str_(g)input:Nn}, +% and in the regular expression module to go through the regular expression +% once before actually parsing it. The goal in that case is to turn any +% character with a meaning in regular expressions (\texttt{*}, \texttt{?}, +% \texttt{\{}, etc.) into a marker indicating that this was a special character, +% and replace any escaped character by the corresponding unescaped character, +% so that the \pkg{l3regex} code can avoid caring about escaping issues +% (those can become quite complex to handle in combination with ranges +% in character classes). +% +% The idea is to feed unescaped characters to one function, +% escaped characters to another, and feed |\a|, |\e|, |\f|, +% |\n|, |\r|, |\t| and |\x| converted to the approriate +% character to a third function. Spaces are ignored unless +% escaped. +% For the \cs{str_(g)input:Nn} application, all the functions are simply +% \cs{token_to_str:N} (this ensures that spaces correctly get assigned +% category code $10$). +% For the \pkg{l3regex} applications, the functions do some further +% tests on the character they receive. +% +% \begin{macro}{\str_input:Nn,\str_ginput:Nn} +% Simple wrappers around the internal \cs{str_aux_escape:NNNn}. +% \begin{macrocode} +\cs_new_protected:Npn \str_input:Nn #1#2 + { + \str_aux_escape:NNNn \token_to_str:N \token_to_str:N \token_to_str:N {#2} + \tl_set_eq:NN #1 \g_str_result_tl + } +\cs_new_protected:Npn \str_ginput:Nn #1#2 + { + \str_aux_escape:NNNn \token_to_str:N \token_to_str:N \token_to_str:N {#2} + \tl_gset_eq:NN #1 \g_str_result_tl + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[int]{\str_aux_escape:NNNn} +% \begin{macro}[aux]{\str_aux_escape_unescaped:N} +% \begin{macro}[aux]{\str_aux_escape_escaped:N} +% \begin{macro}[aux]{\str_aux_escape_raw:N} +% \begin{macro}[aux]{\str_aux_escape_loop:N} +% \begin{macro}[aux]+\str_aux_escape_\:w+ +% Treat the argument as an \meta{escaped string}, +% and store the corresponding \meta{string} globally +% in \cs{g_str_result_tl}. +% \begin{macrocode} +\cs_new_eq:NN \str_aux_escape_unescaped:N \use:n +\cs_new_eq:NN \str_aux_escape_escaped:N \use:n +\cs_new_eq:NN \str_aux_escape_raw:N \use:n +\cs_new_protected:Npn \str_aux_escape:NNNn #1#2#3#4 + { + \group_begin: + \cs_set_nopar:Npn \str_aux_escape_unescaped:N { #1 } + \cs_set_nopar:Npn \str_aux_escape_escaped:N { #2 } + \cs_set_nopar:Npn \str_aux_escape_raw:N { #3 } + \int_set:Nn \tex_escapechar:D {92} + \tl_gset:Nx \g_str_result_tl { \tl_to_other_str:n {#4} } + \tl_gset:Nx \g_str_result_tl + { + \exp_after:wN \str_aux_escape_loop:N \g_str_result_tl + \q_recursion_tail \q_recursion_stop + } + \group_end: + } +\cs_new_nopar:Npn \str_aux_escape_loop:N #1 + { + \cs_if_exist_use:cF { str_aux_escape_\token_to_str:N #1:w } + { \str_aux_escape_unescaped:N #1 } + \str_aux_escape_loop:N + } +\cs_new_nopar:cpn { str_aux_escape_ \c_backslash_str :w } + \str_aux_escape_loop:N #1 + { + \cs_if_exist_use:cF { str_aux_escape_/\token_to_str:N #1:w } + { \str_aux_escape_escaped:N #1 } + \str_aux_escape_loop:N + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[aux]+\str_aux_escape_\q_recursion_tail:w+ +% \begin{macro}[aux]+\str_aux_escape_/\q_recursion_tail:w+ +% \begin{macro}[aux]+\str_aux_escape_ :w+ +% \begin{macro}[aux]{ +% \str_aux_escape_/a:w, \str_aux_escape_/e:w, \str_aux_escape_/f:w, +% \str_aux_escape_/n:w, \str_aux_escape_/r:w, \str_aux_escape_/t:w} +% The loop is ended upon seeing \cs{q_recursion_tail}. +% Spaces are ignored, and |\a|, |\e|, |\f|, |\n|, |\r|, |\t| take +% their meaning here. +% \begin{macrocode} +\cs_new_eq:cN + { str_aux_escape_ \c_backslash_str q_recursion_tail :w } + \use_none_delimit_by_q_recursion_stop:w +\cs_new_eq:cN + { str_aux_escape_ / \c_backslash_str q_recursion_tail :w } + \use_none_delimit_by_q_recursion_stop:w +\cs_new_nopar:cpn { str_aux_escape_~:w } { } +\cs_new_nopar:cpx { str_aux_escape_/a:w } + { \exp_not:N \str_aux_escape_raw:N \iow_char:N \^^G } +\cs_new_nopar:cpx { str_aux_escape_/t:w } + { \exp_not:N \str_aux_escape_raw:N \iow_char:N \^^I } +\cs_new_nopar:cpx { str_aux_escape_/n:w } + { \exp_not:N \str_aux_escape_raw:N \iow_char:N \^^J } +\cs_new_nopar:cpx { str_aux_escape_/f:w } + { \exp_not:N \str_aux_escape_raw:N \iow_char:N \^^L } +\cs_new_nopar:cpx { str_aux_escape_/r:w } + { \exp_not:N \str_aux_escape_raw:N \iow_char:N \^^M } +\cs_new_nopar:cpx { str_aux_escape_/e:w } + { \exp_not:N \str_aux_escape_raw:N \iow_char:N \^^[ } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[aux]{\str_aux_escape_/x:w} +% \begin{macro}[aux]{\str_aux_escape_x_test:N} +% \begin{macro}[aux]{\str_aux_escape_x_unbraced_i:N} +% \begin{macro}[aux]{\str_aux_escape_x_unbraced_ii:N} +% \begin{macro}[aux]{\str_aux_escape_x_braced_loop:N} +% \begin{macro}[aux]{\str_aux_escape_x_braced_end:N} +% \begin{macro}[aux]{\str_aux_escape_x_end:} +% When |\x| is encountered, interrupt the assignment, +% and distinguish the cases of a braced or unbraced syntax. +% In the braced case, collect arbitrarily many hexadecimal digits, +% building the number in \cs{l_str_char_int} (this is a side effect +% of \cs{str_aux_hexadecimal_test:NTF}), and check that +% the run of digits was interrupted by a closing brace. +% In the unbraced case, collect up to two hexadecimal digits, +% possibly less, building the character number in \cs{l_str_char_int}. +% In both cases, once all digits have been collected, use +% the \TeX{} primitive \tn{lowercase} to produce that character, +% and use a \cs{if_false:} trick to restart the assignment. +% \begin{macrocode} +\cs_new_nopar:cpn { str_aux_escape_/x:w } \str_aux_escape_loop:N + { + \if_false: { \fi: } + \int_zero:N \l_str_char_int + \str_aux_escape_x_test:N + } +\cs_new_protected_nopar:Npx \str_aux_escape_x_test:N #1 + { + \exp_not:N \token_if_eq_charcode:NNTF \c_space_token #1 + { \exp_not:N \str_aux_escape_x_test:N } + { + \exp_not:N \token_if_eq_charcode:NNTF \c_lbrace_str #1 + { \exp_not:N \str_aux_escape_x_braced_loop:N } + { \exp_not:N \str_aux_escape_x_unbraced_i:N #1 } + } + } +\cs_new_protected_nopar:Npn \str_aux_escape_x_unbraced_i:N #1 + { + \str_aux_hexadecimal_test:NTF #1 + { \str_aux_escape_x_unbraced_ii:N } + { \str_aux_escape_x_end: #1 } + } +\cs_new_protected_nopar:Npn \str_aux_escape_x_unbraced_ii:N #1 + { + \token_if_eq_charcode:NNTF \c_space_token #1 + { \str_aux_escape_x_unbraced_ii:N } + { + \str_aux_hexadecimal_test:NTF #1 + { \str_aux_escape_x_end: } + { \str_aux_escape_x_end: #1 } + } + } +\cs_new_protected_nopar:Npn \str_aux_escape_x_braced_loop:N #1 + { + \token_if_eq_charcode:NNTF \c_space_token #1 + { \str_aux_escape_x_braced_loop:N } + { + \str_aux_hexadecimal_test:NTF #1 + { \str_aux_escape_x_braced_loop:N } + { \str_aux_escape_x_braced_end:N #1 } + } + } +\cs_new_protected_nopar:Npx \str_aux_escape_x_braced_end:N #1 + { + \exp_not:N \token_if_eq_charcode:NNTF \c_rbrace_str #1 + { \exp_not:N \str_aux_escape_x_end: } + { + \msg_error:nn { str } { x-missing-brace } + \exp_not:N \str_aux_escape_x_end: #1 + } + } +\group_begin: + \char_set_catcode_other:N \^^@ + \cs_new_protected_nopar:Npn \str_aux_escape_x_end: + { + \group_begin: + \char_set_lccode:nn { \c_zero } { \l_str_char_int } + \tl_to_lowercase:n + { + \group_end: + \tl_gput_right:Nx \g_str_result_tl + { \if_false: } \fi: + \str_aux_escape_raw:N ^^@ + \str_aux_escape_loop:N + } + } +\group_end: +\msg_new:nnn { str } { x-missing-brace } + { + You~wrote~something~like~ + `\iow_char:N\\x\{ \int_to_hexadecimal:n { \l_str_char_int }'.~ + The~closing~brace~is~missing. + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \subsubsection{Escape and unescape strings for pdf use} +% +% \begin{macro}{\str_aux_convert_store:} +% \begin{macro}{\str_aux_convert_store:NNn} +% \begin{macrocode} +\group_begin: + \char_set_catcode_other:n {`\^^@} + \cs_new_protected_nopar:Npn \str_aux_convert_store: + { + \char_set_lccode:nn {\c_zero} {\l_str_char_int} + \tl_to_lowercase:n { \tl_gput_right:Nx \g_str_result_tl {^^@} } + } + \cs_new_protected_nopar:Npn \str_aux_convert_store:NNn #1#2#3 + { + \char_set_lccode:nn {\c_zero} {#3} + \tl_to_lowercase:n { #1 #2 {^^@} } + } +\group_end: +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\str_aux_byte_to_hexadecimal:N} +% \begin{macro}{\str_aux_byte_to_octal:N} +% \begin{macrocode} +\cs_new_nopar:Npn \str_aux_byte_to_hexadecimal:N #1 + { + \int_compare:nNnTF {`#1} < {256} + { + \int_to_letter:n { \int_div_truncate:nn {`#1} \c_sixteen } + \int_to_letter:n { \int_mod:nn {`#1} \c_sixteen } + } + { \msg_expandable_error:n { Invalid~byte~`#1'. } } + } +\cs_new_nopar:Npn \str_aux_byte_to_octal:N #1 + { + \int_compare:nNnTF {`#1} < {64} + { + 0 + \int_to_letter:n { \int_div_truncate:nn {`#1} \c_eight } + \int_to_letter:n { \int_mod:nn {`#1} \c_eight } + } + { + \int_compare:nNnTF {`#1} < {256} + { \int_to_octal:n {`#1} } + { \msg_expandable_error:n { Invalid~byte~`#1'. } } + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\str_bytes_escape_hexadecimal:NN} +% \begin{macrocode} +\cs_new_protected_nopar:Npn \str_bytes_escape_hexadecimal:NN #1#2 + { + \str_set:Nx #1 + { \str_map_tokens:Nn #2 \str_aux_byte_to_hexadecimal:N } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\str_bytes_escape_name:NN} +% \begin{macrocode} +\tl_const:Nx \c_str_bytes_escape_name_str + { \c_hash_str \c_percent_str \c_lbrace_str \c_rbrace_str () /<>[] } +\cs_new_protected_nopar:Npn \str_bytes_escape_name:NN #1#2 + { + \str_set:Nx #1 + { \str_map_tokens:Nn #2 \str_bytes_escape_name_aux:N } + } +\cs_new_nopar:Npn \str_bytes_escape_name_aux:N #1 + { + \int_compare:nNnTF {`#1} < {"21} + { \c_hash_str \str_aux_byte_to_hexadecimal:N #1 } + { + \int_compare:nNnTF {`#1} > {"7E} + { \c_hash_str \str_aux_byte_to_hexadecimal:N #1 } + { + \str_if_contains_char:NNTF \c_str_bytes_escape_name_str #1 + { \c_hash_str \str_aux_byte_to_hexadecimal:N #1 } + {#1} + } + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\str_bytes_escape_string:NN} +% Any character below (and including) space, and any character +% above (anc including) \texttt{del}, are converted to octal. +% One backslash is added before each parenthesis and backslash. +% \begin{macrocode} +\tl_const:Nx \c_str_bytes_escape_string_str { \c_backslash_str ( ) } +\cs_new_protected_nopar:Npn \str_bytes_escape_string:NN #1#2 + { + \str_set:Nx #1 + { \str_map_tokens:Nn #2 \str_bytes_escape_string_aux:N } + } +\cs_new_nopar:Npn \str_bytes_escape_string_aux:N #1 + { + \int_compare:nNnTF {`#1} < {"21} + { \c_backslash_str \str_aux_byte_to_octal:N #1 } + { + \int_compare:nNnTF {`#1} > {"7E} + { \c_backslash_str \str_aux_byte_to_octal:N #1 } + { + \str_if_contains_char:NNT \c_str_bytes_escape_string_str #1 + { \c_backslash_str } + #1 + } + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\str_bytes_unescape_hexadecimal:NN} +% \begin{macro}[aux]{\str_bytes_unescape_hexadecimal_aux:N} +% \begin{macro}[aux]{\str_bytes_unescape_hexadecimal_aux_ii:N} +% Takes chars two by two, and interprets each pair as a hexadecimal code +% for a character. Any non-hexadecimal-digit is ignored. An odd-length +% string gets a |0| appended to it (this is equivalent to appending a |0| +% in all cases, and dropping it if it is alone). +% \begin{macrocode} +\cs_new_protected_nopar:Npn \str_bytes_unescape_hexadecimal:NN #1#2 + { + \group_begin: + \tl_gclear:N \g_str_result_tl + \int_zero:N \l_str_char_int + \exp_last_unbraced:Nf \str_bytes_unescape_hexadecimal_aux:N + { \tl_to_other_str:N #2 } 0 + \q_recursion_tail \q_recursion_stop + \group_end: + \tl_set_eq:NN #1 \g_str_result_tl + } +\cs_new_protected_nopar:Npn \str_bytes_unescape_hexadecimal_aux:N #1 + { + \quark_if_recursion_tail_stop:N #1 + \str_aux_hexadecimal_test:NTF #1 + { \str_bytes_unescape_hexadecimal_aux_ii:N } + { \str_bytes_unescape_hexadecimal_aux:N } + } +\cs_new_protected_nopar:Npn \str_bytes_unescape_hexadecimal_aux_ii:N #1 + { + \quark_if_recursion_tail_stop:N #1 + \str_aux_hexadecimal_test:NTF #1 + { + \str_aux_convert_store: + \int_zero:N \l_str_char_int + \str_bytes_unescape_hexadecimal_aux:N + } + { \str_bytes_unescape_hexadecimal_aux_ii:N } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\str_bytes_unescape_name:NN} +% \begin{macro}[aux]{\str_bytes_unescape_name_aux:wN} +% \begin{macro}[aux]{\str_bytes_unescape_name_aux_ii:N} +% This changes all occurrences of |#| followed by two upper- or lowercase +% hexadecimal digits by the corresponding unescaped character. +% \begin{macrocode} +\group_begin: + \char_set_lccode:nn {`\*} {`\#} + \tl_to_lowercase:n + { + \group_end: + \cs_new_protected_nopar:Npn \str_bytes_unescape_name:NN #1#2 + { + \group_begin: + \tl_gclear:N \g_str_result_tl + \int_zero:N \l_str_char_int + \exp_last_unbraced:Nf \str_bytes_unescape_name_aux:wN + { \tl_to_other_str:N #2 } + * \q_recursion_tail \q_recursion_stop + \group_end: + \tl_set_eq:NN #1 \g_str_result_tl + } + \cs_new_protected_nopar:Npn \str_bytes_unescape_name_aux:wN #1 * #2 + { + \tl_gput_right:Nx \g_str_result_tl {#1} + \quark_if_recursion_tail_stop:N #2 + \str_aux_hexadecimal_test:NTF #2 + { \str_bytes_unescape_name_aux_ii:NN #2 } + { + \tl_gput_right:Nx \g_str_result_tl { \c_hash_str } + \str_bytes_unescape_name_aux:wN #2 + } + } + } +\cs_new_protected_nopar:Npn \str_bytes_unescape_name_aux_ii:NN #1#2 + { + \str_aux_hexadecimal_test:NTF #2 + { + \str_aux_convert_store: + \int_zero:N \l_str_char_int + \str_bytes_unescape_name_aux:wN + } + { + \tl_gput_right:Nx \g_str_result_tl { \c_hash_str #1 } + \int_zero:N \l_str_char_int + \str_bytes_unescape_name_aux:wN #2 + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\str_bytes_unescape_string:NN} +% Here, we need to detect backslashes, which escape characters +% as follows. +% \begin{itemize} +% \item[\texttt{\string\n}] Line feed ($10$) +% \item[\texttt{\string\r}] Carriage return ($13$) +% \item[\texttt{\string\t}] Horizontal tab ($9$) +% \item[\texttt{\string\b}] Backspace ($8$) +% \item[\texttt{\string\f}] Form feed ($12$) +% \item[\texttt{(}] Left parenthesis +% \item[\texttt{)}] Right parenthesis +% \item[\texttt{\string\\}] Backslash +% \item[\texttt{\string\ddd}] Character code \texttt{ddd} (octal) +% \end{itemize} +% If followed by an end-of-line character, the backslash and +% the end-of-line are ignored. If followed by anything else, +% the backslash is ignored. +% The pdf specification indicates that LF, CR, and CRLF should be +% converted to LF: \emph{this is not implemented here}. +% \begin{macrocode} +\group_begin: + \char_set_lccode:nn {`\*} {`\\} + \char_set_catcode_other:N \^^J + \char_set_catcode_other:N \^^M + \tl_to_lowercase:n + { + \group_end: + \cs_new_protected_nopar:Npn \str_bytes_unescape_string:NN #1#2 + { + \group_begin: + \tl_gclear:N \g_str_result_tl + \exp_last_unbraced:Nf \str_bytes_unescape_string_aux:wN + { \tl_to_other_str:N #2 } + * \q_recursion_tail \q_recursion_stop + \group_end: + \tl_set_eq:NN #1 \g_str_result_tl + } + \cs_new_protected_nopar:Npn \str_bytes_unescape_string_aux:wN #1 * #2 + { + \tl_gput_right:Nx \g_str_result_tl {#1} + \quark_if_recursion_tail_stop:N #2 + \int_zero:N \l_str_char_int + \str_aux_octal_test:NTF #2 + { \str_bytes_unescape_string_aux_d:N } + { + \int_set:Nn \l_str_char_int + { + \prg_case_str:xxn {#2} + { + {n} {10} + {r} {13} + {t} {9} + {b} {8} + {f} {12} + {(} {40} + {)} {41} + {\c_backslash_str} {92} + {^^J} {-1} + {^^M} {-1} + } + {`#2} + } + \int_compare:nNnF \l_str_char_int = \c_minus_one + { \str_aux_convert_store: } + \str_bytes_unescape_string_aux:wN + } + } + } +\cs_new_protected_nopar:Npn \str_bytes_unescape_string_aux_d:N #1 + { + \str_aux_octal_test:NTF #1 + { \str_bytes_unescape_string_aux_dd:N } + { + \str_aux_convert_store: + \str_bytes_unescape_string_aux:wN #1 + } + } +\cs_new_protected_nopar:Npn \str_bytes_unescape_string_aux_dd:N #1 + { + \str_aux_octal_test:NTF #1 + { + \str_aux_convert_store: + \str_bytes_unescape_string_aux:wN + } + { + \str_aux_convert_store: + \str_bytes_unescape_string_aux:wN #1 + } + } +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Percent encoding} +% +% \begin{macro}{\str_bytes_percent_encode:NN} +% \begin{macrocode} +\tl_const:Nx \c_str_bytes_percent_encode_str { \tl_to_str:n { [] } } +\tl_const:Nx \c_str_bytes_percent_encode_not_str { \tl_to_str:n { "-.<> } } +\cs_new_protected_nopar:Npn \str_bytes_percent_encode:NN #1#2 + { + \str_set:Nx #1 + { \str_map_tokens:Nn #2 \str_bytes_percent_encode_aux:N } + } +\cs_new_nopar:Npn \str_bytes_percent_encode_aux:N #1 + { + \int_compare:nNnTF {`#1} < {"41} + { + \str_if_contains_char:NNTF \c_str_bytes_percent_encode_not_str #1 + { #1 } + { \c_percent_str \str_aux_byte_to_hexadecimal:N #1 } + } + { + \int_compare:nNnTF {`#1} > {"7E} + { \c_percent_str \str_aux_byte_to_hexadecimal:N #1 } + { + \str_if_contains_char:NNTF \c_str_bytes_percent_encode_str #1 + { \c_percent_str \str_aux_byte_to_hexadecimal:N #1 } + { #1 } + } + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\str_bytes_percent_decode:NN} +% \begin{macro}[aux]{\str_bytes_percent_decode_aux:wN} +% \begin{macro}[aux]{\str_bytes_percent_decode_aux_ii:NN} +% \begin{macrocode} +\group_begin: + \char_set_lccode:nn {`\*} {`\%} + \tl_to_lowercase:n + { + \group_end: + \cs_new_protected_nopar:Npn \str_bytes_percent_decode:NN #1#2 + { + \group_begin: + \tl_gclear:N \g_str_result_tl + \exp_last_unbraced:Nf \str_bytes_percent_decode_aux:wN + { \tl_to_other_str:N #2 } + * \q_recursion_tail \q_recursion_stop + \group_end: + \tl_set_eq:NN #1 \g_str_result_tl + } + \cs_new_protected_nopar:Npn \str_bytes_percent_decode_aux:wN #1 * #2 + { + \tl_gput_right:Nx \g_str_result_tl {#1} + \quark_if_recursion_tail_stop:N #2 + \int_zero:N \l_str_char_int + \str_aux_hexadecimal_test:NTF #2 + { \str_bytes_percent_decode_aux_ii:NN #2 } + { + \tl_gput_right:Nx \g_str_result_tl { \c_percent_str } + \str_bytes_percent_decode_aux:wN #2 + } + } + } +\cs_new_protected_nopar:Npn \str_bytes_percent_decode_aux_ii:NN #1#2 + { + \str_aux_hexadecimal_test:NTF #2 + { + \str_aux_convert_store: + \str_bytes_percent_decode_aux:wN + } + { + \tl_gput_right:Nx \g_str_result_tl {#1} + \str_bytes_percent_decode_aux:wN #2 + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \subsubsection{UTF-8 support} +% +% \begin{macro}{\str_native_from_UTF_viii:NN} +% \begin{macrocode} +\cs_new_protected_nopar:Npn \str_native_from_UTF_viii:NN #1#2 + { + \group_begin: + \tl_gclear:N \g_str_result_tl + \exp_last_unbraced:Nf \str_native_from_UTF_viii_aux_i:N + { \tl_to_other_str:N #2 } + \q_recursion_tail \q_recursion_stop + \group_end: + \tl_set_eq:NN #1 \g_str_result_tl + } +\cs_new_protected_nopar:Npn \str_native_from_UTF_viii_aux_i:N #1 + { + \quark_if_recursion_tail_stop:N #1 + \int_set:Nn \l_str_char_int { `#1 } + \str_native_from_UTF_viii_aux_ii:nN {128} \c_one + \str_native_from_UTF_viii_aux_ii:nN {64} \c_zero + \str_native_from_UTF_viii_aux_ii:nN {32} \c_two + \str_native_from_UTF_viii_aux_ii:nN {16} \c_three + \str_native_from_UTF_viii_aux_ii:nN {8} \c_four + \msg_error:nnx { str } { utf8-invalid-byte } {#1} + \use_i:nnn \str_native_from_UTF_viii_aux_i:N + \q_stop + \str_native_from_UTF_viii_aux_iii:N + } +\cs_new_protected_nopar:Npn + \str_native_from_UTF_viii_aux_ii:nN #1#2 + { + \int_compare:nNnTF \l_str_char_int < {#1} + { + \int_set_eq:NN \l_str_bytes_int #2 + \use_none_delimit_by_q_stop:w + } + { \int_sub:Nn \l_str_char_int {#1} } + } +\cs_new_protected_nopar:Npn + \str_native_from_UTF_viii_aux_iii:N #1 + { + \int_compare:nNnTF \l_str_bytes_int < \c_two + { \str_native_from_UTF_viii_aux_iv: #1 } + { + \if_meaning:w \q_recursion_tail #1 + \msg_error:nn { str } { utf8-premature-end } + \exp_after:wN \use_none_delimit_by_q_recursion_stop:w + \fi: + \quark_if_recursion_tail_stop:N #1 + \tex_multiply:D \l_str_char_int by 64 \scan_stop: % no interface?? + \int_decr:N \l_str_bytes_int + \int_compare:nNnTF {`#1} < {128} + { \use_ii:nn } + { \int_compare:nNnTF {`#1} < {192} } + { + \int_add:Nn \l_str_char_int { `#1 - 128 } + \str_native_from_UTF_viii_aux_iii:N + } + { + \msg_error:nnx { str } { utf8-missing-byte } {#1} + \str_native_from_UTF_viii_aux_i:N #1 + } + } + } +\cs_new_protected_nopar:Npn + \str_native_from_UTF_viii_aux_iv: + { + \int_compare:nNnTF \l_str_bytes_int = \c_zero + { + \msg_error:nnx { str } { utf8-extra-byte } + { \int_eval:n { \l_str_char_int + 128 } } + } + { + \pdftex_if_engine:TF + { + \int_compare:nNnTF { \l_str_char_int } < { 256 } + { \str_aux_convert_store: } + { + \msg_error:nnx { str } { utf8-pdftex-overflow } + { \int_use:N \l_str_char_int } + } + } + { \str_aux_convert_store: } + } + \str_native_from_UTF_viii_aux_i:N + } +\msg_new:nnn { str } { utf8-invalid-byte } + { + \int_compare:nNnTF {`#1} < {256} + { Byte~number~\int_eval:n {`#1}~invalid~in~utf-8~encoding. } + { The~character~number~\int_eval:n {`#1}~is~not~a~byte. } + } +\msg_new:nnn { str } { utf8-missing-byte } + { The~byte~number~\int_eval:n {`#1}~is~not~a~valid~continuation~byte. } +\msg_new:nnn { str } { utf8-extra-byte } + { The~byte~number~\int_eval:n {`#1}~is~only~valid~as~a~continuation~byte. } +\msg_new:nnnn { str } { utf8-premature-end } + { Incomplete~last~utf8~character. } + { + The~sequence~of~byte~that~I~need~to~convert~to~utf8 \ + ended~before~the~last~character~was~complete.~Perhaps \ + it~was~mistakenly~truncated? + } +\msg_new:nnn { str } { utf8-pdftex-overflow } + { The~character~number~#1~is~too~big~for~pdfTeX. } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\str_UTF_viii_from_native:NN} +% \begin{macrocode} +\cs_new_protected_nopar:Npn \str_UTF_viii_from_native:NN #1#2 + { + \group_begin: + \tl_gclear:N \g_str_result_tl + \str_map_tokens:Nn #2 \str_UTF_viii_from_native_aux_i:N + \group_end: + \tl_set_eq:NN #1 \g_str_result_tl + } +\cs_new_protected_nopar:Npn \str_UTF_viii_from_native_aux_i:N #1 + { + \int_set:Nn \l_str_char_int {`#1} + \int_compare:nNnTF \l_str_char_int < {128} + { \tl_gput_right:Nx \g_str_result_tl {#1} } + { + \tl_gclear:N \g_str_tmpa_tl + \int_set:Nn \l_str_bytes_int { 64 } + \str_UTF_viii_from_native_aux_ii:n {32} + \str_UTF_viii_from_native_aux_ii:n {16} + \str_UTF_viii_from_native_aux_ii:n {8} + \ERROR % somehow the unicode char was > "1FFFFF > "10FFFF + \tl_gclear:N \g_str_tmpa_tl + \use_none:n \q_stop + \int_add:Nn \l_str_char_int { 2 * \l_str_bytes_int } + \str_aux_convert_store: + \tl_gput_right:Nx \g_str_result_tl { \g_str_tmpa_tl } + } + } +\cs_new_protected_nopar:Npn \str_UTF_viii_from_native_aux_ii:n #1 + { + \str_aux_convert_store:NNn + \tl_gput_left:Nx \g_str_tmpa_tl + { 128 + \int_mod:nn { \l_str_char_int } {64} } + \tex_divide:D \l_str_char_int by 64 \scan_stop: % no interface?? + \int_add:Nn \l_str_bytes_int {#1} + \int_compare:nNnT \l_str_char_int < {#1} + { \use_none_delimit_by_q_stop:w } + } +% \end{macrocode} +% \end{macro} +% +% \subsection{Deprecated string functions} +% +% \begin{macro}{\str_length_skip_spaces:N,\str_length_skip_spaces:n} +% The naming scheme is a little bit more consistent +% with \enquote{ignore_spaces} instead of \enquote{skip_spaces}. +% \begin{macrocode} +\cs_gset:Npn \str_length_skip_spaces:N + { \exp_args:No \str_length_skip_spaces:n } +\cs_gset_eq:NN \str_length_skip_spaces:n \str_length_ignore_spaces:n +% \end{macrocode} +% \end{macro} +% +% \begin{macrocode} +% +% \end{macrocode} +% +% \end{implementation} +% +% \PrintIndex diff --git a/Master/texmf-dist/source/latex/l3experimental/l3str/l3str.ins b/Master/texmf-dist/source/latex/l3experimental/l3str/l3str.ins new file mode 100644 index 00000000000..5aef8b5f0fe --- /dev/null +++ b/Master/texmf-dist/source/latex/l3experimental/l3str/l3str.ins @@ -0,0 +1,43 @@ +\iffalse meta-comment + +File l3str.ins Copyright (C) 2011 The LaTeX3 Project + +It may be distributed and/or modified under the conditions of the +LaTeX Project Public License (LPPL), either version 1.3c of this +license or (at your option) any later version. The latest version +of this license is in the file + + http://www.latex-project.org/lppl.txt + +This file is part of the "l3experimental bundle" (The Work in LPPL) +and all files in that bundle must be distributed together. + +The released version of this bundle is available from CTAN. + +\fi + +\input docstrip.tex +\askforoverwritefalse + +\preamble + +EXPERIMENTAL CODE + +Do not distribute this file without also distributing the +source files specified above. + +Do not distribute a modified version of this file. + +\endpreamble +% stop docstrip adding \endinput +\postamble +\endpostamble + +\keepsilent + +\generate{\file{l3str.sty} {\from{l3str.dtx} {package}}} +\generate{\file{l3regex.sty} {\from{l3regex.dtx} {package}}} + +\generate{\file{l3regex-trace.sty} {\from{l3regex.dtx} {package,trace}}} + +\endbatchfile diff --git a/Master/texmf-dist/source/latex/l3experimental/xcoffins/xcoffins.dtx b/Master/texmf-dist/source/latex/l3experimental/xcoffins/xcoffins.dtx index ca25807a810..103934d5fe8 100644 --- a/Master/texmf-dist/source/latex/l3experimental/xcoffins/xcoffins.dtx +++ b/Master/texmf-dist/source/latex/l3experimental/xcoffins/xcoffins.dtx @@ -36,7 +36,7 @@ % %<*driver|package> \RequirePackage{xparse} -\GetIdInfo$Id: xcoffins.dtx 2349 2011-05-11 18:44:37Z joseph $ +\GetIdInfo$Id: xcoffins.dtx 2895 2011-10-09 15:58:19Z joseph $ {L3 Experimental design-level coffins} % %<*driver> @@ -298,9 +298,9 @@ % \begin{syntax} % \cs{RotateCoffin} \meta{coffin} \Arg{angle} % \end{syntax} -% Rotates the \meta{coffin} by the given \meta{angle} (given in -% degrees counter-clockwise). This process will rotate both the -% coffin content and poles. Multiple rotations will not result in +% Rotates the \meta{coffin} by the given \meta{angle} about its reference +% point (given in degrees counter-clockwise) . This process will rotate both +% the coffin content and poles. Multiple rotations will not result in % the bounding box of the coffin growing unnecessarily. % % The effect of rotation on a coffin is illustrated in @@ -474,13 +474,13 @@ % \hfil % \begin{minipage}{0.4\textwidth} % \centering -% \fbox{\TypesetCoffin\ExampleCoffin} +% \fbox{\copy\ExampleCoffin} % \end{minipage} % \hfil % \begin{minipage}{0.4\textwidth} % \centering % \RotateCoffin\ExampleCoffin{135} -% \fbox{\TypesetCoffin\ExampleCoffin} +% \fbox{\copy\ExampleCoffin} % \end{minipage} % \hfil % \caption{The effect of rotation of a joined coffin: the black line diff --git a/Master/texmf-dist/source/latex/l3experimental/xgalley/l3galley.dtx b/Master/texmf-dist/source/latex/l3experimental/xgalley/l3galley.dtx new file mode 100644 index 00000000000..fa54ac37e59 --- /dev/null +++ b/Master/texmf-dist/source/latex/l3experimental/xgalley/l3galley.dtx @@ -0,0 +1,2140 @@ +% \iffalse +% +%% File: l3galley.dtx Copyright (C) 1999-2001, 2004-2009 Frank Mittelbach +%% (C) 2010,2011 The LaTeX3 Project +%% +%% It may be distributed and/or modified under the conditions of the +%% LaTeX Project Public License (LPPL), either version 1.3c of this +%% license or (at your option) any later version. The latest version +%% of this license is in the file +%% +%% http://www.latex-project.org/lppl.txt +%% +%% This file is part of the "l3experimental bundle" (The Work in LPPL) +%% and all files in that bundle must be distributed together. +%% +%% The released version of this bundle is available from CTAN. +%% +%% ----------------------------------------------------------------------- +%% +%% The development version of the bundle can be found at +%% +%% http://www.latex-project.org/svnroot/experimental/trunk/ +%% +%% for those people who are interested. +%% +%%%%%%%%%%% +%% NOTE: %% +%%%%%%%%%%% +%% +%% Snapshots taken from the repository represent work in progress and may +%% not work or may contain conflicting material! We therefore ask +%% people _not_ to put them into distributions, archives, etc. without +%% prior consultation with the LaTeX Project Team. +%% +%% ----------------------------------------------------------------------- +%% +% +%<*driver|package> +\RequirePackage{l3names} +\GetIdInfo$Id: l3galley.dtx 2899 2011-10-09 21:22:06Z joseph $ + {L3 Experimental galley code} +% +%<*driver> +\documentclass[full]{l3doc} +\begin{document} + \DocInput{\jobname.dtx} +\end{document} +% +% \fi +% +% \title{^^A +% The \textsf{l3galley} package\\ Galley code^^A +% \thanks{This file describes v\ExplFileVersion, +% last revised \ExplFileDate.}^^A +% } +% +% \author{^^A +% The \LaTeX3 Project\thanks +% {^^A +% E-mail: +% \href{mailto:latex-team@latex-project.org} +% {latex-team@latex-project.org}^^A +% }^^A +% } +% +% \date{Released \ExplFileDate} +% +% \maketitle +% +% \begin{documentation} +% +% \section{Introduction} +% +% In \LaTeX3 terminology a galley is a rectangular area which receives +% text and other material filling it from top. The vertically extend of +% a galley is normally not restricted: instead certain chunks are taken +% off the top of an already partially filled galley to form columns or +% similar areas on a page. This process is typically asynchronous but +% there are ways to control or change its behaviour. +% +% Examples for galleys are \enquote{the main galley}, where the +% continuous document data gets formatted into and from which columns +% and pages are constructed, and \enquote{vertical box galleys}, such +% as the body of a minipage environment. The latter galleys are +% typically not split after formatting, though there can be exceptions. +% +% \section{Formatting layers} +% +% The present module is mainly concerned with the formatting of text +% in galleys. The mechanism by which this is achieved uses four +% (somewhat) distinct layers, some of which can be addressed using the +% templates provided here. +% +% \subsection{Layer one: external dimensions} +% +% The bottom layer of the system is the external dimensions of the +% galley. Normally only the horizontal dimension is fixed externally, +% while the vertical (filling) dimension is unspecified. The external +% dimensions are fixed when starting a new galley, and are therefore +% not modifiable within the galley. +% +% There are no templates for setting this layer directly, although the +% external values are influenced by other parts of the system (for +% example when creating minipage environments). +% +% \subsection{Layer two: internal dimensions} +% +% The second layer is the internal dimensions of the galley: the +% \emph{measure} used for paragraph text and the position of the +% paragraph relative to the edges of the galley. +% +% This layer is normally accessed by higher-level templates +% \emph{via} the object type \texttt{measure}. Changes made using +% level two templates will often extend for large parts of a document +% (up to and including the entire document). +% +% \subsection{Layer three: paragraph shape} +% +% The third layer defines the paragraph shape within the measure as +% provided by the second layer. In the absence of any specification +% for that layer the paragraph shape used will be that of a +% rectangular area of the width of the current measure. +% +% There are some restrictions imposed on the shape of a paragraph by the +% underlying \TeX{} mechanisms. For example, cut out sections in +% paragraphs can be specified from the top of the paragraph but not from +% the bottom. +% +% \subsection{Layer four: formatting inside the paragraph} +% +% The forth layer deals with the paragraph formatting aspects such as +% hyphenation and justification within the paragraph (this is sometimes +% referred to as \enquote{\texttt{h\&j}} or \enquote{\texttt{hj}}). This +% layer is somewhat distinct from the galley as such, but is handled in +% the same place as there is, internally, interaction between the different +% layers. +% +% \section{Code interfaces} +% +% \subsection{Galley layers} +% +% \begin{variable}{\l_galley_width_dim} +% The total width of a galley, set either by the page geometry code +% for the main vertical galley or when creating an independent galley, +% such as a minipage. +% \end{variable} +% +% \begin{function}{\galley_level:} +% \begin{syntax} +% \cs{galley_level:} +% \end{syntax} +% Sets up a vertical box to contain a new galley level. The box should +% be \enquote{colour safe}, which is automatic for \LaTeX3 coffins but +% must be included manually (using \cs{color_group_begin:} and +% \cs{color_group_end:}) in \enquote{raw} vertical boxes. +% \end{function} +% +% \subsection{Measure} +% +% \begin{function} +% {\galley_margins_set_absolute:nn, \galley_margins_set_relative:nn} +% \begin{syntax} +% \cs{galley_margins_set_absolute:nn} \Arg{left margin} \Arg{right margin} +% \cs{galley_margins_set_relative:nn} \Arg{left margin} \Arg{right margin} +% \end{syntax} +% Sets the width of the measure to have the \meta{left margin} and +% \meta{right margin} specified by the arguments, both of which are +% \meta{dimension expressions}. The \texttt{relative} function will adjust +% the text width within any existing margins, whereas the \texttt{absolute} +% measure sets the margins based on the edges of the galley only. One or +% both of the \meta{margins} may be negative, to specify and outdent. +% \end{function} +% +% \subsection{Between paragraphs} +% +% \begin{variable}{\g_galley_restore_running_tl} +% When galley settings need to be reset at the end of a paragraph, the +% appropriate detail should be added to this token list. It is inserted +% immediately before the start of each paragraph, and can therefore be +% used to clear otherwise global settings. The token list itself is +% also cleared as part of this process. +% \end{variable} +% +% \begin{variable}{\g_galley_no_break_next_bool} +% Indicates that no page break should be allowed between the current +% paragraph and the next paragraph. +% \end{variable} +% +% \begin{function}{\g_galley_omit_next_indent_bool} +% Indicates that the indent should be omitted from the start of the next +% paragraph started. +% \end{function} +% +% \begin{variable}{\l_galley_interpar_penalty_int} +% The \meta{penalty} for a break between paragraphs. The \meta{penalty} +% should be in the range $-10\,000$ to $10\,000$, where $-10\,000$ forces a +% page break, $0$ has no effect at all and $10\,000$ forbids a page break. +% Note that setting \cs{g_galley_no_break_next_bool} to \texttt{true} +% will override any setting of \cs{l_galley_interpar_penalty_int}. +% \end{variable} +% +% \begin{variable}{\l_galley_interpar_vspace_skip} +% Stretchable space to be inserted between paragraphs, set at the design +% or template level. +% \end{variable} +% +% \begin{function}{\galley_set_user_penalty:n} +% \begin{syntax} +% \cs{galley_set_user_penalty:n} \Arg{penalty} +% \end{syntax} +% Sets the \meta{penalty} for a break between the current and next +% paragraph on a one-off basis. This function is intended for user-level +% adjustments to design, and takes precedent over both settings from +% \cs{galley_set_penalty:n} and from \cs{galley_no_break_next:}. +% \end{function} +% +% \begin{function}{\galley_set_user_vspace:n} +% \begin{syntax} +% \cs{galley_set_user_vspace:n} \Arg{space} +% \end{syntax} +% Sets the \meta{space} f to be inserted between the current and next +% paragraph on a one-off basis. This function is intended for user-level +% adjustments to design, and otherwise is analogous to +% \cs{galley_set_vspace:n}. +% \end{function} +% +% \subsection{Paragraph shape} +% +% \begin{function} +% { +% \galley_parshape_multi_par:nnnN, +% \galley_parshape_multi_par:nVVN, +% \galley_parshape_single_par:nnnN, +% \galley_parshape_single_par:nVVN +% } +% \begin{syntax} +% \cs{galley_parshape_multi_par:nnnN} \Arg{unaltered lines} \Arg{left indents} \Arg{right indents} \meta{resume flag} +% \cs{galley_parshape_single_par:nnnN} \Arg{unaltered lines} \Arg{left indents} \Arg{right indents} \meta{resume flag} +% \end{syntax} +% Sets the current paragraph shape to create an arbitrary paragraph shape. +% The paragraph shape is set such that there are \meta{unaltered lines} which +% have width and indent as set by the measure. The \enquote{altered} lines +% are then defined by the comma-separated lists of \meta{left indents} and +% \meta{right indents}. These are both indents from the edge of the measure, +% and may be negative, and should both contain the same number of items. +% If the \meta{resume flag} is \texttt{true}, after the last +% altered line the paragraph shape returns to that of the measure. On the +% other hand, if the flag is \texttt{false} then the shape of the last line +% is retained for the rest of the paragraph. +% For example, +% \begin{verbatim} +% \galley_parshape_set_multi_par:nnnN { 1 } +% { 2 pt , 4 pt , 6 pt } { 2 pt , 4 pt , 6 pt } \c_true_bool +% \end{verbatim} +% would create a paragraph shape in which the first line is the full +% width of the measure, the second line is indented by $2$\,pt on each side, +% the third line by by $4$\,pt and the fourth line and subsequent lines by +% $6$\,pt from the edge of the measure on each side. +% +% The \texttt{single_par} version applies only to a single paragraph, +% while the \texttt{multi_par} function sets the paragraph shape on an +% ongoing basis within the scope of the current \TeX{} group. +% \end{function} +% +% \begin{function} +% { +% \galley_parshape_fixed_lines:nnn, +% \galley_parshape_fixed_lines:nVV +% } +% \begin{syntax} +% \cs{galley_parshape_fixed_lines:nnn} \Arg{unaltered lines} \Arg{left indents} \Arg{right indents} +% \end{syntax} +% Sets the paragraph shape to create an arbitrary paragraph shape which will +% apply to an exact number of lines. +% The paragraph shape is set such that there are \meta{unaltered lines} which +% have width and indent as set by the measure. The \enquote{altered} lines +% are then defined by the comma-separated lists of \meta{left indents} and +% \meta{right indents}. These are both indents from the edge of the measure, +% and may be negative, and should both contain the same number of items. +% The altered lines will apply to one or more paragraphs, such that the +% entire indent specification is honoured before the standard measure +% resumes. +% \end{function} +% +% \subsection{Formatting inside the paragraph} +% +% The settings described here apply \enquote{inside} the paragraph, and so +% are active irrespective of any paragraph shape within the measure. +% +% \begin{variable}{\l_galley_line_left_skip, \l_galley_line_right_skip} +% Stretchable space added to the appropriate side each line in a paragraph. +% \end{variable} +% +% \begin{variable}{\l_galley_par_begin_skip, \l_galley_par_end_skip} +% Stretchable space added to the beginning of the first line and end of +% the last line of a paragraph, respectively. +% \end{variable} +% +% \begin{variable}{\l_galley_par_indent_dim} +% Fixed space added to the start of each paragraph except for those where +% \cs{l_galley_omit_next_indent_bool} is \texttt{true}. +% \end{variable} +% +% \begin{variable}{\l_galley_last_line_fit_int} +% Determines how the inter-word stretch is set for the last line of a +% paragraph when +% \begin{enumerate} +% \item The value of \cs{l_galley_par_end_skip} contains an infinite +% (\texttt{fil}) component; +% \item The values of \cs{l_galley_line_left_skip} and +% \cs{l_galley_line_right_skip} do \emph{not} contain an infinite +% (\texttt{fil}) component. +% \end{enumerate} +% Under these circumstances, \cs{l_galley_last_line_fit_int} is active, and +% applies as follows: +% \begin{itemize} +% \item Set to $0$, the last line of the paragraph is set with +% the inter-word spacing at natural width; +% \item Set to a $1000$ (or above), the inter-word spacing in the last line +% is stretched by the same factor as that applied to the penultimate +% line; +% \item Set to $n$ between these extremes, the inter-word spacing in the +% last line is stretched by $n/1000$ times the factor used for the +% penultimate line. +% \end{itemize} +% \end{variable} +% +% \begin{function}{\galley_set_interword_spacing:N} +% \begin{syntax} +% \cs{galley_set_interword_spacing:N} \meta{fixed spacing bool} +% \end{syntax} +% Sets the inter-word spacing used based on the values supplied by the +% current font. If the \meta{fixed spacing bool} flag is \texttt{true} then +% no stretch is permitted between words, otherwise the stretch specified by +% the font designer is used. +% \end{function} +% +% \subsection{Display material} +% +% Material which is set in \enquote{display-style} require additional settings +% to control the relationship with the surrounding material. +% +% \begin{function}{\galley_display_begin:, \galley_display_end:} +% \begin{syntax} +% \cs{galley_display_begin:} +% \ldots +% \cs{galley_display_end:} +% \end{syntax} +% Sets up a group to contain display-style material. Unlike an independent +% galley level, settings are inherited from the surroundings. However, +% the interaction of a display block with the paragraphs before and after it +% can be adjusted independent of the design of text. +% \end{function} +% +% \subsection{Line breaking} +% +% \begin{variable}{\l_galley_binop_penalty_int} +% Penalty charged if an inline math formula is broken at a +% binary operator. +% \end{variable} +% +% \begin{variable}{\l_galley_double_hyphen_demerits_int} +% Extra demerit charge of two (or more) lines in succession end +% in a hyphen. +% \end{variable} +% +% \begin{variable}{\l_galley_emergency_stretch_skip} +% Additional stretch assumed for each line if no better line breaking +% can be found without it. This stretch is not actually added to lines, +% so its use may result in underfull box warnings. +% \end{variable} +% +% \begin{variable}{\l_galley_final_hyphen_demerits_int} +% Extra demerit charge if the second last line is hyphenated. +% \end{variable} +% +% \begin{variable}{\l_galley_linebreak_badness_int} +% Boundary that if exceeded will cause \TeX{} to report an +% underfull line. +% \end{variable} +% +% \begin{variable}{\l_galley_linebreak_fuzz_dim} +% Boundary below which overfull lines are not reported. +% \end{variable} +% +% \begin{variable}{\l_galley_linebreak_penalty_int} +% Extra penalty charged per line in the paragraph. By making +% this penalty higher \TeX{} will try harder to produce compact +% paragraphs. +% \end{variable} +% +% \begin{variable}{\l_galley_linebreak_pretolerance_int} +% Maximum tolerance allowed for individual lines to break the +% paragraph without attempting hyphenation. +% \end{variable} +% +% \begin{variable}{\l_galley_linebreak_tolerance_int} +% Maximum tolerance allowed for individual lines when breaking a +% paragraph while attempting hyphenation (if this limit can't be +% met \cs{l_galley_emergency_stretch_skip} comes into play). +% \end{variable} +% +% \begin{variable}{\l_galley_mismatch_demerits_int} +% Extra demerit charge if two visually incompatible lines follow +% each other. +% \end{variable} +% +% \begin{variable}{\l_galley_relation_penalty_int} +% Penalty charged if an inline math formula is broken at a +% relational symbol. +% \end{variable} +% +% \subsection{Paragraph breaking} +% +% \begin{variable}{\l_galley_parbreak_badness_int} +% Boundary that if exceeded will cause \TeX{} to report an +% underfull vertical box. +% \end{variable} +% +% \begin{variable}{\l_galley_parbreak_fuzz_dim} +% Boundary below which overfull vertical boxes are not reported. +% \end{variable} +% +% \begin{variable}{\l_galley_broken_penalty_int} +% Penalty for page breaking after a hyphenated line. +% \end{variable} +% +% \begin{variable}{\l_galley_pre_display_penalty_int} +% Penalty for breaking between immediately before display math material. +% \end{variable} +% +% \begin{variable}{\l_galley_post_display_penalty_int} +% Penalty for breaking between immediately after display math material. +% \end{variable} +% +% \begin{function} +% { +% \galley_set_club_penalties:n, +% \galley_set_club_penalties:V, +% \galley_set_club_penalties:v, +% \galley_set_display_club_penalties:n, +% \galley_set_display_club_penalties:V, +% \galley_set_display_club_penalties:v, +% \galley_set_display_widow_penalties:n, +% \galley_set_display_widow_penalties:V, +% \galley_set_display_widow_penalties:v, +% \galley_set_widow_penalties:n, +% \galley_set_widow_penalties:V, +% \galley_set_widow_penalties:v +% } +% \begin{syntax} +% \cs{galley_set_club_penalties:n} \Arg{penalty list} +% \end{syntax} +% Set the penalties for breaking lines at the beginning and end of +% (partial) paragraphs. In each case, the \meta{penalty list} is a +% comma-separated list of penalty values. The list applies as follows: +% \begin{itemize} +% \item[\texttt{club}] Penalties for breaking after the first, second, +% third, \emph{etc.}~line of the paragraph. +% \item[\texttt{display_club}] Penalties for breaking after the first, +% second, third, \emph{etc.}~line after a display math environment. +% \item[\texttt{display_club}] Penalties for breaking before the last, +% penultimate, antepenultimate, \emph{etc.}~line before a display +% math environment. +% \item[\texttt{widow}] Penalties for breaking before the last, +% penultimate, antepenultimate, \emph{etc.}~line of the paragraph. +% \end{itemize} +% In all cases, these penalties apply in addition to the general interline +% penalty or to any \enquote{special} line penalties. +% \end{function} +% +% \begin{function}{\galley_set_interline_penalty:n} +% \begin{syntax} +% \cs{galley_set_interline_penalty:n} \Arg{penalty} +% \end{syntax} +% Sets the standard interline penalty applied between lines of a paragraph. +% This value is added to any (display) club or widow penalty in force. +% \end{function} +% +% \begin{function} +% { +% \galley_set_interline_penalties:n, +% \galley_set_interline_penalties:V +% } +% \begin{syntax} +% \cs{galley_set_interline_penalties:n} \Arg{penalty list} +% \end{syntax} +% Sets \enquote{special} interline penalties to be used in place of +% the standard value, specified as a comma-separated \meta{penalty list}. +% The \meta{penalties} apply to the first, second, third, \emph{etc.}~line +% of the paragraph. +% \end{function} +% +% \begin{function} +% { +% \galley_save_club_penalties:N, +% \galley_save_display_club_penalties:N, +% \galley_save_display_widow_penalties:N, +% \galley_save_interline_penalties:N, +% \galley_save_widow_penalties:N +% } +% \begin{syntax} +% \cs{galley_save_club_penalties:N} \Arg{comma list} +% \end{syntax} +% These functions save the current value of the appropriate to the +% comma list specified, within the current \TeX{} group. +% \end{function} +% +% \begin{function}[EXP]{\galley_interline_penalty:} +% \begin{syntax} +% \cs{galley_interline_penalty:} +% \end{syntax} +% Expands to the current interline penalty as a \meta{integer denotation}. +% \end{function} +% +% \section{Hooks and insertion points} +% +% \begin{variable}{\g_galley_par_begin_hook_tl} +% Token list inserted at the beginning of every paragraph in horizontal mode. +% This is inserted after any paragraph indent but before any other horizontal +% mode material. +% \end{variable} +% +% \begin{variable}{\g_galley_par_end_hook_tl} +% Token list inserted at the end of every paragraph in horizontal mode. +% \end{variable} +% +% \begin{variable}{\g_galley_par_after_hook_tl} +% Token list inserted after each paragraph. This is used for resetting +% galley parameters, and is therefore cleared after use. +% \end{variable} +% +% \begin{variable}{\g_galley_whatsit_next_tl} +% Token list for whatsits to be inserted at the very beginning of the next +% paragraph started. +% \end{variable} +% +% \begin{variable}{\g_galley_whatsit_previous_hook_tl} +% Token list for whatsits to be inserted at the very end of the last +% paragraph started. +% \end{variable} +% +% \section{Internal variables} +% +% Some of the internal variables for the galley mechanism may be of +% interest to the programmer. These should all be treated as read-only +% values and accessed only through the defined interfaces described above. +% +% \begin{variable}{\l_galley_total_left_margin_dim} +% The total margin between the left side of the galley and the left side of +% the text block. This may be negative if the measure is set to overlap +% the text beyond the edge of the galley. +% \end{variable} +% +% \begin{variable}{\l_galley_total_right_margin_dim} +% The total margin between the right side of the galley and the right side +% of the text block. This may be negative if the measure is set to overlap +% the text beyond the edge of the galley. +% \end{variable} +% +% \begin{variable}{\l_galley_text_width_dim} +% The width of a line of text within the galley, taking account of +% any margins added. This may be larger than \cs{l_galley_width_dim} +% if the margins are negative. +% \end{variable} +% +% \end{documentation} +% +% \begin{implementation} +% +% \section{\pkg{l3galley} Implementation} +% +% At the implementation level, there are a number of challenges which +% have to be overcome in order to make the galley easy to use at the +% designer and user levels. Inserting material into the main vertical +% list is in many ways an irreversible operation. Inserting items as +% they appear in the source is therefore not desirable. Instead, +% inserting vertical-mode material needs to be delayed until the start +% of the \enquote{next} paragraph. This is particularly notable for +% invisible items such as whatsits and specials, which will otherwise +% cause changes in spacing. Delaying insertion enables user-supplied +% settings to override design settings in a reliable fashion. This can +% be achieved as the design-level material can be ignored if a user +% value is supplied. There is a need to allow proper nesting of +% galleys, which means that all of the above needs to be set up so that +% it can be saved and restored. All of these manipulations require +% altering the meaning of the \cs{par} token, which is particularly +% awkward as \TeX{} inserts a token \emph{called} \cs{par} rather than +% one with a particular meaning. This makes moving \cs{par} to somewhere +% \enquote{safe} extremely challenging. +% +% Added to all of this complexity, there is a need to deal with +% \enquote{display-like} material. The most obvious example is the way +% lists are handled. These use \cs{par} tokens to achieve the correct +% appearance, but at the same time +% \begin{verbatim} +% Text +% \begin{itemize} +% \item An item +% \end{itemize} +% More text +% \end{verbatim} +% should form one visual paragraph while +% \begin{verbatim} +% Text +% \begin{itemize} +% \item An item +% \end{itemize} +% +% More text +% \end{verbatim} +% should be shown as two paragraphs. This requires an additional level +% of handling so that the \cs{par} token used to end the list in the +% first case does not start a new paragraph in a visual sense while the +% second does. +% +% Another factor to bear in mind is that \cs{tex_everypar:D} may be +% executed inside a group. For example, a paragraph starting +% \begin{verbatim} +% {Text} here +% \end{verbatim} +% will insert the tokens such that the current group level is $1$ +% higher for \enquote{Text} than for \enquote{here}. The result of this +% is that it's very important to watch how flags are set and reset. This +% can only be done reliably on a global level, which then has a knock-on +% effect on the rest of the implementation. +% +% At a \TeX{} level, settings can only apply to the current paragraph, +% but conceptually there is a need to allow for both single-paragraph +% and \enquote{running} settings. Whenever the code switches galley +% level both of these need to be correctly saved. +% +% \begin{macrocode} +%<*initex|package> +% \end{macrocode} +% +% \begin{macrocode} +%<*package> +\ProvidesExplPackage + {\ExplFileName}{\ExplFileDate}{\ExplFileVersion}{\ExplFileDescription} +\package_check_loaded_expl: +% +% \end{macrocode} +% +% \subsection{Support items} +% +% Functions or settings which are needed by the galley but perhaps also +% elsewhere. +% +% \begin{macro}{\galley_leave_vmode:} +% The standard mode to leave vertical mode, starting a paragraph. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \galley_leave_vmode: + { \tex_unhbox:D \c_empty_box } +% \end{macrocode} +% \end{macro} +% +% The default hyphenation character should be set and hyphenation should be +% enabled. +% \begin{macrocode} +%<*initex> +\tex_defaulthyphenchar:D 45 \scan_stop: +% +% \end{macrocode} +% +% \subsection{Galley settings} +% +% Settings for application by the galley respect the usual \TeX{} grouping and +% so are all local variables. +% +% \begin{variable} +% { +% \l_galley_parshape_left_indent_clist, +% \l_galley_parshape_right_indent_clist +% } +% \begin{variable} +% { +% \l_galley_parshape_multipar_bool, +% \l_galley_parshape_resume_std_bool, +% \l_galley_parshape_fixed_lines_bool +% } +% Setting up paragraph shape interacts with setting up the measure. The only +% way to keep things flexible is to have a rather \enquote{rich} set of data +% available. +% \begin{macrocode} +\clist_new:N \l_galley_parshape_left_indent_clist +\clist_new:N \l_galley_parshape_right_indent_clist +\bool_new:N \l_galley_parshape_multipar_bool +\bool_new:N \l_galley_parshape_resume_std_bool +\bool_new:N \l_galley_parshape_fixed_lines_bool +\int_new:N \l_galley_parshape_std_lines_int +% \end{macrocode} +% \end{variable} +% \end{variable} +% +% \begin{variable}{\l_galley_text_width_dim} +% The width of the current measure: the \enquote{running} setting can be +% inherited from \LaTeXe{}. +% \begin{macrocode} +%<*initex> +\dim_new:N \l_galley_text_width_dim +% +%<*package> +\cs_new_eq:NN \l_galley_text_width_dim \linewidth +% +% \end{macrocode} +% \end{variable} +% +% \begin{variable} +% {\l_galley_total_left_margin_dim, \l_galley_total_right_margin_dim} +% Margins of the current text within the galley: these plus the galley +% width are one way to define the measure width. See also the text width, +% which is an alternative view (and should be in sync with this one!). +% \begin{macrocode} +%<*initex> +\dim_new:N \l_galley_total_left_margin_dim +% +%<*package> +\cs_new_eq:NN \l_galley_total_left_margin_dim \@totalleftmargin +% +\dim_new:N \l_galley_total_right_margin_dim +% \end{macrocode} +% \end{variable} +% +% \begin{variable} +% {\l_galley_interpar_penalty_skip, \l_galley_interpar_vspace_skip} +% Items between paragraphs at the design level. +% \begin{macrocode} +\int_new:N \l_galley_interpar_penalty_int +\skip_new:N \l_galley_interpar_vspace_skip +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_galley_width_dim} +% The external size of a galley is the stored in the \TeX{} primitive +% \cs{tex_hsize:D}, which is renamed. This will only ever be reset by +% the code constructing a new galley, for example the start of a +% minipage. This value will be set for the main galley by the page +% layout system. +% \begin{macrocode} +\cs_new_eq:NN \l_galley_width_dim \tex_hsize:D +% \end{macrocode} +% \end{variable} +% +% \subsection{Galley data structures} +% +% In contrast to settings, the data structures used by the galley are all set +% globally. To allow different galley levels to exist, a local variant is +% defined for each one to save the value when starting a new level. +% +% \begin{variable}{\g_galley_begin_level_bool, \l_galley_begin_level_bool} +% Indicates that the galley is at the very beginning of the level, and that +% no material has yet been set. As a result, the global version is set +% \texttt{true} to begin with. +% \begin{macrocode} +\bool_new:N \g_galley_begin_level_bool +\bool_new:N \l_galley_begin_level_bool +\bool_gset_true:N \g_galley_begin_level_bool +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\g_galley_omit_next_indent_bool, \l_galley_omit_next_indent_bool} +% A global flag is needed for suppressing indentation of the next +% paragraph. This does not need a \enquote{running} version since that +% should be handled using the \texttt{justification} object: the two +% concepts are related but not identical. The flag here is needed in +% cases such as the very first paragraph in a galley or immediately +% following a heading. +% \begin{macrocode} +\bool_new:N \g_galley_omit_next_indent_bool +\bool_new:N \l_galley_omit_next_indent_bool +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\g_galley_parshape_set_bool, \l_galley_parshape_set_bool} +% This is not a setting for paragraph shape, but rather a tracker for the +% galley system. +% \begin{macrocode} +\bool_new:N \g_galley_parshape_set_bool +\bool_new:N \l_galley_parshape_set_bool +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\g_galley_nobreak_next_bool, \l_galley_nobreak_next_bool} +% Dealing with the no-break flag is pretty much the same as the case +% for the indent: this applies on a single paragraph basis. +% \begin{macrocode} +\bool_new:N \g_galley_nobreak_next_bool +\bool_new:N \l_galley_nobreak_next_bool +% \end{macrocode} +% \end{variable} +% +% \begin{variable} +% { +% \g_galley_par_begin_hook_tl, \l_galley_par_begin_hook_tl, +% \g_galley_par_end_hook_tl, \l_galley_par_end_hook_tl, +% } +% Hooks for user-level code: these are not used by the galley itself. +% \begin{macrocode} +\tl_new:N \g_galley_par_begin_hook_tl +\tl_new:N \l_galley_par_begin_hook_tl +\tl_new:N \g_galley_par_end_hook_tl +\tl_new:N \l_galley_par_end_hook_tl +% \end{macrocode} +% \end{variable} +% +% \begin{variable} +% {\g_galley_par_after_hook_tl, \l_galley_par_after_hook_tl} +% This one is used by the galley: it happens \enquote{after} the current +% paragraph, and is used for reset purposes. +% \begin{macrocode} +\tl_new:N \g_galley_par_after_hook_tl +\tl_new:N \l_galley_par_after_hook_tl +% \end{macrocode} +% \end{variable} +% +% \begin{variable} +% {\g_galley_previous_par_lines_int, \l_galley_previous_par_lines_int} +% The number of lines in the previous typeset paragraph. This is reset at +% the start of the paragraph and \emph{added to} when each \cs{tex_par:D} +% primitive is used: \LaTeX{} uses the primitive in places that do not end +% a (conceptual) paragraph. +% \begin{macrocode} +\int_new:N \g_galley_previous_par_lines_int +\int_new:N \l_galley_previous_par_lines_int +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\g_galley_restore_running_tl, \l_galley_restore_running_tl} +% When a parameter is altered from the \enquote{running} value to a +% different \enquote{current} one, there needs to be a method to restore +% the \enquote{running} value. This is done by adding the necessary +% assignment to a token list, which can be executed when needed. At the +% same time, this information is itself part of the galley parameter +% structure, and so there has to be a local save version. +% \begin{macrocode} +\tl_new:N \g_galley_restore_running_tl +\tl_new:N \l_galley_restore_running_tl +% \end{macrocode} +% \end{variable} +% +% \begin{variable} +% { +% \g_galley_whatsit_next_tl, \l_galley_whatsit_next_tl, +% \g_galley_whatsit_previous_tl, \l_galley_whatsit_previous_tl +% } +% Whatsits only apply on a per-paragraph basis and so there is no need +% to differentiate between current and running values. However, there +% is a need to differentiate between whatsits that attach to the +% previous (completed) paragraph and those that attach to the next +% paragraph. +% \begin{macrocode} +\tl_new:N \g_galley_whatsit_next_tl +\tl_new:N \l_galley_whatsit_next_tl +\tl_new:N \g_galley_whatsit_previous_tl +\tl_new:N \l_galley_whatsit_previous_tl +% \end{macrocode} +% \end{variable} +% +% \begin{variable} +% {\g_galley_interpar_penalty_user_tl, \l_galley_interpar_penalty_user_tl} +% The user may want to over-ride the penalty for a break between +% paragraphs, for example to prevent a break when the overall design +% allows one. This is handled using an additional penalty. +% \begin{macrocode} +\tl_new:N \g_galley_interpar_penalty_user_tl +\tl_new:N \l_galley_interpar_penalty_user_tl +% \end{macrocode} +% \end{variable} +% +% \begin{variable} +% {\g_galley_interpar_vspace_user_tl, \l_galley_interpar_vspace_user_tl} +% Arbitrary vertical space can be inserted by the user on a one-off +% basis. This is used in place of any running space between paragraphs. +% \begin{macrocode} +\tl_new:N \g_galley_interpar_vspace_user_tl +\tl_new:N \l_galley_interpar_vspace_user_tl +% \end{macrocode} +% \end{variable} +% +% \subsection{Independent galley levels} +% +% As well as the main vertical list, independent galleys are required +% for items such as minipages and marginal notes. Each of these galleys +% requires an independent set of global data structures. This is +% achieved by storing the data structures in \emph{local} variables. The +% later are only used to save and restore the global value, and so \TeX{} +% grouping will manage the values correctly. This implies that each +% galley level must form a group: galley levels are tided to vertical +% boxes and so this is a reasonable requirements. +% +% \begin{macro}{\galley_initialise_variables:} +% At the start of a galley level, both the global and local variables +% will need to be reset to standard values. For example, the measure +% is set to the galley width and any paragraph shape is cleared. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \galley_initialise_variables: + { + \bool_gset_true:N \g_galley_begin_level_bool + \tl_gclear:N \g_galley_interpar_penalty_user_tl + \tl_gclear:N \g_galley_interpar_vspace_user_tl + \bool_gset_true:N \g_galley_omit_next_indent_bool + \bool_gset_false:N \g_galley_nobreak_next_bool + \tl_gclear:N \g_galley_par_begin_hook_tl + \tl_gclear:N \g_galley_par_end_hook_tl + \tl_gclear:N \g_galley_par_after_hook_tl + \bool_gset_false:N \g_galley_parshape_set_bool + \int_gzero:N \g_galley_previous_par_lines_int + \tl_gclear:N \g_galley_restore_running_tl + \tl_gclear:N \g_galley_whatsits_previous_tl + \tl_gclear:N \g_galley_whatsits_next_tl + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\galley_initialise_settings:} +% This sets the local values of the various galley settings. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \galley_initialise_settings: + { + \dim_set_eq:NN \l_galley_text_width_dim \l_galley_width_dim + \dim_zero:N \l_galley_left_margin_dim + \dim_zero:N \l_galley_right_margin_dim + \dim_zero:N \l_galley_total_left_margin_dim + \dim_zero:N \l_galley_total_right_margin_dim + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\galley_save_parameters:, \galley_restore_parameters:} +% Saving and restoring parameters is carried out by a series of copy +% functions. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \galley_save_parameters: + { + \bool_set_eq:NN \l_galley_begin_level_bool + \g_galley_begin_level_bool + \tl_set_eq:NN \l_galley_interpar_penalty_user_tl + \g_galley_interpar_penalty_user_tl + \tl_set_eq:NN \l_galley_interpar_vspace_user_tl + \g_galley_interpar_vspace_user_tl + \bool_set_eq:NN \l_galley_omit_next_indent_bool + \g_galley_omit_next_indent_bool + \bool_set_eq:NN \l_galley_nobreak_next_bool + \g_galley_nobreak_next_bool + \tl_set_eq:NN \l_galley_par_begin_hook_tl + \g_galley_par_begin_hook_tl + \tl_set_eq:NN \l_galley_par_end_hook_tl + \g_galley_par_end_hook_tl + \tl_set_eq:NN \l_galley_par_after_hook_tl + \g_galley_par_after_hook_tl + \bool_set_eq:NN \l_galley_parshape_set_bool + \g_galley_parshape_set_bool + \int_set_eq:NN \l_galley_previous_par_lines_int + \g_galley_previous_par_lines_int + \tl_set_eq:NN \l_galley_restore_running_tl + \g_galley_restore_running_tl + \tl_set_eq:NN \l_galley_whatsits_previous_tl + \g_galley_whatsits_previous_tl + \tl_set_eq:NN \l_galley_whatsits_next_tl + \g_galley_whatsits_next_tl + } +\cs_new_protected_nopar:Npn \galley_restore_parameters: + { + \bool_gset_eq:NN \g_galley_begin_level_bool + \l_galley_begin_level_bool + \tl_gset_eq:NN \g_galley_interpar_penalty_user_tl + \l_galley_interpar_penalty_user_tl + \tl_gset_eq:NN \g_galley_interpar_vspace_user_tl + \l_galley_interpar_vspace_user_tl + \bool_gset_eq:NN \g_galley_omit_next_indent_bool + \l_galley_omit_next_indent_bool + \bool_gset_eq:NN \g_galley_nobreak_next_bool + \l_galley_nobreak_next_bool + \tl_gset_eq:NN \g_galley_par_begin_hook_tl + \l_galley_par_begin_hook_tl + \tl_gset_eq:NN \g_galley_par_end_hook_tl + \l_galley_par_end_hook_tl + \tl_gset_eq:NN \g_galley_par_after_hook_tl + \l_galley_par_after_hook_tl + \bool_gset_eq:NN \g_galley_parshape_set_bool + \l_galley_parshape_set_bool + \int_gset_eq:NN \g_galley_previous_par_lines_int + \l_galley_previous_par_lines_int + \tl_gset_eq:NN \g_galley_restore_running_tl + \l_galley_restore_running_tl + \tl_gset_eq:NN \g_galley_whatsits_previous_tl + \l_galley_whatsits_previous_tl + \tl_gset_eq:NN \g_galley_whatsits_next_tl + \l_galley_whatsits_next_tl + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\galley_level:} +% \begin{macro}[aux]{\galley_level_end:} +% Galley levels are created by saving all of the current global +% settings, starting a group then initialising both the local and global +% variables. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \galley_level: + { + \galley_save_parameters: + \group_begin: + \galley_initialise_variables: + \galley_initialise_settings: + \group_insert_after:N \galley_level_end: + } +% \end{macrocode} +% At the end of the level, the global values are restored using the +% saved \emph{local} versions, hence the position of the close-of-group +% instruction. As this code can be inserted automatically, at the point +% of use only the start of a galley level needs to be marked up: the end +% must come in a fixed location. All of this relies on the the +% \enquote{colour safe} group used inside a box. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \galley_level_end: + { + \par + \galley_restore_parameters: + \group_end: + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \subsection{The \cs{par} token} +% +% \begin{macro}{\galley_std_par:} +% In vertical mode, the standard paragraph insert is just the primitive +% \cs{tex_par:D}. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \galley_std_par: + { + \mode_if_vertical:TF + { \tex_par:D } +% \end{macrocode} +% In horizontal mode, the paragraph shape is set \enquote{just in time} +% before inserting \cs{tex_par:D}. The \cs{tex_par:D} is inside a +% group to preserve some dynamic settings (for example +% \cs{etex_interlinepenalties}). Once the +% paragraph has been typeset, the number of lines is \emph{added} to the +% running total. It's possible that the conceptual paragraph contains +% display-like material, and simply setting the number of lines equal to +% \cs{tex_prevgraf:D} would \enquote{loose} these. +% \begin{macrocode} + { + \g_galley_par_end_hook_tl + \galley_set_measure_and_parshape: + \group_begin: + \tex_par:D + \group_end: + \int_gadd:Nn \g_galley_previous_par_lines_int \tex_prevgraf:D + } + \g_galley_par_after_hook_tl + \tl_gclear:N \g_galley_par_after_hook_tl +% \end{macrocode} +% The non-breaking penalty is needed here as within the \cs{tex_everypar:D} +% hook there is an additional \cs{tex_par:D}. This leads to an extra +% \cs{tex_parskip:D}, which will leave an unwanted break-point here +% otherwise. +% \begin{macrocode} + \tex_penalty:D \c_ten_thousand + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\par} +% The meaning of the token \cs{par} itself starts off as a standard +% paragraph. +% \begin{macrocode} +\cs_set_protected_nopar:Npn \par { \galley_std_par: } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@par} +% \LaTeXe{} requires a \enquote{long term} version of \cs{par}, which is +% stored as \cs{@par}. Things are done a bit differently by \LaTeX3 and +% so this will only be needed in package mode. +% \begin{macrocode} +%<*package> +\tl_set:Nn \@par { \galley_std_par: } +% +% \end{macrocode} +% \end{macro} +% +% \subsection{Display levels} +% +% \begin{macro}{\galley_display_begin:, \galley_display_end:} +% \begin{macro}[aux]{\galley_display_penalty:N, \galley_display_vspace:N} +% \begin{macro}[aux]{\galley_display_par_setup:, \galley_display_par:} +% Display items within the galley are a bit like galley levels: they +% may have different paragraph settings to the main part of the galley. +% On the other hand, unlike independent galleys they should inherit the +% settings from the surrounding material. They may also start and end with +% special spacing values. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \galley_display_begin: + { + \group_begin: + \galley_save_parameters: + \mode_if_vertical:TF + { + \galley_display_penalty:N \l_galley_display_begin_par_penalty_tl + \galley_display_vspace:N \l_galley_display_begin_par_vspace_tl + } + { + \galley_display_penalty:N \l_galley_display_begin_penalty_tl + \galley_display_vspace:N \l_galley_display_begin_vspace_tl + } + \par + } +% \end{macrocode} +% Two short-cuts for setting up any special penalty or vertical space. +% The idea is that the standard value is saved to the \enquote{restore} +% token list, before setting up the value to the special value needed +% in this one case. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \galley_display_penalty:N #1 + { + \tl_if_empty:NF #1 + { + \tl_gput_right:Nx \g_galley_restore_running_tl + { + \int_gset:Nn \exp_not:N \g_galley_penalty_int + { \int_use:N \g_galley_penalty_int } + } + \int_gset:Nn \g_galley_penalty_int {#1} + } + } +\cs_new_protected_nopar:Npn \galley_display_vspace:N #1 + { + \tl_if_empty:NF #1 + { + \tl_gput_right:Nx \g_galley_restore_running_tl + { + \skip_gset:Nn \exp_not:N \g_galley_vspace_skip + { \skip_use:N \g_galley_vspace_skip } + } + \skip_gset:Nn \g_galley_vspace_int {#1} + } + } +% \end{macrocode} +% The \cs{par} token at the end of the display needs to go in at the same +% group level as the text, hence this function cannot be placed using +% \cs{group_insert_after:N}. Resetting the meaning of the \cs{par} token +% needs to be carried out after the group used for the environment. +% As \LaTeXe{} already adds one group, there are two \enquote{escapes} +% here: the format version needs only one escape. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \galley_display_end: + { + \par + \galley_restore_parameters: + \group_end: +%<*package> + \group_insert_after:N \group_insert_after:N +% + \group_insert_after:N \galley_display_par_setup: + } +% \end{macrocode} +% The method used here is to assume that the next piece of horizontal +% mode material will follow on from the displayed output without an +% intervening \cs{par} token (probably a blank line). The meaning of the +% \cs{par} token is then altered so that a check can be made to see if +% this assumption was correct. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \galley_display_par_setup: + { + \bool_gset_false:N \g_galley_omit_next_indent_bool + \cs_set_eq:NN \par \galley_display_par: + } +% \end{macrocode} +% The \enquote{special} meaning of the paragraph token starts by putting +% things back to normal: there should never need to be more than one +% special paragraph marker in one group. If \TeX{} is in vertical mode, +% then there has been a paragraph token inserted, most likely by a +% blank line. Thus the next piece of material is a separate conceptual +% paragraph from the display. In that case, the assumption from above is +% undone and the indent is turned back on. On the other hand, for the +% case where \TeX{} is in horizontal mode then a \cs{tex_par:D} primitive +% is required in the same way as in \cs{galley_standard_par:}. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \galley_display_par: + { + \cs_set_eq:NN \par \galley_std_par: + \mode_if_vertical:TF + { + \par + \bool_gset_false:N \g_galley_omit_next_indent_bool + \galley_display_penalty:N \l_galley_display_end_par_penalty_tl + \galley_display_vspace:N \l_galley_display_end_par_vspace_tl + } + { + \galley_set_measure_and_parshape: + \group_begin: + \tex_par:D + \group_end: + \int_gadd:Nn \g_galley_previous_par_lines_int \tex_prevgraf:D + \galley_display_penalty:N \l_galley_display_end_penalty_tl + \galley_display_vspace:N \l_galley_display_end_vspace_tl + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \subsection{Insertions using \cs{tex_everypar:D}} +% +% The key to the entire galley mechanism is hooking into the +% \cs{tex_everypar:D} token register. This requires that the original +% is moved out of the way, with appropriate hooks left attached for +% further modification by other modules and by the user. This is all +% done such that there is no danger of accidentally deactivating the +% galley mechanism. +% +% \begin{macro}{\everypar} +% When used on top of \LaTeXe{} the original primitive name needs to be +% available without the risk of completely overwriting the new +% mechanism. This is implemented as a token register in case low-level +% \TeX{} is used. The \TeX{} primitive is set here as otherwise the +% \LaTeXe{} \cs{@nodocument} is never removed from the register. +% This precaution is not be needed for a stand-alone format. +% \begin{macrocode} +%<*initex> +\tex_everypar:D % TEMP + { + \bool_if:NTF \g_galley_begin_level_bool + { \galley_start_paragraph_first: } + { \galley_start_paragraph_std: } + } +% +%<*package> +\cs_undefine:N \everypar +\newtoks \everypar +\AtBeginDocument + { + \tex_everypar:D + { + \bool_if:NTF \g_galley_begin_level_bool + { \galley_start_paragraph_first: } + { \galley_start_paragraph_std: } + \tex_the:D \everypar + } + } +% +% \end{macrocode} +% \end{macro} +% +% \subsection{The galley mechanism} +% +% \begin{variable}{\g_galley_last_box} +% A temporary box to hold the box inserted by \TeX{} when a paragraph +% is inserted with an indent. The galley actually inserts the space +% (\emph{i.e.}~\cs{tex_parindent:D} is globally zero), but there is +% still an empty box to test for. +% \begin{macrocode} +\box_new:N \g_galley_last_box +% \end{macrocode} +% \end{variable} +% +% The \enquote{start of paragraph} routines are fired by \cs{tex_everypar:D}. +% This can take place within a group in a construction such as +% \begin{verbatim} +% ... end of last par. +% +% {\Large Start} of par +% \end{verbatim} +% and so anything applied here must be done globally. +% +% \begin{macro}[int]{\galley_start_paragraph_std:} +% The routine at the start of a paragraph starts by removing any +% (empty) indent box from the vertical list. As there may be vertical +% mode items still to insert, a \cs{tex_par:D} primitive is used to +% get back into vertical mode before they are tidied up. To get back +% again to horizontal mode, \cs{tex_noindent:D} can be used. To avoid +% an infinite loop, \cs{tex_everypar:D} is locally cleared before doing +% that. Back in horizontal mode, the horizontal mode items can be +% tidied up before sorting out any items which have been set on a +% single-paragraph basis. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \galley_start_paragraph_std: + { + \group_begin: + \box_gset_to_last:N \g_galley_last_box + \tex_par:D + \galley_insert_vertical_items: + \tex_everypar:D { } + \tex_noindent:D + \group_end: + \int_gzero:N \g_galley_previous_par_lines_int + \galley_insert_horizontal_items: + \galley_restore_running_parameters: + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[int]{\galley_start_paragraph_first:} +% For the very first paragraph in a galley, the code needs to avoid +% adding any unnecessary vertical items at the top as it will interfere with +% vertical positioning in \cs{tex_vtop:D}. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \galley_start_paragraph_first: + { + \bool_gset_false:N \g_galley_begin_level_bool + \mode_if_horizontal:TF + { + \group_begin: + \box_gset_to_last:N \g_galley_last_box + \tex_par:D + \galley_insert_vspace: + \tex_everypar:D { } + \tex_noindent:D + \group_end: + } + { \galley_insert_vspace: } + \galley_insert_horizontal_items: + \galley_restore_running_parameters: + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[int]{\galley_insert_vertical_items, \galley_insert_vspace:} +% The aim here is to insert the vertical items such that they attach to +% the correct place. This function is used as part of the \cs{tex_everypar:D} +% mechanism, meaning that the immediately-preceding item on the vertical +% list is the \cs{tex_parskip:D}, always zero-length but an implicit +% penalty. So any whatsits \enquote{attached} to the previous paragraph +% should stay glued on. After the whatsits, a penalty for +% breaking will be inserted. This will be the user penalty if supplied, +% or the running penalty unless the no-break flag is set. Finally, +% the inter-paragraph space is applied. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \galley_insert_vertical_items: + { + \g_galley_whatsit_previous_tl + \tl_gclear:N \g_galley_whatsit_previous_tl + \tl_if_empty:NTF \g_galley_interpar_penalty_user_tl + { + \bool_if:NTF \g_galley_nobreak_next_bool + { \tex_penalty:D \c_ten_thousand } + { \tex_penalty:D \l_galley_interpar_penalty_int } + } + { + \tex_penalty:D + \int_eval:w \g_galley_interpar_penalty_user_tl \int_eval_end: + \tl_gclear:N \g_galley_interpar_penalty_user_tl + } + \bool_gset_false:N \g_galley_nobreak_next_bool + \galley_insert_vspace: + } +% \end{macrocode} +% Inserting vertical space is set up as a separate function as it comes +% up in a few places. The idea here is that any user-set space will +% override the design value, and only one space is ever inserted. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \galley_insert_vspace: + { + \tl_if_empty:NTF \g_galley_interpar_vspace_user_tl + { \skip_vertical:N \l_galley_interpar_vspace_skip } + { + \skip_vertical:n { \g_galley_interpar_vspace_user_tl } + \tl_gclear:N \g_galley_interpar_vspace_user_tl + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[int]{\galley_insert_horizontal_items:} +% Horizontal mode objects start with the whatsits for the next paragraph. An +% indent is then included if the removed box was not void. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \galley_insert_horizontal_items: + { + \g_galley_whatsit_next_tl + \tl_gclear:N \g_galley_whatsit_next_tl + \bool_if:NT \g_galley_omit_next_indent_bool + { + \box_if_empty:NF \g_galley_last_box + { \hbox_to_wd:nn \l_galley_par_indent_dim { } } + } + \skip_horizontal:N \l_galley_par_begin_skip + \g_galley_par_begin_hook_tl + \bool_gset_true:N \g_galley_omit_next_indent_bool + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[int]{\galley_restore_running_parameters:} +% Restoring the ongoing parameters just means using the token list +% variable in which the appropriate assignments are stored. The +% list can then be cleared. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \galley_restore_running_parameters: + { + \g_galley_restore_running_tl + \tl_gclear:N \g_galley_restore_running_tl + } +% \end{macrocode} +% \end{macro} +% +% \subsection{Measure} +% +% \begin{macro} +% {\galley_margins_set_absolute:nn, \galley_margins_set_relative:nn} +% Setting the measure is just a question of adjusting margins, either +% in a relative or absolute sense. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \galley_margins_set_absolute:nn #1#2 + { + \dim_set:Nn \l_galley_total_left_margin_dim {#1} + \dim_set:Nn \l_galley_total_right_margin_dim {#2} + \dim_set:Nn \l_galley_text_width_dim + { + \l_galley_width_dim + - \l_galley_total_left_margin_dim + - \l_galley_total_right_margin_dim + } + } +\cs_new_protected_nopar:Npn \galley_margins_set_relative:nn #1#2 + { + \dim_add:Nn \l_galley_total_left_margin_dim {#1} + \dim_add:Nn \l_galley_total_right_margin_dim {#2} + \dim_set:Nn \l_galley_text_width_dim + { + \l_galley_width_dim + - \l_galley_total_left_margin_dim + - \l_galley_total_right_margin_dim + } + } +% \end{macrocode} +% \end{macro} +% +% \subsection{Paragraph shape} +% +% \begin{macro} +% { +% \galley_parshape_fixed_lines:nnn, +% \galley_parshape_fixed_lines:nVV, +% \galley_parshape_multi_par:nnnN, +% \galley_parshape_multi_par:nVVN, +% \galley_parshape_single_par:nnnN, +% \galley_parshape_single_par:nVVN +% } +% Setting the paragraph shape is easy as most of the real work is done +% later. So this is just a case of saving the various pieces of data +% to the correct locations. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \galley_parshape_fixed_lines:nnn #1#2#3 + { + \bool_gset_true:N \g_galley_parshape_set_bool + \bool_set_true:N \l_galley_parshape_fixed_lines_bool + \int_set:Nn \l_galley_parshape_std_lines_int {#1} + \clist_set:Nn \l_galley_parshape_left_indent_clist {#2} + \clist_set:Nn \l_galley_parshape_right_indent_clist {#3} + \bool_set_true:N \l_galley_parshape_resume_std_bool + } +\cs_new_protected_nopar:Npn \galley_parshape_multi_par:nnnN #1#2#3#4 + { + \bool_gset_true:N \g_galley_parshape_set_bool + \bool_set_true:N \l_galley_parshape_multipar_bool + \bool_set_false:N \l_galley_parshape_fixed_lines_bool + \int_set:Nn \l_galley_parshape_std_lines_int {#1} + \clist_set:Nn \l_galley_parshape_left_indent_clist {#2} + \clist_set:Nn \l_galley_parshape_right_indent_clist {#3} + \bool_set_eq:NN \l_galley_parshape_resume_std_bool #4 + } +\cs_new_protected_nopar:Npn \galley_parshape_single_par:nnnN #1#2#3#4 + { + \bool_gset_true:N \g_galley_parshape_set_bool + \bool_set_false:N \l_galley_parshape_multipar_bool + \bool_set_false:N \l_galley_parshape_fixed_lines_bool + \int_set:Nn \l_galley_parshape_std_lines_int {#1} + \clist_set:Nn \l_galley_parshape_left_indent_clist {#2} + \clist_set:Nn \l_galley_parshape_right_indent_clist {#3} + \bool_set_eq:NN \l_galley_parshape_resume_std_bool #4 + } +\cs_generate_variant:Nn \galley_parshape_fixed_lines:nnn { nVV } +\cs_generate_variant:Nn \galley_parshape_multi_par:nnnN { nVV } +\cs_generate_variant:Nn \galley_parshape_single_par:nnnN { nVV } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[int]{\galley_set_measure_and_parshape:} +% To set the paragraph shape for the current paragraph, there is a check to +% see if the measure alone should be used. If not, then the shape may be +% built by paragraph or based on the number of lines required. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \galley_set_measure_and_parshape: + { + \bool_if:NTF \g_galley_parshape_set_bool + { + \bool_if:NTF \l_galley_parshape_fixed_lines_bool + { + \int_compare:nNnTF \g_galley_previous_par_lines_int > \c_zero + { \galley_generate_parshape_lines: } + { \galley_generate_parshape: } + } + { + \bool_gset_eq:NN \g_galley_parshape_set_bool + \l_galley_parshape_multipar_bool + \galley_generate_parshape: + } + } + { + \tex_global:D \tex_parshape:D + \c_one + \dim_use:N \l_galley_total_left_margin_dim + \c_space_tl + \dim_use:N \l_galley_text_width_dim + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[int]{\galley_generate_parshape:} +% \begin{macro}[aux]{\galley_set_parshape_map:nn, \galley_set_parshape_map:oo} +% \begin{macro}[aux]{\galley_set_parshape_map_aux:nw} +% For a shape to apply on a paragraph basis, the two user-supplied comma +% lists are taken and converted into left-side offsets and line lengths. +% This is all dependent on the current measure. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \galley_generate_parshape: + { + \tex_global:D \tex_parshape:D + \int_eval:w + \l_galley_parshape_std_lines_int + + \int_min:nn + { \clist_length:N \l_galley_parshape_left_indent_clist } + { \clist_length:N \l_galley_parshape_right_indent_clist } + \bool_if:NT \l_galley_parshape_resume_std_bool { + 1 } + \int_eval_end: + \prg_replicate:nn \l_galley_parshape_std_lines_int + { + \dim_use:N \l_galley_total_left_margin_dim + \c_space_tl + \dim_use:N \l_galley_text_width_dim + \c_space_tl + } + \galley_set_parshape_map:oo + \l_galley_parshape_left_indent_clist + \l_galley_parshape_right_indent_clist + \bool_if:NT \l_galley_parshape_resume_std_bool + { + \c_space_tl + \dim_use:N \l_galley_total_left_margin_dim + \c_space_tl + \dim_use:N \l_galley_text_width_dim + } + } +\cs_new_nopar:Npn \galley_set_parshape_map:nn #1#2 + { \galley_set_parshape_map_aux:nw { } #1 , \q_mark #2 , \q_stop } +\cs_generate_variant:Nn \galley_set_parshape_map:nn { oo } +\cs_new_nopar:Npn \galley_set_parshape_map_aux:nw + #1#2 , #3 \q_mark #4 , #5 \q_stop + { + \bool_if:nTF { \tl_if_empty_p:n {#3} || \tl_if_empty_p:n {#4} } + { + #1 + \dim_eval:n { \l_galley_total_left_margin_dim + ( #2 ) } + \c_space_tl + \dim_eval:n { \l_galley_text_width_dim - ( ( #2 ) + ( #4 ) ) } + } + { + \galley_set_parshape_map_aux:nw + { + #1 + \dim_eval:n { \l_galley_total_left_margin_dim + ( #2 ) } + \c_space_tl + \dim_eval:n { \l_galley_text_width_dim - ( ( #2 ) + ( #4 ) ) } + \c_space_tl + } + #3 \q_mark #5 \q_stop + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[int]{\galley_generate_parshape_lines:} +% \begin{macro}[aux]{\galley_generate_parshape_lines_aux:n} +% The idea here is to construct a paragraph shape based on the remaining +% lines from the shape in the previous paragraph. If the previous paragraph +% was sufficiently long, then life is \enquote{back to normal} and the +% standard shape is set. If a special shape is needed, this is recovered +% from the paragraph shape using the \eTeX{} primitives. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \galley_generate_parshape_lines: + { + \int_compare:nNnTF \tex_parshape:D > \g_galley_previous_par_lines_int + { + \tex_global:D \tex_parshape:D + \int_eval:w \tex_parshape:D - \g_galley_previous_par_lines_int + \int_eval_end: + \prg_stepwise_function:nnnN + { \g_galley_previous_par_lines_int + \c_one } + \c_one \tex_parshape:D \galley_generate_parshape_lines_aux:n + } + { + \bool_gset_false:N \g_galley_parshape_set_bool + \tex_global:D \tex_parshape:D + \c_one + \dim_use:N \l_galley_total_left_margin_dim + \c_space_tl + \dim_use:N \l_galley_text_width_dim + } + } +\cs_new_nopar:Npn \galley_generate_parshape_lines_aux:n #1 + { + \etex_parshapeindent:D #1 + ~ + \etex_parshapelength:D #1 + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \subsection{Between paragraphs} +% +% \begin{macro}{\galley_set_user_penalty:n, \galley_set_user_vspace:n} +% User supplied penalties and spaces only apply for a single paragraph. +% In both cases, the input values need to be checked for the correct +% form but are stored as token lists. The \texttt{x}-type expansion +% deals with this nicely. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \galley_set_user_penalty:n #1 + { \tl_gset:Nx \g_galley_interpar_penalty_user_tl { \int_eval:n {#1} } } +\cs_new_protected_nopar:Npn \galley_set_user_vspace:n #1 + { \tl_gset:Nx \g_galley_interpar_vspace_user_tl { \skip_eval:n {#1} } } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\parskip} +% For the package, the \cs{parskip} primitive is moved out of the +% way as the code above is handling things. +% \begin{macrocode} +%<*package> +\dim_set:Nn \parskip \c_zero_dim +\cs_undefine:N \parskip +\skip_new:N \parskip +% +% \end{macrocode} +% \end{macro} +% +% \subsection{Formatting inside the paragraph} +% +% Justification is more complex than is necessarily desirable as the various +% \TeX{} parameters here interact in ways which mean that clear separation +% between different areas is not so easy. +% +% \begin{variable} +% { +% \l_galley_line_left_skip, +% \l_galley_line_right_skip, +% \l_galley_par_begin_skip, +% \l_galley_par_end_skip, +% \l_galley_par_indent_dim +% } +% The variables for setting paragraph shape: essentially, these are +% the \TeX{} set. +% \begin{macrocode} +\cs_new_eq:NN \l_galley_line_left_skip \tex_leftskip:D +\cs_new_eq:NN \l_galley_line_right_skip \tex_rightskip:D +\dim_new:N \l_galley_par_begin_skip +\cs_new_eq:NN \l_galley_par_end_skip \tex_parfillskip:D +\cs_new_eq:NN \l_galley_par_indent_dim \tex_parindent:D +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_galley_last_line_fit_int} +% One from \eTeX{}. +% \begin{macrocode} +\cs_new_eq:NN \l_galley_last_line_fit_int \etex_lastlinefit:D +% \end{macrocode} +% \end{variable} +% +% \subsection{Inter-word spacing} +% +% Setting the spacing between words and between sentences is important +% for achieving the correct output from ragged and centred output. At +% the same time, as far as possible the aim is to retain the spacing +% specified by the font designer and not to use arbitrary values +% (\emph{cf.}~the approach in \emph{The \TeX{}book}, p.~101). +% +% \begin{macro}{\galley_set_interword_spacing:N} +% The approach taken to setting a fixed space is to use the information +% from the current font to set the spacing. This means that only +% \cs{tex_spacefactor:D} needs to be set, while \cs{tex_xspacefactor:D} +% is left alone. However, this is only necessary for fonts which have +% a stretch component to the inter-word spacing in the first place, +% \emph{i.e.}~monospaced fonts require no changes. The code therefore +% checks whether there is any stretch, and if there is uses the fixed +% component to set \cs{tex_spaceskip:D}. If there is a stretch component +% (non-zero \cs{tex_fontdimen:D} \texttt{3}), then the \cs{teX_spaceskip:D} +% is set to the fixed component from the font. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \galley_set_interword_spacing:N #1 + { + \bool_if:NTF #1 + { % TODO Hook for font changes required! + \dim_compare:nNnTF { \tex_fontdimen:D \c_three \tex_font:D } + = \c_zero_dim + { \tex_spaceskip:D \c_zero_dim } + { \tex_spaceskip:D \tex_fontdimen:D \c_two \tex_font:D } + } + { \tex_spaceskip:D \c_zero_dim } + } +% \end{macrocode} +% \end{macro} +% +% \subsection{Hyphenation} +% +% \subsection{Line breaking} +% +% \begin{variable} +% { +% \l_galley_binop_penalty_int, +% \l_galley_double_hyphen_demerits_int, +% \l_galley_emergency_stretch_skip, +% \l_galley_final_hyphen_demerits_int, +% \l_galley_linebreak_badness_int, +% \l_galley_linebreak_fuzz_dim, +% \l_galley_linebreak_penalty_int, +% \l_galley_linebreak_pretolerance_int, +% \l_galley_linebreak_tolerance_int, +% \l_galley_mismatch_demerits_int, +% \l_galley_relation_penalty_int +% } +% All \TeX{} primitives renamed. +% \begin{macrocode} +\cs_new_eq:NN \l_galley_binop_penalty_int \tex_binoppenalty:D +\cs_new_eq:NN \l_galley_double_hyphen_demerits_int \tex_doublehyphendemerits:D +\cs_new_eq:NN \l_galley_emergency_stretch_skip \tex_emergencystretch:D +\cs_new_eq:NN \l_galley_final_hyphen_demerits_int \tex_finalhyphendemerits:D +\cs_new_eq:NN \l_galley_linebreak_badness_int \tex_hbadness:D +\cs_new_eq:NN \l_galley_linebreak_fuzz_dim \tex_hfuzz:D +\cs_new_eq:NN \l_galley_linebreak_penalty_int \tex_linepenalty:D +\cs_new_eq:NN \l_galley_linebreak_pretolerance_int \tex_pretolerance:D +\cs_new_eq:NN \l_galley_mismatch_demerits_int \tex_adjdemerits:D +\cs_new_eq:NN \l_galley_relation_penalty_int \tex_relpenalty:D +\cs_new_eq:NN \l_galley_linebreak_tolerance_int \tex_tolerance:D +% \end{macrocode} +% \end{variable} +% +% \subsection{Paragraph breaking} +% +% \begin{variable} +% { +% \l_galley_broken_penalty_int, +% \l_galley_interline_penalty_int, +% \l_galley_parbreak_badness_int, +% \l_galley_parbreak_fuzz_dim, +% \l_galley_post_display_penalty_int, +% \l_galley_pre_display_penalty_int +% +% } +% \TeX{} primitives renamed cover \emph{some} of this. +% \begin{macrocode} +\cs_new_eq:NN \l_galley_broken_penalty_int \tex_brokenpenalty:D +\cs_new_eq:NN \l_galley_interline_penalty_int \tex_interlinepenalty:D +\cs_new_eq:NN \l_galley_parbreak_badness_int \tex_vbadness:D +\cs_new_eq:NN \l_galley_parbreak_fuzz_dim \tex_vfuzz:D +\cs_new_eq:NN \l_galley_post_display_penalty_int \tex_postdisplaypenalty:D +\cs_new_eq:NN \l_galley_pre_display_penalty_int \tex_predisplaypenalty:D +% \end{macrocode} +% \end{variable} +% +% \begin{macro}{\l_galley_club_penalties_clist, \l_galley_line_penalties_clist} +% These are used to keep a track of information which cannot be +% extracted out of the primitives due to the overlapping nature of +% the meanings. +% \begin{macrocode} +\clist_new:N \l_galley_club_penalties_clist +\clist_new:N \l_galley_line_penalties_clist +% \end{macrocode} +% \end{macro} +% +% \begin{macro} +% { +% \galley_set_display_widow_penalties:n, +% \galley_set_display_widow_penalties:V, +% \galley_set_display_widow_penalties:v, +% \galley_set_widow_penalties:n, +% \galley_set_widow_penalties:V, +% \galley_set_widow_penalties:v +% } +% \begin{macro}[aux]{\galley_set_aux:n} +% By far the easiest penalties to deal with are those for widows. These +% work exactly as the names imply, with the display version only used +% immediately before display math, and the standard penalty used at the end +% of a paragraph. Thus there is only the need to convert the argument into +% the correct form, and add a $0$ penalty at the end to nullify the effect of +% repeating the last value. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \galley_set_display_widow_penalties:n #1 + { + \etex_displaywidowpenalties:D + \int_eval:w \clist_length:n {#1} + \c_one \int_eval_end: + \clist_map_function:nN {#1} \galley_set_aux:n + \c_zero + } +\cs_generate_variant:Nn \galley_set_display_widow_penalties:n { V , v } +\cs_new_protected_nopar:Npn \galley_set_widow_penalties:n #1 + { + \etex_widowpenalties:D + \int_eval:w \clist_length:n {#1} + \c_one \int_eval_end: + \clist_map_function:nN {#1} \galley_set_aux:n + \c_zero + } +\cs_generate_variant:Nn \galley_set_widow_penalties:n { V , v } +\cs_new_nopar:Npn \galley_set_aux:n #1 { #1 ~ } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro} +% { +% \galley_set_club_penalties:n, +% \galley_set_club_penalties:V, +% \galley_set_club_penalties:v, +% \galley_set_interline_penalties:n, +% \galley_set_interline_penalties:V, +% \galley_set_interline_penalties:v +% } +% Setting club or special line penalties is easy, as these are handled +% mainly by the interline set up function. The two concepts are essentially +% the same, but having two takes makes some special effects easier to +% carry out. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \galley_set_club_penalties:n #1 + { + \clist_set:Nn \l_galley_club_penalties_clist {#1} + \galley_calc_interline_penalties: + } +\cs_generate_variant:Nn \galley_set_club_penalties:n { V , v } +\cs_new_protected_nopar:Npn \galley_set_interline_penalties:n #1 + { + \clist_set:Nn \l_galley_line_penalties_clist {#1} + \galley_calc_interline_penalties: + } +\cs_generate_variant:Nn \galley_set_interline_penalties:n { V , v } +% \end{macrocode} +% \end{macro} +% +% \begin{macro} +% { +% \galley_set_display_club_penalties:n, +% \galley_set_display_club_penalties:V, +% \galley_set_display_club_penalties:v +% } +% Setting the display club penalties means first setting the primitive, +% then recalculating the interline array to allow for these new values. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \galley_set_display_club_penalties:n #1 + { + \etex_clubpenalties:D + \int_eval:w \clist_length:n {#1} + \c_one \int_eval_end: + \clist_map_function:nN {#1} \galley_set_aux:n + \c_zero + \galley_calc_interline_penalties: + } +\cs_generate_variant:Nn \galley_set_display_club_penalties:n { V , v } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\galley_set_interline_penalty:n} +% \begin{macro}[aux]{\galley_set_interline_penalty_aux:nn} +% \begin{macro}[aux] +% { +% \galley_set_interline_penalty_aux_i:n, +% \galley_set_interline_penalty_aux_ii:n +% } +% Dealing with the general interline penalty is handled in one shot. +% The idea is that for lines with no special penalty, the old general +% penalty is removed and the new one is added. If there is currently +% no shape set, then after adding the general interline value the +% generic build system is invoked (in case the +% \cs{etex_interlinepenalties:D} has accidentally been cleared). +% \begin{macrocode} +\cs_new_protected_nopar:Npn \galley_set_interline_penalty:n #1 + { + \int_compare:nNnTF { \etex_interlinepenalties:D \c_zero } = \c_zero + { + \etex_interlinepenalties:D \c_one \int_eval:w #1 \int_eval_end: + \galley_calc_interline_penalties: + } + { + \cs_set_nopar:Npn \galley_set_interline_penalty_aux_ii:n ##1 + { + \int_eval:w + \etex_interlinepenalties:D ##1 + - \etex_interlinepenalties:D \etex_interlinepenalties:D \c_zero + + #1 + \int_eval_end: + } + \exp_args:Nf \galley_set_interline_penalty_aux:nn + { \clist_length:N \l_galley_line_penalties_clist } {#1} + } + } +\cs_new_protected_nopar:Npn \galley_set_interline_penalty_aux:nn #1#2 + { + \etex_interlinepenalties:D + \etex_interlinepenalties:D \c_zero + \prg_stepwise_function:nnnN \c_one \c_one {#1} + \galley_set_interline_penalty_aux_i:n + \prg_stepwise_function:nnnN { #1 + \c_one } \c_one + { \etex_interlinepenalties:D \c_zero - \c_one } + \galley_set_interline_penalty_aux_ii:n + \int_eval:w #2 \int_eval_end: + } +\cs_new_nopar:Npn \galley_set_interline_penalty_aux_i:n #1 + { \etex_interlinepenalties:D \int_eval:w #1 \int_eval_end: } +\cs_new_nopar:Npn \galley_set_interline_penalty_aux_ii:n #1 { } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[int]{\galley_calc_interline_penalties:} +% \begin{macro}[aux]{\galley_calc_interline_penalties_aux:nn} +% \begin{macro}[aux] +% { +% \galley_calc_interline_penalties_aux_i:n, +% \galley_calc_interline_penalties_aux_ii:n +% } +% The underlying interline penalty array has to deal with club penalties, +% display club penalties and any special line penalties, and include +% the general interline penalty. These requirements lead to a rather +% complex requirement on how many lines to deal with. This is needed twice, +% so an \texttt{f}-type expansion is used to make life a little less +% complex. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \galley_calc_interline_penalties: + { + \exp_args:Nff \galley_calc_interline_penalties_aux:nn + { + \int_eval:n + { + \int_max:nn + { + \clist_length:N \l_galley_club_penalties_clist + + \c_one + } + { + \int_max:nn + { + \clist_length:N \l_galley_line_penalties_clist + + \c_one + } + { \etex_clubpenalties:D \c_zero } + } + } + } + { \clist_length:N \l_galley_line_penalties_clist } + } +% \end{macrocode} +% The idea is now to calculate the correct penalties. Two auxiliary functions +% are used: one for any \enquote{special penalty} lines and a second for +% normal lines. At the end of the process, the standard interline +% penalty is always included. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \galley_calc_interline_penalties_aux:nn + #1#2 + { + \etex_interlinepenalties:D #1 ~ + \prg_stepwise_function:nnnN \c_one \c_one {#2} + \galley_calc_interline_penalties_aux_i:n + \prg_stepwise_function:nnnN { #2 + \c_one } \c_one { #1 - \c_one } + \galley_calc_interline_penalties_aux_ii:n + \etex_interlinepenalties:D \etex_interlinepenalties:D \c_zero + } +\cs_new_nopar:Npn \galley_calc_interline_penalties_aux_i:n + #1 + { + \int_eval:w + \clist_item:Nn \l_galley_line_penalties_clist { #1 - \c_one } + + 0 \clist_item:Nn \l_galley_club_penalties_clist + { #1 - \c_one } + - \etex_clubpenalties:D #1 ~ + \int_eval_end: + } +\cs_new_nopar:Npn \galley_calc_interline_penalties_aux_ii:n + #1 + { + \int_eval:w + \etex_interlinepenalties:D \etex_interlinepenalties:D \c_zero + + 0 \clist_item:Nn \l_galley_club_penalties_clist + { #1 - \c_one } + - \etex_clubpenalties:D #1 ~ + \int_eval_end: + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro} +% { +% \galley_save_club_penalties:N, +% \galley_save_interline_penalties:N, +% \galley_save_display_club_penalties:N, +% \galley_save_display_widow_penalties:N, +% \galley_save_widow_penalties:N +% } +% \begin{macro}[aux] +% { +% \galley_save_display_club_penalties_aux:n, +% \galley_save_display_widow_penalties_aux:n, +% \galley_save_widow_penalties_aux:n +% } +% \begin{macro}{\galley_interline_penalty:} +% Saving the array penalties varies in complexity depending on how they are +% stored internally. The first two are easy: these are simply copies. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \galley_save_club_penalties:N #1 + { \clist_set_eq:NN #1 \l_galley_club_penalties_clist } +\cs_new_protected_nopar:Npn \galley_save_interline_penalties:N #1 + { \clist_set_eq:NN #1 \l_galley_line_penalties_clist } +% \end{macrocode} +% These all require appropriate mappings, using the fact that +% \cs{clist_set:Nx} will tidy up the excess comma. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \galley_save_display_club_penalties:N #1 + { + \clist_set:Nx #1 + { + \prg_stepwise_function:nnnN \c_one \c_one + { \etex_clubpenalties:D \c_zero - \c_one } + \galley_save_display_club_penalties:_aux:n + } + } +\cs_new_nopar:Npn \galley_save_display_club_penalties:_aux:n #1 + { \int_use:N \etex_clubpenaltes:D \int_eval:w #1 \int_eval_end: , } +\cs_new_protected_nopar:Npn \galley_save_display_widow_penalties:N #1 + { + \clist_set:Nx #1 + { + \prg_stepwise_function:nnnN \c_one \c_one + { \etex_displaywidowpenalties:D \c_zero - \c_one } + \galley_save_display_widow_penalties:_aux:n + } + } +\cs_new_nopar:Npn \galley_save_display_widow_penalties:_aux:n #1 + { \int_use:N \etex_displaywidowpenalties:D \int_eval:w #1 \int_eval_end: , } +\cs_new_protected_nopar:Npn \galley_save_widow_penalties:N #1 + { + \clist_set:Nx #1 + { + \prg_stepwise_function:nnnN \c_one \c_one + { \etex_widowpenalties:D \c_zero - \c_one } + \galley_save_widow_penalties:_aux:n + } + } +\cs_new_nopar:Npn \galley_save_widow_penalties:_aux:n #1 + { \int_use:N \etex_widowpenalties:D \int_eval:w #1 \int_eval_end: , } +% \end{macrocode} +% This one is not an array, but is stored in a primitive, so there is +% a simple conversion. The general interline penalty is always the +% last value in the primitive array. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \galley_interline_penalty: + { \int_use:N \etex_interlinepenalties:D \etex_interlinepenalties:D \c_zero } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \subsection{\LaTeXe{} fixes} +% +% \begin{macrocode} +%<*package> +% \end{macrocode} +% +% Purely for testing, some internal \LaTeXe{} functions are altered to work +% with the mechanism here. This material is not comprehensive: additions are +% made as-needed for test purposes. +% +% \begin{macro}{\@afterheading} +% Set some flags and hope for the best! +% \begin{macrocode} +\cs_set_protected_nopar:Npn \@afterheading + { + \bool_gset_true:N \g_galley_no_break_next_bool + \if@afterindent + \else + \bool_gset_true:N \galley_omit_next_indent_bool + \fi + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@hangfrom} +% The \cs{tex_handindent:D} primitive is no longer used, so the paragraph +% shape is set in a different way. As a result, the label is part of the +% same paragraph as the main body, hence the need to leave vertical mode. +% \begin{macrocode} +\cs_set_protected:Npn \@hangfrom #1 + { + \setbox \@tempboxa = \hbox { {#1} } + \galley_parshape_single_par:nnnN + \c_one + { \box_wd:N \@tempboxa } + \c_zero_dim + \c_false_bool + \bool_gset_true:N \g_galley_no_break_next_bool + \bool_gset_true:N \galley_omit_next_indent_bool + \box \@tempboxa + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@makefntext} +% Here, the paragraph indent is set up using the template system, and +% as in other places the code ensures that the label is set in horizontal +% mode. The flag used for omitting the indent is once again the galley +% one. +% \begin{macrocode} +\cs_set_protected:Npn \@makefntext #1 + { + \UseTemplate { justification } { std } { indent-width = 1 em } + \bool_gset_true:N \galley_omit_next_indent_bool + \galley_leave_vmode: + \hb@xt@ 1.8em { \hss \@makefnmark } #1 + } +% \end{macrocode} +% \end{macro} +% +% \begin{macrocode} +% +% \end{macrocode} +% +% \begin{macrocode} +% +% \end{macrocode} +% +% \end{implementation} +% +% \PrintIndex \ No newline at end of file diff --git a/Master/texmf-dist/source/latex/l3experimental/xgalley/xgalley.dtx b/Master/texmf-dist/source/latex/l3experimental/xgalley/xgalley.dtx new file mode 100644 index 00000000000..0dc1c85b610 --- /dev/null +++ b/Master/texmf-dist/source/latex/l3experimental/xgalley/xgalley.dtx @@ -0,0 +1,1602 @@ +% \iffalse +% +%% File: xgalley.dtx Copyright (C) 1999-2001, 2004-2009 Frank Mittelbach +%% (C) 2010,2011 The LaTeX3 Project +%% +%% It may be distributed and/or modified under the conditions of the +%% LaTeX Project Public License (LPPL), either version 1.3c of this +%% license or (at your option) any later version. The latest version +%% of this license is in the file +%% +%% http://www.latex-project.org/lppl.txt +%% +%% This file is part of the "l3experimental bundle" (The Work in LPPL) +%% and all files in that bundle must be distributed together. +%% +%% The released version of this bundle is available from CTAN. +%% +%% ----------------------------------------------------------------------- +%% +%% The development version of the bundle can be found at +%% +%% http://www.latex-project.org/svnroot/experimental/trunk/ +%% +%% for those people who are interested. +%% +%%%%%%%%%%% +%% NOTE: %% +%%%%%%%%%%% +%% +%% Snapshots taken from the repository represent work in progress and may +%% not work or may contain conflicting material! We therefore ask +%% people _not_ to put them into distributions, archives, etc. without +%% prior consultation with the LaTeX Project Team. +%% +%% ----------------------------------------------------------------------- +%% +% +%<*driver|package> +\RequirePackage{l3names} +\GetIdInfo$Id: xgalley.dtx 2895 2011-10-09 15:58:19Z joseph $ + {L3 Experimental galley} +% +%<*driver> +\documentclass[full]{l3doc} +\begin{document} + \DocInput{\jobname.dtx} +\end{document} +% +% \fi +% +% \title{^^A +% The \textsf{xgalley} package\\ Galley^^A +% \thanks{This file describes v\ExplFileVersion, +% last revised \ExplFileDate.}^^A +% } +% +% \author{^^A +% The \LaTeX3 Project\thanks +% {^^A +% E-mail: +% \href{mailto:latex-team@latex-project.org} +% {latex-team@latex-project.org}^^A +% }^^A +% } +% +% \date{Released \ExplFileDate} +% +% \maketitle +% +% \begin{documentation} +% +% \section{Introduction} +% +% In \LaTeX3 terminology a galley is a rectangular area which receives +% text and other material filling it from top. The vertically extend of +% a galley is normally not restricted: instead certain chunks are taken +% off the top of an already partially filled galley to form columns or +% similar areas on a page. This process is typically asynchronous but +% there are ways to control or change its behaviour. +% +% Examples for galleys are \enquote{the main galley}, where the +% continuous document data gets formatted into and from which columns +% and pages are constructed, and \enquote{vertical box galleys}, such +% as the body of a minipage environment. The latter galleys are +% typically not split after formatting, though there can be exceptions. +% +% \section{Formatting layers} +% +% The present module is mainly concerned with the formatting of text +% in galleys. The mechanism by which this is achieved uses four +% (somewhat) distinct layers, some of which can be addressed using the +% templates provided here. +% +% \subsection{Layer one: external dimensions} +% +% The bottom layer of the system is the external dimensions of the +% galley. Normally only the horizontal dimension is fixed externally, +% while the vertical (filling) dimension is unspecified. The external +% dimensions are fixed when starting a new galley, and are therefore +% not modifiable within the galley. +% +% There are no templates for setting this layer directly, although the +% external values are influenced by other parts of the system (for +% example when creating minipage environments). +% +% \subsection{Layer two: internal dimensions} +% +% The second layer is the internal dimensions of the galley: the +% \emph{measure} used for paragraph text and the position of the +% paragraph relative to the edges of the galley. +% +% This layer is normally accessed by higher-level templates +% \emph{via} the object type \texttt{measure}. Changes made using +% level two templates will often extend for large parts of a document +% (up to and including the entire document). +% +% \subsection{Layer three: paragraph shape} +% +% The third layer defines the paragraph shape within the measure as +% provided by the second layer. In the absence of any specification +% for that layer the paragraph shape used will be that of a +% rectangular area of the width of the current measure. +% +% There are some restrictions imposed on the shape of a paragraph by the +% underlying \TeX{} mechanisms. For example, cut out sections in +% paragraphs can be specified from the top of the paragraph but not from +% the bottom. +% +% \subsection{Layer four: formatting inside the paragraph} +% +% The forth layer deals with the paragraph formatting aspects such as +% hyphenation and justification within the paragraph (this is sometimes +% referred to as \enquote{\texttt{h\&j}} or \enquote{\texttt{hj}}). +% +% \section{Templates} +% +% \subsection{Layer two: internal dimensions} +% +% \begin{TemplateInterfaceDescription}{measure} +% \TemplateArgument{}{} ^^A A hack +% \TemplateSemantics +% Sets the width available to typeset material within the galley. +% The \meta{left margin} and \meta{right margin} values are used in the +% adjustment to over-ride any given in the template. Depending upon +% the template in use, the margins may be absolute (relative only to the +% edges of the galley) or relative (taking account of |measure| adjustments +% already made). The template applies to the galley from the point of +% us forward, unless over-ridden by another use of the |measure| +% object type. +% \end{TemplateInterfaceDescription} +% +% \begin{TemplateDescription}{measure}{absolute} +% \TemplateKey{left-margin}{length} +% {^^A +% The distance from the left edge of the galley to the left edge of +% the area for typeset material. A negative value will cause the +% typeset material to extend beyond the edge of the galley.^^A +% } +% {0 pt} +% \TemplateKey{right-margin}{length} +% {^^A +% The distance from the right edge of the galley to the right edge of +% the area for typeset material. A negative value will cause the +% typeset material to extend beyond the edge of the galley.^^A +% } +% {0 pt} +% \TemplateSemantics +% This template sets up the typesetting area such that typeset material +% runs from |left-margin| away from the left edge of the galley to +% |right-margin| away from the right edge of the galley. Both of these +% distances are absolute, \emph{i.e.}~no account is taken of previous +% |measure| settings. Either on or both values may be negative, in which +% case the typeset material will protrude outside of the edges of the +% galley. +% \end{TemplateDescription} +% +% \begin{TemplateDescription}{measure}{relative} +% \TemplateKey{left-margin}{length} +% {^^A +% The distance from the previous left margin of the typeset material +% within the galley to the new position of the left margin. A negative +% value will cause the new margin to be \enquote{outside} of the +% previous one, and \emph{may} cause the typeset material to +% protrude outside of the edge of the galley. +% } +% {0 pt} +% \TemplateKey{right-margin}{length} +% {^^A +% The distance from the previous right margin of the typeset material +% within the galley to the new position of the right margin. A negative +% value will cause the new margin to be \enquote{outside} of the +% previous one, and \emph{may} cause the typeset material to +% protrude outside of the edge of the galley. +% } +% {0 pt} +% \TemplateSemantics +% This template sets up the typesetting area such that it has margins +% |left-margin| and |right-margin| within those previously set. For a +% galley within no previous margins, this will result in margins relative +% to the edges of the galley. Within a galley in which the |measure| has +% already been set, using the |relative| template will indent the typeset +% material relative to the existing margins. +% Either on or both values may be negative, in which +% case the typeset material may protrude outside of the edges of the +% galley. +% \end{TemplateDescription} +% +% \subsection{Layer three: paragraph shape} +% +% \begin{TemplateInterfaceDescription}{parshape} +% \TemplateArgument{}{}^^A A hack +% \TemplateSemantics +% Template of this type define any shaping of the paragraph within the +% current measure of the galley. Thus they are used to generate +% \enquote{special} paragraph shapes, for example placing a cutout +% in one side of the paragraph. Typically, \texttt{parshape} templates +% will apply in a limited sense (to a single paragraph or a defined number of +% lines). However, \texttt{parshape} templates may also apply in an +% \enquote{ongoing} manner. +% +% Note that \texttt{parshape} templates do not alter any first-line +% indent for paragraphs (or any other \enquote{in paragraph} setting). +% Instead, they define a shape inside which the paragraph material will be +% placed. +% \end{TemplateInterfaceDescription} +% +% \begin{TemplateDescription}{parshape}{hang} +% \TemplateKey{indent}{length} +% {^^A +% The hanging indent from either the left- or right-hand margin +% (as determined by \texttt{on-left-side}).^^A +% } +% {0 pt} +% \TemplateKey{on-left-side}{boolean} +% {^^A +% If \texttt{true}, causes the hanging indent to be on the left-hand +% side of the paragraph.^^A +% } +% {true} +% \TemplateKey{lines}{integer} +% {The number of lines of full width before hanging begins.} +% {1} +% \TemplateSemantics +% Sets the paragraph shape such that the after a number of full-width +% lines, specified by |lines|, the paragraph is indented by the +% |indent| from a margin. If |on-left-side| is |true| this indent will be +% from the left-hand margin, otherwise it will be from the right. In either +% case, the indent is relative to the edge of the current |measure| and +% may be negative (in which case an outdent will result). +% This template type applies only to a single paragraph. +% \end{TemplateDescription} +% +% \begin{TemplateDescription}{parshape}{initial} +% \TemplateKey{indent}{length} +% {^^A +% The indent for the initial lines from either the left- or right-hand +% margin (as determined by \texttt{on-left-side}).^^A +% } +% {0 pt} +% \TemplateKey{on-left-side}{boolean} +% {^^A +% If \texttt{true}, causes the indent to be on the left-hand +% side of the paragraph.^^A +% } +% {true} +% \TemplateKey{lines}{integer} +% {The number of lines of indented lines before full-width line begins.} +% {2} +% \TemplateSemantics +% Sets the paragraph shape such that the first |lines| lines +% are indented by the |indent| given, before lines of full width begin. +% If |on-left-side| is |true| this indent will be +% from the left-hand margin, otherwise it will be from the right. In either +% case, the indent is relative to the edge of the current |measure| and +% may be negative (in which case an outdent will result). +% This template type applies only to a single paragraph. +% \end{TemplateDescription} +% +% \begin{TemplateDescription}{parshape}{std} +% \TemplateKey{}{}{}{} ^^A hack +% \TemplateSemantics +% Sets a rectangular paragraph shape which occupies the full width +% specified by the |measure|. It is therefore intended as a +% \enquote{do nothing} template for use where a paragraph shape is required +% but where no special formatting is needed. This template type applies only +% to a single paragraph. +% \end{TemplateDescription} +% +% \subsection{Layer four: formatting inside the paragraph} +% +% \begin{TemplateInterfaceDescription}{hyphenation} +% \TemplateArgument{}{} ^^A Hack +% \TemplateSemantics +% Controls whether hyphenation is attempted within the current +% galley. This object type may also alter the degree to which hyphenation +% is encouraged by manipulating the underlying \TeX{} parameters. This +% object type applies to the galley from the point of use forward. +% \end{TemplateInterfaceDescription} +% +% \begin{TemplateDescription}{hyphenation}{std} +% \TemplateKey{enable}{boolean} +% {Switches all hyphenation on or off.} +% {true} +% \TemplateKey{enable-upper-case} +% {boolean} +% {^^A +% Switches hyphenation on or off for words beginning with upper case +% letters.^^A +% } +% {true} +% \TemplateKey{penalty}{choice} +% {^^A +% Sets the degree to which \TeX{} is discouraged from undertaking +% hyphenation, from the choices |low|, |medium| and |high|.^^A +% } +% {low} +% \TemplateSemantics +% Determines both whether hyphenation is allowed at all, and if so to +% what degree it is discouraged. Setting |penalty| to |high| does not +% prevent hyphenation: this is only done if |enable| is set |false|. +% \end{TemplateDescription} +% +% \begin{TemplateInterfaceDescription}{justification} +% \TemplateArgument{}{} ^^A Hack +% \TemplateSemantics +% Controls the nature of justification undertaken within the galley. +% The template applies from the point of use forward. +% \end{TemplateInterfaceDescription} +% +% \begin{TemplateDescription}{justification}{std} +% \TemplateKey{end-skip}{skip} +% {The skip inserted to fill the last line of a paragraph.} +% {0 pt plus 1 fil} +% \TemplateKey{fixed-word-spacing}{boolean} +% {^^A +% Determines whether inter-word spacing has a stretch component (for +% non-monospaced fonts.^^A +% } +% {false} +% \TemplateKey{indent-width}{length} +% {^^A +% The length of the indent inserted at the start of the first line of a +% new paragraph.^^A +% } +% {} +% \TemplateKey{left-skip}{skip} +% {^^A +% The skip between the left margin of the galley and the left edge of a +% paragraph.^^A +% } +% {0 pt} +% \TemplateKey{right-skip}{skip} +% {^^A +% The skip between the right margin of the galley and the right edge of a +% paragraph.^^A +% } +% {0 pt} +% \TemplateKey{start-skip}{skip} +% {^^A +% The skip inserted in addition to |indent-width| at the start of a +% paragraph.^^A +% } +% {0 pt} +% \TemplateSemantics +% The |std| template for justification provides rubber lengths +% at the start and end of the paragraph and at each side of the paragraph. +% It also allows for both flexible and fixed inter-word spacing. The +% interaction between the settings is demonstrated in the selection of +% standard instances provided. +% \end{TemplateDescription} +% +% \begin{InstanceDescription}{justification}{justified}{std} +% \InstanceKey{indent-width}{15 pt} +% \InstanceSemantics +% Sets paragraphs fully-justified with the first line indented by +% |15 pt|. +% \end{InstanceDescription} +% +% \begin{InstanceDescription}{justification}{noindent}{std} +% \InstanceKey{end-skip}{15 pt plus 1 fil} +% \InstanceKey{indent-width}{0 pt} +% \InstanceSemantics +% Sets paragraphs fully-justified with no indent for the first line. To +% ensure that paragraphs have some visual distinction, the |end-skip| is +% set to insert some space in all cases. +% \end{InstanceDescription} +% +% \begin{TemplateDescription}{justification}{single} +% \TemplateKey{end-skip}{skip} +% {The skip inserted to fill the last line of a paragraph.} +% {0 pt plus 1 fil} +% \TemplateKey{fixed-word-spacing}{boolean} +% {^^A +% Determines whether inter-word spacing has a stretch component (for +% non-monospaced fonts.^^A +% } +% {false} +% \TemplateKey{indent-width}{length} +% {^^A +% The length of the indent inserted at the start of the first line of a +% new paragraph.^^A +% } +% {} +% \TemplateKey{left-skip}{skip} +% {^^A +% The skip between the left margin of the galley and the left edge of a +% paragraph.^^A +% } +% {0 pt} +% \TemplateKey{right-skip}{skip} +% {^^A +% The skip between the right margin of the galley and the right edge of a +% paragraph.^^A +% } +% {0 pt} +% \TemplateKey{start-skip}{skip} +% {^^A +% The skip inserted in addition to |indent-width| at the start of a +% paragraph.^^A +% } +% {0 pt} +% \TemplateKey{stretch-last-line}{boolean} +% { +% Determines whether inter-word spacing in the last line is stretched. +% If \texttt{true}, the spacing in the last line is stretched in the +% by the same factor as that in the penultimate line.^^A +% } +% {false} +% \TemplateSemantics +% The |single| template for justification provides rubber lengths +% at the start and end of the paragraph and at each side of the paragraph. +% It also allows for both flexible and fixed inter-word spacing. The +% interaction between the settings is demonstrated in the selection of +% standard instances provided. The template applies only to a single +% paragraph. +% \end{TemplateDescription} +% +% \begin{InstanceDescription}{justification}{centered}{std} +% \InstanceKey{end-skip}{0 pt} +% \InstanceKey{fixed-word-spacing}{true} +% \InstanceKey{indent-width}{0 pt} +% \InstanceKey{left-skip}{0 pt plus 1 em} +% \InstanceKey{right-skip}{0 pt plus 1 em} +% \InstanceSemantics +% Centres typeset material such that hyphenation will still occur and +% such that very short lines are discouraged. This is similar to the +% \LaTeXe{} \pkg{ragged2e} \env{Centering} environment. +% \end{InstanceDescription} +% +% \begin{InstanceDescription}{justification}{ragged-left}{std} +% \InstanceKey{end-skip}{0 pt} +% \InstanceKey{fixed-word-spacing}{true} +% \InstanceKey{indent-width}{0 pt} +% \InstanceKey{left-skip}{0 pt plus 2 em} +% \InstanceKey{right-skip}{0 pt} +% \InstanceSemantics +% Typesets material with a ragged left margin such that hyphenation will +% still occur and such that very short lines are discouraged. This is +% similar to the \LaTeXe{} \pkg{ragged2e} \env{RaggedLeft} environment. +% \end{InstanceDescription} +% +% \begin{InstanceDescription}{justification}{ragged-right}{std} +% \InstanceKey{end-skip}{0 pt} +% \InstanceKey{fixed-word-spacing}{true} +% \InstanceKey{indent-width}{0 pt} +% \InstanceKey{left-skip}{0 pt} +% \InstanceKey{right-skip}{0 pt plus 2 em} +% \InstanceSemantics +% Typesets material with a ragged right margin such that hyphenation will +% still occur and such that very short lines are discouraged. This is +% similar to the \LaTeXe{} \pkg{ragged2e} \env{RaggedLeft} environment. +% \end{InstanceDescription} +% +% \begin{InstanceDescription}{justification}{center}{std} +% \InstanceKey{end-skip}{0 pt} +% \InstanceKey{fixed-word-spacing}{true} +% \InstanceKey{indent-width}{0 pt} +% \InstanceKey{left-skip}{0 pt plus 1 fil} +% \InstanceKey{right-skip}{0 pt plus 1 fil} +% \InstanceSemantics +% Centres typeset material such that hyphenation is strongly discouraged +% and short lines are allowed. This template is suited to centring arbitrary +% material (such as boxes) rather than centring text. In the later case, the +% |centered| instance should be used. +% \end{InstanceDescription} +% +% \begin{TemplateDescription}{justification}{compound} +% \TemplateKey{first-paragraph}{instance} +% {Justification for the first paragraph.} +% {} +% \TemplateKey{other-paragraphs}{instance} +% {Justification for the remaining paragraphs.} +% {} +% \TemplateSemantics +% Here, both keys should themselves be instances of the |justification| +% template. The |compound| template is used to set up a single +% \enquote{non-standard} paragraph followed by \enquote{standard} ones. +% For example, it can be used to ensure that one |noindent| paragraph is +% then followed by |std| justification. +% \end{TemplateDescription} +% +% \begin{TemplateInterfaceDescription}{line-breaking} +% \TemplateArgument{}{} ^^A Hack +% \TemplateSemantics +% Controls the line breaking attempted by \TeX{} when typesetting +% material for the galley. This does not include whether words are +% hyphenated, which is handled separately. +% \end{TemplateInterfaceDescription} +% +% \begin{TemplateDescription}{line-breaking}{std} +% \TemplateKey{badness}{integer} +% {^^A +% Boundary that if exceeded will cause \TeX{} to report an +% underfull line.^^A +% } +% {1000} +% \TemplateKey{binop-penalty}{integer} +% {^^A +% Penalty charged if an inline math formula is broken at a +% binary operator.^^A +% } +% {700} +% \TemplateKey{double-hyphen-demerits}{integer} +% {^^A +% Extra demerit charge of two (or more) lines in succession end +% in a hyphen.^^A +% } +% {10 000} +% \TemplateKey{emergency-stretch}{skip} +% {^^A +% Additional stretch assumed for each line if no better line breaking +% can be found without it. This stretch is not actually added to lines, +% so its use may result in underfull box warnings.^^A +% } +% {0 pt} +% \TemplateKey{final-hyphen-demerits}{integer} +% {Extra demerit charge if the second last line is hyphenated.} +% {5000} +% \TemplateKey{fuzz}{length} +% {Boundary below overfull lines are not reported.} +% {0.1 pt} +% \TemplateKey{mismatch-demerits}{integer} +% {^^A +% Extra demerit charge if two visually incompatible lines follow +% each other.^^A +% } +% {10000} +% \TemplateKey{line-penalty}{integer} +% {^^A +% Extra penalty charged per line in the paragraph. By making +% this penalty higher \TeX{} will try harder to produce compact +% paragraphs.^^A +% } +% {10} +% \TemplateKey{pretolerance}{integer} +% {^^A +% Maximum tolerance allowed for individual lines to break the +% paragraph without attempting hyphenation.^^A +% } +% {100} +% \TemplateKey{relation-penalty}{integer} +% {^^A +% Penalty charged if an inline math formula is broken at a +% relational symbol.^^A +% } +% {500} +% \TemplateKey{tolerance}{integer} +% {^^A +% Maximum tolerance allowed for individual lines when breaking a +% paragraph while attempting hyphenation (if this limit can't be +% met \texttt{emergency-stretch} comes into play).^^A +% } +% {200} +% \TemplateSemantics +% This is an interface to the underlying \TeX{} system for determining +% line breaking. +% \end{TemplateDescription} +% +% \subsection{Between paragraphs} +% +% \begin{TemplateInterfaceDescription}{paragraph-breaking} +% \TemplateArgument{}{} ^^A Hack +% \TemplateSemantics +% This object type determines how \TeX{} determines the behaviour when +% the paragraph-breaking algorithm is calculating whether to break up a +% paragraph. Thus for example an instance of this object type may prevent +% breaks within a paragraph, forbid widows or orphans, \emph{etc.} +% \end{TemplateInterfaceDescription} +% +% \begin{TemplateDescription}{paragraph-breaking}{std} +% \TemplateKey{badness}{integer} +% {^^A +% Boundary that if exceeded will cause \TeX{} to report an +% underfull vertical box.^^A +% } +% {1000} +% \TemplateKey{broken-penalty}{integer} +% {Penalty for page breaking after a hyphenated line.} +% {100} +% \TemplateKey{club-penalty}{integer} +% {Penalty for generating a club line when page breaking.} +% {150} +% \TemplateKey{display-club-penalty}{integer} +% {^^A +% Penalty for breaking between to leave a club line after display +% math.^^A +% } +% {150} +% \TemplateKey{display-widow-penalty}{integer} +% {^^A +% Penalty for breaking between to leave a widow line before display +% math.^^A +% } +% {150} +% \TemplateKey{fuzz}{length} +% {Boundary below which overfull vertical boxes are not reported.} +% {0.1 pt} +% \TemplateKey{interline-penalty}{integer} +% {Penalty for breaking between lines in a paragraph.} +% {0} +% \TemplateKey{pre-display-penalty}{integer} +% {Penalty for breaking between immediately before display math material.} +% {10 000} +% \TemplateKey{post-display-penalty}{integer} +% {Penalty for breaking between immediately after display math material.} +% {0} +% \TemplateKey{widow-penalty}{integer} +% {Penalty for generating a widow line when page breaking.} +% {150} +% \TemplateSemantics +% This template provides an interface to the underlying \TeX{} mechanism for +% controlling page breaking. The template applies on an ongoing basis to all +% paragraphs after the template is used. +% \end{TemplateDescription} +% +% \begin{InstanceDescription}{paragraph-breaking}{std}{std} +% \InstanceSemantics +% Sets paragraphs such that they can break with widows and orphans +% discouraged but not prevented. Breaks are possible after display math +% material but no immediately before it. +% \end{InstanceDescription} +% +% \begin{InstanceDescription}{paragraph-breaking}{nobreak}{std} +% \InstanceKey{interline-penalty}{10 000} +% \InstanceKey{post-display-penalty}{10 000} +% \InstanceSemantics +% Sets paragraphs such that they cannot be broken at all (as far as is +% possible in \TeX{}). +% \end{InstanceDescription} +% +% \begin{InstanceDescription}{paragraph-breaking}{nolone}{std} +% \InstanceKey{club-penalty}{10 000} +% \InstanceKey{display-widow-penalty}{10 000} +% \InstanceKey{widow-penalty}{10 000} +% \InstanceSemantics +% Sets paragraphs such that they cannot be broken to leave a club or +% widow line (as far as is possible in \TeX{}). +% \end{InstanceDescription} +% +% \begin{TemplateDescription}{paragraph-breaking}{single} +% \TemplateKey{badness}{integer} +% {^^A +% Boundary that if exceeded will cause \TeX{} to report an +% underfull vertical box.^^A +% } +% {\meta{none}} +% \TemplateKey{broken-penalty}{integer} +% {Penalty for page breaking after a hyphenated line.} +% {\meta{none}} +% \TemplateKey{club-penalty}{integer} +% {Penalty for generating a club line when page breaking.} +% {\meta{none}} +% \TemplateKey{display-club-penalty}{integer} +% {^^A +% Penalty for breaking between to leave a club line after display +% math.^^A +% } +% {\meta{none}} +% \TemplateKey{display-widow-penalty}{integer} +% {^^A +% Penalty for breaking between to leave a widow line before display +% math.^^A +% } +% {\meta{none}} +% \TemplateKey{fuzz}{length} +% {Boundary below which overfull vertical boxes are not reported.} +% {\meta{none}} +% \TemplateKey{interline-penalty}{integer} +% {Penalty for breaking between lines in a paragraph.} +% {\meta{none}} +% \TemplateKey{pre-display-penalty}{integer} +% {Penalty for breaking between immediately before display math material.} +% {\meta{none}} +% \TemplateKey{post-display-penalty}{integer} +% {Penalty for breaking between immediately after display math material.} +% {\meta{none}} +% \TemplateKey{widow-penalty}{integer} +% {Penalty for generating a widow line when page breaking.} +% {\meta{none}} +% \TemplateSemantics +% This template provides an interface to the underlying \TeX{} mechanism for +% controlling page breaking. The template applies only to the next paragraph, +% and can thus be used to achieve effects such as non-breaking paragraphs. +% \end{TemplateDescription} +% +% \begin{InstanceDescription}{paragraph-breaking}{single-std}{single} +% \InstanceSemantics +% Sets the next paragraph such that it can break with widows and orphans +% discouraged but not prevented. Breaks are possible after display math +% material but no immediately before it. +% \end{InstanceDescription} +% +% \begin{InstanceDescription}{paragraph-breaking}{single-nobreak}{single} +% \InstanceKey{interline-penalty}{10 000} +% \InstanceKey{post-display-penalty}{10 000} +% \InstanceSemantics +% Sets the next paragraph such that it cannot be broken at all (as far as is +% possible in \TeX{}). +% \end{InstanceDescription} +% +% \begin{InstanceDescription}{paragraph-breaking}{single-noclub}{single} +% \InstanceKey{club-penalty}{10 000} +% \InstanceKey{display-club-penalty}{10 000} +% \InstanceSemantics +% Sets the next paragraph such that it cannot be broken to leave a club +% line (as far as is possible in \TeX{}). +% \end{InstanceDescription} +% +% \begin{InstanceDescription}{paragraph-breaking}{single-nolone}{single} +% \InstanceKey{club-penalty}{10 000} +% \InstanceKey{display-club-penalty}{10 000} +% \InstanceKey{display-widow-penalty}{10 000} +% \InstanceKey{widow-penalty}{10 000} +% \InstanceSemantics +% Sets the next paragraph such that it cannot be broken to leave a club or +% widow line (as far as is possible in \TeX{}). +% \end{InstanceDescription} +% +% \begin{InstanceDescription}{paragraph-breaking}{single-nowidow}{single} +% \InstanceKey{display-widow-penalty}{10 000} +% \InstanceKey{widow-penalty}{10 000} +% \InstanceSemantics +% Sets the next paragraph such that it cannot be broken to leave a +% widow line (as far as is possible in \TeX{}). +% \end{InstanceDescription} +% +% \end{documentation} +% +% \begin{implementation} +% +% \section{\pkg{xgalley} Implementation} +% +% This module provided a template-level interface for the \LaTeX3 galley. As +% such, the code here is intended for design-level changes which apply to +% large blocks. The variables provided are therefore used only for supporting +% the templates, while any documented interfaces are in \pkg{l3galley}. +% +% \begin{macrocode} +%<*package> +% \end{macrocode} +% +% \begin{macrocode} +\ProvidesExplPackage + {\ExplFileName}{\ExplFileDate}{\ExplFileVersion}{\ExplFileDescription} +\RequirePackage{xparse,xtemplate,l3galley} +% \end{macrocode} +% +% \subsection{Variables} +% +% \begin{variable}{\l_galley_tmpa_clist, \l_galley_tmpb_clist} +% Scratch space. +% \begin{macrocode} +\clist_new:N \l_galley_tmpa_clist +\clist_new:N \l_galley_tmpb_clist +% \end{macrocode} +% \end{variable} +% +% \subsection{Layer two: internal dimensions} +% +% There is a single object type for level two, the \texttt{measure} for +% the text in the galley. There are no arguments, as the measure is a design +% concept. +% \begin{macrocode} +\DeclareObjectType { measure } { 0 } +% \end{macrocode} +% +% There are two templates for galley measures: absolute and relative. +% Both use the same interface. +% \begin{macrocode} +\DeclareTemplateInterface { measure } { absolute } { 0 } + { + left-margin : length = 0 pt , + right-margin : length = 0 pt + } +\DeclareTemplateInterface { measure } { relative } { 0 } + { + left-margin : length = 0 pt , + right-margin : length = 0 pt + } +% \end{macrocode} +% +% \begin{variable}{\l_galley_left_margin_dim, \l_galley_right_margin_dim} +% In the \texttt{absolute} template, the two margin values are relative +% to the edges of the galley. This means that any existing offset or +% line-length adjustment are ignored. +% \begin{macrocode} +%<*package> +\cs_new_eq:NN \l_galley_left_margin_dim \leftmargin +% +%<*package> +\cs_new_eq:NN \l_galley_right_margin_dim \rightmargin +% +\DeclareTemplateCode { measure } { absolute } { 0 } + { + left-margin = \l_galley_left_margin_dim , + right-margin = \l_galley_right_margin_dim + } + { + \AssignTemplateKeys + \galley_margins_set_absolute:nn \l_galley_left_margin_dim + \l_galley_right_margin_dim + } +% \end{macrocode} +% On the other hand, the \texttt{relative} template works relative to +% the current indentation at both sides. +% \begin{macrocode} +\DeclareTemplateCode { measure } { relative } { 0 } + { + left-margin = \l_galley_left_margin_dim , + right-margin = \l_galley_right_margin_dim + } + { + \AssignTemplateKeys + \galley_margins_set_relative:nn \l_galley_left_margin_dim + \l_galley_right_margin_dim + } +% \end{macrocode} +% \end{variable} +% +% \subsection{Layer three: paragraph shape} +% +% The object type \texttt{parshape} is a somewhat extended interface +% to the \TeX{} \cs{tex_parshape:D} primitive. As with the \texttt{measure}, +% the \texttt{parshape} template has no arguments as it is essentially a +% design-oriented concept. +% \begin{macrocode} +\DeclareObjectType { parshape } { 0 } +% \end{macrocode} +% +% There are two standard templates for paragraph shapes which do +% something, both with the same interface. The \texttt{hang} template +% provides one or more standard lines followed by a hanging paragraph, +% while the \texttt{initial} template cuts out a space at the start of +% the paragraph. +% \begin{macrocode} +\DeclareTemplateInterface { parshape } { hang } { 0 } + { + indent : length = 0 pt , + on-left-side : boolean = true , + lines : integer = 1 + } +\DeclareTemplateInterface { parshape } { initial } { 0 } + { + indent : length = 0 pt , + on-left-side : boolean = true , + lines : integer = 2 + } +% \end{macrocode} +% \begin{variable}{\l_galley_parshape_indent_dim} +% \begin{variable}{\l_galley_parshape_on_left_bool} +% \begin{variable}{\l_galley_parshape_lines_int} +% Both of the templates are implemented as special cases of the more +% general function defined earlier. +% \begin{macrocode} +\DeclareTemplateCode { parshape } { hang } { 0 } + { + indent = \l_galley_parshape_indent_dim , + on-left-side = \l_galley_parshape_on_left_bool , + lines = \l_galley_parshape_lines_int + } + { + \AssignTemplateKeys + \bool_if:NTF \l_galley_parshape_on_left_bool + { + \galley_parshape_single_par:nVVN + \l_galley_parshape_lines_int + \l_galley_parshape_indent_dim + \c_zero_dim + \c_false_bool + } + { + \galley_parshape_single_par:nVVN + \l_galley_parshape_lines_int + \c_zero_dim + \l_galley_parshape_indent_dim + \c_false_bool + } + } +\DeclareTemplateCode { parshape } { initial } { 0 } + { + indent = \l_galley_parshape_indent_dim , + on-left-side = \l_galley_parshape_on_left_bool , + lines = \l_galley_parshape_lines_int + } + { + \AssignTemplateKeys + \clist_clear:N \l_galley_tmpa_clist + \clist_clear:N \l_galley_tmpb_clist + \prg_replicate:nn { \l_galley_parshape_lines_int } + { + \clist_put_right:Nn \l_galley_tmpa_clist + { \l_galley_parshape_indent_dim } + \clist_put_right:Nn \l_galley_tmpb_clist + { \c_zero_dim } + } + \bool_if:NTF \l_galley_parshape_on_left_bool + { + \galley_parshape_single_par:nVVN + \c_zero + \l_galley_tmpa_clist + \l_galley_tmpb_clist + \c_true_bool + } + { + \galley_parshape_single_par:nVVN + \c_zero + \l_galley_tmpb_clist + \l_galley_tmpa_clist + \c_true_bool + } + } +% \end{macrocode} +% \end{variable} +% \end{variable} +% \end{variable} +% +% There is also a \enquote{do nothing} paragraph shape for cases where +% a template is needed but no action is desirable. +% \begin{macrocode} +\DeclareTemplateInterface { parshape } { std } { 0 } { } +\DeclareTemplateCode { parshape } { std } { 0 } { } { } +% \end{macrocode} +% +% \subsection{Layer four: formatting inside the paragraph} +% +% The first type of object within a paragraph is the hyphenation. This +% object needs no arguments. +% \begin{macrocode} +\DeclareObjectType { hyphenation } { 0 } +% \end{macrocode} +% +% There is only hyphenation template as standard. This provides a +% semi-flexible interface to the underlying \TeX{} methods. (The detail +% is therefore hidden within the implementation phase.) +% \begin{macrocode} +\DeclareTemplateInterface { hyphenation } { std } { 0 } + { + enable : boolean = true , + enable-upper-case : boolean = true , + penalty : choice { low , medium , high } = low + } +% \end{macrocode} +% The implementation for hyphenation mainly sets low-level values. +% The minimum number of characters after a hyphen is set directly, +% whereas the number before is not. This is so that +% \cs{tex_lefthyphenmin:D} can also be used to completely prevent +% hyphenation. +% \begin{macrocode} +\DeclareTemplateCode { hyphenation } { std } { 0 } + { + enable = \l_galley_hyphen_enable_bool , + enable-upper-case = \l_galley_hyphen_uppercase_bool , + penalty = + { + low = + { + \int_set:Nn \tex_hyphenpenalty:D { 51 } + \int_set:Nn \tex_exhyphenpenalty:D { 51 } + } , + medium = + { + \int_set:Nn \tex_hyphenpenalty:D { 151 } + \int_set:Nn \tex_exhyphenpenalty:D { 151 } + } , + high = + { + \int_set:Nn \tex_hyphenpenalty:D { 301 } + \int_set:Nn \tex_exhyphenpenalty:D { 301 } + } , + } + } + { + \AssignTemplateKeys + \int_set:Nn \tex_lefthyphenmin:D + { + \bool_if:NTF \l_galley_hyphen_enable_bool + { \l_galley_hyphen_left_int } { 63 } + } + \int_set:Nn \tex_uchyph:D + { \bool_if:NTF \l_galley_hyphen_uppercase_bool { 1 } { 0 } } + } +% \end{macrocode} +% At this stage, the default hyphenation character should be set and +% hyphenation should be enabled. +% \begin{macrocode} +%\UseTemplate { hyphenation } { std } { } +\tex_defaulthyphenchar:D 45 \scan_stop: +% \end{macrocode} +% +% \begin{variable}{\l_galley_justification_other_tl} +% Used for the reset system for justification: using this token list means +% that there is no need to remove anything from +% \cs{g_galley_restore_running_tl}. +% \begin{macrocode} +\tl_new:N \l_galley_justification_other_tl +% \end{macrocode} +% \end{variable} +% +% The second level four object is the justification, which again +% takes no arguments. +% \begin{macrocode} +\DeclareObjectType { justification } { 0 } +% \end{macrocode} +% There are two templates here with the same interface: the standard one +% to apply from this point onward, and one which applies only to a single +% paragraph. +% \begin{macrocode} +\DeclareTemplateInterface { justification } { std } { 0 } + { + end-skip : skip = 0 pt plus 1 fil , + fixed-word-spacing : boolean = false , + indent-width : length , + left-skip : skip = 0 pt , + right-skip : skip = 0 pt , + start-skip : skip = 0 pt , + stretch-last-line : boolean = false + } +\DeclareTemplateInterface { justification } { single } { 0 } + { + end-skip : skip = 0 pt plus 1 fil , + fixed-word-spacing : boolean = false , + indent-width : length , + left-skip : skip = 0 pt , + right-skip : skip = 0 pt , + start-skip : skip = 0 pt , + stretch-last-line : boolean = false + } +% \end{macrocode} +% \begin{variable}{\l_galley_fixed_spacing_bool} +% The implementation here is pretty simple as almost everything that +% goes on is a simple case of saving the settings, which are then +% applied either by \TeX{} itself or the rest of the galley system. +% \begin{macrocode} +\DeclareTemplateCode { justification } { std } { 0 } + { + end-skip = \l_galley_par_end_skip , + fixed-word-spacing = \l_galley_fixed_spacing_bool , + indent-width = \l_galley_par_indent_dim , + left-skip = \l_galley_line_left_skip , + right-skip = \l_galley_line_right_skip , + start-skip = \l_galley_par_begin_skip , + stretch-last-line = \l_galley_par_stretch_last_bool + } + { + \AssignTemplateKeys + \tl_clear:N \l_galley_justification_other_tl + \galley_set_interword_spacing:N \l_galley_fixed_spacing_bool + \bool_if:NTF \l_galley_par_stretch_last_bool + { \int_set_eq:NN \l_galley_last_line_fit_int \c_one_thousand } + { \int_zero:N \l_galley_last_line_fit_int } + \skip_set:Nn \@rightskip { \l_galley_line_right_skip } + } +% \end{macrocode} +% \end{variable} +% To deal with a single paragraph, the approach used is to save the current +% settings to the paragraph-reset code, then to assign the template in the +% same way as for the |std| template. +% \begin{macrocode} +\DeclareTemplateCode { justification } { single } { 0 } + { + end-skip = \l_galley_par_end_skip , + fixed-word-spacing = \l_galley_fixed_spacing_bool , + indent-width = \l_galley_par_indent_dim , + left-skip = \l_galley_line_left_skip , + right-skip = \l_galley_line_right_skip , + start-skip = \l_galley_par_begin_skip , + stretch-last-line = \l_galley_par_stretch_last_bool + } + { + \tl_put_left:Nx \l_galley_justification_other_tl + { + \skip_set:Nn \exp_not:N \l_galley_par_end_skip + { \skip_use:N \l_galley_par_end_skip } + \bool_if:NTF \l_galley_fixed_spacing_bool + { \bool_set_true:N \exp_not:N \l_galley_fixed_spacing_bool } + { \bool_set_false:N \exp_not:N \l_galley_fixed_spacing_bool } + \galley_set_interword_space:N + \exp_not:N \l_galley_fixed_spacing_bool + \dim_set:Nn \exp_not:N \l_galley_par_indent_dim + { \dim_use:N \l_galley_par_indent_dim } + \skip_set:Nn \l_galley_line_left_skip + { \skip_use:N \l_galley_line_left_skip } + \skip_set:Nn \exp_not:N \l_galley_line_right_skip + { \skip_use:N \l_galley_line_right_skip } + \skip_set:Nn \exp_not:N \l_galley_par_start_skip + { \skip_use:N \l_galley_par_start_skip } + \int_set:Nn \exp_not:N \l_galley_last_line_fit_int + { \int_use:N \l_galley_last_line_fit_int } + \skip_set:Nn \exp_not:N \@rightskip + { \skip_use:N \l_galley_line_right_skip } + } + \tl_gput_right:Nn \g_galley_restore_running_tl + { \l_galley_justification_other_tl } + \AssignTemplateKeys + \galley_set_interword_spacing:N \l_galley_fixed_spacing_bool + \bool_if:NTF \l_galley_par_stretch_last_bool + { \int_set_eq:NN \l_galley_last_line_fit_int \c_one_thousand } + { \int_zero:N \l_galley_last_line_fit_int } + \skip_set:Nn \@rightskip { \l_galley_line_right_skip } + } +% \end{macrocode} +% The standard instance for justification is very simple to set up as +% the default values for the template are set up for exactly this case. +% The advantage of this scheme is that at a design level altering the +% indent used for justified paragraphs is very easy to do. As this is +% the standard template for all \LaTeX3 documents, it is applied here. +% \begin{macrocode} +\DeclareInstance { justification } { justified } { std } + { indent-width = 15 pt } +\UseInstance { justification } { justified } +% \end{macrocode} +% The instance for no indentation at all but with justified text is +% intended for layouts which leave white space between paragraphs. With +% no indentation, some space has to be included at the end of each +% paragraph. This is set up to mirror the indent that has been removed. +% \begin{macrocode} +\DeclareInstance { justification } { noindent } { std } + { + end-skip = 15 pt plus 1 fil , + indent-width = 0 pt + } +% \end{macrocode} +% The other standard justification schemes are for text which is either +% centred or ragged. The settings here are taken from the \LaTeXe{} +% \pkg{ragged2e} package, as they maintain a reasonable appearance by +% ensuring that \TeX{} will not be too tolerant of very short lines. +% To keep the design clear here, no default values are relied on even +% though this would make the instance declarations shorter. +% \begin{macrocode} +\DeclareInstance { justification } { centered } { std } + { + end-skip = 0 pt , + fixed-word-spacing = true , + indent-width = 0 pt , + left-skip = 0 pt plus 1 em , + right-skip = 0 pt plus 1 em + } +\DeclareInstance { justification } { ragged-left } { std } + { + end-skip = 0 pt , + fixed-word-spacing = true , + indent-width = 0 pt , + left-skip = 0 pt plus 2 em , + right-skip = 0 pt + } +\DeclareInstance { justification } { ragged-right } { std } + { + end-skip = 0 pt plus 1 fil , + fixed-word-spacing = true , + indent-width = 0 pt , + left-skip = 0 pt , + right-skip = 0 pt plus 2 em + } +% \end{macrocode} +% The \texttt{centering} instance is used to centre material without +% hyphenation: this is used for centring arbitrary material rather +% than text. +% \begin{macrocode} +\DeclareInstance { justification } { centering } { std } + { + end-skip = 0 pt , + fixed-word-spacing = true , + indent-width = 0 pt , + left-skip = 0 pt plus 1 fil , + right-skip = 0 pt plus 1 fil + } +% \end{macrocode} +% +% \begin{macro}{\galley_justification_first:, \galley_justification_other:} +% A second form of justification template is the case where the first +% paragraph is different from all of the others. This is set up by +% getting the justification to reset itself after the first paragraph. +% The code built into the \texttt{std} version will ensure that any +% subsequent template use will over-ride the setting here correctly. +% \begin{macrocode} +\DeclareTemplateInterface { justification } { compound } { 0 } + { + first-paragraph : instance { justification } , + other-paragraphs : instance { justification } + } +\DeclareTemplateCode { justification } { compound } { 0 } + { + first-paragraph = \galley_justification_first: , + other-paragraphs = \galley_justification_other: + } + { + \AssignTemplateKeys + \galley_justification_first: + \tl_set:Nn \l_galley_justification_other_tl + { \galley_justification_other: } + \tl_gput_right:Nn \g_galley_restore_running_tl + { \l_galley_justification_other_tl } + } +% \end{macrocode} +% \end{macro} +% +% How \TeX{} breaks text into lines is influences by a number of +% parameters, most of which are not actually likely to change. These +% work with the \texttt{hyphenation} but are independent of whether +% any hyphenation is actually active. The math values here could be +% set up as a separate template, but in practice this seems likely to +% be overkill. +% \begin{macrocode} +\DeclareObjectType { line-breaking } { 0 } +% \end{macrocode} +% The only template provided for line breaking is a simple interface to +% \TeX{}'s parameters. There is not really much that can be added to this: +% after all, the way that penalties work is more or less arbitrary but +% works well! The default values given here are intended to be sensible +% for a lot of cases. +% \begin{macrocode} +\DeclareTemplateInterface { line-breaking } { std } { 0 } + { + badness : integer = 1000 , + binop-penalty : integer = 700 , + double-hyphen-demerits : integer = 10 000 , + emergency-stretch : skip = 0 pt , + final-hyphen-demerits : integer = 5000 , + fuzz : length = 0.1 pt , + line-penalty : integer = 10 , + mismatch-demerits : integer = 10 000 , + pretolerance : integer = 100 , + relation-penalty : integer = 500 , + tolerance : integer = 200 + } +\DeclareTemplateCode{ line-breaking } { std } { 0 } + { + badness = \l_galley_linebreak_badness_int , + binop-penalty = \l_galley_binop_penalty_int , + double-hyphen-demerits = \l_galley_double_hyphen_demerits_int , + emergency-stretch = \l_galley_emergency_stretch_skip , + final-hyphen-demerits = \l_galley_final_hyphen_demerits_int , + fuzz = \l_galley_linebreak_fuzz_dim , + line-penalty = \l_galley_linebreak_penalty_int , + mismatch-demerits = \l_galley_mismatch_demerits_int , + pretolerance = \l_galley_linebreak_pretolerance_int , + relation-penalty = \l_galley_relation_penalty_int , + tolerance = \l_galley_linebreak_tolerance_int + } + { \AssignTemplateKeys } +% \end{macrocode} +% The default values are set such that they are suitable for good +% quality typesetting. So the standard template changes nothing at +% all from the template. This instance should also be applied now, +% as it will then apply to the entire document unless changed +% deliberately. +% \begin{macrocode} +\DeclareInstance { line-breaking } { std } { std } { } +\UseInstance { line-breaking } { std } +% \end{macrocode} +% +% \subsection{Between paragraphs} +% +% \begin{variable} +% { +% \l_galley_club_penalty_int , +% \l_galley_display_club_penalty_int , +% \l_galley_display_widow_penalty_int , +% \l_galley_interline_penalty_int , +% \l_galley_widow_penalty_int +% } +% The second object here sets up how \TeX{} acts to break paragraphs at +% page boundaries. As with the \texttt{line-breaking} object, there is not +% much to do except provide an interface to the \TeX{} internals. The +% \texttt{std} template does \emph{not} make the \eTeX{} array nature of +% various penalties available. +% \begin{macrocode} +\DeclareObjectType { paragraph-breaking } { 0 } +\DeclareTemplateInterface { paragraph-breaking } { std } { 0 } + { + badness : integer = 1000 , + broken-penalty : integer = 100 , + club-penalty : integer = 150 , + display-club-penalty : integer = 150 , + display-widow-penalty : integer = 150 , + fuzz : length = 0.1 pt , + interline-penalty : integer = 0 , + post-display-penalty : integer = 0 , + pre-display-penalty : integer = 10 000 , + widow-penalty : integer = 150 + } +\DeclareTemplateCode { paragraph-breaking } { std } { 0 } + { + badness = \l_galley_parbreak_badness_int , + broken-penalty = \l_galley_broken_penalty_int , + club-penalty = \l_galley_club_penalty_int , + display-club-penalty = \l_galley_display_club_penalty_int , + display-widow-penalty = \l_galley_display_widow_penalty_int , + fuzz = \l_galley_parbreak_fuzz_dim , + interline-penalty = \l_galley_interline_penalty_int , + post-display-penalty = \l_galley_post_display_penalty_int , + pre-display-penalty = \l_galley_pre_display_penalty_int , + widow-penalty = \l_galley_widow_penalty_int + } + { + \AssignTemplateKeys + \galley_set_club_penalties:V \l_galley_club_penalty_int + \galley_set_display_club_penalties:V \l_galley_display_club_penalty_int + \galley_set_display_widow_penalties:V \l_galley_display_widow_penalty_int + \galley_set_interline_penalty:n \l_galley_interline_penalty_int + \galley_set_widow_penalties:V \l_galley_widow_penalty_int + } +% \end{macrocode} +% \end{variable} +% The standard instance of the \texttt{paragraph-breaking} object simply +% applies the defaults: this is used. +% \begin{macrocode} +\DeclareInstance { paragraph-breaking } { std } { std } { } +\UseInstance { paragraph-breaking } { std } +% \end{macrocode} +% Two additional instances are provided: one to prevent any breaks +% at all, and a second to prevent any widow or club lines. +% \begin{macrocode} +\DeclareInstance { paragraph-breaking } { nobreak } { std } + { + interline-penalty = 10 000 , + post-display-penalty = 10 000 + } +\DeclareInstance { paragraph-breaking } { nolone } { std } + { + club-penalty = 10 000 , + display-club-penalty = 10 000 , + display-widow-penalty = 10 000 , + widow-penalty = 10 000 + } +% \end{macrocode} +% \begin{variable} +% { +% \l_galley_parbreak_badness_tl , +% \l_galley_broken_penalty_tl , +% \l_galley_club_penalties_tl , +% \l_galley_display_club_penalties_tl , +% \l_galley_display_widow_penalties_tl , +% \l_galley_parbreak_fuzz_tl , +% \l_galley_interline_penalty_tl , +% \l_galley_post_display_penalty_tl , +% \l_galley_pre_display_penalty_tl , +% \l_galley_widow_penalties_tl , +% \c_galley_parbreak_multi_seq , +% \c_galley_parbreak_single_seq +% } +% There is also a version of this code which applies only to one +% paragraph. This is done by storing the input in token list variables +% with no default: only explicit settings will be picked up. +% \begin{macrocode} +\DeclareTemplateInterface { paragraph-breaking } { single } { 0 } + { + badness : tokenlist , + broken-penalty : tokenlist , + club-penalty : tokenlist , + display-club-penalty : tokenlist , + display-widow-penalty : tokenlist , + fuzz : tokenlist , + interline-penalty : tokenlist , + post-display-penalty : tokenlist , + pre-display-penalty : tokenlist , + widow-penalty : tokenlist + } +\DeclareTemplateCode { paragraph-breaking } { single } { 0 } + { + badness = \l_galley_parbreak_badness_tl , + broken-penalty = \l_galley_broken_penalty_tl , + club-penalty = \l_galley_club_penalties_tl , + display-club-penalty = \l_galley_display_club_penalties_tl , + display-widow-penalty = \l_galley_display_widow_penalties_tl , + fuzz = \l_galley_parbreak_fuzz_tl , + interline-penalty = \l_galley_interline_penalty_tl , + post-display-penalty = \l_galley_post_display_penalty_tl , + pre-display-penalty = \l_galley_pre_display_penalty_tl , + widow-penalty = \l_galley_widow_penalties_tl + } + { + \AssignTemplateKeys +% \end{macrocode} +% The fuzz and interline penalties are handled explicitly as they have +% particular requirements. +% \begin{macrocode} + \tl_if_empty:NF \l_galley_interline_penalty_tl + { + \tl_gput_right:Nx \g_galley_par_after_hook_tl + { + \int_set:Nn \exp_not:N \l_galley_interline_penalty_int + { \galley_interline_penalty: } + } + \int_set:Nn \l_galley_interline_penalty_int + { \l_galley_interline_penalty_tl } + } + \tl_if_empty:NF \l_galley_parbreak_fuzz_tl + { + \tl_gput_right:Nx \g_galley_par_after_hook_tl + { + \dim_set:Nn \exp_not:N \l_galley_parbreak_fuzz_dim + { \dim_use:N \l_galley_parbreak_fuzz_dim } + } + \dim_set:Nn \l_galley_parbreak_fuzz_dim { \l_galley_parbreak_fuzz_tl } + } +% \end{macrocode} +% For the single integer penalties, a simple check is needed to save the +% value. +% \begin{macrocode} + \seq_map_inline:Nn \c_galley_parbreak_single_seq + { + \tl_if_empty:cF { l_galley_ ##1 _tl } + { + \tl_gput_right:Nx \g_galley_par_after_hook_tl + { + \int_set:Nn \exp_not:c { l_galley_ ##1 _int } + { \int_use:c { l_galley_ ##1 _int } } + } + \int_set:cn { l_galley_ ##1 _int } + { \tl_use:c { l_galley_ ##1 _tl } } + } + } +% \end{macrocode} +% A bit more complex for the array penalties. Although the interface here +% does not expose the arrays, it is necessary to correctly save them. +% \begin{macrocode} + \seq_map_inline:Nn \c_galley_parbreak_multi_seq + { + \tl_if_empty:cF { l_galley_ ##1 _tl } + { + \use:c { galley_save_ ##1 :N } \l_galley_tmpa_clist + \tl_gput_right:Nx \g_galley_par_after_hook_tl + { + \exp_not:c { galley_set_ ##1 :n } + { \exp_not:o \l_galley_tmpa_clist } + } + \use:c { galley_set_ ##1 :v } { l_galley_ ##1 _tl } + } + } + } +\seq_new:N \c_galley_parbreak_multi_seq +\seq_gput_right:Nn \c_galley_parbreak_multi_seq { club_penalties } +\seq_gput_right:Nn \c_galley_parbreak_multi_seq { display_club_penalties } +\seq_gput_right:Nn \c_galley_parbreak_multi_seq { display_widow_penalties } +\seq_gput_right:Nn \c_galley_parbreak_multi_seq { widow_penalties } +\seq_new:N \c_galley_parbreak_single_seq +\seq_gput_right:Nn \c_galley_parbreak_single_seq { parbreak_badness } +\seq_gput_right:Nn \c_galley_parbreak_single_seq { broken_penalty } +\seq_gput_right:Nn \c_galley_parbreak_single_seq { post_display_penalty } +\seq_gput_right:Nn \c_galley_parbreak_single_seq { pre_display_penalty } +% \end{macrocode} +% \end{variable} +% \begin{macrocode} +\DeclareInstance { paragraph-breaking } { single-std } { single } { } +\DeclareInstance { paragraph-breaking } { single-nobreak } { single } + { + interline-penalty = 10 000 , + post-display-penalty = 10 000 + } +\DeclareInstance { paragraph-breaking } { single-noclub } { single } + { + club-penalty = 10 000 , + display-club-penalty = 10 000 + } +\DeclareInstance { paragraph-breaking } { single-nolone } { single } + { + club-penalty = 10 000 , + display-club-penalty = 10 000 , + display-widow-penalty = 10 000 , + widow-penalty = 10 000 + } +\DeclareInstance { paragraph-breaking } { single-nowidow } { single } + { + display-widow-penalty = 10 000 , + widow-penalty = 10 000 + } +% \end{macrocode} +% +% \subsection{Templates for display material} +% +% To allow special handling of display-like material, templates are +% needed at the beginning and end of the block which set up any special +% space or breaks. These need to be optional, and so are stored as token +% lists: rather than \enquote{magic} values, empty lists indicate that +% standard settings are to be used. To ensure that the error checking +% needed takes place early, each token list is re-set with the +% appropriate evaluation. +% \begin{macrocode} +\DeclareObjectType { display-begin } { 0 } +\DeclareObjectType { display-end } { 0 } +\DeclareTemplateInterface { display-begin } { std } { 0 } + { + par-penalty : tokenlist , + par-space : tokenlist , + penalty : tokenlist , + space : tokenlist + } +\DeclareTemplateInterface { display-end } { std } { 0 } + { + par-penalty : tokenlist , + par-space : tokenlist , + penalty : tokenlist , + space : tokenlist + } +\DeclareTemplateCode { display-begin } { std } { 0 } + { + par-penalty = \l_galley_display_begin_par_vpenalty_tl , + par-space = \l_galley_display_begin_par_vspace_tl , + penalty = \l_galley_display_begin_vpenalty_tl , + space = \l_galley_display_begin_vspace_tl + } + { + \AssignTemplateKeys + \tl_if_empty:NF \l_galley_display_begin_par_vpenalty_tl + { + \tl_set:Nx \l_galley_display_begin_par_vpenalty_tl + { \int_eval:n { \l_galley_display_begin_par_vpenalty_tl } } + } + \tl_if_empty:NF \l_galley_display_begin_par_vspace_tl + { + \tl_set:Nx \l_galley_display_begin_par_vspace_tl + { \skip_eval:n { \l_galley_display_begin_par_vspace_tl } } + } + \tl_if_empty:NF \l_galley_display_begin_vpenalty_tl + { + \tl_set:Nx \l_galley_display_begin_vpenalty_tl + { \int_eval:n { \l_galley_display_begin_vpenalty_tl } } + } + \tl_if_empty:NF \l_galley_display_begin_vspace_tl + { + \tl_set:Nx \l_galley_display_begin_vspace_tl + { \skip_eval:n { \l_galley_display_begin_vspace_tl } } + } + } +\DeclareTemplateCode { display-end } { std } { 0 } + { + par-penalty = \l_galley_display_end_par_vpenalty_tl , + par-space = \l_galley_display_end_par_vspace_tl , + penalty = \l_galley_display_end_vpenalty_tl , + space = \l_galley_display_end_vspace_tl + } + { + \AssignTemplateKeys + \tl_if_empty:NF \l_galley_display_end_par_vpenalty_tl + { + \tl_set:Nx \l_galley_display_end_par_vpenalty_tl + { \int_eval:n { \l_galley_display_end_par_vpenalty_tl } } + } + \tl_if_empty:NF \l_galley_display_end_par_vspace_tl + { + \tl_set:Nx \l_galley_display_end_par_vspace_tl + { \skip_eval:n { \l_galley_display_end_par_vspace_tl } } + } + \tl_if_empty:NF \l_galley_display_end_vpenalty_tl + { + \tl_set:Nx \l_galley_display_end_vpenalty_tl + { \int_eval:n { \l_galley_display_end_vpenalty_tl } } + } + \tl_if_empty:NF \l_galley_display_end_vspace_tl + { + \tl_set:Nx \l_galley_display_end_vspace_tl + { \skip_eval:n { \l_galley_display_end_vspace_tl } } + } + } +% \end{macrocode} +% +% \subsection{\LaTeXe{} functions} +% +% \begin{macro}{\nobreak} +% \begin{macro}{\noindent} +% \begin{macro}{\vspace} +% In package mode, some of \LaTeXe{}'s functions are re-implemented using +% the galley system. Not all of the optional arguments currently work! +% \begin{macrocode} +\RenewDocumentCommand \nobreak { } + { \bool_gset_true:N \g_galley_no_break_next_bool } +\RenewDocumentCommand \noindent { } + { \bool_gset_false:N \g_galley_omit_next_indent_bool } +\RenewDocumentCommand \vspace { s m } { \galley_set_vspace_user:n {#2} } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macrocode} +% +% \end{macrocode} +% +% \end{implementation} +% +% \PrintIndex diff --git a/Master/texmf-dist/source/latex/l3experimental/xgalley/xgalley.ins b/Master/texmf-dist/source/latex/l3experimental/xgalley/xgalley.ins new file mode 100644 index 00000000000..cadc1d2a98f --- /dev/null +++ b/Master/texmf-dist/source/latex/l3experimental/xgalley/xgalley.ins @@ -0,0 +1,41 @@ +\iffalse meta-comment + +File xgalley.ins Copyright (C) 2010,2011 The LaTeX3 Project + +It may be distributed and/or modified under the conditions of the +LaTeX Project Public License (LPPL), either version 1.3c of this +license or (at your option) any later version. The latest version +of this license is in the file + + http://www.latex-project.org/lppl.txt + +This file is part of the "l3experimental bundle" (The Work in LPPL) +and all files in that bundle must be distributed together. + +The released version of this bundle is available from CTAN. + +\fi + +\input docstrip.tex +\askforoverwritefalse + +\preamble + +EXPERIMENTAL CODE + +Do not distribute this file without also distributing the +source files specified above. + +Do not distribute a modified version of this file. + +\endpreamble +% stop docstrip adding \endinput +\postamble +\endpostamble + +\keepsilent + +\generate{\file{l3galley.sty}{\from{l3galley.dtx}{package}}} +\generate{\file{xgalley.sty} {\from{xgalley.dtx} {package}}} + +\endbatchfile diff --git a/Master/texmf-dist/tex/latex/l3experimental/l3str/l3regex-trace.sty b/Master/texmf-dist/tex/latex/l3experimental/l3str/l3regex-trace.sty new file mode 100644 index 00000000000..29045fcdea9 --- /dev/null +++ b/Master/texmf-dist/tex/latex/l3experimental/l3str/l3regex-trace.sty @@ -0,0 +1,1637 @@ +%% +%% This is file `l3regex-trace.sty', +%% generated with the docstrip utility. +%% +%% The original source files were: +%% +%% l3regex.dtx (with options: `package,trace') +%% +%% EXPERIMENTAL CODE +%% +%% Do not distribute this file without also distributing the +%% source files specified above. +%% +%% Do not distribute a modified version of this file. +%% +%% File: l3regex.dtx Copyright (C) 2011 The LaTeX3 Project +%% +%% It may be distributed and/or modified under the conditions of the +%% LaTeX Project Public License (LPPL), either version 1.3c of this +%% license or (at your option) any later version. The latest version +%% of this license is in the file +%% +%% http://www.latex-project.org/lppl.txt +%% +%% This file is part of the "l3experimental bundle" (The Work in LPPL) +%% and all files in that bundle must be distributed together. +%% +%% The released version of this bundle is available from CTAN. +%% +%% ----------------------------------------------------------------------- +%% +%% The development version of the bundle can be found at +%% +%% http://www.latex-project.org/svnroot/experimental/trunk/ +%% +%% for those people who are interested. +%% +%%%%%%%%%%% +%% NOTE: %% +%%%%%%%%%%% +%% +%% Snapshots taken from the repository represent work in progress and may +%% not work or may contain conflicting material! We therefore ask +%% people _not_ to put them into distributions, archives, etc. without +%% prior consultation with the LaTeX3 Project. +%% +%% ----------------------------------------------------------------------- +\RequirePackage{expl3} +\GetIdInfo$Id: l3regex.dtx 2895 2011-10-09 15:58:19Z joseph $ + {L3 Experimental Regular Expressions} +\ProvidesExplPackage + {\ExplFileName}{\ExplFileDate}{\ExplFileVersion}{\ExplFileDescription} +\RequirePackage{l3str} +\cs_new:Npn \regex_tmp:w { } +\tl_new:N \g_regex_tmpa_tl +\tl_new:N \l_regex_tmpa_tl +\tl_new:N \l_regex_tmpb_tl +\int_new:N \l_regex_tmpa_int +\tl_new:N \l_regex_pattern_str +\int_new:N \l_regex_max_state_int +\int_new:N \l_regex_left_state_int +\int_new:N \l_regex_right_state_int +\seq_new:N \l_regex_left_state_seq +\seq_new:N \l_regex_right_state_seq +\int_new:N \l_regex_capturing_group_int +\seq_new:N \l_regex_capturing_group_seq +\tl_new:N \l_regex_one_or_group_tl +\int_new:N \l_regex_repetition_int +\bool_new:N \l_regex_look_behind_bool +\tl_new:N \l_regex_look_behind_str +\cs_new_eq:NN \regex_build_tmp_class:n \use_none:n +\bool_new:N \l_regex_class_bool +\tl_new:N \l_regex_class_tl +\tl_const:Nn \c_regex_d_tl + { + \regex_item_range:nn {48} {57} % 0--9 + } +\tl_const:Nn \c_regex_D_tl + { + \regex_item_more:n {57} % 9 + \regex_item_range:nn {0} {47} % 0 + } +\tl_const:Nn \c_regex_h_tl + { + \regex_item_equal:n {32} % space + \regex_item_equal:n {9} % tab + } +\tl_const:Nn \c_regex_H_tl + { + \regex_item_neq:n {32} % space + \regex_item_neq:n {9} % tab + \regex_break_true:w + } +\tl_const:Nn \c_regex_s_tl + { + \regex_item_equal:n {32} % space + \regex_item_neq:n {11} % vtab + \regex_item_range:nn {9} {13} % tab, lf, vtab, ff, cr + } +\tl_const:Nn \c_regex_S_tl + { + \regex_item_more:n {32} % > space + \regex_item_range:nn {14} {31} % tab < ... < space + \regex_item_range:nn {0} {8} % < tab + \regex_item_equal:n {11} % vtab + } +\tl_const:Nn \c_regex_v_tl + { + \regex_item_range:nn {10} {13} % lf, vtab, ff, cr + } +\tl_const:Nn \c_regex_V_tl + { + \regex_item_more:n {13} % cr + \regex_item_range:nn {0} {9} % < lf + } +\tl_const:Nn \c_regex_w_tl + { + \regex_item_range:nn {97} {122} % a--z + \regex_item_range:nn {65} {90} % A--Z + \regex_item_range:nn {48} {57} % 0--9 + \regex_item_equal:n {95} % _ + } +\tl_const:Nn \c_regex_W_tl + { + \regex_item_range:nn {0} {47} % <`0 + \regex_item_range:nn {58} {64} % (`9+1)--(`A-1) + \regex_item_range:nn {91} {94} % (`Z+1)--(`_-1) + \regex_item_equal:n {96} % ` + \regex_item_more:n {122} % z + } +\tl_const:Nn \c_regex_N_tl + { + \regex_item_neq:n {10} % lf + \regex_break_true:w + } +\tl_new:N \l_regex_query_str +\int_new:N \l_regex_start_step_int +\int_new:N \l_regex_current_char_int +\int_new:N \l_regex_current_step_int +\int_new:N \l_regex_unique_step_int +\int_new:N \l_regex_current_state_int +\prop_new:N \l_regex_current_submatches_prop +\int_new:N \l_regex_max_index_int +\tl_new:N \l_regex_every_match_tl +\cs_new_protected:Npn \regex_last_match_empty_no:F #1 {#1} +\cs_new_protected:Npn \regex_last_match_empty_yes:F + { \int_compare:nNnF \l_regex_start_step_int = \l_regex_current_step_int } +\cs_new_eq:NN \regex_last_match_empty:F \regex_last_match_empty_no:F +\bool_new:N \l_regex_success_bool +\bool_new:N \l_regex_success_empty_bool +\int_new:N \l_regex_success_step_int +\prop_new:N \l_regex_success_submatches_prop +\bool_new:N \l_regex_fresh_thread_bool +\seq_new:N \g_regex_submatches_seq +\int_new:N \g_regex_match_count_int +\seq_new:N \g_regex_split_seq +\tl_new:N \l_regex_replacement_tl +\tl_new:N \g_regex_replaced_str +\cs_new_eq:NN \s_regex_toks \scan_stop: +\cs_new_protected:Npn \regex_toks_put_left:Nx #1#2 + { + \cs_set_nopar:Npx \regex_tmp:w \s_regex_toks { \s_regex_toks #2 } + \tex_toks:D #1 \exp_after:wN \exp_after:wN \exp_after:wN + { \exp_after:wN \regex_tmp:w \tex_the:D \tex_toks:D #1 } + } +\cs_new_protected:Npn \regex_toks_put_right:Nx #1#2 + { + \cs_set_nopar:Npx \regex_tmp:w {#2} + \tex_toks:D #1 \exp_after:wN + { \tex_the:D \tex_toks:D \exp_after:wN #1 \regex_tmp:w } + } +\cs_new_eq:NN \regex_if_tail_stop:N \quark_if_recursion_tail_stop:N +\cs_new_protected_nopar:Npn \regex_if_tail_error:Nn #1#2 + { \quark_if_recursion_tail_stop_do:Nn #1 { \regex_build_error:n {#2} } } +\cs_new_protected_nopar:Npn \regex_build_error:n #1 + { + \msg_error:nnxx { regex } { build-error } {\int_eval:n{#1}} + { + \prg_case_int:nnn {#1} + { + {-999} {File~not~found} + {-998} {Unsupported~construct} + {-997} {The~regular~expression~is~too~large~(32768~states).} + {1} {\iow_char:N\\~at~end~of~pattern } + {4} {Numbers~out~of~order~in~\iow_char:N\{\iow_char\}~quantifier.} + {6} {Missing~terminating~\iow_char:N\]~for~character~class } + {7} {Invalid~escape~sequence~in~character~class} + {8} {Range~out~of~order~in~character~class} + {22} {Mismatched~parentheses} + {34} {Character~value~in~\iow_char:N\\x{...}~sequence~is~too~large} + } + { Internal~bug. } + } + } +\msg_new:nnn { regex } { build-error } { (error~#1): ~ #2 } +\cs_new_nopar:Npn \regex_break_true:w #1 \regex_break_point:TF #2 #3 {#2} +\cs_new_nopar:Npn \regex_break_false:w #1 \regex_break_point:TF #2 #3 {#3} +\cs_new_eq:NN \regex_break_point:TF \use_ii:nn +\cs_new_nopar:Npn \regex_item_equal:n #1 + { + \if_num:w #1 = \l_regex_current_char_int + \exp_after:wN \regex_break_true:w + \fi: + } +\cs_new_nopar:Npn \regex_item_range:nn #1 #2 + { + \reverse_if:N \if_num:w #1 > \l_regex_current_char_int + \reverse_if:N \if_num:w #2 < \l_regex_current_char_int + \exp_after:wN \exp_after:wN \exp_after:wN \regex_break_true:w + \fi: + \fi: + } +\cs_new_nopar:Npn \regex_item_less:n #1 + { + \if_num:w #1 > \l_regex_current_char_int + \exp_after:wN \regex_break_true:w + \fi: + } +\cs_new_nopar:Npn \regex_item_more:n #1 + { + \if_num:w #1 < \l_regex_current_char_int + \exp_after:wN \regex_break_true:w + \fi: + } +\cs_new_nopar:Npn \regex_item_neq:n #1 + { + \if_num:w #1 = \l_regex_current_char_int + \exp_after:wN \regex_break_false:w + \fi: + } +\cs_new_protected:Npn \regex_get_digits:nw #1 + { + \tex_afterassignment:D \regex_tmp:w + \cs_set_nopar:Npx \regex_tmp:w + { + \exp_not:n {#1} + { \if_false: } } \fi: + \regex_get_digits_aux:N + } +\cs_new_nopar:Npn \regex_get_digits_aux:N #1 + { + \if_num:w 9 < 1 \exp_not:N #1 \exp_stop_f: + \else: + \if_charcode:w \c_space_token \exp_not:N #1 + \else: + \regex_get_digits_end:w + \fi: + \fi: + #1 \regex_get_digits_aux:N + } +\cs_new_nopar:Npn \regex_get_digits_end:w + \fi: \fi: #1 \regex_get_digits_aux:N + { + \fi: \fi: + \if_false: { { \fi: } } + #1 + } +\prg_new_conditional:Npnn \regex_aux_char_if_special:N #1 { TF } + { + \if_num:w `#1 < 97 \exp_stop_f: + \if_num:w `#1 < 58 \exp_stop_f: + \if_num:w \int_eval:w (`#1 - \c_eight)/\c_sixteen = \c_two + \prg_return_true: + \else: + \prg_return_false: + \fi: + \else: + \if_num:w \int_eval:w `#1 / 26 = \c_three + \prg_return_false: + \else: + \prg_return_true: + \fi: + \fi: + \else: + \if_num:w \int_eval:w `#1 / \c_five = 25 \exp_stop_f: + \prg_return_true: + \else: + \prg_return_false: + \fi: + \fi: + } +\prg_new_conditional:Npnn \regex_aux_char_if_alphanumeric:N #1 { TF } + { + \if_num:w `#1 < 91 \exp_stop_f: + \if_num:w `#1 < 65 \exp_stop_f: + \if_num:w \c_nine < 1 #1 \exp_stop_f: + \prg_return_true: + \else: + \prg_return_false: + \fi: + \else: + \prg_return_true: + \fi: + \else: + \if_num:w \int_eval:w (`#1-\c_six)/26 = \c_four + \prg_return_true: + \else: + \prg_return_false: + \fi: + \fi: + } +\cs_new_protected_nopar:Npn \regex_build_new_state: + { + \int_compare:nNnTF \l_regex_max_state_int > {32766} + { \regex_build_error:n {-997} } + { + \int_incr:N \l_regex_max_state_int + \tex_toks:D \l_regex_max_state_int { \s_regex_toks } + } + \int_set_eq:NN \l_regex_left_state_int \l_regex_right_state_int + \int_set_eq:NN \l_regex_right_state_int \l_regex_max_state_int + } +\cs_new_protected_nopar:Npn \regex_build_transition_aux:NN #1#2 + { + \regex_build_new_state: + \regex_toks_put_right:Nx \l_regex_left_state_int + { #1 { \int_use:N #2 } } + } +\cs_new_protected_nopar:Npn \regex_build_transitions_aux:NNNN #1#2#3#4 + { + \regex_build_new_state: + \regex_toks_put_right:Nx \l_regex_left_state_int + { + #1 { \int_use:N #2 } + #3 { \int_use:N #4 } + } + } +\cs_new_protected:Npn \regex_build:n #1 + { + \regex_build_setup: + \str_aux_escape:NNNn + \regex_build_i_unescaped:N + \regex_build_i_escaped:N + \regex_build_i_raw:N + { #1 } + \regex_build_open_aux: + \g_str_result_tl \prg_do_nothing: \prg_do_nothing: + \seq_push:Nn \l_regex_capturing_group_seq { 0 } + \regex_build_close_aux: \regex_build_group_: + \regex_build_end: + } +\cs_new_nopar:Npn \regex_build_i_unescaped:N #1 + { + \regex_aux_char_if_special:NTF #1 + { \exp_not:N \regex_build_control:N #1 } + { \exp_not:N \regex_build_raw:N #1 } + } +\cs_new_nopar:Npn \regex_build_i_escaped:N #1 + { + \regex_aux_char_if_alphanumeric:NTF #1 + { \exp_not:N \regex_build_control:N #1 } + { \exp_not:N \regex_build_raw:N #1 } + } +\cs_new_nopar:Npn \regex_build_i_raw:N #1 + { \exp_not:N \regex_build_raw:N #1 } +\cs_new_protected_nopar:Npn \regex_build_default_control:N #1 + { + \cs_if_exist_use:cF { regex_build_#1: } + { \regex_build_default_raw:N #1 } + } +\cs_new_protected_nopar:Npn \regex_build_setup: + { + \cs_set_eq:NN \regex_build_control:N \regex_build_default_control:N + \cs_set_eq:NN \regex_build_raw:N \regex_build_default_raw:N + \int_set_eq:NN \l_regex_capturing_group_int \c_zero + \int_zero:N \l_regex_left_state_int + \int_zero:N \l_regex_right_state_int + \int_zero:N \l_regex_max_state_int + \regex_build_new_state: + \regex_build_new_state: + \regex_toks_put_right:Nx \l_regex_left_state_int + { + \regex_action_start_wildcard:nn + { \int_use:N \l_regex_left_state_int } + { \int_use:N \l_regex_right_state_int } + } + } +\cs_new_protected_nopar:Npn \regex_build_end: + { + \seq_if_empty:NF \l_regex_capturing_group_seq + { + \regex_build_error:n {22} + \prg_replicate:nn + { \seq_length:N \l_regex_capturing_group_seq } % ( + { \regex_build_close_aux: \regex_build_group_: } + } + \regex_toks_put_right:Nx \l_regex_right_state_int + { \regex_action_success: } + } +\cs_new_protected_nopar:cpn { regex_build_^: } + { \regex_build_anchor_start:N \c_zero } +\cs_new_protected_nopar:Npn \regex_build_A: + { \regex_build_anchor_start:N \c_zero } +\cs_new_protected_nopar:Npn \regex_build_G: + { \regex_build_anchor_start:N \l_regex_start_step_int } +\cs_new_protected_nopar:Npn \regex_build_anchor_start:N #1 + { + \regex_build_new_state: + \regex_toks_put_right:Nx \l_regex_left_state_int + { + \exp_not:N \int_compare:nNnT {#1} = \l_regex_current_step_int + { \regex_action_free:n { \int_use:N \l_regex_right_state_int } } + } + } +\cs_new_protected_nopar:cpn { regex_build_$: } % $ + { + \regex_build_new_state: + \regex_toks_put_right:Nx \l_regex_left_state_int + { + \exp_not:N \int_compare:nNnT + \c_minus_one = \l_regex_current_char_int + { \regex_action_free:n { \int_use:N \l_regex_right_state_int } } + } + } +\cs_new_eq:Nc \regex_build_Z: { regex_build_$: } %$ +\cs_new_eq:Nc \regex_build_z: { regex_build_$: } %$ +\cs_new_protected_nopar:Npn \regex_build_b: + { + \bool_set_true:N \l_regex_look_behind_bool + \regex_build_new_state: + \regex_toks_put_right:Nx \l_regex_left_state_int + { + \exp_not:N \regex_if_word_boundary:TF + { \regex_action_free:n { \int_use:N \l_regex_right_state_int } } + { } + } + } +\cs_new_protected_nopar:Npn \regex_build_B: + { + \bool_set_true:N \l_regex_look_behind_bool + \regex_build_new_state: + \regex_toks_put_right:Nx \l_regex_left_state_int + { + \exp_not:N \regex_if_word_boundary:TF + { } + { \regex_action_free:n { \int_use:N \l_regex_right_state_int } } + } + } +\cs_new_protected_nopar:Npn \regex_if_word_boundary:TF + { + \tl_if_empty:NTF \l_regex_look_behind_str + { \c_regex_w_tl } + { + \group_begin: + \cs_set_nopar:Npx \regex_tmp:w + { + \int_set:Nn \l_regex_current_char_int + { ` \str_head:N \l_regex_look_behind_str } + } + \regex_tmp:w + \c_regex_w_tl + \regex_break_point:TF + { \group_end: \c_regex_W_tl } + { \group_end: \c_regex_w_tl } + } + \regex_break_point:TF + } +\cs_new_protected_nopar:Npn \regex_build_default_raw:N #1 + { + \cs_set:Npx \regex_build_tmp_class:n ##1 + { + \exp_not:n { \exp_not:N \if_num:w } + \int_value:w `#1 = \l_regex_current_char_int + \regex_action_cost:n { ##1 } + \exp_not:n { \exp_not:N \fi: } + } + \regex_build_one_quantifier: + } +\cs_new_protected_nopar:cpn { regex_build_.: } + { + \cs_set:Npn \regex_build_tmp_class:n ##1 + { + \exp_not:N \if_num:w \c_minus_one < \l_regex_current_char_int + \regex_action_cost:n {##1} + \exp_not:N \fi: + } + \regex_build_one_quantifier: + } +\cs_new_protected_nopar:Npn \regex_build_char_type:N #1 + { + \cs_set:Npn \regex_build_tmp_class:n ##1 + { + \exp_not:N #1 + \exp_not:N \regex_break_point:TF + { \regex_action_cost:n {##1} } + { } + } + \regex_build_one_quantifier: + } +\tl_map_inline:nn { dDhHsSvVwWN } + { + \cs_new_protected_nopar:cpx { regex_build_#1: } + { + \exp_not:N \regex_build_char_type:N + \exp_not:c { c_regex_#1_tl } + } + } +\cs_new_protected_nopar:cpn { regex_build_[: } + { + \tl_clear:N \l_regex_class_tl + \cs_set_eq:NN \regex_build_control:N \regex_class_control:N + \cs_set_eq:NN \regex_build_raw:N \regex_class_raw:N + \regex_class_first:NN + } +\cs_new_protected_nopar:Npn \regex_class_control:N #1 + { + \cs_if_exist_use:cF { regex_class_#1: } + { \regex_class_raw:N #1 } + } +\cs_new_protected_nopar:cpn { regex_class_]: } + { + \tl_if_empty:NTF \l_regex_class_tl %[ + { \regex_class_raw:N ] } + { + \cs_set_eq:NN \regex_build_control:N \regex_build_default_control:N + \cs_set_eq:NN \regex_build_raw:N \regex_build_default_raw:N + \cs_set:Npn \regex_build_tmp_class:n ##1 + { + \exp_not:o \l_regex_class_tl + \bool_if:NTF \l_regex_class_bool + { + \exp_not:N \regex_break_point:TF + { \regex_action_cost:n {##1} } + { } + } + { + \exp_not:N \regex_break_point:TF + { } + { \regex_action_cost:n {##1} } + } + } + \regex_build_one_quantifier: + } + } +\cs_new_protected_nopar:Npn \regex_class_first:NN #1#2 + { + \str_if_eq:nnTF {#1#2} { \regex_build_control:N ^ } + { \bool_set_false:N \l_regex_class_bool } + { + \bool_set_true:N \l_regex_class_bool + #1 #2 + } + } +\cs_new_protected_nopar:Npn \regex_class_raw:N #1#2#3 + { + \str_if_eq:nnTF {#2#3} { \regex_build_control:N - } + { \regex_class_range:Nw #1 } + { + \regex_class_single:N #1 + #2 #3 + } + } +\cs_new_protected_nopar:Npn \regex_class_single:N #1 + { + \tl_put_right:Nx \l_regex_class_tl + { \exp_not:N \regex_item_equal:n { \int_value:w `#1 } } + } +\cs_new_protected_nopar:Npn \regex_class_range:Nw #1#2#3 + { + \token_if_eq_meaning:NNTF #2 \regex_build_control:N + { + \cs_if_exist:cTF { regex_class_#3: } + { + \regex_class_single:N #1 + \regex_class_single:N - + #2#3 + } + { \regex_class_range_put:NN #1#3 } + } + { \regex_class_range_put:NN #1#3 } + } +\cs_new_protected_nopar:Npn \regex_class_range_put:NN #1#2 + { + \if_num:w `#1 > `#2 \exp_stop_f: + \regex_build_error:n {8} + \else: + \tl_put_right:Nx \l_regex_class_tl + { + \if_num:w `#1 = `#2 \exp_stop_f: + \exp_not:N \regex_item_equal:n + \else: + \exp_not:N \regex_item_range:nn { \int_value:w `#1 } + \fi: + { \int_value:w `#2 } + } + \fi: + } +\tl_map_inline:nn { dDhHsSvVwWN } + { + \cs_new_protected_nopar:cpx { regex_class_#1: } + { + \tl_put_right:Nn \exp_not:N \l_regex_class_tl + { \exp_not:c { c_regex_#1_tl } } + } + } +\cs_new_protected_nopar:Npn \regex_build_quantifier:w #1#2 + { + \token_if_eq_meaning:NNTF #1 \regex_build_control:N + { + \cs_if_exist_use:cF { regex_build_quantifier_#2:w } + { + \regex_build_quantifier_end:n { } + #1 #2 + } + } + { + \regex_build_quantifier_end:n { } + #1 #2 + } + } +\cs_new_protected_nopar:cpn { regex_build_quantifier_?:w } + { \regex_build_quantifier_aux:nnNN { } { ? } } +\cs_new_protected_nopar:cpn { regex_build_quantifier_*:w } + { \regex_build_quantifier_aux:nnNN { } { * } } +\cs_new_protected_nopar:cpn { regex_build_quantifier_+:w } + { \regex_build_quantifier_aux:nnNN { } { + } } +\cs_new_protected_nopar:Npn \regex_build_quantifier_aux:nnNN #1#2#3#4 + { + \str_if_eq:nnTF { #3 #4 } { \regex_build_control:N ? } + { \regex_build_quantifier_end:n { #2 #4 } #1 } + { + \regex_build_quantifier_end:n { #2 } #1 + #3 #4 + } + } +\cs_new_protected_nopar:cpn { regex_build_quantifier_ \c_lbrace_str :w } + { \regex_get_digits:nw { \regex_build_quantifier_lbrace:n } } +\cs_new_protected_nopar:Npn \regex_build_quantifier_lbrace:n #1 + { + \tl_if_empty:nTF {#1} + { + \regex_build_quantifier_end:n { } + \exp_after:wN \regex_build_raw:N \c_lbrace_str + } + { \regex_build_quantifier_lbrace:nw {#1} } + } +\cs_new_protected_nopar:Npx \regex_build_quantifier_lbrace:nw #1#2#3 + { + \exp_not:N \prg_case_str:nnn { #2 #3 } + { + { \exp_not:N \regex_build_control:N , } + { + \exp_not:N \regex_get_digits:nw + { \exp_not:N \regex_build_quantifier_lbrace:nnw {#1} } + } + { \exp_not:N \regex_build_control:N \c_rbrace_str } + { \exp_not:N \regex_build_quantifier_end:n {n} {#1} } + } + { + \exp_not:N \regex_build_quantifier_end:n { } + \exp_not:N \regex_build_raw:N \c_lbrace_str #1#2 + } + } +\cs_new_protected_nopar:Npn \regex_build_quantifier_lbrace:nnw #1#2#3 + { + \str_if_eq:xxTF { \exp_not:N #3 } { \c_rbrace_str } + { + \tl_if_empty:nTF {#2} + { \regex_build_quantifier_aux:nnN { {#1} {\c_max_int} } {nn} } + { + \int_compare:nNnT {#1} > {#2} + { \regex_build_error:n {4} } + \regex_build_quantifier_aux:nnN { {#1} {#2} } {nn} + } + } + { + \regex_build_quantifier_end:n { } + \use:x + { + \exp_not:n { \exp_args:No \tl_map_function:nN } + { \c_lbrace_str #1 #2 , } + \regex_build_raw:N + } + } + } +\cs_new_protected_nopar:Npn \regex_build_quantifier_end:n #1 + { \use:c { regex_build_ \l_regex_one_or_group_tl _ #1 : } } +\cs_new_protected_nopar:Npn \regex_build_one_quantifier: + { + \tl_set:Nx \l_regex_one_or_group_tl { one } + \regex_build_quantifier:w + } +\cs_new_protected_nopar:Npn \regex_build_one_: + { + \regex_build_transition_aux:NN + \regex_build_tmp_class:n \l_regex_right_state_int + } +\cs_new_protected_nopar:cpn { regex_build_one_?: } + { + \regex_build_transitions_aux:NNNN + \regex_build_tmp_class:n \l_regex_right_state_int + \regex_action_free:n \l_regex_right_state_int + } +\cs_new_protected_nopar:cpn { regex_build_one_??: } + { + \regex_build_transitions_aux:NNNN + \regex_action_free:n \l_regex_right_state_int + \regex_build_tmp_class:n \l_regex_right_state_int + } +\cs_new_protected_nopar:cpn { regex_build_one_*: } + { + \regex_build_transitions_aux:NNNN + \regex_build_tmp_class:n \l_regex_left_state_int + \regex_action_free:n \l_regex_right_state_int + } +\cs_new_protected_nopar:cpn { regex_build_one_*?: } + { + \regex_build_transitions_aux:NNNN + \regex_action_free:n \l_regex_right_state_int + \regex_build_tmp_class:n \l_regex_left_state_int + } +\cs_new_protected_nopar:cpn { regex_build_one_+: } + { + \regex_build_one_: + \int_set_eq:NN \l_regex_tmpa_int \l_regex_left_state_int + \regex_build_transitions_aux:NNNN + \regex_action_free:n \l_regex_tmpa_int + \regex_action_free:n \l_regex_right_state_int + } +\cs_new_protected_nopar:cpn { regex_build_one_+?: } + { + \regex_build_one_: + \int_set_eq:NN \l_regex_tmpa_int \l_regex_left_state_int + \regex_build_transitions_aux:NNNN + \regex_action_free:n \l_regex_right_state_int + \regex_action_free:n \l_regex_tmpa_int + } +\cs_new_protected_nopar:Npn \regex_build_one_n: #1 + { + \int_set_eq:NN \l_regex_tmpa_int \l_regex_right_state_int + \regex_build_new_state: + \regex_build_new_state: + \regex_toks_put_right:Nx \l_regex_tmpa_int + { %^^A safe \if nesting? + \exp_not:N \if_num:w #1 > \l_regex_repetition_int + \regex_action_repeat_move:n + { \int_use:N \l_regex_left_state_int } + \exp_not:N \else: + \regex_action_no_repeat_move:n + { \int_use:N \l_regex_right_state_int } + \exp_not:N \fi: + } + \regex_toks_put_right:Nx \l_regex_left_state_int + { + \regex_build_tmp_class:n + { \int_use:N \l_regex_tmpa_int } + } + } +\cs_new_eq:cN { regex_build_one_n?: } \regex_build_one_n: +\cs_new_protected_nopar:Npn \regex_build_one_nn: #1#2 + { + \regex_build_one_nn_aux:nn {#1} + { + \exp_not:N \if_num:w #2 > \l_regex_repetition_int + \regex_action_repeat_copy:n + { \int_use:N \l_regex_left_state_int } + \exp_not:N \fi: + \regex_action_no_repeat_move:n + { \int_use:N \l_regex_right_state_int } + } + } +\cs_new_protected_nopar:cpn { regex_build_one_nn?: } #1#2 + { + \regex_build_one_nn_aux:nn {#1} + { + \regex_action_no_repeat_copy:n + { \int_use:N \l_regex_right_state_int } + \exp_not:N \if_num:w #2 > \l_regex_repetition_int + \regex_action_repeat_move:n + { \int_use:N \l_regex_left_state_int } + \exp_not:N \fi: + } + } +\cs_new_protected_nopar:Npn \regex_build_one_nn_aux:nn #1#2 + { + \int_set_eq:NN \l_regex_tmpa_int \l_regex_right_state_int + \regex_build_new_state: + \regex_build_new_state: + \regex_toks_put_right:Nx \l_regex_tmpa_int + { + \exp_not:N \if_num:w #1 > \l_regex_repetition_int + \regex_action_repeat_move:n + { \int_use:N \l_regex_left_state_int } + \exp_not:N \else: + #2 + \exp_not:N \fi: + } + \regex_toks_put_right:Nx \l_regex_left_state_int + { + \regex_build_tmp_class:n + { \int_use:N \l_regex_tmpa_int } + } + } +\cs_new_protected_nopar:cpn { regex_build_(: } #1#2 + { + \str_if_eq:nnTF { #1 #2 } { \regex_build_control:N ? } + { \regex_build_special_group:NN } + { + \int_incr:N \l_regex_capturing_group_int + \seq_push:Nx \l_regex_capturing_group_seq + { \int_use:N \l_regex_capturing_group_int } + \regex_build_open_aux: + #1 #2 + } + } +\cs_new_protected_nopar:Npn \regex_build_open_aux: + { + \regex_build_new_state: + \seq_push:Nx \l_regex_left_state_seq + { \int_use:N \l_regex_left_state_int } + \seq_push:Nx \l_regex_right_state_seq + { \int_use:N \l_regex_right_state_int } + \regex_build_begin_alternation: + } +\cs_new_protected_nopar:cpn { regex_build_|: } + { + \regex_build_end_alternation: + \regex_build_begin_alternation: + } +\cs_new_protected_nopar:cpn { regex_build_): } + { + \seq_if_empty:NTF \l_regex_capturing_group_seq + { \regex_build_error:n {22} } + { + \regex_build_close_aux: + \regex_build_group_quantifier: + } + } +\cs_new_protected_nopar:Npn \regex_build_close_aux: + { + \regex_build_end_alternation: + \seq_pop:NN \l_regex_left_state_seq \l_regex_tmpa_tl + \int_set:Nn \l_regex_left_state_int \l_regex_tmpa_tl + \seq_pop:NN \l_regex_right_state_seq \l_regex_tmpa_tl + \int_set:Nn \l_regex_right_state_int \l_regex_tmpa_tl + } +\cs_new_protected_nopar:Npn \regex_build_begin_alternation: + { + \regex_build_new_state: + \seq_get:NN \l_regex_left_state_seq \l_regex_tmpa_tl + \int_set:Nn \l_regex_left_state_int \l_regex_tmpa_tl + \regex_toks_put_right:Nx \l_regex_left_state_int + { \regex_action_free:n { \int_use:N \l_regex_right_state_int } } + } +\cs_new_protected_nopar:Npn \regex_build_end_alternation: + { + \seq_get:NN \l_regex_right_state_seq \l_regex_tmpa_tl + \regex_toks_put_right:Nx \l_regex_right_state_int + { \regex_action_free:n { \l_regex_tmpa_tl } } + } +\cs_new_protected_nopar:Npn \regex_build_special_group:NN #1#2 + { + \cs_if_exist_use:cF { regex_build_special_group_\token_to_str:N #2 : } + { + \regex_build_error:n { -998 } + \regex_build_control:N ( % ) + \regex_build_raw:N ? + #1 #2 + } + } +\cs_new_protected_nopar:cpn { regex_build_special_group_:: } + { + \seq_push:Nx \l_regex_capturing_group_seq { * } + \regex_build_open_aux: + } +\cs_new_protected_nopar:Npn \regex_build_group_quantifier: + { + \tl_set:Nn \l_regex_one_or_group_tl { group } + \regex_build_quantifier:w + } +\cs_new_protected_nopar:Npn \regex_build_group_submatches:NN #1#2 + { + \seq_pop:NN \l_regex_capturing_group_seq \l_regex_tmpa_tl + \str_if_eq:xxF { \l_regex_tmpa_tl } { * } + { + \regex_toks_put_left:Nx #1 + { \regex_action_submatch:n { \l_regex_tmpa_tl < } } + \regex_toks_put_left:Nx #2 + { \regex_action_submatch:n { \l_regex_tmpa_tl > } } + } + } +\cs_new_protected_nopar:Npn \regex_build_group_: + { + \regex_build_group_submatches:NN + \l_regex_left_state_int \l_regex_right_state_int + \regex_build_transition_aux:NN + \regex_action_free:n \l_regex_right_state_int + } +\cs_new_protected_nopar:Npn \regex_build_group_shift:N #1 + { + \int_set_eq:NN \l_regex_tmpa_int \l_regex_left_state_int + \regex_build_new_state: + \tex_toks:D \l_regex_right_state_int = \tex_toks:D \l_regex_tmpa_int + \use:x + { + \tex_toks:D \l_regex_tmpa_int + { \s_regex_toks #1 { \int_use:N \l_regex_right_state_int } } + } + \regex_build_group_submatches:NN + \l_regex_right_state_int \l_regex_left_state_int + \int_set_eq:NN \l_regex_right_state_int \l_regex_left_state_int + } +\cs_new_protected_nopar:Npn \regex_build_group_qs_aux:NN #1#2 + { + \regex_build_group_shift:N \regex_action_free:n + \regex_build_transition_aux:NN \regex_action_free:n #1 + #2 \l_regex_tmpa_int + { \regex_action_free:n { \int_use:N \l_regex_right_state_int } } + } +\cs_new_protected_nopar:cpn { regex_build_group_?: } + { + \regex_build_group_qs_aux:NN + \l_regex_right_state_int \regex_toks_put_right:Nx + } +\cs_new_protected_nopar:cpn { regex_build_group_??: } + { + \regex_build_group_qs_aux:NN + \l_regex_right_state_int \regex_toks_put_left:Nx + } +\cs_new_protected_nopar:cpn { regex_build_group_*: } + { + \regex_build_group_qs_aux:NN + \l_regex_tmpa_int \regex_toks_put_right:Nx + } +\cs_new_protected_nopar:cpn { regex_build_group_*?: } + { + \regex_build_group_qs_aux:NN + \l_regex_tmpa_int \regex_toks_put_left:Nx + } +\cs_new_protected_nopar:cpn { regex_build_group_+: } + { + \regex_build_group_submatches:NN + \l_regex_left_state_int \l_regex_right_state_int + \int_set_eq:NN \l_regex_tmpa_int \l_regex_left_state_int + \regex_build_transitions_aux:NNNN + \regex_action_free:n \l_regex_tmpa_int + \regex_action_free:n \l_regex_right_state_int + } +\cs_new_protected_nopar:cpn { regex_build_group_+?: } + { + \regex_build_group_submatches:NN + \l_regex_left_state_int \l_regex_right_state_int + \int_set_eq:NN \l_regex_tmpa_int \l_regex_left_state_int + \regex_build_transitions_aux:NNNN + \regex_action_free:n \l_regex_right_state_int + \regex_action_free:n \l_regex_tmpa_int + } +\cs_new_protected_nopar:Npn \regex_build_group_n: #1 + { + \regex_build_group_shift:N \regex_action_repeat_move:n + \regex_build_transition_aux:NN + \regex_action_free:n \l_regex_tmpa_int + \use:x + { + \tex_toks:D \l_regex_tmpa_int + { + \s_regex_toks + \exp_not:N \if_num:w #1 > \l_regex_repetition_int + \tex_the:D \tex_toks:D \l_regex_tmpa_int + \exp_not:N \else: + \regex_action_no_repeat_move:n + { \int_use:N \l_regex_right_state_int } + \exp_not:N \fi: + } + } + } +\cs_new_eq:cN { regex_build_group_n?: } \regex_build_group_n: +\cs_new_protected_nopar:Npn \regex_build_group_nn: #1#2 + { + \regex_build_group_shift:N \regex_action_repeat_move:n + \regex_build_transition_aux:NN + \regex_action_free:n \l_regex_tmpa_int + \use:x + { + \tex_toks:D \l_regex_tmpa_int + { + \s_regex_toks + \exp_not:N \if_num:w #1 > \l_regex_repetition_int + \tex_the:D \tex_toks:D \l_regex_tmpa_int + \exp_not:N \else: + \exp_not:N \if_num:w #2 > \l_regex_repetition_int + \tex_the:D \tex_toks:D \l_regex_tmpa_int + \exp_not:N \fi: + \regex_action_no_repeat_copy:n + { \int_use:N \l_regex_right_state_int } + \exp_not:N \fi: + } + } + } +\cs_new_protected_nopar:cpn { regex_build_group_nn?: } #1#2 + { + \regex_build_group_shift:N \regex_action_repeat_move:n + \regex_build_transition_aux:NN + \regex_action_free:n \l_regex_tmpa_int + \use:x + { + \tex_toks:D \l_regex_tmpa_int + { + \s_regex_toks + \exp_not:N \if_num:w #1 > \l_regex_repetition_int + \tex_the:D \tex_toks:D \l_regex_tmpa_int + \exp_not:N \else: + \regex_action_no_repeat_copy:n + { \int_use:N \l_regex_right_state_int } + \exp_not:N \if_num:w #2 > \l_regex_repetition_int + \tex_the:D \tex_toks:D \l_regex_tmpa_int + \exp_not:N \fi: + \exp_not:N \fi: + } + } + } +\cs_new_protected:Npn \regex_if_state_free:nT #1 + { + \if_num:w \tex_dimen:D #1 = \l_regex_unique_step_int + \exp_after:wN \use_none:n + \else: + \exp_after:wN \use:n + \fi: + } +\cs_new_protected:Npn \regex_store_state:n #1 + { + \int_incr:N \l_regex_max_index_int + \tex_skip:D \l_regex_max_index_int #1 sp \scan_stop: + \regex_store_submatches:n {#1} + } +\cs_new_protected_nopar:Npn \regex_state_use_submatches: + { \regex_state_use_aux:n { } } +\cs_new_protected_nopar:Npn \regex_state_use: + { \regex_state_use_aux:n { \exp_after:wN \regex_state_use_aux_ii:w } } +\cs_new:Npn \regex_state_use_aux_ii:w #1 \s_regex_toks { } +\cs_new_protected:Npn \regex_state_use_aux:n #1 + { + \regex_if_state_free:nT { \l_regex_current_state_int } + { + \tex_dimen:D \l_regex_current_state_int + = \l_regex_unique_step_int sp \scan_stop: + #1 \tex_the:D \tex_toks:D \l_regex_current_state_int \scan_stop: + } + } +\cs_new_protected_nopar:Npn \regex_disable_submatches: + { + \cs_set_eq:NN \regex_state_use_submatches: \regex_state_use: + \cs_set_eq:NN \regex_store_submatches:n + \regex_protected_use_none:n + \cs_set_eq:NN \regex_action_submatches:n + \regex_protected_use_none:n + } +\cs_new_protected:Npn \regex_protected_use_none:n #1 { } +\cs_new_protected:Npn \regex_store_submatches:n #1 + { + \exp_after:wN \regex_store_submatches_aux:Nw + \exp_after:wN \l_regex_current_submatches_prop + \tex_the:D \tex_toks:D #1 \scan_stop: \q_stop + { #1 } + } +\cs_new_protected:Npn \regex_store_submatches_aux:Nw #1 #2 \s_regex_toks + { + \exp_args:No \regex_store_submatches_aux_ii:wnnwwn { #1 } + #2 + \regex_state_submatches:nn { \c_minus_one } { \q_prop } + \s_regex_toks + } +\cs_new_protected:Npn \regex_store_submatches_aux_ii:wnnwwn + #1 \regex_state_submatches:nn #2#3 #4 \s_regex_toks #5 \q_stop #6 + { + \tex_toks:D #6 \exp_after:wN + { + \exp_after:wN \regex_state_submatches:nn \exp_after:wN + { \int_value:w \int_eval:w \l_regex_unique_step_int + \c_one } + { #1 } + \regex_state_submatches:nn {#2} {#3} + \s_regex_toks + #5 + } + } +\cs_new_protected:Npn \regex_state_submatches:nn #1#2 + { + \if_num:w #1 = \l_regex_unique_step_int + \tl_set:Nn \l_regex_current_submatches_prop {#2} + \fi: + } +\cs_new_protected:Npn \regex_match:n #1 + { + \tl_set:Nx \l_regex_query_str { \tl_to_other_str:n {#1} } + \regex_match_initial_setup: + \regex_match_once: + } +\cs_new_protected_nopar:Npn \regex_match_once: + { + \regex_match_setup: + \exp_after:wN \regex_match_once_aux: \l_regex_query_str + \q_recursion_tail \q_recursion_stop + \bool_if:NT \l_regex_success_bool { \l_regex_every_match_tl } + } +\cs_new_protected_nopar:Npn \regex_match_once_aux: + { \str_skip_do:nn { \l_regex_start_step_int } { \regex_match_loop:N } } +\cs_new_protected_nopar:Npn \regex_match_initial_setup: + { + \tl_clear:N \l_regex_look_behind_str + \prg_stepwise_inline:nnnn {1} {1} { \l_regex_max_state_int } + { \tex_dimen:D ##1 \c_minus_one sp \scan_stop: } + \int_set_eq:NN \l_regex_unique_step_int \c_minus_one + \int_set_eq:NN \l_regex_start_step_int \c_minus_one + \int_set_eq:NN \l_regex_current_step_int \c_zero + \int_set_eq:NN \l_regex_success_step_int \c_zero + \bool_set_false:N \l_regex_success_empty_bool + } +\cs_new_protected_nopar:Npn \regex_match_setup: + { + \prop_clear:N \l_regex_current_submatches_prop + \bool_if:NTF \l_regex_success_empty_bool + { \cs_set_eq:NN \regex_last_match_empty:F \regex_last_match_empty_yes:F } + { \cs_set_eq:NN \regex_last_match_empty:F \regex_last_match_empty_no:F } + \int_set_eq:NN \l_regex_start_step_int \l_regex_success_step_int + \int_set_eq:NN \l_regex_current_step_int \l_regex_start_step_int + \int_decr:N \l_regex_current_step_int + \bool_set_false:N \l_regex_success_bool + \int_zero:N \l_regex_max_index_int + \regex_store_state:n {1} + } +\cs_new_protected_nopar:Npn \regex_match_loop_setup: + { + \int_incr:N \l_regex_current_step_int + \int_incr:N \l_regex_unique_step_int + \bool_set_false:N \l_regex_fresh_thread_bool + } +\cs_new_eq:NN \s_regex_step \scan_stop: +\cs_new:Npn \regex_break_step:w #1 \s_regex_step { } +\cs_new_protected_nopar:Npn \regex_match_loop:N #1 + { + \regex_match_loop_setup: + \token_if_eq_meaning:NNTF #1 \q_recursion_tail + { \int_set_eq:NN \l_regex_current_char_int \c_minus_one } + { \int_set:Nn \l_regex_current_char_int {`#1} } + \cs_set_nopar:Npx \regex_tmp:w + { + \int_zero:N \l_regex_max_index_int + \prg_stepwise_function:nnnN + {1} {1} { \l_regex_max_index_int } + \regex_match_one_index:n + } + \regex_tmp:w + \s_regex_step + \if_num:w \l_regex_max_index_int = \c_zero + \exp_after:wN \use_none_delimit_by_q_recursion_stop:w + \fi: + \quark_if_recursion_tail_stop:N #1 + \bool_if:NT \l_regex_look_behind_bool + { \tl_put_left:Nx \l_regex_look_behind_str {#1} } + \regex_match_loop:N + } +\cs_new_nopar:Npn \regex_match_one_index:n #1 + { + \regex_match_one_index_aux:n { \int_value:w \tex_skip:D #1 } + } +\cs_new_protected_nopar:Npn \regex_match_one_index_aux:n #1 + { + \int_set:Nn \l_regex_current_state_int {#1} + \prop_clear:N \l_regex_current_submatches_prop + \regex_state_use_submatches: + } +\cs_new_protected_nopar:Npn \regex_action_start_wildcard:nn #1#2 + { + \bool_set_true:N \l_regex_fresh_thread_bool + \regex_action_free:n {#2} + \bool_set_false:N \l_regex_fresh_thread_bool + \regex_action_cost:n {#1} + } +\cs_new_protected_nopar:Npn \regex_action_cost:n #1 + { \regex_store_state:n {#1} } +\cs_new_protected_nopar:Npn \regex_action_success: + { + \regex_last_match_empty:F + { + \bool_set_true:N \l_regex_success_bool + \bool_set_eq:NN \l_regex_success_empty_bool + \l_regex_fresh_thread_bool + \int_set_eq:NN \l_regex_success_step_int + \l_regex_current_step_int + \prop_set_eq:NN \l_regex_success_submatches_prop + \l_regex_current_submatches_prop + \regex_break_step:w + } + } +\cs_new_protected_nopar:Npn \regex_action_free:n #1 + { + \regex_if_state_free:nT {#1} + { + \use:x + { + \int_set:Nn \l_regex_current_state_int {#1} + \regex_store_submatches:n { \l_regex_current_state_int } + \regex_state_use: + \int_set:Nn \l_regex_current_state_int + { \int_use:N \l_regex_current_state_int } + \tl_set:Nn \exp_not:N \l_regex_current_submatches_prop + { \exp_not:o \l_regex_current_submatches_prop } + } + } + } +\cs_new_protected_nopar:Npn \regex_action_submatch:n #1 + { + \prop_put:Nno \l_regex_current_submatches_prop {#1} + { \int_use:N \l_regex_current_step_int } + \regex_store_submatches:n { \l_regex_current_state_int } + } +\cs_new_protected_nopar:Npn \regex_action_repeat_copy:n { \ERROR } +\cs_new_protected_nopar:Npn \regex_action_repeat_move:n { \ERROR } +\cs_new_protected_nopar:Npn \regex_action_no_repeat_copy:n { \ERROR } +\cs_new_protected_nopar:Npn \regex_action_no_repeat_move:n { \ERROR } +\cs_new_protected_nopar:Npn \regex_extract: + { + \seq_gclear:N \g_regex_submatches_seq + \prg_stepwise_inline:nnnn + {0} {1} { \l_regex_capturing_group_int } + { + \regex_extract_aux:nTF { ##1 } + { + \seq_gput_right:Nx \g_regex_submatches_seq + { + \str_from_to:Nnn \l_regex_query_str + { \l_regex_tmpa_tl } + { \l_regex_tmpb_tl } + } + } + { \seq_gput_right:Nn \g_regex_submatches_seq { } } + } + } +\cs_new_protected_nopar:Npn \regex_extract_aux:nTF #1#2#3 + { + \prop_get:NnNTF \l_regex_success_submatches_prop + { #1 < } \l_regex_tmpa_tl + { + \prop_get:NnNTF \l_regex_success_submatches_prop + { #1 > } \l_regex_tmpb_tl + {#2} + {#3} + } + {#3} + } +\cs_new_protected_nopar:Npn \regex_set:Nn + { \regex_set_aux:NNn \tl_set_eq:NN } +\cs_new_protected_nopar:Npn \regex_gset:Nn + { \regex_set_aux:NNn \tl_gset_eq:NN } +\cs_new_protected:Npn \regex_set_aux:NNn #1#2#3 + { + \group_begin: + \regex_build:n {#3} + \tl_gset:Nx \g_regex_tmpa_tl + { + \exp_not:N \regex_nfa:Nw \exp_not:N #2 + \l_regex_max_state_int + = \int_use:N \l_regex_max_state_int + \l_regex_capturing_group_int + = \int_use:N \l_regex_capturing_group_int + \bool_if:NTF \l_regex_look_behind_bool + { \bool_set_true:N \l_regex_look_behind_bool } + { \bool_set_false:N \l_regex_look_behind_bool } + \prg_stepwise_function:nnnN + {1} {1} {\l_regex_max_state_int} + \regex_set_aux:n + \regex_nfa_end: + } + \group_end: + #1 #2 \g_regex_tmpa_tl + } +\cs_new_nopar:Npn \regex_set_aux:n #1 + { \tex_toks:D #1 { \tex_the:D \tex_toks:D #1 } } +\cs_new:Npn \regex_nfa:Nw #1 #2 \regex_nfa_end: + { \msg_expandable_error:n { Automaton~#1 used~incorrectly. } } +\cs_new_eq:NN \regex_nfa_end: \scan_stop: +\prg_new_protected_conditional:Npnn \regex_check_nfa:N #1 { TF } + { \exp_after:wN \regex_check_nfa_aux:Nw #1 \q_stop } +\cs_new:Npn \regex_check_nfa_aux:Nw #1 #2 \q_stop + { + \if_meaning:w \regex_nfa:Nw #1 + \prg_return_true: + \else: + \msg_error:nnx { regex } { not-nfa } { \token_to_str:N #1 } + \prg_return_false: + \fi: + } +\msg_new:nnn { regex } { not-nfa } + { + I~was~expecting~a~regular~expression~variable.\\ + Instead,~I~got~#1. + } +\cs_new_protected_nopar:Npn \regex_use:N #1 + { \exp_after:wN \use_none:nn #1 } +\cs_new_protected:Npn \regex_user_aux:n #1 + { + \group_begin: + \tl_clear:N \l_regex_every_match_tl + #1 + \group_end: + } +\cs_new_protected_nopar:Npn \regex_return_after_group: + { + \if_bool:N \l_regex_success_bool + \group_insert_after:N \prg_return_true: + \else: + \group_insert_after:N \prg_return_false: + \fi: + } +\cs_new_protected_nopar:Npn \regex_extract_after_group:N #1 + { + \if_bool:N \l_regex_success_bool + \regex_extract: + \group_insert_after:N \seq_set_eq:NN + \group_insert_after:N #1 + \group_insert_after:N \g_regex_submatches_seq + \fi: + } +\cs_new_protected_nopar:Npn \regex_count_after_group:N #1 + { + \group_insert_after:N \int_set_eq:NN + \group_insert_after:N #1 + \group_insert_after:N \g_regex_match_count_int + } +\prg_new_protected_conditional:Npnn \regex_match:nn #1#2 { T , F , TF } + { + \regex_user_aux:n + { + \regex_disable_submatches: + \regex_build:n {#1} + \regex_match:n {#2} + \regex_return_after_group: + } + } +\prg_new_protected_conditional:Npnn \regex_match:Nn #1#2 { T , F , TF } + { + \regex_check_nfa:NTF #1 + { + \regex_user_aux:n + { + \regex_disable_submatches: + \regex_use:N #1 + \regex_match:n {#2} + \regex_return_after_group: + } + } + { \prg_return_false: } + } +\cs_new_protected:Npn \regex_count:nnN #1#2#3 + { + \regex_user_aux:n + { + \regex_disable_submatches: + \int_gzero:N \g_regex_match_count_int + \tl_set:Nn \l_regex_every_match_tl + { + \int_gincr:N \g_regex_match_count_int + \regex_match_once: + } + \regex_build:n {#1} + \regex_match:n {#2} + \regex_count_after_group:N #3 + } + } +\cs_new_protected:Npn \regex_count:NnN #1#2#3 + { + \regex_check_nfa:NTF #1 + { + \regex_user_aux:n + { + \regex_disable_submatches: + \int_gzero:N \g_regex_match_count_int + \tl_set:Nn \l_regex_every_match_tl + { + \int_gincr:N \g_regex_match_count_int + \regex_match_once: + } + \regex_use:N #1 + \regex_match:n {#2} + \regex_count_after_group:N #3 + } + } + { } + } +\cs_new_protected:Npn \regex_extract:nnN #1#2#3 + { + \regex_user_aux:n + { + \regex_build:n {#1} + \regex_match:n {#2} + \regex_extract_after_group:N #3 + } + } +\prg_new_protected_conditional:Npnn \regex_extract:nnN #1#2#3 { T , F , TF } + { + \regex_user_aux:n + { + \regex_build:n {#1} + \regex_match:n {#2} + \regex_extract_after_group:N #3 + \regex_return_after_group: + } + } +\cs_new_protected:Npn \regex_extract:NnN #1#2#3 + { + \regex_check_nfa:NTF #1 + { + \regex_user_aux:n + { + \regex_use:N #1 + \regex_match:n {#2} + \regex_extract_after_group:N #3 + } + } + { } + } +\prg_new_protected_conditional:Npnn \regex_extract:NnN #1#2#3 { T , F , TF } + { + \regex_check_nfa:NTF #1 + { + \regex_user_aux:n + { + \regex_use:N #1 + \regex_match:n {#2} + \regex_extract_after_group:N #3 + \regex_return_after_group: + } + } + { \prg_return_false: } + } +\cs_new_protected:Npn \regex_split:nnN #1#2#3 + { + \regex_user_aux:n + { + \seq_gclear:N \g_regex_split_seq + \tl_set:Nn \l_regex_every_match_tl { \regex_split_aux: } + \regex_build:n {#1} + \regex_match:n {#2} + \regex_split_after_group:N #3 + } + } +\cs_new_protected:Npn \regex_split:NnN #1#2#3 + { + \regex_check_nfa:NTF #1 + { + \regex_user_aux:n + { + \seq_gclear:N \g_regex_split_seq + \tl_set:Nn \l_regex_every_match_tl { \regex_split_aux: } + \regex_use:N #1 + \regex_match:n {#2} + \regex_split_after_group:N #3 + } + } + { } + } +\cs_new_protected_nopar:Npn \regex_split_aux: + { + \int_compare:nNnF \l_regex_start_step_int = \l_regex_success_step_int + { + \regex_extract: + \seq_pop:NN \g_regex_submatches_seq \l_regex_tmpa_tl + \regex_extract_aux:nTF {0} + { + \seq_gput_left:Nx \g_regex_submatches_seq + { + \str_from_to:Nnn \l_regex_query_str + { \int_use:N \l_regex_start_step_int } + { \l_regex_tmpa_tl } + } + } + { \msg_error:nn { regex } { internal } } + \seq_gconcat:NNN \g_regex_split_seq + \g_regex_split_seq \g_regex_submatches_seq + } + \regex_match_once: + } +\cs_new_protected_nopar:Npn \regex_split_after_group:N #1 + { + \int_compare:nNnTF \l_regex_start_step_int = \l_regex_current_step_int + { + \bool_if:NF \l_regex_success_empty_bool + { \seq_gput_right:Nn \g_regex_split_seq { } } + } + { + \seq_gput_right:Nx \g_regex_split_seq + { + \str_from_to:Nnn \l_regex_query_str + { \int_use:N \l_regex_start_step_int } + { \int_use:N \l_regex_current_step_int } + } + } + \group_insert_after:N \seq_set_eq:NN + \group_insert_after:N #1 + \group_insert_after:N \g_regex_split_seq + } +\cs_new_protected:Npn \regex_replacement:n #1 + { + \str_aux_escape:NNNn + \prg_do_nothing: + \regex_replacement_escaped:N + \regex_replacement_raw:N + {#1} + \tl_set_eq:NN \l_regex_replacement_tl \g_str_result_tl + \tl_set:Nx \l_regex_replacement_tl + { \l_regex_replacement_tl \prg_do_nothing: } + } +\cs_new_nopar:Npn \regex_replacement_raw:N { \exp_not:N \prg_do_nothing: } +\cs_new_nopar:Npn \regex_replacement_escaped:N #1 + { + \if_num:w 9 < 1 #1 \exp_stop_f: + \exp_not:N \regex_replacement_escaped_ii:nN {#1} + \else: + #1 + \fi: + } +\cs_new_nopar:Npn \regex_replacement_escaped_ii:nN #1#2 + { + \regex_token_if_other_digit:NTF #2 + { \regex_replacement_escaped_ii:nN {#1#2} } + { \exp_not:N \seq_item:Nn \exp_not:N \g_regex_submatches_seq {#1} #2 } + } +\prg_new_conditional:Npnn \regex_token_if_other_digit:N #1 { TF } + { + \if_num:w 9 < 1 \exp_not:N #1 \exp_stop_f: + \prg_return_true: + \else: + \prg_return_false: + \fi: + } +\cs_new_protected_nopar:Npn \regex_replace_after_group:N #1 + { + \group_insert_after:N \tl_set_eq:NN + \group_insert_after:N #1 + \group_insert_after:N \g_regex_replaced_str + } +\cs_new_protected:Npn \regex_replace_once_aux:Nn #1#2 + { + \group_begin: + \regex_replace_after_group:N #1 + \tl_clear:N \l_regex_every_match_tl + #2 + \exp_args:No \regex_match:n {#1} + \regex_extract: + \regex_extract_aux:nTF {0} + { + \tl_gset:Nx \g_regex_replaced_str + { + \str_from_to:Nnn \l_regex_query_str {0} { \l_regex_tmpa_tl } + \l_regex_replacement_tl + \str_from_to:Nnn \l_regex_query_str + { \l_regex_tmpb_tl } { \c_max_int } + } + } + { \tl_gset_eq:NN \g_regex_replaced_str \l_regex_query_str } + \group_end: + } +\cs_new_protected:Npn \regex_replace_once:nnN #1#2#3 + { + \regex_replace_once_aux:Nn #3 + { + \regex_build:n {#1} + \regex_replacement:n {#2} + } + } +\cs_new_protected:Npn \regex_replace_once:NnN #1#2#3 + { + \regex_replace_once_aux:Nn #3 + { + \regex_use:N #1 + \regex_replacement:n {#2} + } + } +\prg_new_protected_conditional:Npnn \regex_replace_once:nnN #1#2#3 {T,F,TF} + { + \regex_replace_once_aux:Nn #3 + { + \regex_build:n {#1} + \regex_replacement:n {#2} + \regex_return_after_group: + } + } +\prg_new_protected_conditional:Npnn \regex_replace_once:NnN #1#2#3 {T,F,TF} + { + \regex_replace_once_aux:Nn #3 + { + \regex_use:N #1 + \regex_replacement:n {#2} + \regex_return_after_group: + } + } +\cs_new_protected:Npn \regex_replace_all_aux:Nn #1#2 + { + \group_begin: + \regex_replace_after_group:N #1 + \tl_set:Nn \l_regex_every_match_tl { \regex_replace_all_aux: } + \tl_gclear:N \g_regex_replaced_str + #2 + \exp_args:No \regex_match:n {#1} + \tl_gput_right:Nx \g_regex_replaced_str + { + \str_from_to:Nnn \l_regex_query_str + { \int_use:N \l_regex_start_step_int } + { \c_max_int } + } + \group_end: + } +\cs_new_protected_nopar:Npn \regex_replace_all_aux: + { + \regex_extract: + \regex_extract_aux:nTF {0} + { + \tl_gput_right:Nx \g_regex_replaced_str + { + \str_from_to:Nnn \l_regex_query_str + { \int_use:N \l_regex_start_step_int } + { \l_regex_tmpa_tl } + \l_regex_replacement_tl + } + } + { \msg_error:nn { regex } { internal } } + \regex_match_once: + } +\cs_new_protected:Npn \regex_replace_all:nnN #1#2#3 + { + \regex_replace_all_aux:Nn #3 + { + \regex_build:n {#1} + \regex_replacement:n {#2} + } + } +\cs_new_protected:Npn \regex_replace_all:NnN #1#2#3 + { + \regex_replace_all_aux:Nn #3 + { + \regex_use:N #1 + \regex_replacement:n {#2} + } + } +%% +%% +%% End of file `l3regex-trace.sty'. diff --git a/Master/texmf-dist/tex/latex/l3experimental/l3str/l3regex.sty b/Master/texmf-dist/tex/latex/l3experimental/l3str/l3regex.sty new file mode 100644 index 00000000000..5da00fdd3d0 --- /dev/null +++ b/Master/texmf-dist/tex/latex/l3experimental/l3str/l3regex.sty @@ -0,0 +1,1637 @@ +%% +%% This is file `l3regex.sty', +%% generated with the docstrip utility. +%% +%% The original source files were: +%% +%% l3regex.dtx (with options: `package') +%% +%% EXPERIMENTAL CODE +%% +%% Do not distribute this file without also distributing the +%% source files specified above. +%% +%% Do not distribute a modified version of this file. +%% +%% File: l3regex.dtx Copyright (C) 2011 The LaTeX3 Project +%% +%% It may be distributed and/or modified under the conditions of the +%% LaTeX Project Public License (LPPL), either version 1.3c of this +%% license or (at your option) any later version. The latest version +%% of this license is in the file +%% +%% http://www.latex-project.org/lppl.txt +%% +%% This file is part of the "l3experimental bundle" (The Work in LPPL) +%% and all files in that bundle must be distributed together. +%% +%% The released version of this bundle is available from CTAN. +%% +%% ----------------------------------------------------------------------- +%% +%% The development version of the bundle can be found at +%% +%% http://www.latex-project.org/svnroot/experimental/trunk/ +%% +%% for those people who are interested. +%% +%%%%%%%%%%% +%% NOTE: %% +%%%%%%%%%%% +%% +%% Snapshots taken from the repository represent work in progress and may +%% not work or may contain conflicting material! We therefore ask +%% people _not_ to put them into distributions, archives, etc. without +%% prior consultation with the LaTeX3 Project. +%% +%% ----------------------------------------------------------------------- +\RequirePackage{expl3} +\GetIdInfo$Id: l3regex.dtx 2895 2011-10-09 15:58:19Z joseph $ + {L3 Experimental Regular Expressions} +\ProvidesExplPackage + {\ExplFileName}{\ExplFileDate}{\ExplFileVersion}{\ExplFileDescription} +\RequirePackage{l3str} +\cs_new:Npn \regex_tmp:w { } +\tl_new:N \g_regex_tmpa_tl +\tl_new:N \l_regex_tmpa_tl +\tl_new:N \l_regex_tmpb_tl +\int_new:N \l_regex_tmpa_int +\tl_new:N \l_regex_pattern_str +\int_new:N \l_regex_max_state_int +\int_new:N \l_regex_left_state_int +\int_new:N \l_regex_right_state_int +\seq_new:N \l_regex_left_state_seq +\seq_new:N \l_regex_right_state_seq +\int_new:N \l_regex_capturing_group_int +\seq_new:N \l_regex_capturing_group_seq +\tl_new:N \l_regex_one_or_group_tl +\int_new:N \l_regex_repetition_int +\bool_new:N \l_regex_look_behind_bool +\tl_new:N \l_regex_look_behind_str +\cs_new_eq:NN \regex_build_tmp_class:n \use_none:n +\bool_new:N \l_regex_class_bool +\tl_new:N \l_regex_class_tl +\tl_const:Nn \c_regex_d_tl + { + \regex_item_range:nn {48} {57} % 0--9 + } +\tl_const:Nn \c_regex_D_tl + { + \regex_item_more:n {57} % 9 + \regex_item_range:nn {0} {47} % 0 + } +\tl_const:Nn \c_regex_h_tl + { + \regex_item_equal:n {32} % space + \regex_item_equal:n {9} % tab + } +\tl_const:Nn \c_regex_H_tl + { + \regex_item_neq:n {32} % space + \regex_item_neq:n {9} % tab + \regex_break_true:w + } +\tl_const:Nn \c_regex_s_tl + { + \regex_item_equal:n {32} % space + \regex_item_neq:n {11} % vtab + \regex_item_range:nn {9} {13} % tab, lf, vtab, ff, cr + } +\tl_const:Nn \c_regex_S_tl + { + \regex_item_more:n {32} % > space + \regex_item_range:nn {14} {31} % tab < ... < space + \regex_item_range:nn {0} {8} % < tab + \regex_item_equal:n {11} % vtab + } +\tl_const:Nn \c_regex_v_tl + { + \regex_item_range:nn {10} {13} % lf, vtab, ff, cr + } +\tl_const:Nn \c_regex_V_tl + { + \regex_item_more:n {13} % cr + \regex_item_range:nn {0} {9} % < lf + } +\tl_const:Nn \c_regex_w_tl + { + \regex_item_range:nn {97} {122} % a--z + \regex_item_range:nn {65} {90} % A--Z + \regex_item_range:nn {48} {57} % 0--9 + \regex_item_equal:n {95} % _ + } +\tl_const:Nn \c_regex_W_tl + { + \regex_item_range:nn {0} {47} % <`0 + \regex_item_range:nn {58} {64} % (`9+1)--(`A-1) + \regex_item_range:nn {91} {94} % (`Z+1)--(`_-1) + \regex_item_equal:n {96} % ` + \regex_item_more:n {122} % z + } +\tl_const:Nn \c_regex_N_tl + { + \regex_item_neq:n {10} % lf + \regex_break_true:w + } +\tl_new:N \l_regex_query_str +\int_new:N \l_regex_start_step_int +\int_new:N \l_regex_current_char_int +\int_new:N \l_regex_current_step_int +\int_new:N \l_regex_unique_step_int +\int_new:N \l_regex_current_state_int +\prop_new:N \l_regex_current_submatches_prop +\int_new:N \l_regex_max_index_int +\tl_new:N \l_regex_every_match_tl +\cs_new_protected:Npn \regex_last_match_empty_no:F #1 {#1} +\cs_new_protected:Npn \regex_last_match_empty_yes:F + { \int_compare:nNnF \l_regex_start_step_int = \l_regex_current_step_int } +\cs_new_eq:NN \regex_last_match_empty:F \regex_last_match_empty_no:F +\bool_new:N \l_regex_success_bool +\bool_new:N \l_regex_success_empty_bool +\int_new:N \l_regex_success_step_int +\prop_new:N \l_regex_success_submatches_prop +\bool_new:N \l_regex_fresh_thread_bool +\seq_new:N \g_regex_submatches_seq +\int_new:N \g_regex_match_count_int +\seq_new:N \g_regex_split_seq +\tl_new:N \l_regex_replacement_tl +\tl_new:N \g_regex_replaced_str +\cs_new_eq:NN \s_regex_toks \scan_stop: +\cs_new_protected:Npn \regex_toks_put_left:Nx #1#2 + { + \cs_set_nopar:Npx \regex_tmp:w \s_regex_toks { \s_regex_toks #2 } + \tex_toks:D #1 \exp_after:wN \exp_after:wN \exp_after:wN + { \exp_after:wN \regex_tmp:w \tex_the:D \tex_toks:D #1 } + } +\cs_new_protected:Npn \regex_toks_put_right:Nx #1#2 + { + \cs_set_nopar:Npx \regex_tmp:w {#2} + \tex_toks:D #1 \exp_after:wN + { \tex_the:D \tex_toks:D \exp_after:wN #1 \regex_tmp:w } + } +\cs_new_eq:NN \regex_if_tail_stop:N \quark_if_recursion_tail_stop:N +\cs_new_protected_nopar:Npn \regex_if_tail_error:Nn #1#2 + { \quark_if_recursion_tail_stop_do:Nn #1 { \regex_build_error:n {#2} } } +\cs_new_protected_nopar:Npn \regex_build_error:n #1 + { + \msg_error:nnxx { regex } { build-error } {\int_eval:n{#1}} + { + \prg_case_int:nnn {#1} + { + {-999} {File~not~found} + {-998} {Unsupported~construct} + {-997} {The~regular~expression~is~too~large~(32768~states).} + {1} {\iow_char:N\\~at~end~of~pattern } + {4} {Numbers~out~of~order~in~\iow_char:N\{\iow_char\}~quantifier.} + {6} {Missing~terminating~\iow_char:N\]~for~character~class } + {7} {Invalid~escape~sequence~in~character~class} + {8} {Range~out~of~order~in~character~class} + {22} {Mismatched~parentheses} + {34} {Character~value~in~\iow_char:N\\x{...}~sequence~is~too~large} + } + { Internal~bug. } + } + } +\msg_new:nnn { regex } { build-error } { (error~#1): ~ #2 } +\cs_new_nopar:Npn \regex_break_true:w #1 \regex_break_point:TF #2 #3 {#2} +\cs_new_nopar:Npn \regex_break_false:w #1 \regex_break_point:TF #2 #3 {#3} +\cs_new_eq:NN \regex_break_point:TF \use_ii:nn +\cs_new_nopar:Npn \regex_item_equal:n #1 + { + \if_num:w #1 = \l_regex_current_char_int + \exp_after:wN \regex_break_true:w + \fi: + } +\cs_new_nopar:Npn \regex_item_range:nn #1 #2 + { + \reverse_if:N \if_num:w #1 > \l_regex_current_char_int + \reverse_if:N \if_num:w #2 < \l_regex_current_char_int + \exp_after:wN \exp_after:wN \exp_after:wN \regex_break_true:w + \fi: + \fi: + } +\cs_new_nopar:Npn \regex_item_less:n #1 + { + \if_num:w #1 > \l_regex_current_char_int + \exp_after:wN \regex_break_true:w + \fi: + } +\cs_new_nopar:Npn \regex_item_more:n #1 + { + \if_num:w #1 < \l_regex_current_char_int + \exp_after:wN \regex_break_true:w + \fi: + } +\cs_new_nopar:Npn \regex_item_neq:n #1 + { + \if_num:w #1 = \l_regex_current_char_int + \exp_after:wN \regex_break_false:w + \fi: + } +\cs_new_protected:Npn \regex_get_digits:nw #1 + { + \tex_afterassignment:D \regex_tmp:w + \cs_set_nopar:Npx \regex_tmp:w + { + \exp_not:n {#1} + { \if_false: } } \fi: + \regex_get_digits_aux:N + } +\cs_new_nopar:Npn \regex_get_digits_aux:N #1 + { + \if_num:w 9 < 1 \exp_not:N #1 \exp_stop_f: + \else: + \if_charcode:w \c_space_token \exp_not:N #1 + \else: + \regex_get_digits_end:w + \fi: + \fi: + #1 \regex_get_digits_aux:N + } +\cs_new_nopar:Npn \regex_get_digits_end:w + \fi: \fi: #1 \regex_get_digits_aux:N + { + \fi: \fi: + \if_false: { { \fi: } } + #1 + } +\prg_new_conditional:Npnn \regex_aux_char_if_special:N #1 { TF } + { + \if_num:w `#1 < 97 \exp_stop_f: + \if_num:w `#1 < 58 \exp_stop_f: + \if_num:w \int_eval:w (`#1 - \c_eight)/\c_sixteen = \c_two + \prg_return_true: + \else: + \prg_return_false: + \fi: + \else: + \if_num:w \int_eval:w `#1 / 26 = \c_three + \prg_return_false: + \else: + \prg_return_true: + \fi: + \fi: + \else: + \if_num:w \int_eval:w `#1 / \c_five = 25 \exp_stop_f: + \prg_return_true: + \else: + \prg_return_false: + \fi: + \fi: + } +\prg_new_conditional:Npnn \regex_aux_char_if_alphanumeric:N #1 { TF } + { + \if_num:w `#1 < 91 \exp_stop_f: + \if_num:w `#1 < 65 \exp_stop_f: + \if_num:w \c_nine < 1 #1 \exp_stop_f: + \prg_return_true: + \else: + \prg_return_false: + \fi: + \else: + \prg_return_true: + \fi: + \else: + \if_num:w \int_eval:w (`#1-\c_six)/26 = \c_four + \prg_return_true: + \else: + \prg_return_false: + \fi: + \fi: + } +\cs_new_protected_nopar:Npn \regex_build_new_state: + { + \int_compare:nNnTF \l_regex_max_state_int > {32766} + { \regex_build_error:n {-997} } + { + \int_incr:N \l_regex_max_state_int + \tex_toks:D \l_regex_max_state_int { \s_regex_toks } + } + \int_set_eq:NN \l_regex_left_state_int \l_regex_right_state_int + \int_set_eq:NN \l_regex_right_state_int \l_regex_max_state_int + } +\cs_new_protected_nopar:Npn \regex_build_transition_aux:NN #1#2 + { + \regex_build_new_state: + \regex_toks_put_right:Nx \l_regex_left_state_int + { #1 { \int_use:N #2 } } + } +\cs_new_protected_nopar:Npn \regex_build_transitions_aux:NNNN #1#2#3#4 + { + \regex_build_new_state: + \regex_toks_put_right:Nx \l_regex_left_state_int + { + #1 { \int_use:N #2 } + #3 { \int_use:N #4 } + } + } +\cs_new_protected:Npn \regex_build:n #1 + { + \regex_build_setup: + \str_aux_escape:NNNn + \regex_build_i_unescaped:N + \regex_build_i_escaped:N + \regex_build_i_raw:N + { #1 } + \regex_build_open_aux: + \g_str_result_tl \prg_do_nothing: \prg_do_nothing: + \seq_push:Nn \l_regex_capturing_group_seq { 0 } + \regex_build_close_aux: \regex_build_group_: + \regex_build_end: + } +\cs_new_nopar:Npn \regex_build_i_unescaped:N #1 + { + \regex_aux_char_if_special:NTF #1 + { \exp_not:N \regex_build_control:N #1 } + { \exp_not:N \regex_build_raw:N #1 } + } +\cs_new_nopar:Npn \regex_build_i_escaped:N #1 + { + \regex_aux_char_if_alphanumeric:NTF #1 + { \exp_not:N \regex_build_control:N #1 } + { \exp_not:N \regex_build_raw:N #1 } + } +\cs_new_nopar:Npn \regex_build_i_raw:N #1 + { \exp_not:N \regex_build_raw:N #1 } +\cs_new_protected_nopar:Npn \regex_build_default_control:N #1 + { + \cs_if_exist_use:cF { regex_build_#1: } + { \regex_build_default_raw:N #1 } + } +\cs_new_protected_nopar:Npn \regex_build_setup: + { + \cs_set_eq:NN \regex_build_control:N \regex_build_default_control:N + \cs_set_eq:NN \regex_build_raw:N \regex_build_default_raw:N + \int_set_eq:NN \l_regex_capturing_group_int \c_zero + \int_zero:N \l_regex_left_state_int + \int_zero:N \l_regex_right_state_int + \int_zero:N \l_regex_max_state_int + \regex_build_new_state: + \regex_build_new_state: + \regex_toks_put_right:Nx \l_regex_left_state_int + { + \regex_action_start_wildcard:nn + { \int_use:N \l_regex_left_state_int } + { \int_use:N \l_regex_right_state_int } + } + } +\cs_new_protected_nopar:Npn \regex_build_end: + { + \seq_if_empty:NF \l_regex_capturing_group_seq + { + \regex_build_error:n {22} + \prg_replicate:nn + { \seq_length:N \l_regex_capturing_group_seq } % ( + { \regex_build_close_aux: \regex_build_group_: } + } + \regex_toks_put_right:Nx \l_regex_right_state_int + { \regex_action_success: } + } +\cs_new_protected_nopar:cpn { regex_build_^: } + { \regex_build_anchor_start:N \c_zero } +\cs_new_protected_nopar:Npn \regex_build_A: + { \regex_build_anchor_start:N \c_zero } +\cs_new_protected_nopar:Npn \regex_build_G: + { \regex_build_anchor_start:N \l_regex_start_step_int } +\cs_new_protected_nopar:Npn \regex_build_anchor_start:N #1 + { + \regex_build_new_state: + \regex_toks_put_right:Nx \l_regex_left_state_int + { + \exp_not:N \int_compare:nNnT {#1} = \l_regex_current_step_int + { \regex_action_free:n { \int_use:N \l_regex_right_state_int } } + } + } +\cs_new_protected_nopar:cpn { regex_build_$: } % $ + { + \regex_build_new_state: + \regex_toks_put_right:Nx \l_regex_left_state_int + { + \exp_not:N \int_compare:nNnT + \c_minus_one = \l_regex_current_char_int + { \regex_action_free:n { \int_use:N \l_regex_right_state_int } } + } + } +\cs_new_eq:Nc \regex_build_Z: { regex_build_$: } %$ +\cs_new_eq:Nc \regex_build_z: { regex_build_$: } %$ +\cs_new_protected_nopar:Npn \regex_build_b: + { + \bool_set_true:N \l_regex_look_behind_bool + \regex_build_new_state: + \regex_toks_put_right:Nx \l_regex_left_state_int + { + \exp_not:N \regex_if_word_boundary:TF + { \regex_action_free:n { \int_use:N \l_regex_right_state_int } } + { } + } + } +\cs_new_protected_nopar:Npn \regex_build_B: + { + \bool_set_true:N \l_regex_look_behind_bool + \regex_build_new_state: + \regex_toks_put_right:Nx \l_regex_left_state_int + { + \exp_not:N \regex_if_word_boundary:TF + { } + { \regex_action_free:n { \int_use:N \l_regex_right_state_int } } + } + } +\cs_new_protected_nopar:Npn \regex_if_word_boundary:TF + { + \tl_if_empty:NTF \l_regex_look_behind_str + { \c_regex_w_tl } + { + \group_begin: + \cs_set_nopar:Npx \regex_tmp:w + { + \int_set:Nn \l_regex_current_char_int + { ` \str_head:N \l_regex_look_behind_str } + } + \regex_tmp:w + \c_regex_w_tl + \regex_break_point:TF + { \group_end: \c_regex_W_tl } + { \group_end: \c_regex_w_tl } + } + \regex_break_point:TF + } +\cs_new_protected_nopar:Npn \regex_build_default_raw:N #1 + { + \cs_set:Npx \regex_build_tmp_class:n ##1 + { + \exp_not:n { \exp_not:N \if_num:w } + \int_value:w `#1 = \l_regex_current_char_int + \regex_action_cost:n { ##1 } + \exp_not:n { \exp_not:N \fi: } + } + \regex_build_one_quantifier: + } +\cs_new_protected_nopar:cpn { regex_build_.: } + { + \cs_set:Npn \regex_build_tmp_class:n ##1 + { + \exp_not:N \if_num:w \c_minus_one < \l_regex_current_char_int + \regex_action_cost:n {##1} + \exp_not:N \fi: + } + \regex_build_one_quantifier: + } +\cs_new_protected_nopar:Npn \regex_build_char_type:N #1 + { + \cs_set:Npn \regex_build_tmp_class:n ##1 + { + \exp_not:N #1 + \exp_not:N \regex_break_point:TF + { \regex_action_cost:n {##1} } + { } + } + \regex_build_one_quantifier: + } +\tl_map_inline:nn { dDhHsSvVwWN } + { + \cs_new_protected_nopar:cpx { regex_build_#1: } + { + \exp_not:N \regex_build_char_type:N + \exp_not:c { c_regex_#1_tl } + } + } +\cs_new_protected_nopar:cpn { regex_build_[: } + { + \tl_clear:N \l_regex_class_tl + \cs_set_eq:NN \regex_build_control:N \regex_class_control:N + \cs_set_eq:NN \regex_build_raw:N \regex_class_raw:N + \regex_class_first:NN + } +\cs_new_protected_nopar:Npn \regex_class_control:N #1 + { + \cs_if_exist_use:cF { regex_class_#1: } + { \regex_class_raw:N #1 } + } +\cs_new_protected_nopar:cpn { regex_class_]: } + { + \tl_if_empty:NTF \l_regex_class_tl %[ + { \regex_class_raw:N ] } + { + \cs_set_eq:NN \regex_build_control:N \regex_build_default_control:N + \cs_set_eq:NN \regex_build_raw:N \regex_build_default_raw:N + \cs_set:Npn \regex_build_tmp_class:n ##1 + { + \exp_not:o \l_regex_class_tl + \bool_if:NTF \l_regex_class_bool + { + \exp_not:N \regex_break_point:TF + { \regex_action_cost:n {##1} } + { } + } + { + \exp_not:N \regex_break_point:TF + { } + { \regex_action_cost:n {##1} } + } + } + \regex_build_one_quantifier: + } + } +\cs_new_protected_nopar:Npn \regex_class_first:NN #1#2 + { + \str_if_eq:nnTF {#1#2} { \regex_build_control:N ^ } + { \bool_set_false:N \l_regex_class_bool } + { + \bool_set_true:N \l_regex_class_bool + #1 #2 + } + } +\cs_new_protected_nopar:Npn \regex_class_raw:N #1#2#3 + { + \str_if_eq:nnTF {#2#3} { \regex_build_control:N - } + { \regex_class_range:Nw #1 } + { + \regex_class_single:N #1 + #2 #3 + } + } +\cs_new_protected_nopar:Npn \regex_class_single:N #1 + { + \tl_put_right:Nx \l_regex_class_tl + { \exp_not:N \regex_item_equal:n { \int_value:w `#1 } } + } +\cs_new_protected_nopar:Npn \regex_class_range:Nw #1#2#3 + { + \token_if_eq_meaning:NNTF #2 \regex_build_control:N + { + \cs_if_exist:cTF { regex_class_#3: } + { + \regex_class_single:N #1 + \regex_class_single:N - + #2#3 + } + { \regex_class_range_put:NN #1#3 } + } + { \regex_class_range_put:NN #1#3 } + } +\cs_new_protected_nopar:Npn \regex_class_range_put:NN #1#2 + { + \if_num:w `#1 > `#2 \exp_stop_f: + \regex_build_error:n {8} + \else: + \tl_put_right:Nx \l_regex_class_tl + { + \if_num:w `#1 = `#2 \exp_stop_f: + \exp_not:N \regex_item_equal:n + \else: + \exp_not:N \regex_item_range:nn { \int_value:w `#1 } + \fi: + { \int_value:w `#2 } + } + \fi: + } +\tl_map_inline:nn { dDhHsSvVwWN } + { + \cs_new_protected_nopar:cpx { regex_class_#1: } + { + \tl_put_right:Nn \exp_not:N \l_regex_class_tl + { \exp_not:c { c_regex_#1_tl } } + } + } +\cs_new_protected_nopar:Npn \regex_build_quantifier:w #1#2 + { + \token_if_eq_meaning:NNTF #1 \regex_build_control:N + { + \cs_if_exist_use:cF { regex_build_quantifier_#2:w } + { + \regex_build_quantifier_end:n { } + #1 #2 + } + } + { + \regex_build_quantifier_end:n { } + #1 #2 + } + } +\cs_new_protected_nopar:cpn { regex_build_quantifier_?:w } + { \regex_build_quantifier_aux:nnNN { } { ? } } +\cs_new_protected_nopar:cpn { regex_build_quantifier_*:w } + { \regex_build_quantifier_aux:nnNN { } { * } } +\cs_new_protected_nopar:cpn { regex_build_quantifier_+:w } + { \regex_build_quantifier_aux:nnNN { } { + } } +\cs_new_protected_nopar:Npn \regex_build_quantifier_aux:nnNN #1#2#3#4 + { + \str_if_eq:nnTF { #3 #4 } { \regex_build_control:N ? } + { \regex_build_quantifier_end:n { #2 #4 } #1 } + { + \regex_build_quantifier_end:n { #2 } #1 + #3 #4 + } + } +\cs_new_protected_nopar:cpn { regex_build_quantifier_ \c_lbrace_str :w } + { \regex_get_digits:nw { \regex_build_quantifier_lbrace:n } } +\cs_new_protected_nopar:Npn \regex_build_quantifier_lbrace:n #1 + { + \tl_if_empty:nTF {#1} + { + \regex_build_quantifier_end:n { } + \exp_after:wN \regex_build_raw:N \c_lbrace_str + } + { \regex_build_quantifier_lbrace:nw {#1} } + } +\cs_new_protected_nopar:Npx \regex_build_quantifier_lbrace:nw #1#2#3 + { + \exp_not:N \prg_case_str:nnn { #2 #3 } + { + { \exp_not:N \regex_build_control:N , } + { + \exp_not:N \regex_get_digits:nw + { \exp_not:N \regex_build_quantifier_lbrace:nnw {#1} } + } + { \exp_not:N \regex_build_control:N \c_rbrace_str } + { \exp_not:N \regex_build_quantifier_end:n {n} {#1} } + } + { + \exp_not:N \regex_build_quantifier_end:n { } + \exp_not:N \regex_build_raw:N \c_lbrace_str #1#2 + } + } +\cs_new_protected_nopar:Npn \regex_build_quantifier_lbrace:nnw #1#2#3 + { + \str_if_eq:xxTF { \exp_not:N #3 } { \c_rbrace_str } + { + \tl_if_empty:nTF {#2} + { \regex_build_quantifier_aux:nnN { {#1} {\c_max_int} } {nn} } + { + \int_compare:nNnT {#1} > {#2} + { \regex_build_error:n {4} } + \regex_build_quantifier_aux:nnN { {#1} {#2} } {nn} + } + } + { + \regex_build_quantifier_end:n { } + \use:x + { + \exp_not:n { \exp_args:No \tl_map_function:nN } + { \c_lbrace_str #1 #2 , } + \regex_build_raw:N + } + } + } +\cs_new_protected_nopar:Npn \regex_build_quantifier_end:n #1 + { \use:c { regex_build_ \l_regex_one_or_group_tl _ #1 : } } +\cs_new_protected_nopar:Npn \regex_build_one_quantifier: + { + \tl_set:Nx \l_regex_one_or_group_tl { one } + \regex_build_quantifier:w + } +\cs_new_protected_nopar:Npn \regex_build_one_: + { + \regex_build_transition_aux:NN + \regex_build_tmp_class:n \l_regex_right_state_int + } +\cs_new_protected_nopar:cpn { regex_build_one_?: } + { + \regex_build_transitions_aux:NNNN + \regex_build_tmp_class:n \l_regex_right_state_int + \regex_action_free:n \l_regex_right_state_int + } +\cs_new_protected_nopar:cpn { regex_build_one_??: } + { + \regex_build_transitions_aux:NNNN + \regex_action_free:n \l_regex_right_state_int + \regex_build_tmp_class:n \l_regex_right_state_int + } +\cs_new_protected_nopar:cpn { regex_build_one_*: } + { + \regex_build_transitions_aux:NNNN + \regex_build_tmp_class:n \l_regex_left_state_int + \regex_action_free:n \l_regex_right_state_int + } +\cs_new_protected_nopar:cpn { regex_build_one_*?: } + { + \regex_build_transitions_aux:NNNN + \regex_action_free:n \l_regex_right_state_int + \regex_build_tmp_class:n \l_regex_left_state_int + } +\cs_new_protected_nopar:cpn { regex_build_one_+: } + { + \regex_build_one_: + \int_set_eq:NN \l_regex_tmpa_int \l_regex_left_state_int + \regex_build_transitions_aux:NNNN + \regex_action_free:n \l_regex_tmpa_int + \regex_action_free:n \l_regex_right_state_int + } +\cs_new_protected_nopar:cpn { regex_build_one_+?: } + { + \regex_build_one_: + \int_set_eq:NN \l_regex_tmpa_int \l_regex_left_state_int + \regex_build_transitions_aux:NNNN + \regex_action_free:n \l_regex_right_state_int + \regex_action_free:n \l_regex_tmpa_int + } +\cs_new_protected_nopar:Npn \regex_build_one_n: #1 + { + \int_set_eq:NN \l_regex_tmpa_int \l_regex_right_state_int + \regex_build_new_state: + \regex_build_new_state: + \regex_toks_put_right:Nx \l_regex_tmpa_int + { %^^A safe \if nesting? + \exp_not:N \if_num:w #1 > \l_regex_repetition_int + \regex_action_repeat_move:n + { \int_use:N \l_regex_left_state_int } + \exp_not:N \else: + \regex_action_no_repeat_move:n + { \int_use:N \l_regex_right_state_int } + \exp_not:N \fi: + } + \regex_toks_put_right:Nx \l_regex_left_state_int + { + \regex_build_tmp_class:n + { \int_use:N \l_regex_tmpa_int } + } + } +\cs_new_eq:cN { regex_build_one_n?: } \regex_build_one_n: +\cs_new_protected_nopar:Npn \regex_build_one_nn: #1#2 + { + \regex_build_one_nn_aux:nn {#1} + { + \exp_not:N \if_num:w #2 > \l_regex_repetition_int + \regex_action_repeat_copy:n + { \int_use:N \l_regex_left_state_int } + \exp_not:N \fi: + \regex_action_no_repeat_move:n + { \int_use:N \l_regex_right_state_int } + } + } +\cs_new_protected_nopar:cpn { regex_build_one_nn?: } #1#2 + { + \regex_build_one_nn_aux:nn {#1} + { + \regex_action_no_repeat_copy:n + { \int_use:N \l_regex_right_state_int } + \exp_not:N \if_num:w #2 > \l_regex_repetition_int + \regex_action_repeat_move:n + { \int_use:N \l_regex_left_state_int } + \exp_not:N \fi: + } + } +\cs_new_protected_nopar:Npn \regex_build_one_nn_aux:nn #1#2 + { + \int_set_eq:NN \l_regex_tmpa_int \l_regex_right_state_int + \regex_build_new_state: + \regex_build_new_state: + \regex_toks_put_right:Nx \l_regex_tmpa_int + { + \exp_not:N \if_num:w #1 > \l_regex_repetition_int + \regex_action_repeat_move:n + { \int_use:N \l_regex_left_state_int } + \exp_not:N \else: + #2 + \exp_not:N \fi: + } + \regex_toks_put_right:Nx \l_regex_left_state_int + { + \regex_build_tmp_class:n + { \int_use:N \l_regex_tmpa_int } + } + } +\cs_new_protected_nopar:cpn { regex_build_(: } #1#2 + { + \str_if_eq:nnTF { #1 #2 } { \regex_build_control:N ? } + { \regex_build_special_group:NN } + { + \int_incr:N \l_regex_capturing_group_int + \seq_push:Nx \l_regex_capturing_group_seq + { \int_use:N \l_regex_capturing_group_int } + \regex_build_open_aux: + #1 #2 + } + } +\cs_new_protected_nopar:Npn \regex_build_open_aux: + { + \regex_build_new_state: + \seq_push:Nx \l_regex_left_state_seq + { \int_use:N \l_regex_left_state_int } + \seq_push:Nx \l_regex_right_state_seq + { \int_use:N \l_regex_right_state_int } + \regex_build_begin_alternation: + } +\cs_new_protected_nopar:cpn { regex_build_|: } + { + \regex_build_end_alternation: + \regex_build_begin_alternation: + } +\cs_new_protected_nopar:cpn { regex_build_): } + { + \seq_if_empty:NTF \l_regex_capturing_group_seq + { \regex_build_error:n {22} } + { + \regex_build_close_aux: + \regex_build_group_quantifier: + } + } +\cs_new_protected_nopar:Npn \regex_build_close_aux: + { + \regex_build_end_alternation: + \seq_pop:NN \l_regex_left_state_seq \l_regex_tmpa_tl + \int_set:Nn \l_regex_left_state_int \l_regex_tmpa_tl + \seq_pop:NN \l_regex_right_state_seq \l_regex_tmpa_tl + \int_set:Nn \l_regex_right_state_int \l_regex_tmpa_tl + } +\cs_new_protected_nopar:Npn \regex_build_begin_alternation: + { + \regex_build_new_state: + \seq_get:NN \l_regex_left_state_seq \l_regex_tmpa_tl + \int_set:Nn \l_regex_left_state_int \l_regex_tmpa_tl + \regex_toks_put_right:Nx \l_regex_left_state_int + { \regex_action_free:n { \int_use:N \l_regex_right_state_int } } + } +\cs_new_protected_nopar:Npn \regex_build_end_alternation: + { + \seq_get:NN \l_regex_right_state_seq \l_regex_tmpa_tl + \regex_toks_put_right:Nx \l_regex_right_state_int + { \regex_action_free:n { \l_regex_tmpa_tl } } + } +\cs_new_protected_nopar:Npn \regex_build_special_group:NN #1#2 + { + \cs_if_exist_use:cF { regex_build_special_group_\token_to_str:N #2 : } + { + \regex_build_error:n { -998 } + \regex_build_control:N ( % ) + \regex_build_raw:N ? + #1 #2 + } + } +\cs_new_protected_nopar:cpn { regex_build_special_group_:: } + { + \seq_push:Nx \l_regex_capturing_group_seq { * } + \regex_build_open_aux: + } +\cs_new_protected_nopar:Npn \regex_build_group_quantifier: + { + \tl_set:Nn \l_regex_one_or_group_tl { group } + \regex_build_quantifier:w + } +\cs_new_protected_nopar:Npn \regex_build_group_submatches:NN #1#2 + { + \seq_pop:NN \l_regex_capturing_group_seq \l_regex_tmpa_tl + \str_if_eq:xxF { \l_regex_tmpa_tl } { * } + { + \regex_toks_put_left:Nx #1 + { \regex_action_submatch:n { \l_regex_tmpa_tl < } } + \regex_toks_put_left:Nx #2 + { \regex_action_submatch:n { \l_regex_tmpa_tl > } } + } + } +\cs_new_protected_nopar:Npn \regex_build_group_: + { + \regex_build_group_submatches:NN + \l_regex_left_state_int \l_regex_right_state_int + \regex_build_transition_aux:NN + \regex_action_free:n \l_regex_right_state_int + } +\cs_new_protected_nopar:Npn \regex_build_group_shift:N #1 + { + \int_set_eq:NN \l_regex_tmpa_int \l_regex_left_state_int + \regex_build_new_state: + \tex_toks:D \l_regex_right_state_int = \tex_toks:D \l_regex_tmpa_int + \use:x + { + \tex_toks:D \l_regex_tmpa_int + { \s_regex_toks #1 { \int_use:N \l_regex_right_state_int } } + } + \regex_build_group_submatches:NN + \l_regex_right_state_int \l_regex_left_state_int + \int_set_eq:NN \l_regex_right_state_int \l_regex_left_state_int + } +\cs_new_protected_nopar:Npn \regex_build_group_qs_aux:NN #1#2 + { + \regex_build_group_shift:N \regex_action_free:n + \regex_build_transition_aux:NN \regex_action_free:n #1 + #2 \l_regex_tmpa_int + { \regex_action_free:n { \int_use:N \l_regex_right_state_int } } + } +\cs_new_protected_nopar:cpn { regex_build_group_?: } + { + \regex_build_group_qs_aux:NN + \l_regex_right_state_int \regex_toks_put_right:Nx + } +\cs_new_protected_nopar:cpn { regex_build_group_??: } + { + \regex_build_group_qs_aux:NN + \l_regex_right_state_int \regex_toks_put_left:Nx + } +\cs_new_protected_nopar:cpn { regex_build_group_*: } + { + \regex_build_group_qs_aux:NN + \l_regex_tmpa_int \regex_toks_put_right:Nx + } +\cs_new_protected_nopar:cpn { regex_build_group_*?: } + { + \regex_build_group_qs_aux:NN + \l_regex_tmpa_int \regex_toks_put_left:Nx + } +\cs_new_protected_nopar:cpn { regex_build_group_+: } + { + \regex_build_group_submatches:NN + \l_regex_left_state_int \l_regex_right_state_int + \int_set_eq:NN \l_regex_tmpa_int \l_regex_left_state_int + \regex_build_transitions_aux:NNNN + \regex_action_free:n \l_regex_tmpa_int + \regex_action_free:n \l_regex_right_state_int + } +\cs_new_protected_nopar:cpn { regex_build_group_+?: } + { + \regex_build_group_submatches:NN + \l_regex_left_state_int \l_regex_right_state_int + \int_set_eq:NN \l_regex_tmpa_int \l_regex_left_state_int + \regex_build_transitions_aux:NNNN + \regex_action_free:n \l_regex_right_state_int + \regex_action_free:n \l_regex_tmpa_int + } +\cs_new_protected_nopar:Npn \regex_build_group_n: #1 + { + \regex_build_group_shift:N \regex_action_repeat_move:n + \regex_build_transition_aux:NN + \regex_action_free:n \l_regex_tmpa_int + \use:x + { + \tex_toks:D \l_regex_tmpa_int + { + \s_regex_toks + \exp_not:N \if_num:w #1 > \l_regex_repetition_int + \tex_the:D \tex_toks:D \l_regex_tmpa_int + \exp_not:N \else: + \regex_action_no_repeat_move:n + { \int_use:N \l_regex_right_state_int } + \exp_not:N \fi: + } + } + } +\cs_new_eq:cN { regex_build_group_n?: } \regex_build_group_n: +\cs_new_protected_nopar:Npn \regex_build_group_nn: #1#2 + { + \regex_build_group_shift:N \regex_action_repeat_move:n + \regex_build_transition_aux:NN + \regex_action_free:n \l_regex_tmpa_int + \use:x + { + \tex_toks:D \l_regex_tmpa_int + { + \s_regex_toks + \exp_not:N \if_num:w #1 > \l_regex_repetition_int + \tex_the:D \tex_toks:D \l_regex_tmpa_int + \exp_not:N \else: + \exp_not:N \if_num:w #2 > \l_regex_repetition_int + \tex_the:D \tex_toks:D \l_regex_tmpa_int + \exp_not:N \fi: + \regex_action_no_repeat_copy:n + { \int_use:N \l_regex_right_state_int } + \exp_not:N \fi: + } + } + } +\cs_new_protected_nopar:cpn { regex_build_group_nn?: } #1#2 + { + \regex_build_group_shift:N \regex_action_repeat_move:n + \regex_build_transition_aux:NN + \regex_action_free:n \l_regex_tmpa_int + \use:x + { + \tex_toks:D \l_regex_tmpa_int + { + \s_regex_toks + \exp_not:N \if_num:w #1 > \l_regex_repetition_int + \tex_the:D \tex_toks:D \l_regex_tmpa_int + \exp_not:N \else: + \regex_action_no_repeat_copy:n + { \int_use:N \l_regex_right_state_int } + \exp_not:N \if_num:w #2 > \l_regex_repetition_int + \tex_the:D \tex_toks:D \l_regex_tmpa_int + \exp_not:N \fi: + \exp_not:N \fi: + } + } + } +\cs_new_protected:Npn \regex_if_state_free:nT #1 + { + \if_num:w \tex_dimen:D #1 = \l_regex_unique_step_int + \exp_after:wN \use_none:n + \else: + \exp_after:wN \use:n + \fi: + } +\cs_new_protected:Npn \regex_store_state:n #1 + { + \int_incr:N \l_regex_max_index_int + \tex_skip:D \l_regex_max_index_int #1 sp \scan_stop: + \regex_store_submatches:n {#1} + } +\cs_new_protected_nopar:Npn \regex_state_use_submatches: + { \regex_state_use_aux:n { } } +\cs_new_protected_nopar:Npn \regex_state_use: + { \regex_state_use_aux:n { \exp_after:wN \regex_state_use_aux_ii:w } } +\cs_new:Npn \regex_state_use_aux_ii:w #1 \s_regex_toks { } +\cs_new_protected:Npn \regex_state_use_aux:n #1 + { + \regex_if_state_free:nT { \l_regex_current_state_int } + { + \tex_dimen:D \l_regex_current_state_int + = \l_regex_unique_step_int sp \scan_stop: + #1 \tex_the:D \tex_toks:D \l_regex_current_state_int \scan_stop: + } + } +\cs_new_protected_nopar:Npn \regex_disable_submatches: + { + \cs_set_eq:NN \regex_state_use_submatches: \regex_state_use: + \cs_set_eq:NN \regex_store_submatches:n + \regex_protected_use_none:n + \cs_set_eq:NN \regex_action_submatches:n + \regex_protected_use_none:n + } +\cs_new_protected:Npn \regex_protected_use_none:n #1 { } +\cs_new_protected:Npn \regex_store_submatches:n #1 + { + \exp_after:wN \regex_store_submatches_aux:Nw + \exp_after:wN \l_regex_current_submatches_prop + \tex_the:D \tex_toks:D #1 \scan_stop: \q_stop + { #1 } + } +\cs_new_protected:Npn \regex_store_submatches_aux:Nw #1 #2 \s_regex_toks + { + \exp_args:No \regex_store_submatches_aux_ii:wnnwwn { #1 } + #2 + \regex_state_submatches:nn { \c_minus_one } { \q_prop } + \s_regex_toks + } +\cs_new_protected:Npn \regex_store_submatches_aux_ii:wnnwwn + #1 \regex_state_submatches:nn #2#3 #4 \s_regex_toks #5 \q_stop #6 + { + \tex_toks:D #6 \exp_after:wN + { + \exp_after:wN \regex_state_submatches:nn \exp_after:wN + { \int_value:w \int_eval:w \l_regex_unique_step_int + \c_one } + { #1 } + \regex_state_submatches:nn {#2} {#3} + \s_regex_toks + #5 + } + } +\cs_new_protected:Npn \regex_state_submatches:nn #1#2 + { + \if_num:w #1 = \l_regex_unique_step_int + \tl_set:Nn \l_regex_current_submatches_prop {#2} + \fi: + } +\cs_new_protected:Npn \regex_match:n #1 + { + \tl_set:Nx \l_regex_query_str { \tl_to_other_str:n {#1} } + \regex_match_initial_setup: + \regex_match_once: + } +\cs_new_protected_nopar:Npn \regex_match_once: + { + \regex_match_setup: + \exp_after:wN \regex_match_once_aux: \l_regex_query_str + \q_recursion_tail \q_recursion_stop + \bool_if:NT \l_regex_success_bool { \l_regex_every_match_tl } + } +\cs_new_protected_nopar:Npn \regex_match_once_aux: + { \str_skip_do:nn { \l_regex_start_step_int } { \regex_match_loop:N } } +\cs_new_protected_nopar:Npn \regex_match_initial_setup: + { + \tl_clear:N \l_regex_look_behind_str + \prg_stepwise_inline:nnnn {1} {1} { \l_regex_max_state_int } + { \tex_dimen:D ##1 \c_minus_one sp \scan_stop: } + \int_set_eq:NN \l_regex_unique_step_int \c_minus_one + \int_set_eq:NN \l_regex_start_step_int \c_minus_one + \int_set_eq:NN \l_regex_current_step_int \c_zero + \int_set_eq:NN \l_regex_success_step_int \c_zero + \bool_set_false:N \l_regex_success_empty_bool + } +\cs_new_protected_nopar:Npn \regex_match_setup: + { + \prop_clear:N \l_regex_current_submatches_prop + \bool_if:NTF \l_regex_success_empty_bool + { \cs_set_eq:NN \regex_last_match_empty:F \regex_last_match_empty_yes:F } + { \cs_set_eq:NN \regex_last_match_empty:F \regex_last_match_empty_no:F } + \int_set_eq:NN \l_regex_start_step_int \l_regex_success_step_int + \int_set_eq:NN \l_regex_current_step_int \l_regex_start_step_int + \int_decr:N \l_regex_current_step_int + \bool_set_false:N \l_regex_success_bool + \int_zero:N \l_regex_max_index_int + \regex_store_state:n {1} + } +\cs_new_protected_nopar:Npn \regex_match_loop_setup: + { + \int_incr:N \l_regex_current_step_int + \int_incr:N \l_regex_unique_step_int + \bool_set_false:N \l_regex_fresh_thread_bool + } +\cs_new_eq:NN \s_regex_step \scan_stop: +\cs_new:Npn \regex_break_step:w #1 \s_regex_step { } +\cs_new_protected_nopar:Npn \regex_match_loop:N #1 + { + \regex_match_loop_setup: + \token_if_eq_meaning:NNTF #1 \q_recursion_tail + { \int_set_eq:NN \l_regex_current_char_int \c_minus_one } + { \int_set:Nn \l_regex_current_char_int {`#1} } + \cs_set_nopar:Npx \regex_tmp:w + { + \int_zero:N \l_regex_max_index_int + \prg_stepwise_function:nnnN + {1} {1} { \l_regex_max_index_int } + \regex_match_one_index:n + } + \regex_tmp:w + \s_regex_step + \if_num:w \l_regex_max_index_int = \c_zero + \exp_after:wN \use_none_delimit_by_q_recursion_stop:w + \fi: + \quark_if_recursion_tail_stop:N #1 + \bool_if:NT \l_regex_look_behind_bool + { \tl_put_left:Nx \l_regex_look_behind_str {#1} } + \regex_match_loop:N + } +\cs_new_nopar:Npn \regex_match_one_index:n #1 + { + \regex_match_one_index_aux:n { \int_value:w \tex_skip:D #1 } + } +\cs_new_protected_nopar:Npn \regex_match_one_index_aux:n #1 + { + \int_set:Nn \l_regex_current_state_int {#1} + \prop_clear:N \l_regex_current_submatches_prop + \regex_state_use_submatches: + } +\cs_new_protected_nopar:Npn \regex_action_start_wildcard:nn #1#2 + { + \bool_set_true:N \l_regex_fresh_thread_bool + \regex_action_free:n {#2} + \bool_set_false:N \l_regex_fresh_thread_bool + \regex_action_cost:n {#1} + } +\cs_new_protected_nopar:Npn \regex_action_cost:n #1 + { \regex_store_state:n {#1} } +\cs_new_protected_nopar:Npn \regex_action_success: + { + \regex_last_match_empty:F + { + \bool_set_true:N \l_regex_success_bool + \bool_set_eq:NN \l_regex_success_empty_bool + \l_regex_fresh_thread_bool + \int_set_eq:NN \l_regex_success_step_int + \l_regex_current_step_int + \prop_set_eq:NN \l_regex_success_submatches_prop + \l_regex_current_submatches_prop + \regex_break_step:w + } + } +\cs_new_protected_nopar:Npn \regex_action_free:n #1 + { + \regex_if_state_free:nT {#1} + { + \use:x + { + \int_set:Nn \l_regex_current_state_int {#1} + \regex_store_submatches:n { \l_regex_current_state_int } + \regex_state_use: + \int_set:Nn \l_regex_current_state_int + { \int_use:N \l_regex_current_state_int } + \tl_set:Nn \exp_not:N \l_regex_current_submatches_prop + { \exp_not:o \l_regex_current_submatches_prop } + } + } + } +\cs_new_protected_nopar:Npn \regex_action_submatch:n #1 + { + \prop_put:Nno \l_regex_current_submatches_prop {#1} + { \int_use:N \l_regex_current_step_int } + \regex_store_submatches:n { \l_regex_current_state_int } + } +\cs_new_protected_nopar:Npn \regex_action_repeat_copy:n { \ERROR } +\cs_new_protected_nopar:Npn \regex_action_repeat_move:n { \ERROR } +\cs_new_protected_nopar:Npn \regex_action_no_repeat_copy:n { \ERROR } +\cs_new_protected_nopar:Npn \regex_action_no_repeat_move:n { \ERROR } +\cs_new_protected_nopar:Npn \regex_extract: + { + \seq_gclear:N \g_regex_submatches_seq + \prg_stepwise_inline:nnnn + {0} {1} { \l_regex_capturing_group_int } + { + \regex_extract_aux:nTF { ##1 } + { + \seq_gput_right:Nx \g_regex_submatches_seq + { + \str_from_to:Nnn \l_regex_query_str + { \l_regex_tmpa_tl } + { \l_regex_tmpb_tl } + } + } + { \seq_gput_right:Nn \g_regex_submatches_seq { } } + } + } +\cs_new_protected_nopar:Npn \regex_extract_aux:nTF #1#2#3 + { + \prop_get:NnNTF \l_regex_success_submatches_prop + { #1 < } \l_regex_tmpa_tl + { + \prop_get:NnNTF \l_regex_success_submatches_prop + { #1 > } \l_regex_tmpb_tl + {#2} + {#3} + } + {#3} + } +\cs_new_protected_nopar:Npn \regex_set:Nn + { \regex_set_aux:NNn \tl_set_eq:NN } +\cs_new_protected_nopar:Npn \regex_gset:Nn + { \regex_set_aux:NNn \tl_gset_eq:NN } +\cs_new_protected:Npn \regex_set_aux:NNn #1#2#3 + { + \group_begin: + \regex_build:n {#3} + \tl_gset:Nx \g_regex_tmpa_tl + { + \exp_not:N \regex_nfa:Nw \exp_not:N #2 + \l_regex_max_state_int + = \int_use:N \l_regex_max_state_int + \l_regex_capturing_group_int + = \int_use:N \l_regex_capturing_group_int + \bool_if:NTF \l_regex_look_behind_bool + { \bool_set_true:N \l_regex_look_behind_bool } + { \bool_set_false:N \l_regex_look_behind_bool } + \prg_stepwise_function:nnnN + {1} {1} {\l_regex_max_state_int} + \regex_set_aux:n + \regex_nfa_end: + } + \group_end: + #1 #2 \g_regex_tmpa_tl + } +\cs_new_nopar:Npn \regex_set_aux:n #1 + { \tex_toks:D #1 { \tex_the:D \tex_toks:D #1 } } +\cs_new:Npn \regex_nfa:Nw #1 #2 \regex_nfa_end: + { \msg_expandable_error:n { Automaton~#1 used~incorrectly. } } +\cs_new_eq:NN \regex_nfa_end: \scan_stop: +\prg_new_protected_conditional:Npnn \regex_check_nfa:N #1 { TF } + { \exp_after:wN \regex_check_nfa_aux:Nw #1 \q_stop } +\cs_new:Npn \regex_check_nfa_aux:Nw #1 #2 \q_stop + { + \if_meaning:w \regex_nfa:Nw #1 + \prg_return_true: + \else: + \msg_error:nnx { regex } { not-nfa } { \token_to_str:N #1 } + \prg_return_false: + \fi: + } +\msg_new:nnn { regex } { not-nfa } + { + I~was~expecting~a~regular~expression~variable.\\ + Instead,~I~got~#1. + } +\cs_new_protected_nopar:Npn \regex_use:N #1 + { \exp_after:wN \use_none:nn #1 } +\cs_new_protected:Npn \regex_user_aux:n #1 + { + \group_begin: + \tl_clear:N \l_regex_every_match_tl + #1 + \group_end: + } +\cs_new_protected_nopar:Npn \regex_return_after_group: + { + \if_bool:N \l_regex_success_bool + \group_insert_after:N \prg_return_true: + \else: + \group_insert_after:N \prg_return_false: + \fi: + } +\cs_new_protected_nopar:Npn \regex_extract_after_group:N #1 + { + \if_bool:N \l_regex_success_bool + \regex_extract: + \group_insert_after:N \seq_set_eq:NN + \group_insert_after:N #1 + \group_insert_after:N \g_regex_submatches_seq + \fi: + } +\cs_new_protected_nopar:Npn \regex_count_after_group:N #1 + { + \group_insert_after:N \int_set_eq:NN + \group_insert_after:N #1 + \group_insert_after:N \g_regex_match_count_int + } +\prg_new_protected_conditional:Npnn \regex_match:nn #1#2 { T , F , TF } + { + \regex_user_aux:n + { + \regex_disable_submatches: + \regex_build:n {#1} + \regex_match:n {#2} + \regex_return_after_group: + } + } +\prg_new_protected_conditional:Npnn \regex_match:Nn #1#2 { T , F , TF } + { + \regex_check_nfa:NTF #1 + { + \regex_user_aux:n + { + \regex_disable_submatches: + \regex_use:N #1 + \regex_match:n {#2} + \regex_return_after_group: + } + } + { \prg_return_false: } + } +\cs_new_protected:Npn \regex_count:nnN #1#2#3 + { + \regex_user_aux:n + { + \regex_disable_submatches: + \int_gzero:N \g_regex_match_count_int + \tl_set:Nn \l_regex_every_match_tl + { + \int_gincr:N \g_regex_match_count_int + \regex_match_once: + } + \regex_build:n {#1} + \regex_match:n {#2} + \regex_count_after_group:N #3 + } + } +\cs_new_protected:Npn \regex_count:NnN #1#2#3 + { + \regex_check_nfa:NTF #1 + { + \regex_user_aux:n + { + \regex_disable_submatches: + \int_gzero:N \g_regex_match_count_int + \tl_set:Nn \l_regex_every_match_tl + { + \int_gincr:N \g_regex_match_count_int + \regex_match_once: + } + \regex_use:N #1 + \regex_match:n {#2} + \regex_count_after_group:N #3 + } + } + { } + } +\cs_new_protected:Npn \regex_extract:nnN #1#2#3 + { + \regex_user_aux:n + { + \regex_build:n {#1} + \regex_match:n {#2} + \regex_extract_after_group:N #3 + } + } +\prg_new_protected_conditional:Npnn \regex_extract:nnN #1#2#3 { T , F , TF } + { + \regex_user_aux:n + { + \regex_build:n {#1} + \regex_match:n {#2} + \regex_extract_after_group:N #3 + \regex_return_after_group: + } + } +\cs_new_protected:Npn \regex_extract:NnN #1#2#3 + { + \regex_check_nfa:NTF #1 + { + \regex_user_aux:n + { + \regex_use:N #1 + \regex_match:n {#2} + \regex_extract_after_group:N #3 + } + } + { } + } +\prg_new_protected_conditional:Npnn \regex_extract:NnN #1#2#3 { T , F , TF } + { + \regex_check_nfa:NTF #1 + { + \regex_user_aux:n + { + \regex_use:N #1 + \regex_match:n {#2} + \regex_extract_after_group:N #3 + \regex_return_after_group: + } + } + { \prg_return_false: } + } +\cs_new_protected:Npn \regex_split:nnN #1#2#3 + { + \regex_user_aux:n + { + \seq_gclear:N \g_regex_split_seq + \tl_set:Nn \l_regex_every_match_tl { \regex_split_aux: } + \regex_build:n {#1} + \regex_match:n {#2} + \regex_split_after_group:N #3 + } + } +\cs_new_protected:Npn \regex_split:NnN #1#2#3 + { + \regex_check_nfa:NTF #1 + { + \regex_user_aux:n + { + \seq_gclear:N \g_regex_split_seq + \tl_set:Nn \l_regex_every_match_tl { \regex_split_aux: } + \regex_use:N #1 + \regex_match:n {#2} + \regex_split_after_group:N #3 + } + } + { } + } +\cs_new_protected_nopar:Npn \regex_split_aux: + { + \int_compare:nNnF \l_regex_start_step_int = \l_regex_success_step_int + { + \regex_extract: + \seq_pop:NN \g_regex_submatches_seq \l_regex_tmpa_tl + \regex_extract_aux:nTF {0} + { + \seq_gput_left:Nx \g_regex_submatches_seq + { + \str_from_to:Nnn \l_regex_query_str + { \int_use:N \l_regex_start_step_int } + { \l_regex_tmpa_tl } + } + } + { \msg_error:nn { regex } { internal } } + \seq_gconcat:NNN \g_regex_split_seq + \g_regex_split_seq \g_regex_submatches_seq + } + \regex_match_once: + } +\cs_new_protected_nopar:Npn \regex_split_after_group:N #1 + { + \int_compare:nNnTF \l_regex_start_step_int = \l_regex_current_step_int + { + \bool_if:NF \l_regex_success_empty_bool + { \seq_gput_right:Nn \g_regex_split_seq { } } + } + { + \seq_gput_right:Nx \g_regex_split_seq + { + \str_from_to:Nnn \l_regex_query_str + { \int_use:N \l_regex_start_step_int } + { \int_use:N \l_regex_current_step_int } + } + } + \group_insert_after:N \seq_set_eq:NN + \group_insert_after:N #1 + \group_insert_after:N \g_regex_split_seq + } +\cs_new_protected:Npn \regex_replacement:n #1 + { + \str_aux_escape:NNNn + \prg_do_nothing: + \regex_replacement_escaped:N + \regex_replacement_raw:N + {#1} + \tl_set_eq:NN \l_regex_replacement_tl \g_str_result_tl + \tl_set:Nx \l_regex_replacement_tl + { \l_regex_replacement_tl \prg_do_nothing: } + } +\cs_new_nopar:Npn \regex_replacement_raw:N { \exp_not:N \prg_do_nothing: } +\cs_new_nopar:Npn \regex_replacement_escaped:N #1 + { + \if_num:w 9 < 1 #1 \exp_stop_f: + \exp_not:N \regex_replacement_escaped_ii:nN {#1} + \else: + #1 + \fi: + } +\cs_new_nopar:Npn \regex_replacement_escaped_ii:nN #1#2 + { + \regex_token_if_other_digit:NTF #2 + { \regex_replacement_escaped_ii:nN {#1#2} } + { \exp_not:N \seq_item:Nn \exp_not:N \g_regex_submatches_seq {#1} #2 } + } +\prg_new_conditional:Npnn \regex_token_if_other_digit:N #1 { TF } + { + \if_num:w 9 < 1 \exp_not:N #1 \exp_stop_f: + \prg_return_true: + \else: + \prg_return_false: + \fi: + } +\cs_new_protected_nopar:Npn \regex_replace_after_group:N #1 + { + \group_insert_after:N \tl_set_eq:NN + \group_insert_after:N #1 + \group_insert_after:N \g_regex_replaced_str + } +\cs_new_protected:Npn \regex_replace_once_aux:Nn #1#2 + { + \group_begin: + \regex_replace_after_group:N #1 + \tl_clear:N \l_regex_every_match_tl + #2 + \exp_args:No \regex_match:n {#1} + \regex_extract: + \regex_extract_aux:nTF {0} + { + \tl_gset:Nx \g_regex_replaced_str + { + \str_from_to:Nnn \l_regex_query_str {0} { \l_regex_tmpa_tl } + \l_regex_replacement_tl + \str_from_to:Nnn \l_regex_query_str + { \l_regex_tmpb_tl } { \c_max_int } + } + } + { \tl_gset_eq:NN \g_regex_replaced_str \l_regex_query_str } + \group_end: + } +\cs_new_protected:Npn \regex_replace_once:nnN #1#2#3 + { + \regex_replace_once_aux:Nn #3 + { + \regex_build:n {#1} + \regex_replacement:n {#2} + } + } +\cs_new_protected:Npn \regex_replace_once:NnN #1#2#3 + { + \regex_replace_once_aux:Nn #3 + { + \regex_use:N #1 + \regex_replacement:n {#2} + } + } +\prg_new_protected_conditional:Npnn \regex_replace_once:nnN #1#2#3 {T,F,TF} + { + \regex_replace_once_aux:Nn #3 + { + \regex_build:n {#1} + \regex_replacement:n {#2} + \regex_return_after_group: + } + } +\prg_new_protected_conditional:Npnn \regex_replace_once:NnN #1#2#3 {T,F,TF} + { + \regex_replace_once_aux:Nn #3 + { + \regex_use:N #1 + \regex_replacement:n {#2} + \regex_return_after_group: + } + } +\cs_new_protected:Npn \regex_replace_all_aux:Nn #1#2 + { + \group_begin: + \regex_replace_after_group:N #1 + \tl_set:Nn \l_regex_every_match_tl { \regex_replace_all_aux: } + \tl_gclear:N \g_regex_replaced_str + #2 + \exp_args:No \regex_match:n {#1} + \tl_gput_right:Nx \g_regex_replaced_str + { + \str_from_to:Nnn \l_regex_query_str + { \int_use:N \l_regex_start_step_int } + { \c_max_int } + } + \group_end: + } +\cs_new_protected_nopar:Npn \regex_replace_all_aux: + { + \regex_extract: + \regex_extract_aux:nTF {0} + { + \tl_gput_right:Nx \g_regex_replaced_str + { + \str_from_to:Nnn \l_regex_query_str + { \int_use:N \l_regex_start_step_int } + { \l_regex_tmpa_tl } + \l_regex_replacement_tl + } + } + { \msg_error:nn { regex } { internal } } + \regex_match_once: + } +\cs_new_protected:Npn \regex_replace_all:nnN #1#2#3 + { + \regex_replace_all_aux:Nn #3 + { + \regex_build:n {#1} + \regex_replacement:n {#2} + } + } +\cs_new_protected:Npn \regex_replace_all:NnN #1#2#3 + { + \regex_replace_all_aux:Nn #3 + { + \regex_use:N #1 + \regex_replacement:n {#2} + } + } +%% +%% +%% End of file `l3regex.sty'. diff --git a/Master/texmf-dist/tex/latex/l3experimental/l3str/l3str.sty b/Master/texmf-dist/tex/latex/l3experimental/l3str/l3str.sty new file mode 100644 index 00000000000..9a69d0da0a6 --- /dev/null +++ b/Master/texmf-dist/tex/latex/l3experimental/l3str/l3str.sty @@ -0,0 +1,970 @@ +%% +%% This is file `l3str.sty', +%% generated with the docstrip utility. +%% +%% The original source files were: +%% +%% l3str.dtx (with options: `package') +%% +%% EXPERIMENTAL CODE +%% +%% Do not distribute this file without also distributing the +%% source files specified above. +%% +%% Do not distribute a modified version of this file. +%% +%% File: l3str.dtx Copyright (C) 2011 The LaTeX3 Project +%% +%% It may be distributed and/or modified under the conditions of the +%% LaTeX Project Public License (LPPL), either version 1.3c of this +%% license or (at your option) any later version. The latest version +%% of this license is in the file +%% +%% http://www.latex-project.org/lppl.txt +%% +%% This file is part of the "l3experimental bundle" (The Work in LPPL) +%% and all files in that bundle must be distributed together. +%% +%% The released version of this bundle is available from CTAN. +%% +%% ----------------------------------------------------------------------- +%% +%% The development version of the bundle can be found at +%% +%% http://www.latex-project.org/svnroot/experimental/trunk/ +%% +%% for those people who are interested. +%% +%%%%%%%%%%% +%% NOTE: %% +%%%%%%%%%%% +%% +%% Snapshots taken from the repository represent work in progress and may +%% not work or may contain conflicting material! We therefore ask +%% people _not_ to put them into distributions, archives, etc. without +%% prior consultation with the LaTeX3 Project. +%% +%% ----------------------------------------------------------------------- +\RequirePackage{expl3} +\GetIdInfo$Id: l3str.dtx 2895 2011-10-09 15:58:19Z joseph $ + {L3 Experimental Strings} +\ProvidesExplPackage + {\ExplFileName}{\ExplFileDate}{\ExplFileVersion}{\ExplFileDescription} +\cs_if_exist:NF \cs_if_exist_use:cF + { + \cs_new:Npn \cs_if_exist_use:cF #1 + { \cs_if_exist:cTF {#1} { \use:c {#1} } } + } +\cs_if_exist:NF \use_i:nnnnnnnn + { \cs_new:Npn \use_i:nnnnnnnn #1#2#3#4#5#6#7#8 {#1} } +\cs_set:Npn \str_tmp:w #1 + { + \cs_new_protected:cpx { str_#1:Nn } ##1##2 + { \exp_not:c { tl_#1:Nx } ##1 { \exp_not:N \tl_to_str:n {##2} } } + \exp_args:Nc \cs_generate_variant:Nn { str_#1:Nn } { Nx , cn , cx } + } +\str_tmp:w {set} +\str_tmp:w {gset} +\str_tmp:w {put_left} +\str_tmp:w {gput_left} +\str_tmp:w {put_right} +\str_tmp:w {gput_right} +\cs_set_protected_nopar:Npn \str_tmp:w { } +\tl_new:N \g_str_tmpa_tl +\tl_new:N \g_str_result_tl +\int_new:N \l_str_char_int +\int_new:N \l_str_bytes_int +\tl_const:Nx \c_backslash_str { \cs_to_str:N \\ } +\tl_const:Nx \c_lbrace_str { \cs_to_str:N \{ } +\tl_const:Nx \c_rbrace_str { \cs_to_str:N \} } +\tl_const:Nx \c_hash_str { \cs_to_str:N \# } +\tl_const:Nx \c_percent_str { \cs_to_str:N \% } +\group_begin: +\char_set_lccode:nn {`\*}{`\ } +\char_set_lccode:nn {`\A}{`\A} +\tl_to_lowercase:n { + \group_end: + \cs_new:Npn \tl_to_other_str:n #1 + { + \exp_after:wN \tl_to_other_str_loop:w \tl_to_str:n {#1} ~ % + A ~ A ~ A ~ A ~ A ~ A ~ A ~ A ~ \q_mark \q_stop + } + \cs_new_nopar:Npn \tl_to_other_str_loop:w + #1 ~ #2 ~ #3 ~ #4 ~ #5 ~ #6 ~ #7 ~ #8 ~ #9 \q_stop + { + \if_meaning:w A #8 + \tl_to_other_str_end:w + \fi: + \tl_to_other_str_loop:w + #9 #1 * #2 * #3 * #4 * #5 * #6 * #7 * #8 * \q_stop + } + \cs_new_nopar:Npn \tl_to_other_str_end:w \fi: #1 \q_mark #2 * A #3 \q_stop + { \fi: #2 } +} +\cs_new_nopar:Npn \tl_to_other_str:N + { \exp_args:No \tl_to_other_str:n } +\cs_new:Npn \str_sanitize_args:Nn #1 #2 + { + \exp_args:Nf #1 + { \tl_to_other_str:n {#2} } + } +\cs_new:Npn \str_sanitize_args:Nnn #1#2#3 + { + \exp_args:Nff #1 + { \tl_to_other_str:n {#2} } + { \tl_to_other_str:n {#3} } + } +\cs_new_nopar:Npn \str_length:N { \exp_args:No \str_length:n } +\cs_new:Npn \str_length:n { \str_sanitize_args:Nn \str_length_unsafe:n } +\cs_new_nopar:Npn \str_length_unsafe:n #1 + { \str_length_aux:n { \str_length_loop:NNNNNNNNN #1 } } +\cs_new:Npn \str_length_ignore_spaces:n #1 + { + \str_length_aux:n + { \exp_after:wN \str_length_loop:NNNNNNNNN \tl_to_str:n {#1} } + } +\cs_new:Npn \str_length_aux:n #1 + { + \int_eval:n + { + #1 + { X \c_eight } { X \c_seven } { X \c_six } + { X \c_five } { X \c_four } { X \c_three } + { X \c_two } { X \c_one } { X \c_zero } + \q_stop + } + } +\cs_set:Npn \str_length_loop:NNNNNNNNN #1#2#3#4#5#6#7#8#9 + { + \if_catcode:w X #9 + \exp_after:wN \use_none_delimit_by_q_stop:w + \fi: + \c_nine + \str_length_loop:NNNNNNNNN + } +\cs_set_nopar:Npn \str_head:N { \exp_args:No \str_head:n } +\cs_set:Npn \str_head:n #1 + { + \exp_after:wN \str_head_aux:w + \tl_to_str:n {#1} + { { } } ~ \q_stop + } +\cs_set_nopar:Npx \str_head_aux:w #1 ~ % + { \exp_not:N \use_i_delimit_by_q_stop:nw #1 { ~ } } +\cs_new:Npn \str_head_ignore_spaces:n #1 + { \exp_after:wN \use_i_delimit_by_q_stop:nw \tl_to_str:n {#1} { } \q_stop } +\cs_new_nopar:Npn \str_head_unsafe:n #1 + { \use_i_delimit_by_q_stop:nw #1 { } \q_stop } +\cs_new_nopar:Npn \str_tail:N { \exp_args:No \str_tail:n } +\cs_set:Npn \str_tail:n #1 + { + \exp_after:wN \str_tail_aux:w + \reverse_if:N \if_charcode:w + \scan_stop: \tl_to_str:n {#1} X X \q_stop + } +\cs_set_nopar:Npn \str_tail_aux:w #1 X #2 \q_stop { \fi: #1 } +\cs_new:Npn \str_tail_ignore_spaces:n #1 + { + \exp_after:wN \str_tail_aux_ii:w + \tl_to_str:n {#1} X { } X \q_stop + } +\cs_new_nopar:Npn \str_tail_unsafe:n #1 + { \str_tail_aux_ii:w #1 X { } X \q_stop } +\cs_set_nopar:Npn \str_tail_aux_ii:w #1 #2 X #3 \q_stop { #2 } +\cs_new:Npn \str_skip_do:nn #1 + { + \int_compare:nNnTF {#1} > \c_seven + { \str_skip_aux:nnnnnnnnn } + { \str_skip_end:n } + {#1} + } +\cs_new:Npn \str_skip_aux:nnnnnnnnn #1#2 #3#4#5#6#7#8#9 + { \exp_args:Nf \str_skip_do:nn { \int_eval:n { #1 - \c_seven } } {#2} } +\cs_new:Npn \str_skip_end:n #1 + { + \if_case:w \int_eval:w #1 \int_eval_end: + \str_skip_end_ii:nwn { XXXXXXX } + \or: \str_skip_end_ii:nwn { XXXXXX } + \or: \str_skip_end_ii:nwn { XXXXX } + \or: \str_skip_end_ii:nwn { XXXX } + \or: \str_skip_end_ii:nwn { XXX } + \or: \str_skip_end_ii:nwn { XX } + \or: \str_skip_end_ii:nwn { X } + \or: \str_skip_end_ii:nwn { } + \else: \str_skip_end_ii:nwn { XXXXXXX } + \fi: + } +\cs_new:Npn \str_skip_end_ii:nwn #1 #2 \fi: #3 + { \fi: \use_i:nnnnnnnn {#3} #1 } +\cs_new:Npn \str_collect_do:nn #1#2 + { \str_collect_aux:n {#1} { \str_collect_end_iii:nwNNNNNNN {#2} } } +\cs_new:Npn \str_collect_aux:n #1 + { + \int_compare:nNnTF {#1} > \c_seven + { \str_collect_aux:nnNNNNNNN } + { \str_collect_end:n } + {#1} + } +\cs_new:Npn \str_collect_aux:nnNNNNNNN #1#2 #3#4#5#6#7#8#9 + { + \exp_args:Nf \str_collect_aux:n + { \int_eval:n { #1 - \c_seven } } + { #2 #3#4#5#6#7#8#9 } + } +\cs_new:Npn \str_collect_end:n #1 + { + \if_case:w \int_eval:w #1 \int_eval_end: + \str_collect_end_ii:nwn { {}{}{}{}{}{}{} } + \or: \str_collect_end_ii:nwn { {}{}{}{}{}{} } + \or: \str_collect_end_ii:nwn { {}{}{}{}{} } + \or: \str_collect_end_ii:nwn { {}{}{}{} } + \or: \str_collect_end_ii:nwn { {}{}{} } + \or: \str_collect_end_ii:nwn { {}{} } + \or: \str_collect_end_ii:nwn { {} } + \or: \str_collect_end_ii:nwn { } + \else: \str_collect_end_ii:nwn { {}{}{}{}{}{}{} } + \fi: + } +\cs_new:Npn \str_collect_end_ii:nwn #1 #2 \fi: #3 + { \fi: #3 \q_stop #1 } +\cs_new:Npn \str_collect_end_iii:nwNNNNNNN #1 #2 \q_stop #3#4#5#6#7#8#9 + { #1 {#2#3#4#5#6#7#8#9} } +\cs_new_nopar:Npn \str_item:Nn { \exp_args:No \str_item:nn } +\cs_new:Npn \str_item:nn #1#2 + { + \exp_args:Nf \tl_to_str:n + { \str_sanitize_args:Nn \str_item_unsafe:nn {#1} {#2} } + } +\cs_new:Npn \str_item_ignore_spaces:nn #1 + { \exp_args:No \str_item_unsafe:nn { \tl_to_str:n {#1} } } +\cs_new_nopar:Npn \str_item_unsafe:nn #1#2 + { + \exp_args:Nff \str_item_aux:nn + { \int_eval:n {#2} } + { \str_length_unsafe:n {#1} } + #1 + \q_stop + } +\cs_new_nopar:Npn \str_item_aux:nn #1#2 + { + \int_compare:nNnTF {#1} < \c_zero + { + \int_compare:nNnTF {#1} < {-#2} + { \use_none_delimit_by_q_stop:w } + { + \str_skip_do:nn { #1 + #2 } + { \use_i_delimit_by_q_stop:nw } + } + } + { + \int_compare:nNnTF {#1} < {#2} + { + \str_skip_do:nn {#1} + { \use_i_delimit_by_q_stop:nw } + } + { \use_none_delimit_by_q_stop:w } + } + } +\cs_new_nopar:Npn \str_from_to:Nnn { \exp_args:No \str_from_to:nnn } +\cs_new:Npn \str_from_to:nnn #1#2#3 + { + \exp_args:Nf \tl_to_str:n + { \str_sanitize_args:Nn \str_from_to_unsafe:nnn {#1}{#2}{#3} } + } +\cs_new:Npn \str_from_to_ignore_spaces:nnn #1 + { \exp_args:No \str_from_to_unsafe:nnn { \tl_to_str:n {#1} } } +\cs_new:Npn \str_from_to_unsafe:nnn #1#2#3 % + { + \str_aux_eval_args:Nnnn \str_from_to_aux:nnnw + { \str_length_unsafe:n {#1} } + {#2} + {#3} + #1 + \q_stop + } +\cs_new:Npn \str_from_to_aux:nnnw #1#2#3 % + { + \exp_args:Nf \str_from_to_aux_ii:nnw + { \str_aux_normalize_range:nn {#2} {#1} } + { \str_aux_normalize_range:nn {#3} {#1} } + } +\cs_new:Npn \str_from_to_aux_ii:nnw #1#2 + { + \str_skip_do:nn {#1} + { + \exp_args:Nf \str_collect_do:nn + { \int_eval:n { #2 - #1 } } + { \use_i_delimit_by_q_stop:nw } + } + } +\cs_new:Npn \str_aux_eval_args:Nnnn #1#2#3#4 + { + \exp_after:wN #1 + \exp_after:wN { \int_value:w \int_eval:w #2 \exp_after:wN } + \exp_after:wN { \int_value:w \int_eval:w #3 \exp_after:wN } + \exp_after:wN { \int_value:w \int_eval:w #4 } + } +\cs_new:Npn \str_aux_normalize_range:nn #1#2 + { + \int_eval:n + { + \if_num:w #1 < \c_zero + \if_num:w #1 < - #2 \exp_stop_f: + \c_zero + \else: + #1 + #2 + \fi: + \else: + \if_num:w #1 < #2 \exp_stop_f: + #1 + \else: + #2 + \fi: + \fi: + } + } +\cs_new_nopar:Npn \str_map_tokens:Nn + { \exp_args:No \str_map_tokens:nn } +\cs_new_nopar:Npn \str_map_tokens:nn + { \str_sanitize_args:Nn \str_map_tokens_aux:nn } +\cs_new:Npn \str_map_tokens_aux:nn #1#2 + { + \str_map_tokens_loop:nN {#2} #1 + { ? \use_none_delimit_by_q_recursion_stop:w } \q_recursion_stop + } +\cs_new_nopar:Npn \str_map_tokens_loop:nN #1#2 + { + \use_none:n #2 + #1 #2 + \str_map_tokens_loop:nN {#1} + } +\cs_new_eq:NN \str_map_break_do:n \use_i_delimit_by_q_recursion_stop:nw +\prg_new_conditional:Npnn \str_if_eq:NN #1#2 { p , TF , T , F } + { + \if_int_compare:w \pdftex_strcmp:D { \tl_to_str:N #1 } { \tl_to_str:N #2 } + = \c_zero \prg_return_true: \else: \prg_return_false: \fi: + } +\prg_new_conditional:Npnn \str_if_contains_char:NN #1#2 { p , T , F , TF } + { + \str_map_tokens:Nn #1 { \str_if_contains_char_aux:NN #2 } + \prg_return_false: + } +\prg_new_conditional:Npnn \str_if_contains_char:nN #1#2 { p , T , F , TF } + { + \str_map_tokens:nn {#1} { \str_if_contains_char_aux:NN #2 } + \prg_return_false: + } +\cs_new_nopar:Npn \str_if_contains_char_aux:NN #1#2 + { + \if_num:w `#1 = `#2 \exp_stop_f: + \str_if_contains_char_end:w + \fi: + } +\cs_new_nopar:Npn \str_if_contains_char_end:w \fi: #1 \prg_return_false: + { \fi: \prg_return_true: } +\prg_new_conditional:Npnn \str_if_bytes:N #1 { p , T , F , TF } + { + \str_map_tokens:Nn #1 { \str_if_bytes_aux:N } + \prg_return_true: + } +\cs_new_nopar:Npn \str_if_bytes_aux:N #1 + { + \int_compare:nNnF {`#1} < \c_two_hundred_fifty_six + { + \use_i_delimit_by_q_recursion_stop:nw + { \prg_return_false: \use_none:n } + } + } +\prg_new_protected_conditional:Npnn \str_aux_hexadecimal_test:N #1 { TF } + { + \tl_if_in:onTF { \tl_to_str:n {abcdef} } {#1} + { + \int_set:Nn \l_str_char_int + { \c_sixteen * \l_str_char_int + `#1 - 87 } + \prg_return_true: + } + { + \if_num:w \c_fifteen < "1 \exp_not:N #1 \exp_stop_f: + \int_set:Nn \l_str_char_int + { \c_sixteen * \l_str_char_int + "#1 } + \prg_return_true: + \else: + \prg_return_false: + \fi: + } + } +\prg_new_protected_conditional:Npnn \str_aux_octal_test:N #1 { TF } + { + \if_num:w \c_seven < '1 \exp_not:N #1 \exp_stop_f: + \int_set:Nn \l_str_char_int + { \c_eight * \l_str_char_int + '#1 } + \prg_return_true: + \else: + \prg_return_false: + \fi: + } +\cs_new_protected_nopar:Npn \str_aux_char_if_octal_digit:NTF #1 + { \tl_if_in:nnTF { 01234567 } {#1} } +\cs_new_protected:Npn \str_input:Nn #1#2 + { + \str_aux_escape:NNNn \token_to_str:N \token_to_str:N \token_to_str:N {#2} + \tl_set_eq:NN #1 \g_str_result_tl + } +\cs_new_protected:Npn \str_ginput:Nn #1#2 + { + \str_aux_escape:NNNn \token_to_str:N \token_to_str:N \token_to_str:N {#2} + \tl_gset_eq:NN #1 \g_str_result_tl + } +\cs_new_eq:NN \str_aux_escape_unescaped:N \use:n +\cs_new_eq:NN \str_aux_escape_escaped:N \use:n +\cs_new_eq:NN \str_aux_escape_raw:N \use:n +\cs_new_protected:Npn \str_aux_escape:NNNn #1#2#3#4 + { + \group_begin: + \cs_set_nopar:Npn \str_aux_escape_unescaped:N { #1 } + \cs_set_nopar:Npn \str_aux_escape_escaped:N { #2 } + \cs_set_nopar:Npn \str_aux_escape_raw:N { #3 } + \int_set:Nn \tex_escapechar:D {92} + \tl_gset:Nx \g_str_result_tl { \tl_to_other_str:n {#4} } + \tl_gset:Nx \g_str_result_tl + { + \exp_after:wN \str_aux_escape_loop:N \g_str_result_tl + \q_recursion_tail \q_recursion_stop + } + \group_end: + } +\cs_new_nopar:Npn \str_aux_escape_loop:N #1 + { + \cs_if_exist_use:cF { str_aux_escape_\token_to_str:N #1:w } + { \str_aux_escape_unescaped:N #1 } + \str_aux_escape_loop:N + } +\cs_new_nopar:cpn { str_aux_escape_ \c_backslash_str :w } + \str_aux_escape_loop:N #1 + { + \cs_if_exist_use:cF { str_aux_escape_/\token_to_str:N #1:w } + { \str_aux_escape_escaped:N #1 } + \str_aux_escape_loop:N + } +\cs_new_eq:cN + { str_aux_escape_ \c_backslash_str q_recursion_tail :w } + \use_none_delimit_by_q_recursion_stop:w +\cs_new_eq:cN + { str_aux_escape_ / \c_backslash_str q_recursion_tail :w } + \use_none_delimit_by_q_recursion_stop:w +\cs_new_nopar:cpn { str_aux_escape_~:w } { } +\cs_new_nopar:cpx { str_aux_escape_/a:w } + { \exp_not:N \str_aux_escape_raw:N \iow_char:N \^^G } +\cs_new_nopar:cpx { str_aux_escape_/t:w } + { \exp_not:N \str_aux_escape_raw:N \iow_char:N \^^I } +\cs_new_nopar:cpx { str_aux_escape_/n:w } + { \exp_not:N \str_aux_escape_raw:N \iow_char:N \^^J } +\cs_new_nopar:cpx { str_aux_escape_/f:w } + { \exp_not:N \str_aux_escape_raw:N \iow_char:N \^^L } +\cs_new_nopar:cpx { str_aux_escape_/r:w } + { \exp_not:N \str_aux_escape_raw:N \iow_char:N \^^M } +\cs_new_nopar:cpx { str_aux_escape_/e:w } + { \exp_not:N \str_aux_escape_raw:N \iow_char:N \^^[ } +\cs_new_nopar:cpn { str_aux_escape_/x:w } \str_aux_escape_loop:N + { + \if_false: { \fi: } + \int_zero:N \l_str_char_int + \str_aux_escape_x_test:N + } +\cs_new_protected_nopar:Npx \str_aux_escape_x_test:N #1 + { + \exp_not:N \token_if_eq_charcode:NNTF \c_space_token #1 + { \exp_not:N \str_aux_escape_x_test:N } + { + \exp_not:N \token_if_eq_charcode:NNTF \c_lbrace_str #1 + { \exp_not:N \str_aux_escape_x_braced_loop:N } + { \exp_not:N \str_aux_escape_x_unbraced_i:N #1 } + } + } +\cs_new_protected_nopar:Npn \str_aux_escape_x_unbraced_i:N #1 + { + \str_aux_hexadecimal_test:NTF #1 + { \str_aux_escape_x_unbraced_ii:N } + { \str_aux_escape_x_end: #1 } + } +\cs_new_protected_nopar:Npn \str_aux_escape_x_unbraced_ii:N #1 + { + \token_if_eq_charcode:NNTF \c_space_token #1 + { \str_aux_escape_x_unbraced_ii:N } + { + \str_aux_hexadecimal_test:NTF #1 + { \str_aux_escape_x_end: } + { \str_aux_escape_x_end: #1 } + } + } +\cs_new_protected_nopar:Npn \str_aux_escape_x_braced_loop:N #1 + { + \token_if_eq_charcode:NNTF \c_space_token #1 + { \str_aux_escape_x_braced_loop:N } + { + \str_aux_hexadecimal_test:NTF #1 + { \str_aux_escape_x_braced_loop:N } + { \str_aux_escape_x_braced_end:N #1 } + } + } +\cs_new_protected_nopar:Npx \str_aux_escape_x_braced_end:N #1 + { + \exp_not:N \token_if_eq_charcode:NNTF \c_rbrace_str #1 + { \exp_not:N \str_aux_escape_x_end: } + { + \msg_error:nn { str } { x-missing-brace } + \exp_not:N \str_aux_escape_x_end: #1 + } + } +\group_begin: + \char_set_catcode_other:N \^^@ + \cs_new_protected_nopar:Npn \str_aux_escape_x_end: + { + \group_begin: + \char_set_lccode:nn { \c_zero } { \l_str_char_int } + \tl_to_lowercase:n + { + \group_end: + \tl_gput_right:Nx \g_str_result_tl + { \if_false: } \fi: + \str_aux_escape_raw:N ^^@ + \str_aux_escape_loop:N + } + } +\group_end: +\msg_new:nnn { str } { x-missing-brace } + { + You~wrote~something~like~ + `\iow_char:N\\x\{ \int_to_hexadecimal:n { \l_str_char_int }'.~ + The~closing~brace~is~missing. + } +\group_begin: + \char_set_catcode_other:n {`\^^@} + \cs_new_protected_nopar:Npn \str_aux_convert_store: + { + \char_set_lccode:nn {\c_zero} {\l_str_char_int} + \tl_to_lowercase:n { \tl_gput_right:Nx \g_str_result_tl {^^@} } + } + \cs_new_protected_nopar:Npn \str_aux_convert_store:NNn #1#2#3 + { + \char_set_lccode:nn {\c_zero} {#3} + \tl_to_lowercase:n { #1 #2 {^^@} } + } +\group_end: +\cs_new_nopar:Npn \str_aux_byte_to_hexadecimal:N #1 + { + \int_compare:nNnTF {`#1} < {256} + { + \int_to_letter:n { \int_div_truncate:nn {`#1} \c_sixteen } + \int_to_letter:n { \int_mod:nn {`#1} \c_sixteen } + } + { \msg_expandable_error:n { Invalid~byte~`#1'. } } + } +\cs_new_nopar:Npn \str_aux_byte_to_octal:N #1 + { + \int_compare:nNnTF {`#1} < {64} + { + 0 + \int_to_letter:n { \int_div_truncate:nn {`#1} \c_eight } + \int_to_letter:n { \int_mod:nn {`#1} \c_eight } + } + { + \int_compare:nNnTF {`#1} < {256} + { \int_to_octal:n {`#1} } + { \msg_expandable_error:n { Invalid~byte~`#1'. } } + } + } +\cs_new_protected_nopar:Npn \str_bytes_escape_hexadecimal:NN #1#2 + { + \str_set:Nx #1 + { \str_map_tokens:Nn #2 \str_aux_byte_to_hexadecimal:N } + } +\tl_const:Nx \c_str_bytes_escape_name_str + { \c_hash_str \c_percent_str \c_lbrace_str \c_rbrace_str () /<>[] } +\cs_new_protected_nopar:Npn \str_bytes_escape_name:NN #1#2 + { + \str_set:Nx #1 + { \str_map_tokens:Nn #2 \str_bytes_escape_name_aux:N } + } +\cs_new_nopar:Npn \str_bytes_escape_name_aux:N #1 + { + \int_compare:nNnTF {`#1} < {"21} + { \c_hash_str \str_aux_byte_to_hexadecimal:N #1 } + { + \int_compare:nNnTF {`#1} > {"7E} + { \c_hash_str \str_aux_byte_to_hexadecimal:N #1 } + { + \str_if_contains_char:NNTF \c_str_bytes_escape_name_str #1 + { \c_hash_str \str_aux_byte_to_hexadecimal:N #1 } + {#1} + } + } + } +\tl_const:Nx \c_str_bytes_escape_string_str { \c_backslash_str ( ) } +\cs_new_protected_nopar:Npn \str_bytes_escape_string:NN #1#2 + { + \str_set:Nx #1 + { \str_map_tokens:Nn #2 \str_bytes_escape_string_aux:N } + } +\cs_new_nopar:Npn \str_bytes_escape_string_aux:N #1 + { + \int_compare:nNnTF {`#1} < {"21} + { \c_backslash_str \str_aux_byte_to_octal:N #1 } + { + \int_compare:nNnTF {`#1} > {"7E} + { \c_backslash_str \str_aux_byte_to_octal:N #1 } + { + \str_if_contains_char:NNT \c_str_bytes_escape_string_str #1 + { \c_backslash_str } + #1 + } + } + } +\cs_new_protected_nopar:Npn \str_bytes_unescape_hexadecimal:NN #1#2 + { + \group_begin: + \tl_gclear:N \g_str_result_tl + \int_zero:N \l_str_char_int + \exp_last_unbraced:Nf \str_bytes_unescape_hexadecimal_aux:N + { \tl_to_other_str:N #2 } 0 + \q_recursion_tail \q_recursion_stop + \group_end: + \tl_set_eq:NN #1 \g_str_result_tl + } +\cs_new_protected_nopar:Npn \str_bytes_unescape_hexadecimal_aux:N #1 + { + \quark_if_recursion_tail_stop:N #1 + \str_aux_hexadecimal_test:NTF #1 + { \str_bytes_unescape_hexadecimal_aux_ii:N } + { \str_bytes_unescape_hexadecimal_aux:N } + } +\cs_new_protected_nopar:Npn \str_bytes_unescape_hexadecimal_aux_ii:N #1 + { + \quark_if_recursion_tail_stop:N #1 + \str_aux_hexadecimal_test:NTF #1 + { + \str_aux_convert_store: + \int_zero:N \l_str_char_int + \str_bytes_unescape_hexadecimal_aux:N + } + { \str_bytes_unescape_hexadecimal_aux_ii:N } + } +\group_begin: + \char_set_lccode:nn {`\*} {`\#} + \tl_to_lowercase:n + { + \group_end: + \cs_new_protected_nopar:Npn \str_bytes_unescape_name:NN #1#2 + { + \group_begin: + \tl_gclear:N \g_str_result_tl + \int_zero:N \l_str_char_int + \exp_last_unbraced:Nf \str_bytes_unescape_name_aux:wN + { \tl_to_other_str:N #2 } + * \q_recursion_tail \q_recursion_stop + \group_end: + \tl_set_eq:NN #1 \g_str_result_tl + } + \cs_new_protected_nopar:Npn \str_bytes_unescape_name_aux:wN #1 * #2 + { + \tl_gput_right:Nx \g_str_result_tl {#1} + \quark_if_recursion_tail_stop:N #2 + \str_aux_hexadecimal_test:NTF #2 + { \str_bytes_unescape_name_aux_ii:NN #2 } + { + \tl_gput_right:Nx \g_str_result_tl { \c_hash_str } + \str_bytes_unescape_name_aux:wN #2 + } + } + } +\cs_new_protected_nopar:Npn \str_bytes_unescape_name_aux_ii:NN #1#2 + { + \str_aux_hexadecimal_test:NTF #2 + { + \str_aux_convert_store: + \int_zero:N \l_str_char_int + \str_bytes_unescape_name_aux:wN + } + { + \tl_gput_right:Nx \g_str_result_tl { \c_hash_str #1 } + \int_zero:N \l_str_char_int + \str_bytes_unescape_name_aux:wN #2 + } + } +\group_begin: + \char_set_lccode:nn {`\*} {`\\} + \char_set_catcode_other:N \^^J + \char_set_catcode_other:N \^^M + \tl_to_lowercase:n + { + \group_end: + \cs_new_protected_nopar:Npn \str_bytes_unescape_string:NN #1#2 + { + \group_begin: + \tl_gclear:N \g_str_result_tl + \exp_last_unbraced:Nf \str_bytes_unescape_string_aux:wN + { \tl_to_other_str:N #2 } + * \q_recursion_tail \q_recursion_stop + \group_end: + \tl_set_eq:NN #1 \g_str_result_tl + } + \cs_new_protected_nopar:Npn \str_bytes_unescape_string_aux:wN #1 * #2 + { + \tl_gput_right:Nx \g_str_result_tl {#1} + \quark_if_recursion_tail_stop:N #2 + \int_zero:N \l_str_char_int + \str_aux_octal_test:NTF #2 + { \str_bytes_unescape_string_aux_d:N } + { + \int_set:Nn \l_str_char_int + { + \prg_case_str:xxn {#2} + { + {n} {10} + {r} {13} + {t} {9} + {b} {8} + {f} {12} + {(} {40} + {)} {41} + {\c_backslash_str} {92} + {^^J} {-1} + {^^M} {-1} + } + {`#2} + } + \int_compare:nNnF \l_str_char_int = \c_minus_one + { \str_aux_convert_store: } + \str_bytes_unescape_string_aux:wN + } + } + } +\cs_new_protected_nopar:Npn \str_bytes_unescape_string_aux_d:N #1 + { + \str_aux_octal_test:NTF #1 + { \str_bytes_unescape_string_aux_dd:N } + { + \str_aux_convert_store: + \str_bytes_unescape_string_aux:wN #1 + } + } +\cs_new_protected_nopar:Npn \str_bytes_unescape_string_aux_dd:N #1 + { + \str_aux_octal_test:NTF #1 + { + \str_aux_convert_store: + \str_bytes_unescape_string_aux:wN + } + { + \str_aux_convert_store: + \str_bytes_unescape_string_aux:wN #1 + } + } +\tl_const:Nx \c_str_bytes_percent_encode_str { \tl_to_str:n { [] } } +\tl_const:Nx \c_str_bytes_percent_encode_not_str { \tl_to_str:n { "-.<> } } +\cs_new_protected_nopar:Npn \str_bytes_percent_encode:NN #1#2 + { + \str_set:Nx #1 + { \str_map_tokens:Nn #2 \str_bytes_percent_encode_aux:N } + } +\cs_new_nopar:Npn \str_bytes_percent_encode_aux:N #1 + { + \int_compare:nNnTF {`#1} < {"41} + { + \str_if_contains_char:NNTF \c_str_bytes_percent_encode_not_str #1 + { #1 } + { \c_percent_str \str_aux_byte_to_hexadecimal:N #1 } + } + { + \int_compare:nNnTF {`#1} > {"7E} + { \c_percent_str \str_aux_byte_to_hexadecimal:N #1 } + { + \str_if_contains_char:NNTF \c_str_bytes_percent_encode_str #1 + { \c_percent_str \str_aux_byte_to_hexadecimal:N #1 } + { #1 } + } + } + } +\group_begin: + \char_set_lccode:nn {`\*} {`\%} + \tl_to_lowercase:n + { + \group_end: + \cs_new_protected_nopar:Npn \str_bytes_percent_decode:NN #1#2 + { + \group_begin: + \tl_gclear:N \g_str_result_tl + \exp_last_unbraced:Nf \str_bytes_percent_decode_aux:wN + { \tl_to_other_str:N #2 } + * \q_recursion_tail \q_recursion_stop + \group_end: + \tl_set_eq:NN #1 \g_str_result_tl + } + \cs_new_protected_nopar:Npn \str_bytes_percent_decode_aux:wN #1 * #2 + { + \tl_gput_right:Nx \g_str_result_tl {#1} + \quark_if_recursion_tail_stop:N #2 + \int_zero:N \l_str_char_int + \str_aux_hexadecimal_test:NTF #2 + { \str_bytes_percent_decode_aux_ii:NN #2 } + { + \tl_gput_right:Nx \g_str_result_tl { \c_percent_str } + \str_bytes_percent_decode_aux:wN #2 + } + } + } +\cs_new_protected_nopar:Npn \str_bytes_percent_decode_aux_ii:NN #1#2 + { + \str_aux_hexadecimal_test:NTF #2 + { + \str_aux_convert_store: + \str_bytes_percent_decode_aux:wN + } + { + \tl_gput_right:Nx \g_str_result_tl {#1} + \str_bytes_percent_decode_aux:wN #2 + } + } +\cs_new_protected_nopar:Npn \str_native_from_UTF_viii:NN #1#2 + { + \group_begin: + \tl_gclear:N \g_str_result_tl + \exp_last_unbraced:Nf \str_native_from_UTF_viii_aux_i:N + { \tl_to_other_str:N #2 } + \q_recursion_tail \q_recursion_stop + \group_end: + \tl_set_eq:NN #1 \g_str_result_tl + } +\cs_new_protected_nopar:Npn \str_native_from_UTF_viii_aux_i:N #1 + { + \quark_if_recursion_tail_stop:N #1 + \int_set:Nn \l_str_char_int { `#1 } + \str_native_from_UTF_viii_aux_ii:nN {128} \c_one + \str_native_from_UTF_viii_aux_ii:nN {64} \c_zero + \str_native_from_UTF_viii_aux_ii:nN {32} \c_two + \str_native_from_UTF_viii_aux_ii:nN {16} \c_three + \str_native_from_UTF_viii_aux_ii:nN {8} \c_four + \msg_error:nnx { str } { utf8-invalid-byte } {#1} + \use_i:nnn \str_native_from_UTF_viii_aux_i:N + \q_stop + \str_native_from_UTF_viii_aux_iii:N + } +\cs_new_protected_nopar:Npn + \str_native_from_UTF_viii_aux_ii:nN #1#2 + { + \int_compare:nNnTF \l_str_char_int < {#1} + { + \int_set_eq:NN \l_str_bytes_int #2 + \use_none_delimit_by_q_stop:w + } + { \int_sub:Nn \l_str_char_int {#1} } + } +\cs_new_protected_nopar:Npn + \str_native_from_UTF_viii_aux_iii:N #1 + { + \int_compare:nNnTF \l_str_bytes_int < \c_two + { \str_native_from_UTF_viii_aux_iv: #1 } + { + \if_meaning:w \q_recursion_tail #1 + \msg_error:nn { str } { utf8-premature-end } + \exp_after:wN \use_none_delimit_by_q_recursion_stop:w + \fi: + \quark_if_recursion_tail_stop:N #1 + \tex_multiply:D \l_str_char_int by 64 \scan_stop: % no interface?? + \int_decr:N \l_str_bytes_int + \int_compare:nNnTF {`#1} < {128} + { \use_ii:nn } + { \int_compare:nNnTF {`#1} < {192} } + { + \int_add:Nn \l_str_char_int { `#1 - 128 } + \str_native_from_UTF_viii_aux_iii:N + } + { + \msg_error:nnx { str } { utf8-missing-byte } {#1} + \str_native_from_UTF_viii_aux_i:N #1 + } + } + } +\cs_new_protected_nopar:Npn + \str_native_from_UTF_viii_aux_iv: + { + \int_compare:nNnTF \l_str_bytes_int = \c_zero + { + \msg_error:nnx { str } { utf8-extra-byte } + { \int_eval:n { \l_str_char_int + 128 } } + } + { + \pdftex_if_engine:TF + { + \int_compare:nNnTF { \l_str_char_int } < { 256 } + { \str_aux_convert_store: } + { + \msg_error:nnx { str } { utf8-pdftex-overflow } + { \int_use:N \l_str_char_int } + } + } + { \str_aux_convert_store: } + } + \str_native_from_UTF_viii_aux_i:N + } +\msg_new:nnn { str } { utf8-invalid-byte } + { + \int_compare:nNnTF {`#1} < {256} + { Byte~number~\int_eval:n {`#1}~invalid~in~utf-8~encoding. } + { The~character~number~\int_eval:n {`#1}~is~not~a~byte. } + } +\msg_new:nnn { str } { utf8-missing-byte } + { The~byte~number~\int_eval:n {`#1}~is~not~a~valid~continuation~byte. } +\msg_new:nnn { str } { utf8-extra-byte } + { The~byte~number~\int_eval:n {`#1}~is~only~valid~as~a~continuation~byte. } +\msg_new:nnnn { str } { utf8-premature-end } + { Incomplete~last~utf8~character. } + { + The~sequence~of~byte~that~I~need~to~convert~to~utf8 \ + ended~before~the~last~character~was~complete.~Perhaps \ + it~was~mistakenly~truncated? + } +\msg_new:nnn { str } { utf8-pdftex-overflow } + { The~character~number~#1~is~too~big~for~pdfTeX. } +\cs_new_protected_nopar:Npn \str_UTF_viii_from_native:NN #1#2 + { + \group_begin: + \tl_gclear:N \g_str_result_tl + \str_map_tokens:Nn #2 \str_UTF_viii_from_native_aux_i:N + \group_end: + \tl_set_eq:NN #1 \g_str_result_tl + } +\cs_new_protected_nopar:Npn \str_UTF_viii_from_native_aux_i:N #1 + { + \int_set:Nn \l_str_char_int {`#1} + \int_compare:nNnTF \l_str_char_int < {128} + { \tl_gput_right:Nx \g_str_result_tl {#1} } + { + \tl_gclear:N \g_str_tmpa_tl + \int_set:Nn \l_str_bytes_int { 64 } + \str_UTF_viii_from_native_aux_ii:n {32} + \str_UTF_viii_from_native_aux_ii:n {16} + \str_UTF_viii_from_native_aux_ii:n {8} + \ERROR % somehow the unicode char was > "1FFFFF > "10FFFF + \tl_gclear:N \g_str_tmpa_tl + \use_none:n \q_stop + \int_add:Nn \l_str_char_int { 2 * \l_str_bytes_int } + \str_aux_convert_store: + \tl_gput_right:Nx \g_str_result_tl { \g_str_tmpa_tl } + } + } +\cs_new_protected_nopar:Npn \str_UTF_viii_from_native_aux_ii:n #1 + { + \str_aux_convert_store:NNn + \tl_gput_left:Nx \g_str_tmpa_tl + { 128 + \int_mod:nn { \l_str_char_int } {64} } + \tex_divide:D \l_str_char_int by 64 \scan_stop: % no interface?? + \int_add:Nn \l_str_bytes_int {#1} + \int_compare:nNnT \l_str_char_int < {#1} + { \use_none_delimit_by_q_stop:w } + } +\cs_gset:Npn \str_length_skip_spaces:N + { \exp_args:No \str_length_skip_spaces:n } +\cs_gset_eq:NN \str_length_skip_spaces:n \str_length_ignore_spaces:n +%% +%% +%% End of file `l3str.sty'. diff --git a/Master/texmf-dist/tex/latex/l3experimental/xcoffins/xcoffins.sty b/Master/texmf-dist/tex/latex/l3experimental/xcoffins/xcoffins.sty index cbadf49e9fb..79490432742 100644 --- a/Master/texmf-dist/tex/latex/l3experimental/xcoffins/xcoffins.sty +++ b/Master/texmf-dist/tex/latex/l3experimental/xcoffins/xcoffins.sty @@ -47,7 +47,7 @@ %% ----------------------------------------------------------------------- %% \RequirePackage{xparse} -\GetIdInfo$Id: xcoffins.dtx 2349 2011-05-11 18:44:37Z joseph $ +\GetIdInfo$Id: xcoffins.dtx 2895 2011-10-09 15:58:19Z joseph $ {L3 Experimental design-level coffins} \ProvidesExplPackage {\ExplFileName}{\ExplFileDate}{\ExplFileVersion}{\ExplFileDescription} diff --git a/Master/texmf-dist/tex/latex/l3experimental/xgalley/l3galley.sty b/Master/texmf-dist/tex/latex/l3experimental/xgalley/l3galley.sty new file mode 100644 index 00000000000..b626f072d30 --- /dev/null +++ b/Master/texmf-dist/tex/latex/l3experimental/xgalley/l3galley.sty @@ -0,0 +1,764 @@ +%% +%% This is file `l3galley.sty', +%% generated with the docstrip utility. +%% +%% The original source files were: +%% +%% l3galley.dtx (with options: `package') +%% +%% EXPERIMENTAL CODE +%% +%% Do not distribute this file without also distributing the +%% source files specified above. +%% +%% Do not distribute a modified version of this file. +%% +%% File: l3galley.dtx Copyright (C) 1999-2001, 2004-2009 Frank Mittelbach +%% (C) 2010,2011 The LaTeX3 Project +%% +%% It may be distributed and/or modified under the conditions of the +%% LaTeX Project Public License (LPPL), either version 1.3c of this +%% license or (at your option) any later version. The latest version +%% of this license is in the file +%% +%% http://www.latex-project.org/lppl.txt +%% +%% This file is part of the "l3experimental bundle" (The Work in LPPL) +%% and all files in that bundle must be distributed together. +%% +%% The released version of this bundle is available from CTAN. +%% +%% ----------------------------------------------------------------------- +%% +%% The development version of the bundle can be found at +%% +%% http://www.latex-project.org/svnroot/experimental/trunk/ +%% +%% for those people who are interested. +%% +%%%%%%%%%%% +%% NOTE: %% +%%%%%%%%%%% +%% +%% Snapshots taken from the repository represent work in progress and may +%% not work or may contain conflicting material! We therefore ask +%% people _not_ to put them into distributions, archives, etc. without +%% prior consultation with the LaTeX Project Team. +%% +%% ----------------------------------------------------------------------- +%% +\RequirePackage{l3names} +\GetIdInfo$Id: l3galley.dtx 2899 2011-10-09 21:22:06Z joseph $ + {L3 Experimental galley code} +\ProvidesExplPackage + {\ExplFileName}{\ExplFileDate}{\ExplFileVersion}{\ExplFileDescription} +\package_check_loaded_expl: +\cs_new_protected_nopar:Npn \galley_leave_vmode: + { \tex_unhbox:D \c_empty_box } +\clist_new:N \l_galley_parshape_left_indent_clist +\clist_new:N \l_galley_parshape_right_indent_clist +\bool_new:N \l_galley_parshape_multipar_bool +\bool_new:N \l_galley_parshape_resume_std_bool +\bool_new:N \l_galley_parshape_fixed_lines_bool +\int_new:N \l_galley_parshape_std_lines_int +\cs_new_eq:NN \l_galley_text_width_dim \linewidth +\cs_new_eq:NN \l_galley_total_left_margin_dim \@totalleftmargin +\dim_new:N \l_galley_total_right_margin_dim +\int_new:N \l_galley_interpar_penalty_int +\skip_new:N \l_galley_interpar_vspace_skip +\cs_new_eq:NN \l_galley_width_dim \tex_hsize:D +\bool_new:N \g_galley_begin_level_bool +\bool_new:N \l_galley_begin_level_bool +\bool_gset_true:N \g_galley_begin_level_bool +\bool_new:N \g_galley_omit_next_indent_bool +\bool_new:N \l_galley_omit_next_indent_bool +\bool_new:N \g_galley_parshape_set_bool +\bool_new:N \l_galley_parshape_set_bool +\bool_new:N \g_galley_nobreak_next_bool +\bool_new:N \l_galley_nobreak_next_bool +\tl_new:N \g_galley_par_begin_hook_tl +\tl_new:N \l_galley_par_begin_hook_tl +\tl_new:N \g_galley_par_end_hook_tl +\tl_new:N \l_galley_par_end_hook_tl +\tl_new:N \g_galley_par_after_hook_tl +\tl_new:N \l_galley_par_after_hook_tl +\int_new:N \g_galley_previous_par_lines_int +\int_new:N \l_galley_previous_par_lines_int +\tl_new:N \g_galley_restore_running_tl +\tl_new:N \l_galley_restore_running_tl +\tl_new:N \g_galley_whatsit_next_tl +\tl_new:N \l_galley_whatsit_next_tl +\tl_new:N \g_galley_whatsit_previous_tl +\tl_new:N \l_galley_whatsit_previous_tl +\tl_new:N \g_galley_interpar_penalty_user_tl +\tl_new:N \l_galley_interpar_penalty_user_tl +\tl_new:N \g_galley_interpar_vspace_user_tl +\tl_new:N \l_galley_interpar_vspace_user_tl +\cs_new_protected_nopar:Npn \galley_initialise_variables: + { + \bool_gset_true:N \g_galley_begin_level_bool + \tl_gclear:N \g_galley_interpar_penalty_user_tl + \tl_gclear:N \g_galley_interpar_vspace_user_tl + \bool_gset_true:N \g_galley_omit_next_indent_bool + \bool_gset_false:N \g_galley_nobreak_next_bool + \tl_gclear:N \g_galley_par_begin_hook_tl + \tl_gclear:N \g_galley_par_end_hook_tl + \tl_gclear:N \g_galley_par_after_hook_tl + \bool_gset_false:N \g_galley_parshape_set_bool + \int_gzero:N \g_galley_previous_par_lines_int + \tl_gclear:N \g_galley_restore_running_tl + \tl_gclear:N \g_galley_whatsits_previous_tl + \tl_gclear:N \g_galley_whatsits_next_tl + } +\cs_new_protected_nopar:Npn \galley_initialise_settings: + { + \dim_set_eq:NN \l_galley_text_width_dim \l_galley_width_dim + \dim_zero:N \l_galley_left_margin_dim + \dim_zero:N \l_galley_right_margin_dim + \dim_zero:N \l_galley_total_left_margin_dim + \dim_zero:N \l_galley_total_right_margin_dim + } +\cs_new_protected_nopar:Npn \galley_save_parameters: + { + \bool_set_eq:NN \l_galley_begin_level_bool + \g_galley_begin_level_bool + \tl_set_eq:NN \l_galley_interpar_penalty_user_tl + \g_galley_interpar_penalty_user_tl + \tl_set_eq:NN \l_galley_interpar_vspace_user_tl + \g_galley_interpar_vspace_user_tl + \bool_set_eq:NN \l_galley_omit_next_indent_bool + \g_galley_omit_next_indent_bool + \bool_set_eq:NN \l_galley_nobreak_next_bool + \g_galley_nobreak_next_bool + \tl_set_eq:NN \l_galley_par_begin_hook_tl + \g_galley_par_begin_hook_tl + \tl_set_eq:NN \l_galley_par_end_hook_tl + \g_galley_par_end_hook_tl + \tl_set_eq:NN \l_galley_par_after_hook_tl + \g_galley_par_after_hook_tl + \bool_set_eq:NN \l_galley_parshape_set_bool + \g_galley_parshape_set_bool + \int_set_eq:NN \l_galley_previous_par_lines_int + \g_galley_previous_par_lines_int + \tl_set_eq:NN \l_galley_restore_running_tl + \g_galley_restore_running_tl + \tl_set_eq:NN \l_galley_whatsits_previous_tl + \g_galley_whatsits_previous_tl + \tl_set_eq:NN \l_galley_whatsits_next_tl + \g_galley_whatsits_next_tl + } +\cs_new_protected_nopar:Npn \galley_restore_parameters: + { + \bool_gset_eq:NN \g_galley_begin_level_bool + \l_galley_begin_level_bool + \tl_gset_eq:NN \g_galley_interpar_penalty_user_tl + \l_galley_interpar_penalty_user_tl + \tl_gset_eq:NN \g_galley_interpar_vspace_user_tl + \l_galley_interpar_vspace_user_tl + \bool_gset_eq:NN \g_galley_omit_next_indent_bool + \l_galley_omit_next_indent_bool + \bool_gset_eq:NN \g_galley_nobreak_next_bool + \l_galley_nobreak_next_bool + \tl_gset_eq:NN \g_galley_par_begin_hook_tl + \l_galley_par_begin_hook_tl + \tl_gset_eq:NN \g_galley_par_end_hook_tl + \l_galley_par_end_hook_tl + \tl_gset_eq:NN \g_galley_par_after_hook_tl + \l_galley_par_after_hook_tl + \bool_gset_eq:NN \g_galley_parshape_set_bool + \l_galley_parshape_set_bool + \int_gset_eq:NN \g_galley_previous_par_lines_int + \l_galley_previous_par_lines_int + \tl_gset_eq:NN \g_galley_restore_running_tl + \l_galley_restore_running_tl + \tl_gset_eq:NN \g_galley_whatsits_previous_tl + \l_galley_whatsits_previous_tl + \tl_gset_eq:NN \g_galley_whatsits_next_tl + \l_galley_whatsits_next_tl + } +\cs_new_protected_nopar:Npn \galley_level: + { + \galley_save_parameters: + \group_begin: + \galley_initialise_variables: + \galley_initialise_settings: + \group_insert_after:N \galley_level_end: + } +\cs_new_protected_nopar:Npn \galley_level_end: + { + \par + \galley_restore_parameters: + \group_end: + } +\cs_new_protected_nopar:Npn \galley_std_par: + { + \mode_if_vertical:TF + { \tex_par:D } + { + \g_galley_par_end_hook_tl + \galley_set_measure_and_parshape: + \group_begin: + \tex_par:D + \group_end: + \int_gadd:Nn \g_galley_previous_par_lines_int \tex_prevgraf:D + } + \g_galley_par_after_hook_tl + \tl_gclear:N \g_galley_par_after_hook_tl + \tex_penalty:D \c_ten_thousand + } +\cs_set_protected_nopar:Npn \par { \galley_std_par: } +\tl_set:Nn \@par { \galley_std_par: } +\cs_new_protected_nopar:Npn \galley_display_begin: + { + \group_begin: + \galley_save_parameters: + \mode_if_vertical:TF + { + \galley_display_penalty:N \l_galley_display_begin_par_penalty_tl + \galley_display_vspace:N \l_galley_display_begin_par_vspace_tl + } + { + \galley_display_penalty:N \l_galley_display_begin_penalty_tl + \galley_display_vspace:N \l_galley_display_begin_vspace_tl + } + \par + } +\cs_new_protected_nopar:Npn \galley_display_penalty:N #1 + { + \tl_if_empty:NF #1 + { + \tl_gput_right:Nx \g_galley_restore_running_tl + { + \int_gset:Nn \exp_not:N \g_galley_penalty_int + { \int_use:N \g_galley_penalty_int } + } + \int_gset:Nn \g_galley_penalty_int {#1} + } + } +\cs_new_protected_nopar:Npn \galley_display_vspace:N #1 + { + \tl_if_empty:NF #1 + { + \tl_gput_right:Nx \g_galley_restore_running_tl + { + \skip_gset:Nn \exp_not:N \g_galley_vspace_skip + { \skip_use:N \g_galley_vspace_skip } + } + \skip_gset:Nn \g_galley_vspace_int {#1} + } + } +\cs_new_protected_nopar:Npn \galley_display_end: + { + \par + \galley_restore_parameters: + \group_end: + \group_insert_after:N \group_insert_after:N + \group_insert_after:N \galley_display_par_setup: + } +\cs_new_protected_nopar:Npn \galley_display_par_setup: + { + \bool_gset_false:N \g_galley_omit_next_indent_bool + \cs_set_eq:NN \par \galley_display_par: + } +\cs_new_protected_nopar:Npn \galley_display_par: + { + \cs_set_eq:NN \par \galley_std_par: + \mode_if_vertical:TF + { + \par + \bool_gset_false:N \g_galley_omit_next_indent_bool + \galley_display_penalty:N \l_galley_display_end_par_penalty_tl + \galley_display_vspace:N \l_galley_display_end_par_vspace_tl + } + { + \galley_set_measure_and_parshape: + \group_begin: + \tex_par:D + \group_end: + \int_gadd:Nn \g_galley_previous_par_lines_int \tex_prevgraf:D + \galley_display_penalty:N \l_galley_display_end_penalty_tl + \galley_display_vspace:N \l_galley_display_end_vspace_tl + } + } +\cs_undefine:N \everypar +\newtoks \everypar +\AtBeginDocument + { + \tex_everypar:D + { + \bool_if:NTF \g_galley_begin_level_bool + { \galley_start_paragraph_first: } + { \galley_start_paragraph_std: } + \tex_the:D \everypar + } + } +\box_new:N \g_galley_last_box +\cs_new_protected_nopar:Npn \galley_start_paragraph_std: + { + \group_begin: + \box_gset_to_last:N \g_galley_last_box + \tex_par:D + \galley_insert_vertical_items: + \tex_everypar:D { } + \tex_noindent:D + \group_end: + \int_gzero:N \g_galley_previous_par_lines_int + \galley_insert_horizontal_items: + \galley_restore_running_parameters: + } +\cs_new_protected_nopar:Npn \galley_start_paragraph_first: + { + \bool_gset_false:N \g_galley_begin_level_bool + \mode_if_horizontal:TF + { + \group_begin: + \box_gset_to_last:N \g_galley_last_box + \tex_par:D + \galley_insert_vspace: + \tex_everypar:D { } + \tex_noindent:D + \group_end: + } + { \galley_insert_vspace: } + \galley_insert_horizontal_items: + \galley_restore_running_parameters: + } +\cs_new_protected_nopar:Npn \galley_insert_vertical_items: + { + \g_galley_whatsit_previous_tl + \tl_gclear:N \g_galley_whatsit_previous_tl + \tl_if_empty:NTF \g_galley_interpar_penalty_user_tl + { + \bool_if:NTF \g_galley_nobreak_next_bool + { \tex_penalty:D \c_ten_thousand } + { \tex_penalty:D \l_galley_interpar_penalty_int } + } + { + \tex_penalty:D + \int_eval:w \g_galley_interpar_penalty_user_tl \int_eval_end: + \tl_gclear:N \g_galley_interpar_penalty_user_tl + } + \bool_gset_false:N \g_galley_nobreak_next_bool + \galley_insert_vspace: + } +\cs_new_protected_nopar:Npn \galley_insert_vspace: + { + \tl_if_empty:NTF \g_galley_interpar_vspace_user_tl + { \skip_vertical:N \l_galley_interpar_vspace_skip } + { + \skip_vertical:n { \g_galley_interpar_vspace_user_tl } + \tl_gclear:N \g_galley_interpar_vspace_user_tl + } + } +\cs_new_protected_nopar:Npn \galley_insert_horizontal_items: + { + \g_galley_whatsit_next_tl + \tl_gclear:N \g_galley_whatsit_next_tl + \bool_if:NT \g_galley_omit_next_indent_bool + { + \box_if_empty:NF \g_galley_last_box + { \hbox_to_wd:nn \l_galley_par_indent_dim { } } + } + \skip_horizontal:N \l_galley_par_begin_skip + \g_galley_par_begin_hook_tl + \bool_gset_true:N \g_galley_omit_next_indent_bool + } +\cs_new_protected_nopar:Npn \galley_restore_running_parameters: + { + \g_galley_restore_running_tl + \tl_gclear:N \g_galley_restore_running_tl + } +\cs_new_protected_nopar:Npn \galley_margins_set_absolute:nn #1#2 + { + \dim_set:Nn \l_galley_total_left_margin_dim {#1} + \dim_set:Nn \l_galley_total_right_margin_dim {#2} + \dim_set:Nn \l_galley_text_width_dim + { + \l_galley_width_dim + - \l_galley_total_left_margin_dim + - \l_galley_total_right_margin_dim + } + } +\cs_new_protected_nopar:Npn \galley_margins_set_relative:nn #1#2 + { + \dim_add:Nn \l_galley_total_left_margin_dim {#1} + \dim_add:Nn \l_galley_total_right_margin_dim {#2} + \dim_set:Nn \l_galley_text_width_dim + { + \l_galley_width_dim + - \l_galley_total_left_margin_dim + - \l_galley_total_right_margin_dim + } + } +\cs_new_protected_nopar:Npn \galley_parshape_fixed_lines:nnn #1#2#3 + { + \bool_gset_true:N \g_galley_parshape_set_bool + \bool_set_true:N \l_galley_parshape_fixed_lines_bool + \int_set:Nn \l_galley_parshape_std_lines_int {#1} + \clist_set:Nn \l_galley_parshape_left_indent_clist {#2} + \clist_set:Nn \l_galley_parshape_right_indent_clist {#3} + \bool_set_true:N \l_galley_parshape_resume_std_bool + } +\cs_new_protected_nopar:Npn \galley_parshape_multi_par:nnnN #1#2#3#4 + { + \bool_gset_true:N \g_galley_parshape_set_bool + \bool_set_true:N \l_galley_parshape_multipar_bool + \bool_set_false:N \l_galley_parshape_fixed_lines_bool + \int_set:Nn \l_galley_parshape_std_lines_int {#1} + \clist_set:Nn \l_galley_parshape_left_indent_clist {#2} + \clist_set:Nn \l_galley_parshape_right_indent_clist {#3} + \bool_set_eq:NN \l_galley_parshape_resume_std_bool #4 + } +\cs_new_protected_nopar:Npn \galley_parshape_single_par:nnnN #1#2#3#4 + { + \bool_gset_true:N \g_galley_parshape_set_bool + \bool_set_false:N \l_galley_parshape_multipar_bool + \bool_set_false:N \l_galley_parshape_fixed_lines_bool + \int_set:Nn \l_galley_parshape_std_lines_int {#1} + \clist_set:Nn \l_galley_parshape_left_indent_clist {#2} + \clist_set:Nn \l_galley_parshape_right_indent_clist {#3} + \bool_set_eq:NN \l_galley_parshape_resume_std_bool #4 + } +\cs_generate_variant:Nn \galley_parshape_fixed_lines:nnn { nVV } +\cs_generate_variant:Nn \galley_parshape_multi_par:nnnN { nVV } +\cs_generate_variant:Nn \galley_parshape_single_par:nnnN { nVV } +\cs_new_protected_nopar:Npn \galley_set_measure_and_parshape: + { + \bool_if:NTF \g_galley_parshape_set_bool + { + \bool_if:NTF \l_galley_parshape_fixed_lines_bool + { + \int_compare:nNnTF \g_galley_previous_par_lines_int > \c_zero + { \galley_generate_parshape_lines: } + { \galley_generate_parshape: } + } + { + \bool_gset_eq:NN \g_galley_parshape_set_bool + \l_galley_parshape_multipar_bool + \galley_generate_parshape: + } + } + { + \tex_global:D \tex_parshape:D + \c_one + \dim_use:N \l_galley_total_left_margin_dim + \c_space_tl + \dim_use:N \l_galley_text_width_dim + } + } +\cs_new_protected_nopar:Npn \galley_generate_parshape: + { + \tex_global:D \tex_parshape:D + \int_eval:w + \l_galley_parshape_std_lines_int + + \int_min:nn + { \clist_length:N \l_galley_parshape_left_indent_clist } + { \clist_length:N \l_galley_parshape_right_indent_clist } + \bool_if:NT \l_galley_parshape_resume_std_bool { + 1 } + \int_eval_end: + \prg_replicate:nn \l_galley_parshape_std_lines_int + { + \dim_use:N \l_galley_total_left_margin_dim + \c_space_tl + \dim_use:N \l_galley_text_width_dim + \c_space_tl + } + \galley_set_parshape_map:oo + \l_galley_parshape_left_indent_clist + \l_galley_parshape_right_indent_clist + \bool_if:NT \l_galley_parshape_resume_std_bool + { + \c_space_tl + \dim_use:N \l_galley_total_left_margin_dim + \c_space_tl + \dim_use:N \l_galley_text_width_dim + } + } +\cs_new_nopar:Npn \galley_set_parshape_map:nn #1#2 + { \galley_set_parshape_map_aux:nw { } #1 , \q_mark #2 , \q_stop } +\cs_generate_variant:Nn \galley_set_parshape_map:nn { oo } +\cs_new_nopar:Npn \galley_set_parshape_map_aux:nw + #1#2 , #3 \q_mark #4 , #5 \q_stop + { + \bool_if:nTF { \tl_if_empty_p:n {#3} || \tl_if_empty_p:n {#4} } + { + #1 + \dim_eval:n { \l_galley_total_left_margin_dim + ( #2 ) } + \c_space_tl + \dim_eval:n { \l_galley_text_width_dim - ( ( #2 ) + ( #4 ) ) } + } + { + \galley_set_parshape_map_aux:nw + { + #1 + \dim_eval:n { \l_galley_total_left_margin_dim + ( #2 ) } + \c_space_tl + \dim_eval:n { \l_galley_text_width_dim - ( ( #2 ) + ( #4 ) ) } + \c_space_tl + } + #3 \q_mark #5 \q_stop + } + } +\cs_new_protected_nopar:Npn \galley_generate_parshape_lines: + { + \int_compare:nNnTF \tex_parshape:D > \g_galley_previous_par_lines_int + { + \tex_global:D \tex_parshape:D + \int_eval:w \tex_parshape:D - \g_galley_previous_par_lines_int + \int_eval_end: + \prg_stepwise_function:nnnN + { \g_galley_previous_par_lines_int + \c_one } + \c_one \tex_parshape:D \galley_generate_parshape_lines_aux:n + } + { + \bool_gset_false:N \g_galley_parshape_set_bool + \tex_global:D \tex_parshape:D + \c_one + \dim_use:N \l_galley_total_left_margin_dim + \c_space_tl + \dim_use:N \l_galley_text_width_dim + } + } +\cs_new_nopar:Npn \galley_generate_parshape_lines_aux:n #1 + { + \etex_parshapeindent:D #1 + ~ + \etex_parshapelength:D #1 + } +\cs_new_protected_nopar:Npn \galley_set_user_penalty:n #1 + { \tl_gset:Nx \g_galley_interpar_penalty_user_tl { \int_eval:n {#1} } } +\cs_new_protected_nopar:Npn \galley_set_user_vspace:n #1 + { \tl_gset:Nx \g_galley_interpar_vspace_user_tl { \skip_eval:n {#1} } } +\dim_set:Nn \parskip \c_zero_dim +\cs_undefine:N \parskip +\skip_new:N \parskip +\cs_new_eq:NN \l_galley_line_left_skip \tex_leftskip:D +\cs_new_eq:NN \l_galley_line_right_skip \tex_rightskip:D +\dim_new:N \l_galley_par_begin_skip +\cs_new_eq:NN \l_galley_par_end_skip \tex_parfillskip:D +\cs_new_eq:NN \l_galley_par_indent_dim \tex_parindent:D +\cs_new_eq:NN \l_galley_last_line_fit_int \etex_lastlinefit:D +\cs_new_protected_nopar:Npn \galley_set_interword_spacing:N #1 + { + \bool_if:NTF #1 + { % TODO Hook for font changes required! + \dim_compare:nNnTF { \tex_fontdimen:D \c_three \tex_font:D } + = \c_zero_dim + { \tex_spaceskip:D \c_zero_dim } + { \tex_spaceskip:D \tex_fontdimen:D \c_two \tex_font:D } + } + { \tex_spaceskip:D \c_zero_dim } + } +\cs_new_eq:NN \l_galley_binop_penalty_int \tex_binoppenalty:D +\cs_new_eq:NN \l_galley_double_hyphen_demerits_int \tex_doublehyphendemerits:D +\cs_new_eq:NN \l_galley_emergency_stretch_skip \tex_emergencystretch:D +\cs_new_eq:NN \l_galley_final_hyphen_demerits_int \tex_finalhyphendemerits:D +\cs_new_eq:NN \l_galley_linebreak_badness_int \tex_hbadness:D +\cs_new_eq:NN \l_galley_linebreak_fuzz_dim \tex_hfuzz:D +\cs_new_eq:NN \l_galley_linebreak_penalty_int \tex_linepenalty:D +\cs_new_eq:NN \l_galley_linebreak_pretolerance_int \tex_pretolerance:D +\cs_new_eq:NN \l_galley_mismatch_demerits_int \tex_adjdemerits:D +\cs_new_eq:NN \l_galley_relation_penalty_int \tex_relpenalty:D +\cs_new_eq:NN \l_galley_linebreak_tolerance_int \tex_tolerance:D +\cs_new_eq:NN \l_galley_broken_penalty_int \tex_brokenpenalty:D +\cs_new_eq:NN \l_galley_interline_penalty_int \tex_interlinepenalty:D +\cs_new_eq:NN \l_galley_parbreak_badness_int \tex_vbadness:D +\cs_new_eq:NN \l_galley_parbreak_fuzz_dim \tex_vfuzz:D +\cs_new_eq:NN \l_galley_post_display_penalty_int \tex_postdisplaypenalty:D +\cs_new_eq:NN \l_galley_pre_display_penalty_int \tex_predisplaypenalty:D +\clist_new:N \l_galley_club_penalties_clist +\clist_new:N \l_galley_line_penalties_clist +\cs_new_protected_nopar:Npn \galley_set_display_widow_penalties:n #1 + { + \etex_displaywidowpenalties:D + \int_eval:w \clist_length:n {#1} + \c_one \int_eval_end: + \clist_map_function:nN {#1} \galley_set_aux:n + \c_zero + } +\cs_generate_variant:Nn \galley_set_display_widow_penalties:n { V , v } +\cs_new_protected_nopar:Npn \galley_set_widow_penalties:n #1 + { + \etex_widowpenalties:D + \int_eval:w \clist_length:n {#1} + \c_one \int_eval_end: + \clist_map_function:nN {#1} \galley_set_aux:n + \c_zero + } +\cs_generate_variant:Nn \galley_set_widow_penalties:n { V , v } +\cs_new_nopar:Npn \galley_set_aux:n #1 { #1 ~ } +\cs_new_protected_nopar:Npn \galley_set_club_penalties:n #1 + { + \clist_set:Nn \l_galley_club_penalties_clist {#1} + \galley_calc_interline_penalties: + } +\cs_generate_variant:Nn \galley_set_club_penalties:n { V , v } +\cs_new_protected_nopar:Npn \galley_set_interline_penalties:n #1 + { + \clist_set:Nn \l_galley_line_penalties_clist {#1} + \galley_calc_interline_penalties: + } +\cs_generate_variant:Nn \galley_set_interline_penalties:n { V , v } +\cs_new_protected_nopar:Npn \galley_set_display_club_penalties:n #1 + { + \etex_clubpenalties:D + \int_eval:w \clist_length:n {#1} + \c_one \int_eval_end: + \clist_map_function:nN {#1} \galley_set_aux:n + \c_zero + \galley_calc_interline_penalties: + } +\cs_generate_variant:Nn \galley_set_display_club_penalties:n { V , v } +\cs_new_protected_nopar:Npn \galley_set_interline_penalty:n #1 + { + \int_compare:nNnTF { \etex_interlinepenalties:D \c_zero } = \c_zero + { + \etex_interlinepenalties:D \c_one \int_eval:w #1 \int_eval_end: + \galley_calc_interline_penalties: + } + { + \cs_set_nopar:Npn \galley_set_interline_penalty_aux_ii:n ##1 + { + \int_eval:w + \etex_interlinepenalties:D ##1 + - \etex_interlinepenalties:D \etex_interlinepenalties:D \c_zero + + #1 + \int_eval_end: + } + \exp_args:Nf \galley_set_interline_penalty_aux:nn + { \clist_length:N \l_galley_line_penalties_clist } {#1} + } + } +\cs_new_protected_nopar:Npn \galley_set_interline_penalty_aux:nn #1#2 + { + \etex_interlinepenalties:D + \etex_interlinepenalties:D \c_zero + \prg_stepwise_function:nnnN \c_one \c_one {#1} + \galley_set_interline_penalty_aux_i:n + \prg_stepwise_function:nnnN { #1 + \c_one } \c_one + { \etex_interlinepenalties:D \c_zero - \c_one } + \galley_set_interline_penalty_aux_ii:n + \int_eval:w #2 \int_eval_end: + } +\cs_new_nopar:Npn \galley_set_interline_penalty_aux_i:n #1 + { \etex_interlinepenalties:D \int_eval:w #1 \int_eval_end: } +\cs_new_nopar:Npn \galley_set_interline_penalty_aux_ii:n #1 { } +\cs_new_protected_nopar:Npn \galley_calc_interline_penalties: + { + \exp_args:Nff \galley_calc_interline_penalties_aux:nn + { + \int_eval:n + { + \int_max:nn + { + \clist_length:N \l_galley_club_penalties_clist + + \c_one + } + { + \int_max:nn + { + \clist_length:N \l_galley_line_penalties_clist + + \c_one + } + { \etex_clubpenalties:D \c_zero } + } + } + } + { \clist_length:N \l_galley_line_penalties_clist } + } +\cs_new_protected_nopar:Npn \galley_calc_interline_penalties_aux:nn + #1#2 + { + \etex_interlinepenalties:D #1 ~ + \prg_stepwise_function:nnnN \c_one \c_one {#2} + \galley_calc_interline_penalties_aux_i:n + \prg_stepwise_function:nnnN { #2 + \c_one } \c_one { #1 - \c_one } + \galley_calc_interline_penalties_aux_ii:n + \etex_interlinepenalties:D \etex_interlinepenalties:D \c_zero + } +\cs_new_nopar:Npn \galley_calc_interline_penalties_aux_i:n + #1 + { + \int_eval:w + \clist_item:Nn \l_galley_line_penalties_clist { #1 - \c_one } + + 0 \clist_item:Nn \l_galley_club_penalties_clist + { #1 - \c_one } + - \etex_clubpenalties:D #1 ~ + \int_eval_end: + } +\cs_new_nopar:Npn \galley_calc_interline_penalties_aux_ii:n + #1 + { + \int_eval:w + \etex_interlinepenalties:D \etex_interlinepenalties:D \c_zero + + 0 \clist_item:Nn \l_galley_club_penalties_clist + { #1 - \c_one } + - \etex_clubpenalties:D #1 ~ + \int_eval_end: + } +\cs_new_protected_nopar:Npn \galley_save_club_penalties:N #1 + { \clist_set_eq:NN #1 \l_galley_club_penalties_clist } +\cs_new_protected_nopar:Npn \galley_save_interline_penalties:N #1 + { \clist_set_eq:NN #1 \l_galley_line_penalties_clist } +\cs_new_protected_nopar:Npn \galley_save_display_club_penalties:N #1 + { + \clist_set:Nx #1 + { + \prg_stepwise_function:nnnN \c_one \c_one + { \etex_clubpenalties:D \c_zero - \c_one } + \galley_save_display_club_penalties:_aux:n + } + } +\cs_new_nopar:Npn \galley_save_display_club_penalties:_aux:n #1 + { \int_use:N \etex_clubpenaltes:D \int_eval:w #1 \int_eval_end: , } +\cs_new_protected_nopar:Npn \galley_save_display_widow_penalties:N #1 + { + \clist_set:Nx #1 + { + \prg_stepwise_function:nnnN \c_one \c_one + { \etex_displaywidowpenalties:D \c_zero - \c_one } + \galley_save_display_widow_penalties:_aux:n + } + } +\cs_new_nopar:Npn \galley_save_display_widow_penalties:_aux:n #1 + { \int_use:N \etex_displaywidowpenalties:D \int_eval:w #1 \int_eval_end: , } +\cs_new_protected_nopar:Npn \galley_save_widow_penalties:N #1 + { + \clist_set:Nx #1 + { + \prg_stepwise_function:nnnN \c_one \c_one + { \etex_widowpenalties:D \c_zero - \c_one } + \galley_save_widow_penalties:_aux:n + } + } +\cs_new_nopar:Npn \galley_save_widow_penalties:_aux:n #1 + { \int_use:N \etex_widowpenalties:D \int_eval:w #1 \int_eval_end: , } +\cs_new_protected_nopar:Npn \galley_interline_penalty: + { \int_use:N \etex_interlinepenalties:D \etex_interlinepenalties:D \c_zero } +\cs_set_protected_nopar:Npn \@afterheading + { + \bool_gset_true:N \g_galley_no_break_next_bool + \if@afterindent + \else + \bool_gset_true:N \galley_omit_next_indent_bool + \fi + } +\cs_set_protected:Npn \@hangfrom #1 + { + \setbox \@tempboxa = \hbox { {#1} } + \galley_parshape_single_par:nnnN + \c_one + { \box_wd:N \@tempboxa } + \c_zero_dim + \c_false_bool + \bool_gset_true:N \g_galley_no_break_next_bool + \bool_gset_true:N \galley_omit_next_indent_bool + \box \@tempboxa + } +\cs_set_protected:Npn \@makefntext #1 + { + \UseTemplate { justification } { std } { indent-width = 1 em } + \bool_gset_true:N \galley_omit_next_indent_bool + \galley_leave_vmode: + \hb@xt@ 1.8em { \hss \@makefnmark } #1 + } +%% +%% +%% End of file `l3galley.sty'. diff --git a/Master/texmf-dist/tex/latex/l3experimental/xgalley/xgalley.sty b/Master/texmf-dist/tex/latex/l3experimental/xgalley/xgalley.sty new file mode 100644 index 00000000000..160fca57631 --- /dev/null +++ b/Master/texmf-dist/tex/latex/l3experimental/xgalley/xgalley.sty @@ -0,0 +1,616 @@ +%% +%% This is file `xgalley.sty', +%% generated with the docstrip utility. +%% +%% The original source files were: +%% +%% xgalley.dtx (with options: `package') +%% +%% EXPERIMENTAL CODE +%% +%% Do not distribute this file without also distributing the +%% source files specified above. +%% +%% Do not distribute a modified version of this file. +%% +%% File: xgalley.dtx Copyright (C) 1999-2001, 2004-2009 Frank Mittelbach +%% (C) 2010,2011 The LaTeX3 Project +%% +%% It may be distributed and/or modified under the conditions of the +%% LaTeX Project Public License (LPPL), either version 1.3c of this +%% license or (at your option) any later version. The latest version +%% of this license is in the file +%% +%% http://www.latex-project.org/lppl.txt +%% +%% This file is part of the "l3experimental bundle" (The Work in LPPL) +%% and all files in that bundle must be distributed together. +%% +%% The released version of this bundle is available from CTAN. +%% +%% ----------------------------------------------------------------------- +%% +%% The development version of the bundle can be found at +%% +%% http://www.latex-project.org/svnroot/experimental/trunk/ +%% +%% for those people who are interested. +%% +%%%%%%%%%%% +%% NOTE: %% +%%%%%%%%%%% +%% +%% Snapshots taken from the repository represent work in progress and may +%% not work or may contain conflicting material! We therefore ask +%% people _not_ to put them into distributions, archives, etc. without +%% prior consultation with the LaTeX Project Team. +%% +%% ----------------------------------------------------------------------- +%% +\RequirePackage{l3names} +\GetIdInfo$Id: xgalley.dtx 2895 2011-10-09 15:58:19Z joseph $ + {L3 Experimental galley} +\ProvidesExplPackage + {\ExplFileName}{\ExplFileDate}{\ExplFileVersion}{\ExplFileDescription} +\RequirePackage{xparse,xtemplate,l3galley} +\clist_new:N \l_galley_tmpa_clist +\clist_new:N \l_galley_tmpb_clist +\DeclareObjectType { measure } { 0 } +\DeclareTemplateInterface { measure } { absolute } { 0 } + { + left-margin : length = 0 pt , + right-margin : length = 0 pt + } +\DeclareTemplateInterface { measure } { relative } { 0 } + { + left-margin : length = 0 pt , + right-margin : length = 0 pt + } +\cs_new_eq:NN \l_galley_left_margin_dim \leftmargin +\cs_new_eq:NN \l_galley_right_margin_dim \rightmargin +\DeclareTemplateCode { measure } { absolute } { 0 } + { + left-margin = \l_galley_left_margin_dim , + right-margin = \l_galley_right_margin_dim + } + { + \AssignTemplateKeys + \galley_margins_set_absolute:nn \l_galley_left_margin_dim + \l_galley_right_margin_dim + } +\DeclareTemplateCode { measure } { relative } { 0 } + { + left-margin = \l_galley_left_margin_dim , + right-margin = \l_galley_right_margin_dim + } + { + \AssignTemplateKeys + \galley_margins_set_relative:nn \l_galley_left_margin_dim + \l_galley_right_margin_dim + } +\DeclareObjectType { parshape } { 0 } +\DeclareTemplateInterface { parshape } { hang } { 0 } + { + indent : length = 0 pt , + on-left-side : boolean = true , + lines : integer = 1 + } +\DeclareTemplateInterface { parshape } { initial } { 0 } + { + indent : length = 0 pt , + on-left-side : boolean = true , + lines : integer = 2 + } +\DeclareTemplateCode { parshape } { hang } { 0 } + { + indent = \l_galley_parshape_indent_dim , + on-left-side = \l_galley_parshape_on_left_bool , + lines = \l_galley_parshape_lines_int + } + { + \AssignTemplateKeys + \bool_if:NTF \l_galley_parshape_on_left_bool + { + \galley_parshape_single_par:nVVN + \l_galley_parshape_lines_int + \l_galley_parshape_indent_dim + \c_zero_dim + \c_false_bool + } + { + \galley_parshape_single_par:nVVN + \l_galley_parshape_lines_int + \c_zero_dim + \l_galley_parshape_indent_dim + \c_false_bool + } + } +\DeclareTemplateCode { parshape } { initial } { 0 } + { + indent = \l_galley_parshape_indent_dim , + on-left-side = \l_galley_parshape_on_left_bool , + lines = \l_galley_parshape_lines_int + } + { + \AssignTemplateKeys + \clist_clear:N \l_galley_tmpa_clist + \clist_clear:N \l_galley_tmpb_clist + \prg_replicate:nn { \l_galley_parshape_lines_int } + { + \clist_put_right:Nn \l_galley_tmpa_clist + { \l_galley_parshape_indent_dim } + \clist_put_right:Nn \l_galley_tmpb_clist + { \c_zero_dim } + } + \bool_if:NTF \l_galley_parshape_on_left_bool + { + \galley_parshape_single_par:nVVN + \c_zero + \l_galley_tmpa_clist + \l_galley_tmpb_clist + \c_true_bool + } + { + \galley_parshape_single_par:nVVN + \c_zero + \l_galley_tmpb_clist + \l_galley_tmpa_clist + \c_true_bool + } + } +\DeclareTemplateInterface { parshape } { std } { 0 } { } +\DeclareTemplateCode { parshape } { std } { 0 } { } { } +\DeclareObjectType { hyphenation } { 0 } +\DeclareTemplateInterface { hyphenation } { std } { 0 } + { + enable : boolean = true , + enable-upper-case : boolean = true , + penalty : choice { low , medium , high } = low + } +\DeclareTemplateCode { hyphenation } { std } { 0 } + { + enable = \l_galley_hyphen_enable_bool , + enable-upper-case = \l_galley_hyphen_uppercase_bool , + penalty = + { + low = + { + \int_set:Nn \tex_hyphenpenalty:D { 51 } + \int_set:Nn \tex_exhyphenpenalty:D { 51 } + } , + medium = + { + \int_set:Nn \tex_hyphenpenalty:D { 151 } + \int_set:Nn \tex_exhyphenpenalty:D { 151 } + } , + high = + { + \int_set:Nn \tex_hyphenpenalty:D { 301 } + \int_set:Nn \tex_exhyphenpenalty:D { 301 } + } , + } + } + { + \AssignTemplateKeys + \int_set:Nn \tex_lefthyphenmin:D + { + \bool_if:NTF \l_galley_hyphen_enable_bool + { \l_galley_hyphen_left_int } { 63 } + } + \int_set:Nn \tex_uchyph:D + { \bool_if:NTF \l_galley_hyphen_uppercase_bool { 1 } { 0 } } + } +\tex_defaulthyphenchar:D 45 \scan_stop: +\tl_new:N \l_galley_justification_other_tl +\DeclareObjectType { justification } { 0 } +\DeclareTemplateInterface { justification } { std } { 0 } + { + end-skip : skip = 0 pt plus 1 fil , + fixed-word-spacing : boolean = false , + indent-width : length , + left-skip : skip = 0 pt , + right-skip : skip = 0 pt , + start-skip : skip = 0 pt , + stretch-last-line : boolean = false + } +\DeclareTemplateInterface { justification } { single } { 0 } + { + end-skip : skip = 0 pt plus 1 fil , + fixed-word-spacing : boolean = false , + indent-width : length , + left-skip : skip = 0 pt , + right-skip : skip = 0 pt , + start-skip : skip = 0 pt , + stretch-last-line : boolean = false + } +\DeclareTemplateCode { justification } { std } { 0 } + { + end-skip = \l_galley_par_end_skip , + fixed-word-spacing = \l_galley_fixed_spacing_bool , + indent-width = \l_galley_par_indent_dim , + left-skip = \l_galley_line_left_skip , + right-skip = \l_galley_line_right_skip , + start-skip = \l_galley_par_begin_skip , + stretch-last-line = \l_galley_par_stretch_last_bool + } + { + \AssignTemplateKeys + \tl_clear:N \l_galley_justification_other_tl + \galley_set_interword_spacing:N \l_galley_fixed_spacing_bool + \bool_if:NTF \l_galley_par_stretch_last_bool + { \int_set_eq:NN \l_galley_last_line_fit_int \c_one_thousand } + { \int_zero:N \l_galley_last_line_fit_int } + \skip_set:Nn \@rightskip { \l_galley_line_right_skip } + } +\DeclareTemplateCode { justification } { single } { 0 } + { + end-skip = \l_galley_par_end_skip , + fixed-word-spacing = \l_galley_fixed_spacing_bool , + indent-width = \l_galley_par_indent_dim , + left-skip = \l_galley_line_left_skip , + right-skip = \l_galley_line_right_skip , + start-skip = \l_galley_par_begin_skip , + stretch-last-line = \l_galley_par_stretch_last_bool + } + { + \tl_put_left:Nx \l_galley_justification_other_tl + { + \skip_set:Nn \exp_not:N \l_galley_par_end_skip + { \skip_use:N \l_galley_par_end_skip } + \bool_if:NTF \l_galley_fixed_spacing_bool + { \bool_set_true:N \exp_not:N \l_galley_fixed_spacing_bool } + { \bool_set_false:N \exp_not:N \l_galley_fixed_spacing_bool } + \galley_set_interword_space:N + \exp_not:N \l_galley_fixed_spacing_bool + \dim_set:Nn \exp_not:N \l_galley_par_indent_dim + { \dim_use:N \l_galley_par_indent_dim } + \skip_set:Nn \l_galley_line_left_skip + { \skip_use:N \l_galley_line_left_skip } + \skip_set:Nn \exp_not:N \l_galley_line_right_skip + { \skip_use:N \l_galley_line_right_skip } + \skip_set:Nn \exp_not:N \l_galley_par_start_skip + { \skip_use:N \l_galley_par_start_skip } + \int_set:Nn \exp_not:N \l_galley_last_line_fit_int + { \int_use:N \l_galley_last_line_fit_int } + \skip_set:Nn \exp_not:N \@rightskip + { \skip_use:N \l_galley_line_right_skip } + } + \tl_gput_right:Nn \g_galley_restore_running_tl + { \l_galley_justification_other_tl } + \AssignTemplateKeys + \galley_set_interword_spacing:N \l_galley_fixed_spacing_bool + \bool_if:NTF \l_galley_par_stretch_last_bool + { \int_set_eq:NN \l_galley_last_line_fit_int \c_one_thousand } + { \int_zero:N \l_galley_last_line_fit_int } + \skip_set:Nn \@rightskip { \l_galley_line_right_skip } + } +\DeclareInstance { justification } { justified } { std } + { indent-width = 15 pt } +\UseInstance { justification } { justified } +\DeclareInstance { justification } { noindent } { std } + { + end-skip = 15 pt plus 1 fil , + indent-width = 0 pt + } +\DeclareInstance { justification } { centered } { std } + { + end-skip = 0 pt , + fixed-word-spacing = true , + indent-width = 0 pt , + left-skip = 0 pt plus 1 em , + right-skip = 0 pt plus 1 em + } +\DeclareInstance { justification } { ragged-left } { std } + { + end-skip = 0 pt , + fixed-word-spacing = true , + indent-width = 0 pt , + left-skip = 0 pt plus 2 em , + right-skip = 0 pt + } +\DeclareInstance { justification } { ragged-right } { std } + { + end-skip = 0 pt plus 1 fil , + fixed-word-spacing = true , + indent-width = 0 pt , + left-skip = 0 pt , + right-skip = 0 pt plus 2 em + } +\DeclareInstance { justification } { centering } { std } + { + end-skip = 0 pt , + fixed-word-spacing = true , + indent-width = 0 pt , + left-skip = 0 pt plus 1 fil , + right-skip = 0 pt plus 1 fil + } +\DeclareTemplateInterface { justification } { compound } { 0 } + { + first-paragraph : instance { justification } , + other-paragraphs : instance { justification } + } +\DeclareTemplateCode { justification } { compound } { 0 } + { + first-paragraph = \galley_justification_first: , + other-paragraphs = \galley_justification_other: + } + { + \AssignTemplateKeys + \galley_justification_first: + \tl_set:Nn \l_galley_justification_other_tl + { \galley_justification_other: } + \tl_gput_right:Nn \g_galley_restore_running_tl + { \l_galley_justification_other_tl } + } +\DeclareObjectType { line-breaking } { 0 } +\DeclareTemplateInterface { line-breaking } { std } { 0 } + { + badness : integer = 1000 , + binop-penalty : integer = 700 , + double-hyphen-demerits : integer = 10 000 , + emergency-stretch : skip = 0 pt , + final-hyphen-demerits : integer = 5000 , + fuzz : length = 0.1 pt , + line-penalty : integer = 10 , + mismatch-demerits : integer = 10 000 , + pretolerance : integer = 100 , + relation-penalty : integer = 500 , + tolerance : integer = 200 + } +\DeclareTemplateCode{ line-breaking } { std } { 0 } + { + badness = \l_galley_linebreak_badness_int , + binop-penalty = \l_galley_binop_penalty_int , + double-hyphen-demerits = \l_galley_double_hyphen_demerits_int , + emergency-stretch = \l_galley_emergency_stretch_skip , + final-hyphen-demerits = \l_galley_final_hyphen_demerits_int , + fuzz = \l_galley_linebreak_fuzz_dim , + line-penalty = \l_galley_linebreak_penalty_int , + mismatch-demerits = \l_galley_mismatch_demerits_int , + pretolerance = \l_galley_linebreak_pretolerance_int , + relation-penalty = \l_galley_relation_penalty_int , + tolerance = \l_galley_linebreak_tolerance_int + } + { \AssignTemplateKeys } +\DeclareInstance { line-breaking } { std } { std } { } +\UseInstance { line-breaking } { std } +\DeclareObjectType { paragraph-breaking } { 0 } +\DeclareTemplateInterface { paragraph-breaking } { std } { 0 } + { + badness : integer = 1000 , + broken-penalty : integer = 100 , + club-penalty : integer = 150 , + display-club-penalty : integer = 150 , + display-widow-penalty : integer = 150 , + fuzz : length = 0.1 pt , + interline-penalty : integer = 0 , + post-display-penalty : integer = 0 , + pre-display-penalty : integer = 10 000 , + widow-penalty : integer = 150 + } +\DeclareTemplateCode { paragraph-breaking } { std } { 0 } + { + badness = \l_galley_parbreak_badness_int , + broken-penalty = \l_galley_broken_penalty_int , + club-penalty = \l_galley_club_penalty_int , + display-club-penalty = \l_galley_display_club_penalty_int , + display-widow-penalty = \l_galley_display_widow_penalty_int , + fuzz = \l_galley_parbreak_fuzz_dim , + interline-penalty = \l_galley_interline_penalty_int , + post-display-penalty = \l_galley_post_display_penalty_int , + pre-display-penalty = \l_galley_pre_display_penalty_int , + widow-penalty = \l_galley_widow_penalty_int + } + { + \AssignTemplateKeys + \galley_set_club_penalties:V \l_galley_club_penalty_int + \galley_set_display_club_penalties:V \l_galley_display_club_penalty_int + \galley_set_display_widow_penalties:V \l_galley_display_widow_penalty_int + \galley_set_interline_penalty:n \l_galley_interline_penalty_int + \galley_set_widow_penalties:V \l_galley_widow_penalty_int + } +\DeclareInstance { paragraph-breaking } { std } { std } { } +\UseInstance { paragraph-breaking } { std } +\DeclareInstance { paragraph-breaking } { nobreak } { std } + { + interline-penalty = 10 000 , + post-display-penalty = 10 000 + } +\DeclareInstance { paragraph-breaking } { nolone } { std } + { + club-penalty = 10 000 , + display-club-penalty = 10 000 , + display-widow-penalty = 10 000 , + widow-penalty = 10 000 + } +\DeclareTemplateInterface { paragraph-breaking } { single } { 0 } + { + badness : tokenlist , + broken-penalty : tokenlist , + club-penalty : tokenlist , + display-club-penalty : tokenlist , + display-widow-penalty : tokenlist , + fuzz : tokenlist , + interline-penalty : tokenlist , + post-display-penalty : tokenlist , + pre-display-penalty : tokenlist , + widow-penalty : tokenlist + } +\DeclareTemplateCode { paragraph-breaking } { single } { 0 } + { + badness = \l_galley_parbreak_badness_tl , + broken-penalty = \l_galley_broken_penalty_tl , + club-penalty = \l_galley_club_penalties_tl , + display-club-penalty = \l_galley_display_club_penalties_tl , + display-widow-penalty = \l_galley_display_widow_penalties_tl , + fuzz = \l_galley_parbreak_fuzz_tl , + interline-penalty = \l_galley_interline_penalty_tl , + post-display-penalty = \l_galley_post_display_penalty_tl , + pre-display-penalty = \l_galley_pre_display_penalty_tl , + widow-penalty = \l_galley_widow_penalties_tl + } + { + \AssignTemplateKeys + \tl_if_empty:NF \l_galley_interline_penalty_tl + { + \tl_gput_right:Nx \g_galley_par_after_hook_tl + { + \int_set:Nn \exp_not:N \l_galley_interline_penalty_int + { \galley_interline_penalty: } + } + \int_set:Nn \l_galley_interline_penalty_int + { \l_galley_interline_penalty_tl } + } + \tl_if_empty:NF \l_galley_parbreak_fuzz_tl + { + \tl_gput_right:Nx \g_galley_par_after_hook_tl + { + \dim_set:Nn \exp_not:N \l_galley_parbreak_fuzz_dim + { \dim_use:N \l_galley_parbreak_fuzz_dim } + } + \dim_set:Nn \l_galley_parbreak_fuzz_dim { \l_galley_parbreak_fuzz_tl } + } + \seq_map_inline:Nn \c_galley_parbreak_single_seq + { + \tl_if_empty:cF { l_galley_ ##1 _tl } + { + \tl_gput_right:Nx \g_galley_par_after_hook_tl + { + \int_set:Nn \exp_not:c { l_galley_ ##1 _int } + { \int_use:c { l_galley_ ##1 _int } } + } + \int_set:cn { l_galley_ ##1 _int } + { \tl_use:c { l_galley_ ##1 _tl } } + } + } + \seq_map_inline:Nn \c_galley_parbreak_multi_seq + { + \tl_if_empty:cF { l_galley_ ##1 _tl } + { + \use:c { galley_save_ ##1 :N } \l_galley_tmpa_clist + \tl_gput_right:Nx \g_galley_par_after_hook_tl + { + \exp_not:c { galley_set_ ##1 :n } + { \exp_not:o \l_galley_tmpa_clist } + } + \use:c { galley_set_ ##1 :v } { l_galley_ ##1 _tl } + } + } + } +\seq_new:N \c_galley_parbreak_multi_seq +\seq_gput_right:Nn \c_galley_parbreak_multi_seq { club_penalties } +\seq_gput_right:Nn \c_galley_parbreak_multi_seq { display_club_penalties } +\seq_gput_right:Nn \c_galley_parbreak_multi_seq { display_widow_penalties } +\seq_gput_right:Nn \c_galley_parbreak_multi_seq { widow_penalties } +\seq_new:N \c_galley_parbreak_single_seq +\seq_gput_right:Nn \c_galley_parbreak_single_seq { parbreak_badness } +\seq_gput_right:Nn \c_galley_parbreak_single_seq { broken_penalty } +\seq_gput_right:Nn \c_galley_parbreak_single_seq { post_display_penalty } +\seq_gput_right:Nn \c_galley_parbreak_single_seq { pre_display_penalty } +\DeclareInstance { paragraph-breaking } { single-std } { single } { } +\DeclareInstance { paragraph-breaking } { single-nobreak } { single } + { + interline-penalty = 10 000 , + post-display-penalty = 10 000 + } +\DeclareInstance { paragraph-breaking } { single-noclub } { single } + { + club-penalty = 10 000 , + display-club-penalty = 10 000 + } +\DeclareInstance { paragraph-breaking } { single-nolone } { single } + { + club-penalty = 10 000 , + display-club-penalty = 10 000 , + display-widow-penalty = 10 000 , + widow-penalty = 10 000 + } +\DeclareInstance { paragraph-breaking } { single-nowidow } { single } + { + display-widow-penalty = 10 000 , + widow-penalty = 10 000 + } +\DeclareObjectType { display-begin } { 0 } +\DeclareObjectType { display-end } { 0 } +\DeclareTemplateInterface { display-begin } { std } { 0 } + { + par-penalty : tokenlist , + par-space : tokenlist , + penalty : tokenlist , + space : tokenlist + } +\DeclareTemplateInterface { display-end } { std } { 0 } + { + par-penalty : tokenlist , + par-space : tokenlist , + penalty : tokenlist , + space : tokenlist + } +\DeclareTemplateCode { display-begin } { std } { 0 } + { + par-penalty = \l_galley_display_begin_par_vpenalty_tl , + par-space = \l_galley_display_begin_par_vspace_tl , + penalty = \l_galley_display_begin_vpenalty_tl , + space = \l_galley_display_begin_vspace_tl + } + { + \AssignTemplateKeys + \tl_if_empty:NF \l_galley_display_begin_par_vpenalty_tl + { + \tl_set:Nx \l_galley_display_begin_par_vpenalty_tl + { \int_eval:n { \l_galley_display_begin_par_vpenalty_tl } } + } + \tl_if_empty:NF \l_galley_display_begin_par_vspace_tl + { + \tl_set:Nx \l_galley_display_begin_par_vspace_tl + { \skip_eval:n { \l_galley_display_begin_par_vspace_tl } } + } + \tl_if_empty:NF \l_galley_display_begin_vpenalty_tl + { + \tl_set:Nx \l_galley_display_begin_vpenalty_tl + { \int_eval:n { \l_galley_display_begin_vpenalty_tl } } + } + \tl_if_empty:NF \l_galley_display_begin_vspace_tl + { + \tl_set:Nx \l_galley_display_begin_vspace_tl + { \skip_eval:n { \l_galley_display_begin_vspace_tl } } + } + } +\DeclareTemplateCode { display-end } { std } { 0 } + { + par-penalty = \l_galley_display_end_par_vpenalty_tl , + par-space = \l_galley_display_end_par_vspace_tl , + penalty = \l_galley_display_end_vpenalty_tl , + space = \l_galley_display_end_vspace_tl + } + { + \AssignTemplateKeys + \tl_if_empty:NF \l_galley_display_end_par_vpenalty_tl + { + \tl_set:Nx \l_galley_display_end_par_vpenalty_tl + { \int_eval:n { \l_galley_display_end_par_vpenalty_tl } } + } + \tl_if_empty:NF \l_galley_display_end_par_vspace_tl + { + \tl_set:Nx \l_galley_display_end_par_vspace_tl + { \skip_eval:n { \l_galley_display_end_par_vspace_tl } } + } + \tl_if_empty:NF \l_galley_display_end_vpenalty_tl + { + \tl_set:Nx \l_galley_display_end_vpenalty_tl + { \int_eval:n { \l_galley_display_end_vpenalty_tl } } + } + \tl_if_empty:NF \l_galley_display_end_vspace_tl + { + \tl_set:Nx \l_galley_display_end_vspace_tl + { \skip_eval:n { \l_galley_display_end_vspace_tl } } + } + } +\RenewDocumentCommand \nobreak { } + { \bool_gset_true:N \g_galley_no_break_next_bool } +\RenewDocumentCommand \noindent { } + { \bool_gset_false:N \g_galley_omit_next_indent_bool } +\RenewDocumentCommand \vspace { s m } { \galley_set_vspace_user:n {#2} } +%% +%% +%% End of file `xgalley.sty'. -- cgit v1.2.3