[インデックス 11980] ファイルの概要
このコミットは、Go言語のコンパイラツールチェーンの一部である src/pkg/go/parser/parser.go ファイルに対する変更です。go/parser パッケージは、Go言語のソースコードを解析し、抽象構文木 (AST: Abstract Syntax Tree) を構築する役割を担っています。具体的には、字句解析器 (lexer) から受け取ったトークンストリームを基に、Go言語の文法規則に従って構文解析を行い、プログラムの構造を表現するASTを生成します。このファイルは、その構文解析ロジックの中核をなす部分です。
コミット
go/parser パッケージにおいて、虚数定数 (IMAG) と論理否定演算子 (!) が式の開始トークンとして正しく認識されるようにパーサーを更新しました。これにより、トップレベルの式を開始できるトークンのリストが完全になりました。
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/f43d2b7fa89e148b1570551d5a27e0c155a03cb2
元コミット内容
go/parser: imaginary constants and ! may start an expression
Complete list of tokens that can start a top-level expression.
R=rsc
CC=golang-dev
https://golang.org/cl/5671074
変更の背景
Go言語のパーサーは、ソースコードをトークンに分解し、それらのトークンが特定の文法構造(例えば、式や文)の開始点となり得るかを判断する必要があります。このコミット以前は、Go言語のパーサーが式(特にトップレベルの式)の開始を判断する際に、虚数定数(例: 3i)と論理否定演算子 (!) を正しく認識していませんでした。
Go言語では、虚数定数は数値リテラルの一種として扱われ、式の一部として使用されます。また、! はブール値を反転させる単項演算子であり、これも式の一部を形成します。これらの要素が式の先頭に来る場合、パーサーはそれを正しく解釈し、適切な構文木を構築する必要があります。
このコミットの目的は、パーサーが式の開始トークンとして認識するトークンのリストを更新し、token.IMAG(虚数定数)と token.NOT(!)を追加することで、Go言語の文法規則に完全に準拠するようにすることでした。これにより、これらの要素で始まる有効なGoコードが正しく解析されるようになります。
前提知識の解説
1. go/parser パッケージ
go/parser はGo言語の標準ライブラリの一部であり、Goソースコードを解析して抽象構文木 (AST) を生成するためのパッケージです。コンパイラやリンター、コード分析ツールなど、Goコードをプログラム的に扱う多くのツールで利用されます。
2. トークン (Token)
ソースコードは、まず字句解析器 (lexer) によって意味のある最小単位である「トークン」に分割されます。例えば、var x = 10 + 5i; というコードは、var (キーワード), x (識別子), = (代入演算子), 10 (整数リテラル), + (加算演算子), 5i (虚数リテラル), ; (セミコロン) といったトークンに分解されます。go/token パッケージは、これらのトークンの種類を定義しています。
3. 式 (Expression)
式は、値を生成するコードの断片です。例えば、1 + 2、x * y、myFunc() などが式です。Go言語の文法では、特定のトークンが式の開始点となり得ます。
4. 虚数定数 (Imaginary Constants)
Go言語は複素数型 (complex64, complex128) をサポートしており、虚数定数は 3i や 1.2i のように記述されます。これらは数値リテラルの一種であり、token.IMAG というトークンとして扱われます。
5. 単項演算子 (Unary Operators)
単項演算子は、一つのオペランド(被演算子)に対して作用する演算子です。Go言語には、+ (単項プラス), - (単項マイナス), * (ポインタの間接参照), & (アドレス取得), <- (チャネル受信), ! (論理否定), ^ (ビットごとの否定) などがあります。このコミットでは、! (論理否定) が式の開始トークンとして追加されました。
6. parseStmt 関数
parser.go 内の parseStmt 関数は、Go言語の「文 (statement)」を解析する主要な関数の一つです。文は、プログラムの実行可能な命令の単位であり、式文、宣言文、制御フロー文などがあります。この関数は、現在のトークンを見て、それがどのような文の開始であるかを判断します。式で始まる文(式文や単純文)の場合、その開始トークンを識別する必要があります。
技術的詳細
この変更は、go/parser パッケージ内の parser.go ファイルにある parseStmt 関数の内部ロジックに焦点を当てています。具体的には、parseStmt 関数が文の開始トークンを識別する switch ステートメント内の case ブロックが修正されました。
Go言語の文法では、単純文 (SimpleStmt) は式で始まることがあります。parseStmt 関数は、現在のトークンが token.IDENT (識別子)、token.INT (整数リテラル)、token.FLOAT (浮動小数点リテラル)、token.CHAR (文字リテラル)、token.STRING (文字列リテラル)、token.FUNC (関数リテラル)、token.LPAREN (() などである場合、それが式で始まる文であると判断し、p.parseSimpleStmt を呼び出して解析を進めます。
このコミットでは、この「式を開始できるトークン」のリストに以下の2つの変更が加えられました。
-
token.IMAGの追加: 虚数定数(例:3i)は、Go言語において有効な数値リテラルであり、単独で式を構成することができます。例えば、_ = 3iのような文は有効です。以前のパーサーは、このtoken.IMAGが式の開始トークンとして明示的にリストされていなかったため、正しく処理できない可能性がありました。今回の変更でtoken.IMAGがoperandsのリストに追加され、虚数定数で始まる式が正しく認識されるようになりました。 -
token.NOTの追加と単項演算子の並び替え: 論理否定演算子!は、Go言語において単項演算子として機能し、ブール式を反転させます(例:!true)。これもまた、式を開始できる有効なトークンです。以前のリストにはtoken.MUL(*),token.AND(&),token.ARROW(<-),token.ADD(+),token.SUB(-),token.XOR(^) といった単項演算子が含まれていましたが、token.NOTが欠落していました。この変更により、token.NOTがunary operatorsのリストに追加され、!で始まる式も正しく解析されるようになりました。また、単項演算子のリストの順序が変更されていますが、これは機能的な意味合いよりも、おそらくコードの可読性やグループ化のためと考えられます。
これらの変更により、Go言語のパーサーは、より広範な有効なGoコードパターンを正確に解析できるようになり、言語仕様への準拠が強化されました。
コアとなるコードの変更箇所
--- a/src/pkg/go/parser/parser.go
+++ b/src/pkg/go/parser/parser.go
@@ -1861,10 +1861,10 @@ func (p *parser) parseStmt() (s ast.Stmt) {
case token.CONST, token.TYPE, token.VAR:
s = &ast.DeclStmt{Decl: p.parseDecl()}
case
- // tokens that may start a top-level expression
- token.IDENT, token.INT, token.FLOAT, token.CHAR, token.STRING, token.FUNC, token.LPAREN, // operand
- token.LBRACK, token.STRUCT, // composite type
- token.MUL, token.AND, token.ARROW, token.ADD, token.SUB, token.XOR: // unary operators
+ // tokens that may start an expression
+ token.IDENT, token.INT, token.FLOAT, token.IMAG, token.CHAR, token.STRING, token.FUNC, token.LPAREN, // operands
+ token.LBRACK, token.STRUCT, // composite types
+ token.ADD, token.SUB, token.MUL, token.AND, token.XOR, token.ARROW, token.NOT: // unary operators
s, _ = p.parseSimpleStmt(labelOk)
// because of the required look-ahead, labeled statements are
// parsed by parseSimpleStmt - don't expect a semicolon after
コアとなるコードの解説
変更は func (p *parser) parseStmt() (s ast.Stmt) 関数内の switch ステートメントの case ブロックにあります。この case は、現在のトークンが式を開始する可能性がある場合に実行されるパスを定義しています。
変更前:
case
// tokens that may start a top-level expression
token.IDENT, token.INT, token.FLOAT, token.CHAR, token.STRING, token.FUNC, token.LPAREN, // operand
token.LBRACK, token.STRUCT, // composite type
token.MUL, token.AND, token.ARROW, token.ADD, token.SUB, token.XOR: // unary operators
この行では、式を開始し得るトークンが列挙されています。
token.IDENT(識別子),token.INT(整数),token.FLOAT(浮動小数点数),token.CHAR(文字),token.STRING(文字列),token.FUNC(関数リテラル),token.LPAREN(() は、オペランド(被演算子)として式を開始します。token.LBRACK([) とtoken.STRUCTは、複合型(配列、スライス、マップ、チャネル、構造体)の宣言の一部として式を開始します。token.MUL(*),token.AND(&),token.ARROW(<-),token.ADD(+),token.SUB(-),token.XOR(^) は、単項演算子として式を開始します。
変更後:
case
// tokens that may start an expression
token.IDENT, token.INT, token.FLOAT, token.IMAG, token.CHAR, token.STRING, token.FUNC, token.LPAREN, // operands
token.LBRACK, token.STRUCT, // composite types
token.ADD, token.SUB, token.MUL, token.AND, token.XOR, token.ARROW, token.NOT: // unary operators
変更点は以下の通りです。
-
コメントの変更:
// tokens that may start a top-level expressionから// tokens that may start an expressionに変更されました。これは、このリストがトップレベルの式だけでなく、あらゆる式の開始トークンを網羅していることをより正確に示しています。 -
token.IMAGの追加:token.FLOATの後にtoken.IMAGが追加されました。これにより、虚数定数(例:3i)が式を開始する有効なオペランドとして認識されるようになりました。 -
token.NOTの追加: 単項演算子のリストの最後にtoken.NOT(!) が追加されました。これにより、論理否定演算子で始まる式(例:!isValid)が正しく解析されるようになりました。 -
単項演算子の順序変更:
token.MUL,token.AND,token.ARROW,token.ADD,token.SUB,token.XORの順序がtoken.ADD,token.SUB,token.MUL,token.AND,token.XOR,token.ARROWに変更されました。この順序変更は機能的な影響はありませんが、おそらく関連性の高い演算子をまとめるなど、コードの整理を意図したものと考えられます。
これらの変更により、Go言語のパーサーは、虚数定数と論理否定演算子で始まる有効なGoの式を正しく認識し、構文解析を進めることができるようになりました。
関連リンク
- Go言語の公式ドキュメント: https://golang.org/
- Go言語の仕様: https://golang.org/ref/spec
- Go言語のトークン定義 (
go/token): https://pkg.go.dev/go/token - Go言語のパーサー (
go/parser): https://pkg.go.dev/go/parser - このコミットのGo Gerrit Code Reviewリンク: https://golang.org/cl/5671074
参考にした情報源リンク
- Go言語の公式ドキュメントおよび仕様書
- Go言語の
go/tokenおよびgo/parserパッケージのソースコード - Go言語のコミット履歴と関連するコードレビュー
- Go言語における虚数定数と単項演算子に関する一般的な情報