[インデックス 18955] ファイルの概要
このコミットは、Goコンパイラ(cmd/gc
)の字句解析器(lexer)を定義するsrc/cmd/gc/lex.c
ファイルに対する変更です。lex.c
は、Goのソースコードを読み込み、それをコンパイラが理解できる最小単位である「トークン」に分解する役割を担っています。
コミット
cmd/gc: add missing entries to lexn and yytfix. Makes gc -x better.
LGTM=r R=golang-codereviews, bradfitz, r CC=golang-codereviews https://golang.org/cl/73090043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/aa1391949caeff27f50da999eb83f23d83ba5552
元コミット内容
commit aa1391949caeff27f50da999eb83f23d83ba5552
Author: Shenghou Ma <minux.ma@gmail.com>
Date: Tue Mar 25 23:21:57 2014 -0400
cmd/gc: add missing entries to lexn and yytfix.
Makes gc -x better.
LGTM=r
R=golang-codereviews, bradfitz, r
CC=golang-codereviews
https://golang.org/cl/73090043
変更の背景
このコミットは、Goコンパイラ(gc
)の字句解析器が認識するトークンリストに、いくつかの重要なエントリが欠落していた問題を修正するものです。具体的には、lexn
というトークン名と文字列のマッピングを定義する構造体と、yytfix
というトークンIDと文字列のマッピングを定義する構造体(またはそれに類するもの)に、特定のGo言語のキーワードや演算子に対応するエントリが追加されました。
これらのエントリが欠落していると、コンパイラのデバッグや診断に用いられる可能性のあるgc -x
フラグ(Goコンパイラ内部の動作に関する詳細な情報を出力する非公開またはデバッグ用のフラグと推測されます)の出力が不完全になったり、誤った情報が表示されたりする可能性がありました。このコミットは、これらの欠落したエントリを追加することで、gc -x
の出力をより正確で有用なものにすることを目的としています。これにより、コンパイラの開発者やデバッグを行うユーザーが、字句解析の段階で何が起こっているかをより明確に把握できるようになります。
前提知識の解説
Goコンパイラ (gc
) の役割
gc
は、Go言語の公式コンパイラであり、Goのソースコードを機械語に変換する主要なツールです。コンパイラは、ソースコードを複数の段階を経て処理します。
字句解析 (Lexical Analysis) と構文解析 (Parsing)
コンパイラの最初の段階は字句解析(Lexical Analysis、またはスキャンニング、トークン化とも呼ばれる)です。字句解析器(lexerまたはscanner)は、ソースコードの文字列を読み込み、それを意味のある最小単位であるトークンのストリームに変換します。例えば、var x = 10
というコードは、var
(キーワード)、x
(識別子)、=
(代入演算子)、10
(整数リテラル)といったトークンに分解されます。
字句解析の次に構文解析(Parsing)が行われます。構文解析器(parser)は、字句解析器から受け取ったトークンのストリームを基に、プログラムの文法構造を検証し、抽象構文木(Abstract Syntax Tree: AST)と呼ばれるツリー構造を構築します。ASTは、プログラムの構造を階層的に表現したもので、コンパイラの以降の段階で利用されます。
lex.c
の役割
src/cmd/gc/lex.c
は、Goコンパイラの字句解析器の実装の一部です。このファイルには、Go言語の様々なキーワード、演算子、リテラルなどをトークンとして認識するためのロジックや、それらのトークンに対応する内部表現が定義されています。
lexn
と yytfix
コミットメッセージに登場するlexn
とyytfix
は、Goコンパイラの字句解析および構文解析に関連する内部データ構造であると推測されます。
lexn
: コードの差分から判断すると、lexn
はトークンの内部的な定数名(例:LANDAND
)と、それに対応する文字列表現(例:"ANDAND"
)をマッピングする構造体の配列であると考えられます。これは、デバッグ出力などでトークンを人間が読める形式で表示するために使用されます。yytfix
:yy
というプレフィックスは、Yacc(Yet Another Compiler Compiler)やBisonといったパーサジェネレータツールで生成されたコードによく見られます。GoコンパイラはかつてYacc/Bisonを使用していましたが、後に手書きのパーサに移行しています。このコミットが行われた2014年時点では、まだYacc/Bisonの遺産がコードベースに残っていた可能性があります。yytfix
は、トークンの内部IDと、そのトークンに対応する文字列(またはデバッグ用の情報)をマッピングする構造体であると推測されます。
gc -x
フラグの機能
gc -x
は、Goコンパイラgc
の内部的なデバッグフラグであると考えられます。公式ドキュメントには記載されていませんが、コンパイラの開発者がコンパイルプロセスの詳細なステップや中間状態を検査するために使用するものです。字句解析の段階で認識されたトークンに関する情報が出力されることが期待されます。
Go言語のキーワードと演算子(トークン)
Go言語には、特定の意味を持つキーワード(例: chan
, default
, select
)や演算子(例: &^
, ...
)があります。これらは字句解析の段階でそれぞれ固有のトークンとして識別されます。
&^
(AND NOT): ビットごとのAND NOT演算子。...
(Ellipsis): 可変長引数(variadic functions)の宣言や、配列リテラルで要素数をコンパイラに推論させる場合に使用される。chan
: チャネル(goroutine間の通信に使用される型)を宣言するためのキーワード。default
:switch
文のデフォルトケースや、select
文でどのチャネル操作も準備できていない場合に実行されるケースを指定するキーワード。select
: 複数のチャネル操作を待機し、準備ができた最初の操作を実行するための制御構造。
技術的詳細
このコミットは、Goコンパイラの字句解析器が使用する内部テーブルに、いくつかのGo言語の要素を追加することで、コンパイラのデバッグ出力の正確性を向上させています。
lex.c
ファイルには、Go言語の各トークンに対応する内部的な定数(例: LANDNOT
, LCOMM
など、L
プレフィックスはLexer Tokenを意味すると考えられる)と、そのトークンの文字列表現をマッピングするlexn
という構造体配列が定義されています。この配列は、コンパイラがトークンを認識した際に、そのトークンの種類を人間が読める形式で表示するために利用されます。
また、yytfix
(または関連するデータ構造)は、トークンの内部IDと、そのトークンに対応する文字列をマッピングする役割を担っています。これは、特にYacc/Bisonのようなパーサジェネレータが生成するコードにおいて、トークンIDからその文字列表現への逆引きを行う際に使用されることがあります。
このコミットで追加されたエントリは以下の通りです。
LANDNOT
:&^
演算子に対応。LCOMM
: コメントに対応する内部トークン。Goのgo/token
パッケージではCOMMENT
が使われますが、gc
内部では異なる命名がされていた可能性があります。LDDD
:...
(エリプシス)演算子に対応。LDEFAULT
:default
キーワードに対応。LSELECT
:select
キーワードに対応。LCHAN
:chan
キーワードに対応。
これらのエントリがlexn
とyytfix
に存在しない場合、gc -x
のようなデバッグフラグを使用してコンパイラの字句解析過程をトレースした際に、これらのトークンが正しく表示されず、単に不明なトークンIDとして表示されたり、全く表示されなかったりする可能性がありました。この修正により、gc -x
の出力がより完全になり、コンパイラの内部動作をより正確に把握できるようになります。これは、コンパイラのバグ修正や機能追加を行う際に、非常に重要な情報源となります。
コアとなるコードの変更箇所
変更はsrc/cmd/gc/lex.c
ファイルに対して行われました。
--- a/src/cmd/gc/lex.c
+++ b/src/cmd/gc/lex.c
@@ -2171,14 +2171,18 @@ struct
} lexn[] =
{
LANDAND, "ANDAND",
+ LANDNOT, "ANDNOT",
LASOP, "ASOP",
LBREAK, "BREAK",
LCASE, "CASE",
LCHAN, "CHAN",
LCOLAS, "COLAS",
+ LCOMM, "<-",
LCONST, "CONST",
LCONTINUE, "CONTINUE",
+ LDDD, "...",
LDEC, "DEC",
+ LDEFAULT, "DEFAULT",
LDEFER, "DEFER",
LELSE, "ELSE",
LEQ, "EQ",
@@ -2205,6 +2209,7 @@ struct
LRANGE, "RANGE",
LRETURN, "RETURN",
LRSH, "RSH",
+ LSELECT, "SELECT",
LSTRUCT, "STRUCT",
LSWITCH, "SWITCH",
LTYPE, "TYPE",
@@ -2235,6 +2240,7 @@ struct
"LASOP", "op=",
"LBREAK", "break",
"LCASE", "case",
+ "LCHAN", "chan",
"LCOLAS", ":=",
"LCONST", "const",
"LCONTINUE", "continue",
コアとなるコードの解説
この差分は、lexn
という構造体配列と、その後に続く別の配列(おそらくyytfix
に関連するもの)に、新しいエントリを追加していることを示しています。
-
lexn
配列への追加:LANDNOT, "ANDNOT",
: ビットごとのAND NOT演算子&^
に対応するトークンLANDNOT
と、その文字列表現"ANDNOT"
が追加されました。LCOMM, "<-",
: コメントに対応するトークンLCOMM
と、その文字列表現"<-"
が追加されました。ここで"<-"
がコメントの文字列表現として使われているのは興味深い点です。これは、Goのチャネル操作の方向を示す<-
演算子と混同されやすいですが、この文脈では字句解析器の内部的なデバッグ表示のための文字列である可能性が高いです。LDDD, "...",
: エリプシス演算子...
に対応するトークンLDDD
と、その文字列表現"..."
が追加されました。LDEFAULT, "DEFAULT",
:default
キーワードに対応するトークンLDEFAULT
と、その文字列表現"DEFAULT"
が追加されました。LSELECT, "SELECT",
:select
キーワードに対応するトークンLSELECT
と、その文字列表現"SELECT"
が追加されました。
-
yytfix
に関連する配列への追加:"LCHAN", "chan",
:chan
キーワードに対応するトークンLCHAN
と、その文字列表現"chan"
が追加されました。このセクションは、トークンの内部IDからその文字列表現へのマッピングを補完していると考えられます。
これらの追加により、Goコンパイラの字句解析器は、これらのGo言語の要素を正しく認識し、内部的なデバッグ出力において、より正確で完全なトークン情報を提供できるようになりました。これにより、gc -x
のようなデバッグツールが、コンパイル過程のより詳細なビューを提供し、コンパイラの開発やデバッグ作業を支援します。
関連リンク
- Go CL 73090043: https://golang.org/cl/73090043
参考にした情報源リンク
- Go compiler lexical analysis: https://chermehdi.com/posts/go-compiler-lexical-analysis/
- The Go Programming Language Specification - Lexical elements: https://go.dev/ref/spec#Lexical_elements
- Go compiler internals (general overview): https://go.dev/blog/go1.7-compiler
- Understanding Go's
gcflags
: https://cheney.net/blog/understanding-go-gcflags - Go Language Tokens (general): https://www.ubc.ca/cs/courses/cpsc411/2018W1/lectures/L02-Go-Lexical-Analysis.pdf
- Go
select
statement: https://go.dev/tour/concurrency/5 - Go
chan
keyword: https://go.dev/tour/concurrency/2 - Go
...
(ellipsis): https://go.dev/tour/moretypes/12 - Go
&^
(bitwise AND NOT): https://go.dev/ref/spec#Arithmetic_operators