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

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

このコミットは、Go言語の初期開発段階における、抽象構文木(AST)の処理、コードの整形(プリンター)、およびドキュメント生成に関する大規模なリファクタリングと機能強化を記録しています。特に、parser.goprinter.goという主要なファイルの削除と、astprinter.goおよびdocprinter.goへの機能の再配分が特徴的です。これにより、Go言語のツールチェインにおけるコード解析とドキュメント生成の基盤がよりモジュール化され、整理されました。

コミット

commit 8f628f4955a7b2a70ac7fbdcdf7225cf566d1000
Author: Robert Griesemer <gri@golang.org>
Date:   Tue Mar 31 16:53:58 2009 -0700

    daily snapshot:
    - adjustments to match new ast/parser interface
    - removed printer.go; functionality now in astprinter.go and docprinter.go
      (more cleanups pending)
    - enabled new doc printing in gds
      (lots of fine tuning missing, but pieces falling into place; e.g. methods
      associated with types. Consts, Vars, to come. Collection of all files
      belonging to a package to come)
    
    R=r
    OCL=26970
    CL=26972

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

https://github.com/golang/go/commit/8f628f4955a7b2a70ac7fbdcdf7225cf566d1000

元コミット内容

このコミットは、Go言語の「daily snapshot」として記録されており、以下の主要な変更点を含んでいます。

  • 新しいAST(抽象構文木)/パーサーインターフェースに合わせた調整。
  • printer.goが削除され、その機能がastprinter.godocprinter.goに再配置された。
  • gds(Go Document Server)で新しいドキュメント印刷機能が有効化された。これにより、型に関連付けられたメソッドの表示など、ドキュメント生成の基礎が確立された。

変更の背景

Go言語の初期開発において、コンパイラ、ツール、および標準ライブラリの構造は継続的に進化していました。このコミットが行われた2009年3月は、Go言語が一般に公開される前の段階であり、言語仕様やツールの設計が活発に行われていた時期です。

この変更の背景には、以下の目的があったと考えられます。

  1. モジュール性の向上: printer.goが担っていた多様な印刷機能をastprinter.go(ASTの構造的な印刷)とdocprinter.go(ドキュメント生成のための印刷)に分割することで、各モジュールの責任を明確にし、コードベースの保守性と拡張性を向上させる狙いがありました。
  2. ASTとパーサーの進化への対応: Go言語のAST構造やパーサーのインターフェースが変更されたことに伴い、関連するツールもそれに合わせて更新する必要がありました。これにより、言語の進化に合わせたツールの整合性が保たれます。
  3. ドキュメント生成の強化: gdsにおける新しいドキュメント印刷機能の導入は、Go言語のコードから自動的に高品質なドキュメントを生成する仕組みを構築する上で重要な一歩でした。これは、Go言語の「godoc」ツールの原型となる機能開発の一部と考えられます。初期段階ではまだ「微調整」が必要とされていますが、基本的な枠組みが整えられました。

前提知識の解説

このコミットを理解するためには、以下の概念についての知識が役立ちます。

1. 抽象構文木 (Abstract Syntax Tree, AST)

ASTは、プログラミング言語のソースコードの抽象的な構文構造を木構造で表現したものです。コンパイラやインタプリタがソースコードを解析する際に中間表現として生成します。各ノードはソースコードの構成要素(変数、関数、式、文など)を表し、親子関係はそれらの構文的な包含関係を示します。

Go言語のASTは、go/astパッケージで定義されており、コンパイラ、リンター、コード整形ツール(gofmt)、ドキュメント生成ツール(godoc)など、Go言語の様々なツールで利用される基盤となります。

2. パーサー (Parser)

パーサーは、字句解析器(lexer/scanner)によって生成されたトークン列を入力として受け取り、その言語の文法規則に従ってASTを構築するコンポーネントです。構文エラーの検出もパーサーの重要な役割です。

3. コードプリンター (Code Printer)

