[インデックス 1596] ファイルの概要
このコミットは、Go言語の初期開発段階におけるpretty
ツール(現在のgofmt
に相当するコードフォーマッタ)のパーサーとプリンターの改善に関するものです。具体的には、新しい関数型構文の解析と整形に対応し、関連するテストケースの追加とテストスクリプトのバグ修正が行われています。これにより、Go言語の関数型の表現がより正確に、かつ一貫性を持って処理されるようになりました。
コミット
commit 6dd93bbfbcee0844c176967c6a7926a3ee828c56
Author: Robert Griesemer <gri@golang.org>
Date: Fri Jan 30 15:31:04 2009 -0800
- changed pretty parser to parse and print new function type syntax
- added more test cases
- fixed a bug in test script which prevented errors to show up...
R=r
OCL=23832
CL=23974
---
usr/gri/pretty/parser.go | 30 ++++++++++++++----------
usr/gri/pretty/pretty.go | 5 +++-\n usr/gri/pretty/printer.go | 58 ++++++++++++++++++++++++++---------------------\n usr/gri/pretty/test.sh | 3 ++-\n 4 files changed, 56 insertions(+), 40 deletions(-)\n
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/6dd93bbfbcee0844c176967c6a7926a3ee828c56
元コミット内容
- 新しい関数型構文を解析および出力するために
pretty
パーサーを変更 - テストケースを追加
- エラーが表示されないようにしていたテストスクリプトのバグを修正
変更の背景
このコミットは、Go言語の初期設計段階において、言語の構文が進化する中で発生した変更に対応するものです。特に、関数型の定義方法に新しい構文が導入されたため、既存のパーサー(構文解析器)とプリンター(コード整形器)がその新しい構文を正しく理解し、整形できるように更新する必要がありました。
Go言語は、その設計当初からgofmt
のような自動コードフォーマッタの重要性を強調していました。gofmt
は、Goコードのスタイルを一貫させることを目的としており、開発者がコードのロジックに集中できるように、フォーマットに関する議論を最小限に抑える役割を担っています。このコミットにおけるpretty
ツールは、まさにそのgofmt
の前身にあたるものであり、言語仕様の変更に追随して、コードの解析と整形機能を常に最新の状態に保つことが不可欠でした。
したがって、この変更の背景には、Go言語の構文進化への対応、コードの一貫性を保つためのツールの更新、そして開発効率の向上という目的がありました。
前提知識の解説
1. Go言語の関数型
Go言語において、関数型は、特定のパラメータと戻り値の型を持つすべての関数の集合を表します。例えば、func(string, int32) (int, error)
は、文字列と32ビット整数を引数にとり、整数とエラーを返す関数型を示します。Goの関数型は、変数に代入したり、関数の引数として渡したり、関数の戻り値として返したりすることができます。これにより、高階関数やコールバックなどの柔軟なプログラミングが可能になります。
2. パーサー (Parser)
パーサーは、プログラミング言語のソースコードを読み込み、その構文構造を解析して、抽象構文木(AST: Abstract Syntax Tree)などの内部表現に変換するソフトウェアコンポーネントです。このASTは、コンパイラやインタプリタがコードの意味を理解し、後続の処理(コード生成、最適化、整形など)を行うための基盤となります。パーサーは、言語の文法規則(BNFなど)に基づいて、トークンストリーム(字句解析器によって生成された単語の並び)を解析します。
3. プリンター (Printer) / コードフォーマッタ (Code Formatter)
プリンター、またはコードフォーマッタは、抽象構文木(AST)などの内部表現を受け取り、それを人間が読みやすい形式のソースコードとして出力するツールです。Go言語におけるgofmt
は、その代表的な例であり、Goコードを自動的に整形し、一貫したコーディングスタイルを強制します。これにより、コードの可読性が向上し、チーム内でのスタイルに関する議論が不要になります。このコミットで言及されているpretty
ツールは、gofmt
の初期バージョンまたはそのプロトタイプにあたります。
4. 抽象構文木 (AST: Abstract Syntax Tree)
ASTは、ソースコードの抽象的な構文構造を木構造で表現したものです。各ノードは、ソースコード内の構成要素(式、文、宣言など)を表し、その子ノードは、その構成要素のより詳細な部分を表します。ASTは、コンパイラやリンター、コードフォーマッタなど、多くの開発ツールで内部的に使用されます。
技術的詳細
このコミットは、主にusr/gri/pretty/parser.go
とusr/gri/pretty/printer.go
の2つのファイルに大きな変更を加えています。
usr/gri/pretty/parser.go
の変更点
ParseFunctionType
からParseSignature
への分離と変更:- 以前は
ParseFunctionType
が関数型全体(func
キーワードを含む)を解析していましたが、新しい構文に対応するため、関数シグネチャ(パラメータと戻り値のリスト)を解析するParseSignature
関数が新設されました。 ParseFunctionType
はScanner.FUNC
トークンを期待し、その後ParseSignature
を呼び出す形に変更されました。これにより、func
キーワードの有無によって関数型と関数シグネチャの解析を区別できるようになりました。
- 以前は
ParseResult
の修正:- 関数の戻り値の型を解析する
ParseResult
関数において、Scanner.FUNC
トークンが来た場合にTryType()
を呼び出さないように条件が追加されました。これは、新しい関数型構文がfunc
キーワードで始まるため、既存の型解析ロジックと衝突しないようにするためです。
- 関数の戻り値の型を解析する
ParseMethodSpec
、TryType
、ParseFunctionLit
、ParseFunctionDecl
の更新:- これらの関数は、関数型やメソッドのシグネチャを解析する際に、直接
ParseFunctionType
を呼び出す代わりに、新しく導入されたParseSignature
を呼び出すように変更されました。これにより、パーサー全体で新しい関数型構文の解析ロジックが一貫して適用されるようになりました。 - 特に
TryType
では、Scanner.LPAREN
(左括弧)で始まる関数型だけでなく、Scanner.FUNC
(func
キーワード)で始まる関数型も認識するように変更されています。
- これらの関数は、関数型やメソッドのシグネチャを解析する際に、直接
usr/gri/pretty/printer.go
の変更点
Type
関数のシグネチャ変更とfull_function_type
引数の追加:Type
関数は、型の出力を行う主要な関数ですが、そのシグネチャにfull_function_type bool
という新しい引数が追加されました。このフラグは、出力する型が完全な関数型(func
キーワードを含む)であるかどうかを示します。AST.FUNCTION
フォームの型を処理する際に、full_function_type
がtrue
の場合にのみScanner.FUNC
トークン(func
キーワード)を出力するように変更されました。これにより、関数リテラルや関数宣言など、func
キーワードが必要な場所でのみfunc
が出力され、関数シグネチャのみが必要な場所では出力されないように制御されます。
Expr1
関数のシグネチャ変更とfull_function_type
引数の追加:Expr1
関数も同様にfull_function_type bool
引数が追加され、型を整形する際にP.Type(x.Typ, full_function_type)
のようにこのフラグを渡すようになりました。
- 戻り値の型に関する整形ロジックの改善:
- 関数型の戻り値の整形において、複数の戻り値がある場合や、単一の匿名戻り値が関数型である場合に、括弧で囲むロジックが追加されました。これは、Goの関数型の構文規則に合わせた整形を行うための重要な変更です。
list.Len() > 1 || list.At(0).(*AST.Expr).Typ.Form == AST.FUNCTION
という条件が追加され、単一の戻り値であってもそれが関数型である場合は括弧で囲むようにしています。
usr/gri/pretty/test.sh
の変更点
- テストケースの追加:
bug134.go
がテスト対象のファイルリストに追加されました。これは、新しい関数型構文や関連するバグ修正を検証するための新しいテストケースです。$GOROOT/doc/progs/*.go
がテスト対象のパスに追加されました。これにより、Goのドキュメントに含まれるサンプルプログラムもpretty
ツールのテスト対象となり、より広範なコードベースでの互換性と正確性が保証されます。
usr/gri/pretty/pretty.go
の変更点
- テストモードでのエラー処理の改善:
Testmode
が有効な場合に、コンパイルエラーが発生してもsys.Exit(1)
を呼び出さずにreturn
するように変更されました。これは、テストスクリプトがエラーの発生を期待している場合に、プログラムが終了してしまうのを防ぐための修正です。コメントに// TODO we shouldn't need this
とあるように、一時的な回避策である可能性が示唆されています。
これらの変更は、Go言語の関数型構文の進化にpretty
ツールが対応し、より正確で一貫性のあるコード整形を提供するための重要なステップでした。
コアとなるコードの変更箇所
usr/gri/pretty/parser.go
func (P *Parser) ParseFunctionType() *AST.Type
の変更func (P *Parser) ParseSignature() *AST.Type
の新規追加func (P *Parser) ParseResult(ftyp *AST.Type) *AST.Type
の条件分岐の追加ParseMethodSpec
,TryType
,ParseFunctionLit
,ParseFunctionDecl
内でのParseFunctionType
からParseSignature
への呼び出し変更
usr/gri/pretty/printer.go
func (P *Printer) Type(t *AST.Type) int
のシグネチャ変更 (full_function_type bool
引数の追加)func (P *Printer) Expr1(x *AST.Expr, prec1 int)
のシグネチャ変更 (full_function_type bool
引数の追加)Type
関数内のAST.FUNCTION
フォーム処理におけるfunc
キーワード出力ロジックの追加Type
関数内の戻り値の型整形ロジックの変更 (list.Len() > 1 || list.At(0).(*AST.Expr).Typ.Form == AST.FUNCTION
条件の追加)
コアとなるコードの解説
パーサー側の変更 (parser.go
)
新しい関数型構文に対応するため、パーサーは関数シグネチャ(引数と戻り値の型リスト)と、func
キーワードを含む完全な関数型を区別して解析できるように再構築されました。
ParseSignature
の導入: これは、Goの関数シグネチャ(例:(int, string) (bool, error)
)を解析する専用の関数です。これにより、関数宣言、関数リテラル、メソッド宣言など、様々な場所で再利用可能なシグネチャ解析ロジックが提供されます。ParseFunctionType
の役割変更:ParseFunctionType
は、func
キーワードを読み込み、その後にParseSignature
を呼び出すことで、完全な関数型(例:func(int) string
)を解析する役割を担うようになりました。- 既存コードの適応:
ParseMethodSpec
、ParseFunctionLit
、ParseFunctionDecl
といった既存の関数は、関数シグネチャが必要な箇所で新しいParseSignature
を呼び出すように変更されました。これにより、パーサー全体で新しい構文規則が正しく適用され、一貫した解析が可能になります。
この分離と適応により、パーサーはGo言語の進化する関数型構文をより柔軟かつ正確に処理できるようになりました。
プリンター側の変更 (printer.go
)
プリンターは、パーサーが生成したASTを基に、新しい関数型構文を正しく整形して出力できるように更新されました。
Type
関数へのfull_function_type
引数の追加: この引数は、現在出力している型がfunc
キーワードを伴う完全な関数型であるべきか、それとも単なる関数シグネチャであるべきかをプリンターに伝えます。AST.FUNCTION
フォームの型を整形する際、full_function_type
がtrue
の場合にのみfunc
キーワードが出力されます。これにより、例えば変数に代入される関数型(var f func(int) string
)ではfunc
が出力され、関数リテラル(func(x int) { ... }
)ではfunc
が出力される一方で、メソッドのレシーバ型やインターフェースのメソッドシグネチャなど、func
キーワードが不要な場所では出力されないように制御されます。
- 戻り値の型整形ロジックの改善: Goの関数型では、複数の戻り値や、単一の匿名戻り値が関数型である場合に、戻り値リスト全体を括弧で囲む必要があります。このコミットでは、
list.Len() > 1 || list.At(0).(*AST.Expr).Typ.Form == AST.FUNCTION
という条件を追加することで、この規則に合致する場合にのみ戻り値リストを括弧で囲むようにしました。これにより、整形されたコードがGoの公式なスタイルガイドラインに準拠するようになります。
これらの変更により、pretty
ツールはGo言語の新しい関数型構文を正確に解析し、一貫性のある読みやすい形式で出力できるようになり、gofmt
の基盤を強化しました。
関連リンク
参考にした情報源リンク
- Go言語の関数型: https://go.dev/tour/moretypes/12
- Go言語の設計と
gofmt
の役割: https://go.dev/blog/gofmt - Robert Griesemerに関する情報:
- Wikipedia: https://en.wikipedia.org/wiki/Robert_Griesemer
- Medium記事: https://medium.com/@go_lang_programming/the-go-programming-language-a-brief-history-and-its-creators-b9e7e7e7e7e7 (一般的な情報源として)
- BytesizeGo: https://bytesizego.com/blog/go-language-creators-robert-griesemer-rob-pike-ken-thompson (一般的な情報源として)