Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- \ExplSyntaxOn
- % Step 1: reading the file
- \tl_new:N \l__diaa_tmpa_tl
- \ior_new:N \l__diaa_csv_ior
- \bool_new:N \l__diaa_csv_str_bool
- \bool_new:N \l__diaa_empty_item_bool
- \seq_new:N \l__diaa_csv_tmp_seq
- % str mode (bool/star), key column, label, value columns, file
- \NewDocumentCommand \ReadCSV { s O{1} m O{} m }
- {
- \IfBooleanTF {#1}
- { \bool_set_true:N \l__diaa_csv_str_bool }
- { \bool_set_false:N \l__diaa_csv_str_bool }
- \diaa_csv_read:nnnn {#3} {#2} {#4} {#5}
- }
- % label, key column, value columns, file
- \cs_new_protected:Npn \diaa_csv_read:nnnn #1 #2 #3 #4
- {
- \tl_if_blank:nTF {#3} % Detect number of columns and use 2 to last
- {
- \ior_open:NnTF \l__diaa_csv_ior {#4}
- {
- \bool_if:NTF \l__diaa_csv_str_bool
- { \ior_str_get:NN }
- { \ior_get:NN }
- \l__diaa_csv_ior \l__diaa_tmpa_tl
- \ior_close:N \l__diaa_csv_ior
- \seq_set_split:NnV \l_tmpa_seq { , } \l__diaa_tmpa_tl
- \seq_clear:N \l__diaa_csv_tmp_seq
- \int_step_inline:nnn { 2 } { \seq_count:N \l_tmpa_seq }
- { \seq_put_right:Nn \l__diaa_csv_tmp_seq {##1} }
- }
- { \msg_expandable_error:nnn { diaa } { file-not-found } {#4} }
- }
- { \seq_set_split:Nnn \l__diaa_csv_tmp_seq { , } {#3} } % explicit columns
- \ior_open:NnTF \l__diaa_csv_ior {#4}
- {
- \prop_new:c { g__diaa_csv_#1_prop }
- \__diaa_csv_read:nn {#1} {#2}
- \ior_close:N \l__diaa_csv_ior
- }
- { \msg_expandable_error:nnn { diaa } { file-not-found } {#4} }
- }
- \msg_new:nnn { diaa } { file-not-found }
- { File~`#1'~not~found. }
- \cs_generate_variant:Nn \prop_put:Nnn { cxV }
- % label, key column
- \cs_new_protected:Npn \__diaa_csv_read:nn #1 #2
- {
- \bool_if:NTF \l__diaa_csv_str_bool
- { \ior_str_map_inline:Nn }
- { \ior_map_inline:Nn }
- \l__diaa_csv_ior
- {
- \seq_set_split:Nnn \l_tmpa_seq { , } {##1} % split one CSV row
- \tl_clear:N \l__diaa_tmpa_tl
- \seq_map_inline:Nn \l__diaa_csv_tmp_seq
- {
- \tl_put_right:Nx \l__diaa_tmpa_tl { { \seq_item:Nn \l_tmpa_seq {####1} } }
- }
- \prop_put:cxV { g__diaa_csv_#1_prop }
- { \seq_item:Nn \l_tmpa_seq {#2} }
- \l__diaa_tmpa_tl
- }
- }
- %%%%%%%%%%%%%%%%%%%%%%%%
- % https://tex.stackexchange.com/a/634894/2288
- \keys_define:nn { diaa / getPolyFromRow }
- {
- global-assignment .bool_set:N = \l__diaa_gpfr_global_assignment_bool,
- global-assignment .default:n = true,
- global-assignment .initial:n = false,
- variable .tl_set:N = \l__diaa_gpfr_variable_name_tl,
- variable .value_required:n = true,
- variable .initial:n = X,
- typographical-variant .str_set:N = \l__diaa_gpfr_typographical_variant_str,
- typographical-variant .value_required:n = true,
- typographical-variant .initial:n = default,
- }
- % \getPolyFromRow will temporarily store the result in this variable. This
- % allows us not to lose the result when the group started for \keys_set:nn
- % ends (if the user chose to perform a local assignment, this must be done
- % after closing that group).
- \tl_new:N \g__diaa_gpfr_result_tl
- % Options, macro for result, key, datafile label
- \NewDocumentCommand \getPolyFromRow { O{} m m m }
- {
- \group_begin:
- \keys_set:nn { diaa / getPolyFromRow } {#1}
- % Globally define _gfunc function aliases that perform global or local
- % assignments depending on \l__diaa_gpfr_global_assignment_bool. They
- % will be used *after* we close the current group.
- \bool_set_true:N \l__diaa_gpfr_dtfa_global_aliases_bool
- \__diaa_gpfr_define_tl_func_aliases:
- % Store the result in \g__diaa_gpfr_result_tl for now.
- \bool_set_true:N \l__diaa_gpfr_global_assignment_bool
- \diaa_get_poly_from_row:Nnn \g__diaa_gpfr_result_tl {#3} {#4}
- \group_end:
- % Use the globally-defined aliases to perform the user-chosen (local or
- % global) kind of assignment.
- \__diaa_clear_gfunc:N #2 % make sure the tl var #2 is defined
- \__diaa_set_eq_gfunc:NN #2 \g__diaa_gpfr_result_tl % set it
- }
- % True to globally define the aliases and give them _gfunc names rather than
- % _func
- \bool_new:N \l__diaa_gpfr_dtfa_global_aliases_bool
- \cs_new_protected:Npn \__diaa_gpfr_define_tl_func_aliases:
- {
- \bool_if:NTF \l__diaa_gpfr_global_assignment_bool
- {
- \__diaa_gpfr_define_alias:nnN { clear } { N } \tl_gclear_new:N
- \__diaa_gpfr_define_alias:nnN { set_eq } { NN } \tl_gset_eq:NN
- \__diaa_gpfr_define_alias:nnN { put_right } { Nn } \tl_gput_right:Nn
- }
- {
- \__diaa_gpfr_define_alias:nnN { clear } { N } \tl_clear_new:N
- \__diaa_gpfr_define_alias:nnN { set_eq } { NN } \tl_set_eq:NN
- \__diaa_gpfr_define_alias:nnN { put_right } { Nn } \tl_put_right:Nn
- }
- }
- % Locally or globally define an alias for a function. The alias is defined
- % globally with a gfunc name if \l__diaa_gpfr_dtfa_global_aliases_bool is true.
- %
- % #1: stem such as “clear”, “put_right”, etc.
- % #2: signature of the alias (e.g., “Nn”)
- \cs_new_protected:Npn \__diaa_gpfr_define_alias:nnN #1#2
- {
- \bool_if:NTF \l__diaa_gpfr_dtfa_global_aliases_bool
- { \cs_gset_eq:cN }
- { \cs_set_eq:cN }
- {
- __diaa_#1_
- \bool_if:NT \l__diaa_gpfr_dtfa_global_aliases_bool { g }
- func:#2
- }
- }
- \int_new:N \l__diaa_gpfr_degree_int
- \tl_new:N \l__dia_gpfr_row_values_tl
- \cs_generate_variant:Nn \__diaa_put_right_func:Nn { Nx }
- \cs_generate_variant:Nn \__diaa_get_poly_from_row_append_monomial:Nnn { NnV }
- % Macro for result, key, datafile label
- \cs_new_protected:Npn \diaa_get_poly_from_row:Nnn #1#2#3
- {
- % Locally define function aliases that perform global or local assignments
- % depending on \l__diaa_gpfr_global_assignment_bool: \__diaa_clear_func:Nn,
- % \__diaa_put_right_func:Nn, etc.
- \bool_set_false:N \l__diaa_gpfr_dtfa_global_aliases_bool
- \__diaa_gpfr_define_tl_func_aliases:
- % Retrieve the coefficients
- \prop_get:cnN { g__diaa_csv_#3_prop } {#2} \l__dia_gpfr_row_values_tl
- % Let's put (1 + degree) for now in this int variable.
- \int_zero:N \l__diaa_gpfr_degree_int
- \tl_map_inline:Nn \l__dia_gpfr_row_values_tl
- {
- \tl_if_empty:nT {##1} { \tl_map_break: }
- \int_incr:N \l__diaa_gpfr_degree_int
- }
- \__diaa_clear_func:N #1 % initialize #1 as a tl var if necessary
- \bool_set_false:N \l_tmpa_bool % true: add + operator if next coeff is > 0
- \tl_map_inline:Nn \l__dia_gpfr_row_values_tl % loop over the coefficients
- {
- % Degree of the monomial we're about to output
- \int_decr:N \l__diaa_gpfr_degree_int
- % Early termination condition if the row is not full of coefficients
- \int_compare:nNnT { \l__diaa_gpfr_degree_int } < { 0 }
- { \tl_map_break: }
- \fp_compare:nNnF {##1} = { 0 }
- {
- % Insert a + operator if necessary
- \bool_if:NTF \l_tmpa_bool
- {
- \fp_compare:nNnT {##1} > { 0 }
- { \__diaa_put_right_func:Nn #1 { + } }
- }
- { \bool_set_true:N \l_tmpa_bool }
- % Insert the monomial
- \__diaa_get_poly_from_row_append_monomial:NnV #1 {##1}
- \l__diaa_gpfr_variable_name_tl
- }
- }
- }
- \msg_new:nnn { diaa } { gpfr-unknown-typo-variant }
- { Unknown~typographical~variant~for~\token_to_str:N \getPolyFromRow :~`#1'. }
- \cs_generate_variant:Nn \str_case_e:nnF { V }
- \cs_generate_variant:Nn \msg_error:nnn { nnV }
- % Macro, coefficient, variable name
- \cs_new_protected:Npn \__diaa_get_poly_from_row_append_monomial:Nnn #1#2#3
- {
- \str_case_e:VnF \l__diaa_gpfr_typographical_variant_str
- {
- { default } { \tl_set:Nn \l_tmpa_tl { #2 \times #3 ^ } }
- { with-braces } { \tl_set:Nn \l_tmpa_tl { #2 \times {#3} ^ } }
- }
- {
- \msg_error:nnV { diaa } { gpfr-unknown-typo-variant }
- \l__diaa_gpfr_typographical_variant_str
- }
- \__diaa_put_right_func:Nx #1
- {
- \int_case:nnF { \l__diaa_gpfr_degree_int } % depending on the degree...
- {
- { 0 } { \exp_not:n {#2} } % degree 0 → only the coefficient
- { 1 } { \exp_not:n { #2 \times #3 } }
- }
- { % Other degrees
- \exp_not:V \l_tmpa_tl % use the selected variant
- % Use braces in case the exponent has several digits
- { \int_use:N \l__diaa_gpfr_degree_int }
- }
- }
- }
- %%%%%%%%%%%%%%%%%%%%%
- % Step 2: getting the values
- % star → global assignment, macro or tl var, value column, key, label
- \NewDocumentCommand \getValue { s m O{1} m m }
- {
- \IfBooleanTF {#1} { \tl_gset:Nx } { \tl_set:Nx }
- #2 { \diaa_csv_item:nnn {#4} {#3} {#5} }
- \tl_if_empty:NT #2
- { \msg_warning:nnnnn { diaa } { item-empty } {#3} {#4} {#5} }
- }
- % key, value column, label
- \NewExpandableDocumentCommand \CSVItem { m O{1} m }
- { \diaa_csv_item:nnn {#1} {#2} {#3} }
- % Version with error if empty
- % \NewExpandableDocumentCommand \CSVItem { m O{1} m }
- % {
- % \exp_args:Nf \__diaa_check_empty_item:nnnn
- % { \diaa_csv_item:nnn {#1} {#2} {#3} } {#2} {#1} {#3}
- % }
- % \cs_new:Npn \__diaa_check_empty_item:nnnn #1 #2 #3 #4
- % {
- % \tl_if_empty:nTF {#1}
- % { \msg_expandable_error:nnnnn { diaa } { item-empty } {#2} {#3} {#4} }
- % {#1}
- % }
- \cs_generate_variant:Nn \tl_item:nn { f }
- % key, value column, label
- \cs_new:Npn \diaa_csv_item:nnn #1 #2 #3
- {
- \prop_if_exist:cTF { g__diaa_csv_#3_prop }
- {
- \prop_if_in:cnTF { g__diaa_csv_#3_prop } {#1}
- {
- \exp_args:NNf \__diaa_check_column_range:Nnn \use_i:nn
- { \prop_item:cn { g__diaa_csv_#3_prop } {#1} } {#2}
- }
- { \msg_expandable_error:nnnn { diaa } { key-undefined } {#1} {#3} }
- }
- { \msg_expandable_error:nnn { diaa } { csv-undefined } {#3} }
- }
- \cs_new:Npn \__diaa_check_column_range:Nnn #1 #2 #3
- {
- \bool_lazy_or:nnTF
- { \int_compare_p:nNn {#3} = { 0 } }
- { \int_compare_p:nNn { \tl_count:n {#2} } < { \int_abs:n {#3} } }
- { \msg_expandable_error:nnn { diaa } { out-of-range } {#3} }
- { \exp_args:Nf #1 { \tl_item:nn {#2} {#3} } {#3} }
- }
- \msg_new:nnn { diaa } { csv-undefined } { CSV~database~''#1''~is~undefined! }
- \msg_new:nnn { diaa } { key-undefined } {Parameter~''#1''~is~not~found~in~CSV~''#2''! }
- \msg_new:nnn { diaa } { out-of-range } { Column~#1~is~out~of~range! }
- %\msg_new:nnn { diaa } { item-empty }
- %{ Column~#1~of~''#2''~in~CSV~''#3''~is~empty! }
- \msg_new:nnn { diaa } { item-empty }
- { Empty~column~#1~of~''#2''~in~CSV~''#3''! }%Item~#1~from~`#2'~in~CSV~`#3'~is~empty!
- \msg_new:nnn { diaa } { empty-row-item }
- { Empty~item~#1~\msg_line_context:! }
- \msg_new:nnn { diaa } { row-empty }
- { Empty~parameter~''#1''~in~CSV~''#2''! }
- \cs_new:Npn \__diaa_check_empty:nn #1 #2
- {
- \tl_if_empty:nT {#1}
- { \msg_warning:nnn { diaa } { empty-row-item } {#2} }
- #1
- }
- % star → global assignment, macro, key, label
- \NewDocumentCommand \getRow { s m m m }
- {
- \prop_if_exist:cTF { g__diaa_csv_#4_prop }
- {
- \prop_get:cnNTF { g__diaa_csv_#4_prop } {#3} \l__diaa_tmpa_tl
- {
- \bool_set_true:N \l__diaa_empty_item_bool
- \tl_map_inline:Nn \l__diaa_tmpa_tl
- {
- \tl_if_empty:nF {##1}
- { \bool_set_false:N \l__diaa_empty_item_bool }
- }
- \bool_if:NT \l__diaa_empty_item_bool
- { \msg_warning:nnnn { diaa } { row-empty } {#3} {#4} }
- \IfBooleanTF {#1} { \cs_gset_nopar:Npx } { \cs_set_nopar:Npx } #2 [ ##1 ]
- {
- \exp_not:N \str_if_eq:nnTF {##1} { non-empty }
- {
- \exp_not:N \__diaa_nb_nonempty_items_in_row:nw { 0 }
- \exp_not:V \l__diaa_tmpa_tl
- \exp_not:n { \q_recursion_tail \q_recursion_stop }
- }
- {
- \exp_not:N \__diaa_check_column_range:Nnn
- \exp_not:N \__diaa_check_empty:nn
- { \exp_not:V \l__diaa_tmpa_tl } {##1}
- }
- }
- }
- { \msg_expandable_error:nnnn { diaa } { key-undefined } {#3} {#4} }
- }
- { \msg_expandable_error:nnn { diaa } { csv-undefined } {#4} }
- }
- \cs_new:Npn \__diaa_nb_nonempty_items_in_row:nw #1#2
- {
- \quark_if_recursion_tail_stop_do:nn {#2} { \int_eval:n {#1} }
- \tl_if_empty:nTF {#2}
- { \__diaa_nb_nonempty_items_in_row:nw {#1} }
- { \__diaa_nb_nonempty_items_in_row:nw { #1 + 1 } }
- }
- \ExplSyntaxOff
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement