Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

和文文字のコントロール・シンボルと \DeclareRobustCommand #3

Closed
wtsnjp opened this issue Nov 15, 2017 · 15 comments
Closed

Comments

@wtsnjp
Copy link

wtsnjp commented Nov 15, 2017

ZR さんのブログ記事で指摘されていた件です.

% uplatex, xelatex, lualatex で実行
\documentclass[autodetect-engine,a4paper]{bxjsarticle}
\usepackage{scsnowman}
% ☃のマフラーは赤色
\DeclareRobustCommand*{\☃}{\scsnowman[%
  hat,arms,buttons,snow,muffler=red,scale=1.3]}
\DeclareRobustCommand*{\!}{!}
\begin{document}
% 目次
\tableofcontents

% 本文
\section{{\TeX}とは何か}
アレ。
\section{\☃とは何か}
非アレ。
\section{\!とは何か}
非ナントカ。
\end{document}

上記の共通ソースを LuaLaTeX, XeLaTeX で実行した場合,aux ファイルには

\relax 
\@writefile{toc}{\contentsline {section}{\numberline {1}{T\kern -.1667em\lower .5ex\hbox {E}\kern -.125emX\bxjs@SE {}}とは何か}{1}}
\@writefile{toc}{\contentsline {section}{\numberline {2}\☃とは何か}{1}}
\@writefile{toc}{\contentsline {section}{\numberline {3}\!とは何か}{1}}

が書き込まれます.一方 upLaTeX で実行した場合は

\relax 
\@writefile{toc}{\contentsline {section}{\numberline {1}{T\kern -.1667em\lower .5ex\hbox {E}\kern -.125emX\bxjs@SE {}}とは何か}{1}}
\@writefile{toc}{\contentsline {section}{\numberline {2}\☃  とは何か}{1}}
\@writefile{toc}{\contentsline {section}{\numberline {3}\!とは何か}{1}}

となり \☃ の後ろに余分な(無視されない)空白が入り,これが結果的に目次の表示をおかしくします.

この違いが生じる理由ですが,調べた限り XeLaTeX や LuaLaTeX が特別な処理を加えているわけではなく,例の記事に北川さんがコメントなさっているように「pTeX 系エンジンが単文字コントロール・シークエンスを特殊扱いしない」という仕様からくる挙動のようです.

@wtsnjp wtsnjp changed the title 和文文字のコントロール・シンボルと \\DeclareRobustCommand 和文文字のコントロール・シンボルと \DeclareRobustCommand Nov 15, 2017
@wtsnjp
Copy link
Author

wtsnjp commented Nov 15, 2017

以下は個人的な意見です.

