%% dbshow.sty %% Copyright 2022 Li Changkai % % This work 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. % % This work has the LPPL maintenance status `maintained'. % % The Current Maintainer of this work is Li Changkai. % % This work consists of the files dbshow.sty, dbshow-doc.tex. \def\myfileversion{v1.3} \def\myfiledate{2022/01/10} \RequirePackage{expl3, xparse} \ProvidesExplPackage {dbshow} {\myfiledate} {\myfileversion} {database to store and show data} \msg_new:nnn { dbshow } { non-existent-database } { Database~'#1'~does~not~exist~\msg_line_context:. } % #1 database \cs_new:Nn \dbshow_check_database:n { \prop_if_exist:cF { g__dbshow_type_map_#1 } { \msg_fatal:nnn { dbshow } { non-existent-database } { #1 } } } \msg_new:nnn { dbshow } { non-existent-attr } { Attribute~'#2'~of~database~'#1'~does~not~exist~\msg_line_context:. } % #1 database #2 attr \cs_new:Nn \dbshow_check_attr:nn { \prop_if_in:cnF { g__dbshow_type_map_#1 } { #2 } { \msg_fatal:nnnn { dbshow } { non-existent-attr } { #1 } { #2 } } } \msg_new:nnn { dbshow } { non-existent-style } { Style~'#1'~of~database~'#2'~does~not~exist~\msg_line_context:. } % #1 style #2 database \cs_new:Nn \dbshow_check_style:nn { \tl_if_exist:cF { g__dbshow_style_opts_#1_#2 } { \msg_warning:nnnn { dbshow } { non-existent-style } { #1 } { #2 } } } \msg_new:nnn { dbshow } { non-existent-cond } { Conditional~'#2'~of~database~'#1'~does~not~exist~\msg_line_context:. } % #1 database #2 cond \cs_new:Nn \dbshow_check_cond:nnn { \tl_if_exist:cF { g__dbshow_filter_attr_#1_#2 } { \msg_fatal:nnnn { dbshow } { non-existent-cond } { #1 } { #2 } } } \msg_new:nnn { dbshow } { non-existent-filter } { Filter~'#2'~of~database~'#1'~does~not~exist~and~is~ignored~\msg_line_context:. } \cs_generate_variant:Nn \msg_warning:nnnn { nnnx } % #1 database #2 filter \cs_new:Nn \dbshow_check_filter:nn { \seq_if_exist:cF { g__dbshow_filter_run_seq_#1_#2 } { \str_if_eq:eeF { #2 } { -none- } { \msg_warning:nnnx { dbshow } { non-existent-filter } { #1 } { #2 } } } } \msg_new:nnn { dbshow } { non-existent-type } { Type~'#1'~does~not~exist,~the~type~of~attribute~should~be~one~of~ \{date,~str,~tl,~clist,~int,~fp\}~\msg_line_context:. } % #1 type \cs_new:Nn \dbshow_check_type:n { \clist_if_in:NnF \dbshow_type_clist { #1 } { \msg_fatal:nnn { dbshow } { non-existent-type } { #1 } } } % #1 valid count #2 real count #3 content \msg_new:nnn { dbshow } { wrong-seperator } { option~'sep'~should~contain~#1~items~but~only~#2~items~was~given,~ sep~=~\{#3\}~\msg_line_context:. } % #1 valid count #2 real count #3 content \cs_new:Nn \dbshow_sep_error:nnn { \msg_error:nnnnn { dbshow } { wrong-seperator } { #1 } { #2 } { #3 } } \cs_generate_variant:Nn \dbshow_sep_error:nnn { xxx } \msg_new:nnn { dbshow } { unsupported-sort-type } { unsupported~sort~type:~'#1'~\msg_line_context:.~The~type~should~be~one~of~ \{str,~date,~int,~fp\}. } \msg_new:nnn { dbshow } { invalid-relation } { invalid~relation:~'#1'. } \clist_const:Nn \dbshow_type_clist { date, str, tl, clist, int, fp } \prop_const_from_keyval:Nn \dbshow_default_value { date = \dbtoday, str = , tl = , clist = , int = 0, fp = 0 } \cs_generate_variant:Nn \clist_use:nn { xx } \cs_generate_variant:Nn \clist_use:nnnn { xxxx } \cs_generate_variant:Nn \prop_get:NnN { cVN } \cs_generate_variant:Nn \regex_extract_all:nnN { nVN } \cs_generate_variant:Nn \regex_split:nnN { nVN } \prg_generate_conditional_variant:Nnn \str_compare:nNn { VNV } { TF } \prg_generate_conditional_variant:Nnn \int_compare:nNn { VNV } { TF } \prg_generate_conditional_variant:Nnn \fp_compare:nNn { VNV } { TF } \prg_generate_conditional_variant:Nnn \regex_match:nn { VV } { TF } \prg_generate_conditional_variant:Nnn \clist_if_in:Nn { Nx } { TF } % #1 database #2 attr #3 type #4 default value \cs_new:Npn \dbshow_process_default_value:w #1|#2=#3|#4\scan_stop { \dbshow_check_type:n { #3 } \prop_gput:cxx { g__dbshow_type_map_#1 } { #2 } { #3 } \prop_gput:cxx { g__dbshow_default_map_#1 } { #2 } { #4 } } \cs_new_protected:Nn \dbshow_process_type_map:n { \prop_gclear_new:c { g__dbshow_default_map_#1 } \prop_map_inline:cn { g__dbshow_type_map_#1 } { \str_if_in:nnTF { ##2 } { | } { \dbshow_process_default_value:w #1|##1=##2\scan_stop } { \prop_get:NnN \dbshow_default_value { ##2 } \l_tmp_default \dbshow_process_default_value:w #1|##1=##2|\l_tmp_default\scan_stop } } } % #1 database_name, #2 clist, attr=type \cs_new_protected:Nn \dbshow_database_new:nn { \int_gzero_new:c { g__dbshow_counter_#1 } \prop_gset_from_keyval:cn { g__dbshow_type_map_#1 } { #2 } } % #1 database_name, #2 clist \cs_new_protected:Nn \dbshow_database_new_append:nn { \int_gzero_new:c { g__dbshow_counter_#1 } \prop_if_exist:cF { g__dbshow_type_map_#1 } { \prop_new:c { g__dbshow_type_map_#1 } } \prop_gset_from_keyval:Nn \l_tmpa_prop { #2 } \prop_concat:ccc { g__dbshow_type_map_#1 } { g__dbshow_type_map_#1 } { l_tmpa_prop } } % #1 database_name, #2 database inherit from #3 clist \cs_new_protected:Nn \dbshow_database_new_inherit:nnn { \dbshow_check_database:n { #2 } \str_if_eq:nnTF { #1 } { #2 } { \dbshow_database_new_append:nn { #1 } { #3 } } { \int_gzero_new:c { g__dbshow_counter_#1 } \prop_gset_from_keyval:cn { g__dbshow_type_map_#1 } { #3 } \prop_concat:ccc { g__dbshow_type_map_#1 } { g__dbshow_type_map_#2 } { g__dbshow_type_map_#1 } } } % #1 database_name, #2 attr \cs_new:Nn \dbshow_get_type:nn { \prop_item:cn { g__dbshow_type_map_#1 } { #2 } } % #1 database_name \cs_new:Nn \dbshow_get_counter:n { \int_use:c { g__dbshow_counter_#1 } } % #1 database_name \cs_new:Nn \dbshow_step_counter:n { \int_gincr:c { g__dbshow_counter_#1 } } \NewDocumentCommand { \dbclear } { m } { \int_gzero:c { g__dbshow_counter_#1 } } % #1 database #2 attr #3 content \cs_new:Nn \dbshow_save_data:nnn { \dbshow_check_attr:nn { #1 } { #2 } \use:c { \str_case_e:nn { \prop_item:cn { g__dbshow_type_map_#1 } { #2 } } { { str } { str_clear_new:c } { tl } { tl_gclear_new:c } { clist } { clist_gclear_new:c } { int } { int_gzero_new:c } { fp } { fp_gzero_new:c } { date } { dbshow_date_gclear_new:n } } } { g__dbshow_data_#1_#2_\int_use:c { g__dbshow_counter_#1 } } \use:c { \str_case_e:nn { \prop_item:cn { g__dbshow_type_map_#1 } { #2 } } { { str } { str_gset:cn } { tl } { tl_gset:cn } { clist } { clist_gset:cn } { int } { int_gset:cn } { fp } { fp_gset:cn } { date } { dbshow_date_gset:nx } } } { g__dbshow_data_#1_#2_\int_use:c { g__dbshow_counter_#1 } } { #3 } } \cs_generate_variant:Nn \dbshow_save_data:nnn { nnx } % #1 before code tl #2 after code tl #3 item \cs_new:Nn \dbshow_clist_wrapper:NNn { \exp_not:n { { #1#3#2 }, } } % #1 clist #2 sep #3 before code tl #4 after code tl \cs_new:Nn \dbshow_clist_use:NNNN { \int_case:nnF { \clist_count:N #2 } { { 1 } { \clist_use:xx { \clist_map_tokens:Nn #1 { \dbshow_clist_wrapper:NNn #3 #4 } } { \clist_item:Nn #2 { 1 } } } { 3 } { \clist_use:xxxx { \clist_map_tokens:Nn #1 { \dbshow_clist_wrapper:NNn #3 #4 } } { \clist_item:Nn #2 { 1 } } { \clist_item:Nn #2 { 2 } } { \clist_item:Nn #2 { 3 } } } } { \dbshow_sep_error:xxx { 1~or~3 } { \clist_count:N #2 } { \clist_use:Nn #2 { ,~ } } } } \cs_generate_variant:Nn \dbshow_clist_use:NNNN { cccc } % #1 date #2 sep #3 zfill bool \cs_new:Nn \dbshow_date_use:nNN { \int_case:nnF { \clist_count:N #2 } { { 1 } { \bool_if:NTF { #3 } { \dbshow_date_use_zfill:nf } { \dbshow_date_use:nf } { #1 } { \clist_item:Nn #2 { 1 } } } { 4 } { \bool_if:NTF { #3 } { \dbshow_date_use_zfill:nffff } { \dbshow_date_use:nffff } { #1 } { \clist_item:Nn #2 { 1 } } { \clist_item:Nn #2 { 2 } } { \clist_item:Nn #2 { 3 } } { \clist_item:Nn #2 { 4 } } } } { \dbshow_sep_error:xxx { 1~or~4 } { \clist_count:N #2 } { \clist_use:Nn #2 { ,~ } } } } \cs_generate_variant:Nn \dbshow_date_use:nNN { ncc } % #1 database #2 attr #3 index #4 style \cs_new:Nn \dbshow_use_data:nnnn { \str_case_e:nn { \prop_item:cn { g__dbshow_type_map_#1 } { #2 } } { { str } { \str_use:c { g__dbshow_data_#1_#2_#3 } } { tl } { \tl_use:c { g__dbshow_data_#1_#2_#3 } } { int } { \int_use:c { g__dbshow_data_#1_#2_#3 } } { fp } { \fp_use:c { g__dbshow_data_#1_#2_#3 } } { clist } { \dbshow_clist_use:cccc { g__dbshow_data_#1_#2_#3 } { g__dbshow_style_attr_sep_#4_#1_#2 } { g__dbshow_style_attr_item_before_#4_#1_#2 } { g__dbshow_style_attr_item_after_#4_#1_#2 } } { date } { \exp_args:Nnx \tl_use:c { g__dbshow_style_attr_wrapper_#4_#1_#2 } { \dbshow_date_use:ncc { g__dbshow_data_#1_#2_#3 } { g__dbshow_style_attr_sep_#4_#1_#2 } { g__dbshow_style_attr_zfill_#4_#1_#2 } } } } } % #1 database \cs_new_protected:Nn \dbshow_set_database_keys:n { \prop_map_inline:cn { g__dbshow_type_map_#1 } { \keys_define:nn { dbshow/database/#1 } { ##1 .code:n = \dbshow_save_data:nnn { #1 } { ##1 } { ####1 }, } } } % #1 append or not #2 inherit #3 database #4 attrs \NewDocumentCommand { \dbNewDatabase } { s o m m } { \IfNoValueTF { #2 } { \IfBooleanTF { #1 } { \dbshow_database_new_append:nn { #3 } { #4 } } { \dbshow_database_new:nn { #3 } { #4 } } } { \dbshow_database_new_inherit:nnn { #3 } { #2 } { #4 } } \dbshow_database_new_append:nn { #3 } { id=int } \dbshow_process_type_map:n { #3 } \dbshow_set_database_keys:n { #3 } \dbNewStyle{default}{#3}{} } % #1 database #2 attr-content map \NewDocumentEnvironment { dbitem } { m O{} +b } { \dbshow_check_database:n { #1 } \dbshow_step_counter:n { #1 } \cs_set:Nn \dbshow_set_default:nn { \dbshow_save_data:nnx { #1 } { ##1 } { \prop_item:cn { g__dbshow_default_map_#1 } { ##1 } } } \prop_map_function:cN { g__dbshow_type_map_#1 } \dbshow_set_default:nn \dbshow_save_data:nnx { #1 } { id } { \dbshow_get_counter:n { #1 } } \keys_set:nn { dbshow/database/#1 } { #2 } % ##1 exp or not ##2 attr ##3 content \NewDocumentCommand { \dbsave } { s m m } { \IfBooleanTF { ##1 } { \dbshow_save_data:nnn { #1 } { ##2 } { \exp_not:n { ##3 } } } { \dbshow_save_data:nnn { #1 } { ##2 } { ##3 } } } } { #3 } \cs_new:Nn \dbshow_if_int: { \int_compare:nTF { \l_dbshow_rela } { \dbshow_set_true: } { \dbshow_set_false: } } \cs_new:Nn \dbshow_if_fp: { \fp_compare:nTF { \l_dbshow_rela } { \dbshow_set_true: } { \dbshow_set_false: } } % star: all in % no star: one in \cs_new:Nn \dbshow_if_clist: { \bool_if:NTF \l_star_bool { % and \dbshow_set_true: \exp_args:Nx \clist_map_inline:nn { \l_dbshow_rela } { \clist_if_in:NnF \dbval { ##1 } { \dbshow_set_false: \clist_map_break: } } } { % or \dbshow_set_false: \exp_args:Nx \clist_map_inline:nn { \l_dbshow_rela } { \clist_if_in:NnT \dbval { ##1 } { \dbshow_set_true: \clist_map_break: } } } } % star: match full % no star: match part \cs_new:Nn \dbshow_if_str: { \bool_if:NT \l_star_bool { \tl_put_left:Nn \l_dbshow_rela { \A } \tl_put_right:Nn \l_dbshow_rela { \Z } } \regex_match:VVTF \l_dbshow_rela \dbval { \dbshow_set_true: } { \dbshow_set_false: } } \cs_set_eq:NN \dbshow_if_tl: \dbshow_if_str: \cs_new:Npn \dbshow_parse_date:w #1|#2\dbshow_stop { \clist_set_eq:Nc \l_point_clist { g__review_points_#1 } \tl_set:Nn \l_date_tl { #2 } } \cs_new_protected:Nn \dbshow_if_date: { \int_zero_new:N \l__dbshow_if_tmpa_int \int_zero_new:N \l__dbshow_if_tmpb_int \int_zero_new:N \l__dbshow_if_diff_int \bool_if:NTF \l_star_bool { % review points \exp_after:wN \dbshow_parse_date:w \l_dbshow_rela\dbshow_stop \dbshow_date_clear_new:n { tmp_day1 } \dbshow_date_clear_new:n { tmp_day2 } \dbshow_date_set:nx { tmp_day1 } { \l_date_tl } \dbshow_date_set:nx { tmp_day2 } { \dbval } \dbshow_date_sub:nnN { tmp_day1 } { tmp_day2 } \l__dbshow_if_diff_int \clist_if_in:NxTF \l_point_clist { \int_use:N \l__dbshow_if_diff_int } { \dbshow_set_true: } { \dbshow_set_false: } } { % rela \tl_set:Nx \l_dbshow_rela { \l_dbshow_rela } \regex_extract_all:nVN { \d{4}/\d+/\d+ } \l_dbshow_rela \l_date_seq \regex_split:nVN { \d{4}/\d+/\d+ } \l_dbshow_rela \l_other_seq \tl_clear:N \l_dbshow_rela \int_set:Nn \l__dbshow_if_tmpa_int { \seq_count:N \l_date_seq } \int_step_inline:nn { \l__dbshow_if_tmpa_int } { \tl_put_right:Nx \l_dbshow_rela { \seq_item:Nn \l_other_seq { ##1 } } \dbshow_date_clear_new:n { rela-tmp } \dbshow_date_set:nx { rela-tmp } { \seq_item:Nn \l_date_seq { ##1 } } \dbshow_date_to_int:nN { rela-tmp } \l__dbshow_if_tmpb_int \tl_put_right:Nx \l_dbshow_rela { \int_use:N \l__dbshow_if_tmpb_int } } \tl_put_right:Nx \l_dbshow_rela { \seq_item:Nn \l_other_seq { \l__dbshow_if_tmpa_int + 1 } } \int_compare:nTF { \l_dbshow_rela } { \dbshow_set_true: } { \dbshow_set_false: } } } % #1 database name #2 cond name #3 index \cs_set:Nn \dbshow_if:nnn { \tl_set_eq:Nc \l_dbshow_attr { g__dbshow_filter_attr_#1_#2 } \tl_set_eq:Nc \l_dbshow_rela { g__dbshow_filter_rela_#1_#2 } \prop_get:cVN { g__dbshow_type_map_#1 } \l_dbshow_attr \l_tmp_type \cs_set_eq:Nc \dbval { g__dbshow_data_#1_\l_dbshow_attr _#3 } \cs_set:Nn \dbshow_set_true: { \bool_gset_true:c { g__dbshow_filter_bool_#1_#2 } } \cs_set:Nn \dbshow_set_false: { \bool_gset_false:c { g__dbshow_filter_bool_#1_#2 } } \bool_set_eq:Nc \l_star_bool { g__dbshow_cond_star_#1_#2 } \use:c { dbshow_if_\l_tmp_type : } } % 将cond->attr, cond->rela, cond->bool关联起来 % #1 database name #2 cond name #3 attr #4 rela #5 star \cs_new_protected:Nn \dbshow_new_conditional:nnnnn { \tl_gset:cn { g__dbshow_filter_attr_#1_#2 } { #3 } \tl_gset:cn { g__dbshow_filter_rela_#1_#2 } { #4 } \bool_if_exist:cF { g__dbshow_filter_bool_#1_#2 } { \bool_new:c { g__dbshow_filter_bool_#1_#2 } } \bool_if_exist:cF { g__dbshow_cond_star_#1_#2 } { \bool_new:c { g__dbshow_cond_star_#1_#2 } } \IfBooleanTF { #5 } { \bool_gset_true:c { g__dbshow_cond_star_#1_#2 } } { \bool_gset_false:c { g__dbshow_cond_star_#1_#2 } } \cs_gset:cn { g__dbshow_filter_cs_#1_#2:n } { \dbshow_if:nnn { #1 } { #2 } { ##1 } } \seq_gput_right:cn { g__dbshow_cond_list_#1 } { #2 } } % 将组合条件cond1, cond2, ..., 替换成cond1->bool, cond2->bool, ... % #1 database name #2 filter name #3 filters \cs_new_protected:Nn \dbshow_combine_conditional:nnn { \tl_gset_eq:cN { g__dbshow_filter_bool_tl_#1_#2 } \c_true_bool \seq_gclear_new:c { g__dbshow_filter_run_seq_#1_#2 } \regex_extract_all:nnN { \w+ } { #3 } \l_tmpa_seq \seq_map_inline:Nn \l_tmpa_seq { % ##1 cond name \seq_if_in:cnT { g__dbshow_cond_list_#1 } { ##1 } { \seq_gput_right:cn { g__dbshow_filter_run_seq_#1_#2 } { g__dbshow_filter_cs_#1_##1:n } } } \tl_set:Nn \l_tmpa_tl { #3 } \regex_replace_all:nnN { \w+ } { \c{ g__dbshow_filter_bool_#1_\0 } } \l_tmpa_tl \tl_gset_eq:cN { g__dbshow_filter_bool_tl_#1_#2 } \l_tmpa_tl } % #1 database #2 content \NewDocumentEnvironment { dbFilters } { m +b } { \seq_gclear_new:c { g__dbshow_cond_list_#1 } % #1 star #2 cond name #3 attr #4 rela \DeclareDocumentCommand { \dbNewConditional } { s m m m } { \dbshow_new_conditional:nnnnn { #1 } { ##2 } { ##3 } { ##4 } { ##1 } } % ##1 filter name ##2 filters ##3 info \DeclareDocumentCommand { \dbCombineConditionals } { m m O{} } { \tl_gset:cn { g__dbshow_filter_info_#1_##1 } { ##3 } \dbshow_combine_conditional:nnn { #1 } { ##1 } { ##2 } } #2 } { } % #1 name #2 points \NewDocumentCommand { \dbNewReviewPoints } { m m } { \clist_set:cn { g__review_points_#1 } { #2 } } \cs_new:Nn \dbshow_identity:n { #1 } % #1 style name #2 database name #3 attr \cs_new_protected:Nn \dbshow_new_attr_style:nnn { \dbshow_check_attr:nn { #2 } { #3 } \keys_define:nn { dbshow/style/#1/#3 } { before-code .tl_gset:c = { g__dbshow_style_attr_before_#1_#2_#3 }, before-code .initial:n = , after-code .tl_gset:c = { g__dbshow_style_attr_after_#1_#2_#3 }, after-code .initial:n = , % for date and clist sep .clist_gset:c = { g__dbshow_style_attr_sep_#1_#2_#3 }, % only for clist item-before-code .tl_gset:c = { g__dbshow_style_attr_item_before_#1_#2_#3 }, item-before-code .initial:n = , item-after-code .tl_gset:c = { g__dbshow_style_attr_item_after_#1_#2_#3 }, item-after-code .initial:n = , % only for date zfill .bool_gset:c = { g__dbshow_style_attr_zfill_#1_#2_#3 }, zfill .initial:n = true, zfill .default:n = true, wrapper .tl_gset:c = { g__dbshow_style_attr_wrapper_#1_#2_#3 }, wrapper .initial:n = { \dbshow_identity:n }, } \str_case_e:nn { \prop_item:cn { g__dbshow_type_map_#2 } { #3 } } { { clist } { \keys_set:nn { dbshow/style/#1/#3 } { sep = { { ,~ } } } } { date } { \keys_set:nn { dbshow/style/#1/#3 } { sep = { { / } } } } } } \int_gzero_new:N \g__dbshow_raw_filter_int % #1 style name #2 database name \cs_new_protected:Nn \dbshow_new_database_style:nn { \dbshow_check_database:n { #2 } \keys_define:nn { dbshow/style/#1 } { filter .tl_gset:c = { g__dbshow_filter_#1_#2 }, filter .initial:n = -none-, raw-filter .code:n = { \int_gincr:N \g__dbshow_raw_filter_int \str_set:Nx \l_tmpa_str { -raw\int_use:N \g__dbshow_raw_filter_int - } \exp_args:NnV \tl_gset:cn { g__dbshow_filter_#1_#2 } \l_tmpa_str \exp_args:NnV \dbshow_combine_conditional:nnn { #2 } \l_tmpa_str { ##1 } }, sort .clist_gset:c = { g__dbshow_sort_#1_#2 }, before-code .tl_gset:c = { g__dbshow_style_before_#1_#2 }, before-code .initial:n = , item-code .tl_gset:c = { g__dbshow_style_database_item_#1_#2 }, item-code .initial:n = , after-code .tl_gset:c = { g__dbshow_style_after_#1_#2 }, after-code .initial:n = , record-before-code .tl_gset:c = { g__dbshow_style_record_before_#1_#2 }, record-before-code .initial:n = , record-after-code .tl_gset:c = { g__dbshow_style_record_after_#1_#2 }, record-after-code .initial:n = , } \prop_map_inline:cn { g__dbshow_type_map_#2 } { \dbshow_new_attr_style:nnn { #1 } { #2 } { ##1 } } } % #1 base style #2 style #3 database #4 opt \NewDocumentCommand { \dbNewStyle } { o m m +m } { \tl_gset:cn { g__dbshow_style_opts_#2_#3 } { #4, } \IfValueT { #1 } { \tl_gclear:N \g_tmpa_tl \clist_map_inline:nn { #1 } { \dbshow_check_style:nn { ##1 } { #3 } \tl_if_exist:cT { g__dbshow_style_opts_##1_#3 } { \tl_gconcat:ccc { g_tmpa_tl } { g_tmpa_tl } { g__dbshow_style_opts_##1_#3 } } } \tl_gconcat:ccc { g__dbshow_style_opts_#2_#3 } { g_tmpa_tl } { g__dbshow_style_opts_#2_#3 } } \dbshow_new_database_style:nn { #2 } { #3 } \exp_args:Nnv \keys_set:nn { dbshow/style/#2 } { g__dbshow_style_opts_#2_#3 } } \cs_new:Npn \dbshow_sort_parse_star:w #1* { \tl_set:Nx \l_op_same { > } \tl_set:Nx \l_op_swap { < } \str_set:Nn \l_tmpa_str { #1 } } % #1 database #2 index #3 style \cs_new:Nn \dbshow_sort:nNn { \int_set:Nn \l_tmpb_int { \clist_count:c { g__dbshow_sort_#3_#1 } } \clist_sort:Nn #2 { \int_zero:N \l_tmpa_int \cs_set:Nn \dbshow_sort_single: { \int_incr:N \l_tmpa_int \str_set:Nx \l_tmpa_str % attr { \clist_item:cn { g__dbshow_sort_#3_#1 } { \l_tmpa_int } } \str_if_in:NnTF \l_tmpa_str { * } { \exp_after:wN \dbshow_sort_parse_star:w \l_tmpa_str } { \tl_set:Nx \l_op_same { < } \tl_set:Nx \l_op_swap { > } } \exp_args:NnV \dbshow_check_attr:nn { #1 } \l_tmpa_str \prop_get:cVN { g__dbshow_type_map_#1 } \l_tmpa_str \l_tmp_type \clist_if_in:nVF { str, int, date, fp } \l_tmp_type { \msg_error:nnx { dbshow } { unsupported-sort-type } { \l_tmp_type } } \str_if_eq:eeT { \l_tmp_type } { date } { \str_set:Nn \l_tmp_type { str } } \cs_set_eq:Nc \l_tmpa_tl { g__dbshow_data_#1_\l_tmpa_str _##1 } \cs_set_eq:Nc \l_tmpb_tl { g__dbshow_data_#1_\l_tmpa_str _##2 } \cs_set_eq:Nc \dbshow_compare { \l_tmp_type _compare:VNVTF } \dbshow_compare \l_tmpa_tl \l_op_same \l_tmpb_tl { \sort_return_same: } { \dbshow_compare { \l_tmpa_tl } \l_op_swap { \l_tmpb_tl } { \sort_return_swapped: } { \int_compare:nTF { \l_tmpa_int = \l_tmpb_int } { \sort_return_same: } { \dbshow_sort_single: } } } } \dbshow_sort_single: } } % #1 style #2 database #3 filter \cs_new_protected:Nn \dbshow_init_iterator:nnn { \tl_set:Nn \dbDatabase { #2 } \tl_set:Nx \dbFilterName { #3 } \tl_set_eq:Nc \dbFilterInfo { g__dbshow_filter_info_#2_#3 } \clist_clear_new:N \l_index %% run filters % ##1 index \cs_set:Nn \dbshow_filter:n { \seq_if_exist:cTF { g__dbshow_filter_run_seq_#2_#3 } { \seq_map_inline:cn { g__dbshow_filter_run_seq_#2_#3 } { \use:c { ####1 } { ##1 } } \exp_args:Nv \bool_if:nT { g__dbshow_filter_bool_tl_#2_#3 } { \clist_put_right:Nn \l_index { ##1 } } } { \clist_put_right:Nn \l_index { ##1 } } } \int_step_function:nN { \dbshow_get_counter:n { #2 } } \dbshow_filter:n \clist_if_empty:cF { g__dbshow_sort_#1_#2 } { \dbshow_sort:nNn { #2 } \l_index { #1 } } % ##1 index \int_zero_new:N \l_counter_int \cs_set:Nn \dbshow_iter:n { \int_incr:N \l_counter_int \tl_set:Nx \dbalph { \int_to_alph:n { \l_counter_int } } \tl_set:Nx \dbAlph { \int_to_Alph:n { \l_counter_int } } \tl_set:Nx \dbarabic { \int_to_arabic:n { \l_counter_int } } \tl_set:Nx \dbRoman { \int_to_Roman:n { \l_counter_int } } \tl_set:Nx \dbroman { \int_to_roman:n { \l_counter_int } } \tl_set:Nn \dbIndex { ##1 } % ####1 attr \DeclareExpandableDocumentCommand { \dbuse } { m } { \dbshow_check_attr:nn { #2 } { ####1 } \tl_use:c { g__dbshow_style_attr_before_#1_#2_####1 } \dbshow_use_data:nnnn { #2 } { ####1 } { ##1 } { #1 } \tl_use:c { g__dbshow_style_attr_after_#1_#2_####1 } } \tl_use:c { g__dbshow_style_record_before_#1_#2 } \tl_use:c { g__dbshow_style_database_item_#1_#2 } \tl_use:c { g__dbshow_style_record_after_#1_#2 } } \prg_set_conditional:Nnn \dbshow_if_empty: { T, F, TF } { \clist_if_empty:NTF \l_index { \prg_return_true: } { \prg_return_false: } } \cs_set_eq:NN \dbIfEmptyT\dbshow_if_empty:T \cs_set_eq:NN \dbIfEmptyF\dbshow_if_empty:F \cs_set_eq:NN \dbIfEmptyTF\dbshow_if_empty:TF \tl_use:c { g__dbshow_style_before_#1_#2 } \clist_map_function:NN \l_index \dbshow_iter:n \tl_use:c { g__dbshow_style_after_#1_#2 } } % #1 style #2 database \NewDocumentCommand { \dbshow } { m m } { \dbshow_check_database:n { #2 } \tl_set_eq:Nc \l_dbshow_filter { g__dbshow_filter_#1_#2 } \exp_args:Nnv \dbshow_check_filter:nn { #2 } { g__dbshow_filter_#1_#2 } \exp_args:Nnnv \dbshow_init_iterator:nnn { #1 } { #2 } { g__dbshow_filter_#1_#2 } } \cs_set_eq:NN \dbIntAbs \int_abs:n \cs_set_eq:NN \dbIntSign \int_sign:n \cs_set_eq:NN \dbIntDivRound \int_div_round:nn \cs_set_eq:NN \dbIntDivTruncate \int_div_truncate:nn \cs_set_eq:NN \dbIntMax \int_max:nn \cs_set_eq:NN \dbIntMin \int_min:nn \cs_set_eq:NN \dbIntMod \int_mod:nn \cs_set_eq:NN \dbFpSign \fp_sign:n %%%%%%%%%%%%%%%%% date % #1 year \prg_new_conditional:Nnn \dbshow_date_if_leap:n { T, F, TF, p } { \bool_if:nTF { \int_compare_p:nNn { \int_mod:nn { #1 } { 400 } } = { 0 } || ( !\int_compare_p:nNn { \int_mod:nn { #1 } { 100 } } = { 0 } && \int_compare_p:nNn { \int_mod:nn { #1 } { 4 } } = { 0 } ) } { \prg_return_true: } { \prg_return_false: } } \clist_const:Nn \dbshow_date_month_clist { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } \int_new:N \l__date_ans_int \int_new:N \l__date_tmpa_int \int_new:N \l__date_tmpb_int % #1 year #2 month #3 day #4 token to save the result \cs_new_protected:Nn \dbshow_date_to_int:nnnN { \int_set:Nn \l__date_ans_int { #3 - 1 } \int_step_inline:nn { #2 - 1 } { \int_add:Nn \l__date_ans_int { \clist_item:Nn \dbshow_date_month_clist { ##1 } } \bool_if:nT { \int_compare_p:nNn { ##1 } = { 2 } && \dbshow_date_if_leap_p:n { #1 } } { \int_incr:N \l__date_ans_int } } \int_add:Nn \l__date_ans_int { 365 * (#1 - 1971) } \int_add:Nn \l__date_ans_int { \int_div_truncate:nn { #1 - 1 } { 4 } - \int_div_truncate:nn { 1971 } { 4 } } \int_sub:Nn \l__date_ans_int { \int_div_truncate:nn { #1 - 1 } { 100 } - \int_div_truncate:nn { 1971 } { 100 } } \int_add:Nn \l__date_ans_int { \int_div_truncate:nn { #1 - 1 } { 400 } - \int_div_truncate:nn { 1971 } { 400 } } \int_set_eq:NN #4 \l__date_ans_int } \cs_new_protected:Nn \dbshow_date_to_int:NNNN { \dbshow_date_to_int:nnnN { #1 } { #2 } { #3 } #4 } \cs_generate_variant:Nn \dbshow_date_to_int:NNNN { cccN } % #1 date #2 int \cs_new:Nn \dbshow_date_to_int:nN { \dbshow_date_to_int:cccN { dbshow_date_year_#1 } { dbshow_date_month_#1 } { dbshow_date_day_#1 } #2 } \cs_new_protected:Nn \dbshow_date_set_val:n { \tl_set:cx { #1 } { \dbshow_date_use_zfill:nn { #1 } { / } } } \cs_new_protected:Nn \dbshow_date_gset_val:n { \tl_gset:cx { #1 } { \dbshow_date_use_zfill:nn { #1 } { / } } } \cs_new_protected:Nn \dbshow_date_init:n { \dbshow_date_set:nnnn { #1 } { 1971 } { 1 } { 1 } \dbshow_date_set_val:n { #1 } } \cs_new_protected:Nn \dbshow_date_ginit:n { \dbshow_date_gset:nnnn { #1 } { 1971 } { 1 } { 1 } \dbshow_date_gset_val:n { #1 } } \cs_new_protected:Nn \dbshow_date_new:n { \int_new:c { dbshow_date_year_#1 } \int_new:c { dbshow_date_month_#1 } \int_new:c { dbshow_date_day_#1 } \dbshow_date_ginit:n { #1 } } \cs_new_protected:Nn \dbshow_date_clear_new:n { \int_zero_new:c { dbshow_date_year_#1 } \int_zero_new:c { dbshow_date_month_#1 } \int_zero_new:c { dbshow_date_day_#1 } \dbshow_date_init:n { #1 } } \cs_new_protected:Nn \dbshow_date_gclear_new:n { \int_gzero_new:c { dbshow_date_year_#1 } \int_gzero_new:c { dbshow_date_month_#1 } \int_gzero_new:c { dbshow_date_day_#1 } \dbshow_date_ginit:n { #1 } } % #1 name #2 year #3 month #4 day \cs_new_protected:Nn \dbshow_date_set:nnnn { \int_set:cn { dbshow_date_year_#1 } { #2 } \int_set:cn { dbshow_date_month_#1 } { #3 } \int_set:cn { dbshow_date_day_#1 } { #4 } \dbshow_date_set_val:n { #1 } } % #1 name #2 year #3 month #4 day \cs_new_protected:Nn \dbshow_date_gset:nnnn { \int_gset:cn { dbshow_date_year_#1 } { #2 } \int_gset:cn { dbshow_date_month_#1 } { #3 } \int_gset:cn { dbshow_date_day_#1 } { #4 } \dbshow_date_gset_val:n { #1 } } \cs_new_protected:Npn \dbshow_date_set:w #1\dbshow_sep#2/#3/#4\dbshow_stop { \dbshow_date_clear_new:n { #1 } \dbshow_date_set:nnnn { #1 } { #2 } { #3 } { #4 } } \cs_new_protected:Npn \dbshow_date_gset:w #1\dbshow_sep#2/#3/#4\dbshow_stop { \dbshow_date_gclear_new:n { #1 } \dbshow_date_gset:nnnn { #1 } { #2 } { #3 } { #4 } } % #1 date name #2 date \cs_new_protected:Nn \dbshow_date_set:nn { \dbshow_date_set:w #1\dbshow_sep#2\dbshow_stop } \cs_generate_variant:Nn \dbshow_date_set:nn { nx } \cs_new_protected:Nn \dbshow_date_gset:nn { \dbshow_date_gset:w #1\dbshow_sep#2\dbshow_stop } \cs_generate_variant:Nn \dbshow_date_gset:nn { nx } % #1 date 1 #2 date 2 #3 int token \cs_new_protected:Nn \dbshow_date_sub:nnN { \dbshow_date_to_int:nN { #1 } \l__date_tmpa_int \dbshow_date_to_int:nN { #2 } \l__date_tmpb_int \int_set:Nn #3 { \l__date_tmpa_int - \l__date_tmpb_int } } \cs_new:Nn \dbshow_date_show_two:N { \int_compare:nNnTF { #1 } > { 9 } { \int_use:N #1 } { 0\int_use:N #1 } } \cs_generate_variant:Nn \dbshow_date_show_two:N { c } \cs_new:Nn \dbshow_date_use:nnnnn { #2\int_use:c { dbshow_date_year_#1 } #3\int_use:c { dbshow_date_month_#1 } #4\int_use:c { dbshow_date_day_#1 }#5 } \cs_generate_variant:Nn \dbshow_date_use:nnnnn { nffff } \cs_new:Nn \dbshow_date_use_zfill:nnnnn { #2\int_use:c { dbshow_date_year_#1 } #3\dbshow_date_show_two:c { dbshow_date_month_#1 } #4\dbshow_date_show_two:c { dbshow_date_day_#1 }#5 } \cs_generate_variant:Nn \dbshow_date_use_zfill:nnnnn { nffff } \cs_new:Nn \dbshow_date_use:nn { \dbshow_date_use:nnnnn { #1 } { } { #2 } { #2 } { } } \cs_generate_variant:Nn \dbshow_date_use:nn { nf } \cs_new:Nn \dbshow_date_use_zfill:nn { \dbshow_date_use_zfill:nnnnn { #1 } { } { #2 } { #2 } { } } \cs_generate_variant:Nn \dbshow_date_use_zfill:nn { nf } \tl_set:Nx \dbtoday { \int_use:N \c_sys_year_int/ \int_use:N \c_sys_month_int/ \int_use:N \c_sys_day_int } \endinput