LaTeX3 Tutorial (3)-Starting with an example

Identify the target

Learning a programming language, if you only understand the grammar, is bound to be very boring and useless. Therefore, we are going to start with a basic example and cover the important knowledge of LaTeX3 as much as possible.

The example here is a Chinese test text (random number fake text) macro package. The purpose of the fake text is to generate a large section of text without actual meaning, which is often used to test the typesetting effect. For Western languages, several macro packages have been included in the TEX distribution, including  lipsum, kantlipsum and  blindtext etc .; for Chinese, I wrote it myself  zhlipsum. Our goal is to let everyone complete a similar zhlipsum macro package in these tutorials  . Readers who have read Liu Haiyang's "Introduction to LaTeX" will find that this is actually Exercise 8.6 in the book.

To write a Chinese test text macro package, first of all, we need to do overall planning and design:

  • The simplest implementation, as long as you define some commands that contain a lot of text, and can make users easy to use.

  • Next, in order to be able to change the length of the hypothetical fragment, we need to use functions such as counting and looping, and also introduce some basic data structures.

  • Afterwards, because it is for Chinese testing, our macro package must also be able to support multiple character sets and encodings, which need to be handled by the category code mechanism.

  • Finally, as a complete macro package, a set of good interfaces and perfect error prompts are also required to facilitate users' use.

Of course, if possible, we will also consider introducing some advanced features, such as:

  • Use pseudo-random numbers to generate true "random numbers" hypotheses.

  • By  DocStrip and  doc packages literary program.

  • Build sufficient coverage tests to complete the release of the macro package.

ready

Development environment configuration

Considering that LaTeX3 is still under active development, it is recommended to use the latest TEX distribution. On the other hand, the development of LaTeX packages is slightly different from the daily writing documents. In many cases, we need to debug through the command line output, rather than just compiling TEX documents to generate PDFs. Therefore, it is recommended to use the command line to compile directly. Editors developed for writing with TEX, such as WinEdt, TeXstudio, etc., may not be suitable for this. Our project will involve Chinese character processing, so we must ensure that the editor has good support for multiple encodings.

I personally use  TeX Live 2019  +  Visual Studio Code .

In addition, if you want to start a formal project / project, it is strongly recommended to establish a Git repository to manage. For details, please refer to Liao Xuefeng's Git tutorial .

Directory Structure

We intend to name this macro package  zhdummy. Of course, the name can be taken arbitrarily, but please make sure to search the Internet for conflicts before making sure. In the TEX distribution, files with the same name cannot appear; similarly, the names of macro packages must not be repeated.

The suffix name of the LaTeX package is  .sty, so the file name is  zhdummy.sty. The macro package itself cannot generally be compiled directly. Therefore, in order to check its correctness, some test files need to be added. We only use one simple for the time being  test.tex. These two files currently need to be placed in the same directory (may be set to  zhdummy):

zhdummy/
  ├─zhdummy.sty
  └─test.tex

Below we start to write the macro package:

% zhdummy.sty
\NeedsTeXFormat{LaTeX2e}
\RequirePackage{expl3}
\ProvidesExplPackage{zhdummy}{2019/11/20}{0.1}{Chinese dummy text (demo)}

\def\mypkgname{zhdummy}

\NeedsTeXFormat{LaTeX2e} Shows that the macro package requires LaTeX 2 ε  format, and does not accept plain TeX and ConTeXt formats. The subsequent statements have been introduced before, and will not be repeated here.

The test file can be written like this:

% test.tex
\documentclass{ctexart}
\usepackage{zhdummy}

\begin{document}
你好,\mypkgname{}!
\end{document}

Compile with X⁠ELaTeX (no special instructions later, we will always use X⁠ELaTeX) test.tex, the result should be as follows:

Hello, zhdummy!

In addition, in the log file  test.log , the following information should be found:

(./zhdummy.sty
Package: zhdummy 2019/11/20 v0.1 Chinese dummy text (demo)
)

