您好,欢迎来到12图资源库!分享精神,快乐你我!我们只是素材的搬运工!!
  • 首 页
  • 当前位置:首页 > 开发 > WEB开发 >
    AST解析基础: 如何写一个复杂的html语法剖析库(2)
    时间:2017-08-21 21:54 来源:网络整理 作者:网络 浏览:收藏 挑错 推荐 打印

      quote: '\'' | '"' | void; // 引号类型, 能够是', ", 或许没有 

    Token解析

    AST解析首先需求解析原始文本失掉符号列表, 然后再经过上下文语境剖析失掉最终的语法树.

    相关于JSON, html虽然看起来复杂, 但是上下文是必需的, 所以虽然JSON可以直接经过token剖析失掉最终的结果, 但是html却不能, token剖析是第一步, 这是必需的. (JSON解析可以参考我的另一篇文章: 徒手写一个JSON解析器(Golang) ).

    token解析时, 需求依据以后的形状来剖析token的含义, 然后得出一个token列表.

    首先定义token的结构:

    export interface IToken { 

      start: number;    // 起始位置 

      end: number;      // 完毕位置 

      value: string;    // token 

      type: TokenKind;  // 类型 

    Token类型一共有以下几种:

    export enum TokenKind { 

      Literal     = 'Literal',      // 文本 

      OpenTag     = 'OpenTag',      // 标签称号 

      OpenTagEnd  = 'OpenTagEnd',   // 末尾标签完毕符, 能够是 '/', 或许 '''--' 

      CloseTag    = 'CloseTag',     // 封锁标签 

      Whitespace  = 'Whitespace',   // 末尾标签类属性值之间的空白 

      AttrValueEq = 'AttrValueEq',  // 属性中的= 

      AttrValueNq = 'AttrValueNq',  // 属性中没有引号的值 

      AttrValueSq = 'AttrValueSq',  // 被单引号包起来的属性值 

      AttrValueDq = 'AttrValueDq',  // 被双引号包起来的属性值 

    Token剖析时并没有思索属性的键/值关系, 均一致视为属性中的一个片段, 同时, 视 = 为一个

    特殊的独立段片段, 然后交给下层的 parser 去剖析键值关系. 这么做的缘由是为了在token剖析

    时避免上下文处置, 并简化形状机形状表. 形状列表如下:

    enum State { 

      Literal              = 'Literal'

      BeforeOpenTag        = 'BeforeOpenTag'

      OpeningTag           = 'OpeningTag'

      AfterOpenTag         = 'AfterOpenTag'

      InValueNq            = 'InValueNq'

      InValueSq            = 'InValueSq'

      InValueDq            = 'InValueDq'

      ClosingOpenTag       = 'ClosingOpenTag'

      OpeningSpecial       = 'OpeningSpecial'

      OpeningDoctype       = 'OpeningDoctype'

      OpeningNormalComment = 'OpeningNormalComment'

      InNormalComment      = 'InNormalComment'

      InShortComment       = 'InShortComment'

      ClosingNormalComment = 'ClosingNormalComment'

      ClosingTag           = 'ClosingTag'

    整个解析采用函数式编程, 没有运用OO, 为了简化在函数间传递形状参数, 由于是一个同步操作,

    这里应用了JavaScript的事情模型, 采用全局变量来保存形状. Token剖析时所需求的全局变量列表如下:

    let state: State          // 以后的形状 

    let buffer: string        // 输入的字符串 

    let bufSize: number       // 输入字符串长度 

    let sectionStart: number  // 正在解析的Token的起始位置 

    let index: number         // 以后解析的字符的位置 

    let tokens: IToken[]      // 已解析的token列表 

    let char: number          // 以后解析的位置的字符的UnicodePoint 

    在末尾解析前, 需求初始化全局变量:

    function init(input: string) { 

      state        = State.Literal 

      buffer       = input 

      bufSize      = input.length 

      sectionStart = 0 

      index        = 0 

      tokens       = [] 

    然后末尾解析, 解析时需求遍历输入字符串中的一切字符, 并依据以后形状停止相应的处置

    (改动形状, 输入token等), 解析完成后, 清空全局变量, 前往完毕.

    export function tokenize(input: string): IToken[] { 

      init(input) 

      while (index < bufSize) { 

        char = buffer.charCodeAt(index

        switch (state) { 

        // ...依据不同的形状停止相应的处置 

        // 文章疏忽了对各个形状的处置, 详细了解可以查看源代码 

        } 

        index++ 

      } 

      const _nodes = nodes 

      // 清空形状 

      init(''

      return _nodes 

    语法树解析

    在获取到token列表之后, 需求依据上下文解析失掉最终的节点树, 方式与tokenize相似,均采用全局变量保存传递形状, 遍历一切的token, 不同之处在于这里没有一个全局的形状机。

    由于形状完全可以经过正在解析的节点的类型来判别。

    export function parse(input: string): INode[] { 

      init(input) 

      while (index < count) { 

        token = tokens[index

        switch (token.type) { 

          case TokenKind.Literal: 

            if (!node) { 

              node = createLiteral() 

              pushNode(node) 

            } else { 

              appendLiteral(node) 

            } 

            break 

          case TokenKind.OpenTag: 

            node = void 0 

            parseOpenTag() 

            break 

          case TokenKind.CloseTag: 

            node = void 0 

            parseCloseTag() 

            break 

          default

            unexpected() 

            break 

        } 

        index++ 

      } 

      const _nodes = nodes 

      init() 

      return _nodes 

    不太多解释, 可以到GitHub查看源代码.

    结语

    项目已开源, 称号是 html5parser , 可以经过npm/yarn安装:

    npm install html5parser -S  

    OR  

    yarn add html5parser 

    或许到GitHub查看源代码: acrazing/html5parser 。

    目前对正常的HTML解析已完全经过测试, 已知的BUG包括对注释的解析, 以及未正常完毕的

    输入的解析处置(均在语法剖析层面, token剖析已经过测试).

    【编辑引荐】

    立足当下共享未来 第四届HTML5移动生态大会浩荡召开

    白鹭时代产品线片面退化 携手行业抓住HTML5游戏拐点机遇

    HTML5游戏开发难点之效率、功用和加载量

    HTML5音频API Web Audio

    HTML5中手势原理剖析与数学知识的实际

    (责任编辑:admin)