基于C语言的javascript语法解析器

First Post:

Last Update:

Word Count:
4.2k

Read Time:
18 min

本博客记录本人完成的基于vibe coding的编译原理课程大作业:基于C语言的javascript语法解析器。

对测试用例basic.jsfunctions.js进行了补充,下面的内容还是以旧的测试用例为准,实际运行以实际测试用例情况为准(编译器代码没有改变)


工具:re2c+bison(Flex不支持unicode定义,javascript中的多种符合是应用unicode定义的)

目标:可以判断输入的javascript脚本是否符合语法;针对省略的分号,实现ASI(Automatic Semicolon Insertion) 机制。

规则:https://tc39.es/ecma262/

接下来是整个基于C语言的javascript语法解析器的项目介绍:

项目介绍

这是一个基于C语言的JavaScript语法解析器,使用re2c进行词法分析和bison进行语法分析,完全遵循ECMAScript规范并实现了ASI(自动分号插入)机制。

完整的项目源代码压缩包(旧测试用例): js-parser.zip 项目下载

完整的项目源代码压缩包(新测试用例): js-parser-2.zip 项目下载

包含内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
js-parser/
├── src/
│ ├── ast.h - AST节点定义
│ ├── ast.c - AST实现
│ ├── lexer.re - re2c词法分析器
│ ├── parser.y - bison语法分析器
│ └── main.c - 主程序
├── tests/
│ ├── positive/ - 正向测试用例
│ │ ├── basic.js
│ │ ├── functions.js
│ │ ├── asi_return.js
│ │ ├── asi_statements.js
│ │ └── unicode.js
│ └── negative/ - 负向测试用例
│ ├── invalid_syntax.js
│ └── asi_ambiguous.js
└── Makefile

技术架构

  • 整体流程

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    JavaScript源代码

    词法分析器 (lexer.re - re2c)

    Token流 + ASI处理

    语法分析器 (parser.y - bison)

    抽象语法树 (AST)

    语法验证结果
  • 核心组件

    1. 词法分析器 (src/lexer.re)

      • 使用re2c生成,支持UTF-8编码
      • 识别JavaScript的所有词法单元(关键字、标识符、字面量、运算符等)
      • 实现ASI的词法层处理(跟踪换行符)
      • 关键变量:seen_newlinelast_token
    2. 语法分析器 (src/parser.y)

      • 使用bison生成LALR(1)解析器
      • 定义完整的JavaScript语法规则
      • 实现ASI的语法层处理(受限产生式)
      • 生成抽象语法树
    3. AST模块 (src/ast.c/h)

      • 定义26种AST节点类型
      • 提供38个创建函数
      • 实现AST打印和内存管理
    4. 主程序 (src/main.c)

      • 文件读取和命令行参数处理

      • 调用解析器并输出结果

