%%
%% 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.4}
\def\myfiledate{2023/02/14}

\bigskip

%%


\NeedsTeXFormat{LaTeX2e}
\RequirePackage{l3keys2e}
\ProvidesExplPackage
  {piton}
  {\myfiledate}
  {\myfileversion}
  {Highlight Python codes with LPEG on LuaLaTeX}
\msg_new:nnn { piton } { LuaLaTeX~mandatory }
  { The~package~'piton'~must~be~used~with~LuaLaTeX.\\ It~won't~be~loaded. }
\sys_if_engine_luatex:F { \msg_critical:nn { piton } { LuaLaTeX~mandatory } }
\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
\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 } }
  }
\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 }
  }
\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
          { \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 ,
    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 { 0.4 em }
      }
  }
\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 + 0.5em }
        }
      \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 + 0.5 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 ,
    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 ,
    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 #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 ##1 }
          \l__piton_style_tl
      }
  }
\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~<return>.
  }
  {
    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.
  }
\ExplSyntaxOff
\RequirePackage{luacode}
\begin{luacode*}
piton = piton or { }
if piton.comment_latex == nil then piton.comment_latex = ">" end
piton.comment_latex = "#" .. piton.comment_latex
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(pattern, style)
  if style
  then
  return
     Lc ( "{\\PitonStyle{" .. style .. "}{" )
     * Q ( pattern )
     * Lc ( "}}" )
  else
  return Q ( pattern )
  end
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 (
      ( digit^1 * P "." * digit^0 + digit^0 * P "." * digit^1 + digit^1 )
      * ( S "eE" * S "+-" ^ -1 * digit^1 ) ^ -1
      + digit^1 ,
      'Number'
    )
local Word
if piton_begin_escape ~= ''
then Word = K ( ( ( 1 - space - P(piton_begin_escape) - P(piton_end_escape) )
                   - S "'\"\r[()]" - digit ) ^ 1 )
else Word = K ( ( ( 1 - space ) - S "'\"\r[()]" - digit ) ^ 1 )
end
local Space = ( K " " ) ^ 1

local SkipSpace = ( K " " ) ^ 0

local Punct = K ( S ".,:;!" )

local Tab = P "\t" * Lc ( '\\l__piton_tab_tl' )
local SpaceIndentation = Lc ( '\\__piton_an_indentation_space:' ) * ( K " " )
local Delim = K ( S "[()]" )
local VisualSpace = space * Lc "\\l__piton_space_tl"
local Operator =
  K ( P "!=" + P "<>" + P "==" + P "<<" + P ">>" + P "<=" + P ">=" + P ":="
      + P "//" + P "**" + S "-~+/*%=<>&.@|"
      ,
      'Operator'
    )

local OperatorWord =
  K ( P "in" + P "is" + P "and" + P "or" + P "not" , 'Operator.Word')

local Keyword =
  K ( P "as" + P "assert" + P "break" + P "case" + P "class" + P "continue"
      + P "def" + P "del" + P "elif" + P "else" + P "except" + P "exec"
      + P "finally" + P "for" + P "from" + P "global" + P "if" + P "import"
      + P "lambda" + P "non local" + P "pass" + P "return" + P "try"
      + P "while" + P "with" + P "yield" + P "yield from" ,
  'Keyword' )
  + K ( P "True" + P "False" + P "None" , 'Keyword.Constant' )

local Builtin =
  K ( P "__import__" + P "abs" + P "all" + P "any" + P "bin" + P "bool"
    + P "bytearray" + P "bytes" + P "chr" + P "classmethod" + P "compile"
    + P "complex" + P "delattr" + P "dict" + P "dir" + P "divmod"
    + P "enumerate" + P "eval" + P "filter" + P "float" + P "format"
    + P "frozenset" + P "getattr" + P "globals" + P "hasattr" + P "hash"
    + P "hex" + P "id" + P "input" + P "int" + P "isinstance" + P "issubclass"
    + P "iter" + P "len" + P "list" + P "locals" + P "map" + P "max"
    + P "memoryview" + P "min" + P "next" + P "object" + P "oct" + P "open"
    + P "ord" + P "pow" + P "print" + P "property" + P "range" + P "repr"
    + P "reversed" + P "round" + P "set" + P "setattr" + P "slice" + P "sorted"
    + P "staticmethod" + P "str" + P "sum" + P "super" + P "tuple" + P "type"
    + P "vars" + P "zip" ,
  'Name.Builtin' )

local Exception =
  K ( 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" ,
  'Exception' )

local RaiseException = K ( P "raise" , 'Keyword' ) * SkipSpace * Exception * K ( P "(" )

local Decorator = K ( P "@" * letter^1 , 'Name.Decorator' )
local DefClass =
  K ( P "class" , 'Keyword' ) * Space * K ( identifier , 'Name.Class' )
local ImportAs =
  K ( P "import" , 'Keyword' )
   * Space
   * K ( identifier * ( P "." * identifier ) ^ 0 ,
         'Name.Namespace'
       )
   * (
       ( Space * K ( P "as" , 'Keyword' ) * Space
          * K ( identifier , 'Name.Namespace' ) )
       +
       ( SkipSpace * K ( P "," ) * SkipSpace
          * K ( identifier , 'Name.Namespace' ) ) ^ 0
     )
local FromImport =
  K ( P "from" , 'Keyword' )
    * Space * K ( identifier , 'Name.Namespace' )
    * Space * K ( P "import" , 'Keyword' )
local PercentInterpol =
  K ( P "%"
      * ( P "(" * alphanum ^ 1 * P ")" ) ^ -1
      * ( S "-#0 +" ) ^ 0
      * ( digit ^ 1 + P "*" ) ^ -1
      * ( P "." * ( digit ^ 1 + P "*" ) ) ^ -1
      * ( S "HlL" ) ^ -1
      * S "sdfFeExXorgiGauc%" ,
      'String.Interpol'
    )
local SingleShortString =
  Lc ( "{\\PitonStyle{String.Short}{" )
   * (
         K ( P "f'" + P "F'" )
         * (
             K ( P "{" , 'String.Interpol')
              * K ( ( 1 - S "}':" ) ^ 0 , 'Interpol.Inside' )
              * K ( P ":" * (1 - S "}:'") ^ 0 ) ^ -1
              * K ( P "}" , 'String.Interpol' )
             +
             VisualSpace
             +
             K ( ( P "\\'" + P "{{" + P "}}" + 1 - S " {}'" ) ^ 1 )
           ) ^ 0
         * K ( P "'" )
       +
         K ( P "'" + P "r'" + P "R'" )
         * ( K ( ( P "\\'" + 1 - S " '\r%" ) ^ 1 )
             + VisualSpace
             + PercentInterpol
             + K ( P "%" )
           ) ^ 0
         * K ( P "'" )
     )
   * Lc ( "}}" )

local DoubleShortString =
  Lc ( "{\\PitonStyle{String.Short}{" )
   * (
         K ( P "f\"" + P "F\"" )
         * (
             K ( P "{" , 'String.Interpol' )
               * K ( ( 1 - S "}\":" ) ^ 0 , 'Interpol.Inside' )
               * ( K ( P ":" , 'String.Interpol' ) * K ( (1 - S "}:\"") ^ 0 ) ) ^ -1
               * K ( P "}" , 'String.Interpol' )
             +
             VisualSpace
             +
             K ( ( P "\\\"" + P "{{" + P "}}" + 1 - S " {}\"" ) ^ 1 )
            ) ^ 0
         * K ( P "\"" )
       +
         K ( P "\"" + P "r\"" + P "R\"" )
         * ( K ( ( P "\\\"" + 1 - S " \"\r%" ) ^ 1 )
             + VisualSpace
             + PercentInterpol
             + K ( P "%" )
           ) ^ 0
         * K ( P "\"" )
     )
   * Lc ( "}}" )

local ShortString = SingleShortString + DoubleShortString
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"

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 ( BalancedBraces , 'ParseAgain.noCR' )
      * L ( P "}" )
    +
      L (
          ( P "\\alt" )
          * P "<" * (1 - P ">") ^ 0 * P ">"
          * P "{"
        )
      * K ( BalancedBraces , 'ParseAgain.noCR' )
      * L ( P "}{" )
      * K ( BalancedBraces , 'ParseAgain.noCR' )
      * L ( P "}" )
    +
      L (
          ( P "\\temporal" )
          * P "<" * (1 - P ">") ^ 0 * P ">"
          * P "{"
        )
      * K ( BalancedBraces , 'ParseAgain.noCR' )
      * L ( P "}{" )
      * K ( BalancedBraces , 'ParseAgain.noCR' )
      * L ( P "}{" )
      * K ( BalancedBraces , 'ParseAgain.noCR' )
      * 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 ( ( ( P ">>>" + P "..." ) * P " " ^ -1 ) ^ -1 , 'Prompt' )
local EOL
if piton_beamer
then
EOL =
  P "\r"
  *
  (
    ( space^0 * -1 )
    +
    Lc ( '\\__piton_end_line:' )
    * BeamerEndEnvironments
    * BeamerBeginEnvironments
    * PromptHastyDetection
    * Lc ( '\\__piton_newline: \\__piton_begin_line:' )
    * Prompt
  )
  *
  SpaceIndentation ^ 0
else
EOL =
  P "\r"
  *
  (
    ( space ^ 0 * -1 )
    +
    Lc ( '\\__piton_end_line:' )
    * PromptHastyDetection
    * Lc ( '\\__piton_newline: \\__piton_begin_line:' )
    * Prompt
  )
  *
  SpaceIndentation ^ 0
end
function EOL_for_style ( s )
   return Lc "}}" * EOL * Lc ( "{\\PitonStyle{" .. s .. "}{" )
end
local SingleLongString =
  Lc "{\\PitonStyle{String.Long}{"
   * (
         K ( S "fF" * P "'''" )
         * (
             K ( P "{" , 'String.Interpol' )
               * K ( ( 1 - S "}:\r" - P "'''" ) ^ 0 , 'Interpol.Inside' )
               * K ( P ":" * (1 - S "}:\r" - P "'''" ) ^ 0 ) ^ -1
               * K ( P "}" , 'String.Interpol' )
             +
             K ( ( 1 - P "'''" - S "{}'\r" ) ^ 1 )
             +
             EOL_for_style 'String.Long'
           ) ^ 0
       +
         K ( ( S "rR" ) ^ -1  * P "'''" )
         * (
             K ( ( 1 - P "'''" - S "\r%" ) ^ 1 )
             +
             PercentInterpol
             +
             P "%"
             +
             EOL_for_style 'String.Long'
           ) ^ 0
      )
   * K ( P "'''" )
   * Lc "}}"

local DoubleLongString =
  Lc "{\\PitonStyle{String.Long}{"
   * (
         K ( S "fF" * P "\"\"\"" )
         * (
             K ( P "{" , 'String.Interpol' )
               * K ( ( 1 - S "}:\r" - P "\"\"\"" ) ^ 0 , 'Interpol.Inside' )
               * K ( P ":" * (1 - S "}:\r" - P "\"\"\"" ) ^ 0 ) ^ -1
               * K ( P "}" , 'String.Interpol' )
             +
             K ( ( 1 - P "\"\"\"" - S "{}\"\r" ) ^ 1 )
             +
             EOL_for_style 'String.Long'
           ) ^ 0
       +
         K ( ( S "rR" ) ^ -1  * P "\"\"\"" )
         * (
             K ( ( 1 - P "\"\"\"" - S "%\r" ) ^ 1 )
             +
             PercentInterpol
             +
             P "%"
             +
             EOL_for_style 'String.Long'
           ) ^ 0
      )
   * K ( P "\"\"\"" )
   * Lc "}}"
local LongString = SingleLongString + DoubleLongString
local StringDoc =
    K ( P "\"\"\"" , 'String.Doc' )
      * ( K ( (1 - P "\"\"\"" - P "\r" ) ^ 0 , 'String.Doc' ) * EOL
          * Tab ^ 0
        ) ^ 0
      * K ( ( 1 - P "\"\"\"" - P "\r" ) ^ 0 * P "\"\"\"" , 'String.Doc' )
local CommentMath =
  P "$" * K ( ( 1 - S "$\r" ) ^ 1 , 'Comment.Math' ) * P "$"

local Comment =
  Lc ( "{\\PitonStyle{Comment}{" )
  * K ( P "#" )
  * ( CommentMath + K ( ( 1 - S "$\r" ) ^ 1 ) ) ^ 0
  * Lc ( "}}" )
  * ( EOL + -1 )
local CommentLaTeX =
  P(piton.comment_latex)
  * Lc "{\\PitonStyle{Comment.LaTeX}{\\ignorespaces"
  * L ( ( 1 - P "\r" ) ^ 0 )
  * Lc "}}"
  * ( EOL + -1 )
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 ( P "=" * Expression , 'InitialValues' )
       + K ( P ":" ) * SkipSpace * K ( letter^1 , 'Name.Type' )
     ) ^ -1
local Params = ( Param * ( K "," * Param ) ^ 0 ) ^ -1
local DefFunction =
  K ( P "def" , 'Keyword' )
  * Space
  * K ( identifier , 'Name.Function' )
  * SkipSpace
  * K ( P "(" ) * Params * K ( P ")" )
  * SkipSpace
  * ( K ( P "->" ) * SkipSpace * K ( identifier , 'Name.Type' ) ) ^ -1
  * K ( ( 1 - S ":\r" )^0 , 'ParseAgain' )
  * K ( 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 * K ( P ":" ) * K ( Expression , 'Dict.Value' )

local ItemOfSet = SkipSpace * ( ItemDict + ShortString ) * SkipSpace

local Set =
  K ( P "{" )
  * ItemOfSet * ( K ( P "," ) * ItemOfSet )  ^ 0
  * K ( P "}" )
local ExceptionInConsole = Exception *  K ( ( 1 - P "\r" ) ^ 0 ) * EOL
MainLoop =
  (  ( space^1 * -1 )
     + EOL
     + Space
     + Tab
     + Escape
     + CommentLaTeX
     + Beamer
     + 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
       * MainLoop
       * -1
       * Lc ( '\\__piton_end_line:' )
     )
local languages = { }
languages['python'] = python
local Punct = K ( S ",:;!" )
local identifier =
  ( R "az" + R "AZ" + P "_") * ( R "az" + R "AZ" + S "_'" + digit ) ^ 0

local Identifier = K ( identifier )

local Operator =
  K ( P "!=" + P "<>" + P "==" + P "<<" + P ">>" + P "<=" + P ">=" + P ":="
      + P "||" + P "&&" + P "//" + P "**" + P ";;" + P "::" + P "->"
      + P "+." + P "-." + P "*." + P "/."
      + S "-~+/*%=<>&@|"
      ,
      'Operator'
    )

local OperatorWord =
  K ( P "and" + P "asr" + P "land" + P "lor" + P "lsl" + P "lxor"
      + P "mod" + P "or" ,
      'Operator.Word')

local Keyword =
  K ( 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" ,
  'Keyword' )
  + K ( P "true" + P "false" , 'Keyword.Constant' )

local Builtin =
  K (   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"
      , 'Name.Builtin' )
local Exception =
  K ( 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" ,
  'Exception' )
local Char =
  K ( P "'" * ( ( 1 - P "'" ) ^ 0 + P "\\'" ) * P "'" , 'String.Short' )
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 ( BalancedBraces , 'ParseAgain.noCR' )
      * L ( P "}" )
    +
      L (
          ( P "\\alt" )
          * P "<" * (1 - P ">") ^ 0 * P ">"
          * P "{"
        )
      * K ( BalancedBraces , 'ParseAgain.noCR' )
      * L ( P "}{" )
      * K ( BalancedBraces , 'ParseAgain.noCR' )
      * L ( P "}" )
    +
      L (
          ( P "\\temporal" )
          * P "<" * (1 - P ">") ^ 0 * P ">"
          * P "{"
        )
      * K ( BalancedBraces , 'ParseAgain.noCR' )
      * L ( P "}{" )
      * K ( BalancedBraces , 'ParseAgain.noCR' )
      * L ( P "}{" )
      * K ( BalancedBraces , 'ParseAgain.noCR' )
      * 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
if piton_beamer
then
EOL =
  P "\r"
  *
  (
    ( space^0 * -1 )
    +
    Lc ( '\\__piton_end_line:' )
    * BeamerEndEnvironments
    * BeamerBeginEnvironments
    * Lc ( '\\__piton_newline: \\__piton_begin_line:' )
  )
  *
  SpaceIndentation ^ 0
else
EOL =
  P "\r"
  *
  (
    ( space ^ 0 * -1 )
    +
    Lc ( '\\__piton_end_line:' )
    * Lc ( '\\__piton_newline: \\__piton_begin_line:' )
  )
  *
  SpaceIndentation ^ 0
end
function EOL_for_style ( s )
   return Lc "}}" * EOL * Lc ( "{\\PitonStyle{" .. s .. "}{" )
end
local String =
  Lc "{\\PitonStyle{String.Long}{"
   * K ( P "\"" )
   * (
       VisualSpace
       +
       K ( ( 1 - S " \"\r" ) ^ 1 )
       +
       EOL_for_style 'String.Long'
     ) ^ 0
   * K ( P "\"" )
   * Lc "}}"
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 =
   Lc "{\\PitonStyle{String.Long}{"
   *
     (
        VisualSpace
        +
        K ( ( 1 - S " \r" ) ^ 1 )
        +
        EOL_for_style 'String.Long'
     ) ^ 0
   * Lc "}}"
local QuotedString =
   C ( open * ( 1 - closeeq ) ^ 0  * close ) /
  ( function (s) return QuotedStringBis : match(s) end )
local Comment =
  Lc "{\\PitonStyle{Comment}{"
  *
  P {
      "A" ,
      A = K "(*"
          * ( V "A"
              + K ( ( 1 - P "(*" - P "*)" - S "\r$" ) ^ 1 ) -- $
              + P "$" * K ( ( 1 - S "$\r" ) ^ 1 , 'Comment.Math' ) * P "$" -- $
              + EOL_for_style 'Comment'
            ) ^ 0
          * K "*)"
    }
  *
  Lc "}}"
local DefFunction =
  ( K ( P "let rec" + P "let" + P "and" , 'Keyword' ) )
  * Space
  * K ( identifier , 'Name.Function' )
  * Space
  * # ( 1 - P "=" )
local TypeParameter = K ( P "'" * alpha * # ( 1 - P "'") , 'TypeParameter' )
MainLoop =
  (  ( 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
       * MainLoop
       * -1
       * Lc ( '\\__piton_end_line:' )
     )
languages['ocaml'] = ocaml
local MinimalSyntax = Ct ( ( (1 - P "\r" ) ^ 1 + EOL ) ^ 0 )
function piton.Parse(language,code)
  local t = languages[language] : match ( code )
  if t == nil then t = MinimalSyntax : match ( code ) end
  for _ , s in ipairs(t) do tex.tprint(s) 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'.