コードプリンターは、ASTを入力として受け取り、それを人間が読める形式のソースコードに変換するコンポーネントです。単にコードを再生成するだけでなく、特定のコーディングスタイル(インデント、スペース、改行など)を適用してコードを整形する役割も担います。Go言語におけるgofmtは、このプリンター機能の代表的な例です。

4. ドキュメント生成 (Documentation Generation)

プログラミング言語において、ソースコードから自動的にドキュメントを生成する仕組みは非常に重要です。Go言語では、godocツールがこの役割を担っています。godocは、ソースコード内のコメントや宣言から情報を抽出し、それを整形してHTMLなどの形式で表示します。これにより、開発者はコードとドキュメントを密接に連携させ、常に最新のドキュメントを維持することができます。

5. gds (Go Document Server)

コミットメッセージに登場するgdsは、おそらくGo Document Serverの略称であり、Go言語のドキュメントをWebブラウザ経由で提供するための初期のプロトタイプまたはコンポーネントであると推測されます。これは現在のgodocコマンドが提供するWebサーバー機能の先駆けとなるものです。

技術的詳細

このコミットの技術的な詳細を掘り下げます。

1. AST/パーサーインターフェースの変更

usr/gri/pretty/parser.goが削除されていることから、このコミット以前に存在していたパーサーの実装が完全に置き換えられたか、あるいはその機能が別のモジュールに統合されたことを示唆しています。コミットメッセージの「adjustments to match new ast/parser interface」は、ASTのデータ構造(astパッケージ)や、パーサーがASTを構築する際のAPIが変更されたことを意味します。これにより、ASTを操作する他のコンポーネント(プリンター、型チェッカーなど)も、新しいインターフェースに合わせて修正する必要が生じました。

具体的には、astprinter.gocompilation.goの変更箇所を見ると、ast.FunctionTypeast.FuncTypeに、ast.ChannelTypeast.ChanTypeに、ast.Packageast.Programに変更されていることがわかります。これは、ASTノードの命名規則の変更や、より汎用的な構造への移行を示しています。例えば、ast.Packageからast.Programへの変更は、単一のパッケージだけでなく、複数のパッケージを含むプログラム全体を表現するためのAST構造への適応を意味する可能性があります。

また、astprinter.goでは、noposという変数名がnoPosにキャメルケースで変更されています。これはGo言語のコーディング規約に合わせたリファクタリングの一環と考えられます。

2. printer.goの削除と機能の分割

usr/gri/pretty/printer.goは1413行ものコードが削除されており、これは非常に大規模な変更です。このファイルが担っていた機能は、astprinter.godocprinter.goに分割されました。

  • astprinter.go: このファイルは、ASTをGoのソースコード形式で「印刷」する役割を担います。変更点を見ると、ASTノードのトラバーサルと、それに対応するトークンや文字列の出力ロジックが多数含まれています。例えば、DoFuncTypeDoFuncLitDoChanTypeなどのメソッドが追加または修正されており、新しいASTノードタイプに対応した印刷処理が実装されています。これは、gofmtのようなコード整形ツールの中核となる機能です。
  • docprinter.go: このファイルは、ASTからドキュメントを生成するためのロジックを担います。コミットメッセージにあるように、「methods associated with types. Consts, Vars, to come. Collection of all files belonging to a package to come」といった、ドキュメント生成に必要な情報の収集と整形に関する機能が追加されています。特に、PackageDoc構造体やaddDeclメソッドの変更は、パッケージ内の定数、変数、関数、型、およびそれらに付随するメソッドをどのように収集し、ドキュメントとして表現するかというロジックの進化を示しています。また、HTMLエスケープやコメントのクリーンアップといった、ドキュメント出力に特化したユーティリティ関数もこのファイルに移動されています。

この分割により、コードの整形とドキュメント生成という異なる目的を持つ処理が明確に分離され、それぞれの開発と保守が独立して行えるようになりました。

3. gdsにおける新しいドキュメント印刷の有効化