使用方法:

  • 步骤一:

    安装依赖

    1
    2
    sudo apt-get update
    sudo apt-get install build-essential re2c bison

    验证安装

    1
    2
    3
    re2c --version   # 应显示版本号 (需要 >= 1.0)
    bison --version # 应显示版本号 (需要 >= 3.0)
    gcc --version # 应显示版本号
  • 步骤二:

    下载文件,解压并编译

    1
    2
    cd js-parser
    make

    应该看到:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    mkdir -p build
    gcc -Wall -Wextra -g -std=c11 -c src/main.c -o build/main.o
    gcc -Wall -Wextra -g -std=c11 -c src/ast.c -o build/ast.o
    re2c -o src/lexer.c -8 --input-encoding utf8 src/lexer.re
    bison -d -v -o src/parser.tab.c src/parser.y
    src/parser.y: warning: 48 shift/reduce conflicts [-Wconflicts-sr]
    src/parser.y: warning: 38 reduce/reduce conflicts [-Wconflicts-rr]
    src/parser.y: note: rerun with option '-Wcounterexamples' to generate conflict counterexamples
    gcc -Wall -Wextra -g -std=c11 -Wno-unused-variable -Wno-unused-function -c src/lexer.c -o build/lexer.o
    gcc -Wall -Wextra -g -std=c11 -Wno-unused-function -c src/parser.tab.c -o build/parser.tab.o
    gcc -Wall -Wextra -g -std=c11 -o js-parser build/main.o build/ast.o build/lexer.o build/parser.tab.o

    ======================================
    ✓ Build successful!
    ======================================

    编译过程说明:

    1. re2c生成 src/lexer.c(词法分析器)
    2. bison生成 src/parser.tab.csrc/parser.tab.h(语法分析器)
    3. gcc编译所有C文件
    4. 链接生成可执行文件 js-parser
  • 步骤三:

    • 基本用法:

      1
      2
      3
      # 基本用法
      # 解析JavaScript文件
      ./js-parser tests/positive/basic.js

      应该看到输出:

      1
      2
      3
      Parsing 'tests/positive/basic.js'...
      ✓ Parsing successful!
      Total lines: 16
    • 详细模式(查看AST):

      1
      2
      # 使用 -v 或 --verbose 显示完整的AST
      ./js-parser -v tests/positive/basic.js

      输出:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      Parsing 'tests/positive/basic.js'...
      ✓ Parsing successful!
      Total lines: 16

      === Abstract Syntax Tree ===
      Program
      StatementList (4 statements)
      VariableDeclaration(var) name=x
      Initializer:
      Literal (number) 42
      VariableDeclaration(let) name=y
      Initializer:
      Literal (string) "hello"
      ...
    • 运行所有测试:

      1
      make test

      这将:

      1. 运行所有正向测试(应该成功)
      2. 运行所有负向测试(应该失败)
      3. 显示测试结果摘要

      输出:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      ======================================
      Running Positive Tests
      ======================================

      Testing: tests/positive/asi_return.js
      Parsing 'tests/positive/asi_return.js'...
      ✓ Parsing successful!
      Total lines: 15

      Testing: tests/positive/asi_statements.js
      Parsing 'tests/positive/asi_statements.js'...
      ✓ Parsing successful!
      Total lines: 19

      Testing: tests/positive/basic.js
      Parsing 'tests/positive/basic.js'...
      ✓ Parsing successful!
      Total lines: 18

      Testing: tests/positive/functions.js
      Parsing 'tests/positive/functions.js'...
      ✓ Parsing successful!
      Total lines: 16

      Testing: tests/positive/unicode.js
      Parsing 'tests/positive/unicode.js'...
      ✓ Parsing successful!
      Total lines: 9

      ======================================
      Running Negative Tests (should fail)
      ======================================

      Testing: tests/negative/asi_ambiguous.js
      Parsing 'tests/negative/asi_ambiguous.js'...
      ✗ Parsing failed with 1 error(s)
      ✓ Failed as expected

      Testing: tests/negative/invalid_syntax.js
      Parsing 'tests/negative/invalid_syntax.js'...
      ✗ Parsing failed with 1 error(s)
      ✓ Failed as expected

      ======================================
      ✓ All tests passed!
      ======================================

测试用例说明

  • 正向测试(tests/positive/)

    • basic.js - 基本语法

      1
      2
      3
      4
      5
      6
      var x = 42;
      let y = "hello";
      const z = true;
      function add(a, b) { return a + b; }
      if (x > 10) { ... }
      while (y < 100) { ... }
    • functions.js - 函数声明

      1
      2
      3
      4
      5
      6
      function regular() { return 1; }
      async function asyncFunc() { return 2; }
      function outer() {
      function inner() { return 42; }
      return inner();
      }
    • asi_return.js - ASI(自动分号插入)与return

      1
      2
      3
      4
      5
      6
      7
      8
      function test1() {
      return
      42; // ASI在return后插入分号,返回undefined
      }

      function test2() {
      return 42; // 返回42
      }
    • asi_statements.js - ASI(自动分号插入)与语句

      1
      2
      3
      4
      var a = 1
      var b = 2 // ASI自动插入分号
      a = b + c
      a++
    • unicode.js - Unicode支持

      1
      2
      3
      var α = 42;           // 希腊字母
      var 变量 = "中文"; // 中文
      var ヴァリアブル = 123; // 日文
  • 负向测试(tests/negative/)

    • invalid_syntax.js - 语法错误

      1
      2
      3
      4
      5
      6
      7
      var = 123;           // 缺少标识符
      function { // 缺少函数名
      return 42;
      }
      if x > 10 { // 缺少括号
      console.log("error");
      }
    • asi_ambiguous.js - ASI(自动分号插入)歧义

      1
      2
      a = b + c
      (d + e).print() // 会被解析为: a = b + c(d + e).print()

测试覆盖说明

  • 测试矩阵

    测试类别 测试文件 测试内容 预期结果
    基本语法 basic.js 变量声明、函数、控制流 ✅ 通过
    函数 functions.js 函数声明、嵌套、async ✅ 通过
    ASI-受限 asi_return.js return后换行 ✅ 通过
    ASI-一般 asi_statements.js 语句间ASI ✅ 通过
    Unicode unicode.js 多语言标识符 ✅ 通过
    语法错误 invalid_syntax.js 各种错误语法 ✅ 正确拒绝
    ASI歧义 asi_ambiguous.js 歧义情况 ✅ 正确处理
  • 覆盖的JavaScript特性

    已实现:

    • 变量声明(var, let, const)
    • 函数声明(普通、async)
    • 表达式(二元、一元、三元、赋值)
    • 控制流(if, while, for, do-while)
    • 跳转语句(return, break, continue, throw)
    • 对象和数组字面量
    • 成员访问和函数调用
    • Unicode标识符

    未实现:

    • ES6+特性(class, arrow function, destructuring)
    • 模块系统(import/export)
    • 生成器和迭代器
    • Promise和async/await完整支持

简单的 ASI(自动分号插入)机制验证

  • 测试受限产生式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    # 创建测试文件
    cat > test_asi.js << 'EOF'
    function test() {
    return
    42
    }
    EOF

    ./js-parser -v test_asi.js

    预期行为:

    • return后的换行会触发ASI

    • 自动插入分号:return; 42;

    • 函数返回undefined而非42

    输出:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    Parsing 'test_asi.js'...
    ✓ Parsing successful!
    Total lines: 5

    === Abstract Syntax Tree ===
    Program
    StatementList (1 statements)
    FunctionDeclaration name=test
    Body:
    BlockStatement
    StatementList (2 statements)
    ReturnStatement
    (no argument)
    ExpressionStatement
    Literal (number) 42

    AST输出结构解释:

    这个输出是抽象语法树(Abstract Syntax Tree),以缩进的树形结构显示代码的语法结构:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    === Abstract Syntax Tree ===
    Program ← 根节点:整个程序
    StatementList (1 statements) ← 程序包含1个语句
    FunctionDeclaration name=test ← 语句是:函数声明,名字是test
    Body: ← 函数体
    BlockStatement ← 代码块 { ... }
    StatementList (2 statements) ← 代码块里有2个语句 关键!
    ReturnStatement ← 第1个语句:return
    (no argument) ← 关键!return没有参数!返回undefined
    ExpressionStatement ← 第2个语句:表达式语句
    Literal (number) 42 ← 字面量42(永远不会执行)
  • 测试EOF处的ASI

    1
    2
    3
    4
    5
    cat > test_eof.js << 'EOF'
    var x = 42
    EOF

    ./js-parser -v test_eof.js

    预期行为:

    • 文件末尾自动插入分号

    • 成功解析

    输出:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Parsing 'test_eof.js'...
    ✓ Parsing successful!
    Total lines: 2

    === Abstract Syntax Tree ===
    Program
    StatementList (1 statements)
    StatementList (1 statements)
    VariableDeclaration(var) name=x
    Initializer:
    Literal (number) 42

    AST输出结构解释:

    这个AST展示了文件末尾(EOF)的ASI处理:

    1
    2
    3
    4
    5
    6
    7
    === Abstract Syntax Tree ===
    Program ← 根节点:整个程序
    StatementList (1 statements) ← 程序包含1个语句
    StatementList (1 statements) ← 嵌套的列表(变量声明列表)
    VariableDeclaration(var) name=x ← 变量声明:var x
    Initializer: ← 初始化器
    Literal (number) 42 ← 值是 42

    补充:为什么有两层 StatementList?

    1
    2
    3
    StatementList (程序级别)
    └─ StatementList (变量声明列表)
    └─ VariableDeclaration

    这是因为语法规则设计:

    • 外层 StatementList: 程序的所有语句
    • 内层 StatementList: VariableDeclarationList 可以包含多个声明(如 var a=1, b=2, c=3

    对比:如果有多个变量声明

    1
    var a = 1, b = 2, c = 3

    AST会是:

    1
    2
    3
    4
    5
    StatementList (程序级别)
    └─ StatementList (变量声明列表,3个)
    ├─ VariableDeclaration name=a
    ├─ VariableDeclaration name=b
    └─ VariableDeclaration name=c

ASI机制实现详解

ASI(Automatic Semicolon Insertion,自动分号插入)是JavaScript语言的一个重要特性,允许在某些情况下省略分号。本解析器严格按照ECMAScript规范实现了完整的ASI机制。

ASI的核心变量

在词法分析器中,我们使用以下全局变量来跟踪ASI状态:

1
2
3
4
5
6
7
8
/* lexer.re 第11-17行 */
/* Global variables for tracking position and ASI */
int yylineno = 1;
int yycolno = 1;
int seen_newline = 0; /* 追踪是否遇到换行 */
int last_token = 0; /* 上一个token */
int paren_depth = 0; /* 括号嵌套深度 */
int in_for_header = 0; /* 是否在for循环头部 */

变量说明:

  • seen_newline: 标记当前位置前是否遇到换行符,用于触发ASI规则1
  • last_token: 记录上一个识别的token,用于判断受限产生式
  • paren_depth: 跟踪括号嵌套深度,用于识别for循环结束
  • in_for_header: 标记是否在for循环头部,防止误插入分号

ASI规则映射到代码

规则1:违规token (Offending Token)

ECMAScript规范定义: 当遇到一个token,且该token与前一个token之间有至少一个行终止符分隔时,如果该token不能按语法规则被解析,则在该token前自动插入分号。

代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* lexer.re 第48-70行 */
/* Check if current context allows ASI */
int can_insert_semicolon(int next_token) {
/* Rule 1: Offending token after newline */
if (seen_newline) {
/* Rule 3: Restricted productions */
if (should_insert_semicolon_after(last_token)) {
return 1;
}
/* Rule 4: Before closing brace */
if (next_token == '}') {
return 1;
}
/* Exception: Never in for-loop headers */
if (in_for_header) {
return 0;
}
}
/* Rule 2: EOF */
if (next_token == 0) {
return 1;
}
return 0;
}

换行符检测:

1
2
3
4
5
6
7
8
/* lexer.re 第275-280行 */
// Line terminators - trigger ASI check
LineTerminatorSequence {
yylineno++;
yycolno = 1;
seen_newline = 1;
goto yylex_start; /* Continue to next token */
}

当词法分析器遇到换行符(\n\r\u2028\u2029)时,设置seen_newline = 1,为后续ASI判断提供依据。

规则2:输入结束 (EOF)

ECMAScript规范定义: 当到达输入流末尾且解析器无法将输入解析为完整的程序时,在末尾自动插入分号。

代码实现:

1
2
3
4
5
6
7
8
9
10
/* lexer.re 第305-312行 */
// End of input
[\x00] {
/* Check for ASI at EOF */
if (can_insert_semicolon(0) && last_token != ';' && last_token != '}') {
last_token = ';';
return ';';
}
return 0;
}

测试示例:

1
var x = 42  // EOF处自动插入分号

在文件末尾,如果最后一个token不是分号或右花括号,则自动插入分号。

规则3:受限产生式 (Restricted Productions)

ECMAScript规范定义: 某些语句(return、break、continue、throw、后缀++/–)在关键字和后续表达式之间不允许出现行终止符。如果出现换行,则在关键字后自动插入分号。

受限产生式判断函数:

1
2
3
4
5
6
7
/* lexer.re 第42-46行 */
/* Check if ASI should be applied based on restricted productions */
int should_insert_semicolon_after(int token) {
return (token == RETURN || token == BREAK ||
token == CONTINUE || token == THROW ||
token == POSTINC || token == POSTDEC);
}

在词法分析器中主动插入分号:

1
2
3
4
5
6
7
/* lexer.re 第84-89行 */
/* Check for ASI insertion point */
if (seen_newline && should_insert_semicolon_after(last_token)) {
seen_newline = 0;
last_token = ';';
return ';';
}

在语法分析器中的体现:

Return语句:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/* parser.y 第318-329行 */
ReturnStatement:
RETURN ';' {
$$ = create_return_statement(NULL);
}
| RETURN {
/* ASI: restricted production - return followed by newline */
$$ = create_return_statement(NULL);
}
| RETURN Expression ';' {
$$ = create_return_statement($2);
}
| RETURN Expression {
$$ = create_return_statement($2);
}
;

Continue语句:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* parser.y 第281-297行 */
ContinueStatement:
CONTINUE ';' {
$$ = create_continue_statement(NULL);
}
| CONTINUE {
/* ASI: restricted production */
$$ = create_continue_statement(NULL);
}
| CONTINUE IDENTIFIER ';' {
$$ = create_continue_statement(create_identifier($2));
free($2);
}
| CONTINUE IDENTIFIER {
$$ = create_continue_statement(create_identifier($2));
free($2);
}
;

Break语句:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/* parser.y 第301-313行 */
BreakStatement:
BREAK ';' {
$$ = create_break_statement(NULL);
}
| BREAK {
/* ASI: restricted production */
$$ = create_break_statement(NULL);
}
| BREAK IDENTIFIER ';' {
$$ = create_break_statement(create_identifier($2));
free($2);
}
| BREAK IDENTIFIER {
$$ = create_break_statement(create_identifier($2));
}
;

Throw语句:

1
2
3
4
5
6
7
8
9
10
/* parser.y 第333-341行 */
ThrowStatement:
THROW Expression ';' {
$$ = create_throw_statement($2);
}
| THROW Expression {
/* ASI after throw statement */
$$ = create_throw_statement($2);
}
;

测试示例:

1
2
3
4
function test() {
return
42 // ASI在return后插入分号,实际返回undefined
}

规则4:右花括号前插入分号

ECMAScript规范定义: 当遇到右花括号}时,如果该花括号前需要一个分号但没有,则在花括号前自动插入分号。

代码实现:

can_insert_semicolon函数中已包含此规则:

1
2
3
4
5
/* lexer.re 第56-59行 */
/* Rule 4: Before closing brace */
if (next_token == '}') {
return 1;
}

右花括号的特殊处理:

1
2
/* lexer.re 第189行 */
"}" { save_token(); seen_newline = 0; last_token = '}'; return '}'; }

遇到右花括号时,重置seen_newline标志,防止后续误触发ASI。

测试示例:

1
2
3
4
function test() {
var x = 1
var y = 2 // 花括号前自动插入分号
}

规则5:for循环头部特殊处理

ECMAScript规范定义: 在for循环的头部(初始化、条件、更新表达式),即使遇到换行也不应插入分号,因为分号是for语法的分隔符。

for关键字标记:

1
2
/* lexer.re 第161行 */
"for" { save_token(); in_for_header = 1; last_token = FOR; return FOR; }

当识别到for关键字时,设置in_for_header = 1

for循环右括号处理:

1
2
/* lexer.re 第191行 */
")" { save_token(); paren_depth--; if (paren_depth == 0 && in_for_header) in_for_header = 0; last_token = ')'; return ')'; }

通过paren_depth跟踪括号嵌套,当最外层右括号闭合时,重置in_for_header标志。

ASI函数中的异常处理:

1
2
3
4
5
/* lexer.re 第60-63行 */
/* Exception: Never in for-loop headers */
if (in_for_header) {
return 0;
}

在for循环头部内,即使遇到换行也不插入分号。

语法规则中的体现:

1
2
3
4
5
6
7
8
9
10
11
/* parser.y 第241-276行 - for循环的各种形式 */
IterationStatement:
/* ... */
| FOR '(' Expression ';' Expression ';' Expression ')' Statement {
$$ = create_for_statement($3, $5, $7, $9);
}
| FOR '(' VAR VariableDeclaration ';' Expression ';' Expression ')' Statement {
$$ = create_for_statement($4, $6, $8, $10);
}
/* ... */
;

for循环的三部分用显式的分号;分隔,不依赖ASI。

测试示例(正确):

1
2
3
4
5
for (var i = 0
i < 10 // 不会在此处插入分号
i++) {
console.log(i);
}

一般语句的ASI支持

除了受限产生式,普通语句也支持ASI:

变量声明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/* parser.y 第154-174行 */
VariableStatement:
VAR VariableDeclarationList ';' {
$$ = $2;
}
| VAR VariableDeclarationList {
/* ASI: semicolon automatically inserted */
$$ = $2;
}
| LET VariableDeclarationList ';' {
$$ = $2;
}
| LET VariableDeclarationList {
$$ = $2;
}
| CONST VariableDeclarationList ';' {
$$ = $2;
}
| CONST VariableDeclarationList {
$$ = $2;
}
;

表达式语句:

1
2
3
4
5
6
7
8
9
10
/* parser.y 第207-215行 */
ExpressionStatement:
Expression ';' {
$$ = create_expression_statement($1);
}
| Expression {
/* ASI: semicolon automatically inserted */
$$ = create_expression_statement($1);
}
;

Do-While语句:

1
2
3
4
5
6
7
8
/* parser.y 第234-240行 */
| DO Statement WHILE '(' Expression ')' ';' {
$$ = create_while_statement($5, $2);
}
| DO Statement WHILE '(' Expression ')' {
/* ASI after do-while */
$$ = create_while_statement($5, $2);
}

ASI实现流程总结

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
源代码输入

词法分析器扫描

遇到换行符? → 设置 seen_newline = 1

识别下一个token

检查ASI条件:
├─ 规则1: seen_newline && (违规token)
├─ 规则2: EOF
├─ 规则3: 受限产生式后换行
├─ 规则4: 右花括号前
└─ 例外: for循环头部不插入

满足条件? → 插入虚拟分号token

语法分析器接收

构建AST

关键设计要点

  1. 词法层和语法层协作
    • 词法层负责检测换行、EOF、受限产生式
    • 语法层提供带/不带分号的双重规则
  2. 状态追踪
    • seen_newline: 追踪换行位置
    • last_token: 判断受限产生式
    • in_for_header: 处理for循环特例
  3. 精确控制
    • 受限产生式立即插入分号
    • 一般语句由语法规则宽容处理
    • for循环显式禁止ASI

这种实现方式确保了与ECMAScript规范的完全一致性,同时保持了代码的清晰和可维护性。

项目特性

  • re2c词法分析器

    • 完整Unicode支持

    • 所有JavaScript token识别

    • 注释处理(单行和多行)

  • bison语法分析器

    • 遵循ECMAScript规范

    • 表达式和语句完整支持

    • 函数声明(包括async)

    • 控制流语句(if/while/for)

  • ASI机制

    • 受限产生式(return/break/continue/throw)

    • EOF处理

    • 右花括号处理

    • for循环特殊处理

  • 完整AST

    • 26种节点类型

    • 详细打印功能

    • 完整内存管理

性能和限制

  • 性能特点

    • 编译时间: <2秒(取决于机器)

    • 解析速度: 毫秒级(小型文件)

    • 内存使用: 取决于AST大小

  • 当前限制

    1. 不支持的ES6+特性

      • 类声明(class)
      • 箭头函数(=>)
      • 模板字面量(完整语法)
      • 解构赋值
      • import/export
    2. ASI的固有限制
      某些情况下ASI可能导致非预期行为(这是JavaScript语言本身的特性)

    3. 正则表达式
      仅基本支持,不解析正则表达式内部语法

致谢

感谢编译原理课程提供的学习机会,通过这个项目加深了对编译器构造的理解。特别感谢ECMAScript规范文档和开源社区提供的丰富资料。

reward
微信 | Wechat