This  ./zhdummy.sty means ./that the zhdummy.sty file in the current directory (ie ) is  read in  . The brackets outside will wrap up all the information generated during the reading process (such as the macro package version here). If the read-in file calls a new file, it will be nested in layers. Although our macro package is also called  expl3.sty, the previous  ctexart document class has actually been called once, so the macro package  \RequirePackage{expl3} is ignored. This is very similar to C / C ++ language header file calls.

After a compilation, the current directory structure will become like this:

zhdummy/
  ├─zhdummy.sty    宏包
  ├─test.tex       测试文件
  ├─test.aux       编译辅助文件
  ├─test.log       编译日志
  └─test.pdf       生成的 PDF

So far, we have written a simplest macro package. Of course, it has no function other than printing its own name.

Add a fake

Our macro package now actually has only one line to have practical effect:

\def\mypkgname{zhdummy}

It defines a \mypkgname macro named  and can be expanded to  zhdummy. In fact, the so-called "false text" is nothing more than such macros that can be expanded into text, but the text is longer.

In LaTeX3, the text is very suitable for routine called token list type (token lists) storage. The prefix of the related function is  tl.

A list of tokens, as the name implies, consists of a series of tokens (also called characters). The mark refers to either a character with a category code or a control sequence. For example, in the standard case, it {\hskip 36 pt} is the following set of symbols (the subscript indicates the category code and the  space, \hskip and the space after attention  is ignored):

{\hskipControl sequence  312  612  10  p11  t11  }2

However, for now, we can ignore these technical details first. After all, the fake text contains almost only Chinese characters, punctuation, and some letters and numbers. They are relatively "normal" things and do not require special treatment.

Using the list of tokens, we can rewrite the previous macro definition into the following form:

\tl_const:Nn \c_zhdummy_text_i { 天地玄黄,宇宙洪荒。 }

\tl_const:Nn It means to create a  tl constant and use the second parameter as its content. There are a few points to note here:

  1. Constants  c start with

  2. We start with the module name  zhdummy, usually it should be the same as the package name

  3. Temporarily set this constant to public (  c_ not  c__ at the beginning)

  4. Spaces are ignored in LaTeX3 syntax

  5. The use of "Thousand Characters" here is just a demonstration, and the actual hypothesis will be much longer

Similarly, we can add more hypotheses:

\tl_const:Nn \c_zhdummy_text_i     { 天地玄黄,宇宙洪荒。 }
\tl_const:Nn \c_zhdummy_text_ii    { 日月盈昃,辰宿列张。 }
\tl_const:Nn \c_zhdummy_text_iii   { 寒来暑往,秋收冬藏。 }
\tl_const:Nn \c_zhdummy_text_iv    { 闰馀成岁,律吕调阳。 }
\tl_const:Nn \c_zhdummy_text_v     { 云腾致雨,露结为霜。 }
\tl_const:Nn \c_zhdummy_text_vi    { 金生丽水,玉出昆冈。 }
\tl_const:Nn \c_zhdummy_text_vii   { 剑号巨阙,珠称夜光。 }
\tl_const:Nn \c_zhdummy_text_viii  { 果珍李柰,菜重芥姜。 }
\tl_const:Nn \c_zhdummy_text_ix    { 海咸河淡,鳞潜羽翔。 }
\tl_const:Nn \c_zhdummy_text_x     { 龙师火帝,鸟官人皇。 }
\tl_const:Nn \c_zhdummy_text_xi    { 始制文字,乃服衣裳。 }
\tl_const:Nn \c_zhdummy_text_xii   { 推位让国,有虞陶唐。 }
\tl_const:Nn \c_zhdummy_text_xiii  { 吊民伐罪,周发殷汤。 }
\tl_const:Nn \c_zhdummy_text_xiv   { 坐朝问道,垂拱平章。 }
\tl_const:Nn \c_zhdummy_text_xv    { 爱育黎首,臣伏戎羌。 }
\tl_const:Nn \c_zhdummy_text_xvi   { 遐迩壹体,率宾归王。 }
\tl_const:Nn \c_zhdummy_text_xvii  { 鸣凤在树,白驹食场。 }
\tl_const:Nn \c_zhdummy_text_xviii { 化被草木,赖及万方。 }

