diff options
Diffstat (limited to 'macros/luatex/latex/piton/piton.dtx')
-rw-r--r-- | macros/luatex/latex/piton/piton.dtx | 1000 |
1 files changed, 805 insertions, 195 deletions
diff --git a/macros/luatex/latex/piton/piton.dtx b/macros/luatex/latex/piton/piton.dtx index d7180b9fa3..4aeabac72b 100644 --- a/macros/luatex/latex/piton/piton.dtx +++ b/macros/luatex/latex/piton/piton.dtx @@ -1,3 +1,4 @@ +% \iffalse -*- coding: utf-8 ; -*- \fi % \iffalse meta-comment % % Copyright (C) 2022 by F. Pantigny @@ -15,8 +16,8 @@ % % \fi % \iffalse -\def\myfileversion{0.6a} -\def\myfiledate{2022/09/19} +\def\myfileversion{0.7} +\def\myfiledate{2022/10/08} % % %<*batchfile> @@ -72,7 +73,7 @@ def arctan(x,n=10): 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\#\#} + ## (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): @@ -128,12 +129,12 @@ def fact(n): % \PitonInputFile{Example1.txt} % % -% \section{Installation} +% \bigskip % -% 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. The best way is to install them in a |texmf| tree. +% The package \pkg{piton} is entirely contained in the file +% |piton.sty|. This file may be put in the current directory or in a +% |texmf| tree. However, the best is to install \pkg{piton} with a TeX +% distribution such as MiKTeX, TeX Live or MacTeX. % % % \section{Use of the package} @@ -166,12 +167,13 @@ def fact(n): % % \subsection{The command \textbackslash PitonOptions} % -% The command |\PitonOptions| provides five keys: |gobble|, |auto-gobble|, -% |env-gobble|, |line-numbers| and |all-line-numbers|. +% 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. % % \begin{itemize} % \item The key |gobble| takes in as value a positive integer~$n$: the first $n$ -% characters are discarded (before the process of hightlighning of the code) for +% characters are discarded (before the process of highlightning of the code) for % each line of the environment |{Piton}|. % % \item Then the key |auto-gobble| is in force, the extension \pkg{piton} computes @@ -181,7 +183,9 @@ def fact(n): % % \item When the key |env-gobble| is in force, \pkg{piton} applies |gobble| with % a value of $n$ equal to the number of spaces before |\end{Piton}| on the last -% line (if that line contains only spaces). +% line (if that line contains only spaces). The name of that key comes from +% \emph{environment gobble}: the effect of gobble is set by the position of the +% commands |\begin{Piton}| and |\end{Piton}| which delimit the current environment. % % \item With the key |line-numbers|, the \emph{non empty} lines are numbered in % the environments |{Piton}| and in the listings resulting from the use of @@ -189,6 +193,24 @@ def fact(n): % % \item With the key |all-line-numbers|, \emph{all} the lines are numbered, % including the empty ones. +% +% \item \colorbox{yellow!50}{\textbf{New 0.7}}\enskip By default, the counter of +% lines is set to zero at the beginning of each environment |{Piton}| or use of +% |\PitonInputFile|. With the key |resume|, that reinitialisation is not done. +% +% \item \colorbox{yellow!50}{\textbf{New 0.7}}\enskip The key |splittable| allows +% pages breaks within the environments |{Piton}| and the listings produced by +% |\PitonInputFile|. +% +% \item \colorbox{yellow!50}{\textbf{New 0.7}}\enskip The key |background-color| +% sets the background color of the environments |{Piton}| and the listings +% produced by |\PitonInputFile| (that background has a width of |\linewidth|). +% Even with a background color, the pages breaks are allowed, as soon as the key +% |splittable| is in force.\footnote{The environments \texttt{\{Piton\}} are breakable, +% even whithin 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}.} % \end{itemize} % % \bigskip @@ -196,7 +218,7 @@ def fact(n): % \begingroup % \fvset{commandchars=\~\&\@,formatcom=\small\color{gray}} % \begin{Verbatim} -% ~emphase&\PitonOptions{line-numbers,auto-gobble}@ +% ~emphase&\PitonOptions{line-numbers,auto-gobble,background-color = gray!15}@ % \begin{Piton} % from math import pi % @@ -207,7 +229,7 @@ def fact(n): % return -arctan(-x) # recursive call % elif x > 1: % return pi/2 - arctan(1/x) -% ## (on a utilisé le fait que $\arctan(x)+\arctan(1/x)=\frac{\pi}{2}$ pour $x>0$) +% ## (we have used that $\arctan(x)+\arctan(1/x)=\frac{\pi}{2}$ pour $x>0$) % else % s = 0 % for k in range(n): @@ -219,17 +241,17 @@ def fact(n): % % % \begingroup -% \PitonOptions{line-numbers,auto-gobble} +% \PitonOptions{line-numbers,auto-gobble,background-color = gray!15} % \PitonInputFile{Example1.txt} % \endgroup % -% \subsection{The option escape-inside} +% \subsection{The key escape-inside} % -% The option |escape-inside| must be used when loading the package \pkg{piton} +% The key |escape-inside| must be used when loading the package \pkg{piton} % (that is to say in the instruction |\usepackage|). For technical reasons, it % can't be used in the command |\PitonOptions|. That option takes in as value % two characters which will be used to delimit pieces of code which will -% composed in LaTeX. +% thrown directly to LaTeX (and composed by LaTeX).e % % \medskip % In the following example, we assume that the extension \pkg{piton} has been @@ -292,7 +314,7 @@ def fact(n): % % In that example, |\colorbox{yellow!50}| must be considered as the name of a % LaTeX command which takes in exactly one argument, since, usually, it is used -% with the syntax |\colorbox{yellow!50}{text}|. +% with the syntax |\colorbox{yellow!50}{...}|. % % \medskip % \begingroup @@ -324,7 +346,7 @@ def fact(n): % is to say the characters \texttt{\{}, \texttt{\}} and \texttt{:}) \\ % Operator & the following operators : \texttt{!= == << >> - \~{} + / * \% = < > \& .} \verb+|+ \verb|@|\\ % Operator.Word & the following operators : \texttt{in}, \texttt{is}, -% \texttt{and}, \texttt{or} et \texttt{not} \\ +% \texttt{and}, \texttt{or} and \texttt{not} \\ % Name.Builtin & the predefined functions of Python \\ % Name.Function & the name of the functions defined by the user, at the point of % their definition (that is to say after the keyword |def|) \\ @@ -368,8 +390,8 @@ def fact(n): % \verb|\NewPitonEnvironment{Python}{}{}{}| % % \bigskip -% If on wished to format Python code in a box in a box of \pkg{tcolorbox}, it's -% possible to define an environment |{Python}| with the following code: +% If one wishes to format Python code in a box of \pkg{tcolorbox}, it's possible +% to define an environment |{Python}| with the following code: % %\begin{verbatim} % \NewPitonEnvironment{Python}{} @@ -405,11 +427,12 @@ def fact(n): % \end{macrocode} % % \bigskip -% We define a set of keys |piton/package| for these options. +% We define a set of keys for the options at load-time. % \begin{macrocode} \keys_define:nn { piton / package } { escape-inside .tl_set:N = \c_@@_escape_inside_tl , + escape-inside .initial:n = , unknown .code:n = \msg_error:nn { piton } { unknown~key~for~package } } % \end{macrocode} @@ -417,19 +440,16 @@ def fact(n): % \begin{macrocode} \msg_new:nnn { piton } { unknown~key~for~package } { - Unknown~key\\ + Unknown~key.\\ You~have~used~the~key~'\l_keys_key_str'~but~the~only~key~available~here~ - is~the~key~'escape-inside'.\\ + is~the~key~'escape-inside'.~Other~keys~are~available~in~\token_to_str:N + \PitonOptions.\\ That~key~will~be~ignored. } % \end{macrocode} % % \bigskip -% \begin{macrocode} -\tl_clear_new:N \c_@@_escape_inside_tl -% \end{macrocode} -% -% We process the options when the package is loaded (with |\usepackage|). +% We process the options provided by the user at load-time. % \begin{macrocode} \ProcessKeysOptions { piton / package } % \end{macrocode} @@ -439,8 +459,8 @@ def fact(n): \begingroup \cs_new_protected:Npn \@@_set_escape_char:nn #1 #2 { - \directlua { begin_escape = "#1" } - \directlua { end_escape = "#2" } + \lua_now:n { begin_escape = "#1" } + \lua_now:n { end_escape = "#2" } } \cs_generate_variant:Nn \@@_set_escape_char:nn { x x } \@@_set_escape_char:xx @@ -452,7 +472,7 @@ def fact(n): % % \bigskip % \begin{macrocode} -\AtBeginDocument +\hook_gput_code:nnn { begindocument } { . } { \@ifpackageloaded { xcolor } { } @@ -469,125 +489,110 @@ def fact(n): } % \end{macrocode} % -% \bigskip -% \begin{macrocode} -\msg_new:nnn { piton } { 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} % -% \begin{macrocode} -\lua_now:e { require("piton.lua") } -% \end{macrocode} -% -% \bigskip -% The following function has not a name with the conventions of L3 because it -% will be used in the Lua code. -% \begin{macrocode} -\cs_new:Npn \pitonEOL - { - \par \leavevmode - \@@_print_number: - } -% \end{macrocode} -% % \medskip -% \begin{macrocode} -\cs_new_protected:Npn \@@_print_number: - { - \bool_if:NT \l_@@_line_numbers_bool - { \@@_actually_print_number: } - } -% \end{macrocode} +% \subsection{Parameters} % % \medskip +% The following flag corresponds to the key |splittable| of |\PitonOptions|. % \begin{macrocode} -\cs_new_protected:Npn \@@_actually_print_number: - { -% \end{macrocode} -% The empty lines in the code are not numbered. -% \begin{macrocode} - \bool_if:NF \l_@@_all_line_numbers_bool - { \peek_meaning:NF \pitonEOL } - \@@_actually_print_number_i: - } +\bool_new:N \l_@@_splittable_bool % \end{macrocode} % +% \medskip +% The following string corresponds to the key |background-color|. % \begin{macrocode} -\cs_new_protected:Npn \@@_actually_print_number_i: - { - \int_incr:N \l_@@_lineno_int - \hbox_overlap_left:n - { - { \color{gray} \footnotesize \int_to_arabic:n \l_@@_lineno_int } - \quad - } - } +\str_new:N \l_@@_background_color_str % \end{macrocode} % -% \bigskip -% 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. -% \begin{macrocode} -\int_new:N \l_@@_lineno_int -% \end{macrocode} % -% \bigskip +% \subsection{The gobbling mechanism} +% +% \medskip % The following integer is the number of characters to gobble on the left side % of the Python listings. Of course, the initial value is $0$. % \begin{macrocode} \int_new:N \l_@@_gobble_int % \end{macrocode} % -% -% \bigskip +% \medskip % \begin{macrocode} \cs_new_protected:Npn \@@_define_gobble_syntax:n #1 { \lua_now:n { define_gobble_syntax(#1) } } % \end{macrocode} +% +% \medskip +% \subsection{Treatment of a line of code} % -% \bigskip +% In the contents provided by Lua, each line of the Python code will be +% surrounded by |\@@_begin_line:| and |\@@_end_line:|. +% % \begin{macrocode} -\NewDocumentCommand { \piton } { v } - { - \group_begin: - \ttfamily - \lua_now:e { Parse(token.scan_argument()) } { #1 } - \group_end: - } +\cs_set_protected:Npn \@@_begin_line: #1 \@@_end_line: + { % \end{macrocode} -% -% \bigskip +% Be careful: the following |\hbox_set:Nn| et |\hbox_set_to_wd:Nnn| take in an +% argument by curryfication. % \begin{macrocode} -\NewDocumentCommand { \PitonInputFile } { m } - { - \group_begin: - \ttfamily - \bool_if:NT \l_@@_line_numbers_bool - { - \@@_actually_print_number: - \vspace{-\baselineskip} - } - \lua_now:e { ParseFile(token.scan_argument()) } { #1 } - \group_end: + \str_if_empty:NTF \l_@@_background_color_str + { \hbox_set:Nn \l_tmpa_box } + { \hbox_set_to_wd:Nnn \l_tmpa_box \linewidth } + { + \bool_if:NT \l_@@_line_numbers_bool + { + \bool_if:NF \l_@@_all_line_numbers_bool + { \tl_if_empty:nF { #1 } } + \@@_print_number: + } + \strut + \str_if_empty:NF \l_@@_background_color_str \space + #1 \hfill + } + \box_set_dp:Nn \l_tmpa_box { \box_dp:N \l_tmpa_box + 1 pt } + \box_set_ht:Nn \l_tmpa_box { \box_ht:N \l_tmpa_box + 1.5 pt } + \tl_if_empty:NTF \l_@@_background_color_str + { \box_use_drop:N \l_tmpa_box } + { + \vbox_top:n + { + \hbox_to_wd:nn \linewidth + { + \exp_args:NV \color \l_@@_background_color_str + \vrule height \box_ht:N \l_tmpa_box + depth \box_dp:N \l_tmpa_box + width \linewidth + } + \skip_vertical:n { - \box_ht_plus_dp:N \l_tmpa_box } + \box_use_drop:N \l_tmpa_box + } + } + \vspace { - 2.5 pt } } % \end{macrocode} % +% +% \medskip % \subsection{PitonOptions} % +% \medskip +% The following parameters correspond to the keys |line-numbers| and +% |all-line-numbers|. % \begin{macrocode} \bool_new:N \l_@@_line_numbers_bool \bool_new:N \l_@@_all_line_numbers_bool % \end{macrocode} % +% \medskip +% The following flag corresponds to the key |resume|. +% \begin{macrocode} +\bool_new:N \l_@@_resume_bool +% \end{macrocode} +% +% \bigskip +% Be careful! The name of the following set of keys must be considered as +% public! Hence, it should \emph{not} be changed. +% % \begin{macrocode} \keys_define:nn { PitonOptions } { @@ -603,6 +608,12 @@ def fact(n): \bool_set_true:N \l_@@_line_numbers_bool \bool_set_true:N \l_@@_all_line_numbers_bool , all-line-numbers .value_forbidden:n = true , + resume .bool_set:N = \l_@@_resume_bool , + resume .value_forbidden:n = true , + splittable .bool_set:N = \l_@@_splittable_bool , + splittable .default:n = true , + background-color .str_set:N = \l_@@_background_color_str , + background-color .value_required:n = true , unknown .code:n = \msg_error:nn { piton } { Unknown~key~for~PitonOptions } } @@ -612,69 +623,132 @@ def fact(n): % \begin{macrocode} \msg_new:nnn { piton } { Unknown~key~for~PitonOptions } { - Unknown~key \\ + Unknown~key. \\ The~key~'\l_keys_key_str'~is~unknown~for~\token_to_str:N \PitonOptions.~The~ - available~keys~are:~all-line-numbers,~auto-gobble,~env-gobble,~gobble~and~ - line-numbers.\\ + available~keys~are:~all-line-numbers,~auto-gobble,~env-gobble,~gobble,~ + line-numbers,~resume~and~splittable.\\ If~you~go~on,~that~key~will~be~ignored. } % \end{macrocode} % % \bigskip -% The argument of |\PitonOptions| is provided by currification. +% The argument of |\PitonOptions| is provided by curryfication. +% \begin{macrocode} +\NewDocumentCommand \PitonOptions { } { \keys_set:nn { PitonOptions } } +% \end{macrocode} +% +% \medskip +% \subsection{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. +% \begin{macrocode} +\int_new:N \g_@@_line_int +% \end{macrocode} +% +% +% \begin{macrocode} +\cs_new_protected:Npn \@@_print_number: + { + \int_gincr:N \g_@@_line_int + \hbox_overlap_left:n + { + { \color { gray } \footnotesize \int_to_arabic:n \g_@@_line_int } + \quad + } + } +% \end{macrocode} +% +% +% +% \medskip +% \subsection{The main commands and environments for the final user} +% +% \begin{macrocode} +\NewDocumentCommand { \piton } { v } + { + \group_begin: + \ttfamily + \cs_set_protected:Npn \@@_begin_line: { } + \cs_set_protected:Npn \@@_end_line: { } + \lua_now:n { Parse(token.scan_argument()) } { #1 } + \group_end: + } +% \end{macrocode} +% +% \medskip +% The command |\@@_piton:n| does \emph{not} take in its argument verbatim. +% \begin{macrocode} +\cs_new_protected:Npn \@@_piton:n #1 + { + \group_begin: + \cs_set_protected:Npn \@@_begin_line: { } + \cs_set_protected:Npn \@@_end_line: { } + \lua_now:e { Parse(token.scan_argument()) } { #1 } + \group_end: + } +% \end{macrocode} +% +% +% \bigskip % \begin{macrocode} -\NewDocumentCommand \PitonOptions { } - { \keys_set:nn { PitonOptions } } +\NewDocumentCommand { \PitonInputFile } { m } + { + \bool_if:NF \l_@@_resume_bool { \int_gzero:N \g_@@_line_int } + \group_begin: + \dim_set_eq:NN \parindent \c_zero_dim + \ttfamily + \lua_now:e { ParseFile(token.scan_argument()) } { #1 } + \group_end: + } % \end{macrocode} +% % % \bigskip % \begin{macrocode} \NewDocumentCommand { \NewPitonEnvironment } { m m m m } { % \end{macrocode} -% We construct a TeX macro which will catch as its argument all the tokens -% until \textsl{\texttt{newline}} + |\end{Piton}| with, in that -% \textsl{\texttt{newline}}| + \end{Piton}|, the catcode of -% \textsl{\texttt{newline}}, |\|, |{| and |}| equal to 12 (``\texttt{other}''). -% The latter explains why the definition of that function is a bit complicated. +% 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 { __piton_collect_ #1 :w } + \use:c { _@@_collect_ #1 :w } ####1 \c_backslash_str end \c_left_brace_str #1 \c_right_brace_str } { \group_end: - \par \addvspace { 0.5 em } - { - \dim_set_eq:NN \parindent \c_zero_dim - \ttfamily - \bool_if:NT \l_@@_line_numbers_bool - { - \@@_actually_print_number: - \vspace{-\baselineskip} - } - \int_case:nnF \l_@@_gobble_int - { - 0 -% \end{macrocode} -% Be careful: the last argument is provided by currification. -% \begin{macrocode} - { \lua_now:e { Parse(token.scan_argument()) } } - { -1 } - { \lua_now:e { AutoGobbleParse(token.scan_argument()) } } - { -2 } - { \lua_now:e { EnvGobbleParse(token.scan_argument()) } } - } - { - \exp_args:NV \@@_define_gobble_syntax:n \l_@@_gobble_int - \lua_now:e { GobbleParse(token.scan_argument()) } - } - { ##1 } - } - \par \addvspace { 0.5 em } + \mode_if_vertical:TF \mode_leave_vertical: \newline + \bool_if:NF \l_@@_splittable_bool \vbox_top:n + { + \ttfamily + \dim_zero:N \lineskip + \int_case:nnF \l_@@_gobble_int + { + 0 +% \end{macrocode} +% Be careful: the last argument is provided by curryfication. +% \begin{macrocode} + { \lua_now:n { Parse(token.scan_argument()) } } + { -1 } + { \lua_now:n { AutoGobbleParse(token.scan_argument()) } } + { -2 } + { \lua_now:n { EnvGobbleParse(token.scan_argument()) } } + } + { + \exp_args:NV \@@_define_gobble_syntax:n \l_@@_gobble_int + \lua_now:n { GobbleParse(token.scan_argument()) } + } + { ##1 } + \vspace { 2.5 pt } + } % \end{macrocode} % The following |\end{#1}| is only for the groups and the stack of % environments of LaTeX. @@ -686,48 +760,65 @@ def fact(n): % % \bigskip % We can now define the new environment. +% +% We are still in the definition of the command |\NewPitonEnvironment|... % \begin{macrocode} \NewDocumentEnvironment { #1 } { #2 } { #3 + \bool_if:NF \l_@@_resume_bool { \int_gzero:N \g_@@_line_int } \group_begin: + \box_clear:N \l_tmpa_box \tl_map_function:nN { \ \\ \{ \} \$ \& \# \^ \_ \% \~ } \char_set_catcode_other:N - \use:c { __piton_collect_ #1 :w } + \use:c { _@@_collect_ #1 :w } } { #4 } % \end{macrocode} % +% \medskip +% The following code is for technical reasons. We want to change the catcode of +% |^^M| before catching the arguments of the new environment we are defining. +% Indeed, if not, we will have problems if there is a final optional argument in +% our environment (if that final argument is not used by the user in an +% instance of the environment, a spurious space is inserted, probably because +% the |^^M| is converted to space). % \begin{macrocode} \AddToHook { env / #1 / begin } { \char_set_catcode_other:N \^^M } } % \end{macrocode} +% This is the end of the definition of the command |\NewPitonEnvironment|. % % \bigskip % \begin{macrocode} \NewPitonEnvironment { Piton } { } { } { } % \end{macrocode} -% -% \bigskip % +% \bigskip +% \subsection{The styles} +% +% \medskip +% The following command is fundamental: it will be used by the Lua code. % \begin{macrocode} -\NewDocumentCommand { \PitonStyle } { m } { \csname pitonStyle#1\endcsname } +\NewDocumentCommand { \PitonStyle } { m } { \use:c { pitonStyle #1 } } % \end{macrocode} % -% \bigskip +% \medskip +% The following command takes in its argument by curryfication. % \begin{macrocode} -\NewDocumentCommand { \SetPitonStyle } { } { \keys_set:nn { piton } } +\NewDocumentCommand { \SetPitonStyle } { } { \keys_set:nn { piton / Styles } } % \end{macrocode} % +% \medskip % \begin{macrocode} \cs_new_protected:Npn \@@_math_scantokens:n #1 { \normalfont \scantextokens { $#1$ } } % \end{macrocode} % -% \bigskip +% \medskip % \begin{macrocode} -\keys_define:nn { piton } +\keys_define:nn { piton / Styles } { String.Interpol .tl_set:c = pitonStyle String.Interpol , String.Interpol .value_required:n = true , @@ -779,40 +870,48 @@ def fact(n): Exception .value_required:n = true , Comment.LaTeX .tl_set:c = pitonStyle Comment.LaTeX , Comment.LaTeX .value_required:n = true , - unknown .code:n = \msg_error:nn { piton }{ Unknown~key~for~SetPitonStyle } + unknown .code:n = + \msg_error:nn { piton } { Unknown~key~for~SetPitonStyle } } % \end{macrocode} % % % \bigskip % \begin{macrocode} -\msg_new:nnn { piton } { Unknown~key~for~SetPitonStyle } { - The~style~'\l_keys_key_str'~is~unknown.\\ - This~key~will~be~ignored.\\ - The~available~styles~are~(in~alphabetic~order):~ - Comment,~ - Comment.LaTeX,~ - Dict.Value,~ - Exception,~ - InitialValues,~ - Keyword,~ - Keyword.Constant,~ - Name.Builtin,~ - Name.Class,~ - Name.Decorator,~ - Name.Function,~ - Name.Namespace,~ - Number,~ - Operator,~ - Operator.Word,~ - String,~ - String.Doc,~ - String.Long,~ - String.Short,~and~ - String.Interpol. } +\msg_new:nnn { piton } { Unknown~key~for~SetPitonStyle } + { + The~style~'\l_keys_key_str'~is~unknown.\\ + This~key~will~be~ignored.\\ + The~available~styles~are~(in~alphabetic~order):~ + Comment,~ + Comment.LaTeX,~ + Dict.Value,~ + Exception,~ + InitialValues,~ + Keyword,~ + Keyword.Constant,~ + Name.Builtin,~ + Name.Class,~ + Name.Decorator,~ + Name.Function,~ + Name.Namespace,~ + Number,~ + Operator,~ + Operator.Word,~ + String,~ + String.Doc,~ + String.Long,~ + String.Short,~and~ + String.Interpol. + } % \end{macrocode} % -% \bigskip +% +% \subsection{The initial style} +% +% The initial style is inspired by the style ``manni'' of Pygments. +% +% \medskip % \begin{macrocode} \SetPitonStyle { @@ -833,17 +932,528 @@ def fact(n): String.Interpol = \color[HTML]{AA0000} , Comment.LaTeX = \normalfont \color[rgb]{.468,.532,.6} , Name.Type = \color[HTML]{336666} , - InitialValues = \piton , - Dict.Value = \piton , - Post.Function = \piton , - Interpol.Inside = \color{black}\piton , + InitialValues = \@@_piton:n , + Dict.Value = \@@_piton:n , + Post.Function = \@@_piton:n , + Interpol.Inside = \color{black}\@@_piton:n , } % \end{macrocode} % +% \subsection{Security} +% +% \begin{macrocode} +\AddToHook { env / piton / begin } + { \msg_fatal:nn { piton } { No~environment~piton } } + +\msg_new:nnn { piton } { No~environment~piton } + { + There~is~no~environment~piton!\\ + There~is~an~environment~{Piton}~and~a~command~ + \token_to_str:N \piton\ but~there~is~no~environment~ + {piton}.~This~error~is~fatal. + } +% \end{macrocode} +% % +% \bigskip +% \subsection{The Lua code} +% +% \medskip +% \begin{macrocode} +\ExplSyntaxOff +\RequirePackage{luacode} +% \end{macrocode} +% % \vspace{1cm} -% \tableofcontents +% \begin{macrocode} +\begin{luacode*} +local P, S, V , C , Ct, Cc, Cf = lpeg.P, lpeg.S, lpeg.V, lpeg.C, lpeg.Ct, lpeg.Cc, lpeg.Cf + + +--[[ By convention, a capture which provides as value a table (and not a string), provides, in fact, +a string (the first element of the table) which is a formatting LaTeX instruction (it will be +thrown back to TeX with normal catcodes (ant not ``other'' catcode for everybody).]] + +local function L(string) + return Cc ( { string } ) +end + +local function K(pattern, style) + return + L ( "{\\PitonStyle{" .. style .. "}{" ) + * C(pattern) + * L ( "}}" ) +end + + +--[[ The text in "escape" (between begin_escape and end_escape) is captured +and put in a table (with only one component). Indeed, we have decided that a capture +which is encapsulated in a table must be transmitted to TeX with the normal TeX catcodes.]] + +local Escape = P(begin_escape) + * Ct ( C ( ( 1 - P(end_escape) ) ^ 1 ) ) + * P(end_escape) + + +lpeg.locale(lpeg) -- mandatory + +local alpha , digit , space , punct = lpeg.alpha , lpeg.digit , lpeg.space , lpeg.punct + +local letter = alpha + S"âàçéèêëïîôûüÂÀÇÉÈÊËÏÎÔÛÜ_" + +local alphanum = letter + digit + +local identifier = letter * alphanum ^ 0 + +local Identifier = C ( identifier ) + +local Space = C ( ( space - P "\r" ) ^ 1 ) + +local SkipSpace = C ( ( space - P "\r" ) ^ 0 ) + +local Punct = C ( punct ) + + +local EOL = ( P "\r" ) + * + ( + ( space^0 * -1 ) + + + Cc ( + { + luatexbase.catcodetables.expl , + '\\__piton_end_line: \\bool_if:NT \\l__piton_splittable_bool \\newline \\__piton_begin_line:' + } + ) + ) + + +local Number = + K ( + ( digit^1 * P "." * digit^0 + digit^0 * P "." * digit^1 + digit^1 ) + * ( S "eE" * S "+-" ^ -1 * digit^1 ) ^ -1 + + digit^1 + , 'Number' ) + + +local Word = C ( ( ( 1 - space ) - S "'\"\r[()]" - digit ) ^ 1 ) + +if begin_escape ~= '' +then Word = C ( ( ( 1 - space - P(begin_escape) - P(end_escape) ) - S "'\"\r[()]" - digit ) ^ 1 ) +end + +local Delim = C ( S "[()]" ) + + +local Keyword = + K ( P "assert" + P "break" + P "case" + P "continue" + P "del" + + P "elif" + P "else" + P "except" + P "exec" + P "finally" + P "for" + P "from" + + P "global" + P "if" + P "import" + P "lambda" + P "non local" + + P "pass" + P "return" + P "try" + P "while" + + P "with" + P "yield" + P "yield from" , + 'Keyword' ) + + K ( P "True" + P "False" + P "None" , 'Keyword.Constant' ) + + +local Builtin = + K ( P "__import__" + P "abs" + P "all" + P "any" + P "bin" + P "bool" + P "bytearray" + + P "bytes" + P "chr" + P "classmethod" + P "compile" + P "complex" + P "delattr" + + P "dict" + P "dir" + P "divmod" + P "enumerate" + P "eval" + P "filter" + + P "float" + P "format" + P "frozenset" + P "getattr" + P "globals" + P "hasattr" + + P "hash" + P "hex" + P "id" + P "input" + P "int" + P "isinstance" + P "issubclass" + + P "iter" + P "len" + P "list" + P "locals" + P "map" + P "max" + P "memoryview" + P "min" + + P "next" + P "object" + P "oct" + P "open" + P "ord" + P "pow" + P "print" + P "property" + + P "range" + P "repr" + P "reversed" + P "round" + P "set" + P "setattr" + P "slice" + + P "sorted" + P "staticmethod" + P "str" + P "sum" + P "super" + P "tuple" + P "type" + + P "vars" + P "zip" , + 'Name.Builtin' ) + + +local Exception = + K ( "ArithmeticError" + P "AssertionError" + P "AttributeError" + + P "BaseException" + P "BufferError" + P "BytesWarning" + P "DeprecationWarning" + + P "EOFError" + P "EnvironmentError" + P "Exception" + P "FloatingPointError" + + P "FutureWarning" + P "GeneratorExit" + P "IOError" + P "ImportError" + + P "ImportWarning" + P "IndentationError" + P "IndexError" + P "KeyError" + + P "KeyboardInterrupt" + P "LookupError" + P "MemoryError" + P "NameError" + + P "NotImplementedError" + P "OSError" + P "OverflowError" + + P "PendingDeprecationWarning" + P "ReferenceError" + P "ResourceWarning" + + P "RuntimeError" + P "RuntimeWarning" + P "StopIteration" + + P "SyntaxError" + P "SyntaxWarning" + P "SystemError" + P "SystemExit" + + P "TabError" + P "TypeError" + P "UnboundLocalError" + P "UnicodeDecodeError" + + P "UnicodeEncodeError" + P "UnicodeError" + P "UnicodeTranslateError" + + P "UnicodeWarning" + P "UserWarning" + P "ValueError" + P "VMSError" + + P "Warning" + P "WindowsError" + P "ZeroDivisionError" + + P "BlockingIOError" + P "ChildProcessError" + P "ConnectionError" + + P "BrokenPipeError" + P "ConnectionAbortedError" + P "ConnectionRefusedError" + + P "ConnectionResetError" + P "FileExistsError" + P "FileNotFoundError" + + P "InterruptedError" + P "IsADirectoryError" + P "NotADirectoryError" + + P "PermissionError" + P "ProcessLookupError" + P "TimeoutError" + + P "StopAsyncIteration" + P "ModuleNotFoundError" + P "RecursionError" , + 'Exception' ) + +local RaiseException = K ( P "raise" , 'Keyword' ) * SkipSpace * Exception * C ( P "(" ) + +local ExceptionInConsole = Exception * C ( ( 1 - P "\r" ) ^ 0 ) * EOL + + +local Namespace = + K ( P "from" , 'Keyword' ) * Space * K ( alphanum^1 , 'Name.Namespace' ) + * ( Space * K ( P "import" , 'Keyword' ) ) ^ -1 + + +local ImportAs = K ( P "import" , 'Keyword' ) + * Space + * K ( identifier , 'Name.Namespace' ) + * ( SkipSpace * C ( P "," ) * SkipSpace * K ( identifier , 'Name.Namespace' ) ) ^ 0 + * ( + Space * K ( P "as" , 'Keyword' ) * Space * K ( identifier , 'Name.Namespace' ) + ) ^ 0 + +local Class = K ( P "class" , 'Keyword' ) + * ( Space * K ( identifier , 'Name.Class' ) ) ^ -1 + +local Decorator = K ( P "@" * letter^1 , 'Name.Decorator' ) + +local SingleShortInterpol = + K ( P "{" , 'String.Interpol') + * K ( ( 1 - S "}':" ) ^ 0 , 'Interpol.Inside' ) + * C ( P ":" * (1 - S "}:'") ^ 0 ) ^ -1 + * K ( P "}" , 'String.Interpol' ) + +local DoubleShortInterpol = + K ( P "{" , 'String.Interpol' ) + * K ( ( 1 - S "}\":" ) ^ 0 , 'Interpol.Inside' ) + * ( K ( P ":" , 'String.Interpol' ) * C ( (1 - S "}:\"") ^ 0 ) ) ^ -1 + * K ( P "}" , 'String.Interpol' ) + +local SingleLongInterpol = + K ( P "{" , 'String.Interpol' ) + * K ( ( 1 - S "}:\r" - P "'''" ) ^ 0 , 'Interpol.Inside' ) + * C ( P ":" * (1 - S "}:\r" - P "'''" ) ^ 0 ) ^ -1 + * K ( P "}" , 'String.Interpol' ) + +local DoubleLongInterpol = + K ( P "{" , 'String.Interpol' ) + * K ( ( 1 - S "}:\r" - P "\"\"\"" ) ^ 0 , 'Interpol.Inside' ) + * C ( P ":" * (1 - S "}:\r" - P "\"\"\"" ) ^ 0 ) ^ -1 + * K ( P "}" , 'String.Interpol' ) + +local SingleShortPureString = C ( ( P "\\'" + P "{{" + P "}}" + 1 - S "{}'" ) ^ 1 ) + +local DoubleShortPureString = C ( ( P "\\\"" + P "{{" + P "}}" + 1 - S "{}\"" ) ^ 1 ) + +local SingleLongPureString = C ( ( 1 - P "'''" - S "{}'\r" ) ^ 1 ) + +local DoubleLongPureString = C ( ( 1 - P "\"\"\"" - S "{}\"\r" ) ^ 1 ) + +local SingleShortString = + L ( "{\\PitonStyle{String.Short}{" ) + * ( + C ( P "f'" + P "F'" ) + * ( SingleShortInterpol + SingleShortPureString ) ^ 0 + * C ( P "'" ) + + + C ( ( P "'" + P "r'" + P "R'" ) * ( P "\\'" + 1 - S "'\r" ) ^ 0 * P "'" ) + ) + * L ( "}}" ) + +local DoubleShortString = + L ( "{\\PitonStyle{String.Short}{" ) + * ( + C ( P "f\"" + P "F\"" ) + * ( DoubleShortInterpol + DoubleShortPureString ) ^ 0 + * C ( P "\"" ) + + + C ( ( P "\"" + P "r\"" + P "R\"" ) * ( P "\\\"" + 1 - S "\"\r" ) ^ 0 * P "\"" ) + ) + * L ( "}}" ) + + +local ShortString = SingleShortString + DoubleShortString + + +local SingleLongString = + L "{\\PitonStyle{String.Long}{" + * ( + C ( S "fF" * P "'''" ) + * ( SingleLongInterpol + SingleLongPureString ) ^ 0 + * L "}}" + * ( + EOL + + + L "{\\PitonStyle{String.Long}{" + * ( SingleLongInterpol + SingleLongPureString ) ^ 0 + * L "}}" + * EOL + ) ^ 0 + * L "{\\PitonStyle{String.Long}{" + * ( SingleLongInterpol + SingleLongPureString ) ^ 0 + + + C ( ( S "rR" ) ^ -1 * P "'''" * ( 1 - P "'''" - P "\r" ) ^ 0 ) + * L "}}" + * ( + L "{\\PitonStyle{String.Long}{" + * C ( ( 1 - P "'''" - P "\r" ) ^ 0 ) + * L "}}" + * EOL + ) ^ 0 + * L "{\\PitonStyle{String.Long}{" + * C ( ( 1 - P "'''" - P "\r" ) ^ 0 ) + ) + * C ( P "'''" ) + * L "}}" + + +local DoubleLongString = + L "{\\PitonStyle{String.Long}{" + * ( + C ( S "fF" * P "\"\"\"" ) + * ( DoubleLongInterpol + DoubleLongPureString ) ^ 0 + * L "}}" + * ( + EOL + + + L "{\\PitonStyle{String.Long}{" + * ( DoubleLongInterpol + DoubleLongPureString ) ^ 0 + * L "}}" + * EOL + ) ^ 0 + * L "{\\PitonStyle{String.Long}{" + * ( DoubleLongInterpol + DoubleLongPureString ) ^ 0 + + + C ( ( S "rR" ) ^ -1 * P "\"\"\"" * ( 1 - P "\"\"\"" - P "\r" ) ^ 0 ) + * L "}}" + * ( + L "{\\PitonStyle{String.Long}{" + * C ( ( 1 - P "\"\"\"" - P "\r" ) ^ 0 ) + * L "}}" + * EOL + ) ^ 0 + * L "{\\PitonStyle{String.Long}{" + * C ( ( 1 - P "\"\"\"" - P "\r" ) ^ 0 ) + ) + * C ( P "\"\"\"" ) + * L "}}" + + + +local LongString = SingleLongString + DoubleLongString + + +local Expression = + P { "E" , + E = ( 1 - S "{}()[]\r," ) ^ 0 + * ( + ( P "{" * V "F" * P "}" + + P "(" * V "F" * P ")" + + P "[" * V "F" * P "]" ) * ( 1 - S "{}()[]\r," ) ^ 0 + ) ^ 0 , + F = ( 1 - S "{}()[]\r\"'" ) ^ 0 + * ( ( + P "'" * (P "\\'" + 1 - S"'\r" )^0 * P "'" + + P "\"" * (P "\\\"" + 1 - S"\"\r" )^0 * P "\"" + + P "{" * V "F" * P "}" + + P "(" * V "F" * P ")" + + P "[" * V "F" * P "]" + ) * ( 1 - S "{}()[]\r\"'" ) ^ 0 ) ^ 0 , + } + +local Param = SkipSpace * K ( identifier , '' ) * SkipSpace + * ( K ( P "=" * Expression , 'InitialValues' ) + + K ( P ":" , '' ) * SkipSpace * K ( letter^1 , 'Name.Type' )) + + SkipSpace * K ( alphanum ^ 1 , '' ) * SkipSpace + +local Params = Param * ( K ( P "," , '' ) * Param ) ^ 0 + +local StringDoc = K ( P "\"\"\"" , 'String.Doc' ) + * ( K ( (1 - P "\"\"\"" - P "\r" ) ^ 0 , 'String.Doc' ) * EOL ) ^ 0 + * K ( ( 1 - P "\"\"\"" - P "\r" ) ^ 0 * P "\"\"\"" , 'String.Doc' ) + + K ( P "'''" , 'String.Doc' ) + * ( K ( (1 - P "'''" - P "\r")^0 , 'String.Doc' ) * EOL ) ^ 0 + * K ( ( 1 - P "'''" - P "\r")^0 * P "'''" , 'String.Doc' ) + +local CommentMath = P "$" * K ( ( 1 - S "$\r" ) ^ 1 , 'Comment.Math' ) * P "$" + + +local Comment = L ( "{\\PitonStyle{Comment}{" ) + * C ( P "#" ) * ( CommentMath + C ( ( 1 - S "$\r" ) ^ 1 ) ) ^ 0 + * L ( "}}" ) + * ( EOL + -1 ) + +local CommentLaTeX = + P "##" + * L "{\\PitonStyle{Comment.LaTeX}{\\ignorespaces" + * Ct ( C ( ( 1 - P "\r" ) ^ 0 ) ) + * L "}}" + * ( EOL + -1 ) + +local DefFunction = + K ( P "def" , 'Keyword' ) + * ( Space + * K ( identifier , 'Name.Function' ) + * ( SkipSpace * K ( P "(" , '' ) * Params * K ( P ")" , '' ) ) ^ -1 + * ( SkipSpace + * K ( ( 1 - S ":\r" )^0 , 'Post.Function' ) + * K ( P ":" , 'Keyword' ) + * SkipSpace + * ( EOL + CommentLaTeX + Comment ) + * SkipSpace + * StringDoc ) ^ -1 + ) ^ -1 + +local ItemDict = ShortString * SkipSpace * C ( P ":" ) * K ( Expression , 'Dict.Value' ) + +local ItemOfSet = SkipSpace * ( ItemDict + ShortString ) * SkipSpace + +local Set = C ( P "{" ) + * ItemOfSet * ( C ( P "," ) * ItemOfSet ) ^ 0 + * C ( P "}" ) + + +local Operator = K ( P "!=" + P "==" + P "<<" + P ">>" + S "-~+/*%=<>&.@|" , 'Operator') + +local OperatorWord = K ( P "in" + P "is" + P "and" + P "or" + P "not" , 'Operator.Word') + +local SyntaxPython = + ( ( space - P "\r" ) ^0 * P "\r" ) ^ -1 * + ( ( space^1 * -1 ) + + EOL + + Space + + Escape + + CommentLaTeX + + LongString + + Comment + + ExceptionInConsole + + Set + + Delim + + Class * ( Space + Punct + EOL ) + + Namespace * ( Space + Punct + EOL ) + + ImportAs + + RaiseException + + Keyword * ( Space + Punct + EOL ) + + DefFunction + + ShortString + + Decorator * ( Space + Punct + EOL ) + + Operator + + OperatorWord * ( Space + Punct + EOL ) + + Builtin * ( Space + Punct + EOL ) + + Identifier + + Number + + Word + ) ^0 * -1 + + +local MinimalSyntax = + P { "S" ; + S = K ( (1 - P "\r" ) ^ 0 , '') + EOL * S + } + + +function Parse(code) + local t = Ct(SyntaxPython) : match(code) + tex.sprint( luatexbase.catcodetables.expl , '\\__piton_begin_line:' ) + if t then else t = Ct(MinimalSyntax) : match(code) end + for i = 1 , #t do + if type(t[i]) == 'string' + then + tex.sprint(luatexbase.catcodetables.CatcodeTableOther, t[i]) + else + tex.tprint(t[i]) + end + end + tex.sprint( luatexbase.catcodetables.expl , '\\__piton_end_line:' ) +end + +function ParseFile(name) + s = '' + for line in io.lines(name) do s = s .. '\r' .. line end + Parse(s) +end + + +function define_gobble_syntax(n) + GobbleSyntax = ( 1 - P "\r" ) ^ (-n) * C ( ( 1 - P "\r" ) ^0 ) + * ( C ( P "\r" ) + * ( 1 - P "\r" ) ^ (-n) + * C ( ( 1 - P "\r" ) ^0 ) + ) ^ 0 +end + +function GobbleParse(code) + local t = Ct(GobbleSyntax):match(code) + local new_code = "" + for i = 1 , #t do + new_code = new_code .. t[i] + end + Parse(new_code) +end + +function add(acc,new_value) + return acc + new_value +end + + + + +--[[ The following LPEG returns as capture the minimal number of spaces at +the beginning of the lines of code]] +AutoGobbleSyntax = + ( space ^ 0 * P "\r" ) ^ -1 + * Cf ( + ( + ( P " " ) ^ 0 * P "\r" + + + Cf ( Cc(0) * ( P " " * Cc(1) ) ^ 0 , add ) + * ( 1 - P " " ) * ( 1 - P "\r" ) ^ 0 * P "\r" + ) ^ 0 + * + ( Cf ( Cc(0) * ( P " " * Cc(1) ) ^ 0 , add ) + * ( 1 - P " " ) * ( 1 - P "\r" ) ^ 0 ) ^ -1 , + math.min + ) + +function AutoGobbleParse(code) + local n = AutoGobbleSyntax:match(code) + if n==0 + then Parse(code) + else define_gobble_syntax(n) + GobbleParse(code) + end +end + + +--[[ The following LPEG returns as capture the number of spaces at the last line, +that is to say begin the \end{Piton} ]] + +EnvGobbleSyntax = + ( ( 1 - P "\r" ) ^ 0 * P "\r" ) ^ 0 + * Cf ( Cc(0) * ( P " " * Cc(1) ) ^ 0 , add ) * -1 + +function EnvGobbleParse(code) + local n = EnvGobbleSyntax:match(code) + if n==0 + then Parse(code) + else define_gobble_syntax(n) + GobbleParse(code) + end +end +\end{luacode*} +% \end{macrocode} +% +% \vspace{1cm} +% \section{History} +% +% +% \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 enterily contained in the file |piton.sty|. +% % \end{document} +% % % Local Variables: % TeX-fold-mode: t |