HTML 文章とそこから構築される DOM 木のペアを考える。いくつかのブラウザで、ペアの集合を図にするとこんなかんじになると思う。それぞれのブラウザの共通部分のなかのごく小さい部分が、いままでの、仕様として明確に定義された HTML だった。

世の中の多くの HTML 文章は、実際には仕様にそっていない。各ブラウザはがんばってそこから DOM 木を作っていて、その DOM 木が一致することもある。というか、一致しないとユーザーが困るのでなんとか一致させている。
HTML5 がいままでの HTML と大きくちがうのは、この、世の中の色々な HTML と周辺の問題とを、仕様として明確に定義しようとしているところだ。

例えば
- エンコーディングをいかにして推測するか?
- & ではない単体の & をどう扱うべきか
- DOCTYPE が "-//O'Reilly and Associates//DTD HTML Extended Relaxed 1.0//" ではじまっていたらどうするか?
- 要素の入れ子がおかしいときはどうするべきか?
なんてことがひたすら明文化されていて、規格の仕様書というより実装の仕様書といったほうが似つかわしいことになっている。きれいだとは思わないけど、すごいとは思う。泣ける。
Validator.nu の HTML5 パーサ
HTML5 の validation サービスとして有名な Validator.nu はパーサ部分を単体でつかえる。パーサは JAXP に準拠しているので、Java まわりのほかのもの、たとえば Scala の XML まわりとも組み合わせられる。
import nu.validator.htmlparser.sax.HtmlParser
import scala.xml.parsing.NoBindingFactoryAdapter
import scala.xml.TopScope
import org.xml.sax.InputSource
import java.io.StringReader
object Main {
def main(argv: Array[String]) = {
val adapter = new NoBindingFactoryAdapter
val parser = new HtmlParser
parser.setContentHandler(adapter)
val reader = new StringReader("<p>1<b>2<i>3</b>4</i>5</p>")
adapter.scopeStack.push(TopScope)
parser.parse(new InputSource(reader))
println(adapter.rootElem)
}
}
このパーサは Firefox 4 でも使われるらしい。といっても Firefox は Java じゃなくて C++ で書かれている。
FirefoxにHTML 5パーサ、Java→C++自動変換で性能改善3%
ここからが興味深い。Henri Sivonen氏はValidator.nu (X)HTML5 Validatorを取り込むにあたって、JavaのソースコードをC++に自動変換する処理を追加。手動で実施することなく、自動的に変換したコードをGeckoに取り込ませることに成功したという。
実際にソースをみてみると JavaParser のパーサをつかって Java のソースコードをパースして C++ のコードを吐く、という部分があるのがわかる。ただ、これは変換元の Java 側の協力あってのもので、あらゆる Java のコードが C++ に変換できるわけではない。
public final void endTokenization() throws SAXException {
Portability.releaseElement(formPointer);
formPointer = null;
Portability.releaseElement(headPointer);
headPointer = null;
while (currentPtr > -1) {
stack[currentPtr].release();
currentPtr--;
}
Portability.releaseArray(stack);
stack = null;
while (listPtr > -1) {
if (listOfActiveFormattingElements[listPtr] != null) {
listOfActiveFormattingElements[listPtr].release();
}
listPtr--;
}
Portability.releaseArray(listOfActiveFormattingElements);
listOfActiveFormattingElements = null;
// [NOCPP[
idLocations.clear();
// ]NOCPP]
Portability.releaseArray(charBuffer);
charBuffer = null;
end();
}
こんなふうに "portability" の名のもとに資源の開放を明示的におこなったり、それでも足りなくて NOCPP でかこったりしている。hacky ですね。