在浏览器的背后(二) —— HTML语言的语法解析

当你看到这篇文章意味着我辜负了@教主的殷切指望周末木有去约会,以及苏老师@我思故我在北京鼓楼乘人之危成功了……html

本文demo powered by 已经结婚的@老赵的再也不维护的wind.jsgit

物是人非啊……github

 

说回正经事,在上一篇文章中,咱们取得了初步成果,毫无心义的字符变成了有意义的token。数组

接下来咱们要把这些简单的词变成DOM树,这个过程咱们是使用栈来实现的,任何语言几乎都有栈,为了给你们跑着玩咱们仍是用JS来实现吧,JS中的栈只要用数组就行了:浏览器

function HTMLSyntaticalParser(){
    var stack = [new HTMLDocument];
    this.receiveInput = function(token) {
        //TODO
    }
    this.getOutput = function(){
        return stack[0];
    }
}

为了构建DOM树,咱们须要一个Node类,接下来咱们全部的节点都会是这个Node类的实例。在彻底符合标准的浏览器中,不同的HTML节点对应了不一样的Node的子类,咱们为了简化,就不完整实现这个继承体系了。咱们仅仅把Node分为Element和Text(若是是基于类的OOP的话,咱们须要抽象工厂来建立对象。)this

function Element(){
    this.childNodes = [];
}
function Text(value){
this.value = value || "";
}

前面咱们的token中,如下两个是须要成对匹配的:翻译

  • tag start
  • tag end

因而咱们的作法是遇到tag start就入栈,遇到tag end就出栈,而且校验一下是否匹配。code

对于Text节点,咱们则须要把相邻的Text节点合并起来,咱们的作法是当字符token入栈时检查栈顶是不是Text节点,若是是的话就合并Text节点htm

一样咱们来看看直观的解析过程:对象

 
 

当咱们的源代码彻底遵循xhtml时,这很是简单问题,然而HTML具备很强的容错能力,奥妙在于当tag end跟栈顶的start tag不匹配的时候如何处理。

因而有一个极其复杂的规则来的,幸亏w3c又一次很贴心地把所有规则都整理的很好,咱们只要翻译成对应的伪代码就行了:

http://www.w3.org/html/wg/drafts/html/master/syntax.html#tree-construction

略微干净的代码能够在这个gist找到:

https://gist.github.com/wintercn/5618683#file-htmlsyntaticalparser-js

相关文章
相关标签/搜索