【Acorn】JS解析器编译原理

2023-07-26 23:44:23

在这里插入图片描述

Acorn是什么?

Acorn 是一个用 JavaScript 编写的解析器,专门用于将源代码解析为抽象语法树(Abstract Syntax Tree,AST)。

它是一个轻量级、高性能的解析器,被广泛应用于许多 JavaScript 工具和框架中。

Acorn的整体工作流程

  1. 输入源代码(Input Source Code):接收 JavaScript 源代码作为输入。
  2. 词法分析(Lexical Analysis):Acorn 使用有限状态机将源代码拆分成一个个标记(tokens),包括关键字、标识符、运算符等。它会跳过空白字符,并处理字符串和注释等特殊情况。
  3. 语法分析(Syntax Parsing):使用递归下降解析器,Acorn 根据 JavaScript 语言规范定义的文法规则对标记序列进行语法分析。它逐个检查每个标记,并根据上下文判断其属于哪种语句或表达式类型。
  4. AST 构建与生成:通过解析器中内部数据结构来构建抽象语法树(AST)。AST 是以 JSON 形式表示源码结构和元素之间关系的数据结构。在这一步骤中,Acorn 将每个标记转换成相应的 AST 节点,并按照其在源代码中出现的顺序组织起来。
  5. 可选阶段:除了基本的词法和语义分析之外,在需要更复杂转换或静态分析时,可能还包含其他额外操作。例如,可以在这个阶段执行自定义的访问者模式,对 AST 进行进一步处理或生成新的代码。

Acorn - Parser Class 核心类

Parser类是 Acorn 解析器的核心部分

核心代码示例:

https://github.com/acornjs/acorn/tree/master/acorn/src 源码仓库

export class Parser {
  constructor(options, input, startPos) {
    //...构造函数
  }
  parse() {
    // ...执行解析操作
  }
  // 通过读取当前变量作用域(currentVarScope)以及相关标记位(flags)
  // 判断当前上下文所处的环境状态。
  get inFunction() { return (this.currentVarScope().flags & SCOPE_FUNCTION) > 0 }
  get inGenerator() { return (this.currentVarScope().flags & SCOPE_GENERATOR) > 0 }
  get inAsync() { return (this.currentVarScope().flags & SCOPE_ASYNC) > 0 }
  get allowSuper() { return (this.currentThisScope().flags & SCOPE_SUPER) > 0 }
  get allowDirectSuper() { return (this.currentThisScope().flags & SCOPE_DIRECT_SUPER) > 0 }
  get treatFunctionsAsVar() { return this.treatFunctionsAsVarInScope(this.currentScope()) }
  get inNonArrowFunction() { return (this.currentThisScope().flags & SCOPE_FUNCTION) > 0 }
  static extend(...plugins) {
    // ...允许扩展 Parser 类
  }
  // 解析入口
  // 在实例化 Parser 对象后调用其 parse 方法并返回结果
  static parse(input, options) {
    return new this(options, input).parse()
  }
  //  在指定位置 pos 解析表达式
  static parseExpressionAt(input, pos, options) {
    // 创建一个新的 Parser 实例对象
    let parser = new this(options, input, pos)
    // 调用 nextToken 来获取下一个 token
    parser.nextToken()
    // 调用 parseExpression 进行表达式解析
    return parser.parseExpression()
  }
  // 分词入口 词法分析器
  static tokenizer(input, options) {
    return new this(options, input)
  }
}

分词 Tokenization(上述流程词法分析阶段)

分词(Tokenization)是Acorn编译的一个关键步骤,核心目标是将源代码字符串转换为一个个具有语义信息的标记(tokens)。而tokens,也就是上述流程中的词法分析的运行结果。

状态机

对每个字符意义进行状态机的判断,状态机即是用来描述在分词过程中,如何根据当前字符和之前的上下文转换到下一个状态。

状态机在分词阶段做了什么?
  1. 读取字符:从输入源码中逐个读取字符。
  2. 状态转移和匹配规则
    标识符(Identifier):
    如果当前字符是字母或下划线,则进入标识符状态,并持续读取后续的字母、数字或下划线以构成完整的标识符。如果遇到其他类型的字符,结束标识符并生成相应的 Token。
    数字(Numeric):
    如果当前字符是数字,则进入数字状态,并持续读取后续的数字和小数点以构成完整的数值常量。同时记录是否为科学计数法形式。遇到其他类型的字符,结束数字并生成相应 Token。
    字符串(String):
    如果当前字符是引号(单引号或双引号),则进入字符串状态,并持续读取直到遇到匹配引号为止。期间还需要处理转义序列等特殊情况。
    注释(Comment):
    如果当前字符是斜杠 / ,则进行注释检测。如果接下来连续两个斜杠 // 或者一个斜杠加上星号 /*则进入注释处理过程,直至遇到对应结束符 */ 为止。
    运算符和标点符号(Operator and Punctuation):
    根据当前字符的特定运算符或标点符号,直接生成相应 Token,并结束状态。 其他情况:如果遇到无法匹配上述规则的字符,则报告错误并结束状态。

对分词结果tokens进行分析(上述流程语法分析阶段)

依据 JavaScript 语言规范中定义的文法规则,检查每个标记并确定其属于哪种语句或表达式类型。在这一过程中,Parser利用上下文信息来判断正确的结构,并生成相应节点。

Acorn 在语法分析阶段使用了递归下降解析器(Recursive Descent Parser)的技术,它是一种自顶向下、从左到右递归地进行语法分析的方法。用于将输入代码序列转换为抽象语法树(Abstract Syntax Tree,AST)
在这里插入图片描述