この問題に upLaTeX の範疇で(latex.ltx の \DeclareRobustCommand の定義を上書きするなどの方法で)特別に対策を講じることは原理上は可能だと思いますが,それがまた別の問題の原因とならない保証はありません(つい最近も texjporg/platex#55 のようなバグが発生しました).

この件についてはほとんどの場合 TeX 言語レベルでの回避はそれほど難しくないと考えられるので(例えば,和文文字のコントロール・シンボルを robust なコントロール・ワードの下請け命令に展開されるマクロにしてしまう回避策が考えられると思います)pTeX 系エンジンの仕様にしたがった upLaTeX の仕様ということにしてもいい気がしています.

@aminophen
Copy link
Member

aminophen commented Nov 15, 2017

「pTeX 系エンジンが単文字コントロール・シークエンスを特殊扱いしない」という仕様

細かいですがここは “2バイト以上の単文字” が正確そうですね。また,例がたまたま☃️なだけで,この現象自体は 「 のような “記号扱いされる文字” なら何でも起きる(pLaTeX でも起きる)と思います。

あと,一連の議論で出てくる単語が混乱するので確認したいのですが,(edit: 修正)

「コントロール・ワード」が(1文字か2文字以上かにかかわらず)\catcode = 11 の文字からなる命令

「コントロール・シンボル」が \catcode = 11 以外の文字(“記号扱いされる文字”)からなる命令(これは事実上1文字命令ばかり)

で,両方合わせて「コントロール・シークエンス」 = 「制御綴」か。

@aminophen
Copy link
Member

ちなみに TeX 言語レベルでの回避については,一番難しいのが「和文文字のコントロール・シンボルかどうかの判定」だろうと思うので,マクロレベルでの対処はしたくないです…。

@wtsnjp
Copy link
Author

wtsnjp commented Nov 15, 2017

コントロール・ワード,コントロール・シンボル,コントロール・シークエンスの定義について,アミノフェンさんの認識と一致しています.

「TeX 言語レベルでの回避」というのは,私は “2バイト以上の単文字” のコントロール・シンボルを使う upLaTeX ユーザ側の話をしていました.

upLaTeX のマクロレベルで対応するのは,おそらく ad-hoc なコードにならざるを得ないと思うので私も反対です.

@aminophen
Copy link
Member

(まちがって投稿ボタンを押したのでもう一回)

あと一点,よくわかっていないのですが,

「pTeX 系エンジンが単文字コントロール・シークエンスを特殊扱いしない」という仕様

から今回の挙動が直接の帰結として導けていないのですが,どういうことなのでしょうか? 実際 LuaTeX は(最近の \pdfprimitive のバグ (texjporg/tex-jp-build#29) から無縁だったように)単文字の命令を特別扱いしていませんが,それでも今回の件では問題が起きていないですよね。

@aminophen
Copy link
Member

aminophen commented Nov 15, 2017

あと一点,よくわかっていないのですが,

もう少し具体的に書くと,「pTeX 系エンジンが単文字コントロール・シークエンスを特殊扱いしない」ことと「pTeX 系での以下の結果が XeTeX/LuaTeX と一致していない」ことが,私の中でまだ直接には結びついていないんですよね。

% plain TeX ソース
\def\+{A}
\def\+{A}
\def\X{\+}
\def\Y{\+}
% 記号類扱い → この時の挙動が問題
\ifx\kanjiskip\undefined
  \catcode`\+=12
  \catcode`\+=12
\else
  \catcode`\+=12
  \kcatcode`+=18
\fi
\show\X\relax\message{(\meaning\X)}
\show\Y\relax\message{(\meaning\Y)}
% 普通の文字扱い
\ifx\kanjiskip\undefined
  \catcode`\+=11
  \catcode`\+=11
\else
  \catcode`\+=11
  \kcatcode`+=17
\fi
\show\X\relax\message{(\meaning\X)}
\show\Y\relax\message{(\meaning\Y)}
\end

@zr-tex8r
Copy link

自分は次のように把握しています:

  • 「制御記号の文字列化の仕様」を和文に拡張することについて、きちんと考慮していなかった。(推定;これが“原因”)
  • 従って、TeXの内部実装がそのまま適用された。
  • 和文の単文字の制御綴は内部実装での「単文字の特別扱い」の対象にならないため、欧文と異なる動作になった。
    ※和文の制御綴名は内部漢字コードのバイト列で保持されるので、そもそも単文字(単一バイト)にならない。

@zr-tex8r
Copy link

zr-tex8r commented Nov 16, 2017

改めて整理しておきます。

【用語】

  • 英字(letter): カテゴリコード11の文字(“文字トークン”ではない)。
    • pTeX系ではカテゴリコード16、17、19の和文文字を含む。
  • 制御語(control word): 非空の英字列の名前をもつ制御綴。
  • 制御記号(control symbol): 単一の非英字の名前をもつ制御綴。
  • 制御空白(control space): 単一のASCII空白文字の名前をもつ制御綴(制御記号の一種)。
    ※制御語でも制御記号でもない制御綴が存在する。

【問題】

  • 欧文(pTeX系の和文以外の全て)については制御記号は特別な振舞をする:
    1. 字句解析でエスケープ文字により制御綴を構成する場合、制御記号は(名前が必ず1文字で終結するため)後続の空白文字が吸収されない。※制御空白は例外
    2. トークン列の(=\string以外の)文字列化の際に、制御記号については「後に空白文字を補う」挙動が抑止される。
    3. iとiiより、再トークン化(文字列化して再び字句解析する)した場合に、制御語と制御記号については必ず元に戻る。
  • pTeX系の和文の制御記号について、上記の“特別な振舞”は:
    • iは適用される。
    • 一方で、iiは適用されない。つまり、和文の制御記号の文字列化は空白文字が補われる。
    • このため、和文の制御記号は再トークン化した場合に完全には元に戻らない。

@zr-tex8r
Copy link

zr-tex8r commented Nov 16, 2017

ところで、最近はLaTeXレベル保護(\DeclareRobustCommand)の代わりにe-TeXのエンジンレベル保護(\protected)が使われることが多くなっているため、「仕様をどうするべきか」を考えるのであれば、後者についても留意すべきです。

エンジンレベル保護の場合、エンジンでの文字列化の仕様が(マクロを介さずに)直接絡んでくることになります。結局、「LaTeXで頑強な命令を作る」ためにエンジンレベル保護を使うのは、和文制御記号に関しては原理的に不可能ということになります。(エンジンの仕様を変えるのは非現実的だろう。)

このことを考慮に入れると、LaTeXレベルの保護(\DeclareRobustCommand)だけ対処する、というのは得策ではないと思えます。

@t-tk
Copy link
Collaborator

t-tk commented Nov 16, 2017

  • 「制御記号の文字列化の仕様」を和文に拡張することについて、きちんと考慮していなかった。(推定;これが“原因”)
  • 従って、TeXの内部実装がそのまま適用された。
  • 和文の単文字の制御綴は内部実装での「単文字の特別扱い」の対象にならないため、欧文と異なる動作になった。
    ※和文の制御綴名は内部漢字コードのバイト列で保持されるので、そもそも単文字(単一バイト)にならない。

この通りだと思います。
和文の単文字の制御綴の取り扱いについて、upTeX では、pTeX の実装をそのまま受け継いでいるはずです。
(u)pTeX で、「トークン列の(=\string以外の)文字列化の際に、制御記号については「後に空白文字を補う」挙動が抑止される。」が出来るようになれば良いのでしょうか?

@wtsnjp
Copy link
Author

wtsnjp commented Nov 16, 2017

ZR さんのまとめてくださった【問題】を読む限り(そして私の知識の範囲では)そのように思えます.

もし今後,pTeX 系列エンジンの「トークン列の文字列化」に関わる仕様を変更することを議論するのであれば,現状の

和文の制御記号は再トークン化した場合に完全には元に戻らない

という性質を利用(悪用?)しているケースがないかどうかを調べておいた方が良さそうです.

@zr-tex8r
Copy link

自分の考えとしては
「『気をつけよう!』で済ませればよい」
なんですけど、どうやらLaTeXユーザにも影響がある(つまり“気をつける”必要がある)らしいです。というのも、\protect も同じ問題を抱えているからです。

% pLaTeX文書
\documentclass[a4paper]{jsarticle}
\usepackage{otf}
\newcommand*{\☆}{\UTF{2603}}
\begin{document}
\tableofcontents
\section{{\TeX}と\protect\☆の関係}
一切ありません。
\end{document}

@aminophen
Copy link
Member

LaTeXレベル保護(\DeclareRobustCommand)
e-TeXのエンジンレベル保護(\protected)
\protect も同じ問題を抱えている

いずれも tex.web l.5586 あたりの関数 print_cs

@<Basic printing...@>=
procedure print_cs(@!p:integer); {prints a purported control sequence}
begin if p<hash_base then {single character}
  if p>=single_base then
    if p=null_cs then
      begin print_esc("csname"); print_esc("endcsname"); print_char(" ");
      end
    else  begin print_esc(p-single_base);
      if cat_code(p-single_base)=letter then print_char(" ");
      end
  else if p<active_base then print_esc("IMPOSSIBLE.")
@.IMPOSSIBLE@>
  else print(p-active_base)
else if p>=undefined_control_sequence then print_esc("IMPOSSIBLE.")
else if (text(p)<0)or(text(p)>=str_ptr) then print_esc("NONEXISTENT.")
@.NONEXISTENT@>
else  begin print_esc(text(p));
  print_char(" ");
  end;
end;

の分岐で,最後の else に落ちるので print_char(" "); されるという「根っこ」は同じですね。

@aminophen
Copy link
Member

和文のコントロールシンボル

pTeX だけを考慮したコードになっていますが,ちょっと ptex-base.ch をいじってみました。今後は upLaTeX の issue ではないと思いますので,より適切そうな texjporg/tex-jp-build#37 に飛びます。

@aminophen
Copy link
Member

aminophen commented Dec 1, 2017

和文の制御記号は再トークン化した場合に完全には元に戻らない
という性質を利用(悪用?)しているケースがないかどうかを調べておいた方が良さそうです.

和文の制御記号 (control symbol) の表示変更を r45950 でコミットしたので,ビルドできる方(または W32TeX [2017/12/01] 以降を使える方)は試していただき,上記のようなケースが見当たらないか調べていただけると助かります。

# abenori さんの jlreq.cls のように,従来から「制御記号の後の空白を吸収するため \ignorespaces する」というような工夫していたケースは問題ないはずだと思っています。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants