字句化

構文解析器 (Web)

[1] 本項では HTMLXML構文解析器の基礎的な概念を扱います。専ら一方のみに関係する事項は、 HTML構文解析器XML構文解析器の項をご覧ください。

状態

[83] 次のような状態を持ちます。

HTML構文解析器またはXML構文解析器です。
入力バイトストリーム
入力ストリーム
script-created parser では書き込みが行われます。
confidence
初期値が与えられます。
挿入点
入力ストリーム中の位置または未定義です。 document.write からも参照されます。 仕様書HTML構文解析器にのみ定義していますが、 XML構文解析器にも存在すると考えることで、両者の処理を統合できます。 (仕様書でも stop parsingXML構文解析器であっても挿入点が参照されています。)
次入力文字
開いている要素のスタック
現在節点
挿入モード
文書
abort 済み
構文解析器のabortが行われたかどうか。 構文解析器外部から参照されることがあります。
stop parsing 済み
stop parsing が行われたかどうか。 構文解析器のフラグというよりは文書completely loaded フラグと解釈するのが自然かもしれません (stops parsing 参照)。 構文解析器外部から参照されることがあります。
スクリプト入れ子水準 (script nesting level)
document.write の再帰呼び出しを管理する変数です。 初期状態では0に設定されます。 仕様書HTML構文解析器にのみ定義していますが、 XML構文解析器にも存在すると考えることで、両者の処理を統合できます。
構文解析器停止フラグ (parser pause flag)
document.write による構文解析器の動作停止を管理するフラグです。 初期状態ではに設定されます。 仕様書HTML構文解析器にのみ定義していますが、 XML構文解析器にも存在すると考えることで、両者の処理を統合できます。
素片構文解析
構文解析素片構文解析モードで実行されるか否か。 現在節点null ではないか否かと等価。

構文解析器と文書

[10] 構文解析器文書に関連付けられています。

navigate において構文解析器を作成した時に関連付けられます。

[11] 文書から見て動作中の構文解析器のことを(あれば)活性構文解析器といいます。

[73] 文書構文解析器を持つか否かとその状態は、 try to scroll to the fragment で参照されます。

[12] ある構文解析器インスタンスが扱う文書は1つだけです。

ただし template 要素があるときは、雛形内容文書も関係してきます。 また、スクリプトによって挿入先の要素が別の文書に移動された時は、その文書に新しい節点も挿入されることになります。

[13] 文書に対してそれを扱う構文解析器は1つとは限りません。例えばその文書自体を作った構文解析器innerHTML構文解析器は別物です。また document.open では新しい構文解析器が作られます。

[81] スクリプトの準備では、script 要素節点文書script 要素を作成した構文解析器文書が等しいか比較されます。

[82] スクリプトの準備では、 script 要素を作成した構文解析器の種別 (HTML or XML) やスクリプト入れ子水準が参照されます。 作成した構文解析器文書スクリプトをブロックしているスタイルシートを持つかどうかや、 スクリプトに関するリストや集合各種も参照されます。

[100] この他、文書には document.write の動作に関わる内部状態がいくつかあり、 構文解析器から参照・変更されます。


[26] 文書は、 活性構文解析器 (active parser) を持つことがあります。

[75] 文書は、 活性構文解析器がabortされた (active parser was aborted) を持ちます。 初期値はです。 HTML Standard 文書のabortに設定されます。 document.open, document.write などに影響します。

入力

[24] 構文解析の対象となる入力は、入力バイトストリーム (バイト列) または入力ストリーム (文字列) として構文解析器に与えられます。

[25] 入力がバイト列の場合は、バイトの解釈に用いる文字符号化ロケール、 呼び出される文脈 (navigateXHR か) といった情報も与える必要があります。 詳しくは charset sniffing を参照。

[92] かつてはロケールisindex 開始タグの解釈にも影響しました。

[30] HTML構文解析器XML構文解析器では、構文解析の各段階の動作に様々な違いが生じます。

[27] HTML構文解析器の動作は、スクリプト有効か否かで変化します。

スクリプトフラグを参照してください。
[72] スクリプトを実装しない場合は、スクリプトが無効な場合の動作だけ実装すれば十分です。スクリプトを実装する場合は、 スクリプトが無効な場合とスクリプトが有効な場合の両方を実装する必要があります。

[29] innerHTML など素片構文解析では、 文脈要素によって構文解析の開始の時点の状態やその後の動作が左右されます。

文脈要素の名前や属性の他、祖先が影響することもあります。

出力

[28] 構文解析の結果は、開始時に指定されたとなる節点子孫という形で現れます。 また、奇癖モードか否かなど、内部状態 (IDL属性など) を変化させることもあります。

[31] また、構文解析器は、1つ以上の構文解析エラーを発見すると、 これを報告することがあります。仕様上義務付けられてはいませんが、 Webブラウザーによっては Console構文解析エラーを報告するようです。

スクリプトなどで著者がこれを受け取ることはできないようです。

一時停止

構文解析器内部での一時停止

[2] 構文解析器HTMLscript 終了タグの処理の一部でイベントループをスピンすることがあります。 また字句解析器構文解析器ブロックされます。これらにより、特定のスクリプト群の実行が終わるまで構文解析器は処理を停止することになります。

[3] HTML構文解析器に(のみ)は、 parser pause flag があり、このフラグが設定されている間は字句化器の処理が止まります。 これは入れ子の document.write の呼び出しや SVG script 要素からの document.write の呼び出しの構文解析処理をスクリプト実行後まで遅延させるために使われています。

XML では document.write が使えないので、相当するフラグはありません。

[15] XML構文解析器外部実体を展開する時は、主たる構文解析を一時停止して、外部実体側の構文解析が終了したら戻ってくる形で実装することになるでしょう。 これは document.write のように入力ストリームの現在位置に外部実体側の入力ストリームからバイト列を挿入していく形でも実装できるでしょうし、 別の構文解析器の結果を受け取って主たる構文解析器の出力に挿入する形でも実装できるでしょう。

[16] ただし HTML Standard に従うXML構文解析器外部実体の展開を行いません。

[52] HTML字句化器は「各ステップ前」に parser pause flag をチェックして、 なら字句化しないことになっています。「各ステップ」が何を指しているか不明瞭ですが、 parser pause flagHTML/SVG script タグによるスクリプト処理の際と字句の要素の作成カスタム要素を作成する際にのみになることがあるものなので、 スクリプト実行の次の字句の取得の処理や、 次のタスクによる字句化器からの字句の取得の処理を実行しようとした時にチェックすれば十分そうです。

[53] parser pause flag と「ブロック」は同じ動作のように見えますが、 parser pause flag はあらゆる字句化器の呼び出しを止めるのに対し、 ブロックは別のタスクによる字句化器の呼び出しだけを止めます。 ですから、ブロック中に実行されたスクリプトによって document.write が呼び出された場合は、 parser pause flagでなければ、 その字句化は行われます。

スクリプトによる一時停止

[54] 構文解析器によるスクリプト実行のための一時停止以外にも、 構文解析器から呼び出されたスクリプトの処理のためにイベントループの実行の一時停止が発生することがあります。 当然ながらその間も構文解析器は停止したままです。

[55] 例えばスクリプト内部で alert を実行するとイベントループpause します。また showModalDialog を実行するとイベントループのスピンが派生します。

[56] 明示的に実行が停止されなくても、処理の実行が長時間に渡り実質的に停止状態となることもあります。

[57] 例えば XHR を同期モードで呼び出すと、 fetch が完了するまでメソッド呼び出しが完了しないので、 当然その間構文解析器は停止したままです。

停止

[40] 構文解析は、正常終了 (>>6) か中断 (>>4) のいずれかによって終了します。

[41] 構文解析は、終了するとは限りません。から無限のバイト列が送信され続けている場合は、 構文解析も無限に続きます。

正常終了

[6] 構文解析の処理がすべて終了すると、 stop parsing が実行されます。

中断

[4] 構文解析器abort a parser が呼び出されると入力ストリーム開いている要素のスタックを破棄します。 これによって未処理の入力は捨てられ、それ以降の字句化が行われません。

[5] abort は、 window.closedocument.open が呼ばれた時や、 navigate されて現在の文書が破棄される場合などに呼ばれます。

[74] abort a parser を参照。

[58] スクリプトは実行時間等の制限超過によって停止させられることがあります (スクリプトの項を参照)。構文解析器から呼び出されたスクリプトが停止された場合、 スクリプトが正常終了した場合と同じように構文解析器の処理が再開されるものと思われます。

[59] スクリプトから呼び出した構文解析器の処理に時間がかかるような場合、 構文解析器の動作が途中で停止させられることもあるかもしれません。 構文解析器から呼び出されたスクリプトからの構文解析器の呼び出しに時間がかかる場合どのように処理されるのか謎です。

再帰的実行

[7] HTML構文解析器(のみ)は document.writedocument.writeln により、スクリプトを介して再帰的に呼び出されることがあります。外側と内側でどちらも同じ構文解析器ですから、 入力ストリームや種々の状態はすべて共有されています。外側の構文解析処理は、内側で行われた構文解析器への操作により発生する処理の続きとしてスクリプト終了後に継続されます。

[8] このため HTML構文解析器スクリプトが実行するまで続きを字句解析構文解析できません (投機的実行はできますが)。

[9] document.write がなかったとしても、また実際に無い XML であっても、 DOM に反映させるとスクリプトから観測可能になってしまうので、その直前までしか処理できません。
[42] ここでいう再帰的呼び出しとは、同一の構文解析器インスタンススクリプトを介して呼び出されることをいいます。 別の対象を構文解析する別のインスタンススクリプトから呼び出されること (例えば navigate によって呼び出された HTML構文解析器の処理中にスクリプトから innerHTML によって別の構文解析器が作られ、実行されること) はよくあります。

再起動

[14] HTML構文解析器(のみ)は文字符号化宣言を発見し、処理中の文字符号化と実際の文字符号化が一致していないことを検出したとき、 change the encoding 処理により再起動することがあります。この再起動は、現在の文書破棄して同じ入力バイト列に対して navigate しなおすというかなり強引な形で行われます。スクリプトにより著者に、 レンダリングにより利用者に観測可能なこともあります。

[70] HTML構文解析器XML構文解析器によって呼び出されるアプリケーションキャッシュ選択アルゴリズムも、 navigate 全体をやり直させることがあります。

構成要素

[61] 構文解析器は次の部分で構成されます。

[62] なお、これはあくまで仕様書上の構成であって、同じ入力に対して同じ動作をする限り、 この構成に従う必要はありません。
net
ネットワーク
v
->
ibs
ibs
入力バイトストリーム
->
is
is
入力ストリーム
->
token
token
字句
->
dom
dom
DOM
->
ibs
->
script
script
スクリプト
->
is
->
net
>>
2
v

字句化器と木構築器

[63] 字句化木構築は、2つのほぼ独立した処理段階として規定されていますが、 いくつか例外的に両者の動作が干渉することがあります。いずれもHTML構文解析器のみの動作です。

[67] これは一例として次のように実現できます。

  1. 入力ストリームEOF に達するまで次の処理を繰り返す。
    1. 字句化を行う。ただし、次の条件のいずれかを満たしたら停止する。
    2. 得られた字句の列を木構築器に渡して処理させる。

[94] ここで、 >>63 だけでなく、スクリプトdocument.write から入力ストリームに介入する可能性も考慮する必要があります。 具体的には、次のものがあります。

[71] アプリケーションキャッシュ選択アルゴリズムの実行タイミングにも注意が必要です。 >>67 だけでは正しく処理できないケースがあります。 (構文解析器の動作そのものには影響しませんが、 スクリプトfetch 経由で動作が変化する可能性はあります。)

字句化器と API

[68] navigate によって、あるいはスクリプトDOM API によって構文解析器字句化器を作成し動作させる場合以外に、 次の場合に外部から字句化器の挙動が操作されることがあります。 これは構文解析器中の文書に含まれるスクリプトの実行によって呼び出されることもあれば、 他の閲覧文脈から呼び出されることもあります。

