Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 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の設計が頻繁に調整されていました。

変更の主な背景としては、以下の点が挙げられます。

  1. 言語仕様の進化への対応: Go言語の構文やセマンティクスが洗練されるにつれて、それを正確に表現できるAST構造が必要とされました。特に、リテラル表現の多様化や、ステートメントの種類の増加に対応するため、既存のAST定義では不十分になったと考えられます。
  2. ASTの整合性と拡張性の向上: 初期段階のASTは、試行錯誤の中で定義された部分も多く、構造的な一貫性や将来的な拡張性において改善の余地がありました。このコミットでは、ASTノードの命名規則の統一(例: StatからStmtへの変更)や、より細分化されたノードタイプの導入により、AST全体の整合性を高め、今後の言語機能追加に備える狙いがあったと推測されます。
  3. コードの明確化と保守性の向上: ASTはコンパイラの中心的なデータ構造であるため、その定義が明確で理解しやすいことは、開発効率と保守性に直結します。不要な抽象化の削除(例: BlockSignatureの直接的な型定義の削除)や、より具体的なノードタイプの導入は、コードの可読性を向上させる目的があったと考えられます。

前提知識の解説

抽象構文木(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ノードの再編成と命名規則の統一にあります。

  1. Stat インターフェースから Stmt インターフェースへの変更:

    • 以前はステートメントを表すインターフェースとしてStatが使われていましたが、より一般的で明確なStmtに名称が変更されました。これに伴い、関連するVisitorインターフェースもStatVisitorからStmtVisitorに、具体的なステートメントノードの型名もBadStatからBadStmtDeclStatからDeclStmtなど、一貫してStatStmtに置き換えられています。これは、Go言語の標準的な命名規則への準拠と、コードベース全体での一貫性を高めるための変更です。
  2. リテラル表現の細分化と明確化:

    • 以前はBasicLitという単一のノードで整数、浮動小数点数、文字、文字列リテラルを扱っていましたが、このコミットにより、それぞれ専用のノードが導入されました。
      • IntLit (整数リテラル)
      • FloatLit (浮動小数点数リテラル)
      • CharLit (文字リテラル)
      • StringLit (単一の文字列リテラル)
    • さらに、複数の隣接する文字列リテラル(Goでは自動的に連結される)を表現するためにStringListノードが追加されました。これにより、ASTがソースコードの具体的なリテラル表現をより正確に反映できるようになり、意味解析フェーズでの処理が容易になります。
  3. Ellipsis ノードの再定義:

    • 可変長引数(...)や配列の長さ指定([...]T)を表すEllipsisノードが、以前はExprインターフェースを実装しない独立した型でしたが、このコミットでExprインターフェースを実装するようになりました。これにより、Ellipsisが式として扱えるようになり、ASTの型システムにおける一貫性が向上しました。
  4. Field 構造体の再定義と用途の明確化:

    • Field構造体は、構造体のフィールド、インターフェースのメソッド、関数のパラメータ/戻り値を表現するために使用されます。このコミットでは、Tagフィールドの型がExprから[]*StringLitに変更され、より具体的な文字列リテラルのリストとしてタグを扱えるようになりました。また、DocフィールドがComments型になり、ドキュメンテーションコメントの扱いが統一されました。
  5. FunctionTypeSignature の統合と簡素化:

    • 以前はSignatureという独立した構造体で関数のパラメータと戻り値を表現し、FunctionTypeがそれを参照する形でした。このコミットでは、Signature構造体が削除され、その内容(ParamsResults)が直接FunctionType構造体内に組み込まれました。これにより、関数型の表現が簡素化され、冗長性が排除されました。
  6. Block 構造体の削除と BlockStmt の導入:

    • 以前はBlockという構造体でブロック({ ... })を表現していましたが、これが削除され、BlockStmtという新しいステートメントノードが導入されました。これにより、ブロックが明確にステートメントの一種として扱われるようになり、ASTの階層構造がより論理的になりました。
  7. Pos() メソッドの改善:

    • 多くのASTノードでPos()メソッドの実装が変更され、より正確な位置情報を返すようになりました。特に、FunctionLitCompositeLitなど、複合的な構造を持つノードのPos()が、その開始位置をより適切に指すように修正されています。

これらの変更は、Go言語のASTがより堅牢で、表現力豊かであり、コンパイラの各フェーズで効率的に利用できるような基盤を構築するための重要なステップでした。

コアとなるコードの変更箇所

このコミットの主要な変更は、usr/gri/pretty/ast.go ファイル内の以下の構造体とインターフェースの定義に集中しています。

  1. 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インターフェース名が変更されています。

  2. リテラルノードの細分化:

    --- 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が新設されました。

  3. FunctionLitFunctionType の変更:

    --- 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が削除され、FunctionTypeParamsResultsが直接含まれるようになりました。また、Field構造体のTagフィールドの型が変更されています。

  4. 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ノードとして構築し、その後のフェーズで連結処理を行うことができます。
  • FunctionLitFunctionType の変更:

    • Signature構造体を削除し、その内容をFunctionTypeに直接組み込むことで、関数型の表現が簡素化されました。これは、ASTのノード数を減らし、データ構造をよりフラットにすることで、メモリ使用量の削減やASTの走査効率の向上に寄与します。また、関数リテラル(FunctionLit)がFunctionTypeを直接参照するようになり、ASTの構造がより直感的になりました。
  • Block の削除と BlockStmt の導入:

    • Block構造体は、波括弧で囲まれたステートメントのリストを表していましたが、これがBlockStmtというステートメントノードに置き換えられました。この変更は、ブロックがプログラムの実行フローの一部である「ステートメント」として明確に位置づけられることを意味します。これにより、ASTの階層構造がより論理的になり、ステートメントを処理するVisitorパターンなどの実装が簡素化されます。

これらの変更は、Go言語のコンパイラが、より正確に、より効率的にソースコードを解析し、処理するための基盤を強化するものであり、言語の進化と安定化に不可欠なステップでした。

関連リンク

参考にした情報源リンク