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

[インデックス 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.gousr/gri/pretty/printer.goの2つのファイルに大きな変更を加えています。

usr/gri/pretty/parser.goの変更点

  • ParseFunctionTypeからParseSignatureへの分離と変更:
    • 以前はParseFunctionTypeが関数型全体(funcキーワードを含む)を解析していましたが、新しい構文に対応するため、関数シグネチャ(パラメータと戻り値のリスト)を解析するParseSignature関数が新設されました。
    • ParseFunctionTypeScanner.FUNCトークンを期待し、その後ParseSignatureを呼び出す形に変更されました。これにより、funcキーワードの有無によって関数型と関数シグネチャの解析を区別できるようになりました。
  • ParseResultの修正:
    • 関数の戻り値の型を解析するParseResult関数において、Scanner.FUNCトークンが来た場合にTryType()を呼び出さないように条件が追加されました。これは、新しい関数型構文がfuncキーワードで始まるため、既存の型解析ロジックと衝突しないようにするためです。
  • ParseMethodSpecTryTypeParseFunctionLitParseFunctionDeclの更新:
    • これらの関数は、関数型やメソッドのシグネチャを解析する際に、直接ParseFunctionTypeを呼び出す代わりに、新しく導入されたParseSignatureを呼び出すように変更されました。これにより、パーサー全体で新しい関数型構文の解析ロジックが一貫して適用されるようになりました。
    • 特にTryTypeでは、Scanner.LPAREN(左括弧)で始まる関数型だけでなく、Scanner.FUNCfuncキーワード)で始まる関数型も認識するように変更されています。

usr/gri/pretty/printer.goの変更点

  • Type関数のシグネチャ変更とfull_function_type引数の追加:
    • Type関数は、型の出力を行う主要な関数ですが、そのシグネチャにfull_function_type boolという新しい引数が追加されました。このフラグは、出力する型が完全な関数型(funcキーワードを含む)であるかどうかを示します。
    • AST.FUNCTIONフォームの型を処理する際に、full_function_typetrueの場合にのみ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)を解析する役割を担うようになりました。
  • 既存コードの適応: ParseMethodSpecParseFunctionLitParseFunctionDeclといった既存の関数は、関数シグネチャが必要な箇所で新しいParseSignatureを呼び出すように変更されました。これにより、パーサー全体で新しい構文規則が正しく適用され、一貫した解析が可能になります。

この分離と適応により、パーサーはGo言語の進化する関数型構文をより柔軟かつ正確に処理できるようになりました。

プリンター側の変更 (printer.go)

プリンターは、パーサーが生成したASTを基に、新しい関数型構文を正しく整形して出力できるように更新されました。

  • Type関数へのfull_function_type引数の追加: この引数は、現在出力している型がfuncキーワードを伴う完全な関数型であるべきか、それとも単なる関数シグネチャであるべきかをプリンターに伝えます。
    • AST.FUNCTIONフォームの型を整形する際、full_function_typetrueの場合にのみ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の基盤を強化しました。

関連リンク

参考にした情報源リンク