% File: prettytok.sty % Copyright 2022 user202729 % % This work may be distributed and/or modified under the conditions of the % LaTeX Project Public License (LPPL), either version 1.3c of this license or % (at your option) any later version. The latest version of this license is in % the file: % % http://www.latex-project.org/lppl.txt % % This work has the LPPL maintenance status `maintained'. % % The Current Maintainer of this work is user202729. \RequirePackage{expl3} \ProvidesExplPackage{prettytok}{2022/05/28}{0.0.0}{Pretty-print token list} \@ifpackagelater{expl3}{2022/04/10} % https://github.com/latex3/latex3/blob/main/l3kernel/CHANGELOG.md#2022-04-10 { } { \cs_set_protected:Npn \tl_analysis_map_inline:Nn #1 { \exp_args:No \tl_analysis_map_inline:nn #1 } \cs_set_protected:Npn \tl_analysis_map_inline:nn #1 { \__tl_analysis:n {#1} \int_gincr:N \g__kernel_prg_map_int \exp_args:Nc \__tl_analysis_map:Nn { _tl_analysis_map_inline_ \int_use:N \g__kernel_prg_map_int :wNw } } \cs_set_protected:Npn \__tl_analysis_map:Nn #1#2 { \cs_gset_protected:Npn #1 ##1##2##3 {#2} \exp_after:wN \__tl_analysis_map:NwNw \exp_after:wN #1 \g__tl_analysis_result_tl \s__tl { ? \tl_map_break: } \s__tl \prg_break_point:Nn \tl_map_break: { \int_gdecr:N \g__kernel_prg_map_int } } \cs_set_protected:Npn \__tl_analysis_map:NwNw #1 #2 \s__tl #3 #4 \s__tl { \use_none:n #3 #1 {#2} {#4} {#3} \__tl_analysis_map:NwNw #1 } } \cs_generate_variant:Nn \EI_append:n {x} \cs_generate_variant:Nn \int_compare:nNnTF {VNnTF} \cs_generate_variant:Nn \tl_set:Nn {Nx} \cs_generate_variant:Nn \int_compare:nNnT {VNnT} \cs_generate_variant:Nn \cs_if_exist_use:NT {cT} % ======== define handler functions. % a few things it can do: % • \tl_build_gput_right:Nn to \_EIresult (content being put there must be wrapped in \unexpanded i.e. x-expand to the desired result \cs_set_protected:Npn \EI_append_raw:n #1 { \tl_build_gput_right:Nn \_EIresult {#1} } \cs_set_protected:Npn \EI_append:n #1 { \EI_append_raw:n {\exp_not:n {#1}} } % handler can call this to collect following tokens in the input stream as argument. % #1: the function to pass argument in. (arguments are passed as brace-hack-included) % #2: number of arguments \cs_set_protected:Npn \EI_collect_arg:Nn #1 #2 { \def \_EIcollecting_balanced {#2} \def \_EIbalance_level {0} \tl_build_gbegin:N \_EIbalanced_content \tl_build_gput_right:Nn \_EIbalanced_content {\noexpand #1} } \cs_set_protected:Npn \EIhandler_EIexpand { \EI_collect_arg:Nn \EI_append:x {1} } \cs_set_protected:Npn \EIhandler_EIexecute { \EI_collect_arg:Nn \use:n {1} } \cs_set_protected:Npn \execinsideprepare:n #1 { \tl_build_gbegin:N \_EIresult \def \_EIcollecting_balanced {0} \tl_analysis_map_inline:nn {#1} { % reminder: ##1: token, ##2: char code (int), ##3: cat code (hex digit) \int_compare:VNnTF \_EIcollecting_balanced > 0 { % collecting content. \tl_build_gput_right:Nn \_EIbalanced_content {##1} \str_case:nn {##3} { {1} { \tl_set:Nx \_EIbalance_level {\_EIbalance_level+1} } {2} { \tl_set:Nx \_EIbalance_level {\_EIbalance_level-1} } } % we check this every time instead of only after egroup tokens... % so that if there's exactly one token to be collected it's still correctly passed through \int_compare:nNnT {\_EIbalance_level} = {0} { % done, call handler function and revert to normal mode. \tl_set:Nx \_EIcollecting_balanced {\int_eval:n{\_EIcollecting_balanced-1}} \int_compare:VNnT \_EIcollecting_balanced = 0 { \tl_build_gend:N \_EIbalanced_content \tl_set:Nx \_EIbalanced_content {\_EIbalanced_content} %\pretty:V \_EIbalanced_content \_EIbalanced_content % ← the handler function is embedded in here %\pretty:n {done} } } } { \let \_EIprocessed \c_false_bool % check for \EIexecute etc. and handle accordingly. \int_compare:nNnT {##2} = {-1} { \cs_if_exist_use:cT { EIhandler_ \expandafter \cs_to_str:N ##1 } { \let \_EIprocessed \c_true_bool } } % if not, just append to result. \bool_if:NF \_EIprocessed { \tl_build_gput_right:Nn \_EIresult {##1} } } } \tl_build_gend:N \_EIresult \tl_set:Nx \_EIresult {\_EIresult} } \cs_set_protected:Npn \execinside:n #1 { \execinsideprepare:n {#1} \_EIresult } \let\execinside\execinside:n % normal-catcode alias \cs_set_protected:Npn \execinside_set:Nn #1 #2 { \execinsideprepare:n {#2} \let #1 \_EIresult } \let\execinsideSet\execinside_set:Nn \cs_set_protected:Npn \execinside_gset:Nn #1 #2 { \execinsideprepare:n {#2} \global \let #1 \_EIresult } \let\execinsideGset\execinside_gset:Nn \cs_generate_variant:Nn \exp_args:NN {Nc} \cs_generate_variant:Nn \EI_append:n {x} \cs_generate_variant:Nn \exp_args:Nn {No} \cs_generate_variant:Nn \EI_append:n {o} \cs_generate_variant:Nn \tl_put_right:Nn {Nx} \cs_generate_variant:Nn \use:nn {nV} \cs_generate_variant:Nn \execinside_set:Nn {NV} \def \_precattlappend_space #1 { \begingroup \lccode `\ =#1 \lowercase{ \def\_precattltmp{\endgroup \EI_append:n {~}} } \_precattltmp } \begingroup \global\def \_precattlappend_begin #1 { \begingroup \lccode `\{=#1 \lowercase{\endgroup \EI_append_raw:n { \iftrue { \else } \fi }} } \global\def \_precattlappend_end #1 { \begingroup \lccode `\}=#1 \lowercase{\endgroup \EI_append_raw:n { \iffalse { \else } \fi }} } \endgroup \def \_precattldo_special_cC #1 { \exp_args:Nc \EI_append:n {#1} } \def \_precattldo_special_cB #1 { \str_map_inline:nn {#1} { \_precattlappend_begin {`##1} } } \def \_precattldo_special_cE #1 { \str_map_inline:nn {#1} { \_precattlappend_end {`##1} } } \def \_precattldo_special_cM #1 { \str_map_inline:nn {#1} { \EI_append:x {\char_generate:nn {`##1} {3}} } } \def \_precattldo_special_cT #1 { \str_map_inline:nn {#1} { \EI_append:x {\char_generate:nn {`##1} {4}} } } \def \_precattldo_special_cP #1 { \str_map_inline:nn {#1} { \exp_args:No \EI_append:o {\char_generate:nn {`##1} {6}} } } \def \_precattldo_special_cU #1 { \str_map_inline:nn {#1} { \EI_append:x {\char_generate:nn {`##1} {7}} } } \def \_precattldo_special_cD #1 { \str_map_inline:nn {#1} { \EI_append:x {\char_generate:nn {`##1} {8}} } } \def \_precattldo_special_cS #1 { \str_map_inline:nn {#1} { \_precattlappend_space {`##1} } } \def \_precattldo_special_cL #1 { \str_map_inline:nn {#1} { \EI_append:x {\char_generate:nn {`##1} {11}} } } \def \_precattldo_special_cO #1 { \str_map_inline:nn {#1} { \EI_append:x {\char_generate:nn {`##1} {12}} } } \def \_precattldo_special_cA #1 { \str_map_inline:nn {#1} { \exp_args:No \EI_append:o {\char_generate:nn {`##1} {13}} } } \def \_precattldo_special #1 #2 { % given #1 e.g. \cO, #2 e.g. {abc}, append in OTHER catcode to EI. { \escapechar=-1 % let \_precattltmp be the concatenation of \string of all tokens within #2. \def \_precattltmp {} \tl_analysis_map_inline:nn {#2} { \tl_put_right:Nx \_precattltmp {\exp_after:wN \string ##1} } % then pass it to the specific handler. \exp_args:Nc \exp_args:NV {_precattldo_special_\string #1} \_precattltmp } } \def \_precattldo_special_cC_outer { \_precattldo_special\cC } \def \_precattldo_special_cB_outer { \_precattldo_special\cB } \def \_precattldo_special_cE_outer { \_precattldo_special\cE } \def \_precattldo_special_cM_outer { \_precattldo_special\cM } \def \_precattldo_special_cT_outer { \_precattldo_special\cT } \def \_precattldo_special_cP_outer { \_precattldo_special\cP } \def \_precattldo_special_cU_outer { \_precattldo_special\cU } \def \_precattldo_special_cD_outer { \_precattldo_special\cD } \def \_precattldo_special_cS_outer { \_precattldo_special\cS } \def \_precattldo_special_cL_outer { \_precattldo_special\cL } \def \_precattldo_special_cO_outer { \_precattldo_special\cO } \def \_precattldo_special_cA_outer { \_precattldo_special\cA } \def \_precattl_tmp_help #1 { \def \_precattl_do_frozen_relax { \EI_append:n {#1} } % #1 here will be substituted by the frozen relax } \expandafter \_precattl_tmp_help \ifnum0=0\fi \let \_precattl_tmp_help \undefined \def \precattl_prepare:n #1 { \begingroup \let \EIhandler_EIexpand \undefined \let \EIhandler_EIexecute \undefined \def \EIhandler_cC { \EI_collect_arg:Nn \_precattldo_special_cC_outer 1 } \def \EIhandler_cB { \EI_collect_arg:Nn \_precattldo_special_cB_outer 1 } \def \EIhandler_cE { \EI_collect_arg:Nn \_precattldo_special_cE_outer 1 } \def \EIhandler_cM { \EI_collect_arg:Nn \_precattldo_special_cM_outer 1 } \def \EIhandler_cT { \EI_collect_arg:Nn \_precattldo_special_cT_outer 1 } \def \EIhandler_cP { \EI_collect_arg:Nn \_precattldo_special_cP_outer 1 } \def \EIhandler_cU { \EI_collect_arg:Nn \_precattldo_special_cU_outer 1 } \def \EIhandler_cD { \EI_collect_arg:Nn \_precattldo_special_cD_outer 1 } \def \EIhandler_cS { \EI_collect_arg:Nn \_precattldo_special_cS_outer 1 } \def \EIhandler_cL { \EI_collect_arg:Nn \_precattldo_special_cL_outer 1 } \def \EIhandler_cO { \EI_collect_arg:Nn \_precattldo_special_cO_outer 1 } \def \EIhandler_cA { \EI_collect_arg:Nn \_precattldo_special_cA_outer 1 } \let \EIhandler_cFrozenRelax \_precattl_do_frozen_relax \execinside_gset:Nn \_precattlvalueg {#1} \endgroup \let \_precattlvalue \_precattlvalueg } \def \precattl_set:Nn #1 #2 { \precattl_prepare:n {#2} \tl_set_eq:NN #1 \_precattlvalue } \let\precattlSet\precattl_set:Nn \def \precattl_exec:n #1 { \precattl_prepare:n {#1} \_precattlvalue } \let\precattlExec\precattl_exec:n \RequirePackage{filecontentsdef} %\sys_if_engine_luatex:T{\directlua{require("prettytok")}} % usage: \pretty:n, etc. \cs_generate_variant:Nn \exp_args:NNn {NNx} \cs_generate_variant:Nn \iow_open:Nn {NV} \cs_generate_variant:Nn \exp_args:NNn {NNV} \cs_generate_variant:Nn \iow_now:Nn {Nx} \cs_generate_variant:Nn \tl_if_eq:nnF {xoF} \cs_generate_variant:Nn \use:N {c} \cs_generate_variant:Nn \str_map_inline:nn {xn} \cs_generate_variant:Nn \tl_build_put_right:Nn {Nx} \cs_generate_variant:Nn \iow_now:Nn {NV} \iow_new:N \__prettyh_file \exp_args:NNx \providecommand \prettyfilename {pretty-\jobname.html} \providecommand \prettyrefreshstrategy {4} \providecommand \prettyrefreshduration {1000} \cs_new_protected:Npn \pretty:n #1 { % ======== first write out the preamble if it isn't written. Only do once. Delete the preamble macro if it's already written. \cs_if_exist:NT \prettypreamble { \iow_open:NV \__prettyh_file \prettyfilename { \str_set:NV \prettypreamble \prettypreamble \precattl_exec:n { \str_replace_all:Nnn \prettypreamble {\cO\^^M} {\cO\^^J} } \iow_now:NV \__prettyh_file \prettypreamble \iow_now:Nx \__prettyh_file {set_refresh_strategy(\prettyrefreshstrategy,\prettyrefreshduration)} } \cs_undefine:N \prettypreamble } % ======== the actual content. Collect to one \write to put them on the same line (also maybe for efficiency???) \tl_build_begin:N \__prettyh_content \tl_build_put_right:Nn \__prettyh_content {print_tl(} \tl_analysis_map_inline:nn {#1} { % by documentation of \tl_analysis_map_inline:nn: #1=token, #2=char code, #3=catcode \int_compare:nNnTF {##2} = {-1} { % token is control sequence \tl_build_put_right:Nn \__prettyh_content {cs(} \tl_if_eq:xoF {\use:c {}} {##1} { \str_map_inline:xn {\exp_after:wN \cs_to_str:N ##1} { % side note, must use str here to preserve spaces \tl_build_put_right:Nx \__prettyh_content {\int_eval:n {`####1},} } } \tl_build_put_right:Nn \__prettyh_content {),} } { % token is not control sequence \tl_build_put_right:Nn \__prettyh_content {token(##2, "##3"),} } } \tl_build_put_right:Nn \__prettyh_content {)//