% \iffalse -*- coding: utf-8 ; -*- \fi % \iffalse meta-comment % % Copyright (C) 2022-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 % %<*batchfile> \begingroup \input l3docstrip.tex \keepsilent \usedir{tex/latex/cascade} \preamble Copyright (C) 2022-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 % % %<@@=piton> %<*STY> \def\myfileversion{2.2b} \def\myfiledate{2023/11/03} % %<*driver> \def\myfileversion{2.2b} \def\myfiledate{2023/11/03} \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,luacolor,lua-ul,upquote} \def\emphase{\bgroup\color{RoyalPurple}\let\next=} \fvset{commandchars=\~\#\@,formatcom=\color{gray}} \captionsetup{labelfont = bf} \usepackage{ragged2e} \usepackage[footnotehyper]{piton} \PitonOptions { splittable = 4 , math-comments, begin-escape = ! , end-escape = ! , begin-escape-math = \( , end-escape-math = \) } \parindent 0pt \skip\footins = 2\bigskipamount \def\CC{{C\nolinebreak[4]\hspace{-.05em}\raisebox{.4ex}{\tiny\bfseries ++}}} \PitonOptions{gobble=2} \EnableCrossrefs \begin{document} \DocInput{piton.dtx} \end{document} % % \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 computer listings in Python, % OCaml, C and SQL 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, OCaml, C or SQL listings and typesets 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} % % % \section{Installation} % % The package \pkg{piton} is contained in two files: |piton.sty| and |piton.lua| % (the LaTeX file |piton.sty| loaded by |\usepackage| will load the Lua file % |piton.lua|). Both files must be in a repertory where LaTeX will be able to % find them, for instance 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{Choice of the computer language} % % In current version, the package \pkg{piton} supports four computer languages: % Python, OCaml, SQL and C (in fact \CC). % % \smallskip % By default, the language used is Python. % % \smallskip % It's possible to change the current language with the command |\PitonOptions| % and its key |language|: |\PitonOptions{language = C}|. % % \smallskip % In what follows, we will speak of Python, but the features described also % apply to the other languages. % % \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 external file. % % It's possible to insert only a part of the file: cf. % part~\ref{part-of-a-file}, p.~\pageref{part-of-a-file}. % % \colorbox{yellow!50}{\textbf{New 2.2}}\enskip The key |path| of the command % |\PitonOptions| specifies a path where the files included by |\PitonInputFile| % will be searched. % \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 (and the % also the character of end on line), % % {\color{cyan} but the command |\|␣ is provided to force the insertion of a space}; % % \item it's not possible to use |%| inside the argument, % % {\color{cyan} but the command |\%| is provided to insert a |%|}; % % \item the braces must be appear by pairs correctly nested % % {\color{cyan} but the commands |\{| and |\}| are also provided for individual braces}; % % \item the LaTeX commands\footnote{That concerns the commands beginning with a % backslash but also the active characters (with catcode equal to 13).} are % fully expanded and not executed, % % {\color{cyan} so it's possible to use |\\| to insert a backslash}. % \end{itemize} % % % The other characters (including |#|, |^|, |_|, |&|, |$| and |@|) % must be inserted without backslash. % % \bigskip %\begin{tabular}{>{\color{gray}}w{l}{75mm}@{\hspace*{1cm}}l} % \omit Examples : \hfil \\ % \noalign{\vskip1mm} % \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{c="#" \ \ \ # an affectation }| & % \piton{c="#" \ \ \ # an affectation } \\ % \verb|\piton{MyDict = {'a': 3, 'b': 4 }}| & % \piton{MyDict = {'a': 3, 'b': 4 }} % \end{tabular} % % \bigskip % 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 % % \begin{tabular}{>{\color{gray}}w{l}{75mm}@{\hspace*{1cm}}l} % \omit Examples : \hfil \\ % \noalign{\vskip1mm} % \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} % % With regard to the font used by \pkg{piton} in its listings, it's only the % current monospaced font. The package \pkg{piton} merely uses internally the % standard LaTeX command |\texttt|. % % \subsection{The keys of 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.} % % These keys may also be applied to an individual environment |{Piton}| (between % square brackets). % % \begin{itemize} % \item The key \Definition{language} speficies which computer language is % considered (that key is case-insensitive). Four values are allowed : % |Python|, |OCaml|, |C| and |SQL|. The initial value is |Python|. % % \item The key \Definition{path} specifies a path where the files included by % |\PitonInputFile| will be searched. % % \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 The key \Definition{line-numbers} activates the line numbering. in the % environments |{Piton}| and in the listings resulting from the use of % |\PitonInputFile|. % % \colorbox{yellow!50}{\textbf{New 2.1}}\enskip In fact, the key |line-numbers| has % several subkeys. % \begin{itemize} % \item With the key \Definition{line-numbers/skip-empty-lines}, the empty lines % are considered as non existent for the line numbering (if the key |/absolute| % is in force, the key |/skip-empty-lines| is no-op in |\PitonInputFile|). The % initial value of that key is |true| (and not |false|).\footnote{For the % language Python, the empty lines in the docstrings are taken into account (by % design).} % \item With the key \Definition{line-numbers/label-empty-lines}, the labels % (that is to say the numbers) of the empty lines are displayed. If the key % |/skip-empty-line| is in force, the clé |/label-empty-lines| is no-op. The % initial value of that key is~|true|. % \item With the key \Definition{line-numbers/absolute}, in the listings % generated in |\PitonInputFile|, the numbers of the lines displayed are % \emph{absolute} (that is to say: they are the numbers of the lines in the % file). That key may be useful when |\PitonInputFile| is used to insert only a % part of the file (cf. part~\ref{part-of-a-file}, p.~\pageref{part-of-a-file}). % The key |/absolute| is no-op in the environments |{Piton}|. % \item The key \Definition{line-numbers/start} requires that the line numbering % begins to the value of the key. % \item With the key \Definition{line-numbers/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{line-numbers/sep} is the horizontal distance between % the numbers of lines (inserted by |line-numbers|) and the beginning of the % lines of code. The initial value is 0.7~em. % \end{itemize} % % For convenience, a mechanism of factorisation of the prefix |line-numbers| is % provided. That means that it is possible, for instance, to write: % \begin{Verbatim} % \PitonOptions % { % line-numbers = % { % skip-empty-lines = false , % label-empty-lines = false , % sep = 1 em % } % } % \end{Verbatim} % % % \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| 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| is in force, 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| (it's % possible to fix the width of that background with the key |width| described % below). % % \smallskip % The key |background-color| supports also as value a \emph{list} of colors. In % this case, the successive rows are colored by using the colors of the list in % a cyclic way. % % \emph{Example} : |\PitonOptions{background-color = {gray!5,white}}| % % The key |background-color| accepts a color defined «on the fly». For example, % it's possible to write |background-color = [cmyk]{0.1,0.05,0,0}|. % % \item With the key \Definition{prompt-background-color}, \pkg{piton} adds a % color background to the lines beginning with the prompt ``|>>>|'' (and its % continuation ``|...|'') characteristic of the Python consoles with % \textsc{repl} (\emph{read-eval-print loop}). % % \item The key \Definition{width} will fix the width of the listing. That width % applies to the colored backgrounds specified by |background-color| and % |prompt-background-color| but also for the automatic breaking of the lines % (when required by |break-lines|: cf.~\ref{line-breaks}, % p.~\pageref{line-breaks}). % % That key may take in as value a numeric value but also the special % value~|min|. With that value, the width will be computed from the maximal % width of the lines of code. Caution: the special value~|min| requires two % compilations with LuaLaTeX\footnote{The maximal width is computed during the % first compilation, written on the |aux| file and re-used during the second % compilation. Several tools such as |latexmk| (used by Overleaf) do % automatically a sufficient number of compilations.}. % % For an example of use of |width=min|, see the section~\ref{example-comments}, % p.~\pageref{example-comments}. % % % \item 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 the package \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|\footnote{cf. \ref{line-breaks} p.~\pageref{line-breaks}} is in % force). % \end{itemize} % % \bigskip % \begingroup % \fvset{commandchars=\~\&\@,formatcom=\small\color{gray}} % \begin{Verbatim} % ~emphase&\begin{Piton}[language=C,line-numbers,auto-gobble,background-color = gray!15]@ % void bubbleSort(int arr[], int n) { % int temp; % int swapped; % for (int i = 0; i < n-1; i++) { % swapped = 0; % for (int j = 0; j < n - i - 1; j++) { % if (arr[j] > arr[j + 1]) { % temp = arr[j]; % arr[j] = arr[j + 1]; % arr[j + 1] = temp; % swapped = 1; % } % } % if (!swapped) break; % } % } % \end{Piton} % \end{Verbatim} % \endgroup % % \begingroup % \PitonOptions{language=C,line-numbers,gobble=6,background-color = gray!15} % \begin{Piton} % void bubbleSort(int arr[], int n) { % int temp; % int swapped; % for (int i = 0; i < n-1; i++) { % swapped = 0; % for (int j = 0; j < n - i - 1; j++) { % if (arr[j] > arr[j + 1]) { % temp = arr[j]; % arr[j] = arr[j + 1]; % arr[j + 1] = temp; % swapped = 1; % } % } % if (!swapped) break; % } % } % \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} % % \subsubsection{Notion of style} % % 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 a 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. That code % uses the command |\highLight| of \pkg{lua-ul} (that package requires also the % package \pkg{luacolor}). % % \begin{verbatim} % \SetPitonStyle{ Name.Function = \bfseries \highLight[red!50] } % \end{verbatim} % % In that example, |\highLight[red!50]| must be considered as the name of a % LaTeX command which takes in exactly one argument, since, usually, it is used % with |\highLight[red!50]{...}|. % % \medskip % \begingroup % \SetPitonStyle % { Name.Function = \bfseries \highLight[red!50] } % With that setting, we will have : \piton{def cube(x) : return x * x * x } % \endgroup % % \bigskip % The different styles, and their use by \pkg{piton} in the different languages % which it supports (Python, OCaml, C and SQL), are described in the part % \ref{Semantic}, starting at the page \pageref{Semantic}. % % % \bigskip % The command |\PitonStyle| takes in as argument the name of a style and allows % to retrieve the value (as a list of LaTeX instructions) of that style. % % \smallskip % For example, it's possible to write |{\PitonStyle{Keyword}{function}}| and we % will have the word {\PitonStyle{Keyword}{function}} formatted as a keyword. % % \smallskip % The syntax |{\PitonStyle{|\textsl{\texttt{style}}|}{...}}| is mandatory in % order to be able to deal both with the semi-global commands and the commands % with arguments which may be present in the definition of the style % \texttt{\textsl{style}}. % % \bigskip % \subsubsection{Global styles and local styles} % % A style may be defined globally with the command |\SetPitonStyle|. That means % that it will apply to all the informatic languages that use that style. % % \bigskip % For example, with the command % \begin{Verbatim} % \SetPitonStyle{Comment = \color{gray}} % \end{Verbatim} % all the comments will be composed in gray in all the listings, whatever % informatic language they use (Python, C, OCaml, etc.). % % \bigskip % \colorbox{yellow!50}{\textbf{New 2.2}}\enskip But it's also possible to define % a style locally for a given informatic langage by providing the name of that % language as optional argument (between square brackets) to the command % |\SetPitonStyle|.\footnote{We recall, that, in the package \pkg{piton}, the % names of the informatic languages are case-insensitive.} % % \bigskip % For example, with the command % \begin{Verbatim} % \SetPitonStyle~emphase#[SQL]@{Keywords = \color[HTML]{006699} \bfseries \MakeUppercase} % \end{Verbatim} % the keywords in the SQL listings will be composed in capital letters, even if % they appear in lower case in the LaTeX source (we recall that, in SQL, the % keywords are case-insensitive). % % \bigskip % As expected, if an informatic language uses a given style and if that style has % no local definition for that language, the global version is used. That notion % of ``global style'' has no link with the notion of global definition in TeX % (the notion of \emph{group} in TeX). % % \bigskip % The package \pkg{piton} itself (that is to say the file |piton.sty|) defines % all the styles globally. % % \bigskip % % \subsubsection{The style UserFunction} % The extension \pkg{piton} provides a special style called~|UserFunction|. That % style applies to the names of the functions previously defined by the user % (for example, in Python, these names are those following the keyword % \piton{def} in a previous Python listing). The initial value of that style is % empty, and, therefore, the names of the functions are formatted as standard % text (in black). However, it's possible to change the value of that style, as % any other style, with the command |\SetPitonStyle|. % % \medskip % In the following example, we fix as value for that style |UserFunction| the % initial value of the style |Name.Function| (which applies to the name of % the functions, \emph{at the moment of their definition}). % % \begingroup % % \begin{Verbatim} % \SetPitonStyle{~emphase#UserFunction@ = \color[HTML]{CC00FF}} % \end{Verbatim} % % \SetPitonStyle{UserFunction = \color[HTML]{CC00FF}} % % \begin{Piton} % def transpose(v,i,j): % x = v[i] % v[i] = v[j] % v[j] = x % % def passe(v): % for in in range(0,len(v)-1): % if v[i] > v[i+1]: % transpose(v,i,i+1) % \end{Piton} % % \endgroup % % As one see, the name |transpose| has been highlighted because it's the name of % a Python function previously defined by the user (hence the name % |UserFunction| for that style). % % \PitonClearUserFunctions[Python] % % \bigskip % \begin{small} % Of course, the list of the names of Python functions previously défined is % kept in the memory of LuaLaTeX (in a global way, that is to say independently % of the TeX groups). The extension \pkg{piton} provides a command to clear that % list : it's the command |\PitonClearUserFunctions|. When it is used without % argument, that command is applied to all the informatic languages used by the % user but it's also possible to use it with an optional argument (between % square brackets) which is a list of informatic languages to which the command % will be applied.\footnote{We remind that, in \pkg{piton}, the name of the % informatic languages are case-insensitive.} % \end{small} % % \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| (of standard LaTeX) or |\NewDocumentEnvironment| (of % LaTeX3). % % 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}{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 (of course, the % package \pkg{tcolorbox} must be loaded). % %\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{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 \Definition{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} % % \label{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 \Definition{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 \Definition{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 \Definition{break-lines} is a conjonction of the two previous keys. % \end{itemize} % % \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 \Definition{indent-broken-lines}, the indentation of a % broken line is respected at carriage return. % % \item The key \Definition{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 \Definition{continuation-symbol} corresponds to the symbol % placed at each carriage return. The initial value is: |+\;| (the command |\;| % inserts a small horizontal space). % % \item The key \Definition{continuation-symbol-on-indentation} corresponds 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 with the following tuning: % % \begin{Verbatim} % \PitonOptions{width=12cm,break-lines,indent-broken-lines,background-color=gray!15} % \end{Verbatim} % % \begin{center} % \PitonOptions{width=12cm,break-lines,indent-broken-lines,background-color=gray!15} % \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{center} % % % \bigskip % \subsection{Insertion of a part of a file} % % \label{part-of-a-file} % % The command |\PitonInputFile| inserts (with formating) the content of a file. % In fact, it's possible to insert only \emph{a part} of that file. Two % mechanisms are provided in this aim. % \begin{itemize} % \item It's possible to specify the part that we want to insert by the numbers % of the lines (in the original file). % \item \colorbox{yellow!50}{\textbf{New 2.1}}\enskip It's also possible to % specify the part to insert with textual markers. % \end{itemize} % In both cases, if we want to number the lines with the numbers of the % lines in the file, we have to use the key |line-numbers/absolute|. % % \subsubsection{With line numbers} % % The command |\PitonInputFile| supports the keys \Definition{first-line} and % \Definition{last-line} in order to insert only the part of file between the % corresponding lines. Not to be confused with the key |line-numbers/start| % which fixes the first line number for the line numbering. In a sens, % |line-numbers/start| deals with the output whereas |first-line| and % |last-line| deal with the input. % % \subsubsection{With textual markers} % % \colorbox{yellow!50}{\textbf{New 2.1}} % % In order to use that feature, we first have to specify the format of the % markers (for the beginning and the end of the part to include) with the keys % \Definition{marker-beginning} and \Definition{marker-end} (usually with the % command |\PitonOptions|). % % % \medskip % Let us take a practical example. % % \medskip % We assume that the file to include contains solutions to exercises of % programmation on the following model. % % \begin{Verbatim}[formatcom=\small\color{gray}] % ~#[Exercise 1] Iterative version % def fibo(n): % if n==0: return 0 % else: % u=0 % v=1 % for i in range(n-1): % w = u+v % u = v % v = w % return v % ~# % \end{Verbatim} % % The markers of the beginning and the end are the strings |#[Exercise 1]| and % |#|. The string ``|Exercise 1|'' will be called the \emph{label} % of the exercise (or of the part of the file to be included). % % In order to specify such markers in \pkg{piton}, we will use the keys % |marker/beginning| and |marker/end| with the following instruction (the % character |#| of the comments of Python must be inserted with the protected % form |\#|). % % \begin{Verbatim} % \PitonOptions{ ~emphase#marker/beginning@ = \~#[~#1] , ~emphase#marker/end@ = \~#<~#1> } % \end{Verbatim} % % As one can see, |marker/beginning| is an expression corresponding to the % mathematical function which transforms the label (here |Exercise 1|) into the % the beginning marker (in the example |#[Exercise 1]|). The string |#1| % corresponds to the occurrences of the argument of that function, which the % classical syntax in TeX. Idem for |marker/end|. % % \bigskip % Now, you only have to use the key \Definition{range} of |\PitonInputFile| to % insert a marked content of the file. % % \smallskip % \begin{Verbatim} % \PitonInputFile[~emphase#range = Exercise 1@]{~textsl#file_name@} % \end{Verbatim} % % \medskip % \begin{Piton} % def fibo(n): % if n==0: return 0 % else: % u=0 % v=1 % for i in range(n-1): % w = u+v % u = v % v = w % return v % \end{Piton} % % \vspace{1cm} % The key \Definition{marker/include-line} requires the insertion of the lines % containing the markers. % % \begin{Verbatim} % \PitonInputFile[~emphase#marker/include-lines@,range = Exercise 1]{~textsl#file_name@} % \end{Verbatim} % % \begin{Piton} % #[Exercise 1] Iterative version % def fibo(n): % if n==0: return 0 % else: % u=0 % v=1 % for i in range(n-1): % w = u+v % u = v % v = w % return v % # % \end{Piton} % % % \bigskip % In fact, there exist also the keys \Definition{begin-range} and % \Definition{end-range} to insert several marked contents at the same time. % % For example, in order to insert the solutions of the exercises~3 to~5, we will % write (if the file has the correct structure!): % % % \begin{Verbatim} % \PitonInputFile[~emphase#begin-range = Exercise 3, end-range = Exercise 5@]{~textsl#file_name@} % \end{Verbatim} % % % % \subsection{Highlighting some identifiers} % % It's possible to require a changement of formating for some identifiers with % the key |identifiers| of |\PitonOptions|.\footnote{This feature is not % available for the language SQL because, in SQL, there is no identifiers : % there are only names of fields and names of tables.} % % \smallskip % That key takes in as argument a value of the following format: % % \quad |{ names = |\textsl{\ttfamily names}|, style = |\textsl{\ttfamily % instructions}| }| % % \begin{itemize} % \item \textsl{\ttfamily names} is a (comma-separated) list of identifier % names; % % \item \textsl{\ttfamily instructions} is a list of LaTeX instructions of the % same type as \pkg{piton} ``styles'' previously presented (cf~\ref{styles} % p.~\pageref{styles}). % \end{itemize} % % \emph{Caution}: Only the identifiers may be concerned by that key. The % keywords and the built-in functions won't be affected, even if their name is % in the list \textsl{\ttfamily names}. % % \begin{Verbatim} % \PitonOptions % { % ~emphase#identifiers@ = % { % ~emphase#names@ = { l1 , l2 } , % ~emphase#style@ = \color{red} % } % } % % \begin{Piton} % def tri(l): % """Segmentation sort""" % if len(l) <= 1: % return l % else: % a = l[0] % l1 = [ x for x in l[1:] if x < a ] % l2 = [ x for x in l[1:] if x >= a ] % return tri(l1) + [a] + tri(l2) % \end{Piton} % \end{Verbatim} % % % \bigskip % % \begingroup % % \PitonOptions % { % identifiers = % { % names = { l1 , l2 } , % style = \color{red} % } % } % % \begin{Piton} % def tri(l): % """Segmentation sort""" % if len(l) <= 1: % return l % else: % a = l[0] % l1 = [ x for x in l[1:] if x < a ] % l2 = [ x for x in l[1:] if x >= a ] % return tri(l1) + [a] + tri(l2) % \end{Piton} % % \endgroup % % \bigskip % By using the key |identifier|, it's possible to add other built-in functions % (or other new keywords, etc.) that will be detected by \pkg{piton}. % % % \begin{Verbatim} % \PitonOptions % { % ~emphase#identifiers@ = % { % names = { cos, sin, tan, floor, ceil, trunc, pow, exp, ln, factorial } , % style = \PitonStyle{Name.Builtin} % } % } % % \begin{Piton} % from math import * % cos(pi/2) % factorial(5) % ceil(-2.3) % floor(5.4) % \end{Piton} % \end{Verbatim} % % \begingroup % % \PitonOptions % { % identifiers = % { % names = { cos, sin, tan, floor, ceil, trunc, pow, exp, ln, factorial } , % style = \PitonStyle{Name.Builtin} % } % } % % \begin{Piton} % from math import * % cos(pi/2) % factorial(5) % ceil(-2.3) % floor(5.4) % \end{Piton} % % % \endgroup % % % \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} % % One should aslo remark that, when the extension \pkg{piton} is used with the % class \cls{beamer}, \pkg{piton} detects in |{Piton}| many commands and % environments of Beamer: cf. \ref{beamer} p.~\pageref{beamer}. % % \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 only in the % preamble of the document, allows to choice the characters which, % preceded by |#|, will be the syntatic marker. % % For example, if the preamble contains the following instruction: % % \quad \verb|\PitonOptions{comment-latex = LaTeX}| % % 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} % % \bigskip % If the user has required line numbers (with the key |line-numbers|), it's % possible to refer to a number of line with the command |\label| used in a % LaTeX comment.\footnote{That feature is implemented by using a redefinition of % the standard command \texttt{\textbackslash label} in the environments % \texttt{\{Piton\}}. Therefore, incompatibilities may occur with extensions % which redefine (globally) that command \texttt{\textbackslash label} (for % example: \pkg{varioref}, \pkg{refcheck}, \pkg{showlabels}, etc.)} % % \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 \Definition{math-comments}, which is % available only in the preamble of the document. % % \medskip % Here is a example, where we have assumed that the preamble of the document % contains the instruction |\PitonOptions{math-comment}|: % % \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''} % % 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 delimiters for that kind of escape. % % In order to use this mechanism, it's necessary to specify the delimiters which % will delimit the escape (one for the beginning and one for the end) by using % the keys \Definition{begin-escape} and \Definition{end-escape}, available only % in the preamble of the document. % % \medskip % In the following example, we assume that the preamble of the document contains % the following instruction: % % \begin{Verbatim} % \PitonOptions{~emphase#begin-escape=!,end-escape=!@} % \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. That example uses the command |\highLight| of \pkg{lua-ul} % (that package requires itself the package \pkg{luacolor}). % \begin{Verbatim} % \begin{Piton} % def fact(n): % if n==0: % return 1 % else: % ~emphase#!\highLight{!@return n*fact(n-1)~emphase#!}!@ % \end{Piton} % \end{Verbatim} % % \begin{Piton} % def fact(n): % if n==0: % return 1 % else: % !\highLight{!return n*fact(n-1)!}! % \end{Piton} % % \bigskip % In fact, in that case, it's probably easier to use the command |\@highLight| % of \pkg{lua-ul}: that command sets a yellow background until the end of the % current TeX group. Since the name of that command contains the character |@|, % it's necessary to define a synonym without |@| in order to be able to use it % directly in |{Piton}|. % \begingroup % \fvset{commandchars=\~\#\+,formatcom=\color{gray}} % \begin{Verbatim} % \makeatletter % ~emphase#\NewCommandCopy{\Yellow}{\@highLight}+ % \makeatother % \end{Verbatim} % \endgroup % % \begin{Verbatim} % \begin{Piton} % def fact(n): % if n==0: % return 1 % else: % ~emphase#!\Yellow!@return n*fact(n-1) % \end{Piton} % \end{Verbatim} % % \makeatletter % \NewCommandCopy{\Yellow}{\@highLight} % \makeatother % % % \begin{Piton} % def fact(n): % if n==0: % return 1 % else: % !\Yellow!return n*fact(n-1) % \end{Piton} % % % % \bigskip % \emph{Caution} : The escape to LaTeX allowed by the |begin-escape| and % |end-escape| 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{The mechanism ``escape-math''} % % The mechanism ``|escape-math|'' is very similar to the mechanism ``|escape|'' % since the only difference is that the elements sent to LaTeX are composed in % the math mode of LaTeX. % % This mechanism is activated with the keys \Definition{begin-escape-math} and % \Definition{end-escape-math} (which are available only in the preamble of the % document). % % Despite the technical similarity, the use of the the mechanism % ``|escape-math|'' is in fact rather different from that of the mechanism % ``|escape|''. Indeed, since the elements are composed in a mathématical mode % of LaTeX, they are, in particular, composed within a TeX group and therefore, % they can't be used to change the formatting of other lexical units. % % In the langages where the character \verb|$| does not play a important role, % it's possible to activate that mechanism ``|escape-math|'' with the character % \verb|$|: % \begin{Verbatim} % \PitonOptions{~emphase#begin-escape-math=$,end-escape-math=$@} % \end{Verbatim} % Remark that the character \verb|$| must \emph{not} be protected by a backslash. % % \bigskip % However, it's probably more prudent to use |\(| et |\)|. % \begin{Verbatim} % \PitonOptions{~emphase#begin-escape-math=\(,end-escape-math=\)@} % \end{Verbatim} % % \bigskip % Here is an example of utilisation. % % \medskip % \begin{Verbatim} % \begin{Piton}[line-numbers] % def arctan(x,n=10): % if ~emphase#\(x < 0\)@ : % return ~emphase#\(-\arctan(-x)\)@ % elif ~emphase#\(x > 1\)@ : % return ~emphase#\(\pi/2 - \arctan(1/x)\)@ % else: % s = ~emphase#\(0\)@ % for ~emphase#\(k\)@ in range(~emphase#\(n\)@): s += ~emphase#\(\smash{\frac{(-1)^k}{2k+1} x^{2k+1}}\)@ % return s % \end{Piton} % \end{Verbatim} % % % \bigskip % % \begin{Piton}[line-numbers] % def arctan(x,n=10): % if \(x < 0\) : % return \(-\arctan(-x)\) % elif \(x > 1\) : % return \(\pi/2 - \arctan(1/x)\) % else: % s = \(0\) % for \(k\) in range(\(n\)): s += \(\smash{\frac{(-1)^k}{2k+1} x^{2k+1}}\) % return s % \end{Piton} % % % \subsection{Behaviour in the class Beamer} % % \label{beamer} % % \emph{First remark}\par\nobreak % 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|, i.e. beginning with % |\begin{frame}[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).} % % % \bigskip % When the package \pkg{piton} is used within the class % \cls{beamer}\footnote{The extension \pkg{piton} detects the class \cls{beamer} % and the package \pkg{beamerarticle} if it is loaded previously % 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 behaviour of \pkg{piton} is slightly % modified, as described now. % % \subsubsection{\{Piton\} et \textbackslash PitonInputFile are % ``overlay-aware''} % % When \pkg{piton} is used in the class \cls{beamer}, the environment |{Piton}| % and the command |\PitonInputFile| accept the optional argument |<...>| of % Beamer for the overlays which are involved. % % For example, it's possible to write: % % \begin{Verbatim} % \begin{Piton}~emphase#<2-5>@ % ... % \end{Piton} % \end{Verbatim} % % and % % \begin{Verbatim} % \PitonInputFile~emphase#<2-5>@{my_file.py} % \end{Verbatim} % % \subsubsection{Commands of Beamer allowed in \{Piton\} and \textbackslash PitonInputFile} % % When \pkg{piton} is used in the class \cls{beamer} , the following commands of % \cls{beamer} (classified upon their number of arguments) are automatically % detected in the environments |{Piton}| (and in the listings processed by % |\PitonInputFile|): % \begin{itemize} % \item no mandatory argument : |\pause|\footnote{One should remark that it's % also possible to use the command \texttt{\textbackslash pause} in a ``LaTeX % comment'', that is to say by writing \texttt{\#> \textbackslash pause}. By % this way, if the Python code is copied, it's still executable by Python}. ; % \item one mandatory argument : |\action|, |\alert|, |\invisible|, |\only|, |\uncover| and |\visible| ; % \item two mandatory arguments : |\alt| ; % \item three mandatory arguments : |\temporal|. % \end{itemize} % \medskip % In the mandatory arguments of these commands, the braces must be balanced. % However, the braces included 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. % % \medskip % Regarding the fonctions |\alt| and |\temporal| there should be no carriage % returns in the mandatory arguments of these functions. % % \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 % \subsubsection{Environments of Beamer allowed in \{Piton\} and \textbackslash PitonInputFile} % % When \pkg{piton} is used in the class \pkg{beamer}, the following environments % of Beamer are directly detected in the environments |{Piton}| (and in the % listings processed by |\PitonInputFile|): |{actionenv}|, |{alertenv}|, % |{invisibleenv}|, |{onlyenv}|, |{uncoverenv}| and |{visibleenv}|. % % 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} % % % \vspace{1cm} % \textbf{Remark concerning the command \textbackslash alert and the environment % \{alertenv\} of Beamer}\par\nobreak % % \smallskip % Beamer provides an easy way to change the color used by the environment % |{alertenv}| (and by the command |\alert| which relies upon it) to highlight % its argument. Here is an example: % % \begin{Verbatim} % \setbeamercolor{~emphase#alerted text@}{fg=blue} % \end{Verbatim} % % However, when used inside an environment |{Piton}|, such tuning will probably % not be the best choice because \pkg{piton} will, by design, change (most of % the time) the color the different elements of text. One may prefer an environment % |{alertenv}| that will change the background color for the elements to be % hightlighted. % % \smallskip % Here is a code that will do that job and add a yellow background. That code % uses the command |\@highLight| of \pkg{lua-ul} (that extension requires also % the package \pkg{luacolor}). % % \begingroup % \fvset{commandchars=\~\#\+,formatcom=\color{gray}} % \begin{Verbatim} % \setbeamercolor{alerted text}{bg=yellow!50} % \makeatletter % \AddToHook{env/Piton/begin} % {\renewenvironment<>{alertenv}{\only~#1{~emphase#\@highLight+[alerted text.bg]}}{}} % \makeatother % \end{Verbatim} % \endgroup % % That code redefines locally the environment |{alertenv}| within the % environments |{Piton}| (we recall that the command |\alert| relies upon that % environment |{alertenv}|). % % % \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|. % % 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) #> (recursive call) % elif x > 1: % return pi/2 - arctan(1/x) #> (other 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,left-margin = auto, line-numbers} % \begin{Piton} % def arctan(x,n=10): % if x < 0: % return -arctan(-x) #> (recursive call) % elif x > 1: % return pi/2 - arctan(1/x) #> (other recursive call) % 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) #> recursive call % elif x > 1: % return pi/2 - arctan(1/x) #> other 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} % \SetPitonStyle{Comment.LaTeX = \hfill \normalfont\color{gray}} % \begin{Piton} % def arctan(x,n=10): % if x < 0: % return -arctan(-x) #> recursive call % elif x > 1: % return pi/2 - arctan(1/x) #> another recursive call % 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 with the key |width|. In the % following example, we use the key |width| with the special value~|min|. % % % \begingroup % \fvset{commandchars=\~\&\@,formatcom=\small\color{gray}} % \begin{Verbatim} % \PitonOptions{background-color=gray!10, width=min} % ~emphase&\NewDocumentCommand{\MyLaTeXCommand}{m}{\hfill \normalfont\itshape\rlap{\quad #1}}@ % ~emphase&\SetPitonStyle{Comment.LaTeX = \MyLaTeXCommand}@ % \begin{Piton} % def arctan(x,n=10): % if x < 0: % return -arctan(-x) #> recursive call % elif x > 1: % return pi/2 - arctan(1/x) #> another recursive call % 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{background-color=gray!10, width=min} % \NewDocumentCommand{\MyLaTeXCommand}{m}{\hfill \normalfont\itshape\rlap{\quad #1}} % \SetPitonStyle{Comment.LaTeX = \MyLaTeXCommand} % \begin{Piton} % def arctan(x,n=10): % if x < 0: % return -arctan(-x) #> recursive call % elif x > 1: % return pi/2 - arctan(1/x) #> another recursive call % else: % s = 0 % for k in range(n): % s += (-1)**k/(2*k+1)*x**(2*k+1) % return s % \end{Piton} % \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 % % % % \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}. % % That tuning uses the command |\highLight| of \pkg{lua-ul} (that package % requires itself the package \pkg{luacolor}). % % \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 \highLight[gray!20] , % Comment = \color{gray} , % Comment.LaTeX = \normalfont \color{gray}, % Keyword = \bfseries , % Name.Namespace = , % Name.Class = , % Name.Type = , % InitialValues = \color{gray} % } % \end{Verbatim} % % In that tuning, many values given to the keys are empty: that means that the % corresponding style won't insert any formating instruction (the element will % be composed in the standard color, usually in black, etc.). Nevertheless, % those entries are mandatory because the initial value of those keys in % \pkg{piton} is \emph{not} empty. % % \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 \highLight[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) # recursive call % 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 (for technical reasons, the |!| is mandatory in the signature % of the environment). % % % \begin{Verbatim} % \ExplSyntaxOn % \NewDocumentEnvironment { ~emphase#PitonExecute@ } { ! O { } } % the ! is mandatory % { % \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{The styles for the different computer languages} % % \label{Semantic} % % % \subsection{The language Python} % % In \pkg{piton}, the default language is Python. If necessary, it's possible to % come back to the language Python with |\PitonOptions{language=Python}|. % % \bigskip % % The initial settings % done by \pkg{piton} in |piton.sty| are inspired by the style \pkg{manni} de % Pygments, as applied by Pygments to the language Python.\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}. It's possible to have the same color in \texttt{\{Piton\}} % with the instruction \texttt{\textbackslash PitonOptions\{background-color = % [HTML]\{F0F3F3\}\}}.} % % \vspace{1cm} % % \begin{center} % \begin{tabularx}{0.9\textwidth}{@{}>{\ttfamily}l>{\raggedright\arraybackslash}X@{}} % \toprule % \normalfont Style & Use \\ % \midrule % Number & the numbers \\ % String.Short & the short strings (entre \texttt{'} ou \texttt{"}) \\ % String.Long & the long strings (entre \texttt{'''} ou \texttt{"""}) excepted % the doc-strings (governed by |String.Doc|)\\ % String & that key fixes both |String.Short| et |String.Long| \\ % String.Doc & the doc-strings (only with |"""| following PEP~257) \\ % String.Interpol & the syntactic elements of the fields of the f-strings % (that is to say the characters \texttt{\{} et \texttt{\}}); that style % inherits for the styles |String.Short| and |String.Long| (according the kind % of string where the interpolation appears) \\ % Interpol.Inside & the content of the interpolations in the f-strings (that % is to say the elements between \texttt{\{} and~\texttt{\}}); if the final % user has not set that key, those elements will be formatted by \pkg{piton} % as done for any Python code. \\ % Operator & the following operators: \texttt{!= == << >> - \~{} + / * \% = < > \& .} \verb+|+ \verb|@| \\ % Operator.Word & the following operators: |in|, |is|, |and|, |or| et |not| \\ % Name.Builtin & almost all the functions predefined by Python \\ % Name.Decorator & the decorators (instructions beginning by \verb|@|) \\ % Name.Namespace & the name of the modules \\ % Name.Class & the name of the Python classes defined by the user \emph{at their point of definition} (with the keyword |class|) \\ % Name.Function & the name of the Python functions defined by the user \emph{at their % point of definition} (with the keyword |def|) \\ % UserFunction & the name of the Python functions previously defined by the user % (the initial value of that parameter is empty and, hence, these % elements are drawn, by default, in the current color, usually black) \\ % Exception & les exceptions prédéfinies (ex.: \texttt{SyntaxError}) \\ % InitialValues & the initial values (and the preceding symbol |=|) of the % optional arguments in the definitions of functions; if the final % user has not set that key, those elements will be formatted by \pkg{piton} % as done for any Python code. \\ % Comment & the comments beginning with \texttt{\#} \\ % Comment.LaTeX & the comments beginning with \texttt{\#>}, which are composed by % \pkg{piton} as LaTeX code (merely named ``LaTeX comments'' in this document) \\ % Keyword.Constant & |True|, |False| et |None| \\ % Keyword & the following keywords: % \ttfamily assert, break, case, continue, del, % elif, else, except, exec, finally, for, from, % global, if, import, lambda, non local, % pass, raise, return, try, while, % with, yield et yield from.\\ % \bottomrule % \end{tabularx} % \end{center} % % % \newpage % % \subsection{The language OCaml} % % It's possible to switch to the language |OCaml| with |\PitonOptions{language = OCaml}|. % % \bigskip % It's also possible to set the language OCaml for an individual environment |{Piton}|. % % % \begin{Verbatim} % \begin{Piton}~emphase#[language=OCaml]@ % ... % \end{Piton} % \end{Verbatim} % % \bigskip % The option exists also for |\PitonInputFile| : |\PitonInputFile[language=OCaml]{...}| % % \vspace{1cm} % % % \begin{center} % \begin{tabularx}{0.9\textwidth}{@{}>{\ttfamily}l>{\raggedright\arraybackslash}X@{}} % \toprule % \normalfont Style & Use \\ % \midrule % Number & the numbers \\ % String.Short & the characters (between \texttt{'}) \\ % String.Long & the strings, between |"| but also the \emph{quoted-strings} \\ % String & that key fixes both |String.Short| and |String.Long| \\ % Operator & les opérateurs, en particulier |+|, |-|, |/|, |*|, |@|, |!=|, |==|, |&&| \\ % Operator.Word & les opérateurs suivants : |and|, |asr|, |land|, |lor|, |lsl|, |lxor|, |mod| et |or| \\ % Name.Builtin & les fonctions |not|, |incr|, |decr|, |fst| et |snd| \\ % Name.Type & the name of a type of OCaml \\ % Name.Field & the name of a field of a module \\ % Name.Constructor & the name of the constructors of types (which begins by a capital) \\ % Name.Module & the name of the modules \\ % Name.Function & the name of the Python functions defined by the user \emph{at their % point of definition} (with the keyword |let|) \\ % UserFunction & the name of the OCaml functions previously defined by the user % (the initial value of that parameter is empty and these % elements are drawn in the current color, usually black) \\ % Exception & the predefined exceptions (eg : |End_of_File|) \\ % TypeParameter & the parameters of the types \\ % Comment & the comments, between |(*| et |*)|; these comments may be nested \\ % Keyword.Constant & |true| et |false| \\ % Keyword & the following keywords: % |assert|, |as|, |begin|, |class|, |constraint|, |done|, % |downto|, |do|, |else|, |end|, |exception|, |external|, % |for|, |function|, |functor|, |fun| , |if| % |include|, |inherit|, |initializer|, |in| , |lazy|, |let|, % |match|, |method|, |module|, |mutable|, |new|, |object|, % |of|, |open|, |private|, |raise|, |rec|, |sig|, % |struct|, |then|, |to|, |try|, |type|, % |value|, |val|, |virtual|, |when|, |while| and |with| \\ % \bottomrule % \end{tabularx} % \end{center} % % \newpage % % \subsection[The language C (and C++)]{The language C (and \CC)} % % % It's possible to switch to the language |C| with |\PitonOptions{language = C}|. % % \bigskip % It's also possible to set the language C for an individual environment |{Piton}|. % % % \begin{Verbatim} % \begin{Piton}~emphase#[language=C]@ % ... % \end{Piton} % \end{Verbatim} % % \bigskip % The option exists also for |\PitonInputFile| : |\PitonInputFile[language=C]{...}| % % \vspace{1cm} % % \begin{center} % \begin{tabularx}{0.9\textwidth}{@{}>{\ttfamily}l>{\raggedright\arraybackslash}X@{}} % \toprule % \normalfont Style & Use \\ % \midrule % Number & the numbers \\ % String.Long & the strings (between \texttt{"}) \\ % String.Interpol & the elements \texttt{\%d}, \texttt{\%i}, \texttt{\%f}, % \texttt{\%c}, etc. in the strings; that style inherits from the style |String.Long| \\ % Operator & the following operators : \texttt{!= == << >> - \~{} + / * \% = < > \& .} \verb+|+ \verb|@| \\ % Name.Type & the following predefined types: % |bool|, |char|, |char16_t|, |char32_t|, |double|, |float|, |int|, |int8_t|, |int16_t|, |int32_t|, % |int64_t|, |long|, |short|, |signed|, |unsigned|, |void| et |wchar_t| \\ % Name.Builtin & the following predefined functions: |printf|, |scanf|, |malloc|, |sizeof| and |alignof| \\ % Name.Class & le nom des classes au moment de leur définition, c'est-à-dire % après le mot-clé \verb|class| \\ % Name.Function & the name of the Python functions defined by the user \emph{at their % point of definition} (with the keyword |let|) \\ % UserFunction & the name of the Python functions previously defined by the user % (the initial value of that parameter is empty and these % elements are drawn in the current color, usually black) \\ % Preproc & the instructions of the preprocessor (beginning par |#|) \\ % Comment & the comments (beginning by \texttt{//} or between |/*| and |*/|) \\ % Comment.LaTeX & the comments beginning by \texttt{//>} which are composed by % \pkg{piton} as LaTeX code (merely named ``LaTeX comments'' in this document) \\ % Keyword.Constant & |default|, |false|, |NULL|, |nullptr| and |true| \\ % Keyword & the following keywords: % |alignas|, |asm|, |auto|, |break|, |case|, |catch|, |class|, % |constexpr|, |const|, |continue|, |decltype|, |do|, |else|, |enum|, % |extern|, |for|, |goto|, |if|, |nexcept|, |private|, |public|, |register|, |restricted|, |try|, % |return|, |static|, |static_assert|, |struct|, |switch|, |thread_local|, |throw|, % |typedef|, |union|, |using|, |virtual|, |volatile| and |while| % \\ % \bottomrule % \end{tabularx} % \end{center} % % \newpage % % \subsection{The language SQL} % % % It's possible to switch to the language |SQL| with |\PitonOptions{language = SQL}|. % % \bigskip % It's also possible to set the language SQL for an individual environment |{Piton}|. % % % \begin{Verbatim} % \begin{Piton}~emphase#[language=SQL]@ % ... % \end{Piton} % \end{Verbatim} % % \bigskip % The option exists also for |\PitonInputFile| : |\PitonInputFile[language=SQL]{...}| % % % \vspace{1cm} % % \begin{center} % \begin{tabularx}{0.9\textwidth}{@{}>{\ttfamily}l>{\raggedright\arraybackslash}X@{}} % \toprule % \normalfont Style & Use \\ % \midrule % Number & the numbers \\ % String.Long & the strings (between \texttt{'} and not \texttt{"} because the % elements between \texttt{"} are names of fields and formatted with |Name.Field|) \\ % Operator & the following operators : \texttt{= != <> >= > < <= * + / } \\ % Name.Table & the names of the tables \\ % Name.Field & the names of the fields of the tables \\ % Name.Builtin & the following built-in functions (their names are \emph{not} case-sensitive): % |avg|, |count|, |char_lenght|, |concat|, |curdate|, |current_date|, % |date_format|, |day|, |lower|, |ltrim|, |max|, |min|, |month|, |now|, % |rank|, |round|, |rtrim|, |substring|, |sum|, |upper| and |year|. \\ % Comment & the comments (beginning by \texttt{--} or between |/*| and |*/|) \\ % Comment.LaTeX & the comments beginning by \texttt{-->} which are composed by % \pkg{piton} as LaTeX code (merely named ``LaTeX comments'' in this document) \\ % Keyword & the following keywords (their names are \emph{not} case-sensitive): % |add|, |after|, |all|, |alter|, |and|, |as|, |asc|, |between|, |by|, % |change|, |column|, |create|, |cross join|, |delete|, |desc|, |distinct|, % |drop|, |from|, |group|, |having|, |in|, |inner|, |insert|, |into|, |is|, % |join|, |left|, |like|, |limit|, |merge|, |not|, |null|, |on|, |or|, % |order|, |over|, |right|, |select|, |set|, |table|, |then|, |truncate|, % |union|, |update|, |values|, |when|, |where| and |with|. \\ % \bottomrule % \end{tabularx} % \end{center} % % % \newpage % % \section{Implementation} % % \medskip % The development of the extension \pkg{piton} is done on the following GitHub % depot: % % \verb|https://github.com/fpantigny/piton| % % \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 |python|. 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} |python| 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} %<*STY> \NeedsTeXFormat{LaTeX2e} \RequirePackage{l3keys2e} \ProvidesExplPackage {piton} {\myfiledate} {\myfileversion} {Highlight Python codes with LPEG on LuaLaTeX} % \end{macrocode} % % \bigskip % \begin{macrocode} \cs_new_protected:Npn \@@_error:n { \msg_error:nn { piton } } \cs_new_protected:Npn \@@_warning:n { \msg_warning:nn { piton } } \cs_new_protected:Npn \@@_error:nn { \msg_error:nnn { piton } } \cs_new_protected:Npn \@@_error:nnn { \msg_error:nnnn { piton } } \cs_new_protected:Npn \@@_fatal:n { \msg_fatal:nn { piton } } \cs_new_protected:Npn \@@_fatal:nn { \msg_fatal:nnn { piton } } \cs_new_protected:Npn \@@_msg_new:nn { \msg_new:nnn { piton } } % \end{macrocode} % % \bigskip % \begin{macrocode} \@@_msg_new:nn { LuaLaTeX~mandatory } { LuaLaTeX~is~mandatory.\\ The~package~'piton'~requires~the~engine~LuaLaTeX.\\ \str_if_eq:VnT \c_sys_jobname_str { output } { If~you~use~Overleaf,~you~can~switch~to~LuaLaTeX~in~the~"Menu". \\} If~you~go~on,~the~package~'piton'~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 % \begin{macrocode} \@@_msg_new:nn { piton.lua~not~found } { The~file~'piton.lua'~can't~be~found.\\ The package~'piton'~won't~be~loaded. } % \end{macrocode} % % \bigskip % \begin{macrocode} \file_if_exist:nF { piton.lua } { \msg_critical:nn { piton } { piton.lua~not~found } } % \end{macrocode} % % % \bigskip % The boolean |\g_@@_footnotehyper_bool| will indicate if the option % |footnotehyper| is used. % \begin{macrocode} \bool_new:N \g_@@_footnotehyper_bool % \end{macrocode} % % \medskip % The boolean |\g_@@_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 \g_@@_footnote_bool % \end{macrocode} % % \medskip % The following boolean corresponds to the key |math-comments| (only at load-time). % \begin{macrocode} \bool_new:N \g_@@_math_comments_bool % \end{macrocode} % % \medskip % \begin{macrocode} \bool_new:N \g_@@_beamer_bool \tl_new:N \g_@@_escape_inside_tl % \end{macrocode} % % \bigskip % We define a set of keys for the options at load-time. % \begin{macrocode} \keys_define:nn { piton / package } { footnote .bool_gset:N = \g_@@_footnote_bool , footnotehyper .bool_gset:N = \g_@@_footnotehyper_bool , beamer .bool_gset:N = \g_@@_beamer_bool , beamer .default:n = true , escape-inside .code:n = \@@_error:n { key-escape-inside-deleted } , math-comments .code:n = \@@_error:n { moved~to~preamble } , comment-latex .code:n = \@@_error:n { moved~to~preamble } , unknown .code:n = \@@_error:n { Unknown~key~for~package } } % \end{macrocode} % % \bigskip % \begin{macrocode} \@@_msg_new:nn { key-escape-inside-deleted } { The~key~'escape-inside'~has~been~deleted.~You~must~now~use~ the~keys~'begin-escape'~and~'end-escape'~in~ \token_to_str:N \PitonOptions.\\ That~key~will~be~ignored. } % \end{macrocode} % % \bigskip % \begin{macrocode} \@@_msg_new:nn { moved~to~preamble } { The~key~'\l_keys_key_str'~*must*~now~be~used~with~ \token_to_str:N \PitonOptions`in~the~preamble~of~your~ document.\\ That~key~will~be~ignored. } % \end{macrocode} % % \begin{macrocode} \@@_msg_new:nn { Unknown~key~for~package } { Unknown~key.\\ You~have~used~the~key~'\l_keys_key_str'~but~the~only~keys~available~here~ are~'beamer',~'footnote',~'footnotehyper'.~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} \@ifclassloaded { beamer } { \bool_gset_true:N \g_@@_beamer_bool } { } \@ifpackageloaded { beamerarticle } { \bool_gset_true:N \g_@@_beamer_bool } { } \bool_if:NT \g_@@_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:nn { xcolor~not~loaded } { xcolor~not~loaded \\ The~package~'xcolor'~is~required~by~'piton'.\\ This~error~is~fatal. } % \end{macrocode} % % % \begin{macrocode} \@@_msg_new:nn { 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:nn { 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 \g_@@_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_gset_false:N \g_@@_footnote_bool } { \@ifpackageloaded { footnotehyper } { \@@_error:n { footnote~with~footnotehyper~package } } { \usepackage { footnote } } } } % \end{macrocode} % % \begin{macrocode} \bool_if:NT \g_@@_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_gset_false:N \g_@@_footnote_bool } { \@ifpackageloaded { footnote } { \@@_error:n { footnotehyper~with~footnote~package } } { \usepackage { footnotehyper } } \bool_gset_true:N \g_@@_footnote_bool } } % \end{macrocode} % The flag |\g_@@_footnote_bool| is raised and so, we will only have to test % |\g_@@_footnote_bool| in order to know if we have to insert an environment % |{savenotes}|. % % \bigskip % \begin{macrocode} \lua_now:n { piton = piton~or { } } % \end{macrocode} % % \bigskip % \subsubsection{Parameters and technical definitions} % % The following string will contain the name of the informatic language % considered (the initial value is |python|). % % \begin{macrocode} \str_new:N \l_@@_language_str \str_set:Nn \l_@@_language_str { python } % \end{macrocode} % % \medskip % \begin{macrocode} \str_new:N \l_@@_path_str % \end{macrocode} % % % \medskip % In order to have a better control over the keys. % \begin{macrocode} \bool_new:N \l_@@_in_PitonOptions_bool \bool_new:N \l_@@_in_PitonInputFile_bool % \end{macrocode} % % \medskip % The following flag will be raised in the |\AtBeginDocument|. % \begin{macrocode} \bool_new:N \g_@@_in_document_bool % \end{macrocode} % % \medskip % 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 contain 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} \clist_new:N \l_@@_bg_color_clist % \end{macrocode} % % \medskip % The package \pkg{piton} will also detect the lines of code which correspond to % the user input in a Python console, that is to say the lines of code beginning % with |>>>| and |...|. It's possible, with the key |prompt-background-color|, % to require a background for these lines of code (and the other lines of code % will have the standard background color specified by |background-color|). % \begin{macrocode} \tl_new:N \l_@@_prompt_bg_color_tl % \end{macrocode} % % \medskip % The following parameters correspond to the keys |begin-range| and |end-range| of % the command |\PitonInputFile|. % \begin{macrocode} \str_new:N \l_@@_begin_range_str \str_new:N \l_@@_end_range_str % \end{macrocode} % % \medskip % The argument of |\PitonInputFile|. % \begin{macrocode} \str_new:N \l_@@_file_name_str % \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} % % \bigskip % The following dimension will be the width of the listing constructed by % |{Piton}| or |\PitonInputFile|. % \begin{itemize} % \item If the user uses the key |width| of |\PitonOptions| with a numerical % value, that value will be stored in |\l_@@_width_dim|. % \item If the user uses the key |width| with the special value~|min|, the % dimension |\l_@@_width_dim| will, \emph{in the second run}, be computed from % the value of |\l_@@_line_width_dim| stored in the |aux| file % (computed during the first run the maximal width of the lines of the listing). % During the first run, |\l_@@_width_line_dim| will be set equal to |\linewidth|. % \item Elsewhere, |\l_@@_width_dim| will be set at the beginning of the listing % (in |\@@_pre_env:|) equal to the current value of |\linewidth|. % \end{itemize} % % \begin{macrocode} \dim_new:N \l_@@_width_dim % \end{macrocode} % % \medskip % We will also use another dimension called |\l_@@_line_width_dim|. That % will the width of the actual lines of code. That dimension may be lower than % the whole |\l_@@_width_dim| because we have to take into account the value of % |\l_@@_left_margin_dim| (for the numbers of lines when |line-numbers| is in % force) and another small margin when a background color is used (with the key % |background-color|). % \begin{macrocode} \dim_new:N \l_@@_line_width_dim % \end{macrocode} % % \medskip % The following flag will be raised with the key |width| is used with the % special value |min|. % \begin{macrocode} \bool_new:N \l_@@_width_min_bool % \end{macrocode} % % \medskip % If the key |width| is used with the special value~|min|, we will compute the % maximal width of the lines of an environment |{Piton}| in |\g_@@_tmp_width_dim| % because we need it for the case of the key |width| is used with the spacial % value |min|. We need a global variable because, when the key |footnote| is in % force, each line when be composed in an environment |{savenotes}| and we need % to exit our |\g_@@_tmp_width_dim| from that environment. % \begin{macrocode} \dim_new:N \g_@@_tmp_width_dim % \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 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 following dimension corresponds to the key |numbers-sep| of % |\PitonOptions|. % \begin{macrocode} \dim_new:N \l_@@_numbers_sep_dim \dim_set:Nn \l_@@_numbers_sep_dim { 0.7 em } % \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 % Be careful. The following sequence |\g_@@_languages_seq| is not the list of % the languages supported by \pkg{piton}. It's the list of the languages for % which at least a user function has been defined. We need that sequence only % for the command |\PitonClearUserFunctions| when it is used without its % optional argument: it must clear all the list of languages for which at least % a user function has been defined. % \begin{macrocode} \seq_new:N \g_@@_languages_seq % \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 % In the environment |{Piton}|, the command |\label| will be linked to the % following command. % \begin{macrocode} \cs_new_protected:Npn \@@_label:n #1 { \bool_if:NTF \l_@@_line_numbers_bool { \@bsphack \protected@write \@auxout { } { \string \newlabel { #1 } { % \end{macrocode} % Remember that the content of a line is typeset in a box \emph{before} the % composition of the potential number of line. % \begin{macrocode} { \int_eval:n { \g_@@_visual_line_int + 1 } } { \thepage } } } \@esphack } { \@@_error:n { label~with~lines~numbers } } } % \end{macrocode} % % \bigskip % The following commands corresponds to the keys |marker/beginning| and % |marker/end|. The values of that keys are functions that will be applied to % the ``\emph{range}'' specified by the final user in an individual % |\PitonInputFile|. They will construct the markers used to find textually in % the external file loaded by \pkg{piton} the part which must be included (and % formatted). % \begin{macrocode} \cs_new_protected:Npn \@@_marker_beginning:n #1 { } \cs_new_protected:Npn \@@_marker_end:n #1 { } % \end{macrocode} % % % \bigskip % The following commands are a easy way to insert safely braces (|{| and |}|) in % the TeX flow. % \begin{macrocode} \cs_new_protected:Npn \@@_open_brace: { \directlua { piton.open_brace() } } \cs_new_protected:Npn \@@_close_brace: { \directlua { piton.close_brace() } } % \end{macrocode} % % \bigskip % The following token list will be evaluated at the beginning of % |\@@_begin_line:|... |\@@_end_line:| and cleared at the end. It will be used % by LPEG acting between the lines of the Python code in order to add % instructions to be executed at the beginning of the line. % \begin{macrocode} \tl_new:N \g_@@_begin_line_hook_tl % \end{macrocode} % % \smallskip % For example, the LPEG |Prompt| will trigger the following command which will % insert an instruction in the hook |\g_@@_begin_line_hook| to specify that a % background must be inserted to the current line of code. % \begin{macrocode} \cs_new_protected:Npn \@@_prompt: { \tl_gset:Nn \g_@@_begin_line_hook_tl { \tl_if_empty:NF \l_@@_prompt_bg_color_tl % added 2023-04-24 { \clist_set:NV \l_@@_bg_color_clist \l_@@_prompt_bg_color_tl } } } % \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+0020 (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 } % \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_line:| is a % LaTeX command that we will define now but |\@@_end_line:| is only a syntactic % marker that has no definition. % % \begin{macrocode} \cs_set_protected:Npn \@@_begin_line: #1 \@@_end_line: { \group_begin: \g_@@_begin_line_hook_tl \int_gzero:N \g_@@_indentation_int % \end{macrocode} % % \medskip % First, we will put in the coffin |\l_tmpa_coffin| the actual content of a line % of the code (without the potential number of line). % % Be careful: There is curryfication in the following code. % \begin{macrocode} \bool_if:NTF \l_@@_width_min_bool \@@_put_in_coffin_ii:n \@@_put_in_coffin_i:n { \language = -1 \raggedright \strut \@@_replace_spaces:n { #1 } \strut \hfil } % \end{macrocode} % Now, we add the potential number of line, the potential left margin and the % potential background. % \begin{macrocode} \hbox_set:Nn \l_tmpa_box { \skip_horizontal:N \l_@@_left_margin_dim \bool_if:NT \l_@@_line_numbers_bool { \bool_if:nF { \str_if_eq_p:nn { #1 } { \PitonStyle {Prompt}{} } && \l_@@_skip_empty_lines_bool } { \int_gincr:N \g_@@_visual_line_int} \bool_if:nT { ! \str_if_eq_p:nn { #1 } { \PitonStyle {Prompt}{} } || ( ! \l_@@_skip_empty_lines_bool && \l_@@_label_empty_lines_bool ) } \@@_print_number: } % \end{macrocode} % If there is a background, we must remind that there is a left margin of 0.5~em % for the background... % \begin{macrocode} \clist_if_empty:NF \l_@@_bg_color_clist { % \end{macrocode} % ... but if only if the key |left-margin| is not used ! % \begin{macrocode} \dim_compare:nNnT \l_@@_left_margin_dim = \c_zero_dim { \skip_horizontal:n { 0.5 em } } } \coffin_typeset:Nnnnn \l_tmpa_coffin T l \c_zero_dim \c_zero_dim } \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 } \clist_if_empty:NTF \l_@@_bg_color_clist { \box_use_drop:N \l_tmpa_box } { \vtop { \hbox:n { \@@_color:N \l_@@_bg_color_clist \vrule height \box_ht:N \l_tmpa_box depth \box_dp:N \l_tmpa_box width \l_@@_width_dim } \skip_vertical:n { - \box_ht_plus_dp:N \l_tmpa_box } \box_use_drop:N \l_tmpa_box } } \vspace { - 2.5 pt } \group_end: \tl_gclear:N \g_@@_begin_line_hook_tl } % \end{macrocode} % % \bigskip % In the general case (which is also the simpler), the key |width| is not used, % or (if used) it is not used with the special value~|min|. % In that case, the content of a line of code is composed in a vertical coffin % with a width equal to |\l_@@_line_width_dim|. That coffin may, % eventually, contains several lines when the key |broken-lines-in-Piton| (or % |broken-lines|) is used. % % That commands takes in its argument by curryfication. % \begin{macrocode} \cs_set_protected:Npn \@@_put_in_coffin_i:n { \vcoffin_set:Nnn \l_tmpa_coffin \l_@@_line_width_dim } % \end{macrocode} % % \bigskip % The second case is the case when the key |width| is used with the special % value~|min|. % \begin{macrocode} \cs_set_protected:Npn \@@_put_in_coffin_ii:n #1 { % \end{macrocode} % First, we compute the natural width of the line of code because we have to % compute the natural width of the whole listing (and it will be written on the % |aux| file in the variable |\l_@@_width_dim|). % \begin{macrocode} \hbox_set:Nn \l_tmpa_box { #1 } % \end{macrocode} % Now, you can actualize the value of |\g_@@_tmp_width_dim| (it will be used to % write on the |aux| file the natural width of the environment). % \begin{macrocode} \dim_compare:nNnT { \box_wd:N \l_tmpa_box } > \g_@@_tmp_width_dim { \dim_gset:Nn \g_@@_tmp_width_dim { \box_wd:N \l_tmpa_box } } % \end{macrocode} % % \begin{macrocode} \hcoffin_set:Nn \l_tmpa_coffin { \hbox_to_wd:nn \l_@@_line_width_dim % \end{macrocode} % We unpack the block in order to free the potential |\hfill| springs present in % the LaTeX comments (cf. section~\ref{example-comments}, p.~\pageref{example-comments}). % \begin{macrocode} { \hbox_unpack:N \l_tmpa_box \hfil } } } % \end{macrocode} % % % \bigskip % The command |\@@_color:N| will take in as argument a reference to a % comma-separated list of colors. A color will be picked by using the value of % |\g_@@_line_int| (modulo the number of colors in the list). % \begin{macrocode} \cs_set_protected:Npn \@@_color:N #1 { \int_set:Nn \l_tmpa_int { \clist_count:N #1 } \int_set:Nn \l_tmpb_int { \int_mod:nn \g_@@_line_int \l_tmpa_int + 1 } \tl_set:Nx \l_tmpa_tl { \clist_item:Nn #1 \l_tmpb_int } \tl_if_eq:NnTF \l_tmpa_tl { none } % \end{macrocode} % By setting |\l_@@_width_dim| to zero, the colored rectangle will be % drawn with zero width and, thus, it will be a mere strut (and we need that strut). % \begin{macrocode} { \dim_zero:N \l_@@_width_dim } { \exp_args:NV \@@_color_i:n \l_tmpa_tl } } % \end{macrocode} % % The following command |\@@_color:n| will accept both the instruction % |\@@_color:n { red!15 }| and the instruction |\@@_color:n { [rgb]{0.9,0.9,0} }|. % \begin{macrocode} \cs_set_protected:Npn \@@_color_i:n #1 { \tl_if_head_eq_meaning:nNTF { #1 } [ { \tl_set:Nn \l_tmpa_tl { #1 } \tl_set_rescan:Nno \l_tmpa_tl { } \l_tmpa_tl \exp_last_unbraced:NV \color \l_tmpa_tl } { \color { #1 } } } \cs_generate_variant:Nn \@@_color:n { V } % \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 \g_@@_footnote_bool { \end { savenotes } } \par \mode_leave_vertical: % \newline \bool_if:NT \g_@@_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 } \clist_if_empty:NF \l_@@_bg_color_clist { \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 % \begin{macrocode} \bool_new:N \l_@@_line_numbers_bool \bool_new:N \l_@@_skip_empty_lines_bool \bool_set_true:N \l_@@_skip_empty_lines_bool \bool_new:N \l_@@_line_numbers_absolute_bool \bool_new:N \l_@@_label_empty_lines_bool \bool_set_true:N \l_@@_label_empty_lines_bool \int_new:N \l_@@_number_lines_start_int \bool_new:N \l_@@_resume_bool % \end{macrocode} % % % \bigskip % \begin{macrocode} \keys_define:nn { PitonOptions / marker } { beginning .code:n = \cs_set:Nn \@@_marker_beginning:n { #1 } , beginning .value_required:n = true , end .code:n = \cs_set:Nn \@@_marker_end:n { #1 } , end .value_required:n = true , include-lines .bool_set:N = \l_@@_marker_include_lines_bool , include-lines .default:n = true , unknown .code:n = \@@_error:n { Unknown~key~for~marker } } % \end{macrocode} % % \bigskip % \begin{macrocode} \keys_define:nn { PitonOptions / line-numbers } { true .code:n = \bool_set_true:N \l_@@_line_numbers_bool , false .code:n = \bool_set_false:N \l_@@_line_numbers_bool , start .code:n = \bool_if:NTF \l_@@_in_PitonOptions_bool { Invalid~key } { \bool_set_true:N \l_@@_line_numbers_bool \int_set:Nn \l_@@_number_lines_start_int { #1 } } , start .value_required:n = true , skip-empty-lines .code:n = \bool_if:NF \l_@@_in_PitonOptions_bool { \bool_set_true:N \l_@@_line_numbers_bool } \str_if_eq:nnTF { #1 } { false } { \bool_set_false:N \l_@@_skip_empty_lines_bool } { \bool_set_true:N \l_@@_skip_empty_lines_bool } , skip-empty-lines .default:n = true , label-empty-lines .code:n = \bool_if:NF \l_@@_in_PitonOptions_bool { \bool_set_true:N \l_@@_line_numbers_bool } \str_if_eq:nnTF { #1 } { false } { \bool_set_false:N \l_@@_label_empty_lines_bool } { \bool_set_true:N \l_@@_label_empty_lines_bool } , label-empty-lines .default:n = true , absolute .code:n = \bool_if:NTF \l_@@_in_PitonOptions_bool { \bool_set_true:N \l_@@_line_numbers_absolute_bool } { \bool_set_true:N \l_@@_line_numbers_bool } \bool_if:NT \l_@@_in_PitonInputFile_bool { \bool_set_true:N \l_@@_line_numbers_absolute_bool \bool_set_false:N \l_@@_skip_empty_lines_bool } \bool_lazy_or:nnF \l_@@_in_PitonInputFile_bool \l_@@_in_PitonOptions_bool { \@@_error:n { Invalid~key } } , absolute .value_forbidden:n = true , resume .code:n = \bool_set_true:N \l_@@_resume_bool \bool_if:NF \l_@@_in_PitonOptions_bool { \bool_set_true:N \l_@@_line_numbers_bool } , resume .value_forbidden:n = true , sep .dim_set:N = \l_@@_numbers_sep_dim , sep .value_required:n = true , unknown .code:n = \@@_error:n { Unknown~key~for~line-numbers } } % \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 } { % \end{macrocode} % First, we put keys that should be avalaible only in the preamble. % \begin{macrocode} begin-escape .code:n = \lua_now:e { piton.begin_escape = "\lua_escape:n{#1}" } , begin-escape .value_required:n = true , begin-escape .usage:n = preamble , end-escape .code:n = \lua_now:e { piton.end_escape = "\lua_escape:n{#1}" } , end-escape .value_required:n = true , end-escape .usage:n = preamble , begin-escape-math .code:n = \lua_now:e { piton.begin_escape_math = "\lua_escape:n{#1}" } , begin-escape-math .value_required:n = true , begin-escape-math .usage:n = preamble , end-escape-math .code:n = \lua_now:e { piton.end_escape_math = "\lua_escape:n{#1}" } , end-escape-math .value_required:n = true , end-escape-math .usage:n = preamble , comment-latex .code:n = \lua_now:n { comment_latex = "#1" } , comment-latex .value_required:n = true , comment-latex .usage:n = preamble , math-comments .bool_set:N = \g_@@_math_comments_bool , math-comments .default:n = true , math-comments .usage:n = preamble , % \end{macrocode} % % \bigskip % Now, general keys. % \begin{macrocode} language .code:n = \str_set:Nx \l_@@_language_str { \str_lowercase:n { #1 } } , language .value_required:n = true , path .str_set:N = \l_@@_path_str , path .value_required:n = true , 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 , marker .code:n = \bool_lazy_or:nnTF \l_@@_in_PitonInputFile_bool \l_@@_in_PitonOptions_bool { \keys_set:nn { PitonOptions / marker } { #1 } } { \@@_error:n { Invalid~key } } , marker .value_required:n = true , line-numbers .code:n = \keys_set:nn { PitonOptions / line-numbers } { #1 } , line-numbers .default:n = true , splittable .int_set:N = \l_@@_splittable_int , splittable .default:n = 1 , background-color .clist_set:N = \l_@@_bg_color_clist , background-color .value_required:n = true , prompt-background-color .tl_set:N = \l_@@_prompt_bg_color_tl , prompt-background-color .value_required:n = true , width .code:n = \str_if_eq:nnTF { #1 } { min } { \bool_set_true:N \l_@@_width_min_bool \dim_zero:N \l_@@_width_dim } { \bool_set_false:N \l_@@_width_min_bool \dim_set:Nn \l_@@_width_dim { #1 } } , width .value_required: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 } \bool_set_false:N \l_@@_left_margin_auto_bool } , 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 , first-line .code:n = \@@_in_PitonInputFile:n { \int_set:Nn \l_@@_first_line_int { #1 } } , first-line .value_required:n = true , last-line .code:n = \@@_in_PitonInputFile:n { \int_set:Nn \l_@@_last_line_int { #1 } } , last-line .value_required:n = true , begin-range .code:n = \@@_in_PitonInputFile:n { \str_set:Nn \l_@@_begin_range_str { #1 } } , begin-range .value_required:n = true , end-range .code:n = \@@_in_PitonInputFile:n { \str_set:Nn \l_@@_end_range_str { #1 } } , end-range .value_required:n = true , range .code:n = \@@_in_PitonInputFile:n { \str_set:Nn \l_@@_begin_range_str { #1 } \str_set:Nn \l_@@_end_range_str { #1 } } , range .value_required:n = true , resume .meta:n = line-numbers/resume , unknown .code:n = \@@_error:n { Unknown~key~for~PitonOptions } , % deprecated all-line-numbers .code:n = \bool_set_true:N \l_@@_line_numbers_bool \bool_set_false:N \l_@@_skip_empty_lines_bool , all-line-numbers .value_forbidden:n = true , % deprecated numbers-sep .dim_set:N = \l_@@_numbers_sep_dim , numbers-sep .value_required:n = true } % \end{macrocode} % % \bigskip % \begin{macrocode} \cs_new_protected:Npn \@@_in_PitonInputFile:n #1 { \bool_if:NTF \l_@@_in_PitonInputFile_bool { #1 } { \@@_error:n { Invalid~key } } } % \end{macrocode} % % % \bigskip % \begin{macrocode} \NewDocumentCommand \PitonOptions { m } { \bool_set_true:N \l_@@_in_PitonOptions_bool \keys_set:nn { PitonOptions } { #1 } \bool_set_false:N \l_@@_in_PitonOptions_bool } % \end{macrocode} % % \bigskip % When using |\NewPitonEnvironment| a user may use |\PitonOptions| inside. % However, the set of keys available should be different that in standard % |\PitonOptions|. That's why we define a version of |\PitonOptions| with no % restrection on the set of available keys and we will link that version to % |\PitonOptions| in such environment. % \begin{macrocode} \NewDocumentCommand \@@_fake_PitonOptions { } { \keys_set:nn { PitonOptions } } % \end{macrocode} % % % % \bigskip % \begin{macrocode} \hook_gput_code:nnn { begindocument } { . } { \bool_gset_true:N \g_@@_in_document_bool } % \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|). % % \begin{macrocode} \int_new:N \g_@@_visual_line_int % \end{macrocode} % % % \begin{macrocode} \cs_new_protected:Npn \@@_print_number: { \hbox_overlap_left:n { { \color { gray } \footnotesize \int_to_arabic:n \g_@@_visual_line_int } \skip_horizontal:N \l_@@_numbers_sep_dim } } % \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} % % \bigskip % The following macro with be used only when the key |width| is used with the % special value~|min|. % \begin{macrocode} \cs_new_protected:Npn \@@_width_to_aux: { \tl_gput_right:Nx \g_@@_aux_tl { \dim_set:Nn \l_@@_line_width_dim { \dim_eval:n { \g_@@_tmp_width_dim } } } } % \end{macrocode} % % \bigskip % \subsubsection{The main commands and environments for the final user} % % \bigskip % \begin{macrocode} \NewDocumentCommand { \piton } { } { \peek_meaning:NTF \bgroup \@@_piton_standard \@@_piton_verbatim } % \end{macrocode} % % \begin{macrocode} \NewDocumentCommand { \@@_piton_standard } { m } { \group_begin: \ttfamily % \end{macrocode} % The following tuning of LuaTeX in order to avoid all break of lines on the % hyphens. % \begin{macrocode} \automatichyphenmode = 1 \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_eq:cN { ~ } \space \cs_set_protected:Npn \@@_begin_line: { } \cs_set_protected:Npn \@@_end_line: { } \tl_set:Nx \l_tmpa_tl { \lua_now:e { piton.ParseBis('\l_@@_language_str',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 \automatichyphenmode = 1 \cs_set_protected:Npn \@@_begin_line: { } \cs_set_protected:Npn \@@_end_line: { } \tl_set:Nx \l_tmpa_tl { \lua_now:e { piton.Parse('\l_@@_language_str',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 % % % \bigskip % The following command is not a user command. It will be used when we 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: { } \bool_lazy_or:nnTF \l_@@_break_lines_in_piton_bool \l_@@_break_lines_in_Piton_bool { \tl_set:Nx \l_tmpa_tl { \lua_now:e { piton.ParseTer('\l_@@_language_str',token.scan_string()) } { #1 } } } { \tl_set:Nx \l_tmpa_tl { \lua_now:e { piton.Parse('\l_@@_language_str',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 } } \bool_lazy_or:nnTF \l_@@_break_lines_in_piton_bool \l_@@_break_lines_in_Piton_bool { \tl_set:Nx \l_tmpa_tl { \lua_now:e { piton.ParseTer('\l_@@_language_str',token.scan_string()) } { #1 } } } { \tl_set:Nx \l_tmpa_tl { \lua_now:e { piton.Parse('\l_@@_language_str',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| and % in the environments such as |{Piton}|. % \begin{macrocode} \cs_new:Npn \@@_pre_env: { \automatichyphenmode = 1 \int_gincr:N \g_@@_env_int \tl_gclear:N \g_@@_aux_tl \dim_compare:nNnT \l_@@_width_dim = \c_zero_dim { \dim_set_eq:NN \l_@@_width_dim \linewidth } % \end{macrocode} % We read the information written on the |aux| file by previous run (when the % key |width| is used with the special value~|min|). At this time, the only % potential information written on the |aux| file is the value of % |\l_@@_line_width_dim| when the key |width| has been used with the special % value~|min|). % \begin{macrocode} \cs_if_exist_use:c { c_@@ _ \int_use:N \g_@@_env_int _ tl } \bool_if:NF \l_@@_resume_bool { \int_gzero:N \g_@@_visual_line_int } \dim_gzero:N \g_@@_tmp_width_dim \int_gzero:N \g_@@_line_int \dim_zero:N \parindent \dim_zero:N \lineskip \dim_zero:N \parindent \cs_set_eq:NN \label \@@_label:n } % \end{macrocode} % % % \bigskip % 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|. The first argument of % the following function is the name of the Lua function that will be applied to % the second argument in order to count the number of lines. % \begin{macrocode} \cs_new_protected:Npn \@@_compute_left_margin:nn #1 #2 { \bool_lazy_and:nnT \l_@@_left_margin_auto_bool \l_@@_line_numbers_bool { \hbox_set:Nn \l_tmpa_box { \footnotesize \bool_if:NTF \l_@@_skip_empty_lines_bool { \lua_now:n { piton.#1(token.scan_argument()) } { #2 } \int_to_arabic:n { \g_@@_visual_line_int + \l_@@_nb_non_empty_lines_int } } { \int_to_arabic:n { \g_@@_visual_line_int + \l_@@_nb_lines_int } } } \dim_set:Nn \l_@@_left_margin_dim { \box_wd:N \l_tmpa_box + \l_@@_numbers_sep_dim + 0.1 em } } } \cs_generate_variant:Nn \@@_compute_left_margin:nn { n V } % \end{macrocode} % % % % \bigskip % Whereas |\l_@@_with_dim| is the width of the environment, % |\l_@@_line_width_dim| is the width of the lines of code without the % potential margins for the numbers of lines and the background. Depending on % the case, you have to compute |\l_@@_line_width_dim| from |\l_@@_width_dim| or % we have to do the opposite. % \begin{macrocode} \cs_new_protected:Npn \@@_compute_width: { \dim_compare:nNnTF \l_@@_line_width_dim = \c_zero_dim { \dim_set_eq:NN \l_@@_line_width_dim \l_@@_width_dim \clist_if_empty:NTF \l_@@_bg_color_clist % \end{macrocode} % If there is no background, we only subtract the left margin. % \begin{macrocode} { \dim_sub:Nn \l_@@_line_width_dim \l_@@_left_margin_dim } % \end{macrocode} % If there is a background, we subtract 0.5~em for the margin on the right. % \begin{macrocode} { \dim_sub:Nn \l_@@_line_width_dim { 0.5 em } % \end{macrocode} % And we subtract also for the left margin. If the key |left-margin| has been % used (with a numerical value or with the special value~|min|), % |\l_@@_left_margin_dim| has a non-zero value\footnote{If the key % \texttt{left-margin} has been used with the special value \texttt{min}, the % actual value of \texttt{\textbackslash l_\@\@_left_margin_dim} has yet been % computed when we use the current command.} and we use that value. Elsewhere, % we use a value of 0.5~em. % \begin{macrocode} \dim_compare:nNnTF \l_@@_left_margin_dim = \c_zero_dim { \dim_sub:Nn \l_@@_line_width_dim { 0.5 em } } { \dim_sub:Nn \l_@@_line_width_dim \l_@@_left_margin_dim } } } % \end{macrocode} % If |\l_@@_line_width_dim| has yet a non-empty value, that means that it has % been read on the |aux| file: it has been written on a previous run because the % key |width| is used with the special value~|min|). We compute now the width of % the environment by computations opposite to the preceding ones. % \begin{macrocode} { \dim_set_eq:NN \l_@@_width_dim \l_@@_line_width_dim \clist_if_empty:NTF \l_@@_bg_color_clist { \dim_add:Nn \l_@@_width_dim \l_@@_left_margin_dim } { \dim_add:Nn \l_@@_width_dim { 0.5 em } \dim_compare:nNnTF \l_@@_left_margin_dim = \c_zero_dim { \dim_add:Nn \l_@@_width_dim { 0.5 em } } { \dim_add:Nn \l_@@_width_dim \l_@@_left_margin_dim } } } } % \end{macrocode} % % % % \bigskip % \begin{macrocode} \NewDocumentCommand { \NewPitonEnvironment } { m m m m } { % \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} % The first argument of the following function is the name of the Lua function % that will be applied to the second argument in order to count the number of lines. % \begin{macrocode} \@@_compute_left_margin:nn { CountNonEmptyLines } { ##1 } \@@_compute_width: \ttfamily \dim_zero:N \parskip % added 2023/07/06 % \end{macrocode} % |\g_@@_footnote_bool| is raised when the package \pkg{piton} has been load % with the key |footnote| \emph{or} the key |footnotehyper|. % \begin{macrocode} \bool_if:NT \g_@@_footnote_bool { \begin { savenotes } } \vtop \bgroup \lua_now:e { piton.GobbleParse ( '\l_@@_language_str' , \int_use:N \l_@@_gobble_int , token.scan_argument() ) } { ##1 } \vspace { 2.5 pt } \egroup \bool_if:NT \g_@@_footnote_bool { \end { savenotes } } % \end{macrocode} % If the user has used the key |width| with the special value~|min|, we write on % the |aux| file the value of |\l_@@_line_width_dim| (largest width of the lines % of code of the environment). % \begin{macrocode} \bool_if:NT \l_@@_width_min_bool \@@_width_to_aux: % \end{macrocode} % The following |\end{#1}| is only for 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 } { \cs_set_eq:NN \PitonOptions \@@_fake_PitonOptions #3 \@@_pre_env: \int_compare:nNnT \l_@@_number_lines_start_int > 0 { \int_gset:Nn \g_@@_visual_line_int { \l_@@_number_lines_start_int - 1 } } \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 % Now, we define the environment |{Piton}|, which is the main environment % provided by the package \pkg{piton}. Of course, you use % |\NewPitonEnvironment|. % \begin{macrocode} \bool_if:NTF \g_@@_beamer_bool { \NewPitonEnvironment { Piton } { d < > O { } } { \keys_set:nn { PitonOptions } { #2 } \IfValueTF { #1 } { \begin { uncoverenv } < #1 > } { \begin { uncoverenv } } } { \end { uncoverenv } } } { \NewPitonEnvironment { Piton } { O { } } { \keys_set:nn { PitonOptions } { #1 } } { } } % \end{macrocode} % % % \bigskip % The code of the command |\PitonInputFile| is somewhat similar to the code of % the environment |{Piton}|. In fact, it's simpler because there isn't the % problem of catching the content of the environment in a verbatim mode. % \begin{macrocode} \NewDocumentCommand { \PitonInputFile } { d < > O { } m } { \group_begin: \tl_if_empty:NTF \l_@@_path_str { \str_set:Nn \l_@@_file_name_str { #3 } } { \str_set_eq:NN \l_@@_file_name_str \l_@@_path_str \str_put_right:Nn \l_@@_file_name_str { / } \str_put_right:Nn \l_@@_file_name_str { #3 } } \exp_args:NV \file_if_exist:nTF \l_@@_file_name_str { \@@_input_file:nn { #1 } { #2 } } { \msg_error:nnn { piton } { Unknown~file } { #3 } } \group_end: } % \end{macrocode} % % The following command uses as implicit argument the name of the file in |\l_@@_file_name_str|. % \begin{macrocode} \cs_new_protected:Npn \@@_input_file:nn #1 #2 { % \end{macrocode} % We recall that, if we are in Beamer, the command |\PitonInputFile| is % ``overlay-aware'' and that's why there is an optional argument between angular % brackets (|<| and |>|). % \begin{macrocode} \tl_if_novalue:nF { #1 } { \bool_if:NTF \g_@@_beamer_bool { \begin { uncoverenv } < #1 > } { \@@_error:n { overlay~without~beamer } } } \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 \bool_set_true:N \l_@@_in_PitonInputFile_bool \keys_set:nn { PitonOptions } { #2 } \bool_if:NT \l_@@_line_numbers_absolute_bool { \bool_set_false:N \l_@@_skip_empty_lines_bool } \bool_if:nTF { ( \int_compare_p:nNn \l_@@_first_line_int > 0 || \int_compare_p:nNn \l_@@_last_line_int < \c_max_int ) && ! \str_if_empty_p:N \l_@@_begin_range_str } { \@@_error:n { bad~range~specification } \int_zero:N \l_@@_first_line_int \int_set_eq:NN \l_@@_last_line_int \c_max_int } { \str_if_empty:NF \l_@@_begin_range_str { \@@_compute_range: \bool_lazy_or:nnT \l_@@_marker_include_lines_bool { ! \str_if_eq_p:NN \l_@@_begin_range_str \l_@@_end_range_str } { \int_decr:N \l_@@_first_line_int \int_incr:N \l_@@_last_line_int } } } \@@_pre_env: \bool_if:NT \l_@@_line_numbers_absolute_bool { \int_gset:Nn \g_@@_visual_line_int { \l_@@_first_line_int - 1 } } \int_compare:nNnT \l_@@_number_lines_start_int > 0 { \int_gset:Nn \g_@@_visual_line_int { \l_@@_number_lines_start_int - 1 } } % \end{macrocode} % The following case arise when the code |line-numbers/absolute| is in force % without the use of a marked range. % \begin{macrocode} \int_compare:nNnT \g_@@_visual_line_int < 0 { \int_gzero:N \g_@@_visual_line_int } \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:e { piton.CountLinesFile('\l_@@_file_name_str') } % \end{macrocode} % The first argument of the following function is the name of the Lua function % that will be applied to the second argument in order to count the number of lines. % \begin{macrocode} \@@_compute_left_margin:nV { CountNonEmptyLinesFile } \l_@@_file_name_str \@@_compute_width: \ttfamily \bool_if:NT \g_@@_footnote_bool { \begin { savenotes } } \vtop \bgroup \lua_now:e { piton.ParseFile( '\l_@@_language_str' , '\l_@@_file_name_str' , \int_use:N \l_@@_first_line_int , \int_use:N \l_@@_last_line_int ) } \egroup \bool_if:NT \g_@@_footnote_bool { \end { savenotes } } \bool_if:NT \l_@@_width_min_bool \@@_width_to_aux: \group_end: % \end{macrocode} % We recall that, if we are in Beamer, the command |\PitonInputFile| is % ``overlay-aware'' and that's why we close now an environment |{uncoverenv}| % that we have opened at the beginning of the command. % \begin{macrocode} \tl_if_novalue:nF { #1 } { \bool_if:NT \g_@@_beamer_bool { \end { uncoverenv } } } \@@_write_aux: } % \end{macrocode} % % % \bigskip % The following command computes the values of |\l_@@_first_line_int| and % |\l_@@_last_line_int| when |\PitonInputFile| is used with textual markers. % \begin{macrocode} \cs_new_protected:Npn \@@_compute_range: { % \end{macrocode} % We store the markers in L3 strings (|str|) in order to do safely the following % replacement of |\#|. % \begin{macrocode} \str_set:Nx \l_tmpa_str { \@@_marker_beginning:n \l_@@_begin_range_str } \str_set:Nx \l_tmpb_str { \@@_marker_end:n \l_@@_end_range_str } % \end{macrocode} % We replace the sequences |\#| which may be present in the prefixes (and, more % unlikely, suffixes) added to the markers by the functions % |\@@_marker_beginning:n| and |\@@_marker_end:n| % \begin{macrocode} \exp_args:NnV \regex_replace_all:nnN { \\\# } \c_hash_str \l_tmpa_str \exp_args:NnV \regex_replace_all:nnN { \\\# } \c_hash_str \l_tmpb_str \lua_now:e { piton.ComputeRange ( '\l_tmpa_str' , '\l_tmpb_str' , '\l_@@_file_name_str' ) } } % \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 } { \cs_if_exist_use:cF { pitonStyle _ \l_@@_language_str _ #1 } { \use:c { pitonStyle _ #1 } } } % \end{macrocode} % % \medskip % \begin{macrocode} \NewDocumentCommand { \SetPitonStyle } { O { } m } { \str_set:Nx \l_@@_SetPitonStyle_option_str { \str_lowercase:n { #1 } } \str_if_eq:VnT \l_@@_SetPitonStyle_option_str { current-language } { \str_set_eq:NN \l_@@_SetPitonStyle_option_str \l_@@_language_str } \keys_set:nn { piton / Styles } { #2 } \str_clear:N \l_@@_SetPitonStyle_option_str } % \end{macrocode} % % \medskip % \begin{macrocode} \cs_new_protected:Npn \@@_math_scantokens:n #1 { \normalfont \scantextokens { $#1$ } } % \end{macrocode} % % \medskip % \begin{macrocode} \clist_new:N \g_@@_style_clist \clist_set:Nn \g_@@_styles_clist { Comment , Comment.LaTeX , Exception , FormattingType , Identifier , InitialValues , Interpol.Inside , Keyword , Keyword.Constant , Name.Builtin , Name.Class , Name.Constructor , Name.Decorator , Name.Field , Name.Function , Name.Module , Name.Namespace , Name.Table , Name.Type , Number , Operator , Operator.Word , Preproc , Prompt , String.Doc , String.Interpol , String.Long , String.Short , TypeParameter , UserFunction } \clist_map_inline:Nn \g_@@_styles_clist { \keys_define:nn { piton / Styles } { #1 .value_required:n = true , #1 .code:n = \tl_set:cn { pitonStyle _ \str_if_empty:NF \l_@@_SetPitonStyle_option_str { \l_@@_SetPitonStyle_option_str _ } #1 } { ##1 } } } \keys_define:nn { piton / Styles } { 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 = , ParseAgain .tl_set:c = pitonStyle ParseAgain , ParseAgain .value_required:n = true , ParseAgain.noCR .tl_set:c = pitonStyle ParseAgain.noCR , ParseAgain.noCR .value_required:n = true , unknown .code:n = \@@_error:n { Unknown~key~for~SetPitonStyle } } % \end{macrocode} % % \bigskip % We add the word |String| to the list of the styles because we will use that % list in the error message for an unknown key in |\SetPitonStyle|. % % \begin{macrocode} \clist_gput_left:Nn \g_@@_styles_clist { String } % \end{macrocode} % % \bigskip % Of course, we sort that clist. % \begin{macrocode} \clist_gsort:Nn \g_@@_styles_clist { \str_compare:nNnTF { #1 } < { #2 } \sort_return_same: \sort_return_swapped: } % \end{macrocode} % % % \bigskip % \subsubsection{The initial styles} % % The initial styles are 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} , Name.Constructor = \color[HTML]{006000} \bfseries , Name.Field = \color[HTML]{AA6600} , Name.Module = \color[HTML]{0060A0} \bfseries , Name.Table = \color[HTML]{309030} , 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 , Interpol.Inside = \color{black}\@@_piton:n , TypeParameter = \color[HTML]{336666} \itshape , Preproc = \color[HTML]{AA6600} \slshape , Identifier = \@@_identifier:n , UserFunction = , Prompt = , ParseAgain.noCR = \@@_piton_no_cr:n , ParseAgain = \@@_piton:n , } % \end{macrocode} % The last styles |ParseAgain.noCR| and |ParseAgain| should be considered as % ``internal style'' (not available for the final user). However, maybe we will % change that and document these styles for the final user (why not?). % % \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 \g_@@_math_comments_bool { \SetPitonStyle { Comment.Math } } % \end{macrocode} % % \bigskip % % \bigskip % \subsubsection{Highlighting some identifiers} % % \begin{macrocode} \cs_new_protected:Npn \@@_identifier:n #1 { \cs_if_exist_use:c { PitonIdentifier _ \l_@@_language_str _ #1 } { #1 } } % \end{macrocode} % % \bigskip % \begin{macrocode} \keys_define:nn { PitonOptions } { identifiers .code:n = \@@_set_identifiers:n { #1 } } % \end{macrocode} % % \bigskip % \begin{macrocode} \keys_define:nn { Piton / identifiers } { names .clist_set:N = \l_@@_identifiers_names_tl , style .tl_set:N = \l_@@_style_tl , } % \end{macrocode} % % \bigskip % \begin{macrocode} \cs_new_protected:Npn \@@_set_identifiers:n #1 { \clist_clear_new:N \l_@@_identifiers_names_tl \tl_clear_new:N \l_@@_style_tl \keys_set:nn { Piton / identifiers } { #1 } \clist_map_inline:Nn \l_@@_identifiers_names_tl { \tl_set_eq:cN { PitonIdentifier _ \l_@@_language_str _ ##1 } \l_@@_style_tl } } % \end{macrocode} % % % \bigskip % In particular, we have an highlighting of the indentifiers which are the % names of Python functions previously defined by the user. Indeed, when a % Python function is defined, the style |Name.Function.Internal| is applied to % that name. We define now that style (you define it directly and you short-cut % the function |\SetPitonStyle|). % \begin{macrocode} \cs_new_protected:cpn { pitonStyle _ Name.Function.Internal } #1 { % \end{macrocode} % First, the element is composed in the TeX flow with the style |Name.Function| % which is provided to the final user. % \begin{macrocode} { \PitonStyle { Name.Function } { #1 } } % \end{macrocode} % Now, we specify that the name of the new Python function is a known identifier % that will be formated with the Piton style |UserFunction|. Of course, % here the affectation is global because we have to exit many groups and even % the environments |{Piton}|). % \begin{macrocode} \cs_gset_protected:cpn { PitonIdentifier _ \l_@@_language_str _ #1 } { \PitonStyle { UserFunction } } % \end{macrocode} % Now, we put the name of that new user function in the dedicated sequence % (specific of the current language). {\bfseries That sequence will be used only % by |\PitonClearUserFunctions|}. % \begin{macrocode} \seq_if_exist:cF { g_@@_functions _ \l_@@_language_str _ seq } { \seq_new:c { g_@@_functions _ \l_@@_language_str _ seq } } \seq_gput_right:cn { g_@@_functions _ \l_@@_language_str _ seq } { #1 } % \end{macrocode} % We update |\g_@@_languages_seq| which is used only by the command % |\PitonClearUserFunctions| when it's used without its optional argument. % \begin{macrocode} \seq_if_in:NVF \g_@@_languages_seq \l_@@_language_str { \seq_gput_left:NV \g_@@_languages_seq \l_@@_language_str } } % \end{macrocode} % % \bigskip % \begin{macrocode} \NewDocumentCommand \PitonClearUserFunctions { ! o } { \tl_if_novalue:nTF { #1 } % \end{macrocode} % If the command is used without its optional argument, we will deleted the % user language for all the informatic languages. % \begin{macrocode} { \@@_clear_all_functions: } { \@@_clear_list_functions:n { #1 } } } % \end{macrocode} % % \bigskip % \begin{macrocode} \cs_new_protected:Npn \@@_clear_list_functions:n #1 { \clist_set:Nn \l_tmpa_clist { #1 } \clist_map_function:NN \l_tmpa_clist \@@_clear_functions_i:n \clist_map_inline:nn { #1 } { \seq_gremove_all:Nn \g_@@_languages_seq { ##1 } } } % \end{macrocode} % % \bigskip % \begin{macrocode} \cs_new_protected:Npn \@@_clear_functions_i:n #1 { \exp_args:Ne \@@_clear_functions_ii:n { \str_lowercase:n { #1 } } } % \end{macrocode} % % The following command clears the list of the user-defined functions for the % language provided in argument (mandatory in lower case). % \begin{macrocode} \cs_new_protected:Npn \@@_clear_functions_ii:n #1 { \seq_if_exist:cT { g_@@_functions _ #1 _ seq } { \seq_map_inline:cn { g_@@_functions _ #1 _ seq } { \cs_undefine:c { PitonIdentifier _ #1 _ ##1} } \seq_gclear:c { g_@@_functions _ #1 _ seq } } } % \end{macrocode} % % \bigskip % \begin{macrocode} \cs_new_protected:Npn \@@_clear_functions:n #1 { \@@_clear_functions_i:n { #1 } \seq_gremove_all:Nn \g_@@_languages_seq { #1 } } % \end{macrocode} % % \bigskip % The following command clears all the user-defined functions for all the % informatic languages. % \begin{macrocode} \cs_new_protected:Npn \@@_clear_all_functions: { \seq_map_function:NN \g_@@_languages_seq \@@_clear_functions_i:n \seq_gclear:N \g_@@_languages_seq } % \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 error messages of the package} % % % \begin{macrocode} \@@_msg_new:nn { Unknown~key~for~SetPitonStyle } { The~style~'\l_keys_key_str'~is~unknown.\\ This~key~will~be~ignored.\\ The~available~styles~are~(in~alphabetic~order):~ \clist_use:Nnnn \g_@@_styles_clist { ~and~ } { ,~ } { ~and~ }. } % \end{macrocode} % % \begin{macrocode} \@@_msg_new:nn { Invalid~key } { Wrong~use~of~key.\\ You~can't~use~the~key~'\l_keys_key_str'~here.\\ That~key~will~be~ignored. } % \end{macrocode} % % \begin{macrocode} \@@_msg_new:nn { Unknown~key~for~line-numbers } { Unknown~key. \\ The~key~'line-numbers / \l_keys_key_str'~is~unknown.\\ The~available~keys~of~the~family~'line-numbers'~are~(in~ alphabetic~order):~ absolute,~false,~label-empty-lines,~resume,~skip-empty-lines,~ sep,~start~and~true.\\ That~key~will~be~ignored. } % \end{macrocode} % % \begin{macrocode} \@@_msg_new:nn { Unknown~key~for~marker } { Unknown~key. \\ The~key~'marker / \l_keys_key_str'~is~unknown.\\ The~available~keys~of~the~family~'marker'~are~(in~ alphabetic~order):~ beginning,~end~and~include-lines.\\ That~key~will~be~ignored. } % \end{macrocode} % % \begin{macrocode} \@@_msg_new:nn { bad~range~specification } { Incompatible~keys.\\ You~can't~specify~the~range~of~lines~to~include~by~using~both~ markers~and~explicit~number~of~lines.\\ Your~whole~file~'\l_@@_file_name_str'~will~be~included. } % \end{macrocode} % % \begin{macrocode} \@@_msg_new:nn { syntax~error } { Your~code~is~not~syntactically~correct.\\ It~won't~be~printed~in~the~PDF~file. } % \end{macrocode} % % \begin{macrocode} \NewDocumentCommand \PitonSyntaxError { } { \@@_error:n { syntax~error } } % \end{macrocode} % % % \begin{macrocode} \@@_msg_new:nn { begin~marker~not~found } { Marker~not~found.\\ The~range~'\l_@@_begin_range_str'~provided~to~the~ command~\token_to_str:N \PitonInputFile\ has~not~been~found.~ The~whole~file~'\l_@@_file_name_str'~will~be~inserted. } % \end{macrocode} % % % \begin{macrocode} \@@_msg_new:nn { end~marker~not~found } { Marker~not~found.\\ The~marker~of~end~of~the~range~'\l_@@_end_range_str'~ provided~to~the~command~\token_to_str:N \PitonInputFile\ has~not~been~found.~The~file~'\l_@@_file_name_str'~will~ be~inserted~till~the~end. } % \end{macrocode} % % % \begin{macrocode} \NewDocumentCommand \PitonBeginMarkerNotFound { } { \@@_error:n { begin~marker~not~found } } \NewDocumentCommand \PitonEndMarkerNotFound { } { \@@_error:n { end~marker~not~found } } % \end{macrocode} % % \begin{macrocode} \@@_msg_new:nn { Unknown~file } { Unknown~file. \\ The~file~'#1'~is~unknown.\\ Your~command~\token_to_str:N \PitonInputFile\ will~be~discarded. } % \end{macrocode} % % \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~. } { The~available~keys~are~(in~alphabetic~order):~ auto-gobble,~ background-color,~ break-lines,~ break-lines-in-piton,~ break-lines-in-Piton,~ continuation-symbol,~ continuation-symbol-on-indentation,~ end-of-broken-line,~ end-range,~ env-gobble,~ gobble,~ identifiers,~ indent-broken-lines,~ language,~ left-margin,~ line-numbers/,~ marker/,~ path,~ prompt-background-color,~ resume,~ show-spaces,~ show-spaces-in-strings,~ splittable,~ tabs-auto-gobble,~ tab-size~and~width. } % \end{macrocode} % % \bigskip % \begin{macrocode} \@@_msg_new:nn { label~with~lines~numbers } { You~can't~use~the~command~\token_to_str:N \label\ because~the~key~'line-numbers'~is~not~active.\\ If~you~go~on,~that~command~will~ignored. } % \end{macrocode} % % \bigskip % \begin{macrocode} \@@_msg_new:nn { 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 % \begin{macrocode} \@@_msg_new:nn { overlay~without~beamer } { You~can't~use~an~argument~<...>~for~your~command~ \token_to_str:N \PitonInputFile\ because~you~are~not~ in~Beamer.\\ If~you~go~on,~that~argument~will~be~ignored. } % \end{macrocode} % % \bigskip % \begin{macrocode} \@@_msg_new:nn { Python~error } { A~Python~error~has~been~detected. } % \end{macrocode} % % \bigskip % \subsubsection{We load piton.lua} % % % \bigskip % \begin{macrocode} \hook_gput_code:nnn { begindocument } { . } { \lua_now:e { require("piton.lua") } } % % \end{macrocode} % % \bigskip % \subsection{The Lua part of the implementation} % % % % \bigskip % The Lua code will be loaded via a |{luacode*}| environment. The 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|. % % % \begin{macrocode} %<*LUA> if piton.comment_latex == nil then piton.comment_latex = ">" end piton.comment_latex = "#" .. piton.comment_latex % \end{macrocode} % % % \bigskip % The following functions are an easy way to safely insert braces (|{| and |}|) % in the TeX flow. % \begin{macrocode} function piton.open_brace () tex.sprint("{") end function piton.close_brace () tex.sprint("}") end % \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 , Cg , Cmt , Cb = lpeg.Cf, lpeg.Cs, lpeg.Cg , lpeg.Cmt , lpeg.Cb local R = lpeg.R % \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 % |begin-escape| and |end-escape|. 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 Lua string corresponding to the name of % a \pkg{piton} style and the second element is a pattern (that is to say a % \textsc{lpeg} without capture) % \begin{macrocode} local function K(style, pattern) return Lc ( "{\\PitonStyle{" .. style .. "}{" ) * Q ( pattern ) * Lc ( "}}" ) 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 function |WithStyle| is similar to the function |K| but should % be used for multi-lines elements. % \begin{macrocode} local function WithStyle(style,pattern) return Ct ( Cc "Open" * Cc ( "{\\PitonStyle{" .. style .. "}{" ) * Cc "}}" ) * pattern * Ct ( Cc "Close" ) end % \end{macrocode} % % \bigskip % The following \textsc{lpeg} catches the Python chunks which are in LaTeX % escapes (and that chunks will be considered as normal LaTeX constructions). % 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} Escape = P ( false ) if piton.begin_escape ~= nil then Escape = P(piton.begin_escape) * L ( ( 1 - P(piton.end_escape) ) ^ 1 ) * P(piton.end_escape) end % \end{macrocode} % % \begin{macrocode} EscapeMath = P ( false ) if piton.begin_escape_math ~= nil then EscapeMath = P(piton.begin_escape_math) * Lc ( "\\ensuremath{" ) * L ( ( 1 - P(piton.end_escape_math) ) ^ 1 ) * Lc ( "}" ) * P(piton.end_escape_math) end % \end{macrocode} % % \vspace{1cm} % The following line is mandatory. % \begin{macrocode} lpeg.locale(lpeg) % \end{macrocode} % % \bigskip % \paragraph{The basic syntactic LPEG} % % \begin{macrocode} local alpha, digit = lpeg.alpha, lpeg.digit local space = P " " % \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' , 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 ( 'Number' , ( digit^1 * P "." * digit^0 + digit^0 * P "." * digit^1 + digit^1 ) * ( S "eE" * S "+-" ^ -1 * digit^1 ) ^ -1 + digit^1 ) % \end{macrocode} % % \bigskip % We recall that |piton.begin_espace| and |piton_end_escape| are Lua strings % corresponding to the keys |begin-escape| and |end-escape|. % \begin{macrocode} local Word if piton.begin_escape ~= nil -- before : '' then Word = Q ( ( ( 1 - space - P(piton.begin_escape) - P(piton.end_escape) ) - S "'\"\r[()]" - digit ) ^ 1 ) else Word = Q ( ( ( 1 - space ) - S "'\"\r[()]" - digit ) ^ 1 ) end % \end{macrocode} % % \bigskip % \begin{macrocode} local Space = ( Q " " ) ^ 1 local SkipSpace = ( Q " " ) ^ 0 local Punct = Q ( S ".,:;!" ) local Tab = P "\t" * Lc ( '\\l_@@_tab_tl' ) % \end{macrocode} % % \bigskip % \begin{macrocode} local SpaceIndentation = Lc ( '\\@@_an_indentation_space:' ) * ( Q " " ) % \end{macrocode} % % % \bigskip % \begin{macrocode} local Delim = Q ( S "[()]" ) % \end{macrocode} % % \bigskip % The following \textsc{lpeg} catches a space (U+0020) and replace it by % |\l_@@_space_tl|. It will be used in the 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 = space * Lc "\\l_@@_space_tl" % \end{macrocode} % % \bigskip % If the classe Beamer is used, some environemnts and commands of Beamer are % automatically detected in the listings of \pkg{piton}. % \begin{macrocode} local Beamer = P ( false ) local BeamerBeginEnvironments = P ( true ) local BeamerEndEnvironments = P ( true ) if piton_beamer then % \bigskip % The following function will return a \textsc{lpeg} which will catch an % environment of Beamer (supported by \pkg{piton}), that is to say |{uncover}|, % |{only}|, etc. % \begin{macrocode} local BeamerNamesEnvironments = P "uncoverenv" + P "onlyenv" + P "visibleenv" + P "invisibleenv" + P "alertenv" + P "actionenv" 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{macrocode} % % % \bigskip % The following function will return a \textsc{lpeg} which will catch an % environment of Beamer (supported by \pkg{piton}), that is to say % |{uncoverenv}|, etc. The argument |lpeg| should be |MainLoopPython|, % |MainLoopC|, etc. % \begin{macrocode} function OneBeamerEnvironment(name,lpeg) return Ct ( Cc "Open" * C ( P ( "\\begin{" .. name .. "}" ) * ( P "<" * (1 - P ">") ^ 0 * P ">" ) ^ -1 ) * Cc ( "\\end{" .. name .. "}" ) ) * ( C ( ( 1 - P ( "\\end{" .. name .. "}" ) ) ^ 0 ) / ( function (s) return lpeg : match(s) end ) ) * P ( "\\end{" .. name .. "}" ) * Ct ( Cc "Close" ) end end % \end{macrocode} % % % % \bigskip % \begin{macrocode} local languages = { } % \end{macrocode} % % \bigskip % \subsubsection{The LPEG python} % % \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 ( 'Operator' , P "!=" + P "<>" + P "==" + P "<<" + P ">>" + P "<=" + P ">=" + P ":=" + P "//" + P "**" + S "-~+/*%=<>&.@|" ) local OperatorWord = K ( 'Operator.Word' , P "in" + P "is" + P "and" + P "or" + P "not" ) local Keyword = K ( 'Keyword' , 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" ) + K ( 'Keyword.Constant' ,P "True" + P "False" + P "None" ) local Builtin = K ( 'Name.Builtin' , 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" ) local Exception = K ( 'Exception' , P "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" ) local RaiseException = K ( 'Keyword' , P "raise" ) * SkipSpace * Exception * Q ( 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 ( 'Name.Decorator' , P "@" * letter^1 ) % \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 ( 'Keyword' , P "class" ) * Space * K ( 'Name.Class' , identifier ) % \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 ( 'Keyword' , P "import" ) * Space * K ( 'Name.Namespace' , identifier * ( P "." * identifier ) ^ 0 ) * ( ( Space * K ( 'Keyword' , P "as" ) * Space * K ( 'Name.Namespace' , identifier ) ) + ( SkipSpace * Q ( P "," ) * SkipSpace * K ( 'Name.Namespace' , identifier ) ) ^ 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 ( 'Keyword' , P "from" ) * Space * K ( 'Name.Namespace' , identifier ) * Space * K ( 'Keyword' , P "import" ) % \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}{@{}lcc@{}} % \toprule % & |Single| & |Double| \\ % \midrule % |Short| & |'text'| & |"text"| \\ % |Long| & |'''test'''| & |"""text"""| \\ % \bottomrule % \end{tabular} % \end{center} % % % \bigskip % We have also to deal with 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} €'} % % % \bigskip % The interpolations beginning by |%| (even though there is more modern % technics now in Python). % \begin{macrocode} local PercentInterpol = K ( 'String.Interpol' , P "%" * ( P "(" * alphanum ^ 1 * P ")" ) ^ -1 * ( S "-#0 +" ) ^ 0 * ( digit ^ 1 + P "*" ) ^ -1 * ( P "." * ( digit ^ 1 + P "*" ) ) ^ -1 * ( S "HlL" ) ^ -1 * S "sdfFeExXorgiGauc%" ) % \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 = WithStyle ( 'String.Short' , % \end{macrocode} % First, we deal with the f-strings of Python, which are prefixed by |f| or |F|. % \begin{macrocode} Q ( P "f'" + P "F'" ) * ( K ( 'String.Interpol' , P "{" ) * K ( 'Interpol.Inside' , ( 1 - S "}':" ) ^ 0 ) * Q ( P ":" * (1 - S "}:'") ^ 0 ) ^ -1 * K ( 'String.Interpol' , P "}" ) + VisualSpace + Q ( ( P "\\'" + P "{{" + P "}}" + 1 - S " {}'" ) ^ 1 ) ) ^ 0 * Q ( P "'" ) + % \end{macrocode} % Now, we deal with the standard strings of Python, but also the ``raw strings''. % \begin{macrocode} Q ( P "'" + P "r'" + P "R'" ) * ( Q ( ( P "\\'" + 1 - S " '\r%" ) ^ 1 ) + VisualSpace + PercentInterpol + Q ( P "%" ) ) ^ 0 * Q ( P "'" ) ) local DoubleShortString = WithStyle ( 'String.Short' , Q ( P "f\"" + P "F\"" ) * ( K ( 'String.Interpol' , P "{" ) * Q ( ( 1 - S "}\":" ) ^ 0 , 'Interpol.Inside' ) * ( K ( 'String.Interpol' , P ":" ) * Q ( (1 - S "}:\"") ^ 0 ) ) ^ -1 * K ( 'String.Interpol' , P "}" ) + VisualSpace + Q ( ( P "\\\"" + P "{{" + P "}}" + 1 - S " {}\"" ) ^ 1 ) ) ^ 0 * Q ( P "\"" ) + Q ( P "\"" + P "r\"" + P "R\"" ) * ( Q ( ( P "\\\"" + 1 - S " \"\r%" ) ^ 1 ) + VisualSpace + PercentInterpol + Q ( P "%" ) ) ^ 0 * Q ( P "\"" ) ) local ShortString = SingleShortString + DoubleShortString % \end{macrocode} % % \bigskip % \paragraph{Beamer} % % The following pattern |balanced_braces| 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 balanced_braces = P { "E" , E = ( P "{" * V "E" * P "}" + ShortString + ( 1 - S "{}" ) ) ^ 0 } % \end{macrocode} % % \bigskip % \begin{macrocode} if piton_beamer then Beamer = L ( P "\\pause" * ( P "[" * ( 1 - P "]" ) ^ 0 * P "]" ) ^ -1 ) + Ct ( Cc "Open" * C ( ( P "\\uncover" + P "\\only" + P "\\alert" + P "\\visible" + P "\\invisible" + P "\\action" ) * ( P "<" * (1 - P ">") ^ 0 * P ">" ) ^ -1 * P "{" ) * Cc "}" ) * ( C ( balanced_braces ) / (function (s) return MainLoopPython:match(s) end ) ) * P "}" * Ct ( Cc "Close" ) + OneBeamerEnvironment ( "uncoverenv" , MainLoopPython ) + OneBeamerEnvironment ( "onlyenv" , MainLoopPython ) + OneBeamerEnvironment ( "visibleenv" , MainLoopPython ) + OneBeamerEnvironment ( "invisibleenv" , MainLoopPython ) + OneBeamerEnvironment ( "alertenv" , MainLoopPython ) + OneBeamerEnvironment ( "actionenv" , MainLoopPython ) + 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 ( 'ParseAgain.noCR' , balanced_braces ) * L ( P "}{" ) * K ( 'ParseAgain.noCR' , balanced_braces ) * L ( P "}" ) + L ( % \end{macrocode} % For |\\temporal|, the specification of the overlays (between angular brackets) is mandatory. % \begin{macrocode} ( P "\\temporal" ) * P "<" * (1 - P ">") ^ 0 * P ">" * P "{" ) * K ( 'ParseAgain.noCR' , balanced_braces ) * L ( P "}{" ) * K ( 'ParseAgain.noCR' , balanced_braces ) * L ( P "}{" ) * K ( 'ParseAgain.noCR' , balanced_braces ) * L ( P "}" ) end % \end{macrocode} % % \bigskip % \paragraph{EOL} % % \bigskip % The following LPEG will detect the Python prompts when the user is typesetting % an interactive session of Python (directly or through |{pyconsole}| of % \pkg{pyluatex}). We have to detect that prompt twice. The first detection % (called \emph{hasty detection}) will be before the |\@@_begin_line:| because % you want to trigger a special background color for that row (and, after the % |\@@_begin_line:|, it's too late to change de background). % \begin{macrocode} local PromptHastyDetection = ( # ( P ">>>" + P "..." ) * Lc ( '\\@@_prompt:' ) ) ^ -1 % \end{macrocode} % We remind that the marker |#| of \textsc{lpeg} specifies that the pattern will be % detected but won't consume any character. % % \medskip % With the following \textsc{lpeg}, a style will actually be applied to the % prompt (for instance, it's possible to decide to discard these prompts). % \begin{macrocode} local Prompt = K ( 'Prompt' , ( ( P ">>>" + P "..." ) * P " " ^ -1 ) ^ -1 ) % \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} Ct ( Cc "EOL" * Ct ( Lc "\\@@_end_line:" * BeamerEndEnvironments * BeamerBeginEnvironments * PromptHastyDetection * Lc "\\@@_newline: \\@@_begin_line:" * Prompt ) ) ) * SpaceIndentation ^ 0 % \end{macrocode} % % % \bigskip % \paragraph{The long strings} % % % \begin{macrocode} local SingleLongString = WithStyle ( 'String.Long' , ( Q ( S "fF" * P "'''" ) * ( K ( 'String.Interpol' , P "{" ) * K ( 'Interpol.Inside' , ( 1 - S "}:\r" - P "'''" ) ^ 0 ) * Q ( P ":" * (1 - S "}:\r" - P "'''" ) ^ 0 ) ^ -1 * K ( 'String.Interpol' , P "}" ) + Q ( ( 1 - P "'''" - S "{}'\r" ) ^ 1 ) + EOL ) ^ 0 + Q ( ( S "rR" ) ^ -1 * P "'''" ) * ( Q ( ( 1 - P "'''" - S "\r%" ) ^ 1 ) + PercentInterpol + P "%" + EOL ) ^ 0 ) * Q ( P "'''" ) ) local DoubleLongString = WithStyle ( 'String.Long' , ( Q ( S "fF" * P "\"\"\"" ) * ( K ( 'String.Interpol', P "{" ) * K ( 'Interpol.Inside' , ( 1 - S "}:\r" - P "\"\"\"" ) ^ 0 ) * Q ( P ":" * (1 - S "}:\r" - P "\"\"\"" ) ^ 0 ) ^ -1 * K ( 'String.Interpol' , P "}" ) + Q ( ( 1 - P "\"\"\"" - S "{}\"\r" ) ^ 1 ) + EOL ) ^ 0 + Q ( ( S "rR" ) ^ -1 * P "\"\"\"" ) * ( Q ( ( 1 - P "\"\"\"" - S "%\r" ) ^ 1 ) + PercentInterpol + P "%" + EOL ) ^ 0 ) * Q ( P "\"\"\"" ) ) % \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 ( 'String.Doc' , P "\"\"\"" ) * ( K ( 'String.Doc' , (1 - P "\"\"\"" - P "\r" ) ^ 0 ) * EOL * Tab ^ 0 ) ^ 0 * K ( 'String.Doc' , ( 1 - P "\"\"\"" - P "\r" ) ^ 0 * P "\"\"\"" ) % \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 ( 'Comment.Math' , ( 1 - S "$\r" ) ^ 1 ) * P "$" local Comment = WithStyle ( 'Comment' , Q ( P "#" ) * ( CommentMath + Q ( ( 1 - S "$\r" ) ^ 1 ) ) ^ 0 ) * ( 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 = ( 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 , F = ( P "{" * V "F" * P "}" + P "(" * V "F" * P ")" + P "[" * V "F" * P "]" + ( 1 - S "{}()[]\r\"'" ) ) ^ 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 ( 'InitialValues' , P "=" * expression ) + Q ( P ":" ) * SkipSpace * K ( 'Name.Type' , letter ^ 1 ) ) ^ -1 % \end{macrocode} % % \medskip % \begin{macrocode} local Params = ( Param * ( Q "," * 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 ( 'Keyword' , P "def" ) * Space * K ( 'Name.Function.Internal' , identifier ) * SkipSpace * Q ( P "(" ) * Params * Q ( P ")" ) * SkipSpace * ( Q ( P "->" ) * SkipSpace * K ( 'Name.Type' , identifier ) ) ^ -1 % \end{macrocode} % Here, we need a \pkg{piton} style |ParseAgain| 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 ( 'ParseAgain' , ( 1 - S ":\r" )^0 ) * Q ( 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}). % % % \paragraph{Miscellaneous} % % \begin{macrocode} local ExceptionInConsole = Exception * Q ( ( 1 - P "\r" ) ^ 0 ) * EOL % \end{macrocode} % % % \bigskip % \paragraph{The main LPEG for the language Python} % % First, the main loop : % \begin{macrocode} local MainPython = EOL + Space + Tab + Escape + EscapeMath + CommentLaTeX + Beamer + LongString + Comment + ExceptionInConsole + Delim + Operator + OperatorWord * ( Space + Punct + Delim + EOL + -1 ) + ShortString + Punct + FromImport + RaiseException + DefFunction + DefClass + Keyword * ( Space + Punct + Delim + EOL + -1 ) + Decorator + Builtin * ( Space + Punct + Delim + EOL + -1 ) + Identifier + Number + Word % \end{macrocode} % % Here, we must not put |local|! % \begin{macrocode} MainLoopPython = ( ( space^1 * -1 ) + MainPython ) ^ 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 python = P ( true ) python = Ct ( ( ( space - P "\r" ) ^0 * P "\r" ) ^ -1 * BeamerBeginEnvironments * PromptHastyDetection * Lc '\\@@_begin_line:' * Prompt * SpaceIndentation ^ 0 * MainLoopPython * -1 * Lc '\\@@_end_line:' ) % \end{macrocode} % % \begin{macrocode} languages['python'] = python % \end{macrocode} % % \bigskip % \subsubsection{The LPEG ocaml} % % \begin{macrocode} local Delim = Q ( P "[|" + P "|]" + S "[()]" ) % \end{macrocode} % % \begin{macrocode} local Punct = Q ( S ",:;!" ) % \end{macrocode} % % The identifiers catched by |cap_identifier| begin with a cap. In OCaml, it's % used for the constructors of types and for the modules. % \begin{macrocode} local cap_identifier = R "AZ" * ( R "az" + R "AZ" + S "_'" + digit ) ^ 0 % \end{macrocode} % % \begin{macrocode} local Constructor = K ( 'Name.Constructor' , cap_identifier ) local ModuleType = K ( 'Name.Type' , cap_identifier ) % \end{macrocode} % % The identifiers which begin with a lower case letter or an underscore are used % elsewhere in OCaml. % \begin{macrocode} local identifier = ( R "az" + P "_") * ( R "az" + R "AZ" + S "_'" + digit ) ^ 0 local Identifier = K ( 'Identifier' , identifier ) % \end{macrocode} % % % Now, we deal with the records because we want to catch the names of the fields % of those records in all circunstancies. % \begin{macrocode} local expression_for_fields = P { "E" , E = ( P "{" * V "F" * P "}" + P "(" * V "F" * P ")" + P "[" * V "F" * P "]" + P "\"" * (P "\\\"" + 1 - S "\"\r" )^0 * P "\"" + P "'" * ( P "\\'" + 1 - S "'\r" )^0 * P "'" + ( 1 - S "{}()[]\r;" ) ) ^ 0 , F = ( P "{" * V "F" * P "}" + P "(" * V "F" * P ")" + P "[" * V "F" * P "]" + ( 1 - S "{}()[]\r\"'" ) ) ^ 0 } % \end{macrocode} % % \begin{macrocode} local OneFieldDefinition = ( K ( 'KeyWord' , P "mutable" ) * SkipSpace ) ^ -1 * K ( 'Name.Field' , identifier ) * SkipSpace * Q ":" * SkipSpace * K ( 'Name.Type' , expression_for_fields ) * SkipSpace local OneField = K ( 'Name.Field' , identifier ) * SkipSpace * Q "=" * SkipSpace * ( C ( expression_for_fields ) / ( function (s) return LoopOCaml:match(s) end ) ) * SkipSpace local Record = Q "{" * SkipSpace * ( OneFieldDefinition * ( Q ";" * SkipSpace * OneFieldDefinition ) ^ 0 + OneField * ( Q ";" * SkipSpace * OneField ) ^ 0 ) * Q "}" % \end{macrocode} % % \bigskip % Now, we deal with the notations with points (eg: |List.length|). In OCaml, % such notation is used for the fields of the records and for the modules. % \begin{macrocode} local DotNotation = ( K ( 'Name.Module' , cap_identifier ) * Q "." * ( Identifier + Constructor + Q "(" + Q "[" + Q "{" ) + Identifier * Q "." * K ( 'Name.Field' , identifier ) ) * ( Q "." * K ( 'Name.Field' , identifier ) ) ^ 0 % \end{macrocode} % % \begin{macrocode} local Operator = K ( 'Operator' , P "!=" + P "<>" + P "==" + P "<<" + P ">>" + P "<=" + P ">=" + P ":=" + P "||" + P "&&" + P "//" + P "**" + P ";;" + P "::" + P "->" + P "+." + P "-." + P "*." + P "/." + S "-~+/*%=<>&@|" ) local OperatorWord = K ( 'Operator.Word' , P "and" + P "asr" + P "land" + P "lor" + P "lsl" + P "lxor" + P "mod" + P "or" ) local Keyword = K ( 'Keyword' , P "assert" + P "as" + P "begin" + P "class" + P "constraint" + P "done" + P "downto" + P "do" + P "else" + P "end" + P "exception" + P "external" + P "for" + P "function" + P "functor" + P "fun" + P "if" + P "include" + P "inherit" + P "initializer" + P "in" + P "lazy" + P "let" + P "match" + P "method" + P "module" + P "mutable" + P "new" + P "object" + P "of" + P "open" + P "private" + P "raise" + P "rec" + P "sig" + P "struct" + P "then" + P "to" + P "try" + P "type" + P "value" + P "val" + P "virtual" + P "when" + P "while" + P "with" ) + K ( 'Keyword.Constant' , P "true" + P "false" ) local Builtin = K ( 'Name.Builtin' , P "not" + P "incr" + P "decr" + P "fst" + P "snd" ) % \end{macrocode} % % \bigskip % The following exceptions are exceptions in the standard library of OCaml (Stdlib). % \begin{macrocode} local Exception = K ( 'Exception' , P "Division_by_zero" + P "End_of_File" + P "Failure" + P "Invalid_argument" + P "Match_failure" + P "Not_found" + P "Out_of_memory" + P "Stack_overflow" + P "Sys_blocked_io" + P "Sys_error" + P "Undefined_recursive_module" ) % \end{macrocode} % % \bigskip % \paragraph{The characters in OCaml} % % \begin{macrocode} local Char = K ( 'String.Short' , P "'" * ( ( 1 - P "'" ) ^ 0 + P "\\'" ) * P "'" ) % \end{macrocode} % % % \bigskip % \paragraph{Beamer} % % \begin{macrocode} local balanced_braces = P { "E" , E = ( P "{" * V "E" * P "}" + P "\"" * ( 1 - S "\"" ) ^ 0 * P "\"" -- OCaml strings + ( 1 - S "{}" ) ) ^ 0 } % \end{macrocode} % % % \bigskip % \begin{macrocode} if piton_beamer then Beamer = L ( P "\\pause" * ( P "[" * ( 1 - P "]" ) ^ 0 * P "]" ) ^ -1 ) + Ct ( Cc "Open" * C ( ( P "\\uncover" + P "\\only" + P "\\alert" + P "\\visible" + P "\\invisible" + P "\\action" ) * ( P "<" * (1 - P ">") ^ 0 * P ">" ) ^ -1 * P "{" ) * Cc "}" ) * ( C ( balanced_braces ) / (function (s) return MainLoopOCaml:match(s) end ) ) * P "}" * Ct ( Cc "Close" ) + OneBeamerEnvironment ( "uncoverenv" , MainLoopOCaml ) + OneBeamerEnvironment ( "onlyenv" , MainLoopOCaml ) + OneBeamerEnvironment ( "visibleenv" , MainLoopOCaml ) + OneBeamerEnvironment ( "invisibleenv" , MainLoopOCaml ) + OneBeamerEnvironment ( "alertenv" , MainLoopOCaml ) + OneBeamerEnvironment ( "actionenv" , MainLoopOCaml ) + 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 ( 'ParseAgain.noCR' , balanced_braces ) * L ( P "}{" ) * K ( 'ParseAgain.noCR' , balanced_braces ) * L ( P "}" ) + L ( % \end{macrocode} % For |\\temporal|, the specification of the overlays (between angular brackets) is mandatory. % \begin{macrocode} ( P "\\temporal" ) * P "<" * (1 - P ">") ^ 0 * P ">" * P "{" ) * K ( 'ParseAgain.noCR' , balanced_braces ) * L ( P "}{" ) * K ( 'ParseAgain.noCR' , balanced_braces ) * L ( P "}{" ) * K ( 'ParseAgain.noCR' , balanced_braces ) * L ( P "}" ) end % \end{macrocode} % % % \bigskip % \paragraph{EOL} % \begin{macrocode} local EOL = P "\r" * ( ( space^0 * -1 ) + Ct ( Cc "EOL" * Ct ( Lc "\\@@_end_line:" * BeamerEndEnvironments * BeamerBeginEnvironments * PromptHastyDetection * Lc "\\@@_newline: \\@@_begin_line:" * Prompt ) ) ) * SpaceIndentation ^ 0 % \end{macrocode} % % % \paragraph{The strings en OCaml} % % We need a pattern |ocaml_string| without captures because it will be used % within the comments of OCaml. % \begin{macrocode} local ocaml_string = Q ( P "\"" ) * ( VisualSpace + Q ( ( 1 - S " \"\r" ) ^ 1 ) + EOL ) ^ 0 * Q ( P "\"" ) % \end{macrocode} % % \begin{macrocode} local String = WithStyle ( 'String.Long' , ocaml_string ) % \end{macrocode} % % % \bigskip % Now, the ``quoted strings'' of OCaml (for example \verb+{ext|Essai|ext}+). % % For those strings, we will do two consecutive analysis. First an analysis to % determine the whole string and, then, an analysis for the potential visual % spaces and the EOL in the string. % % The first analysis require a match-time capture. For explanations about that % programmation, see the paragraphe \emph{Lua's long % strings} in % |www.inf.puc-rio.br/~roberto/lpeg|. % \begin{macrocode} local ext = ( R "az" + P "_" ) ^ 0 local open = "{" * Cg(ext, 'init') * "|" local close = "|" * C(ext) * "}" local closeeq = Cmt ( close * Cb('init'), function (s, i, a, b) return a==b end ) % \end{macrocode} % % \medskip % The \textsc{lpeg} |QuotedStringBis| will do the second analysis. % \begin{macrocode} local QuotedStringBis = WithStyle ( 'String.Long' , ( VisualSpace + Q ( ( 1 - S " \r" ) ^ 1 ) + EOL ) ^ 0 ) % \end{macrocode} % % \medskip % We use a ``function capture'' (as called in the official documentation of the % \textsc{lpeg}) in order to do the second analysis on the result of the first one. % \begin{macrocode} local QuotedString = C ( open * ( 1 - closeeq ) ^ 0 * close ) / ( function (s) return QuotedStringBis : match(s) end ) % \end{macrocode} % % % \bigskip % \paragraph{The comments in the OCaml listings} % % In OCaml, the delimiters for the comments are |(*| and |*)|. There are % unsymmetrical and OCaml allow those comments to be nested. That's why we need a % grammar. % % In these comments, we embed the math comments (between |$| and |$|) and we % embed also a treatment for the end of lines (since the comments may be multi-lines). % % \begin{macrocode} local Comment = WithStyle ( 'Comment' , P { "A" , A = Q "(*" * ( V "A" + Q ( ( 1 - P "(*" - P "*)" - S "\r$\"" ) ^ 1 ) -- $ + ocaml_string + P "$" * K ( 'Comment.Math' , ( 1 - S "$\r" ) ^ 1 ) * P "$" -- $ + EOL ) ^ 0 * Q "*)" } ) % \end{macrocode} % % \bigskip % \paragraph{The DefFunction} % % \begin{macrocode} local balanced_parens = P { "E" , E = ( P "(" * V "E" * P ")" + ( 1 - S "()" ) ) ^ 0 } % \end{macrocode} % % \begin{macrocode} local Argument = K ( 'Identifier' , identifier ) + Q "(" * SkipSpace * K ( 'Identifier' , identifier ) * SkipSpace * Q ":" * SkipSpace * K ( 'Name.Type' , balanced_parens ) * SkipSpace * Q ")" % \end{macrocode} % % Despite its name, then \textsc{lpeg} |DefFunction| deals also with |let open| % which opens locally a module. % \begin{macrocode} local DefFunction = K ( 'Keyword' , P "let open" ) * Space * K ( 'Name.Module' , cap_identifier ) + K ( 'Keyword' , P "let rec" + P "let" + P "and" ) * Space * K ( 'Name.Function.Internal' , identifier ) * Space * ( Q "=" * SkipSpace * K ( 'Keyword' , P "function" ) + Argument * ( SkipSpace * Argument ) ^ 0 * ( SkipSpace * Q ":" * K ( 'Name.Type' , ( 1 - P "=" ) ^ 0 ) ) ^ -1 ) % \end{macrocode} % % % \bigskip % \paragraph{The DefModule}\par % % The following LPEG will be used in the definitions of modules but also in the % definitions of \emph{types} of modules. % \begin{macrocode} local DefModule = K ( 'Keyword' , P "module" ) * Space * ( K ( 'Keyword' , P "type" ) * Space * K ( 'Name.Type' , cap_identifier ) + K ( 'Name.Module' , cap_identifier ) * SkipSpace * ( Q "(" * SkipSpace * K ( 'Name.Module' , cap_identifier ) * SkipSpace * Q ":" * SkipSpace * K ( 'Name.Type' , cap_identifier ) * SkipSpace * ( Q "," * SkipSpace * K ( 'Name.Module' , cap_identifier ) * SkipSpace * Q ":" * SkipSpace * K ( 'Name.Type' , cap_identifier ) * SkipSpace ) ^ 0 * Q ")" ) ^ -1 * ( Q "=" * SkipSpace * K ( 'Name.Module' , cap_identifier ) * SkipSpace * Q "(" * K ( 'Name.Module' , cap_identifier ) * SkipSpace * ( Q "," * K ( 'Name.Module' , cap_identifier ) * SkipSpace ) ^ 0 * Q ")" ) ^ -1 ) + K ( 'Keyword' , P "include" + P "open" ) * Space * K ( 'Name.Module' , cap_identifier ) % \end{macrocode} % % \bigskip % \paragraph{The parameters of the types} % % \begin{macrocode} local TypeParameter = K ( 'TypeParameter' , P "'" * alpha * # ( 1 - P "'" ) ) % \end{macrocode} % % \bigskip % \paragraph{The main LPEG for the language OCaml} % % First, the main loop : % \begin{macrocode} MainOCaml = EOL + Space + Tab + Escape + EscapeMath + Beamer + TypeParameter + String + QuotedString + Char + Comment + Delim + Operator + Punct + FromImport + Exception + DefFunction + DefModule + Record + Keyword * ( Space + Punct + Delim + EOL + -1 ) + OperatorWord * ( Space + Punct + Delim + EOL + -1 ) + Builtin * ( Space + Punct + Delim + EOL + -1 ) + DotNotation + Constructor + Identifier + Number + Word LoopOCaml = MainOCaml ^ 0 MainLoopOCaml = ( ( space^1 * -1 ) + MainOCaml ) ^ 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 ocaml = P ( true ) ocaml = Ct ( ( ( space - P "\r" ) ^0 * P "\r" ) ^ -1 * BeamerBeginEnvironments * Lc ( '\\@@_begin_line:' ) * SpaceIndentation ^ 0 * MainLoopOCaml * -1 * Lc ( '\\@@_end_line:' ) ) % \end{macrocode} % % \begin{macrocode} languages['ocaml'] = ocaml % \end{macrocode} % % \bigskip % \subsubsection{The LPEG language C} % % \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 identifier = letter * alphanum ^ 0 local Operator = K ( 'Operator' , P "!=" + P "==" + P "<<" + P ">>" + P "<=" + P ">=" + P "||" + P "&&" + S "-~+/*%=<>&.@|!" ) local Keyword = K ( 'Keyword' , P "alignas" + P "asm" + P "auto" + P "break" + P "case" + P "catch" + P "class" + P "const" + P "constexpr" + P "continue" + P "decltype" + P "do" + P "else" + P "enum" + P "extern" + P "for" + P "goto" + P "if" + P "nexcept" + P "private" + P "public" + P "register" + P "restricted" + P "return" + P "static" + P "static_assert" + P "struct" + P "switch" + P "thread_local" + P "throw" + P "try" + P "typedef" + P "union" + P "using" + P "virtual" + P "volatile" + P "while" ) + K ( 'Keyword.Constant' , P "default" + P "false" + P "NULL" + P "nullptr" + P "true" ) local Builtin = K ( 'Name.Builtin' , P "alignof" + P "malloc" + P "printf" + P "scanf" + P "sizeof" ) local Type = K ( 'Name.Type' , P "bool" + P "char" + P "char16_t" + P "char32_t" + P "double" + P "float" + P "int" + P "int8_t" + P "int16_t" + P "int32_t" + P "int64_t" + P "long" + P "short" + P "signed" + P "unsigned" + P "void" + P "wchar_t" ) local DefFunction = Type * Space * K ( 'Name.Function.Internal' , identifier ) * SkipSpace * # P "(" % \end{macrocode} % We remind that the marker |#| of \textsc{lpeg} specifies that the pattern will be % detected but won't consume any character. % % \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 ( 'Keyword' , P "class" ) * Space * K ( 'Name.Class' , identifier ) % \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 % \paragraph{The strings of C} % % \begin{macrocode} local String = WithStyle ( 'String.Long' , Q "\"" * ( VisualSpace + K ( 'String.Interpol' , P "%" * ( S "difcspxXou" + P "ld" + P "li" + P "hd" + P "hi" ) ) + Q ( ( P "\\\"" + 1 - S " \"" ) ^ 1 ) ) ^ 0 * Q "\"" ) % \end{macrocode} % % \bigskip % \paragraph{Beamer} % % The following \textsc{lpeg} |balanced_braces| 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 balanced_braces = P { "E" , E = ( P "{" * V "E" * P "}" + String + ( 1 - S "{}" ) ) ^ 0 } % \end{macrocode} % % % \bigskip % \begin{macrocode} if piton_beamer then Beamer = L ( P "\\pause" * ( P "[" * ( 1 - P "]" ) ^ 0 * P "]" ) ^ -1 ) + Ct ( Cc "Open" * C ( ( P "\\uncover" + P "\\only" + P "\\alert" + P "\\visible" + P "\\invisible" + P "\\action" ) * ( P "<" * (1 - P ">") ^ 0 * P ">" ) ^ -1 * P "{" ) * Cc "}" ) * ( C ( balanced_braces ) / (function (s) return MainLoopC:match(s) end ) ) * P "}" * Ct ( Cc "Close" ) + OneBeamerEnvironment ( "uncoverenv" , MainLoopC ) + OneBeamerEnvironment ( "onlyenv" , MainLoopC ) + OneBeamerEnvironment ( "visibleenv" , MainLoopC ) + OneBeamerEnvironment ( "invisibleenv" , MainLoopC ) + OneBeamerEnvironment ( "alertenv" , MainLoopC ) + OneBeamerEnvironment ( "actionenv" , MainLoopC ) + 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 ( 'ParseAgain.noCR' , balanced_braces ) * L ( P "}{" ) * K ( 'ParseAgain.noCR' , balanced_braces ) * L ( P "}" ) + L ( % \end{macrocode} % For |\\temporal|, the specification of the overlays (between angular brackets) is mandatory. % \begin{macrocode} ( P "\\temporal" ) * P "<" * (1 - P ">") ^ 0 * P ">" * P "{" ) * K ( 'ParseAgain.noCR' , balanced_braces ) * L ( P "}{" ) * K ( 'ParseAgain.noCR' , balanced_braces ) * L ( P "}{" ) * K ( 'ParseAgain.noCR' , balanced_braces ) * L ( P "}" ) end % \end{macrocode} % % \bigskip % \paragraph{EOL} % % % 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} Ct ( Cc "EOL" * Ct ( Lc "\\@@_end_line:" * BeamerEndEnvironments * BeamerBeginEnvironments * PromptHastyDetection * Lc "\\@@_newline: \\@@_begin_line:" * Prompt ) ) ) * SpaceIndentation ^ 0 % \end{macrocode} % % \bigskip % \paragraph{The directives of the preprocessor} % % \begin{macrocode} local Preproc = K ( 'Preproc' , P "#" * (1 - P "\r" ) ^ 0 ) * ( EOL + -1 ) % \end{macrocode} % % % \bigskip % \paragraph{The comments in the C listings} % % We define different \textsc{lpeg} dealing with comments in the C listings. % \begin{macrocode} local CommentMath = P "$" * K ( 'Comment.Math' , ( 1 - S "$\r" ) ^ 1 ) * P "$" local Comment = WithStyle ( 'Comment' , Q ( P "//" ) * ( CommentMath + Q ( ( 1 - S "$\r" ) ^ 1 ) ) ^ 0 ) * ( EOL + -1 ) local LongComment = WithStyle ( 'Comment' , Q ( P "/*" ) * ( CommentMath + Q ( ( 1 - P "*/" - S "$\r" ) ^ 1 ) + EOL ) ^ 0 * Q ( P "*/" ) ) -- $ % \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{The main LPEG for the language C} % % First, the main loop : % \begin{macrocode} local MainC = EOL + Space + Tab + Escape + EscapeMath + CommentLaTeX + Beamer + Preproc + Comment + LongComment + Delim + Operator + String + Punct + DefFunction + DefClass + Type * ( Q ( "*" ) ^ -1 + Space + Punct + Delim + EOL + -1 ) + Keyword * ( Space + Punct + Delim + EOL + -1 ) + Builtin * ( Space + Punct + Delim + EOL + -1 ) + Identifier + Number + Word % \end{macrocode} % % Here, we must not put |local|! % \begin{macrocode} MainLoopC = ( ( space^1 * -1 ) + MainC ) ^ 0 % \end{macrocode} % % \bigskip % We recall that each line in the C 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} languageC = Ct ( ( ( space - P "\r" ) ^0 * P "\r" ) ^ -1 * BeamerBeginEnvironments * Lc '\\@@_begin_line:' * SpaceIndentation ^ 0 * MainLoopC * -1 * Lc '\\@@_end_line:' ) % \end{macrocode} % % \begin{macrocode} languages['c'] = languageC % \end{macrocode} % % % \bigskip % \subsubsection{The LPEG language SQL} % % \bigskip % In the identifiers, we will be able to catch those contening spaces, that is % to say like |"last name"|. % \begin{macrocode} local identifier = letter * ( alphanum + P "-" ) ^ 0 + P '"' * ( ( alphanum + space - P '"' ) ^ 1 ) * P '"' local Operator = K ( 'Operator' , P "=" + P "!=" + P "<>" + P ">=" + P ">" + P "<=" + P "<" + S "*+/" ) % \end{macrocode} % In SQL, the keywords are case-insensitive. That's why we have a little % complication. We will catch the keywords with the identifiers and, then, % distinguish the keywords with a Lua function. However, some keywords will be % catched in special LPEG because we want to detect the names of the SQL tables. % \begin{macrocode} local function Set (list) local set = {} for _, l in ipairs(list) do set[l] = true end return set end local set_keywords = Set { "ADD" , "AFTER" , "ALL" , "ALTER" , "AND" , "AS" , "ASC" , "BETWEEN" , "BY" , "CHANGE" , "COLUMN" , "CREATE" , "CROSS JOIN" , "DELETE" , "DESC" , "DISTINCT" , "DROP" , "FROM" , "GROUP" , "HAVING" , "IN" , "INNER" , "INSERT" , "INTO" , "IS" , "JOIN" , "LEFT" , "LIKE" , "LIMIT" , "MERGE" , "NOT" , "NULL" , "ON" , "OR" , "ORDER" , "OVER" , "RIGHT" , "SELECT" , "SET" , "TABLE" , "THEN" , "TRUNCATE" , "UNION" , "UPDATE" , "VALUES" , "WHEN" , "WHERE" , "WITH" } local set_builtins = Set { "AVG" , "COUNT" , "CHAR_LENGHT" , "CONCAT" , "CURDATE" , "CURRENT_DATE" , "DATE_FORMAT" , "DAY" , "LOWER" , "LTRIM" , "MAX" , "MIN" , "MONTH" , "NOW" , "RANK" , "ROUND" , "RTRIM" , "SUBSTRING" , "SUM" , "UPPER" , "YEAR" } % \end{macrocode} % % The \textsc{lpeg} |Identifer| will catch the identifiers of the fields % but also the keywords and the built-in functions of SQL. If will \emph{not} % catch the names of the SQL tables. % \begin{macrocode} local Identifier = C ( identifier ) / ( function (s) if set_keywords[string.upper(s)] -- the keywords are case-insensitive in SQL % \end{macrocode} % Remind that, in Lua, it's possible to return \emph{several} values. % \begin{macrocode} then return { "{\\PitonStyle{Keyword}{" } , { luatexbase.catcodetables.other , s } , { "}}" } else if set_builtins[string.upper(s)] then return { "{\\PitonStyle{Name.Builtin}{" } , { luatexbase.catcodetables.other , s } , { "}}" } else return { "{\\PitonStyle{Name.Field}{" } , { luatexbase.catcodetables.other , s } , { "}}" } end end end ) % \end{macrocode} % % \bigskip % \paragraph{The strings of SQL} % % \begin{macrocode} local String = K ( 'String.Long' , P "'" * ( 1 - P "'" ) ^ 1 * P "'" ) % \end{macrocode} % % \bigskip % \paragraph{Beamer} % % The following \textsc{lpeg} |balanced_braces| 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 balanced_braces = P { "E" , E = ( P "{" * V "E" * P "}" + String + ( 1 - S "{}" ) ) ^ 0 } % \end{macrocode} % % % \bigskip % \begin{macrocode} if piton_beamer then Beamer = L ( P "\\pause" * ( P "[" * ( 1 - P "]" ) ^ 0 * P "]" ) ^ -1 ) + Ct ( Cc "Open" * C ( ( P "\\uncover" + P "\\only" + P "\\alert" + P "\\visible" + P "\\invisible" + P "\\action" ) * ( P "<" * (1 - P ">") ^ 0 * P ">" ) ^ -1 * P "{" ) * Cc "}" ) * ( C ( balanced_braces ) / (function (s) return MainLoopSQL:match(s) end ) ) * P "}" * Ct ( Cc "Close" ) + OneBeamerEnvironment ( "uncoverenv" , MainLoopSQL ) + OneBeamerEnvironment ( "onlyenv" , MainLoopSQL ) + OneBeamerEnvironment ( "visibleenv" , MainLoopSQL ) + OneBeamerEnvironment ( "invisibleenv" , MainLoopSQL ) + OneBeamerEnvironment ( "alertenv" , MainLoopSQL ) + OneBeamerEnvironment ( "actionenv" , MainLoopSQL ) + 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 ( 'ParseAgain.noCR' , balanced_braces ) * L ( P "}{" ) * K ( 'ParseAgain.noCR' , balanced_braces ) * L ( P "}" ) + L ( % \end{macrocode} % For |\\temporal|, the specification of the overlays (between angular brackets) is mandatory. % \begin{macrocode} ( P "\\temporal" ) * P "<" * (1 - P ">") ^ 0 * P ">" * P "{" ) * K ( 'ParseAgain.noCR' , balanced_braces ) * L ( P "}{" ) * K ( 'ParseAgain.noCR' , balanced_braces ) * L ( P "}{" ) * K ( 'ParseAgain.noCR' , balanced_braces ) * L ( P "}" ) end % \end{macrocode} % % \bigskip % \paragraph{EOL} % % \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 SQL 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} Ct ( Cc "EOL" * Ct ( Lc "\\@@_end_line:" * BeamerEndEnvironments * BeamerBeginEnvironments * Lc "\\@@_newline: \\@@_begin_line:" ) ) ) * SpaceIndentation ^ 0 % \end{macrocode} % % % % \bigskip % \paragraph{The comments in the SQL listings} % % We define different \textsc{lpeg} dealing with comments in the SQL listings. % \begin{macrocode} local CommentMath = P "$" * K ( 'Comment.Math' , ( 1 - S "$\r" ) ^ 1 ) * P "$" local Comment = WithStyle ( 'Comment' , Q ( P "--" ) -- syntax of SQL92 * ( CommentMath + Q ( ( 1 - S "$\r" ) ^ 1 ) ) ^ 0 ) * ( EOL + -1 ) local LongComment = WithStyle ( 'Comment' , Q ( P "/*" ) * ( CommentMath + Q ( ( 1 - P "*/" - S "$\r" ) ^ 1 ) + EOL ) ^ 0 * Q ( P "*/" ) ) -- $ % \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{The main LPEG for the language SQL} % % % \begin{macrocode} local function LuaKeyword ( name ) return Lc ( "{\\PitonStyle{Keyword}{" ) * Q ( Cmt ( C ( identifier ) , function(s,i,a) return string.upper(a) == name end ) ) * Lc ( "}}" ) end % \end{macrocode} % % \begin{macrocode} local TableField = K ( 'Name.Table' , identifier ) * Q ( P "." ) * K ( 'Name.Field' , identifier ) local OneField = ( Q ( P "(" * ( 1 - P ")" ) ^ 0 * P ")" ) + K ( 'Name.Table' , identifier ) * Q ( P "." ) * K ( 'Name.Field' , identifier ) + K ( 'Name.Field' , identifier ) ) * ( Space * LuaKeyword ( "AS" ) * Space * K ( 'Name.Field' , identifier ) ) ^ -1 * ( Space * ( LuaKeyword ( "ASC" ) + LuaKeyword ( "DESC" ) ) ) ^ -1 local OneTable = K ( 'Name.Table' , identifier ) * ( Space * LuaKeyword ( "AS" ) * Space * K ( 'Name.Table' , identifier ) ) ^ -1 local WeCatchTableNames = LuaKeyword ( "FROM" ) * ( Space + EOL ) * OneTable * ( SkipSpace * Q ( P "," ) * SkipSpace * OneTable ) ^ 0 + ( LuaKeyword ( "JOIN" ) + LuaKeyword ( "INTO" ) + LuaKeyword ( "UPDATE" ) + LuaKeyword ( "TABLE" ) ) * ( Space + EOL ) * OneTable % \end{macrocode} % % % First, the main loop : % \begin{macrocode} local MainSQL = EOL + Space + Tab + Escape + EscapeMath + CommentLaTeX + Beamer + Comment + LongComment + Delim + Operator + String + Punct + WeCatchTableNames + ( TableField + Identifier ) * ( Space + Operator + Punct + Delim + EOL + -1 ) + Number + Word % \end{macrocode} % % Here, we must not put |local|! % \begin{macrocode} MainLoopSQL = ( ( space^1 * -1 ) + MainSQL ) ^ 0 % \end{macrocode} % % \bigskip % We recall that each line in the C 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} languageSQL = Ct ( ( ( space - P "\r" ) ^ 0 * P "\r" ) ^ -1 * BeamerBeginEnvironments * Lc '\\@@_begin_line:' * SpaceIndentation ^ 0 * MainLoopSQL * -1 * Lc '\\@@_end_line:' ) % \end{macrocode} % % \begin{macrocode} languages['sql'] = languageSQL % \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} corresponding to the considered language (|languages[language]|) % which returns as capture a Lua table containing data to send to LaTeX. % % \bigskip % \begin{macrocode} function piton.Parse(language,code) local t = languages[language] : match ( code ) if t == nil then tex.sprint("\\PitonSyntaxError") return -- to exit in force the function end local left_stack = {} local right_stack = {} for _ , one_item in ipairs(t) do if one_item[1] == "EOL" then for _ , s in ipairs(right_stack) do tex.sprint(s) end for _ , s in ipairs(one_item[2]) do tex.tprint(s) end for _ , s in ipairs(left_stack) do tex.sprint(s) end else % \end{macrocode} % % Here is an example of an item beginning with |"Open"|. % % |{ "Open" , "\begin{uncover}<2>" , "\end{cover}" }| % % In order to deal with the ends of lines, we have to close the environment % (|{cover}| in this example) at the end of each line and reopen it at the % beginning of the new line. That's why we use two Lua stacks, called % |left_stack| and |right_stack|. |left_stack| will be for the elements like % |\begin{uncover}<2>| and |right_stack| will be for the elements like % |\end{cover}|. % \begin{macrocode} if one_item[1] == "Open" then tex.sprint( one_item[2] ) table.insert(left_stack,one_item[2]) table.insert(right_stack,one_item[3]) else if one_item[1] == "Close" then tex.sprint( right_stack[#right_stack] ) left_stack[#left_stack] = nil right_stack[#right_stack] = nil else tex.tprint(one_item) end end end end 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(language,name,first_line,last_line) local 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 % \end{macrocode} % We extract the BOM of utf-8, if present. % \begin{macrocode} if string.byte(s,1) == 13 then if string.byte(s,2) == 239 then if string.byte(s,3) == 187 then if string.byte(s,4) == 191 then s = string.sub(s,5,-1) end end end end piton.Parse(language,s) end % \end{macrocode} % % \bigskip % \subsubsection{Two variants of the function Parse with integrated preprocessors} % % 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.ParseBis(language,code) local s = ( Cs ( ( P '##' / '#' + 1 ) ^ 0 ) ) : match ( code ) return piton.Parse(language,s) end % \end{macrocode} % % \bigskip % The following command will be used when we have to parse some small chunks of % code that have yet been parsed. They are re-scanned by LaTeX because it has % been required by |\@@_piton:n| in the \pkg{piton} style of the syntaxic % element. In that case, you have to remove the potential |\@@_breakable_space:| % that have been inserted when the key |break-lines| is in force. % \begin{macrocode} function piton.ParseTer(language,code) local s = ( Cs ( ( P '\\@@_breakable_space:' / ' ' + 1 ) ^ 0 ) ) : match ( code ) return piton.Parse(language,s) end % \end{macrocode} % % % \bigskip % \subsubsection{Preprocessors of the function Parse for gobble} % % 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(language,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(language,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 io.open(name) -- added 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 % The following function stores in |\l_@@_first_line_int| and % |\l_@@_last_line_int| the numbers of lines of the file |file_name| % corresponding to the strings |marker_beginning| and |marker_end|. % \begin{macrocode} function piton.ComputeRange(marker_beginning,marker_end,file_name) local s = ( Cs ( ( P '##' / '#' + 1 ) ^ 0 ) ) : match ( marker_beginning ) local t = ( Cs ( ( P '##' / '#' + 1 ) ^ 0 ) ) : match ( marker_end ) local first_line = -1 local count = 0 local last_found = false for line in io.lines(file_name) do if first_line == -1 then if string.sub(line,1,#s) == s then first_line = count end else if string.sub(line,1,#t) == t then last_found = true break end end count = count + 1 end if first_line == -1 then tex.sprint("\\PitonBeginMarkerNotFound") else if last_found == false then tex.sprint("\\PitonEndMarkerNotFound") end end tex.sprint( luatexbase.catcodetables.expl , '\\int_set:Nn \\l_@@_first_line_int {' .. first_line .. ' + 2 }' .. '\\int_set:Nn \\l_@@_last_line_int {' .. count .. ' }' ) end % % \end{macrocode} % % % % \vspace{1cm} % \section{History} % % The successive versions of the file |piton.sty| provided by TeXLive are available on the % \textsc{svn} server of TeXLive:\par\nobreak % % \smallskip % { % \small % \nolinkurl{https://tug.org/svn/texlive/trunk/Master/texmf-dist/tex/lualatex/piton/piton.sty} % } % % \medskip % The development of the extension \pkg{piton} is done on the following GitHub % repository: % % \verb|https://github.com/fpantigny/piton| % % \subsection*{Changes between versions 2.1 and 2.2} % % New key |path| for |\PitonOptions|. % % New language SQL. % % It's now possible to define styles locally to a given language (with the % optional argument of |\SetPitonStyle|). % % \subsection*{Changes between versions 2.0 and 2.1} % % The key |line-numbers| has now subkeys |line-numbers/skip-empty-lines|, % |line-numbers/label-empty-lines|, etc. % % The key |all-line-numbers| is deprecated: use % |line-numbers/skip-empty-lines=false|. % % New system to import, with |\PitonInputFile|, only a part (of the file) % delimited by textual markers. % % New keys |begin-escape|, |end-escape|, |begin-escape-math| and |end-escape-math|. % % The key |escape-inside| is deprecated: use |begin-escape| and |end-escape|. % % % \subsection*{Changes between versions 1.6 and 2.0} % % The extension \pkg{piton} nows supports the computer languages OCaml and C % (and, of course, Python). % % \subsection*{Changes between versions 1.5 and 1.6} % % New key |width| (for the total width of the listing). % % New style |UserFunction| to format the names of the Python functions % previously defined by the user. Command |\PitonClearUserFunctions| to clear % the list of such functions names. % % \subsection*{Changes between versions 1.4 and 1.5} % % New key |numbers-sep|. % % % \subsection*{Changes between versions 1.3 and 1.4} % % New key |identifiers| in |\PitonOptions|. % % New command |\PitonStyle|. % % |background-color| now accepts as value a \emph{list} of colors. % % \subsection*{Changes between versions 1.2 and 1.3} % % When the class Beamer is used, the environment |{Piton}| and the command % |\PitonInputFile| are ``overlay-aware'' (that is to say, they accept a % specification of overlays between angular brackets). % % New key |prompt-background-color| % % It's now possible to use the command |\label| to reference a line of code in % an environment |{Piton}|. % % A new command |\|␣ is available in the argument of the command |\piton{...}| to % insert a space (otherwise, several spaces are replaced by a single space). % % \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|. % % % % \tableofcontents % % \end{document} % % % Local Variables: % TeX-fold-mode: t % TeX-fold-preserve-comments: nil % flyspell-mode: nil % fill-column: 80 % End: