> For the complete documentation index, see [llms.txt](https://overleaf-pro.ayaka.space/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://overleaf-pro.ayaka.space/latex/ko/in-depth-articles/09-an-introduction-to-luatex-part-2-understanding-directlua.md).

# LuaTeX 소개(2부): \directlua 이해하기

## 이 글의 목표

이 글의 첫 부분에서, [LuaTeX 소개(1부): 무엇이며—무엇이 그렇게 다른가?](/latex/ko/in-depth-articles/07-an-introduction-to-luatex-part-1-what-is-it-and-what-makes-it-so-different.md), 우리는 LuaTeX를 매우 다재다능한 TeX 엔진으로 간략히 살펴보았다. 즉, 정교하고 프로그래밍 가능한 조판 시스템으로, 문서 엔지니어링 및 제작 솔루션을 구축할 수 있는 다양한 도구를 제공한다.

이 마무리 편에서는 LuaTeX 도구상자의 가장 핵심적인 구성 요소인 `\directlua` 명령을 자세히 살펴본다. 이 명령은 Lua 스크립트 언어를 통해 LuaTeX의 조판을 프로그램적으로 제어할 수 있는 “관문”을 제공한다.

하지만 `\directlua` 를 통해 LuaTeX를 완전히 활용하려면 몇 가지 TeX 주제에 대한 배경 지식이 필요하다: TeX의 토큰, 토큰 목록, 그리고 확장 메커니즘. 이 글의 목표는 이러한 기본적인 TeX 개념을 탐구하고 설명하는 것이다: 그 뒤에 있는 TeX 관련 프로세스를 하나씩 짚어 가며 `\directlua` 의 작동 방식을 이해하고, LuaTeX를 사용해 직접 조판 솔루션을 구축할 수 있는 기초를 제공하는 것이다.

이 글은 다음의 핵심적인 측면을 보여 주고 설명하기 위해 수많은 짧은 예제를 포함한다: `\directlua`의 동작을 설명하며, 지나치게 복잡한 코드 대신 짧은 코드 조각을 의도적으로 사용한다. 필요한 경우 예제는 기본(원시/plain) TeX를 사용한다. 대부분의 사람들은 LaTeX(매크로)를 사용하고 선호하지만, 기본 TeX 명령은 단순하다는 장점이 있다.

## LuaTeX의 Lua 소개

[Lua](https://www.lua.org/about.html) 은 [소스 코드가](https://www.lua.org/download.html) 이식성이 매우 뛰어나고 소프트웨어 애플리케이션에 쉽게 내장할 수 있는 스크립트 언어로, 개발자가 자신의 프로그램에 스크립팅 기능을 포함할 수 있게 해 준다. Lua는 [많은 애플리케이션에 내장되어 왔고](https://en.wikipedia.org/wiki/List_of_applications_using_Lua) 소프트웨어 게임 산업에서도 널리 쓰이는 선택지이며—가장 유명한 예는 아마도 [월드 오브 워크래프트](https://wowwiki.fandom.com/wiki/Lua_functions).

일 것이다. 이름이 시사하듯 LuaTeX는 Lua 스크립트 언어를 내장한 TeX 엔진으로, 문서에 Lua 프로그램(스크립트)을 포함시켜 LuaTeX의 조판 동작을 제어할 수 있게 해 준다. LuaTeX를 직접 제어하는 것 외에도, 사용자는 Lua를 순수하게 매우 강력한 프로그래밍 언어로 활용해 TeX 언어만으로는 달성하기 매우 어려울 수 있는 작업을 수행할 수 있다. TeX 언어는 어떤 공정한 기준으로 보더라도 배우고 익히기 쉽지 않다. Lua를 추가하고 통합함으로써 LuaTeX는 두 개의 프로그래밍 언어를 직접 지원하는 매우 다재다능하고 강력한 TeX 엔진이 된다.

### 문서에서 Lua와 TeX 사용하기: \directlua 입력

Lua와 TeX는 두 *매우 다른* 프로그래밍 언어다: Lua는 대부분의 사람들이 생각하는 프로그래밍 언어에 훨씬 가깝지만, TeX는 카테고리 코드, 토큰, 매크로, 확장 메커니즘 때문에 프로그램을 작성하는 언어에 대한 대부분의 사람들의 경험/기대와는 매우 거리가 있다. 그러나 역사에서 보듯 TeX 언어는 그 설계 목적—즉, 다소 난해하더라도 조판 제어—에 매우 잘 맞기 때문에 오래 살아남았다.

Lua와 TeX 언어를 하나의 TeX 문서에서 섞어 사용하는 과제를 해결하기 위해 LuaTeX 개발자들은 `\directlua` 라는 새 명령을 도입했으며, 이것이 Lua를—독립적인 프로그래밍 언어로서도, LuaTeX의 조판 동작을 제어하기 위해서도—사용하는 경로가 된다.

이 `\directlua` 명령을 사용하면 사용자는 TeX 문서 안에 Lua 코드를 삽입할 수 있고, 그 코드는 이후 LuaTeX에 내장된 Lua 언어 인터프리터로 전달된다. 그러나 `\directlua` 은 또한 *결합할 수 있게* Lua와 (La)TeX 코드를 함께, 동일한 `\directlua` 명령 안에서 사용하게 해 준다. 다만 Lua와 TeX 기반 프로그래밍 언어의 근본적인 차이 때문에 추가적인 복잡성이 생긴다. (La)TeX와 Lua 코드를 함께 사용할 때의 핵심 과제는 두 언어가 평화롭게 공존하며 서로 “방해하지” 않도록 하는 것이다.

`\directlua` 은 짧은 문서 내 Lua 코드 조각과 함께 쓰기에 가장 적합하지만, 원한다면 더 큰 Lua 프로그램에도 사용할 수 있다. 일반적으로 더 방대한 Lua 프로그램과 Lua 코드 라이브러리는 외부 파일에 저장되며, Lua의 `dofile()` 함수를 `\directlua` 명령 안에서 사용해 불러온다. TeX 처리 관점에서 외부 Lua 코드 파일을 사용하는 중요한 장점은 TeX의 카테고리 코드 메커니즘에서 비롯되는 복잡성을 피할 수 있다는 점이다. 이 주제는 이 글에서 완전히 다룬다.

### \directlua의 보다 형식적인 설명

이 [LuaTeX 참조 매뉴얼](http://www.pragma-ade.com/general/manuals/luatex.pdf) 은 다음과 같이 설명한다(약간 수정됨): `\directlua` Lua 코드를 TeX 입력과 병합하려면 몇 가지 새로운 원시 명령이 필요하다. 원시 명령

> 은 Lua 코드를 즉시 실행하는 데 사용된다. 기본 구문은 `\directlua` LuaTeX 참조 매뉴얼은 다음과 같이 설명한다(약간 수정됨): `\directlua{⟨code⟩}`로 알려져 있습니다.  `⟨code⟩` 는 완전히 확장된 다음 Lua 인터프리터로 전달된다. 읽기와 확장이 `⟨code⟩`에 적용된 후, 결과 토큰 목록은 `\the\toks`.

를 사용해 표시한 것처럼 문자열로 변환된다. 물론 이것은 기술적으로는 정확하지만, 토큰과 확장과 같은 더 낮은 수준의 TeX 프로세스에 대한 지식이 없으면 이해하기 쉽지 않을 수도 있다.

## \directlua 이해하기: 어떤 주제를 다룰까?

이 글에서는 몇 가지 핵심 배경 주제를 자세히 살펴보고, `\directlua` 가 어떻게 작동하는지, 그리고 TeX와 Lua를 문서 안에서 결합할 때 어디에서(또는 왜) 주의해야 하는지 보여 주기 위한 여러 예제를 제시한다. `⟨code⟩`.

다음 주제들을 충분한 세부 수준으로 살펴보아 `\directlua` 과 그 안에서 사용하는 코드의 “전처리”를 이해할 수 있는 기초를 제공하겠다:

* 카테고리 코드와 TeX 토큰: 텍스트를 토큰으로, 토큰을 텍스트로 변환하기;
* TeX의 확장 과정(및 확장 방지);
* 문자와 문자열에 대한 Lua 이스케이프 시퀀스/메커니즘;
* Lua 스타일 주석 사용하기;
* LuaTeX의 Lua API에 대한 짧은 소개.

TeX 엔진이 토큰을 어떻게 만들고 사용하는지 이해하고, TeX의 확장 메커니즘을 인식하게 되면 LuaTeX의 놀라운 다재다능함을 풀어내는 데 필요한 기초를 갖추게 된다 `\directlua` 명령을 어떻게 해석해야 하는지 알게 되므로 오류 메시지가 사라집니다.

## 기초: 텍스트에서 토큰으로, 토큰에서 텍스트로

Overleaf는 TeX 토큰과 관련 개념을 심층적으로 다룬 여러 글을 발표했으므로 여기서 그 내용을 모두 되풀이하지는 않겠다. 대신, 더 나은 이해를 개발하는 데 관련된 영역/주제를 개괄하겠다 `\directlua`.

관심 있을 수 있는 이전에 게시된 글 목록은 다음과 같다:

* [TeX 토큰이란 무엇인가?](/latex/ko/in-depth-articles/53-what-is-a-tex-token.md)
* [TeX 토큰 리스트란 무엇인가?](/latex/ko/in-depth-articles/54-what-is-a-tex-token-list.md)
* [\expandafter는 어떻게 작동하는가: TeX 토큰 소개](/latex/ko/in-depth-articles/19-how-does-expandafter-work-an-introduction-to-tex-tokens.md)
* [6부작 시리즈: TeX 매크로는 실제로 어떻게 작동하는가?](/latex/ko/more-topics/01-a-six-part-series-how-do-tex-macros-actually-work.md)

### 문자 토큰 이해하기

TeX 엔진이 텍스트 파일에서 읽을 수 있는 모든 문자는 두 개의 숫자 값으로 표현된다:

* 그 문자 고유의 *문자 코드* (ASCII 값 또는 오늘날에는 유니코드 코드 포인트);
* 두 번째 TeX 중심 값인 *카테고리 코드*.

카테고리 코드에 대해 더 알고 싶은 독자는 Overleaf가 발표한 다음 소개글을 읽어 보면 좋다: [그렇다면 어디서 시작할까? 카테고리 코드부터](/latex/ko/more-topics/19-how-tex-macros-actually-work-part-1.md#so2c-where-do-we-start3f-with-category-codes).

예를 들어, TeX 엔진이 문자 `복사된` 를 읽어 들이면 두 가지 정보를 얻게 된다: `복사된`의 문자 코드(65)와 카테고리 코드(보통 11)다. TeX가 그 문자 `복사된`를 입력하면 카테고리 코드는 바뀌지 않지만, 사용자 매크로는 이후의 *후속* 문자가 없으면 `복사된` 문자에 영향을 줄 수 있는 카테고리 코드 변경을 할 수 있다 *아직 읽히지 않은* 것에 대해 TeX는 기록해 둘 필요가 있다. 따라서 TeX는 *이* 문자가 없으면 `복사된`, *방금 읽어 들인*의 카테고리 코드가 11임을 기억해야 한다. 이를 위해 TeX는 정수 쌍 (65,11)을 사용해 자신이 *문자 토큰*이라고 부르는 또 다른 정수 값을 계산한다. 그 토큰 값을 계산하여 TeX의 내부 처리로 넘김으로써, 해당 `복사된` 와 그 카테고리 코드가 *하나로 묶인다*; 사실상 그 문자 토큰 *은* TeX 엔진 더 깊은 내부에서 이후에 수행되는 조판 작업에 사용될 그 문자에 대해 TeX가 알아야 할 데이터를 담아 둔다.

#### 문자 토큰은 어떻게 계산되는가?

먼저, TeX 엔진이 소위 *활성 문자*를 만들기 위해 카테고리 코드 13을 사용한다는 점을 기억해야 한다: 카테고리 코드 13인 문자는 mini-macro처럼 동작한다. 따라서 아래에서 보겠지만 활성 문자에 대한 토큰은 카테고리 코드 10, 11, 12 같은 일반 문자와는 다르게 계산된다.

의 *비활성* 문자:

* 옛 8비트 엔진(Knuth의 TeX, e-TeX, pdfTeX)은 *비활성* 문자에 대한 토큰을

$$\text{(non-active) character token} = (256 \times \text{category code}) + (\text{ASCII character code})$$

* LuaTeX의 경우, 유니코드 문자 값을 다뤄야 하므로 *비활성* 문자에 대한 계산은 비슷하지만 훨씬 더 큰 정수 값을 생성한다:

$$\text{(non-active) character token} = (2^{21} \times \text{category code}) + (\text{Unicode value})$$

이전의 카테고리 코드 11인 문자 A 예로 돌아가면, LuaTeX는 $$2^{21} \times 11 + 65 = 23068737$$의 문자 토큰 값을 계산할 것이다 *는* 그 특정 문자 A를 카테고리 코드 값 11에 묶는다. 사용자 매크로는 이후의 문자 A에 대해 카테고리 코드를 바꿀 수 있지만, 이 문자의 카테고리 코드는 토큰으로 변환되어 LuaTeX의 내부 동작을 통과하면서 고정된다. LuaTeX는 읽혀 들어온 시점에 결정된 그 문자의 의도된 의미를 보존, 즉 캡슐화해 둔다.

TeX 엔진은 모두 합쳐 [16가지 서로 다른 카테고리 코드](/latex/ko/more-topics/43-table-of-tex-category-codes.md) 및 *를 사용한다. 이* 들 중 어떤 것이라도 `\catcode` 명령을 통해 *를 사용한다. 이* TeX 엔진이 읽을 수 있는 모든 문자에 지정할 수 있다. 카테고리 코드의 변경은 입력에서 특정 문자를 TeX 엔진이 처리하는 방식을 바꾸는 데 사용되며, TeX 사용자가 특별한 조판 결과나 동작을 내는 매크로를 작성할 수 있게 해 준다.

**활성 문자**

앞서 언급했듯이 TeX 엔진은 카테고리 코드 13을 사용해 문자에 “특별한 의미”를 부여하며, 이로써 그 문자는 소위 *활성 문자* 가 된다. 이것은 mini-macro처럼 동작한다. 즉 앞에 `\` 가 필요하지 않으며, 그 문자 하나만으로도 카테고리 코드 덕분에 매크로 같은 동작을 유발하기에 충분하다.

활성 문자는 mini-macro처럼 동작하므로, *문자 토큰* 문자 토큰으로 변환되지 않고, 대신 *명령 토큰*이라고 하는 두 번째(정수형) 토큰 유형이 된다. 계산 방식은 다음과 같다:

* 옛 8비트 엔진(Knuth의 TeX, e-TeX, pdfTeX)에서는 활성 문자에 대한 토큰이 다음과 같이 계산된다:

1. 중간 값을 계산한다. 이를 $$\text{curcs}$$ (**cur**rent **c**ontrol **s**equence)이며, 여기서 $$\text{curcs} = \text{character code} + 1$$3. 토큰 값을 계산한다. 여기서 $$\text{active character token} = \text{curcs} + \text{4095}$$

* LuaTeX의 경우, 유니코드 전체 범위를 다뤄야 하므로 계산이 조금 더 복잡하지만, 어떤 문자든 활성 문자로 만들 수 있다:

1. 중간 정수 값 $$\text{curcs}$$ 을 계산하고 *해시 함수* 라고 불리는 것을 적용하여 $$\text{curcs}=\texttt{hashfunction}\text{(UTF-8 text for Unicode value of active character)}$$3. 정수 토큰 값을 계산한다: $$\text{active character token} = \text{curcs} + 2^{29} - 1$$

**예시**

* 8비트 엔진: 활성 문자 `~` (문자 코드 126)에 대한 토큰 계산 결과는 $$\text{curcs} = 126 + 1 = 127$$, 토큰 값은 $$4095 + 127 = 4222$$.
* LuaTeX: 활성 문자 `~` 의 토큰 계산 결과는 $$\text{curcs}=3186$$ 토큰 값은 $$3186 + 2^{29} - 1 = 536874097$$가 된다. LuaTeX의 토큰은 훨씬 더 큰 정수 값을 사용한다!

### 명령 토큰 이해하기

을 처리하는 것 외에도 *개별* 문자를 처리할 수 있으며, 물론 *문자들의* 연속 *명령* (혹은 더 정확히는 *제어 시퀀스*)이라고 불리는 것들을 처리할 수 있다. 전통적으로 `\` 문자가 명령의 시작을 알리는 데 사용되지만, 그것은 단지 관례일 뿐이다. 사실 카테고리 코드 0(이스케이프 문자)을 가진 어떤 문자라도 대신 사용할 수 있다.

TeX 엔진은 다음 두 종류의 명령을 인식하며, 이는 *제어 단어* 및 *제어 기호*:

* **제어 단어**: 카테고리 코드 11을 가진 하나 이상의 문자로 구성된 명령;
* **제어 기호**: 그 문자의 카테고리 코드가 *는* 11인 단일 문자 명령. 예를 들면 `\$`, `\#` 또는 `\\\\`.

**참고**: TeX 원시 명령 `\chardef`, `\mathchardef`, `\countdef`, `\dimendef`, `\skipdef`, `\muskipdef` 및 `\toksdef` 는 제어 시퀀스를 정의하는 데도 사용된다. 하지만 일반적인 매크로 정의와 달리, 그 결과로 생기는 제어 시퀀스(제어 단어 또는 제어 기호)는 *확장 가능하지 않다*—이에 대해서는 아래에서 더 자세히 살펴보겠다.

#### 명령 토큰은 어떻게 계산되는가?

활성 문자와 마찬가지로 TeX 엔진은 명령을 나타내기 위해 두 번째 유형의 정수 토큰 값을 사용한다: *명령 토큰*—활성 문자도 mini-macro처럼 동작하기 때문에 명령 토큰을 생성한다는 점을 기억하자.

8비트 엔진이 명령 토큰 정수를 만드는 데 사용하는 계산은 이 [Overleaf의 글](/latex/ko/in-depth-articles/19-how-does-expandafter-work-an-introduction-to-tex-tokens.md#how-tex-calculates-token-values)에서 찾을 수 있다. 여기서는 LuaTeX의 명령 토큰 계산의 핵심 단계를 요약하겠다. LuaTeX는 8비트 값보다 훨씬 클 수 있는 유니코드 문자 코드 값을 처리해야 하므로 약간 다르지만, LuaTeX의 계산은 옛 8비트 엔진이 사용하는 일반 원칙을 따른다.

들어오는 명령을 감지한 뒤 TeX 엔진들, LuaTeX를 포함해, 앞의 `\` 문자는 무시한다. 이 문자는 명령 토큰 값을 계산하는 데 사용되지 않고, 단지 TeX 엔진에 명령을 처리해야 한다는 것을 알리는 “스위치” 역할만 한다. 명령 토큰 값은 명령 이름에 들어 있는 (하나 이상의) 문자들의 시퀀스를 사용해 계산된다. LuaTeX는 제어 기호와 제어 단어에 대한 명령 토큰을 같은 알고리즘으로 계산한다:

1. 중간 정수 값 $$\text{curcs}$$ 을 계산하고 [해시 함수](https://en.wikipedia.org/wiki/Hash_function) 명령 이름에 들어 있는 문자들의 유니코드 UTF-8 문자열에 $$\text{curcs}=\texttt{hashfunction}\text{(Unicode UTF-8 string of characters in command name)}$$3. 명령 토큰 값을 계산한다. 여기서 $$\text{command token} = \text{curcs} + 2^{29} - 1$$

**예시**

* 는 `\\\\` 명령(제어 기호)에서 LuaTeX는 $$\text{curcs}=94$$, 그 결과로 `\\\\` 에 대한 토큰 값을 계산한다 $$94 + 2^{29} - 1 = 536871005$$.
* 는 `\vskip` 원시 명령(제어 단어) LuaTeX는 $$\text{curcs}=3560$$, 그 결과로 `\vskip` 에 대한 토큰 값을 계산한다 $$3560 + 2^{29} -1 = 536874471$$.
* 사용자 정의 매크로 `\mynewmacro` (제어 단어) LuaTeX는 $$\text{curcs} = 2971$$, 그 결과로 `\mynewmacro` 에 대한 토큰 값을 계산한다 $$2971 + 2^{29} -1 = 536873882$$.

한번 생성되면 토큰은 이른바 *토큰 목록* 을 통해 나중에 사용할 수 있도록 저장될 수 있고, TeX 엔진 내부에서 추가 처리되도록 즉시 전달될 수도 있다. 토큰을 정수 값으로 표현하면 모든 컴퓨팅 플랫폼/운영체제에서 통용될 뿐 아니라 TeX가 데이터를 저장/처리하는 매우 효율적인 방법이 되기도 한다.

### TeX 엔진이 토큰의 유형(명령 또는 문자)을 식별하는 방법

특정 정수 토큰 값이 주어지면, $$T$$TeX 엔진은 $$T$$ 가 명령인지 문자인지 쉽게 판단할 수 있다. 왜냐하면 $$T$$ 가 특정 $$\text{threshold value}$$값을 초과하는지 확인하면 되기 때문이다 $$\text{threshold value}$$ —이는 TeX 엔진에 따라 다르다. 만약 $$T \geq \text{threshold value}$$ 그렇다면 $$T$$ 가 명령 토큰이면 그렇지 않으면 $$T$$ 는 문자 토큰이다. 이 $$\text{threshold value}$$ 는 $$4095$$ 은 8비트 엔진의 경우 $$2^{29}-1$$ (536,870,911)이며 LuaTeX의 경우

## 토큰은 분해될 수 있고(그리고 다시 텍스트로 변환될 수 있다)

토큰(정수)은 TeX 엔진이 입력 항목(문자 또는 명령)에 대해 기록해야 하는 모든 것을 “캡슐화”하는 메커니즘이다. 그러나 TeX 엔진이 토큰화 과정을 되돌려서, 즉 그 토큰 값을 만들어 낸 원래 입력이 무엇이었는지—개별 문자 하나인지, 아니면 명령 이름을 이루는 하나 이상의 문자 시퀀스인지—알아내야 할 때도 있다:

* **문자 토큰의 경우**: 어떤 문자 토큰이든 두 구성 요소로 분해할 수 있다. 즉 그 문자에 할당된 문자 코드와 대응하는 카테고리 코드 *가 원래 읽혀 들어왔던 시점의*다. 모든 TeX 엔진과 마찬가지로 LuaTeX도 그 원래의 카테고리 코드 할당을 바꾸지는 않지만, 이후의 내부 처리에서 그것을 사용한다.
* **명령 토큰의 경우:** 이것들은 조금 더 자세하지만, 활성 문자의 토큰을 포함해 LuaTeX의 명령 토큰 계산을 보면 다음과 같은 패턴을 따른다는 것을 알 수 있다: $$\text{command token} = \text{curcs} + 2^{29} -1$$

여기서 $$\text{curcs}$$ 는 생성되는 명령 토큰의 유형에 따라 계산된다: 활성 문자, 제어 기호 또는 제어 단어. 변수 $$\text{curcs}$$ 는 *매우* TeX 엔진의 내부 동작에서 중요한 구성 요소다: 어떤 명령 토큰(정수) 값이든 LuaTeX는 $$\text{curcs}$$ 의 값을 그 명령 토큰에서 매우 쉽게 추출할 수 있으며 $$\text{curcs} = \text{token value} - (2^{29} -1)$$.

### 왜 $$\text{curcs}$$ 가 그렇게 중요한가?

내부 TeX 변수 $$\text{curcs}$$ (**cur**rent **c**ontrol **s**equence)는 TeX 엔진 내부의 “속사정” 작업에서 매우 중요한 구성 요소다. 코드에서 직접 사용하거나 접근할 수는 없고, 해서도 안 되지만, $$\text{curcs}$$ 는 매우 중요한 역할을 한다. TeX 엔진은 $$\text{curcs}$$ 의 현재 값을 엔진이 현재 알고 있는 모든 명령에 대한 데이터를 저장한 내부 테이블의 인덱스로 사용하기 때문이다. 그 테이블은 명령의 현재 의미—무엇을 하는지 또는 무엇을 나타내는지—에 대한 정보를 저장할 뿐 아니라, 그 $$\text{curcs}$$ 값을 계산할 때 원래 사용된 문자 시퀀스도 기록한다. 명령 토큰에서 $$\text{curcs}$$ 의 값을 추출함으로써 TeX 엔진은 해당 (명령) 토큰에 대응하는 이름, 즉 사람이 읽을 수 있는 텍스트를 판별할 수 있고, 이를 통해 `\directlua`의 작동에서 핵심적인 토큰-텍스트 변환을 수행할 수 있다.

### 정수 토큰을 문자 또는 문자 시퀀스(명령 이름)로 다시 변환하기

TeX 엔진이 입력 문자 또는 문자 시퀀스를 정수 토큰 값으로 변환한다는 것을 보았지만, 어떤 경우에는 TeX 엔진이 *역으로* 그 과정을 수행해, 그 정수 토큰 값을 만드는 데 원래 사용된 사람이 읽을 수 있는 텍스트를 출력해야 할 때가 있다. 예를 들어:

* 화면에 오류 또는 경고 메시지를 쓰거나 `.log` 파일;
* \write를 통해 텍스트 파일로 TeX/LaTeX 코드를 출력할 때 `\write` 명령으로 변경할 수 있으며;
* 안에서 토큰 시퀀스를 텍스트로 변환할 때 `\directlua` (곧 보겠지만!)

#### 문자 토큰을 텍스트로 변환하기

앞서 언급했듯이 비활성 문자의 토큰은 입력 문자의 카테고리 코드와 문자 코드(유니코드 값)를 사용해 계산된다. LuaTeX는 다음 공식을 사용한다:

$$\text{character token} = (2^{21} \times \text{category code}) + (\text{Unicode value})$$

정수 $$\text{character token}$$ 값을 분해해 구성 요소인 문자 코드($$\text{Unicode value}$$)와 $$\text{category code}$$.

#### 명령 토큰을 텍스트로 변환하기

모든 TeX 엔진은 자신이 “알고 있는” 모든 명령의 이름(문자열 시퀀스)을 저장한다. 그 명령이 사용자 정의 매크로인지, 내장 원시 명령인지에 상관없이 말이다—원시 명령 이름의 저장은 TeX 엔진이 시작될 때, 즉 코드를 처리하기 훨씬 전에 이루어진다. 사용자 정의 명령(매크로)의 경우, 그 매크로의 이름(앞의 `\`를 뺀 것)이 TeX 엔진 내부의 매크로 정의 과정의 일부로 저장된다.

TeX 엔진이 정수 명령 토큰을 처음 계산할 때 사용된 사람이 읽을 수 있는 텍스트에 접근하거나 출력해야 할 때는, 먼저 그 토큰의 $$\text{curcs}$$ 값을 결정한다. LuaTeX에서는 $$\text{curcs} = \text{token} - (2^{29} -1$$). 명령 토큰에서 추출한 값을 사용해 $$\text{curcs}$$ 값을 사용해 TeX 엔진은 *string pool* 이라고 하는 내부 데이터 구조에 접근해, 그 특정 $$\text{curcs}$$ 값을 계산할 때 원래 사용된 사람이 읽을 수 있는 문자들의 시퀀스를 판별할 수 있고, 그에 따라 대응하는 명령 토큰을 알아낸다.

보게 되겠지만, 이러한 토큰 처리 작업—문자 시퀀스를 정수 토큰 값으로 변환하고 정수 토큰 값을 다시 문자 시퀀스로 변환하는(“역토큰화”) 작업—이 *기본 메커니즘* 이다 `\directlua`.

## 토큰 목록

TeX 엔진이 입력을 읽고, 문자 및 명령 토큰을 생성하고(그리고 처리하고) 있을 때, 엔진이 토큰을 더 이상 후속 처리로 넘기지 말고(일시적으로) 저장해 두었다가 나중에 사용하라고 지시하는 특정 명령을 만날 수 있다. 가장 흔한 예는 다음 매크로 정의 명령 중 하나를 사용해 매크로를 정의하는 경우다 `\def`, `\edef`, `\gdef` 또는 `\xdef`—LaTeX 명령인 `\newcommand` 같은 것들은 결국 실제 매크로 정의 과정을 수행하는 저수준 원시 명령을 바탕으로 추가 기능을 제공하는 매크로다. 매크로는 특정 저장된 토큰 목록의 이름으로 볼 수 있다: 즉 토큰 목록이다.

TeX 엔진은 *광범위하게* 토큰 목록을 사용하며, 특히 [임시적인 내부 전용 목록을](/latex/ko/in-depth-articles/21-how-does-expandafter-work-tex-uses-temporary-token-lists.md) 내부 처리 목적으로 사용한다. 모든 TeX 엔진은 또한 사용자가, 또는 TeX 엔진 자체가 필요로 할 때를 위해 저장해 둘 토큰 목록을 만드는 사용자 수준 명령도 제공한다. 토큰 목록 생성 명령(내장 원시 명령)의 수는 TeX 엔진에 따라 다르지만, 모든 엔진이 지원하는 핵심 최소 집합을 공유하는데, 예를 들어 `\toks` 원시 명령이 그렇다.

실제로 토큰 목록은 그냥 정수 값들의 저장된 시퀀스일 뿐이다:

* 입력을 읽어 개별 토큰(문자 또는 명령을 나타내는)을 생성(계산)하고;
* 그런 다음 각 토큰은 입력에서 생성된 순서를 보존한 채 저장된다.

TeX 엔진은 [연결 리스트](https://en.wikipedia.org/wiki/Linked_list) (단일 연결 리스트 형태의)를 사용하는데, 토큰 목록에 대해 더 알고 싶은 독자는 Overleaf의 글 [TeX 토큰 리스트란 무엇인가?](/latex/ko/in-depth-articles/54-what-is-a-tex-token-list.md) 을 읽어 보길 권한다. 그 글은 유추를 사용해 토큰 목록의 개념/아이디어를 구성한다. TeX의 토큰 목록과 그것이 매크로 처리에서 어떻게 사용되는지에 대한 심층 탐구는 Overleaf 글 연재 [TeX 매크로는 실제로 어떻게 동작하는가?](/latex/ko/more-topics/01-a-six-part-series-how-do-tex-macros-actually-work.md)

#### 그림으로 본 토큰 목록

다음 그림은 LuaTeX가 생성한 토큰 목록과, 다음 입력으로부터 생성된 대응 토큰 값을 보여 준다

`Hi, \TeX! \hskip 5bp`

예를 들어, 다음을 정의하면 `\mymacro` as `\def\mymacro{Hi, \TeX! \hskip 5bp}` 의 정의는 `\mymacro` 다음과 같은 토큰 목록을 사용해 메모리에 저장될 것이다:

![](/files/7bda13123fdc7d1630dc19651680212062b79260)

토큰 목록은 다음과 같은 연결 항목들의 시퀀스다 *노드*노드라는 것으로, 각 항목을 목록에 담기 위해 할당된 LuaTeX 메모리의 작은 묶음에 붙인 이름이다(사슬의 개별 연결고리처럼). 각 노드는 정수 토큰 값과 *다음* 노드의 메모리 주소를 포함하며, 이렇게 [연결 리스트](https://en.wikipedia.org/wiki/Linked_list)라고 하는 데이터 구조를 형성한다. 마지막 노드는 다음 노드가 없기 때문에 특수한 “null 값”을 사용해 목록의 끝을 나타낸다.

**참고:**

* 편의를 위해 각 개별 노드의 주소를 포함했지만, 실제로는 그 데이터가 토큰 목록 노드 안에 저장되지는 않는다. TeX 엔진 토큰 목록을 만들기 위해 필요한 것은 *다음 노드* 의 주소뿐이다.
* “각 토큰이 의미하는 것”이라는 제목의 그림에서 두 번째 열은 각 노드에 포함된 토큰에 관한 정보를 담은 회색 상자들의 시퀀스를 보여 준다. 이것들은 순전히 정보용이며 *않는다* 토큰 목록에 실제로 저장되는 데이터의 일부를 이룬다.

위에 묘사된 토큰 목록에 들어 있는 토큰 값 표는 다음과 같다:

|           |           |                                                                  |           |
| --------- | --------- | ---------------------------------------------------------------- | --------- |
| **입력 항목** | **입력 유형** | <p><strong>카테고리 코드</strong><br><br><strong>(문자인 경우)</strong></p> | **토큰 값**  |
| H         | 문자가 없으면   | 11                                                               | 23068744  |
| i         | 문자가 없으면   | 11                                                               | 23068777  |
| ,         | 문자가 없으면   | 12                                                               | 25165868  |
|           | 문자가 없으면   | 10                                                               | 20971552  |
| \TeX      | 명령(매크로)   |                                                                  | 536871539 |
| !         | 문자가 없으면   | 12                                                               | 25165857  |
|           | 문자가 없으면   | 10                                                               | 20971552  |
| \hskip    | 명령(원시 명령) |                                                                  | 536874247 |
| 5         | 문자가 없으면   | 12                                                               | 25165877  |
| b         | 문자가 없으면   | 11                                                               | 23068770  |
| p         | 문자가 없으면   | 11                                                               | 23068784  |

**참고:** 원래 입력 텍스트에는 명령 뒤에 a가 있지만, 토큰 목록에는 그 문자를 나타내는 토큰이 없다. 그 문자는 LuaTeX의 입력 스캐닝(읽기) 과정에서 흡수되었는데, 이는 LuaTeX가 `\hskip` 를 구성하는 문자들에 대한 검색을 끝내도록 하는 데 그 문자가 사용되었기 때문이다 `\hskip` 명령을 어떻게 해석해야 하는지 알게 되므로 오류 메시지가 사라집니다.

## \directlua가 실제로 작동하는 방식

이제 토큰, 토큰 목록 및 토큰을 텍스트로 변환하는 방법을 살펴보았으므로, 다음 과제는 토큰에 대한 TeX 엔진 개념을 이해하는 것입니다 *확장*.

언급했듯이, `\directlua{⟨code⟩}` 처리하도록 요청할 수 있습니다 `⟨code⟩` Lua와 TeX/LaTeX 코드를 모두 포함하지만 LuaTeX에 내장된 Lua 언어 인터프리터는 TeX나 LaTeX를 이해하지 못합니다. 그렇다면 이것이 어떻게 작동할 수 있을까요? 어떻게 `⟨code⟩` Lua 인터프리터가 이해하지 못하는 명령으로 완전히 혼란스러워지지 않고 TeX/LaTeX 명령을 포함할 수 있을까요? 예를 들어, 다음 `\directlua` 명령은 TeX 매크로만 사용하지만 작동합니다:

```
\def\aa{tex}
\def\bb{.}
\def\cc{print}
\def\dd{("Hello")}
\directlua{
   \aa\bb\cc\dd
}
```

외부 파일들이 컴파일되도록 보장하는 코드가 포함되어 있습니다. `\directlua` 명령은 LuaTeX가 다음을 조판하게 합니다 `Hello` 하지만 Lua 언어는 TeX 매크로를 이해하지 못하는데, 이것은 왜 그리고 어떻게 작동할까요?

답은 앞서 우리가 차용한 설명에 포함되어 있습니다 [LuaTeX 참조 매뉴얼](http://www.pragma-ade.com/general/manuals/luatex.pdf) 하지만 우리는 다음과 같이 생각할 수 있습니다 `\directlua{⟨code⟩}` LuaTeX가 처음에 다음을 “전처리”하여 작동합니다 `⟨code⟩` Lua 인터프리터에 무엇이 전달되기 전에. 이 “전처리”의 성격, 즉 그것이 실제로 무엇을 의미하는지와 여러분의 `⟨code⟩`에 미치는 결과는 독자가 의 힘과 유연성을 활용하는 데 도움이 되도록 다음에 다룰 주제입니다 `\directlua`.

### LuaTeX가 \directlua를 처리하는 방법: 첫 번째 살펴보기

에 대한 이해를 넓히기 위해 `\directlua`의 “전처리” 활동은, 어떤 일이 일어나는지 개괄적으로 보여 주는 다음의 단순화된 도표부터 시작할 수 있습니다.  `⟨code⟩` 에 제공된 `\directlua{⟨code⟩}` 은 앞서 논의한 과정과 계산을 사용하여 먼저 토큰으로 변환되며, 그 토큰 시퀀스는 토큰 목록에 저장됩니다. 토큰 목록이 만들어지면 그 목록의 각 토큰은 다시 텍스트 표현으로 변환됩니다. 문자 토큰이든 명령 토큰이든 모든 토큰이 만들어 내는 텍스트가 결합(연결)되어, 실행을 위해 Lua 인터프리터에 전달되는 하나의 코드 문자열을 생성합니다.

![](/files/3823db036e738c74c054212b752e2af008898a5d)

하지만 잠깐, 텍스트에서 토큰으로 갔다가 그 토큰을 곧바로 다시 텍스트로 변환하는 데 무슨 의미가 있을까요? 놀랍지 않게도, 예, 이 그림에 포함하지 않은 추가적이고 중요한 과정이 있습니다: *토큰 확장*. 여러분의 에 있는 텍스트로부터 생성된 모든 토큰은 `⟨code⟩` 라는 부분집합에 속하는 명령을 나타내는지 확인하기 위해 LuaTeX가 해당 토큰에 검사를 적용하는 일종의 “검사”를 받습니다 *확장 가능한 명령*. 그렇다면 LuaTeX는 그 명령을 다음 방식으로 걸러냅니다 *제거하는 것* 여러분의 에서 그것을 `⟨code⟩` 및 *그것을 대체하여* TeX 엔진이 다음이라고 부르는 과정의 결과로 *토큰 확장*.

### LuaTeX가 \directlua를 처리하는 방법: 두 번째 살펴보기(확장)

TeX의 확장 메커니즘은 모든 TeX 기반 조판 엔진의 핵심 구성 요소입니다. 궁극적으로 각각은 Knuth의 원래 TeX 소스 코드와 설계에서 파생되었거나 이를 기반으로 하기 때문입니다. 그러나 확장 개념을 간결하면서도 이해하기 쉬운 언어로 설명하기는 어렵습니다. 실제로 확장은 하나의 과정을 설명하는 데 쓰이는 “포괄적” 용어이지만, 다양한 결과를 만들어 내기 때문입니다. 이러한 다양한 결과는 확장을 적용할 수 있는 다소 절충적인 명령 집합의 결과이므로, 각각의 확장 가능한 명령에는 특정한 “확장 동작”이 있다고 생각할 수 있습니다.

하나의 *첫 번째 근사* 로서 확장을 이해하기 위해, 토큰(명령)의 확장이란 다음을 의미한다고 말할 수 있습니다 *제거하는 것* TeX의 현재 입력에서 그 명령(토큰)을 제거하고 *대체하는 것* 그 특정 확장 가능 명령을 실행하여 생기는 토큰 시퀀스로 그것을 대체하는 것, 즉 원래 토큰을 그 확장의 결과/효과로 대체하는 것 *동작을*. 그러나 TeX가 읽을 새 토큰을 생성한다는 관점에서 본 이 초기 확장 “정의”는 모든 확장 가능한 명령에 완전히 정확하지는 않지만, 출발점으로는 충분합니다.

간단한 예를 들면, TeX 프리미티브 `\jobname` 은 확장 가능한 명령이며, 그 *확장* 은 주 TeX 입력 파일의 이름을 나타내는 문자 토큰 시퀀스입니다. TeX가 다음을 확장하기로 결정하면 `\jobname` 명령(토큰)은 *제거되고* TeX의 현재 입력 소스에서 *대체됩니다* 그것이 생성하는 문자 토큰 시퀀스로, 이후 TeX는 이를 읽고 처리합니다.

내에서 `\directlua`, 확장 가능한 토큰이 처리(제거)되고 새 토큰으로 대체된 후 LuaTeX는 방금 배치한 그 새 토큰들을 계속 읽습니다. 하지만 그 새 토큰 중 일부도 확장 가능할 수 있습니다.  `\directlua` 가 이른바 *완전 확장*을 수행하므로, LuaTeX는 그 새 토큰을 읽고 다시 확장 과정을 거쳐 새로운 확장 가능 토큰을 확장(제거)합니다. 이 확장 과정은 확장 가능한 토큰이 더 이상 남지 않을 때까지 계속됩니다. 그러나 이 “계속 확장” 규칙에는 중요한 예외가 두 가지 있으며, 둘 다 아래에서 논의합니다:

* 구문을 사용하여 `\the\toks`;
* 선택한 하나 이상의 토큰에 대한 의도적인 확장 방지(억제).

언급했듯이, 확장을 이해하기 위한 작업 정의(첫 번째 근사)는 확장 가능 명령 부분집합이 보이는 모든 확장 동작 범위를 다루지 못합니다. 예를 들어, 일부 확장 가능 명령은 다음처럼 토큰을 생성하지 않지만 `\jobname` 은 생성하지만, 다음과 같은 일을 할 수 있습니다:

* 입력에서 토큰을 “필터링”합니다: TeX 엔진의 조건부 명령(`\if`, `\ifcat`, `\ifnum`, `\ifdim`, `\ifodd`, `\ifvmode`, …)은 확장 가능합니다. 이들의 확장 동작은 일종의 “토큰 필터링”이며, 조건문은 다음에서 사용할 수 있습니다 `\directlua`.
* 입력의 토큰을 “재배열”합니다:  [`\expandafter` 명령을](/latex/ko/in-depth-articles/03-a-six-part-article-series-on-expandafter-tex-tokens-and-expansion.md) 은 확장 가능하며 두 토큰이 확장되는 순서를 변경합니다.
* 확장을 방지합니다: 확장 가능한 명령 `\noexpand` 및 `\unexpanded` 는 입력의 명령 토큰 확장을 억제합니다.
* 입력의 문자 시퀀스를 명령 토큰으로 변환합니다: `\csname … \endcsname.`
* 내부 수량을 문자 토큰 시퀀스로 변환합니다: `\number` 및 `\the` 는 내부 수량의 값을 나타내는 문자 토큰 시퀀스를 생성하는 확장 가능한 명령입니다.
* 명령 토큰을 문자 토큰으로 변환합니다: `\string` 및 `\detokenize` 는 인수를 범주 코드 12를 가진 문자 토큰 시퀀스로 변환하는 확장 가능한 명령입니다. 다음에 유의하십시오 `\detokenize` 은 다음과 다릅니다 `\string`: `\detokenize` 은 여러 토큰을 처리할 수 있으며, 에서 생성된 명령 토큰을 처리한 후 범주 코드 10의 공백 문자를 삽입합니다 *제어 단어*. 사실상,  `\detokenize` 은 명령 이름 뒤에 후행 공백 문자를 추가합니다. 이 글의 뒤에서 몇 가지 예를 살펴보겠습니다.

#### 확장에 대한 “정의” 다듬기

이제 정의를 일반화하여 명령(토큰)의 확장은 다음을 수반한다고 말할 수 있습니다 *제거* 해당 명령(토큰)을 TeX의 현재 입력 소스에서 제거하고 *그것을 대체하여* 다음의 결과로 대체하는 것 *토큰 연산* 해당 명령이 수행하는 것입니다. 본질적으로 확장 과정은 확장 가능한 명령이 TeX의 현재 입력에 있는 토큰에 대해 어떤 종류의 “연산”을 수행하게 하며, 이는 이후 TeX가 읽을 토큰의 수나 동작에 영향을 줍니다. 그 “연산”의 정확한 성격은 어떤 명령이 확장되는지에 따라 달라집니다. 모든 매크로와 활성 문자는 확장 가능하지만, TeX 엔진의 내장 명령(프리미티브) 중 확장 가능으로 분류되는 것은 소수에 불과합니다. 확장 가능한 명령 목록은 사용 중인 TeX 엔진에 따라 달라집니다.

모든 새 TeX 엔진은 그것이 파생된 이전 세대 TeX 엔진, 즉 조상 엔진에 내장된 프리미티브 명령을 상속하며, 그 상속된 프리미티브 중 일부는 확장 가능할 것입니다. 물론 새 TeX 엔진은 이전 엔진에 있던 일부 프리미티브 명령을 구현하지 않거나 새 엔진의 요구에 맞게 그 동작을 수정할 수 있습니다. 또한 새 TeX 엔진은 일반적으로 자체의 향상된 기능을 지원하기 위한 추가 프리미티브를 구현하며, 그중 일부도 확장 가능할 수 있습니다. 따라서 사용 가능한 확장 가능 명령의 수는 사용하는 TeX 엔진에 따라 달라지며, LuaTeX에는 상당히 많이 있습니다.

확장을 설명하고 이해하는 또 다른 어려움, 그리고 어쩌면 진정한 과제는 정확히 다음을 아는 것입니다 *언제* TeX 엔진이 실제로 확장 과정을 수행할지 또는 수행하지 않을지를 아는 것입니다. 확장은 TeX 엔진의 내부 작동 전반에 깊이 내재되어 있으므로 이는 크고 복잡한 주제입니다. 에서의 확장 사용을 제외하고 이를 자세히 다룰 공간은 없습니다 `\directlua`.

### LuaTeX가 \directlua를 처리하는 방법: 마지막 살펴보기

다음 도표는 `\directlua` LuaTeX 엔진 내부에서 일어나는 전처리 활동을 요약합니다. 이 도표에서는 실제 작업을 수행하는 두 개의 저수준(내부) LuaTeX 함수도 보여 줍니다: `scan_toks()` 및 `tokenlist_to_cstring()`. 이 함수들은 C 언어로 작성되었고 실행 가능한 LuaTeX 소프트웨어 깊숙한 곳에 존재합니다. 이는 LuaTeX 내부 기계장치의 일부이며 *직접* 여러분의 TeX/LaTeX 코드에서 접근할 수 없습니다.

![](/files/ea65a2a04afa720748b17438a121e147174afd02)

다음에 대한 설명 `\directlua ⟨code⟩`의 전처리 활동은 위 도표를 요약한 것입니다.

1. 여러분의 ⟨code⟩에 있는 문자 시퀀스는 다음에 의해 처리됩니다 `scan_toks()`. 그 목적은 ⟨code⟩를 문자 단위로 읽어 문자 토큰과 명령 토큰을 생성하는 것입니다. 토큰을 생성하므로, ⟨code⟩의 각 문자에 읽히는 시점에 할당된 범주 코드는 매우 중요합니다.
2. 중에 `scan_toks()`의 토큰 처리(생성) 과정에서 모든 확장 가능한 명령(토큰)은 확장됩니다 *다만* 다음과 같은 명령을 통해 방지되지 않는 한 `\protected` (매크로 정의), `\noexpand`, `\unexpanded` 등. 활성 문자(범주 코드 13)도 방지되지 않는 한 확장됩니다.
3. 다음에 의해 생성된 토큰 스트림은 `scan_toks()` 하나의 긴 토큰 목록으로 구성됩니다. 그 목록에 있는 토큰에는 여러분의 에 있는 확장 가능한 명령(예: 매크로)에 확장을 적용하여 생성된 토큰도 포함됩니다 `⟨code⟩`. 또한 다음에 유의하십시오 `scan_toks()` *더 이상* 는 확장 불가능 명령을 나타내는 토큰의 실행을 유발하거나 실행하지 않습니다. 그러한 확장 불가능 토큰은 단순히 통과되어 구성 중인 토큰 목록에 포함됩니다.
4. 토큰 목록이 완성되고 모든 확장 활동이 끝나면, 그 토큰 목록은 다음이라는 다른 함수에 의해 처리됩니다 `tokenlist_to_cstring()` 는 최종 토큰 목록의 각 토큰을 다시 텍스트 표현으로 변환합니다. 그러면 Lua 인터프리터에 전달할 Lua 코드인 텍스트 문자열이 생성됩니다. 성공적으로 실행되려면 그 문자열에는 구문적으로 올바른 Lua 코드가 포함되어야 합니다.
5. Lua의 해당 코드 처리는 두 단계로 이루어집니다:
6. LuaTeX의 내장 Lua 인터프리터는 이전 단계에서 생성된 Lua 코드를 구문 분석하고 “컴파일”합니다. 이 구문 분석/컴파일에 실패하면 Lua 인터프리터는 오류(예: 구문 오류)를 발생시키며, 명령줄에서 다음을 사용하도록 선택하지 않는 한 이러한 오류는 LuaTeX 실행을 실패하게 할 수 있습니다 `--interaction=nonstopmode` 를 명령줄에서.
7. 구문 분석/컴파일에 성공하면 Lua 인터프리터는 단계 (5a)에서 컴파일된 코드를 실행합니다.

본질적으로,  `scan_toks()` 함수는 LuaTeX 전처리 활동의 핵심입니다. 주된 작업은 여러분의 텍스트에 포함된 모든 확장 가능한 TeX/LaTeX 명령을 확장하는 것입니다 `⟨code⟩` 하고 처리한 모든 것으로 토큰 목록을 구성합니다. 다시 강조하지만,  `scan_toks()` *는 확장 불가능 명령을 실행하지 않습니다* (토큰): 단지 *저장합니다* 그 토큰을 구성 중인 토큰 목록에 저장합니다. 완료되면 이 토큰 목록은 이후 변환됩니다 *다시 텍스트 표현으로* 에 의해 `tokenlist_to_cstring()`—토큰 목록은 Lua 인터프리터에는 완전히 낯선 TeX 전용 개념이므로, Lua 인터프리터로 전달할 Lua 코드가 되도록 텍스트로 변환해야 합니다.

## 프로그래밍 언어 “인터페이스”로서의 확장

다음을 생각할 수 있습니다 `\directlua`의 확장 과정은 “TeX 세계”에서 “Lua 세계”로 데이터/정보를 전달하기 위한 메커니즘 또는 인터페이스로 사용됩니다. 즉, TeX 언어가 Lua 언어에 데이터를 전달하는 방법을 제공합니다. 예를 들어, 다음과 같은 TeX 코드 `\number\count75` 는 카운트 레지스터 75에 저장된 “TeX 세계” 값을 “Lua 세계” 정수 변수 x로 전달하는 데 사용할 수 있습니다:

```
\count75=1564 % "TeX 세계"에 존재하는 데이터
\directlua{
   local x=\number\count75 \space % TeX 데이터를 "Lua 세계"로 전달
   tex.print("x= "..x)
   local y = (2*x-65)/5
   tex.print(" and y = "..y)
}
```

그러면 다음 Lua 코드가 생성됩니다

```
 local x=1564 tex.print("x= "..x) local y = (2*x-65)/5 tex.print(" and y = "..y)
```

**참고**: 우리는 다음을 추가했습니다 `<space>\space` 뒤에 `\number\count75` 와 `1564` 및 `tex.print`사이에 공백 문자가 유지되도록 하기 위해서입니다. 여기에서는 이를 생략해도 Lua가 코드를 올바르게 구문 분석하므로 엄밀히 필요하지는 않습니다. 바로 다음에 오는 공백 문자는 `\count75` 은 TeX 엔진이 숫자 값을 찾는 과정에서 흡수됩니다. 여기서 그 값은 다음에 제공됩니다 `\count`. 다음 뒤의 공백 문자 `75` 은 LuaTeX의 숫자 시퀀스 검색을 종료하는 데 사용됩니다 `75` 그리고 입력에서 흡수됩니다.  `\space` 매크로는 텍스트를 구분하는 데 필요한 공백 문자를 제공하도록 확장됩니다 `1564` 및 `tex.print`.

위 코드를 사용하면 LuaTeX는 다음을 조판합니다

`x= 1564 and y = 612.6`

여기서 “데이터 전달” 메커니즘은 다음에 의해 구현됩니다 `\number`: 이 경우 TeX에 에 저장된 값을 가져오도록 지시하는 확장 가능한 명령입니다 `\count` 레지스터에 저장해 보자 `75` 그리고 그 값 (`1546`)으로부터 각 숫자에 하나씩 문자 토큰을 생성하여, 숫자에 대한 토큰 시퀀스를 생성합니다 `1`, `5`, `6` 및 `4`. 이 4개의 문자 토큰은 다음에 의해 구성되는 주 토큰 목록에 포함됩니다 `\directlua` 그리고 이후 토큰 목록이 텍스트로 변환될 때 다시 텍스트 표현으로 변환됩니다. LuaTeX 내부에 저장된 `\count75` 레지스터 값에서 Lua 코드에 들어갈 숫자로 가는 길은 분명 매우 우회적이지만, 결국 작동합니다.

**팁:** LuaTeX의 확장 활동 결과를 검사하려면 다음과 같은 코드를 작성할 수 있습니다:

```
\directlua{
   local foo=[[local x=\number\count75
   tex.print("x= "..x)
   local y = (2*x-65)/5
   tex.print(" and y = "..y)]]
   print(foo)
}
```

이 예에서는 긴 대괄호 방법을 사용하여 문자열 변수를 만듭니다 `foo` 의 목적은 다음 사이의 모든 것의 확장으로 생성된 Lua 코드 문자열을 보관하는 것입니다 `[[` 및 `]]`. 이 문자열은 Lua 함수 호출을 통해 콘솔에 출력됩니다 `print(foo)`.

Overleaf에서는 의 내용을 기록하여 비슷한 결과를 볼 수 있습니다 `foo` 를 `.log` 파일에, LuaTeX Lua 함수를 사용하여 `texio.write()`:

```
\directlua{
   local foo=[[local x=\number\count75
   tex.print("x= "..x)
   local y = (2*x-65)/5
   tex.print(" and y = "..y)]]
   texio.write(foo)
}
```

## \directlua 토큰 목록의 토큰: 확장 불가능 토큰과 미확장 토큰

우리는 다음을 언급했습니다 `\directlua{⟨code⟩}` 는 다음을 수행합니다 *완전 확장* 여러분의 에 대한 `⟨code⟩`: 확장 불가능 토큰만 남을 때까지 모든 확장 가능한 명령을 제거하고 확장합니다. 다음에 의해 생성된 토큰 시퀀스 `\directlua`의 처리 과정(  `scan_toks()` 함수에서)은 연결되어 개별 토큰이 Lua로 전달하기 위해 다시 텍스트로 변환될 토큰 목록을 형성합니다.

그러나 아직 이 이야기의 마지막 부분은 다루지 않았습니다. 다음 내부에서 구성되는 토큰 목록까지 통과할 수 있는 두 “부류”의 명령 토큰을 고려해야 하기 때문입니다 `\directlua`: 우리는 이를 다음과 같이 부르겠습니다 *약식 명령 토큰* 및 *미확장* 토큰:

* **약식 명령 토큰**: 이 유형의 명령 토큰은 TeX 프리미티브 중 하나를 사용해 정의된 제어 시퀀스에서 발생합니다 `\chardef`, `\mathchardef`, `\countdef`, `\dimendef`, `\skipdef`, `\muskipdef` 및 `\toksdef`. 이러한 프리미티브 명령은 숫자 값을 나타내는 제어 시퀀스를 정의하는 데 사용되며, 그 결과 제어 시퀀스는 *실제 데이터 값을 포함하지* 확장 불가능합니다.
* **미확장 토큰**: 이 토큰 유형은 일반적으로 확장되지만 다음이 `\directlua` 다음 중 하나를 수행한 명령에서 발생합니다:
* 명시적으로 지시받았습니다 *실제 데이터 값을 포함하지* 그것들을 확장하지 않도록 지시받았습니다. 예를 들어, 명령에 의한 확장 억제 `\noexpand` 또는 `\unexpanded`—이것이 어떻게 이루어지는지는 곧 설명하겠습니다;
* 시퀀스를 처리하여 토큰을 삽입했습니다 `\the\toks` (이에 대해서는 아래에서 더 설명합니다).

### \directlua 토큰 목록의 두 “그룹” 토큰

논의를 바탕으로, 의 첫 단계 동안 구성되는 토큰 목록에 포함된 토큰은 `\directlua`의 전처리(  `scan_toks()` 함수에서)는 두 그룹으로 나뉜다고 말할 수 있습니다:

1. *본질적으로 확장 불가능한* 토큰

* 비활성 *문자가 없으면*;
* 확장 불가능 *프리미티브* *명령을*;
* 다음을 나타내는 모든 토큰 *약식 명령* (이들은 확장 가능하지 않으며, 아래를 참조하십시오).

3. *미확장* 토큰:

* 확장 가능한 명령을 나타내며 그 확장이 *억제된* (또는 회피된) 모든 토큰 `\directlua`의 전처리 중에.

#### 약식 명령 토큰: 확장 불가능 명령 만들기

언급했듯이, TeX 엔진은 다음을 구성하는 데 사용할 수 있는 프리미티브(내장 명령) 집합을 제공합니다 *확장 불가능한* 제어 시퀀스(여기서는 다음으로 표시됨 `⟨command⟩`). 이 프리미티브는 다음 형식을 취합니다:

* `\chardef ⟨command⟩ = ⟨numeric value⟩`
* `\mathchardef ⟨command⟩ = ⟨numeric value⟩`
* `\countdef ⟨command⟩ = ⟨numeric value⟩`
* `\dimendef ⟨command⟩ = ⟨numeric value⟩`
* `\skipdef ⟨command⟩ = ⟨numeric value⟩`
* `\muskipdef ⟨command⟩ = ⟨numeric value⟩`
* `\toksdef ⟨command⟩ = ⟨numeric value⟩`

여기서 `⟨numeric value⟩` 는 각 명령에 적합한 어떤 정수값입니다.

여기서는 다음의 사용을 간략히 살펴보겠습니다 `\chardef` 이 프리미티브의 핵심 기능, 즉 다음을 생성하는 기능을 보여 주기 위해 `⟨command⟩` 확장 불가능한 것입니다. 다음을 사용할 수 있습니다 ``\chardef\mydollar=`\$`` 를 사용하여 제어 시퀀스를 만듭니다 `\mydollar` 그리고 이를 사용하여 다음을 조판합니다 `$`:

`I paid \mydollar30.`

그러면 다음이 조판됩니다 `I paid $30.` 제어 시퀀스 `\mydollar` 에 의해 생성된 `\chardef` 은 확장 가능하지 않으며, 다음 예에서 확인할 수 있습니다:

```
\chardef\mydollar=`\$
\directlua{
   local x =[[I paid \mydollar30.]]
   texio.write(x)
}
```

그러면 다음의 텍스트가 생성됩니다 `.log` 파일

`I paid \mydollar 30.`

이는 다음이 `\mydollar` 는 *실제 데이터 값을 포함하지* 중에 확장되었음을 보여 줍니다 `\directlua`의 전처리. 다음 뒤에 나타나는 공백은 `\mydollar` 명령 토큰이 텍스트 표현으로 변환될 때 추가됩니다.

다음을 사용할 때 `\chardef` 를 사용하여 제어 시퀀스를 만들면, TeX의 해당 제어 시퀀스(명령)에 대한 내부 분류 결과 그것은 *확장 불가능한* 이 됩니다. 이는 매크로 정의 명령 \def, \edef, \gdef 또는 \xdef 중 하나로 정의된 제어 시퀀스와 매우 다른 동작입니다. 위에서 언급했듯이, 토큰 목록을 구성하는 과정에서  `\directlua` 는 들어오는 각 명령 토큰의 확장 가능 여부를 검사합니다. 명령 토큰이 확장 가능하지 않으면 토큰 목록으로 바로 통과하며, 나중에 토큰 목록의 토큰을 다시 텍스트 형식으로 변환하여 생성되는 Lua 코드 문자열에 그 텍스트 표현이 다시 나타납니다.

**plain TeX와 LaTeX에 대한 간략한 참고 사항**

역사적으로 Knuth의 원래 plain TeX는 일반적으로 사용되는 제어 기호 `\%`, `\&`, `\#` 및 `\$` 를 다음을 사용하여 정의했습니다 `\chardef`—표준 매크로 정의 명령 중 하나를 사용하지 않고 `\def`, `\edef`, `\gdef` 또는 `\xdef`. 예를 들면:

```
   \chardef\#=`\#
   \chardef\$=`\$
   \chardef\%=`\%
   \chardef\&=`\&
```

이 특이한 `` `\ `` 구문은 숫자 문자 코드 값을 얻는 TeX 방식입니다. 이전 plain TeX 체계에서 이러한 제어 기호는 확장 가능하지 않지만( 때문에 `\chardef`) LaTeX(또는 패키지)는 향상된 기능을 제공하기 위해 이를 다음과 같이 재정의할 수 있습니다 *매크로* 그러면 확장 가능하게 되므로, 이 점을 알아둘 필요가 있을 수 있습니다.

**이것이 \directlua에 어떤 영향을 미칠까요?**

plain TeX와 LaTeX에서 실행한 다음 코드의 결과를 비교해 봅시다. 단순화를 위해 결과를 다음에 기록하겠습니다 `.log` 파일에, LuaTeX Lua API 함수를 사용하여 `texio.write()`.

```
\directlua{
   local x=[[\$150 for the "\#1" product---20\%! more than its competitor, Widget \& Co.]]
   texio.write(x)
}
```

다음을 사용하여 이 코드를 실행하면 **plain TeX** 는 다음에 다음 출력을 생성합니다 `.log` 파일, 모든 확장의 결과를 보여 줍니다:

```
\$150 for the "\#1" product---20\%! more than its competitor, Widget \& Co.
```

분명히 plain TeX에서는 제어 기호 중 어느 것도`\$`, `\#`, `\%` 또는 `\&` 가 확장되지 않았습니다. 모두 다음을 사용하여 생성되었기 때문입니다 `\chardef`.

다음 코드를 다음으로 실행하면 **LaTeX** 문서:

```
\documentclass{article}
\begin{document}
   \directlua{local x=[[\$150 for the "\#1" product---20\%! more than its competitor, Widget \& Co.]] texio.write(x)}
\end{document}
```

는 다음에 다음 출력을 생성합니다 `.log` 파일

```
\protect \TU\textdollar 150 for the "\#1" product---20\%! more than its competitor, Widget \& Co.
```

분명히 LaTeX를 실행하면 plain TeX와 다른 결과가 생성됩니다. LaTeX에서는 명령 `\$` 이 확장되었으므로, 이것이 매크로임을 나타냅니다.

**참고:** plain TeX와 LaTeX 모두에서 `\directlua` 는 제어 기호 중 어느 것도 완전히 처리하지 않았습니다 `\%`, `\&`, `\#` 및 `\$` 해당 문자를 생성하도록. 다음에 의해 수행되는 확장 과정 중 `\directlua` 이러한 제어 기호를 나타내는 토큰, 또는 LaTeX의 경우 그 확장은 구성 중인 주 토큰 목록으로 바로 통과합니다.

**참고:** 제어 기호는 다음과 같이 범주 코드 11이 아닌 단일 문자로 구성됩니다 `\#`. 제어 기호를 나타내는 토큰이 다시 텍스트 표현으로 변환될 때, TeX 엔진은 해당 텍스트 뒤에 공백 문자를 삽입하지 않습니다. 이러한 제어 기호의 특별한 처리는 TeX 엔진 작동 방식에 내장된 규칙입니다.

### 미확장 토큰: 확장 억제

`\directlua`’의 전처리 과정은 텍 엔진이 확장을 수행하는 한 예이지만, 당신은 *방지하다* 확장이 적용되는 것을 방지하고 싶을 수 있는 `\directlua`, 이들을 처리할 때 `\write` 명령:

`\write file-number {⟨material⟩}`

\write는 텍 엔진이 다음을 출력하도록 지시합니다 `⟨material⟩`—종종 TeX/LaTeX 명령을 포함하는—텍스트 파일로(`파일 번호`); 그 안의 확장 가능한 명령은 모두 `⟨material⟩` 방지되지 않는 한, 다음 전에 확장됩니다 `⟨material⟩` 가 실제로 해당 파일에 기록됩니다.

예상할 수 있듯이, 텍 엔진은 확장을 억제하거나 제어하는 명령을 제공합니다:

* `\noexpand⟨token⟩`: 단일 `⟨token⟩`;
* `\unexpanded{⟨material⟩}`: 다음 안의 모든 확장 가능한 명령(토큰)의 확장을 방지합니다 `⟨material⟩`. 사실상 다음의 다중 토큰 버전입니다 `\noexpand`;
* `\protected`: 매크로 정의에 추가하는 접두사로, 특정 상황(예: 다음 중)에서 해당 매크로의 확장을 방지합니다 `\directlua`, `\write` 또는 `\edef`).

이름이 달리 암시하더라도, 둘 다 `\noexpand` 및 `\unexpanded` 당신이 *확장 가능한 명령* 는 텍 엔진의 확장 과정을 “토큰 연산”을 수행하는 것으로 보는 좋은 예입니다. 여기서의 연산은 하나 이상의 뒤따르는 토큰(명령)의 확장을 방지하는 것입니다. 왜냐하면 `\noexpand` 및 `\unexpanded` 둘 다 확장 가능한 명령이기 때문에, 이들은 제거되고 처리(실행)되며, 다음 동안 `\directlua`의 전처리 과정에서, 입력으로부터 토큰 목록을 구성하면서 `⟨code⟩`.

#### \noexpand ⟨token⟩

`\noexpand ⟨token⟩` 단일 `⟨token⟩`. `\noexpand` 안에서 `\directlua` 는 확장되어(입력에서 제거되어) 그 “확장 동작”의 결과로 대체됩니다. 다음을 확장한 결과는 `\noexpand` 는 특수한(숨겨진) `⟨marker token⟩` 을 원래의 앞에 배치하는 것입니다 `⟨token⟩` 그 확장이 억제될 대상입니다: 이는 `⟨marker token⟩` “다음 토큰은 확장하지 말라”는 플래그처럼 작동합니다. 왜냐하면 `\directlua` 는 완전 확장을 수행하므로, 확장 가능한 명령의 “확장 동작”에서 나온 토큰을 다시 처리합니다. 따라서 다음의 확장이 완료되면 `\noexpand ⟨token⟩` LuaTeX는 결과를 다시 읽으러 돌아가 두 토큰 시퀀스 `⟨marker token⟩⟨token⟩` 를 보고, 그 결과 원래의 `⟨token⟩` 가 확장되지 않은 채로, 다음이 구성하는 토큰 목록으로 통과하게 됩니다 `\directlua`.

**예제**

다음과 같이 작성하면

```
\directlua{
   local x= "\TeX"
}
```

그 `\TeX` 매크로는 구성 토큰으로 확장되며, plain TeX에서는 다음 텍스트가 Lua로 전달됩니다(참고: Lua는 이 코드를 처리할 수 없으며, 이는 단지 과정을 보여주기 위한 예시입니다):

`local x = "T\kern -.1667em\lower .5ex\hbox {E}\kern -.125emX"`

만약 우리가 *억제하면* 다음의 확장을 `\TeX` 매크로를 사용하여 `\noexpand`

`\directlua{local x= "\noexpand\TeX"}`

다음 Lua 코드가 생성됩니다(다시 말하지만, Lua는 이 코드를 실행할 수 없으며, 이는 단지 보여주기 위한 예시입니다 `\noexpand`):

`local x= "\TeX "`

다음 때문에 `\noexpand`, `\directlua` 확장되지 않습니다 `\TeX` 대신 다음을 나타내는 토큰 값을 단순히 통과시킬 뿐입니다 `\TeX` 명령이 아무 손상 없이 통과하여, 다음의 첫 번째 단계에서 구축되는 토큰 목록에 들어가도록 `\directlua`의 전처리 중에.

**참고:** 다음 뒤에 나타나는 공백 문자는 `\TeX` LuaTeX가 이후에 다음을 변환하면서 도입됩니다 `\TeX` 정수 토큰 값을 텍스트 표현으로 되돌리는 과정에서(다음 안에서 `tokenlist_to_cstring()` 함수).

#### \unexpanded{⟨material⟩}

`\unexpanded` 는 다음으로부터 형성된 모든 토큰의 확장을 억제하는 확장 가능한 명령입니다 `⟨material⟩`. *제거되고* 입력에서 ...을 제거하고 *대체됩니다* 그 “확장 동작”의 결과로 대체됩니다. 그렇다면 그것이 실제로 다음에 대해 무엇을 의미할까요? `\unexpanded`? *완전 확장*일반적으로, 동안 `\unexpanded` *우회합니다* 추가 확장을 전혀 하지 않습니다: 그 방법은 다음과 같습니다.

텍 엔진 내부에서 `\unexpanded` 명령은 먼저 다음 안의 문자와 명령을 `⟨material⟩` 로 변환하여 다음으로 구성된 임시 토큰 목록을 만듭니다 *미확장* 토큰으로 만듭니다. 모든 토큰이 생성되어 그 임시 토큰 목록에 저장된 뒤, 다음 `\unexpanded` 명령은 `\directlua` 에서 *건너뛰게 합니다* 되돌아가 그것들을 읽고 처리하는 것을—\directlua가 완전 확장을 수행하고 있음에도 불구하고—막습니다. 대신, 그 *미확장* 토큰들은 그대로 통과하여 다음이 구축하는 मुख्य 토큰 목록에 포함됩니다 `\directlua` (다음 `scan_toks()` 함수에서). 이렇게 해서, 다음 안의 모든 것이 `⟨material⟩` 토큰으로 변환되며, 그 토큰 집합에 대해서는 확장 과정이 건너뛰어집니다. 다음의 동작은 `\unexpanded{⟨material⟩}` 의 사용과 유사합니다 `\the\toks`, 이는 아래에서 설명합니다.

**예제**

`\unexpanded` 다음과 유사한 방식으로 결과를 생성합니다 `\noexpand` 다만 여러 토큰의 확장을 방지할 수 있습니다. 예를 들면 다음과 같습니다:

```
\directlua{
   local x = "\unexpanded{\foo\bar\foobar}. But Lua can't process this code!"
}
```

그 결과 Lua용 코드로 다음 텍스트가 생성됩니다:

`local x = "\foo \bar \foobar . But Lua can't process this code!"`

**참고**: 각 명령 이름 뒤에는 공백 문자가 있습니다. 이것 역시 LuaTeX가 이후에 확장되지 않은 토큰을 변환한 결과입니다 `\foo`, `\bar` 및 `\foobar` 다음 안에서 다시 텍스트로 `tokenlist_to_cstring()` 함수에서.

#### \protected 매크로 정의

이 `\protected` 명령은 TeX가 확장된 토큰 목록을 구성할 때(예: 다음이 구성하는 토큰 목록) 해당 매크로가 확장되는 것을 막기 위해 매크로 정의 앞에 붙이는 접두사입니다 `\directlua`의 전처리 중에.

**예제**

다음과 같은 매크로를, 해당 `\protected` 접두사를 사용하거나 사용하지 않고 정의했다고 가정해 봅시다:

```
\def\macroA{"This unprotected macro contains a string"}
\protected\def\macroB{"This protected macro also contains a string"}
```

Lua의 문자열 연결 연산자(`..`)를 사용하여 다음을 작성하면

```
\directlua{
   local x=\macroA..\macroB
}
```

`\directlua`의 전처리 과정은 Lua로 전달하기 위해 다음 코드를 생성합니다:

`local x="This unprotected macro contains a string"..\macroB`

`\macroA` 는 다음을 사용하여 정의되지 않았으므로 `\protected` 확장되어 연결될 문자열의 첫 부분을 생성하지만, `\macroB` 는 다음을 사용하여 정의되었으므로 `\protected` 확장되지 않았습니다.

전처리 과정에서 LuaTeX의 `scan_toks()` 함수는 다음에 대한 토큰을 생성하고 `\macroA`, 그것이 일반적인 확장 가능한 명령임을 인식해 확장했습니다. 그 확장은 다음 각 문자마다 하나의 문자 토큰으로 이루어진 문자 토큰 시퀀스를 생성합니다 `"This unprotected macro contains a string"`. 각 문자 토큰은 전달되어 구축 중인 토큰 목록에 추가됩니다.

다음이 `scan_toks()` 다음에 대한 토큰을 생성하고 `\macroB` 그 명령이 다음으로 정의되었음을 알아차리고 `\protected` 확장하지 않습니다: 다음을 나타내는 토큰은 `\macroB` 손대지 않은 채(확장되지 않은 채) 통과하여 구축 중인 토큰 목록에 들어갑니다. 그 토큰 목록이 만들어진 뒤, 전처리의 다음 단계는 `tokenlist_to_cstring()` 함수에서, 토큰 목록의 모든 토큰을 다시 텍스트 표현으로 변환하는 것입니다. 다음을 나타내는 확장되지 않은 토큰은 `\macroB` 이 감지되어 텍스트 표현으로 변환되고, 그 결과 텍스트 `\macroB` 가 Lua로 갈 코드에 나타납니다. Lua는 실제로 다음을 연결할 수 없다는 점에 유의하세요 `"This unprotected macro contains a string"..\macroB` 를 사용해 최종 문자열을 만들 수 없습니다. 왜냐하면 `\macroB` 는 Lua 구문에서 의미가 없기 때문에, 다음 오류가 발생합니다 `unexpected symbol near '\'`.

**잡학**: 이 `\protected` 명령은 다음에 의해 도입되었습니다 $$\varepsilon\text{-}\mathrm{\TeX}$$, Knuth의 원래 TeX 소프트웨어에 대한 첫 번째 주요 확장판이며, 코드 계보에 다음이 포함되는 모든 텍 엔진에서 지원됩니다 $$\varepsilon\text{-}\mathrm{\TeX}$$.

### 확장되지 않은 토큰: \directlua에서 \the\toks 사용하기

프로그램에서 그런 “특수한 경우”들을 다루고 활용하는 일이 없다면 삶은 같지 않을 것입니다 `\the` 와 함께 `\toks` 에서의 `\directlua` 명령은 그런 특수한 경우 중 하나입니다.

#### \toks에 대한 간단한 배경

TeX 기본 명령 `\toks` 는 텍 엔진에 나중에 사용할 토큰 몇 개를 저장하도록 지시합니다. 그 토큰들은 추가 처리를 위해 전달되는 대신 한쪽에 따로 두어, 다음을 사용해 지정한 메모리 위치에 저장됩니다 *토큰 레지스터*. 예를 들어, 텍 엔진이 몇 개의 토큰을 만들고 토큰 레지스터 위치에 저장하도록 지시할 수 있습니다 `100` 를 다음을 사용하여 정의했습니다

`\toks100={Hi, \TeX! \hskip 5bp}`

여기서 TeX는 토큰 레지스터 `100` 를 사용하여 메모리 내부의 알려진 위치, 즉 토큰 목록을 보관하기 위해 지정된 저장 영역에 접근합니다.

다음 사이의 모든 것을 나타내는 토큰은 `{` 및 `}` 생성되며, *확장되지는 않지만*, 그리고 토큰 목록으로 이어집니다—이 글 앞부분에서 살펴본 토큰 목록과 비슷합니다. 그 토큰들을 다시 사용하려면 다음과 같이 작성합니다 `\the\toks100` 여기서 `\the` (확장 가능한 명령)은 TeX에 저장된 토큰을 가져와, თქვენ가 작성한 위치에 삽입하도록 지시합니다 `\the\toks100`. `\the\toks` 는 TeX가 해당 위치에 몇 개의 토큰을 삽입하게 합니다.

이 `\toks` 명령을 *확장하지 않습니다* 자신이 만들고 저장하라고 요청받은 토큰 중 어느 것도: 단지 다음 사이의 문자와 명령을 `{` 및 `}` 토큰으로 변환해 저장할 뿐입니다.

#### \directlua로 돌아가기

확장에 대한 설명에서 우리는 다음을 언급했습니다 `\directlua{⟨code⟩}` 는 다음을 수행합니다 *완전 확장* 에 대한 토큰 값을 계산한다 `⟨code⟩`: 모든 확장 가능한 명령을 제거하고 그 확장 동작의 결과로 대체하는 것—계속해서 *추가 확장하다* 확장 가능한 명령의 최초 확장으로 생겨난 모든 토큰을

`\the` 는 확장 가능한 명령이므로 `\directlua` 확장됩니다. 그러나 다음과 함께 사용될 때 `\the` 가 다음과 함께 사용되면 `\toks` 안에서 `\directlua`, 예를 들어 `\the\toks⟨token register⟩`, 삽입된 토큰은 *더 이상 확장되지 않습니다*. `\the\toks⟨token register⟩` 다음 시퀀스를 주입합니다 *미확장* 토큰을, 다음에 저장된 `⟨token register⟩`를, 다음이 구축하는 토큰 목록에 직접 `\directlua`: 이 동작은 일반적인 완전 확장 과정을 우회합니다. 결과적으로 그 토큰들은 통과하여, *미확장*, 다음이 구축하는 주 토큰 목록에 포함됩니다 `\directlua`—확장되지 않은 토큰에 대한 이 통과 과정은 다음과 동작상 유사합니다 `\unexpanded`, 앞서 설명한 바와 같습니다.

**예제**

다음 매크로를 정의한다고 가정해 봅시다 `\mymacro` as `\def\mymacro{\TeX}`. 여기에는 다음에 대한 토큰 하나만 들어 있습니다 `\TeX` 명령(이는 매크로입니다): 따라서 우리는 확장 가능한 명령 `\mymacro` 이 또 다른 매크로 `\TeX`, 역시 확장 가능합니다.

다음 코드는 Lua가 문자열 변수 `x`:

```
\def\mymacro{\TeX}
\directlua{
   local x="\mymacro"
}
```

안에서 \\`directlua`, 다음에 대한 토큰은 `\mymacro` 확장되지만, 그 결과 또 다른 확장 가능한 토큰 `\TeX`이 추가로 확장됩니다. plain TeX에서는 이러한 확장이 Lua로 전달되는 다음 텍스트를 생성합니다:

`local x = "T\kern -.1667em\lower .5ex\hbox {E}\kern -.125emX"`

이 코드는 다음의 확장된 버전을 나타내는 텍스트를 포함하는 문자열을 정의하려고 합니다 `\TeX` 매크로입니다. 이 예제를 실행하려고 하면 Lua가 그 문자열을 만들려고 시도하지만 실패하며 다음 오류를 생성합니다:

`invalid escape sequence near ' "T\k'.`

이 글 후반부에서 “invalid escape sequence”의 의미를 살펴보겠습니다.

이제 다음의 사용을 대비해 봅시다 `\mymacro` 와 다음을 배치하는 것 `\TeX` 토큰을 다음이 생성한 토큰 목록 안에 넣는 것 `\toks` 명령:

```
\toks100={\TeX}
\directlua{
   local x="\the\toks100"
}
```

LuaTeX의 `\directlua` 처리는 Lua를 위해 다음 문자열을 생성합니다:

`local x = "\TeX "`

다음 뒤의 공백 문자는 `\TeX` LuaTeX의 명령 토큰→문자열 변환 과정에서 생성됩니다.

**하지만 주의하세요**: 이 `\TeX` 매크로는 *실제 데이터 값을 포함하지* 구성 토큰으로 확장되었습니다. `\the\toks100` 레지스터 100에 저장된 토큰이 삽입되도록 했지만, 그게 전부입니다: 그 토큰들은 *실제 데이터 값을 포함하지* 더 이상 확장되지 않고, 다음이 구축하는 주 토큰 목록에 포함됩니다 `\directlua` (다음 함수 안에서 `scan_toks()`). 다음이 생성한 토큰 목록에 토큰을 넣는 것은 `\toks` 토큰이 확장되는 것을 막는 또 다른 방법입니다.

이 예제를 실행하면 역시 오류가 발생합니다:

`invalid escape sequence near ' "\T'.`

Lua 이스케이프 시퀀스는 이 글 후반부에서 살펴봅니다.

## 확장에서 사용되는 기타 명령/기법

이 섹션에서는 확장이 적용되는 상황(예: 다음 내부)에서 유용할 수 있는 몇 가지 추가 TeX 명령/방법을 살펴봅니다 `\directlua`).

### \string ⟨token⟩

`\string` 는 ⟨token⟩을 각각 범주 코드 12인 문자 토큰들의 연속으로 변환하는 확장 가능한 명령입니다.

예를 들어, `\string\TeX` 는 4개의 문자 토큰 시퀀스를 생성합니다 `\`, `T`, `e` 및 `X` 여기서 각 문자는 범주 코드 12가 할당됩니다(앞의 `\` 문자도 포함하여).

다음과 같이 작성하면

```
\directlua{
   local x="I will use \string\newcommand"
   print(x)
}
```

그 `\string` 명령은 확장되어, 범주 코드 12를 가진 문자 토큰 시퀀스가 됩니다. 다음이 확장된 후 `\string` 가 확장되면, 그 결과로 생성된 문자 토큰들( `\newcommand`)가 다음이 구축하는 주 토큰 목록에 포함됩니다 `\directlua`. `\directlua` 가 주 토큰 목록 구성을 마치면, 그 구성 토큰들은 다시 텍스트 표현으로 변환되어 Lua 인터프리터로 전달할 다음 코드를 생성합니다:

`local x="I will use \newcommand" print(x)`

이 코드가 Lua로 전달되면, `print(x)` 은 문자열을 출력합니다 `x` 를 화면(콘솔)에 출력합니다. 하지만 우리는 약간 교묘하게도 의도적으로 다음으로 시작하는 예제 명령을 사용했습니다 `\n`. 로컬 TeX 설치에서 이 예제를 실행할 수 있다면 Lua가 다음 텍스트를 화면에 출력하는 것을 볼 수 있을 것입니다:

```
   나는 사용할 것이다
   ewcommand
```

Overleaf에서 이 코드를 실행하려면 LuaTeX가 다음에 직접 쓰도록 지시할 수 있습니다 `.log` 파일에, LuaTeX Lua API 함수를 사용하여 `texio.write(*string*)`:

```
\directlua{
   local x="I will use \string\newcommand"
   texio.write(x)
}
```

생성된 결과를 살펴보면 `.log` 파일에도 다음이 포함되어 있음을 알 수 있습니다

```
   나는 사용할 것이다
   ewcommand
```

이 예상치 못한 출력은 Lua가 다음을 해석했기 때문입니다 `\n` 다음 시작 부분의 `**\n**ewcommand` 를 개행 문자(문자 코드 10)의 이스케이프 시퀀스로 보았기 때문입니다. 이는 당신이 다음으로 시작하는 새 텍스트 줄을 시작하고자 한다고 가정합니다 `ewcommand`. Lua 이스케이프 시퀀스는 이 글 후반부에서 다룹니다.

### \detokenize{⟨material⟩}

`\detokenize` 은 효과 면에서 다음의 다중 토큰 버전입니다 `\string` 그리고 역시 다음 안의 모든 것을 변환하는 확장 가능한 명령입니다 `⟨material⟩` 를 범주 코드 12를 가진 문자 토큰 시퀀스로—*다만* 범주 코드 10이 부여되는 공백 문자(ASCII/Unicode 값 32) `\detokenize` 또한 다음과 같은 명령 이름 뒤에는 뒤따르는 공백 문자를 삽입합니다 *제어 단어* (예: `\foo`) 하지만 다음 뒤에는 공백 문자가 삽입되지 않습니다 *제어 기호* (예: `\#`, `\%` 등).

### 예제

매크로들이 `\foohoo`, `\foo`, `\bar` 및 `\foobar` 정의되어 있지 않더라도, 다음과 같이 작성하면:

```
\directlua{
   local x = "\string\foohoo\detokenize{\foo\bar\foobar}"
}
```

Lua 인터프리터로 전달할 코드로 다음 텍스트가 생성됩니다

`local x = "\foohoo\foo \bar \foobar "`

다음을 사용하지 않으면 `\string` 및 `\detokenize` 그리고 다음과 같이 작성하면:

`\directlua{local x = "\foohoo\foo\bar\foobar"}`

`\directlua` 처리할 것입니다 `\foohoo`, 그것이 명령임을 인식하고 확장하려고 시도합니다. 그러나 `\foohoo` 가 정의되어 있지 않으므로 오류가 발생합니다:

```
   ! Undefined control sequence.
   l.1 \directlua{local x = "\foohoo
                      \foo\bar\foobar"}
         ?
```

왜냐하면 `\string` 및 `\detokenize` 인수들을 일련의 문자 토큰으로 변환합니다, `\directlua`의 확장 과정은 확장 가능한 명령 토큰을 감지할 기회를 얻습니다 `\foohoo`, `\foo`, `\bar`, 또는 `\foobar`: 이들은 확장을 유발하기 훨씬 전에 문자 토큰의 시퀀스로 변환됩니다.

앞서 언급했듯이, 명령의 확장은 입력에서 그 명령을 제거하고 그 “확장 동작”의 결과로 대체하는 것을 의미합니다. 확장의 결과(보통 토큰)는 이후 텍 엔진이 읽습니다. 여기서 다음의 “확장 동작”은 `\string` 및 `\detokenize` 입력에서 문자 및 명령 토큰을 흡수해 문자 토큰 시퀀스로 변환하는 것입니다. 이들은 처음에는 임시 토큰 목록에 저장되며, 그 목록을 `\directlua` 그다음 읽습니다. 그 문자 토큰들은 다음이 구축하는 주 토큰 목록에 포함됩니다 `\directlua`.

다음 그림은 다음이 어떻게 `\string` 를 변환하는지 보여줍니다 `\foohoo` 명령을 문자 토큰 시퀀스로 변환하여, 이후 다음이 읽는 임시 토큰 목록을 생성합니다 `\directlua` 그 문자 토큰들을 구축 중인 주 토큰 목록에 포함시키기 위해

![](/files/24797ac616dcce557ce9e3b1196c13d624166e06)

만약 `\string` 또는 `\detokenize` 인수에서 문자를 만나게 됩니다. 예를 들어, `\string a` 또는 `\detokenize{abc}` 그 문자들(여기서는 범주 코드 11)을 사용하면 범주 코드 12를 가진 문자 토큰이 생성됩니다.

참고:

위의 예로 돌아가면:

`\directlua{local x = "\string\foohoo\detokenize{\foo\bar\foobar}"}`

그 결과 Lua 인터프리터로 전달할 코드로 다음 텍스트가 생성됩니다

`local x = "\foohoo\foo \bar \foobar "`

다음과 같은 점을 관찰할 수 있습니다:

* `\detokenize` 는 각 매크로 이름 뒤에 공백 문자를 삽입했지만 `\string` 그렇지 않았습니다.
* `\string` 단일 토큰에 작용합니다.
* 문자열 `"\foohoo\foo \bar \foobar "` 를 정의하는 데 사용된 `x` 에서는 다시 Lua의 이스케이프 문자 메커니즘(아래에서 설명)을 만나게 됩니다:

  * `\bar` 는 다음으로 시작합니다 `\b` 이는 다음을 나타내는 데 사용되는 Lua 이스케이프 시퀀스입니다 [백스페이스 문자](https://en.wikipedia.org/wiki/Backspace) (문자 코드 8);
  * 명령 `\foohoo`, `\foo` 및 `\foobar` 모두 다음으로 시작합니다 `\f`, 다음을 나타내는 데 사용되는 Lua 이스케이프 시퀀스입니다 [폼피드 문자](https://en.wikipedia.org/wiki/Page_break#Form_feed) (문자 코드 12).

  문자 시퀀스 때문에 `\b` 및 `\f` 큰따옴표로 만든 문자열 안에서 사용됩니다 `"..."` Lua의 이른바 *롱 브래킷* 문자열 방식: 이제 Lua의 이스케이프 시퀀스와 함께 논의할 수 있는 주제입니다.

## “Lua 이스케이프 시퀀스”란 무엇인가?

프로그래밍 언어는 언어 문법의 일부로 특정 문자를 “특수 용도”로 예약해 둡니다. 실제로 그런 문자들은 어떤 형태의 특별한 의미를 갖도록 정의됩니다. 그러나 예를 들어 더 긴 문자열의 일부로 그 문자를 포함하고 싶은데, 원래 동작이 문법 오류를 일으킨다면 그 문자의 특별한 의미를 일시적으로 “꺼야” 할 때가 있습니다. 본질적으로 그 문자는 처리되어야 합니다 *없는* 기본 해석이 발동되지 않도록 해서—눈에 띄지 않고 지나가게 하는 것입니다. 이를 위해 프로그래머는 다음과 같은 기법을 사용합니다: *이스케이프* 이때 “특수 문자”는 이른바 *이스케이프 시퀀스*.

Lua에서도 지원하는 표준 예로는 문자열 안에서 큰따옴표를 사용하는 경우가 있으며, 이때 안쪽 큰따옴표를 다음 이스케이프 시퀀스로 이스케이프합니다 `\"`:

`"LuaTeX에 대해 물었을 때 그들은 이렇게 답했다: \"정말 멋진 TeX 엔진입니다!\" 나도 동의했다."`

Lua 언어는 이스케이프 시퀀스를 다루기 위한 여러 메커니즘을 제공합니다:

* 다음과 같은 표준 시퀀스들이 있습니다 `\n` (줄바꿈), `\r` (캐리지 리턴), `\\\\` (역슬래시), `\"` (큰따옴표), `\t` (수평 탭), `\v` (수직 탭) 그리고 `\'` (작은따옴표);
* `\xXX`, 여기서 `XX` 정확히 두 개의 16진수 숫자로 이루어진 시퀀스입니다;
* `\ddd`, 여기서 `ddd` 최대 세 개의 10진수 숫자로 이루어진 시퀀스입니다;
* 이 글을 쓴 시점(2019년 8월)에는 Overleaf에서는 아직 사용할 수 없었지만, LuaTeX의 최신 버전은 Lua 5.3을 사용하며, 여기에는 UTF-8 이스케이프 시퀀스 지원이 추가되어 있습니다: `\u{XXX}`. 이 이스케이프 메커니즘은 UTF-8로 인코딩된 유니코드 문자에 사용되며, 여기서 `XXX` 은(는) 해당 문자 코드 포인트를 나타내는 하나 이상의 16진수 숫자로 이루어진 시퀀스입니다. 둘러싼 괄호 `{ }` 는 필수입니다.

### 이스케이프 시퀀스 제어하기

전통적으로 문자열은 다음과 같이 큰따옴표를 사용해 정의합니다 `"this is a string"`; 그런 문자열 안에서는 이스케이프 시퀀스를 사용할 수 있습니다: `"this is a string.\nI'll now start on a new line."`. 그러나 Lua에는 두 번째이자 *매우* 더 편리한 문자열 정의 메커니즘이 있습니다. 이른바 *롱 브래킷* 텍스트를 다음 안에 넣어 문자열을 정의하는 메커니즘입니다 `[[` 및 `]]`:

`[[I am a long brackets string]]`

롱 브래킷 방식으로 만든 문자열 안에서는 Lua의 문자 이스케이프 메커니즘이 *꺼집니다*: 이스케이프 시퀀스는 일반 문자로 취급됩니다. 예를 들어 문자열

`[[I am a long brackets\n string]]`

그 `\n` 이스케이프 시퀀스는 단일 캐리지 리턴 문자(ASCII 코드 13)로 취급되지 않고 두 개의 일반 문자로 취급됩니다: `\` 뒤에 `n`.

### 롱 브래킷 문자열이 왜 그토록 유용할까요?

나중에 살펴보겠지만, LuaTeX은 다음과 함께 사용할 수 있는 전문화된 내장 Lua 함수 모음을 제공합니다 `\directlua` LuaTeX의 조판 동작을 제어하기 위한 것입니다. 그 많은 함수들 중 하나는 다음과 같은 함수입니다 `tex.print(*string*)` 이를 통해 다음을 전달할 수 있습니다 `*string*` Lua 코드의 सामग्री를 조판을 위해 LuaTeX로 다시 전달할 수 있습니다. 매우 간단한 예는 다음과 같습니다:

`\directlua{tex.print("Hello, World!")}`

그 결과 LuaTeX이 다음을 조판하게 됩니다 `안녕하세요, 세상!`

이 `*string*` 에서 사용되는 `tex.print(*string*)` LuaTeX가 처리할 TeX 및 LaTeX 명령을 나타내는 텍스트도 포함할 수 있습니다. 그러나 TeX/LaTeX 명령은 다음 문자로 시작합니다 `\` 문자는 큰따옴표로 만든 문자열에서는 문제가 됩니다. Lua가 문자열을 구문 분석하면서 맨 앞의 `\` 문자를 감지해 이를 이스케이프 시퀀스의 시작으로 해석하기 때문입니다. Lua가 이스케이프 시퀀스를 처리하려고 하면, 대개 처음의 `\` 와 많은 TeX/LaTeX 명령 이름의 첫 글자를 결합해도 Lua가 인식하는 유효한 이스케이프 시퀀스가 되지 않기 때문에 실패합니다. 예를 들어 다음과 같은 문자열을 처리할 때 `"I like \LaTeX"` Lua는 다음을 보게 됩니다 `\L` 그리고 “invalid escape sequence” 오류로 실패하며, 이것이 앞서 언급한 오류의 원인입니다.

#### 롱 브래킷 문자열이 해결해 줍니다!

롱 브래킷 방식으로 문자열을 만들고(정의하고) 사용하는 것은 매우 유용합니다. TeX/LaTeX 명령은 다음 문자로 시작하지만 `\` 롱 브래킷 문자열 방식은 Lua의 이스케이프 시퀀스 메커니즘을 비활성화(꺼짐)하기 때문입니다. 다음은 간단한 예이며, 예를 들어 매크로가 확장되지 않도록 해야 한다는 점을 기억해 둡시다 `\protected` 또는 `\noexpand`.

다음과 같은 `\newtest` 매크로를 정의했다고 합시다

`**\protected**\def\newtest#1{The argument: #1}`

그리고 이를 다음에서 사용합니다 `\directlua` LuaTeX Lua API 함수 `tex.print()`:

```
\directlua{
   tex.print("\newtest{Hello}")
}
```

다음의 사용으로 인해 `\protected`, `\newtest` 는 확장되지 않으며, 그 결과 다음 텍스트가 Lua로 전달됩니다:

`tex.print("\newtest {Hello}")`

다음 뒤에 추가된 공백 문자 `\newtest` 와 여는 중괄호 (`{`) 앞의 공백은 다음의 부작용입니다 `\directlua`의 명령 토큰을 다시 텍스트 표현으로 변환하는 과정.

이 코드는 Lua로 전달되고, 이후 LuaTeX 함수 `tex.print()` 하지만 문제가 하나 있으며, 이는 사용하는 글꼴에 따라 다른 방식으로 드러납니다. Overleaf의 LaTeX에서는 다음과 같은 출력이 보일 것입니다:

![](/files/ad04e2b3e7ad7c0cc0a5674208bac2cb20ed0d41)

그리고 로그 파일에는 경고가 함께 표시됩니다:

```
   누락된 문자: 다음 문자가 없습니다
   (U+000A)이 글꼴 [lmroman10-regular]:+tlig;에 없습니다!
```

plain TeX에서는 다음과 비슷한 출력이 보일 수 있습니다:

![](/files/cd44a6dbc8e92256bfea4754b03ae3ecb8a63b40)

두 경우 모두 `\newtest` 매크로가 호출되지 않으며 출력도 의도한 바와 다릅니다. 오류는 Lua의 이스케이프 문자 메커니즘 때문에 발생합니다. 다음 텍스트에서 `\newtest {Hello}` 매크로 이름은 다음으로 시작합니다 `\n` Lua가 줄바꿈 문자에 대한 이스케이프 시퀀스로 인식하므로 다음을 `\n` ASCII 문자 10, 즉 16진수 0A로 바꿉니다. LaTeX 오류 메시지에서 `U+000A` 는 4개의 16진수 숫자를 사용해 유니코드 값을 나타내는 방법입니다.

패키지가 `\n` 가 줄바꿈 문자로 변환되면, LuaTeX은 매크로 호출로 보지 않고 ASCII 문자 코드 10으로 시작하는 텍스트를 조판하라는 요청으로 여깁니다:

`⟨ASCII 10⟩ewtest {Hello}`

사용하는 글꼴에 따라 LuaTeX이 다음 문자를 조판할 수도 있고 그렇지 못할 수도 있습니다 `⟨ASCII 10⟩` 문자를 조판할 수 있지만, 나머지 텍스트는 그대로 출력되며 `{` 및 `}` 는 하나의 그룹으로 취급되어 출력되지 않습니다.

Plain TeX에서는 기본 글꼴이 Computer Modern Roman인데, 이 글꼴은 특이한 인코딩을 사용하므로 문자 코드 10이 보이면 대문자 오메가가 조판되어 다른 결과가 나옵니다.

이러한 문제를 막으려면 Lua의 이스케이핑 메커니즘이 적용되지 않도록 롱 브래킷 문자열을 사용해야 합니다. 올바른 결과는 다음으로 만들어집니다

`\directlua{tex.print([[\newtest{Hello}]])}`

그 결과는 다음 스크린샷에 나온 것과 같습니다:

![](/files/348ab2d13517fa77744f1e67a43526c488f8764f)

### 확장과 비확장 명령의 비실행

확장을 논의할 때 우리는 이것이 TeX 엔진이 *제거하고* 현재 입력에서 확장 가능한 명령(토큰)을 *대체하며* 그 확장 가능한 명령이 생성한 결과로 바꾸는 과정이라고 설명했습니다. \directlua가 수행하는 작업은 *확장 전용* 작업(토큰 목록을 생성하는 작업)이기 때문에, 그것은 *더 이상* LuaTeX의 처리를 그 이상 진행하지 않습니다. 확장 가능한 명령이 한 번 읽혀 완전히 확장되면, 그 확장의 결과는—대개 비확장 명령(토큰)을 포함하는데—구성 중인 토큰 목록에 포함되어 Lua로 전달하기 위해 다시 텍스트로 변환될 준비를 하게 됩니다.

여기에는 중요한 원리가 작용합니다: 다음 동안 *확장 전용* 토큰 목록을 생성하도록 설계된 작업에서는 LuaTeX을 포함한 TeX 엔진은 *실행하지 않습니다* 어떠한 비확장 primitive 내장 TeX 명령도.

의 경우 `\directlua{⟨code⟩}`, 당신의 완전히 확장된 버전이 `⟨code⟩` 비확장 TeX/LaTeX 명령을 생성하거나 포함하더라도 그것들은 *Lua로 전달됩니다* (텍스트로 표현됨).

#### 예제

다음은 비확장 primitive가 확장 전용 처리 동안(예: 다음 내부에서) 실행되지 않음을 보여 주는 예입니다 `\directlua`). 다음 매크로를 정의했다고 합시다 `\setcountreg` 다음과 같이:

`\def\setcountreg#1#2{\count#1=#2\relax}`

**참고**: 여기서는 `\relax` 를 매개변수 뒤에 사용해 `#2` LuaTeX이 매개변수와 일치할 숫자 값(인수)을 찾기 위해 입력을 스캔할 때 지나치게 앞질러 읽지 않도록 합니다 `#2`.

만약 다음 밖에서 `\directlua`, 나중에 다음과 같이 매크로를 실행하면

```
   \setcountreg{100}{50}
   카운트 레지스터 100의 값은 \the\count100입니다.
```

다음이 출력됩니다

`카운트 레지스터 100의 값은 50입니다.`

이 문맥에서 어떤 TeX 엔진이든 다음 매크로를 처리합니다 `\setcountreg`—매크로를 확장하고, 인수를 결정한 뒤 계속 읽고 *그리고* (실행) 매크로의 대체 텍스트(정의)에 포함된 명령을 수행합니다. 여기서 결과는 다음 값을 할당하는 것입니다 `50` 를 레지스터에 저장된 값으로 `\count100`.

그러나 TeX 엔진이 다음을 수행할 때는 *확장 전용* 작업을 하는 경우처럼, 다음과 같이 `\directlua`, 그것은 *실행하지 않습니다* 매크로 정의에 들어 있는 비확장 명령을.

다음과 같이 작성하면

```
\def\setcountreg#1#2{\count#1=#2\relax}
\directlua{
   local x = [[\setcountreg{100}{50}]]
}
```

그러면 Lua용 코드로 다음 텍스트가 생성됩니다:

`local x = [[\count 100=50\relax ]]`

위에서 생성된 Lua 코드는 다음 안에서 `\directlua` 그 `\setcountreg` 가 확장되고, 인수가 식별되어 적절한 매개변수(`#1` 및 `#2`)에 대입되었지만, 거기서 더 나아가지는 않습니다: 비확장 primitive TeX 명령 `\count` 는 *은 실행되지 않습니다* 동안 `\directlua`의 확장 처리 중에.

그러나 결과 문자열을 다음에 전달하면 LuaTeX이 TeX 코드를 실행합니다 `x` *LuaTeX로 다시* 통해 `tex.print(x)` 이렇게

```
\count100=50 % set \count100 to a starting value of 50
\def\setcountreg#1#2{\count#1=#2\relax}
\directlua{
   local x = [[\setcountreg{100}{250}]]
   tex.print(x)
}
카운트 레지스터 100에 저장된 값은 \the\count100입니다.
```

다음 뒤에 `\directlua` 가 끝나면 출력은 다음과 같습니다

`카운트 레지스터 100에 저장된 값은 250입니다.`

이는 카운트 레지스터 `100` 에 이제 값이 들어 있음을 보여 줍니다 `250`.

위 예제에서 생성된 Lua 코드는 다음과 같습니다

`local x = [[\count 100=250\relax ]] tex.print(x)`

이 코드는 다음을 정의합니다 `x` 를 잘못된 이스케이프 시퀀스로 인한 오류를 피하기 위해 사용되는 롱 브래킷 방식으로 만든 문자열로 정의합니다. 만약 큰따옴표를 사용했다면 `"..."` x를 정의할 때 문자 조합 `\c` 다음 시작 부분의 `\count` 가 오류를 일으킬 것입니다: `invalid escape sequence near ' "\c'`.

LuaTeX Lua API 호출 `tex.print(x)` 그 결과 LuaTeX은 다음 TeX 코드 시퀀스를 실행합니다 `\count 100=250\relax` 및 `\count100` 에 값이 할당됩니다 `250` 조판된 출력에서 볼 수 있듯이:

`카운트 레지스터 100에 저장된 값은 250입니다.`

#### 주의: 매크로와 LuaTeX Lua API

위 예제에서 우리는 다음 동안 `\directlua`의 전처리(확장) 동안 LuaTeX이 다음 코드를 실행하지 않았다는 것을 보았습니다 `\count 100=250`, 여기에는 다음이 포함됩니다 `확장 불가능한` primitive 명령 `\count`: 그 코드를 실행하려면 우리는 *이를 LuaTeX로 다시 전달해야 했습니다* 통해 `tex.print()`.

`\directlua` 는 LuaTeX이 토큰 목록을 구성하기 위해 확장 전용 처리를 수행하는 한 가지 예일 뿐입니다. 이와 비슷한 확장 처리 및 토큰 목록 생성 작업을 수행하는 다른 명령들도 있으며, 예를 들어 `\write` 및 `\edef`: 그런 명령들 역시 확장 처리 중에는 비확장 primitive를 실행하지 않습니다. TeX 엔진은 확장 전용 처리 작업 중 토큰 목록을 구성할 때 비확장 primitive를 실행하지 않는다는 것이 일반적인 원칙입니다.

**LuaTeX Lua API를 사용하도록 매크로 다시 쓰기**

우리는 다음을 다시 쓸 수 있습니다 `\setcountreg` 라는 LuaTeX Lua API 함수를 사용해 매크로를 `tex.setcount()`, 이렇게 함으로써 카운트 레지스터에 저장된 값을 바꾸는 TeX 명령을 피할 수 있습니다 `100`:

```
   \def\setcount#1#2{\directlua{tex.setcount(#1,#2)}}
   \count100=50
   카운트 레지스터 100에는 \the\count100이 들어 있습니다\par
   \setcount{100}{250}
   카운트 레지스터 100에는 이제 \the\count100이 들어 있습니다\par
```

이 코드는 다음을 조판합니다:

```
카운트 레지스터 100에는 50이 들어 있습니다
카운트 레지스터 100에는 이제 250이 들어 있습니다
```

여기서는 `tex.setcount()`LuaTeX의 많은 Lua API 함수 중 하나를 사용해 *직접 접근하고 있습니다* LuaTeX의 내부 데이터 저장 영역에 값을 넣기 위해 `250` 카운트 레지스터를 나타내는 메모리 위치에 `100`. 결과적으로 우리는 *우회했습니다* LuaTeX의 표준 TeX 엔진 입력 처리 방식인 입력 읽기, 토큰 생성, TeX primitive 명령 실행을 우회한 것입니다. 그러나 주의할 점이 있습니다: LuaTeX의 Lua API 함수를 사용하면 확장 전용 처리 작업이 *부작용을 일으킬 수 있습니다*: 순수 TeX/LaTeX 명령만으로는 불가능했을 TeX 엔진 내부 저장 값의 변경입니다.

**예: 예상치 못한 부작용**

다음은 이를 보여 주는 예입니다 *예상치 못한* 다음과 같은 매크로에서 발생할 수 있는 부작용입니다 `\directlua`. 다음 코드를 작성했다고 합시다:

```
\def\dochange{\directlua{tex.setcount(999,12345)}}
\edef\careful{\dochange}
\the\count999
```

이 코드를 실행하면 다음이 조판됩니다 `12345`!

어떻게 그럴 수 있죠? 우리는 *명시적으로* 그 값을 카운트 레지스터에 넣는 코드나 매크로를 호출하지 않았습니다 `999`. 아니, 정말 그랬을까요?

우리는 다음을 정의했습니다 `\dochange` 다음을 사용합니다 `\directlua` 를 사용하는 명령을 `tex.setcount()` 사용해 값을 저장하도록 `12345` 카운트 레지스터에 `999`: TeX 코드에서는 다음과 같습니다 `\count999=12345`. 그런 다음 표준 TeX primitive `\edef` 를 사용해 매크로 `\careful`을 정의했습니다—바로 다음의 사용이 `\edef` 예상치 못한 부작용을 일으키는 것입니다.

`\edef` 는 인수를 완전히 확장합니다: 여기서는 확장 가능한 매크로를 감지하고 `\dochange` 이를 확장합니다.  `\dochange` 매크로는 확장 가능한 명령 `\directlua` 를 사용하며, 여기에는 Lua API 호출이 포함되어 있습니다. 따라서 다음의 확장은 `\dochange` 의 확장으로 이어지고 `\directlua` 그로 인해 `tex.setcount()` 가 호출되어 카운트 레지스터의 값을 변경합니다 `999`.

다음으로 다시 정의하면 `\dochange` TeX 명령을 사용하도록:

```
   이전: 카운트 레지스터 999에는 \the\count999이 들어 있습니다.\par
   \def\dochange{\count999=12345\relax}
   \edef\careful{\dochange}
   이후: 카운트 레지스터 999에는 \the\count999이 들어 있습니다.\par
```

이 코드를 실행하면 다음이 조판됩니다

```
이전: 카운트 레지스터 999에는 0이 들어 있습니다.
이후: 카운트 레지스터 999에는 0이 들어 있습니다.
```

분명히 다음에는 아무 영향도 없었습니다 `\count999`. 다음이 `\edef` 정의할 때 `\careful` 그것을 확장하지만 `\dochange` 그 확장은 비확장 TeX primitive만 만들어 냅니다: 그것들은 *은 실행되지 않습니다* 하지만 단지 *저장될 뿐* 다음을 이루는 토큰 목록 안에 `\careful`.

참고로, 같은 원리가 다음이 조판 결과를 만들어 내는 이유도 설명합니다:

```
\def\dochange{\directlua{tex.print("Hello")}}
\edef\careful{\dochange}
```

## LuaTeX의 Lua API 간단 소개

앞서 보았듯이, `\directlua` 는 일반적인 Lua 코드나 Lua와 TeX/LaTeX 코드를 섞어 작성할 수 있게 해줄 뿐만 아니라, LuaTeX의 내부 동작과 통신하거나 직접 제어하는 데 사용할 수 있는 추가 Lua 함수 묶음(LuaTeX 전용)에도 접근할 수 있게 해줍니다. 이 글에서는 여러 Lua 함수를 사용했으며, `tex.print()`, `texio.write()`, `tex.setcount()` 이것들과 함께 *많은* 더 많은 함수가 문서화되어 있습니다 [LuaTeX 참고 매뉴얼](http://www.pragma-ade.com/general/manuals/luatex.pdf) 관련 함수들의 묶음을 다음과 같이 부르는 *라이브러리*.

이 Lua 함수들을 LuaTeX의 Lua API(**복사된**애플리케이션 **P**프로그래밍 **I**인터페이스)라고 생각할 수 있으며, Lua를 구동기로 사용해 LuaTeX의 조판 동작을 제어함으로써 정교한 조판 및 문서 엔지니어링 솔루션을 구성할 도구를 제공합니다.

앞서 언급했듯이, LuaTeX은 API를 라이브러리라고 부르는 함수들의 집합으로 조직합니다: 목적이나 동작이 서로 관련된 함수들의 묶음입니다. 각 함수 집합은 LuaTeX 내부 프로세스, 데이터 구조, 데이터 저장소 및 조판 알고리즘의 특정 측면에 접근할 수 있도록 설계되어 있습니다. 내부적으로 LuaTeX은 여러 구성 요소로 이루어져 있습니다: TeX 엔진 자체뿐만 아니라 Lua, MetaPost, Kpathsea, FontForge, libpng, zlib을 포함한 다른 하위 시스템을 이루는 소프트웨어 라이브러리/도구(대부분 C로 작성됨)입니다. 이러한 라이브러리들은 LuaTeX 실행 소프트웨어의 기능을 구성하도록 통합되어 있으며, 사용자는 Lua API를 통해 이러한 여러 소프트웨어 구성 요소의 통합과 조정에서 파생된 LuaTeX의 기능에 접근합니다.

## 몇 가지 예와 함정

이 섹션에서는 이 글에서 제공한 주제, 개념, 설명을 활용하는 몇 가지 추가 예를 소개합니다.

### 물결표(\~) 문자 사용하기

Lua 언어는 다음 문자를 `~` 문자(물결표라고 함)를 문법의 일부로 사용하며, “같지 않음” 검사를 수행하는 문법도 포함합니다. 예를 들어 변수 `x` 가 다음과 같지 않은지 `4` 확인하려면 다음과 같이 쓸 수 있습니다:

```
   local x=3
   if x ~= 4 then
   print("x is not equal to 4")
   end
```

이 간단한 Lua 코드를 통해 실행해 보려고 하면 `\directlua`:

```
\directlua{
   local x=3
   if x ~= 4 then
   print("x is not equal to 4")
   end
}
```

오류가 발생합니다:

`[\directlua]:1: 'then' expected near '\'.`

이상한데, 우리의 코드는 올바르기 때문입니다. 우리는 `'then'` 을 사용했고 `\` 문자가 코드에 없는데, 무엇이 잘못된 걸까요? 이를 이해하려면 TeX/LaTeX에서, `~` 은 보통 범주 코드 13을 가진 “특수 문자”로 정의됩니다. 이른바 활성 문자(active characters)는 미니 매크로이므로 확장의 대상입니다.  `\directlua` 이 문자를 감지하면 `~` 문자를 *제거한 뒤* 입력에서 ...을 제거하고 *그것을 대체하여* 그리고 확장 결과를 사용합니다. plain TeX을 사용하면 LuaTeX가 생성하여 Lua 인터프리터에 전달하는 결과 텍스트(코드)에는 실제로 해당 `~` 문자가 포함되지 않으며, 다음과 같습니다:

`local x=3 if x \penalty \@M \ = 4 then print("x is not equal to 4") end`

이 `~` 문자는 *제거되고* 및 *확장되었습니다* 그 구성 명령들로—위 Lua 코드는 plain TeX이 활성 문자를 정의한 결과입니다 `~`. 이제 Lua가 다음 오류를 반환하는 이유를 알 수 있습니다 `'then' expected near '\'`—그는 이 코드를 파싱하기 시작하지만 다음 단어를 만나게 됩니다 `\penalty` 이는 Lua에겐 아무 의미가 없고 문법 오류를 생성합니다.

이를 수정하려면,  `~` 문자가 안전한 범주 코드를 가져야 합니다.  `\directlua` 가 귀하의 코드를 처리하는 시점에 예를 들어 `~` 의 범주 코드를 11(문자)로 임시 변경하려면 다음과 같이 그룹으로 감싸면 됩니다:

```
\begingroup
\catcode`\~=11
\directlua{
   local x=3
   if x ~= 4 then
   print("x is not equal to 4")
   end
}
\endgroup
```

이 코드는 예상대로 작동하며 `x is not equal to 4` 가 콘솔에 출력됩니다. 다른 방법도 있습니다. 확장 가능한 명령 `\noexpand` 또는 `\string`.

#### \string⟨token⟩ 사용하기

다음을 적용할 수 있습니다 `\string` 단일 문자 `⟨token⟩` `~` 에, 범주 코드 13(활성 문자)을 가진 `\string` 를 변환하는지 보여줍니다 `~` 문자를 사용하여 범주 코드 12를 가진 문자 토큰을 생성할 수 있습니다. 이렇게 하면

```
\directlua{
   local x=3
   if x \string~= 4 then
   print("x is not equal to 4")
   end
}
```

우리가 필요한 Lua 코드를 생성합니다:

`local x=3 if x ~= 4 then tex.print("x is not equal to 4") end`

#### \noexpand⟨token⟩ 사용하기

다음을 사용할 수 있습니다 `\noexpand~` 활성 문자의 확장을 억제하기 위해 `~`

```
\directlua{
   local x=3
   if x \noexpand~= 4 then
   print("x is not equal to 4")
   end
}
```

확장되지 않은 `~` 토큰은 `\directlua` 에서 만들어지는 토큰 목록을 통과하고, 다시 텍스트로 변환되어 작동하는 Lua 코드를 생성합니다.

### # 문자 사용하기

Lua 언어에서는 `#` # 문자는 테이블의 길이를 구하는 데 사용할 수 있습니다. 그러나 다음 코드를 시도해 보면

```
\directlua{
   local tbl = {}
   tbl[1] = "Hello"
   tbl[2] = "World"
   tex.print("Table length is "..#tbl)
}
```

LuaTeX가 다음을 조판할 것이라고 예상할 수 있지만

`Table length is 2`

하지만 오류가 발생합니다:

`\directlua]:1: attempt to get length of a number value`

이 오류는 다음 때문에 발생합니다 `#` # 문자는 보통 범주 코드 6(매크로 매개변수)을 가지는데,  `#` # 문자는 TeX/LaTeX에서 두 가지 용도로 사용됩니다: 매크로 매개변수(`#1`, `#2`… `#9`)를 나타내는 것과 정렬 템플릿의 대체 텍스트(예: `\halign` 및 `\valign`).

다음이 `\directlua` 토큰 목록을 만들기 위해 토큰을 생성할 때 `#` 범주 코드 6을 가진 문자로 보고 이를 나타내는 적절한 문자 토큰을 만듭니다. 최종 토큰 목록을 다시 텍스트 형식으로 변환할 때, #에 대한 문자 토큰(범주 코드 6)은 특별한 처리를 받는데, 다음과 같이 출력됩니다 *연속된 두 문자*: `##`, 그 결과 다음 코드가 Lua로 전달됩니다:

`local tbl = {} tbl[1] = "Hello" tbl[2] = "World" print(##tbl)`

Lua 코드로 변환할 때 원래의 `#` 가 두 배로 늘어나 오류가 발생합니다:

`\directlua]:1: attempt to get length of a number value`

이 문제는 TeX 문법이 이중 해시 기호 `##` 를 사용해 하나의 `#` 토큰을 나타내거나 생성하기 때문입니다. 이 구문은 매개변수를 받는 다른 매크로를 정의하는 매크로나, `\halign` 또는 `\valign` 테이블 생성 명령의 템플릿을 만드는 데 쓰는 매크로에서 사용됩니다. 꽤 혼란스럽지만 예를 살펴보겠습니다.

#### 예제

다음과 같은 매크로를 정의한다고 가정해 봅시다 `\mymacro` 이는 하나의 매개변수를 받으며, `#1`, 또한 두 번째 매크로도 정의합니다 `\foo` 그 자체도 하나의 매개변수를 받습니다. 매개변수 `#1` 가 사용되는 `\mymacro` 것과, `\foo` 자체 매개변수를 사용하도록 `#1` TeX 문법에서는 다음을 사용해야 합니다 `##1` 안에서 `\mymacro` 와 함께 사용될 매개변수를 나타내기 위해 `\foo`:

`\def\mymacro#1{\def\foo##1{#1 Hello##1}}`

만약 다음과 같이 쓴다면 `\mymacro{Hey!}` 그것은 다음 매크로를 정의하게 됩니다 `\foo` 를

`\def\foo#1{Hey! Hello#1}`

다음 사항에 유의하세요: `\mymacro`의 매개변수 `#1` (`Hey!`)가 다음의 정의에 포함되었습니다 `\foo` 그리고 다음 시퀀스가 `##1` 다음으로 변환되었습니다 `#1` 의 정의에서 `\foo`. 따라서 우리는 다음을 사용할 수 있습니다 `\foo` 다음과 같이:

`\foo{, World!}`

가 `Hey! Hello, World!`

다음을 해결할 수 있습니다 `\directlua`의 다음에 대한 처리 `#` 문자를 LuaTeX가 코드를 처리하기 전에 범주 코드를 임시로 바꿈으로써 해결할 수 있습니다. 예를 들면:

```
\begingroup
   \catcode`\#=11
   \directlua{
   local tbl = {}
   tbl[1] = "Hello"
   tbl[2] = "World"
   tex.print("Table length is "..#tbl)
}
\endgroup
```

그러면 다음 Lua 코드가 생성됩니다

```
local tbl = {} tbl[1] = "Hello" tbl[2] = "World" tex.print("Table length is "..#tbl)
```

그 결과 우리가 예상한 결과를 조판합니다:

`Table length is 2`

### % 문자 사용하기

TeX/LaTeX에서는 `%` 문자는 일반적으로 코드에 한 줄 주석을 넣는 데 사용됩니다. 즉, TeX 엔진에게 그 지점부터 해당 줄의 끝까지의 모든 내용을 무시하라고 알리는 역할을 합니다 `%` 가 적힌 줄의 끝까지 무시하라는 뜻입니다. 그러나 Lua 언어에서는 `%` 문자가 다음과 같은 매우 유용한 문자열 처리 함수들에서 사용됩니다: `string.format(...)`, `string.gmatch(...)`와 `string.gsub(...)` 이들에서 `%` 문자는 이러한 함수의 문법 일부로서 중요한 역할을 합니다.

TeX/LaTeX에서 사용될 때, `%` 범주 코드 14가 할당되어 있으므로 주석 문자로 작동합니다. 이를 일반 문자처럼 동작하게 하고 평소의 TeX/LaTeX 동작을 끄려면 범주 코드를 12와 같은 안전한 값으로 바꿔야 합니다. 아래 `\directlua` 예제는 이 글 앞부분에서 논의한 여러 기법과 아직 언급하지 않은 한 가지 기법을 함께 사용합니다: ``\catcode`\^^M=12``, 이를 통해 코드에서 Lua 주석을 사용할 수 있습니다. 이는 아래에서 설명합니다.

#### 예제

다음 예제들은 [lua-users.org](http://lua-users.org/wiki/StringLibraryTutorial)에서 가져왔으며, 다음 환경에서 사용하도록 적절히 수정되었습니다: `\directlua`.

```
\documentclass{article}
\begin{document}
\begingroup
\ttfamily
\let\\\relax
\catcode`\^^M=12 %<---we further explore this below!
\catcode`\%=12
\directlua{
   local str -- declare a local variable to hold the result

   tex.print("Using string.format():".."\\par")

   str=string.format("%s %q", "Hello", "Lua user!") -- string and quoted string
   tex.print(str.."\\par")
   str = string.format("%c%c%c", 76, 117, 97) -- char
   tex.print(str.."\\par")
   str=string.format("%e, %E", math.pi, math.pi) -- exponent
   tex.print(str.."\\par")
   str=string.format("%f", math.pi) -- float
   tex.print(str.."\\par")
   str=string.format("%g, %g", math.pi, 10^9) -- float or exponent
   tex.print(str.."\\par")
   str = string.format("%o, %x, %X", 99, 125, 125)  -- octal, hexadecimal, hexadecimal
   tex.print(str.."\\par")

   tex.print("\\vskip3mm".."Using string.gmatch():".."\\par")

   for word in string.gmatch("Hello TeX user", "%a+") do
      tex.print(word.."\\par")
   end

   tex.print("\\vskip3mm".."Using string.gsub():".."\\par")
   str=string.gsub("banana", "(an)", "%1-") -- capture any occurrences of "an" and replace
   tex.print(str.."\\par")
}
\endgroup
\end{document}
```

다음 스크린샷은 위 코드의 조판 결과를 보여줍니다:

![\directlua에서 Lua 문자열 함수 사용하기](/files/66a1729c3c42ab02bba4ad020838976a89c3c199)

## Lua 코드가 왜 한 줄로 표시되나요?

눈치채셨을지 모르지만, 이 글의 예제에 표시된 모든(생성된) Lua 코드 조각은 한 줄의 텍스트로 제시됩니다. 원래 `\directlua` 코드 조각에 있던 줄바꿈은 그대로 유지되지 않습니다. 왜 그럴까요? Lua 코드의 줄바꿈이 *제거되었기* 때문인데, 이는 LuaTeX의 전처리 과정에서 `\directlua`, 그 결과 Lua 코드가 하나의 긴 텍스트 줄이 됩니다. 이런 동작은 TeX 엔진이 줄 끝 문자를 처리하는 방식—즉, `\r` (캐리지 리턴)와 `\n` (라인 피드)로 표기되는 문자들을 다루는 방식에서 비롯됩니다. 이런 세부 사항을 왜 신경 써야 하는지는 Lua의 코드 주석 처리 메커니즘을 이야기할 때 분명해질 것입니다.

소프트웨어가 텍스트 파일을 기록(저장)할 때 각 텍스트 줄은 이른바 “newline” 문자로 끝납니다. 실제 newline 문자는 해당 파일을 기록하는 데 사용된 애플리케이션과 운영체제에 따라 달라집니다. Wikipedia에는 [흥미로운 글이](https://en.wikipedia.org/wiki/Newline) 있는데, 이는 오늘날 사용되는 newline 문자의 역사/진화를 살펴봅니다.

어떤 텍스트 파일이든 각 텍스트 줄은 캐리지 리턴(ASCII/Unicode 문자 13) 및/또는 라인 피드(ASCII/Unicode 문자 10)라 불리는 다양한 문자 조합으로 끝날 수 있으며, 이는 `\r` 및 `\n` 로 각각 표시됩니다. TeX 엔진은 플랫폼 독립적으로 설계되었기 때문에 텍스트 파일에서 사용되는 줄 끝의 본질적으로 플랫폼 의존적인 특성을 우회할 방법이 필요합니다. 당연히 TeX 엔진에는 줄 종료 문자를 다루는 내장(하지만 설정 가능한) 방법이 있습니다.

### TeX 엔진이 줄 끝을 처리하는 방법

LuaTeX가 처리할 때 `\directlua{⟨code⟩}` 그 안에 포함된 텍스트를 읽고 `⟨code⟩` 그리고 귀하의 안에 포함된 모든 줄 끝을 처리하기 위해 표준 TeX 엔진 방식을 적용합니다 `⟨code⟩`. 기본적으로 이러한 표준 TeX 방식은 모든 줄 종료 문자(캐리지 리턴과 라인 피드)를 제거하고 공백 문자로 대체합니다. “기본적으로”라고 말하는 이유는 TeX 엔진의 줄 종료 문자 처리가 사용자가 설정할 수 있는 다음 매개변수를 통해 수정될 수 있기 때문입니다: `\endlinechar`. 여기서는 간단한 두 단계 개요를 제공하겠지만, 더 자세한 내용은 Overleaf 글에서 확인할 수 있습니다 [\endlinechar 소개: TeX가 텍스트 파일에서 줄을 읽는 방법](/latex/ko/in-depth-articles/05-an-introduction-to-endlinechar-how-tex-reads-lines-from-text-files.md).

#### 1단계: TeX가 자체 줄 끝 문자를 삽입합니다

입력 파일에서 텍스트 한 줄을 읽은 뒤, TeX 엔진은 즉시 해당 줄 끝의 `\r` 또는 `\n` 문자들을 제거합니다. 다음으로, TeX 엔진은 *삽입합니다* 그 줄의 끝에 자체 줄 종료 문자를 (되돌려) 추가합니다. 그 문자는 사용자가 설정할 수 있는 다음 TeX 매개변수의 값으로 결정됩니다: `\endlinechar` 그리고 이 메커니즘을 통해 TeX 엔진은 줄 끝 문자를 플랫폼 독립적인 방식으로 처리할 수 있습니다. 즉, 입력 텍스트 파일에 원래 무엇이 들어 있었는지와 무관하게 줄 끝 문자를 선택하고 설정합니다.

일반적으로 TeX 엔진은 다음 설정을 사용합니다

`\endlinechar=13`

이는 캐리지 리턴 문자(`\r`). 하지만 사용자는 언제든지 다른 문자 코드를 다음의 값으로 지정할 수 있습니다 `\endlinechar`—이는 이 글의 뒤에서 살펴보겠습니다.

따라서 귀하의 안에 포함된 모든 줄 종료 문자(들)는 `⟨code⟩` 가 처리하는 동안 `\directlua{⟨code⟩}` 제거되어 TeX 엔진 자체가 정한 하나의 문자로 대체됩니다. TeX 엔진은 파일에서 새 텍스트 줄을 읽은 직후 이 줄 끝 처리를 수행하고, *이전에* 해당 줄의 문자들을 처리하여(토큰을 생성하기 위해) 작업합니다. 하지만 이야기는 여기서 끝나지 않습니다. TeX 엔진이 *는* 그 줄 끝 문자들(이미 삽입한 것)을 어떻게 처리하는지가 Lua 코드가 왜 하나의 한 줄이 되는지를 설명합니다.

#### 2단계: TeX가 줄 끝 문자를 공백으로 변환합니다

자체 줄 종료 문자를 삽입하는 것 외에도, 그 값으로 정의되는 `\endlinechar`를 사용하며, TeX 엔진은 또한 다음처럼 다뤄져야 하는 문자에 범주 코드 5를 사용합니다 *로 취급될* 줄 끝 문자입니다. 그 결과 TeX 엔진은 보통 다음과 같이 동작합니다:

1. 다음으로 정의된 줄 끝 문자와 `\endlinechar`;
2. 동일한 그 문자에 *보통* 범주 코드 5가 할당됩니다.

TeX가 그 줄 끝 문자에 대해 하는 일이 Lua 코드가 한 줄로 되는 이유를 설명합니다. TeX 엔진이 입력 줄을 처리할 때 결국 그 줄의 마지막 문자인, 다음으로 정의된 문자를 감지합니다: `\endlinechar`. 보통 그 문자는 범주 코드 5를 가지며, 이로 인해 TeX는 *이를* 공백 문자로 바꿉니다. 즉, 줄 끝에서 TeX은 사실상 줄 종료 문자를 제거하고 공백으로 대체합니다. 참고로 TeX 엔진은 범주 코드 5를 가진 문자를 사용해 빈 줄을 감지하고 새 문단을 시작하기도 하지만, 여기서는 다루지 않겠습니다.

물론 TeX답게, 다음을 다시 설정함으로써 온갖 특수 매크로 프로그래밍 기법을 사용할 수 있습니다 `\endlinechar` 를 다른 문자로 바꾸거나, 그리고 다음에 할당된 문자에 `\endlinechar` 원하는 범주 코드 값을 부여할 수 있습니다.

Lua 코드가 하나의 텍스트 줄이 되는 것을 막고 싶다면, 다음에 할당된 값을 (임시로) 바꾸거나 `\endlinechar` 또는 표준 줄 끝 종결 문자의 범주 코드를 바꾸면 됩니다 `\r`.

### TeX의 기묘한 ^^ 표기법

다음 섹션들에서 TeX의 특이한 `^^` 표기법을 만나게 될 것입니다. 이는 “확장 문자 메커니즘”으로 알려져 있습니다. Knuth가 줄 끝 종결자, 탭 등과 같은 “제어 문자”를 입력하기 쉽게 만들기 위해 고안한 것입니다. 예를 들면:

* `^^J` 는 문자 코드 10(`\n`, 라인 피드)을 나타냅니다;
* `^^M` 는 문자 코드 13(`\r`, 캐리지 리턴)을 나타냅니다.

다음과 같은 문자 시퀀스 `^^M` 는 TeX의 입력 스캐닝 과정 초기에, 즉 TeX가 대응하는 문자 토큰을 생성하기 위해 입력 문자를 읽는 동안 해당 문자 코드로 변환됩니다.

### \endlinechar에 할당된 문자를 바꾸기

여전히 다음의 확장을 막아야 한다는 점을 기억하면서 `~` 문자를, 다음과 같이 쓸 수 있습니다

```
\begingroup
\endlinechar=10 % 줄 끝 문자를 \n으로 변경
\directlua{
   local x=3
   if x \noexpand~= 4 then
   print("x is not equal to 4")
   end
}% 여기서는 \n이 나타나길 원하지 않습니다
\endgroup% 또는 여기에도 \n이 나오면 안 됩니다
```

위의 설정은 `\endlinechar` LuaTeX가 문자 코드 10(`\n`, 라인 피드)을 읽어들인 각 줄의 끝에 덧붙이게 합니다. 이렇게 하는 이유는 `\n` (라인 피드)가 보통 범주 코드 12를 가지며, 이는 다음을 써서 확인할 수 있기 때문입니다 ``\the\catcode`\^^J``. 왜냐하면 `\n` 는 범주 코드 5를 가지지 않으므로 LuaTeX가 이를 공백 문자로 변환하지 않고, LuaTeX가 읽어들인 모든 줄의 끝에 그대로 남습니다. 그 결과 코드 10을 가진 문자가 각 줄 끝에 남아, 다음이 만드는 토큰 목록에 그대로 들어가게 됩니다: `\directlua` 그리고 토큰 목록이 텍스트로 변환되면 Lua 코드에서 다시 나타납니다. 위와 같이 바꾸면 Lua 코드는 다음 문자 시퀀스로 Lua 인터프리터에 전달됩니다:

**\n**local x=3\*\*\n**if x \~= 4 then**\n**print("x is not equal to 4")**\n**end**\n\*\*

여기서 **\n** 표기법은 문자 코드 10을 나타냅니다 *실제 데이터 값을 포함하지* 어떤 알 수 없는 매크로 `\n`. 이제 Lua 인터프리터는 코드의 줄바꿈을, 원래 `\directlua` 명령:

```
   local x=3
   if x ~= 4 then
   print("x is not equal to 4")
   end
```

참고로 Lua 코드 문자열의 맨 처음 문자는 `\n` (다음 앞의 `로컬` 키워드). 이 `\n` 는 다음 줄에서 비롯됩니다

`\directlua{`

여는 문자 바로 뒤에 줄바꿈이 있기 때문이며 `{` 이것도 그대로 보존됩니다. 이를 막으려면 다음과 같이 쓸 수 있습니다

`\directlua{%`

### Changing the category code of \r

Lua 코드의 줄바꿈을 유지하려면 다음의 범주 코드도 바꿀 수 있습니다 `\r` 를 5가 아닌 다른 값으로 바꾸면 `\r` 는 더 이상 줄 끝 문자로 인식(취급)되지 않습니다. 이 기법을 사용하면 LuaTeX는 여전히 `\endlinechar=13` 를 사용하며 계속해서 `\r` 를 각 줄 끝에 추가합니다. 그러나 `\r` 이 더 이상 범주 코드 5를 가지지 않으므로 LuaTeX는 `\r` 문자를 줄 끝으로 인식하지 않습니다. 공백으로 변환하지 않고 그대로 통과시켜 Lua 코드에 나타나게 합니다.

여전히 다음의 확장을 막아야 한다는 점을 기억하면서 `~` 문자를, 다음과 같이 쓸 수 있습니다

```
\begingroup
\catcode`\^^M=12 % \r의 범주 코드를 12로 변경
\directlua{
   local x=3
   if x \noexpand~= 4 then
   print("x is not equal to 4")
   end
}
\endgroup
```

이 경우 Lua 코드는 다음과 같이 Lua 인터프리터에 전달됩니다:

**\r**local x=3\*\*\r**if x \~= 4 then**\r**print("x is not equal to 4")**\r**end**\r\*\*

여기서 `\r` 표기법은 어떤 알 수 없는 매크로가 아니라 문자 코드 13을 나타내기 위한 것입니다 `\r`. 앞의 `\endlinechar` 예제와 마찬가지로, Lua 인터프리터는 이제 코드의 줄바꿈을 원래 `\directlua` 명령:

```
   local x=3
   if x ~= 4 then
   print("x is not equal to 4")
   end
```

참고로 다시 말하면 Lua 코드 문자열의 맨 처음 문자는 `\r` (local 키워드 앞에 있습니다): 이것도 다음 줄에서 비롯됩니다

`\directlua{`

#### 왜 \r에는 범주 코드 12를 사용하고 11은 사용하지 않았을까요?

그 이유는 다음으로 인해 유발되는 오류를 실수로 도입할 위험 때문입니다 `\r` (범주 코드 11)을 입력 파일에서 읽어들인 TeX/LaTeX 명령의 끝에 덧붙이는 경우입니다. 다음 예를 보겠습니다:

```
\begingroup
\catcode`\^^M=11 % \r의 범주 코드를 11로 변경
\directlua{
   local x=3
   if x \noexpand~= 4 then
   print("x is not equal to 4")
   end
}
\endgroup
```

그 결과 오류가 발생합니다:

```
   ! Undefined control sequence.
   l.9 \endgroup
```

어떻게 그럴 수 있을까요? 왜냐하면 `\endgroup` 은 표준 TeX 기본 명령인데요? 오류의 원인은 꽤 미묘합니다. LuaTeX가 마지막 텍스트 줄—즉 다음을 포함한 줄—을 읽을 때 `\endgroup`—LuaTeX는 또한 다음을 덧붙였습니다 `\endlinechar` 문자가 없으면 `\r` 를 그 줄의 끝에. 이제 LuaTeX의 메모리 안에서는 다음 문자 시퀀스가 보입니다

`\endgroup\r`

여기서 우리는 `\r` 을 사용해 코드 13의 문자를 나타냅니다. 이는 어떤 알 수 없는 TeX 매크로의 이름이 아닙니다 `\r`.

LuaTeX가 이 줄을 텍스트 파일에서 읽을 당시 원래의 `\begingroup` 는 아직 유효합니다. 우리는 아직 대응하는 `\endgroup` 명령을 실행해 닫히지 않은 그룹 안에 있습니다. 이 명령은 `\r` 를 이전의 범주 코드 값 5로 되돌릴 것입니다.

LuaTeX가 텍스트 줄에서 처리(토큰 생성)를 시작하면 `\endgroup\r` 첫 번째 문자를 인식합니다 `\` 를 이스케이프 문자로 인식하여 LuaTeX가 명령 이름을 찾기 시작하게 합니다. 명령 이름을 식별하기 위해 LuaTeX는 범주 코드 11을 가진 문자 시퀀스를 찾는데, 하지만 `\r` 도 범주 코드 11을 가지므로 LuaTeX는 `\r` 문자(여전히 범주 코드 11)가 *명령의 일부를 이룬다고* 이름이 `\endgroup\r` 로 된 명령이 있다고 생각하지만, 물론 그런 명령은 없으므로 LuaTeX는 `정의되지 않은 제어 시퀀스` 오류를 보고합니다. 그래서 11이 아니라 범주 코드 12를 사용한 것입니다.

LuaTeX의 오류 메시지가 콘솔에 출력되었기 때문에 우리는 다음을 쉽게 보고/인지할 수 없었습니다 `\r` 문자를, 그래서 무엇이 오류를 일으켰는지 분명하지 않았습니다.

### 왜 줄 끝을 신경 쓰는 걸까요?

그 이유는 코드에서 Lua의 주석 방식을 사용할 수 있게 하기 위해서입니다! LuaTeX의 표준 메커니즘으로 다음을 추가하여 `%` 문자를 더해 코드의 한 줄을 주석 처리할 수 있지만, Lua 언어에는 자체적인 매우 유용한 *여러 줄* 주석 처리 메커니즘이 있어 이를 활용하고 싶을 수 있습니다.

줄바꿈 문제를 해결하지 않고 Lua 언어의 한 줄 주석을 사용해 보려고 하면 무슨 일이 일어나는지부터 살펴보겠습니다. TeX는 `%` 문자를 사용해 코드 한 줄을 주석 처리하는 반면, Lua는 이중 하이픈을 사용합니다: `--`.

이것을 실행해 보면 어떻게 될까요:

```
\directlua{
   local x=3
   if x \noexpand~= 4 then
   -- I'm going to output the result of this complex test
   print("x is not equal to 4")
   end
}
```

오류가 발생합니다:

`[\directlua]:1: 'end' expected near <eof>`

이 오류는 인터프리터에 전달된 Lua 코드에 줄바꿈이 없기 때문에 발생합니다. 인터프리터는 오직 하나의 연속된 문자열만 보며, 그 문자열의 중간쯤에서 주석이 시작되는 것으로 인식합니다:

```

local x=3 if x ~= 4 then -- I'm going to output the result of this complex test print("x is not equal to 4") end
```

다음 이후의 모든 것은 `**local x=3 if x ~= 4 then**` 주석 처리된 것으로 간주되므로, 인터프리터는 불완전한 Lua 코드 조각을 보게 되고 그 결과 다음 오류가 발생합니다

`'end' expected near <eof>`.

여기서 `<eof>` 은 파일 끝을 의미합니다.

이미 짐작하셨겠지만, 줄바꿈이 결과 Lua 코드로 전달되도록 보장해야 하며, 이는 예를 들어 다음의 범주 코드를 바꿈으로써 해결할 수 있습니다 `\r` 를 12로:

```
\begingroup
\catcode`\^^M=12 % \r의 범주 코드를 12로 변경
\directlua{
   local x=3
   if x \noexpand~= 4 then
   -- 이 복잡한 테스트의 결과를 출력할 예정입니다
   print("x is not equal to 4")
   end
}
\endgroup
```

이제 Lua 인터프리터는 문자열을 보지만, 그 안에는 `\r` 그 안에 적힌 줄바꿈 `\directlua` 조각:

**\r**local x=3\*\*\r**if x \~= 4 then**\r\*\*-- 이 복잡한 테스트의 결과를 출력할 예정입니다\*\*\r**tex.print("x is not equal to 4")**\r**end**\r\*\*

이는 사실상 다음과 같이 작성하는 것과 같습니다

```
   local x=3
   if x \noexpand~= 4 then
   -- 이 복잡한 테스트의 결과를 출력할 예정입니다
   print("x is not equal to 4")
   end
```

즉 Lua가 이 코드를 올바르게 처리하고 주석 처리한 줄은 무시할 수 있다는 뜻입니다.

**블록 주석**

Lua 언어는 또한 다음과 같은 구문을 지원합니다 [“블록 주석”](https://www.lua.org/pil/1.3.html) (또는 *긴 주석*): 이것들은 다음으로 시작합니다 `--[[` 그리고 해당하는 것이 나올 때까지 유효합니다 `]]`. 이 편리한 구문을 사용하면 여러 줄 주석을 작성하거나, 일시적으로 제거하고 싶은 코드 부분을 주석 처리할 수 있습니다:

```
\begingroup
\catcode`\^^M=12 % \r의 범주 코드를 12로 변경
\directlua{
   local x=3
   if x \noexpand~= 4 then
   --[[ 이 복잡한 테스트의 결과를 출력할 예정입니다
   단순히 정말 그렇기 때문입니다
   그만큼 놀라운 결론이기 때문입니다]]
   print("x is not equal to 4")
   end
}
\endgroup
```

## 결론적으로

먼저, 이 방대한 글을 끝까지 읽어내신 것을 축하드립니다! 우리는 LuaTeX를 통해 최대한 활용하는 데 필요한 배경을 제공하는 TeX 관련 개념과 주제에 대한 비교적 포괄적인 가이드를 만들고자 했습니다. 다음을 통해 `\directlua` 명령어. 이 글이 유익하고 Overleaf 사용자 커뮤니티와 그 밖의 사람들에게도 유용하고 가치 있는 무언가를 보탤 수 있기를 바랍니다. 늘 그렇듯 의견을 받게 되어 기쁘니, 주저하지 말고 [문의해 주세요](https://www.overleaf.com/contact) 이 글에 대한 의견이나, 앞으로 우리가 다뤄주었으면 하는 추가 주제에 대한 제안을 보내주세요.

즐거운 $$\text{Lua}\mathrm{\TeX}\text{-ing!}$$ Graham Douglas와 Overleaf 팀이 전합니다.

### 그리고 마지막으로... 그냥 luacode 패키지를 사용하세요

TeX와 Lua는 근본적으로 서로 다른 방식으로 동작하지만, 두 언어는 각각의 맥락에서 “특수한 의미”를 지니는 여러 문자를 공유합니다—예를 들면 \\\\, %, \~, #, ^, &—물론 Lua와 TeX는 이러한 특수한 의미를 *매우* 각기 다른 목적으로 사용합니다. 문제가 되는 문자들을 살펴보면 왜 어려움이 생길 수 있는지와 그것을 어떻게 해결할 수 있는지 알 수 있습니다. 그러나 작은 Lua 코드 조각들을 일일이 수동으로 고치는 일은 꽤 번거로울 수 있으므로, 대부분의 사용자는 그런 어려움을 없애 주는 LaTeX 패키지를 사용하길 선호합니다. 그런 패키지 중 하나가 [`luacode`](https://ctan.org/pkg/luacode?lang=en) 다음 작업을 쉽게 하도록 설계된 여러 기능을 제공합니다 `\directlua`하지만 적어도 이제는 이러한 문제를 더 잘 이해하게 되었을 것입니다 `luacode` 을 대신 해결해 줍니다.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://overleaf-pro.ayaka.space/latex/ko/in-depth-articles/09-an-introduction-to-luatex-part-2-understanding-directlua.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
