diff options
Diffstat (limited to 'Master/texmf-dist/tex')
-rw-r--r-- | Master/texmf-dist/tex/latex/pegmatch/pegmatch.sty | 928 |
1 files changed, 928 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/latex/pegmatch/pegmatch.sty b/Master/texmf-dist/tex/latex/pegmatch/pegmatch.sty new file mode 100644 index 00000000000..ea746ec2d84 --- /dev/null +++ b/Master/texmf-dist/tex/latex/pegmatch/pegmatch.sty @@ -0,0 +1,928 @@ +%%% ---------------------------------------------------------------------------- +%%% Caption : Parsing Expression Grammars for TeX +%%% Author : Jianrui Lyu <tolvjr@163.com> +%%% CTAN Page : https://ctan.org/pkg/pegmatch +%%% Repository : https://github.com/lvjr/pegmatch +%%% License : The LaTeX Project Public License 1.3c +%%% ---------------------------------------------------------------------------- + +\NeedsTeXFormat{LaTeX2e} +\ProvidesExplPackage{pegmatch}{2025-02-16}{v2025B} + {Parsing Expression Grammars for TeX} + +\cs_generate_variant:Nn \iow_log:n {V} +\cs_generate_variant:Nn \str_range:nnn {nne} +\cs_generate_variant:Nn \tl_analysis_map_inline:nn {e} + +\prg_generate_conditional_variant:Nnn \int_compare:nNn {eN} {p,TF} +\prg_generate_conditional_variant:Nnn \str_if_eq:nn {en} {TF} +\prg_generate_conditional_variant:Nnn \str_if_in:nn {nV} {TF} +\prg_generate_conditional_variant:Nnn \tl_if_head_is_group:n {V} {TF} +\prg_generate_conditional_variant:Nnn \tl_if_head_is_space:n {v} {TF} + +%% Every speg variable starts with scan mark \s__speg. +\scan_new:N \s__speg + +\cs_new_protected:Npn \speg_new:N #1 + { + \tl_new:N #1 + \tl_set:Nn #1 { \s__speg } + } +\cs_set_eq:NN \NewSpeg \speg_new:N + +\speg_new:N \lTmpaSpeg +\speg_new:N \lTmpbSpeg +\speg_new:N \gTmpaSpeg +\speg_new:N \gTmpbSpeg + +\seq_new:N \lSpegTmpaSeq +\seq_new:N \lSpegTmpbSeq +\cs_set_eq:NN \MapSpegSeqInline \seq_map_inline:Nn + +%%% ------------------------------------------------------- +%%> \section{Set speg variables} +%%% ------------------------------------------------------- + +\int_new:N \g__speg_prg_map_int + +%% Split tl #1 into items separated by tl #2, and pass each item to code #3. +%% Braces around each item are kept but spaces around each item are removed. +\cs_new_protected:Npn \__speg_tl_split_map_inline:nnn #1 #2 #3 + { + \int_gincr:N \g__speg_prg_map_int + \cs_gset_protected:cpn + {__speg_map_ \int_use:N \g__speg_prg_map_int :w} ##1 {#3} + \__speg_tl_split_map_function:nnc + {#1} {#2} {__speg_map_ \int_use:N \g__speg_prg_map_int :w} + \int_gdecr:N \g__speg_prg_map_int + } +\cs_generate_variant:Nn \__speg_tl_split_map_inline:nnn {V} + +%% Split tl #1 into items separated by tl #2, and pass each item to function #3. +%% Braces around each item are kept but spaces around each item are removed. +%% We insert \prg_do_nothing: before each item to avoid losing outermost braces. +\cs_new_protected:Npn \__speg_tl_split_map_function:nnN #1 #2 #3 + { + \cs_set_protected:cpn + { __speg_tl_split_map_ \int_use:N \g__speg_prg_map_int _aux:Nw } ##1 ##2 #2 + { + \tl_if_eq:nnF {\prg_do_nothing: \c_novalue_tl} {##2} + { + \exp_args:Ne ##1 {\tl_trim_spaces:o {##2}} + \use:c {__speg_tl_split_map_\int_use:N \g__speg_prg_map_int _aux:Nw} + ##1 \prg_do_nothing: + } + } + \use:c {__speg_tl_split_map_ \int_use:N \g__speg_prg_map_int _aux:Nw} + #3 \prg_do_nothing: #1 #2 \c_novalue_tl #2 + } +\cs_generate_variant:Nn \__speg_tl_split_map_function:nnN {V, nnc} + +\tl_new:N \l__speg_result_tl + +\cs_new_protected:Npn \speg_set:Nn #1 #2 + { + \__speg_tracing:nn {set} { \tl_log:n {Input=#2} } + \tl_clear:N \l__speg_result_tl + \__speg_parse_expr:n {#2} + \tl_set:Ne #1 { \s__speg \exp_not:V \l__speg_result_tl } + \__speg_tracing:nn {set} { \speg_log:N #1 } + } +\cs_set_eq:NN \SetSpeg \speg_set:Nn + +\cs_new_protected:Npn \__speg_parse_expr:n #1 + { + \__speg_parse_choice:n {#1} + %\tl_log:N \l__speg_result_tl + \tl_set:Ne \l__speg_result_tl { \l__speg_result_tl } + } + +\tl_const:Nn \c__speg_left_brace_tl { \exp_after:wN {\if_false:} \fi: } +\tl_const:Nn \c__speg_right_brace_tl { \if_false: {\fi:} } + +%% parse choice operator +\cs_new_protected:Npn \__speg_parse_choice:n #1 + { + \tl_if_in:nnTF {#1} {/} + { + \tl_put_right:Nn \l__speg_result_tl {\SpegChoice \c__speg_left_brace_tl} + \__speg_tl_split_map_inline:nnn {#1} {/} + { + \tl_put_right:Nn \l__speg_result_tl {\c__speg_left_brace_tl} + \__speg_parse_concat:n {##1} + \tl_put_right:Nn \l__speg_result_tl {\c__speg_right_brace_tl} + } + \tl_put_right:Nn \l__speg_result_tl {\c__speg_right_brace_tl} + } + { \__speg_parse_concat:n {#1} } + } +\cs_generate_variant:Nn \__speg_parse_choice:n {e} + +%% parse concat operator +\cs_new_protected:Npn \__speg_parse_concat:n #1 + { + \tl_if_in:nnTF {#1} {*} + { + \tl_put_right:Nn \l__speg_result_tl {\SpegConcat \c__speg_left_brace_tl} + \__speg_tl_split_map_inline:nnn {#1} {*} + { + \tl_put_right:Nn \l__speg_result_tl {\c__speg_left_brace_tl} + \__speg_parse_predicate:n {##1} + \tl_put_right:Nn \l__speg_result_tl {\c__speg_right_brace_tl} + } + \tl_put_right:Nn \l__speg_result_tl {\c__speg_right_brace_tl} + } + { \__speg_parse_predicate:n {#1} } + } + +\tl_new:N \l__speg_predicate_head_tl +\tl_new:N \l__speg_predicate_tail_tl +\tl_const:Nn \c__speg_not_tl {!} +\tl_const:Nn \c__speg_and_tl {&} + +%% parse "not predicate" and "and predicate" operators +\cs_new_protected:Npn \__speg_parse_predicate:n #1 + { + \tl_set:Ne \l__speg_predicate_head_tl {\tl_head:n {#1}} + \tl_set:Ne \l__speg_predicate_tail_tl {\tl_trim_spaces:e {\tl_tail:n {#1}}} + \tl_case:NnTF \l__speg_predicate_head_tl + { + \c__speg_not_tl + { + \tl_put_right:Nn \l__speg_result_tl {\SpegNot} + } + \c__speg_and_tl + { + \tl_put_right:Nn \l__speg_result_tl {\SpegAnd} + } + } + { + \tl_put_right:Nn \l__speg_result_tl {\c__speg_left_brace_tl} + \__speg_parse_repeat:V \l__speg_predicate_tail_tl + \tl_put_right:Nn \l__speg_result_tl {\c__speg_right_brace_tl} + } + { \__speg_parse_repeat:n {#1} } + } + +\quark_new:N \q__speg_stop + +%% parse repeat operator +\cs_new_protected:Npn \__speg_parse_repeat:n #1 + { + \tl_if_in:nnTF {#1} {^} + { + %% we prepend \prg_do_nothing: to avoid losing outermost braces + \__speg_parse_repeat_aux:wnw \prg_do_nothing: #1 \q__speg_stop + } + { \__speg_parse_or_store_atom:e { \tl_trim_spaces:n {#1} } } + } +\cs_generate_variant:Nn \__speg_parse_repeat:n {V} + +\msg_new:nnn {speg} {invalid-repeat-arguments} + {Invalid ~ arguments ~ '#1' ~ for ~ \token_to_str:N \SpegRepeat!} + +\cs_new_protected:Npn \__speg_parse_repeat_aux:wnw #1 ^ #2 #3 \q__speg_stop + { + %% remove \prg_do_nothing: at the beginning of #1 + \__speg_parse_repeat_real:eee { \tl_trim_spaces:e { \tl_tail:n {#1} } } + { \tl_trim_spaces:n {#2} } { \tl_trim_spaces:n {#3} } + } + +\cs_new_protected:Npn \__speg_parse_repeat_real:nnn #1 #2 #3 + { + \tl_if_empty:eF {#3} {\msg_error:nne {speg} {invalid-repeat-arguments} {#3}} + \tl_put_right:Nn \l__speg_result_tl {\SpegRepeat \c__speg_left_brace_tl} + \__speg_parse_or_store_atom:n {#1} + \tl_put_right:Nn \l__speg_result_tl {\c__speg_right_brace_tl {#2}} + } +\cs_generate_variant:Nn \__speg_parse_repeat_real:nnn {eee} + +%% We need to put speg atom (especially speg variable) inside \exp_not:n, +%% to protect it from e-expansion in \__speg_parse_expr:n function. +\cs_new_protected:Npn \__speg_parse_or_store_atom:n #1 + { + \tl_if_head_is_group:nTF {#1} + { \__speg_parse_choice:n #1 } + { + \tl_if_head_eq_meaning:nNTF {#1} \SpegC + { + \tl_put_right:Nn \l__speg_result_tl {\SpegC \c__speg_left_brace_tl} + \__speg_parse_choice:e { \tl_tail:n {#1} } + \tl_put_right:Nn \l__speg_result_tl {\c__speg_right_brace_tl} + } + { \tl_put_right:Nn \l__speg_result_tl { \exp_not:n {#1} } } + } + } +\cs_generate_variant:Nn \__speg_parse_or_store_atom:n {e} + +%%% ------------------------------------------------------- +%%> \section{Log speg variables} +%%% ------------------------------------------------------- + +\int_new:N \l__speg_log_indent_int +\tl_new:N \l__speg_log_line_tl +\seq_new:N \l__speg_log_seq + +\cs_new_protected:Npn \speg_log:N #1 + { + \int_zero:N \l__speg_log_indent_int + \seq_clear:N \l__speg_log_seq + \tl_clear:N \l__speg_log_line_tl + %% Remove leading \s__speg in speg variable #1 + \tl_analysis_map_inline:en { \tl_tail:N #1 } + { + \int_case:nnF {"##3} % convert hexadecimal digit to integer + { + {1} % begin-group + { + \tl_if_empty:NF \l__speg_log_line_tl {\__speg_log_line:} + \tl_set_eq:NN \l__speg_log_line_tl \c_left_brace_str + \__speg_log_line: + \int_incr:N \l__speg_log_indent_int + } + {2} % end-group + { + \tl_if_empty:NF \l__speg_log_line_tl {\__speg_log_line:} + \int_decr:N \l__speg_log_indent_int + \tl_set_eq:NN \l__speg_log_line_tl \c_right_brace_str + \__speg_log_line: + } + } + {\tl_put_right:Ne \l__speg_log_line_tl {##1}} + } + \iow_log:e + { + >~Compiled~speg~variable~\token_to_str:N #1:^^J + \seq_use:Nn \l__speg_log_seq {^^J} + } + } +\cs_set_eq:NN \LogSpeg \speg_log:N + +\cs_new_protected:Npn \__speg_log_line: + { + \seq_put_right:Ne \l__speg_log_seq + { + \prg_replicate:nn {\l__speg_log_indent_int * 2} {~} + \exp_not:V \l__speg_log_line_tl + } + \tl_clear:N \l__speg_log_line_tl + } + +%%% ------------------------------------------------------- +%%> \section{Match speg variables} +%%% ------------------------------------------------------- + +\int_new:N \g__speg_match_level_int +\int_new:N \g__speg_match_level_max_int +\prop_new_linked:N \g__speg_match_index_prop + +\cs_new_protected:Npn \__speg_match_gzero_level: + { + \int_gzero:N \g__speg_match_level_int + } + +\cs_new_protected:Npn \__speg_match_gincr_level: + { + \int_gincr:N \g__speg_match_level_int + \int_compare:nNnT + { \g__speg_match_level_int } > { \g__speg_match_level_max_int } + { + \int_gset_eq:NN \g__speg_match_level_max_int \g__speg_match_level_int + \bool_new:c + { g__speg_match_ \int_use:N \g__speg_match_level_int _bool } + \str_new:c { g__speg_match_ \int_use:N \g__speg_match_level_int _str } + } + } + +\cs_new_protected:Npn \__speg_match_gdecr_level: + { + \int_gdecr:N \g__speg_match_level_int + } + +\cs_new_protected:Npn \__speg_match_gset_true: + { + \bool_gset_true:c + { g__speg_match_ \int_use:N \g__speg_match_level_int _bool } + } + +\cs_new_protected:Npn \__speg_match_gset_false: + { + \bool_gset_false:c + { g__speg_match_ \int_use:N \g__speg_match_level_int _bool } + } + +\cs_new:Npn \__speg_match_use_status: + { + \bool_if:cTF + { g__speg_match_ \int_use:N \g__speg_match_level_int _bool } + {true} {false} + } + +\prg_new_conditional:Npnn \__speg_match_if_success: { p, T, F, TF } + { + \bool_if:cTF + { g__speg_match_ \int_use:N \g__speg_match_level_int _bool } + { \prg_return_true: } { \prg_return_false: } + } + +\prg_new_conditional:Npnn \__speg_match_if_sub_success: { p, T, F, TF } + { + \bool_if:cTF + { g__speg_match_ \int_eval:n { \g__speg_match_level_int + 1 } _bool } + { \prg_return_true: } { \prg_return_false: } + } + +\cs_new_protected:Npn \__speg_match_gincr_index: + { + \prop_gput:Nee \g__speg_match_index_prop + { \int_use:N \g__speg_match_level_int } + { + \int_eval:n + { + \prop_item:Ne \g__speg_match_index_prop + { \int_use:N \g__speg_match_level_int } + + 1 + } + } + } + +\cs_new_protected:Npn \__speg_match_gdecr_index: + { + \prop_gput:Nee \g__speg_match_index_prop + { \int_use:N \g__speg_match_level_int } + { + \int_eval:n + { + \prop_item:Ne \g__speg_match_index_prop + { \int_use:N \g__speg_match_level_int } + - 1 + } + } + } + +\cs_new_protected:Npn \__speg_match_gset_index:n #1 + { + \prop_gput:Nen \g__speg_match_index_prop + { \int_use:N \g__speg_match_level_int } {#1} + } +\cs_generate_variant:Nn \__speg_match_gset_index:n {e} + +\cs_new_protected:Npn \__speg_match_gset_eq_index: + { + \prop_gput:Nee \g__speg_match_index_prop + { \int_use:N \g__speg_match_level_int } + { + \prop_item:Ne \g__speg_match_index_prop + { \int_eval:n { \g__speg_match_level_int + 1 } } + } + } + +\cs_new_protected:Npn \__speg_match_update_index: + { + \prop_gput:Nee \g__speg_match_index_prop + { \int_use:N \g__speg_match_level_int } + { + \int_eval:n + { + \prop_item:Ne \g__speg_match_index_prop + { \int_use:N \g__speg_match_level_int } + + + \prop_item:Ne \g__speg_match_index_prop + { \int_eval:n { \g__speg_match_level_int + 1 } } + - 1 + } + } + } + +\cs_new:Npn \__speg_match_use_index: + { + \prop_item:Ne \g__speg_match_index_prop + { \int_use:N \g__speg_match_level_int } + } + +\cs_new:Npn \__speg_match_use_sub_index: + { + \prop_item:Ne \g__speg_match_index_prop + { \int_eval:n { \g__speg_match_level_int + 1 } } + } + +\cs_new:Npn \__speg_match_use_index:n #1 + { + \prop_item:Nn \g__speg_match_index_prop {#1} + } + +\cs_new_protected:Npn \__speg_match_get_position:N #1 + { + \int_set:Nn #1 {1} + \int_step_inline:nnn {2} { \g__speg_match_level_int } + { + \int_add:Nn #1 + { \prop_item:Nn \g__speg_match_index_prop {##1} - 1 } + } + %\int_log:N #1 + } + +\cs_new_protected:Npn \__speg_match_gclear_subject: + { + \str_gclear:c + { g__speg_match_ \int_use:N \g__speg_match_level_int _str } + } + +\cs_new_protected:Npn \__speg_match_gset_subject:n #1 + { + \str_gset:cn + { g__speg_match_ \int_use:N \g__speg_match_level_int _str } {#1} + } +\cs_generate_variant:Nn \__speg_match_gset_subject:n {e} + +\cs_new_protected:Npn \__speg_match_gset_eq_subject: + { + \str_gset_eq:cc + { g__speg_match_ \int_use:N \g__speg_match_level_int _str } + { g__speg_match_ \int_eval:n { \g__speg_match_level_int + 1 } _str } + } + +\cs_new_protected:Npn \__speg_match_get_capture:nN #1 #2 + { + \str_set:Ne #2 + { + \str_range:nne {#1} {1} + { \int_eval:n { \__speg_match_use_sub_index: - 1 } } + } + } + +\cs_new:Npn \__speg_match_use_subject: + { + \str_use:c { g__speg_match_ \int_use:N \g__speg_match_level_int _str } + } + +\cs_new_protected:Npn \__speg_match_apply_subject:n #1 + { + \__speg_match_apply_subject_aux:vn + { g__speg_match_ \int_use:N \g__speg_match_level_int _str } {#1} + } + +\cs_new_protected:Npn \__speg_match_apply_subject_aux:nn #1 #2 + { + #2 {#1} + } +\cs_generate_variant:Nn \__speg_match_apply_subject_aux:nn {vn} + +\prg_new_conditional:Npnn \__speg_match_if_head_is_space: {TF} + { + \tl_if_head_is_space:vTF + { g__speg_match_ \int_use:N \g__speg_match_level_int _str } + { \prg_return_true: } { \prg_return_false: } + } + +\cs_new:Npn \__speg_match_head_subject: + { + \tl_head:v { g__speg_match_ \int_use:N \g__speg_match_level_int _str } + } + +\cs_new:Npn \__speg_match_tail_subject: + { + \tl_tail:v { g__speg_match_ \int_use:N \g__speg_match_level_int _str } + } + +\exp_last_unbraced:NNo \cs_new:Npn \__speg_gobble_space:w \c_space_tl { } + +\cs_new:Npn \__speg_match_head_subject_keep_space: + { + \__speg_match_if_head_is_space:TF { ~ } { \__speg_match_head_subject: } + } + +\cs_new:Npn \__speg_match_tail_subject_keep_space: + { + \__speg_match_if_head_is_space:TF + { + \exp_last_unbraced:Nv \__speg_gobble_space:w + { g__speg_match_ \int_use:N \g__speg_match_level_int _str } + } + { \__speg_match_tail_subject: } + } + +\cs_new_protected:Npn \__speg_match_gpop_subject:N #1 + { + \tl_set:Ne #1 { \__speg_match_head_subject_keep_space: } + \__speg_match_gset_subject:e + { \__speg_match_tail_subject_keep_space: } + } + +%% #1: pattern variable; #2: subject string. +\cs_new_protected:Npn \__speg_match:Nn #1 #2 + { + %\iow_log:e { Matching ~ \exp_not:V #1 : } + \__speg_match_gincr_level: + \__speg_tracing_match_start:n { Match {#2} } + \__speg_match_gset_index:n {1} + \__speg_match_gset_false: + #1 {#2} + \__speg_match_if_sub_success:T + { + \__speg_match_gset_true: + \__speg_match_update_index: + } + \__speg_tracing_match_stop:n { Match } + \__speg_match_gdecr_level: + } +\cs_set_eq:NN \MatchSpeg \__speg_match:Nn + +\prg_new_protected_conditional:Npnn \speg_match:Nn #1 #2 { T, F, TF } + { + \__speg_match:Nn #1 {#2} + \bool_if:cTF { g__speg_match_1_bool } + { \prg_return_true: } { \prg_return_false: } + } +\cs_set_eq:NN \IfSpegMatchT \speg_match:NnT +\cs_set_eq:NN \IfSpegMatchF \speg_match:NnF +\cs_set_eq:NN \IfSpegMatchTF \speg_match:NnTF + +\seq_new:N \g__speg_capture_seq + +%% #1: pattern variable; #2: subject string; #3: seq variable for captures. +\cs_new_protected:Npn \__speg_extract:NnN #1 #2 #3 + { + \seq_gclear:N \g__speg_capture_seq + \__speg_match:Nn #1 {#2} + \seq_set_eq:NN #3 \g__speg_capture_seq + } +\cs_set_eq:NN \ExtractSpeg \__speg_extract:NnN + +\prg_new_protected_conditional:Npnn \speg_extract:NnN #1 #2 #3 { T, F, TF } + { + \__speg_extract:NnN #1 {#2} #3 + \bool_if:cTF { g__speg_match_1_bool } + { \prg_return_true: } + { + \seq_clear:N #3 + \prg_return_false: + } + } +\cs_set_eq:NN \IfSpegExtractT \speg_extract:NnNT +\cs_set_eq:NN \IfSpegExtractF \speg_extract:NnNF +\cs_set_eq:NN \IfSpegExtractTF \speg_extract:NnNTF + +%% #1: pattern from "patt1 / patt2 / patt3"; #2: subject string. +\cs_new_protected:Npn \speg_choice:nn #1 #2 + { + \__speg_match_gincr_level: + \__speg_tracing_match_start:n { Choice {#2} } + \__speg_match_gset_index:n {1} + \__speg_match_gset_false: + \tl_map_inline:nn {#1} + { + ##1 {#2} + \__speg_match_if_sub_success:T + { + \__speg_match_gset_true: + \tl_map_break: + } + } + \__speg_match_if_success:T + { + \__speg_match_gset_eq_subject: + \__speg_match_update_index: + } + \__speg_tracing_match_stop:n { Choice } + \__speg_match_gdecr_level: + } +\cs_generate_variant:Nn \speg_choice:nn {nV} +\cs_set_eq:NN \SpegChoice \speg_choice:nn + +\tl_new:N \l__speg_pattern_head_tl + +%% #1: pattern from "patt1 * patt2 * patt3"; #2: subject string. +\cs_new_protected:Npn \speg_concat:nn #1 #2 + { + \__speg_match_gincr_level: + \__speg_tracing_match_start:n { Concat {#2} } + \__speg_match_gset_subject:n {#2} + \__speg_match_gset_index:n {1} + \__speg_match_gset_true: + \tl_map_inline:nn {#1} + { + \tl_set:Ne \l__speg_pattern_head_tl { \tl_head:n {##1} } + \exp_after:wN \tl_if_eq:NNTF \l__speg_pattern_head_tl \SpegCp + { ##1 } + { + \__speg_match_apply_subject:n {##1} + \__speg_match_if_sub_success:TF + { + \__speg_match_gset_eq_subject: + \__speg_match_update_index: + } + { + \__speg_match_gset_false: + \tl_map_break: + } + } + } + \__speg_tracing_match_stop:n { Concat } + \__speg_match_gdecr_level: + } +\cs_generate_variant:Nn \speg_concat:nn {nV} +\cs_set_eq:NN \SpegConcat \speg_concat:nn + +%% #1: pattern from "! patt"; #2: subject string. +\cs_new_protected:Npn \speg_not:nn #1 #2 + { + \__speg_match_gincr_level: + \__speg_tracing_match_start:n { Not {#2} } + \__speg_match_gset_subject:n {#2} + \__speg_match_gset_index:n {1} + #1 {#2} + \__speg_match_if_sub_success:TF + { \__speg_match_gset_false: } { \__speg_match_gset_true: } + \__speg_tracing_match_stop:n { Not } + \__speg_match_gdecr_level: + } +\cs_generate_variant:Nn \speg_not:nn {nV} +\cs_set_eq:NN \SpegNot \speg_not:nn + +%% #1: pattern from "& patt"; #2: subject string. +\cs_new_protected:Npn \speg_and:nn #1 #2 + { + \__speg_match_gincr_level: + \__speg_tracing_match_start:n { And {#2} } + \__speg_match_gset_subject:n {#2} + \__speg_match_gset_index:n {1} + #1 {#2} + \__speg_match_if_sub_success:TF + { \__speg_match_gset_true: } { \__speg_match_gset_false: } + \__speg_tracing_match_stop:n { And } + \__speg_match_gdecr_level: + } +\cs_generate_variant:Nn \speg_and:nn {nV} +\cs_set_eq:NN \SpegAnd \speg_and:nn + +%% #1 and #2: pattern and repeat number from "patt ^ {n}"; #3: subject string. +\cs_new_protected:Npn \speg_repeat:nnn #1 #2 #3 + { + \__speg_match_gincr_level: + \__speg_tracing_match_start:n { Repeat {#2} {#3} } + \__speg_match_gset_index:n {1} + \__speg_match_gset_subject:n {#3} + \int_compare:nNnTF {#2} < {0} + %% at most -#2 occurrences + { + \__speg_match_gset_true: + \int_step_inline:nn {-#2} + { + \__speg_match_apply_subject:n {#1} + \__speg_match_if_sub_success:TF + { + \__speg_match_gset_eq_subject: + \__speg_match_update_index: + } + { \prg_break: } + } + } + %% at least #2 occurrences + { + \__speg_match_gset_true: + \int_step_inline:nn {#2} + { + \__speg_match_apply_subject:n {#1} + \__speg_match_if_sub_success:TF + { + \__speg_match_gset_eq_subject: + \__speg_match_update_index: + } + { + \__speg_match_gset_false: + \prg_break: + } + } + \__speg_match_if_success:T + { + \bool_do_while:nn { \__speg_match_if_sub_success_p: } + { + \__speg_match_apply_subject:n {#1} + \__speg_match_if_sub_success:T + { + \__speg_match_gset_eq_subject: + \__speg_match_update_index: + } + } + } + } + \__speg_tracing_match_stop:n { Repeat } + \__speg_match_gdecr_level: + } +\cs_generate_variant:Nn \speg_repeat:nnn {nnV} +\cs_set_eq:NN \SpegRepeat \speg_repeat:nnn + +%% #1: string to match; #2: subject string. +\cs_new_protected:Npn \speg_p:nn #1 #2 + { + \__speg_match_gincr_level: + \__speg_tracing_match_start:n { P {#1} {#2} } + \__speg_match_gset_true: + \__speg_match_gset_index:n {1} + \__speg_match_gset_subject:n {#2} + \str_map_inline:nn {#1} + { + \__speg_match_gpop_subject:N \l_tmpa_str + \str_if_eq:VnTF \l_tmpa_str {##1} + { + \__speg_match_gincr_index: + } + { + \__speg_match_gset_false: + \str_map_break: + } + } + \__speg_tracing_match_stop:n { P } + \__speg_match_gdecr_level: + } +\cs_generate_variant:Nn \speg_p:nn {nV} +\cs_set_eq:NN \SpegP \speg_p:nn + +%% #1: number of characters; #2: subject string. +\cs_new_protected:Npn \speg_q:nn #1 #2 + { + \__speg_match_gincr_level: + \__speg_tracing_match_start:n { Q {#1} {#2} } + \int_compare:nNnTF { \str_count:n {#2} } < {#1} + { + \__speg_match_gset_false: + \__speg_match_gset_index:n {1} + } + { + \__speg_match_gset_true: + \__speg_match_gset_subject:e { \str_range:nnn {#2} {1 + #1} {-1} } + \__speg_match_gset_index:e { \int_eval:n {1 + #1} } + } + \__speg_tracing_match_stop:n { Q } + \__speg_match_gdecr_level: + } +\cs_generate_variant:Nn \speg_q:nn {nV} +\cs_set_eq:NN \SpegQ \speg_q:nn + +%% #1: character ranges; #2: subject string. +\cs_new_protected:Npn \speg_r:nn #1 #2 + { + \__speg_match_gincr_level: + \__speg_tracing_match_start:n { R {#1} {#2} } + \__speg_match_gset_false: + \__speg_match_gset_index:n {1} + \str_if_empty:nF {#2} + { + %% \l_tmpa_str is empty when #2 is empty + \__speg_match_gset_subject:n {#2} + \__speg_match_gpop_subject:N \l_tmpa_str + \__speg_r_aux:nn #1 {} {} \prg_break_point: + } + \__speg_tracing_match_stop:n { R } + \__speg_match_gdecr_level: + } +\cs_generate_variant:Nn \speg_r:nn {nV} +\cs_set_eq:NN \SpegR \speg_r:nn + +\cs_new_protected:Npn \__speg_r_aux:nn #1 #2 + { + \tl_if_empty:nTF {#2} + { \prg_break: } + { + \bool_lazy_or:nnTF + { \int_compare_p:eNn { `\l_tmpa_str } < { `#1 } } + { \int_compare_p:eNn { `\l_tmpa_str } > { `#2 } } + { \__speg_r_aux:nn } + { + \__speg_match_gset_true: + \__speg_match_gset_index:n {2} + \prg_break: + } + } + } + +%% #1: character set; #2: subject string. +\cs_new_protected:Npn \speg_s:nn #1 #2 + { + \__speg_match_gincr_level: + \__speg_tracing_match_start:n { S {#1} {#2} } + \tl_if_empty:nTF {#2} + { + \__speg_match_gset_false: + \__speg_match_gset_index:n {1} + } + { + \__speg_match_gset_subject:n {#2} + \__speg_match_gpop_subject:N \l_tmpa_str + \str_if_in:nVTF {#1} \l_tmpa_str + { + \__speg_match_gset_true: + \__speg_match_gset_index:n {2} + } + { + \__speg_match_gset_false: + \__speg_match_gset_index:n {1} + } + } + \__speg_tracing_match_stop:n { S } + \__speg_match_gdecr_level: + } +\cs_generate_variant:Nn \speg_s:nn {nV} +\cs_set_eq:NN \SpegS \speg_s:nn + +\int_new:N \l__speg_pos_int + +%% \SpegC: simple capture. #1: pattern; #2: subject string. +\cs_new_protected:Npn \speg_c:nn #1 #2 + { + \__speg_match_gincr_level: + \__speg_match_gset_subject:n {#2} + \__speg_tracing_match_start:n { C {#2} } + #1 {#2} + \__speg_match_if_sub_success:TF + { + \__speg_match_gset_true: + \__speg_match_gset_eq_index: + \__speg_match_gset_eq_subject: + \__speg_match_get_capture:nN {#2} \l_tmpa_str + \seq_gput_right:NV \g__speg_capture_seq \l_tmpa_str + %\seq_log:N \g__speg_capture_seq + } + { + \__speg_match_gset_false: + \__speg_match_gset_index:n {1} + } + \__speg_tracing_match_stop:n { C } + \__speg_match_gdecr_level: + } +\cs_set_eq:NN \SpegC \speg_c:nn + +%% \SpegCp: position capture. +\cs_new_protected:Npn \speg_cp: + { + \__speg_tracing_match_start:n { Cp } + \__speg_match_get_position:N \l__speg_pos_int + \seq_gput_right:Ne \g__speg_capture_seq { \int_use:N \l__speg_pos_int } + \__speg_tracing_match_stop:n { Cp } + } +\cs_set_eq:NN \SpegCp \speg_cp: + +%%% ------------------------------------------------------- +%%> \section{Trace speg functions} +%%% ------------------------------------------------------- + +\NewDocumentCommand \SetSpegTracing { m } + { + \keys_set:nn { speg/tracing/main } {#1} + } + +\bool_new:N \g__speg_tracing_set_bool +\bool_new:N \g__speg_tracing_match_bool + +\keys_define:nn { speg/tracing/main } + { + +set .code:n = \bool_gset_true:N \g__speg_tracing_set_bool, + -set .code:n = \bool_gset_false:N \g__speg_tracing_set_bool, + +match .code:n = \bool_gset_true:N \g__speg_tracing_match_bool, + -match .code:n = \bool_gset_false:N \g__speg_tracing_match_bool, + all .code:n = \__speg_enable_all_tracings:, + none .code:n = \__speg_disable_all_tracings:, + } + +\cs_new_protected_nopar:Npn \__speg_enable_all_tracings: + { + \bool_gset_true:N \g__speg_tracing_set_bool + \bool_gset_true:N \g__speg_tracing_match_bool + } + +\cs_new_protected_nopar:Npn \__speg_disable_all_tracings: + { + \bool_gset_false:N \g__speg_tracing_set_bool + \bool_gset_false:N \g__speg_tracing_match_bool + } + +\cs_new_protected:Npn \__speg_tracing:nn #1 #2 + { + \bool_if:cT { g__speg_tracing_ #1 _bool } {#2} + } + +\cs_new_protected:Npn \__speg_tracing_match_start:n #1 + { + \__speg_tracing:nn {match} + { + \iow_log:e + { + \prg_replicate:nn { (\g__speg_match_level_int - 1) * 2 } {~} + Start#1 + } + } + } + +\cs_new_protected:Npn \__speg_tracing_match_stop:n #1 + { + \__speg_tracing:nn {match} + { + \iow_log:e + { + \prg_replicate:nn { (\g__speg_match_level_int - 1) * 2 } {~} + Stop#1: \__speg_match_use_status:, \__speg_match_use_index: + %, {\__speg_match_use_subject:} + } + } + } |