usr/gri/pretty/gds.goの変更は、gdsが従来のprinterモジュールではなく、新しく導入されたdocprinterモジュールを使用してドキュメントを生成するように切り替わったことを示しています。

  • 以前は*newdocフラグによって新旧のドキュメント印刷を切り替えるロジックがありましたが、このコミットで*newdocフラグが削除され、常にdocprinter.PackageDocを使用するようになりました。
  • doc.AddPackage(prog)doc.AddProgram(prog)に変更されており、これは前述のast.Packageからast.ProgramへのAST構造の変更に対応しています。
  • tabwriterを使用した整形出力の初期化ロジックは維持されており、生成されるドキュメントの可読性を確保しています。

この変更は、Go言語のドキュメント生成システムが、より構造化されたASTベースのアプローチへと移行したことを明確に示しています。

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

主要な変更箇所は以下のファイルに集中しています。

  • usr/gri/pretty/Makefile: ビルド依存関係の更新。printer.6の削除と、astprinter.6docprinter.6の追加。
  • usr/gri/pretty/astprinter.go: ASTの構造的な印刷ロジックの修正と拡張。特に、noposからnoPosへの変更、ast.FunctionTypeからast.FuncTypeへの型参照の変更、および各種ASTノードの印刷メソッドの調整。
  • usr/gri/pretty/compilation.go: Compile関数の戻り値の型が*ast.Packageから*ast.Programに変更。
  • usr/gri/pretty/docprinter.go: ドキュメント生成ロジックの主要な変更。constDoc, varDoc, funcDoc, typeDoc構造体のフィールド名変更(*astからdeclへ)、addDeclメソッドでの型、関数、メソッドの収集ロジックの追加、AddPackageからAddProgramへの変更、およびHTMLエスケープやコメント処理のユーティリティ関数の追加。
  • usr/gri/pretty/gds.go: newdocフラグの削除と、docprinterを使用したドキュメント生成への完全な移行。doc.AddPackageからdoc.AddProgramへの変更。
  • usr/gri/pretty/parser.go: ファイルが削除された。
  • usr/gri/pretty/printer.go: ファイルが削除された。

コアとなるコードの解説

astprinter.goにおける変更の例

--- a/usr/gri/pretty/astprinter.go
+++ b/usr/gri/pretty/astprinter.go
@@ -35,9 +34,8 @@ var (
 )
 
 
-// When we don\'t have a position use nopos.
-// TODO make sure we always have a position.
-var nopos token.Position;
+// When we don\'t have a position use noPos.
+var noPos token.Position;

noposという変数名がnoPosに変更されています。これはGo言語の慣習であるキャメルケース(camelCase)命名規則に合わせたリファクタリングです。token.Positionはソースコード内の位置情報を示す型です。

--- a/usr/gri/pretty/astprinter.go
+++ b/usr/gri/pretty/astprinter.go
@@ -502,7 +500,7 @@ func (P *Printer) Signature(params, result []*ast.Field) {
 	\t\t// single anonymous result
 	\t\t// => no parentheses needed unless it\'s a function type
 	\t\tfld := result[0];
-\t\t\tif dummy, is_ftyp := fld.Type.(*ast.FunctionType); !is_ftyp {
+\t\t\tif dummy, is_ftyp := fld.Type.(*ast.FuncType); !is_ftyp {
 	\t\t\tP.Expr(fld.Type);\
 	\t\t\treturn;\
 	\t\t}\

ast.FunctionTypeast.FuncTypeに置き換えられています。これはASTの型定義が変更されたことに対応するもので、プリンターが新しい型構造を正しく処理できるようにするための修正です。同様の変更が他の多くの箇所でも行われています。

docprinter.goにおける変更の例

--- a/usr/gri/pretty/docprinter.go
+++ b/usr/gri/pretty/docprinter.go
@@ -40,22 +40,22 @@ func hasExportedNames(names []*ast.Ident) bool {
 // ----------------------------------------------------------------------------
 
 type constDoc struct {
-\tcast *ast.ConstDecl;
+\tdecl *ast.ConstDecl;
 }\
 
 
 type varDoc struct {
-\tvast *ast.VarDecl;
+\tdecl *ast.VarDecl;
 }\
 
 
 type funcDoc struct {
-\tfast *ast.FuncDecl;
+\tdecl *ast.FuncDecl;
 }\
 
 
 type typeDoc struct {
-\ttast *ast.TypeDecl;
+\tdecl *ast.TypeDecl;
 	methods map[string] *funcDoc;
 }\

constDoc, varDoc, funcDoc, typeDocといったドキュメント生成用の構造体において、ASTノードを保持するフィールド名がcast, vast, fast, tastから一貫してdeclに変更されています。これにより、コードの可読性と一貫性が向上しています。

--- a/usr/gri/pretty/docprinter.go
+++ b/usr/gri/pretty/docprinter.go
@@ -74,94 +74,284 @@ type PackageDoc struct {
 // The package name is provided as initial argument. Use AddPackage to
 // add the AST for each source file belonging to the same package.
 //
-func (P *PackageDoc) Init(name string) {
-\tP.name = name;\
-\tP.imports = make(map[string] string);\
-\tP.consts = make(map[string] *constDoc);\
-\tP.types = make(map[string] *typeDoc);\
-\tP.vars = make(map[string] *varDoc);\
-\tP.funcs = make(map[string] *funcDoc);\
+func (doc *PackageDoc) Init(name string) {
+\tdoc.name = name;\
+\tdoc.imports = make(map[string] string);\
+\tdoc.consts = make(map[string] *constDoc);\
+\tdoc.types = make(map[string] *typeDoc);\
+\tdoc.vars = make(map[string] *varDoc);\
+\tdoc.funcs = make(map[string] *funcDoc);\
 }
 
 
-func (P *PackageDoc) addDecl(decl ast.Decl) {
-\tswitch d := decl.(type) {\
-\tcase *ast.ImportDecl:\
-\tcase *ast.ConstDecl:\
-\t\tif hasExportedNames(d.Names) {\
-\t\t}\
-+\
-\tcase *ast.TypeDecl:\
-\t\tif isExported(d.Name) {\
-+\t\t\t// TODO only add if not there already - or ignore?\
-+\t\t\tname := string(d.Name.Lit);\
-+\t\t\ttdoc := &typeDoc{d, make(map[string] *funcDoc)};\
-+\t\t\tdoc.types[name] = tdoc;\
-\t\t}\
-+\
-\tcase *ast.VarDecl:\
-\t\tif hasExportedNames(d.Names) {\
-\t\t}\
-+\
-\tcase *ast.FuncDecl:\
-\t\tif isExported(d.Name) {\
-\t\t\tif d.Recv != nil {\
-\t\t\t\t// method\
-+\t\t\t\t// determine receiver type name\
-+\t\t\t\tvar name string;\
-+\t\t\t\tswitch t := d.Recv.Type.(type) {\
-+\t\t\t\tcase *ast.Ident:\
-+\t\t\t\t\tname = string(t.Lit);\
-+\t\t\t\tcase *ast.StarExpr:\
-+\t\t\t\t\t// recv must be of the form *name\
-+\t\t\t\t\tname = string(t.X.(*ast.Ident).Lit)\
-+\t\t\t\t}\
-+\t\t\t\ttyp, found := doc.types[name];\
-+\t\t\t\tif found {\
-+\t\t\t\t\tfdoc := &funcDoc{d};\
-+\t\t\t\t\ttyp.methods[string(d.Name.Lit)] = fdoc;\
-+\t\t\t\t}\
-+\t\t\t\t// otherwise ignore\
-\t\t\t} else {\
-\t\t\t\t// ordinary function\
-+\t\t\t\tfdoc := &funcDoc{d};\
-+\t\t\t\tdoc.funcs[string(d.Name.Lit)] = fdoc;\
-\t\t\t}\
-\t\t}\
-+\
-\tcase *ast.DeclList:\
-\t\tfor i, decl := range d.List {\
-\t\t\tP.addDecl(decl);\
-+\t\t\tdoc.addDecl(decl);\
-\t\t}\
-\t}\
-\ }\

PackageDoc.Initメソッドのレシーバー名がPからdocに変更されています。これはGo言語の慣習に合わせた変更です。 addDeclメソッドでは、エクスポートされた型(ast.TypeDecl)や関数(ast.FuncDecl)をPackageDoc内の適切なマップ(types, funcs)に収集するロジックが追加されています。特に、メソッド(レシーバーを持つ関数)の場合、そのレシーバーの型名に基づいて、対応するtypeDocmethodsマップに格納されるようになっています。これにより、ドキュメント生成時に型とそれに紐づくメソッドを関連付けて表示できるようになります。

gds.goにおける変更の例

--- a/usr/gri/pretty/gds.go
+++ b/usr/gri/pretty/gds.go
@@ -33,9 +32,8 @@ var (
 	root = flag.String("root", Platform.GOROOT, "go root directory");
 
 	// layout control
-\ttabwidth = flag.Int("gds_tabwidth", 4, "tab width");
-\tusetabs = flag.Bool("gds_usetabs", false, "align with tabs instead of blanks");
-\tnewdoc = flag.Bool("newdoc", false, "use new document printing");  // TODO remove once this works
+\ttabwidth = flag.Int("tabwidth", 4, "tab width");
+\tusetabs = flag.Bool("usetabs", false, "align with tabs instead of blanks");
 )
 
 
@@ -167,29 +165,23 @@ func serveFile(c *http.Conn, filename string) {
 
 	c.SetHeader("content-type", "text/html; charset=utf-8");
 	
-\tif *newdoc {
-\t\t// initialize tabwriter for nicely aligned output
-\t\tpadchar := byte(' ');
-\t\tif *usetabs {
-\t\t\tpadchar = '\t';
-\t\t}\
-\t\twriter := tabwriter.NewWriter(c, *tabwidth, 1, padchar, tabwriter.FilterHTML);\
-\n-\t\t// write documentation
-\t\tvar doc docPrinter.PackageDoc;\
-\t\tdoc.Init(string(prog.Name.Lit));\
-\t\tdoc.AddPackage(prog);\
-\t\tdoc.Print(writer);\
-\n-\t\t// flush any pending output
-\t\terr := writer.Flush();\
-\t\tif err != nil {\
-\t\t\tpanic("print error - exiting");\
-\t\t}\
-\t} else {\
-\t\t// TODO remove once the new document stuff works better
-\t\t//      than the old code
-\t\tPrinter.Print(c, prog, true);\
+\t// initialize tabwriter for nicely aligned output
+\tpadchar := byte(' ');
+\tif *usetabs {
+\t\tpadchar = '\t';
+\t}\
+\twriter := tabwriter.NewWriter(c, *tabwidth, 1, padchar, tabwriter.FilterHTML);\
+\n+\t// write documentation
+\tvar doc docPrinter.PackageDoc;\
+\tdoc.Init(string(prog.Name.Lit));\
+\tdoc.AddProgram(prog);\
+\tdoc.Print(writer);\
+\n+\t// flush any pending output
+\terr := writer.Flush();
+\tif err != nil {\
+\t\tpanic("print error - exiting");
 \t}\
 }\

gds.goでは、newdocというフラグが削除され、常に新しいdocprinterを使用したドキュメント生成パスが実行されるようになっています。これは、新しいドキュメント生成システムが十分に安定し、旧システムが不要になったことを示しています。また、doc.AddPackage(prog)doc.AddProgram(prog)に変更されており、compilation.goの変更と合わせて、ASTのルートノードがパッケージからプログラム全体を表すものに変わったことを示唆しています。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • Go言語のソースコードリポジトリ (GitHub)
  • Go言語の初期開発に関するメーリングリストや設計ドキュメント (公開されている場合)
  • AST、パーサー、プリンターに関する一般的なコンピュータサイエンスの概念