When using, you can use it directly, or you can use the  \tl_use:N command:

% test.tex
\documentclass{ctexart}
\usepackage{zhdummy}

\begin{document}
\ExplSyntaxOn
\c_zhdummy_text_i
\tl_use:N \c_zhdummy_text_ii
\ExplSyntaxOff
\end{document}

Heaven and Earth Xuanhuang, cosmic flood. The sun and the moon are full, and Chen Su is listed.

Such a definition is obviously too long and inefficient. However, a more serious problem is that the tl variables defined in this way  can only be used in the LaTeX3 environment. Direct use will cause errors:

% test.tex
\documentclass{ctexart}
\usepackage{zhdummy}

\begin{document}
\tl_use:N \c_zhdummy_text_i
\end{document}

Obtained after compilation (you can press return to continue when interrupted)

! Undefined control sequence.
l.6 \tl
  _use:N \c_zhdummy_text_i
?
! Missing $ inserted.
<inserted text>
              $
l.6 \tl_
      use:N \c_zhdummy_text_i
?

LaTeX Warning: Command \c invalid in math mode on input line 6.

! Missing $ inserted.
<inserted text>
              $
l.7 \end{document}
?

_ The subscript under the conventional category code setting must be used in the mathematical environment, so  the commands defined with  _ and  :cannot be accepted by LaTeX. This will cause great trouble to the user, obviously contrary to our original intention of writing macro packages. Therefore, next we have to create some user layer (or document layer ) commands to distinguish it from the programming layer .

User interface

We analyze the hypothetical commands defined above, and we can see that they all have some things in common:

  • The front is unified \c_zhdummy_text_

  • Followed by lowercase Roman numerals, such as  i,ii

Thereby, the user can input the required paragraph number and select the required hypothesis accordingly. Of course, in most cases, what the user needs is probably a simple command. This can be achieved by default parameters.

We treat it  \zhdummy as a user-level command. It is specified as follows:

\zhdummy
\zhdummy[<序号>]

Without parameters, output the first four paragraphs of fake text; with parameters, output the specified  <序号> fake text, which  <序号> is represented by Arabic numerals.

To implement this user-level command, the following problems need to be solved:

  • Convert Arabic numerals to (lowercase) Roman numerals

  • Flatten command (control sequence)

  • Define user-level commands in a more reliable way

It is not easy to do these tasks from scratch. Fortunately, LaTeX3 provides us with a relatively good and easy-to-use framework. We will introduce them in turn below.

Digital conversion

expl3 The conversion function provided is  \int_to_roman:n. As the name implies, this function takes an integer parameter and converts it to lowercase Roman numerals. In addition  \int_to_Roman:n, it is easy to guess its meaning.

We can do some experiments ( ~ representing spaces in LaTeX3):

\int_to_roman:n { 1 } ~
\int_to_roman:n { 5 } ~
\int_to_roman:n { 4999 } ~
\int_to_Roman:n { 1 } ~
\int_to_Roman:n { 5 } ~
\int_to_Roman:n { 4999 }

The result should be:

iv mmmmcmxcix IV MMMMCMXCIX

Flatten commands

Similar to the C language  ## macros , Python in  eval and in Mathematica  Symbol, expl3 also provides a means to convert "string" as the command.

To this end, we first review the parameter designation mentioned earlier . It is located : behind a function  and describes the parameter structure of the function. The basic parameter specification includes  n,, Nand so p on. For example  \tl_use:N, it means accepting a token (such as a control sequence) as a parameter.

A new parameter specification is now introduced  c, which means that the parameter is treated as the name of a control sequence. For example, the following types of writing are equivalent:

\tl_use:N \c_zhdummy_text_i
\tl_use:c { c_zhdummy_text_i }
\tl_use:c { c _ zhdummy _ text _ i }  % 注意空格是忽略掉的

Introduction to `xparse` package

As the saying goes, "all right", all of our work above must be oriented towards users. The solution provided by LaTeX3 is a  xparse macro package, which can easily declare user layer (document layer) commands.

At the bottom of the code, the programmer should control the appropriate granularity so that most functions only complete a single job. Therefore, the parameters of the underlying function should be determined. But at the user level, the requirements can vary, but the interface should be as uniform as possible, which requires a certain diversity of parameter forms. This is similar to the so-called ad hoc  polymorphism realized  by function overloading in C ++ .

xparse The macro package provides  \NewDocumentCommand functions with the following syntax:

\NewDocumentCommand <func> {<arg-spec>} {<code>}
  • <func> We shall provide to the end-user's command, in general, it should contain only letters, but excluding  _, :, @ and other special symbols

  • plaintext <arg-spec> It is the parameter specification (note that it is different from the parameter specification of the previous LaTeX3 function), which can be:

  • m: Indicates the standard mandatory ( m andatory) parameter, which can be a single token or {…} a group of tokens surrounded by curly braces 

  • o: Represents the standard optional ( O OPTIONAL) parameters required brackets  […] surround; if not given, then return a special  -NoValue- marker

  • O{}: Also an optional parameter, but returns the default value if not given

  • Other more complicated parameter assignments, as well as some special cases, we will introduce later

  • Examples are as follows:

    Parameter specification input value #1 #2 #3
    m m {foo}{bar} foo bar  
    o m {foo} -NoValue- foo  
    o o m [foo]{bar} foo -NoValue- bar
    m O{default} {foo} foo default  
    m O{default} {foo}[bar] foo bar  
    m O{default} [bar] Report an error    
  • <code> For specific implementation code,  such parameters can be used  #1, #2which is consistent with traditional TEX programming

  • In addition, xparse several functions are provided. Their usage is the  \NewDocumentCommand same, but the meaning is slightly different:

    function <func> Defined <func> Undefined
    \NewDocumentCommand Report an error Give a definition
    \RenewDocumentCommand redefine Report an error
    \ProvideDocumentCommand do nothing Give a definition
    \DeclareDocumentCommand redefine Give a definition

As mentioned above, when the input is empty, the o type parameter will return a special  -NoValue- mark. This token is not a simple token list, it must \IfNoValue(TF) be judged by the  function:

\IfNoValueTF {<arg>} {<true code>} {<false code>}
\IfNoValueT  {<arg>} {<true code>}
\IfNoValueF  {<arg>} {<false code>}

Depending on <arg> whether the parameter  is -NoValue-, it \IfNoValue(TF) will decide whether  to execute  <true code> or not  <false code>.

Code

Finally, we combine the above analysis, we can write the following code:

% 定义命令 `\zhdummy`,允许带一个可选参数
\NewDocumentCommand \zhdummy { o }
  {
    % 根据参数 `#1` 是否为 `-NoValue-` 分别进行处理
    \IfNoValueTF {#1}
      {
        % `#1` = `-NoValue-`,即不带参数
        % 直接使用假文命令
        \tl_use:N \c_zhdummy_text_i
        \tl_use:N \c_zhdummy_text_ii
        \tl_use:N \c_zhdummy_text_iii
        \tl_use:N \c_zhdummy_text_iv
      }
      {
        % `#1` ≠ `-NoValue-`,即带有可选参数
        % 把 `#1` 转换为小写罗马数字,再拼合成假文命令
        \tl_use:c { c_zhdummy_text_ \int_to_roman:n {#1} }
      }
  }

At this point,  test.tex you can use the fake text in a more conventional way:

% test.tex
\documentclass{ctexart}
\usepackage{zhdummy}

\begin{document}
\zhdummy

\zhdummy[1]
\zhdummy[2]
\zhdummy[18]
\end{document}

Got after compiling

Heaven and Earth Xuanhuang, cosmic flood. The sun and the moon are full, and Chen Su is listed. Cold travels in summer, and autumn harvests in winter. Lei Yucheng was a year old, Lu Lviaoyang. Yunteng caused rain, and the dew formed frost.

Heaven and Earth Xuanhuang, cosmic flood. The sun and the moon are full, and Chen Su is listed. Turned into vegetation, rely on Wanfang.

Published 117 original articles · 69 praises · 10,000+ views

Guess you like

Origin blog.csdn.net/zsd0819qwq/article/details/105378717