AST
工具
AST步骤(I/O)
I
Parse-> 语法分析词法分析
(Lexical Analysis)将整个代码字符串分割成最小语法单元数组。
语法分析
(Syntactic Analysis,也称 Parsing)在分词基础上建立分析语法单元之间的关系。组合分词的结果,确定词语之间的关系,确定词语最终的表达含义,生成抽象语法树。
Traverse-> 遍历
O
Manipulate-> 操作Generate Code-> 生成代码
Acron
acorn 是基于 JavaScript 开发的一个小型、快速的JavaScript解析器。
- acorn: 解析器
- acorn-loose: 解析器
(宽松模式) - acorn-walk: 遍历
ast树 的node
parse(input, options)
输入参数是一个字符串, 选项必须是设置以下列出的一些选项的对象。返回值将是 ESTree 规范 指定的抽象语法树对象。
let acorn = require("acorn");
console.log(acorn.parse("1 + 1", {ecmaVersion: 2020}));
遇到语法错误时, 解析器将引发带有有意义消息的 SyntaxError 对象。 错误对象将具有 pos 属性, 该属性指示发生错误的字符串偏移量, 以及一个 loc 对象, 其中包含引用同一位置的 {line, column} 对象。
选项由第二个参数提供, 该参数应该是包含以下任何字段的对象 (仅ecmaVersion是必需的):
optionsecmaVersion指定要解析的
ECMAScript版本。必须为3, 5, 6(2015), 7(2016), 8(2017), 9(2018), 10(2019), 11(2020)或12(2021, 部分支持)或 最新(该库支持的最新版本)。 这会影响对严格模式的支持, 保留字的设置以及对新语法功能的支持。 注意:Acorn只实现了 阶段4(最终定稿)的ECMAScript特性。其他提议的新特性必须通过插件来实现。sourceType指定代码应该被解析的模式。可以是
script或module。 这将影响全局的严格模式, 和解析import和export的声明。注意: 如果设置为
module, 即使ecmaVersion小于6, 静态import和export语法也将有效。onInsertedSemicolon如果有回调函数, 则只要解析器插入了一个缺少的分号, 就会调用该回调函数。 回调函数将被赋予插入分号的点的字符偏移量作为参数, 如果
locations被设置, 则表示此位置的{line, column}对象。onTrailingComma类似onInsertedSemicolon`, 但是为了跟踪逗号插入。
allowReserved如果为
false, 则使用保留字会产生错误。 对于ecmaVersion 3, 默认为true, 对于较高版本, 默认为false。 当value为never时, 保留字和关键字也不能用作属性名称 (如IE低版本浏览器)allowReturnOutsideFunction默认情况下, 顶层的return语句会引发错误。 将其设置为
true以接受此类代码。allowImportExportEverywhere默认情况下, 导入和导出声明只能显示在程序的顶层。 将此选项设置为
true可以允许在允许语句的任何地方allowAwaitOutsideFunction默认情况下, 等待表达式只能出现在异步函数中。 将此选项设置为
true允许具有top level await表达式。 不过, 非异步(no-async)功能仍不允许使用它们。allowHashBang启用此功能 (默认情况下关闭) , 如果代码以
#!开始 (如在shellscript中) , 第一行将被视为注释。locations当为
true时, 每个Node都有一个连接起始和终止子对象的loc对象, 每个对象包含{line, column}形式的一个行号和列号。 默认值为false。onToken如果为此选项传递了一个函数, 则每个发现的token将以与从
tokenizer() getToken()返回的tokens相同的格式传递。 如果参数是array, 则每个发现的token将被push到array中。 注意不能从回调中调用解析器, 从而破坏其内部状态。onComment如果为此选项传递函数, 每当遇到注释时, 将使用以下参数调用该函数:
block: 如果注释是块注释, 则为true, 如果是行注释, 则为false。text: 评论的内容。start: 注释开头的字符偏移量。end: 注释结尾的字符偏移量。 当locations参数被设置时, 注释的开始和结束位置{line, column}将作为两个附加参数传递。当此选项是array时, 每个注释被push到它作为对象以Esprima格式:{
"type": "Line" | "Block",
"value": "comment text",
"start": Number,
"end": Number,
// If `locations` option is on:
"loc": {
"start": {line: Number, column: Number}
"end": {line: Number, column: Number}
},
// If `ranges` option is on:
"range": [Number, Number]
}
请注意, 您不能从回调函数中调用解析器, 从而破坏其内部状态。
ranges节点的起始和终止字符偏移记录在起始和结束属性中 (直接在节点上, 而不是保存行/列数据的
loc对象) , 还要添加一个保持[start, end]数组的半标准化(semi-standardized)范围属性, 使用相同的数字, 将ranges设置为true。program通过解析第一个文件生成的树, 作为后续解析中的程序选项传递多个文件, 可以将多个文件解析为单个
AST。 会将已解析的文件以top level形式添加到现有解析树的Program(program即顶部节点) 节点。sourceFile当
locations选项为true时, 您可以传递此选项以在每个节点的loc对象中添加一个源属性。 请注意, 此选项的内容不以任何方式进行检查或处理; 您可以随意使用您选择的任何格式。directSourceFile类似
sourceFile一样, 但是一个sourceFile属性将直接添加到节点, 而不是loc对象。preserveParens如果此选项为
true, 则括号表达式由 (非标准) 括号化表达式节点表示, 该节点具有包含括号内的表达式的单个表达式属性。
parseExpressionAt(input, offset, options)
将解析字符串中的单个表达式, 并返回其AST。 如果表达式之后还有更多的字符串, 不会去解析。
tokenizer(input, options)
返回具有getToken方法的对象, 该方法可以重复调用以获取下一个token和{start, end, type, value}对象 (启用了location选项时添加了loc属性, 并且启用了ranges选项时的range属性) 。 当令牌的类型为tokTypes.eof时, 您应该停止调用该方法, 因为它将永远返回相同的token。
在ES6环境中, 返回的结果可以用作任何其他符合协议的可迭代项
for (let token of acorn.tokenizer(str)) {
// iterate over the tokens
}
// transform code to array of tokens:
var tokens = [...acorn.tokenizer(str)];
tokTypes 有一个将name映射到token object的对象, 该对象最终在token的type属性中。
getLineInfo(input, offset) 可用于为给定的字符串和偏移量获取{line, column}对象。
Parse类
Parser类的实例包含所有驱动解析的状态和逻辑。 它具有静态方法parse, parseExpressionAt和tokenizer, 它们与同名的顶级函数匹配。
使用插件扩展解析器时, 需要在类的扩展版本上调用这些方法。 要使用插件扩展解析器, 可以使用其静态扩展方法。
var acorn = require("acorn");
var jsx = require("acorn-jsx");
var JSXParser = acorn.Parser.extend(jsx());
JSXParser.parse("foo(<bar/>)", {ecmaVersion: 2020});
expand 方法采用任意数量的插件值, 并返回一个新的Parser类, 其中包括插件提供的额外的解析器逻辑。
EsTree规范
参考资料
Vedio -> Master the Art of the AST and Take Control of Your JS
Build a JS Interpreter in JavaScript Using Acorn as a Parser