1. 初始化:
设置初始状态和其他必要变量。
2. 标记流读取:
逐个读取词法分析器生成的标记,并记录当前位置信息。
3. 语法规则匹配与处理:
根据 JavaScript 语言的文法规范,在遇到不同类型的标记时应用相应的文法规则。
例如:
表达式解析:处理各种表达式,如算术表达式、赋值表达式、函数调用等。
声明解析:处理变量声明、函数声明、类声明等。
控制流解析:处理条件语句、循环结构等控制流程相关内容。
4. AST 构建与节点生成:
根据匹配到的语法规则,在相应位置上创建对应类型的 AST 节点,并填充相关信息。
每个节点通常包含一个或多个子节点来表示嵌套关系和操作符优先级等细节。
5. 错误检测与报告:
如果遇到无效或意外输入,则进行错误检测并报告相应的语法错误。这些错误可能包括缺失分号、未定义变量等。
6. AST 返回:
当整个标记流处理完成时,将生成的 AST 返回给调用者。

更多推荐

MySQL性能优化——MYSQL执行流程

MySQL执行流程1-5如下图。MySQL的架构共分为两层:Server层和存储引擎层,Server层负责建立连接、分析和执行SQL。MySQL大多数的核心功能模块都在这实现,主要包括连接器,查询缓存、解析器、预处理器、优化器、执行器等。另外,所有的内置函数(如日期、时间、数学和加密函数等)和所有跨存储引擎的功能(如存

移动端适配以及多屏幕自适应方案

文章目录前言一、移动端适配问题二、meta-viewport标记三、rem字体适配四、vw和vh五、postcss转换插件总结前言本文主要记录适配移动端以及多屏幕的解决办法,还有postcss转换插件的编写。一、移动端适配问题在MDN中提到:在移动设备和其他窄屏设备中,某些内容在比普通屏幕更宽的虚拟窗口或视口中渲染页面

PC微信3.9.7内测版,更新功能一览(附下载)

之前小编发布了PC微信3.9.7的内测版本,不过大家没有内测权限,不能够安装体验,本次正式版终于来了,大家可以下载安装体验,和之前一样小编给大家介绍本次PC版微信更新的内容,感兴趣的朋友可以下载体验一下!1、聊天界面表情弹窗新增搜索表情功能大家比较期待的表情搜索功能终于上线了,大家以后聊天终于可以使用更加丰富的表情包了

C语言零基础教程(memset,memcpy函数,memmove函数)

文章目录前言一、memset函数二、memcpy函数三、memmove函数总结前言本篇文章来讲解一下memset和memcpy函数,这两个函数在C语言中也是比较重要的,这里我们就来学习一下这两个函数的使用方法吧。一、memset函数memset函数是一个C标准库中的函数,用于将一块内存区域的每个字节设置为指定的值。me

双向链表的实现(增删查改)——最好理解的链表

双向链表的实现一,双向链表的特点二,双向链表的结构三,双向链表的内容实现3.1创建node节点3.2初始化3.3打印3.4插入3.4.1尾插3.4.2头插3.4.3在pos位置上插入3.5删除3.5.1尾删3.5.2头删3.5.3删除pos位置上的数据四,调试技巧(具体示例)五,总结一,双向链表的特点这里的双向链表就是

Python语言学习实战-内置函数reduce()的使用(附源码和实现效果)

实现功能reduce()是一个内置函数,它用于对一个可迭代对象中的元素进行累积操作。它接受一个函数和一个可迭代对象作为参数,并返回一个单个的累积结果。reduce()函数的语法如下:reduce(function,iterable[,initializer])其中,function是一个二元函数,它接受两个参数并返回一

Mysql的基本查询练习

目录一、Create1.1单行数据+全列插入1.2多行数据+指定列插入1.3插入否则更新1.4替换二、Retrieve2.1全列查询2.2指定列查询2.3查询字段为表达式2.4为查询结果指定别名2.5结果去重2.6where条件2.6NULL的查询2.7结果排序三、Update四、Delete五、插入查询结果六、聚合函

学科知识图谱学习平台项目 :技术栈Java、Neo4j、MySQL等超详细教学

项目设计集合(人工智能方向):助力新人快速实战掌握技能、自主完成项目设计升级,提升自身的硬实力(不仅限NLP、知识图谱、计算机视觉等领域):汇总有意义的项目设计集合,助力新人快速实战掌握技能,助力用户更好利用CSDN平台,自主完成项目设计升级,提升自身的硬实力。专栏订阅:项目大全提升自身的硬实力[专栏详细介绍:项目设计

ARMv7处理器

本文档介绍常见的ARM架构,包括Cortex-A5,Cortex-A7,Cortex-A8,Cortex-A9,Cortex-A15.常见的术语DFT(DesignforTest),为了增强芯片可测性而采用的一种设计方法APB(AdvancedPeripheralBus),是一种低速外设总线接口,通常用于将外部设备(如

2.策略模式

UML图代码main.cpp#include"Strategy.h"#include"Context.h"voidtest(){Context*pContext=nullptr;/*StrategyA*/pContext=newContext(newStrategyA());pContext->contextInter

如何理解JavaScript定时器的4种写法-附带面试题讲解

在JavaScript里,我们已经会使用一些原生提供的方法来实现需要延时执行的操作代码,比如很多在线时钟的制作,图片轮播的实现,还有一些广告弹窗,但凡可以自动执行的东西,都是可以和定时器有关的。今天就来和大家分享一下,关于我们在JavaScript里经常会使用到的定时器方法在JavaScript里,我们要学习四个定时器

热文推荐