[インデックス 1931] ファイルの概要
このコミットは、Go言語の初期開発段階における、抽象構文木(AST)の処理、コードの整形(プリンター)、およびドキュメント生成に関する大規模なリファクタリングと機能強化を記録しています。特に、parser.go
とprinter.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.go
とdocprinter.go
に再配置された。gds
(Go Document Server)で新しいドキュメント印刷機能が有効化された。これにより、型に関連付けられたメソッドの表示など、ドキュメント生成の基礎が確立された。
変更の背景
Go言語の初期開発において、コンパイラ、ツール、および標準ライブラリの構造は継続的に進化していました。このコミットが行われた2009年3月は、Go言語が一般に公開される前の段階であり、言語仕様やツールの設計が活発に行われていた時期です。
この変更の背景には、以下の目的があったと考えられます。
- モジュール性の向上:
printer.go
が担っていた多様な印刷機能をastprinter.go
(ASTの構造的な印刷)とdocprinter.go
(ドキュメント生成のための印刷)に分割することで、各モジュールの責任を明確にし、コードベースの保守性と拡張性を向上させる狙いがありました。 - ASTとパーサーの進化への対応: Go言語のAST構造やパーサーのインターフェースが変更されたことに伴い、関連するツールもそれに合わせて更新する必要がありました。これにより、言語の進化に合わせたツールの整合性が保たれます。
- ドキュメント生成の強化:
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.go
やcompilation.go
の変更箇所を見ると、ast.FunctionType
がast.FuncType
に、ast.ChannelType
がast.ChanType
に、ast.Package
がast.Program
に変更されていることがわかります。これは、ASTノードの命名規則の変更や、より汎用的な構造への移行を示しています。例えば、ast.Package
からast.Program
への変更は、単一のパッケージだけでなく、複数のパッケージを含むプログラム全体を表現するためのAST構造への適応を意味する可能性があります。
また、astprinter.go
では、nopos
という変数名がnoPos
にキャメルケースで変更されています。これはGo言語のコーディング規約に合わせたリファクタリングの一環と考えられます。
2. printer.go
の削除と機能の分割
usr/gri/pretty/printer.go
は1413行ものコードが削除されており、これは非常に大規模な変更です。このファイルが担っていた機能は、astprinter.go
とdocprinter.go
に分割されました。
astprinter.go
: このファイルは、ASTをGoのソースコード形式で「印刷」する役割を担います。変更点を見ると、ASTノードのトラバーサルと、それに対応するトークンや文字列の出力ロジックが多数含まれています。例えば、DoFuncType
、DoFuncLit
、DoChanType
などのメソッドが追加または修正されており、新しい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.6
、docprinter.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.FunctionType
がast.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
)に収集するロジックが追加されています。特に、メソッド(レシーバーを持つ関数)の場合、そのレシーバーの型名に基づいて、対応するtypeDoc
のmethods
マップに格納されるようになっています。これにより、ドキュメント生成時に型とそれに紐づくメソッドを関連付けて表示できるようになります。
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言語の抽象構文木 (AST) について: https://pkg.go.dev/go/ast
- Go言語の
go/printer
パッケージ (コード整形): https://pkg.go.dev/go/printer - Go言語の
godoc
ツール: https://pkg.go.dev/golang.org/x/tools/cmd/godoc
参考にした情報源リンク
- Go言語の公式ドキュメント
- Go言語のソースコードリポジトリ (GitHub)
- Go言語の初期開発に関するメーリングリストや設計ドキュメント (公開されている場合)
- AST、パーサー、プリンターに関する一般的なコンピュータサイエンスの概念