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

[インデックス 1775] ファイルの概要

このコミットは、Go言語のコードを整形して表示する「pretty-printer」に関連するファイル群に修正を加えています。主な変更は以下の2つのファイルに集中しています。

  • usr/gri/pretty/printer.go: Goの抽象構文木(AST)を走査し、整形されたコードを出力するロジックを担うファイルです。このコミットでは、関数の宣言を処理するロジックが改善され、特にパッケージのインターフェース(関数シグネチャのみ)を抽出する機能が追加されました。
  • usr/gri/pretty/template.html: printer.goが整形したコードを埋め込むためのHTMLテンプレートファイルです。このコミットでは、新しく抽出されたパッケージインターフェースを表示するためのプレースホルダーが追加されました。

これらの変更により、Goパッケージの公開インターフェースを自動的に抽出し、整形された形式で表示する基盤が構築されています。

コミット

weekend snapshot
- fixed a minor bug
- some initial code to extract interface of a package

R=r
OCL=25866
CL=25866

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/63090761589d4f4330c48f6e66df5d6b856fa051

元コミット内容

commit 63090761589d4f4330c48f6e66df5d6b856fa051
Author: Robert Griesemer <gri@golang.org>
Date:   Fri Mar 6 16:54:26 2009 -0800

    weekend snapshot
    - fixed a minor bug
    - some initial code to extract interface of a package
    
    R=r
    OCL=25866
    CL=25866
---
 usr/gri/pretty/printer.go    | 26 +++++++++++++++++++++++---\
 usr/gri/pretty/template.html |  1 +\
 2 files changed, 24 insertions(+), 3 deletions(-)\

diff --git a/usr/gri/pretty/printer.go b/usr/gri/pretty/printer.go
index 009dde35cb..5a75483fb2 100644
--- a/usr/gri/pretty/printer.go
+++ b/usr/gri/pretty/printer.go
@@ -1016,7 +1016,7 @@ func (P *Printer) DoVarDecl(d *ast.VarDecl) {\
 }\
 \
 \
-func (P *Printer) DoFuncDecl(d *ast.FuncDecl) {\
+func (P *Printer) funcDecl(d *ast.FuncDecl, with_body bool) {\
 	P.Token(d.Pos_, token.FUNC);\
 	P.separator = blank;\
 	if recv := d.Recv; recv != nil {\
@@ -1032,7 +1032,7 @@ func (P *Printer) DoFuncDecl(d *ast.FuncDecl) {\
 	}\
 	P.Expr(d.Ident);\
 	P.Signature(d.Sig);\
-\tif d.Body != nil {\
+\tif with_body && d.Body != nil {\
 \t\tP.separator = blank;\
 \t\tP.Block(d.Body, true);\
 \t}\
@@ -1040,6 +1040,11 @@ func (P *Printer) DoFuncDecl(d *ast.FuncDecl) {\
 }\
 \
 \
+func (P *Printer) DoFuncDecl(d *ast.FuncDecl) {\
+\tP.funcDecl(d, true);\
+}\
+\
+\
 func (P *Printer) DoDeclList(d *ast.DeclList) {\
 \tif !*def || d.Tok == token.IMPORT || d.Tok == token.VAR {\
 \t\tP.Token(d.Pos, d.Tok);\
@@ -1073,6 +1078,20 @@ func (P *Printer) Decl(d ast.Decl) {\
 }\
 \
 \
+// ----------------------------------------------------------------------------\
+// Interface\
+\
+func (P *Printer) Interface(p *ast.Program) {\
+\tfor i := 0; i < len(p.Decls); i++ {\
+\t\tdecl := p.Decls[i];\
+\t\t// TODO use type switch\
+\t\tif fun, is_fun := decl.(*ast.FuncDecl); is_fun {\
+\t\t\tP.funcDecl(fun, false);\
+\t\t}\
+\t}\
+}\
+\
+\
 // ----------------------------------------------------------------------------\
 // Program\
 \
@@ -1110,7 +1129,8 @@ func Print(writer io.Write, html bool, prog *ast.Program) {\
 \
 \tif P.html {\
 \t\terr := templ.Apply(text, \"<!--\", template.Substitution {\
-\t\t\t\"PACKAGE-->\" : func() { /* P.Expr(prog.Ident); */ },\
+\t\t\t\"PACKAGE-->\" : func() { P.Printf(\"%s\", prog.Ident.Str); },\
+\t\t\t\"INTERFACE-->\" : func() { P.Interface(prog); },\
 \t\t\t\"BODY-->\" : func() { P.Program(prog); },\
 \t\t});\
 \t\tif err != nil {\
diff --git a/usr/gri/pretty/template.html b/usr/gri/pretty/template.html
index 4689bee64b..71126499b6 100644
--- a/usr/gri/pretty/template.html
+++ b/usr/gri/pretty/template.html
@@ -1,3 +1,4 @@\
+\
 <h1><!--PACKAGE--></h1>\
 \
 <pre>\

変更の背景

このコミットの主な目的は、Go言語のパッケージからその「インターフェース」を抽出する機能の初期実装を提供することです。ここでいうインターフェースとは、パッケージが外部に公開する関数やメソッドのシグネチャ(引数と戻り値の型、関数名など)の集合を指します。

このような機能は、以下のような目的で重要となります。

  1. ドキュメンテーション生成: パッケージのAPIドキュメントを自動生成する際に、関数やメソッドのシグネチャは必須の情報です。
  2. APIリファレンス: 開発者がパッケージの利用方法を素早く理解するためのリファレンスとして、インターフェース一覧は非常に有用です。
  3. 静的解析ツール: パッケージ間の依存関係を分析したり、互換性をチェックしたりする静的解析ツールにおいて、インターフェース情報は基盤となります。
  4. コードの概要表示: 大規模なコードベースにおいて、特定のパッケージが提供する機能を一目で把握するために、そのインターフェースを簡潔に表示できることは開発効率向上に寄与します。

このコミットは、Go言語の初期段階における「pretty-printer」の一部として、コードの表示方法を柔軟にするためのステップであり、将来的なツール開発の基盤を築くものです。

前提知識の解説

このコミットを理解するためには、以下のGo言語および関連技術の概念を把握しておく必要があります。

  1. Go言語の抽象構文木 (AST):

    • Goコンパイラは、ソースコードを直接解釈するのではなく、まずソースコードを解析して「抽象構文木 (Abstract Syntax Tree, AST)」と呼ばれるツリー構造のデータ表現に変換します。
    • ASTは、プログラムの構造を抽象的に表現したもので、各ノードがコードの要素(変数宣言、関数宣言、式、文など)に対応します。
    • Go言語では、go/astパッケージがASTのデータ構造を提供します。例えば、ast.FuncDeclは関数宣言を表し、ast.Programはプログラム全体(複数のファイルやパッケージを含む)を表します。
    • このコミットで変更されているprinter.goは、このASTを受け取り、それを整形されたコードとして出力する役割を担っています。
  2. Go言語のインターフェース (Interface):

    • Go言語におけるインターフェースは、メソッドのシグネチャの集合を定義する型です。ある型がインターフェースのすべてのメソッドを実装していれば、その型はそのインターフェースを満たしていると見なされます。
    • このコミットで言及される「パッケージのインターフェース」とは、Go言語の型システムにおけるインターフェース型そのものではなく、パッケージが外部に公開している関数やメソッドの「シグネチャ」の集合を指します。つまり、そのパッケージが提供する機能の「顔」となる部分です。
  3. コードの「Pretty-Printing」:

    • ソースコードを読みやすく、一貫したスタイルで整形して表示するプロセスを指します。インデント、空白、改行などを適切に配置することで、コードの可読性を高めます。
    • usr/gri/pretty/ディレクトリは、Go言語のコードを整形して表示するためのツール群の一部であることが示唆されます。
  4. Go言語のtext/templateパッケージ (推測):

    • template.htmlというファイル名と、templ.Applyという関数呼び出しから、Goの標準ライブラリであるtext/templateパッケージ(または類似のテンプレートエンジン)が使用されている可能性が高いです。
    • このパッケージは、プレースホルダーを含むテキストテンプレートをデータで埋めることで、動的なテキスト(この場合はHTML)を生成するために使用されます。<!--PACKAGE--><!--INTERFACE-->のようなコメント形式のプレースホルダーは、このようなテンプレートシステムでよく見られます。

技術的詳細

このコミットの技術的な変更点は、主にusr/gri/pretty/printer.goにおける関数宣言の処理ロジックの再構築と、パッケージインターフェース抽出機能の追加、そしてそれらをHTMLテンプレートに統合する部分にあります。

  1. DoFuncDeclからfuncDeclへのリファクタリングとwith_bodyパラメータの導入:

    • 元のDoFuncDecl関数は、常にGoの関数宣言(ast.FuncDecl)を受け取り、その関数名、シグネチャ、そして関数本体を整形して出力していました。
    • このコミットでは、DoFuncDeclfuncDeclという新しいプライベートなヘルパー関数にリファクタリングされました。
    • funcDeclは、d *ast.FuncDeclに加えてwith_body boolという新しいブール型パラメータを受け取るようになりました。
    • このwith_bodyパラメータがtrueの場合、d.Bodyが存在すれば関数本体も出力されます。falseの場合、関数本体は出力されません。
    • これにより、関数宣言の整形ロジックを再利用しつつ、関数本体の有無を制御できる柔軟性が生まれました。これは、インターフェース抽出(本体不要)と通常のコード表示(本体必要)の両方に対応するために不可欠な変更です。
  2. 新しいDoFuncDeclの導入:

    • funcDeclがプライベートなヘルパー関数になったため、外部から呼び出される元のDoFuncDeclのシグネチャと振る舞いを維持するために、新しいfunc (P *Printer) DoFuncDecl(d *ast.FuncDecl)が追加されました。
    • この新しいDoFuncDeclは、単にP.funcDecl(d, true)を呼び出します。これにより、従来のDoFuncDeclが常に関数本体を出力していた振る舞いが維持されます。
  3. Interfaceメソッドの実装:

    • func (P *Printer) Interface(p *ast.Program)という新しいメソッドが追加されました。これがパッケージインターフェース抽出の核心です。
    • このメソッドは、*ast.Program(Goプログラム全体のAST)を受け取ります。
    • p.Decls(プログラム内のすべてのトップレベル宣言のリスト)をループで走査します。
    • 各宣言が*ast.FuncDecl(関数宣言)であるかどうかを型アサーションdecl.(*ast.FuncDecl)でチェックします。
    • 関数宣言が見つかった場合、P.funcDecl(fun, false)を呼び出します。ここでwith_bodyfalseを渡すことで、関数本体は出力されず、関数シグネチャのみが整形されて出力されます。
    • これにより、プログラム内のすべてのトップレベル関数のシグネチャが抽出され、整形された形で出力されるようになります。
  4. Print関数とtemplate.htmlの連携強化:

    • Print関数は、整形されたGoコードをHTMLテンプレートに適用して最終的な出力を生成する役割を担っています。
    • template.htmlには、<!--PACKAGE--><!--INTERFACE-->という新しいプレースホルダーが追加されました。
    • Print関数内のtempl.Apply呼び出しにおいて、これらのプレースホルダーに対応する匿名関数が追加されました。
      • "PACKAGE-->" : func() { P.Printf("%s", prog.Ident.Str); }: パッケージ名を整形して出力する部分です。元のコードではコメントアウトされていましたが、このコミットで有効化され、prog.Ident.Str(プログラムの識別子、つまりパッケージ名)を直接出力するように修正されました。これは「minor bug fix」の一部である可能性があります。
      • "INTERFACE-->" : func() { P.Interface(prog); }: 新しく追加されたInterfaceメソッドを呼び出し、抽出されたパッケージインターフェースをHTML出力に埋め込みます。

これらの変更により、printer.goは関数宣言の整形をより柔軟に行えるようになり、特にパッケージのインターフェースを抽出してHTML出力に含める機能が実現されました。

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

usr/gri/pretty/printer.go

--- a/usr/gri/pretty/printer.go
+++ b/usr/gri/pretty/printer.go
@@ -1016,7 +1016,7 @@ func (P *Printer) DoVarDecl(d *ast.VarDecl) {
 }
 
 
-func (P *Printer) DoFuncDecl(d *ast.FuncDecl) {
+func (P *Printer) funcDecl(d *ast.FuncDecl, with_body bool) {
 	P.Token(d.Pos_, token.FUNC);
 	P.separator = blank;
 	if recv := d.Recv; recv != nil {
@@ -1032,7 +1032,7 @@ func (P *Printer) DoFuncDecl(d *ast.FuncDecl) {
 	}
 	P.Expr(d.Ident);
 	P.Signature(d.Sig);
-	if d.Body != nil {
+	if with_body && d.Body != nil {
 		P.separator = blank;
 		P.Block(d.Body, true);
 	}
@@ -1040,6 +1040,11 @@ func (P *Printer) DoFuncDecl(d *ast.FuncDecl) {
 }
 
 
+func (P *Printer) DoFuncDecl(d *ast.FuncDecl) {
+	P.funcDecl(d, true);
+}
+
+
 func (P *Printer) DoDeclList(d *ast.DeclList) {
 	if !*def || d.Tok == token.IMPORT || d.Tok == token.VAR {
 		P.Token(d.Pos, d.Tok);
@@ -1073,6 +1078,20 @@ func (P *Printer) Decl(d ast.Decl) {
 }
 
 
+// ----------------------------------------------------------------------------
+// Interface
+
+func (P *Printer) Interface(p *ast.Program) {
+	for i := 0; i < len(p.Decls); i++ {
+		decl := p.Decls[i];
+		// TODO use type switch
+		if fun, is_fun := decl.(*ast.FuncDecl); is_fun {
+			P.funcDecl(fun, false);
+		}
+	}
+}
+
+
 // ----------------------------------------------------------------------------
 // Program
 
@@ -1110,7 +1129,8 @@ func Print(writer io.Write, html bool, prog *ast.Program) {
 
 	if P.html {
 		err := templ.Apply(text, "<!--", template.Substitution {
-			"PACKAGE-->" : func() { /* P.Expr(prog.Ident); */ },
+			"PACKAGE-->" : func() { P.Printf("%s", prog.Ident.Str); },
+			"INTERFACE-->" : func() { P.Interface(prog); },
 			"BODY-->" : func() { P.Program(prog); },
 		});
 		if err != nil {

usr/gri/pretty/template.html

--- a/usr/gri/pretty/template.html
+++ b/usr/gri/pretty/template.html
@@ -1,3 +1,4 @@
+
 <h1><!--PACKAGE--></h1>
 
 <pre>

コアとなるコードの解説

usr/gri/pretty/printer.go

  1. func (P *Printer) funcDecl(d *ast.FuncDecl, with_body bool):

    • この関数は、Goの関数宣言(ast.FuncDecl)を整形して出力する主要なロジックを含んでいます。
    • with_bodyという新しいブール型引数が追加され、これがtrueの場合にのみ関数本体(d.Body)が出力されるようになりました。これにより、関数シグネチャのみを抽出する際に本体をスキップできるようになります。
    • この変更は、関数整形ロジックの再利用性を高め、インターフェース抽出機能の基盤となります。
  2. func (P *Printer) DoFuncDecl(d *ast.FuncDecl):

    • 元のDoFuncDecl関数がfuncDeclにリファクタリングされた後、外部からの呼び出し互換性を保つために、この新しいDoFuncDeclが導入されました。
    • この関数は、単にP.funcDecl(d, true)を呼び出します。これは、従来のDoFuncDeclが常に関数本体を含めて出力していた振る舞いを維持することを意味します。
  3. func (P *Printer) Interface(p *ast.Program):

    • この新しいメソッドは、Goプログラム全体のAST(ast.Program)を受け取り、その中のすべてのトップレベル関数宣言を走査します。
    • 各関数宣言に対して、P.funcDecl(fun, false)を呼び出します。ここでfalseを渡すことで、関数本体は出力されず、関数シグネチャ(インターフェース)のみが整形されて出力されます。
    • この関数が、パッケージのインターフェースを抽出する主要なロジックを実装しています。
  4. func Print(writer io.Write, html bool, prog *ast.Program) 内の変更:

    • templ.Apply関数への引数として、新しいtemplate.Substitutionエントリが追加されました。
    • "PACKAGE-->" : func() { P.Printf("%s", prog.Ident.Str); }: パッケージ名をHTMLテンプレートに挿入するロジックが有効化されました。
    • "INTERFACE-->" : func() { P.Interface(prog); }: template.html内の<!--INTERFACE-->プレースホルダーに対応し、新しく実装されたP.Interface(prog)メソッドを呼び出すことで、抽出されたパッケージインターフェースがHTML出力に埋め込まれるようになりました。

usr/gri/pretty/template.html

  1. 新しい行の追加と<!--PACKAGE--><!--INTERFACE-->の配置:
    • ファイルの先頭に新しい空行が追加されました。
    • <h1><!--PACKAGE--></h1>という行が追加され、パッケージ名を表示するためのプレースホルダーが導入されました。
    • このコミットの差分には直接表示されていませんが、<!--INTERFACE-->というプレースホルダーがこのHTMLテンプレートのどこかに(おそらく<h1>タグの下か<pre>タグの上あたりに)追加されたと推測されます。これはprinter.goPrint関数でP.Interface(prog)が呼び出される場所に対応します。これにより、生成されるHTMLページにパッケージのインターフェース一覧が表示されるようになります。

これらの変更は、Goコードの整形ツールが、単にコードを整形するだけでなく、その構造(特にAPIインターフェース)を抽出し、よりリッチな形式で表示できるような機能拡張の第一歩を示しています。

関連リンク

参考にした情報源リンク

  • Go言語のASTに関する一般的な解説記事
  • Go言語のテンプレートエンジンの使用例に関する記事
  • Go言語の初期のコミット履歴(GitHubリポジトリ)