diff options
Diffstat (limited to 'macros/luatex/latex/piton/piton.dtx')
-rw-r--r-- | macros/luatex/latex/piton/piton.dtx | 7669 |
1 files changed, 4006 insertions, 3663 deletions
diff --git a/macros/luatex/latex/piton/piton.dtx b/macros/luatex/latex/piton/piton.dtx index 523aa38586..9e3e4e246d 100644 --- a/macros/luatex/latex/piton/piton.dtx +++ b/macros/luatex/latex/piton/piton.dtx @@ -1,3663 +1,4006 @@ -% \iffalse -*- coding: utf-8 ; -*- \fi \iffalse meta-comment -% -% Copyright (C) 2023 by F. Pantigny -% ----------------------------------- -% -% This file may be distributed and/or modified under the -% conditions of the LaTeX Project Public License, either version 1.3 -% of this license or (at your option) any later version. -% The latest version of this license is in: -% -% http://www.latex-project.org/lppl.txt -% -% and version 1.3 or later is part of all distributions of LaTeX -% version 2005/12/01 or later. -% -% \fi -% \iffalse -\def\myfileversion{1.1} -\def\myfiledate{2023/01/07} -% -% -%<*batchfile> -\begingroup -\input l3docstrip.tex -\keepsilent -\usedir{tex/latex/cascade} -\preamble - -Copyright (C) 2023 by F. Pantigny ------------------------------------ - -This file may be distributed and/or modified under the -conditions of the LaTeX Project Public License, either version 1.3 -of this license or (at your option) any later version. -The latest version of this license is in: - -http://www.latex-project.org/lppl.txt - -and version 1.3 or later is part of all distributions of LaTeX -version 2005/12/01 or later. - -\endpreamble -\askforoverwritefalse -\endgroup -%</batchfile> -% -%<@@=piton> -%<*driver> -\documentclass{l3doc} -\usepackage{geometry} -\geometry{left=2.8cm,right=2.8cm,top=2.5cm,bottom=2.5cm,papersize={21cm,29.7cm}} -\usepackage{fontspec} -\usepackage[dvipsnames]{xcolor} -\usepackage{caption,tabularx} -\def\emphase{\bgroup\color{RoyalPurple}\let\next=} -\fvset{commandchars=\~\#\@,formatcom=\color{gray}} -\captionsetup{labelfont = bf} -\usepackage{ragged2e} -\usepackage[footnotehyper,escape-inside=$$,math-comments]{piton} % $$ - -\parindent 0pt -\skip\footins = 2\bigskipamount - -\PitonOptions{gobble=2} - -\EnableCrossrefs - -\begin{document} -\DocInput{piton.dtx} -\end{document} -%</driver> - -% \fi -% -% \catcode`\" = 11 -% -% \title{The package \pkg{piton}\thanks{This document corresponds to the -% version~\myfileversion\space of \pkg{piton}, at the date of~\myfiledate.}} -% \author{F. Pantigny \\ \texttt{fpantigny@wanadoo.fr}} -% -% \maketitle -% -% \begin{abstract} -% The package \pkg{piton} provides tools to typeset Python listings with -% syntactic highlighting by using the Lua library LPEG. It requires LuaLaTeX. -% \end{abstract} -% -% -% -% \section{Presentation} -% -% -% The package \pkg{piton} uses the Lua library LPEG\footnote{LPEG is a -% pattern-matching library for Lua, written in C, based on \emph{parsing -% expression grammars}: \url{http://www.inf.puc-rio.br/~roberto/lpeg/}} for -% parsing Python listings and typeset them with syntactic highlighting. Since it -% uses Lua code, it works with |lualatex| only (and won't work with the other -% engines: |latex|, |pdflatex| and |xelatex|). It does not use external program -% and the compilation does not require |--shell-escape|. The compilation is very -% fast since all the parsing is done by the library LPEG, written in C. -% -% \bigskip -% Here is an example of code typeset by \pkg{piton}, with the environment |{Piton}|. -% -% \bigskip -% -% \begin{Piton} -% from math import pi -% -% def arctan(x,n=10): -% """Compute the mathematical value of arctan(x) -% -% n is the number of terms in the sum -% """ -% if x < 0: -% return -arctan(-x) # recursive call -% elif x > 1: -% return pi/2 - arctan(1/x) -% #> (we have used that $\arctan(x)+\arctan(1/x)=\frac{\pi}{2}$ for $x>0$)\footnote{This LaTeX escape has been done by beginning the comment by \ttfamily\#>.} -% else: -% s = 0 -% for k in range(n): -% s += (-1)**k/(2*k+1)*x**(2*k+1) -% return s -% \end{Piton} -% -% -% \bigskip -% -% The package \pkg{piton} is entirely contained in the file -% |piton.sty|. This file may be put in the current directory or in a -% |texmf| tree. However, the best is to install \pkg{piton} with a TeX -% distribution such as MiKTeX, TeX Live or MacTeX. -% -% -% \section{Use of the package} -% -% -% \subsection{Loading the package} -% -% The package \pkg{piton} should be loaded with the classical command -% |\usepackage|: |\usepackage{piton}|. -% -% \smallskip -% Nevertheless, we have two remarks: -% \begin{itemize} -% \item the package \pkg{piton} uses the package \pkg{xcolor} (but \pkg{piton} -% does \emph{not} load \pkg{xcolor}: if \pkg{xcolor} is not loaded before the -% |\begin{document}|, a fatal error will be raised). -% \item the package \pkg{piton} must be used with LuaLaTeX exclusively: if -% another LaTeX engine (|latex|, |pdflatex|, |xelatex|,\dots ) is used, a -% fatal error will be raised. -% \end{itemize} -% -% \subsection{The tools provided to the user} -% -% The package \pkg{piton} provides several tools to typeset Python code: the -% command |\piton|, the environment |{Piton}| and the command |\PitonInputFile|. -% -% \begin{itemize}\setlength{\fboxsep}{1pt} -% \item The command \colorbox{gray!20}{\texttt\textbackslash piton} should be -% used to typeset small pieces of code inside a paragraph. For example: -% -% {\color{gray}\verb|\piton{def square(x): return x*x}|}\qquad -% \piton{def square(x): return x*x} -% -% The syntax and particularities of the command |\piton| are detailed below. -% -% \item The environment \colorbox{gray!20}{\ttfamily\{Piton\}} should be used to -% typeset multi-lines code. Since it takes its argument in a verbatim mode, it -% can't be used within the argument of a LaTeX command. For sake of -% customization, it's possible to define new environments similar to the -% environment |{Piton}| with the command |\NewPitonEnvironment|: -% cf.~\ref{NewPitonEnvironment} p.~\pageref{NewPitonEnvironment}. -% -% \item The command \colorbox{gray!20}{\ttfamily\textbackslash PitonInputFile} -% is used to insert and typeset a whole external file. -% -% That command takes in as optional argument (between square brackets) two keys -% |first-line| and |last-line|: only the part between the corresponding lines -% will be inserted. -% \end{itemize} -% -% \subsection{The syntax of the command \textbackslash piton} -% -% In fact, the command |\piton| is provided with a double syntax. It may be used -% as a standard command of LaTeX taking its argument between curly braces -% (|\piton{...}|) but it may also be used with a syntax similar to the syntax of -% the command -% |\verb|, that is to say with the argument delimited by two identical characters (e.g.: \verb!\piton|...|!). -% -% \begin{itemize} -% \item {\color{blue} \textsf{Syntax} \verb|\piton{...}|}\par\nobreak -% When its argument is given between curly braces, the command |\piton| does not -% take its argument in verbatim mode. In particular: -% \begin{itemize} -% \item several consecutive spaces will be replaced by only one space; -% \item it's not possible to use |%| inside the argument; -% \item the braces must be appear by pairs correctly nested; -% \item the LaTeX commands (those beginning with a backslash |\| but also the -% active characters) are fully expanded (but not executed). -% \end{itemize} -% -% An escaping mechanism is provided: the commands |\\|, |\%|, |\{| and |\}| -% insert the corresponding characters |\|, |%|, |{| and |}|. The last two -% commands are necessary only if one need to insert braces which are not -% balanced. -% -% The other characters (including |#|, |^|, |_|, |&|, |$| and |@|) -% must be inserted without backslash. -% -% Examples: -% -%\begin{tabular}{>{\color{gray}}l@{\hspace*{1cm}}l} -% \verb|\piton{MyString = '\\n'}| & -% \piton{MyString = '\\n'} \\ -% \verb|\piton{def even(n): return n\%2==0}| & -% \piton{def even(n): return n\%2==0} \\ -% \verb|\piton{c="#" # an affectation }| & -% \piton{c="#" # an affectation } \\ -% \verb|\piton{MyDict = {'a': 3, 'b': 4 }}| & -% \piton{MyDict = {'a': 3, 'b': 4 }} -% \end{tabular} -% -% -% It's possible to use the command |\piton| in the arguments of a -% LaTeX command.\footnote{For example, it's possible to use the command -% \texttt{\textbackslash piton} in a footnote. Example : -% \piton{s = 'A string'}.} -% -% \bigskip -% \item {\color{blue} \textsf{Syntaxe} \verb!\piton|...|!}\par\nobreak -% -% When the argument of the command |\piton| is provided between two identical -% characters, that argument is taken in a \emph{verbatim mode}. Therefore, with -% that syntax, the command |\piton| can't be used within the argument of another -% command. -% -% \medskip -% Examples: -% -% \begin{tabular}{>{\color{gray}}l@{\hspace*{1cm}}l} -% \verb!\piton|MyString = '\n'|! & -% \piton|MyString = '\n'| \\ -% \verb|\piton!def even(n): return n%2==0!| & -% \piton!def even(n): return n%2==0! \\ -% \verb|\piton+c="#" # an affectation +| & -% \piton+c="#" # an affectation + \\ -% \verb|\piton?MyDict = {'a': 3, 'b': 4}?| & -% \piton!MyDict = {'a': 3, 'b': 4}! -% \end{tabular} -% -% \end{itemize} -% -% \section{Customization} -% -% \subsection{The command \textbackslash PitonOptions} -% -% The command |\PitonOptions| takes in as argument a comma-separated list of -% \textsl{key=value} pairs. The scope of the settings done by that command is -% the current TeX group.\footnote{We remind that a LaTeX environment is, in -% particular, a TeX group.} -% -% \begin{itemize} -% \item The key |gobble| takes in as value a positive integer~$n$: the first $n$ -% characters are discarded (before the process of highlightning of the code) for -% each line of the environment |{Piton}|. These characters are not necessarily -% spaces. -% -% \item When the key |auto-gobble| is in force, the extension \pkg{piton} computes -% the minimal value $n$ of the number of consecutive spaces beginning each (non -% empty) line of the environment |{Piton}| and applies |gobble| with that value -% of~$n$. -% -% \item When the key |env-gobble| is in force, \pkg{piton} analyzes the last -% line of the environment |{Piton}|, that is to say the line which contains -% |\end{Piton}| and determines whether that line contains only spaces followed -% by the |\end{Piton}|. If we are in that situation, \pkg{piton} computes the -% number~$n$ of spaces on that line and applies |gobble| with that value of~$n$. -% The name of that key comes from \emph{environment gobble}: the effect of -% gobble is set by the position of the commands |\begin{Piton}| and -% |\end{Piton}| which delimit the current environment. -% -% \item With the key |line-numbers|, the \emph{non empty} lines (and all the -% lines of the \emph{docstrings}, even the empty ones) are numbered in the -% environments |{Piton}| and in the listings resulting from the use of -% |\PitonInputFile|. -% -% \item With the key |all-line-numbers|, \emph{all} the lines are numbered, -% including the empty ones. -% -% \item With the key |resume| the counter of lines is not set to zero at the -% beginning of each environment |{Piton}| or use of |\PitonInputFile| as it is -% otherwise. That allows a numbering of the lines across several -% environments. -% -% \item The key |left-margin| corresponds to a margin on the left. That key may -% be useful in conjonction with the key |line-numbers| or the key -% |line-all-numbers| if one does not want the numbers in an overlapping position -% on the left. -% -% It's possible to use the key |left-margin| with the value |auto|. With that -% value, if the key |line-numbers| or the key |all-line-numbers| is used, a -% margin will be automatically inserted to fit the numbers of lines. See an -% example part \ref{example-numbering} on page~\pageref{example-numbering}. -% -% \item The key |background-color| sets the background color of the environments -% |{Piton}| and the listings produced by |\PitonInputFile| (that background has -% a width of |\linewidth|). -% -% \item When the key |show-spaces| is activated, the spaces in the short strings -% (that is to say those delimited by |'| or |"|) are replaced by the character~␣ -% (U+2423 : \textsc{open box}). Of course, that character~U+2423 must be present -% in the monospaced font which is used.\footnote{The package \pkg{piton} simply -% uses the current monospaced font. The best way to change that font is to use -% the command \texttt{\textbackslash setmonofont} of -% \pkg{fontspec}.}\par\nobreak \begingroup \PitonOptions{show-spaces} Example : -% \piton|my_string = 'Very good answer'| \endgroup -% \end{itemize} -% -% \bigskip -% -% \begingroup -% \fvset{commandchars=\~\&\@,formatcom=\small\color{gray}} -% \begin{Verbatim} -% ~emphase&\PitonOptions{line-numbers,auto-gobble,background-color = gray!15}@ -% \begin{Piton} -% from math import pi -% def arctan(x,n=10): -% """Compute the mathematical value of arctan(x) -% -% n is the number of terms in the sum -% """ -% if x < 0: -% return -arctan(-x) # recursive call -% elif x > 1: -% return pi/2 - arctan(1/x) -% #> (we have used that $\arctan(x)+\arctan(1/x)=\frac{\pi}{2}$ pour $x>0$) -% else -% s = 0 -% for k in range(n): -% s += (-1)**k/(2*k+1)*x**(2*k+1) -% return s -% \end{Piton} -% \end{Verbatim} -% \endgroup -% -% \begingroup -% \PitonOptions{line-numbers,background-color = gray!15} -% \begin{Piton} -% from math import pi -% -% def arctan(x,n=10): -% """Compute the mathematical value of arctan(x) -% -% n is the number of terms in the sum -% """ -% if x < 0: -% return -arctan(-x) # recursive call -% elif x > 1: -% return pi/2 - arctan(1/x) -% #> (we have used that $\arctan(x)+\arctan(1/x)=\frac{\pi}{2}$ for $x>0$) -% else -% s = 0 -% for k in range(n): -% s += (-1)**k/(2*k+1)*x**(2*k+1) -% return s -% \end{Piton} -% \endgroup -% -% \bigskip -% The command |\PitonOptions| provides in fact several other keys which will be -% described further (see in particular the ``Pages breaks and line breaks'' -% p.~\pageref{breakable}). -% -% \subsection{The styles} -% -% \label{styles} -% -% The package \pkg{piton} provides the command |\SetPitonStyle| to customize the -% different styles used to format the syntactic elements of the Python listings. -% The customizations done by that command are limited to the current TeX -% group.\footnote{We remind that an LaTeX environment is, in particular, a TeX group.} -% -% \bigskip -% The command |\SetPitonStyle| takes in as argument a comma-separated list of -% \textsl{key=value} pairs. The keys are names of styles and the value are LaTeX -% formatting instructions. -% -% \bigskip -% These LaTeX instructions must be formatting instructions such as -% |\color{...}|, |\bfseries|, |\slshape|, etc. (the commands of this kind are -% sometimes called \emph{semi-global} commands). It's also possible to put, -% \emph{at the end of the list of instructions}, a LaTeX command taking exactly -% one argument. -% -% \bigskip -% Here an example which changes the style used to highlight, in the definition -% of a Python function, the name of the function which is defined. -% -% \begin{verbatim} -% \SetPitonStyle -% { Name.Function = \bfseries \setlength{\fboxsep}{1pt}\colorbox{yellow!50} } -% \end{verbatim} -% -% In that example, |\colorbox{yellow!50}| must be considered as the name of a -% LaTeX command which takes in exactly one argument, since, usually, it is used -% with the syntax |\colorbox{yellow!50}{...}|. -% -% \medskip -% \begingroup -% \SetPitonStyle -% { Name.Function = \bfseries \setlength{\fboxsep}{1pt}\colorbox{yellow!50} } -% With that setting, we will have : \piton{def cube(x) : return x * x * x } -% \endgroup -% -% \bigskip -% The different styles are described in the table \ref{Semantic}. The initial -% settings done by \pkg{piton} in |piton.sty| are inspired by the style -% \pkg{manni} de Pygments.\footnote{See: \url{https://pygments.org/styles/}. -% Remark that, by default, Pygments provides for its style \pkg{manni} a colored -% background whose color is the HTML color \texttt{\#F0F3F3}.} -% -% -% -% \begin{table}[b] -% \centering -% \caption{Usage of the different styles} -% \label{Semantic} -% \begin{tabular}{@{}>{\ttfamily}l>{\RaggedRight\arraybackslash}p{9cm}@{}} -% \toprule -% \normalfont Style & Usage \\ -% \midrule -% Number & the numbers \\ -% String.Short & the short strings (between \texttt{'} or \verb|"|) \\ -% String.Long & the long strings (between \texttt{'''} or \verb|"""|) except the -% documentation strings \\ -% String & that keys sets both |String.Short| and |String.Long| \\ -% String.Doc & the documentation strings (only between \texttt{"""} following -% PEP~257) \\ -% String.Interpol & the syntactic elements of the fields of the f-strings (that -% is to say the characters \texttt{\{} and \texttt{\}}) \\ -% Operator & the following operators : \texttt{!= == << >> - \~{} + / * \% = < > \& .} \verb+|+ \verb|@|\\ -% Operator.Word & the following operators : \texttt{in}, \texttt{is}, -% \texttt{and}, \texttt{or} and \texttt{not} \\ -% Name.Builtin & the predefined functions of Python \\ -% Name.Function & the name of the functions defined by the user, at the point of -% their definition (that is to say after the keyword |def|) \\ -% Name.Decorator & the decorators (instructions beginning by \verb|@|) \\ -% Name.Namespace & the name of the modules (= external libraries) \\ -% Name.Class & the name of the classes at the point of their definition (that is -% to say after the keyword |class|) \\ -% Exception & the names of the exceptions (eg: \texttt{SyntaxError}) \\ -% Comment & the comments beginning with \verb|#| \\ -% Comment.LaTeX & the comments beginning by \verb|#>|, which are composed in LaTeX by -% \pkg{piton} (and simply called ``LaTeX comments'' in this document) \\ -% Keyword.Constant & |True|, |False| and |None| \\ -% Keyword & the following keywords : -% \ttfamily as, assert, break, case, continue, def, del, -% elif, else, except, exec, finally, for, from, -% global, if, import, lambda, non local, -% pass, raise, return, try, while, -% with, yield, yield from.\\ -% \bottomrule -% \end{tabular} -% \end{table} -% -% -% -% -% \subsection{Creation of new environments} -% -% \label{NewPitonEnvironment} -% -% Since the environment |{Piton}| has to catch its body in a special way (more -% or less as verbatim text), it's not possible to construct new environments -% directly over the environment |{Piton}| with the classical commands -% |\newenvironment| or |\NewDocumentEnvironment|. -% -% That's why \pkg{piton} provides a command |\NewPitonEnvironment|. That -% command takes in three mandatory arguments. - -% That command has the same syntax as the classical environment -% |\NewDocumentEnvironment|. -% -% \bigskip -% With the following instruction, a new environment |{Python}| will be -% constructed with the same behaviour as |{Piton}|: -% -% {\color{gray}\verb|\NewPitonEnvironment{Python}{}{}{}|} -% -% \bigskip -% If one wishes an environment |{Python}| with takes in as optional argument -% (between square brackets) the keys of the command |\PitonOptions|, it's -% possible to program as follows: -% -% {\color{gray}\verb|\NewPitonEnvironment{Python}{O{}}{\PitonOptions{#1}}{}|} -% -% \bigskip -% If one wishes to format Python code in a box of \pkg{tcolorbox}, it's possible -% to define an environment |{Python}| with the following code: -% -%\begin{verbatim} -% \NewPitonEnvironment{Python}{} -% {\begin{tcolorbox}} -% {\end{tcolorbox}} -% \end{verbatim} -% -% -% \section{Advanced features} -% -% \subsection{Mechanisms to escape to LaTeX} -% -% The package \pkg{piton} provides several mechanisms for escaping to LaTeX: -% \begin{itemize} -% \item It's possible to compose comments entirely in LaTeX. -% \item It's possible to have the elements between \texttt{\$} in the comments -% composed in LateX mathematical mode. -% \item It's also possible to insert LaTeX code almost everywhere in a Python listing. -% \end{itemize} -% -% \subsubsection{The ``LaTeX comments''} -% -% In this document, we call ``LaTeX comments'' the comments which begins by -% |#>|. The code following those characters, until the end of the line, will be -% composed as standard LaTeX code. There is two tools to customize those -% comments. -% -% \begin{itemize} -% \item It's possible to change the syntatic mark (which, by default, is |#>|). -% For this purpose, there is a key |comment-latex| available at load-time (that -% is to say at the |\usepackage|) which allows to choice the characters which, -% preceded by |#|, will be the syntatic marker. -% -% For example, with the following loading: -% -% \quad \verb|\usepackage[comment-latex = LaTeX]{piton}| -% -% the LaTeX comments will begin by |#LaTeX|. -% -% If the key |comment-latex| is used with the empty value, all the Python -% comments (which begins by |#|) will, in fact, be ``LaTeX comments''. -% -% \smallskip -% \item It's possible to change the formatting of the LaTeX comment itself by -% changing the \pkg{piton} style |Comment.LaTeX|. -% -% For example, with |\SetPitonStyle{Comment.LaTeX = \normalfont\color{blue}}|, -% the LaTeX comments will be composed in blue. -% -% If you want to have a character |#| at the beginning of the LaTeX comment in -% the \textsc{pdf}, you can use set |Comment.LaTeX| as follows: -% -% \begin{Verbatim} -% \SetPitonStyle{Comment.LaTeX = \color{gray}\~#\normalfont\space } -% \end{Verbatim} -% -% For other examples of customization of the LaTeX comments, see the part -% \ref{example-comments} p.~\pageref{example-comments} -% \end{itemize} -% -% \subsubsection{The key ``math-comments''} -% -% It's possible to request that, in the standard Python comments (that is to say -% those beginning by |#| and not |#>|), the elements between \texttt{\$} be -% composed in LaTeX mathematical mode (the other elements of the comment being -% composed verbatim). -% -% That feature is activated by the key |math-comments| at load-time (that is to -% say with the |\usepackage|). -% -% \medskip -% In the following example, we assume that the key |math-comments| has been used -% when loading \pkg{piton}. -% -% \begin{Verbatim} -% \begin{Piton} -% def square(x): -% return x*x ~# compute $x^2$ -% \end{Piton} -% \end{Verbatim} -% -% \begin{Piton} -% def square(x): -% return x*x # compute $x^2$ -% \end{Piton} -% -% \subsubsection{The mechanism ``escape-inside''} -% -% It's also possible to overwrite the Python listings to insert LaTeX code -% almost everywhere (but between lexical units, of course). By default, -% \pkg{piton} does not fix any character for that kind of escape. -% -% In order to use this mechanism, it's necessary to specify two characters which -% will delimit the escape (one for the beginning and one for the end) by using -% the key |escape-inside| at load-time (that is to say a the -% |\begin{docuemnt}|). -% -% \medskip -% In the following example, we assume that the extension \pkg{piton} has been -% loaded by the following instruction. -% -% \begin{Verbatim} -% \usepackage[~emphase#escape-inside=$$@]{piton} -% \end{Verbatim} -% -% \medskip -% In the following code, which is a recursive programmation of the mathematical -% factorial, we decide to highlight in yellow the instruction which contains the -% recursive call. -% \begin{Verbatim} -% \begin{Piton} -% def fact(n): -% if n==0: -% return 1 -% else: -% ~emphase#$\colorbox{yellow!50}{$@return n*fact(n-1)~emphase#$}$@ -% \end{Piton} -% \end{Verbatim} -% -% \begin{Piton} -% def fact(n): -% if n==0: -% return 1 -% else: -% $\colorbox{yellow!50}{$return n*fact(n-1)$}$ -% \end{Piton} -% -% \bigskip -% -% \emph{Caution} : The escape to LaTeX allowed by the characters of -% |escape-inside| is not active in the strings nor in the Python comments -% (however, it's possible to have a whole Python comment composed in LaTeX by -% beginning it with |#>|; such comments are merely called ``LaTeX comments'' in -% this document). -% -% -% \subsubsection{Behaviour in the class Beamer} -% -% \colorbox{yellow!50}{\textbf{New 1.1}} -% -% \smallskip -% When \pkg{piton} is used in the class \cls{beamer}, the following commands of -% \cls{beamer} are automatically detected in the environments |{Piton}| -% (without any escaping mechanism) : |\action|, -% |\alert|, |\invisible|, |\only|, |\uncover| and |\visible|.\footnote{The extension \pkg{piton} detects the class -% \cls{beamer} but, if needed, it's also possible to activate that mechanism -% with the key |beamer| provided by \pkg{piton} at load-time: |\textbackslash usepackage[beamer]{piton}|} -% -% \medskip -% However, there must be \textbf{no end-of-line} in the arguments of those commands. -% -% \medskip -% Remark that, since the environment |{Piton}| takes in its body with a verbatim -% mode, it's necessary to use the environments |{Piton}| within environments -% |{frame}| protected by the key |fragile|. -% -% \medskip -% Here is a complete example of file: -% -% \begin{Verbatim} -% \documentclass{beamer} -% \usepackage{xcolor} -% \usepackage{piton} -% -% \begin{document} -% -% \begin{frame}[fragile] -% \begin{Piton} -% def square(x): -% ~emphase#\only<2>{ return x*x}@ -% \end{Piton} -% \end{frame} -% -% \end{document} -% \end{Verbatim} -% -% -% \subsection{Page breaks and line breaks} -% -% \label{breakable} -% -% \subsubsection{Page breaks} -% -% By default, the listings produced by the environment |{Piton}| and the command -% |\PitonInputFile| are not breakable. -% -% However, the command |\PitonOptions| provides the key |splittable| to allow such -% breaks. -% -% \begin{itemize} -% \item If the key |splittable| is used without any value, the listings are -% breakable everywhere. -% \item If the key |splittable| is used with a numeric value~$n$ (which must be -% a non-negative integer number), the listings are breakable but no break will -% occur within the first $n$ lines and within the last $n$ lines. Therefore, -% |splittable=1| is equivalent to |splittable|. -% \end{itemize} -% -% \medskip -% Even with a background color (set by |background-color|), the pages breaks are -% allowed, as soon as the key |splittable| is in force.\footnote{With the key -% |splittable|, the environments \texttt{\{Piton\}} are breakable, even within a -% (breakable) environment of \pkg{tcolorbox}. Remind that an environment of -% \pkg{tcolorbox} included in another environment of \pkg{tcolorbox} is -% \emph{not} breakable, even when both environments use the key |breakable| of -% \pkg{tcolorbox}.} -% -% \subsubsection{Line breaks} -% -% By default, the lines of the listings produced by |{Piton}| and -% |\PitonInputFile| are not breakable. -% -% \smallskip -% There exist several keys (available in |\PitonOptions|) to allow and control -% such line breaks. -% -% \begin{itemize} -% \item The key |break-lines| actives the lines breaks. Only the spaces (even in -% the strings) are allowed break points. -% -% \item With the key |indent-broken-lines|, the indentation of a broken line is -% respected at carriage return. -% -% \item The key |end-of-broken-line| corresponds to the symbol placed at the end -% of a broken line. The initial value is: |\hspace*{0.5em}\textbackslash|. -% -% \item The key |continuation-symbol| corresponds to the symbol placed at each -% carriage return. The initial value is: |+\;|. -% -% \item The key |continuation-symbol-on-indentation| coresponds to the symbol -% placed at each carriage return, on the position of the indentation (only when -% the key |indent-broken-line| is in force). The initial value is: -% |$\hookrightarrow\;$|. -% \end{itemize} -% -% -% \bigskip -% The following code has been composed in a |{minipage}| of width 12~cm with the -% following tuning: -% -% \begin{Verbatim} -% \PitonOptions{break-lines,indent-broken-lines,background-color=gray!15} -% \end{Verbatim} -% -% \begin{center} -% \PitonOptions{break-lines,indent-broken-lines,background-color=gray!15} -% \begin{minipage}{12cm} -% \begin{Piton} -% def dict_of_list(l): -% """Converts a list of subrs and descriptions of glyphs in a dictionary""" -% our_dict = {} -% for list_letter in l: -% if (list_letter[0][0:3] == 'dup'): # if it's a subr -% name = list_letter[0][4:-3] -% print("We treat the subr of number " + name) -% else: -% name = list_letter[0][1:-3] # if it's a glyph -% print("We treat the glyph of number " + name) -% our_dict[name] = [treat_Postscript_line(k) for k in list_letter[1:-1]] -% return dict -% \end{Piton} -% \end{minipage} -% \end{center} -% -% \subsection{Footnotes in the environments of piton} -% -% \label{footnote} -% If you want to put footnotes in an environment |{Piton}| or -% (or, more unlikely, in a listing produced by |\PitonInputFile|), you can use a -% pair |\footnotemark|--|\footnotetext|. -% -% \smallskip -% However, it's also possible to extract the footnotes with the help of the -% package \pkg{footnote} or the package \pkg{footnotehyper}. -% -% \smallskip -% If \pkg{piton} is loaded with the option |footnote| (with -% |\usepackage[footnote]{piton}| or with |\PassOptionsToPackage|), the -% package \pkg{footnote} is loaded (if it is not yet loaded) and it is used to -% extract the footnotes. -% -% \smallskip -% If \pkg{piton} is loaded with the option |footnotehyper|, the package -% \pkg{footnotehyper} is loaded (if it is not yet loaded) ant it is used to -% extract footnotes. -% -% \smallskip -% Caution: The packages \pkg{footnote} and \pkg{footnotehyper} are incompatible. -% The package \pkg{footnotehyper} is the successor of the package \pkg{footnote} -% and should be used preferently. The package \pkg{footnote} has some drawbacks, -% in particular: it must be loaded after the package \pkg{xcolor} and it is not -% perfectly compatible with \pkg{hyperref}. -% -% \medskip -% In this document, the package \pkg{piton} has been loaded with the -% option |footnotehyper|. For examples of notes, cf. \ref{notes-examples}, -% p.~\pageref{notes-examples}. -% -% \subsection{Tabulations} -% -% \smallskip -% Even though it's recommended to indent the Python listings with spaces (see -% PEP~8), \pkg{piton} accepts the characters of tabulation (that is to say the -% characters U+0009) at the beginning of the lines. Each character U+0009 is -% replaced by $n$~spaces. The initial value of $n$ is $4$ but it's possible to -% change it with the key |tab-size| of |\PitonOptions|. -% -% \smallskip -% There exists also a key |tabs-auto-gobble| which computes the minimal value -% $n$ of the number of consecutive characters U+0009 beginning each (non empty) -% line of the environment |{Piton}| and applies |gobble| with that value of~$n$ -% (before replacement of the tabulations by spaces, of course). Hence, that key -% is similar to the key |auto-gobble| but acts on U+0009 instead of U+0020 -% (spaces). -% -% \section{Examples} -% -% \subsection{Line numbering} -% -% \label{example-numbering} -% -% We remind that it's possible to have an automatic numbering of the lines in -% the Python listings by using the key |line-numbers| or the key |all-line-numbers|. -% -% By default, the numbers of the lines are composed by \pkg{piton} in an -% overlapping position on the left (by using internally the command |\llap| of LaTeX). -% -% In order to avoid that overlapping, it's possible to use the option |left-margin=auto| -% which will insert automatically a margin adapted to the numbers of lines that -% will be written (that margin is larger when the numbers are greater than~10). -% -% -% \begingroup -% \fvset{commandchars=\~\&\@,formatcom=\small\color{gray}} -% \begin{Verbatim} -% ~emphase&\PitonOptions{background-color=gray!10, left-margin = auto, line-numbers}@ -% \begin{Piton} -% def arctan(x,n=10): -% if x < 0: -% return -arctan(-x) #> (appel récursif) -% elif x > 1: -% return pi/2 - arctan(1/x) #> (autre appel récursif) -% else: -% return sum( (-1)**k/(2*k+1)*x**(2*k+1) for k in range(n) ) -% \end{Piton} -% \end{Verbatim} -% \endgroup -% -% -% -% \begingroup -% \PitonOptions{background-color=gray!10,left-margin = auto, line-numbers} -% \begin{Piton} -% def arctan(x,n=10): -% if x < 0: -% return -arctan(-x) #> (appel récursif) -% elif x > 1: -% return pi/2 - arctan(1/x) #> (autre appel récursif) -% else: -% return sum( (-1)**k/(2*k+1)*x**(2*k+1) for k in range(n) ) -% \end{Piton} -% \endgroup -% -% -% -% \bigskip -% \subsection{Formatting of the LaTeX comments} -% -% \label{example-comments} -% -% It's possible to modify the style |Comment.LaTeX| (with |\SetPitonStyle|) in -% order to display the LaTeX comments (which begin with |#>|) aligned on the -% right margin. -% -% -% \begingroup -% \fvset{commandchars=\~\&\@,formatcom=\small\color{gray}} -% \begin{Verbatim} -% \PitonOptions{background-color=gray!10} -% ~emphase&\SetPitonStyle{Comment.LaTeX = \hfill \normalfont\color{gray}}@ -% \begin{Piton} -% def arctan(x,n=10): -% if x < 0: -% return -arctan(-x) #> appel récursif -% elif x > 1: -% return pi/2 - arctan(1/x) #> autre appel récursif -% else: -% return sum( (-1)**k/(2*k+1)*x**(2*k+1) for k in range(n) ) -% \end{Piton} -% \end{Verbatim} -% \endgroup -% -% \begingroup -% \PitonOptions{background-color=gray!10} -% \SetPitonStyle{Comment.LaTeX = \hfill \normalfont\color{gray}} -% \begin{Piton} -% def arctan(x,n=10): -% if x < 0: -% return -arctan(-x) #> appel récursif -% elif x > 1: -% return pi/2 - arctan(1/x) #> autre appel récursif -% else: -% return sum( (-1)**k/(2*k+1)*x**(2*k+1) for k in range(n) ) -% \end{Piton} -% \endgroup -% -% -% \vspace{1cm} -% It's also possible to display these LaTeX comments in a kind of second column -% by limiting the width of the Python code by an environment |{minipage}| of LaTeX. -% -% -% \begingroup -% \fvset{commandchars=\~\&\@,formatcom=\small\color{gray}} -% \begin{Verbatim} -% \PitonOptions{background-color=gray!10} -% ~emphase&\NewDocumentCommand{\MyLaTeXCommand}{m}{\hfill \normalfont\itshape\rlap{\quad #1}}@ -% ~emphase&\SetPitonStyle{Comment.LaTeX = \MyLaTeXCommand}@ -% ~emphase&\begin{minipage}{12cm}@ -% \begin{Piton} -% def arctan(x,n=10): -% if x < 0: -% return -arctan(-x) #> appel récursif -% elif x > 1: -% return pi/2 - arctan(1/x) #> autre appel récursif -% else: -% s = 0 -% for k in range(n): -% s += (-1)**k/(2*k+1)*x**(2*k+1) -% return s -% \end{Piton} -% ~emphase&\end{minipage}@ -% \end{Verbatim} -% \endgroup -% -% -% -% \begingroup -% \PitonOptions{background-color=gray!10} -% \NewDocumentCommand{\MyLaTeXCommand}{m}{\hfill \normalfont\itshape\rlap{\quad #1}} -% \SetPitonStyle{Comment.LaTeX = \MyLaTeXCommand} -% \begin{minipage}{12cm} -% \begin{Piton} -% def arctan(x,n=10): -% if x < 0: -% return -arctan(-x) #> appel récursif -% elif x > 1: -% return pi/2 - arctan(1/x) #> autre appel récursif -% else: -% s = 0 -% for k in range(n): -% s += (-1)**k/(2*k+1)*x**(2*k+1) -% return s -% \end{Piton} -% \end{minipage} -% \endgroup -% -% -% \bigskip -% \subsection{Notes in the listings} -% -% \label{notes-examples} -% -% In order to be able to extract the notes (which are typeset with the command -% |\footnote|), the extension \pkg{piton} must be loaded with the key |footnote| -% or the key |footenotehyper| as explained in the section \ref{footnote} -% p.~\pageref{footnote}. In this document, the extension \pkg{piton} has been -% loaded with the key |footnotehyper|. -% -% Of course, in an environment |{Piton}|, a command |\footnote| may appear only -% within a LaTeX comment (which begins with |#>|). It's possible to have comments -% which contain only that command |\footnote|. That's the case in the following example. -% -% -% -% \begingroup -% \fvset{commandchars=\~\&\@,formatcom=\small\color{gray}} -% \begin{Verbatim} -% \PitonOptions{background-color=gray!10} -% \begin{Piton} -% def arctan(x,n=10): -% if x < 0: -% return -arctan(-x)~emphase&#>\footnote{First recursive call.}]@ -% elif x > 1: -% return pi/2 - arctan(1/x)~emphase&#>\footnote{Second recursive call.}@ -% else: -% return sum( (-1)**k/(2*k+1)*x**(2*k+1) for k in range(n) ) -% \end{Piton} -% \end{Verbatim} -% \endgroup -% -% \begingroup -% \PitonOptions{background-color=gray!10} -% \begin{Piton} -% def arctan(x,n=10): -% if x < 0: -% return -arctan(-x)#>\footnote{First recursive call.} -% elif x > 1: -% return pi/2 - arctan(1/x)#>\footnote{Second recursive call.} -% else: -% return sum( (-1)**k/(2*k+1)*x**(2*k+1) for k in range(n) ) -% \end{Piton} -% \endgroup -% -% -% \vspace{1cm} -% -% If an environment |{Piton}| is used in an environment |{minipage}| of LaTeX, -% the notes are composed, of course, at the foot of the environment -% |{minipage}|. Recall that such |{minipage}| can't be broken by a page break. - -% -% \begingroup -% \fvset{commandchars=\~\&\@,formatcom=\small\color{gray}} -% \begin{Verbatim} -% \PitonOptions{background-color=gray!10} -% \emphase\begin{minipage}{\linewidth} -% \begin{Piton} -% def arctan(x,n=10): -% if x < 0: -% return -arctan(-x)~emphase&#>\footnote{First recursive call.}@ -% elif x > 1: -% return pi/2 - arctan(1/x)~emphase&#>\footnote{Second recursive call.}@ -% else: -% return sum( (-1)**k/(2*k+1)*x**(2*k+1) for k in range(n) ) -% \end{Piton} -% \end{minipage} -% \end{Verbatim} -% \endgroup -% -% \begingroup -% \PitonOptions{background-color=gray!10} -% \begin{minipage}{\linewidth} -% \begin{Piton} -% def arctan(x,n=10): -% if x < 0: -% return -arctan(-x)#>\footnote{First recursive call.} -% elif x > 1: -% return pi/2 - arctan(1/x)#>\footnote{Second recursive call.} -% else: -% return sum( (-1)**k/(2*k+1)*x**(2*k+1) for k in range(n) ) -% \end{Piton} -% \end{minipage} -% \endgroup -% -% -% \vspace{1cm} -% If we embed an environment |{Piton}| in an environment |{minipage}| -% (typically in order to limit the width of a colored background), it's -% necessary to embed the whole environment |{minipage}| in an environment -% |{savenotes}| (of \pkg{footnote} or \pkg{footnotehyper}) in order to have the -% footnotes composed at the bottom of the page. -% -% \begingroup -% \fvset{commandchars=\~\&\@,formatcom=\small\color{gray}} -% \begin{Verbatim} -% \PitonOptions{background-color=gray!10} -% ~emphase&\begin{savenotes}@ -% \begin{minipage}{13cm} -% \begin{Piton} -% def arctan(x,n=10): -% if x < 0: -% return -arctan(-x)~emphase&#>\footnote{First recursive call.}@ -% elif x > 1: -% return pi/2 - arctan(1/x)~emphase&#>\footnote{Second recursive call.}@ -% else: -% return sum( (-1)**k/(2*k+1)*x**(2*k+1) for k in range(n) ) -% \end{Piton} -% \end{minipage} -% ~emphase&\end{savenotes}@ -% \end{Verbatim} -% \endgroup -% -% -% -% \begin{savenotes} -% \PitonOptions{background-color=gray!10} -% \begin{minipage}{13cm} -% \begin{Piton} -% def arctan(x,n=10): -% if x < 0: -% return -arctan(-x)#>\footnote{First recursive call.} -% elif x > 1: -% return pi/2 - arctan(1/x)#>\footnote{Second recursive call.} -% else: -% return sum( (-1)**k/(2*k+1)*x**(2*k+1) for k in range(n) ) -% \end{Piton} -% \end{minipage} -% \end{savenotes} -% -% \bigskip -% -% \subsection{An example of tuning of the styles} -% -% The graphical styles have been presented in the section \ref{styles}, -% p.~\pageref{styles}. -% -% \smallskip -% We present now an example of tuning of these styles adapted to the documents -% in black and white. We use the font \emph{DejaVu Sans Mono}\footnote{See: -% \url{https://dejavu-fonts.github.io}} specified by the command |\setmonofont| of -% \pkg{fontspec}. -% -% -% \begin{Verbatim} -% \setmonofont[Scale=0.85]{DejaVu Sans Mono} -% -% \SetPitonStyle -% { -% Number = , -% String = \itshape , -% String.Doc = \color{gray} \slshape , -% Operator = , -% Operator.Word = \bfseries , -% Name.Builtin = , -% Name.Function = \bfseries \colorbox{gray!20} , -% Comment = \color{gray} , -% Comment.LaTeX = \normalfont \color{gray}, -% Keyword = \bfseries , -% Name.Namespace = , -% Name.Class = , -% Name.Type = , -% InitialValues = \color{gray} -% } -% \end{Verbatim} -% -% -% \begingroup -% -% \setmonofont[Scale=0.85]{DejaVu Sans Mono} -% -% \PitonOptions{splittable} -% -% \SetPitonStyle -% { -% Number = , -% String = \itshape , -% String.Doc = \color{gray} \slshape , -% Operator.Word = \bfseries , -% Operator = , -% Name.Builtin = , -% Name.Function = \bfseries \colorbox{gray!20} , -% Comment = \color{gray} , -% Comment.LaTeX = \normalfont \color{gray} , -% Keyword = \bfseries , -% Name.Namespace = , -% Name.Class = , -% Name.Type = , -% InitialValues = \color{gray} -% } -% -% -% \bigskip -% -% \begin{Piton} -% from math import pi -% -% def arctan(x,n=10): -% """Compute the mathematical value of arctan(x) -% -% n is the number of terms in the sum -% """ -% if x < 0: -% return -arctan(-x) # appel récursif -% elif x > 1: -% return pi/2 - arctan(1/x) -% #> (we have used that $\arctan(x)+\arctan(1/x)=\pi/2$ for $x>0$) -% else: -% s = 0 -% for k in range(n): -% s += (-1)**k/(2*k+1)*x**(2*k+1) -% return s -% \end{Piton} -% -% \endgroup -% -% \subsection{Use with pyluatex} -% -% The package \pkg{pyluatex} is an extension which allows the execution of some -% Python code from |lualatex| (provided that Python is installed on the machine -% and that the compilation is done with |lualatex| and |--shell-escape|). -% -% Here is, for example, an environment |{PitonExecute}| which formats a Python -% listing (with \pkg{piton}) but display also the output of the execution of the -% code with Python. -% -% -% \begin{Verbatim} -% \ExplSyntaxOn -% \NewDocumentEnvironment { ~emphase#PitonExecute@ } { ! O { } } -% { -% \PyLTVerbatimEnv -% \begin{pythonq} -% } -% { -% \end{pythonq} -% \directlua -% { -% tex.print("\\PitonOptions{~#1}") -% tex.print("\\begin{Piton}") -% tex.print(pyluatex.get_last_code()) -% tex.print("\\end{Piton}") -% tex.print("") -% } -% \begin{center} -% \directlua{tex.print(pyluatex.get_last_output())} -% \end{center} -% } -% \ExplSyntaxOff -% \end{Verbatim} -% -% \ExplSyntaxOn -% \NewDocumentEnvironment { PitonExecute } { ! O { } } -% { -% \PyLTVerbatimEnv -% \begin{pythonq} -% } -% { -% \end{pythonq} -% \directlua -% { -% tex.print("\\PitonOptions{~#1}") -% tex.print("\\begin{Piton}") -% tex.print(pyluatex.get_last_code()) -% tex.print("\\end{Piton}") -% tex.print("") -% } -% \begin{center} -% \directlua{tex.print(pyluatex.get_last_output())} -% \end{center} -% } -% \ExplSyntaxOff -% -% This environment |{PitonExecute}| takes in as optional argument (between -% square brackets) the options of the command |\PitonOptions|. -% -% -% \bigskip -% \clearpage -% -% \section{Implementation} -% -% \subsection{Introduction} -% -% The main job of the package \pkg{piton} is to take in as input a Python -% listing and to send back to LaTeX as output that code \emph{with interlaced LaTeX -% instructions of formatting}. -% -% In fact, all that job is done by a \textsc{lpeg} called |SyntaxPython|. That -% \textsc{lpeg}, when matched against the string of a Python listing, -% returns as capture a Lua table containing data to send to LaTeX. -% The only thing to do after will be to apply |tex.tprint| to each element of -% that table.\footnote{Recall that |tex.tprint| takes in as argument a Lua table whose -% first component is a ``catcode table'' and the second element a string. The -% string will be sent to LaTeX with the regime of catcodes specified by the -% catcode table. If no catcode table is provided, the standard catcodes of LaTeX -% will be used.} -% -% \bigskip -% Consider, for example, the following Python code: -% -% \begin{Piton} -% def parity(x): -% return x%2 -% \end{Piton} -% -% The capture returned by the \pkg{lpeg} |SyntaxPython| against that code is the -% Lua table containing the following elements : -% -% \bigskip -% \begin{minipage}{\linewidth} -% \color{gray} -% -% |{ "\\__piton_begin_line:" }|\footnote{Each line of the Python listings will -% be encapsulated in a pair: \texttt{\textbackslash_@@_begin_line:} -- -% \texttt{\textbackslash@@_end_line:}. The token -% \texttt{\textbackslash@@_end_line:} must be explicit because it will be used as -% marker in order to delimit the argument of the command \texttt{\textbackslash -% @@\_begin\_line:}. Both tokens \texttt{\textbackslash_@@_begin_line:} and -% \texttt{\textbackslash@@_end_line:} will be nullified in the command -% \texttt{\textbackslash piton} (since there can't be lines breaks in the -% argument of a command \texttt{\textbackslash piton}).} -% -% \texttt{\{ "\{\textbackslash PitonStyle\{Keyword\}\{" \}}\footnote{The -% lexical elements of Python for which we have a \pkg{piton} style will be -% formatted via the use of the command \texttt{\textbackslash PitonStyle}. -% Such an element is typeset in LaTeX via the syntax \texttt{\{\textbackslash -% PitonStyle\{\textsl{style}\}\{...\}\}} because the instructions inside an \texttt{\textbackslash -% PitonStyle} may be both semi-global declarations like -% \texttt{\textbackslash bfseries} and commands with one argument like -% \texttt{\textbackslash fbox}.} -% -% \texttt{\{ -% luatexbase.catcodetables.CatcodeTableOther\footnote{\texttt{luatexbase.catcodetables.CatcodeTableOther} is a mere number which corresponds to the ``catcode table'' whose all characters have the catcode ``other'' (which means that they will be typeset by LaTeX verbatim).}, "def" \} } -% -% |{ "}}" }| -% -% |{ luatexbase.catcodetables.CatcodeTableOther, " " }| -% -% |{ "{\PitonStyle{Name.Function}{" }| -% -% |{ luatexbase.catcodetables.CatcodeTableOther, "parity" }| -% -% |{ "}}" }| -% -% |{ luatexbase.catcodetables.CatcodeTableOther, "(" }| -% -% |{ luatexbase.catcodetables.CatcodeTableOther, "x" }| -% -% |{ luatexbase.catcodetables.CatcodeTableOther, ")" }| -% -% |{ luatexbase.catcodetables.CatcodeTableOther, ":" }| -% -% |{ "\\__piton_end_line: \\__piton_newline: \\__piton_begin_line:" }| -% -% |{ luatexbase.catcodetables.CatcodeTableOther, " " }| -% -% |{ "{\PitonStyle{Keyword}{" }| -% -% |{ luatexbase.catcodetables.CatcodeTableOther, "return" }| -% -% |{ "}}" }| -% -% |{ luatexbase.catcodetables.CatcodeTableOther, " " }| -% -% |{ luatexbase.catcodetables.CatcodeTableOther, "x" }| -% -% |{ "{\PitonStyle{Operator}{" }| -% -% |{ luatexbase.catcodetables.CatcodeTableOther, "&" }| -% -% |{ "}}" }| -% -% |{ "{\PitonStyle{Number}{" }| -% -% |{ luatexbase.catcodetables.CatcodeTableOther, "2" }| -% -% |{ "}}" }| -% -% |{ "\\__piton_end_line:" }| -% -% \end{minipage} -% -% \bigskip -% We give now the LaTeX code which is sent back by Lua to TeX (we have written -% on several lines for legibility but no character |\r| will be sent to LaTeX). The -% characters which are greyed-out are sent to LaTeX with the catcode ``other'' -% (=12). All the others characters are sent with the regime of catcodes of L3 -% (as set by |\ExplSyntaxOn|) -% -% -% \begingroup -% \def\gbox#1{\colorbox{gray!20}{\strut #1}} -% \setlength{\fboxsep}{1pt} -% -% \begin{Verbatim*}[formatcom = \color{black}] -% \__piton_begin_line:{\PitonStyle{Keyword}{~gbox#def@}} -% ~gbox# @{\PitonStyle{Name.Function}{~gbox#parity@}}~gbox#(x):@\__piton_end_line:\__piton_newline: -% \__piton_begin_line:~gbox# @{\PitonStyle{Keyword}{~gbox#return@}} -% ~gbox# x@{\PitonStyle{Operator}{~gbox#%@}}{\PitonStyle{Number}{~gbox#2@}}\__piton_end_line: -% \end{Verbatim*} -% \endgroup -% -% -% -% -% \subsection{The L3 part of the implementation} -% -% \subsubsection{Declaration of the package} -% \begin{macrocode} -\NeedsTeXFormat{LaTeX2e} -\RequirePackage{l3keys2e} -\ProvidesExplPackage - {piton} - {\myfiledate} - {\myfileversion} - {Highlight Python codes with LPEG on LuaLaTeX} -% \end{macrocode} -% -% \bigskip -% \begin{macrocode} -\msg_new:nnn { piton } { LuaLaTeX~mandatory } - { The~package~'piton'~must~be~used~with~LuaLaTeX.\\ It~won't~be~loaded. } -\sys_if_engine_luatex:F { \msg_critical:nn { piton } { LuaLaTeX~mandatory } } -% \end{macrocode} -% -% \bigskip -% \begin{macrocode} -\RequirePackage { luatexbase } -% \end{macrocode} -% -% \bigskip -% The boolean |\c_@@_footnotehyper_bool| will indicate if the option -% |footnotehyper| is used. -% \begin{macrocode} -\bool_new:N \c_@@_footnotehyper_bool -% \end{macrocode} -% -% \medskip -% The boolean |\c_@@_footnote_bool| will indicate if the option |footnote| is -% used, but quicky, it will also be set to |true| if the option |footnotehyper| -% is used. -% \begin{macrocode} -\bool_new:N \c_@@_footnote_bool -% \end{macrocode} -% -% \medskip -% The following boolean corresponds to the key |math-comments| (only at load-time). -% \begin{macrocode} -\bool_new:N \c_@@_math_comments_bool -% \end{macrocode} -% -% \medskip -% The following boolean corresponds to the key |beamer|. -% \begin{macrocode} -\bool_new:N \c_@@_beamer_bool -% \end{macrocode} -% -% \bigskip -% We define a set of keys for the options at load-time. -% \begin{macrocode} -\keys_define:nn { piton / package } - { - footnote .bool_set:N = \c_@@_footnote_bool , - footnotehyper .bool_set:N = \c_@@_footnotehyper_bool , - escape-inside .tl_set:N = \c_@@_escape_inside_tl , - escape-inside .initial:n = , - comment-latex .code:n = { \lua_now:n { comment_latex = "#1" } } , - comment-latex .value_required:n = true , - math-comments .bool_set:N = \c_@@_math_comments_bool , - math-comments .default:n = true , - beamer .bool_set:N = \c_@@_beamer_bool , - beamer .default:n = true , - unknown .code:n = \msg_error:nn { piton } { unknown~key~for~package } - } -% \end{macrocode} -% -% \begin{macrocode} -\msg_new:nnn { piton } { unknown~key~for~package } - { - Unknown~key.\\ - You~have~used~the~key~'\l_keys_key_str'~but~the~only~keys~available~here~ - are~'beamer',~'comment-latex',~'escape-inside',~'footnote',~'footnotehyper'~and~ - 'math-comments'.~Other~keys~are~available~in~\token_to_str:N \PitonOptions.\\ - That~key~will~be~ignored. - } -% \end{macrocode} -% -% \bigskip -% We process the options provided by the user at load-time. -% \begin{macrocode} -\ProcessKeysOptions { piton / package } -% \end{macrocode} -% -% \bigskip -% \begin{macrocode} -\begingroup -\cs_new_protected:Npn \@@_set_escape_char:nn #1 #2 - { - \lua_now:n { piton_begin_escape = "#1" } - \lua_now:n { piton_end_escape = "#2" } - } -\cs_generate_variant:Nn \@@_set_escape_char:nn { x x } -\@@_set_escape_char:xx - { \tl_head:V \c_@@_escape_inside_tl } - { \tl_tail:V \c_@@_escape_inside_tl } -\endgroup -% \end{macrocode} -% -% \bigskip -% \begin{macrocode} -\@ifclassloaded { beamer } { \bool_set_true:N \c_@@_beamer_bool } { } -\bool_if:NT \c_@@_beamer_bool { \lua_now:n { piton_beamer = true } } -% \end{macrocode} -% -% \bigskip -% \begin{macrocode} -\hook_gput_code:nnn { begindocument } { . } - { - \@ifpackageloaded { xcolor } - { } - { \msg_fatal:nn { piton } { xcolor~not~loaded } } - } -% \end{macrocode} -% -% \begin{macrocode} -\msg_new:nnn { piton } { xcolor~not~loaded } - { - xcolor~not~loaded \\ - The~package~'xcolor'~is~required~by~'piton'.\\ - This~error~is~fatal. - } -% \end{macrocode} -% -% -% \begin{macrocode} -\msg_new:nnn { piton } { footnote~with~footnotehyper~package } - { - Footnote~forbidden.\\ - You~can't~use~the~option~'footnote'~because~the~package~ - footnotehyper~has~already~been~loaded.~ - If~you~want,~you~can~use~the~option~'footnotehyper'~and~the~footnotes~ - within~the~environments~of~piton~will~be~extracted~with~the~tools~ - of~the~package~footnotehyper.\\ - If~you~go~on,~the~package~footnote~won't~be~loaded. - } -% \end{macrocode} -% -% \begin{macrocode} -\msg_new:nnn { piton } { footnotehyper~with~footnote~package } - { - You~can't~use~the~option~'footnotehyper'~because~the~package~ - footnote~has~already~been~loaded.~ - If~you~want,~you~can~use~the~option~'footnote'~and~the~footnotes~ - within~the~environments~of~piton~will~be~extracted~with~the~tools~ - of~the~package~footnote.\\ - If~you~go~on,~the~package~footnotehyper~won't~be~loaded. - } -% \end{macrocode} -% -% \medskip -% \begin{macrocode} -\bool_if:NT \c_@@_footnote_bool - { -% \end{macrocode} -% The class \cls{beamer} has its own system to extract footnotes and that's why -% we have nothing to do if \cls{beamer} is used. -% \begin{macrocode} - \@ifclassloaded { beamer } - { \bool_set_false:N \c_@@_footnote_bool } - { - \@ifpackageloaded { footnotehyper } - { \@@_error:n { footnote~with~footnotehyper~package } } - { \usepackage { footnote } } - } - } -% \end{macrocode} -% -% \begin{macrocode} -\bool_if:NT \c_@@_footnotehyper_bool - { -% \end{macrocode} -% The class \cls{beamer} has its own system to extract footnotes and that's why -% we have nothing to do if \cls{beamer} is used. -% \begin{macrocode} - \@ifclassloaded { beamer } - { \bool_set_false:N \c_@@_footnote_bool } - { - \@ifpackageloaded { footnote } - { \@@_error:n { footnotehyper~with~footnote~package } } - { \usepackage { footnotehyper } } - \bool_set_true:N \c_@@_footnote_bool - } - } -% \end{macrocode} -% The flag |\c_@@_footnote_bool| is raised and so, we will only have to test -% |\c_@@_footnote_bool| in order to know if we have to insert an environment -% |{savenotes}|. -% -% \bigskip -% \subsubsection{Parameters and technical definitions} -% -% We will compute (with Lua) the numbers of lines of the Python code and store -% it in the following counter. -% \begin{macrocode} -\int_new:N \l_@@_nb_lines_int -% \end{macrocode} -% -% The same for the number of non-empty lines of the Python codes. -% \begin{macrocode} -\int_new:N \l_@@_nb_non_empty_lines_int -% \end{macrocode} -% -% The following counter will be used to count the lines during the composition. -% It will count all the lines, empty or not empty. It won't be used to print the -% numbers of the lines. -% \begin{macrocode} -\int_new:N \g_@@_line_int -% \end{macrocode} -% -% \medskip -% The following token list will contains the (potential) informations to write -% on the |aux| (to be used in the next compilation). -% \begin{macrocode} -\tl_new:N \g_@@_aux_tl -% \end{macrocode} -% -% \medskip -% The following counter corresponds to the key |splittable| of |\PitonOptions|. -% If the value of |\l_@@_splittable_int| is equal to $n$, then no line break can -% occur within the first $n$~lines or the last $n$~lines of the listings. -% \begin{macrocode} -\int_new:N \l_@@_splittable_int -% \end{macrocode} -% -% \medskip -% An initial value of |splittable| equal to 100 is equivalent to say that the -% environments |{Piton}| are unbreakable. -% \begin{macrocode} -\int_set:Nn \l_@@_splittable_int { 100 } -% \end{macrocode} -% -% \medskip -% The following string corresponds to the key |background-color| of |\PitonOptions|. -% \begin{macrocode} -\str_new:N \l_@@_background_color_str -% \end{macrocode} -% -% \medskip -% We will compute the maximal width of the lines of an environment |{Piton}| in -% |\g_@@_width_dim|. We need a global variable because when the key |footnote| -% is in force, each line when be composed in an environment |{savenotes}| and -% (when |slim| is in force) we need to exit |\g_@@_width_dim| from that -% environment. -% \begin{macrocode} -\dim_new:N \g_@@_width_dim -% \end{macrocode} -% The value of that dimension as written on the |aux| file will be stored in -% |\l_@@_width_on_aux_dim|. -% \begin{macrocode} -\dim_new:N \l_@@_width_on_aux_dim -% \end{macrocode} -% -% \medskip -% We will count the environments |{Piton}| (and, in fact, also the commands -% |\PitonInputFile|, despite the name |\g_@@_env_int|). -% \begin{macrocode} -\int_new:N \g_@@_env_int -% \end{macrocode} -% -% \medskip -% The following booleans correspond to the keys |break-lines| and -% |indent-broken-lines|. -% \begin{macrocode} -\bool_new:N \l_@@_break_lines_bool -\bool_new:N \l_@@_indent_broken_lines_bool -% \end{macrocode} -% -% \medskip -% The following token list corresponds to the key |continuation-symbol|. -% \begin{macrocode} -\tl_new:N \l_@@_continuation_symbol_tl -\tl_set:Nn \l_@@_continuation_symbol_tl { + } -% \end{macrocode} -% -% \medskip -% \begin{macrocode} -% The following token list corresponds to the key -% |continuation-symbol-on-indentation|. The name has been shorten to |csoi|. -\tl_new:N \l_@@_csoi_tl -\tl_set:Nn \l_@@_csoi_tl { $ \hookrightarrow \; $ } -% \end{macrocode} -% -% \medskip -% The following token list corresponds to the key |end-of-broken-line|. -% \begin{macrocode} -\tl_new:N \l_@@_end_of_broken_line_tl -\tl_set:Nn \l_@@_end_of_broken_line_tl { \hspace*{0.5em} \textbackslash } -% \end{macrocode} -% -% \medskip -% The following boolean corresponds to the key |slim| of |\PitonOptions|. -% \begin{macrocode} -\bool_new:N \l_@@_slim_bool -% \end{macrocode} -% -% \medskip -% The following dimension corresponds to the key |left-margin| of -% |\PitonOptions|. -% \begin{macrocode} -\dim_new:N \l_@@_left_margin_dim -% \end{macrocode} -% -% \medskip -% The following boolean correspond will be set when the key |left-margin=auto| -% is used. -% \begin{macrocode} -\bool_new:N \l_@@_left_margin_auto_bool -% \end{macrocode} -% -% \medskip -% The tabulators will be replaced by the content of the following token list. -% \begin{macrocode} -\tl_new:N \l_@@_tab_tl -% \end{macrocode} -% -% \medskip -% \begin{macrocode} -\cs_new_protected:Npn \@@_set_tab_tl:n #1 - { - \tl_clear:N \l_@@_tab_tl - \prg_replicate:nn { #1 } - { \tl_put_right:Nn \l_@@_tab_tl { ~ } } - } -\@@_set_tab_tl:n { 4 } -% \end{macrocode} -% -% \medskip -% The following integer corresponds to the key |gobble|. -% \begin{macrocode} -\int_new:N \l_@@_gobble_int -% \end{macrocode} -% -% \medskip -% \begin{macrocode} -\tl_new:N \l_@@_space_tl -\tl_set:Nn \l_@@_space_tl { ~ } -% \end{macrocode} -% -% -% \medskip -% At each line, the following counter will count the spaces at the beginning. -% \begin{macrocode} -\int_new:N \g_@@_indentation_int -% \end{macrocode} -% -% \medskip -% \begin{macrocode} -\cs_new_protected:Npn \@@_an_indentation_space: - { \int_gincr:N \g_@@_indentation_int } -% \end{macrocode} -% -% \bigskip -% \subsubsection{Treatment of a line of code} -% -% -% \bigskip -% In the contents provided by Lua, each line of the Python code will be -% surrounded by |\@@_begin_line:| and |\@@_end_line:|. -% -% \begin{macrocode} -\cs_set_protected:Npn \@@_begin_line: #1 \@@_end_line: - { - \int_gzero:N \g_@@_indentation_int -% \end{macrocode} -% -% -% Be careful: there is curryfication in the following lines. -% \begin{macrocode} - \bool_if:NTF \l_@@_slim_bool - { \hcoffin_set:Nn \l_tmpa_coffin } - { - \str_if_empty:NTF \l_@@_background_color_str - { - \vcoffin_set:Nnn \l_tmpa_coffin - { \dim_eval:n { \linewidth - \l_@@_left_margin_dim } } - } - { - \vcoffin_set:Nnn \l_tmpa_coffin - { \dim_eval:n { \linewidth - \l_@@_left_margin_dim - 0.5 em } } - } - } - { - \language = -1 - \raggedright - \strut - \tl_set:Nn \l_tmpa_tl { #1 } -% \end{macrocode} -% If the key |break-lines| is in force, we replace all the characters U+0032 -% (that is to say the spaces) by |\@@_breakable_space:|. Remark that, except the -% spaces inserted in the LaTeX comments (and maybe in the math comments), all -% these spaces are of catcode ``other'' (=12) and are unbreakable. -% \begin{macrocode} - \bool_if:NT \l_@@_break_lines_bool - { - \regex_replace_all:nnN - { \x20 } - { \c { @@_breakable_space: } } - \l_tmpa_tl - } - \l_tmpa_tl \strut \hfil - } - \hbox_set:Nn \l_tmpa_box - { - \skip_horizontal:N \l_@@_left_margin_dim - \bool_if:NT \l_@@_line_numbers_bool - { - \bool_if:NF \l_@@_all_line_numbers_bool - { \tl_if_empty:nF { #1 } } - \@@_print_number: - } - \str_if_empty:NF \l_@@_background_color_str - { \skip_horizontal:n { 0.5 em } } - \coffin_typeset:Nnnnn \l_tmpa_coffin T l \c_zero_dim \c_zero_dim - } -% \end{macrocode} -% We compute in |\g_@@_width_dim| the maximal width of the lines of the -% environment. -% \begin{macrocode} - \dim_compare:nNnT { \box_wd:N \l_tmpa_box } > \g_@@_width_dim - { \dim_gset:Nn \g_@@_width_dim { \box_wd:N \l_tmpa_box } } - \box_set_dp:Nn \l_tmpa_box { \box_dp:N \l_tmpa_box + 1.25 pt } - \box_set_ht:Nn \l_tmpa_box { \box_ht:N \l_tmpa_box + 1.25 pt } - \tl_if_empty:NTF \l_@@_background_color_str - { \box_use_drop:N \l_tmpa_box } - { - \vbox_top:n - { - \hbox:n - { - \exp_args:NV \color \l_@@_background_color_str - \vrule height \box_ht:N \l_tmpa_box - depth \box_dp:N \l_tmpa_box - width \l_@@_width_on_aux_dim - } - \skip_vertical:n { - \box_ht_plus_dp:N \l_tmpa_box } - \box_set_wd:Nn \l_tmpa_box \l_@@_width_on_aux_dim - \box_use_drop:N \l_tmpa_box - } - } - \vspace { - 2.5 pt } - } -% \end{macrocode} -% -% \bigskip -% \begin{macrocode} -\cs_new_protected:Npn \@@_newline: - { - \int_gincr:N \g_@@_line_int - \int_compare:nNnT \g_@@_line_int > { \l_@@_splittable_int - 1 } - { - \int_compare:nNnT - { \l_@@_nb_lines_int - \g_@@_line_int } > \l_@@_splittable_int - { - \egroup - \bool_if:NT \c_@@_footnote_bool { \end { savenotes } } - \newline - \bool_if:NT \c_@@_footnote_bool { \begin { savenotes } } - \vtop \bgroup - } - } - } -% \end{macrocode} -% -% \bigskip -% \begin{macrocode} -\cs_set_protected:Npn \@@_breakable_space: - { - \discretionary - { \hbox:n { \color { gray } \l_@@_end_of_broken_line_tl } } - { - \hbox_overlap_left:n - { - { - \normalfont \footnotesize \color { gray } - \l_@@_continuation_symbol_tl - } - \skip_horizontal:n { 0.3 em } - \str_if_empty:NF \l_@@_background_color_str - { \skip_horizontal:n { 0.5 em } } - } - \bool_if:NT \l_@@_indent_broken_lines_bool - { - \hbox:n - { - \prg_replicate:nn { \g_@@_indentation_int } { ~ } - { \color { gray } \l_@@_csoi_tl } - } - } - } - { \hbox { ~ } } - } -% \end{macrocode} -% -% \bigskip -% \subsubsection{PitonOptions} -% -% \medskip -% The following parameters correspond to the keys |line-numbers| and -% |all-line-numbers|. -% \begin{macrocode} -\bool_new:N \l_@@_line_numbers_bool -\bool_new:N \l_@@_all_line_numbers_bool -% \end{macrocode} -% -% \medskip -% The following flag corresponds to the key |resume|. -% \begin{macrocode} -\bool_new:N \l_@@_resume_bool -% \end{macrocode} -% -% \bigskip -% Be careful! The name of the following set of keys must be considered as -% public! Hence, it should \emph{not} be changed. -% -% \begin{macrocode} -\keys_define:nn { PitonOptions } - { - gobble .int_set:N = \l_@@_gobble_int , - gobble .value_required:n = true , - auto-gobble .code:n = \int_set:Nn \l_@@_gobble_int { -1 } , - auto-gobble .value_forbidden:n = true , - env-gobble .code:n = \int_set:Nn \l_@@_gobble_int { -2 } , - env-gobble .value_forbidden:n = true , - tabs-auto-gobble .code:n = \int_set:Nn \l_@@_gobble_int { -3 } , - tabs-auto-gobble .value_forbidden:n = true , - line-numbers .bool_set:N = \l_@@_line_numbers_bool , - line-numbers .default:n = true , - all-line-numbers .code:n = - \bool_set_true:N \l_@@_line_numbers_bool - \bool_set_true:N \l_@@_all_line_numbers_bool , - all-line-numbers .value_forbidden:n = true , - resume .bool_set:N = \l_@@_resume_bool , - resume .value_forbidden:n = true , - splittable .int_set:N = \l_@@_splittable_int , - splittable .default:n = 1 , - background-color .str_set:N = \l_@@_background_color_str , - background-color .value_required:n = true , - slim .bool_set:N = \l_@@_slim_bool , - slim .default:n = true , - left-margin .code:n = - \str_if_eq:nnTF { #1 } { auto } - { - \dim_zero:N \l_@@_left_margin_dim - \bool_set_true:N \l_@@_left_margin_auto_bool - } - { \dim_set:Nn \l_@@_left_margin_dim { #1 } } , - left-margin .value_required:n = true , - tab-size .code:n = \@@_set_tab_tl:n { #1 } , - tab-size .value_required:n = true , - show-spaces .code:n = \tl_set:Nn \l_@@_space_tl { ␣ } , % U+2423 - show-spaces .value_forbidden:n = true , - break-lines .bool_set:N = \l_@@_break_lines_bool , - break-lines .default:n = true , - indent-broken-lines .bool_set:N = \l_@@_indent_broken_lines_bool , - indent-broken-lines .default:n = true , - end-of-broken-line .tl_set:N = \l_@@_end_of_broken_line_tl , - end-of-broken-line .value_required:n = true , - continuation-symbol .tl_set:N = \l_@@_continuation_symbol_tl , - continuation-symbol .value_required:n = true , - continuation-symbol-on-indentation .tl_set:N = \l_@@_csoi_tl , - continuation-symbol-on-indentation .value_required:n = true , - unknown .code:n = - \msg_error:nn { piton } { Unknown~key~for~PitonOptions } - } -% \end{macrocode} -% -% -% \bigskip -% \begin{macrocode} -\msg_new:nnnn { piton } { Unknown~key~for~PitonOptions } - { - Unknown~key. \\ - The~key~'\l_keys_key_str'~is~unknown~for~\token_to_str:N \PitonOptions.~ - It~will~be~ignored.\\ - For~a~list~of~the~available~keys,~type~H~<return>. - } - { - The~available~keys~are~(in~alphabetic~order):~ - all-line-numbers,~ - auto-gobble,~ - break-lines,~ - continuation-symbol,~ - continuation-symbol-on-indentation,~ - end-of-broken-line,~ - env-gobble,~ - gobble,~ - indent-broken-lines,~ - left-margin,~ - line-numbers,~ - resume,~ - show-spaces,~ - slim,~ - splittable,~ - tabs-auto-gobble,~ - and~tab-size. - } -% \end{macrocode} -% -% \bigskip -% The argument of |\PitonOptions| is provided by curryfication. -% \begin{macrocode} -\NewDocumentCommand \PitonOptions { } { \keys_set:nn { PitonOptions } } -% \end{macrocode} -% -% \bigskip -% \subsubsection{The numbers of the lines} -% -% \medskip -% The following counter will be used to count the lines in the code when the -% user requires the numbers of the lines to be printed (with |line-numbers| or -% |all-line-numbers|). -% \begin{macrocode} -\int_new:N \g_@@_visual_line_int -% \end{macrocode} -% -% -% \begin{macrocode} -\cs_new_protected:Npn \@@_print_number: - { - \int_gincr:N \g_@@_visual_line_int - \hbox_overlap_left:n - { - { \color { gray } \footnotesize \int_to_arabic:n \g_@@_visual_line_int } - \skip_horizontal:n { 0.4 em } - } - } -% \end{macrocode} -% -% -% \bigskip -% \subsubsection{The command to write on the aux file} -% -% -% \begin{macrocode} -\cs_new_protected:Npn \@@_write_aux: - { - \tl_if_empty:NF \g_@@_aux_tl - { - \iow_now:Nn \@mainaux { \ExplSyntaxOn } - \iow_now:Nx \@mainaux - { - \tl_gset:cn { c_@@_ \int_use:N \g_@@_env_int _ tl } - { \exp_not:V \g_@@_aux_tl } - } - \iow_now:Nn \@mainaux { \ExplSyntaxOff } - } - \tl_gclear:N \g_@@_aux_tl - } -% \end{macrocode} -% -% \begin{macrocode} -\cs_new_protected:Npn \@@_width_to_aux: - { - \bool_if:NT \l_@@_slim_bool - { - \str_if_empty:NF \l_@@_background_color_str - { - \tl_gput_right:Nx \g_@@_aux_tl - { - \dim_set:Nn \l_@@_width_on_aux_dim - { \dim_eval:n { \g_@@_width_dim + 0.5 em } } - } - } - } - } -% \end{macrocode} -% -% \bigskip -% \subsubsection{The main commands and environments for the final user} -% -% \medskip -% \begin{macrocode} -\NewDocumentCommand { \piton } { } - { \peek_meaning:NTF \bgroup \@@_piton_standard \@@_piton_verbatim } -% \end{macrocode} -% -% \begin{macrocode} -\NewDocumentCommand { \@@_piton_standard } { m } - { - \group_begin: - \ttfamily - \cs_set_eq:NN \\ \c_backslash_str - \cs_set_eq:NN \% \c_percent_str - \cs_set_eq:NN \{ \c_left_brace_str - \cs_set_eq:NN \} \c_right_brace_str - \cs_set_eq:NN \$ \c_dollar_str - \cs_set_protected:Npn \@@_begin_line: { } - \cs_set_protected:Npn \@@_end_line: { } - \lua_now:n { piton.pitonParse(token.scan_string()) } { #1 } - \group_end: - } -% \end{macrocode} -% -% -% \begin{macrocode} -\NewDocumentCommand { \@@_piton_verbatim } { v } - { - \group_begin: - \ttfamily - \cs_set_protected:Npn \@@_begin_line: { } - \cs_set_protected:Npn \@@_end_line: { } - \lua_now:n { piton.Parse(token.scan_string()) } { #1 } - \group_end: - } -% \end{macrocode} -% -% \bigskip -% The following command is not a user command. It will be used when you will -% have to ``rescan'' some chunks of Python code. For example, if will be the -% initial value of the Piton style |InitialValues| (the default values of the -% arguments of a Python function). -% \begin{macrocode} -\cs_new_protected:Npn \@@_piton:n #1 - { - \group_begin: - \cs_set_protected:Npn \@@_begin_line: { } - \cs_set_protected:Npn \@@_end_line: { } - \lua_now:n { piton.Parse(token.scan_string()) } { #1 } - \group_end: - } -% \end{macrocode} -% -% \bigskip -% Despite its name, |\@@_pre_env:| will be used both in |\PitonInputFile| dans -% in the environments such as |{Piton}|. -% \begin{macrocode} -\cs_new:Npn \@@_pre_env: - { - \int_gincr:N \g_@@_env_int - \tl_gclear:N \g_@@_aux_tl - \cs_if_exist_use:c { c_@@ _ \int_use:N \g_@@_env_int _ tl } - \dim_compare:nNnT \l_@@_width_on_aux_dim = \c_zero_dim - { \dim_set_eq:NN \l_@@_width_on_aux_dim \linewidth } - \bool_if:NF \l_@@_resume_bool { \int_gzero:N \g_@@_visual_line_int } - \dim_gzero:N \g_@@_width_dim - \int_gzero:N \g_@@_line_int - \dim_zero:N \parindent - \dim_zero:N \lineskip - } -% \end{macrocode} -% -% \bigskip -% \begin{macrocode} -\keys_define:nn { PitonInputFile } - { - first-line .int_set:N = \l_@@_first_line_int , - first-line .value_required:n = true , - last-line .int_set:N = \l_@@_last_line_int , - last-line .value_required:n = true , - } -% \end{macrocode} -% -% \bigskip -% \begin{macrocode} -\NewDocumentCommand { \PitonInputFile } { O { } m } - { - \group_begin: - \int_zero_new:N \l_@@_first_line_int - \int_zero_new:N \l_@@_last_line_int - \int_set_eq:NN \l_@@_last_line_int \c_max_int - \keys_set:nn { PitonInputFile } { #1 } - \@@_pre_env: - \mode_if_vertical:TF \mode_leave_vertical: \newline -% \end{macrocode} -% We count with Lua the number of lines of the argument. The result will be -% stored by Lua in |\l_@@_nb_lines_int|. That information will be used to allow -% or disallow page breaks. -% \begin{macrocode} - \lua_now:n { piton.CountLinesFile(token.scan_argument()) } { #2 } -% \end{document} -% If the final user has used both |left-margin=auto| and |line-numbers| or -% |all-line-numbers|, we have to compute the width of the maximal number of -% lines at the end of the composition of the listing to fix the correct value to -% |left-margin|. -% \begin{macrocode} - \bool_lazy_and:nnT \l_@@_left_margin_auto_bool \l_@@_line_numbers_bool - { - \hbox_set:Nn \l_tmpa_box - { - \footnotesize - \bool_if:NTF \l_@@_all_line_numbers_bool - { - \int_to_arabic:n - { \g_@@_visual_line_int + \l_@@_nb_lines_int } - } - { - \lua_now:n - { piton.CountNonEmptyLinesFile(token.scan_argument()) } - { #2 } - \int_to_arabic:n - { \g_@@_visual_line_int + \l_@@_nb_non_empty_lines_int } - } - } - \dim_set:Nn \l_@@_left_margin_dim { \box_wd:N \l_tmpa_box + 0.5em } - } -% \end{macrocode} -% Now, the main job. -% \begin{macrocode} - \ttfamily - \bool_if:NT \c_@@_footnote_bool { \begin { savenotes } } - \vtop \bgroup - \lua_now:e - { piton.ParseFile(token.scan_argument(), - \int_use:N \l_@@_first_line_int , - \int_use:N \l_@@_last_line_int ) - } - { #2 } - \egroup - \bool_if:NT \c_@@_footnote_bool { \end { savenotes } } - \@@_width_to_aux: - \group_end: - \@@_write_aux: - } -% \end{macrocode} -% -% -% \bigskip -% \begin{macrocode} -\NewDocumentCommand { \NewPitonEnvironment } { m m m m } - { - \dim_zero:N \parindent -% \end{macrocode} -% We construct a TeX macro which will catch as argument all the tokens until -% |\end{|\texttt{\textsl{name_env}}|}| with, in that -% |\end{|\texttt{\textsl{name_env}}|}|, the catcodes of |\|, |{| and |}| equal to -% 12 (``\texttt{other}''). The latter explains why the definition of that -% function is a bit complicated. -% \begin{macrocode} - \use:x - { - \cs_set_protected:Npn - \use:c { _@@_collect_ #1 :w } - ####1 - \c_backslash_str end \c_left_brace_str #1 \c_right_brace_str - } - { - \group_end: - \mode_if_vertical:TF \mode_leave_vertical: \newline -% \end{macrocode} -% We count with Lua the number of lines of the argument. The result will be -% stored by Lua in |\l_@@_nb_lines_int|. That information will be used to allow -% or disallow page breaks. -% \begin{macrocode} - \lua_now:n { piton.CountLines(token.scan_argument()) } { ##1 } -% \end{macrocode} -% If the final user has used both |left-margin=auto| and |line-numbers|, we have -% to compute the width of the maximal number of lines at the end of the -% environment to fix the correct value to |left-margin|. -% \begin{macrocode} - \bool_lazy_and:nnT \l_@@_left_margin_auto_bool \l_@@_line_numbers_bool - { - \bool_if:NTF \l_@@_all_line_numbers_bool - { - \hbox_set:Nn \l_tmpa_box - { - \footnotesize - \int_to_arabic:n - { \g_@@_visual_line_int + \l_@@_nb_lines_int } - } - } - { - \lua_now:n - { piton.CountNonEmptyLines(token.scan_argument()) } - { ##1 } - \hbox_set:Nn \l_tmpa_box - { - \footnotesize - \int_to_arabic:n - { \g_@@_visual_line_int + \l_@@_nb_non_empty_lines_int } - } - } - \dim_set:Nn \l_@@_left_margin_dim - { \box_wd:N \l_tmpa_box + 0.5 em } - } -% \end{macrocode} -% Now, the main job. -% \begin{macrocode} - \ttfamily - \bool_if:NT \c_@@_footnote_bool { \begin { savenotes } } - \vtop \bgroup - \lua_now:e - { - piton.GobbleParse - ( \int_use:N \l_@@_gobble_int , token.scan_argument() ) - } - { ##1 } - \vspace { 2.5 pt } - \egroup - \bool_if:NT \c_@@_footnote_bool { \end { savenotes } } - \@@_width_to_aux: -% \end{macrocode} -% The following |\end{#1}| is only for the groups and the stack of -% environments of LaTeX. -% \begin{macrocode} - \end { #1 } - \@@_write_aux: - } -% \end{macrocode} -% -% -% \bigskip -% We can now define the new environment. -% -% We are still in the definition of the command |\NewPitonEnvironment|... -% \begin{macrocode} - \NewDocumentEnvironment { #1 } { #2 } - { - #3 - \@@_pre_env: - \group_begin: - \tl_map_function:nN - { \ \\ \{ \} \$ \& \# \^ \_ \% \~ \^^I } - \char_set_catcode_other:N - \use:c { _@@_collect_ #1 :w } - } - { #4 } -% \end{macrocode} -% -% \medskip -% The following code is for technical reasons. We want to change the catcode of -% |^^M| before catching the arguments of the new environment we are defining. -% Indeed, if not, we will have problems if there is a final optional argument in -% our environment (if that final argument is not used by the user in an -% instance of the environment, a spurious space is inserted, probably because -% the |^^M| is converted to space). -% \begin{macrocode} - \AddToHook { env / #1 / begin } { \char_set_catcode_other:N \^^M } - } -% \end{macrocode} -% This is the end of the definition of the command |\NewPitonEnvironment|. -% -% \bigskip -% \begin{macrocode} -\NewPitonEnvironment { Piton } { } { } { } -% \end{macrocode} -% -% \bigskip -% \subsubsection{The styles} -% -% \medskip -% The following command is fundamental: it will be used by the Lua code. -% \begin{macrocode} -\NewDocumentCommand { \PitonStyle } { m } { \use:c { pitonStyle #1 } } -% \end{macrocode} -% -% \medskip -% The following command takes in its argument by curryfication. -% \begin{macrocode} -\NewDocumentCommand { \SetPitonStyle } { } { \keys_set:nn { piton / Styles } } -% \end{macrocode} -% -% \medskip -% \begin{macrocode} -\cs_new_protected:Npn \@@_math_scantokens:n #1 - { \normalfont \scantextokens { $#1$ } } -% \end{macrocode} -% -% \medskip -% \begin{macrocode} -\keys_define:nn { piton / Styles } - { - String.Interpol .tl_set:c = pitonStyle String.Interpol , - String.Interpol .value_required:n = true , - FormattingType .tl_set:c = pitonStyle FormattingType , - FormattingType .value_required:n = true , - Dict.Value .tl_set:c = pitonStyle Dict.Value , - Dict.Value .value_required:n = true , - Name.Decorator .tl_set:c = pitonStyle Name.Decorator , - Name.Decorator .value_required:n = true , - Name.Function .tl_set:c = pitonStyle Name.Function , - Name.Function .value_required:n = true , - Keyword .tl_set:c = pitonStyle Keyword , - Keyword .value_required:n = true , - Keyword.Constant .tl_set:c = pitonStyle Keyword.Constant , - Keyword.constant .value_required:n = true , - String.Doc .tl_set:c = pitonStyle String.Doc , - String.Doc .value_required:n = true , - Interpol.Inside .tl_set:c = pitonStyle Interpol.Inside , - Interpol.Inside .value_required:n = true , - String.Long .tl_set:c = pitonStyle String.Long , - String.Long .value_required:n = true , - String.Short .tl_set:c = pitonStyle String.Short , - String.Short .value_required:n = true , - String .meta:n = { String.Long = #1 , String.Short = #1 } , - Comment.Math .tl_set:c = pitonStyle Comment.Math , - Comment.Math .default:n = \@@_math_scantokens:n , - Comment.Math .initial:n = , - Comment .tl_set:c = pitonStyle Comment , - Comment .value_required:n = true , - InitialValues .tl_set:c = pitonStyle InitialValues , - InitialValues .value_required:n = true , - Number .tl_set:c = pitonStyle Number , - Number .value_required:n = true , - Name.Namespace .tl_set:c = pitonStyle Name.Namespace , - Name.Namespace .value_required:n = true , - Name.Class .tl_set:c = pitonStyle Name.Class , - Name.Class .value_required:n = true , - Name.Builtin .tl_set:c = pitonStyle Name.Builtin , - Name.Builtin .value_required:n = true , - Name.Type .tl_set:c = pitonStyle Name.Type , - Name.Type .value_required:n = true , - Operator .tl_set:c = pitonStyle Operator , - Operator .value_required:n = true , - Operator.Word .tl_set:c = pitonStyle Operator.Word , - Operator.Word .value_required:n = true , - Post.Function .tl_set:c = pitonStyle Post.Function , - Post.Function .value_required:n = true , - Exception .tl_set:c = pitonStyle Exception , - Exception .value_required:n = true , - Comment.LaTeX .tl_set:c = pitonStyle Comment.LaTeX , - Comment.LaTeX .value_required:n = true , - Beamer .tl_set:c = pitonStyle Beamer , - Beamer .value_required:n = true , - unknown .code:n = - \msg_error:nn { piton } { Unknown~key~for~SetPitonStyle } - } -% \end{macrocode} -% -% -% \bigskip -% \begin{macrocode} -\msg_new:nnn { piton } { Unknown~key~for~SetPitonStyle } - { - The~style~'\l_keys_key_str'~is~unknown.\\ - This~key~will~be~ignored.\\ - The~available~styles~are~(in~alphabetic~order):~ - Comment,~ - Comment.LaTeX,~ - Dict.Value,~ - Exception,~ - InitialValues,~ - Keyword,~ - Keyword.Constant,~ - Name.Builtin,~ - Name.Class,~ - Name.Decorator,~ - Name.Function,~ - Name.Namespace,~ - Number,~ - Operator,~ - Operator.Word,~ - String,~ - String.Doc,~ - String.Long,~ - String.Short,~and~ - String.Interpol. - } -% \end{macrocode} -% -% \bigskip -% \subsubsection{The initial style} -% -% The initial style is inspired by the style ``manni'' of Pygments. -% -% \medskip -% \begin{macrocode} -\SetPitonStyle - { - Comment = \color[HTML]{0099FF} \itshape , - Exception = \color[HTML]{CC0000} , - Keyword = \color[HTML]{006699} \bfseries , - Keyword.Constant = \color[HTML]{006699} \bfseries , - Name.Builtin = \color[HTML]{336666} , - Name.Decorator = \color[HTML]{9999FF}, - Name.Class = \color[HTML]{00AA88} \bfseries , - Name.Function = \color[HTML]{CC00FF} , - Name.Namespace = \color[HTML]{00CCFF} , - Number = \color[HTML]{FF6600} , - Operator = \color[HTML]{555555} , - Operator.Word = \bfseries , - String = \color[HTML]{CC3300} , - String.Doc = \color[HTML]{CC3300} \itshape , - String.Interpol = \color[HTML]{AA0000} , - Comment.LaTeX = \normalfont \color[rgb]{.468,.532,.6} , - Name.Type = \color[HTML]{336666} , - InitialValues = \@@_piton:n , - Dict.Value = \@@_piton:n , - Interpol.Inside = \color{black}\@@_piton:n , - Beamer = \@@_piton:n , - Post.Function = \@@_piton:n , - } -% \end{macrocode} -% The last style |Post.Function| should be considered as an ``internal style'' -% (not available for the final user). -% -% \medskip -% If the key |math-comments| has been used at load-time, we change the style -% |Comment.Math| which should be considered only at an ``internal style''. -% However, maybe we will document in a future version the possibility to write -% change the style \emph{locally} in a document)]. -% \begin{macrocode} -\bool_if:NT \c_@@_math_comments_bool - { \SetPitonStyle { Comment.Math } } -% \end{macrocode} -% -% \bigskip -% \subsubsection{Security} -% -% \begin{macrocode} -\AddToHook { env / piton / begin } - { \msg_fatal:nn { piton } { No~environment~piton } } - -\msg_new:nnn { piton } { No~environment~piton } - { - There~is~no~environment~piton!\\ - There~is~an~environment~{Piton}~and~a~command~ - \token_to_str:N \piton\ but~there~is~no~environment~ - {piton}.~This~error~is~fatal. - } -% \end{macrocode} -% -% -% \bigskip -% \subsection{The Lua part of the implementation} -% -% \bigskip -% \begin{macrocode} -\ExplSyntaxOff -\RequirePackage{luacode} -% \end{macrocode} -% -% -% \bigskip -% The Lua code will be loaded via a |{luacode*}| environment. Thei environment -% is by itself a Lua block and the local declarations will be local to that -% block. All the global functions (used by the L3 parts of the implementation) -% will be put in a Lua table |piton|. -% -% \medskip -% \begin{macrocode} -\begin{luacode*} -piton = piton or { } -% \end{macrocode} -% -% \begin{macrocode} -if piton.comment_latex == nil then piton.comment_latex = ">" end -piton.comment_latex = "#" .. piton.comment_latex -% \end{macrocode} -% -% -% \bigskip -% \subsubsection{Special functions dealing with LPEG} -% -% -% \medskip -% We will use the Lua library \pkg{lpeg} which is built in LuaTeX. That's why we -% define first aliases for several functions of that library. -% \begin{macrocode} -local P, S, V, C, Ct, Cc = lpeg.P, lpeg.S, lpeg.V, lpeg.C, lpeg.Ct, lpeg.Cc -local Cf, Cs = lpeg.Cf, lpeg.Cs -% \end{macrocode} -% -% -% \bigskip -% The function |Q| takes in as argument a pattern and returns a \textsc{lpeg} -% \emph{which does a capture} of the pattern. That capture will be sent to LaTeX -% with the catcode ``other'' for all the characters: it's suitable for elements -% of the Python listings that \pkg{piton} will typeset verbatim (thanks to the -% catcode ``other''). -% \begin{macrocode} -local function Q(pattern) - return Ct ( Cc ( luatexbase.catcodetables.CatcodeTableOther ) * C ( pattern ) ) -end -% \end{macrocode} -% -% -% \bigskip -% The function |L| takes in as argument a pattern and returns a \textsc{lpeg} -% \emph{which does a capture} of the pattern. That capture will be sent to LaTeX -% with standard LaTeX catcodes for all the characters: the elements captured -% will be formatted as normal LaTeX codes. It's suitable for the ``LaTeX -% comments'' in the environments |{Piton}| and the elements beetween -% ``|escape-inside|''. That function won't be much used. -% \begin{macrocode} -local function L(pattern) - return Ct ( C ( pattern ) ) -end -% \end{macrocode} -% -% \bigskip -% The function |Lc| (the c is for \emph{constant}) takes in as argument a string -% and returns a \textsc{lpeg} \emph{with does a constant capture} which returns -% that string. The elements captured will be formatted as L3 code. It will be -% used to send to LaTeX all the formatting LaTeX instructions we have to insert -% in order to do the syntactic highlighting (that's the main job of -% \pkg{piton}). That function will be widely used. -% \begin{macrocode} -local function Lc(string) - return Cc ( { luatexbase.catcodetables.expl , string } ) -end -% \end{macrocode} -% -% \bigskip -% The function |K| creates a \textsc{lpeg} which will return as capture -% the whole LaTeX code corresponding to a Python chunk (that is to say with the -% LaTeX formatting instructions corresponding to the syntactic nature of that -% Python chunk). The first argument is a pattern (that is to say a \textsc{lpeg} -% without capture) and the second element is a Lua string corresponding to the -% name of a \pkg{piton} style. If the second argument is not present, the -% function |K| behaves as the function |Q| does. -% \begin{macrocode} -local function K(pattern, style) - if style - then - return - Lc ( "{\\PitonStyle{" .. style .. "}{" ) - * Q ( pattern ) - * Lc ( "}}" ) - else - return Q ( pattern ) - end -end -% \end{macrocode} -% The formatting commands in a given \pkg{piton} style (eg. the style |Keyword|) -% may be semi-global declarations (such as |\bfseries| or |\slshape|) or LaTeX -% macros with an argument (such as |\fbox| or |\colorbox{yellow}|). In order to -% deal with both syntaxes, we have used two pairs of braces: -% |{\PitonStyle{Keyword}{|\texttt{\slshape text to format}|}}|. -% -% \bigskip -% The following \textsc{lpeg} catches the Python chunks which are in LaTeX -% escapes (and that chunks will be considered as normal LaTeX constructions). We -% recall that |piton.begin_espace| and |piton_end_escape| are Lua strings -% corresponding to the key |escape-inside|\footnote{The \pkg{piton} key -% |escape-inside| is available at load-time only.}. Since the elements that will -% be catched must be sent to LaTeX with standard LaTeX catcodes, we put the -% capture (done by the function |C|) in a table (by using |Ct|, which is an -% alias for |lpeg.Ct|) without number of catcode table at the first component of -% the table. -% \begin{macrocode} -local Escape = - P(piton_begin_escape) - * L ( ( 1 - P(piton_end_escape) ) ^ 1 ) - * P(piton_end_escape) -% \end{macrocode} -% -% \vspace{1cm} -% The following line is mandatory. -% \begin{macrocode} -lpeg.locale(lpeg) -% \end{macrocode} -% -% \bigskip -% \subsubsection{The LPEG SyntaxPython} -% -% \begin{macrocode} -local alpha, digit, space = lpeg.alpha, lpeg.digit, lpeg.space -% \end{macrocode} -% -% Remember that, for \textsc{lpeg}, the Unicode characters such as |à|, |â|, -% |ç|, etc. are in fact strings of length 2 (2 bytes) because \pkg{lpeg} is not -% Unicode-aware. -% \begin{macrocode} -local letter = alpha + P "_" - + P "â" + P "à" + P "ç" + P "é" + P "è" + P "ê" + P "ë" + P "ï" + P "î" - + P "ô" + P "û" + P "ü" + P "Â" + P "À" + P "Ç" + P "É" + P "È" + P "Ê" - + P "Ë" + P "Ï" + P "Î" + P "Ô" + P "Û" + P "Ü" - -local alphanum = letter + digit -% \end{macrocode} -% -% \bigskip -% The following \textsc{lpeg} |identifier| is a mere pattern (that is to say -% more or less a regular expression) which matches the Python identifiers (hence -% the name). -% \begin{macrocode} -local identifier = letter * alphanum ^ 0 -% \end{macrocode} -% -% \medskip -% On the other hand, the \textsc{lpeg} |Identifier| (with a capital) also returns -% a \emph{capture}. -% \begin{macrocode} -local Identifier = K ( identifier ) -% \end{macrocode} -% -% \bigskip -% By convention, we will use names with an initial capital for \textsc{lpeg} -% which return captures. -% -% -% \bigskip -% Here is the first use of our function~|K|. That function will be used to -% construct \textsc{lpeg} which capture Python chunks for which we have a -% dedicated \pkg{piton} style. For example, for the numbers, \pkg{piton} -% provides a style which is called |Number|. The name of the style is provided -% as a Lua string in the second argument of the function~|K|. By convention, we -% use single quotes for delimiting the Lua strings which are names of -% \pkg{piton} styles (but this is only a convention). -% \begin{macrocode} -local Number = - K ( - ( digit^1 * P "." * digit^0 + digit^0 * P "." * digit^1 + digit^1 ) - * ( S "eE" * S "+-" ^ -1 * digit^1 ) ^ -1 - + digit^1 , - 'Number' - ) -% \end{macrocode} -% -% \bigskip -% We recall that |piton.begin_espace| and |piton_end_escape| are Lua strings -% corresponding to the key |escape-inside|\footnote{The \pkg{piton} key -% |escape-inside| is available at load-time only.}. Of course, if the final user -% has not used the key |escape-inside|, these strings are empty. -% \begin{macrocode} -local Word -if piton_begin_escape ~= '' -then Word = K ( ( ( 1 - space - P(piton_begin_escape) - P(piton_end_escape) ) - - S "'\"\r[()]" - digit ) ^ 1 ) -else Word = K ( ( ( 1 - space ) - S "'\"\r[()]" - digit ) ^ 1 ) -end -% \end{macrocode} -% -% \bigskip -% \begin{macrocode} -local Space = K ( ( space - P "\r" ) ^ 1 ) - -local SkipSpace = K ( ( space - P "\r" ) ^ 0 ) - -local Punct = K ( S ".,:;!" ) -% \end{macrocode} -% -% \bigskip -% \begin{macrocode} -local Tab = P "\t" * Lc ( '\\l_@@_tab_tl' ) -% \end{macrocode} -% -% \bigskip -% \begin{macrocode} -local SpaceIndentation = - Lc ( '\\@@_an_indentation_space:' ) * K " " -% \end{macrocode} -% -% \bigskip -% The following \textsc{lpeg} |EOL| is for the end of lines. -% \begin{macrocode} -local EOL = - P "\r" - * - ( - ( space^0 * -1 ) - + -% \end{macrocode} -% We recall that each line in the Python code we have to parse will be sent -% back to LaTeX between a pair |\@@_begin_line:| -- -% |\@@_end_line:|\footnote{Remember that the \texttt{\textbackslash -% @@\_end\_line:} must be explicit because it will be used as marker in order to -% delimit the argument of the command \texttt{\textbackslash @@\_begin\_line:}}. -% \begin{macrocode} - Lc ( '\\@@_end_line: \\@@_newline: \\@@_begin_line:' ) - ) - * - SpaceIndentation ^ 0 -% \end{macrocode} -% -% \bigskip -% \begin{macrocode} -local Delim = K ( S "[()]" ) -% \end{macrocode} -% -% \bigskip -% Some strings of length 2 are explicit because we want the corresponding -% ligatures available in some fonts such as \emph{Fira Code} to be active. -% \begin{macrocode} -local Operator = - K ( P "!=" + P "<>" + P "==" + P "<<" + P ">>" + P "<=" + P ">=" + P ":=" - + P "//" + P "**" + S "-~+/*%=<>&.@|" - , - 'Operator' - ) - -local OperatorWord = - K ( P "in" + P "is" + P "and" + P "or" + P "not" , 'Operator.Word') - -local Keyword = - K ( P "as" + P "assert" + P "break" + P "case" + P "class" + P "continue" - + P "def" + P "del" + P "elif" + P "else" + P "except" + P "exec" - + P "finally" + P "for" + P "from" + P "global" + P "if" + P "import" - + P "lambda" + P "non local" + P "pass" + P "return" + P "try" - + P "while" + P "with" + P "yield" + P "yield from" , - 'Keyword' ) - + K ( P "True" + P "False" + P "None" , 'Keyword.Constant' ) - -local Builtin = - K ( P "__import__" + P "abs" + P "all" + P "any" + P "bin" + P "bool" - + P "bytearray" + P "bytes" + P "chr" + P "classmethod" + P "compile" - + P "complex" + P "delattr" + P "dict" + P "dir" + P "divmod" - + P "enumerate" + P "eval" + P "filter" + P "float" + P "format" - + P "frozenset" + P "getattr" + P "globals" + P "hasattr" + P "hash" - + P "hex" + P "id" + P "input" + P "int" + P "isinstance" + P "issubclass" - + P "iter" + P "len" + P "list" + P "locals" + P "map" + P "max" - + P "memoryview" + P "min" + P "next" + P "object" + P "oct" + P "open" - + P "ord" + P "pow" + P "print" + P "property" + P "range" + P "repr" - + P "reversed" + P "round" + P "set" + P "setattr" + P "slice" + P "sorted" - + P "staticmethod" + P "str" + P "sum" + P "super" + P "tuple" + P "type" - + P "vars" + P "zip" , - 'Name.Builtin' ) - -local Exception = - K ( "ArithmeticError" + P "AssertionError" + P "AttributeError" - + P "BaseException" + P "BufferError" + P "BytesWarning" + P "DeprecationWarning" - + P "EOFError" + P "EnvironmentError" + P "Exception" + P "FloatingPointError" - + P "FutureWarning" + P "GeneratorExit" + P "IOError" + P "ImportError" - + P "ImportWarning" + P "IndentationError" + P "IndexError" + P "KeyError" - + P "KeyboardInterrupt" + P "LookupError" + P "MemoryError" + P "NameError" - + P "NotImplementedError" + P "OSError" + P "OverflowError" - + P "PendingDeprecationWarning" + P "ReferenceError" + P "ResourceWarning" - + P "RuntimeError" + P "RuntimeWarning" + P "StopIteration" - + P "SyntaxError" + P "SyntaxWarning" + P "SystemError" + P "SystemExit" - + P "TabError" + P "TypeError" + P "UnboundLocalError" + P "UnicodeDecodeError" - + P "UnicodeEncodeError" + P "UnicodeError" + P "UnicodeTranslateError" - + P "UnicodeWarning" + P "UserWarning" + P "ValueError" + P "VMSError" - + P "Warning" + P "WindowsError" + P "ZeroDivisionError" - + P "BlockingIOError" + P "ChildProcessError" + P "ConnectionError" - + P "BrokenPipeError" + P "ConnectionAbortedError" + P "ConnectionRefusedError" - + P "ConnectionResetError" + P "FileExistsError" + P "FileNotFoundError" - + P "InterruptedError" + P "IsADirectoryError" + P "NotADirectoryError" - + P "PermissionError" + P "ProcessLookupError" + P "TimeoutError" - + P "StopAsyncIteration" + P "ModuleNotFoundError" + P "RecursionError" , - 'Exception' ) - -local RaiseException = K ( P "raise" , 'Keyword' ) * SkipSpace * Exception * K ( P "(" ) - -local ExceptionInConsole = Exception * K ( ( 1 - P "\r" ) ^ 0 ) * EOL -% \end{macrocode} -% -% \bigskip -% In Python, a ``decorator'' is a statement whose begins by |@| which patches -% the function defined in the following statement. -% \begin{macrocode} -local Decorator = K ( P "@" * letter^1 , 'Name.Decorator' ) -% \end{macrocode} -% -% \bigskip -% The following \textsc{lpeg} |DefClass| will be used to detect the definition of a -% new class (the name of that new class will be formatted with the \pkg{piton} -% style |Name.Class|). -% -% \smallskip -% Example:\enskip \piton{class myclass:} -% \begin{macrocode} -local DefClass = - K ( P "class" , 'Keyword' ) * Space * K ( identifier , 'Name.Class' ) -% \end{macrocode} -% -% If the word |class| is not followed by a identifier, it will be catched as -% keyword by the \textsc{lpeg} |Keyword| (useful if we want to type a -% list of keywords). -% -% -% \bigskip -% The following \textsc{lpeg} |ImportAs| is used for the lines beginning by |import|. -% % We have to detect the potential keyword |as| because both the name of the -% module and its alias must be formatted with the \pkg{piton} style |Name.Namespace|. -% -% \smallskip -% Example:\enskip \piton{import numpy as np} -% -% \smallskip -% Moreover, after the keyword |import|, it's possible to have a comma-separated -% list of modules (if the keyword |as| is not used). -% -% \smallskip -% Example:\enskip \piton{import math, numpy} -% \begin{macrocode} -local ImportAs = - K ( P "import" , 'Keyword' ) - * Space - * K ( identifier * ( P "." * identifier ) ^ 0 , - 'Name.Namespace' - ) - * ( - ( Space * K ( P "as" , 'Keyword' ) * Space - * K ( identifier , 'Name.Namespace' ) ) - + - ( SkipSpace * K ( P "," ) * SkipSpace - * K ( identifier , 'Name.Namespace' ) ) ^ 0 - ) -% \end{macrocode} -% Be careful: there is no commutativity of |+| in the previous expression. -% -% \bigskip -% The \textsc{lpeg} |FromImport| is used for the lines beginning by |from|. We -% need a special treatment because the identifier following the keyword |from| -% must be formatted with the \pkg{piton} style |Name.Namespace| and the -% following keyword |import| must be formatted with the \pkg{piton} style -% |Keyword| and must \emph{not} be catched by the \textsc{lpeg} |ImportAs|. -% -% \smallskip -% Example:\enskip \piton{from math import pi} -% -% \smallskip -% \begin{macrocode} -local FromImport = - K ( P "from" , 'Keyword' ) - * Space * K ( identifier , 'Name.Namespace' ) - * Space * K ( P "import" , 'Keyword' ) -% \end{macrocode} -% -% \bigskip -% \paragraph{The strings of Python} -% -% For the strings in Python, there are four categories of delimiters (without -% counting the prefixes for f-strings and raw strings). We will use, in the -% names of our \textsc{lpeg}, prefixes to distinguish the \textsc{lpeg} dealing -% with that categories of strings, as presented in the following tabular. -% \begin{center} -% \begin{tabular}{ccc} -% \toprule -% & |Single| & |Double| \\ -% \midrule -% |Short| & |'text'| & |"text"| \\ -% |Long| & |'''test'''| & |"""text"""| \\ -% \bottomrule -% \end{tabular} -% \end{center} -% -% -% \bigskip -% First, we define \textsc{lpeg} for the interpolations in the f-strings. Here -% is an example of a f-string with an interpolation and a format -% instruction\footnote{There is no special \pkg{piton} style for the formatting -% instruction (after the colon): the style which will be applied will be the -% style of the encompassing string, that is to say |String.Short| or -% |String.Long|.} in that interpolation: -% -% \piton{f'Total price: {total+1:.2f} €'} -% -% \medskip -% The following \textsc{lpeg} |SingleShortInterpol| (and the three variants) -% will catch the whole interpolation, included the braces, that is to say, in -% the previous example:\enskip |{total+1:.2f}| -% -% \medskip -% \begin{macrocode} -local SingleShortInterpol = - K ( P "{" , 'String.Interpol') - * K ( ( 1 - S "}':" ) ^ 0 , 'Interpol.Inside' ) - * K ( P ":" * (1 - S "}:'") ^ 0 ) ^ -1 - * K ( P "}" , 'String.Interpol' ) - -local DoubleShortInterpol = - K ( P "{" , 'String.Interpol' ) - * K ( ( 1 - S "}\":" ) ^ 0 , 'Interpol.Inside' ) - * ( K ( P ":" , 'String.Interpol' ) * K ( (1 - S "}:\"") ^ 0 ) ) ^ -1 - * K ( P "}" , 'String.Interpol' ) - -local SingleLongInterpol = - K ( P "{" , 'String.Interpol' ) - * K ( ( 1 - S "}:\r" - P "'''" ) ^ 0 , 'Interpol.Inside' ) - * K ( P ":" * (1 - S "}:\r" - P "'''" ) ^ 0 ) ^ -1 - * K ( P "}" , 'String.Interpol' ) - -local DoubleLongInterpol = - K ( P "{" , 'String.Interpol' ) - * K ( ( 1 - S "}:\r" - P "\"\"\"" ) ^ 0 , 'Interpol.Inside' ) - * K ( P ":" * (1 - S "}:\r" - P "\"\"\"" ) ^ 0 ) ^ -1 - * K ( P "}" , 'String.Interpol' ) -% \end{macrocode} -% -% -% \bigskip -% The following \textsc{lpeg} catches a space (U+0032) and replace it by -% |\l_@@_space_tl|. It will be used in the short strings. Usually, -% |\l_@@_space_tl| will contain a space and therefore there won't be difference. -% However, when the key |show-spaces| is in force, |\\l_@@_space_tl| will -% contain ␣ (U+2423) in order to visualize the spaces. -% \begin{macrocode} -local VisualSpace = P " " * Lc "\\l_@@_space_tl" -% \end{macrocode} -% -% \bigskip -% Now, we define \textsc{lpeg} for the parts of the strings which are \emph{not} -% in the interpolations. -% -% \begin{macrocode} -local SingleShortPureString = - ( K ( ( P "\\'" + P "{{" + P "}}" + 1 - S " {}'" ) ^ 1 ) + VisualSpace ) ^ 1 - -local DoubleShortPureString = - ( K ( ( P "\\\"" + P "{{" + P "}}" + 1 - S " {}\"" ) ^ 1 ) + VisualSpace ) ^ 1 - -local SingleLongPureString = - K ( ( 1 - P "'''" - S "{}'\r" ) ^ 1 ) - -local DoubleLongPureString = - K ( ( 1 - P "\"\"\"" - S " {}\"\r" ) ^ 1 ) -% \end{macrocode} -% -% \bigskip -% The interpolations beginning by |%| (even though there is more modern -% technics now in Python). -% \begin{macrocode} -local PercentInterpol = - K ( P "%" - * ( P "(" * alphanum ^ 1 * P ")" ) ^ -1 - * ( S "-#0 +" ) ^ 0 - * ( digit ^ 1 + P "*" ) ^ -1 - * ( P "." * ( digit ^ 1 + P "*" ) ) ^ -1 - * ( S "HlL" ) ^ -1 - * S "sdfFeExXorgiGauc%" , - 'String.Interpol' - ) -% \end{macrocode} -% -% \bigskip -% We can now define the \textsc{lpeg} for the four kinds of strings. It's not -% possible to use our function~|K| because of the interpolations which must be -% formatted with another \pkg{piton} style that the rest of the -% string.\footnote{The interpolations are formatted with the \pkg{piton} style -% |Interpol.Inside|. The initial value of that style is \texttt{\textbackslash -% @@\_piton:n} wich means that the interpolations are parsed once again by \pkg{piton}.} -% \begin{macrocode} -local SingleShortString = - Lc ( "{\\PitonStyle{String.Short}{" ) - * ( -% \end{macrocode} -% First, we deal with the f-strings of Python, which are prefixed by |f| or |F|. -% \begin{macrocode} - K ( P "f'" + P "F'" ) - * ( SingleShortInterpol + SingleShortPureString ) ^ 0 - * K ( P "'" ) - + -% \end{macrocode} -% Now, we deal with the standard strings of Python, but also the ``raw strings''. -% \begin{macrocode} - K ( P "'" + P "r'" + P "R'" ) - * ( K ( ( P "\\'" + 1 - S " '\r%" ) ^ 1 ) - + VisualSpace - + PercentInterpol - + K ( P "%" ) - ) ^ 0 - * K ( P "'" ) - ) - * Lc ( "}}" ) - -local DoubleShortString = - Lc ( "{\\PitonStyle{String.Short}{" ) - * ( - K ( P "f\"" + P "F\"" ) - * ( DoubleShortInterpol + DoubleShortPureString ) ^ 0 - * K ( P "\"" ) - + - K ( P "\"" + P "r\"" + P "R\"" ) - * ( K ( ( P "\\\"" + 1 - S " \"\r%" ) ^ 1 ) - + VisualSpace - + PercentInterpol - + K ( P "%" ) - ) ^ 0 - * K ( P "\"" ) - ) - * Lc ( "}}" ) - - -local ShortString = SingleShortString + DoubleShortString -% \end{macrocode} -% -% \bigskip -% Of course, it's more complicated for ``longs strings'' because, by definition, -% in Python, those strings may be broken by an end on line (which is catched by -% the \textsc{lpeg} |EOL|). -% \begin{macrocode} -local SingleLongString = - Lc "{\\PitonStyle{String.Long}{" - * ( - K ( S "fF" * P "'''" ) - * ( SingleLongInterpol + SingleLongPureString ) ^ 0 - * Lc "}}" - * ( - EOL - + - Lc "{\\PitonStyle{String.Long}{" - * ( SingleLongInterpol + SingleLongPureString ) ^ 0 - * Lc "}}" - * EOL - ) ^ 0 - * Lc "{\\PitonStyle{String.Long}{" - * ( SingleLongInterpol + SingleLongPureString ) ^ 0 - + - K ( ( S "rR" ) ^ -1 * P "'''" - * ( 1 - P "'''" - P "\r" ) ^ 0 ) - * Lc "}}" - * ( - Lc "{\\PitonStyle{String.Long}{" - * K ( ( 1 - P "'''" - P "\r" ) ^ 0 ) - * Lc "}}" - * EOL - ) ^ 0 - * Lc "{\\PitonStyle{String.Long}{" - * K ( ( 1 - P "'''" - P "\r" ) ^ 0 ) - ) - * K ( P "'''" ) - * Lc "}}" - - -local DoubleLongString = - Lc "{\\PitonStyle{String.Long}{" - * ( - K ( S "fF" * P "\"\"\"" ) - * ( DoubleLongInterpol + DoubleLongPureString ) ^ 0 - * Lc "}}" - * ( - EOL - + - Lc "{\\PitonStyle{String.Long}{" - * ( DoubleLongInterpol + DoubleLongPureString ) ^ 0 - * Lc "}}" - * EOL - ) ^ 0 - * Lc "{\\PitonStyle{String.Long}{" - * ( DoubleLongInterpol + DoubleLongPureString ) ^ 0 - + - K ( ( S "rR" ) ^ -1 * P "\"\"\"" - * ( 1 - P "\"\"\"" - P "\r" ) ^ 0 ) - * Lc "}}" - * ( - Lc "{\\PitonStyle{String.Long}{" - * K ( ( 1 - P "\"\"\"" - P "\r" ) ^ 0 ) - * Lc "}}" - * EOL - ) ^ 0 - * Lc "{\\PitonStyle{String.Long}{" - * K ( ( 1 - P "\"\"\"" - P "\r" ) ^ 0 ) - ) - * K ( P "\"\"\"" ) - * Lc "}}" -% \end{macrocode} -% -% \begin{macrocode} -local LongString = SingleLongString + DoubleLongString -% \end{macrocode} -% -% \bigskip -% We have a \textsc{lpeg} for the Python docstrings. That \textsc{lpeg} will -% be used in the \textsc{lpeg} |DefFunction| which deals with the whole preamble -% of a function definition (which begins with |def|). -% \begin{macrocode} -local StringDoc = - K ( P "\"\"\"" , 'String.Doc' ) - * ( K ( (1 - P "\"\"\"" - P "\r" ) ^ 0 , 'String.Doc' ) * EOL * Tab ^0 ) ^ 0 - * K ( ( 1 - P "\"\"\"" - P "\r" ) ^ 0 * P "\"\"\"" , 'String.Doc' ) -% \end{macrocode} -% -% \bigskip -% \paragraph{The comments in the Python listings} -% -% We define different \textsc{lpeg} dealing with comments in the Python -% listings. -% \begin{macrocode} -local CommentMath = - P "$" * K ( ( 1 - S "$\r" ) ^ 1 , 'Comment.Math' ) * P "$" - -local Comment = - Lc ( "{\\PitonStyle{Comment}{" ) - * K ( P "#" ) - * ( CommentMath + K ( ( 1 - S "$\r" ) ^ 1 ) ) ^ 0 - * Lc ( "}}" ) - * ( EOL + -1 ) -% \end{macrocode} -% -% -% -% \bigskip -% The following \textsc{lpeg} |CommentLaTeX| is for what is called in that -% document the ``LaTeX comments''. Since the elements that will be catched must -% be sent to LaTeX with standard LaTeX catcodes, we put the capture (done by -% the function~|C|) in a table (by using~|Ct|, which is an alias for |lpeg.Ct|). -% \begin{macrocode} -local CommentLaTeX = - P(piton.comment_latex) - * Lc "{\\PitonStyle{Comment.LaTeX}{\\ignorespaces" - * L ( ( 1 - P "\r" ) ^ 0 ) - * Lc "}}" - * ( EOL + -1 ) -% \end{macrocode} -% -% \bigskip -% \paragraph{DefFunction} -% -% The following \textsc{lpeg} |Expression| will be used for the parameters in -% the \emph{argspec} of a Python function. It's necessary to use a \emph{grammar} -% because that pattern mainly checks the correct nesting of the delimiters -% (and it's known in the theory of formal languages that this can't be done with -% regular expressions \emph{stricto sensu} only). -% \begin{macrocode} -local Expression = - P { "E" , - E = ( 1 - S "{}()[]\r," ) ^ 0 - * ( - ( P "{" * V "F" * P "}" - + P "(" * V "F" * P ")" - + P "[" * V "F" * P "]" ) * ( 1 - S "{}()[]\r," ) ^ 0 - ) ^ 0 , - F = ( 1 - S "{}()[]\r\"'" ) ^ 0 - * ( ( - P "'" * (P "\\'" + 1 - S"'\r" )^0 * P "'" - + P "\"" * (P "\\\"" + 1 - S"\"\r" )^0 * P "\"" - + P "{" * V "F" * P "}" - + P "(" * V "F" * P ")" - + P "[" * V "F" * P "]" - ) * ( 1 - S "{}()[]\r\"'" ) ^ 0 ) ^ 0 , - } -% \end{macrocode} -% -% \bigskip -% We will now define a \textsc{lpeg} |Params| that will catch the list of -% parameters (that is to say the \emph{argspec}) in the definition of a Python -% function. For example, in the line of code -% \begin{center} -% \piton{def MyFunction(a,b,x=10,n:int): return n} -% \end{center} -% the \textsc{lpeg} |Params| will be used to catch the chunk\enskip |a,b,x=10,n:int|. -% -% Or course, a |Params| is simply a comma-separated list of |Param|, and that's -% why we define first the \textsc{lpeg} |Param|. -% -% \medskip -% \begin{macrocode} -local Param = - SkipSpace * Identifier * SkipSpace - * ( - K ( P "=" * Expression , 'InitialValues' ) - + K ( P ":" ) * SkipSpace * K ( letter^1 , 'Name.Type' ) - ) ^ -1 -% \end{macrocode} -% -% \medskip -% \begin{macrocode} -local Params = ( Param * ( K "," * Param ) ^ 0 ) ^ -1 -% \end{macrocode} -% -% \bigskip -% The following \textsc{lpeg} |DefFunction| catches a keyword |def| and the -% following name of function \emph{but also everything else until a potential -% docstring}. That's why this definition of \textsc{lpeg} must occur (in the file -% |piton.sty|) after the definition of several other \textsc{lpeg} such as -% |Comment|, |CommentLaTeX|, |Params|, |StringDoc|... -% \begin{macrocode} -local DefFunction = - K ( P "def" , 'Keyword' ) - * Space - * K ( identifier , 'Name.Function' ) - * SkipSpace - * K ( P "(" ) * Params * K ( P ")" ) - * SkipSpace - * ( K ( P "->" ) * SkipSpace * K ( identifier , 'Name.Type' ) ) ^ -1 -% \end{macrocode} -% Here, we need a \pkg{piton} style |Post.Function| which will be linked to -% |\@@_piton:n| (that means that the capture will be parsed once again by -% \pkg{piton}). We could avoid that kind of trick by using a non-terminal of a -% grammar but we have probably here a better legibility. -% \begin{macrocode} - * K ( ( 1 - S ":\r" )^0 , 'Post.Function' ) - * K ( P ":" ) - * ( SkipSpace - * ( EOL + CommentLaTeX + Comment ) -- in all cases, that contains an EOL - * Tab ^ 0 - * SkipSpace - * StringDoc ^ 0 -- there may be additionnal docstrings - ) ^ -1 -% \end{macrocode} -% Remark that, in the previous code, |CommentLaTeX| \emph{must} appear -% before |Comment|: there is no commutativity of the addition for the -% \emph{parsing expression grammars} (\textsc{peg}). -% -% \smallskip -% If the word |def| is not followed by an identifier and parenthesis, it will be -% catched as keyword by the \textsc{lpeg} |Keyword| (useful if, for example, the -% final user wants to speak of the keyword \piton{def}). -% -% \bigskip -% \paragraph{The dictionaries of Python} -% -% We have \textsc{lpeg} dealing with dictionaries of Python because, in -% typesettings of explicit Python dictionnaries, one may prefer to have all the -% values formatted in black (in order to see more clearly the keys which are -% usually Python strings). That's why we have a \pkg{piton} style |Dict.Value|. -% -% The initial value of that \pkg{piton} style is |\@@_piton:n|, which means that -% the value of the entry of the dictionary is parsed once again by \pkg{piton} -% (and nothing special is done for the dictionary). In the following example, we -% have set the \pkg{piton} style |Dict.Value| to |\color{black}|: -% -% \medskip -% \begingroup -% \SetPitonStyle{Dict.Value = \color{black}} -% \piton{mydict = { 'name' : 'Paul', 'sex' : 'male', 'age' : 31 } } -% \endgroup -% -% \medskip -% At this time, this mechanism works only for explicit dictionaries on a single line! -% -% \begin{macrocode} -local ItemDict = - ShortString * SkipSpace * K ( P ":" ) * K ( Expression , 'Dict.Value' ) - -local ItemOfSet = SkipSpace * ( ItemDict + ShortString ) * SkipSpace - -local Set = - K ( P "{" ) - * ItemOfSet * ( K ( P "," ) * ItemOfSet ) ^ 0 - * K ( P "}" ) -% \end{macrocode} -% -% \bigskip -% \paragraph{Commands of Beamer} -% -% -% \begin{macrocode} -local Beamer = P ( "blablabla" ) - -if piton_beamer then - Beamer = - L ( - ( P "\\uncover" + P "\\only" + P "\\alert" + P "\\visible" - + P "\\invisible" + P "\\action" - ) - * P "<" - * (1 - P ">") ^ 0 - * P ">{" - ) - * K ( Expression , 'Beamer' ) - * L ( P "}" ) - + - L ( - ( P "\\alt" ) - * P "<" - * (1 - P ">") ^ 0 - * P ">{" - ) - * K ( Expression , 'Beamer' ) - * L ( P "}{" ) - * K ( Expression , 'Beamer' ) - * L ( P "}" ) -end -% \end{macrocode} -% -% \bigskip -% \paragraph{The main LPEG} -% -% |SyntaxPython| is the main \textsc{lpeg} of the package \pkg{piton}. We have -% written an auxiliary \textsc{lpeg} |SyntaxPythonAux| only for legibility. -% \begin{macrocode} -local SyntaxPythonAux = -% \end{macrocode} -% We recall that each line in the Python code to parse will be sent back to -% LaTeX between a pair |\@@_begin_line:| -- |\@@_end_line:|\footnote{Remember -% that the \texttt{\textbackslash @@\_end\_line:} must be explicit because it -% will be used as marker in order to delimit the argument of the command -% \texttt{\textbackslash @@\_begin\_line:}}. -% \begin{macrocode} - Lc ( '\\@@_begin_line:' ) * - ( ( space - P "\r" ) ^0 * P "\r" ) ^ -1 * - SpaceIndentation ^ 0 * - ( ( space^1 * -1 ) - + EOL - + Tab - + Space - + Escape - + CommentLaTeX - + Beamer - + LongString - + Comment - + ExceptionInConsole - + Set - + Delim -% \end{macrocode} -% |Operator| must be before |Punct|. -% \begin{macrocode} - + Operator - + ShortString - + Punct - + FromImport - + ImportAs - + RaiseException - + DefFunction - + DefClass - + Keyword * ( Space + Punct + Delim + EOL + -1) - + Decorator - + OperatorWord * ( Space + Punct + Delim + EOL + -1) - + Builtin * ( Space + Punct + Delim + EOL + -1) - + Identifier - + Number - + Word - ) ^0 * -1 * Lc ( '\\@@_end_line:' ) -% \end{macrocode} -% -% We have written an auxiliary \textsc{lpeg} |SyntaxPythonAux| for legibility only. -% \begin{macrocode} -local SyntaxPython = Ct ( SyntaxPythonAux ) -% \end{macrocode} -% -% \bigskip -% \subsubsection{The function Parse} -% -% The function |Parse| is the main function of the package \pkg{piton}. -% It parses its argument and sends back to LaTeX the code with interlaced -% formatting LaTeX instructions. In fact, everything is done by the -% \textsc{lpeg} |SyntaxPython| which returns as capture a Lua table containing -% data to send to LaTeX. -% -% \bigskip -% \begin{macrocode} -function piton.Parse(code) - local t = SyntaxPython : match ( code ) -- match is a method of the LPEG - for _ , s in ipairs(t) do tex.tprint(s) end -end -% \end{macrocode} -% -% -% \bigskip -% The following command will be used by the user commands |\piton|. -% For that command, we have to undo the duplication of the symbols |#|. -% \begin{macrocode} -function piton.pitonParse(code) - local s = ( Cs ( ( P '##' / '#' + 1 ) ^ 0 ) ) : match ( code ) - return piton.Parse(s) -end -% \end{macrocode} -% -% \bigskip -% The function |ParseFile| will be used by the LaTeX command |\PitonInputFile|. -% That function merely reads the whole file (that is to say all its lines) and -% then apply the function~|Parse| to the resulting Lua string. -% \begin{macrocode} -function piton.ParseFile(name,first_line,last_line) - s = '' - local i = 0 - for line in io.lines(name) - do i = i + 1 - if i >= first_line - then s = s .. '\r' .. line - end - if i >= last_line then break end - end - piton.Parse(s) -end -% \end{macrocode} -% -% \bigskip -% \subsubsection{The preprocessors of the function Parse} -% -% We deal now with preprocessors of the function |Parse| which are needed when -% the ``gobble mechanism'' is used. -% -% -% \bigskip -% The function |gobble| gobbles $n$ characters on the left of the code. It uses -% a \textsc{lpeg} that we have to compute dynamically because if depends on the -% value of~$n$. -% \begin{macrocode} -local function gobble(n,code) - function concat(acc,new_value) - return acc .. new_value - end - if n==0 - then return code - else - return Cf ( - Cc ( "" ) * - ( 1 - P "\r" ) ^ (-n) * C ( ( 1 - P "\r" ) ^ 0 ) - * ( C ( P "\r" ) - * ( 1 - P "\r" ) ^ (-n) - * C ( ( 1 - P "\r" ) ^ 0 ) - ) ^ 0 , - concat - ) : match ( code ) - end -end -% \end{macrocode} -% -% -% -% \bigskip -% The following function |add| will be used in the following \textsc{lpeg} -% |AutoGobbleLPEG|, |TabsAutoGobbleLPEG| and |EnvGobbleLPEG|. -% \begin{macrocode} -local function add(acc,new_value) - return acc + new_value -end -% \end{macrocode} -% -% \bigskip -% The following \textsc{lpeg} returns as capture the minimal number of spaces at -% the beginning of the lines of code. The main work is done by two \emph{fold -% captures} (|lpeg.Cf|), one using |add| and the other (encompassing the -% previous one) using |math.min| as folding operator. -% \begin{macrocode} -local AutoGobbleLPEG = - ( space ^ 0 * P "\r" ) ^ -1 - * Cf ( - ( -% \end{macrocode} -% We don't take into account the empty lines (with only spaces). -% \begin{macrocode} - ( P " " ) ^ 0 * P "\r" - + - Cf ( Cc(0) * ( P " " * Cc(1) ) ^ 0 , add ) - * ( 1 - P " " ) * ( 1 - P "\r" ) ^ 0 * P "\r" - ) ^ 0 -% \end{macrocode} -% Now for the last line of the Python code... -% \begin{macrocode} - * - ( Cf ( Cc(0) * ( P " " * Cc(1) ) ^ 0 , add ) - * ( 1 - P " " ) * ( 1 - P "\r" ) ^ 0 ) ^ -1 , - math.min - ) -% \end{macrocode} -% -% \bigskip -% The following \textsc{lpeg} is similar but works with the indentations. -% \begin{macrocode} -local TabsAutoGobbleLPEG = - ( space ^ 0 * P "\r" ) ^ -1 - * Cf ( - ( - ( P "\t" ) ^ 0 * P "\r" - + - Cf ( Cc(0) * ( P "\t" * Cc(1) ) ^ 0 , add ) - * ( 1 - P "\t" ) * ( 1 - P "\r" ) ^ 0 * P "\r" - ) ^ 0 - * - ( Cf ( Cc(0) * ( P "\t" * Cc(1) ) ^ 0 , add ) - * ( 1 - P "\t" ) * ( 1 - P "\r" ) ^ 0 ) ^ -1 , - math.min - ) -% \end{macrocode} -% -% -% -% \bigskip -% The following \textsc{lpeg} returns as capture the number of spaces at the -% last line, that is to say before the |\end{Piton}| (and usually it's also the -% number of spaces before the corresponding |\begin{Piton}| because that's the -% traditionnal way to indent in LaTeX). The main work is done by a \emph{fold -% capture} (|lpeg.Cf|) using the function |add| as folding operator. -% \begin{macrocode} -local EnvGobbleLPEG = - ( ( 1 - P "\r" ) ^ 0 * P "\r" ) ^ 0 - * Cf ( Cc(0) * ( P " " * Cc(1) ) ^ 0 , add ) * -1 -% \end{macrocode} -% -% \bigskip -% \begin{macrocode} -function piton.GobbleParse(n,code) - if n==-1 - then n = AutoGobbleLPEG : match(code) - else if n==-2 - then n = EnvGobbleLPEG : match(code) - else if n==-3 - then n = TabsAutoGobbleLPEG : match(code) - end - end - end - piton.Parse(gobble(n,code)) -end -% \end{macrocode} -% -% \bigskip -% \subsubsection{To count the number of lines} -% -% \medskip -% \begin{macrocode} -function piton.CountLines(code) - local count = 0 - for i in code : gmatch ( "\r" ) do count = count + 1 end - tex.sprint( - luatexbase.catcodetables.expl , - '\\int_set:Nn \\l_@@_nb_lines_int {' .. count .. '}' ) -end -% \end{macrocode} -% -% \begin{macrocode} -function piton.CountNonEmptyLines(code) - local count = 0 - count = - ( Cf ( Cc(0) * - ( - ( P " " ) ^ 0 * P "\r" - + ( 1 - P "\r" ) ^ 0 * P "\r" * Cc(1) - ) ^ 0 - * (1 - P "\r" ) ^ 0 , - add - ) * -1 ) : match (code) - tex.sprint( - luatexbase.catcodetables.expl , - '\\int_set:Nn \\l_@@_nb_non_empty_lines_int {' .. count .. '}' ) -end -% \end{macrocode} -% -% \bigskip -% \begin{macrocode} -function piton.CountLinesFile(name) - local count = 0 - for line in io.lines(name) do count = count + 1 end - tex.sprint( - luatexbase.catcodetables.expl , - '\\int_set:Nn \\l_@@_nb_lines_int {' .. count .. '}' ) -end -% \end{macrocode} -% -% -% \bigskip -% \begin{macrocode} -function piton.CountNonEmptyLinesFile(name) - local count = 0 - for line in io.lines(name) - do if not ( ( ( P " " ) ^ 0 * -1 ) : match ( line ) ) - then count = count + 1 - end - end - tex.sprint( - luatexbase.catcodetables.expl , - '\\int_set:Nn \\l_@@_nb_non_empty_lines_int {' .. count .. '}' ) -end -% \end{macrocode} -% -% -% \bigskip -% \begin{macrocode} -\end{luacode*} -% \end{macrocode} -% -% \vspace{1cm} -% \section{History} -% \subsection*{Changes between versions 0.99 and 1.0} -% -% The extension \pkg{piton} detects the class \cls{beamer} and activates the -% commands |\action|, |\alert|, |\invisible|, |\only|, |\uncover| and |\visible| -% in the environments |{Piton}| when the class \cls{beamer} is used. -% -% \subsection*{Changes between versions 0.99 and 1.0} -% -% New key |tabs-auto-gobble|. -% -% \subsection*{Changes between versions 0.95 and 0.99} -% -% New key |break-lines| to allow breaks of the lines of code (and other keys to -% customize the appearance). -% -% \subsection*{Changes between versions 0.9 and 0.95} -% -% New key |show-spaces|. -% -% The key |left-margin| now accepts the special value |auto|. -% -% New key |latex-comment| at load-time and replacement of |##| by |#>| -% -% New key |math-comments| at load-time. -% -% New keys |first-line| and |last-line| for the command |\InputPitonFile|. -% -% \subsection*{Changes between versions 0.8 and 0.9} -% -% New key |tab-size|. -% -% Integer value for the key |splittable|. -% -% \subsection*{Changes between versions 0.7 and 0.8} -% -% New keys |footnote| and |footnotehyper| at load-time. -% -% New key |left-margin|. -% -% \subsection*{Changes between versions 0.6 and 0.7} -% -% New keys |resume|, |splittable| and |background-color| in |\PitonOptions|. -% -% The file |piton.lua| has been embedded in the file |piton.sty|. That means -% that the extension \pkg{piton} is now entirely contained in the file |piton.sty|. -% -% -% -% -% -% \end{document} -% -% -% Local Variables: -% TeX-fold-mode: t -% TeX-fold-preserve-comments: nil -% flyspell-mode: nil -% fill-column: 80 -% End: - - - - - - +% \iffalse -*- coding: utf-8 ; -*- \fi \iffalse meta-comment
+%
+% Copyright (C) 2023 by F. Pantigny
+% -----------------------------------
+%
+% This file may be distributed and/or modified under the
+% conditions of the LaTeX Project Public License, either version 1.3
+% of this license or (at your option) any later version.
+% The latest version of this license is in:
+%
+% http://www.latex-project.org/lppl.txt
+%
+% and version 1.3 or later is part of all distributions of LaTeX
+% version 2005/12/01 or later.
+%
+% \fi
+% \iffalse
+\def\myfileversion{1.2}
+\def\myfiledate{2023/01/16}
+%
+%
+%<*batchfile>
+\begingroup
+\input l3docstrip.tex
+\keepsilent
+\usedir{tex/latex/cascade}
+\preamble
+
+Copyright (C) 2023 by F. Pantigny
+-----------------------------------
+
+This file may be distributed and/or modified under the
+conditions of the LaTeX Project Public License, either version 1.3
+of this license or (at your option) any later version.
+The latest version of this license is in:
+
+http://www.latex-project.org/lppl.txt
+
+and version 1.3 or later is part of all distributions of LaTeX
+version 2005/12/01 or later.
+
+\endpreamble
+\askforoverwritefalse
+\endgroup
+%</batchfile>
+%
+%<@@=piton>
+%<*driver>
+\documentclass{l3doc}
+\usepackage{geometry}
+\geometry{left=2.8cm,right=2.8cm,top=2.5cm,bottom=2.5cm,papersize={21cm,29.7cm}}
+\usepackage{fontspec}
+\usepackage[dvipsnames]{xcolor}
+\usepackage{caption,tabularx,tcolorbox}
+\def\emphase{\bgroup\color{RoyalPurple}\let\next=}
+\fvset{commandchars=\~\#\@,formatcom=\color{gray}}
+\captionsetup{labelfont = bf}
+\usepackage{ragged2e}
+\usepackage[footnotehyper,escape-inside=$$,math-comments]{piton} % $$
+
+\parindent 0pt
+\skip\footins = 2\bigskipamount
+
+\PitonOptions{gobble=2}
+
+\EnableCrossrefs
+
+\begin{document}
+\DocInput{piton.dtx}
+\end{document}
+%</driver>
+
+% \fi
+%
+% \catcode`\" = 11
+%
+% \title{The package \pkg{piton}\thanks{This document corresponds to the
+% version~\myfileversion\space of \pkg{piton}, at the date of~\myfiledate.}}
+% \author{F. Pantigny \\ \texttt{fpantigny@wanadoo.fr}}
+%
+% \maketitle
+%
+% \begin{abstract}
+% The package \pkg{piton} provides tools to typeset Python listings with
+% syntactic highlighting by using the Lua library LPEG. It requires LuaLaTeX.
+% \end{abstract}
+%
+%
+%
+% \section{Presentation}
+%
+%
+% The package \pkg{piton} uses the Lua library LPEG\footnote{LPEG is a
+% pattern-matching library for Lua, written in C, based on \emph{parsing
+% expression grammars}: \url{http://www.inf.puc-rio.br/~roberto/lpeg/}} for
+% parsing Python listings and typeset them with syntactic highlighting. Since it
+% uses Lua code, it works with |lualatex| only (and won't work with the other
+% engines: |latex|, |pdflatex| and |xelatex|). It does not use external program
+% and the compilation does not require |--shell-escape|. The compilation is very
+% fast since all the parsing is done by the library LPEG, written in C.
+%
+% \bigskip
+% Here is an example of code typeset by \pkg{piton}, with the environment |{Piton}|.
+%
+% \bigskip
+%
+% \begin{Piton}
+% from math import pi
+%
+% def arctan(x,n=10):
+% """Compute the mathematical value of arctan(x)
+%
+% n is the number of terms in the sum
+% """
+% if x < 0:
+% return -arctan(-x) # recursive call
+% elif x > 1:
+% return pi/2 - arctan(1/x)
+% #> (we have used that $\arctan(x)+\arctan(1/x)=\frac{\pi}{2}$ for $x>0$)\footnote{This LaTeX escape has been done by beginning the comment by \ttfamily\#>.}
+% else:
+% s = 0
+% for k in range(n):
+% s += (-1)**k/(2*k+1)*x**(2*k+1)
+% return s
+% \end{Piton}
+%
+%
+% \bigskip
+%
+% The package \pkg{piton} is entirely contained in the file
+% |piton.sty|. This file may be put in the current directory or in a
+% |texmf| tree. However, the best is to install \pkg{piton} with a TeX
+% distribution such as MiKTeX, TeX Live or MacTeX.
+%
+%
+% \section{Use of the package}
+%
+%
+% \subsection{Loading the package}
+%
+% The package \pkg{piton} should be loaded with the classical command
+% |\usepackage|: |\usepackage{piton}|.
+%
+% \smallskip
+% Nevertheless, we have two remarks:
+% \begin{itemize}
+% \item the package \pkg{piton} uses the package \pkg{xcolor} (but \pkg{piton}
+% does \emph{not} load \pkg{xcolor}: if \pkg{xcolor} is not loaded before the
+% |\begin{document}|, a fatal error will be raised).
+% \item the package \pkg{piton} must be used with LuaLaTeX exclusively: if
+% another LaTeX engine (|latex|, |pdflatex|, |xelatex|,\dots ) is used, a
+% fatal error will be raised.
+% \end{itemize}
+%
+% \subsection{The tools provided to the user}
+%
+% The package \pkg{piton} provides several tools to typeset Python code: the
+% command |\piton|, the environment |{Piton}| and the command |\PitonInputFile|.
+%
+% \begin{itemize}\setlength{\fboxsep}{1pt}
+% \item The command \colorbox{gray!20}{\texttt\textbackslash piton} should be
+% used to typeset small pieces of code inside a paragraph. For example:
+%
+% {\color{gray}\verb|\piton{def square(x): return x*x}|}\qquad
+% \piton{def square(x): return x*x}
+%
+% The syntax and particularities of the command |\piton| are detailed below.
+%
+% \item The environment \colorbox{gray!20}{\ttfamily\{Piton\}} should be used to
+% typeset multi-lines code. Since it takes its argument in a verbatim mode, it
+% can't be used within the argument of a LaTeX command. For sake of
+% customization, it's possible to define new environments similar to the
+% environment |{Piton}| with the command |\NewPitonEnvironment|:
+% cf.~\ref{NewPitonEnvironment} p.~\pageref{NewPitonEnvironment}.
+%
+% \item The command \colorbox{gray!20}{\ttfamily\textbackslash PitonInputFile}
+% is used to insert and typeset a whole external file.
+%
+% That command takes in as optional argument (between square brackets) two keys
+% |first-line| and |last-line|: only the part between the corresponding lines
+% will be inserted.
+% \end{itemize}
+%
+% \subsection{The syntax of the command \textbackslash piton}
+%
+% In fact, the command |\piton| is provided with a double syntax. It may be used
+% as a standard command of LaTeX taking its argument between curly braces
+% (|\piton{...}|) but it may also be used with a syntax similar to the syntax of
+% the command
+% |\verb|, that is to say with the argument delimited by two identical characters (e.g.: \verb!\piton|...|!).
+%
+% \begin{itemize}
+% \item {\color{blue} \textsf{Syntax} \verb|\piton{...}|}\par\nobreak
+% When its argument is given between curly braces, the command |\piton| does not
+% take its argument in verbatim mode. In particular:
+% \begin{itemize}
+% \item several consecutive spaces will be replaced by only one space;
+% \item it's not possible to use |%| inside the argument;
+% \item the braces must be appear by pairs correctly nested;
+% \item the LaTeX commands (those beginning with a backslash |\| but also the
+% active characters) are fully expanded (but not executed).
+% \end{itemize}
+%
+% An escaping mechanism is provided: the commands |\\|, |\%|, |\{| and |\}|
+% insert the corresponding characters |\|, |%|, |{| and |}|. The last two
+% commands are necessary only if one need to insert braces which are not
+% balanced.
+%
+% The other characters (including |#|, |^|, |_|, |&|, |$| and |@|)
+% must be inserted without backslash.
+%
+% Examples:
+%
+%\begin{tabular}{>{\color{gray}}l@{\hspace*{1cm}}l}
+% \verb|\piton{MyString = '\\n'}| &
+% \piton{MyString = '\\n'} \\
+% \verb|\piton{def even(n): return n\%2==0}| &
+% \piton{def even(n): return n\%2==0} \\
+% \verb|\piton{c="#" # an affectation }| &
+% \piton{c="#" # an affectation } \\
+% \verb|\piton{MyDict = {'a': 3, 'b': 4 }}| &
+% \piton{MyDict = {'a': 3, 'b': 4 }}
+% \end{tabular}
+%
+%
+% It's possible to use the command |\piton| in the arguments of a
+% LaTeX command.\footnote{For example, it's possible to use the command
+% \texttt{\textbackslash piton} in a footnote. Example :
+% \piton{s = 'A string'}.}
+%
+% \bigskip
+% \item {\color{blue} \textsf{Syntaxe} \verb!\piton|...|!}\par\nobreak
+%
+% When the argument of the command |\piton| is provided between two identical
+% characters, that argument is taken in a \emph{verbatim mode}. Therefore, with
+% that syntax, the command |\piton| can't be used within the argument of another
+% command.
+%
+% \medskip
+% Examples:
+%
+% \begin{tabular}{>{\color{gray}}l@{\hspace*{1cm}}l}
+% \verb!\piton|MyString = '\n'|! &
+% \piton|MyString = '\n'| \\
+% \verb|\piton!def even(n): return n%2==0!| &
+% \piton!def even(n): return n%2==0! \\
+% \verb|\piton+c="#" # an affectation +| &
+% \piton+c="#" # an affectation + \\
+% \verb|\piton?MyDict = {'a': 3, 'b': 4}?| &
+% \piton!MyDict = {'a': 3, 'b': 4}!
+% \end{tabular}
+%
+% \end{itemize}
+%
+% \section{Customization}
+%
+% \subsection{The command \textbackslash PitonOptions}
+%
+%
+% \NewDocumentCommand{\Definition}{m}
+% {{\setlength{\fboxsep}{1pt}\colorbox{gray!20}{\ttfamily \vphantom{gl}#1}}}
+%
+% The command |\PitonOptions| takes in as argument a comma-separated list of
+% \textsl{key=value} pairs. The scope of the settings done by that command is
+% the current TeX group.\footnote{We remind that a LaTeX environment is, in
+% particular, a TeX group.}
+%
+% \begin{itemize}
+% \item The key \Definition{gobble} takes in as value a
+% positive integer~$n$: the first $n$ characters are discarded (before the
+% process of highlightning of the code) for each line of the environment
+% |{Piton}|. These characters are not necessarily spaces.
+%
+% \item When the key \Definition{auto-gobble} is in force, the
+% extension \pkg{piton} computes the minimal value $n$ of the number of
+% consecutive spaces beginning each (non empty) line of the environment
+% |{Piton}| and applies |gobble| with that value of~$n$.
+%
+% \item When the key \Definition{env-gobble} is in force, \pkg{piton} analyzes the last
+% line of the environment |{Piton}|, that is to say the line which contains
+% |\end{Piton}| and determines whether that line contains only spaces followed
+% by the |\end{Piton}|. If we are in that situation, \pkg{piton} computes the
+% number~$n$ of spaces on that line and applies |gobble| with that value of~$n$.
+% The name of that key comes from \emph{environment gobble}: the effect of
+% gobble is set by the position of the commands |\begin{Piton}| and
+% |\end{Piton}| which delimit the current environment.
+%
+% \item With the key \Definition{line-numbers}, the \emph{non empty} lines (and
+% all the lines of the \emph{docstrings}, even the empty ones) are numbered in
+% the environments |{Piton}| and in the listings resulting from the use of
+% |\PitonInputFile|.
+%
+% \item With the key \Definition{all-line-numbers}, \emph{all} the lines are numbered,
+% including the empty ones.
+%
+% \item With the key \Definition{resume} the counter of lines is not set to zero
+% at the beginning of each environment |{Piton}| or use of |\PitonInputFile| as
+% it is otherwise. That allows a numbering of the lines across several
+% environments.
+%
+% \item The key \Definition{left-margin} corresponds to a margin on the left.
+% That key may be useful in conjonction with the key |line-numbers| or the key
+% |line-all-numbers| if one does not want the numbers in an overlapping position
+% on the left.
+%
+% It's possible to use the key |left-margin| with the value |auto|. With that
+% value, if the key |line-numbers| or the key |all-line-numbers| is used, a
+% margin will be automatically inserted to fit the numbers of lines. See an
+% example part \ref{example-numbering} on page~\pageref{example-numbering}.
+%
+% \item The key \Definition{background-color} sets the background color of the
+% environments |{Piton}| and the listings produced by |\PitonInputFile| (that
+% background has a width of |\linewidth|).
+%
+% \item \colorbox{yellow!50}{\textbf{Modified 1.2}}\enskip When the key
+% \Definition{show-spaces-in-strings} is activated, the spaces in the short strings (that
+% is to say those delimited by |'| or |"|) are replaced by the character~␣
+% (U+2423 : \textsc{open box}). Of course, that character~U+2423 must be present
+% in the monospaced font which is used.\footnote{The package \pkg{piton} simply
+% uses the current monospaced font. The best way to change that font is to use
+% the command \texttt{\textbackslash setmonofont} of
+% \pkg{fontspec}.}\par\nobreak \begingroup \PitonOptions{show-spaces-in-strings}
+% Example : \piton|my_string = 'Very good answer'| \endgroup
+%
+% With the key \Definition{show-spaces}, all the spaces are replaced by U+2423 (and no line
+% break can occur on those ``visible spaces'', even when the key |break-lines|
+% is in force).
+% \end{itemize}
+%
+% \bigskip
+%
+% \begingroup
+% \fvset{commandchars=\~\&\@,formatcom=\small\color{gray}}
+% \begin{Verbatim}
+% ~emphase&\PitonOptions{line-numbers,auto-gobble,background-color = gray!15}@
+% \begin{Piton}
+% from math import pi
+% def arctan(x,n=10):
+% """Compute the mathematical value of arctan(x)
+%
+% n is the number of terms in the sum
+% """
+% if x < 0:
+% return -arctan(-x) # recursive call
+% elif x > 1:
+% return pi/2 - arctan(1/x)
+% #> (we have used that $\arctan(x)+\arctan(1/x)=\frac{\pi}{2}$ pour $x>0$)
+% else
+% s = 0
+% for k in range(n):
+% s += (-1)**k/(2*k+1)*x**(2*k+1)
+% return s
+% \end{Piton}
+% \end{Verbatim}
+% \endgroup
+%
+% \begingroup
+% \PitonOptions{line-numbers,background-color = gray!15}
+% \begin{Piton}
+% from math import pi
+%
+% def arctan(x,n=10):
+% """Compute the mathematical value of arctan(x)
+%
+% n is the number of terms in the sum
+% """
+% if x < 0:
+% return -arctan(-x) # recursive call
+% elif x > 1:
+% return pi/2 - arctan(1/x)
+% #> (we have used that $\arctan(x)+\arctan(1/x)=\frac{\pi}{2}$ for $x>0$)
+% else
+% s = 0
+% for k in range(n):
+% s += (-1)**k/(2*k+1)*x**(2*k+1)
+% return s
+% \end{Piton}
+% \endgroup
+%
+% \bigskip
+% The command |\PitonOptions| provides in fact several other keys which will be
+% described further (see in particular the ``Pages breaks and line breaks''
+% p.~\pageref{breakable}).
+%
+% \subsection{The styles}
+%
+% \label{styles}
+%
+% The package \pkg{piton} provides the command |\SetPitonStyle| to customize the
+% different styles used to format the syntactic elements of the Python listings.
+% The customizations done by that command are limited to the current TeX
+% group.\footnote{We remind that an LaTeX environment is, in particular, a TeX group.}
+%
+% \bigskip
+% The command |\SetPitonStyle| takes in as argument a comma-separated list of
+% \textsl{key=value} pairs. The keys are names of styles and the value are LaTeX
+% formatting instructions.
+%
+% \bigskip
+% These LaTeX instructions must be formatting instructions such as
+% |\color{...}|, |\bfseries|, |\slshape|, etc. (the commands of this kind are
+% sometimes called \emph{semi-global} commands). It's also possible to put,
+% \emph{at the end of the list of instructions}, a LaTeX command taking exactly
+% one argument.
+%
+% \bigskip
+% Here an example which changes the style used to highlight, in the definition
+% of a Python function, the name of the function which is defined.
+%
+% \begin{verbatim}
+% \SetPitonStyle
+% { Name.Function = \bfseries \setlength{\fboxsep}{1pt}\colorbox{yellow!50} }
+% \end{verbatim}
+%
+% In that example, |\colorbox{yellow!50}| must be considered as the name of a
+% LaTeX command which takes in exactly one argument, since, usually, it is used
+% with the syntax |\colorbox{yellow!50}{...}|.
+%
+% \medskip
+% \begingroup
+% \SetPitonStyle
+% { Name.Function = \bfseries \setlength{\fboxsep}{1pt}\colorbox{yellow!50} }
+% With that setting, we will have : \piton{def cube(x) : return x * x * x }
+% \endgroup
+%
+% \bigskip
+% The different styles are described in the table \ref{Semantic}. The initial
+% settings done by \pkg{piton} in |piton.sty| are inspired by the style
+% \pkg{manni} de Pygments.\footnote{See: \url{https://pygments.org/styles/}.
+% Remark that, by default, Pygments provides for its style \pkg{manni} a colored
+% background whose color is the HTML color \texttt{\#F0F3F3}.}
+%
+%
+%
+% \begin{table}[b]
+% \centering
+% \caption{Usage of the different styles}
+% \label{Semantic}
+% \begin{tabular}{@{}>{\ttfamily}l>{\RaggedRight\arraybackslash}p{9cm}@{}}
+% \toprule
+% \normalfont Style & Usage \\
+% \midrule
+% Number & the numbers \\
+% String.Short & the short strings (between \texttt{'} or \verb|"|) \\
+% String.Long & the long strings (between \texttt{'''} or \verb|"""|) except the
+% documentation strings \\
+% String & that keys sets both |String.Short| and |String.Long| \\
+% String.Doc & the documentation strings (only between \texttt{"""} following
+% PEP~257) \\
+% String.Interpol & the syntactic elements of the fields of the f-strings (that
+% is to say the characters \texttt{\{} and \texttt{\}}) \\
+% Operator & the following operators : \texttt{!= == << >> - \~{} + / * \% = < > \& .} \verb+|+ \verb|@|\\
+% Operator.Word & the following operators : \texttt{in}, \texttt{is},
+% \texttt{and}, \texttt{or} and \texttt{not} \\
+% Name.Builtin & the predefined functions of Python \\
+% Name.Function & the name of the functions defined by the user, at the point of
+% their definition (that is to say after the keyword |def|) \\
+% Name.Decorator & the decorators (instructions beginning by \verb|@|) \\
+% Name.Namespace & the name of the modules (= external libraries) \\
+% Name.Class & the name of the classes at the point of their definition (that is
+% to say after the keyword |class|) \\
+% Exception & the names of the exceptions (eg: \texttt{SyntaxError}) \\
+% Comment & the comments beginning with \verb|#| \\
+% Comment.LaTeX & the comments beginning by \verb|#>|, which are composed in LaTeX by
+% \pkg{piton} (and simply called ``LaTeX comments'' in this document) \\
+% Keyword.Constant & |True|, |False| and |None| \\
+% Keyword & the following keywords :
+% \ttfamily as, assert, break, case, continue, def, del,
+% elif, else, except, exec, finally, for, from,
+% global, if, import, lambda, non local,
+% pass, raise, return, try, while,
+% with, yield, yield from.\\
+% \bottomrule
+% \end{tabular}
+% \end{table}
+%
+%
+%
+%
+% \subsection{Creation of new environments}
+%
+% \label{NewPitonEnvironment}
+%
+% Since the environment |{Piton}| has to catch its body in a special way (more
+% or less as verbatim text), it's not possible to construct new environments
+% directly over the environment |{Piton}| with the classical commands
+% |\newenvironment| or |\NewDocumentEnvironment|.
+%
+% That's why \pkg{piton} provides a command |\NewPitonEnvironment|. That
+% command takes in three mandatory arguments.
+
+% That command has the same syntax as the classical environment
+% |\NewDocumentEnvironment|.
+%
+% \bigskip
+% With the following instruction, a new environment |{Python}| will be
+% constructed with the same behaviour as |{Piton}|:
+%
+% {\color{gray}\verb|\NewPitonEnvironment{Python}{}{}{}|}
+%
+% \bigskip
+% If one wishes an environment |{Python}| with takes in as optional argument
+% (between square brackets) the keys of the command |\PitonOptions|, it's
+% possible to program as follows:
+%
+% {\color{gray}\verb|\NewPitonEnvironment{Python}{O{}}{\PitonOptions{#1}}{}|}
+%
+% \bigskip
+% If one wishes to format Python code in a box of \pkg{tcolorbox}, it's possible
+% to define an environment |{Python}| with the following code:
+%
+%\begin{verbatim}
+% \NewPitonEnvironment{Python}{}
+% {\begin{tcolorbox}}
+% {\end{tcolorbox}}
+% \end{verbatim}
+%
+\bigskip
+% With this new environment |{Python}|, it's possible to write:
+%
+% \begin{Verbatim}
+% ~emphase#\begin{Python}@
+% def square(x):
+% """Compute the square of a number"""
+% return x*x
+% ~emphase#\end{Python}@
+% \end{Verbatim}
+%
+% \NewPitonEnvironment{Python}{}
+% {\begin{tcolorbox}}
+% {\end{tcolorbox}}
+%
+% \begin{Python}
+% def square(x):
+% """Compute the square of a number"""
+% return x*x
+% \end{Python}
+%
+%
+% \section{Advanced features}
+%
+% \subsection{Mechanisms to escape to LaTeX}
+%
+% The package \pkg{piton} provides several mechanisms for escaping to LaTeX:
+% \begin{itemize}
+% \item It's possible to compose comments entirely in LaTeX.
+% \item It's possible to have the elements between \texttt{\$} in the comments
+% composed in LateX mathematical mode.
+% \item It's also possible to insert LaTeX code almost everywhere in a Python listing.
+% \end{itemize}
+%
+% \subsubsection{The ``LaTeX comments''}
+%
+% In this document, we call ``LaTeX comments'' the comments which begins by
+% |#>|. The code following those characters, until the end of the line, will be
+% composed as standard LaTeX code. There is two tools to customize those
+% comments.
+%
+% \begin{itemize}
+% \item It's possible to change the syntatic mark (which, by default, is |#>|).
+% For this purpose, there is a key |comment-latex| available at load-time (that
+% is to say at the |\usepackage|) which allows to choice the characters which,
+% preceded by |#|, will be the syntatic marker.
+%
+% For example, with the following loading:
+%
+% \quad \verb|\usepackage[comment-latex = LaTeX]{piton}|
+%
+% the LaTeX comments will begin by |#LaTeX|.
+%
+% If the key |comment-latex| is used with the empty value, all the Python
+% comments (which begins by |#|) will, in fact, be ``LaTeX comments''.
+%
+% \smallskip
+% \item It's possible to change the formatting of the LaTeX comment itself by
+% changing the \pkg{piton} style |Comment.LaTeX|.
+%
+% For example, with |\SetPitonStyle{Comment.LaTeX = \normalfont\color{blue}}|,
+% the LaTeX comments will be composed in blue.
+%
+% If you want to have a character |#| at the beginning of the LaTeX comment in
+% the \textsc{pdf}, you can use set |Comment.LaTeX| as follows:
+%
+% \begin{Verbatim}
+% \SetPitonStyle{Comment.LaTeX = \color{gray}\~#\normalfont\space }
+% \end{Verbatim}
+%
+% For other examples of customization of the LaTeX comments, see the part
+% \ref{example-comments} p.~\pageref{example-comments}
+% \end{itemize}
+%
+% \subsubsection{The key ``math-comments''}
+%
+% It's possible to request that, in the standard Python comments (that is to say
+% those beginning by |#| and not |#>|), the elements between \texttt{\$} be
+% composed in LaTeX mathematical mode (the other elements of the comment being
+% composed verbatim).
+%
+% That feature is activated by the key |math-comments| at load-time (that is to
+% say with the |\usepackage|).
+%
+% \medskip
+% In the following example, we assume that the key |math-comments| has been used
+% when loading \pkg{piton}.
+%
+% \begin{Verbatim}
+% \begin{Piton}
+% def square(x):
+% return x*x ~# compute $x^2$
+% \end{Piton}
+% \end{Verbatim}
+%
+% \begin{Piton}
+% def square(x):
+% return x*x # compute $x^2$
+% \end{Piton}
+%
+% \subsubsection{The mechanism ``escape-inside''}
+%
+% It's also possible to overwrite the Python listings to insert LaTeX code
+% almost everywhere (but between lexical units, of course). By default,
+% \pkg{piton} does not fix any character for that kind of escape.
+%
+% In order to use this mechanism, it's necessary to specify two characters which
+% will delimit the escape (one for the beginning and one for the end) by using
+% the key |escape-inside| at load-time (that is to say at the
+% |\begin{docuemnt}|).
+%
+% \medskip
+% In the following example, we assume that the extension \pkg{piton} has been
+% loaded by the following instruction.
+%
+% \begin{Verbatim}
+% \usepackage[~emphase#escape-inside=$$@]{piton}
+% \end{Verbatim}
+%
+% \medskip
+% In the following code, which is a recursive programmation of the mathematical
+% factorial, we decide to highlight in yellow the instruction which contains the
+% recursive call.
+% \begin{Verbatim}
+% \begin{Piton}
+% def fact(n):
+% if n==0:
+% return 1
+% else:
+% ~emphase#$\colorbox{yellow!50}{$@return n*fact(n-1)~emphase#$}$@
+% \end{Piton}
+% \end{Verbatim}
+%
+% \begin{Piton}
+% def fact(n):
+% if n==0:
+% return 1
+% else:
+% $\colorbox{yellow!50}{$return n*fact(n-1)$}$
+% \end{Piton}
+%
+% \bigskip
+%
+% \emph{Caution} : The escape to LaTeX allowed by the characters of
+% |escape-inside| is not active in the strings nor in the Python comments
+% (however, it's possible to have a whole Python comment composed in LaTeX by
+% beginning it with |#>|; such comments are merely called ``LaTeX comments'' in
+% this document).
+%
+%
+% \subsubsection{Behaviour in the class Beamer}
+%
+% \colorbox{yellow!50}{\textbf{New 1.1}}
+%
+% \smallskip
+% When \pkg{piton} is used in the class \cls{beamer}\footnote{The extension
+% \pkg{piton} detects the class \cls{beamer} but, if needed, it's also possible
+% to activate that mechanism with the key |beamer| provided by \pkg{piton} at
+% load-time: |\textbackslash usepackage[beamer]\{piton\}|} , the following
+% commands of \cls{beamer}, classified upon their number of their number of
+% arguments, are automatically detected in the environments |{Piton}| :
+% \begin{itemize}
+% \item no mandatory argument : |\pause| ;
+% \item one mandatory argument : |\action|, |\alert|, |\invisible|, |\only|, |\uncover| and |\visible| ;
+% \item two mandatory arguments : |\alt| ;
+% \item three mandatory arguments : |\temporal|.
+% \end{itemize}
+
+% \medskip
+% However, there is two restrictions for the content of the mandatory arguments
+% of these commands.
+% \begin{itemize}
+% \item In the mandatory arguments of these commands, the braces must be
+% balanced. However, the braces includes in short strings\footnote{The
+% short strings of Python are the strings delimited by characters \texttt{'} or
+% the characters \texttt{"} and not \texttt{'''} nor \texttt{"""}. In Python,
+% the short strings can't extend on several lines.} of Python are not considered.
+% \item The must be \textbf{no carriage return} in the mandatory arguments of the
+% command (if there is, a fatal error will be raised).
+% \end{itemize}
+%
+% \medskip
+% Remark that, since the environment |{Piton}| catches its body with a verbatim
+% mode, it's necessary to use the environments |{Piton}| within environments
+% |{frame}| of Beamer protected by the key |fragile|.\footnote{Remind that for an
+% environment \texttt{\{frame\}} of Beamer using the key |fragile|, the instruction
+% \texttt{\textbackslash end\{frame\}} must be alone on a single line (except for any leading whitespace).}
+%
+% \medskip
+% Here is a complete example of file:
+%
+% \begin{Verbatim}[formatcom = \small\color{gray}]
+% \documentclass{beamer}
+% \usepackage{piton}
+% \begin{document}
+% \begin{frame}[fragile]
+% \begin{Piton}
+% def string_of_list(l):
+% """Convert a list of numbers in string"""
+% ~emphase# \only<2->{s = "{" + str(l[0])}@
+% ~emphase# \only<3->{for x in l[1:]: s = s + "," + str(x)}@
+% ~emphase# \only<4->{s = s + "}"}@
+% return s
+% \end{Piton}
+% \end{frame}
+% \end{document}
+% \end{Verbatim}
+%
+% In the previous example, the braces in the Python strings |"{"| and |"}"| are
+% correctly interpreted (without any escape character).
+%
+% \bigskip
+% \colorbox{yellow!50}{\textbf{New 1.2}}
+%
+% When \pkg{piton} is used in the class \pkg{beamer}, the following environments
+% of Beamer are directly detected in the environments |{Piton}|: |{uncoverenv}|,
+% |{onlyenv}|, |{visibleenv}| and |{invisibleenv}|.
+%
+% However, there is a restriction: these environments must contain only \emph{whole
+% lines of Python code} in their body.
+%%
+%\medskip
+% Here is an example:
+%
+% \begin{Verbatim}[formatcom = \small\color{gray}]
+% \documentclass{beamer}
+% \usepackage{piton}
+% \begin{document}
+% \begin{frame}[fragile]
+% \begin{Piton}
+% def square(x):
+% """Compure the square of its argument"""
+% ~emphase#\begin{uncoverenv}<2>@
+% return x*x
+% ~emphase#\end{uncoverenv}@
+% \end{Piton}
+% \end{frame}
+% \end{document}
+% \end{Verbatim}
+%
+%
+% \subsection{Page breaks and line breaks}
+%
+% \label{breakable}
+%
+% \subsubsection{Page breaks}
+%
+% By default, the listings produced by the environment |{Piton}| and the command
+% |\PitonInputFile| are not breakable.
+%
+% However, the command |\PitonOptions| provides the key |splittable| to allow such
+% breaks.
+%
+% \begin{itemize}
+% \item If the key |splittable| is used without any value, the listings are
+% breakable everywhere.
+% \item If the key |splittable| is used with a numeric value~$n$ (which must be
+% a non-negative integer number), the listings are breakable but no break will
+% occur within the first $n$ lines and within the last $n$ lines. Therefore,
+% |splittable=1| is equivalent to |splittable|.
+% \end{itemize}
+%
+% \medskip
+% Even with a background color (set by the key |background-color|), the pages
+% breaks are allowed, as soon as the key |splittable| is in force.\footnote{With
+% the key |splittable|, the environments \texttt{\{Piton\}} are breakable, even
+% within a (breakable) environment of \pkg{tcolorbox}. Remind that an
+% environment of \pkg{tcolorbox} included in another environment of
+% \pkg{tcolorbox} is \emph{not} breakable, even when both environments use the
+% key |breakable| of \pkg{tcolorbox}.}
+%
+% \subsubsection{Line breaks}
+%
+% By default, the elements produced by \pkg{piton} can't be broken by an end on
+% line. However, there are keys to allow such breaks (the possible breaking
+% points are the spaces, even the spaces in the Python strings).
+% \begin{itemize}
+% \item With the key |break-lines-in-piton|, the line breaks are allowed in the
+% command |\piton{...}| (but not in the command \verb+\piton|...|+, that is to say
+% the command |\piton| in verbatim mode).
+% \item With the key |break-lines-in-Piton|, the line breaks are allowed in the
+% environment |{Piton}| (hence the capital letter |P| in the name) and in the
+% listings produced by |\PitonInputFile|.
+% \item The key |break-lines| is a conjonction of the two previous keys.
+% \end{itemize}
+%
+% \medskip
+% \colorbox{yellow!50}{\textbf{Nouveau 1.2}}\enskip Depuis la version 1.2, la
+% clé |break-lines| autorise les coupures de lignes dans |\piton{...}| et pas
+% seulement dans |{Piton}|.
+
+% \bigskip
+% The package \pkg{piton} provides also several keys to control the appearance
+% on the line breaks allowed by |break-lines-in-Piton|.
+%
+% \begin{itemize}
+% \item With the key |indent-broken-lines|, the indentation of a broken line is
+% respected at carriage return.
+%
+% \item The key |end-of-broken-line| corresponds to the symbol placed at the end
+% of a broken line. The initial value is: |\hspace*{0.5em}\textbackslash|.
+%
+% \item The key |continuation-symbol| corresponds to the symbol placed at each
+% carriage return. The initial value is: |+\;|.
+%
+% \item The key |continuation-symbol-on-indentation| coresponds to the symbol
+% placed at each carriage return, on the position of the indentation (only when
+% the key |indent-broken-line| is in force). The initial value is:
+% |$\hookrightarrow\;$|.
+% \end{itemize}
+%
+%
+% \bigskip
+% The following code has been composed in a |{minipage}| of width 12~cm with the
+% following tuning:
+%
+% \begin{Verbatim}
+% \PitonOptions{break-lines,indent-broken-lines,background-color=gray!15}
+% \end{Verbatim}
+%
+% \begin{center}
+% \PitonOptions{break-lines,indent-broken-lines,background-color=gray!15}
+% \begin{minipage}{12cm}
+% \begin{Piton}
+% def dict_of_list(l):
+% """Converts a list of subrs and descriptions of glyphs in a dictionary"""
+% our_dict = {}
+% for list_letter in l:
+% if (list_letter[0][0:3] == 'dup'): # if it's a subr
+% name = list_letter[0][4:-3]
+% print("We treat the subr of number " + name)
+% else:
+% name = list_letter[0][1:-3] # if it's a glyph
+% print("We treat the glyph of number " + name)
+% our_dict[name] = [treat_Postscript_line(k) for k in list_letter[1:-1]]
+% return dict
+% \end{Piton}
+% \end{minipage}
+% \end{center}
+%
+% \subsection{Footnotes in the environments of piton}
+%
+% \label{footnote}
+% If you want to put footnotes in an environment |{Piton}| or
+% (or, more unlikely, in a listing produced by |\PitonInputFile|), you can use a
+% pair |\footnotemark|--|\footnotetext|.
+%
+% \smallskip
+% However, it's also possible to extract the footnotes with the help of the
+% package \pkg{footnote} or the package \pkg{footnotehyper}.
+%
+% \smallskip
+% If \pkg{piton} is loaded with the option |footnote| (with
+% |\usepackage[footnote]{piton}| or with |\PassOptionsToPackage|), the
+% package \pkg{footnote} is loaded (if it is not yet loaded) and it is used to
+% extract the footnotes.
+%
+% \smallskip
+% If \pkg{piton} is loaded with the option |footnotehyper|, the package
+% \pkg{footnotehyper} is loaded (if it is not yet loaded) ant it is used to
+% extract footnotes.
+%
+% \smallskip
+% Caution: The packages \pkg{footnote} and \pkg{footnotehyper} are incompatible.
+% The package \pkg{footnotehyper} is the successor of the package \pkg{footnote}
+% and should be used preferently. The package \pkg{footnote} has some drawbacks,
+% in particular: it must be loaded after the package \pkg{xcolor} and it is not
+% perfectly compatible with \pkg{hyperref}.
+%
+% \medskip
+% In this document, the package \pkg{piton} has been loaded with the
+% option |footnotehyper|. For examples of notes, cf. \ref{notes-examples},
+% p.~\pageref{notes-examples}.
+%
+% \subsection{Tabulations}
+%
+% \smallskip
+% Even though it's recommended to indent the Python listings with spaces (see
+% PEP~8), \pkg{piton} accepts the characters of tabulation (that is to say the
+% characters U+0009) at the beginning of the lines. Each character U+0009 is
+% replaced by $n$~spaces. The initial value of $n$ is $4$ but it's possible to
+% change it with the key |tab-size| of |\PitonOptions|.
+%
+% \smallskip
+% There exists also a key |tabs-auto-gobble| which computes the minimal value
+% $n$ of the number of consecutive characters U+0009 beginning each (non empty)
+% line of the environment |{Piton}| and applies |gobble| with that value of~$n$
+% (before replacement of the tabulations by spaces, of course). Hence, that key
+% is similar to the key |auto-gobble| but acts on U+0009 instead of U+0020
+% (spaces).
+%
+% \section{Examples}
+%
+% \subsection{Line numbering}
+%
+% \label{example-numbering}
+%
+% We remind that it's possible to have an automatic numbering of the lines in
+% the Python listings by using the key |line-numbers| or the key |all-line-numbers|.
+%
+% By default, the numbers of the lines are composed by \pkg{piton} in an
+% overlapping position on the left (by using internally the command |\llap| of LaTeX).
+%
+% In order to avoid that overlapping, it's possible to use the option |left-margin=auto|
+% which will insert automatically a margin adapted to the numbers of lines that
+% will be written (that margin is larger when the numbers are greater than~10).
+%
+%
+% \begingroup
+% \fvset{commandchars=\~\&\@,formatcom=\small\color{gray}}
+% \begin{Verbatim}
+% ~emphase&\PitonOptions{background-color=gray!10, left-margin = auto, line-numbers}@
+% \begin{Piton}
+% def arctan(x,n=10):
+% if x < 0:
+% return -arctan(-x) #> (appel récursif)
+% elif x > 1:
+% return pi/2 - arctan(1/x) #> (autre appel récursif)
+% else:
+% return sum( (-1)**k/(2*k+1)*x**(2*k+1) for k in range(n) )
+% \end{Piton}
+% \end{Verbatim}
+% \endgroup
+%
+%
+%
+% \begingroup
+% \PitonOptions{background-color=gray!10,left-margin = auto, line-numbers}
+% \begin{Piton}
+% def arctan(x,n=10):
+% if x < 0:
+% return -arctan(-x) #> (appel récursif)
+% elif x > 1:
+% return pi/2 - arctan(1/x) #> (autre appel récursif)
+% else:
+% return sum( (-1)**k/(2*k+1)*x**(2*k+1) for k in range(n) )
+% \end{Piton}
+% \endgroup
+%
+%
+%
+% \bigskip
+% \subsection{Formatting of the LaTeX comments}
+%
+% \label{example-comments}
+%
+% It's possible to modify the style |Comment.LaTeX| (with |\SetPitonStyle|) in
+% order to display the LaTeX comments (which begin with |#>|) aligned on the
+% right margin.
+%
+%
+% \begingroup
+% \fvset{commandchars=\~\&\@,formatcom=\small\color{gray}}
+% \begin{Verbatim}
+% \PitonOptions{background-color=gray!10}
+% ~emphase&\SetPitonStyle{Comment.LaTeX = \hfill \normalfont\color{gray}}@
+% \begin{Piton}
+% def arctan(x,n=10):
+% if x < 0:
+% return -arctan(-x) #> appel récursif
+% elif x > 1:
+% return pi/2 - arctan(1/x) #> autre appel récursif
+% else:
+% return sum( (-1)**k/(2*k+1)*x**(2*k+1) for k in range(n) )
+% \end{Piton}
+% \end{Verbatim}
+% \endgroup
+%
+% \begingroup
+% \PitonOptions{background-color=gray!10}
+% \SetPitonStyle{Comment.LaTeX = \hfill \normalfont\color{gray}}
+% \begin{Piton}
+% def arctan(x,n=10):
+% if x < 0:
+% return -arctan(-x) #> appel récursif
+% elif x > 1:
+% return pi/2 - arctan(1/x) #> autre appel récursif
+% else:
+% return sum( (-1)**k/(2*k+1)*x**(2*k+1) for k in range(n) )
+% \end{Piton}
+% \endgroup
+%
+%
+% \vspace{1cm}
+% It's also possible to display these LaTeX comments in a kind of second column
+% by limiting the width of the Python code by an environment |{minipage}| of LaTeX.
+%
+%
+% \begingroup
+% \fvset{commandchars=\~\&\@,formatcom=\small\color{gray}}
+% \begin{Verbatim}
+% \PitonOptions{background-color=gray!10}
+% ~emphase&\NewDocumentCommand{\MyLaTeXCommand}{m}{\hfill \normalfont\itshape\rlap{\quad #1}}@
+% ~emphase&\SetPitonStyle{Comment.LaTeX = \MyLaTeXCommand}@
+% ~emphase&\begin{minipage}{12cm}@
+% \begin{Piton}
+% def arctan(x,n=10):
+% if x < 0:
+% return -arctan(-x) #> appel récursif
+% elif x > 1:
+% return pi/2 - arctan(1/x) #> autre appel récursif
+% else:
+% s = 0
+% for k in range(n):
+% s += (-1)**k/(2*k+1)*x**(2*k+1)
+% return s
+% \end{Piton}
+% ~emphase&\end{minipage}@
+% \end{Verbatim}
+% \endgroup
+%
+%
+%
+% \begingroup
+% \PitonOptions{background-color=gray!10}
+% \NewDocumentCommand{\MyLaTeXCommand}{m}{\hfill \normalfont\itshape\rlap{\quad #1}}
+% \SetPitonStyle{Comment.LaTeX = \MyLaTeXCommand}
+% \begin{minipage}{12cm}
+% \begin{Piton}
+% def arctan(x,n=10):
+% if x < 0:
+% return -arctan(-x) #> appel récursif
+% elif x > 1:
+% return pi/2 - arctan(1/x) #> autre appel récursif
+% else:
+% s = 0
+% for k in range(n):
+% s += (-1)**k/(2*k+1)*x**(2*k+1)
+% return s
+% \end{Piton}
+% \end{minipage}
+% \endgroup
+%
+%
+% \bigskip
+% \subsection{Notes in the listings}
+%
+% \label{notes-examples}
+%
+% In order to be able to extract the notes (which are typeset with the command
+% |\footnote|), the extension \pkg{piton} must be loaded with the key |footnote|
+% or the key |footenotehyper| as explained in the section \ref{footnote}
+% p.~\pageref{footnote}. In this document, the extension \pkg{piton} has been
+% loaded with the key |footnotehyper|.
+%
+% Of course, in an environment |{Piton}|, a command |\footnote| may appear only
+% within a LaTeX comment (which begins with |#>|). It's possible to have comments
+% which contain only that command |\footnote|. That's the case in the following example.
+%
+%
+%
+% \begingroup
+% \fvset{commandchars=\~\&\@,formatcom=\small\color{gray}}
+% \begin{Verbatim}
+% \PitonOptions{background-color=gray!10}
+% \begin{Piton}
+% def arctan(x,n=10):
+% if x < 0:
+% return -arctan(-x)~emphase&#>\footnote{First recursive call.}]@
+% elif x > 1:
+% return pi/2 - arctan(1/x)~emphase&#>\footnote{Second recursive call.}@
+% else:
+% return sum( (-1)**k/(2*k+1)*x**(2*k+1) for k in range(n) )
+% \end{Piton}
+% \end{Verbatim}
+% \endgroup
+%
+% \begingroup
+% \PitonOptions{background-color=gray!10}
+% \begin{Piton}
+% def arctan(x,n=10):
+% if x < 0:
+% return -arctan(-x)#>\footnote{First recursive call.}
+% elif x > 1:
+% return pi/2 - arctan(1/x)#>\footnote{Second recursive call.}
+% else:
+% return sum( (-1)**k/(2*k+1)*x**(2*k+1) for k in range(n) )
+% \end{Piton}
+% \endgroup
+%
+%
+% \vspace{1cm}
+%
+% If an environment |{Piton}| is used in an environment |{minipage}| of LaTeX,
+% the notes are composed, of course, at the foot of the environment
+% |{minipage}|. Recall that such |{minipage}| can't be broken by a page break.
+
+%
+% \begingroup
+% \fvset{commandchars=\~\&\@,formatcom=\small\color{gray}}
+% \begin{Verbatim}
+% \PitonOptions{background-color=gray!10}
+% \emphase\begin{minipage}{\linewidth}
+% \begin{Piton}
+% def arctan(x,n=10):
+% if x < 0:
+% return -arctan(-x)~emphase&#>\footnote{First recursive call.}@
+% elif x > 1:
+% return pi/2 - arctan(1/x)~emphase&#>\footnote{Second recursive call.}@
+% else:
+% return sum( (-1)**k/(2*k+1)*x**(2*k+1) for k in range(n) )
+% \end{Piton}
+% \end{minipage}
+% \end{Verbatim}
+% \endgroup
+%
+% \begingroup
+% \PitonOptions{background-color=gray!10}
+% \begin{minipage}{\linewidth}
+% \begin{Piton}
+% def arctan(x,n=10):
+% if x < 0:
+% return -arctan(-x)#>\footnote{First recursive call.}
+% elif x > 1:
+% return pi/2 - arctan(1/x)#>\footnote{Second recursive call.}
+% else:
+% return sum( (-1)**k/(2*k+1)*x**(2*k+1) for k in range(n) )
+% \end{Piton}
+% \end{minipage}
+% \endgroup
+%
+%
+% \vspace{1cm}
+% If we embed an environment |{Piton}| in an environment |{minipage}|
+% (typically in order to limit the width of a colored background), it's
+% necessary to embed the whole environment |{minipage}| in an environment
+% |{savenotes}| (of \pkg{footnote} or \pkg{footnotehyper}) in order to have the
+% footnotes composed at the bottom of the page.
+%
+% \begingroup
+% \fvset{commandchars=\~\&\@,formatcom=\small\color{gray}}
+% \begin{Verbatim}
+% \PitonOptions{background-color=gray!10}
+% ~emphase&\begin{savenotes}@
+% \begin{minipage}{13cm}
+% \begin{Piton}
+% def arctan(x,n=10):
+% if x < 0:
+% return -arctan(-x)~emphase&#>\footnote{First recursive call.}@
+% elif x > 1:
+% return pi/2 - arctan(1/x)~emphase&#>\footnote{Second recursive call.}@
+% else:
+% return sum( (-1)**k/(2*k+1)*x**(2*k+1) for k in range(n) )
+% \end{Piton}
+% \end{minipage}
+% ~emphase&\end{savenotes}@
+% \end{Verbatim}
+% \endgroup
+%
+%
+%
+% \begin{savenotes}
+% \PitonOptions{background-color=gray!10}
+% \begin{minipage}{13cm}
+% \begin{Piton}
+% def arctan(x,n=10):
+% if x < 0:
+% return -arctan(-x)#>\footnote{First recursive call.}
+% elif x > 1:
+% return pi/2 - arctan(1/x)#>\footnote{Second recursive call.}
+% else:
+% return sum( (-1)**k/(2*k+1)*x**(2*k+1) for k in range(n) )
+% \end{Piton}
+% \end{minipage}
+% \end{savenotes}
+%
+% \bigskip
+%
+% \subsection{An example of tuning of the styles}
+%
+% The graphical styles have been presented in the section \ref{styles},
+% p.~\pageref{styles}.
+%
+% \smallskip
+% We present now an example of tuning of these styles adapted to the documents
+% in black and white. We use the font \emph{DejaVu Sans Mono}\footnote{See:
+% \url{https://dejavu-fonts.github.io}} specified by the command |\setmonofont| of
+% \pkg{fontspec}.
+%
+%
+% \begin{Verbatim}
+% \setmonofont[Scale=0.85]{DejaVu Sans Mono}
+%
+% \SetPitonStyle
+% {
+% Number = ,
+% String = \itshape ,
+% String.Doc = \color{gray} \slshape ,
+% Operator = ,
+% Operator.Word = \bfseries ,
+% Name.Builtin = ,
+% Name.Function = \bfseries \colorbox{gray!20} ,
+% Comment = \color{gray} ,
+% Comment.LaTeX = \normalfont \color{gray},
+% Keyword = \bfseries ,
+% Name.Namespace = ,
+% Name.Class = ,
+% Name.Type = ,
+% InitialValues = \color{gray}
+% }
+% \end{Verbatim}
+%
+%
+% \begingroup
+%
+% \setmonofont[Scale=0.85]{DejaVu Sans Mono}
+%
+% \PitonOptions{splittable}
+%
+% \SetPitonStyle
+% {
+% Number = ,
+% String = \itshape ,
+% String.Doc = \color{gray} \slshape ,
+% Operator.Word = \bfseries ,
+% Operator = ,
+% Name.Builtin = ,
+% Name.Function = \bfseries \colorbox{gray!20} ,
+% Comment = \color{gray} ,
+% Comment.LaTeX = \normalfont \color{gray} ,
+% Keyword = \bfseries ,
+% Name.Namespace = ,
+% Name.Class = ,
+% Name.Type = ,
+% InitialValues = \color{gray}
+% }
+%
+%
+% \bigskip
+%
+% \begin{Piton}
+% from math import pi
+%
+% def arctan(x,n=10):
+% """Compute the mathematical value of arctan(x)
+%
+% n is the number of terms in the sum
+% """
+% if x < 0:
+% return -arctan(-x) # appel récursif
+% elif x > 1:
+% return pi/2 - arctan(1/x)
+% #> (we have used that $\arctan(x)+\arctan(1/x)=\pi/2$ for $x>0$)
+% else:
+% s = 0
+% for k in range(n):
+% s += (-1)**k/(2*k+1)*x**(2*k+1)
+% return s
+% \end{Piton}
+%
+% \endgroup
+%
+% \subsection{Use with pyluatex}
+%
+% The package \pkg{pyluatex} is an extension which allows the execution of some
+% Python code from |lualatex| (provided that Python is installed on the machine
+% and that the compilation is done with |lualatex| and |--shell-escape|).
+%
+% Here is, for example, an environment |{PitonExecute}| which formats a Python
+% listing (with \pkg{piton}) but display also the output of the execution of the
+% code with Python.
+%
+%
+% \begin{Verbatim}
+% \ExplSyntaxOn
+% \NewDocumentEnvironment { ~emphase#PitonExecute@ } { ! O { } }
+% {
+% \PyLTVerbatimEnv
+% \begin{pythonq}
+% }
+% {
+% \end{pythonq}
+% \directlua
+% {
+% tex.print("\\PitonOptions{~#1}")
+% tex.print("\\begin{Piton}")
+% tex.print(pyluatex.get_last_code())
+% tex.print("\\end{Piton}")
+% tex.print("")
+% }
+% \begin{center}
+% \directlua{tex.print(pyluatex.get_last_output())}
+% \end{center}
+% }
+% \ExplSyntaxOff
+% \end{Verbatim}
+%
+% \ExplSyntaxOn
+% \NewDocumentEnvironment { PitonExecute } { ! O { } }
+% {
+% \PyLTVerbatimEnv
+% \begin{pythonq}
+% }
+% {
+% \end{pythonq}
+% \directlua
+% {
+% tex.print("\\PitonOptions{~#1}")
+% tex.print("\\begin{Piton}")
+% tex.print(pyluatex.get_last_code())
+% tex.print("\\end{Piton}")
+% tex.print("")
+% }
+% \begin{center}
+% \directlua{tex.print(pyluatex.get_last_output())}
+% \end{center}
+% }
+% \ExplSyntaxOff
+%
+% This environment |{PitonExecute}| takes in as optional argument (between
+% square brackets) the options of the command |\PitonOptions|.
+%
+%
+% \bigskip
+% \clearpage
+%
+% \section{Implementation}
+%
+% \subsection{Introduction}
+%
+% The main job of the package \pkg{piton} is to take in as input a Python
+% listing and to send back to LaTeX as output that code \emph{with interlaced LaTeX
+% instructions of formatting}.
+%
+% In fact, all that job is done by a \textsc{lpeg} called |SyntaxPython|. That
+% \textsc{lpeg}, when matched against the string of a Python listing,
+% returns as capture a Lua table containing data to send to LaTeX.
+% The only thing to do after will be to apply |tex.tprint| to each element of
+% that table.\footnote{Recall that |tex.tprint| takes in as argument a Lua table whose
+% first component is a ``catcode table'' and the second element a string. The
+% string will be sent to LaTeX with the regime of catcodes specified by the
+% catcode table. If no catcode table is provided, the standard catcodes of LaTeX
+% will be used.}
+%
+% \bigskip
+% Consider, for example, the following Python code:
+%
+% \begin{Piton}
+% def parity(x):
+% return x%2
+% \end{Piton}
+%
+% The capture returned by the \pkg{lpeg} |SyntaxPython| against that code is the
+% Lua table containing the following elements :
+%
+% \bigskip
+% \begin{minipage}{\linewidth}
+% \color{gray}
+%
+% |{ "\\__piton_begin_line:" }|\footnote{Each line of the Python listings will
+% be encapsulated in a pair: \texttt{\textbackslash_@@_begin_line:} --
+% \texttt{\textbackslash@@_end_line:}. The token
+% \texttt{\textbackslash@@_end_line:} must be explicit because it will be used as
+% marker in order to delimit the argument of the command \texttt{\textbackslash
+% @@\_begin\_line:}. Both tokens \texttt{\textbackslash_@@_begin_line:} and
+% \texttt{\textbackslash@@_end_line:} will be nullified in the command
+% \texttt{\textbackslash piton} (since there can't be lines breaks in the
+% argument of a command \texttt{\textbackslash piton}).}
+%
+% \texttt{\{ "\{\textbackslash PitonStyle\{Keyword\}\{" \}}\footnote{The
+% lexical elements of Python for which we have a \pkg{piton} style will be
+% formatted via the use of the command \texttt{\textbackslash PitonStyle}.
+% Such an element is typeset in LaTeX via the syntax \texttt{\{\textbackslash
+% PitonStyle\{\textsl{style}\}\{...\}\}} because the instructions inside an \texttt{\textbackslash
+% PitonStyle} may be both semi-global declarations like
+% \texttt{\textbackslash bfseries} and commands with one argument like
+% \texttt{\textbackslash fbox}.}
+%
+% \texttt{\{
+% luatexbase.catcodetables.CatcodeTableOther\footnote{\texttt{luatexbase.catcodetables.CatcodeTableOther} is a mere number which corresponds to the ``catcode table'' whose all characters have the catcode ``other'' (which means that they will be typeset by LaTeX verbatim).}, "def" \} }
+%
+% |{ "}}" }|
+%
+% |{ luatexbase.catcodetables.CatcodeTableOther, " " }|
+%
+% |{ "{\PitonStyle{Name.Function}{" }|
+%
+% |{ luatexbase.catcodetables.CatcodeTableOther, "parity" }|
+%
+% |{ "}}" }|
+%
+% |{ luatexbase.catcodetables.CatcodeTableOther, "(" }|
+%
+% |{ luatexbase.catcodetables.CatcodeTableOther, "x" }|
+%
+% |{ luatexbase.catcodetables.CatcodeTableOther, ")" }|
+%
+% |{ luatexbase.catcodetables.CatcodeTableOther, ":" }|
+%
+% |{ "\\__piton_end_line: \\__piton_newline: \\__piton_begin_line:" }|
+%
+% |{ luatexbase.catcodetables.CatcodeTableOther, " " }|
+%
+% |{ "{\PitonStyle{Keyword}{" }|
+%
+% |{ luatexbase.catcodetables.CatcodeTableOther, "return" }|
+%
+% |{ "}}" }|
+%
+% |{ luatexbase.catcodetables.CatcodeTableOther, " " }|
+%
+% |{ luatexbase.catcodetables.CatcodeTableOther, "x" }|
+%
+% |{ "{\PitonStyle{Operator}{" }|
+%
+% |{ luatexbase.catcodetables.CatcodeTableOther, "&" }|
+%
+% |{ "}}" }|
+%
+% |{ "{\PitonStyle{Number}{" }|
+%
+% |{ luatexbase.catcodetables.CatcodeTableOther, "2" }|
+%
+% |{ "}}" }|
+%
+% |{ "\\__piton_end_line:" }|
+%
+% \end{minipage}
+%
+% \bigskip
+% We give now the LaTeX code which is sent back by Lua to TeX (we have written
+% on several lines for legibility but no character |\r| will be sent to LaTeX). The
+% characters which are greyed-out are sent to LaTeX with the catcode ``other''
+% (=12). All the others characters are sent with the regime of catcodes of L3
+% (as set by |\ExplSyntaxOn|)
+%
+%
+% \begingroup
+% \def\gbox#1{\colorbox{gray!20}{\strut #1}}
+% \setlength{\fboxsep}{1pt}
+%
+% \begin{Verbatim*}[formatcom = \color{black}]
+% \__piton_begin_line:{\PitonStyle{Keyword}{~gbox#def@}}
+% ~gbox# @{\PitonStyle{Name.Function}{~gbox#parity@}}~gbox#(x):@\__piton_end_line:\__piton_newline:
+% \__piton_begin_line:~gbox# @{\PitonStyle{Keyword}{~gbox#return@}}
+% ~gbox# x@{\PitonStyle{Operator}{~gbox#%@}}{\PitonStyle{Number}{~gbox#2@}}\__piton_end_line:
+% \end{Verbatim*}
+% \endgroup
+%
+%
+%
+%
+% \subsection{The L3 part of the implementation}
+%
+% \subsubsection{Declaration of the package}
+% \begin{macrocode}
+\NeedsTeXFormat{LaTeX2e}
+\RequirePackage{l3keys2e}
+\ProvidesExplPackage
+ {piton}
+ {\myfiledate}
+ {\myfileversion}
+ {Highlight Python codes with LPEG on LuaLaTeX}
+% \end{macrocode}
+%
+% \bigskip
+% \begin{macrocode}
+\msg_new:nnn { piton } { LuaLaTeX~mandatory }
+ { The~package~'piton'~must~be~used~with~LuaLaTeX.\\ It~won't~be~loaded. }
+\sys_if_engine_luatex:F { \msg_critical:nn { piton } { LuaLaTeX~mandatory } }
+% \end{macrocode}
+%
+% \bigskip
+% \begin{macrocode}
+\RequirePackage { luatexbase }
+% \end{macrocode}
+%
+% \bigskip
+% The boolean |\c_@@_footnotehyper_bool| will indicate if the option
+% |footnotehyper| is used.
+% \begin{macrocode}
+\bool_new:N \c_@@_footnotehyper_bool
+% \end{macrocode}
+%
+% \medskip
+% The boolean |\c_@@_footnote_bool| will indicate if the option |footnote| is
+% used, but quicky, it will also be set to |true| if the option |footnotehyper|
+% is used.
+% \begin{macrocode}
+\bool_new:N \c_@@_footnote_bool
+% \end{macrocode}
+%
+% \medskip
+% The following boolean corresponds to the key |math-comments| (only at load-time).
+% \begin{macrocode}
+\bool_new:N \c_@@_math_comments_bool
+% \end{macrocode}
+%
+% \medskip
+% The following boolean corresponds to the key |beamer|.
+% \begin{macrocode}
+\bool_new:N \c_@@_beamer_bool
+% \end{macrocode}
+%
+% \bigskip
+% We define a set of keys for the options at load-time.
+% \begin{macrocode}
+\keys_define:nn { piton / package }
+ {
+ footnote .bool_set:N = \c_@@_footnote_bool ,
+ footnotehyper .bool_set:N = \c_@@_footnotehyper_bool ,
+ escape-inside .tl_set:N = \c_@@_escape_inside_tl ,
+ escape-inside .initial:n = ,
+ comment-latex .code:n = { \lua_now:n { comment_latex = "#1" } } ,
+ comment-latex .value_required:n = true ,
+ math-comments .bool_set:N = \c_@@_math_comments_bool ,
+ math-comments .default:n = true ,
+ beamer .bool_set:N = \c_@@_beamer_bool ,
+ beamer .default:n = true ,
+ unknown .code:n = \msg_error:nn { piton } { unknown~key~for~package }
+ }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\msg_new:nnn { piton } { unknown~key~for~package }
+ {
+ Unknown~key.\\
+ You~have~used~the~key~'\l_keys_key_str'~but~the~only~keys~available~here~
+ are~'beamer',~'comment-latex',~'escape-inside',~'footnote',~'footnotehyper'~and~
+ 'math-comments'.~Other~keys~are~available~in~\token_to_str:N \PitonOptions.\\
+ That~key~will~be~ignored.
+ }
+% \end{macrocode}
+%
+% \bigskip
+% We process the options provided by the user at load-time.
+% \begin{macrocode}
+\ProcessKeysOptions { piton / package }
+% \end{macrocode}
+%
+% \bigskip
+% \begin{macrocode}
+\begingroup
+\cs_new_protected:Npn \@@_set_escape_char:nn #1 #2
+ {
+ \lua_now:n { piton_begin_escape = "#1" }
+ \lua_now:n { piton_end_escape = "#2" }
+ }
+\cs_generate_variant:Nn \@@_set_escape_char:nn { x x }
+\@@_set_escape_char:xx
+ { \tl_head:V \c_@@_escape_inside_tl }
+ { \tl_tail:V \c_@@_escape_inside_tl }
+\endgroup
+% \end{macrocode}
+%
+% \bigskip
+% \begin{macrocode}
+\@ifclassloaded { beamer } { \bool_set_true:N \c_@@_beamer_bool } { }
+\bool_if:NT \c_@@_beamer_bool { \lua_now:n { piton_beamer = true } }
+% \end{macrocode}
+%
+% \bigskip
+% \begin{macrocode}
+\hook_gput_code:nnn { begindocument } { . }
+ {
+ \@ifpackageloaded { xcolor }
+ { }
+ { \msg_fatal:nn { piton } { xcolor~not~loaded } }
+ }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\msg_new:nnn { piton } { xcolor~not~loaded }
+ {
+ xcolor~not~loaded \\
+ The~package~'xcolor'~is~required~by~'piton'.\\
+ This~error~is~fatal.
+ }
+% \end{macrocode}
+%
+%
+% \begin{macrocode}
+\msg_new:nnn { piton } { footnote~with~footnotehyper~package }
+ {
+ Footnote~forbidden.\\
+ You~can't~use~the~option~'footnote'~because~the~package~
+ footnotehyper~has~already~been~loaded.~
+ If~you~want,~you~can~use~the~option~'footnotehyper'~and~the~footnotes~
+ within~the~environments~of~piton~will~be~extracted~with~the~tools~
+ of~the~package~footnotehyper.\\
+ If~you~go~on,~the~package~footnote~won't~be~loaded.
+ }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\msg_new:nnn { piton } { footnotehyper~with~footnote~package }
+ {
+ You~can't~use~the~option~'footnotehyper'~because~the~package~
+ footnote~has~already~been~loaded.~
+ If~you~want,~you~can~use~the~option~'footnote'~and~the~footnotes~
+ within~the~environments~of~piton~will~be~extracted~with~the~tools~
+ of~the~package~footnote.\\
+ If~you~go~on,~the~package~footnotehyper~won't~be~loaded.
+ }
+% \end{macrocode}
+%
+% \medskip
+% \begin{macrocode}
+\bool_if:NT \c_@@_footnote_bool
+ {
+% \end{macrocode}
+% The class \cls{beamer} has its own system to extract footnotes and that's why
+% we have nothing to do if \cls{beamer} is used.
+% \begin{macrocode}
+ \@ifclassloaded { beamer }
+ { \bool_set_false:N \c_@@_footnote_bool }
+ {
+ \@ifpackageloaded { footnotehyper }
+ { \@@_error:n { footnote~with~footnotehyper~package } }
+ { \usepackage { footnote } }
+ }
+ }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\bool_if:NT \c_@@_footnotehyper_bool
+ {
+% \end{macrocode}
+% The class \cls{beamer} has its own system to extract footnotes and that's why
+% we have nothing to do if \cls{beamer} is used.
+% \begin{macrocode}
+ \@ifclassloaded { beamer }
+ { \bool_set_false:N \c_@@_footnote_bool }
+ {
+ \@ifpackageloaded { footnote }
+ { \@@_error:n { footnotehyper~with~footnote~package } }
+ { \usepackage { footnotehyper } }
+ \bool_set_true:N \c_@@_footnote_bool
+ }
+ }
+% \end{macrocode}
+% The flag |\c_@@_footnote_bool| is raised and so, we will only have to test
+% |\c_@@_footnote_bool| in order to know if we have to insert an environment
+% |{savenotes}|.
+%
+% \bigskip
+% \subsubsection{Parameters and technical definitions}
+%
+% We will compute (with Lua) the numbers of lines of the Python code and store
+% it in the following counter.
+% \begin{macrocode}
+\int_new:N \l_@@_nb_lines_int
+% \end{macrocode}
+%
+% The same for the number of non-empty lines of the Python codes.
+% \begin{macrocode}
+\int_new:N \l_@@_nb_non_empty_lines_int
+% \end{macrocode}
+%
+% The following counter will be used to count the lines during the composition.
+% It will count all the lines, empty or not empty. It won't be used to print the
+% numbers of the lines.
+% \begin{macrocode}
+\int_new:N \g_@@_line_int
+% \end{macrocode}
+%
+% \medskip
+% The following token list will contains the (potential) informations to write
+% on the |aux| (to be used in the next compilation).
+% \begin{macrocode}
+\tl_new:N \g_@@_aux_tl
+% \end{macrocode}
+%
+% \medskip
+% The following counter corresponds to the key |splittable| of |\PitonOptions|.
+% If the value of |\l_@@_splittable_int| is equal to $n$, then no line break can
+% occur within the first $n$~lines or the last $n$~lines of the listings.
+% \begin{macrocode}
+\int_new:N \l_@@_splittable_int
+% \end{macrocode}
+%
+% \medskip
+% An initial value of |splittable| equal to 100 is equivalent to say that the
+% environments |{Piton}| are unbreakable.
+% \begin{macrocode}
+\int_set:Nn \l_@@_splittable_int { 100 }
+% \end{macrocode}
+%
+% \medskip
+% The following string corresponds to the key |background-color| of |\PitonOptions|.
+% \begin{macrocode}
+\str_new:N \l_@@_background_color_str
+% \end{macrocode}
+%
+% \medskip
+% We will compute the maximal width of the lines of an environment |{Piton}| in
+% |\g_@@_width_dim|. We need a global variable because when the key |footnote|
+% is in force, each line when be composed in an environment |{savenotes}| and
+% (when |slim| is in force) we need to exit |\g_@@_width_dim| from that
+% environment.
+% \begin{macrocode}
+\dim_new:N \g_@@_width_dim
+% \end{macrocode}
+% The value of that dimension as written on the |aux| file will be stored in
+% |\l_@@_width_on_aux_dim|.
+% \begin{macrocode}
+\dim_new:N \l_@@_width_on_aux_dim
+% \end{macrocode}
+%
+% \medskip
+% We will count the environments |{Piton}| (and, in fact, also the commands
+% |\PitonInputFile|, despite the name |\g_@@_env_int|).
+% \begin{macrocode}
+\int_new:N \g_@@_env_int
+% \end{macrocode}
+%
+% \medskip
+% The following boolean corresponds to the key |show-spaces|.
+% \begin{macrocode}
+\bool_new:N \l_@@_show_spaces_bool
+% \end{macrocode}
+%
+% \medskip
+% The following booleans correspond to the keys |break-lines| and
+% |indent-broken-lines|.
+% \begin{macrocode}
+\bool_new:N \l_@@_break_lines_in_Piton_bool
+\bool_new:N \l_@@_indent_broken_lines_bool
+% \end{macrocode}
+%
+% \medskip
+% The following token list corresponds to the key |continuation-symbol|.
+% \begin{macrocode}
+\tl_new:N \l_@@_continuation_symbol_tl
+\tl_set:Nn \l_@@_continuation_symbol_tl { + }
+% \end{macrocode}
+%
+% \medskip
+% \begin{macrocode}
+% The following token list corresponds to the key
+% |continuation-symbol-on-indentation|. The name has been shorten to |csoi|.
+\tl_new:N \l_@@_csoi_tl
+\tl_set:Nn \l_@@_csoi_tl { $ \hookrightarrow \; $ }
+% \end{macrocode}
+%
+% \medskip
+% The following token list corresponds to the key |end-of-broken-line|.
+% \begin{macrocode}
+\tl_new:N \l_@@_end_of_broken_line_tl
+\tl_set:Nn \l_@@_end_of_broken_line_tl { \hspace*{0.5em} \textbackslash }
+% \end{macrocode}
+%
+% \medskip
+% The following boolean corresponds to the key |break-lines-in-piton|.
+% \begin{macrocode}
+\bool_new:N \l_@@_break_lines_in_piton_bool
+% \end{macrocode}
+%
+% \medskip
+% The following boolean corresponds to the key |slim| of |\PitonOptions|.
+% \begin{macrocode}
+\bool_new:N \l_@@_slim_bool
+% \end{macrocode}
+%
+% \medskip
+% The following dimension corresponds to the key |left-margin| of
+% |\PitonOptions|.
+% \begin{macrocode}
+\dim_new:N \l_@@_left_margin_dim
+% \end{macrocode}
+%
+% \medskip
+% The following boolean correspond will be set when the key |left-margin=auto|
+% is used.
+% \begin{macrocode}
+\bool_new:N \l_@@_left_margin_auto_bool
+% \end{macrocode}
+%
+% \medskip
+% The tabulators will be replaced by the content of the following token list.
+% \begin{macrocode}
+\tl_new:N \l_@@_tab_tl
+% \end{macrocode}
+%
+% \medskip
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_set_tab_tl:n #1
+ {
+ \tl_clear:N \l_@@_tab_tl
+ \prg_replicate:nn { #1 }
+ { \tl_put_right:Nn \l_@@_tab_tl { ~ } }
+ }
+\@@_set_tab_tl:n { 4 }
+% \end{macrocode}
+%
+% \medskip
+% The following integer corresponds to the key |gobble|.
+% \begin{macrocode}
+\int_new:N \l_@@_gobble_int
+% \end{macrocode}
+%
+% \medskip
+% \begin{macrocode}
+\tl_new:N \l_@@_space_tl
+\tl_set:Nn \l_@@_space_tl { ~ }
+% \end{macrocode}
+%
+%
+% \medskip
+% At each line, the following counter will count the spaces at the beginning.
+% \begin{macrocode}
+\int_new:N \g_@@_indentation_int
+% \end{macrocode}
+%
+% \medskip
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_an_indentation_space:
+ { \int_gincr:N \g_@@_indentation_int }
+% \end{macrocode}
+%
+% \medskip
+% The following command |\@@_beamer_command:n| executes the argument
+% corresponding to its argument but also stores it in |\l_@@_beamer_command_str|.
+% That string is used only in the error message ``|cr~not~allowed|'' raised when
+% there is a carriage return in the mandatory argument of that command.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_beamer_command:n #1
+ {
+ \str_set:Nn \l_@@_beamer_command_str { #1 }
+ \use:c { #1 }
+ }
+% \end{macrocode}
+%
+% \bigskip
+% \subsubsection{Treatment of a line of code}
+%
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_replace_spaces:n #1
+ {
+ \tl_set:Nn \l_tmpa_tl { #1 }
+ \bool_if:NTF \l_@@_show_spaces_bool
+ { \regex_replace_all:nnN { \x20 } { ␣ } \l_tmpa_tl } % U+2423
+ {
+% \end{macrocode}
+% If the key |break-lines-in-Piton| is in force, we replace all the characters
+% U+0032 (that is to say the spaces) by |\@@_breakable_space:|. Remark that,
+% except the spaces inserted in the LaTeX comments (and maybe in the math
+% comments), all these spaces are of catcode ``other'' (=12) and are
+% unbreakable.
+% \begin{macrocode}
+ \bool_if:NT \l_@@_break_lines_in_Piton_bool
+ {
+ \regex_replace_all:nnN
+ { \x20 }
+ { \c { @@_breakable_space: } }
+ \l_tmpa_tl
+ }
+ }
+ \l_tmpa_tl
+ }
+\cs_generate_variant:Nn \@@_replace_spaces:n { x }
+% \end{macrocode}
+%
+% \bigskip
+% In the contents provided by Lua, each line of the Python code will be
+% surrounded by |\@@_begin_line:| and |\@@_end_line:|.
+%
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_begin_line: #1 \@@_end_line:
+ {
+ \int_gzero:N \g_@@_indentation_int
+% \end{macrocode}
+%
+%
+% Be careful: there is curryfication in the following lines.
+% \begin{macrocode}
+ \bool_if:NTF \l_@@_slim_bool
+ { \hcoffin_set:Nn \l_tmpa_coffin }
+ {
+ \str_if_empty:NTF \l_@@_background_color_str
+ {
+ \vcoffin_set:Nnn \l_tmpa_coffin
+ { \dim_eval:n { \linewidth - \l_@@_left_margin_dim } }
+ }
+ {
+ \vcoffin_set:Nnn \l_tmpa_coffin
+ { \dim_eval:n { \linewidth - \l_@@_left_margin_dim - 0.5 em } }
+ }
+ }
+ {
+ \language = -1
+ \raggedright
+ \strut
+ \@@_replace_spaces:n { #1 }
+ \strut \hfil
+ }
+ \hbox_set:Nn \l_tmpa_box
+ {
+ \skip_horizontal:N \l_@@_left_margin_dim
+ \bool_if:NT \l_@@_line_numbers_bool
+ {
+ \bool_if:NF \l_@@_all_line_numbers_bool
+ { \tl_if_empty:nF { #1 } }
+ \@@_print_number:
+ }
+ \str_if_empty:NF \l_@@_background_color_str
+ { \skip_horizontal:n { 0.5 em } }
+ \coffin_typeset:Nnnnn \l_tmpa_coffin T l \c_zero_dim \c_zero_dim
+ }
+% \end{macrocode}
+% We compute in |\g_@@_width_dim| the maximal width of the lines of the
+% environment.
+% \begin{macrocode}
+ \dim_compare:nNnT { \box_wd:N \l_tmpa_box } > \g_@@_width_dim
+ { \dim_gset:Nn \g_@@_width_dim { \box_wd:N \l_tmpa_box } }
+ \box_set_dp:Nn \l_tmpa_box { \box_dp:N \l_tmpa_box + 1.25 pt }
+ \box_set_ht:Nn \l_tmpa_box { \box_ht:N \l_tmpa_box + 1.25 pt }
+ \tl_if_empty:NTF \l_@@_background_color_str
+ { \box_use_drop:N \l_tmpa_box }
+ {
+ \vbox_top:n
+ {
+ \hbox:n
+ {
+ \exp_args:NV \color \l_@@_background_color_str
+ \vrule height \box_ht:N \l_tmpa_box
+ depth \box_dp:N \l_tmpa_box
+ width \l_@@_width_on_aux_dim
+ }
+ \skip_vertical:n { - \box_ht_plus_dp:N \l_tmpa_box }
+ \box_set_wd:Nn \l_tmpa_box \l_@@_width_on_aux_dim
+ \box_use_drop:N \l_tmpa_box
+ }
+ }
+ \vspace { - 2.5 pt }
+ }
+% \end{macrocode}
+%
+% \bigskip
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_newline:
+ {
+ \int_gincr:N \g_@@_line_int
+ \int_compare:nNnT \g_@@_line_int > { \l_@@_splittable_int - 1 }
+ {
+ \int_compare:nNnT
+ { \l_@@_nb_lines_int - \g_@@_line_int } > \l_@@_splittable_int
+ {
+ \egroup
+ \bool_if:NT \c_@@_footnote_bool { \end { savenotes } }
+ \newline
+ \bool_if:NT \c_@@_footnote_bool { \begin { savenotes } }
+ \vtop \bgroup
+ }
+ }
+ }
+% \end{macrocode}
+%
+% \bigskip
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_breakable_space:
+ {
+ \discretionary
+ { \hbox:n { \color { gray } \l_@@_end_of_broken_line_tl } }
+ {
+ \hbox_overlap_left:n
+ {
+ {
+ \normalfont \footnotesize \color { gray }
+ \l_@@_continuation_symbol_tl
+ }
+ \skip_horizontal:n { 0.3 em }
+ \str_if_empty:NF \l_@@_background_color_str
+ { \skip_horizontal:n { 0.5 em } }
+ }
+ \bool_if:NT \l_@@_indent_broken_lines_bool
+ {
+ \hbox:n
+ {
+ \prg_replicate:nn { \g_@@_indentation_int } { ~ }
+ { \color { gray } \l_@@_csoi_tl }
+ }
+ }
+ }
+ { \hbox { ~ } }
+ }
+% \end{macrocode}
+%
+% \bigskip
+% \subsubsection{PitonOptions}
+%
+% \medskip
+% The following parameters correspond to the keys |line-numbers| and
+% |all-line-numbers|.
+% \begin{macrocode}
+\bool_new:N \l_@@_line_numbers_bool
+\bool_new:N \l_@@_all_line_numbers_bool
+% \end{macrocode}
+%
+% \medskip
+% The following flag corresponds to the key |resume|.
+% \begin{macrocode}
+\bool_new:N \l_@@_resume_bool
+% \end{macrocode}
+%
+% \bigskip
+% Be careful! The name of the following set of keys must be considered as
+% public! Hence, it should \emph{not} be changed.
+%
+% \begin{macrocode}
+\keys_define:nn { PitonOptions }
+ {
+ gobble .int_set:N = \l_@@_gobble_int ,
+ gobble .value_required:n = true ,
+ auto-gobble .code:n = \int_set:Nn \l_@@_gobble_int { -1 } ,
+ auto-gobble .value_forbidden:n = true ,
+ env-gobble .code:n = \int_set:Nn \l_@@_gobble_int { -2 } ,
+ env-gobble .value_forbidden:n = true ,
+ tabs-auto-gobble .code:n = \int_set:Nn \l_@@_gobble_int { -3 } ,
+ tabs-auto-gobble .value_forbidden:n = true ,
+ line-numbers .bool_set:N = \l_@@_line_numbers_bool ,
+ line-numbers .default:n = true ,
+ all-line-numbers .code:n =
+ \bool_set_true:N \l_@@_line_numbers_bool
+ \bool_set_true:N \l_@@_all_line_numbers_bool ,
+ all-line-numbers .value_forbidden:n = true ,
+ resume .bool_set:N = \l_@@_resume_bool ,
+ resume .value_forbidden:n = true ,
+ splittable .int_set:N = \l_@@_splittable_int ,
+ splittable .default:n = 1 ,
+ background-color .str_set:N = \l_@@_background_color_str ,
+ background-color .value_required:n = true ,
+ slim .bool_set:N = \l_@@_slim_bool ,
+ slim .default:n = true ,
+ left-margin .code:n =
+ \str_if_eq:nnTF { #1 } { auto }
+ {
+ \dim_zero:N \l_@@_left_margin_dim
+ \bool_set_true:N \l_@@_left_margin_auto_bool
+ }
+ { \dim_set:Nn \l_@@_left_margin_dim { #1 } } ,
+ left-margin .value_required:n = true ,
+ tab-size .code:n = \@@_set_tab_tl:n { #1 } ,
+ tab-size .value_required:n = true ,
+ show-spaces .bool_set:N = \l_@@_show_spaces_bool ,
+ show-spaces .default:n = true ,
+ show-spaces-in-strings .code:n = \tl_set:Nn \l_@@_space_tl { ␣ } , % U+2423
+ show-spaces-in-strings .value_forbidden:n = true ,
+ break-lines-in-Piton .bool_set:N = \l_@@_break_lines_in_Piton_bool ,
+ break-lines-in-Piton .default:n = true ,
+ break-lines-in-piton .bool_set:N = \l_@@_break_lines_in_piton_bool ,
+ break-lines-in-piton .default:n = true ,
+ break-lines .meta:n = { break-lines-in-piton , break-lines-in-Piton } ,
+ break-lines .value_forbidden:n = true ,
+ indent-broken-lines .bool_set:N = \l_@@_indent_broken_lines_bool ,
+ indent-broken-lines .default:n = true ,
+ end-of-broken-line .tl_set:N = \l_@@_end_of_broken_line_tl ,
+ end-of-broken-line .value_required:n = true ,
+ continuation-symbol .tl_set:N = \l_@@_continuation_symbol_tl ,
+ continuation-symbol .value_required:n = true ,
+ continuation-symbol-on-indentation .tl_set:N = \l_@@_csoi_tl ,
+ continuation-symbol-on-indentation .value_required:n = true ,
+ unknown .code:n =
+ \msg_error:nn { piton } { Unknown~key~for~PitonOptions }
+ }
+% \end{macrocode}
+%
+%
+% \bigskip
+% \begin{macrocode}
+\msg_new:nnnn { piton } { Unknown~key~for~PitonOptions }
+ {
+ Unknown~key. \\
+ The~key~'\l_keys_key_str'~is~unknown~for~\token_to_str:N \PitonOptions.~
+ It~will~be~ignored.\\
+ For~a~list~of~the~available~keys,~type~H~<return>.
+ }
+ {
+ The~available~keys~are~(in~alphabetic~order):~
+ all-line-numbers,~
+ auto-gobble,~
+ break-lines,~
+ break-lines-in-piton,~
+ break-lines-in-Piton,~
+ continuation-symbol,~
+ continuation-symbol-on-indentation,~
+ end-of-broken-line,~
+ env-gobble,~
+ gobble,~
+ indent-broken-lines,~
+ left-margin,~
+ line-numbers,~
+ resume,~
+ show-spaces,~
+ show-spaces-in-strings,~
+ slim,~
+ splittable,~
+ tabs-auto-gobble,~
+ and~tab-size.
+ }
+% \end{macrocode}
+%
+% \bigskip
+% The argument of |\PitonOptions| is provided by curryfication.
+% \begin{macrocode}
+\NewDocumentCommand \PitonOptions { } { \keys_set:nn { PitonOptions } }
+% \end{macrocode}
+%
+% \bigskip
+% \subsubsection{The numbers of the lines}
+%
+% \medskip
+% The following counter will be used to count the lines in the code when the
+% user requires the numbers of the lines to be printed (with |line-numbers| or
+% |all-line-numbers|).
+% \begin{macrocode}
+\int_new:N \g_@@_visual_line_int
+% \end{macrocode}
+%
+%
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_print_number:
+ {
+ \int_gincr:N \g_@@_visual_line_int
+ \hbox_overlap_left:n
+ {
+ { \color { gray } \footnotesize \int_to_arabic:n \g_@@_visual_line_int }
+ \skip_horizontal:n { 0.4 em }
+ }
+ }
+% \end{macrocode}
+%
+%
+% \bigskip
+% \subsubsection{The command to write on the aux file}
+%
+%
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_write_aux:
+ {
+ \tl_if_empty:NF \g_@@_aux_tl
+ {
+ \iow_now:Nn \@mainaux { \ExplSyntaxOn }
+ \iow_now:Nx \@mainaux
+ {
+ \tl_gset:cn { c_@@_ \int_use:N \g_@@_env_int _ tl }
+ { \exp_not:V \g_@@_aux_tl }
+ }
+ \iow_now:Nn \@mainaux { \ExplSyntaxOff }
+ }
+ \tl_gclear:N \g_@@_aux_tl
+ }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_width_to_aux:
+ {
+ \bool_if:NT \l_@@_slim_bool
+ {
+ \str_if_empty:NF \l_@@_background_color_str
+ {
+ \tl_gput_right:Nx \g_@@_aux_tl
+ {
+ \dim_set:Nn \l_@@_width_on_aux_dim
+ { \dim_eval:n { \g_@@_width_dim + 0.5 em } }
+ }
+ }
+ }
+ }
+% \end{macrocode}
+%
+% \bigskip
+% \subsubsection{The main commands and environments for the final user}
+%
+% \medskip
+% \begin{macrocode}
+\NewDocumentCommand { \piton } { }
+ { \peek_meaning:NTF \bgroup \@@_piton_standard \@@_piton_verbatim }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\NewDocumentCommand { \@@_piton_standard } { m }
+ {
+ \group_begin:
+ \ttfamily
+ \cs_set_eq:NN \\ \c_backslash_str
+ \cs_set_eq:NN \% \c_percent_str
+ \cs_set_eq:NN \{ \c_left_brace_str
+ \cs_set_eq:NN \} \c_right_brace_str
+ \cs_set_eq:NN \$ \c_dollar_str
+ \cs_set_protected:Npn \@@_begin_line: { }
+ \cs_set_protected:Npn \@@_end_line: { }
+ \tl_set:Nx \l_tmpa_tl
+ { \lua_now:n { piton.pitonParse(token.scan_string()) } { #1 } }
+ \bool_if:NTF \l_@@_show_spaces_bool
+ { \regex_replace_all:nnN { \x20 } { ␣ } \l_tmpa_tl } % U+2423
+% \end{macrocode}
+% The following code replaces the characters U+0020 (spaces) by characters
+% U+0020 of catcode~10: thus, they become breakable by an end of line.
+% \begin{macrocode}
+ {
+ \bool_if:NT \l_@@_break_lines_in_piton_bool
+ { \regex_replace_all:nnN { \x20 } { \x20 } \l_tmpa_tl }
+ }
+ \l_tmpa_tl
+ \group_end:
+ }
+% \end{macrocode}
+%
+%
+% \begin{macrocode}
+\NewDocumentCommand { \@@_piton_verbatim } { v }
+ {
+ \group_begin:
+ \ttfamily
+ \cs_set_protected:Npn \@@_begin_line: { }
+ \cs_set_protected:Npn \@@_end_line: { }
+ \tl_set:Nx \l_tmpa_tl
+ { \lua_now:n { piton.Parse(token.scan_string()) } { #1 } }
+ \bool_if:NT \l_@@_show_spaces_bool
+ { \regex_replace_all:nnN { \x20 } { ␣ } \l_tmpa_tl } % U+2423
+ \l_tmpa_tl
+ \group_end:
+ }
+% \end{macrocode}
+%
+% \bigskip
+% The following command is not a user command. It will be used when you will
+% have to ``rescan'' some chunks of Python code. For example, it will be the
+% initial value of the Piton style |InitialValues| (the default values of the
+% arguments of a Python function).
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_piton:n #1
+ {
+ \group_begin:
+ \cs_set_protected:Npn \@@_begin_line: { }
+ \cs_set_protected:Npn \@@_end_line: { }
+ \tl_set:Nx \l_tmpa_tl
+ { \lua_now:n { piton.Parse(token.scan_string()) } { #1 } }
+ \bool_if:NT \l_@@_show_spaces_bool
+ { \regex_replace_all:nnN { \x20 } { ␣ } \l_tmpa_tl } % U+2423
+ \l_tmpa_tl
+ \group_end:
+ }
+% \end{macrocode}
+%
+% \bigskip
+% The following command is similar to the previous one but raise a fatal error if
+% its argument contains a carriage return.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_piton_no_cr:n #1
+ {
+ \group_begin:
+ \cs_set_protected:Npn \@@_begin_line: { }
+ \cs_set_protected:Npn \@@_end_line: { }
+ \cs_set_protected:Npn \@@_newline:
+ { \msg_fatal:nn { piton } { cr~not~allowed } }
+ \tl_set:Nx \l_tmpa_tl
+ { \lua_now:n { piton.Parse(token.scan_string()) } { #1 } }
+ \bool_if:NT \l_@@_show_spaces_bool
+ { \regex_replace_all:nnN { \x20 } { ␣ } \l_tmpa_tl } % U+2423
+ \l_tmpa_tl
+ \group_end:
+ }
+% \end{macrocode}
+%
+% \bigskip
+% Despite its name, |\@@_pre_env:| will be used both in |\PitonInputFile| dans
+% in the environments such as |{Piton}|.
+% \begin{macrocode}
+\cs_new:Npn \@@_pre_env:
+ {
+ \int_gincr:N \g_@@_env_int
+ \tl_gclear:N \g_@@_aux_tl
+ \cs_if_exist_use:c { c_@@ _ \int_use:N \g_@@_env_int _ tl }
+ \dim_compare:nNnT \l_@@_width_on_aux_dim = \c_zero_dim
+ { \dim_set_eq:NN \l_@@_width_on_aux_dim \linewidth }
+ \bool_if:NF \l_@@_resume_bool { \int_gzero:N \g_@@_visual_line_int }
+ \dim_gzero:N \g_@@_width_dim
+ \int_gzero:N \g_@@_line_int
+ \dim_zero:N \parindent
+ \dim_zero:N \lineskip
+ }
+% \end{macrocode}
+%
+% \bigskip
+% \begin{macrocode}
+\keys_define:nn { PitonInputFile }
+ {
+ first-line .int_set:N = \l_@@_first_line_int ,
+ first-line .value_required:n = true ,
+ last-line .int_set:N = \l_@@_last_line_int ,
+ last-line .value_required:n = true ,
+ }
+% \end{macrocode}
+%
+% \bigskip
+% \begin{macrocode}
+\NewDocumentCommand { \PitonInputFile } { O { } m }
+ {
+ \group_begin:
+ \int_zero_new:N \l_@@_first_line_int
+ \int_zero_new:N \l_@@_last_line_int
+ \int_set_eq:NN \l_@@_last_line_int \c_max_int
+ \keys_set:nn { PitonInputFile } { #1 }
+ \@@_pre_env:
+ \mode_if_vertical:TF \mode_leave_vertical: \newline
+% \end{macrocode}
+% We count with Lua the number of lines of the argument. The result will be
+% stored by Lua in |\l_@@_nb_lines_int|. That information will be used to allow
+% or disallow page breaks.
+% \begin{macrocode}
+ \lua_now:n { piton.CountLinesFile(token.scan_argument()) } { #2 }
+% \end{macrocode}
+% If the final user has used both |left-margin=auto| and |line-numbers| or
+% |all-line-numbers|, we have to compute the width of the maximal number of
+% lines at the end of the composition of the listing to fix the correct value to
+% |left-margin|.
+% \begin{macrocode}
+ \bool_lazy_and:nnT \l_@@_left_margin_auto_bool \l_@@_line_numbers_bool
+ {
+ \hbox_set:Nn \l_tmpa_box
+ {
+ \footnotesize
+ \bool_if:NTF \l_@@_all_line_numbers_bool
+ {
+ \int_to_arabic:n
+ { \g_@@_visual_line_int + \l_@@_nb_lines_int }
+ }
+ {
+ \lua_now:n
+ { piton.CountNonEmptyLinesFile(token.scan_argument()) }
+ { #2 }
+ \int_to_arabic:n
+ { \g_@@_visual_line_int + \l_@@_nb_non_empty_lines_int }
+ }
+ }
+ \dim_set:Nn \l_@@_left_margin_dim { \box_wd:N \l_tmpa_box + 0.5em }
+ }
+% \end{macrocode}
+% Now, the main job.
+% \begin{macrocode}
+ \ttfamily
+ \bool_if:NT \c_@@_footnote_bool { \begin { savenotes } }
+ \vtop \bgroup
+ \lua_now:e
+ { piton.ParseFile(token.scan_argument(),
+ \int_use:N \l_@@_first_line_int ,
+ \int_use:N \l_@@_last_line_int )
+ }
+ { #2 }
+ \egroup
+ \bool_if:NT \c_@@_footnote_bool { \end { savenotes } }
+ \@@_width_to_aux:
+ \group_end:
+ \@@_write_aux:
+ }
+% \end{macrocode}
+%
+%
+% \bigskip
+% \begin{macrocode}
+\NewDocumentCommand { \NewPitonEnvironment } { m m m m }
+ {
+ \dim_zero:N \parindent
+% \end{macrocode}
+% We construct a TeX macro which will catch as argument all the tokens until
+% |\end{|\texttt{\textsl{name_env}}|}| with, in that
+% |\end{|\texttt{\textsl{name_env}}|}|, the catcodes of |\|, |{| and |}| equal to
+% 12 (``\texttt{other}''). The latter explains why the definition of that
+% function is a bit complicated.
+% \begin{macrocode}
+ \use:x
+ {
+ \cs_set_protected:Npn
+ \use:c { _@@_collect_ #1 :w }
+ ####1
+ \c_backslash_str end \c_left_brace_str #1 \c_right_brace_str
+ }
+ {
+ \group_end:
+ \mode_if_vertical:TF \mode_leave_vertical: \newline
+% \end{macrocode}
+% We count with Lua the number of lines of the argument. The result will be
+% stored by Lua in |\l_@@_nb_lines_int|. That information will be used to allow
+% or disallow page breaks.
+% \begin{macrocode}
+ \lua_now:n { piton.CountLines(token.scan_argument()) } { ##1 }
+% \end{macrocode}
+% If the final user has used both |left-margin=auto| and |line-numbers|, we have
+% to compute the width of the maximal number of lines at the end of the
+% environment to fix the correct value to |left-margin|.
+% \begin{macrocode}
+ \bool_lazy_and:nnT \l_@@_left_margin_auto_bool \l_@@_line_numbers_bool
+ {
+ \bool_if:NTF \l_@@_all_line_numbers_bool
+ {
+ \hbox_set:Nn \l_tmpa_box
+ {
+ \footnotesize
+ \int_to_arabic:n
+ { \g_@@_visual_line_int + \l_@@_nb_lines_int }
+ }
+ }
+ {
+ \lua_now:n
+ { piton.CountNonEmptyLines(token.scan_argument()) }
+ { ##1 }
+ \hbox_set:Nn \l_tmpa_box
+ {
+ \footnotesize
+ \int_to_arabic:n
+ { \g_@@_visual_line_int + \l_@@_nb_non_empty_lines_int }
+ }
+ }
+ \dim_set:Nn \l_@@_left_margin_dim
+ { \box_wd:N \l_tmpa_box + 0.5 em }
+ }
+% \end{macrocode}
+% Now, the main job.
+% \begin{macrocode}
+ \ttfamily
+ \bool_if:NT \c_@@_footnote_bool { \begin { savenotes } }
+ \vtop \bgroup
+ \lua_now:e
+ {
+ piton.GobbleParse
+ ( \int_use:N \l_@@_gobble_int , token.scan_argument() )
+ }
+ { ##1 }
+ \vspace { 2.5 pt }
+ \egroup
+ \bool_if:NT \c_@@_footnote_bool { \end { savenotes } }
+ \@@_width_to_aux:
+% \end{macrocode}
+% The following |\end{#1}| is only for the groups and the stack of environments
+% of LaTeX.
+% \begin{macrocode}
+ \end { #1 }
+ \@@_write_aux:
+ }
+% \end{macrocode}
+%
+%
+% \bigskip
+% We can now define the new environment.
+%
+% We are still in the definition of the command |\NewPitonEnvironment|...
+% \begin{macrocode}
+ \NewDocumentEnvironment { #1 } { #2 }
+ {
+ #3
+ \@@_pre_env:
+ \group_begin:
+ \tl_map_function:nN
+ { \ \\ \{ \} \$ \& \# \^ \_ \% \~ \^^I }
+ \char_set_catcode_other:N
+ \use:c { _@@_collect_ #1 :w }
+ }
+ { #4 }
+% \end{macrocode}
+%
+% \medskip
+% The following code is for technical reasons. We want to change the catcode of
+% |^^M| before catching the arguments of the new environment we are defining.
+% Indeed, if not, we will have problems if there is a final optional argument in
+% our environment (if that final argument is not used by the user in an
+% instance of the environment, a spurious space is inserted, probably because
+% the |^^M| is converted to space).
+% \begin{macrocode}
+ \AddToHook { env / #1 / begin } { \char_set_catcode_other:N \^^M }
+ }
+% \end{macrocode}
+% This is the end of the definition of the command |\NewPitonEnvironment|.
+%
+% \bigskip
+% \begin{macrocode}
+\NewPitonEnvironment { Piton } { } { } { }
+% \end{macrocode}
+%
+%
+%
+% \bigskip
+% \subsubsection{The styles}
+%
+% \medskip
+% The following command is fundamental: it will be used by the Lua code.
+% \begin{macrocode}
+\NewDocumentCommand { \PitonStyle } { m } { \use:c { pitonStyle #1 } }
+% \end{macrocode}
+%
+% \medskip
+% The following command takes in its argument by curryfication.
+% \begin{macrocode}
+\NewDocumentCommand { \SetPitonStyle } { } { \keys_set:nn { piton / Styles } }
+% \end{macrocode}
+%
+% \medskip
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_math_scantokens:n #1
+ { \normalfont \scantextokens { $#1$ } }
+% \end{macrocode}
+%
+% \medskip
+% \begin{macrocode}
+\keys_define:nn { piton / Styles }
+ {
+ String.Interpol .tl_set:c = pitonStyle String.Interpol ,
+ String.Interpol .value_required:n = true ,
+ FormattingType .tl_set:c = pitonStyle FormattingType ,
+ FormattingType .value_required:n = true ,
+ Dict.Value .tl_set:c = pitonStyle Dict.Value ,
+ Dict.Value .value_required:n = true ,
+ Name.Decorator .tl_set:c = pitonStyle Name.Decorator ,
+ Name.Decorator .value_required:n = true ,
+ Name.Function .tl_set:c = pitonStyle Name.Function ,
+ Name.Function .value_required:n = true ,
+ Keyword .tl_set:c = pitonStyle Keyword ,
+ Keyword .value_required:n = true ,
+ Keyword.Constant .tl_set:c = pitonStyle Keyword.Constant ,
+ Keyword.constant .value_required:n = true ,
+ String.Doc .tl_set:c = pitonStyle String.Doc ,
+ String.Doc .value_required:n = true ,
+ Interpol.Inside .tl_set:c = pitonStyle Interpol.Inside ,
+ Interpol.Inside .value_required:n = true ,
+ String.Long .tl_set:c = pitonStyle String.Long ,
+ String.Long .value_required:n = true ,
+ String.Short .tl_set:c = pitonStyle String.Short ,
+ String.Short .value_required:n = true ,
+ String .meta:n = { String.Long = #1 , String.Short = #1 } ,
+ Comment.Math .tl_set:c = pitonStyle Comment.Math ,
+ Comment.Math .default:n = \@@_math_scantokens:n ,
+ Comment.Math .initial:n = ,
+ Comment .tl_set:c = pitonStyle Comment ,
+ Comment .value_required:n = true ,
+ InitialValues .tl_set:c = pitonStyle InitialValues ,
+ InitialValues .value_required:n = true ,
+ Number .tl_set:c = pitonStyle Number ,
+ Number .value_required:n = true ,
+ Name.Namespace .tl_set:c = pitonStyle Name.Namespace ,
+ Name.Namespace .value_required:n = true ,
+ Name.Class .tl_set:c = pitonStyle Name.Class ,
+ Name.Class .value_required:n = true ,
+ Name.Builtin .tl_set:c = pitonStyle Name.Builtin ,
+ Name.Builtin .value_required:n = true ,
+ Name.Type .tl_set:c = pitonStyle Name.Type ,
+ Name.Type .value_required:n = true ,
+ Operator .tl_set:c = pitonStyle Operator ,
+ Operator .value_required:n = true ,
+ Operator.Word .tl_set:c = pitonStyle Operator.Word ,
+ Operator.Word .value_required:n = true ,
+ Post.Function .tl_set:c = pitonStyle Post.Function ,
+ Post.Function .value_required:n = true ,
+ Exception .tl_set:c = pitonStyle Exception ,
+ Exception .value_required:n = true ,
+ Comment.LaTeX .tl_set:c = pitonStyle Comment.LaTeX ,
+ Comment.LaTeX .value_required:n = true ,
+ Beamer .tl_set:c = pitonStyle Beamer ,
+ Beamer .value_required:n = true ,
+ unknown .code:n =
+ \msg_error:nn { piton } { Unknown~key~for~SetPitonStyle }
+ }
+% \end{macrocode}
+%
+%
+% \bigskip
+% \begin{macrocode}
+\msg_new:nnn { piton } { Unknown~key~for~SetPitonStyle }
+ {
+ The~style~'\l_keys_key_str'~is~unknown.\\
+ This~key~will~be~ignored.\\
+ The~available~styles~are~(in~alphabetic~order):~
+ Comment,~
+ Comment.LaTeX,~
+ Dict.Value,~
+ Exception,~
+ InitialValues,~
+ Keyword,~
+ Keyword.Constant,~
+ Name.Builtin,~
+ Name.Class,~
+ Name.Decorator,~
+ Name.Function,~
+ Name.Namespace,~
+ Number,~
+ Operator,~
+ Operator.Word,~
+ String,~
+ String.Doc,~
+ String.Long,~
+ String.Short,~and~
+ String.Interpol.
+ }
+% \end{macrocode}
+%
+% \bigskip
+% \subsubsection{The initial style}
+%
+% The initial style is inspired by the style ``manni'' of Pygments.
+%
+% \medskip
+% \begin{macrocode}
+\SetPitonStyle
+ {
+ Comment = \color[HTML]{0099FF} \itshape ,
+ Exception = \color[HTML]{CC0000} ,
+ Keyword = \color[HTML]{006699} \bfseries ,
+ Keyword.Constant = \color[HTML]{006699} \bfseries ,
+ Name.Builtin = \color[HTML]{336666} ,
+ Name.Decorator = \color[HTML]{9999FF},
+ Name.Class = \color[HTML]{00AA88} \bfseries ,
+ Name.Function = \color[HTML]{CC00FF} ,
+ Name.Namespace = \color[HTML]{00CCFF} ,
+ Number = \color[HTML]{FF6600} ,
+ Operator = \color[HTML]{555555} ,
+ Operator.Word = \bfseries ,
+ String = \color[HTML]{CC3300} ,
+ String.Doc = \color[HTML]{CC3300} \itshape ,
+ String.Interpol = \color[HTML]{AA0000} ,
+ Comment.LaTeX = \normalfont \color[rgb]{.468,.532,.6} ,
+ Name.Type = \color[HTML]{336666} ,
+ InitialValues = \@@_piton:n ,
+ Dict.Value = \@@_piton:n ,
+ Interpol.Inside = \color{black}\@@_piton:n ,
+ Beamer = \@@_piton_no_cr:n ,
+ Post.Function = \@@_piton:n ,
+ }
+% \end{macrocode}
+% The last styles |Beamer| and |Post.Function| should be considered as
+% ``internal style'' (not available for the final user).
+%
+% \medskip
+% If the key |math-comments| has been used at load-time, we change the style
+% |Comment.Math| which should be considered only at an ``internal style''.
+% However, maybe we will document in a future version the possibility to write
+% change the style \emph{locally} in a document)].
+% \begin{macrocode}
+\bool_if:NT \c_@@_math_comments_bool
+ { \SetPitonStyle { Comment.Math } }
+% \end{macrocode}
+%
+% \bigskip
+% \subsubsection{Security}
+%
+% \begin{macrocode}
+\AddToHook { env / piton / begin }
+ { \msg_fatal:nn { piton } { No~environment~piton } }
+
+\msg_new:nnn { piton } { No~environment~piton }
+ {
+ There~is~no~environment~piton!\\
+ There~is~an~environment~{Piton}~and~a~command~
+ \token_to_str:N \piton\ but~there~is~no~environment~
+ {piton}.~This~error~is~fatal.
+ }
+% \end{macrocode}
+%
+% \bigskip
+% \subsubsection{The errors messages of the package}
+%
+% \begin{macrocode}
+\msg_new:nnn { piton } { cr~not~allowed }
+ {
+ You~can't~put~any~carriage~return~in~the~argument~
+ of~a~command~\c_backslash_str
+ \l_@@_beamer_command_str\ within~an~
+ environment~of~'piton'.~You~should~consider~using~the~
+ corresponding~environment.\\
+ That~error~is~fatal.
+ }
+% \end{macrocode}
+%
+% \bigskip
+% \subsection{The Lua part of the implementation}
+%
+% \bigskip
+% \begin{macrocode}
+\ExplSyntaxOff
+\RequirePackage{luacode}
+% \end{macrocode}
+%
+%
+% \bigskip
+% The Lua code will be loaded via a |{luacode*}| environment. Thei environment
+% is by itself a Lua block and the local declarations will be local to that
+% block. All the global functions (used by the L3 parts of the implementation)
+% will be put in a Lua table |piton|.
+%
+% \medskip
+% \begin{macrocode}
+\begin{luacode*}
+piton = piton or { }
+% \end{macrocode}
+%
+% \begin{macrocode}
+if piton.comment_latex == nil then piton.comment_latex = ">" end
+piton.comment_latex = "#" .. piton.comment_latex
+% \end{macrocode}
+%
+%
+% \bigskip
+% \subsubsection{Special functions dealing with LPEG}
+%
+%
+% \medskip
+% We will use the Lua library \pkg{lpeg} which is built in LuaTeX. That's why we
+% define first aliases for several functions of that library.
+% \begin{macrocode}
+local P, S, V, C, Ct, Cc = lpeg.P, lpeg.S, lpeg.V, lpeg.C, lpeg.Ct, lpeg.Cc
+local Cf, Cs = lpeg.Cf, lpeg.Cs
+% \end{macrocode}
+%
+%
+% \bigskip
+% The function |Q| takes in as argument a pattern and returns a \textsc{lpeg}
+% \emph{which does a capture} of the pattern. That capture will be sent to LaTeX
+% with the catcode ``other'' for all the characters: it's suitable for elements
+% of the Python listings that \pkg{piton} will typeset verbatim (thanks to the
+% catcode ``other'').
+% \begin{macrocode}
+local function Q(pattern)
+ return Ct ( Cc ( luatexbase.catcodetables.CatcodeTableOther ) * C ( pattern ) )
+end
+% \end{macrocode}
+%
+%
+% \bigskip
+% The function |L| takes in as argument a pattern and returns a \textsc{lpeg}
+% \emph{which does a capture} of the pattern. That capture will be sent to LaTeX
+% with standard LaTeX catcodes for all the characters: the elements captured
+% will be formatted as normal LaTeX codes. It's suitable for the ``LaTeX
+% comments'' in the environments |{Piton}| and the elements beetween
+% ``|escape-inside|''. That function won't be much used.
+% \begin{macrocode}
+local function L(pattern)
+ return Ct ( C ( pattern ) )
+end
+% \end{macrocode}
+%
+% \bigskip
+% The function |Lc| (the c is for \emph{constant}) takes in as argument a string
+% and returns a \textsc{lpeg} \emph{with does a constant capture} which returns
+% that string. The elements captured will be formatted as L3 code. It will be
+% used to send to LaTeX all the formatting LaTeX instructions we have to insert
+% in order to do the syntactic highlighting (that's the main job of
+% \pkg{piton}). That function will be widely used.
+% \begin{macrocode}
+local function Lc(string)
+ return Cc ( { luatexbase.catcodetables.expl , string } )
+end
+% \end{macrocode}
+%
+% \bigskip
+% The function |K| creates a \textsc{lpeg} which will return as capture
+% the whole LaTeX code corresponding to a Python chunk (that is to say with the
+% LaTeX formatting instructions corresponding to the syntactic nature of that
+% Python chunk). The first argument is a pattern (that is to say a \textsc{lpeg}
+% without capture) and the second element is a Lua string corresponding to the
+% name of a \pkg{piton} style. If the second argument is not present, the
+% function |K| behaves as the function |Q| does.
+% \begin{macrocode}
+local function K(pattern, style)
+ if style
+ then
+ return
+ Lc ( "{\\PitonStyle{" .. style .. "}{" )
+ * Q ( pattern )
+ * Lc ( "}}" )
+ else
+ return Q ( pattern )
+ end
+end
+% \end{macrocode}
+% The formatting commands in a given \pkg{piton} style (eg. the style |Keyword|)
+% may be semi-global declarations (such as |\bfseries| or |\slshape|) or LaTeX
+% macros with an argument (such as |\fbox| or |\colorbox{yellow}|). In order to
+% deal with both syntaxes, we have used two pairs of braces:
+% |{\PitonStyle{Keyword}{|\texttt{\slshape text to format}|}}|.
+%
+% \bigskip
+% The following \textsc{lpeg} catches the Python chunks which are in LaTeX
+% escapes (and that chunks will be considered as normal LaTeX constructions). We
+% recall that |piton.begin_espace| and |piton_end_escape| are Lua strings
+% corresponding to the key |escape-inside|\footnote{The \pkg{piton} key
+% |escape-inside| is available at load-time only.}. Since the elements that will
+% be catched must be sent to LaTeX with standard LaTeX catcodes, we put the
+% capture (done by the function |C|) in a table (by using |Ct|, which is an
+% alias for |lpeg.Ct|) without number of catcode table at the first component of
+% the table.
+% \begin{macrocode}
+local Escape =
+ P(piton_begin_escape)
+ * L ( ( 1 - P(piton_end_escape) ) ^ 1 )
+ * P(piton_end_escape)
+% \end{macrocode}
+%
+% \vspace{1cm}
+% The following line is mandatory.
+% \begin{macrocode}
+lpeg.locale(lpeg)
+% \end{macrocode}
+%
+% \bigskip
+% \subsubsection{The LPEG SyntaxPython}
+%
+%
+% \bigskip
+%
+%
+% \bigskip
+% \paragraph{The basic syntactic LPEG}
+%
+% \begin{macrocode}
+local alpha, digit, space = lpeg.alpha, lpeg.digit, lpeg.space
+% \end{macrocode}
+%
+% Remember that, for \textsc{lpeg}, the Unicode characters such as |à|, |â|,
+% |ç|, etc. are in fact strings of length 2 (2 bytes) because \pkg{lpeg} is not
+% Unicode-aware.
+% \begin{macrocode}
+local letter = alpha + P "_"
+ + P "â" + P "à" + P "ç" + P "é" + P "è" + P "ê" + P "ë" + P "ï" + P "î"
+ + P "ô" + P "û" + P "ü" + P "Â" + P "À" + P "Ç" + P "É" + P "È" + P "Ê"
+ + P "Ë" + P "Ï" + P "Î" + P "Ô" + P "Û" + P "Ü"
+
+local alphanum = letter + digit
+% \end{macrocode}
+%
+% \bigskip
+% The following \textsc{lpeg} |identifier| is a mere pattern (that is to say
+% more or less a regular expression) which matches the Python identifiers (hence
+% the name).
+% \begin{macrocode}
+local identifier = letter * alphanum ^ 0
+% \end{macrocode}
+%
+% \medskip
+% On the other hand, the \textsc{lpeg} |Identifier| (with a capital) also returns
+% a \emph{capture}.
+% \begin{macrocode}
+local Identifier = K ( identifier )
+% \end{macrocode}
+%
+% \bigskip
+% By convention, we will use names with an initial capital for \textsc{lpeg}
+% which return captures.
+%
+%
+% \bigskip
+% Here is the first use of our function~|K|. That function will be used to
+% construct \textsc{lpeg} which capture Python chunks for which we have a
+% dedicated \pkg{piton} style. For example, for the numbers, \pkg{piton}
+% provides a style which is called |Number|. The name of the style is provided
+% as a Lua string in the second argument of the function~|K|. By convention, we
+% use single quotes for delimiting the Lua strings which are names of
+% \pkg{piton} styles (but this is only a convention).
+% \begin{macrocode}
+local Number =
+ K (
+ ( digit^1 * P "." * digit^0 + digit^0 * P "." * digit^1 + digit^1 )
+ * ( S "eE" * S "+-" ^ -1 * digit^1 ) ^ -1
+ + digit^1 ,
+ 'Number'
+ )
+% \end{macrocode}
+%
+% \bigskip
+% We recall that |piton.begin_espace| and |piton_end_escape| are Lua strings
+% corresponding to the key |escape-inside|\footnote{The \pkg{piton} key
+% |escape-inside| is available at load-time only.}. Of course, if the final user
+% has not used the key |escape-inside|, these strings are empty.
+% \begin{macrocode}
+local Word
+if piton_begin_escape ~= ''
+then Word = K ( ( ( 1 - space - P(piton_begin_escape) - P(piton_end_escape) )
+ - S "'\"\r[()]" - digit ) ^ 1 )
+else Word = K ( ( ( 1 - space ) - S "'\"\r[()]" - digit ) ^ 1 )
+end
+% \end{macrocode}
+%
+% \bigskip
+% \begin{macrocode}
+local Space = K ( ( space - P "\r" ) ^ 1 )
+
+local SkipSpace = K ( ( space - P "\r" ) ^ 0 )
+
+local Punct = K ( S ".,:;!" )
+% \end{macrocode}
+%
+% \bigskip
+% \begin{macrocode}
+local Tab = P "\t" * Lc ( '\\l_@@_tab_tl' )
+% \end{macrocode}
+%
+% \bigskip
+% \begin{macrocode}
+local SpaceIndentation =
+ Lc ( '\\@@_an_indentation_space:' ) * K " "
+% \end{macrocode}
+%
+
+%
+% \bigskip
+% \begin{macrocode}
+local Delim = K ( S "[()]" )
+% \end{macrocode}
+%
+% \bigskip
+% Some strings of length 2 are explicit because we want the corresponding
+% ligatures available in some fonts such as \emph{Fira Code} to be active.
+% \begin{macrocode}
+local Operator =
+ K ( P "!=" + P "<>" + P "==" + P "<<" + P ">>" + P "<=" + P ">=" + P ":="
+ + P "//" + P "**" + S "-~+/*%=<>&.@|"
+ ,
+ 'Operator'
+ )
+
+local OperatorWord =
+ K ( P "in" + P "is" + P "and" + P "or" + P "not" , 'Operator.Word')
+
+local Keyword =
+ K ( P "as" + P "assert" + P "break" + P "case" + P "class" + P "continue"
+ + P "def" + P "del" + P "elif" + P "else" + P "except" + P "exec"
+ + P "finally" + P "for" + P "from" + P "global" + P "if" + P "import"
+ + P "lambda" + P "non local" + P "pass" + P "return" + P "try"
+ + P "while" + P "with" + P "yield" + P "yield from" ,
+ 'Keyword' )
+ + K ( P "True" + P "False" + P "None" , 'Keyword.Constant' )
+
+local Builtin =
+ K ( P "__import__" + P "abs" + P "all" + P "any" + P "bin" + P "bool"
+ + P "bytearray" + P "bytes" + P "chr" + P "classmethod" + P "compile"
+ + P "complex" + P "delattr" + P "dict" + P "dir" + P "divmod"
+ + P "enumerate" + P "eval" + P "filter" + P "float" + P "format"
+ + P "frozenset" + P "getattr" + P "globals" + P "hasattr" + P "hash"
+ + P "hex" + P "id" + P "input" + P "int" + P "isinstance" + P "issubclass"
+ + P "iter" + P "len" + P "list" + P "locals" + P "map" + P "max"
+ + P "memoryview" + P "min" + P "next" + P "object" + P "oct" + P "open"
+ + P "ord" + P "pow" + P "print" + P "property" + P "range" + P "repr"
+ + P "reversed" + P "round" + P "set" + P "setattr" + P "slice" + P "sorted"
+ + P "staticmethod" + P "str" + P "sum" + P "super" + P "tuple" + P "type"
+ + P "vars" + P "zip" ,
+ 'Name.Builtin' )
+
+local Exception =
+ K ( "ArithmeticError" + P "AssertionError" + P "AttributeError"
+ + P "BaseException" + P "BufferError" + P "BytesWarning" + P "DeprecationWarning"
+ + P "EOFError" + P "EnvironmentError" + P "Exception" + P "FloatingPointError"
+ + P "FutureWarning" + P "GeneratorExit" + P "IOError" + P "ImportError"
+ + P "ImportWarning" + P "IndentationError" + P "IndexError" + P "KeyError"
+ + P "KeyboardInterrupt" + P "LookupError" + P "MemoryError" + P "NameError"
+ + P "NotImplementedError" + P "OSError" + P "OverflowError"
+ + P "PendingDeprecationWarning" + P "ReferenceError" + P "ResourceWarning"
+ + P "RuntimeError" + P "RuntimeWarning" + P "StopIteration"
+ + P "SyntaxError" + P "SyntaxWarning" + P "SystemError" + P "SystemExit"
+ + P "TabError" + P "TypeError" + P "UnboundLocalError" + P "UnicodeDecodeError"
+ + P "UnicodeEncodeError" + P "UnicodeError" + P "UnicodeTranslateError"
+ + P "UnicodeWarning" + P "UserWarning" + P "ValueError" + P "VMSError"
+ + P "Warning" + P "WindowsError" + P "ZeroDivisionError"
+ + P "BlockingIOError" + P "ChildProcessError" + P "ConnectionError"
+ + P "BrokenPipeError" + P "ConnectionAbortedError" + P "ConnectionRefusedError"
+ + P "ConnectionResetError" + P "FileExistsError" + P "FileNotFoundError"
+ + P "InterruptedError" + P "IsADirectoryError" + P "NotADirectoryError"
+ + P "PermissionError" + P "ProcessLookupError" + P "TimeoutError"
+ + P "StopAsyncIteration" + P "ModuleNotFoundError" + P "RecursionError" ,
+ 'Exception' )
+
+local RaiseException = K ( P "raise" , 'Keyword' ) * SkipSpace * Exception * K ( P "(" )
+
+% \end{macrocode}
+%
+% \bigskip
+% In Python, a ``decorator'' is a statement whose begins by |@| which patches
+% the function defined in the following statement.
+% \begin{macrocode}
+local Decorator = K ( P "@" * letter^1 , 'Name.Decorator' )
+% \end{macrocode}
+%
+% \bigskip
+% The following \textsc{lpeg} |DefClass| will be used to detect the definition of a
+% new class (the name of that new class will be formatted with the \pkg{piton}
+% style |Name.Class|).
+%
+% \smallskip
+% Example:\enskip \piton{class myclass:}
+% \begin{macrocode}
+local DefClass =
+ K ( P "class" , 'Keyword' ) * Space * K ( identifier , 'Name.Class' )
+% \end{macrocode}
+%
+% If the word |class| is not followed by a identifier, it will be catched as
+% keyword by the \textsc{lpeg} |Keyword| (useful if we want to type a
+% list of keywords).
+%
+%
+% \bigskip
+% The following \textsc{lpeg} |ImportAs| is used for the lines beginning by |import|.
+% % We have to detect the potential keyword |as| because both the name of the
+% module and its alias must be formatted with the \pkg{piton} style |Name.Namespace|.
+%
+% \smallskip
+% Example:\enskip \piton{import numpy as np}
+%
+% \smallskip
+% Moreover, after the keyword |import|, it's possible to have a comma-separated
+% list of modules (if the keyword |as| is not used).
+%
+% \smallskip
+% Example:\enskip \piton{import math, numpy}
+% \begin{macrocode}
+local ImportAs =
+ K ( P "import" , 'Keyword' )
+ * Space
+ * K ( identifier * ( P "." * identifier ) ^ 0 ,
+ 'Name.Namespace'
+ )
+ * (
+ ( Space * K ( P "as" , 'Keyword' ) * Space
+ * K ( identifier , 'Name.Namespace' ) )
+ +
+ ( SkipSpace * K ( P "," ) * SkipSpace
+ * K ( identifier , 'Name.Namespace' ) ) ^ 0
+ )
+% \end{macrocode}
+% Be careful: there is no commutativity of |+| in the previous expression.
+%
+% \bigskip
+% The \textsc{lpeg} |FromImport| is used for the lines beginning by |from|. We
+% need a special treatment because the identifier following the keyword |from|
+% must be formatted with the \pkg{piton} style |Name.Namespace| and the
+% following keyword |import| must be formatted with the \pkg{piton} style
+% |Keyword| and must \emph{not} be catched by the \textsc{lpeg} |ImportAs|.
+%
+% \smallskip
+% Example:\enskip \piton{from math import pi}
+%
+% \smallskip
+% \begin{macrocode}
+local FromImport =
+ K ( P "from" , 'Keyword' )
+ * Space * K ( identifier , 'Name.Namespace' )
+ * Space * K ( P "import" , 'Keyword' )
+% \end{macrocode}
+%
+% \bigskip
+% \paragraph{The strings of Python}
+%
+% For the strings in Python, there are four categories of delimiters (without
+% counting the prefixes for f-strings and raw strings). We will use, in the
+% names of our \textsc{lpeg}, prefixes to distinguish the \textsc{lpeg} dealing
+% with that categories of strings, as presented in the following tabular.
+% \begin{center}
+% \begin{tabular}{ccc}
+% \toprule
+% & |Single| & |Double| \\
+% \midrule
+% |Short| & |'text'| & |"text"| \\
+% |Long| & |'''test'''| & |"""text"""| \\
+% \bottomrule
+% \end{tabular}
+% \end{center}
+%
+%
+% \bigskip
+% First, we define \textsc{lpeg} for the interpolations in the f-strings. Here
+% is an example of a f-string with an interpolation and a format
+% instruction\footnote{There is no special \pkg{piton} style for the formatting
+% instruction (after the colon): the style which will be applied will be the
+% style of the encompassing string, that is to say |String.Short| or
+% |String.Long|.} in that interpolation:
+%
+% \piton{f'Total price: {total+1:.2f} €'}
+%
+% \medskip
+% The following \textsc{lpeg} |SingleShortInterpol| (and the three variants)
+% will catch the whole interpolation, included the braces, that is to say, in
+% the previous example:\enskip |{total+1:.2f}|
+%
+% \medskip
+% \begin{macrocode}
+local SingleShortInterpol =
+ K ( P "{" , 'String.Interpol')
+ * K ( ( 1 - S "}':" ) ^ 0 , 'Interpol.Inside' )
+ * K ( P ":" * (1 - S "}:'") ^ 0 ) ^ -1
+ * K ( P "}" , 'String.Interpol' )
+
+local DoubleShortInterpol =
+ K ( P "{" , 'String.Interpol' )
+ * K ( ( 1 - S "}\":" ) ^ 0 , 'Interpol.Inside' )
+ * ( K ( P ":" , 'String.Interpol' ) * K ( (1 - S "}:\"") ^ 0 ) ) ^ -1
+ * K ( P "}" , 'String.Interpol' )
+
+local SingleLongInterpol =
+ K ( P "{" , 'String.Interpol' )
+ * K ( ( 1 - S "}:\r" - P "'''" ) ^ 0 , 'Interpol.Inside' )
+ * K ( P ":" * (1 - S "}:\r" - P "'''" ) ^ 0 ) ^ -1
+ * K ( P "}" , 'String.Interpol' )
+
+local DoubleLongInterpol =
+ K ( P "{" , 'String.Interpol' )
+ * K ( ( 1 - S "}:\r" - P "\"\"\"" ) ^ 0 , 'Interpol.Inside' )
+ * K ( P ":" * (1 - S "}:\r" - P "\"\"\"" ) ^ 0 ) ^ -1
+ * K ( P "}" , 'String.Interpol' )
+% \end{macrocode}
+%
+%
+% \bigskip
+% The following \textsc{lpeg} catches a space (U+0032) and replace it by
+% |\l_@@_space_tl|. It will be used in the short strings. Usually,
+% |\l_@@_space_tl| will contain a space and therefore there won't be difference.
+% However, when the key |show-spaces-in-strings| is in force, |\\l_@@_space_tl| will
+% contain ␣ (U+2423) in order to visualize the spaces.
+% \begin{macrocode}
+local VisualSpace = P " " * Lc "\\l_@@_space_tl"
+% \end{macrocode}
+%
+% \bigskip
+% Now, we define \textsc{lpeg} for the parts of the strings which are \emph{not}
+% in the interpolations.
+%
+% \begin{macrocode}
+local SingleShortPureString =
+ ( K ( ( P "\\'" + P "{{" + P "}}" + 1 - S " {}'" ) ^ 1 ) + VisualSpace ) ^ 1
+
+local DoubleShortPureString =
+ ( K ( ( P "\\\"" + P "{{" + P "}}" + 1 - S " {}\"" ) ^ 1 ) + VisualSpace ) ^ 1
+
+local SingleLongPureString =
+ K ( ( 1 - P "'''" - S "{}'\r" ) ^ 1 )
+
+local DoubleLongPureString =
+ K ( ( 1 - P "\"\"\"" - S " {}\"\r" ) ^ 1 )
+% \end{macrocode}
+%
+% \bigskip
+% The interpolations beginning by |%| (even though there is more modern
+% technics now in Python).
+% \begin{macrocode}
+local PercentInterpol =
+ K ( P "%"
+ * ( P "(" * alphanum ^ 1 * P ")" ) ^ -1
+ * ( S "-#0 +" ) ^ 0
+ * ( digit ^ 1 + P "*" ) ^ -1
+ * ( P "." * ( digit ^ 1 + P "*" ) ) ^ -1
+ * ( S "HlL" ) ^ -1
+ * S "sdfFeExXorgiGauc%" ,
+ 'String.Interpol'
+ )
+% \end{macrocode}
+%
+% \bigskip
+% We can now define the \textsc{lpeg} for the four kinds of strings. It's not
+% possible to use our function~|K| because of the interpolations which must be
+% formatted with another \pkg{piton} style that the rest of the
+% string.\footnote{The interpolations are formatted with the \pkg{piton} style
+% |Interpol.Inside|. The initial value of that style is \texttt{\textbackslash
+% @@\_piton:n} wich means that the interpolations are parsed once again by \pkg{piton}.}
+% \begin{macrocode}
+local SingleShortString =
+ Lc ( "{\\PitonStyle{String.Short}{" )
+ * (
+% \end{macrocode}
+% First, we deal with the f-strings of Python, which are prefixed by |f| or |F|.
+% \begin{macrocode}
+ K ( P "f'" + P "F'" )
+ * ( SingleShortInterpol + SingleShortPureString ) ^ 0
+ * K ( P "'" )
+ +
+% \end{macrocode}
+% Now, we deal with the standard strings of Python, but also the ``raw strings''.
+% \begin{macrocode}
+ K ( P "'" + P "r'" + P "R'" )
+ * ( K ( ( P "\\'" + 1 - S " '\r%" ) ^ 1 )
+ + VisualSpace
+ + PercentInterpol
+ + K ( P "%" )
+ ) ^ 0
+ * K ( P "'" )
+ )
+ * Lc ( "}}" )
+
+local DoubleShortString =
+ Lc ( "{\\PitonStyle{String.Short}{" )
+ * (
+ K ( P "f\"" + P "F\"" )
+ * ( DoubleShortInterpol + DoubleShortPureString ) ^ 0
+ * K ( P "\"" )
+ +
+ K ( P "\"" + P "r\"" + P "R\"" )
+ * ( K ( ( P "\\\"" + 1 - S " \"\r%" ) ^ 1 )
+ + VisualSpace
+ + PercentInterpol
+ + K ( P "%" )
+ ) ^ 0
+ * K ( P "\"" )
+ )
+ * Lc ( "}}" )
+
+local ShortString = SingleShortString + DoubleShortString
+% \end{macrocode}
+%
+% \bigskip
+% \paragraph{Beamer}
+%
+% The following \textsc{lpeg} |BalancedBraces| will be used for the (mandatory)
+% argument of the commands |\only| and \emph{al.} of Beamer. It's necessary to
+% use a \emph{grammar} because that pattern mainly checks the correct nesting of
+% the delimiters (and it's known in the theory of formal languages that this
+% can't be done with regular expressions \emph{stricto sensu} only).
+% \begin{macrocode}
+local BalancedBraces =
+ P { "E" ,
+ E = ( ShortString + ( 1 - S "{}" ) ) ^ 0
+ *
+ (
+ P "{" * V "E" * P "}"
+ * ( ShortString + ( 1 - S "{}" ) ) ^ 0
+ ) ^ 0
+ }
+% \end{macrocode}
+%
+% \bigskip
+% If Beamer is used (or if the key |beamer| is used at load-time), the following
+% LPEG will be redefined.
+% \begin{macrocode}
+local Beamer = P ( false )
+local BeamerBeginEnvironments = P ( true )
+local BeamerEndEnvironments = P ( true )
+local BeamerNamesEnvironments =
+ P "uncoverenv" + P "onlyenv" + P "visibleenv" + P "invisibleenv"
+
+% \end{macrocode}
+%
+% \bigskip
+% \begin{macrocode}
+if piton_beamer
+then
+ Beamer =
+ L ( P "\\pause" * ( P "[" * (1 - P "]") ^ 0 * P "]" ) ^ -1 )
+ +
+% \end{macrocode}
+% We recall that the command |\@@_beamer_command:n| executes the argument
+% corresponding to its argument but also stores it in |\l_@@_beamer_command_str|.
+% That string is used only in the error message ``|cr~not~allowed|'' raised when
+% there is a carriage return in the mandatory argument of that command.
+% \begin{macrocode}
+ ( P "\\uncover" * Lc ( '\\@@_beamer_command:n{uncover}' )
+ + P "\\only" * Lc ( '\\@@_beamer_command:n{only}' )
+ + P "\\alert" * Lc ( '\\@@_beamer_command:n{alert}' )
+ + P "\\visible" * Lc ( '\\@@_beamer_command:n{visible}' )
+ + P "\\invisible" * Lc ( '\\@@_beamer_command:n{invisible}' )
+ + P "\\action" * Lc ( '\\@@_beamer_command:n{action}' )
+ )
+ *
+ L ( ( P "<" * (1 - P ">") ^ 0 * P ">" ) ^ -1 * P "{" )
+ * K ( BalancedBraces , 'Beamer' )
+ * L ( P "}" )
+ +
+ L (
+% \end{macrocode}
+% For |\\alt|, the specification of the overlays (between angular brackets) is mandatory.
+% \begin{macrocode}
+ ( P "\\alt" )
+ * P "<" * (1 - P ">") ^ 0 * P ">"
+ * P "{"
+ )
+ * K ( BalancedBraces , 'Beamer' )
+ * L ( P "}{" )
+ * K ( BalancedBraces , 'Beamer' )
+ * L ( P "}" )
+ +
+ L (
+% \end{macrocode}
+% For |\\alt|, the specification of the overlays (between angular brackets) is mandatory.
+% \begin{macrocode}
+ ( P "\\temporal" )
+ * P "<" * (1 - P ">") ^ 0 * P ">"
+ * P "{"
+ )
+ * K ( BalancedBraces , 'Beamer' )
+ * L ( P "}{" )
+ * K ( BalancedBraces , 'Beamer' )
+ * L ( P "}{" )
+ * K ( BalancedBraces , 'Beamer' )
+ * L ( P "}" )
+% \end{macrocode}
+% Now for the environemnts.
+% \begin{macrocode}
+ BeamerBeginEnvironments =
+ ( space ^ 0 *
+ L
+ (
+ P "\\begin{" * BeamerNamesEnvironments * "}"
+ * ( P "<" * ( 1 - P ">") ^ 0 * P ">" ) ^ -1
+ )
+ * P "\r"
+ ) ^ 0
+ BeamerEndEnvironments =
+ ( space ^ 0 *
+ L ( P "\\end{" * BeamerNamesEnvironments * P "}" )
+ * P "\r"
+ ) ^ 0
+end
+% \end{macrocode}
+%
+% \bigskip
+% \paragraph{EOL}
+%
+% \bigskip
+% The following \textsc{lpeg} |EOL| is for the end of lines.
+% \begin{macrocode}
+local EOL
+if piton_beamer
+then
+EOL =
+ P "\r"
+ *
+ (
+ ( space^0 * -1 )
+ +
+% \end{macrocode}
+% We recall that each line in the Python code we have to parse will be sent
+% back to LaTeX between a pair |\@@_begin_line:| --
+% |\@@_end_line:|\footnote{Remember that the \texttt{\textbackslash
+% @@\_end\_line:} must be explicit because it will be used as marker in order to
+% delimit the argument of the command \texttt{\textbackslash @@\_begin\_line:}}.
+% \begin{macrocode}
+ Lc ( '\\@@_end_line:' )
+ * BeamerEndEnvironments
+ * BeamerBeginEnvironments
+ * Lc ( '\\@@_newline: \\@@_begin_line:' )
+ )
+ *
+ SpaceIndentation ^ 0
+else
+EOL =
+ P "\r"
+ *
+ (
+ ( space^0 * -1 )
+ +
+% \end{macrocode}
+% We recall that each line in the Python code we have to parse will be sent
+% back to LaTeX between a pair |\@@_begin_line:| --
+% |\@@_end_line:|\footnote{Remember that the \texttt{\textbackslash
+% @@\_end\_line:} must be explicit because it will be used as marker in order to
+% delimit the argument of the command \texttt{\textbackslash @@\_begin\_line:}}.
+% \begin{macrocode}
+ Lc ( '\\@@_end_line: \\@@_newline: \\@@_begin_line:' )
+ )
+ *
+ SpaceIndentation ^ 0
+end
+% \end{macrocode}
+%
+%
+% \bigskip
+% \paragraph{The long strings}
+%
+%
+% \bigskip
+% Of course, it's more complicated for ``longs strings'' because, by definition,
+% in Python, those strings may be broken by an end on line (which is catched by
+% the \textsc{lpeg} |EOL|).
+% \begin{macrocode}
+local SingleLongString =
+ Lc "{\\PitonStyle{String.Long}{"
+ * (
+ K ( S "fF" * P "'''" )
+ * ( SingleLongInterpol + SingleLongPureString ) ^ 0
+ * Lc "}}"
+ * (
+ EOL
+ +
+ Lc "{\\PitonStyle{String.Long}{"
+ * ( SingleLongInterpol + SingleLongPureString ) ^ 0
+ * Lc "}}"
+ * EOL
+ ) ^ 0
+ * Lc "{\\PitonStyle{String.Long}{"
+ * ( SingleLongInterpol + SingleLongPureString ) ^ 0
+ +
+ K ( ( S "rR" ) ^ -1 * P "'''"
+ * ( 1 - P "'''" - P "\r" ) ^ 0 )
+ * Lc "}}"
+ * (
+ Lc "{\\PitonStyle{String.Long}{"
+ * K ( ( 1 - P "'''" - P "\r" ) ^ 0 )
+ * Lc "}}"
+ * EOL
+ ) ^ 0
+ * Lc "{\\PitonStyle{String.Long}{"
+ * K ( ( 1 - P "'''" - P "\r" ) ^ 0 )
+ )
+ * K ( P "'''" )
+ * Lc "}}"
+
+
+local DoubleLongString =
+ Lc "{\\PitonStyle{String.Long}{"
+ * (
+ K ( S "fF" * P "\"\"\"" )
+ * ( DoubleLongInterpol + DoubleLongPureString ) ^ 0
+ * Lc "}}"
+ * (
+ EOL
+ +
+ Lc "{\\PitonStyle{String.Long}{"
+ * ( DoubleLongInterpol + DoubleLongPureString ) ^ 0
+ * Lc "}}"
+ * EOL
+ ) ^ 0
+ * Lc "{\\PitonStyle{String.Long}{"
+ * ( DoubleLongInterpol + DoubleLongPureString ) ^ 0
+ +
+ K ( ( S "rR" ) ^ -1 * P "\"\"\""
+ * ( 1 - P "\"\"\"" - P "\r" ) ^ 0 )
+ * Lc "}}"
+ * (
+ Lc "{\\PitonStyle{String.Long}{"
+ * K ( ( 1 - P "\"\"\"" - P "\r" ) ^ 0 )
+ * Lc "}}"
+ * EOL
+ ) ^ 0
+ * Lc "{\\PitonStyle{String.Long}{"
+ * K ( ( 1 - P "\"\"\"" - P "\r" ) ^ 0 )
+ )
+ * K ( P "\"\"\"" )
+ * Lc "}}"
+% \end{macrocode}
+%
+% \begin{macrocode}
+local LongString = SingleLongString + DoubleLongString
+% \end{macrocode}
+%
+% \bigskip
+% We have a \textsc{lpeg} for the Python docstrings. That \textsc{lpeg} will
+% be used in the \textsc{lpeg} |DefFunction| which deals with the whole preamble
+% of a function definition (which begins with |def|).
+% \begin{macrocode}
+local StringDoc =
+ K ( P "\"\"\"" , 'String.Doc' )
+ * ( K ( (1 - P "\"\"\"" - P "\r" ) ^ 0 , 'String.Doc' ) * EOL * Tab ^0 ) ^ 0
+ * K ( ( 1 - P "\"\"\"" - P "\r" ) ^ 0 * P "\"\"\"" , 'String.Doc' )
+% \end{macrocode}
+%
+% \bigskip
+% \paragraph{The comments in the Python listings}
+%
+% We define different \textsc{lpeg} dealing with comments in the Python
+% listings.
+% \begin{macrocode}
+local CommentMath =
+ P "$" * K ( ( 1 - S "$\r" ) ^ 1 , 'Comment.Math' ) * P "$"
+
+local Comment =
+ Lc ( "{\\PitonStyle{Comment}{" )
+ * K ( P "#" )
+ * ( CommentMath + K ( ( 1 - S "$\r" ) ^ 1 ) ) ^ 0
+ * Lc ( "}}" )
+ * ( EOL + -1 )
+% \end{macrocode}
+%
+%
+%
+% \bigskip
+% The following \textsc{lpeg} |CommentLaTeX| is for what is called in that
+% document the ``LaTeX comments''. Since the elements that will be catched must
+% be sent to LaTeX with standard LaTeX catcodes, we put the capture (done by
+% the function~|C|) in a table (by using~|Ct|, which is an alias for |lpeg.Ct|).
+% \begin{macrocode}
+local CommentLaTeX =
+ P(piton.comment_latex)
+ * Lc "{\\PitonStyle{Comment.LaTeX}{\\ignorespaces"
+ * L ( ( 1 - P "\r" ) ^ 0 )
+ * Lc "}}"
+ * ( EOL + -1 )
+% \end{macrocode}
+%
+% \bigskip
+% \paragraph{DefFunction}
+%
+% The following \textsc{lpeg} |Expression| will be used for the parameters in
+% the \emph{argspec} of a Python function. It's necessary to use a \emph{grammar}
+% because that pattern mainly checks the correct nesting of the delimiters
+% (and it's known in the theory of formal languages that this can't be done with
+% regular expressions \emph{stricto sensu} only).
+% \begin{macrocode}
+local Expression =
+ P { "E" ,
+ E = ( 1 - S "{}()[]\r," ) ^ 0
+ * (
+ ( P "{" * V "F" * P "}"
+ + P "(" * V "F" * P ")"
+ + P "[" * V "F" * P "]" ) * ( 1 - S "{}()[]\r," ) ^ 0
+ ) ^ 0 ,
+ F = ( 1 - S "{}()[]\r\"'" ) ^ 0
+ * ( (
+ P "'" * (P "\\'" + 1 - S"'\r" )^0 * P "'"
+ + P "\"" * (P "\\\"" + 1 - S"\"\r" )^0 * P "\""
+ + P "{" * V "F" * P "}"
+ + P "(" * V "F" * P ")"
+ + P "[" * V "F" * P "]"
+ ) * ( 1 - S "{}()[]\r\"'" ) ^ 0 ) ^ 0 ,
+ }
+% \end{macrocode}
+%
+% \bigskip
+% We will now define a \textsc{lpeg} |Params| that will catch the list of
+% parameters (that is to say the \emph{argspec}) in the definition of a Python
+% function. For example, in the line of code
+% \begin{center}
+% \piton{def MyFunction(a,b,x=10,n:int): return n}
+% \end{center}
+% the \textsc{lpeg} |Params| will be used to catch the chunk\enskip |a,b,x=10,n:int|.
+%
+% Or course, a |Params| is simply a comma-separated list of |Param|, and that's
+% why we define first the \textsc{lpeg} |Param|.
+%
+% \medskip
+% \begin{macrocode}
+local Param =
+ SkipSpace * Identifier * SkipSpace
+ * (
+ K ( P "=" * Expression , 'InitialValues' )
+ + K ( P ":" ) * SkipSpace * K ( letter^1 , 'Name.Type' )
+ ) ^ -1
+% \end{macrocode}
+%
+% \medskip
+% \begin{macrocode}
+local Params = ( Param * ( K "," * Param ) ^ 0 ) ^ -1
+% \end{macrocode}
+%
+% \bigskip
+% The following \textsc{lpeg} |DefFunction| catches a keyword |def| and the
+% following name of function \emph{but also everything else until a potential
+% docstring}. That's why this definition of \textsc{lpeg} must occur (in the file
+% |piton.sty|) after the definition of several other \textsc{lpeg} such as
+% |Comment|, |CommentLaTeX|, |Params|, |StringDoc|...
+% \begin{macrocode}
+local DefFunction =
+ K ( P "def" , 'Keyword' )
+ * Space
+ * K ( identifier , 'Name.Function' )
+ * SkipSpace
+ * K ( P "(" ) * Params * K ( P ")" )
+ * SkipSpace
+ * ( K ( P "->" ) * SkipSpace * K ( identifier , 'Name.Type' ) ) ^ -1
+% \end{macrocode}
+% Here, we need a \pkg{piton} style |Post.Function| which will be linked to
+% |\@@_piton:n| (that means that the capture will be parsed once again by
+% \pkg{piton}). We could avoid that kind of trick by using a non-terminal of a
+% grammar but we have probably here a better legibility.
+% \begin{macrocode}
+ * K ( ( 1 - S ":\r" )^0 , 'Post.Function' )
+ * K ( P ":" )
+ * ( SkipSpace
+ * ( EOL + CommentLaTeX + Comment ) -- in all cases, that contains an EOL
+ * Tab ^ 0
+ * SkipSpace
+ * StringDoc ^ 0 -- there may be additionnal docstrings
+ ) ^ -1
+% \end{macrocode}
+% Remark that, in the previous code, |CommentLaTeX| \emph{must} appear
+% before |Comment|: there is no commutativity of the addition for the
+% \emph{parsing expression grammars} (\textsc{peg}).
+%
+% \smallskip
+% If the word |def| is not followed by an identifier and parenthesis, it will be
+% catched as keyword by the \textsc{lpeg} |Keyword| (useful if, for example, the
+% final user wants to speak of the keyword \piton{def}).
+%
+% \bigskip
+% \paragraph{The dictionaries of Python}
+%
+% We have \textsc{lpeg} dealing with dictionaries of Python because, in
+% typesettings of explicit Python dictionnaries, one may prefer to have all the
+% values formatted in black (in order to see more clearly the keys which are
+% usually Python strings). That's why we have a \pkg{piton} style |Dict.Value|.
+%
+% The initial value of that \pkg{piton} style is |\@@_piton:n|, which means that
+% the value of the entry of the dictionary is parsed once again by \pkg{piton}
+% (and nothing special is done for the dictionary). In the following example, we
+% have set the \pkg{piton} style |Dict.Value| to |\color{black}|:
+%
+% \medskip
+% \begingroup
+% \SetPitonStyle{Dict.Value = \color{black}}
+% \piton{mydict = { 'name' : 'Paul', 'sex' : 'male', 'age' : 31 } }
+% \endgroup
+%
+% \medskip
+% At this time, this mechanism works only for explicit dictionaries on a single line!
+%
+% \begin{macrocode}
+local ItemDict =
+ ShortString * SkipSpace * K ( P ":" ) * K ( Expression , 'Dict.Value' )
+
+local ItemOfSet = SkipSpace * ( ItemDict + ShortString ) * SkipSpace
+
+local Set =
+ K ( P "{" )
+ * ItemOfSet * ( K ( P "," ) * ItemOfSet ) ^ 0
+ * K ( P "}" )
+% \end{macrocode}
+%
+%
+% \bigskip
+% \paragraph{Miscellaneous}
+%
+% \begin{macrocode}
+local ExceptionInConsole = Exception * K ( ( 1 - P "\r" ) ^ 0 ) * EOL
+% \end{macrocode}
+%
+% \bigskip
+% \paragraph{The user commands and environments}
+%
+% \begin{macrocode}
+UserEnvironments = P ( true )
+% \end{macrocode}
+%
+% \bigskip
+% \paragraph{The main LPEG}
+%
+% First, the main loop :
+% \begin{macrocode}
+MainLoop =
+ ( ( space^1 * -1 )
+ + EOL
+ + Tab
+ + Space
+ + Escape
+ + CommentLaTeX
+ + Beamer
+ + LongString
+ + Comment
+ + ExceptionInConsole
+ + Set
+ + Delim
+% \end{macrocode}
+% |Operator| must be before |Punct|.
+% \begin{macrocode}
+ + Operator
+ + ShortString
+ + Punct
+ + FromImport
+ + ImportAs
+ + RaiseException
+ + DefFunction
+ + DefClass
+ + Keyword * ( Space + Punct + Delim + EOL + -1)
+ + Decorator
+ + OperatorWord * ( Space + Punct + Delim + EOL + -1)
+ + Builtin * ( Space + Punct + Delim + EOL + -1)
+ + Identifier
+ + Number
+ + Word
+ ) ^ 0
+% \end{macrocode}
+%
+% \bigskip
+% We recall that each line in the Python code to parse will be sent back to
+% LaTeX between a pair |\@@_begin_line:| -- |\@@_end_line:|\footnote{Remember
+% that the \texttt{\textbackslash @@\_end\_line:} must be explicit because it
+% will be used as marker in order to delimit the argument of the command
+% \texttt{\textbackslash @@\_begin\_line:}}.
+% \begin{macrocode}
+local SyntaxPython = P ( true )
+
+function piton.defSyntaxPython()
+ SyntaxPython =
+ Ct (
+ ( ( space - P "\r" ) ^0 * P "\r" ) ^ -1
+ * BeamerBeginEnvironments
+ * UserEnvironments
+ * Lc ( '\\@@_begin_line:' )
+ * SpaceIndentation ^ 0
+ * MainLoop
+ * -1
+ * Lc ( '\\@@_end_line:' )
+ )
+end
+
+piton.defSyntaxPython()
+% \end{macrocode}
+%
+%
+% \bigskip
+% \subsubsection{The function Parse}
+%
+% The function |Parse| is the main function of the package \pkg{piton}.
+% It parses its argument and sends back to LaTeX the code with interlaced
+% formatting LaTeX instructions. In fact, everything is done by the
+% \textsc{lpeg} |SyntaxPython| which returns as capture a Lua table containing
+% data to send to LaTeX.
+%
+% \bigskip
+% \begin{macrocode}
+function piton.Parse(code)
+ local t = SyntaxPython : match ( code ) -- match is a method of the LPEG
+ for _ , s in ipairs(t) do tex.tprint(s) end
+end
+% \end{macrocode}
+%
+%
+% \bigskip
+% The following command will be used by the user command |\piton|.
+% For that command, we have to undo the duplication of the symbols |#|.
+% \begin{macrocode}
+function piton.pitonParse(code)
+ local s = ( Cs ( ( P '##' / '#' + 1 ) ^ 0 ) ) : match ( code )
+ return piton.Parse(s)
+end
+% \end{macrocode}
+%
+% \bigskip
+% The function |ParseFile| will be used by the LaTeX command |\PitonInputFile|.
+% That function merely reads the whole file (that is to say all its lines) and
+% then apply the function~|Parse| to the resulting Lua string.
+% \begin{macrocode}
+function piton.ParseFile(name,first_line,last_line)
+ s = ''
+ local i = 0
+ for line in io.lines(name)
+ do i = i + 1
+ if i >= first_line
+ then s = s .. '\r' .. line
+ end
+ if i >= last_line then break end
+ end
+ piton.Parse(s)
+end
+% \end{macrocode}
+%
+% \bigskip
+% \subsubsection{The preprocessors of the function Parse}
+%
+% We deal now with preprocessors of the function |Parse| which are needed when
+% the ``gobble mechanism'' is used.
+%
+%
+% \bigskip
+% The function |gobble| gobbles $n$ characters on the left of the code. It uses
+% a \textsc{lpeg} that we have to compute dynamically because if depends on the
+% value of~$n$.
+% \begin{macrocode}
+local function gobble(n,code)
+ function concat(acc,new_value)
+ return acc .. new_value
+ end
+ if n==0
+ then return code
+ else
+ return Cf (
+ Cc ( "" ) *
+ ( 1 - P "\r" ) ^ (-n) * C ( ( 1 - P "\r" ) ^ 0 )
+ * ( C ( P "\r" )
+ * ( 1 - P "\r" ) ^ (-n)
+ * C ( ( 1 - P "\r" ) ^ 0 )
+ ) ^ 0 ,
+ concat
+ ) : match ( code )
+ end
+end
+% \end{macrocode}
+%
+%
+%
+% \bigskip
+% The following function |add| will be used in the following \textsc{lpeg}
+% |AutoGobbleLPEG|, |TabsAutoGobbleLPEG| and |EnvGobbleLPEG|.
+% \begin{macrocode}
+local function add(acc,new_value)
+ return acc + new_value
+end
+% \end{macrocode}
+%
+% \bigskip
+% The following \textsc{lpeg} returns as capture the minimal number of spaces at
+% the beginning of the lines of code. The main work is done by two \emph{fold
+% captures} (|lpeg.Cf|), one using |add| and the other (encompassing the
+% previous one) using |math.min| as folding operator.
+% \begin{macrocode}
+local AutoGobbleLPEG =
+ ( space ^ 0 * P "\r" ) ^ -1
+ * Cf (
+ (
+% \end{macrocode}
+% We don't take into account the empty lines (with only spaces).
+% \begin{macrocode}
+ ( P " " ) ^ 0 * P "\r"
+ +
+ Cf ( Cc(0) * ( P " " * Cc(1) ) ^ 0 , add )
+ * ( 1 - P " " ) * ( 1 - P "\r" ) ^ 0 * P "\r"
+ ) ^ 0
+% \end{macrocode}
+% Now for the last line of the Python code...
+% \begin{macrocode}
+ *
+ ( Cf ( Cc(0) * ( P " " * Cc(1) ) ^ 0 , add )
+ * ( 1 - P " " ) * ( 1 - P "\r" ) ^ 0 ) ^ -1 ,
+ math.min
+ )
+% \end{macrocode}
+%
+% \bigskip
+% The following \textsc{lpeg} is similar but works with the indentations.
+% \begin{macrocode}
+local TabsAutoGobbleLPEG =
+ ( space ^ 0 * P "\r" ) ^ -1
+ * Cf (
+ (
+ ( P "\t" ) ^ 0 * P "\r"
+ +
+ Cf ( Cc(0) * ( P "\t" * Cc(1) ) ^ 0 , add )
+ * ( 1 - P "\t" ) * ( 1 - P "\r" ) ^ 0 * P "\r"
+ ) ^ 0
+ *
+ ( Cf ( Cc(0) * ( P "\t" * Cc(1) ) ^ 0 , add )
+ * ( 1 - P "\t" ) * ( 1 - P "\r" ) ^ 0 ) ^ -1 ,
+ math.min
+ )
+% \end{macrocode}
+%
+%
+%
+% \bigskip
+% The following \textsc{lpeg} returns as capture the number of spaces at the
+% last line, that is to say before the |\end{Piton}| (and usually it's also the
+% number of spaces before the corresponding |\begin{Piton}| because that's the
+% traditionnal way to indent in LaTeX). The main work is done by a \emph{fold
+% capture} (|lpeg.Cf|) using the function |add| as folding operator.
+% \begin{macrocode}
+local EnvGobbleLPEG =
+ ( ( 1 - P "\r" ) ^ 0 * P "\r" ) ^ 0
+ * Cf ( Cc(0) * ( P " " * Cc(1) ) ^ 0 , add ) * -1
+% \end{macrocode}
+%
+% \bigskip
+% \begin{macrocode}
+function piton.GobbleParse(n,code)
+ if n==-1
+ then n = AutoGobbleLPEG : match(code)
+ else if n==-2
+ then n = EnvGobbleLPEG : match(code)
+ else if n==-3
+ then n = TabsAutoGobbleLPEG : match(code)
+ end
+ end
+ end
+ piton.Parse(gobble(n,code))
+end
+% \end{macrocode}
+%
+% \bigskip
+% \subsubsection{To count the number of lines}
+%
+% \medskip
+% \begin{macrocode}
+function piton.CountLines(code)
+ local count = 0
+ for i in code : gmatch ( "\r" ) do count = count + 1 end
+ tex.sprint(
+ luatexbase.catcodetables.expl ,
+ '\\int_set:Nn \\l_@@_nb_lines_int {' .. count .. '}' )
+end
+% \end{macrocode}
+%
+% \begin{macrocode}
+function piton.CountNonEmptyLines(code)
+ local count = 0
+ count =
+ ( Cf ( Cc(0) *
+ (
+ ( P " " ) ^ 0 * P "\r"
+ + ( 1 - P "\r" ) ^ 0 * P "\r" * Cc(1)
+ ) ^ 0
+ * (1 - P "\r" ) ^ 0 ,
+ add
+ ) * -1 ) : match (code)
+ tex.sprint(
+ luatexbase.catcodetables.expl ,
+ '\\int_set:Nn \\l_@@_nb_non_empty_lines_int {' .. count .. '}' )
+end
+% \end{macrocode}
+%
+% \bigskip
+% \begin{macrocode}
+function piton.CountLinesFile(name)
+ local count = 0
+ for line in io.lines(name) do count = count + 1 end
+ tex.sprint(
+ luatexbase.catcodetables.expl ,
+ '\\int_set:Nn \\l_@@_nb_lines_int {' .. count .. '}' )
+end
+% \end{macrocode}
+%
+%
+% \bigskip
+% \begin{macrocode}
+function piton.CountNonEmptyLinesFile(name)
+ local count = 0
+ for line in io.lines(name)
+ do if not ( ( ( P " " ) ^ 0 * -1 ) : match ( line ) )
+ then count = count + 1
+ end
+ end
+ tex.sprint(
+ luatexbase.catcodetables.expl ,
+ '\\int_set:Nn \\l_@@_nb_non_empty_lines_int {' .. count .. '}' )
+end
+% \end{macrocode}
+%
+%
+% \bigskip
+% \begin{macrocode}
+\end{luacode*}
+% \end{macrocode}
+%
+%
+% \vspace{1cm}
+% \section{History}
+% \subsection*{Changes between versions 1.1 and 1.2}
+%
+% New keys |break-lines-in-piton| and |break-lines-in-Piton|.
+%
+% New key |show-spaces-in-string| and modification of the key |show-spaces|.
+%
+% When the class \cls{beamer} is used, the environements |{uncoverenv}|,
+% |{onlyenv}|, |{visibleenv}| and |{invisibleenv}|
+%
+%
+% \subsection*{Changes between versions 1.0 and 1.1}
+%
+% The extension \pkg{piton} detects the class \cls{beamer} and activates the
+% commands |\action|, |\alert|, |\invisible|, |\only|, |\uncover| and |\visible|
+% in the environments |{Piton}| when the class \cls{beamer} is used.
+%
+% \subsection*{Changes between versions 0.99 and 1.0}
+%
+% New key |tabs-auto-gobble|.
+%
+% \subsection*{Changes between versions 0.95 and 0.99}
+%
+% New key |break-lines| to allow breaks of the lines of code (and other keys to
+% customize the appearance).
+%
+% \subsection*{Changes between versions 0.9 and 0.95}
+%
+% New key |show-spaces|.
+%
+% The key |left-margin| now accepts the special value |auto|.
+%
+% New key |latex-comment| at load-time and replacement of |##| by |#>|
+%
+% New key |math-comments| at load-time.
+%
+% New keys |first-line| and |last-line| for the command |\InputPitonFile|.
+%
+% \subsection*{Changes between versions 0.8 and 0.9}
+%
+% New key |tab-size|.
+%
+% Integer value for the key |splittable|.
+%
+% \subsection*{Changes between versions 0.7 and 0.8}
+%
+% New keys |footnote| and |footnotehyper| at load-time.
+%
+% New key |left-margin|.
+%
+% \subsection*{Changes between versions 0.6 and 0.7}
+%
+% New keys |resume|, |splittable| and |background-color| in |\PitonOptions|.
+%
+% The file |piton.lua| has been embedded in the file |piton.sty|. That means
+% that the extension \pkg{piton} is now entirely contained in the file |piton.sty|.
+%
+%
+%
+%
+%
+% \end{document}
+%
+%
+% Local Variables:
+% TeX-fold-mode: t
+% TeX-fold-preserve-comments: nil
+% flyspell-mode: nil
+% fill-column: 80
+% End:
+
+
+
+
+
+
|