%% %% This is file `piton.sty', %% generated with the docstrip utility. %% %% The original source files were: %% %% piton.dtx (with options: `package') %% %% 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. %% \def\myfileversion{1.5} \def\myfiledate{2023/04/04} \NeedsTeXFormat{LaTeX2e} \RequirePackage{l3keys2e} \ProvidesExplPackage {piton} {\myfiledate} {\myfileversion} {Highlight Python codes with LPEG on LuaLaTeX} \msg_new:nnn { piton } { 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 } } \RequirePackage { luatexbase } \bool_new:N \c__piton_footnotehyper_bool \bool_new:N \c__piton_footnote_bool \bool_new:N \c__piton_math_comments_bool \bool_new:N \c__piton_beamer_bool \keys_define:nn { piton / package } { footnote .bool_set:N = \c__piton_footnote_bool , footnotehyper .bool_set:N = \c__piton_footnotehyper_bool , escape-inside .tl_set:N = \c__piton_escape_inside_tl , escape-inside .initial:n = , comment-latex .code:n = { \lua_now:n { comment_latex = "#1" } } , comment-latex .value_required:n = true , math-comments .bool_set:N = \c__piton_math_comments_bool , math-comments .default:n = true , beamer .bool_set:N = \c__piton_beamer_bool , beamer .default:n = true , unknown .code:n = \msg_error:nn { piton } { unknown~key~for~package } } \msg_new:nnn { piton } { unknown~key~for~package } { Unknown~key.\\ You~have~used~the~key~'\l_keys_key_str'~but~the~only~keys~available~here~ are~'beamer',~'comment-latex',~'escape-inside',~'footnote',~'footnotehyper'~and~ 'math-comments'.~Other~keys~are~available~in~\token_to_str:N \PitonOptions.\\ That~key~will~be~ignored. } \ProcessKeysOptions { piton / package } \begingroup \cs_new_protected:Npn \__piton_set_escape_char:nn #1 #2 { \lua_now:n { piton_begin_escape = "#1" } \lua_now:n { piton_end_escape = "#2" } } \cs_generate_variant:Nn \__piton_set_escape_char:nn { x x } \__piton_set_escape_char:xx { \tl_head:V \c__piton_escape_inside_tl } { \tl_tail:V \c__piton_escape_inside_tl } \endgroup \@ifclassloaded { beamer } { \bool_set_true:N \c__piton_beamer_bool } { } \bool_if:NT \c__piton_beamer_bool { \lua_now:n { piton_beamer = true } } \hook_gput_code:nnn { begindocument } { . } { \@ifpackageloaded { xcolor } { } { \msg_fatal:nn { piton } { xcolor~not~loaded } } } \msg_new:nnn { piton } { xcolor~not~loaded } { xcolor~not~loaded \\ The~package~'xcolor'~is~required~by~'piton'.\\ This~error~is~fatal. } \msg_new:nnn { piton } { footnote~with~footnotehyper~package } { Footnote~forbidden.\\ You~can't~use~the~option~'footnote'~because~the~package~ footnotehyper~has~already~been~loaded.~ If~you~want,~you~can~use~the~option~'footnotehyper'~and~the~footnotes~ within~the~environments~of~piton~will~be~extracted~with~the~tools~ of~the~package~footnotehyper.\\ If~you~go~on,~the~package~footnote~won't~be~loaded. } \msg_new:nnn { piton } { footnotehyper~with~footnote~package } { You~can't~use~the~option~'footnotehyper'~because~the~package~ footnote~has~already~been~loaded.~ If~you~want,~you~can~use~the~option~'footnote'~and~the~footnotes~ within~the~environments~of~piton~will~be~extracted~with~the~tools~ of~the~package~footnote.\\ If~you~go~on,~the~package~footnotehyper~won't~be~loaded. } \bool_if:NT \c__piton_footnote_bool { \@ifclassloaded { beamer } { \bool_set_false:N \c__piton_footnote_bool } { \@ifpackageloaded { footnotehyper } { \__piton_error:n { footnote~with~footnotehyper~package } } { \usepackage { footnote } } } } \bool_if:NT \c__piton_footnotehyper_bool { \@ifclassloaded { beamer } { \bool_set_false:N \c__piton_footnote_bool } { \@ifpackageloaded { footnote } { \__piton_error:n { footnotehyper~with~footnote~package } } { \usepackage { footnotehyper } } \bool_set_true:N \c__piton_footnote_bool } } \str_new:N \l__piton_language_str \str_set:Nn \l__piton_language_str { python } \int_new:N \l__piton_nb_lines_int \int_new:N \l__piton_nb_non_empty_lines_int \int_new:N \g__piton_line_int \tl_new:N \g__piton_aux_tl \int_new:N \l__piton_splittable_int \int_set:Nn \l__piton_splittable_int { 100 } \clist_new:N \l__piton_bg_color_clist \tl_new:N \l__piton_prompt_bg_color_tl \dim_new:N \g__piton_width_dim \dim_new:N \l__piton_width_on_aux_dim \int_new:N \g__piton_env_int \bool_new:N \l__piton_show_spaces_bool \bool_new:N \l__piton_break_lines_in_Piton_bool \bool_new:N \l__piton_indent_broken_lines_bool \tl_new:N \l__piton_continuation_symbol_tl \tl_set:Nn \l__piton_continuation_symbol_tl { + } \tl_new:N \l__piton_csoi_tl \tl_set:Nn \l__piton_csoi_tl { $ \hookrightarrow \; $ } \tl_new:N \l__piton_end_of_broken_line_tl \tl_set:Nn \l__piton_end_of_broken_line_tl { \hspace*{0.5em} \textbackslash } \bool_new:N \l__piton_break_lines_in_piton_bool \bool_new:N \l__piton_slim_bool \dim_new:N \l__piton_left_margin_dim \bool_new:N \l__piton_left_margin_auto_bool \dim_new:N \l__piton_numbers_sep_dim \dim_set:Nn \l__piton_numbers_sep_dim { 0.7 em } \tl_new:N \l__piton_tab_tl \cs_new_protected:Npn \__piton_set_tab_tl:n #1 { \tl_clear:N \l__piton_tab_tl \prg_replicate:nn { #1 } { \tl_put_right:Nn \l__piton_tab_tl { ~ } } } \__piton_set_tab_tl:n { 4 } \int_new:N \l__piton_gobble_int \tl_new:N \l__piton_space_tl \tl_set:Nn \l__piton_space_tl { ~ } \int_new:N \g__piton_indentation_int \cs_new_protected:Npn \__piton_an_indentation_space: { \int_gincr:N \g__piton_indentation_int } \cs_new_protected:Npn \__piton_beamer_command:n #1 { \str_set:Nn \l__piton_beamer_command_str { #1 } \use:c { #1 } } \cs_new_protected:Npn \__piton_label:n #1 { \bool_if:NTF \l__piton_line_numbers_bool { \@bsphack \protected@write \@auxout { } { \string \newlabel { #1 } { { \int_eval:n { \g__piton_visual_line_int + 1 } } { \thepage } } } \@esphack } { \msg_error:nn { piton } { label~with~lines~numbers } } } \cs_new_protected:Npn \__piton_open_brace: { \directlua { piton.open_brace() } } \cs_new_protected:Npn \__piton_close_brace: { \directlua { piton.close_brace() } } \tl_new:N \g__piton_begin_line_hook_tl \cs_new_protected:Npn \__piton_prompt: { \tl_gset:Nn \g__piton_begin_line_hook_tl { \clist_set:NV \l__piton_bg_color_clist \l__piton_prompt_bg_color_tl } } \clist_new:N \g__piton_current_style_clist \clist_set:Nn \g__piton_current_style_clist { __end } \cs_new_protected:Npn \__piton_close_current_styles: { \int_set:Nn \l_tmpa_int { \clist_count:N \g__piton_current_style_clist - 1 } \exp_args:NV \__piton_close_n_styles:n \l_tmpa_int } \cs_new_protected:Npn \__piton_close_n_styles:n #1 { \int_compare:nNnT { #1 } > 0 { \__piton_close_brace: \__piton_close_brace: \__piton_close_n_styles:n { #1 - 1 } } } \cs_new_protected:Npn \__piton_open_current_styles: { \exp_last_unbraced:NV \__piton_open_styles:w \g__piton_current_style_clist , } \cs_new_protected:Npn \__piton_open_styles:w #1 , { \tl_if_eq:nnF { #1 } { __end } { \__piton_open_brace: #1 \__piton_open_brace: \__piton_open_styles:w } } \cs_new_protected:Npn \__piton_pop_style: { \clist_greverse:N \g__piton_current_style_clist \clist_gpop:NN \g__piton_current_style_clist \l_tmpa_tl \clist_gpop:NN \g__piton_current_style_clist \l_tmpa_tl \clist_gpush:Nn \g__piton_current_style_clist { __end } \clist_greverse:N \g__piton_current_style_clist } \cs_new_protected:Npn \__piton_push_style:n #1 { \clist_greverse:N \g__piton_current_style_clist \clist_gpop:NN \g__piton_current_style_clist \l_tmpa_tl \clist_gpush:Nn \g__piton_current_style_clist { #1 } \clist_gpush:Nn \g__piton_current_style_clist { __end } \clist_greverse:N \g__piton_current_style_clist } \cs_new_protected:Npn \__piton_push_and_exec:n #1 { \__piton_push_style:n { #1 } \__piton_open_brace: #1 \__piton_open_brace: } \cs_new_protected:Npn \__piton_replace_spaces:n #1 { \tl_set:Nn \l_tmpa_tl { #1 } \bool_if:NTF \l__piton_show_spaces_bool { \regex_replace_all:nnN { \x20 } { ␣ } \l_tmpa_tl } % U+2423 { \bool_if:NT \l__piton_break_lines_in_Piton_bool { \regex_replace_all:nnN { \x20 } { \c { __piton_breakable_space: } } \l_tmpa_tl } } \l_tmpa_tl } \cs_generate_variant:Nn \__piton_replace_spaces:n { x } \cs_set_protected:Npn \__piton_begin_line: #1 \__piton_end_line: { \group_begin: \g__piton_begin_line_hook_tl \int_gzero:N \g__piton_indentation_int \bool_if:NTF \l__piton_slim_bool { \hcoffin_set:Nn \l_tmpa_coffin } { \clist_if_empty:NTF \l__piton_bg_color_clist { \vcoffin_set:Nnn \l_tmpa_coffin { \dim_eval:n { \linewidth - \l__piton_left_margin_dim } } } { \vcoffin_set:Nnn \l_tmpa_coffin { \dim_eval:n { \linewidth - \l__piton_left_margin_dim - 0.5 em } } } } { \language = -1 \raggedright \strut \__piton_replace_spaces:n { #1 } \strut \hfil } \hbox_set:Nn \l_tmpa_box { \skip_horizontal:N \l__piton_left_margin_dim \bool_if:NT \l__piton_line_numbers_bool { \bool_if:NF \l__piton_all_line_numbers_bool { \tl_if_empty:nF { #1 } } \__piton_print_number: } \clist_if_empty:NF \l__piton_bg_color_clist { \dim_compare:nNnT \l__piton_left_margin_dim = \c_zero_dim { \bool_if:NF \l__piton_left_margin_auto_bool { \skip_horizontal:n { 0.5 em } } } } \coffin_typeset:Nnnnn \l_tmpa_coffin T l \c_zero_dim \c_zero_dim } \dim_compare:nNnT { \box_wd:N \l_tmpa_box } > \g__piton_width_dim { \dim_gset:Nn \g__piton_width_dim { \box_wd:N \l_tmpa_box } } \box_set_dp:Nn \l_tmpa_box { \box_dp:N \l_tmpa_box + 1.25 pt } \box_set_ht:Nn \l_tmpa_box { \box_ht:N \l_tmpa_box + 1.25 pt } \clist_if_empty:NTF \l__piton_bg_color_clist { \box_use_drop:N \l_tmpa_box } { \vbox_top:n { \hbox:n { \__piton_color:N \l__piton_bg_color_clist \vrule height \box_ht:N \l_tmpa_box depth \box_dp:N \l_tmpa_box width \l__piton_width_on_aux_dim } \skip_vertical:n { - \box_ht_plus_dp:N \l_tmpa_box } \box_set_wd:Nn \l_tmpa_box \l__piton_width_on_aux_dim \box_use_drop:N \l_tmpa_box } } \vspace { - 2.5 pt } \group_end: \tl_gclear:N \g__piton_begin_line_hook_tl } \cs_set_protected:Npn \__piton_color:N #1 { \int_set:Nn \l_tmpa_int { \clist_count:N #1 } \int_set:Nn \l_tmpb_int { \int_mod:nn \g__piton_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 } { \dim_zero:N \l__piton_width_on_aux_dim } { \exp_args:NV \__piton_color_i:n \l_tmpa_tl } } \cs_set_protected:Npn \__piton_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 \__piton_color:n { V } \cs_new_protected:Npn \__piton_newline: { \int_gincr:N \g__piton_line_int \int_compare:nNnT \g__piton_line_int > { \l__piton_splittable_int - 1 } { \int_compare:nNnT { \l__piton_nb_lines_int - \g__piton_line_int } > \l__piton_splittable_int { \egroup \bool_if:NT \c__piton_footnote_bool { \end { savenotes } } \newline \bool_if:NT \c__piton_footnote_bool { \begin { savenotes } } \vtop \bgroup } } } \cs_set_protected:Npn \__piton_breakable_space: { \discretionary { \hbox:n { \color { gray } \l__piton_end_of_broken_line_tl } } { \hbox_overlap_left:n { { \normalfont \footnotesize \color { gray } \l__piton_continuation_symbol_tl } \skip_horizontal:n { 0.3 em } \clist_if_empty:NF \l__piton_bg_color_clist { \skip_horizontal:n { 0.5 em } } } \bool_if:NT \l__piton_indent_broken_lines_bool { \hbox:n { \prg_replicate:nn { \g__piton_indentation_int } { ~ } { \color { gray } \l__piton_csoi_tl } } } } { \hbox { ~ } } } \bool_new:N \l__piton_line_numbers_bool \bool_new:N \l__piton_all_line_numbers_bool \bool_new:N \l__piton_resume_bool \keys_define:nn { PitonOptions } { language .str_set:N = \l__piton_language_str , language .value_required:n = true , gobble .int_set:N = \l__piton_gobble_int , gobble .value_required:n = true , auto-gobble .code:n = \int_set:Nn \l__piton_gobble_int { -1 } , auto-gobble .value_forbidden:n = true , env-gobble .code:n = \int_set:Nn \l__piton_gobble_int { -2 } , env-gobble .value_forbidden:n = true , tabs-auto-gobble .code:n = \int_set:Nn \l__piton_gobble_int { -3 } , tabs-auto-gobble .value_forbidden:n = true , line-numbers .bool_set:N = \l__piton_line_numbers_bool , line-numbers .default:n = true , all-line-numbers .code:n = \bool_set_true:N \l__piton_line_numbers_bool \bool_set_true:N \l__piton_all_line_numbers_bool , all-line-numbers .value_forbidden:n = true , resume .bool_set:N = \l__piton_resume_bool , resume .value_forbidden:n = true , splittable .int_set:N = \l__piton_splittable_int , splittable .default:n = 1 , background-color .clist_set:N = \l__piton_bg_color_clist , background-color .value_required:n = true , prompt-background-color .tl_set:N = \l__piton_prompt_bg_color_tl , prompt-background-color .value_required:n = true , slim .bool_set:N = \l__piton_slim_bool , slim .default:n = true , left-margin .code:n = \str_if_eq:nnTF { #1 } { auto } { \dim_zero:N \l__piton_left_margin_dim \bool_set_true:N \l__piton_left_margin_auto_bool } { \dim_set:Nn \l__piton_left_margin_dim { #1 } } , left-margin .value_required:n = true , numbers-sep .dim_set:N = \l__piton_numbers_sep_dim , numbers-sep .value_required:n = true , tab-size .code:n = \__piton_set_tab_tl:n { #1 } , tab-size .value_required:n = true , show-spaces .bool_set:N = \l__piton_show_spaces_bool , show-spaces .default:n = true , show-spaces-in-strings .code:n = \tl_set:Nn \l__piton_space_tl { ␣ } , % U+2423 show-spaces-in-strings .value_forbidden:n = true , break-lines-in-Piton .bool_set:N = \l__piton_break_lines_in_Piton_bool , break-lines-in-Piton .default:n = true , break-lines-in-piton .bool_set:N = \l__piton_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__piton_indent_broken_lines_bool , indent-broken-lines .default:n = true , end-of-broken-line .tl_set:N = \l__piton_end_of_broken_line_tl , end-of-broken-line .value_required:n = true , continuation-symbol .tl_set:N = \l__piton_continuation_symbol_tl , continuation-symbol .value_required:n = true , continuation-symbol-on-indentation .tl_set:N = \l__piton_csoi_tl , continuation-symbol-on-indentation .value_required:n = true , unknown .code:n = \msg_error:nn { piton } { Unknown~key~for~PitonOptions } } \NewDocumentCommand \PitonOptions { } { \keys_set:nn { PitonOptions } } \int_new:N \g__piton_visual_line_int \cs_new_protected:Npn \__piton_print_number: { \int_gincr:N \g__piton_visual_line_int \hbox_overlap_left:n { { \color { gray } \footnotesize \int_to_arabic:n \g__piton_visual_line_int } \skip_horizontal:N \l__piton_numbers_sep_dim } } \cs_new_protected:Npn \__piton_write_aux: { \tl_if_empty:NF \g__piton_aux_tl { \iow_now:Nn \@mainaux { \ExplSyntaxOn } \iow_now:Nx \@mainaux { \tl_gset:cn { c__piton_ \int_use:N \g__piton_env_int _ tl } { \exp_not:V \g__piton_aux_tl } } \iow_now:Nn \@mainaux { \ExplSyntaxOff } } \tl_gclear:N \g__piton_aux_tl } \cs_new_protected:Npn \__piton_width_to_aux: { \bool_if:NT \l__piton_slim_bool { \clist_if_empty:NF \l__piton_bg_color_clist { \tl_gput_right:Nx \g__piton_aux_tl { \dim_set:Nn \l__piton_width_on_aux_dim { \dim_eval:n { \g__piton_width_dim + 0.5 em } } } } } } \NewDocumentCommand { \piton } { } { \peek_meaning:NTF \bgroup \__piton_piton_standard \__piton_piton_verbatim } \NewDocumentCommand { \__piton_piton_standard } { m } { \group_begin: \ttfamily \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 \__piton_begin_line: { } \cs_set_protected:Npn \__piton_end_line: { } \tl_set:Nx \l_tmpa_tl { \lua_now:e { piton.ParseBis('\l__piton_language_str',token.scan_string()) } { #1 } } \bool_if:NTF \l__piton_show_spaces_bool { \regex_replace_all:nnN { \x20 } { ␣ } \l_tmpa_tl } % U+2423 { \bool_if:NT \l__piton_break_lines_in_piton_bool { \regex_replace_all:nnN { \x20 } { \x20 } \l_tmpa_tl } } \l_tmpa_tl \group_end: } \NewDocumentCommand { \__piton_piton_verbatim } { v } { \group_begin: \ttfamily \automatichyphenmode = 1 \cs_set_protected:Npn \__piton_begin_line: { } \cs_set_protected:Npn \__piton_end_line: { } \tl_set:Nx \l_tmpa_tl { \lua_now:e { piton.Parse('\l__piton_language_str',token.scan_string()) } { #1 } } \bool_if:NT \l__piton_show_spaces_bool { \regex_replace_all:nnN { \x20 } { ␣ } \l_tmpa_tl } % U+2423 \l_tmpa_tl \group_end: } \cs_new_protected:Npn \__piton_piton:n #1 { \group_begin: \cs_set_protected:Npn \__piton_begin_line: { } \cs_set_protected:Npn \__piton_end_line: { } \bool_lazy_or:nnTF \l__piton_break_lines_in_piton_bool \l__piton_break_lines_in_Piton_bool { \tl_set:Nx \l_tmpa_tl { \lua_now:e { piton.ParseTer('\l__piton_language_str',token.scan_string()) } { #1 } } } { \tl_set:Nx \l_tmpa_tl { \lua_now:e { piton.Parse('\l__piton_language_str',token.scan_string()) } { #1 } } } \bool_if:NT \l__piton_show_spaces_bool { \regex_replace_all:nnN { \x20 } { ␣ } \l_tmpa_tl } % U+2423 \l_tmpa_tl \group_end: } \cs_new_protected:Npn \__piton_piton_no_cr:n #1 { \group_begin: \cs_set_protected:Npn \__piton_begin_line: { } \cs_set_protected:Npn \__piton_end_line: { } \cs_set_protected:Npn \__piton_newline: { \msg_fatal:nn { piton } { cr~not~allowed } } \bool_lazy_or:nnTF \l__piton_break_lines_in_piton_bool \l__piton_break_lines_in_Piton_bool { \tl_set:Nx \l_tmpa_tl { \lua_now:e { piton.ParseTer('\l__piton_language_str',token.scan_string()) } { #1 } } } { \tl_set:Nx \l_tmpa_tl { \lua_now:e { piton.Parse('\l__piton_language_str',token.scan_string()) } { #1 } } } \bool_if:NT \l__piton_show_spaces_bool { \regex_replace_all:nnN { \x20 } { ␣ } \l_tmpa_tl } % U+2423 \l_tmpa_tl \group_end: } \cs_new:Npn \__piton_pre_env: { \automatichyphenmode = 1 \int_gincr:N \g__piton_env_int \tl_gclear:N \g__piton_aux_tl \cs_if_exist_use:c { c__piton _ \int_use:N \g__piton_env_int _ tl } \dim_compare:nNnT \l__piton_width_on_aux_dim = \c_zero_dim { \dim_set_eq:NN \l__piton_width_on_aux_dim \linewidth } \bool_if:NF \l__piton_resume_bool { \int_gzero:N \g__piton_visual_line_int } \dim_gzero:N \g__piton_width_dim \int_gzero:N \g__piton_line_int \dim_zero:N \parindent \dim_zero:N \lineskip \dim_zero:N \parindent \cs_set_eq:NN \label \__piton_label:n } \keys_define:nn { PitonInputFile } { first-line .int_set:N = \l__piton_first_line_int , first-line .value_required:n = true , last-line .int_set:N = \l__piton_last_line_int , last-line .value_required:n = true , } \NewDocumentCommand { \PitonInputFile } { d < > O { } m } { \tl_if_novalue:nF { #1 } { \bool_if:NTF \c__piton_beamer_bool { \begin { uncoverenv } < #1 > } { \msg_error:nn { piton } { overlay~without~beamer } } } \group_begin: \int_zero_new:N \l__piton_first_line_int \int_zero_new:N \l__piton_last_line_int \int_set_eq:NN \l__piton_last_line_int \c_max_int \keys_set:nn { PitonInputFile } { #2 } \__piton_pre_env: \mode_if_vertical:TF \mode_leave_vertical: \newline \lua_now:n { piton.CountLinesFile(token.scan_argument()) } { #3 } \bool_lazy_and:nnT \l__piton_left_margin_auto_bool \l__piton_line_numbers_bool { \hbox_set:Nn \l_tmpa_box { \footnotesize \bool_if:NTF \l__piton_all_line_numbers_bool { \int_to_arabic:n { \g__piton_visual_line_int + \l__piton_nb_lines_int } } { \lua_now:n { piton.CountNonEmptyLinesFile(token.scan_argument()) } { #3 } \int_to_arabic:n { \g__piton_visual_line_int + \l__piton_nb_non_empty_lines_int } } } \dim_set:Nn \l__piton_left_margin_dim { \box_wd:N \l_tmpa_box + \l__piton_numbers_sep_dim + 0.1 em } } \ttfamily \bool_if:NT \c__piton_footnote_bool { \begin { savenotes } } \vtop \bgroup \lua_now:e { piton.ParseFile('\l__piton_language_str',token.scan_argument() , \int_use:N \l__piton_first_line_int , \int_use:N \l__piton_last_line_int ) } { #3 } \egroup \bool_if:NT \c__piton_footnote_bool { \end { savenotes } } \__piton_width_to_aux: \group_end: \tl_if_novalue:nF { #1 } { \bool_if:NT \c__piton_beamer_bool { \end { uncoverenv } } } \__piton_write_aux: } \NewDocumentCommand { \NewPitonEnvironment } { m m m m } { \use:x { \cs_set_protected:Npn \use:c { __piton_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 \lua_now:n { piton.CountLines(token.scan_argument()) } { ##1 } \bool_lazy_and:nnT \l__piton_left_margin_auto_bool \l__piton_line_numbers_bool { \bool_if:NTF \l__piton_all_line_numbers_bool { \hbox_set:Nn \l_tmpa_box { \footnotesize \int_to_arabic:n { \g__piton_visual_line_int + \l__piton_nb_lines_int } } } { \lua_now:n { piton.CountNonEmptyLines(token.scan_argument()) } { ##1 } \hbox_set:Nn \l_tmpa_box { \footnotesize \int_to_arabic:n { \g__piton_visual_line_int + \l__piton_nb_non_empty_lines_int } } } \dim_set:Nn \l__piton_left_margin_dim { \box_wd:N \l_tmpa_box + \l__piton_numbers_sep_dim + 0.1 em } } \ttfamily \bool_if:NT \c__piton_footnote_bool { \begin { savenotes } } \vtop \bgroup \lua_now:e { piton.GobbleParse ( '\l__piton_language_str' , \int_use:N \l__piton_gobble_int , token.scan_argument() ) } { ##1 } \vspace { 2.5 pt } \egroup \bool_if:NT \c__piton_footnote_bool { \end { savenotes } } \__piton_width_to_aux: \end { #1 } \__piton_write_aux: } \NewDocumentEnvironment { #1 } { #2 } { #3 \__piton_pre_env: \group_begin: \tl_map_function:nN { \ \\ \{ \} \$ \& \# \^ \_ \% \~ \^^I } \char_set_catcode_other:N \use:c { __piton_collect_ #1 :w } } { #4 } \AddToHook { env / #1 / begin } { \char_set_catcode_other:N \^^M } } \bool_if:NTF \c__piton_beamer_bool { \NewPitonEnvironment { Piton } { d < > } { \IfValueTF { #1 } { \begin { uncoverenv } < #1 > } { \begin { uncoverenv } } } { \end { uncoverenv } } } { \NewPitonEnvironment { Piton } { } { } { } } \NewDocumentCommand { \PitonStyle } { m } { \use:c { pitonStyle #1 } } \NewDocumentCommand { \SetPitonStyle } { } { \keys_set:nn { piton / Styles } } \cs_new_protected:Npn \__piton_math_scantokens:n #1 { \normalfont \scantextokens { $#1$ } } \keys_define:nn { piton / Styles } { String.Interpol .tl_set:c = pitonStyle String.Interpol , String.Interpol .value_required:n = true , FormattingType .tl_set:c = pitonStyle FormattingType , FormattingType .value_required:n = true , Dict.Value .tl_set:c = pitonStyle Dict.Value , Dict.Value .value_required:n = true , Name.Decorator .tl_set:c = pitonStyle Name.Decorator , Name.Decorator .value_required:n = true , Name.Function .tl_set:c = pitonStyle Name.Function , Name.Function .value_required:n = true , Name.UserFunction .tl_set:c = pitonStyle Name.UserFunction , Name.UserFunction .value_required:n = true , Keyword .tl_set:c = pitonStyle Keyword , Keyword .value_required:n = true , Keyword.Constant .tl_set:c = pitonStyle Keyword.Constant , Keyword.constant .value_required:n = true , String.Doc .tl_set:c = pitonStyle String.Doc , String.Doc .value_required:n = true , Interpol.Inside .tl_set:c = pitonStyle Interpol.Inside , Interpol.Inside .value_required:n = true , String.Long .tl_set:c = pitonStyle String.Long , String.Long .value_required:n = true , String.Short .tl_set:c = pitonStyle String.Short , String.Short .value_required:n = true , String .meta:n = { String.Long = #1 , String.Short = #1 } , Comment.Math .tl_set:c = pitonStyle Comment.Math , Comment.Math .default:n = \__piton_math_scantokens:n , Comment.Math .initial:n = , Comment .tl_set:c = pitonStyle Comment , Comment .value_required:n = true , InitialValues .tl_set:c = pitonStyle InitialValues , InitialValues .value_required:n = true , Number .tl_set:c = pitonStyle Number , Number .value_required:n = true , Name.Namespace .tl_set:c = pitonStyle Name.Namespace , Name.Namespace .value_required:n = true , Name.Class .tl_set:c = pitonStyle Name.Class , Name.Class .value_required:n = true , Name.Builtin .tl_set:c = pitonStyle Name.Builtin , Name.Builtin .value_required:n = true , TypeParameter .tl_set:c = pitonStyle TypeParameter , TypeParameter .value_required:n = true , Name.Type .tl_set:c = pitonStyle Name.Type , Name.Type .value_required:n = true , Operator .tl_set:c = pitonStyle Operator , Operator .value_required:n = true , Operator.Word .tl_set:c = pitonStyle Operator.Word , Operator.Word .value_required:n = true , Exception .tl_set:c = pitonStyle Exception , Exception .value_required:n = true , Comment.LaTeX .tl_set:c = pitonStyle Comment.LaTeX , Comment.LaTeX .value_required:n = true , Identifier .tl_set:c = pitonStyle Identifier , Comment.LaTeX .value_required:n = true , ParseAgain.noCR .tl_set:c = pitonStyle ParseAgain.noCR , ParseAgain.noCR .value_required:n = true , ParseAgain .tl_set:c = pitonStyle ParseAgain , ParseAgain .value_required:n = true , Prompt .tl_set:c = pitonStyle Prompt , Prompt .value_required:n = true , unknown .code:n = \msg_error:nn { piton } { Unknown~key~for~SetPitonStyle } } \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,~ Identifier,~ InitialValues,~ Keyword,~ Keyword.Constant,~ Name.Builtin,~ Name.Class,~ Name.Decorator,~ Name.Function,~ Name.Namespace,~ Number,~ Operator,~ Operator.Word,~ Prompt,~ String,~ String.Doc,~ String.Long,~ String.Short,~and~ String.Interpol. } \SetPitonStyle { Comment = \color[HTML]{0099FF} \itshape , Exception = \color[HTML]{CC0000} , Keyword = \color[HTML]{006699} \bfseries , Keyword.Constant = \color[HTML]{006699} \bfseries , Name.Builtin = \color[HTML]{336666} , Name.Decorator = \color[HTML]{9999FF}, Name.Class = \color[HTML]{00AA88} \bfseries , Name.Function = \color[HTML]{CC00FF} , Name.Namespace = \color[HTML]{00CCFF} , Number = \color[HTML]{FF6600} , Operator = \color[HTML]{555555} , Operator.Word = \bfseries , String = \color[HTML]{CC3300} , String.Doc = \color[HTML]{CC3300} \itshape , String.Interpol = \color[HTML]{AA0000} , Comment.LaTeX = \normalfont \color[rgb]{.468,.532,.6} , Name.Type = \color[HTML]{336666} , InitialValues = \__piton_piton:n , Dict.Value = \__piton_piton:n , Interpol.Inside = \color{black}\__piton_piton:n , TypeParameter = \color[HTML]{008800} \itshape , Identifier = \__piton_identifier:n , Name.UserFunction = , Prompt = , ParseAgain.noCR = \__piton_piton_no_cr:n , ParseAgain = \__piton_piton:n , } \bool_if:NT \c__piton_math_comments_bool { \SetPitonStyle { Comment.Math } } \cs_new_protected:Npn \__piton_identifier:n #1 { \cs_if_exist_use:c { PitonIdentifier _ \l__piton_language_str _ #1 } { #1 } } \keys_define:nn { PitonOptions } { identifiers .code:n = \__piton_set_identifiers:n { #1 } } \keys_define:nn { Piton / identifiers } { names .clist_set:N = \l__piton_identifiers_names_tl , style .tl_set:N = \l__piton_style_tl , } \cs_new_protected:Npn \__piton_set_identifiers:n #1 { \clist_clear_new:N \l__piton_identifiers_names_tl \tl_clear_new:N \l__piton_style_tl \keys_set:nn { Piton / identifiers } { #1 } \clist_map_inline:Nn \l__piton_identifiers_names_tl { \tl_set_eq:cN { PitonIdentifier _ \l__piton_language_str _ ##1 } \l__piton_style_tl } } \cs_new_protected:cpn { pitonStyle Name.Function.Internal } #1 { { \PitonStyle { Name.Function } { #1 } } \cs_gset_protected:cpn { PitonIdentifier _ \l__piton_language_str _ #1 } { \PitonStyle{ Name.UserFunction } } \seq_if_exist:cF { g__piton_functions _ \l__piton_language_str _ seq } { \seq_new:c { g__piton_functions _ \l__piton_language_str _ seq } } \seq_gput_right:cn { g__piton_functions _ \l__piton_language_str _ seq } { #1 } } \NewDocumentCommand \PitonClearUserFunctions { ! O { \l__piton_language_str } } { \seq_if_exist:cT { g__piton_functions _ #1 _ seq } { \seq_map_inline:cn { g__piton_functions _ #1 _ seq } { \cs_undefine:c { PitonIdentifier _ #1 _ ##1} } \seq_gclear:c { g__piton_functions _ #1 _ seq } } } \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. } \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):~ all-line-numbers,~ auto-gobble,~ background-color,~ break-lines,~ break-lines-in-piton,~ break-lines-in-Piton,~ continuation-symbol,~ continuation-symbol-on-indentation,~ end-of-broken-line,~ env-gobble,~ gobble,~ identifiers,~ indent-broken-lines,~ language,~ left-margin,~ line-numbers,~ prompt-background-color,~ resume,~ show-spaces,~ show-spaces-in-strings,~ slim,~ splittable,~ tabs-auto-gobble~ and~tab-size. } \msg_new:nnn { piton } { label~with~lines~numbers } { You~can't~use~the~command~\token_to_str:N \label\ because~the~key~'line-numbers'~(or~'all-line-numbers')~ is~not~active.\\ If~you~go~on,~that~command~will~ignored. } \msg_new:nnn { piton } { cr~not~allowed } { You~can't~put~any~carriage~return~in~the~argument~ of~a~command~\c_backslash_str \l__piton_beamer_command_str\ within~an~ environment~of~'piton'.~You~should~consider~using~the~ corresponding~environment.\\ That~error~is~fatal. } \msg_new:nnn { piton } { 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. } \msg_new:nnn { Piton } { Python~error } { A~Python~error~has~been~detected. } \ExplSyntaxOff \RequirePackage{luacode} \begin{luacode*} piton = piton or { } if piton.comment_latex == nil then piton.comment_latex = ">" end piton.comment_latex = "#" .. piton.comment_latex function piton.open_brace () tex.sprint("{") end function piton.close_brace () tex.sprint("}") end 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 local function Q(pattern) return Ct ( Cc ( luatexbase.catcodetables.CatcodeTableOther ) * C ( pattern ) ) end local function L(pattern) return Ct ( C ( pattern ) ) end local function Lc(string) return Cc ( { luatexbase.catcodetables.expl , string } ) end local function K(style, pattern) return Lc ( "{\\PitonStyle{" .. style .. "}{" ) * Q ( pattern ) * Lc ( "}}" ) end local function WithStyle(style,pattern) return Ct ( Cc "Open" * Cc ( "{\\PitonStyle{" .. style .. "}{" ) * Cc "}}" ) * pattern * Ct ( Cc "Close" ) end local Escape = P(piton_begin_escape) * L ( ( 1 - P(piton_end_escape) ) ^ 1 ) * P(piton_end_escape) lpeg.locale(lpeg) local alpha, digit = lpeg.alpha, lpeg.digit local space = P " " 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 local identifier = letter * alphanum ^ 0 local Identifier = K ( 'Identifier' , identifier) 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 ) local Word if piton_begin_escape ~= '' 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 local Space = ( Q " " ) ^ 1 local SkipSpace = ( Q " " ) ^ 0 local Punct = Q ( S ".,:;!" ) local Tab = P "\t" * Lc ( '\\l__piton_tab_tl' ) local SpaceIndentation = Lc ( '\\__piton_an_indentation_space:' ) * ( Q " " ) local Delim = Q ( S "[()]" ) local VisualSpace = space * Lc "\\l__piton_space_tl" 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 "(" ) local Decorator = K ( 'Name.Decorator' , P "@" * letter^1 ) local DefClass = K ( 'Keyword' , P "class" ) * Space * K ( 'Name.Class' , identifier ) 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 ) local FromImport = K ( 'Keyword' , P "from" ) * Space * K ( 'Name.Namespace' , identifier ) * Space * K ( 'Keyword' , P "import" ) 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%" ) local SingleShortString = WithStyle ( 'String.Short' , 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 "'" ) + 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 local BalancedBraces = P { "E" , E = ( P "{" * V "E" * P "}" + ShortString + ( 1 - S "{}" ) ) ^ 0 } local Beamer = P ( false ) local BeamerBeginEnvironments = P ( true ) local BeamerEndEnvironments = P ( true ) local BeamerNamesEnvironments = P "uncoverenv" + P "onlyenv" + P "visibleenv" + P "invisibleenv" + P "alertenv" + P "actionenv" UserCommands = Ct ( Cc "Open" * C ( "\\emph{" ) * Cc "}" ) * ( C ( BalancedBraces ) / (function (s) return MainLoopPython:match(s) end ) ) * P "}" * Ct ( Cc "Close" ) function OneBeamerEnvironment(name) 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 MainLoopPython:match(s) end ) ) * P ( "\\end{" .. name .. "}" ) * Ct ( Cc "Close" ) end 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 ( BalancedBraces ) / (function (s) return MainLoopPython:match(s) end ) ) * P "}" * Ct ( Cc "Close" ) + OneBeamerEnvironment "uncoverenv" + OneBeamerEnvironment "onlyenv" + OneBeamerEnvironment "visibleenv" + OneBeamerEnvironment "invisibleenv" + OneBeamerEnvironment "alertenv" + OneBeamerEnvironment "actionenv" + L ( ( P "\\alt" ) * P "<" * (1 - P ">") ^ 0 * P ">" * P "{" ) * K ( 'ParseAgain.noCR' , BalancedBraces ) * L ( P "}{" ) * K ( 'ParseAgain.noCR' , BalancedBraces ) * L ( P "}" ) + L ( ( P "\\temporal" ) * P "<" * (1 - P ">") ^ 0 * P ">" * P "{" ) * K ( 'ParseAgain.noCR' , BalancedBraces ) * L ( P "}{" ) * K ( 'ParseAgain.noCR' , BalancedBraces ) * L ( P "}{" ) * K ( 'ParseAgain.noCR' , BalancedBraces ) * L ( P "}" ) 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 local PromptHastyDetection = ( # ( P ">>>" + P "..." ) * Lc ( '\\__piton_prompt:' ) ) ^ -1 local Prompt = K ( 'Prompt' , ( ( P ">>>" + P "..." ) * P " " ^ -1 ) ^ -1 ) local EOL = P "\r" * ( ( space^0 * -1 ) + Ct ( Cc "EOL" * Ct ( Lc "\\__piton_end_line:" * BeamerEndEnvironments * BeamerBeginEnvironments * PromptHastyDetection * Lc "\\__piton_newline: \\__piton_begin_line:" * Prompt ) ) ) * SpaceIndentation ^ 0 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 "\"\"\"" ) ) local LongString = SingleLongString + DoubleLongString 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 "\"\"\"" ) 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 CommentLaTeX = P(piton.comment_latex) * Lc "{\\PitonStyle{Comment.LaTeX}{\\ignorespaces" * L ( ( 1 - P "\r" ) ^ 0 ) * Lc "}}" * ( EOL + -1 ) -- you could put EOL instead of EOL 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 * Identifier * SkipSpace * ( K ( 'InitialValues' , P "=" * Expression ) + Q ( P ":" ) * SkipSpace * K ( 'Name.Type' , letter^1 ) ) ^ -1 local Params = ( Param * ( Q "," * Param ) ^ 0 ) ^ -1 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 * 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 local ItemDict = ShortString * SkipSpace * Q ( P ":" ) * K ( 'Dict.Value' , Expression ) local ItemOfSet = SkipSpace * ( ItemDict + ShortString ) * SkipSpace local Set = Q ( P "{" ) * ItemOfSet * ( Q ( P "," ) * ItemOfSet ) ^ 0 * Q ( P "}" ) local ExceptionInConsole = Exception * Q ( ( 1 - P "\r" ) ^ 0 ) * EOL MainLoopPython = ( ( space^1 * -1 ) + EOL + Space + Tab + Escape + CommentLaTeX + Beamer + UserCommands + LongString + Comment + ExceptionInConsole + Set + Delim + Operator + ShortString + Punct + FromImport + RaiseException + DefFunction + DefClass + Keyword * ( Space + Punct + Delim + EOL+ -1 ) + Decorator + OperatorWord * ( Space + Punct + Delim + EOL+ -1 ) + Builtin * ( Space + Punct + Delim + EOL+ -1 ) + Identifier + Number + Word ) ^ 0 local python = P ( true ) python = Ct ( ( ( space - P "\r" ) ^0 * P "\r" ) ^ -1 * BeamerBeginEnvironments * PromptHastyDetection * Lc '\\__piton_begin_line:' * Prompt * SpaceIndentation ^ 0 * MainLoopPython * -1 * Lc '\\__piton_end_line:' ) local languages = { } languages['python'] = python local Delim = Q ( P "[|" + P "|]" + S "[()]" ) local Punct = Q ( S ",:;!" ) local identifier = ( R "az" + R "AZ" + P "_") * ( R "az" + R "AZ" + S "_'" + digit ) ^ 0 local Identifier = K ( 'Identifier' , identifier ) 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 "as" + P "assert" + P "begin" + P "class" + P "constraint" + P "done" + P "do" + P "downto" + P "else" + P "end" + P "exception" + P "external" + P "false" + P "for" + P "function" + P "fun" + P "functor" + P "if" + P "in" + P "include" + P "inherit" + P "initializer" + 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 "true" + 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" + P "String.length" + P "List.tl" + P "List.hd" + P "List.mem" + P "List.exists" + P "List.for_all" + P "List.filter" + P "List.length" + P "List.map" + P "List.iter" + P "Array.length" + P "Array.make" + P "Array.make_matrix" + P "Array.init" + P "Array.copy" + P "Array.copy" + P "Array.mem" + P "Array.exists" + P "Array.for_all" + P "Array.map" + P "Array.iter" + P "Queue.create" + P "Queue.is_empty" + P "Queue.push" + P "Queue.pop" + P "Stack.create" + P "Stack.is_empty" + P "Stack.push" + P "Stack.pop" + P "Hashtbl.create" + P "Hashtbl.add" + P "Hashtbl.remove" + P "Hashtbl.mem" + P "Hashtbl.find" + P "Hashtbl.find_opt" + P "Hashtbl.iter" ) 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" ) local Char = K ( 'String.Short' , P "'" * ( ( 1 - P "'" ) ^ 0 + P "\\'" ) * P "'" ) local BalancedBraces = P { "E" , E = ( P "{" * V "E" * P "}" + P "\"" * ( 1 - S "\"" ) ^ 0 * P "\"" -- OCaml strings + ( 1 - S "{}" ) ) ^ 0 } if piton_beamer then Beamer = L ( P "\\pause" * ( P "[" * (1 - P "]") ^ 0 * P "]" ) ^ -1 ) + ( P "\\uncover" * Lc ( '\\__piton_beamer_command:n{uncover}' ) + P "\\only" * Lc ( '\\__piton_beamer_command:n{only}' ) + P "\\alert" * Lc ( '\\__piton_beamer_command:n{alert}' ) + P "\\visible" * Lc ( '\\__piton_beamer_command:n{visible}' ) + P "\\invisible" * Lc ( '\\__piton_beamer_command:n{invisible}' ) + P "\\action" * Lc ( '\\__piton_beamer_command:n{action}' ) ) * L ( ( P "<" * (1 - P ">") ^ 0 * P ">" ) ^ -1 * P "{" ) * K ( 'ParseAgain.noCR' , BalancedBraces ) * L ( P "}" ) + L ( ( P "\\alt" ) * P "<" * (1 - P ">") ^ 0 * P ">" * P "{" ) * K ( 'ParseAgain.noCR' , BalancedBraces ) * L ( P "}{" ) * K ( 'ParseAgain.noCR' , BalancedBraces ) * L ( P "}" ) + L ( ( P "\\temporal" ) * P "<" * (1 - P ">") ^ 0 * P ">" * P "{" ) * K ( 'ParseAgain.noCR' , BalancedBraces ) * L ( P "}{" ) * K ( 'ParseAgain.noCR' , BalancedBraces ) * L ( P "}{" ) * K ( 'ParseAgain.noCR' , BalancedBraces ) * L ( P "}" ) 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 local EOL = P "\r" * ( ( space^0 * -1 ) + Ct ( Cc "EOL" * Ct ( Lc "\\__piton_end_line:" * BeamerEndEnvironments * BeamerBeginEnvironments * PromptHastyDetection * Lc "\\__piton_newline: \\__piton_begin_line:" * Prompt ) ) ) * SpaceIndentation ^ 0 local string = Q ( P "\"" ) * ( VisualSpace + Q ( ( 1 - S " \"\r" ) ^ 1 ) + EOL ) ^ 0 * Q ( P "\"" ) local String = WithStyle ( 'String.Long' , string ) 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 ) local QuotedStringBis = WithStyle ( 'String.Long' , ( VisualSpace + Q ( ( 1 - S " \r" ) ^ 1 ) + EOL ) ^ 0 ) local QuotedString = C ( open * ( 1 - closeeq ) ^ 0 * close ) / ( function (s) return QuotedStringBis : match(s) end ) local Comment = WithStyle ( 'Comment' , P { "A" , A = Q "(*" * ( V "A" + Q ( ( 1 - P "(*" - P "*)" - S "\r$\"" ) ^ 1 ) -- $ + string + P "$" * K ( 'Comment.Math' , ( 1 - S "$\r" ) ^ 1 ) * P "$" -- $ + EOL ) ^ 0 * Q "*)" } ) local DefFunction = K ( 'Keyword' , P "let rec" + P "let" + P "and" ) * Space * K ( 'Name.Function.Internal' , identifier ) * Space * # ( P "=" * space * P "function" + ( 1 - P "=" ) ) local TypeParameter = K ( 'TypeParameter' , P "'" * alpha * # ( 1 - P "'" ) ) MainLoopOCaml = ( ( space^1 * -1 ) + EOL + Space + Tab + Escape + Beamer + TypeParameter + String + QuotedString + Char + Comment + Delim + Operator + Punct + FromImport + ImportAs + Exception + DefFunction + Keyword * ( Space + Punct + Delim + EOL + -1 ) + OperatorWord * ( Space + Punct + Delim + EOL + -1 ) + Builtin * ( Space + Punct + Delim + EOL + -1 ) + Identifier + Number + Word ) ^ 0 local ocaml = P ( true ) ocaml = Ct ( ( ( space - P "\r" ) ^0 * P "\r" ) ^ -1 * BeamerBeginEnvironments * Lc ( '\\__piton_begin_line:' ) * SpaceIndentation ^ 0 * MainLoopOCaml * -1 * Lc ( '\\__piton_end_line:' ) ) languages['ocaml'] = ocaml function piton.Parse(language,code) local t = languages[language] : match ( code ) 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 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 function piton.ParseFile(language,name,first_line,last_line) s = '' local i = 0 for line in io.lines(name) do i = i + 1 if i >= first_line then s = s .. '\r' .. line end if i >= last_line then break end end piton.Parse(language,s) end function piton.ParseBis(language,code) local s = ( Cs ( ( P '##' / '#' + 1 ) ^ 0 ) ) : match ( code ) return piton.Parse(language,s) end function piton.ParseTer(language,code) local s = ( Cs ( ( P '\\__piton_breakable_space:' / ' ' + 1 ) ^ 0 ) ) : match ( code ) return piton.Parse(language,s) end 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 local function add(acc,new_value) return acc + new_value end local AutoGobbleLPEG = ( 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 ) 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 ) local EnvGobbleLPEG = ( ( 1 - P "\r" ) ^ 0 * P "\r" ) ^ 0 * Cf ( Cc(0) * ( P " " * Cc(1) ) ^ 0 , add ) * -1 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 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__piton_nb_lines_int {' .. count .. '}' ) end 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__piton_nb_non_empty_lines_int {' .. count .. '}' ) end function piton.CountLinesFile(name) local count = 0 for line in io.lines(name) do count = count + 1 end tex.sprint( luatexbase.catcodetables.expl , '\\int_set:Nn \\l__piton_nb_lines_int {' .. count .. '}' ) end 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__piton_nb_non_empty_lines_int {' .. count .. '}' ) end \end{luacode*} \endinput %% %% End of file `piton.sty'.