[インデックス 1886] ファイルの概要
このコミットは、Go言語の抽象構文木(AST: Abstract Syntax Tree)の定義を大幅に改訂するものです。具体的には、usr/gri/pretty/ast.go
ファイルに対する変更であり、Goプログラムの構文構造を表現するためのデータ構造が再設計されています。これにより、コンパイラやツールがGoのソースコードを解析し、意味解析やコード生成を行う際の基盤が強化されました。
コミット
- コミットハッシュ:
bafd8c390aa69808feca8de1774ea81c1527926c
- 作者: Robert Griesemer gri@golang.org
- 日付: Wed Mar 25 12:44:18 2009 -0700
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/bafd8c390aa69808feca8de1774ea81c1527926c
元コミット内容
AST for Go programs
R=rsc,r
DELTA=309 (67 added, 51 deleted, 191 changed)
OCL=26611
CL=26745
変更の背景
このコミットが行われた2009年3月は、Go言語がまだ一般に公開される前の開発初期段階にあたります。この時期は、言語仕様が固まりつつあり、それに伴いコンパイラの内部構造、特にASTの設計が頻繁に調整されていました。
変更の主な背景としては、以下の点が挙げられます。
- 言語仕様の進化への対応: Go言語の構文やセマンティクスが洗練されるにつれて、それを正確に表現できるAST構造が必要とされました。特に、リテラル表現の多様化や、ステートメントの種類の増加に対応するため、既存のAST定義では不十分になったと考えられます。
- ASTの整合性と拡張性の向上: 初期段階のASTは、試行錯誤の中で定義された部分も多く、構造的な一貫性や将来的な拡張性において改善の余地がありました。このコミットでは、ASTノードの命名規則の統一(例:
Stat
からStmt
への変更)や、より細分化されたノードタイプの導入により、AST全体の整合性を高め、今後の言語機能追加に備える狙いがあったと推測されます。 - コードの明確化と保守性の向上: ASTはコンパイラの中心的なデータ構造であるため、その定義が明確で理解しやすいことは、開発効率と保守性に直結します。不要な抽象化の削除(例:
Block
やSignature
の直接的な型定義の削除)や、より具体的なノードタイプの導入は、コードの可読性を向上させる目的があったと考えられます。
前提知識の解説
抽象構文木(AST: Abstract Syntax Tree)
ASTは、プログラミング言語のソースコードを抽象的な構文構造で表現した木構造のデータ構造です。コンパイラのフロントエンド(字句解析、構文解析)によって生成され、その後の意味解析、最適化、コード生成といったフェーズで利用されます。
- ノード: ASTの各要素はノードと呼ばれ、変数宣言、関数呼び出し、演算子、リテラルなどの言語の構文要素に対応します。
- 階層構造: ノードは親子関係を持ち、プログラム全体の構造を表現します。例えば、関数定義ノードの下には、その関数のパラメータ、戻り値、本体(ステートメントのリスト)を表すノードがぶら下がります。
- 抽象性: ASTは、ソースコードの具体的な字句(コメント、空白、括弧など)を排除し、プログラムの論理的な構造のみを抽出します。これにより、コンパイラは構文解析の結果をより効率的に処理できます。
Go言語のASTにおける主要な概念
Go言語のASTは、主に以下のカテゴリのノードで構成されます。
- Expr (Expression): 式を表すノード。リテラル(数値、文字列)、識別子、関数呼び出し、演算子など。
- Stmt (Statement): 文を表すノード。変数宣言、代入、if文、for文、return文など。
- Decl (Declaration): 宣言を表すノード。パッケージ宣言、インポート宣言、定数宣言、型宣言、変数宣言、関数宣言など。
- Type: 型を表すノード。プリミティブ型、構造体型、配列型、マップ型、関数型など。
これらのノードは、それぞれが特定の構文要素に対応するstruct
として定義され、多くの場合、Pos()
メソッド(ソースコード上の位置情報)やVisit()
メソッド(ASTウォークのためのVisitorパターン実装)を持ちます。
Visitorパターン
Visitorパターンは、オブジェクト構造(ここではAST)から操作(ASTの走査や処理)を分離するためのデザインパターンです。ASTの各ノードタイプに対応するDoXxx
メソッドを持つVisitor
インターフェースを定義し、各ノードは自身のVisit
メソッドで適切なDoXxx
メソッドを呼び出します。これにより、ASTの構造を変更することなく、新しい操作を追加できます。
技術的詳細
このコミットの技術的な詳細は、主にASTノードの再編成と命名規則の統一にあります。
-
Stat
インターフェースからStmt
インターフェースへの変更:- 以前はステートメントを表すインターフェースとして
Stat
が使われていましたが、より一般的で明確なStmt
に名称が変更されました。これに伴い、関連するVisitorインターフェースもStatVisitor
からStmtVisitor
に、具体的なステートメントノードの型名もBadStat
からBadStmt
、DeclStat
からDeclStmt
など、一貫してStat
がStmt
に置き換えられています。これは、Go言語の標準的な命名規則への準拠と、コードベース全体での一貫性を高めるための変更です。
- 以前はステートメントを表すインターフェースとして
-
リテラル表現の細分化と明確化:
- 以前は
BasicLit
という単一のノードで整数、浮動小数点数、文字、文字列リテラルを扱っていましたが、このコミットにより、それぞれ専用のノードが導入されました。IntLit
(整数リテラル)FloatLit
(浮動小数点数リテラル)CharLit
(文字リテラル)StringLit
(単一の文字列リテラル)
- さらに、複数の隣接する文字列リテラル(Goでは自動的に連結される)を表現するために
StringList
ノードが追加されました。これにより、ASTがソースコードの具体的なリテラル表現をより正確に反映できるようになり、意味解析フェーズでの処理が容易になります。
- 以前は
-
Ellipsis
ノードの再定義:- 可変長引数(
...
)や配列の長さ指定([...]T
)を表すEllipsis
ノードが、以前はExpr
インターフェースを実装しない独立した型でしたが、このコミットでExpr
インターフェースを実装するようになりました。これにより、Ellipsis
が式として扱えるようになり、ASTの型システムにおける一貫性が向上しました。
- 可変長引数(
-
Field
構造体の再定義と用途の明確化:Field
構造体は、構造体のフィールド、インターフェースのメソッド、関数のパラメータ/戻り値を表現するために使用されます。このコミットでは、Tag
フィールドの型がExpr
から[]*StringLit
に変更され、より具体的な文字列リテラルのリストとしてタグを扱えるようになりました。また、Doc
フィールドがComments
型になり、ドキュメンテーションコメントの扱いが統一されました。
-
FunctionType
とSignature
の統合と簡素化:- 以前は
Signature
という独立した構造体で関数のパラメータと戻り値を表現し、FunctionType
がそれを参照する形でした。このコミットでは、Signature
構造体が削除され、その内容(Params
とResults
)が直接FunctionType
構造体内に組み込まれました。これにより、関数型の表現が簡素化され、冗長性が排除されました。
- 以前は
-
Block
構造体の削除とBlockStmt
の導入:- 以前は
Block
という構造体でブロック({ ... }
)を表現していましたが、これが削除され、BlockStmt
という新しいステートメントノードが導入されました。これにより、ブロックが明確にステートメントの一種として扱われるようになり、ASTの階層構造がより論理的になりました。
- 以前は
-
Pos()
メソッドの改善:- 多くのASTノードで
Pos()
メソッドの実装が変更され、より正確な位置情報を返すようになりました。特に、FunctionLit
やCompositeLit
など、複合的な構造を持つノードのPos()
が、その開始位置をより適切に指すように修正されています。
- 多くのASTノードで
これらの変更は、Go言語のASTがより堅牢で、表現力豊かであり、コンパイラの各フェーズで効率的に利用できるような基盤を構築するための重要なステップでした。
コアとなるコードの変更箇所
このコミットの主要な変更は、usr/gri/pretty/ast.go
ファイル内の以下の構造体とインターフェースの定義に集中しています。
-
Stat
からStmt
への名称変更:--- a/usr/gri/pretty/ast.go +++ b/usr/gri/pretty/ast.go @@ -65,12 +59,12 @@ type Expr interface { } -// All statement nodes implement the Stat interface. -type Stat interface { +// All statement nodes implement the Stmt interface. +type Stmt interface { // For a (dynamic) node type X, calling Visit with a statement // visitor v invokes the node-specific DoX function of the visitor. // - Visit(v StatVisitor); + Visit(v StmtVisitor); // Pos returns the (beginning) position of the statement. Pos() Position;
これに伴い、すべてのステートメント関連の型名とVisitorインターフェース名が変更されています。
-
リテラルノードの細分化:
--- a/usr/gri/pretty/ast.go +++ b/usr/gri/pretty/ast.go @@ -127,28 +140,55 @@ type ( Lit []byte; // identifier string (e.g. foobar) }; - // A BasicLit node represents a basic literal. - BasicLit struct { + // An Ellipsis node stands for the "..." type in a + // parameter list or the "..." length in an array type. + // + Ellipsis struct { + Pos_ Position; // position of "..." + }; + + // An IntLit node represents an integer literal. + IntLit struct { Pos_ Position; // literal string position - Tok int; // literal token (INT, FLOAT, CHAR, STRING) - Lit []byte; // literal string + Lit []byte; // literal string; e.g. 42 or 0x7f }; - // A StringLit node represents a sequence of string literals. + // A FloatLit node represents a floating-point literal. + FloatLit struct { + Pos_ Position; // literal string position + Lit []byte; // literal string; e.g. 3.14 or 1e-9 + }; + + // A CharLit node represents a character literal. + CharLit struct { + Pos_ Position; // literal string position + Lit []byte; // literal string, including quotes; e.g. 'a' or '\x7f' + }; + + // A StringLit node represents a string literal. StringLit struct { - Strings []*BasicLit; // sequence of strings + Pos_ Position; // literal string position + Lit []byte; // literal string, including quotes; e.g. "foo" or `\m\n\o` + }; + + // A StringList node represents a sequence of adjacent string literals. + // A single string literal (common case) is represented by a StringLit + // node; StringList nodes are used only if there are two or more string + // literals in a sequence. + // + StringList struct { + Strings []*StringLit; // list of strings, len(Strings) > 1 };
BasicLit
が削除され、IntLit
,FloatLit
,CharLit
,StringLit
が追加されています。また、StringList
が新設されました。 -
FunctionLit
とFunctionType
の変更:--- a/usr/gri/pretty/ast.go +++ b/usr/gri/pretty/ast.go @@ -109,6 +103,25 @@ type Comments []*Comment // ---------------------------------------------------------------------------- // Expressions and types +// Support types. +type ( + Ident struct; + StringLit struct; + FunctionType struct; + BlockStmt struct; + + // A Field represents a Field declaration list in a struct type, + // a method in an interface type, or a parameter/result declaration + // in a signature. + Field struct { + Doc Comments; // associated documentation; or nil + Names []*Ident; // field/method/parameter names; nil if anonymous field + Type Expr; // field/method/parameter type + Tag []*StringLit; // field tag; nil if no tag + }; +); ... --- a/usr/gri/pretty/ast.go +++ b/usr/gri/pretty/ast.go @@ -273,20 +296,13 @@ type ( tRbrace Position; // position of "}" }; -// Note: pointer types are represented via StarExpr nodes. - -// A signature node represents the parameter and result -// sections of a function type only. -// -Signature struct { - Params []*Field; - Result []*Field; -}; +// Pointer types are represented via StarExpr nodes. // A FunctionType node represents a function type. FunctionType struct { Func Position; // position of "func" keyword - Sig *Signature; + Params []*Field; // (incoming) parameters + Results []*Field; // (outgoing) results };
Signature
が削除され、FunctionType
にParams
とResults
が直接含まれるようになりました。また、Field
構造体のTag
フィールドの型が変更されています。 -
Block
の削除とBlockStmt
の導入:--- a/usr/gri/pretty/ast.go +++ b/usr/gri/pretty/ast.go @@ -403,22 +428,6 @@ func (x *MapType) Visit(v ExprVisitor) { v.DoMapType(x); }\n func (x *ChannelType) Visit(v ExprVisitor) { v.DoChannelType(x); }\n \n \n-// ----------------------------------------------------------------------------\n-// Blocks\n-\n-// A Block represents syntactic constructs of the form:\n-// -// "{" StatementList "}"\n // ":" StatementList -// -type Block struct { - Pos_ Position; - Tok int; - List []Stat; - Rparen Position; // position of closing "}" if present -} - - // ---------------------------------------------------------------------------- // Statements @@ -426,236 +435,244 @@ type Block struct { // or more of the following concrete statement nodes. // type ( - // A BadStat node is a placeholder for statements containing + // A BadStmt node is a placeholder for statements containing // syntax errors for which no correct statement nodes can be // created. // - BadStat struct { + BadStmt struct { Pos_ Position; // beginning position of bad statement }; - // A DeclStat node represents a declaration in a statement list. - DeclStat struct { + // A DeclStmt node represents a declaration in a statement list. + DeclStmt struct { Decl Decl; }; - // An EmptyStat node represents an empty statement. + // An EmptyStmt node represents an empty statement. // The "position" of the empty statement is the position // of the immediately preceeding semicolon. // - EmptyStat struct { + EmptyStmt struct { Semicolon Position; // position of preceeding ";" }; - // A LabeledStat node represents a labeled statement. - LabeledStat struct { + // A LabeledStmt node represents a labeled statement. + LabeledStmt struct { Label *Ident; - Stat Stat; + Stmt Stmt; }; - // An ExprStat node represents a (stand-alone) expression + // An ExprStmt node represents a (stand-alone) expression // in a statement list. // - ExprStat struct { + ExprStmt struct { X Expr; // expression }; - // An IncDecStat node represents an increment or decrement statement. - IncDecStat struct { + // An IncDecStmt node represents an increment or decrement statement. + IncDecStmt struct { X Expr; Tok int; // INC or DEC }; - // An AssignmentStat node represents an assignment or + // An AssignStmt node represents an assignment or // a short variable declaration. - AssignmentStat struct { + AssignStmt struct { Lhs []Expr; Pos_ Position; // token position Tok int; // assignment token, DEFINE Rhs []Expr; }; - // A GoStat node represents a go statement. - GoStat struct { + // A GoStmt node represents a go statement. + GoStmt struct { Go Position; // position of "go" keyword - Call Expr; + Call *CallExpr; }; - // A DeferStat node represents a defer statement. - DeferStat struct { + // A DeferStmt node represents a defer statement. + DeferStmt struct { Defer Position; // position of "defer" keyword - Call Expr; + Call *CallExpr; }; - // A ReturnStat node represents a return statement. - ReturnStat struct { + // A ReturnStmt node represents a return statement. + ReturnStmt struct { Return Position; // position of "return" keyword Results []Expr; }; - // A ControlFlowStat node represents a break, continue, goto, + // A BranchStmt node represents a break, continue, goto, // or fallthrough statement. // - ControlFlowStat struct { + BranchStmt struct { Pos_ Position; // position of keyword Tok int; // keyword token (BREAK, CONTINUE, GOTO, FALLTHROUGH) Label *Ident; }; - // A CompositeStat node represents a braced statement list. - CompositeStat struct { - Body *Block; + // A BlockStmt node represents a braced statement list. + BlockStmt struct { + Lbrace Position; + List []Stmt; + Rbrace Position; };
Block
構造体が削除され、BlockStmt
が導入されました。また、他のステートメントノードもStat
からStmt
に名称が変更されています。
コアとなるコードの解説
このコミットにおけるコアとなるコードの変更は、Go言語のASTの基盤をより堅牢で表現力豊かなものにするための構造的な再設計です。
-
Stat
からStmt
への変更:- これは単なる名称変更以上の意味を持ちます。
Stat
という略語からStmt
という完全な単語への変更は、コードの可読性と一貫性を向上させます。Go言語の設計哲学では、明確で簡潔な命名が重視されており、この変更はその原則に沿ったものです。また、コンパイラ開発者にとって、ステートメントを表すインターフェースがStmt
であると明確にわかることで、コードの理解が深まります。
- これは単なる名称変更以上の意味を持ちます。
-
リテラルノードの細分化:
- 以前の
BasicLit
は、異なる種類のリテラル(整数、浮動小数点数、文字、文字列)を単一の構造体で表現していました。これは柔軟性がある一方で、各リテラルの特性に応じた処理を行う際に、型アサーションやトークンタイプのチェックが必要となり、コードが複雑になる可能性がありました。 IntLit
,FloatLit
,CharLit
,StringLit
といった専用のノードを導入することで、ASTの各ノードがより具体的な意味を持つようになります。これにより、コンパイラの意味解析フェーズで、各リテラルタイプに応じた処理を直接的に記述できるようになり、コードの明確性と効率性が向上します。StringList
の導入は、Go言語の仕様で隣接する文字列リテラルが自動的に連結されるという特性をASTレベルで表現するためのものです。これにより、パーサーは複数の文字列リテラルを一つのStringList
ノードとして構築し、その後のフェーズで連結処理を行うことができます。
- 以前の
-
FunctionLit
とFunctionType
の変更:Signature
構造体を削除し、その内容をFunctionType
に直接組み込むことで、関数型の表現が簡素化されました。これは、ASTのノード数を減らし、データ構造をよりフラットにすることで、メモリ使用量の削減やASTの走査効率の向上に寄与します。また、関数リテラル(FunctionLit
)がFunctionType
を直接参照するようになり、ASTの構造がより直感的になりました。
-
Block
の削除とBlockStmt
の導入:Block
構造体は、波括弧で囲まれたステートメントのリストを表していましたが、これがBlockStmt
というステートメントノードに置き換えられました。この変更は、ブロックがプログラムの実行フローの一部である「ステートメント」として明確に位置づけられることを意味します。これにより、ASTの階層構造がより論理的になり、ステートメントを処理するVisitorパターンなどの実装が簡素化されます。
これらの変更は、Go言語のコンパイラが、より正確に、より効率的にソースコードを解析し、処理するための基盤を強化するものであり、言語の進化と安定化に不可欠なステップでした。
関連リンク
- Go言語のASTに関する公式ドキュメント(現在のバージョン):
- Go言語のコンパイラ設計に関する一般的な情報:
参考にした情報源リンク
- Go言語の公式リポジトリのコミット履歴:
- 抽象構文木(AST)に関する一般的な情報:
- Visitorパターンに関する一般的な情報: