[インデックス 1284] ファイルの概要
このコミットは、Go言語の初期開発段階におけるpretty
パッケージの重要な変更を記録しています。主な目的は、定数宣言の文法を仕様変更に合わせて調整することと、HTML出力機能を導入することです。これにより、Goコードの整形ツールがより柔軟になり、HTML形式でのコード表示が可能になります。
コミット
commit 77aaf4f3a2584bdaf6691b0f938c0fe8a789028d
Author: Robert Griesemer <gri@golang.org>
Date: Thu Dec 4 18:18:41 2008 -0800
- adjusted const decl grammar to reflect spec changes
- first cut at html writer (will do html escaping, html tag production)
- first cut at generating basic html output via pretty
- some cleanups
R=r
OCL=20550
CL=20550
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/77aaf4f3a2584bdaf6691b0f938c0fe8a789028d
元コミット内容
このコミットは以下の主要な変更を含んでいます。
- 定数宣言文法の調整: Go言語の仕様変更に合わせて、定数宣言の文法が更新されました。具体的には、複数の識別子と値のリストを一度に宣言できるよう、パーサーが修正されました。
- HTMLライターの導入: HTMLエスケープ処理とHTMLタグ生成を行うための
htmlwriter
パッケージが新たに作成されました。これは、io.Write
インターフェースを実装するフィルターとして機能します。 pretty
パッケージからのHTML出力生成:pretty
パッケージ(コード整形ツール)が、新しく導入されたhtmlwriter
を利用して基本的なHTML出力を生成できるようになりました。これには、HTMLのプロローグ(<html>
,<head>
,<body>
タグなど)とエピローグ、そして識別子をHTMLリンクとして出力する機能が含まれます。- クリーンアップ: コードベースの一般的な整理と改善が行われました。
変更の背景
このコミットが行われた2008年12月は、Go言語がまだ一般に公開される前の、活発な初期開発段階でした。この時期には、言語仕様が頻繁に改訂されており、それに伴いコンパイラやツールチェーンも継続的に更新される必要がありました。
変更の背景には、主に以下の2点があります。
- 言語仕様の進化: Go言語の定数宣言の文法が、より柔軟な複数宣言をサポートするように変更されたため、既存のパーサーが新しい仕様に対応する必要がありました。これは、言語設計の成熟に伴う自然なプロセスです。
- ツールの機能拡張: Goコードを整形し、表示するための
pretty
ツールに、HTML形式での出力機能を追加するニーズがありました。これは、コードのドキュメント生成、Webベースのコードビューア、あるいはIDE連携など、将来的な利用シナリオを見据えた機能拡張と考えられます。HTML出力は、コードのシンタックスハイライトやナビゲーションを容易にするための基盤となります。
前提知識の解説
このコミットを理解するためには、以下のGo言語および関連技術の初期の概念に関する知識が役立ちます。
- Go言語の初期の文法: 2008年当時のGo言語の文法は、現在の安定版とは異なる部分が多くありました。特に、定数宣言の構文や、パッケージのインポート方法(例:
import ("os"; "io"; "array"; "utf8";)
のようにセミコロンで区切るスタイル)など、細部に違いが見られます。 go/parser
とgo/printer
パッケージの原型: このコミットで変更されているparser.go
とprinter.go
は、現在のGo標準ライブラリにあるgo/parser
とgo/printer
パッケージの初期の原型にあたります。これらは、Goソースコードの抽象構文木(AST)の解析と、ASTからソースコードを整形して出力する機能を提供します。io.Writer
インターフェース: Go言語の基本的なI/Oインターフェースであり、データを書き込むための抽象化を提供します。htmlwriter
はこれをラップすることで、任意のio.Writer
に対してHTML形式の出力を提供できるように設計されています。tabwriter
パッケージ:text/tabwriter
パッケージの原型であり、テキストをタブ区切りで整形して出力するためのライターです。このコミットでは、printer
が直接tabwriter
を使用する代わりに、htmlwriter
を介して出力する構造に変更されています。- HTMLエスケープ: HTMLドキュメント内で特殊文字(
<
,>
,&
,"
,'
など)を正しく表示するために、それらを対応するHTMLエンティティ(<
,>
,&
など)に変換する処理です。htmlwriter
の主要な機能の一つとして計画されていました。 - Go言語のビルドシステム(初期):
Makefile
の変更は、当時のGo言語のビルドプロセスの一部を示しています。.6
という拡張子は、初期のGoコンパイラが生成する中間ファイルや実行可能ファイルに関連する可能性があります。
技術的詳細
定数宣言文法の変更
parser.go
における変更は、Go言語の定数宣言の文法が拡張されたことを示しています。
P.ParseIdent()
からP.ParseIdentList()
へ: これは、単一の識別子だけでなく、const a, b = 1, 2
のように複数の識別子をカンマで区切って宣言できるようになったことを意味します。P.ParseExpression(1)
からP.ParseExpressionList()
へ: 同様に、単一の式だけでなく、const a, b = 1 + 2, "hello"
のように複数の式をカンマで区切って指定できるようになったことを示します。
この変更により、Go言語の定数宣言はより簡潔かつ強力になりました。
htmlwriter
パッケージの導入
htmlwriter.go
は、HTML出力を専門に扱う新しいパッケージです。
Writer
構造体:io.Write
インターフェースをラップし、HTMLエスケープやタグ生成のロジックを追加するための基盤を提供します。初期の実装ではWrite
メソッドは単にラップされたライターに書き込むだけですが、コメントには「HTMLエスケープ、HTMLタグ生成を行う」と明記されており、今後の機能拡張が意図されています。New
関数とInit
メソッド:Writer
のインスタンスを生成し、初期化するための標準的なGoのパターンに従っています。
printer
パッケージのHTML出力対応
printer.go
は、Goコードを整形して出力する主要なロジックを含んでいます。このコミットでは、HTML出力機能が大幅に強化されました。
html
フラグの追加: コマンドライン引数で-html
フラグを渡すことで、HTML出力を有効にできるようになりました。これは、pretty
ツールがテキスト出力とHTML出力の両方をサポートするための設定メカニズムです。Printer
のwriter
型の変更:*tabwriter.Writer
から*htmlwriter.Writer
に変更されたことで、Printer
は直接tabwriter
に書き込むのではなく、htmlwriter
を介して書き込むようになりました。これにより、すべての出力がhtmlwriter
によって処理され、HTMLエスケープやタグ生成の恩恵を受けることができます。- HTML関連ヘルパー関数の追加:
HtmlPrologue(title string)
: HTMLドキュメントの冒頭部分(<html>
,<head>
,<body>
,<pre>
タグなど)を生成します。これにより、生成されるHTMLファイルが基本的な構造を持つようになります。HtmlEpilogue()
: HTMLドキュメントの末尾部分(</pre>
,</body>
,</html>
タグ)を生成します。HtmlIdentifier(pos int, ident string)
: 識別子をHTMLリンク(<a href="#ident">ident</a>
)として出力します。これは、生成されたHTMLコード内で識別子をクリック可能にし、コード内の定義箇所へのナビゲーションを可能にするための重要な機能です。
Print
関数の変更:tabwriter
とhtmlwriter
の両方をインスタンス化し、htmlwriter
がtabwriter
をラップする形で連携させています。Printer
はhtmlwriter
で初期化されます。P.HtmlPrologue
とP.HtmlEpilogue
がP.Program(prog)
の前後で呼び出され、Goコードの出力全体がHTML構造で囲まれるようになりました。
これらの変更により、pretty
ツールはGoコードを整形するだけでなく、Webブラウザで閲覧可能なHTML形式で出力する能力を獲得しました。
コアとなるコードの変更箇所
usr/gri/pretty/parser.go
--- a/usr/gri/pretty/parser.go
+++ b/usr/gri/pretty/parser.go
@@ -1348,11 +1348,11 @@ func (P *Parser) ParseConstSpec(exported bool, pos int) *AST.Decl {
P.Trace("ConstSpec");
d := AST.NewDecl(pos, Scanner.CONST, exported);
- d.ident = P.ParseIdent();
+ d.ident = P.ParseIdentList();
d.typ = P.TryType();
if P.tok == Scanner.ASSIGN {
P.Next();
- d.val = P.ParseExpression(1);
+ d.val = P.ParseExpressionList();
}
P.Ecart();
usr/gri/pretty/htmlwriter.go
(新規ファイル)
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package htmlwriter
import (
"os";
"io";
"array";
"utf8";
)
// Writer is a filter implementing the io.Write interface.
// It provides facilities to generate HTML tags and does
// proper HTML-escaping for text written through it.
export type Writer struct {
// TODO should not export any of the fields
writer io.Write;
}
func (b *Writer) Init(writer io.Write) *Writer {
b.writer = writer;
return b;
}
/* export */ func (b *Writer) Flush() *os.Error {
return nil;
}
/* export */ func (b *Writer) Write(buf *[]byte) (written int, err *os.Error) {
written, err = b.writer.Write(buf); // BUG 6g - should just have return
return written, err;
}
export func New(writer io.Write) *Writer {
return new(Writer).Init(writer);
}
usr/gri/pretty/printer.go
--- a/usr/gri/pretty/printer.go
+++ b/usr/gri/pretty/printer.go
@@ -10,6 +10,7 @@ import (
"tabwriter";
"flag";
"fmt";
+ "htmlwriter";
Scanner "scanner";
AST "ast";
)
@@ -24,6 +25,7 @@ var (
maxnewlines = flag.Int("maxnewlines", 3, nil, "max. number of consecutive newlines");
// formatting control
+ html = flag.Bool("html", false, nil, "generate html");
comments = flag.Bool("comments", true, nil, "print comments");
optsemicolons = flag.Bool("optsemicolons", false, nil, "print optional semicolons");
)
@@ -53,7 +55,7 @@ const (
type Printer struct {
// output
- writer *tabwriter.Writer;
+ writer *htmlwriter.Writer;
// comments
comments *array.Array; // the list of all comments
@@ -90,14 +92,10 @@ func (P *Printer) NextComment() {
}
-func (P *Printer) Init(writer *tabwriter.Writer, comments *array.Array) {
+func (P *Printer) Init(writer *htmlwriter.Writer, comments *array.Array) {
// writer
- padchar := byte(' ');
- if usetabs.BVal() {
- padchar = '\t';
- }
- P.writer = tabwriter.New(os.Stdout, int(tabwidth.IVal()), 1, padchar, true);
-
+ P.writer = writer;
+
// comments
P.comments = comments;
P.cindex = -1;
@@ -299,12 +297,6 @@ func (P *Printer) String(pos int, s string) {
}
-func (P *Printer) Separator(separator int) {
- P.separator = separator;
- P.String(0, "");
-}
-
-
func (P *Printer) Token(pos int, tok int) {
P.String(pos, Scanner.TokenString(tok));
}
@@ -317,6 +309,47 @@ func (P *Printer) Error(pos int, tok int, msg string) {
}
+// ----------------------------------------------------------------------------
+// HTML support
+// TODO Move this to html writer
+
+func (P *Printer) HtmlPrologue(title string) {
+ if html.BVal() {
+ P.String(0,
+ "<html>\n"
+ "<head>\n"
+ "\t<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=UTF-8\">\n"
+ "\t<title>" + title + "</title>\n"
+ "\t<style type=\"text/css\">\n"
+ "\t</style>\n"
+ "</head>\n"
+ "<body>\n"
+ "<pre>\n"
+ )
+ }
+}
+
+
+func (P *Printer) HtmlEpilogue() {
+ if html.BVal() {
+ P.String(0,
+ "</pre>\n"
+ "</body>\n"
+ "<html>\n"
+ )
+ }
+}
+
+
+func (P *Printer) HtmlIdentifier(pos int, ident string) {
+ if html.BVal() {
+ P.String(pos, `<a href="#` + ident + `">` + ident + `</a>`);
+ } else {
+ P.String(pos, ident);
+ }
+}
+
+
// ----------------------------------------------------------------------------
// Types
@@ -331,9 +364,9 @@ func (P *Printer) Parameters(pos int, list *array.Array) {
tx := list.At(i).(*AST.Expr);
if i > 0 {
if prev == x.tok || prev == Scanner.TYPE {
- P.Separator(comma);
+ P.separator = comma;
} else {
- P.Separator(blank);
+ P.separator = blank;
}
}
P.Expr(x);
@@ -458,7 +491,10 @@ func (P *Printer) Expr1(x *AST.Expr, prec1 int) {
// type expr
P.Type(x.t);
- case Scanner.IDENT, Scanner.INT, Scanner.STRING, Scanner.FLOAT:
+ case Scanner.IDENT:
+ P.HtmlIdentifier(x.pos, x.s);
+
+ case Scanner.INT, Scanner.STRING, Scanner.FLOAT:
// literal
P.String(x.pos, x.s);
@@ -799,16 +835,16 @@ export func Print(prog *AST.Program) {
if usetabs.BVal() {
padchar = '\t';
}
- writer := tabwriter.New(os.Stdout, int(tabwidth.IVal()), 1, padchar, true);
+ ttwriter := tabwriter.New(os.Stdout, int(tabwidth.IVal()), 1, padchar, true);
+ hwriter := htmlwriter.New(ttwriter);
var P Printer;
- P.Init(writer, prog.comments);
+ P.Init(hwriter, prog.comments);
+ P.HtmlPrologue("<the source>");
P.Program(prog);
+ P.HtmlEpilogue();
- // flush
P.String(0, ""); // flush pending separator/newlines
- err := P.writer.Flush();
- if err != nil {
- panic("print error - exiting");
- }
+ hwriter.Flush(); // ignore errors
+ ttwriter.Flush(); // ignore errors
}
コアとなるコードの解説
parser.go
の変更
ParseConstSpec
関数は、定数宣言を解析する部分です。変更前は単一の識別子と式を期待していましたが、P.ParseIdentList()
とP.ParseExpressionList()
を呼び出すように変更されたことで、以下のような複数宣言の構文に対応できるようになりました。
const (
a, b = 1, 2
c, d string = "hello", "world"
)
これは、Go言語の定数宣言の柔軟性を高めるための重要な文法変更を反映しています。
htmlwriter.go
の新規追加
このファイルは、HTML出力の基盤を提供します。Writer
構造体はio.Write
インターフェースをラップし、将来的にはHTMLエスケープやタグ生成のロジックがここに追加される予定です。現時点では、Write
メソッドは単に下層のライターにバイト列を渡すだけですが、これはHTML出力パイプラインの最初のステップです。
printer.go
の変更
Printer
構造体のwriter
フィールド:*tabwriter.Writer
から*htmlwriter.Writer
への変更は、Printer
が直接タブ整形を行うのではなく、HTML整形を行うhtmlwriter
を介して出力を行うという設計思想の転換を示しています。これにより、HTML出力が有効な場合、すべてのテキスト出力がHTMLのコンテキストで処理されるようになります。HtmlPrologue
とHtmlEpilogue
: これらの関数は、生成されるHTMLドキュメントの基本的な構造を提供します。HtmlPrologue
はHTMLのヘッダーとボディの開始タグ、そしてコードを整形するための<pre>
タグを出力します。HtmlEpilogue
はこれらのタグを閉じます。これにより、pretty
ツールが生成する出力は、単なるテキストではなく、Webブラウザで直接開ける有効なHTMLファイルになります。HtmlIdentifier
: この関数は、Goコード内の識別子をHTMLのアンカータグ(<a>
)で囲むことで、クリック可能なリンクとして出力します。href="#ident"
という形式は、同じHTMLドキュメント内のIDを持つ要素への内部リンクを示唆しており、将来的にはコード内の定義箇所へのジャンプ機能を実現するために使用される可能性があります。Print
関数の出力パイプライン:Print
関数は、tabwriter
とhtmlwriter
を連携させる方法を示しています。まずtabwriter
が作成され、そのtabwriter
をラップするようにhtmlwriter
が作成されます。そして、Printer
はhtmlwriter
を介して出力を行います。これにより、出力はまずHTMLエスケープとタグ生成の処理を受け、その後tabwriter
によってタブ整形されるという多段階の処理が可能になります。
これらの変更は、Go言語のコード整形ツールが、単なるテキスト出力から、よりリッチでインタラクティブなHTML出力へと進化する第一歩を示しています。
関連リンク
- Go言語の初期のコミット履歴: https://github.com/golang/go/commits/master?after=77aaf4f3a2584bdaf6691b0f938c0fe8a789028d+34 (このコミットの周辺の履歴を辿ることができます)
- Go言語の公式ドキュメント: https://go.dev/doc/ (現在のGo言語の仕様やパッケージについて学ぶことができます)
参考にした情報源リンク
- Go言語のGitHubリポジトリ: https://github.com/golang/go
- Go言語の初期の設計に関する議論(Go Wikiなど): 2008年当時の具体的な仕様変更に関する公開された議論を特定するのは困難ですが、Go言語の進化の歴史は公式ブログやGo Wikiで追うことができます。
- Go Wiki: https://go.dev/wiki
- Go Blog: https://go.dev/blog/
text/tabwriter
パッケージのドキュメント: https://pkg.go.dev/text/tabwriter (現在のパッケージの機能について)go/parser
パッケージのドキュメント: https://pkg.go.dev/go/parser (現在のパッケージの機能について)go/printer
パッケージのドキュメント: https://pkg.go.dev/go/printer (現在のパッケージの機能について)