[インデックス 134] ファイルの概要
このコミットは、Goコンパイラの字句解析器(lexer)におけるエスケープ文字の処理に関する変更です。具体的には、escchar
関数のシグネチャが変更され、エスケープされた文字がバイトとして扱われるべきかどうかのフラグが追加されました。これにより、文字列リテラルや文字リテラル内のエスケープシーケンスの解釈がより正確になります。
コミット
commit f9c58c25e0aabf8420aadf12dafd507f97245de6
Author: Ken Thompson <ken@golang.org>
Date: Sun Jun 8 19:02:27 2008 -0700
more nihan
SVN=121622
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/f9c58c25e0aabf8420aadf12dafd507f97245de6
元コミット内容
more nihan
SVN=121622
変更の背景
コミットメッセージの「more nihan」は、Go言語の共同開発者の一人であるKen Thompson氏によるものです。Web検索の結果によると、「nihan」はKen Thompson氏が提供した「nih.a」というファイルに由来し、「not invented here」(ここで発明されたものではない)の略であるとされています。これは、Thompson氏の有名なチューリング賞受賞講演「Reflections on Trusting Trust」で示された、コンパイラにバックドアを仕込む可能性に関する概念と関連しています。
この文脈から推測すると、このコミットはGoコンパイラの初期開発段階において、字句解析器の堅牢性や正確性を向上させるための継続的な作業の一部であると考えられます。特に、エスケープ文字の処理は、文字列や文字リテラルの正確な解釈に不可欠であり、セキュリティや言語仕様の厳密な遵守において重要な側面です。escchar
関数の変更は、エスケープシーケンスが単なる数値としてではなく、バイトとして扱われるべきかどうかのセマンティクスを明確にする必要があったことを示唆しています。これは、Go言語がUnicodeをネイティブにサポートし、UTF-8エンコーディングを前提としているため、文字とバイトの区別が重要になる背景があります。
前提知識の解説
- Goコンパイラ: Go言語のソースコードを機械語に変換するプログラムです。初期のGoコンパイラはKen Thompson氏によって開発され、C言語で書かれていました。
- 字句解析器(Lexer/Scanner): コンパイラの最初の段階であり、ソースコードをトークン(意味のある最小単位)のストリームに変換する役割を担います。例えば、
"hello"
という文字列は、STRING
トークンとhello
という値に変換されます。 - エスケープ文字/シーケンス: プログラミング言語において、特殊な意味を持つ文字(例: 改行、タブ、引用符など)を文字列リテラルや文字リテラル内で表現するために使用される特殊な文字の組み合わせです。通常、バックスラッシュ(
\
)で始まります(例:\n
、\t
、\"
、\xNN
、\uNNNN
)。 - Rune: Go言語におけるUnicodeコードポイントを表す型です。Goの文字列はUTF-8でエンコードされたバイトのシーケンスですが、
rune
は単一のUnicode文字を表します。Runeself
は、UTF-8で1バイトで表現できるUnicodeコードポイントの最大値(U+007F、ASCII範囲)を指す定数であると推測されます。 vlong
: 64ビット整数型(long long
に相当)であると推測されます。エスケープシーケンスによって表現される数値(文字コードなど)を格納するために使用されます。remal
:realloc
のようなメモリ再割り当て関数であると推測されます。字句解析中に文字列バッファのサイズを動的に調整するために使用されます。
技術的詳細
このコミットの主要な変更点は、escchar
関数のシグネチャと、それに関連するlex.c
内のロジックの修正です。
-
escchar
関数のシグネチャ変更:src/cmd/gc/go.h
において、escchar
関数のプロトタイプが以下のように変更されました。 変更前:int escchar(int, vlong*);
変更後:int escchar(int, int*, vlong*);
新しい引数int *escflg
が追加されました。このescflg
は、エスケープされた文字が「バイト」として扱われるべきか(例:\xNN
や\NNN
のような16進数/8進数エスケープ)、それとも通常のUnicodeコードポイントとして扱われるべきかを示すフラグとして機能します。 -
lex.c
におけるescchar
の呼び出しとロジックの変更:yylex
関数内で、文字列リテラル(caseq
)と文字リテラル(case '\''
)の解析において、escchar
の呼び出しが新しいシグネチャに合わせて変更されました。 例:escchar('"', &v)
からescchar('"', &escflag, &v)
へ。escchar
関数内で、escflg
ポインタが初期化され、\x
(16進数エスケープ)や8進数エスケープ(\0
〜\7
で始まるもの)が検出された場合に*escflg = 1;
が設定されるようになりました。これは、これらのエスケープシーケンスがバイト値を表すことを示します。yylex
のcaseq
(文字列リテラル解析)ブロック内で、escflag
の導入により、エスケープされた文字の処理ロジックが変更されました。 変更前は、v >= Runeself
(Unicode文字として扱われるべき場合)とそれ以外で処理が分かれていました。 変更後は、escflag || v < Runeself
という条件が導入されました。escflag
が真の場合(つまり、\xNN
や\NNN
のようなバイトエスケープの場合)、その値v
は直接バイトとして文字列バッファに追加されます。escflag
が偽で、かつv < Runeself
の場合(つまり、ASCII範囲内の通常の文字の場合)、その値v
も直接バイトとして文字列バッファに追加されます。escflag
が偽で、かつv >= Runeself
の場合(つまり、ASCII範囲外のUnicode文字の場合)、v
はrune
として扱われ、runelen
とrunetochar
を使ってUTF-8エンコードされたバイトシーケンスに変換されてから文字列バッファに追加されます。
この変更により、字句解析器はエスケープシーケンスのセマンティクスをより正確に区別できるようになりました。特に、\xNN
や\NNN
のようなエスケープが、Unicodeコードポイントではなく、単一のバイト値として扱われるべきであることを明示的に示すことができるようになりました。これは、Go言語が文字列をUTF-8バイトのシーケンスとして扱うという設計思想と整合しています。
コアとなるコードの変更箇所
src/cmd/gc/go.h
--- a/src/cmd/gc/go.h
+++ b/src/cmd/gc/go.h
@@ -387,7 +387,7 @@ void lexinit(void);
char* lexname(int);
long getr(void);
int getnsc(void);
-int escchar(int, vlong*);
+int escchar(int, int*, vlong*);
int getc(void);
void ungetc(int);
void mkpackage(char*);
src/cmd/gc/lex.c
--- a/src/cmd/gc/lex.c
+++ b/src/cmd/gc/lex.c
@@ -175,6 +175,7 @@ yylex(void)
vlong v;
char *cp;
Rune rune;
+ int escflag;
Sym *s;
l0:
@@ -224,19 +225,19 @@ l0:
caseq:
for(;;) {
-\t\t\tif(escchar('"', &v))
+\t\t\tif(escchar('"', &escflag, &v))
break;
-\t\t\tif(v >= Runeself) {
+\t\t\tif(escflag || v < Runeself) {
+\t\t\t\tcp = remal(cp, c1, 1);
+\t\t\t\tcp[c1++] = v;
+\t\t\t} else {
// botch - this limits size of runes
rune = v;
c = runelen(rune);
cp = remal(cp, c1, c);
runetochar(cp+c1, &rune);
c1 += c;
-\t\t\t\tcontinue;
\t\t\t}
-\t\t\tcp = remal(cp, c1, 1);
-\t\t\tcp[c1++] = v;
}
catem:
@@ -281,9 +282,9 @@ l0:
case '\'':
/* '.' */
-\t\tif(escchar('\'', &v))
+\t\tif(escchar('\'', &escflag, &v))
v = '\''; // allow '''
-\t\tif(!escchar('\'', &v)) {
+\t\tif(!escchar('\'', &escflag, &v)) {
yyerror("missing '");
ungetc(v);
}
@@ -695,12 +696,13 @@ getnsc(void)
int
-escchar(int e, vlong *val)
+escchar(int e, int *escflg, vlong *val)
{
-\tint i;
-\tlong c;
+\tint i, c;
vlong l;
+\t*escflg = 0;
+
loop:
c = getr();
if(c == '\n') {
@@ -720,6 +722,7 @@ loop:
goto loop;
case 'x':
+\t\t*escflg = 1;\t// it's a byte
i = 2;
goto hex;
@@ -739,6 +743,7 @@ loop:
case '5':
case '6':
case '7':
+\t\t*escflg = 1;\t// it's a byte
goto oct;
case 'a': c = '\a'; break;
@@ -793,6 +798,7 @@ oct:
}\n\
if(l > 255)\n\
warn("oct escape value > 255: %d", l);\n\
+\n\
*val = l;\n\
return 0;\n\
}\n
コアとなるコードの解説
このコミットの核心は、Go言語の字句解析器が文字列リテラルや文字リテラル内のエスケープシーケンスをどのように解釈するかをより厳密に制御することにあります。
-
escchar
関数の役割の拡張: 以前のescchar
関数は、エスケープシーケンスを解析し、その結果の数値(vlong
)を返すだけでした。しかし、Go言語では文字列がUTF-8バイトのシーケンスであり、\xNN
や\NNN
のようなエスケープは単一のバイト値を表すのに対し、\uNNNN
や\UHHHHHHHH
はUnicodeコードポイントを表します。この区別は、文字列の内部表現において重要です。 新しいescchar
関数は、escflg
という新しい出力パラメータを受け取ります。このフラグは、解析されたエスケープシーケンスが「バイト」として扱われるべきか(escflg = 1
)、それとも「Unicodeコードポイント」として扱われるべきか(escflg = 0
)を示します。具体的には、\x
エスケープや8進数エスケープが検出された場合にescflg
が1に設定されます。 -
yylex
におけるエスケープ処理の分岐:yylex
関数内の文字列リテラル解析(caseq
)では、escchar
から返されたescflag
の値に基づいて、文字の処理方法が分岐するようになりました。escflag
が真(1)の場合、またはv
がRuneself
未満の場合(ASCII文字の場合): これは、エスケープされた値が単一のバイトとして文字列バッファに直接追加されるべきであることを意味します。\xNN
や\NNN
のようなエスケープは、Goの文字列がバイトのシーケンスであるため、直接バイトとして扱われます。ASCII文字も同様に1バイトとして扱われます。escflag
が偽(0)で、かつv
がRuneself
以上の場合: これは、エスケープされた値がUnicodeコードポイント(rune
)として扱われるべきであることを意味します。この場合、v
はrune
型に変換され、runelen
(UTF-8エンコード後のバイト数を計算)とrunetochar
(rune
をUTF-8バイトシーケンスに変換)を使って、UTF-8エンコードされたバイトシーケンスとして文字列バッファに追加されます。
この変更により、Goコンパイラの字句解析器は、エスケープシーケンスのセマンティクスをより正確に区別し、Go言語の文字列とruneの厳密な定義に沿った形で、ソースコード内のリテラルを正しく内部表現に変換できるようになりました。これは、コンパイラの正確性と堅牢性を高める上で重要な改善です。
関連リンク
- Go言語の仕様: https://go.dev/ref/spec
- Go言語の文字列とruneについて: https://go.dev/blog/strings
参考にした情報源リンク
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFw6pk0IucTPQdrE5yQz4MA1aEWL4xehSNT87GHpkA83TxHek0cnFipAqRGL14Be254XaldG_DVXcl7xEWwDxhJlEHKX12khFY8F-JooL-fLWAv7Ih-bTrgT-OQeWKUl3YG0RaJ
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFyuX_Ig6d8042GN3pQzAsxSQAIVBuTYJaDteepS5p4TeP6sm5Gjs61JPpMDPAaFv1CoQAJ9adXvd-xI3CGPaUKwtziOOoL4Rp2YjySiS5oMWvLZ5e-uLYIwOud_7Fad36eXPWlZyRwKtA5dKc=
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEeA33yyLxbLUcX_yA1LKayAyLrq6g2jGTmAAB4UvSYhDBDKM0wBpzT2NU9KgTp6SkPrcsTRXpW5e6iAl6noQU_eWbdd2Bayg57MZ5MDH6Ct8IsJGY4Iyw9SlheG77b
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQES1p_5MYL4qQImsFzBhuPa64uoI0eAmSFoMfbVUAUTUVDKtjA67f4aCFm9y_9m4CWTRCefakjuQyxddvZWWntv-deTGzBM_6HHiU05AIBUQxU-8RFM3F9qwmkyr4GkwUa-73y0YA==
- https://swtch.com