第二章 - 抽象语法树(AST)概述

第二章 - 抽象语法树(AST)概述

关于抽象语法树及其使用方法的介绍

如果想编写能生成代码或分析代码的程序,首先需要理解什么是抽象语法树。AST这个术语听起来可能很吓人,但其实它和HTML的DOM树一样简单。下面我们就用DOM树作为例子,说明树形结构的基本概念。

HTML DOM示例

假设我们有一个基础网页,包含标题和两个带子标题的内容区块。其HTML代码可能如下:

css 复制代码
<body>
  <main>
    <h1>重要标题</h1>
    <div>
      <h2>子标题1</h2>
      <p>附加信息</p>
    </div>
    <div>
      <h2>子标题2</h2>
      <p>其他信息</p>
    </div>
  </main>
</body>

对应的树形结构表示如下:

css 复制代码
body
|-> main
    |-> h1
    |-> div
        |-> h2
        |-> p
    |-> div
        |-> h2
        |-> p

这表示body节点有一个子节点mainmain包含三个子节点:h1和两个div。每个div又包含h2p子节点。这种树结构是源代码的元数据表示,即使省略具体文本内容,也能完整还原代码结构。

HTML的AST有一些规则限制,比如"p节点不能是另一个p节点的子节点"。这类规则定义了节点间的父子关系限制。在其他编程语言中,这类规则会更加重要。

TypeScript/JavaScript等语言也有类似的树形结构,通过AST可以重新生成原始代码。

JavaScript示例

来看一个TypeScript相关的例子,分析下面这个简单的if语句:

erlang 复制代码
if (6 === 7) {
  console.log("wow")
}

对应的AST结构如下:

rust 复制代码
IfStatement
|-> BinaryExpression
    |-> NumericLiteral
    |-> EqualsEqualsEqualsToken
    |-> NumericLiteral
|-> Block
    |-> ExpressionStatement
        |-> CallExpression
            |-> PropertyAccessExpression
                |-> Identifier
                |-> Identifier
            |-> StringLiteral

这个结构看起来复杂,让我们逐步解析:

最顶层的IfStatement包含两个子节点:

  • BinaryExpression表示条件判断部分
  • Block表示花括号{}内的代码块

BinaryExpression包含三个部分:

  • 左右两边的数字字面量67NumericLiteral
  • 中间的严格相等运算符===EqualsEqualsEqualsToken

Block节点包含一个ExpressionStatement,即console.log("wow")。这个表达式语句又包含:

  • CallExpression表示函数调用

    • PropertyAccessExpression表示属性访问console.log

      • 两个Identifier分别是consolelog
    • StringLiteral是字符串参数"wow"

通过这个例子,我们完整解析了if语句的AST结构。接下来介绍TypeScript中常见的AST节点类型。

TypeScript AST节点类型

下面介绍TypeScript代码中最常用的节点类型。这不是完整列表,但掌握这些核心类型就能理解大部分AST结构。

注意:本节内容偏技术性,后续章节不会这么枯燥。但这些基础知识很重要。

字面量(Literals)

字面量是最直观的节点类型,表示源代码中的具体值。例如:

javascript 复制代码
const age = 102  // NumericLiteral
const name = "Jason"  // StringLiteral
const regex = /hello/  // RegularExpressionLiteral

其他字面量示例:

csharp 复制代码
const a = 1n  // BigIntLiteral
const b = true  // BooleanLiteral
const c = null  // NullLiteral
const d = {}  // ObjectLiteralExpression
const e = []  // ArrayLiteralExpression

表达式(Expressions)

表达式是能产生值或引起副作用的代码片段。常见类型包括:

scss 复制代码
hello()  // CallExpression
process.env  // PropertyAccessExpression
1 + 2  // BinaryExpression
() => {}  // ArrowFunctionExpression
await hello()  // AwaitExpression
this  // ThisExpression

语句(Statements)

语句表示执行动作的代码单元。与表达式的区别在于:大多数关键字都属于语句,如iffor循环等。所有表达式都可以作为语句,但反之不成立。

示例:

scss 复制代码
const a = 1  // VariableStatement
if(5>9){}  // IfStatement
for(let i in [1,2,3]){}  // ForInStatement
return  // ReturnStatement

声明(Declarations)

声明用于引入新类型或值:

javascript 复制代码
import a from "./a"  // ImportDeclaration
class Human {}  // ClassDeclaration
function d() {}  // FunctionDeclaration
export {}  // ExportDeclaration

注意:const a = 1整体是VariableStatement,但其中的a=1部分是VariableDeclaration

标识符(Identifiers)

标识符是其他节点的名称:

csharp 复制代码
function hello() {}  // hello是标识符
class Animal {}  // Animal是标识符
const myVar = 7  // myVar是标识符

令牌(Tokens)

令牌表示特殊符号:

arduino 复制代码
=  // EqualsToken
===  // EqualsEqualsEqualsToken
...  // DotDotDotToken
++  // PlusPlusToken

子句(Clauses)

子句用于提升代码可读性:

scala 复制代码
try {} catch {}  // catch是子句
switch中的case/default  // 也是子句
class A extends B {}  // extends B是HeritageClause

关键字(Keywords)

关键字是语言保留的特殊单词:

vbnet 复制代码
async
await
const
class
interface

类型引用(References)

TypeScript特有,用于类型标注:

java 复制代码
type Age = {}
function myFunc(age: Age) {}  // :Age是TypeReference

总结

本章概述了TypeScript抽象语法树的核心概念。我们学习了AST的各种节点类型,为后续的代码分析和转换打下了基础。

相关推荐
索西引擎22 分钟前
浅谈 Vue 的双向数据绑定
前端·vue.js
iku_ki31 分钟前
axios二次封装-单个、特定的实例的拦截器、所有实例的拦截器。
运维·服务器·前端
断竿散人42 分钟前
前端救急实战:用 patch-package 解决 vue-pdf 电子签章不显示问题
前端·webpack·npm
蓝倾42 分钟前
淘宝获取商品分类接口操作指南
前端·后端·fastapi
十盒半价44 分钟前
深入理解 React 中的 useState:从基础到进阶
前端·react.js·trae
ccc10181 小时前
前端性能优化实践:深入理解懒加载的实现与最佳方案
前端
CodeTransfer1 小时前
今天给大家搬运的是四角线框hover效果
前端·vue.js
归于尽1 小时前
别让类名打架!CSS 模块化教你给样式上 "保险"
前端·css·react.js
凤凰AI1 小时前
Python知识点4-嵌套循环&break和continue使用&死循环
开发语言·前端·python
Lazy_zheng1 小时前
虚拟 DOM 到底是啥?为什么 React 要用它?
前端·javascript·react.js