[インデックス 10457] ファイルの概要
このコミットは、Go言語の標準ライブラリである text/template
パッケージ内のテンプレート解析ロジックに対する変更を記録しています。具体的には、内部関数 Set
の名称を Parse
に変更し、それに伴う関連ファイルの修正が行われています。この変更は、テンプレートAPIの簡素化に向けた前段階として位置づけられています。
コミット
commit 10e012c85fa95ec24d039dcfa710e8d3cd75839d
Author: Rob Pike <r@golang.org>
Date: Fri Nov 18 13:10:15 2011 -0800
template/parse: rename Set to Parse
Preamble to the simplification of the template API.
Although the signature of Parse (nee Set) changes,
it's really an internal function, used only by
text/template.
R=golang-dev, rsc, gri, r
CC=golang-dev
https://golang.org/cl/5415052
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/10e012c85fa95ec24d039dcfa710e8d3cd75839d
元コミット内容
template/parse
パッケージ内の Set
関数を Parse
にリネームしました。これはテンプレートAPIの簡素化に向けた前段階です。Parse
(旧 Set
) のシグネチャは変更されますが、これは text/template
の内部でのみ使用される関数です。
変更の背景
このコミットの主な背景は、Go言語の text/template
パッケージのAPIを簡素化することにあります。コミットメッセージにある「Preamble to the simplification of the template API.」という記述がその意図を明確に示しています。
Goのテンプレートエンジンは、HTMLやテキストの動的な生成に広く利用されています。初期の設計段階では、APIの名称や構造が試行錯誤されることがよくあります。この変更は、parse
サブパッケージ内の Set
という関数名が、その実際の役割(テンプレート定義文字列を解析して構文木を構築すること)を正確に反映していないと判断されたためと考えられます。Parse
という名称は、解析処理を行う関数にとってより直感的で、APIの意図を明確にする効果があります。
また、コミットメッセージには「Although the signature of Parse (nee Set) changes, it's really an internal function, used only by text/template.」とあり、この関数が text/template
パッケージの内部でのみ使用されるため、外部への影響が限定的であることも示唆されています。これにより、APIの変更が比較的容易に行える状況であったことが伺えます。
前提知識の解説
このコミットを理解するためには、Go言語の text/template
パッケージと、一般的なテンプレートエンジンの動作原理に関する知識が必要です。
Go言語の text/template
パッケージ
text/template
パッケージは、Go言語に組み込まれているデータ駆動型テンプレートエンジンです。主にテキスト出力の生成に使用され、HTMLの生成には html/template
パッケージが推奨されます(html/template
は text/template
をベースにしており、XSS攻撃からの保護機能が追加されています)。
基本的な機能は以下の通りです。
- テンプレートの定義: プレースホルダーや制御構造(条件分岐、ループなど)を含むテキストを定義します。
- データの結合: 定義されたテンプレートにGoのデータ構造(構造体、マップ、スライスなど)を結合し、最終的なテキストを生成します。
- アクション: テンプレート内で
{{...}}
のように記述される部分を「アクション」と呼び、データの表示、関数の呼び出し、制御構造の記述などを行います。
テンプレートエンジンの解析(パース)プロセス
一般的なテンプレートエンジンは、以下の主要なステップで動作します。
-
字句解析 (Lexing/Tokenizing): テンプレートの生文字列を読み込み、意味のある最小単位(トークン)に分割します。例えば、
{{.Name}}
という文字列は、{{
(デリミタ開始)、.
(フィールドアクセス)、Name
(識別子)、}}
(デリミタ終了) といったトークンに分解されます。このプロセスは「レキサー (lexer)」または「スキャナー (scanner)」によって行われます。 -
構文解析 (Parsing): 字句解析で生成されたトークンのストリームを受け取り、それらがテンプレート言語の文法規則に従っているかを確認し、抽象構文木 (Abstract Syntax Tree: AST) を構築します。ASTは、テンプレートの構造を階層的に表現したデータ構造であり、後続の処理(実行、最適化など)の基盤となります。このプロセスは「パーサー (parser)」によって行われます。
-
実行 (Execution): 構築されたASTと、テンプレートに結合するデータを受け取り、ASTを走査しながらデータを埋め込み、最終的な出力を生成します。
このコミットは、主に上記の「構文解析」フェーズに関連する内部関数の名称変更と、それに伴うコードの整理を行っています。text/template/parse
パッケージは、この構文解析を担当する部分です。
Tree
構造体
src/pkg/text/template/parse/parse.go
に定義されている Tree
構造体は、解析されたテンプレートの抽象構文木(AST)の表現です。
Name
: テンプレートの名前。Root
: ASTの最上位ノード。通常はListNode
であり、複数のノード(テキストノード、アクションノードなど)を含むことができます。funcs
: テンプレート内で利用可能な関数マップ。lex
: 字句解析器(レキサー)へのポインタ。token
: 現在処理中のトークン。peekCount
: 先読みしたトークンの数。vars
: テンプレート内で定義された変数。
技術的詳細
このコミットの技術的詳細は、text/template
パッケージの内部構造、特にテンプレートの解析(パース)部分に焦点を当てています。
parse
パッケージの役割
src/pkg/text/template/parse
パッケージは、Goのテンプレートエンジンにおいて、テンプレート文字列を解析し、実行可能な内部表現(抽象構文木 Tree
)に変換する役割を担っています。このパッケージは、字句解析(lex.go
)と構文解析(parse.go
)のロジックを含んでいます。
Set
から Parse
へのリネーム
以前は src/pkg/text/template/parse/set.go
に Set
という関数が定義されていました。この関数は、引数として与えられたテンプレート定義文字列を解析し、map[string]*Tree
型のテンプレートのセットを返していました。しかし、Set
という名前は、テンプレートの「設定」や「集合」を意味するように聞こえ、実際の機能である「解析」を直接的に示していませんでした。
このコミットでは、set.go
ファイルを削除し、その機能を parse.go
に統合するとともに、関数名を Parse
に変更しました。新しい Parse
関数は、テンプレート名、テキスト、デリミタ、および関数マップを受け取り、解析されたテンプレートのマップを返します。これにより、関数の名前がその動作をより正確に表現するようになりました。
Parse
関数のシグネチャ変更と内部関数としての位置づけ
コミットメッセージにあるように、Parse
(旧 Set
) のシグネチャは変更されています。
旧 Set
関数 (src/pkg/text/template/parse/set.go):
func Set(text, leftDelim, rightDelim string, funcs ...map[string]interface{}) (tree map[string]*Tree, err error)
この関数は、テンプレートのテキスト、デリミタ、および関数マップを受け取り、map[string]*Tree
を返していました。
新 Parse
関数 (src/pkg/text/template/parse/parse.go):
func Parse(name, text, leftDelim, rightDelim string, funcs ...map[string]interface{}) (treeSet map[string]*Tree, err error)
新しい Parse
関数は、name
引数が追加されています。これは、トップレベルのテンプレートに指定された名前を与えるために使用されます。
また、この関数は text/template
パッケージの内部でのみ使用される「内部関数」であると明記されており、外部APIとしての安定性よりも、内部的な整合性や簡潔性が優先されたことが示唆されます。
Tree
構造体のコメント更新
Tree
構造体のコメントも更新されています。
// Tree is the representation of a parsed template.
から// Tree is the representation of a single parsed template.
へ変更。Name string // Name is the name of the template.
からName string // name of the template represented by the tree.
へ変更。Root *ListNode // Root is the top-level root of the parse tree.
からRoot *ListNode // top-level root of the tree.
へ変更。
これらの変更は、Tree
が単一のテンプレートの構文木を表すことをより明確にし、フィールドのコメントも簡潔にしています。
New
関数のコメント更新
New
関数のコメントも更新されています。
// New allocates a new template with the given name.
から// New allocates a new parse tree with the given name.
へ変更。
これは、New
関数がテンプレート全体ではなく、解析ツリー(Tree
構造体)を割り当てることを明確にしています。
Parse
メソッドのコメント更新
Tree
型の Parse
メソッドのコメントも更新されています。
// Parse parses the template definition string to construct an internal // representation of the template for execution. If either action delimiter // string is empty, the default ("{{" or "}}") is used.
から// Parse parses the template definition string to construct a representation of // the template for execution. If either action delimiter string is empty, the // default ("{{" or "}}") is used. Embedded template definitions are added to // the treeSet map.
へ変更。
最後の行 Embedded template definitions are added to the treeSet map.
が追加され、埋め込みテンプレート定義が treeSet
マップに追加されるという重要な動作が明記されました。これは、テンプレートが他のテンプレートを定義できるGoテンプレートの機能(例: {{define "name"}}...{{end}}
)を反映しています。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更箇所は以下のファイルに集中しています。
-
src/pkg/text/template/parse/Makefile
:set.go
のビルド対象からの削除。
-
src/pkg/text/template/parse/parse.go
:- 新しいトップレベルの
Parse
関数が追加されました。この関数は、以前set.go
にあったSet
関数の機能を置き換えるものです。 Tree
構造体のフィールドコメントがより簡潔に、かつ正確に更新されました。New
関数のコメントが更新されました。Tree
型のParse
メソッドのコメントが更新され、埋め込みテンプレートの扱いに関する記述が追加されました。
- 新しいトップレベルの
-
src/pkg/text/template/parse/set.go
:- ファイル全体が削除されました。
-
src/pkg/text/template/set.go
:parse.Set
の呼び出しがparse.Parse
に変更されました。また、"ROOT"
というプレースホルダー名が渡されるようになりました。
コアとなるコードの解説
src/pkg/text/template/parse/parse.go
の変更点
新しい Parse
関数
// Parse returns a map from template name to parse.Tree, created by parsing the
// templates described in the argument string. The top-level template will be
// given the specified name. If an error is encountered, parsing stops and an
// empty map is returned with the error.
func Parse(name, text, leftDelim, rightDelim string, funcs ...map[string]interface{}) (treeSet map[string]*Tree, err error) {
treeSet = make(map[string]*Tree)
_, err = New(name).Parse(text, leftDelim, rightDelim, treeSet, funcs...)
return
}
この関数は、テンプレートの解析を開始するためのエントリポイントとなります。
name
: 解析するトップレベルテンプレートの名前。text
: 解析対象のテンプレート文字列。leftDelim
,rightDelim
: アクションデリミタ(例:{{
,}}
)。funcs
: テンプレート内で使用できる関数マップ。
内部では、まず treeSet
という map[string]*Tree
を作成します。これは、解析中に見つかったすべてのテンプレート(トップレベルおよび埋め込み)を格納するためのマップです。
次に、New(name)
で新しい Tree
インスタンスを作成し、その Parse
メソッドを呼び出しています。この Tree.Parse
メソッドが実際の解析処理を行い、結果を treeSet
に追加します。
Tree
構造体のコメント変更
// Tree is the representation of a single parsed template.
type Tree struct {
Name string // name of the template represented by the tree.
Root *ListNode // top-level root of the tree.
// Parsing only; cleared after parse.
funcs []map[string]interface{}
lex *lexer
token [3]item // three-token lookahead for parsing.
peekCount int
vars []string // variables defined at the moment.
}
コメントがより正確になり、Tree
が「単一の解析済みテンプレート」を表すこと、Name
が「ツリーによって表されるテンプレートの名前」であること、Root
が「ツリーのトップレベルのルート」であることが明確化されました。
Tree.Parse
メソッドのコメント変更
// Parse parses the template definition string to construct a representation of
// the template for execution. If either action delimiter string is empty, the
// default ("{{" or "}}") is used. Embedded template definitions are added to
// the treeSet map.
func (t *Tree) Parse(s, leftDelim, rightDelim string, treeSet map[string]*Tree, funcs ...map[string]interface{}) (tree *Tree, err error) {
defer t.recover(&err)
t.startParse(funcs, lex(t.Name, s, leftDelim, rightDelim))
t.parse(treeSet)
return t, nil
}
特に重要なのは、Embedded template definitions are added to the treeSet map.
という行が追加されたことです。これは、{{define "name"}}...{{end}}
のような構文で定義されたサブテンプレートが、この Parse
メソッドの実行中に treeSet
に追加されることを明示しています。これにより、Parse
関数が単一のテンプレートだけでなく、その中に含まれる複数のテンプレートを処理する能力を持つことが示されます。
src/pkg/text/template/parse/set.go
の削除
このファイルは完全に削除されました。これにより、Set
関数が提供していた機能が parse.go
の新しい Parse
関数に完全に移行したことを意味します。コードベースの整理と、機能の集中化が図られています。
src/pkg/text/template/set.go
の変更点
func (s *Set) Parse(text string) (*Set, error) {
// TODO: "ROOT" is just a placeholder while we rejig the API.
trees, err := parse.Parse("ROOT", text, s.leftDelim, s.rightDelim, s.parseFuncs, builtins)
if err != nil {
return nil, err
}
// ... (後続の処理)
}
text/template
パッケージの Set
型の Parse
メソッドは、テンプレートセット全体を解析する役割を担っています。このメソッド内で、以前は parse.Set
を呼び出していましたが、このコミットにより parse.Parse
を呼び出すように変更されました。
注目すべきは、"ROOT"
という文字列が parse.Parse
の name
引数として渡されている点です。これには // TODO: "ROOT" is just a placeholder while we rejig the API.
というコメントが付いています。これは、この時点ではトップレベルのテンプレート名がまだ適切に扱われておらず、APIの再構築(rejig)が進行中であることを示しています。つまり、このコミットはAPI簡素化の「前段階 (Preamble)」であり、まだ完全な解決には至っていないことを示唆しています。
関連リンク
- Go言語の
text/template
パッケージ公式ドキュメント: https://pkg.go.dev/text/template - Go言語の
html/template
パッケージ公式ドキュメント: https://pkg.go.dev/html/template - Go言語のコードレビューシステム (Gerrit) の変更リスト: https://golang.org/cl/5415052
参考にした情報源リンク
- 上記のGitHubコミットページ
- Go言語の公式ドキュメント
- Go言語のソースコード
- 一般的なコンパイラ/インタプリタの設計に関する知識(字句解析、構文解析、ASTなど)I have provided the detailed explanation as requested.