[インデックス 10081] ファイルの概要
このコミットは、Go言語の html
パッケージにおける Tokenizer.ReturnComments
オプションの削除に関するものです。このオプションは、HTMLコメントトークンをスキップするかどうかを制御していましたが、WebKit HTMLテストスイートとの互換性を確保するためにコメントのパースが必要となったため、その存在意義がなくなりました。
コミット
commit 18b025d530b2410c74c094c0e78671570c60b7bd
Author: Nigel Tao <nigeltao@golang.org>
Date: Tue Oct 25 11:28:07 2011 +1100
html: remove the Tokenizer.ReturnComments option.
The original intention was to simplify the parser, in making it skip
all comment tokens. However, checking that the Go html package is
100% compatible with the WebKit HTML test suite requires parsing the
comments. There is no longer any real benefit for the option.
R=gri, andybalholm
CC=golang-dev
https://golang.org/cl/5321043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/18b025d530b2410c74c094c0e78671570c60b7bd
元コミット内容
html: remove the Tokenizer.ReturnComments option.
このコミットは、Go言語の標準ライブラリに含まれる html
パッケージから Tokenizer.ReturnComments
オプションを削除するものです。当初の目的は、パーサーを簡素化し、すべてのコメントトークンをスキップさせることでしたが、Goの html
パッケージがWebKit HTMLテストスイートと100%互換性があることを確認するためには、コメントをパースする必要があることが判明しました。そのため、このオプションの実際の利点はもはやありません。
変更の背景
Go言語の html
パッケージは、HTMLドキュメントのパースとトークン化を行うための機能を提供します。初期の設計段階では、パーサーの簡素化とパフォーマンスの最適化のため、HTMLコメントをスキップするオプション Tokenizer.ReturnComments
が導入されていました。これにより、アプリケーションがコメントの内容に興味がない場合に、不要な処理を省くことができました。
しかし、Web標準への準拠とブラウザとの互換性は、Web関連ライブラリにとって非常に重要です。特に、WebKit HTMLテストスイートのような包括的なテストスイートは、HTMLパーサーが様々なエッジケースや複雑な構造を正しく処理できるかを検証するために用いられます。このテストスイートを実行する過程で、Goの html
パッケージがWebKitの挙動と完全に一致するためには、コメントトークンも適切にパースし、処理する必要があることが明らかになりました。
コメントをスキップするオプションが存在すると、テストスイートの要件を満たせない、あるいはテストの実行方法が複雑になるなどの問題が生じます。コメントのパースが必須となったことで、Tokenizer.ReturnComments
オプションは冗長となり、むしろコードの複雑性を増す要因となってしまいました。このため、コードベースをクリーンに保ち、将来的なメンテナンスを容易にするために、このオプションを削除する決定がなされました。
前提知識の解説
HTMLの構造とコメント
HTML (HyperText Markup Language) は、ウェブページの構造を定義するためのマークアップ言語です。HTMLドキュメントは、要素、属性、テキスト、コメントなどで構成されます。
- HTMLコメント: HTMLコメントは
<!--
で始まり-->
で終わるテキストブロックで、ブラウザには表示されません。主に開発者がコード内にメモを残したり、一時的にコードブロックを無効化したりするために使用されます。例:<!-- This is a comment -->
HTMLパーサーとトークナイザー
HTMLドキュメントをコンピュータが理解できる形式に変換するプロセスを「パース(構文解析)」と呼びます。このパースプロセスは通常、以下の2つの主要な段階に分けられます。
- トークン化(Lexing/Tokenizing): 入力されたHTML文字列を、意味のある最小単位である「トークン」のストリームに分解するプロセスです。例えば、
<p>
は開始タグトークン、</p>
は終了タグトークン、Hello
はテキストトークン、<!-- comment -->
はコメントトークンとして識別されます。この役割を担うのが「トークナイザー(Lexer/Tokenizer)」です。 - ツリー構築(Parsing): トークンのストリームを受け取り、それらをDOM(Document Object Model)ツリーと呼ばれる階層的な構造に変換するプロセスです。このツリーは、ウェブページの論理的な構造を表現し、JavaScriptなどのスクリプトからアクセス・操作されます。この役割を担うのが「パーサー(Parser)」です。
Tokenizer.ReturnComments
オプションは、このトークン化の段階で、コメントトークンをトークンストリームに含めるか、それとも完全に無視するかを制御するものでした。
WebKit HTMLテストスイート
WebKitは、Apple SafariやGoogle Chrome(初期)などで使用されていたオープンソースのレンダリングエンジンです。WebKit HTMLテストスイートは、HTMLの仕様に準拠しているかを検証するための包括的なテストケースの集合体です。このテストスイートは、様々なHTMLの構文、要素、属性、そしてコメントの処理など、多岐にわたるシナリオをカバーしており、ブラウザやHTMLパーサーの実装がWeb標準にどれだけ忠実であるかを評価するための重要なベンチマークとして機能します。
Goの html
パッケージがこのテストスイートとの互換性を目指すということは、単にHTMLをパースできるだけでなく、WebKitがHTMLを解釈するのと全く同じように、コメントを含むすべての要素を正確に処理する必要があることを意味します。
技術的詳細
このコミットの技術的な核心は、HTMLトークナイザーの挙動変更と、それに関連するコードの簡素化にあります。
Tokenizer.ReturnComments
オプションの削除
以前の html
パッケージの Tokenizer
構造体には、ReturnComments bool
というフィールドが存在しました。このフィールドが true
に設定されている場合、Next
メソッドはコメントトークンを返しましたが、false
(デフォルト) の場合はコメントトークンをスキップしていました。
このコミットでは、Tokenizer
構造体から ReturnComments
フィールドが完全に削除されました。これにより、Tokenizer
は常にコメントトークンを生成するようになります。
Next
メソッドの変更
Tokenizer
の Next
メソッドは、次のHTMLトークンをスキャンしてそのタイプを返す役割を担っています。変更前は、このメソッド内で ReturnComments
の値に基づいてコメントトークンをスキップするロジックが含まれていました。
変更後、Next
メソッドはコメントトークンをスキップするロジックを削除し、常にコメントトークンを返すように修正されました。具体的には、token.go
内の Next
メソッドから、if z.tt == CommentToken && !z.ReturnComments { continue }
というループと条件が削除されています。これにより、コメントトークンが検出された場合でも、それがすぐに返されるようになります。
parse.go
と token_test.go
の変更
src/pkg/html/parse.go
では、Parse
関数内で p.tokenizer.ReturnComments = true
と明示的に設定していた行が削除されました。これは、ReturnComments
オプションがなくなったため、もはやこの設定が不要になったためです。
src/pkg/html/token_test.go
では、テストケース内で z.ReturnComments = true
と設定していた行が削除されました。これも同様に、オプションが削除されたことによる変更です。
doc.go
の変更
src/pkg/html/doc.go
はパッケージのドキュメントファイルです。このファイルから Tokenizer.ReturnComments
オプションに関する説明が削除されました。これは、このオプションがもはや存在しないため、ドキュメントからその記述を削除することで、ユーザーが誤解するのを防ぐためです。
これらの変更により、html
パッケージのトークナイザーは、HTMLコメントを常にパースし、トークンストリームの一部として扱うようになりました。これにより、WebKit HTMLテストスイートのような厳格な互換性要件を満たすことが可能になります。
コアとなるコードの変更箇所
このコミットにおける主要なコード変更は、src/pkg/html/token.go
ファイルに集中しています。
src/pkg/html/token.go
Tokenizer
構造体からReturnComments bool
フィールドが削除されました。--- a/src/pkg/html/token.go +++ b/src/pkg/html/token.go @@ -116,10 +116,6 @@ type span struct { // A Tokenizer returns a stream of HTML Tokens. type Tokenizer struct { - // If ReturnComments is set, Next returns comment tokens; - // otherwise it skips over comments (default). - ReturnComments bool - // r is the source of the HTML text. r io.Reader // tt is the TokenType of the current token.
Next
メソッドのロジックが変更され、コメントトークンをスキップする条件が削除されました。また、next
という内部関数がNext
に統合され、z.tt
への代入が各return
ステートメントの直前で行われるようになりました。--- a/src/pkg/html/token.go +++ b/src/pkg/html/token.go @@ -596,11 +594,13 @@ loop: if x := z.raw.end - len("<a"); z.raw.start < x { z.raw.end = x z.data.end = x - return TextToken + z.tt = TextToken + return z.tt } switch tokenType { case StartTagToken: - return z.readStartTag() + z.tt = z.readStartTag() + return z.tt case EndTagToken: c = z.readByte() if z.err != nil { @@ -616,39 +616,31 @@ loop: } if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' { z.readEndTag() - return EndTagToken + z.tt = EndTagToken + return z.tt } z.raw.end-- z.readUntilCloseAngle() - return CommentToken + z.tt = CommentToken + return z.tt case CommentToken: if c == '!' { - return z.readMarkupDeclaration() + z.tt = z.readMarkupDeclaration() + return z.tt } z.raw.end-- z.readUntilCloseAngle() - return CommentToken + z.tt = CommentToken + return z.tt } } if z.raw.start < z.raw.end { z.data.end = z.raw.end - return TextToken -} -return ErrorToken -} - -// Next scans the next token and returns its type. -func (z *Tokenizer) Next() TokenType { - for { - z.tt = z.next() - // TODO: remove the ReturnComments option. A tokenizer should - // always return comment tags. - if z.tt == CommentToken && !z.ReturnComments { - continue - } + z.tt = TextToken return z.tt } - panic("unreachable") + z.tt = ErrorToken + return z.tt }
その他のファイル
src/pkg/html/doc.go
:Tokenizer.ReturnComments
に関するドキュメントが削除されました。src/pkg/html/parse.go
:Parse
関数内でp.tokenizer.ReturnComments = true
と設定していた行が削除されました。src/pkg/html/token_test.go
: テストケース内でz.ReturnComments = true
と設定していた行が削除されました。
コアとなるコードの解説
このコミットの核心は、src/pkg/html/token.go
内の Tokenizer
構造体と Next
メソッドの変更にあります。
Tokenizer
構造体からの ReturnComments
フィールドの削除
以前は、Tokenizer
構造体には ReturnComments bool
というフィールドがありました。これは、トークナイザーがHTMLコメントをトークンとして返すかどうかを制御するためのフラグでした。このフィールドが削除されたことで、Tokenizer
はもはやコメントの扱いをオプションとして選択する余地がなくなり、常にコメントをトークンとして処理するようになりました。これは、HTMLパースの挙動を統一し、WebKit HTMLテストスイートのような厳格な互換性要件を満たすための重要な変更です。
Next
メソッドの変更
Next
メソッドは、Tokenizer
の主要なインターフェースであり、入力ストリームから次のHTMLトークンを読み取り、そのタイプを返します。
変更前は、Next
メソッドの内部で z.next()
というプライベートなヘルパー関数を呼び出し、その結果が CommentToken
であり、かつ z.ReturnComments
が false
の場合に continue
して次のトークンを読み込む、というロジックがありました。これは、コメントをスキップするための明示的な処理でした。
変更後、このコメントスキップのロジックが完全に削除されました。Next
メソッドは、z.tt
フィールドに直接トークンタイプを代入し、その値を返すようになりました。これにより、コメントトークンが検出された場合でも、他のトークンと同様に即座に返されるようになります。
また、変更前は Next
メソッドが z.next()
を呼び出すループを持っていましたが、変更後は Next
メソッド自体が直接トークンをスキャンし、z.tt
に結果を格納して返す単一のパスになりました。これにより、コードのフローがより直接的になり、理解しやすくなっています。
この変更は、html
パッケージがHTMLのコメントを常にパースし、それらをトークンストリームの一部として扱うことを強制します。これにより、HTMLの仕様に厳密に準拠し、様々なブラウザの実装との互換性を高めることができます。特に、WebKit HTMLテストスイートのような包括的なテストスイートをパスするためには、コメントの正確なパースが不可欠であるため、この変更はパッケージの堅牢性と信頼性を向上させる上で重要な役割を果たします。
関連リンク
- Go言語の
html
パッケージのドキュメント (現在のバージョン): https://pkg.go.dev/golang.org/x/net/html (注: コミット当時のパスとは異なる可能性がありますが、現在のパッケージ情報として参考になります) - Go言語のコードレビューシステム (Gerrit): https://go-review.googlesource.com/ (コミットメッセージにある
https://golang.org/cl/5321043
はGerritのチェンジリストへのリンクです)
参考にした情報源リンク
- WebKitプロジェクト: https://webkit.org/
- HTMLのコメントに関するMDN Web Docs: https://developer.mozilla.org/ja/docs/Web/HTML/Comments
- HTMLパーサーの仕組みに関する一般的な情報源 (例: MDN Web DocsのWebの仕組み): https://developer.mozilla.org/ja/docs/Web/HowTo/Parsing_and_rendering_web_pages