% \iffalse %<*copyright> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% conv-xkv package %% %% Copyright (C) 2016--2017 D. P. Story %% %% dpstory@uakron.edu %% %% %% %% This program can redistributed and/or modified under %% %% the terms of the LaTeX Project Public License %% %% Distributed from CTAN archives in directory %% %% macros/latex/base/lppl.txt; either version 1.2 of the %% %% License, or (at your option) any later version. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % %\NeedsTeXFormat{LaTeX2e} %\ProvidesPackage{conv-xkv} % [2017/02/17 v1.1c convert xkeyval format (dps)] %<*driver> \documentclass{ltxdoc} \usepackage[colorlinks,hyperindex=false]{hyperref} \OnlyDescription % comment out for implementation details \EnableCrossrefs \CodelineIndex \RecordChanges \InputIfFileExists{aebdocfmt.def}{\PackageInfo{web}{Inputting aebdocfmt.def}} {\def\IndexOpt{\DescribeMacro}\def\IndexKey{\DescribeMacro}\let\setupFullwidth\relax \PackageInfo{web}{aebdocfmt.def cannot be found}} \begin{document} \def\ameta#1{\ensuremath{\langle\textit{\texttt{#1}}\rangle}} \def\meta#1{\textsl{\texttt{#1}}} \def\darg#1{{\ttfamily\char123\relax#1\char125\relax}} \def\CMD#1{\textbackslash#1} \let\pkg\textsf \let\opt\texttt \GetFileInfo{conv-xkv.sty} \title{\textsf{conv-xkv}: Convert \textsf{xkeyval} format style} \author{D. P. Story\\ Email: \texttt{dpstory@uakron.edu}} \date{processed \today} \maketitle \tableofcontents \DocInput{conv-xkv.dtx} \IfFileExists{\jobname.ind}{\newpage\setupFullwidth\par\PrintIndex}{\paragraph*{Index} The index goes here.\\Execute \texttt{makeindex -s gind.ist -o conv-xkv.ind conv-xkv.idx} on the command line and recompile \texttt{conv-xkv.dtx}.} \IfFileExists{\jobname.gls}{\PrintChanges}{\paragraph*{Change History} The list of changes goes here.\\Execute \texttt{makeindex -s gglo.ist -o conv-xkv.gls conv-xkv.glo} on the command line and recompile \texttt{conv-xkv.dtx}.} \end{document} % % \fi % % \MakeShortVerb{|} % \DoNotIndex{\w,\x,\y,\z} % \InputIfFileExists{aebdonotindex.def}{\PackageInfo{web}{Inputting aebdonotindex.def}} % {\PackageInfo{web}{cannot find aebdonotindex.def}} % % \begin{macrocode} %<*package> % \end{macrocode} % \section{Introduction} % This is a intellectual exercise for creating \emph{alternate key-value} notation. The standard {\LaTeX} % notation is \texttt{\ameta{key}=\ameta{value}}. To change to the JavaScript object style of % key-values (\texttt{\ameta{key}:\ameta{value}}), use \cs{cxkvsetkeys} as you would \cs{setkeys}: %\begin{quote} % \cs{cxkvsetkeys}\darg{\ameta{family}}\darg{\ameta{KV-pairs}} %\end{quote} % to convert \texttt{\ameta{key}:\ameta{value}} to \texttt{\ameta{key}=\ameta{value}} and \pkg{xkeyval} processes the keys as it % normally does. The comma (,) separates sets of key-value pairs and must not, therefore, be used as the delimiter % that separates the \ameta{key} from the \ameta{value}. % % The package is more general than what is described above. You can define several key-value delimiters, for whatever reason, % in your document or package. Declare a \textit{named} delimiter: %\begin{quote}\ttfamily %\cs{DeclareDelimiter}\darg{\ameta{name}}\darg{\ameta{delimiter}} %\end{quote} %Use the newly declared delimited as follows: %\begin{quote} %\cs{cxkvsetkeys(\ameta{name})}\darg{\ameta{family}}\darg{\ameta{KV-pairs}} %\end{quote} %The case of using a colon (:) for the delimiter is already defined, its name is `\texttt{colon}' and need not be declared. % %\paragraph*{Important change in syntax} With version dated 2017/01/03 or later, the optional argument \ameta{name} is now delimited %by \textbf{parentheses}, rather than the standard brackets. This is to be able to detect \ameta{name} when the full syntax %of \cs{setkeys} is used: %\changes{v1.1a}{2017/01/03}{Change in syntax, use parentheses rather than brackets} %\begin{quote} % \cs{setkeys*[\ameta{prefix}]\darg{\ameta{families}}[\ameta{na}]\darg{\ameta{keys}}} %\end{quote} %The syntax for \cs{cxkvsetkeys} shall be %\begin{quote} % \cs{cxkvsetkeys(\ameta{name})*[\ameta{prefix}]\darg{\ameta{families}}[\ameta{na}]\darg{\ameta{keys}}} %\end{quote} % The \pkg{conv-xkv} package does nothing with \pkg{xkeyval} arguments \texttt{*[\ameta{prefix}]} and % \texttt{[\ameta{na}]} other than to collect them and pass them on to \cs{setkeys} at the appropriate time. % The \pkg{conv-xkv} is concerned only with converting a new notation \texttt{\ameta{key}\ameta{delim}\ameta{value}} % to \texttt{\ameta{key}=\ameta{value}}. % % If the key-values do not contain the designated delimiter, \pkg{conv-xkv} simply passes everything on to \cs{setkeys}. % What this means is that, for example, both \cs{cxkvsetkeys\darg{myfam}\darg{fname:Don,lname:Story}} works as does % \cs{cxkvsetkeys\darg{myfam}\darg{fname=Don,lname=Story}}. One then has the option of using the standard notation % or an alternate notation. % % % \paragraph*{Demo file} The example file is \texttt{convert2xkeyval.tex}, use it to explore the possibilities and is found % in the \texttt{examples} folder of this distribution. % % \section{Preliminaries} % We require the \pkg{xkeyval} package. % \changes{v1.0}{2016/12/20}{Date of first upload to CTAN} % \begin{macrocode} \RequirePackage{xkeyval} % \end{macrocode} % The code below is taken from \pkg{hyperref}, and set and restore commands are renamed. This hopefully makes % a number of special characters available to act as a delimiter. % \begin{macrocode} \begingroup \@makeother\`% \@makeother\=% \edef\x{% \edef\noexpand\x{% \endgroup \noexpand\toks@{% \catcode 96=\noexpand\the\catcode`\noexpand\`\relax \catcode 61=\noexpand\the\catcode`\noexpand\=\relax }% }% \noexpand\x }% \x \@makeother\` \@makeother\= \def\ckv@SetCatcodes{% \@makeother\`% \@makeother\=% \@makeother\~% \catcode`\$=3 % \catcode`\&=4 % \catcode`\^=7 % \catcode`\_=8 % \@makeother\|% \@makeother\:% \@makeother\(% \@makeother\)% \@makeother\[% \@makeother\]% \@makeother\/% \@makeother\!% \@makeother\<% \@makeother\>% \@makeother\.% \@makeother\;% \@makeother\+% \@makeother\-% \@makeother\"% \@makeother\'% } \begingroup \def\x#1{\catcode`\noexpand#1=\the\catcode`#1\relax}% \xdef\ckv@RestoreCatcodes{% \the\toks@ \x\~% \x\$% \x\&% \x\^% \x\_% \x\|% \x\:% \x\(% \x\)% \x\[% \x\]% \x\/% \x\!% \x\<% \x\>% \x\.% \x\;% \x\+% \x\-% \x\"% \x\'% }% \endgroup \ckv@SetCatcodes % \end{macrocode} % \section{Core commands for this package} % The default delimiter is the colon (:). % \begin{macrocode} \def\csarg#1#2{\expandafter#1\csname#2\endcsname} \csarg\def{kvdelim-colon}{:} % \end{macrocode} % Use \DescribeMacro{\usekvdelim}\cs{usekvdelim} to display delimiter, as associated with % the argument \texttt{\#1}. % \begin{macrocode} \def\usekvdelim#1{\@nameuse{kvdelim-#1}} % \end{macrocode} % \begin{macro}{\DeclareDelimiter} % In the preamble, we declare the delimiter to be used. The command takes one argument, which % is the delimiter to be used, for example `\texttt{:}' or `\texttt{->}'. If this declaration % does not appear in the preamble, the delimiter is taken to be `\texttt{:}'. % \begin{macrocode} \def\DeclareDelimiter{\ckv@SetCatcodes\DeclareDelimiter@i} \def\DeclareDelimiter@i#1#2{\@ifundefined{kvdelim-#1} {\csarg\def{kvdelim-#1}{#2}\ckv@RestoreCatcodes\cxkvSetup{#1}} {\ckv@RestoreCatcodes}} \@onlypreamble\DeclareDelimiter % \end{macrocode} % \end{macro} % \cs{cxkv@tmptoks} is used to hold the converted key-values, the contents of % this token register is passed to \cs{setkeys} in \cs{cxkv@cnvrtDelimniiEquali} % \begin{macrocode} \newtoks\cxkv@tmptoks \cxkv@tmptoks={} \def\cxkv@dummy{dummy} \def\cxkv@dummyc{dummy,} \bgroup \catcode`\#=12\relax\gdef\cxkvarg{#} \obeyspaces\gdef\cxkv@TAB{ } \egroup % \end{macrocode} % \begin{macro}{\cxkvsetkeys} % This is the default definition, setup for using the colon (:) as the key-value % delimiter. But these next two commands are redefined by the % \cs{DeclareDelimiter} command in the preamble. The syntax is %\begin{quote} %\cs{cxkvsetkeys[\ameta{name}]}\darg{\ameta{family}}\darg{\ameta{KV-pairs}} %\end{quote} %where \ameta{KV-pairs} are the key-value pairs using the declared delimiter. %\begin{quote}\ttfamily %\cs{cxkvsetkeys}\darg{myfam}\{fname:\,Fred,lname:\,Flintstone\} %\end{quote} %The family \texttt{myfam} and keys \texttt{fname} and \texttt{lname} must have been defined %earlier: If the optional argument is not specified, then it is assumed the \ameta{name} argument %is \texttt{colon}, a reserved word for this package for this argument. %\begin{verbatim} % \define@key{myfam}{\def\fname{#1}} % \define@key{myfam}{\def\lname{#1}} %\end{verbatim} % \begin{macrocode} \def\cxkv@colon{colon} % \end{macrocode} % The general form for \cs{setkeys} is %\begin{quote} % \cs{setkeys*[\ameta{prefix}]\darg{\ameta{families}}[\ameta{na}]\darg{\ameta{keys}}} %\end{quote} % \begin{macro}{\cxkvsetkeys} %The syntax for \cs{cxkvsetkeys} shall be %\begin{quote} % \cs{cxkvsetkeys(\ameta{name})*[\ameta{prefix}]\darg{\ameta{families}}[\ameta{na}]\darg{\ameta{keys}}} %\end{quote} %The process to pick up the full parameter set of \cs{setkeys} is lengthy. % \begin{macrocode} \newcommand\cxkvsetkeys{% \@ifnextchar({\cxkvsetkeys@i}{\cxkvsetkeys@i(colon)}} \def\cxkvsetkeys@i(#1){\cxkvsetkeys@ii{#1}} \def\cxkvsetkeys@ii#1{\def\cxkv@delimname{#1}\@ifstar {\def\cxkv@skOpts{*}\cxkvsetkeys@iii} {\def\cxkv@skOpts{}\cxkvsetkeys@iii}} \newcommand\cxkvsetkeys@iii[2][]{\def\@rgi{#1}\ifx\@rgi\@empty \expandafter\def\expandafter\cxkv@skOpts \expandafter{\cxkv@skOpts{#2}}\else \expandafter\def\expandafter \cxkv@skOpts\expandafter{\cxkv@skOpts[#1]{#2}}\fi \def\thisxkvF@mily{#2}\cxkvsetkeys@iv} \newcommand\cxkvsetkeys@iv[2][]{\def\@rgi{#1}\ifx\@rgi\@empty\else \expandafter\def\expandafter\cxkv@skOpts \expandafter{\cxkv@skOpts[#1]}\fi \expandafter\cxkvsetkeys@v\expandafter{\thisxkvF@mily}{#2}} \def\cxkvsetkeys@v#1#2{\cxkv@skipfalse \ifx\cxkv@delimname\cxkv@colon\else \InputIfFileExists{xkv-\cxkv@delimname.cut} {\PackageInfo{conv-xkv}{Inputting xkv-\cxkv@delimname.cut}} {\PackageInfo{conv-xkv}{Cannot find xkv-\cxkv@delimname.cut}}\fi \@nameuse{cxkvsetkeys-\cxkv@delimname}{#1}{#2}} % \end{macrocode} % \end{macro} % \begin{macrocode} \csarg\def{cxkvsetkeys-colon}#1#2{% \def\thisxkvF@mily{#1}\def\thisxkvV@lues{#2}\def\cxkv@scratch{}% \cxkv@tmptoks={}% \@nameuse{cxkv@cnvrtDelimniiEqual-colon}#2,dummy:dummy,\@nil} \csarg\def{cxkv@cnvrtDelimniiEqual-colon}#1:#2,#3\@nil{% \cxkv@cnvrtDelimniiEquali{colon}{#1}{#2}{#3}} % \end{macrocode} % \end{macro} % \begin{macro}{\cxkvSetup} % Write the definitions of \cs{cxkvsetkeys} and \cs{cxkv@cnvrtDelimniiEqual} to % the file \texttt{conv-xkv.cut} then input this file back in. % \begin{macrocode} \def\cxkvSetup#1{\bgroup \IfFileExists{xkv-#1.cut}{\PackageInfo{conv-xkv}{xkv-#1.cut already exists,\MessageBreak will not create another one}}{% \PackageInfo{conv-xkv}{Creating the file xkv-#1.cut containing\MessageBreak required definitions}% \newwrite \cxkv@write \uccode`c=`\% \def\w{#1}\def\x{cxkvsetkeys-#1}% \def\y{cxkv@cnvrtDelimniiEqual-#1}% \def\z{kvdelim-#1}% \immediate\openout \cxkv@write xkv-#1.cut \immediate\write\cxkv@write{\string\makeatletter}% \uppercase{\immediate\write\cxkv@write{\string \csarg\string\def{\y}\cxkvarg1\@nameuse{\z}% \cxkvarg2,\cxkvarg3\string\@nil{c^^J\cxkv@TAB \string\cxkv@cnvrtDelimniiEquali{\w}{\cxkvarg1}% {\cxkvarg2}{\cxkvarg3}}}} \uppercase{\immediate\write\cxkv@write{\string\csarg\string\def {\x}\cxkvarg1\cxkvarg2{c^^J\cxkv@TAB \string\def\string\thisxkvF@mily{\cxkvarg1}\string \def\string\thisxkvV@lues{\cxkvarg2}\string \let\string\cxkv@scratch\string\@empty\string \cxkv@tmptoks={}c^^J\cxkv@TAB \string\@nameuse{\y}\cxkvarg2,% \cxkv@dummy\@nameuse{\z}\cxkv@dummy,\string\@nil}}} \immediate\write\cxkv@write{\string\makeatother}% \immediate\closeout \cxkv@write }% \egroup} % \end{macrocode} % \end{macro} % \cs{cxkv@cnvrtDelimniiEquali} continues \cs{cxkv@cnvrtDelimniiEqual}. It is % the part that does not need to be redefined. % \begin{macrocode} \newif\ifcxkv@keyonly \cxkv@keyonlyfalse \def\cxkv@comma{,} \def\cxkv@removecomma#1,\@nil{\def\cxkv@key{#1}} \def\cxkv@parsecomma#1,#2\@nil{\def\@rgi{#1}\def\@rgii{#2}% \ifx\@rgii\@empty\cxkv@keyonlyfalse\else \cxkv@keyonlytrue\cxkv@removecomma#2\@nil\fi} \newif\ifcxkv@skip \cxkv@skipfalse \def\cxkv@cnvrtDelimniiEquali#1#2#3#4{% \def\cxkv@rgiii{#3}\def\cxkv@rgiv{#4}% % \end{macrocode} % If the fourth argument is empty, that means there were no delimiters in the argument, % so we pass the original argument \cs{thisxkvF@mily} to \cs{setkeys}. % \changes{v1.1}{2017/01/03}{Try to detect if the expected delimiter is present at all} % \begin{macrocode} \ifx\thisxkvV@lues\@empty\else \ifx\cxkv@rgiv\@empty \edef\cxkv@next{\noexpand \setkeys\cxkv@skOpts{\thisxkvV@lues}}% \cxkv@skiptrue \fi \fi \let\thisxkvV@lues\@empty \ifcxkv@skip\else \ifx\cxkv@rgiii\cxkv@dummy \cxkv@parsecomma#2,\@nil \ifcxkv@keyonly \edef\cxkv@tmp{\the\cxkv@tmptoks,\@rgi}% \cxkv@tmptoks=\expandafter{\cxkv@tmp}% \edef\cxkv@scratch{\the\cxkv@tmptoks}% \edef\cxkv@next{\noexpand \setkeys\cxkv@skOpts{\the\cxkv@tmptoks}}% \else \edef\cxkv@next{\noexpand \setkeys\cxkv@skOpts{\the\cxkv@tmptoks}}% \fi \else \cxkv@parsecomma#2,\@nil \ifcxkv@keyonly \edef\cxkv@tmp{\the\cxkv@tmptoks,\@rgi}% \cxkv@tmptoks=\expandafter{\cxkv@tmp}% \edef\cxkv@scratch{\the\cxkv@tmptoks}% \edef\cxkv@next{\noexpand \@nameuse{cxkv@cnvrtDelimniiEqual-#1}\cxkv@key \@nameuse{kvdelim-#1}#3,#4\noexpand\@nil} \else % \end{macrocode} % (2017/02/17) Enclose \texttt{\#3} in braces % \changes{v1.1c}{2017/02/17}{Enclose \string\texttt{\#3} in braces} % \begin{macrocode} \cxkv@tmptoks=\expandafter{\cxkv@scratch,#2={#3}}% \edef\cxkv@scratch{\the\cxkv@tmptoks}% \def\cxkv@next{% \@nameuse{cxkv@cnvrtDelimniiEqual-#1}#4\@nil}\fi \fi\fi\cxkv@next } \ckv@RestoreCatcodes % \end{macrocode} % \begin{macrocode} % % \end{macrocode} % \Finale \endinput