diff options
author | Karl Berry <karl@freefriends.org> | 2022-01-14 22:37:24 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2022-01-14 22:37:24 +0000 |
commit | df3e7da4a19a19faec6c43c91dc6fb9b3e3fa20a (patch) | |
tree | 2c60f03e8c3d141ca91b42082361fac36cb8041e /Master/texmf-dist/source | |
parent | ac081f6b6463c402084fb564b12b68890efd524b (diff) |
dbshow (14jan22)
git-svn-id: svn://tug.org/texlive/trunk@61605 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/source')
-rw-r--r-- | Master/texmf-dist/source/latex/dbshow/dbshow.dtx | 3740 | ||||
-rw-r--r-- | Master/texmf-dist/source/latex/dbshow/dbshow.ins | 90 |
2 files changed, 3830 insertions, 0 deletions
diff --git a/Master/texmf-dist/source/latex/dbshow/dbshow.dtx b/Master/texmf-dist/source/latex/dbshow/dbshow.dtx new file mode 100644 index 00000000000..a2af27d9229 --- /dev/null +++ b/Master/texmf-dist/source/latex/dbshow/dbshow.dtx @@ -0,0 +1,3740 @@ +% \iffalse meta-comment +%<*internal> +\iffalse +%</internal> +%<*readme> +Package dbshow +============== + +Introduction +------------ + +The package provides four core functions: + +1. data storage +2. data filtering +3. data sorting +4. data display + +All data is saved once and then you can display these data with custom filters, +orders and styles. The package can be used, for example, to record and +display something you'd like to review, maybe the question you always answered +incorrectly or some forgettable knowledge. But obviously, the package is much +more powerful and extensible for more interesting tasks depending on the +individual. + +Install +------- + +If you are using TeX Live, install `dbshow` by `tlmgr` package manager + + tlmgr update --self --all + tlmgr install dbshow + +You can also download the zip file from +[CTAN](https://ctan.org/pkg/dbshow) or +[GitHub](https://github.com/ZhiyuanLck/dbshow/releases/latest). + +The zip file should contain the file `dbshow.dtx`, and you can extract +`dbshow.sty` from it by + + tex dbshow.dtx + +Compile Document +---------------- + +Use following commands to compile the document: + + git clone https://github.com/ZhiyuanLck/dbshow.git + cd dbshow + latexmk dbshow.dtx + +Copyright and License +--------------------- + + Copyright (C) 2022- by Changkai Li <lichangkai225@qq.com> + -------------------------------------------------------------------------- + + This work may be distributed and/or modified under the + conditions of the LaTeX Project Public License, either + version 1.3c of this license or (at your option) any later + version. This version of this license is in + http://www.latex-project.org/lppl/lppl-1-3c.txt + and 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 Changkai Li. + + This package consists of the file dbshow.dtx, + and the derived files dbshow.pdf, + dbshow.sty, + dbshow.ins and + README.md (this file). +%</readme> +%<*internal> +\fi +\begingroup + \def\temp{LaTeX2e} +\expandafter\endgroup\ifx\temp\fmtname\else +\csname fi\endcsname +%</internal> +%<*install> + +\input l3docstrip.tex +\keepsilent +\askforoverwritefalse +\preamble + + Copyright (C) 2022- by Changkai Li <lichangkai225@qq.com> + -------------------------------------------------------------------------- + + This work may be distributed and/or modified under the + conditions of the LaTeX Project Public License, either + version 1.3c of this license or (at your option) any later + version. This version of this license is in + http://www.latex-project.org/lppl/lppl-1-3c.txt + and 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 Changkai Li. + + This package consists of the file dbshow.dtx, + and the derived files dbshow.pdf, + dbshow.sty, + dbshow.ins and + README.md. + -------------------------------------------------------------------------- + +\endpreamble +\postamble + + -------------------------------------------------------------------------- + This package consists of the file dbshow.dtx, + and the derived files dbshow.pdf, + dbshow.sty, + dbshow.ins and + README.md. +\endpostamble + +\generate{ +%</install> +%<*internal> + \usedir{source/latex/dbshow} + \file{dbshow.ins}{\from{\jobname.dtx}{install}} +%</internal> +%<*install> + \usedir{tex/latex/dbshow} + \file{dbshow.sty}{\from{\jobname.dtx}{package}} + \nopreamble\nopostamble + \usedir{doc/latex/dbshow} + \file{README.md} {\from{\jobname.dtx}{readme}} +} +\endbatchfile +%</install> +%<*internal> +\fi +%</internal> +%<package>\NeedsTeXFormat{LaTeX2e} +%<+package|config>\GetIdInfo$Id: dbshow.dtx 461d9bb 2022-01-14 02:50:24 +0800 Changkai Li <lichangkai225@qq.com> $ +%<package> {Database to store and display data} +%<package>\ProvidesExplPackage{\ExplFileName} +%<package|config> {\ExplFileDate}{1.4}{\ExplFileDescription} +%<*driver> +\documentclass[full]{l3doc} +\usepackage[scheme=plain, fontset=ubuntu]{ctex} +\usepackage{dbshow} +\usepackage{adjustbox} +\usepackage{zhlineskip} +\usepackage{enumitem} +\usepackage{indentfirst} +\usepackage{titling} +\usepackage{geometry} +\usepackage{tabularray} +\usepackage{xcolor} +\usepackage{tabularray} +\usepackage{zhnumber} + +\geometry{ + left=4.5cm, + right=2cm, + top=2cm, + bottom=2cm, +} +\hypersetup { + CJKbookmarks, + bookmarksopen, + bookmarksopenlevel=3, + pdfstartview=FitH, + pdfinfo = { + Title = The package 'dbshow' , + Subject = A LaTeX package , + Author = Li Changkai + } +} + +\IndexPrologue { + \part*{Index} + \markboth{Index}{Index} + \addcontentsline{toc}{part}{Index} + The~italic~numbers~denote~the~pages~where~the~ + corresponding~entry~is~described,~ + numbers~underlined~point~to~the~definition,~ + all~others~indicate~the~places~where~it~is~used. +} + +\GlossaryPrologue { + \part*{Change~History} + {\GlossaryParms\ttfamily\hyphenchar\font=`\-} + \markboth{Change~History}{Change~History} + \addcontentsline{toc}{part}{Change~History} +} + +\let\subsubitem\subitem + +\DoNotIndex{\begin, \end} +\setlength{\parskip}{\medskipamount} + +\newcommand\tikzmark[1]{\tikz \coordinate[overlay, remember picture] (#1);} +\def\levelchar{?} +\def\orbar{\textup{\textbar}} +\def\defaultval#1{\textbf{\textup{#1}}} +\def\TF{true\orbar false} +\def\TTF{\defaultval{true}\orbar false} +\def\TFF{true\orbar\defaultval{false}} +\def\zhbefore{\textbf{前}} +\def\zhafter{\textbf{后}} +\def\enbefore{\textbf{before}~} +\def\enafter{\textbf{after}~} +\DeclareDocumentEnvironment { note } { +b } { + \par\textbf{\textsf{NOTE:~}}#1\par +} {} + +\ExplSyntaxOn +\makeatletter + +\DeclareDocumentCommand { \csnot } { m } { + \group_begin:\ttfamily + \tl_set:Nn \l_csnot_tl { \c_backslash_str #1 } + \tl_replace_all:Nnn \l_csnot_tl { ~ } { \@xobeysp } + \l_csnot_tl + \group_end: +} + +\DeclareDocumentEnvironment{ arguments } { O{1} } { + \tl_set:Nx \l_tmpa_tl { + \int_case:nn {#1} { + { 1 } { \# } + { 2 } { \#\# } + { 3 } { \#\#\#\# } + } + } + \enumerate [ + nolistsep , + label = \texttt{\l_tmpa_tl\arabic*} ~ : , + labelsep = * , + labelwidth = !, + align=left, + ] +} { \endenumerate } + +\DoNotIndex{\begin, \end} + +\renewenvironment{theglossary}{ +\glossary@prologue\GlossaryParms \let\item\@idxitem \ignorespaces}{} + +\cs_gset_eq:NN \dbshowdocnull \use_none:n + +% #1 sort #2 group #3 date #4 type #5 desc +\cs_new_protected:Nn \dbshowdoc_changes:nnnnn { + \@bsphack\group_begin:\@sanitize + \catcode`\\\z@ \catcode`\ 10 \MakePercentIgnore + \exp_args:Nx \glossary { + \dbshowdocnull{#1}~ + \exp_not:N \textbf{#2} + \exp_not:n { \raisebox{1em}{\pdfbookmark[1]{#2}{#2}} } + \levelchar + \exp_not:N \textup{#3}\c_space_tl + \exp_not:N \textbf{#4}\c_colon_str\c_space_tl + \exp_not:n {#5} + } + \group_end:\@esphack +} +\cs_generate_variant:Nn \dbshowdoc_changes:nnnnn { xxnnn, VVnnn } + +\cs_new_protected:Nn \dbshowdoc_print_version:n { + v#1 + \int_compare:nNnT { \str_count:n {#1} } = { 3 } { \phantom{0} } + \c_space_tl +} +\cs_set_eq:NN \dbshowdocver \dbshowdoc_print_version:n + +% #1 ver #2 date #3 type #4 desc +\cs_new_protected:Nn \dbshowdoc_changes_what:nnnn { + \regex_extract_once:nnNT { doc|macro|bug|option } + {#3} \l_tmpa_seq { + \tl_set:Nx \l_tmpa_tl { + \str_case_e:nn { \seq_item:Nn \l_tmpa_seq { 1 } } { + { doc } { Documentation } + { macro } { Macro } + { bug } { Bug } + { option } { Option } + { logic } { Logic } + } + } + \dbshowdoc_changes:VVnnn \l_tmpa_tl \l_tmpa_tl + { \dbshowdocver{#1}#2 } {#3} {#4} + } +} + +% #1 ver #2 date #3 type #4 desc +\cs_new_protected:Nn \dbshowdoc_changes_how:nnnn { + \regex_extract_once:nnNT { Add|Update|Remove|Fix } + {#3} \l_tmpa_seq { + \dbshowdoc_changes:xxnnn + { \seq_item:Nn \l_tmpa_seq { 1 } } + { \seq_item:Nn \l_tmpa_seq { 1 } } + { \dbshowdocver{#1}#2 } {#3} {#4} + } +} + +\cs_new:Nn \dbshowdoc_fill_two:n { + \int_compare:nNnTF {#1} > { 9 } + {#1} { 0#1 } +} + +% #1 major number #2 minor number +\cs_new_protected:Npn \dbshowdoc_changes_ver:w #1.#2\dbshowdoc_stop { + \dbshowdoc_changes:xxnnn { #1.\dbshowdoc_fill_two:n {#2} } +} + +% #1 ver #2 date #3 type #4 desc +\DeclareDocumentCommand \changes { m m m m } { + \dbshowdoc_changes_ver:w #1\dbshowdoc_stop {#1} {#2} {#3} {#4} + \dbshowdoc_changes_what:nnnn {#1} {#2} {#3} {#4} + \dbshowdoc_changes_how:nnnn {#1} {#2} {#3} {#4} +} +\makeatother + +\DeclareDocumentEnvironment { Description } { o +b } { + \hbox_set:Nn \l_tmpa_box {#1} + \dim_set:Nn \l_tmpa_dim { \box_wd:N \l_tmpa_box } + \begin{itemize}[ + itemindent=0pt, + labelindent=0pt, + labelwidth=\l_tmpa_dim, + leftmargin=!, + align=left] + #2 + \end{itemize} +} {} + +\cs_new_protected:Nn \dbshowdoc_function_begin:N { + \cs_set_eq:NN \__codedoc_tmp_cs:nN \__codedoc_typeset_function_block:nN + \cs_set_protected:Npn \__codedoc_typeset_function_block:nN ##1##2 { + \__codedoc_function_label:xN {##1} ##2 + \hbox_set:Nn \l_tmpa_box {##1} + \int_compare:nTF { \str_count:n {##1} <= 22 } + {##1} { \adjustbox{width=.7\marginparwidth, height=\box_ht:N \l_tmpa_box}{##1} } + #1{##1} + \__codedoc_typeset_expandability: \\ + } +} +\cs_new_protected:Nn \dbshowdoc_function_end: { + \cs_set_eq:NN \__codedoc_typeset_function_block:nN \__codedoc_tmp_cs:nN +} +\DeclareDocumentEnvironment { option } { O{} +v } + { + \dbshowdoc_function_begin:N \SpecialOptionIndex + \__codedoc_function:nnw {#1} {#2} + } + { + \__codedoc_function_end: + \dbshowdoc_function_end: + } +\DeclareDocumentEnvironment { environment } { O{} +v } + { + \dbshowdoc_function_begin:N \SpecialEnvIndex + \__codedoc_function:nnw {#1} {#2} + } + { + \__codedoc_function_end: + \dbshowdoc_function_end: + } + +\DeclareDocumentCommand \opt { O{} m } + { \__codedoc_cmd:no {#1} {#2} } + +\NewDocumentCommand \linktarget { m m m } {% + \hyperlink{#1}{#3}% + \raisebox{1em}{\hypertarget{#2}{}}% +} + +\DeclareDocumentCommand \GetFileId { m } { + \GetFileInfo {#1} + \file_get:nnNTF { \c_sys_jobname_str .id } + { \int_set:Nn \tex_endlinechar:D { -1 } } \l__ctxdoc_tmp_tl + { \exp_after:wN \GetIdInfo \l__ctxdoc_tmp_tl } + { \GetIdInfo $Id$ } + { \fileinfo } +} +\ExplSyntaxOff + +\begin{document} +\DocInput{dbshow.dtx} +\newgeometry{ + left=2cm, + right=2cm, + top=2cm, + bottom=2cm +} +\changes{1.4}{2022-01-10}{Fix doc code}{wrong changes history sorting} +\PrintChanges +\PrintIndex +\end{document} +%</driver> +% \fi +% +% \CheckSum{882} +% \GetFileId{dbshow.sty} +% +% \ExplSyntaxOn +% \tl_set:Nx \cnfiledate{ \exp_args:NV \zhdate\filedate } +% \ExplSyntaxOff +% +% \title{ +% \pkg{dbshow} 宏包 \fileversion% +% \protect\footnote{% +% 代码仓库:\url{https://github.com/ZhiyuanLck/dbshow}, +% QQ群:788706534} +% \rlap{\makebox[4cm][r]{ +% \normalsize $\Longrightarrow$ \color{red} +% \linktarget{en}{zh}{English Version} +% }} +% } +% \author{\textit{李昌锴} \protect\path{<lichangkai225@qq.com>}} +% \date{\cnfiledate} +% \maketitle +% +% \tableofcontents +% +% \begin{documentation} +% +% \section{引言} +% 编写本宏包的动机来源于当前没有一个很好的错题本宏包,可以方便的根据各种条件对错 +% 题进行筛选、排序,然后以自定义的样式展示出来。\pkg{dbshow} 宏包实现了四个核心 +% 功能:数据存储和使用、数据筛选、数据排序、数据展示。 +% +% 数据只需要存储一次,就可以通过预定义的筛选、排序条件和样式展示部分或全部的数据。 +% 如上所述,本宏包其实实现了一个非常简单的数据库,复习错题的功能只是其中一个应用, +% 和其他数据库宏包比如 \pkg{datatool} 相比,\pkg{dbshow} 更专注于非图表类型的数 +% 据展示。 +% +% \changes{1.4}{2022-01-10}{Add check}{version of \pkg{l3kernel}} +% \pkg{dbshow} 依赖版本日期至少为 |2022-11-07| 的 \pkg{l3kernel}。 +% +% \changes{1.3}{2022-01-09}{Add doc}{descripton for expansion} +% \begin{itemize} +% \item 名字后带有 $\star$ 的命令是可以完全展开的(fully-expandable); +% \item 名字后带有 \ding{73} 的命令可以有限制地展开(restricted-expandable); +% \item 名字后不带有特殊字符的命令是不可展开的(non-expandable); +% \item 名字后带有 $\star$ 的选项不影响相关的代码的是否可展开; +% \item 名字后带有 \ding{73} 的选项是否影响相关代码的可展开性取决于选项的设置; +% \item 名字后不带有特殊字符的选项会使与之相关的代码变得不可展开。 +% \end{itemize} +% +% \subsection{数据类型} +% +% 宏包基于 \pkg{expl3} 的基础类型构建了6种类型: +% \begin{Description}[\texttt{clist}] +% \item[\texttt{date}] +% 日期类型,以 |yyyy/mm/dd| 形式存储,支持大小比较,排序(转 +% 换成字符串)。默认值为 \cs{dbtoday}。 +% \item[\texttt{str}] +% 字符串类型,支持正则匹配,英文排序。默认值为空。 +% \item[\texttt{tl}] +% \meta{token list}类型,支持正则匹配。默认值为空。 +% \item[\texttt{int}] +% 整数类型,支持大小比较,排序。默认值为0。 +% \item[\texttt{fp}] +% 浮点数类型,支持大小比较,排序。默认值为0。 +% \item[\texttt{clist}] +% 逗号分隔的列表类型。默认值为空列表。 +% \end{Description} +% +% \changes{1.3}{2022-01-08}{Remove dependency}{\pkg{datatime2}} +% 除了日期类型,所有类型都是 \pkg{expl3} 的内置类型。\pkg{dbshow} 构建了一个简单 +% 的 |date| 类型,支持转换成整数以及带样式的打印。 +% +% \subsection{与 \pkg{datatool} 的区别} +% +% \changes{1.2}{2022-01-07}{Add doc}{add comparison to \pkg{datatool}} +% +% 从核心功能上看,\pkg{dbshow} 和 \pkg{datatool} 实现了相同的功能。区别在于 +% \pkg{dbshow} 基于 \pkg{expl3} 实现,支持字符串的正则匹配,还支持多级排序。使用 +% 方式上更倾向于样式与内容分离,所有的样式都可以通过选项提前定义好并且可以复用。 +% \pkg{dbshow} 并没有实现从外部文件读取数据以及将数据持久化的功能,我认为这些应 +% 该是更专业的外部程序的工作而不应该在 \LaTeX 中设计这些功能。因此,\pkg{dbshow} +% 只提供了一个运行时的临时数据库,足够轻便且满足大部分正常需求。如果你想删除或修 +% 改数据库中某一条记录,请去对应的位置删除或修改掉对应的 \env{dbitem} 环境,而不 +% 是让宏包提供一个输出某一行记录的命令。某种意义上记录数据库的 \TeX 源文件本身就 +% 是数据的一种持久化。 +% +% \section{接口文档} +% +% \subsection{创建、展示和清空数据库} +% +% \changes{1.4}{2022-01-10}{Update macro}{change \orbar\ to \cs{dbshow_sep} in +% weird argument of \cs{dbshow_process_default_value:w}} +% \begin{function}[added=2022-01-05, updated=2022-01-10]{\dbNewDatabase, \dbNewDatabase*} +% \begin{syntax} +% \cs{dbNewDatabase} \oarg{base database} \marg{database} \{ \\ +% ~~\meta{attr1} = \meta{type spec1}, \\ +% ~~\meta{attr2} = \meta{type spec2}, \\ +% ~~\ldots{} \\ +% \} \\ +% \cs{dbNewDatabase}* \marg{database} \{ \\ +% ~~\meta{attr1} = \meta{type spec1}, \\ +% ~~\meta{attr2} = \meta{type spec2}, \\ +% ~~\ldots{} \\ +% \} \\ +% \end{syntax} +% +% \end{function} +% +% 新建一个数据库,不带星号的版本可以指定一个数据库来继承其属性设置,该版本总是 +% 会舍弃掉之前的定义。 +% +% 带星号的版本不会舍弃之前已有的定义,而是将新的选项添加到后面。 +% +% \meta{attr} 为属性名称,\meta{type spec} 负责声明属性类型和属性默认值: +% +% \noindent\begin{tblr}{ +% colspec = {ll}, +% column{1} = {font = \ttfamily} +% } +% \meta{attr} = \meta{type} & +% 将 \meta{attr} 声明为 \meta{type} 类型 \\ +% \meta{attr} = \meta{type}\orbar\meta{default} & +% 将 \meta{attr} 声明为 \meta{type} 类型,并且将默认值设置为 \meta{default}。 +% \\ +% \end{tblr} +% +% \begin{note} +% 每个数据库都有一个默认的属性 |id| 用来存储数据的索引。 +% \end{note} +% +% 下面是定义一个错题数据库的示例,|question| 和 |answer| 属性用来存储问题和答 +% 案,|date| 属性存储日期,|info| 属性存储额外信息,|labels| 存储题目标签。 +% \begin{verbatim} +% \dbNewDatabase{ques}{ +% question = tl, +% answer = tl, +% date = date, +% info = tl, +% labels = clist +% } +% \end{verbatim} +% +% \begin{function}[added=2022-01-05]{\dbshow} +% \begin{syntax} +% \cs{dbshow} \marg{style} \marg{database} +% \end{syntax} +% +% 使用 \meta{style} 样式来展示 \meta{database}。 +% \end{function} +% +% \changes{1.2}{2022-01-07}{Add macro}{\cs{dbclear}} +% \begin{function}[added=2022-01-07]{\dbclear} +% \begin{syntax} +% \cs{dbclear} \marg{database} +% \end{syntax} +% 清空 \meta{database} 里的所有内容。 +% \end{function} +% +% \subsection{\cs{dbNewStyle} 和样式选项} +% +% \begin{function}[added=2022-01-05]{\dbNewStyle} +% \begin{syntax} +% \cs{dbNewStyle} \oarg{base styles} \marg{style} \marg{database} \marg{opts} +% \end{syntax} +% +% 为 \meta{database} 定义一个新的样式 \meta{style},该样式可以基于已有的样式 +% \meta{base styles},比如 |\dbNewStyle[base1, base2]{new-style}{ques}{}|。 +% \end{function} +% +% \subsubsection{通用选项} +% +% \begin{option}[added=2022-01-05]{filter} +% \begin{syntax} +% filter = <filter> +% \end{syntax} +% +% 为当前样式设置由 \cs{dbCombineFilters} 所定义的过滤器 +% \end{option} +% +% \begin{option}[added=2022-01-06]{raw-filter} +% \begin{syntax} +% raw-filter = <conditional expression> +% \end{syntax} +% +% 使用条件表达式设置匿名过滤器,这里的条件指通过 \cs{dbNewConditional} 定义的 +% 条件。下面代码中两个示例的过滤器具有相同的功能。 +% \end{option} +% +% \changes{1.1}{2022-01-06}{Add option}{\opt{raw-filter}} +% +% \begin{verbatim} +% % method 1 +% \begin{dbFilters}{db} +% \dbNewConditional{cond1}{int-attr}{\rval > 1} +% \dbNewConditional*{cond2}{str-attr}{\d+} +% \end{dbFilters} +% \dbNewStyle{style}{db}{raw-filter={cond1 && cond2}} +% % method 2 +% \begin{dbFilters}{db} +% \dbNewConditional{cond1}{int-attr}{\rval > 1} +% \dbNewConditional*{cond2}{str-attr}{\d+} +% \dbCombineFilters{filter}{cond1 && cond2} +% \end{dbFilters} +% \dbNewStyle{style}{db}{filter=filter} +% \end{verbatim} +% +% \changes{1.2}{2022-01-08}{Fix bug}{string sorting bug} +% \begin{option}[added=2022-01-05]{sort} +% \begin{syntax} +% sort = \{ <attr spec1>, <attr spec2>, \ldots{} \} +% \end{syntax} +% +% 为当前样式设置排序规则。支持根据 |str|,|date|,|int|,|fp| 类型的数据进行排 +% 序,支持多级排序。\meta{attr} 表示增序,\meta{attr}* 表示降序。下面例子中, +% 使用 |sort-style| 展示数据时的顺序为先按 |level| 降序,|level| 相同的再按出 +% 生日期 |birth| 增序,以此类推。 +% \end{option} +% +% \begin{verbatim} +% \dbNewDatabase{sort-example}{ +% name = str, +% birth = date, +% level = int, +% weight = fp, +% } +% \dbNewStyle{sort-style}{sort-example}{ +% sort = { level*, birth, name, weight } +% } +% \end{verbatim} +% +% \begin{option}[added=2022-01-05, rEXP]{item-code} +% \begin{syntax} +% item-code = <code> +% \end{syntax} +% +% 该选项用来设置展示数据库中每条记录的代码。你可以使用 \cs{dbuse} 来展示属性的 +% 值。 +% \end{option} +% +% \changes{1.3}{2022-01-09}{Update option}{\opt{<attr>/sep}} +% \begin{option}[added=2022-01-05, updated=2022-01-08, rEXP]{<attr>/sep} +% \begin{syntax} +% <attr>/sep = <separator> \\ +% <attr>/sep = \{ \\ +% ~~\meta{separator between two}, \\ +% ~~\meta{separator between more than two}, \\ +% ~~\meta{separator between final two} \\ +% \} \\ +% <attr>/sep = \{ \\ +% ~~\meta{separator before year}, \\ +% ~~\meta{separator between year and month}, \\ +% ~~\meta{separator between month and day}, \\ +% ~~\meta{separator after day} \\ +% \} \\ +% \end{syntax} +% +% 该选项只适用于类型为 |clist| 或 |date| 的属性,用来设置列表间元素的间隔。参 +% 数为一个 \meta{separator} 时,所有元素间的分隔符被设置为 \meta{separator}。 +% \meta{separator before year} 和 \meta{separator after day} 被设置为空。 +% \end{option} +% +% 参数为3个元素的逗号分隔的列表时,此选项用来设置列表元素的分隔符,分别用来设 +% 置只有两个元素时的分隔符 \meta{separator between two},超过两个元素时的分隔 +% 符 \meta{separator between more than two},和最后两个元素之间的分隔符 +% \meta{separator between final two}。对于类型为 |clist| 的属性,设置此选项时 +% 如果参数列表数量不是1或者3会触发报错。 +% +% \begin{verbatim} +% % clist-attr is an attribute of database db +% % suppose the val of clist-attr is { 1, 2, 3 } +% \dbNewStyle{clist-sep}{db}{ +% clist-attr/sep = { ,~ }, % print 1, 2, 3 +% clist-attr/sep = { {,~}, {,~}, {and~} } % print 1, 2 and 3 +% } +% \end{verbatim} +% +% 参数为4个元素的逗号分隔的列表时,此选项用来设置日期的分隔符,分别用来设 +% 置 \meta{year} 之前的分隔符 \meta{separator before year} ,\meta{year} 和 +% \meta{month} 之间的分隔符 \meta{separator between year and month} , +% \meta{month} 和 \meta{day} 之间的分隔符,以及 \meta{day} 之后的分隔符。对于 +% 类型为 |date| 的属性,设置此选项时如果参数列表数量不是1或者4会触发报错。 +% +% \begin{verbatim} +% % date-attr is an attribute of database db +% % suppose the val of date-attr is 2022/01/01 +% \dbNewStyle{date-sep}{db}{ +% date-attr/sep = -, % print 2022-01-01 +% date-attr/sep = { |, -, -, | } % print |2022-01-01| +% } +% \end{verbatim} +% +% \changes{1.3}{2022-01-08}{Add option}{\opt{<attr>/zfill}} +% \begin{option}[added=2022-01-08, EXP]{<attr>/zfill} +% \begin{syntax} +% <attr>/zfill = <\TTF> +% \end{syntax} +% +% 该选项只适用于类型为 |date| 的属性。控制输出月份和天时是否补零。 +% \end{option} +% +% \changes{1.4}{2022-01-13}{Add macro}{\cs{dbdatesep}} +% \begin{function}[added=2022-01-13, EXP]{\dbdatesep} +% \begin{syntax} +% \cs{dbdatesep} \marg{separator} +% \end{syntax} +% +% 设置内部解析日期时的间隔符,默认为 |/|,即存储数据的格式为 |yyyy/mm/dd|。 +% \end{function} +% +% \subsubsection{装饰器} +% +% 下面这些选项在不同层次上装饰原有的展示代码,有些其实不必通过选项的形式来装饰, +% 但这样做的好处是可以进一步使样式与内容分离。下面的例子中,\meta{style1} 和 +% \meta{style2} 是相同的样式,都用 * 将 \meta{attr1} 包裹住了,但是如果你还想定 +% 义一个样式用 = 将 \meta{attr1} 包裹住,如果用 \meta{style1} 的方式,那就可能 +% 需要重复大片代码,用 \meta{style2} 的方式则可以很轻松的继承 \meta{style1} 中的 +% 代码。 +% +% \begin{verbatim} +% \dbNewStyle{style1}{db}{ +% item-code = {% +% *\rvuse{attr1}*\rvuse{attr2} +% % more code +% } +% } +% \dbNewStyle{base-style}{db}{ +% item-code = {% +% \rvuse{attr1}\rvuse{attr2} +% % more code +% } +% } +% \dbNewStyle[base-style]{style2}{db}{ +% attr1/before-code = { * }, +% attr1/after-code = { * }, +% } +% \dbNewStyle[base-style]{style3}{db}{ +% attr1/before-code = { = }, +% attr1/after-code = { = }, +% } +% \end{verbatim} +% +% \changes{1.3}{2022-01-08}{Add option}{\opt{<attr>/wrapper}} +% \begin{option}[added=2022-01-08, rEXP]{<attr>/wrapper} +% \begin{syntax} +% <attr>/wrapper = <control sequence> +% \end{syntax} +% +% 该选项只适用于类型为 |date| 的属性。\meta{control sequence} 只接收一个参数即 +% 日期,如果设置了此选项,则最后输出的日期为 +% \meta{control sequence}\marg{date}。 +% \end{option} +% +% \begin{option}[added=2022-01-05, rEXP]{before-code} +% \begin{syntax} +% before-code = <code> +% \end{syntax} +% +% 该选项用来设置在展示整个数据库之\zhbefore 需要执行的代码。 +% \end{option} +% +% \begin{option}[added=2022-01-05, rEXP]{after-code} +% \begin{syntax} +% after-code = <code> +% \end{syntax} +% +% 该选项用来设置在展示整个数据库之\zhafter 需要执行的代码。 +% \end{option} +% +% \changes{1.2}{2022-01-08}{Add options}{\opt{record-before-code}, +% \opt{record-after-code}} +% \begin{option}[added=2022-01-05, rEXP]{record-before-code} +% \begin{syntax} +% record-before-code = <code> +% \end{syntax} +% +% 该选项用来设置在展示当前记录之\zhbefore 需要执行的代码。 +% \end{option} +% +% \begin{option}[added=2022-01-05, rEXP]{record-after-code} +% \begin{syntax} +% record-after-code = <code> +% \end{syntax} +% +% 该选项用来设置在展示当前记录之\zhafter 需要执行的代码。 +% \end{option} +% +% \begin{option}[added=2022-01-05, rEXP]{<attr>/before-code} +% \begin{syntax} +% <attr>/before-code = <code> +% \end{syntax} +% +% 该选项用来设置展示数据库中属性 \meta{attr} 对应数据之\zhbefore 需要执行的代 +% 码。\cs{dbuse} 会在展示属性数据\zhbefore 执行此代码。 +% \end{option} +% +% \begin{option}[added=2022-01-05, rEXP]{<attr>/after-code} +% \begin{syntax} +% <attr>/after-code = <code> +% \end{syntax} +% +% 该选项用来设置展示数据库中属性 \meta{attr} 对应数据之\zhafter 需要执行的代码。 +% \cs{dbuse} 会在展示属性数据\zhafter 执行此代码。 +% \end{option} +% +% \begin{option}[added=2022-01-05, rEXP]{<attr>/item-before-code} +% \begin{syntax} +% <attr>/item-before-code = <code> +% \end{syntax} +% +% 该选项只适用于类型为 |clist| 的属性,用来设置展示列表每个元素\zhbefore 需要 +% 执行的代码。 +% \end{option} +% +% \begin{option}[added=2022-01-05, rEXP]{<attr>/item-after-code} +% \begin{syntax} +% <attr>/item-after-code = <code> +% \end{syntax} +% +% 该选项只适用于类型为 |clist| 的属性,用来设置展示列表每个元素\zhafter 需要执 +% 行的代码。 +% \end{option} +% +% \subsection{使用 \cs{dbNewReviewPoints} 定义复习点} +% +% \begin{function}[added=2022-01-05]{\dbNewReviewPoints} +% \begin{syntax} +% \cs{dbNewReviewPoints} \marg{name} \marg{points} +% \end{syntax} +% +% 定义名为 \meta{name} 的复习点。这是专门为错题本或复习所定制的功能, +% \meta{points}是一系列整数,现在假设每道错题你都将写错时的日期记录在了 |date| +% 属性中,并且你希望每隔2,5,15天复习一次。下面的代码给出了一个实现示例。 +% \end{function} +% +% \begin{verbatim} +% \dbNewReviewPoints{review-point}{2, 5, 15} % 定义复习点 +% \begin{dbFilters} +% \dbNewConditional{cond1}{date}{review-point|\Today} % 定义复习条件 +% \dbCombineConditionals{filter1}{cond1} % 定义过滤器 +% \end{dbFilters} +% \dbNewStyle{review-style}{ques}{filter=filter1} % 定义展示样式 +% \end{verbatim} +% +% \subsection{在 \env{dbFilters} 环境中定义过滤器} +% +% \begin{environment}[added=2022-01-05]{dbFilters} +% \begin{syntax} +% |\begin{dbFilters}|\marg{database} \\ +% ~~\meta{code} +% |\end{dbFilters}| \\ +% \end{syntax} +% +% \env{dbFilters}用来定义过滤器,此环境中定义了 \cs{dbNewConditional} 命令用来 +% 定义条件和 \cs{dbCombineConditionals} 命令用来组合条件定义过滤器。过滤器独立 +% 于每个 \meta{database},这意味着你可以在不同数据库中定义名称相同的过滤条件和 +% 过滤器。 +% \end{environment} +% +% \begin{function}[added=2022-01-05, updated=2022-01-08]{\dbNewConditional, \dbNewConditional*} +% \begin{syntax} +% \cs{dbNewConditional} \marg{name} \marg{attr} \marg{cond spec} \\ +% \cs{dbNewConditional}* \marg{name} \marg{attr} \marg{cond spec} \\[2pt] +% \cs{dbNewConditional} \marg{name} \marg{int/fp attr} \marg{expr} \\ +% \cs{dbNewConditional}* \marg{name} \marg{int/fp attr} \marg{expr} \\ +% \cs{dbNewConditional} \marg{name} \marg{str/tl attr} \marg{regex expr} \\ +% \cs{dbNewConditional}* \marg{name} \marg{str/tl attr} \marg{regex expr} \\ +% \cs{dbNewConditional} \marg{name} \marg{clist attr} \marg{val list} \\ +% \cs{dbNewConditional}* \marg{name} \marg{clist attr} \marg{val list} \\ +% \cs{dbNewConditional} \marg{name} \marg{date attr} \marg{expr} +% \cs{dbNewConditional}* \marg{name} \marg{date attr} \{\meta{review points}\orbar\meta{date}\} \\ +% \end{syntax} +% +% \cs{dbNewConditional} 用来定义名为 \meta{name} 的条件,\meta{attr} 指定条件 +% 所绑定的属性,在 \meta{cond spec} 中可以用 \cs{dbval} 指代属性的值。 +% \end{function} +% +% \changes{1.3}{2022-01-10}{Update doc}{truncated division} +% 对于类型为 |int| 和 |fp| 的属性,\meta{expr} 传递给 \cs{int_compare:nTF} 或 +% \cs{fp_compare:nTF} 处理。 +% \begin{note} +% |/| 为四舍五入除法,截断除法请用 \cs{dbIntDivTruncate}。 +% \end{note} +% +% 对于类型为 |str| 和 |tl| 的属性,\meta{regex} 为正则表达式, +% \cs{dbNewConditional} 表示部分匹配,\cs{dbNewConditional*} 表示整体匹配。 +% +% \begin{verbatim} +% \dbNewConditional {cond1}{str-attr}{abc} % 匹配 abc, abcd, 1abc, =abc= 等 +% \dbNewConditional*{cond2}{str-attr}{abc} % 只匹配 abc +% \end{verbatim} +% +% 对于类型为 |clist| 的属性,使用 \cs{dbNewConditional} 定义的条件只要 +% \meta{val list} 中的任意一个元素在属性值(列表)中则条件成立;使用 +% \cs{dbNewConditional*} 定义的条件只有 \meta{val list} 中每一个值都在属性值 +% (列表)中条件才成立。 +% +% \begin{verbatim} +% \dbNewConditional {cond1}{clist-attr}{a, b, c} % a, b, d 满足条件 +% \dbNewConditional*{cond2}{clist-attr}{a, b, c} % a, b, d 不满足条件 +% \end{verbatim} +% +% \changes{1.3}{2022-01-08}{Update logic}{swap definition of starred and +% unstarred conditionals of date} +% 对于类型为 |date| 的属性,\cs{dbNewConditional} 定义的条件后续处理中会将 +% \meta{expr} 中的所有日期转换成相对\textit{1971年1月1日}的一个整数值,然后将 +% 处理后的表达式传递给 \cs{int_compare:nTF} 做进一步处理; +% \cs{dbNewConditional*} 使用复习点来定义过滤条件,\meta{review points} 是 +% \cs{dbNewReviewPoints} 定义的复习点,\meta{date} 是用来比较的日期。 +% +% \begin{function}[added=2022-01-05]{\dbCombineConditionals} +% \begin{syntax} +% \cs{dbCombineConditionals} \marg{name} \marg{cond combination} \oarg{info} +% \end{syntax} +% +% \cs{dbCombineConditionals} 定义名为 \marg{name} 的过滤器,并将 +% \cs{dbNewConditional} 定义的条件组合起来,比如 +% \verb=\dbCombineConditionals{filter}{(cond1 && cond2) || !cond3}=。 +% \meta{cond combination} 中可以使用的关系操作符为 \verb=&&, ||, !=。 +% 可以将 \opt{filter} 选项设置为 \meta{name} 来应用过滤器。\meta{info} 为过滤 +% 器的相关信息,在展示数据库的时候可以用 \cs{dbFilterInfo} 指代。 +% \end{function} +% +% \subsection{使用 \env{dbitem} 环境存储数据} +% +% \changes{1.4}{2022-01-13}{Update env}{dbitem} +% \begin{environment}[added=2022-01-05, updated=2022-01-13]{dbitem} +% \begin{syntax} +% |\begin{dbitem}| \marg{database} \oarg{attr-val list} +% ~~\meta{code} \\ +% |\end{dbitem}| +% \end{syntax} +% +% \env{dbitem} 环境用来存储数据。有两种存储数据的方法,较短的数据可以在选项列 +% 表中通过键值对设置值,较长的数据可以在 \meta{code} 中使用 \cs{dbsave} 存储。 +% \meta{attr} = \meta{val} 等同于 \cs{dbsave}\marg{attr}\marg{val}, +% \meta{attr}* = \meta{val} 等同于 \cs{dbsave*}\marg{attr}\marg{val},数据在 +% |e| 或者 |x| 类型的参数中不可展开。\cs{dbsave}会覆盖选项中设置的值。没有设 +% 置的值将会被设置为全局默认值,下面给出一个存储示例。 +% \end{environment} +% +% \begin{verbatim} +% \begin{dbitem}[date = 2022-01-01, info = 测试] +% \dbsave{question}{这是一个测试问题} +% \dbsave{answer} {这是一个测试答案} +% \end{dbitem} +% \end{verbatim} +% +% \changes{1.4}{2022-01-13}{Add macro}{\cs{dbitemkv}} +% \begin{function}[added=2022-01-13]{\dbitemkv} +% \begin{syntax} +% \cs{dbitemkv} \marg{database} \oarg{attr-val list} +% \end{syntax} +% +% 只使用 \meta{attr-val list} 来存储数据。 +% \end{function} +% +% \subsection{\cs{dbsave} 和 \cs{dbuse}} +% +% \changes{1.3}{2022-01-08}{Add macro}{\cs{dbsave*}} +% \begin{function}[added=2022-01-05, updated=2022-01-08]{\dbsave, \dbsave*} +% \begin{syntax} +% \cs{dbsave} \marg{attr} \marg{data} \\ +% \cs{dbsave}* \marg{attr} \marg{data} +% \end{syntax} +% +% \cs{dbsave} 用来存储数据,只能在 \env{item} 环境中使用。使用 \cs{dbsave*} 存 +% 储的数据会被 \cs{exp_not:n} 包裹。 +% \end{function} +% +% \changes{1.2}{2022-01-08}{Update macro}{make \cs{dbuse} fully-expandable} +% \begin{function}[added=2022-01-05, updated=2022-01-08, EXP]{\dbuse} +% \begin{syntax} +% \cs{dbuse} \marg{attr} +% \end{syntax} +% +% \cs{dbuse} 用来展示数据,只能在 \opt{item-code} 选项中使用。\cs{dbuse} 是可 +% 展开的。 +% \end{function} +% +% \subsection{条件判别式} +% +% \begin{function}[added=2022-01-05, EXP]{\dbIfEmptyT, \dbIfEmptyF, \dbIfEmptyTF} +% \begin{syntax} +% \cs{dbIfEmptyTF} \marg{true code} \marg{false code} \\ +% \cs{dbIfEmptyT} \marg{true code} \\ +% \cs{dbIfEmptyF} \marg{false code} +% \end{syntax} +% +% 该判别式用来判断当前数据库是否为空。下面的示例展示了如何预防空的列表环境。 +% \end{function} +% +% \begin{verbatim} +% \dbNewStyle{style-cond1}{database-test}{ +% before-code = {\dbIfEmptyF{\begin{enumerate}}}, +% after-code = {\dbIfEmptyF{\end{enumerate}}}, +% item-code = {\item \dbuse{attr-test}} +% } +% \end{verbatim} +% +% \changes{1.2}{2022-01-08}{Remove macros}{\cs{dbItemIfEmpty(TF)}, \cs{dbClistItemIfEmpty(TF)}} +% +% \subsection{表达式函数} +% +% \changes{1.3}{2022-01-10}{Add macros}{expression function aliases} +% \begin{function}[added=2022-01-10, EXP]{ +% \dbIntAbs, \dbIntSign, \dbIntDivRound, \dbIntDivTruncate, \dbIntMax, +% \dbIntMin, \dbIntMod, \dbFpSign, +% } +% \begin{syntax} +% \cs{dbIntAbs} \Arg{intexpr} +% \cs{dbIntSign} \Arg{intexpr} +% \cs{dbIntDivRound} \Arg{intexpr_1} \marg{intexpr_2} +% \cs{dbIntDivTruncate} \Arg{intexpr_1} \marg{intexpr_2} +% \cs{dbIntMax} \Arg{intexpr_1} \marg{intexpr_2} +% \cs{dbIntMin} \Arg{intexpr_1} \marg{intexpr_2} +% \cs{dbIntMod} \Arg{intexpr_1} \marg{intexpr_2} +% \cs{dbFpSign} \Arg{fpexpr} +% \end{syntax} +% \cs{dbIntAbs} 等同于 \cs{int_abs:n} \\ +% \cs{dbIntSign} 等同于 \cs{int_sign:n} \\ +% \cs{dbIntDivRound} 等同于 \cs{int_div_round:nn} \\ +% \cs{dbIntDivTruncate} 等同于 \cs{int_div_truncate:nn} \\ +% \cs{dbIntMax} 等同于 \cs{int_max:nn} \\ +% \cs{dbIntMin} 等同于 \cs{int_min:nn} \\ +% \cs{dbIntMod} 等同于 \cs{int_mod:nn} \\ +% \cs{dbFpSign} 等同于 \cs{fp_sign:n} \\ +% 详细的文档见 \pkg{interface3} \\ +% \end{function} +% +% \subsection{特殊命令} +% +% \pkg{dbshow} 定义了一些特殊的命令,会根据语境展开为不同的内容。 +% +% \changes{1.1}{2022-01-05}{Add macro}{ +% \cs{dbarabic}, \cs{dbalph}, \cs{dbAlph}, \cs{dbroman}, +% \cs{dbRoman} +% } +% \changes{1.1}{2022-01-06}{Fix bug}{\cs{dbIndex} not defined} +% \begin{function}[added=2022-01-05, EXP]{ +% \dbval, \dbDatabase, \dbFilterName, \dbFilterInfo, +% \dbIndex, \dbarabic, \dbalph, \dbAlph, \dbroman, \dbRoman +% } +% \begin{tblr}{ll} +% \cs{dbval} & 当前属性的值 \\ +% \cs{dbDatabase} & 数据库名称 \\ +% \cs{dbFilterName} & 当前样式过滤器的名称 \\ +% \cs{dbFilterInfo} & 当前样式过滤器的相关信息 \\ +% \cs{dbIndex} & 数据索引,等同于 \cs{dbuse}|{id}| \\ +% \cs{dbarabic} & 用数字表示的查询集数据计数 \\ +% \cs{dbalph} & 用小写字母表示的查询集数据计数 \\ +% \cs{dbAlph} & 用大写字母表示的查询集数据计数 \\ +% \cs{dbroman} & 用小写罗马字母表示的查询集数据计数 \\ +% \cs{dbroman} & 用大写罗马字母表示的查询集数据计数 \\ +% \end{tblr} +% \end{function} +% +% \section{错题本示例} +% 见第 \ref{sec:example} 节。 +% +% \changes{1.1}{2022-01-07}{Update doc}{improve example} +% +% \title{ +% Package \pkg{dbshow} \fileversion% +% \protect\footnote{% +% Repository: \url{https://github.com/ZhiyuanLck/dbshow}, +% Telegram Group: \url{https://t.me/latex_dbshow}} +% \rlap{\makebox[4cm][r]{ +% \normalsize $\Longrightarrow$ \color{red} +% \linktarget{zh}{en}{中文版本} +% }} +% } +% \author{Changkai Li \protect\path{<lichangkai225@qq.com>}} +% \date{\filedate} +% \maketitle +% +% \section{Introduction} +% +% The initial motivation to write this package is that I want to write a +% template, which can collect questions you gave the wrong answer and can +% display those questions you would like to review by some conditionals, such as +% questions with certain label, questions you have answered uncorrectly for +% certain times or questions having not been reviewed for certain days. So this +% package provides a database to do such thing. +% +% The package provides four core functions: data storage and display, data +% filtering, data sorting and data display. All data is saved once and then you +% can display these data with custom filters, orders and styles. +% +% \changes{1.4}{2022-01-10}{Add check}{version of \pkg{l3kernel}} +% \pkg{dbshow} depends on \pkg{l3kernel} with version date after |2022-11-07|. +% +% \changes{1.3}{2022-01-09}{Add doc}{descripton for expansion} +% \begin{itemize} +% \item Macros with a $\star$ are fully-expandable; +% \item Macros with a \ding{73} are restricted-expandable; +% \item Macros without appending a special symbol are nonexpandable; +% \item Options with a $\star$ \textit{do not} affect the expandability of +% related macros; +% \item Options with a \ding{73} affect the expandability of related macros +% according to its value; +% \item Options without appending a special symbol make the related macros +% nonexpandable. +% \end{itemize} +% +% \subsection{Data Types} +% +% The package constructs 6 types based on the internal typed of \pkg{expl3}: +% \begin{Description}[\texttt{clist}] +% \item[\texttt{date}] +% date saved in |yyyy/mm/dd| format, supports comparison, sorting +% (converting to string), default \cs{dbtoday}. +% \item[\texttt{str}] +% string,supports regex match and sorting, default empty. +% \item[\texttt{tl}] +% token list, supports regex match, default empty. +% \item[\texttt{int}] +% integer, supports comparison and sorting, default 0. +% \item[\texttt{fp}] +% floating point, supports comparison and sorting, default 0. +% \item[\texttt{clist}] +% comma list, default empty. +% \end{Description} +% +% \changes{1.3}{2022-01-08}{Remove dependency}{\pkg{datatime2}} +% All types are internal types of \pkg{expl3} except |date| type, which provides +% by \pkg{dbshow} itself and supports converting to integer and printing with +% style. +% +% \subsection{Comparison to \pkg{datatool}} +% +% \changes{1.2}{2022-01-07}{Add doc}{add comparison to \pkg{datatool}} +% +% \pkg{dbshow} and \pkg{datatool} implement the same core functions. But +% \pkg{dbshow} is based on \pkg{expl3} and it supports string regex and +% multi-level sorting. \pkg{dbshow} tries to divide style from the contents +% (data in database): all styles are predefined and can be reused conveniently +% so that there can be only codes to save data and one-line code to show the +% database inside the \env{document} environment. You can hide the details in +% the preamble and focus on the data you want to display. \pkg{dbshow} provides +% a simple temporary runtime database, which means it can not input and output +% data from/to extern files (they should be responsible by some professional +% programming languages). When you need to delete or revise a record, just go to +% where it is recorded in the source code rather than use a macro to manipulate +% data after they are saved. In a sense, \TeX~file is also a kind of data +% persistence. +% +% \section{Interfaces} +% +% \subsection{Create, Display and Clear Database} +% +% \changes{1.4}{2022-01-10}{Update macro}{change \orbar\ to \cs{dbshow_sep} in +% weird argument of \cs{dbshow_process_default_value:w}} +% \begin{function}[added=2022-01-05, updated=2022-01-10]{\dbNewDatabase, \dbNewDatabase*} +% \begin{syntax} +% \cs{dbNewDatabase} \oarg{base database} \marg{database} \{ \\ +% ~~\meta{attr1} = \meta{type spec1}, \\ +% ~~\meta{attr2} = \meta{type spec2}, \\ +% ~~\ldots{} \\ +% \} \\ +% \cs{dbNewDatabase}* \marg{database} \{ \\ +% ~~\meta{attr1} = \meta{type spec1}, \\ +% ~~\meta{attr2} = \meta{type spec2}, \\ +% ~~\ldots{} \\ +% \} \\ +% \end{syntax} +% +% \end{function} +% +% Create a new database named \meta{database}, unstarred form provides the optional +% \meta{base database} from which current database inherit the attributes settings. +% The unstarred form always replace the old definition, while starred form +% appends the new options. +% +% \begin{syntax} +% \meta{attr} = \meta{type} \\ +% \meta{attr} = \meta{type}\orbar\meta{default} +% \end{syntax} +% +% The first form defines the \meta{attr} as \meta{type}, and the second also +% sets the default value. +% +% \begin{note} +% Every database has a default attribute |id| to store the index of the item. +% \end{note} +% +% The example below define a database named |ques|. +% \begin{verbatim} +% \dbNewDatabase{ques}{ +% question = tl, % store question +% answer = tl, % store corresponding answer +% date = date, % store the date when you were wrong +% info = tl, % store extra info +% labels = clist % store question labels +% } +% \end{verbatim} +% +% \begin{function}[added=2022-01-05]{\dbshow} +% \begin{syntax} +% \cs{dbshow} \marg{style} \marg{database} +% \end{syntax} +% +% Show the \meta{database} with \meta{style}. +% \end{function} +% +% \changes{1.2}{2022-01-07}{Add macro}{\cs{dbclear}} +% \begin{function}[added=2022-01-07]{\dbclear} +% \begin{syntax} +% \cs{dbclear} \marg{database} +% \end{syntax} +% +% Clear the content of \meta{database}. +% \end{function} +% +% \subsection{\cs{dbNewStyle} and Style Options} +% +% \begin{function}[added=2022-01-05]{\dbNewStyle} +% \begin{syntax} +% \cs{dbNewStyle} \oarg{base styles} \marg{style} \marg{database} \marg{opts} +% \end{syntax} +% +% Define a new \meta{style} that binds to \meta{database}. The style can +% inherit from a list of \meta{base styles} such as +% |\dbNewStyle[base1, base2]{new-style}{ques}{}|. +% \end{function} +% +% \subsubsection{General Options} +% +% \begin{option}[added=2022-01-05]{filter} +% \begin{syntax} +% filter = <filter> +% \end{syntax} +% +% Set the \meta{filter} defined by \cs{dbCombineFilters}. +% \end{option} +% +% \changes{1.1}{2022-01-06}{Add option}{\opt{raw-filter}} +% \begin{option}[added=2022-01-06]{raw-filter} +% \begin{syntax} +% raw-filter = <conditional expression> +% \end{syntax} +% +% Set anonymous with conditionals defined by \cs{dbNewConditional}. Two +% filters shows in the code below have the same meaning. +% \end{option} +% +% \begin{verbatim} +% % method 1 +% \begin{dbFilters}{db} +% \dbNewConditional{cond1}{int-attr}{\rval > 1} +% \dbNewConditional*{cond2}{str-attr}{\d+} +% \end{dbFilters} +% \dbNewStyle{style}{db}{raw-filter={cond1 && cond2}} +% % method 2 +% \begin{dbFilters}{db} +% \dbNewConditional{cond1}{int-attr}{\rval > 1} +% \dbNewConditional*{cond2}{str-attr}{\d+} +% \dbCombineFilters{filter}{cond1 && cond2} +% \end{dbFilters} +% \dbNewStyle{style}{db}{filter=filter} +% \end{verbatim} +% +% \changes{1.2}{2022-01-08}{Fix bug}{string sorting bug} +% \begin{option}[added=2022-01-05]{sort} +% \begin{syntax} +% sort = \{ <attr spec1>, <attr spec2>, \ldots{} \} +% \end{syntax} +% +% Set sorting rules. Attributes of type |str, date, int, fp| is supported to +% sort. Multi-level sort is allowed. \meta{attr} represents for ascending +% order, and \meta{attr}* represents for descending order. The example below +% use four fields to determine the order of the records. It sorts on |level| +% in descending order first and if two |levels| are same then sorts on |birth| +% in ascending order and so on. +% \end{option} +% +% \begin{verbatim} +% \dbNewDatabase{sort-example}{ +% name = str, +% birth = date, +% level = int, +% weight = fp, +% } +% \dbNewStyle{sort-style}{sort-example}{ +% sort = { level*, birth, name, weight } +% } +% \end{verbatim} +% +% \begin{option}[added=2022-01-05, rEXP]{item-code} +% \begin{syntax} +% item-code = <code> +% \end{syntax} +% +% Set the code that show a single record. You can use \cs{dbuse} to display +% certian attribute. +% \end{option} +% +% \changes{1.3}{2022-01-09}{Update option}{\opt{<attr>/sep}} +% \begin{option}[added=2022-01-05, updated=2022-01-08, rEXP]{<attr>/sep} +% \begin{syntax} +% <attr>/sep = <separator> \\ +% <attr>/sep = \{ \\ +% ~~\meta{separator between two}, \\ +% ~~\meta{separator between more than two}, \\ +% ~~\meta{separator between final two} \\ +% \} \\ +% <attr>/sep = \{ \\ +% ~~\meta{separator before year}, \\ +% ~~\meta{separator between year and month}, \\ +% ~~\meta{separator between month and day}, \\ +% ~~\meta{separator after day} \\ +% \} \\ +% \end{syntax} +% +% Only for attributes of type |clist| or |date|. Set the separator between +% items. If the argument is an one-item comma list, all separators are set to +% \meta{separator} but \meta{separator before year} and \meta{separator after +% day} is set empty. +% \end{option} +% +% If the argument is a comma list of 3 items, it is used to set the separator +% between items of the comma list. Following documentation is quoted from +% \pkg{interface3}: +% \begin{quote} +% If the comma list has more than two items, the \meta{separator between +% more than two} is placed between each pair of items except the last, for +% which the \meta{separator between final two} is used. If the comma list +% has exactly two items, then they are placed in the input stream separated +% by the \meta{separator between two}. If the comma list has a single item, +% it is placed in the input stream, and a comma list with no items produces +% no output. +% \end{quote} +% For attributes of type |clist|, incorrect number (numbers exclude 1 and 3) +% of items of the argument will raise an error. +% +% \begin{verbatim} +% % clist-attr is an attribute of database db +% % suppose the val of clist-attr is { 1, 2, 3 } +% \dbNewStyle{clist-sep}{db}{ +% clist-attr/sep = { ,~ }, % print 1, 2, 3 +% clist-attr/sep = { {,~}, {,~}, {and~} } % print 1, 2 and 3 +% } +% \end{verbatim} +% +% If the argument is a comma list of 4 items, it is used to set the separators +% of the date. For attributes of type |date|, incorrect number (numbers +% exclude 1 and 4) will raise an error. +% +% \begin{verbatim} +% % date-attr is an attribute of database db +% % suppose the val of date-attr is 2022/01/01 +% \dbNewStyle{date-sep}{db}{ +% date-attr/sep = -, % print 2022-01-01 +% date-attr/sep = { |, -, -, | } % print |2022-01-01| +% } +% \end{verbatim} +% +% \changes{1.3}{2022-01-08}{Add option}{\opt{<attr>/zfill}} +% \begin{option}[added=2022-01-08, EXP]{<attr>/zfill} +% \begin{syntax} +% <attr>/zfill = <\TTF> +% \end{syntax} +% +% Only for attributes of type |date|. Control whether to fill zero on the left +% of the month or day. +% \end{option} +% +% \changes{1.4}{2022-01-13}{Add macro}{\cs{dbdatesep}} +% \begin{function}[added=2022-01-13, EXP]{\dbdatesep} +% \begin{syntax} +% \cs{dbdatesep} \marg{separator} +% \end{syntax} +% +% Set the separator for internal date parsing. The default value is |/|, +% i.e. the date must be store in the format of |yyyy/mm/dd|. +% \end{function} +% +% \subsubsection{Decorators} +% +% The options below serves as decorators. In some cases, decorator can also be +% encoded directly into |item-code| or some other places, which is convenient +% sometimes. The benefit of defining decorators with options is that styles step +% further to be divided with contents. In the examples below, \meta{style1} and +% \meta{style2} is the same style, which wrap \meta{attr1} with *. When you want +% another style which wrap \meta{attr1} with =, if you choose the way of +% \meta{style1}, \meta{item code} are repeated, otherwise if you choose the way +% of \meta{style2}, \meta{item code} is inherited and you only need define the +% decorators. +% +% \begin{verbatim} +% \dbNewStyle{style1}{db}{ +% item-code = {% +% *\rvuse{attr1}*\rvuse{attr2} +% % more code +% } +% } +% \dbNewStyle{base-style}{db}{ +% item-code = {% +% \rvuse{attr1}\rvuse{attr2} +% % more code +% } +% } +% \dbNewStyle[base-style]{style2}{db}{ +% attr1/before-code = { * }, +% attr1/after-code = { * }, +% } +% \dbNewStyle[base-style]{style3}{db}{ +% attr1/before-code = { = }, +% attr1/after-code = { = }, +% } +% \end{verbatim} +% +% \changes{1.3}{2022-01-08}{Add option}{\opt{<attr>/wrapper}} +% \begin{option}[added=2022-01-08, rEXP]{<attr>/wrapper} +% \begin{syntax} +% <attr>/wrapper = <control sequence> +% \end{syntax} +% +% Only for attributes of type |date|. Output of \cs{dbuse}\marg{date attr} +% will be \meta{control sequence}\marg{date}. +% \end{option} +% +% \begin{option}[added=2022-01-05, rEXP]{before-code} +% \begin{syntax} +% before-code = <code> +% \end{syntax} +% +% Set the \meta{code} that is executed \enbefore displaying the database. +% \end{option} +% +% \begin{option}[added=2022-01-05, rEXP]{after-code} +% \begin{syntax} +% after-code = <code> +% \end{syntax} +% +% Set the \meta{code} that is executed \enafter displaying the database. +% \end{option} +% +% \changes{1.2}{2022-01-08}{Add options}{\opt{record-before-code}, +% \opt{record-after-code}} +% \begin{option}[added=2022-01-05, rEXP]{record-before-code} +% \begin{syntax} +% record-before-code = <code> +% \end{syntax} +% +% Set the \meta{code} that is executed \enbefore displaying a record. +% \end{option} +% +% \begin{option}[added=2022-01-05, rEXP]{record-after-code} +% \begin{syntax} +% record-after-code = <code> +% \end{syntax} +% +% Set the \meta{code} that is executed \enafter displaying the record. +% \end{option} +% +% \begin{option}[added=2022-01-05, rEXP]{<attr>/before-code} +% \begin{syntax} +% <attr>/before-code = <code> +% \end{syntax} +% +% Set the \meta{code} that is executed by \cs{dbuse} \enbefore displaying +% certain attribute. +% \end{option} +% +% \begin{option}[added=2022-01-05, rEXP]{<attr>/after-code} +% \begin{syntax} +% <attr>/after-code = <code> +% \end{syntax} +% +% Set the \meta{code} that is executed by \cs{dbuse} \enafter displaying +% certain attribute. +% \end{option} +% +% \begin{option}[added=2022-01-05, rEXP]{<attr>/item-before-code} +% \begin{syntax} +% <attr>/item-before-code = <code> +% \end{syntax} +% +% Only for attributes of type |clist|. Set the \meta{code} that is excuted +% \enbefore displaying the item of the comma list. +% \end{option} +% +% \begin{option}[added=2022-01-05, rEXP]{<attr>/item-after-code} +% \begin{syntax} +% <attr>/item-after-code = <code> +% \end{syntax} +% +% Only for attributes of type |clist|. Set the \meta{code} that is excuted +% \enafter displaying the item of the comma list. +% \end{option} +% +% \subsection{Use \cs{dbNewReviewPoints} to Define Review Points} +% +% \begin{function}[added=2022-01-05]{\dbNewReviewPoints} +% \begin{syntax} +% \cs{dbNewReviewPoints} \marg{name} \marg{points} +% \end{syntax} +% +% Define the new \meta{points} that is specially designed for reviewing +% something. \meta{points} is a list of integers. Suppose you record the date +% when you did not answer correctly and you plan to review every 2, 5 and 15 +% days. The following code give what you want. +% \end{function} +% +% \begin{verbatim} +% \dbNewReviewPoints{review-point}{2, 5, 15} % define points +% \begin{dbFilters} +% \dbNewConditional{cond1}{date}{review-point|\Today} % define conditional +% \dbCombineConditionals{filter1}{cond1} % define filter +% \end{dbFilters} +% \dbNewStyle{review-style}{ques}{filter=filter1} % define style +% \end{verbatim} +% +% \subsection{Define Filters inside \env{dbFilters} Environment} +% +% \begin{environment}[added=2022-01-05]{dbFilters} +% \begin{syntax} +% |\begin{dbFilters}|\marg{database} \\ +% ~~\meta{code} +% |\end{dbFilters}| \\ +% \end{syntax} +% +% Filters are defined inside \env{dbFilters} environment, inside which, +% \cs{dbNewConditional} is defined to declare conditionals and +% \cs{dbCombineConditionals} is defined to combine conditionals. Filters are +% independent in different databases, which means the same name of filters is +% allowed in different databases. +% \end{environment} +% +% \begin{function}[added=2022-01-05, updated=2022-01-08]{\dbNewConditional, \dbNewConditional*} +% \begin{syntax} +% \cs{dbNewConditional} \marg{name} \marg{attr} \marg{cond spec} \\ +% \cs{dbNewConditional}* \marg{name} \marg{attr} \marg{cond spec} \\[2pt] +% \cs{dbNewConditional} \marg{name} \marg{int/fp attr} \marg{expr} \\ +% \cs{dbNewConditional}* \marg{name} \marg{int/fp attr} \marg{expr} \\ +% \cs{dbNewConditional} \marg{name} \marg{str/tl attr} \marg{regex expr} \\ +% \cs{dbNewConditional}* \marg{name} \marg{str/tl attr} \marg{regex expr} \\ +% \cs{dbNewConditional} \marg{name} \marg{clist attr} \marg{val list} \\ +% \cs{dbNewConditional}* \marg{name} \marg{clist attr} \marg{val list} \\ +% \cs{dbNewConditional} \marg{name} \marg{date attr} \marg{expr} +% \cs{dbNewConditional}* \marg{name} \marg{date attr} \{\meta{review points}\orbar\meta{date}\} \\ +% \end{syntax} +% +% Define the conditional named \meta{name} that binds to \meta{attr}. \cs{dbval} +% is replaced with the real value of the attribute inside the \meta{cond spec}. +% \end{function} +% +% \changes{1.3}{2022-01-10}{Update doc}{truncated division} +% For attributes of type |int| and |fp|, \meta{expr} is passed to +% \cs{int_compare:nTF} or \cs{fp_compare:nTF}. +% \begin{note} +% Division using |/| rounds to the closest integer. Use \cs{dbIntDivTruncate} to rounds +% the result toward 0. +% \end{note} +% +% For attribute of type |str| and |tl|, unstarred form matches any part while +% starred form matches the whole part with the \meta{regex expr}. +% +% \begin{verbatim} +% \dbNewConditional {cond1}{str-attr}{abc} % match abc, abcd, 1abc, =abc=, etc +% \dbNewConditional*{cond2}{str-attr}{abc} % only match abc +% \end{verbatim} +% +% For attributes of type |clist|, the conditional defined by unstarred form is +% true if any item of \meta{val list} is in the comma list. While the +% conditional defined by starred form is true only if every item of \meta{val +% list} is in the comma list. As is showed below, for |cond1|, |a| is in +% |{a, b, d}| so |cond1| is true. While |c| is not in |{a, b, d}| so |cond2| +% is false. +% +% \begin{verbatim} +% \dbNewConditional {cond1}{clist-attr}{a, b, c} % {a, b, d} -> true +% \dbNewConditional*{cond2}{clist-attr}{a, b, c} % {a, b, d} -> false +% \end{verbatim} +% +% \changes{1.3}{2022-01-08}{Update logic}{swap definition of starred and +% unstarred conditionals of date} +% For attributes of type |date|, unstarred form replace each date with a +% integer representing for the days between \meta{date} and +% \textit{1971/01/01}, and the result is passed to \cs{int_compare:nTF}. +% Starred form defines the conditional with review points defined by +% \cs{dbNewRdbNewReviewPoints} and \meta{date} is the date to be compared. +% +% \begin{function}[added=2022-01-05]{\dbCombineConditionals} +% \begin{syntax} +% \cs{dbCombineConditionals} \marg{name} \marg{cond combination} \oarg{info} +% \end{syntax} +% +% Define the filter \meta{name}, which combine the conditionals and store the +% extra \meta{info} into \cs{dbFilterInfo}. So you can write something as\\ +% \verb=\dbCombineConditionals{filter}{(cond1 && cond2) || !cond3}=.\\ +% Supported operators are \verb=&&, ||, !=. You can set the option \opt{filter} +% to \meta{name} to apply the filter when you display the database. +% \end{function} +% +% \subsection{Store Data with \env{dbitem} Environment} +% +% \changes{1.4}{2022-01-13}{Update env}{dbitem} +% \begin{environment}[added=2022-01-05, updated=2022-01-13]{dbitem} +% \begin{syntax} +% |\begin{dbitem}| \marg{database} \oarg{attr-val list} +% ~~\meta{code} \\ +% |\end{dbitem}| +% \end{syntax} +% +% The data are stored with \env{dbitem} environment in two ways. Short data +% can be stored in \meta{attr-val list} and long data can be stored by +% \cs{dbsave}, which will suppress the value set by the option list. +% \meta{attr} = \meta{val} is equal to \cs{dbsave}\marg{attr}\marg{val}, and +% \meta{attr} = \meta{val} is equal to \cs{dbsave*}\marg{attr}\marg{val}, +% in which case, data will not be expanded in an |e| or |x|-type argument. +% An example code is showned below. +% \end{environment} +% +% \begin{verbatim} +% \begin{dbitem}[date=2022-01-01, info=test] +% \dbsave{question}{This is a test question.} +% \dbsave{answer} {This is a test answer.} +% \end{dbitem} +% \end{verbatim} +% +% \changes{1.4}{2022-01-13}{Add macro}{\cs{dbitemkv}} +% \begin{function}[added=2022-01-13]{\dbitemkv} +% \begin{syntax} +% \cs{dbitemkv} \marg{database} \oarg{attr-val list} +% \end{syntax} +% +% Store data with \meta{attr-val list}. +% \end{function} +% +% \subsection{\cs{dbsave} and \cs{dbuse}} +% +% \changes{1.3}{2022-01-08}{Add macro}{\cs{dbsave*}} +% \begin{function}[added=2022-01-05, updated=2022-01-08]{\dbsave, \dbsave*} +% \begin{syntax} +% \cs{dbsave} \marg{attr} \marg{data} \\ +% \cs{dbsave}* \marg{attr} \marg{data} +% \end{syntax} +% +% \cs{dbsave} save the \meta{data} to \meta{attr} of current record. +% \cs{dbsave} can be used only inside the \env{dbitem} environment. +% \meta{data} stored by \cs{dbsave*} is wrapped with \cs{exp_not:n} while +% \meta{data} stored by \cs{dbsave} keeps the same. +% \end{function} +% +% \changes{1.2}{2022-01-08}{Update macro}{make \cs{dbuse} fully-expandable} +% \begin{function}[added=2022-01-05, updated=2022-01-08, EXP]{\dbuse} +% \begin{syntax} +% \cs{dbuse} \marg{attr} +% \end{syntax} +% +% Display the value of \meta{attr} of current record. \cs{dbuse} is +% \textbf{expandable} and can be only used inside the option \opt{item-code}. +% \end{function} +% +% \subsection{Conditionals} +% +% \begin{function}[added=2022-01-05, EXP]{\dbIfEmptyT, \dbIfEmptyF, \dbIfEmptyTF} +% \begin{syntax} +% \cs{dbIfEmptyTF} \marg{true code} \marg{false code} \\ +% \cs{dbIfEmptyT} \marg{true code} \\ +% \cs{dbIfEmptyF} \marg{false code} +% \end{syntax} +% +% Test if the database is empty. The example below shows how to avoid an empty +% list environment. +% \end{function} +% +% \begin{verbatim} +% \dbNewStyle{style-cond1}{database-test}{ +% before-code = {\dbIfEmptyF{\begin{enumerate}}}, +% after-code = {\dbIfEmptyF{\end{enumerate}}}, +% item-code = {\item \dbuse{attr-test}} +% } +% \end{verbatim} +% +% \changes{1.2}{2022-01-08}{Remove macros}{\cs{dbItemIfEmpty(TF)}, \cs{dbClistItemIfEmpty(TF)}} +% +% \subsection{Expression Functions} +% +% \changes{1.3}{2022-01-10}{Add macros}{expression function aliases} +% \begin{function}[added=2022-01-10, EXP]{ +% \dbIntAbs, \dbIntSign, \dbIntDivRound, \dbIntDivTruncate, \dbIntMax, +% \dbIntMin, \dbIntMod, \dbFpSign, +% } +% \begin{syntax} +% \cs{dbIntAbs} \Arg{intexpr} +% \cs{dbIntSign} \Arg{intexpr} +% \cs{dbIntDivRound} \Arg{intexpr_1} \marg{intexpr_2} +% \cs{dbIntDivTruncate} \Arg{intexpr_1} \marg{intexpr_2} +% \cs{dbIntMax} \Arg{intexpr_1} \marg{intexpr_2} +% \cs{dbIntMin} \Arg{intexpr_1} \marg{intexpr_2} +% \cs{dbIntMod} \Arg{intexpr_1} \marg{intexpr_2} +% \cs{dbFpSign} \Arg{fpexpr} +% \end{syntax} +% \cs{dbIntAbs} is identical to \cs{int_abs:n} \\ +% \cs{dbIntSign} is identical to \cs{int_sign:n} \\ +% \cs{dbIntDivRound} is identical to \cs{int_div_round:nn} \\ +% \cs{dbIntDivTruncate} is identical to \cs{int_div_truncate:nn} \\ +% \cs{dbIntMax} is identical to \cs{int_max:nn} \\ +% \cs{dbIntMin} is identical to \cs{int_min:nn} \\ +% \cs{dbIntMod} is identical to \cs{int_mod:nn} \\ +% \cs{dbFpSign} is identical to \cs{fp_sign:n} \\ +% Detailed documentation see \pkg{interface3} \\ +% \end{function} +% +% \subsection{Special Macros} +% +% Some special macros are defined to expand to different contents according to context. +% +% \changes{1.1}{2022-01-05}{Add macro}{ +% \cs{dbarabic}, \cs{dbalph}, \cs{dbAlph}, \cs{dbroman}, +% \cs{dbRoman} +% } +% \changes{1.1}{2022-01-06}{Fix bug}{\cs{dbIndex} not defined} +% \begin{function}[added=2022-01-05, EXP]{ +% \dbval, \dbDatabase, \dbFilterName, \dbFilterInfo, +% \dbIndex, \dbarabic, \dbalph, \dbAlph, \dbroman, \dbRoman +% } +% \begin{tblr}{ll} +% \cs{dbval} & Attribute value, only according in \cs{dbNewConditional}. \\ +% \cs{dbDatabase} & Database name. \\ +% \cs{dbFilterName} & Filter name. \\ +% \cs{dbFilterInfo} & Filter information. \\ +% \cs{dbIndex} & Record index, identical to \cs{dbuse}|{id}| \\ +% \cs{dbarabic} & Show the counter of query set as digits. \\ +% \cs{dbalph} & Show the counter of query set as lowercase letters. \\ +% \cs{dbAlph} & Show the counter of query set as uppercase letters. \\ +% \cs{dbroman} & Show the counter of query set as lowercase roman numerals. \\ +% \cs{dbroman} & Show the counter of query set as uppercase roman numerals. \\ +% \end{tblr} +% \end{function} +% +% \section{Example of Flaw Sweeper Template} +% \label{sec:example} +% +% \changes{1.1}{2022-01-07}{Update doc}{improve example} +% +% \begin{verbatim} +% \documentclass{article} +% \usepackage{amsmath, physics} +% \usepackage{geometry} +% \usepackage{dbshow} +% \usepackage{tikz} +% \usepackage{tcolorbox} +% \tcbuselibrary{skins} +% \usetikzlibrary{shadings} +% \usepackage[hidelinks]{hyperref} +% +% \geometry{ +% margin=2cm +% } +% +% % #1 link node #2 target node #3 text to show +% \NewDocumentCommand \linktarget { m m m } {% +% \hyperlink{#1}{#3}% +% \raisebox{1em}{\hypertarget{#2}{}}% +% } +% +% % question box +% \tcbset{ +% base/.style={ +% empty, +% frame engine=path, +% colframe=yellow!10, +% coltitle=red!70, +% fonttitle=\bfseries\sffamily, +% sharp corners, +% left=4pt, +% right=4pt, +% drop fuzzy shadow, +% drop fuzzy shadow, +% borderline west={3pt}{-3pt}{red!80}, +% } +% } +% +% \newtcolorbox{mybox}[1]{% +% base, title = {#1} +% } +% +% \dbNewReviewPoints{review}{1, 3, 7, 15, 30, 60} +% +% \dbNewDatabase{ques-book}{ +% ques = tl, +% answer = tl, +% count = int|1, +% labels = clist, +% date = date, +% } +% +% \begin{dbFilters}{ques-book} +% \dbNewConditional{hard}{labels}{hard} +% \dbNewConditional{bad}{count}{\dbval > 1} +% \dbNewConditional*{review}{date}{review|2022/01/07} +% \dbNewConditional{after}{date}{\dbval > 2022/01/02} +% \end{dbFilters} +% +% % show all questions with hyperlink to answers +% \dbNewStyle{ques}{ques-book}{ +% before-code = {\section{Questions}}, +% item-code = { +% \begin{mybox}{% +% \linktarget{answer_\dbIndex}{ques_\dbIndex}{% +% Question \dbarabic% +% \hspace{2em}\dbuse{date}% +% \hspace{2em}\dbuse{labels}% +% \hfill\dbuse{count}% +% }% +% } +% \dbuse{ques}% +% \end{mybox} +% }, +% labels/sep = /, +% } +% +% % show all questions and answers with hyperlink to questions +% \dbNewStyle{answer}{ques-book}{ +% before-code = {\section{Questions and Answers}}, +% item-code = { +% \begin{mybox}{% +% \linktarget{ques_\dbIndex}{answer_\dbIndex}{% +% Question \dbarabic% +% \hspace{2em}\dbuse{date}% +% \hspace{2em}\dbuse{labels}% +% \hfill\dbuse{count}% +% }% +% } +% \dbuse{ques}\tcbsubtitle{Answer}\dbuse{answer}% +% \end{mybox} +% }, +% labels/sep = /, +% } +% +% % show all hard questions with hyperlink to answers +% \dbNewStyle{hard}{ques-book}{ +% before-code = {\section{Hard Questions}}, +% item-code = { +% \begin{mybox}{% +% \hyperlink{answer_\dbIndex}{% +% Question \dbarabic% +% \hspace{2em}\dbuse{date}% +% \hspace{2em}\dbuse{labels}% +% \hfill\dbuse{count}% +% }% +% } +% \dbuse{ques}% +% \end{mybox} +% }, +% raw-filter = hard, +% labels/sep = /, +% } +% +% % show all hard questions that have been answered incorrectly for more than +% % one time with hyperlink to answers +% \dbNewStyle[hard]{bad}{ques-book}{ +% before-code = {\section{Bad Questions}}, +% raw-filter = {bad && hard}, +% } +% % show all questions that plan to be reviewed on 2022/01/07 with hyperlink to +% % answers +% \dbNewStyle[hard]{review}{ques-book}{ +% before-code = {\section{Questions to be Reviewed}}, +% raw-filter = {review}, +% } +% % show all questions that is record after 2022/01/02 with hyperlink to answers +% \dbNewStyle[hard]{after}{ques-book}{ +% before-code = {\section{Questions after 2022/01/02}}, +% raw-filter = {after}, +% } +% +% \AtEndDocument{ +% \dbshow{review}{ques-book} +% \dbshow{hard}{ques-book} +% \dbshow{bad}{ques-book} +% \dbshow{after}{ques-book} +% \dbshow{ques}{ques-book} +% \dbshow{answer}{ques-book} +% } +% +% \begin{document} +% +% \begin{dbitem}{ques-book}[ +% date=2022/01/01, +% labels={math, equation, easy}, +% count=2 +% ] +% \dbsave{ques}{% +% Solve the linear equation: $x + 16 = 31$. +% } +% \dbsave{answer}{% +% $x = 31 - 16 = 15$ +% } +% \end{dbitem} +% +% \begin{dbitem}{ques-book}[ +% date=2022/01/01, +% labels={math, equation, hard}, +% count=3 +% ] +% \dbsave{ques}{% +% Solve the linear equation: $2y = 16$. +% } +% \dbsave{answer}{% +% $y = 16 / 2 = 8$ +% } +% \end{dbitem} +% +% \begin{dbitem}{ques-book}[ +% date=2022/01/04, +% labels={math, integral, hard}, +% count=1 +% ] +% \dbsave{ques}{% +% Find the indefinite integral: $\int 2x \dd x$. +% } +% \dbsave{answer}{% +% $\int 2x \dd x = x^2$ +% } +% \end{dbitem} +% +% \end{document} +% \end{verbatim} +% +% \end{documentation} +% +% \StopEventually{} +% +% \newgeometry{ +% left=5.5cm, +% right=2cm, +% top=2cm, +% bottom=2cm +% } +% +% \begin{implementation} +% +% \section{Implementation} +% +% \changes{1.4}{2022-01-13}{Update code}{merge code and doc into +% \pkg{dbshow.dtx}} +% +% \begin{macrocode} +%<*package> +%<@@=dbshow> +% \end{macrocode} +% +% Check version of \pkg{l3kernel}. +% \begin{macrocode} +% prop_concat, prop_gset_from_keyval +\__kernel_dependency_version_check:nn { 2021-11-07 } { l3prop } +% str_compare +\__kernel_dependency_version_check:nn { 2021-05-17 } { l3str } +% clist_map_tokens, clist_use +\__kernel_dependency_version_check:nn { 2021-05-10 } { l3clist } +% \end{macrocode} +% +% \subsection{Variants and Variables} +% +% \begin{macrocode} +\cs_generate_variant:Nn \msg_warning:nnnn { nnnx } +\cs_generate_variant:Nn \keys_set:nn { nv } +\cs_generate_variant:Nn \clist_use:nn { xx } +\cs_generate_variant:Nn \clist_use:nnnn { xxxx } +\cs_generate_variant:Nn \clist_map_inline:nn { Vn } +\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 } +% \end{macrocode} +% +% \begin{variable}{\c_@@_default_value_prop} +% Default default value of different types. +% \begin{macrocode} +\prop_const_from_keyval:Nn \c_@@_default_value_prop { + date = \dbtoday, + str = , + tl = , + clist = , + int = 0, + fp = 0 +} +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\@@_type_clist} +% Supported types by \pkg{dbshow}. +% \begin{macrocode} +\clist_const:Nn \@@_type_clist { date, str, tl, clist, int, fp } +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\g_@@_raw_filter_int} +% Counter of anonymous filter. +% \begin{macrocode} +\int_gzero_new:N \g_@@_raw_filter_int +% \end{macrocode} +% \end{variable} +% +% \subsection{Messages} +% +% \begin{arguments} +% \item \meta{database} +% \end{arguments} +% \begin{macrocode} +\msg_new:nnn { dbshow } { non-existent-database } { + Database~'#1'~does~not~exist~\msg_line_context:. +} +% \end{macrocode} +% +% \begin{macro}{\@@_check_database:n} +% Check if the database is valid. +% \begin{arguments} +% \item \meta{database} +% \end{arguments} +% \begin{macrocode} +\cs_new:Nn \@@_check_database:n { + \prop_if_exist:cF { g_@@_attr_type_prop_#1 } + { \msg_fatal:nnn { dbshow } { non-existent-database } {#1} } +} +% \end{macrocode} +% \end{macro} +% +% \begin{arguments} +% \item \meta{database} +% \item \meta{attr} +% \end{arguments} +% \begin{macrocode} +\msg_new:nnn { dbshow } { non-existent-attr } { + Attribute~'#2'~of~database~'#1'~does~not~exist~\msg_line_context:. +} +% \end{macrocode} +% +% \begin{macro}{\@@_check_attr:nn, \@@_check_attr:nV} +% Check if the attribute is valid. +% \begin{arguments} +% \item \meta{database} +% \item \meta{attr} +% \end{arguments} +% \begin{macrocode} +\cs_new:Nn \@@_check_attr:nn { + \prop_if_in:cnF { g_@@_attr_type_prop_#1 } {#2} + { \msg_fatal:nnnn { dbshow } { non-existent-attr } {#1} {#2} } +} +\cs_generate_variant:Nn \@@_check_attr:nn { nV } +% \end{macrocode} +% \end{macro} +% +% \begin{arguments} +% \item \meta{style} +% \item \meta{database} +% \end{arguments} +% \begin{macrocode} +\msg_new:nnn { dbshow } { non-existent-style } { + Style~'#1'~of~database~'#2'~does~not~exist~\msg_line_context:. +} +% \end{macrocode} +% +% \begin{macro}{\@@_check_style:nn} +% Check if the style is valid. +% \begin{arguments} +% \item \meta{style} +% \item \meta{database} +% \end{arguments} +% \begin{macrocode} +\cs_new:Nn \@@_check_style:nn { + \tl_if_exist:cF { g_@@_style_opts_tl_#1_#2 } + { \msg_warning:nnnn { dbshow } { non-existent-style } {#1} {#2} } +} +% \end{macrocode} +% \end{macro} +% +% \begin{arguments} +% \item \meta{database} +% \item \meta{cond} +% \end{arguments} +% \begin{macrocode} +\msg_new:nnn { dbshow } { non-existent-cond } { + Conditional~'#2'~of~database~'#1'~does~not~exist~\msg_line_context:. +} +% \end{macrocode} +% +% \begin{macro}{\@@_check_cond:nnn} +% Check if the conditional is valid. +% \begin{arguments} +% \item \meta{database} +% \item \meta{cond} +% \end{arguments} +% \begin{macrocode} +\cs_new:Nn \@@_check_cond:nnn { + \tl_if_exist:cF { g_@@_filter_attr_#1_#2 } + { \msg_fatal:nnnn { dbshow } { non-existent-cond } {#1} {#2} } +} +% \end{macrocode} +% \end{macro} +% +% \begin{arguments} +% \item \meta{database} +% \item \meta{filter} +% \end{arguments} +% \begin{macrocode} +\msg_new:nnn { dbshow } { non-existent-filter } { + Filter~'#2'~of~database~'#1'~does~not~exist~and~is~ignored~\msg_line_context:. +} +% \end{macrocode} +% +% \begin{macro}{\@@_check_filter:nn, \@@_check_filter:nv} +% Check if the filter is valid. Ignore the default filter |-none-|. +% \begin{arguments} +% \item \meta{database} +% \item \meta{filter} +% \end{arguments} +% \begin{macrocode} +\cs_new:Nn \@@_check_filter:nn { + \seq_if_exist:cF { g_@@_filter_run_seq_#1_#2 } { + \str_if_eq:eeF {#2} { -none- } { + \msg_warning:nnnx { dbshow } { non-existent-filter } {#1} {#2} + } + } +} +\cs_generate_variant:Nn \@@_check_filter:nn { nv } +% \end{macrocode} +% \end{macro} +% +% \begin{arguments} +% \item \meta{type} +% \end{arguments} +% \begin{macrocode} +\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:. +} +% \end{macrocode} +% +% \begin{macro}{\@@_check_type:n} +% Check if the type is valid. +% \begin{arguments} +% \item \meta{type} +% \end{arguments} +% \begin{macrocode} +\cs_new:Nn \@@_check_type:n { + \clist_if_in:NnF \@@_type_clist {#1} + { \msg_fatal:nnn { dbshow } { non-existent-type } {#1} } +} +% \end{macrocode} +% \end{macro} +% +% \begin{arguments} +% \item \meta{valid count} +% \item \meta{real count} +% \item \meta{value} +% \end{arguments} +% \begin{macrocode} +\msg_new:nnn { dbshow } { wrong-seperator } { + option~'sep'~should~contain~#1~items~but~only~#2~items~was~given,~ + sep~=~\{#3\}~\msg_line_context:. +} +% \end{macrocode} +% +% \begin{macro}{\@@_sep_error:nnn, \@@_sep_error:xxx} +% Check the value of \opt{<attr>/sep} is valid. +% \begin{arguments} +% \item \meta{valid count} +% \item \meta{real count} +% \item \meta{value} +% \end{arguments} +% \begin{macrocode} +\cs_new:Nn \@@_sep_error:nnn { + \msg_error:nnnnn { dbshow } { wrong-seperator } {#1} {#2} {#3} +} +\cs_generate_variant:Nn \@@_sep_error:nnn { xxx } +% \end{macrocode} +% \end{macro} +% +% \begin{arguments} +% \item \meta{type} +% \end{arguments} +% \begin{macrocode} +\msg_new:nnn { dbshow } { unsupported-sort-type } { + unsupported~sort~type:~'#1'~\msg_line_context:.~The~type~should~be~one~of~ + \{str,~date,~int,~fp\}. +} +% \end{macrocode} +% +% \subsection{Create Database} +% +% \begin{macro}{\@@_process_default_value:w} +% Create map from \meta{attr} to \meta{type} and map from \meta{attr} to +% \meta{default value}. +% \begin{arguments} +% \item \meta{database} +% \item \meta{attr} +% \item \meta{type} +% \item \meta{default value} +% \end{arguments} +% \begin{macrocode} +\cs_new:Npn \@@_process_default_value:w +#1\@@_sep#2\@@_sep#3|#4\@@_stop { + \@@_check_type:n {#3} + \prop_gput:cxx { g_@@_attr_type_prop_#1 } {#2} {#3} + \prop_gput:cxx { g_@@_default_map_#1 } {#2} {#4} +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_process_attr_type_prop:n} +% Parse default value from the value of the type map. If \meta{type spec} is +% \meta{type\orbar default value} then set the default value to \meta{default +% value}, otherwise if \meta{type spec} is \meta{type} then set the default +% value to the default value of the type. +% \begin{arguments} +% \item \meta{database} +% \end{arguments} +% \begin{macrocode} +\cs_new_protected:Nn \@@_process_attr_type_prop:n { + \prop_gclear_new:c { g_@@_default_map_#1 } +% \end{macrocode} +% \begin{arguments}[2] +% \item \meta{attr} +% \item \meta{type spec} +% \end{arguments} +% \begin{macrocode} + \prop_map_inline:cn { g_@@_attr_type_prop_#1 } { + \str_if_in:nnTF {##2} { | } { + \@@_process_default_value:w + #1\@@_sep##1\@@_sep##2\@@_stop + } { + \prop_get:NnN \c_@@_default_value_prop {##2} \l_@@_tmp_default + \@@_process_default_value:w + #1\@@_sep##1\@@_sep##2|\l_@@_tmp_default\@@_stop + } + } +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_database_new:nn} +% Create a new \meta{database} with \meta{database spec}, which is a list of +% \meta{attr} = \meta{type spec}. This function initialize the index counter +% and save \meta{attr} = \meta{type spec} pairs to the type map. If +% \meta{database} has existed, the old definition is discarded. +% \begin{arguments} +% \item \meta{database} +% \item \meta{database spec} +% \end{arguments} +% \begin{macrocode} +\cs_new_protected:Nn \@@_database_new:nn { + \int_gzero_new:c { g_@@_counter_#1 } + \prop_gset_from_keyval:cn { g_@@_attr_type_prop_#1 } {#2} +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_database_new_append:nn} +% Create a new \meta{database} with \meta{database spec}, which is a list of +% \meta{attr} = \meta{type spec}. This function initialize the index counter +% and save \meta{attr} = \meta{type spec} pairs to the type map. If +% \meta{database} has existed, the old definition is merged into the new +% definition that has a higher priority. +% \begin{arguments} +% \item \meta{database} +% \item \meta{database spec} +% \end{arguments} +% \begin{macrocode} +\cs_new_protected:Nn \@@_database_new_append:nn { + \int_gzero_new:c { g_@@_counter_#1 } + \prop_if_exist:cF { g_@@_attr_type_prop_#1 } + { \prop_new:c { g_@@_attr_type_prop_#1 } } + \prop_gset_from_keyval:Nn \l_tmpa_prop {#2} + \prop_concat:ccc { g_@@_attr_type_prop_#1 } + { g_@@_attr_type_prop_#1 } { l_tmpa_prop } +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_database_new_inherit:nnn} +% Create a new \meta{database} with \meta{database spec}, which is a list of +% \meta{attr} = \meta{type spec}. The new \meta{database} is based on +% \meta{base database}. If \meta{database} is equal to \meta{base database}, +% \cs{@@_database_new_append:nn} is called, otherwise the index counter is +% initialized and the definition is merged with the definition of \meta{base +% dadatabase}. +% \begin{arguments} +% \item \meta{database} +% \item \meta{base database} +% \item \meta{database spec} +% \end{arguments} +% \begin{macrocode} +\cs_new_protected:Nn \@@_database_new_inherit:nnn { + \@@_check_database:n {#2} + \str_if_eq:nnTF {#1} {#2} { + \@@_database_new_append:nn {#1} {#3} + } { + \int_gzero_new:c { g_@@_counter_#1 } + \prop_gset_from_keyval:cn { g_@@_attr_type_prop_#1 } {#3} + \prop_concat:ccc { g_@@_attr_type_prop_#1 } + { g_@@_attr_type_prop_#2 } { g_@@_attr_type_prop_#1 } + } +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\dbNewDatabase} +% User interface to create a new \meta{database}. Internal attribute |id| is +% added to \meta{database}. After attributes are settle down, default values +% are parsed by \cs{@@_process_attr_type_prop:n}, and keys serving to save data of +% \meta{database} are defined. At last, we define the |default| style as its +% name implies. +% \begin{arguments} +% \item \meta{star} that indicates append or not +% \item \meta{base database} +% \item \meta{database} +% \item \meta{database spec} +% \end{arguments} +% \begin{macrocode} +\NewDocumentCommand { \dbNewDatabase } { s o m m } { + \IfNoValueTF {#2} { + \IfBooleanTF {#1} + { \@@_database_new_append:nn {#3} {#4} } + { \@@_database_new:nn {#3} {#4} } + } { \@@_database_new_inherit:nnn {#3} {#2} {#4} } + \@@_database_new_append:nn {#3} { id=int } + \@@_process_attr_type_prop:n {#3} + \@@_set_database_keys:n {#3} + \dbNewStyle{default}{#3}{} +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_set_database_keys:n} +% Set keys of \meta{database} to make it able to save data with key-value +% pairs. +% \begin{arguments} +% \item \meta{database} +% \end{arguments} +% \begin{macrocode} +\cs_new_protected:Nn \@@_set_database_keys:n { +% \end{macrocode} +% \begin{arguments}[2] +% \item \meta{type} +% \end{arguments} +% \begin{macrocode} + \prop_map_inline:cn { g_@@_attr_type_prop_#1 } { + \keys_define:nn { dbshow/database/#1 } { +% \end{macrocode} +% \begin{arguments}[3] +% \item \meta{data} +% \end{arguments} +% \begin{macrocode} + ##1 .code:n = \@@_save_data:nnn {#1} {##1} {####1}, + ##1* .code:n = { + \@@_save_data:nnn {#1} {##1} { \exp_not:n {####1} }, + }, + } + } +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_get_type:nn, \@@_get_type:nV} +% Convenient function to get the \meta{type} of \meta{attr} of \meta{database}. +% \begin{arguments} +% \item \meta{database} +% \item \meta{attr} +% \end{arguments} +% \begin{macrocode} +\cs_new:Nn \@@_get_type:nn { + \prop_item:cn { g_@@_attr_type_prop_#1 } {#2} +} +\cs_generate_variant:Nn \@@_get_type:nn { nV } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_get_counter:n} +% Convenient function to get the value of the index counter. +% \begin{arguments} +% \item \meta{database} +% \end{arguments} +% \begin{macrocode} +\cs_new:Nn \@@_get_counter:n { + \int_use:c { g_@@_counter_#1 } +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_step_counter:n} +% Convenient function to step the index counter. +% \begin{arguments} +% \item \meta{database} +% \end{arguments} +% \begin{macrocode} +\cs_new:Nn \@@_step_counter:n { + \int_gincr:c { g_@@_counter_#1 } +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\dbclear} +% User interface to clear the \meta{database}. The index counter is set to +% zero and data is not really wiped. +% \begin{arguments} +% \item \meta{database} +% \end{arguments} +% \begin{macrocode} +\NewDocumentCommand { \dbclear } { m } { + \int_gzero:c { g_@@_counter_#1 } +} +% \end{macrocode} +% \end{macro} +% +% \subsection{Store Data} +% +% \begin{macro}{\@@_save_data:nnn, \@@_save_data:nnx} +% Internal function to store data into +% \csnot{g_@@_data_\meta{database}_\meta{attr}_\meta{index}}. +% \begin{arguments} +% \item \meta{database} +% \item \meta{attr} +% \item \meta{data} +% \end{arguments} +% \begin{macrocode} +\cs_new:Nn \@@_save_data:nnn { + \@@_check_attr:nn {#1} {#2} + \str_case_e:nn { \@@_get_type:nn {#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 } { \__dbdate_gclear_new:x } + } { g_@@_data_#1_#2_\@@_get_counter:n {#1} } + \str_case_e:nn { \@@_get_type:nn {#1} {#2} } { + { str } { \str_gset:cn } + { tl } { \tl_gset:cn } + { clist } { \clist_gset:cn } + { int } { \int_gset:cn } + { fp } { \fp_gset:cn } + { date } { \__dbdate_gset:xx } + } { g_@@_data_#1_#2_\@@_get_counter:n {#1} } {#3} +} +\cs_generate_variant:Nn \@@_save_data:nnn { nnx } +% \end{macrocode} +% \end{macro} +% +% \changes{1.4}{2022-01-14}{Fix bug}{data cannot contain \cs{par}} +% \begin{macro}{dbitem, \@@_set_default:nn, \dbsave, \dbsave*} +% Environment \env{dbitem} is the user interface to save data with \meta{attr} +% = \meta{data} pairs or using \cs{dbsave}. First we step the index counter +% and set the default value for each attribute. Then we set the value by +% \meta{attr-value list} and the index is also stored to default attribute +% |id|. +% \begin{arguments} +% \item \meta{database} +% \item \meta{attr-value list} +% \end{arguments} +% \begin{macrocode} +\NewDocumentEnvironment { dbitem } { m +O{} } { + \@@_check_database:n {#1} + \@@_step_counter:n {#1} +% \end{macrocode} +% This function is used to set the default values of each attribute. +% \begin{arguments} +% \item \meta{attr} +% \end{arguments} +% \begin{macrocode} + \cs_set:Nn \@@_set_default:nn { + \@@_save_data:nnx {#1} {##1} { + \prop_item:cn { g_@@_default_map_#1 } {##1} + } + } + \prop_map_function:cN { g_@@_attr_type_prop_#1 } \@@_set_default:nn + \@@_save_data:nnx {#1} { id } { \@@_get_counter:n {#1} } + \keys_set:nn { dbshow/database/#1 } {#2} +% \end{macrocode} +% \begin{arguments} +% \item \meta{star} that indicates whether to use \cs{exp_not:n} +% \item \meta{attr} +% \item \meta{data} +% \end{arguments} +% \begin{macrocode} + \NewDocumentCommand { \dbsave } { s m +m } { + \IfBooleanTF {##1} { + \@@_save_data:nnn {#1} {##2} { \exp_not:n {##3} } + } { + \@@_save_data:nnn {#1} {##2} {##3} + } + } +} {} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\dbitemkv} +% Store data with \meta{attr-value list} +% \begin{arguments} +% \item \meta{database} +% \item \meta{attr-value list} +% \end{arguments} +% \begin{macrocode} +\NewDocumentCommand { \dbitemkv } { m +m } { + \begin{dbitem}{#1}[#2] + \end{dbitem} +} +% \end{macrocode} +% \end{macro} +% +% \subsection{Filter} +% +% Following functions have the same argument specifications: +% \begin{arguments} +% \item \meta{star boolean} +% \item \meta{expr} specified by \cs{dbNewConditional} +% \item \meta{current value}, i.e. \cs{dbval} +% \item \meta{true code} to set filter boolean to true +% \item \meta{false code} to set filter boolean to false +% \end{arguments} +% These functions aim to filter \meta{attr} of certain type with \meta{expr} +% in every epoch of iteration. The basic idea is to save the filter result +% into a boolean variable corresponding to each conditional. +% +% \begin{macro}{\@@_filter_int:NNNnn, \@@_filter_int:cccnn} +% Filter integers. +% \begin{arguments} +% \item \meta{star boolean} +% \item \meta{expr} specified by \cs{dbNewConditional} +% \item \meta{current value}, i.e. \cs{dbval} +% \item \meta{true code} to set filter boolean to true +% \item \meta{false code} to set filter boolean to false +% \end{arguments} +% \begin{macrocode} +\cs_new:Nn \@@_filter_int:NNNnn { + \int_compare:nTF {#2} {#4} {#5} +} +\cs_generate_variant:Nn \@@_filter_int:NNNnn { cccnn } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_filter_fp:NNNnn, \@@_filter_fp:cccnn} +% Filter floating point values. +% \begin{arguments} +% \item \meta{star boolean} +% \item \meta{expr} specified by \cs{dbNewConditional} +% \item \meta{current value}, i.e. \cs{dbval} +% \item \meta{true code} to set filter boolean to true +% \item \meta{false code} to set filter boolean to false +% \end{arguments} +% \begin{macrocode} +\cs_new:Nn \@@_filter_fp:NNNnn { + \int_compare:nTF {#2} {#4} {#5} +} +\cs_generate_variant:Nn \@@_filter_fp:NNNnn { cccnn } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_filter_clist:NNNnn, \@@_filter_clist:cccnn} +% Filter comma lists. If \meta{star bool} is true, all values specified by +% \meta{conditional} should be contained in current list. Otherwise, condition +% is met if any value specified by \meta{conditional} appears in current list. +% \begin{arguments} +% \item \meta{star boolean} +% \item \meta{expr} specified by \cs{dbNewConditional} +% \item \meta{current value}, i.e. \cs{dbval} +% \item \meta{true code} to set filter boolean to true +% \item \meta{false code} to set filter boolean to false +% \end{arguments} +% \begin{macrocode} +\cs_new_protected:Nn \@@_filter_clist:NNNnn { +% \end{macrocode} +% Initial filter boolean of starred conditional is true. And then we check +% every value in \meta{conditional}. If there is some value that does not +% appear in current list, the result is set to false and the loop is broken. +% \begin{macrocode} + \bool_if:NTF #1 { + #4 \clist_map_inline:Vn #2 + { \clist_if_in:NnF #3 {##1} { #5 \clist_map_break: } } + } { +% \end{macrocode} +% Initial filter boolean of unstarred conditional is false. And then we check +% every value in \meta{conditional}. If there is some value that does appear +% in current list, the result is set to true and the loop is broken. +% \begin{macrocode} + #5 \clist_map_inline:Vn #2 + { \clist_if_in:NnT #3 {##1} { #4 \clist_map_break: } } + } +} +\cs_generate_variant:Nn \@@_filter_clist:NNNnn { cccnn } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_filter_str:NNNnn, \@@_filter_str:cccnn} +% Filter strings with regex expression. Starred filter matches the whole +% string while unstarred matches part of the string. +% \begin{arguments} +% \item \meta{star boolean} +% \item \meta{expr} specified by \cs{dbNewConditional} +% \item \meta{current value}, i.e. \cs{dbval} +% \item \meta{true code} to set filter boolean to true +% \item \meta{false code} to set filter boolean to false +% \end{arguments} +% \begin{macrocode} +\cs_new_protected:Nn \@@_filter_str:NNNnn { + \bool_if:NT #1 { + \tl_put_left:Nn #2 { \A } + \tl_put_right:Nn #2 { \Z } + } + \regex_match:VVTF #2 #3 {#4} {#5} +} +\cs_generate_variant:Nn \@@_filter_str:NNNnn { cccnn } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_filter_tl:NNNnn, \@@_filter_tl:cccnn} +% Filter token list with regex expression. It is the same with string. +% \begin{macrocode} +\cs_set_eq:NN \@@_filter_tl:NNNnn \@@_filter_str:NNNnn +\cs_generate_variant:Nn \@@_filter_tl:NNNnn { cccnn } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_parse_date_cond:NNw} +% Help function to parse \meta{review points} and \meta{date} and store them +% in \meta{clist var} and \meta{tl var}. +% \begin{arguments} +% \item \meta{clist var} to store \meta{review points} +% \item \meta{tl var} to store \meta{date} +% \end{arguments} +% \begin{macrocode} +\cs_new_protected:Npn \@@_parse_date_cond:NNw #1#2#3|#4\@@_stop { + \clist_set_eq:Nc #1 { g__review_points_#3 } + \tl_set:Nn #2 {#4} +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_filter_date:NNNnn, \@@_filter_date:cccnn} +% Filter dates by \meta{review points} or \meta{expr}. +% \begin{arguments} +% \item \meta{star boolean} +% \item \meta{expr} specified by \cs{dbNewConditional} +% \item \meta{current value}, i.e. \cs{dbval} +% \item \meta{true code} to set filter boolean to true +% \item \meta{false code} to set filter boolean to false +% \end{arguments} +% \begin{macrocode} +\cs_new_protected:Nn \@@_filter_date:NNNnn { +% \end{macrocode} +% For starred \meta{conditional}, calculate the days between current day, i.e. +% \cs{dbval} and the date to be compared, i.e. \cs{l_@@_filter_tmp_tl} to see +% if the result appears in the \meta{review points}. +% \begin{macrocode} + \bool_if:NTF #1 { + \int_zero_new:N \l_@@_filter_diff_int + \exp_last_unbraced:NNNV \@@_parse_date_cond:NNw + \l_@@_filter_tmp_clist \l_@@_filter_tmp_tl {#2} \@@_stop + \__dbdate_clear_new:n { tmp_day1 } + \__dbdate_clear_new:n { tmp_day2 } + \__dbdate_set:xx { tmp_day1 } { \l_@@_filter_tmp_tl } + \__dbdate_set:xx { tmp_day2 } {#3} + \__dbdate_sub:nnN { tmp_day1 } { tmp_day2 } \l_@@_filter_diff_int + \clist_if_in:NxTF \l_@@_filter_tmp_clist + { \int_use:N \l_@@_filter_diff_int } {#4} {#5} + } { +% \end{macrocode} +% For unstarred \meta{conditional} which parses \meta{expr}. We first replace +% each date to an absolute integer. We did not use \cs{regex_replace_all:nnN} +% because \cs{_dbdate_to_int:nN} is nonexpandable. So the code seems a little +% complicated and unsightly, but it worked. Note that \meta{expr} should be +% updated locally. +% \begin{macrocode} + \int_zero_new:N \l_@@_filter_tmpa_int + \int_zero_new:N \l_@@_filter_tmpb_int + \tl_set:Nx \l_@@_expr_tl {#2} + \regex_extract_all:nVN { \d{4}/\d+/\d+ } + \l_@@_expr_tl \l_@@_filter_date_seq + \regex_split:nVN { \d{4}/\d+/\d+ } + \l_@@_expr_tl \l_@@_filter_other_seq + \tl_clear:N \l_@@_expr_tl + \int_set:Nn \l_@@_filter_tmpa_int + { \seq_count:N \l_@@_filter_date_seq } + \int_step_inline:nn { \l_@@_filter_tmpa_int } { + \tl_put_right:Nx \l_@@_expr_tl + { \seq_item:Nn \l_@@_filter_other_seq {##1} } + \__dbdate_clear_new:n { date-tmp } + \__dbdate_set:xx { date-tmp } + { \seq_item:Nn \l_@@_filter_date_seq {##1} } + \__dbdate_to_int:nN { date-tmp } \l_@@_filter_tmpb_int + \tl_put_right:Nx \l_@@_expr_tl + { \int_use:N \l_@@_filter_tmpb_int } + } + \tl_put_right:Nx \l_@@_expr_tl { + \seq_item:Nn \l_@@_filter_other_seq + { \l_@@_filter_tmpa_int + 1 } + } + \int_compare:nTF { \l_@@_expr_tl } {#4} {#5} + } +} +\cs_generate_variant:Nn \@@_filter_date:NNNnn { cccnn } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_filter:nnn} +% Filter records with \meta{conditional} +% \begin{arguments} +% \item \meta{database} +% \item \meta{conditional} +% \item \meta{index} +% \end{arguments} +% \begin{macrocode} +\cs_set:Nn \@@_filter:nnn { + \tl_set_eq:Nc \l_@@_attr_tl { g_@@_filter_attr_tl_#1_#2 } + \cs_set_eq:Nc \dbval { g_@@_data_#1_\l_@@_attr_tl _#3 } + \use:c + { @@_filter_\@@_get_type:nV {#1} \l_@@_attr_tl :cccnn } + { g_@@_cond_star_bool_#1_#2 } + { g_@@_filter_expr_tl_#1_#2 } + { dbval } + { \bool_gset_true:c { g_@@_filter_bool_#1_#2 } } + { \bool_gset_false:c { g_@@_filter_bool_#1_#2 } } +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_new_conditional:nnnnn} +% For a \meta{conditional} of \meta{attr}, map \meta{conditional} to +% \meta{attr} and map \meta{conditional} to {expr}. The \meta{boolean var} is +% created to store the filter result. An hook function is defined and the +% \meta{conditional} is recorded in the sequence. +% \begin{arguments} +% \item \meta{database} +% \item \meta{conditional} +% \item \meta{attr} +% \item \meta{expr} +% \item \meta{star} +% \end{arguments} +% \begin{macrocode} +\cs_new_protected:Nn \@@_new_conditional:nnnnn { + \tl_gset:cn { g_@@_filter_attr_tl_#1_#2 } {#3} + \tl_gset:cn { g_@@_filter_expr_tl_#1_#2 } {#4} + \bool_if_exist:cF { g_@@_filter_bool_#1_#2 } + { \bool_new:c { g_@@_filter_bool_#1_#2 } } + \bool_if_exist:cF { g_@@_cond_star_bool_#1_#2 } + { \bool_new:c { g_@@_cond_star_bool_#1_#2 } } + \IfBooleanTF {#5} + { \bool_gset_true:c { g_@@_cond_star_bool_#1_#2 } } + { \bool_gset_false:c { g_@@_cond_star_bool_#1_#2 } } +% \end{macrocode} +% Filter hook function. +% \begin{arguments}[2] +% \item \meta{index} +% \end{arguments} +% \begin{macrocode} + \cs_gset:cn { g_@@_filter_hook_#1_#2:n } { + \@@_filter:nnn {#1} {#2} {##1} + } + \seq_gput_right:cn { g_@@_cond_seq_#1 } {#2} +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_combine_conditional:nnn, \@@_combine_conditional:nVn} +% First extract all the \meta{conditional} in \meta{conditional expr} and for +% every \meta{conditional} in the record sequence, if it is in +% \meta{conditional expr}, then add the corresponding hook function to running +% sequence. Then replace all the \meta{conditional} in \meta{conditional expr} +% with corresponding boolean variable and save the result in the filter +% boolean variable. +% \begin{arguments} +% \item \meta{database} +% \item \meta{filter} +% \item \meta{conditional expr} +% \end{arguments} +% \begin{macrocode} +\cs_new_protected:Nn \@@_combine_conditional:nnn { + \tl_gset_eq:cN { g_@@_filter_bool_tl_#1_#2 } \c_true_bool + \seq_gclear_new:c { g_@@_filter_run_seq_#1_#2 } + \regex_extract_all:nnN { \w+ } {#3} \l_@@_cond_seq +% \end{macrocode} +% \begin{arguments}[2] +% \item \meta{conditional} +% \end{arguments} +% \begin{macrocode} + \seq_map_inline:Nn \l_@@_cond_seq { + \seq_if_in:cnT { g_@@_cond_seq_#1 } {##1} { + \seq_gput_right:cn { g_@@_filter_run_seq_#1_#2 } + { g_@@_filter_hook_#1_##1:n } + } + } + \tl_set:Nn \l_@@_cond_expr_tl {#3} + \regex_replace_all:nnN + { \w+ } { \c{ g_@@_filter_bool_#1_\0 } } + \l_@@_cond_expr_tl + \tl_gset_eq:cN + { g_@@_filter_bool_tl_#1_#2 } \l_@@_cond_expr_tl +} +\cs_generate_variant:Nn \@@_combine_conditional:nnn { nVn } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{dbFilters, \dbNewConditional, \dbCombineConditionals} +% Environment to define conditionals and filters. +% \begin{arguments} +% \item \meta{database} +% \end{arguments} +% \begin{macrocode} +\NewDocumentEnvironment { dbFilters } { m } { + \seq_gclear_new:c { g_@@_cond_seq_#1 } +% \end{macrocode} +% \begin{arguments}[2] +% \item \meta{star} +% \item \meta{conditional} +% \item \meta{attr} +% \item \meta{expr} +% \end{arguments} +% \begin{macrocode} + \DeclareDocumentCommand { \dbNewConditional } { s m m m } { + \@@_new_conditional:nnnnn + {#1} {##2} {##3} {##4} {##1} + } +% \end{macrocode} +% \begin{arguments}[2] +% \item \meta{filter} +% \item \meta{conditional expr} +% \item \meta{filter info} +% \end{arguments} +% \begin{macrocode} + \DeclareDocumentCommand { \dbCombineConditionals } { m m O{} } { + \tl_gset:cn { g_@@_filter_info_tl_#1_##1 } {##3} + \@@_combine_conditional:nnn {#1} {##1} {##2} + } +} {} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\dbNewReviewPoints} +% User interface to define \meta{review points}. +% \begin{arguments} +% \item \meta{name} +% \item \meta{points} +% \end{arguments} +% \begin{macrocode} +\NewDocumentCommand { \dbNewReviewPoints } { m m } { + \clist_set:cn { g__review_points_#1 } {#2} +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\dbIntAbs, \dbIntSign, \dbIntDivRound, \dbIntDivTruncate, +% \dbIntMax, \dbIntMin, \dbIntMod, \dbFpSign} +% Function aliases to support more operations of integer and floating points. +% \begin{macrocode} +\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 +% \end{macrocode} +% \end{macro} +% +% \subsection{Style and Options} +% +% \begin{macro}{\@@_identity:n} +% Does nothing and keep things unchanged. +% \begin{macrocode} +\cs_new:Nn \@@_identity:n {#1} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_new_attr_style:nnn} +% Define style keys for each attribute. +% \begin{arguments} +% \item \meta{style} +% \item \meta{database} +% \item \meta{attr} +% \end{arguments} +% \begin{macrocode} +\cs_new_protected:Nn \@@_new_attr_style:nnn { + \@@_check_attr:nn {#2} {#3} + \keys_define:nn { dbshow/style/#1/#3 } { + before-code .tl_gset:c = { + g_@@_style_attr_before_tl_#1_#2_#3 + }, + before-code .initial:n = , + after-code .tl_gset:c = { + g_@@_style_attr_after_tl_#1_#2_#3 + }, + after-code .initial:n = , +% \end{macrocode} +% For comma list and date. +% \begin{macrocode} + sep .clist_gset:c = { + g_@@_style_attr_sep_#1_#2_#3 + }, +% \end{macrocode} +% Only for comma list. +% \begin{macrocode} + item-before-code .tl_gset:c = { + g_@@_style_attr_item_before_tl_#1_#2_#3 + }, + item-before-code .initial:n = , + item-after-code .tl_gset:c = { + g_@@_style_attr_item_after_tl_#1_#2_#3 + }, + item-after-code .initial:n = , +% \end{macrocode} +% Only for date. +% \begin{macrocode} + zfill .bool_gset:c = { + g_@@_style_attr_zfill_bool_#1_#2_#3 + }, + zfill .initial:n = true, + zfill .default:n = true, + wrapper .tl_gset:c = { + g_@@_style_attr_wrapper_#1_#2_#3 + }, + wrapper .initial:n = { \@@_identity:n }, + } + \str_case_e:nn { \@@_get_type:nn {#2} {#3} } { + { clist } + { \keys_set:nn { dbshow/style/#1/#3 } { sep = { { ,~ } } } } + { date } + { \keys_set:nn { dbshow/style/#1/#3 } { sep = { { / } } } } + } +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_new_database_style:nn} +% Define style keys. +% \begin{arguments} +% \item \meta{style} +% \item \meta{database} +% \end{arguments} +% \begin{macrocode} +\cs_new_protected:Nn \@@_new_database_style:nn { + \@@_check_database:n {#2} + \keys_define:nn { dbshow/style/#1 } { + raw-filter .code:n = { + \int_gincr:N \g_@@_raw_filter_int + \str_set:Nx \l_@@_raw_filter_str + { -raw\int_use:N \g_@@_raw_filter_int - } + \tl_gset:cV { g_@@_filter_#1_#2 } \l_@@_raw_filter_str + \@@_combine_conditional:nnn {#2} \l_@@_raw_filter_str {##1} + }, + filter .tl_gset:c = { g_@@_filter_#1_#2 }, + filter .initial:n = -none-, + sort .clist_gset:c = { g_@@_sort_clist_#1_#2 }, + before-code .tl_gset:c = { g_@@_style_before_tl_#1_#2 }, + before-code .initial:n = , + item-code .tl_gset:c = { g_@@_style_database_item_tl_#1_#2 }, + item-code .initial:n = , + after-code .tl_gset:c = { g_@@_style_after_tl_#1_#2 }, + after-code .initial:n = , + record-before-code .tl_gset:c = { g_@@_style_record_before_tl_#1_#2 }, + record-before-code .initial:n = , + record-after-code .tl_gset:c = { g_@@_style_record_after_tl_#1_#2 }, + record-after-code .initial:n = , + } + \prop_map_inline:cn { g_@@_attr_type_prop_#2 } + { \@@_new_attr_style:nnn {#1} {#2} {##1} } +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\dbNewStyle} +% Set style options based on \meta{base style}. +% \begin{arguments} +% \item \meta{base style clist} +% \item \meta{style} +% \item \meta{database} +% \item \meta{options} +% \end{arguments} +% \begin{macrocode} +\NewDocumentCommand { \dbNewStyle } { o m m +m } { + \tl_gset:cn { g_@@_style_opts_tl_#2_#3 } { #4, } + \IfValueT {#1} { + \tl_clear_new:N \l_@@_style_tmp_tl +% \end{macrocode} +% \begin{arguments}[2] +% \item \meta{base style} +% \end{arguments} +% \begin{macrocode} + \clist_map_inline:nn {#1} { + \@@_check_style:nn {##1} {#3} + \tl_if_exist:cT { g_@@_style_opts_tl_##1_#3 } { + \tl_concat:ccc { l_@@_style_tmp_tl } + { l_@@_style_tmp_tl } { g_@@_style_opts_tl_##1_#3 } + } + } + \tl_gconcat:ccc { g_@@_style_opts_tl_#2_#3 } + { l_@@_style_tmp_tl } { g_@@_style_opts_tl_#2_#3 } + } + \@@_new_database_style:nn {#2} {#3} + \keys_set:nv { dbshow/style/#2 } { g_@@_style_opts_tl_#2_#3 } +} +% \end{macrocode} +% \end{macro} +% +% \subsection{Sort} +% +% \begin{macro}{\@@_sort_parse_star:NNNw} +% Parse descending sorting rule. +% \begin{arguments} +% \item \meta{tl var} representing for relation to keep order the same +% \item \meta{tl var} representing for relation to swap the order +% \item \meta{tl var} to store the \meta{attr} +% \end{arguments} +% \begin{macrocode} +\cs_new_protected:Npn \@@_sort_parse_star:NNNw #1#2#3#4* { + \tl_set:Nn #1 { > } + \tl_set:Nn #2 { < } + \tl_set:Nn #3 {#4} +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_sort:nNn} +% Sort the records. +% \begin{arguments} +% \item \meta{database} +% \item \meta{index clist} +% \item \meta{style} +% \end{arguments} +% \begin{macrocode} +\cs_new_protected:Nn \@@_sort:nNn { + \int_zero_new:N \l_@@_sort_len_int + \int_zero_new:N \l_@@_sort_tmp_int + \int_set:Nn \l_@@_sort_len_int + { \clist_count:c { g_@@_sort_clist_#3_#1 } } +% \end{macrocode} +% Sort recursively. +% \begin{macrocode} + \clist_sort:Nn #2 { + \int_zero:N \l_@@_sort_tmp_int +% \end{macrocode} +% Sort single attribute. +% \begin{macrocode} + \cs_set:Nn \@@_sort_single: { + \int_incr:N \l_@@_sort_tmp_int + \str_set:Nx \l_@@_sort_attr_str { + \clist_item:cn + { g_@@_sort_clist_#3_#1 } + { \l_@@_sort_tmp_int } + } +% \end{macrocode} +% Parse \meta{attr} and corresponding \meta{type} and set compare operators. +% \begin{macrocode} + \str_if_in:NnTF \l_@@_sort_attr_str { * } { + \exp_after:wN \@@_sort_parse_star:NNNw + \exp_after:wN \l_@@_sort_same_op_tl + \exp_after:wN \l_@@_sort_swap_op_tl + \exp_after:wN \l_@@_sort_attr_str + \l_@@_sort_attr_str + } { + \tl_set:Nn \l_@@_sort_same_op_tl { < } + \tl_set:Nn \l_@@_sort_swap_op_tl { > } + } +% \end{macrocode} +% Check if type is valid and transform date to string. +% \begin{macrocode} + \@@_check_attr:nV {#1} \l_@@_sort_attr_str + \tl_set:Nx \l_@@_sort_type_tl + { \@@_get_type:nV {#1} \l_@@_sort_attr_str } + \clist_if_in:nVF + { str, int, date, fp } { \l_@@_sort_type_tl } { + \msg_error:nnx { dbshow } { unsupported-sort-type } + { \l_@@_sort_type_tl } + } + \str_if_eq:eeT { \l_@@_sort_type_tl } { date } + { \tl_set:Nn \l_@@_sort_type_tl { str } } +% \end{macrocode} +% Set operands and compare function. +% \begin{macrocode} + \cs_set_eq:Nc \l_@@_sort_tmpa_tl + { g_@@_data_#1_\l_@@_sort_attr_str _##1 } + \cs_set_eq:Nc \l_@@_sort_tmpb_tl + { g_@@_data_#1_\l_@@_sort_attr_str _##2 } + \cs_set_eq:Nc \@@_compare + { \l_@@_sort_type_tl _compare:VNVTF } +% \end{macrocode} +% Compare two operands and if they are equal, compare the next attribute. +% \begin{macrocode} + \@@_compare \l_@@_sort_tmpa_tl + \l_@@_sort_same_op_tl \l_@@_sort_tmpb_tl + { \sort_return_same: } + { + \@@_compare \l_@@_sort_tmpa_tl + \l_@@_sort_swap_op_tl \l_@@_sort_tmpb_tl + { \sort_return_swapped: } + { + \int_compare:nTF + { \l_@@_sort_len_int = \l_@@_sort_tmp_int } + { \sort_return_same: } + { \@@_sort_single: } + } + } + } + \@@_sort_single: + } +} +% \end{macrocode} +% \end{macro} +% +% \subsection{Display Data} +% +% \begin{macro}{\@@_clist_wrapper:NNn} +% Wrap the clist item with \meta{before code} and \meta{after code}. +% \begin{arguments} +% \item \meta{before code tl} +% \item \meta{after code tl} +% \item \meta{item} +% \end{arguments} +% \begin{macrocode} +\cs_new:Nn \@@_clist_wrapper:NNn { + \exp_not:n { { #1#3#2 }, } +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_clist_use:NNNN, \@@_clist_use:cccc} +% Display a comma list. +% \begin{arguments} +% \item \meta{data clist} +% \item \meta{separator clist} +% \item \meta{before code tl} +% \item \meta{after code tl} +% \end{arguments} +% \begin{macrocode} +\cs_new:Nn \@@_clist_use:NNNN { + \int_case:nnF { \clist_count:N #2 } { + { 1 } { + \clist_use:xx + { \clist_map_tokens:Nn #1 { \@@_clist_wrapper:NNn #3 #4 } } + { \clist_item:Nn #2 { 1 } } + } + { 3 } { + \clist_use:xxxx + { \clist_map_tokens:Nn #1 { \@@_clist_wrapper:NNn #3 #4 } } + { \clist_item:Nn #2 { 1 } } + { \clist_item:Nn #2 { 2 } } + { \clist_item:Nn #2 { 3 } } + } + } { + \@@_sep_error:xxx + { 1~or~3 } + { \clist_count:N #2 } + { \clist_use:Nn #2 { ,~ } } + } +} +\cs_generate_variant:Nn \@@_clist_use:NNNN { cccc } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_date_use:nNN, \@@_date_use:ncc} +% Display date. +% \begin{arguments} +% \item \meta{data} +% \item \meta{separator clist} +% \item \meta{zfill boolean} +% \end{arguments} +% \begin{macrocode} +\cs_new:Nn \@@_date_use:nNN { + \int_case:nnF { \clist_count:N #2 } { + { 1 } { + \bool_if:NTF {#3} + { \__dbdate_use_zfill:nf } + { \__dbdate_use:nf } + {#1} + { \clist_item:Nn #2 { 1 } } + } + { 4 } { + \bool_if:NTF {#3} + { \__dbdate_use_zfill:nffff } + { \__dbdate_use:nffff } + {#1} + { \clist_item:Nn #2 { 1 } } + { \clist_item:Nn #2 { 2 } } + { \clist_item:Nn #2 { 3 } } + { \clist_item:Nn #2 { 4 } } + } + } { + \@@_sep_error:xxx + { 1~or~4 } + { \clist_count:N #2 } + { \clist_use:Nn #2 { ,~ } } + } +} +\cs_generate_variant:Nn \@@_date_use:nNN { ncc } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_use_data:nnnn} +% Display Data. +% \begin{arguments} +% \item \meta{database} +% \item \meta{attr} +% \item \meta{index} +% \item \meta{style} +% \end{arguments} +% \begin{macrocode} +% #1 database #2 attr #3 index #4 style +\cs_new:Nn \@@_use_data:nnnn { + \str_case_e:nn + { \prop_item:cn { g_@@_attr_type_prop_#1 } {#2} } { + { str } { \str_use:c { g_@@_data_#1_#2_#3 } } + { tl } { \tl_use:c { g_@@_data_#1_#2_#3 } } + { int } { \int_use:c { g_@@_data_#1_#2_#3 } } + { fp } { \fp_use:c { g_@@_data_#1_#2_#3 } } + { clist } { + \@@_clist_use:cccc { g_@@_data_#1_#2_#3 } + { g_@@_style_attr_sep_#4_#1_#2 } + { g_@@_style_attr_item_before_tl_#4_#1_#2 } + { g_@@_style_attr_item_after_tl_#4_#1_#2 } + } + { date } { + \exp_args:Nnx + \tl_use:c { g_@@_style_attr_wrapper_#4_#1_#2 } { + \@@_date_use:ncc { g_@@_data_#1_#2_#3 } + { g_@@_style_attr_sep_#4_#1_#2 } + { g_@@_style_attr_zfill_bool_#4_#1_#2 } + } + } + } +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\dbDatabase, \dbFilterName, \dbFilterInfo} +% Define context macros. +% \begin{arguments} +% \item \meta{database} +% \item \meta{filter} +% \end{arguments} +% \begin{macrocode} +\cs_new_protected:Nn \@@_show_set_macro:nn { + \tl_set:Nn \dbDatabase {#1} + \tl_set:Nn \dbFilterName {#2} + \tl_set_eq:Nc \dbFilterInfo { g_@@_filter_info_tl_#1_#2 } +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_show_filter:nn} +% Filter records by executing the hook function in the running sequence and +% then testing the result boolean. +% \begin{arguments} +% \item \meta{database} +% \item \meta{filter} +% \item \meta{index clist} +% \end{arguments} +% \begin{macrocode} +\cs_new_protected:Nn \@@_show_filter:nnN { +% \end{macrocode} +% \begin{arguments}[2] +% \item \meta{index} +% \end{arguments} +% \begin{macrocode} + \int_step_inline:nn { \@@_get_counter:n {#1} } { + \seq_if_exist:cTF { g_@@_filter_run_seq_#1_#2 } { +% \end{macrocode} +% \begin{arguments}[3] +% \item \meta{hook} +% \end{arguments} +% \begin{macrocode} + \seq_map_inline:cn { g_@@_filter_run_seq_#1_#2 } { + \use:c {####1} {##1} + } + \exp_args:Nv + \bool_if:nT { g_@@_filter_bool_tl_#1_#2 } + { \clist_put_right:Nn #3 {##1} } + } { \clist_put_right:Nn #3 {##1} } + } +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_show_set_counter:N, \dbalph, \dbAlph, \dbarabic, \dbroman, +% \dbRoman} +% Define macros to display counter. +% \begin{arguments} +% \item \meta{int var} +% \end{arguments} +% \begin{macrocode} +\cs_new_protected:Nn \@@_show_set_counter:N { + \tl_set:Nx \dbalph { \int_to_alph:n {#1} } + \tl_set:Nx \dbAlph { \int_to_Alph:n {#1} } + \tl_set:Nx \dbarabic { \int_to_arabic:n {#1} } + \tl_set:Nx \dbRoman { \int_to_Roman:n {#1} } + \tl_set:Nx \dbroman { \int_to_roman:n {#1} } +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_show_item:nn, \dbIndex, \dbuse} +% Display single record. +% \begin{arguments} +% \item \meta{style} +% \item \meta{database} +% \item \meta{index clist} +% \end{arguments} +% \begin{macrocode} +\cs_new_protected:Nn \@@_show_item:nnN { + \int_zero_new:N \l_@@_show_int +% \end{macrocode} +% \begin{arguments}[2] +% \item \meta{index} +% \end{arguments} +% \begin{macrocode} + \clist_map_inline:Nn #3 { + \int_incr:N \l_@@_show_int + \@@_show_set_counter:N \l_@@_show_int + \tl_set:Nn \dbIndex {##1} +% \end{macrocode} +% \begin{arguments}[3] +% \item \meta{attr} +% \end{arguments} +% \begin{macrocode} + \cs_set:Npn \dbuse ####1 { + \@@_check_attr:nn {#2} {####1} + \tl_use:c { g_@@_style_attr_before_tl_#1_#2_####1 } + \@@_use_data:nnnn {#2} {####1} {##1} {#1} + \tl_use:c { g_@@_style_attr_after_tl_#1_#2_####1 } + } + \tl_use:c { g_@@_style_record_before_tl_#1_#2 } + \tl_use:c { g_@@_style_database_item_tl_#1_#2 } + \tl_use:c { g_@@_style_record_after_tl_#1_#2 } + } +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_show_set_cond:N, \dbIfEmptyT, \dbIfEmptyF, \dbIfEmptyTF} +% Define conditional to test if the number of records to show is zero. +% \begin{arguments} +% \item \meta{index clist} +% \end{arguments} +% \begin{macrocode} +\cs_new_protected:Nn \@@_show_set_cond:N { + \prg_set_conditional:Nnn \@@_if_empty: { T, F, TF } { + \clist_if_empty:NTF #1 + { \prg_return_true: } + { \prg_return_false: } + } + \cs_set_eq:NN \dbIfEmptyT \@@_if_empty:T + \cs_set_eq:NN \dbIfEmptyF \@@_if_empty:F + \cs_set_eq:NN \dbIfEmptyTF \@@_if_empty:TF +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_show:nnn, \@@_show:nnv} +% First filter records and sort them if needed and display at last. +% \begin{macrocode} +\cs_new_protected:Nn \@@_show:nnn { + \@@_show_set_macro:nn {#2} {#3} + \clist_clear_new:N \l_@@_show_index_clist + \@@_show_filter:nnN {#2} {#3} \l_@@_show_index_clist + \clist_if_empty:cF { g_@@_sort_clist_#1_#2 } + { \@@_sort:nNn {#2} \l_@@_show_index_clist {#1} } + \tl_use:c { g_@@_style_before_tl_#1_#2 } + \@@_show_item:nnN {#1} {#2} \l_@@_show_index_clist + \tl_use:c { g_@@_style_after_tl_#1_#2 } +} +\cs_generate_variant:Nn \@@_show:nnn { nnv } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\dbshow} +% User insterface to display the \meta{database} with \meta{style}. +% \begin{arguments} +% \item \meta{style} +% \item \meta{database} +% \end{arguments} +% \begin{macrocode} +\NewDocumentCommand { \dbshow } { m m } { + \@@_check_database:n {#2} + \@@_check_filter:nv {#2} { g_@@_filter_#1_#2 } + \@@_show:nnv {#1} {#2} { g_@@_filter_#1_#2 } +} +% \end{macrocode} +% \end{macro} +% +% \subsection{Date Type} +% +% \begin{macrocode} +%<@@=dbdate> +% \end{macrocode} +% +% \begin{macro}[pTF]{\@@_if_leap:n} +% Check if the year is leap. +% \begin{arguments} +% \item \meta{year} +% \end{arguments} +% \begin{macrocode} +\prg_new_conditional:Nnn \@@_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: } +} +% \end{macrocode} +% \end{macro} +% +% \begin{variable}{\c_@@_month_clist} +% Number of days of every month. +% \begin{macrocode} +\clist_const:Nn \c_@@_month_clist + { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } +% \end{macrocode} +% \end{variable} +% +% \begin{macro}{\@@_to_int:nnnN} +% Transform date to integer relative to |1971-01-01|. +% \begin{arguments} +% \item \meta{year} +% \item \meta{month} +% \item \meta{day} +% \item \meta{int var} to store the result +% \end{arguments} +% \begin{macrocode} +\cs_new_protected:Nn \@@_to_int:nnnN { + \int_zero_new:N \l_@@_ans_int + \int_zero_new:N \l_@@_tmpa_int + \int_zero_new:N \l_@@_tmpb_int + \int_set:Nn \l_@@_ans_int { #3 - 1 } + \int_step_inline:nn { #2 - 1 } { + \int_add:Nn \l_@@_ans_int { + \clist_item:Nn \c_@@_month_clist {##1} + } + \bool_if:nT { + \int_compare_p:nNn {##1} = { 2 } && + \@@_if_leap_p:n {#1} + } { \int_incr:N \l_@@_ans_int } + } + \int_add:Nn \l_@@_ans_int { 365 * (#1 - 1971) } + \int_add:Nn \l_@@_ans_int { + \int_div_truncate:nn { #1 - 1 } { 4 } - + \int_div_truncate:nn { 1971 } { 4 } + } + \int_sub:Nn \l_@@_ans_int { + \int_div_truncate:nn { #1 - 1 } { 100 } - + \int_div_truncate:nn { 1971 } { 100 } + } + \int_add:Nn \l_@@_ans_int { + \int_div_truncate:nn { #1 - 1 } { 400 } - + \int_div_truncate:nn { 1971 } { 400 } + } + \int_set_eq:NN #4 \l_@@_ans_int +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_to_int:NNNN, \@@_to_int:cccN} +% Transform date to integer relative to |1971-01-01|. +% \begin{arguments} +% \item \meta{year int var} +% \item \meta{month int var} +% \item \meta{day int var} +% \item \meta{int var} to store the result +% \end{arguments} +% \begin{macrocode} +\cs_new_protected:Nn \@@_to_int:NNNN { + \@@_to_int:nnnN {#1} {#2} {#3} #4 +} +\cs_generate_variant:Nn \@@_to_int:NNNN { cccN } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_to_int:nN} +% Transform date to integer relative to |1971-01-01|. +% \begin{arguments} +% \item \meta{date var} +% \item \meta{int var} to store the result +% \end{arguments} +% \begin{macrocode} +\cs_new:Nn \@@_to_int:nN { + \@@_to_int:cccN + { @@_year_#1 } + { @@_month_#1 } + { @@_day_#1 } + #2 +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_set_val:n, \@@_gset_val:n} +% Set the value of \meta{data var} to |yyyy/mm/dd|. +% \begin{arguments} +% \item \meta{date var} +% \end{arguments} +% \begin{macrocode} +\cs_new_protected:Nn \@@_set_val:n { + \tl_set:cx {#1} { \@@_use_zfill:nn {#1} { \g_@@_sep_tl } } +} +\cs_new_protected:Nn \@@_gset_val:n { + \tl_gset:cx {#1} { \@@_use_zfill:nn {#1} { \g_@@_sep_tl } } +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_init:n, \@@_ginit:n} +% Initialize \meta{date var}. +% \begin{arguments} +% \item \meta{date var} +% \end{arguments} +% \begin{macrocode} +\cs_new_protected:Nn \@@_init:n { + \@@_set:nnnn {#1} { 1971 } { 1 } { 1 } + \@@_set_val:n {#1} +} +\cs_new_protected:Nn \@@_ginit:n { + \@@_gset:nnnn {#1} { 1971 } { 1 } { 1 } + \@@_gset_val:n {#1} +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_new:n, \@@_new:x} +% Create a new date variable. +% \begin{arguments} +% \item \meta{date var} +% \end{arguments} +% \begin{macrocode} +\cs_new_protected:Nn \@@_new:n { + \int_new:c { @@_year_#1 } + \int_new:c { @@_month_#1 } + \int_new:c { @@_day_#1 } + \@@_ginit:n {#1} +} +\cs_generate_variant:Nn \@@_new:n { x } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_clear_new:n, \@@_gclear_new:n, \@@_clear_new:x, +% \@@_gclear_new:x} +% Clear or create a new date variable. +% \begin{arguments} +% \item \meta{date var} +% \end{arguments} +% \begin{macrocode} +\cs_new_protected:Nn \@@_clear_new:n { + \int_zero_new:c { @@_year_#1 } + \int_zero_new:c { @@_month_#1 } + \int_zero_new:c { @@_day_#1 } + \@@_init:n {#1} +} +\cs_generate_variant:Nn \@@_clear_new:n { x } +\cs_new_protected:Nn \@@_gclear_new:n { + \int_gzero_new:c { @@_year_#1 } + \int_gzero_new:c { @@_month_#1 } + \int_gzero_new:c { @@_day_#1 } + \@@_ginit:n {#1} +} +\cs_generate_variant:Nn \@@_gclear_new:n { x } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_set:nnnn, \@@_gset:nnnn} +% Set the value of \meta{date var}. +% \begin{arguments} +% \item \meta{date var} +% \item \meta{year} +% \item \meta{month} +% \item \meta{day} +% \end{arguments} +% \begin{macrocode} +\cs_new_protected:Nn \@@_set:nnnn { + \int_set:cn { @@_year_#1 } {#2} + \int_set:cn { @@_month_#1 } {#3} + \int_set:cn { @@_day_#1 } {#4} + \@@_set_val:n {#1} +} +\cs_new_protected:Nn \@@_gset:nnnn { + \int_gset:cn { @@_year_#1 } {#2} + \int_gset:cn { @@_month_#1 } {#3} + \int_gset:cn { @@_day_#1 } {#4} + \@@_gset_val:n {#1} +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_set_sep:n, \@@_set:w, \@@_gset:w, \dbdatesep} +% Set internal date separator. Default is |/|. +% \begin{arguments} +% \item \meta{separator} +% \end{arguments} +% \begin{macrocode} +\cs_new_protected:Nn \@@_set_sep:n { + \tl_gset:Nn \g_@@_sep_tl { #1 } +% \end{macrocode} +% Set the value of \meta{date var}. +% \begin{arguments}[2] +% \item \meta{date var} +% \item \meta{year} +% \item \meta{month} +% \item \meta{day} +% \end{arguments} +% \begin{macrocode} + \cs_gset_protected:Npn \@@_set:w ##1\@@_sep##2#1##3#1##4\@@_stop { + \@@_clear_new:n {##1} + \@@_set:nnnn {##1} {##2} {##3} {##4} + } + \cs_gset_protected:Npn \@@_gset:w ##1\@@_sep##2#1##3#1##4\@@_stop { + \@@_gclear_new:n {##1} + \@@_gset:nnnn {##1} {##2} {##3} {##4} + } +} +\cs_gset_eq:NN \dbdatesep \@@_set_sep:n +\dbdatesep{/} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_set:nn, \@@_set:xx, \@@_gset:nn, \@@_gset:xx} +% Set the value of \meta{date var}. +% \begin{arguments} +% \item \meta{date var} +% \item \meta{date} +% \end{arguments} +% \begin{macrocode} +\cs_new_protected:Nn \@@_set:nn { + \@@_set:w #1\@@_sep#2\@@_stop +} +\cs_generate_variant:Nn \@@_set:nn { xx } +\cs_new_protected:Nn \@@_gset:nn { + \@@_gset:w #1\@@_sep#2\@@_stop +} +\cs_generate_variant:Nn \@@_gset:nn { xx } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_sub:nnN} +% Calculate the number of days between \meta{date var2} and \meta{date var1}. +% \begin{arguments} +% \item \meta{date var1} +% \item \meta{date var2} +% \item \meta{int var} to store the result +% \end{arguments} +% \begin{macrocode} +\cs_new_protected:Nn \@@_sub:nnN { + \int_zero_new:N \l_@@_sub_tmpa_int + \int_zero_new:N \l_@@_sub_tmpb_int + \@@_to_int:nN {#1} \l_@@_sub_tmpa_int + \@@_to_int:nN {#2} \l_@@_sub_tmpb_int + \int_set:Nn #3 { \l_@@_sub_tmpa_int - \l_@@_sub_tmpb_int } +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_show_two:N, \@@_show_two:c} +% Prepend 0 to single digit. +% \begin{macrocode} +\cs_new:Nn \@@_show_two:N { + \int_compare:nNnTF {#1} > { 9 } + { \int_use:N #1 } { 0\int_use:N #1 } +} +\cs_generate_variant:Nn \@@_show_two:N { c } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_use:nnnnn, \@@_use:nffff, \@@_use_zfill:nnnnn, +% \@@_use_zfill:nffff} +% Display date. +% \begin{arguments} +% \item \meta{date var} +% \item \meta{separator 1} +% \item \meta{separator 2} +% \item \meta{separator 3} +% \item \meta{separator 4} +% \end{arguments} +% \begin{macrocode} +\cs_new:Nn \@@_use:nnnnn { + #2\int_use:c { @@_year_#1 } + #3\int_use:c { @@_month_#1 } + #4\int_use:c { @@_day_#1 }#5 +} +\cs_generate_variant:Nn \@@_use:nnnnn { nffff } +\cs_new:Nn \@@_use_zfill:nnnnn { + #2\int_use:c { @@_year_#1 } + #3\@@_show_two:c { @@_month_#1 } + #4\@@_show_two:c { @@_day_#1 }#5 +} +\cs_generate_variant:Nn \@@_use_zfill:nnnnn { nffff } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_use:nn, \@@_use:nf, \@@_use_zfill:nn, \@@_use_zfill:nf} +% Display date with the same separator. +% \begin{macrocode} +\cs_new:Nn \@@_use:nn { + \@@_use:nnnnn {#1} {} {#2} {#2} {} +} +\cs_generate_variant:Nn \@@_use:nn { nf } +\cs_new:Nn \@@_use_zfill:nn { + \@@_use_zfill:nnnnn {#1} {} {#2} {#2} {} +} +\cs_generate_variant:Nn \@@_use_zfill:nn { nf } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_show:n} +% Show date in terminal. +% \begin{arguments} +% \item \meta{date var} +% \end{arguments} +% \begin{macrocode} +\cs_new_protected:Nn \@@_show:n { + \exp_args:Nx \tl_show:n { >#1~=~\@@_use:nn {#1} { - } } +} +% \end{macrocode} +% \end{macro} +% +% \begin{variable}{\dbtoday} +% Display date of today in |yyyy/mm/dd|. +% \begin{macrocode} +\tl_set:Nn \dbtoday { + \int_use:N \c_sys_year_int \g_@@_sep_tl + \int_use:N \c_sys_month_int \g_@@_sep_tl + \int_use:N \c_sys_day_int +} +% \end{macrocode} +% \end{variable} +% +% \begin{macrocode} +\endinput +%</package> +% \end{macrocode} +% +% \end{implementation} +% +% \Finale +% +\endinput diff --git a/Master/texmf-dist/source/latex/dbshow/dbshow.ins b/Master/texmf-dist/source/latex/dbshow/dbshow.ins new file mode 100644 index 00000000000..ebc4b557949 --- /dev/null +++ b/Master/texmf-dist/source/latex/dbshow/dbshow.ins @@ -0,0 +1,90 @@ +%% +%% This is file `dbshow.ins', +%% generated with the docstrip utility. +%% +%% The original source files were: +%% +%% dbshow.dtx (with options: `install') +%% +%% Copyright (C) 2022- by Changkai Li <lichangkai225@qq.com> +%% -------------------------------------------------------------------------- +%% +%% This work may be distributed and/or modified under the +%% conditions of the LaTeX Project Public License, either +%% version 1.3c of this license or (at your option) any later +%% version. This version of this license is in +%% http://www.latex-project.org/lppl/lppl-1-3c.txt +%% and 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 Changkai Li. +%% +%% This package consists of the file dbshow.dtx, +%% and the derived files dbshow.pdf, +%% dbshow.sty, +%% dbshow.ins and +%% README.md. +%% -------------------------------------------------------------------------- +%% + +\input l3docstrip.tex +\keepsilent +\askforoverwritefalse +\preamble + + Copyright (C) 2022- by Changkai Li <lichangkai225@qq.com> + -------------------------------------------------------------------------- + + This work may be distributed and/or modified under the + conditions of the LaTeX Project Public License, either + version 1.3c of this license or (at your option) any later + version. This version of this license is in + http://www.latex-project.org/lppl/lppl-1-3c.txt + and 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 Changkai Li. + + This package consists of the file dbshow.dtx, + and the derived files dbshow.pdf, + dbshow.sty, + dbshow.ins and + README.md. + -------------------------------------------------------------------------- + +\endpreamble +\postamble + + -------------------------------------------------------------------------- + This package consists of the file dbshow.dtx, + and the derived files dbshow.pdf, + dbshow.sty, + dbshow.ins and + README.md. +\endpostamble + +\generate{ + \usedir{tex/latex/dbshow} + \file{dbshow.sty}{\from{\jobname.dtx}{package}} + \nopreamble\nopostamble + \usedir{doc/latex/dbshow} + \file{README.md} {\from{\jobname.dtx}{readme}} +} +\endbatchfile +%% +%% -------------------------------------------------------------------------- +%% This package consists of the file dbshow.dtx, +%% and the derived files dbshow.pdf, +%% dbshow.sty, +%% dbshow.ins and +%% README.md. +%% +%% End of file `dbshow.ins'. |