open
document.open
->
write
->
writeln
write
document.write / document.writeln
->
write
->
close
close
document.close
[69] 平文への navigatetext/plain などを指定した document.open は、字句化器の初期状態を PLAINTEXT state に設定します。

字句化

[84] HTML でも XML でも、文字字句以外のすべての字句に対応するソース上の構造は < から始まります。

[85] 文字字句 (の連続) も、 CDATA区間の場合は < から始まります。
[86] 字句より細かな字句に類する単位としては、属性 (要素字句内) や属性定義 (DOCTYPE字句内の要素宣言内) が < 以外から始まりますが、 その他は < から始まります。

[87] 特定の属性の値によって分岐することは、字句化器内ではありません。

[88] HTML構文解析器木構築器では次の分岐があります。

[89] XML構文解析器木構築器では次の分岐があります。

[90] xml:space 属性構文解析器の動作には影響しません。

[99] 字句の要素の作成では次の分岐があります。

[91] なお、属性はこれ以外には、 Element の作成時に内容属性として設定される他は、 既存の html 要素body 要素に追加されるケース、 活性書式付け要素のリストへの追加時に比較されるケースがあります (いずれもHTML構文解析器のみ)。

DOM 操作

[21] 構文解析器によるDOM木の操作は、基本的には DOM API によって行われる操作と同じものですが、一部は特別な動作が定義されていることもあります。

[22] 構文解析器による DOM 操作の動作については、次の各項を参照してください。

[23] 節点の作成や節点の内部状態 (IDL属性の値) の設定 (createElementNScompatMode など) に加えて、次のようなメソッドに相当する操作が行われます。

[49] 構文解析器による DOM の操作は、変異観察器に報告されます。

[50] 変異イベント発火しません。

[51] 雛形内容は別の文書に属するため、またスクリプトによって節点がまったく別の文書に移動される可能性があるため、 構文解析器が扱う節点が同じ文書に属する保証はありません。

特別な動作

[60] 構文解析器は純粋なDOM木の構築操作の他に、次のような動作をします。

[48] 構文解析器の動作中にスクリプトが実行されることがあります。 スクリプト構文解析器の処理は複雑に関わっています。

詳しくは script字句の要素の作成の項を参照してください。

[93] スクリプトの前後には、マイクロタスクが実行されます。 構文解析器による挿入に伴う変異観察器への報告 (>>49) は、 このタイミングで実行されます。

[80] Webブラウザーによっては、文書要素文書への挿入のタイミングで内容スクリプトを実行できるかもしれません。

歴史

[17]

構文解析 (parsing)
構文解析とは、文書を走査し、 文書の中に含まれている情報情報構造化されている要素の文脈において濾過する行為です。

[18] HTML構文解析器の動作は、長らく明確に規定されていませんでしたが、 2007年頃、 Web Applications 1.0 (後の HTML5) によって初めて詳細な動作が記述されました。

詳しくは HTML構文解析器を参照。

[19] XML構文解析器の動作は、 XML 本体仕様によってごく限定的に制約が規定されていました。 より詳細な動作は XML5 (後の XML-ER) によって HTML構文解析器に倣って定義されましたが、 XML に対する関心の低さのため、仕様書は未完成のまま保留状態にあります。 HTML Standard にも限定的ながらXML構文解析器の動作の規定があります。

[20] 構文解析器を呼び出したり、操作したりする API は、 HTML StandardDOM Parsing の両仕様書によって定義されています。 これらも長らく明確な定義が存在していませんでしたが、 WHATWG において2007年頃から数年かけて整備されました。

[101] Ignore document.open/write after the active parser has been aborted (foolip著, ) https://github.com/whatwg/html/commit/ead4aa8ec576d7d330a04f7ec8508e336b895fdb

[102] [css-selectors-4] Include whitespace in non-optional `<combinator>` · Issue #7027 · w3c/csswg-drafts · GitHub, https://github.com/w3c/csswg-drafts/issues/7027

[103] Hixie が形式的言語記述を避けて英語による厳密な構文・構文解析器の記述にこだわったのはこういう混乱を避けるためだったというのがわかる一件。