【51CTO活动】8.26 带你与清华大学、搜狗、京东大咖们一同讨论基于算法的IT运维实际
前言
虚拟语法树(Abstract Syntax Tree, AST)是解释器/编译器停止语法剖析的基础, 也是众多前端编译工具的基础工具, 比如webpack, postcss, less等. 关于ECMAScript, 由于前端轮子众多, 人力过于充足, 早曾经被人们玩腻了. 光是语法剖析器就有 uglify , acorn , bablyon , typescript , esprima 等等若干种. 并且也有了AST的社区标准: ESTree。
这篇文章主要引见如何去写一个AST解析器, 但是并不是经过火析JavaScript, 而是经过火析 html5 的语法树来引见, 运用 html5 的缘由有两点: 一个是其语法复杂, 归结起来只要两种: Text 和 Tag , 其次是由于JavaScript的语法剖析器曾经有太多太多, 再造一个轮子毫有意义, 而关于 html5 , 虽然也有不少的AST剖析器, 比如 htmlparser2 , parser5 等等, 但是没有像 ESTree 那么标准, 同时, 这些剖析器都有一个成绩: 那就是定义的语法树中无法对标签属性停止操作. 所以为了处置这个成绩, 才写了一个html的语法剖析器, 同时定义了一个完善的AST结构, 然后再有的这篇文章。
AST定义
为了跟踪每个节点的位置属性, 首先定义一个基础节点, 一切的结点都承袭于此结点:
export interface IBaseNode {
start: number; // 节点起始位置
end: number; // 节点完毕位置
}
如前所述, html5的语法类型最终可以归结为两种: 一种是 Text , 另一种是 Tag , 这里用一个枚举类型来标志它们.
export enum SyntaxKind {
Text = 'Text', // 文本类型
Tag = 'Tag', // 标签类型
}
关于文本, 其属性只要一个原始的字符串 value , 因此结构如下:
export interface IText extends IBaseNode {
type: SyntaxKind.Text; // 类型
value: string; // 原始字符串
}
而关于 Tag , 则应该包括标签末尾部分 open , 属性列表 attributes , 标签称号 name , 子标签/文本 body , 以及标签闭合部分 close :
export interface ITag extends IBaseNode {
type: SyntaxKind.Tag; // 类型
open: IText; // 标签末尾部分, 比如 <div id="1">
name: string; // 标签称号, 全部转换为小写
attributes: IAttribute[]; // 属性列表
body: Array<ITag | IText> // 子节点列表, 假设是一个非自闭合的标签, 并且起始标签已完毕, 则为一个数组
| void // 假设是一个自闭合的标签, 则为void 0
| null; // 假设起始标签未完毕, 则为null
close: IText // 封锁标签部分, 存在则为一个文本节点
| void // 自闭合的标签没有封锁部分
| null; // 非自闭合标签, 但是没有封锁标签部分
}
标签的属性是一个键值对, 包含称号 name 及值 value 部分, 定义结构如下:
export interface IAttribute extends IBaseNode {
name: IText; // 称号
value: IAttributeValue | void; // 值
}
其中称号是普通的文本节点, 但是值比较特殊, 表如今其能够被单/双引号包起来, 而引号是有意义的, 因此定义一个标签值结构:
export interface IAttributeValue extends IBaseNode {
value: string; // 值, 不包含引号部分
(责任编辑:admin)