抽象语法树AST

news2024/9/21 5:48:08

什么是AST

在计算机科学中,抽象语法树(Abstract Syntax Tree,AST),或简称语法树(Syntax tree),是源代码语法结构的一种抽象表示。它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。之所以说语法是“抽象”的,是因为这里的语法并不会表示出真实语法中出现的每个细节。比如,嵌套括号被隐含在树的结构中,并没有以节点的形式呈现;而类似于 if-condition-then 这样的条件跳转语句,可以使用带有三个分支的节点来表示。
和抽象语法树相对的是具体语法树(通常称作分析树)。一般的,在源代码的翻译和编译过程中,语法分析器创建出分析树,然后从分析树生成AST。一旦AST被创建出来,在后续的处理过程中,比如语义分析阶段,会添加一些信息。 (摘自维基百科)

JS的编译执行流程

js执行的第一步是读取js文件中的字符流,然后通过词法分析生成token,之后再通过语法分析生成AST(Abstract Syntax Tree),最后生成机器码执行。

词法分析

词法分析是计算机科学中将字符序列转换为单词(Token)序列的过程。进行词法分析的程序或者函数叫作词法分析器(Lexical analyzer,简称Lexer),也叫扫描器(Scanner)。
其作用是将一行行的源码拆解成一个个 token。所谓 token,指的是语法上不可能再分的、最小的单个字符或字符串。以代码为例子,以下就是生成的token序列
例如将 const a = 1 转换为token的话,结果大概如下

[
  {type: 关键字, value: const}, 
  {type: 标识符, value: a},
  {type: 赋值操作符, value: =},
  {type: 常数, value: 1}
]

词法分析器里,每个关键字是一个Token,每个标识符是一个Token,每个操作符是一个Token,每个标点符号也都是一个Token。除此之外,还会过滤掉源程序中的注释和空白字符(换行符、空格、制表符等)。最终,整个代码将被分割进一个tokens列表(或者说一维数组)。

语法分析

语法分析是编译过程的一个逻辑阶段。语法分析的任务是在词法分析的基础上将单词序列组合成各类语法短语,如“程序”,“语句”,“表达式”等等.语法分析程序判断源程序在结构上是否正确.
其作用是将上一步生成的 token 数据,根据语法规则转为 AST。同时也会去验证语法,语法有错的话会抛出语法错误
面对一串代码,先通过词法分析,获得第一个token,为其建立一个ast节点,此时的ast节点的属性以及子节点都不完整。
为了补充这些缺少的部分,接下来移动到下一个单词,生成token,并且将其转换成子节点,添加进现有的ast中,然后重复这个 移动&生成 的递归的过程。
根据上面的词法分析的例子看const a = 1是怎么变成一颗ast的。
在线生成ast网站:https://astexplorer.net/

  • 读取const,生成一个VariableDeclaration节点

  • 读取a,新建VariableDeclarator节点

  • 读取=

  • 读取1,新建NumericLiteral节点

  • 将NumericLiteral赋值给VariableDeclarator的init属性

  • 将VariableDeclarator赋值给VariableDeclaration的declaration属性
    在这里插入图片描述
    补充说明:
    每一个含有type属性的对象这样的每一层结构也被叫做 节点(Node)。一个AST可以由单一的节点或是成百上千个节点构成。它们组合在一起可以描述用于静态分的程序语法。每一种类型的节点定义了一些附加属性用来进一步描述该节点类型。

  • 节点类型
    针对上图出现的节点说明
    【VariableDeclaration】
    变量声明,kind属性表示是什么类型的声明,因为ES6引入了const/let。declarations表示声明的多个描述,因为我们可以这样:let a = 1, b = 2
    【VariableDeclarator】
    变量声明的描述,id表示变量名称节点,init表示初始值的表达式
    【Identifier】
    标志符,如变量名,函数名,属性名,都归为标识符。
    【Literal】
    字面量,一个值的字面量
    除了上述出现的节点类型,ast中还有很多其他节点类型,如函数申明节点,if语句节点,switch语句节点等等;
    JavaScript 生成的所有 AST 节点类型可以访问https://github.com/babel/babel/blob/master/packages/babel-parser/ast/spec.md

  • 公共属性

类型说明
typeAST 节点的类型
start记录该节点代码字符串起始下标
end记录该节点代码字符串结束下标
loc内含 line、column 属性,分别记录开始结束的行列号
leadingComments开始的注释
innerComments中间的注释
trailingComments结尾的注释
extra额外信息

换流程图来看
在这里插入图片描述
问题:操作符等号去哪里了?
在 AST(抽象语法树)中,赋值操作的等号(=)通常不会以节点的形式显示在树中。这是因为 AST 更关注表达式的组织结构和语义,而不是每个具体的字符。
赋值操作的等号通常被表示为 AST 中的一个赋值表达式节点,该节点具有左侧和右侧两个子节点,分别表示被赋值的目标和赋值的值
在这个示例中,我们有一个变量声明语句(VariableDeclaration),它有一个变量声明器(VariableDeclarator)作为子节点。变量声明器包含一个标识符(Identifier)节点表示变量名(“a”),和一个数值字面量(NumericLiteral)节点表示赋给变量的值(1)。
所以,等号本身并没有直接显示在树形结构中,而是通过赋值表达式节点的左右子节点来表示赋值操作

AST的应用:

  1. 开发辅助:eslint、prettier、ts检查
  2. 代码变更:压缩、混淆、css modules
  3. 代码转换:jsx、vue、ts转换为js
  4. 代码混淆和反混淆
  5. wx小程序热更新
  6. 一套代码多端使用

反混淆

  1. 应对反爬虫:通过找规律解析出密钥,然后把加密过的路径进行解密
  2. 阅读他人网站:可以找到插件或者AI,对源码进行反混淆

wx小程序热更新

想在原有代码上执行一段后端接口获取的代码,但微信小程序禁用了eval,new Function,因此可以把想要运行的代码转化为AST,再转为要运行的代码。

Babel

编译原理

在这里插入图片描述

  • 我们需要知道 3 个 Babel 处理流程中的重要工具;
    • 解析
      • Babylon是一个解析器,它可以将javascript字符串,转化为更加友好的表现形式,称之为抽象语法树;
      • 在解析过程中有两个阶段:词法分析和语法分析,
        • 词法分析阶段:字符串形式的代码转换为令牌(tokens)流,令牌类似于AST中的节点;
        • 语法分析阶段:把一个令牌流转化为AST的形式,同时这个阶段会把令牌中的信息转化为AST的表述结构
        • Parser
    • 转换
      • babel-traverse 模块允许你浏览、分析和修改抽象语法树(AST Abstract Syntax Tree)
        • Babel接收解析得到的AST并通过babel-traverse对其进行深度优先遍历,在此过程中对节点进行添加、更新及移除操作。
    • 生成
      • babel-generator 模块用来将转换后的抽象语法树(AST Abstract Syntax Tree)转化为Javascript 字符串
        • 将经过转换的AST通过babel-generator再转换为js代码,过程及时深度遍历整个AST,然后构建转换后的代码字符串。

Babel 中重要的对象Visitor

babel在处理一个节点时,是以访问者的形式获取节点的信息,并进行相关的操作,这种操作是通过visitor对象实现的。

在visitor中定义了处理不同节点的函数。

visitor: {        
   Program: {            
       enter(path, state) {               
        console.log('start processing this module...');           
       },            
       exit(path, state) {                
         console.log('end processing this module!');           
       }        
      },       
   ImportDeclaration:{            
      enter(path, state) {                
        console.log('start processing ImportDeclaration...');                        
       },            
       exit(path, state) {                
       console.log('end processing ImportDeclaration!');                
       }        
    }    
 }

转换&生成

举个例子

test.map(i=>{
        return i
})

使用在线转AST,AST树形遍历转换后的结构
在这里插入图片描述
我们从 ExpressionStatement开始往树形结构里面走,看到它的内部属性有callee,type,arguments,所以我们再依次访问每一个属性及它们的子节点。

ExpressionStatement 进入 
CallExpression 进入  
MemberExpression 进入  
Identifier 进入 
Identifier 离开 
Identifier 进入 
Identifier 离开 
MemberExpression 进入  
ArrowFunctionExpression 进入
···
ArrowFunctionExpression 离开  
CallExpression 离开 
ExpressionStatement 离开  
Program 离开  

Babel 的转换步骤全都是这样的遍历过程。
解析好树结构后,我们手动对箭头函数进行转换。
在这里插入图片描述
在这里插入图片描述
对比两张图,发现主要不一样的地方就是两个函数的arguments.type

babel实现转换的代码

//babel核心库,用来实现核心的转换引擎
let babel = require('babel-core');//可以实现类型判断,生成AST节点
let types = require('babel-types');
let code = `codes.map(code=>{return code.toUpperCase()})`;//转换语句
//visitor可以对特定节点进行处理
let visitor = {    
  ArrowFunctionExpression(path) {//定义需要转换的节点,这里拦截箭头函数        
    let params = path.node.params        
    let blockStatement = path.node.body 
     //使用babel-types的functionExpression方法生成新节点        
    let func = types.functionExpression(null, params, blockStatement, false, false)             //替换节点        
    path.replaceWith(func) //    
   }
  }
   //将code转成ast
   let result = babel.transform(code, {    
     plugins: [       
      { visitor }    
      ]
     })
   console.log(result.code)

直接使用babel的核心包@babel/traverse和@babel/genertator来实现转化和生成

import * as parser from "@babel/parser";
import traverse from "@babel/traverse";
import generate from "@babel/generator";
const code = `function square(n) {
  return n * n;
}`;

const ast = parser.parse(code);

traverse(ast, {
  enter(path) {
    if (path.isIdentifier({ name: "n" })) {//标志符为nde 替换成x
      path.node.name = "x";
    }
  },
});

generate(ast, {}).code

何时使用setTimeout(fn, 0)

  1. 等待渲染结束
  2. ios不可连续调用原生API

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2041256.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Linux】系列入门摘抄笔记-7-用户和用户组管理

用户分类 在Linux操作系统项用户的类型一般分为超级管理员(root)、系统用户和普通用户三类: (1)超级管理员:即root用户,在整个Linux操作系统中权限最高,权限最高也意味着风险最高,若操作失误就可能使整个系统崩溃。 (2)系统用户:默认不登录操作系统,用于运行和维…

大模型常见面试题解析【附答案版】

1、在Transformer模型中,为什么scaled dot-product attention在计算QK内积之后要除以根号d? 简单来说,就是需要压缩softmax输入值,以免输入值过大,进入了softmax的饱和区,导致梯度值太小而难以训练。如果不…

哈希以及闭散列和开散列

哈希 一、哈希1、概念2、哈希冲突 二、哈希函数1、设计原则2、常见的哈希函数(1)直接定址法(2)除留余数法(3)平方取中法(4)折叠法(5)随机数法(6&a…

期权快到期卖不出去了怎么办 ?

期权在最后交易日如果平仓卖不出去,说明没有市场参与者愿意以你的报价买入该期权。这种情况通常出现在期权价值极低,接近于0,或者期权已经深度虚值,即行权价远离当前市场价格而且剩余时间已非常有限,使得该期权的内在价…

vue选中下拉框数据,但是值没有填充到框内

​​ 下拉框选中数据后&#xff0c;roomType的值没有自动更新 查找资料后的已解决&#xff0c;特此记录。 解决办法&#xff1a; 加入强制更新时间 <el-form-item label"房型" prop"roomType"><el-select v-model"form.roomType" pla…

RAG问答系统|QAnything:多类型文本的知识库,安全可靠、一键离线部署

转自老贾探AI 在日常生活和工作中&#xff0c;我们经常面对大量本地信息资料&#xff08;如PDF、Doc等&#xff09;&#xff0c;需要进行关联理解和处理。频繁地切换和区分文件格式&#xff0c;再加上需要跨语言理解的环境&#xff0c;使得如何找到一个准确、快速、可靠地处理文…

48天笔试训练错题——day48

目录 选择题 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 编程题 1. 左右最值最大差 2. 顺时针打印矩阵 选择题 1. cookie 是浏览器存储数据的机制&#xff0c;是用于维护 http 通信当中客户端的用户信息的。 2. 403 Forbidden 表示资源不可用&#xff0c;服务器理解客户请…

前端案例:酷我音乐项目(移动端自适应)(HTML+CSS)

一个简单的移动端案例&#xff0c;模拟不同设备下逻辑分辨率不同&#xff0c;宽高自适应 正常打开整体布局 打开 f12 &#xff08;ctrl shift M&#xff09;或者点击左上角图标,将其模拟为移动端设备 在移动设备iPhone6/7/8&#xff0c;逻辑分辨率375的整体布局 头部和底部的…

虚幻5|角色武器装备的数据库学习(不只是用来装备武器,甚至是角色切换也很可能用到)

虚幻5|在连招基础上&#xff0c;给角色添加武器并添加刀光|在攻击的时候添加武器并返回背后&#xff08;第一部分&#xff0c;下一部分讲刀光&#xff09;_unreal 如何给角色添加攻击-CSDN博客 目的&#xff1a;捡起各种不同的武器&#xff0c;捡起的武器跟装备的武器相匹配 …

C语言日常练习 Day15

目录 一、猴子吃桃问题。 二、两个乒乓球队进行比赛&#xff0c;各出3个人。甲队为A&#xff0c;B&#xff0c;C3人&#xff0c;乙队为X&#xff0c;Y,Z3人。已抽签决定比赛名单。有人向队员打听比赛的名单&#xff0c;A说他不和X比赛&#xff0c;C说他不和X&#xff0c;Z比赛…

Java爬虫中的数据清洗:去除无效信息的技巧

在互联网信息爆炸的时代&#xff0c;数据的获取变得异常容易&#xff0c;但随之而来的是数据质量的问题。对于Java爬虫开发者来说&#xff0c;如何从海量的网页数据中清洗出有价值的信息&#xff0c;是一个既基础又关键的步骤。本文将介绍Java爬虫中数据清洗的重要性&#xff0…

VBA自动发邮件如何配置SMTP实现外部发送?

VBA自动发邮件的教程&#xff1f;如何利用VBA自动化发送邮件&#xff1f; 在使用VBA进行自动化办公任务时&#xff0c;自动发送邮件是一项非常实用的功能。AokSend将详细介绍如何通过VBA自动发邮件&#xff0c;并配置SMTP实现外部发送的过程。 VBA自动发邮件&#xff1a;准备…

前端开发攻略---彻底弄懂跨域解决方案

目录 1、浏览器的同源策略 1.1 源 1.2 同源与非同源 1.3 同源请求与非同源请求 2、跨域受到的限制 3、注意点 4、CORS解决Ajax跨域问题 4.1 CORS概述 4.2 CORS解决简单请求跨域 4.3 简单请求与复杂请求 4.4 CORS解决复杂请求跨域 4.5 借助CORS库快速完成配置 5、JS…

Daiqile SQL注入绕过

上源码。 <?php header("Content-type: text/html; charsetutf-8"); require db.inc.php;function dhtmlspecialchars($string) {if (is_array($string)) {foreach ($string as $key > $val) {$string[$key] dhtmlspecialchars($val);}}else {$string str_…

【C++】掌握C++类的六个默认成员函数:实现高效内存管理与对象操作

C语法相关知识点可以通过点击以下链接进行学习一起加油&#xff01;命名空间缺省参数与函数重载C相关特性类和对象-上篇 本篇为大家分享下在C学习中较为具有挑战与难度&#xff0c;同时也是很重要知识。掌握C类的六个默认成员函数&#xff0c;使得在模拟实现STL中容器过程得心应…

企业必备:2024年顶尖10款人事管理系统

本篇文章介绍了以下几个工具&#xff1a;Moka、卓望ShineHR、华夏HR云、中智人事、i人事、北森iTalentX、红海云、Zenefits、ICE Hrm、ADempiere。 在选择合适的人事管理系统时&#xff0c;很多企业面临如何找到既可靠又能满足特定需求的平台的难题。每个系统都有其独特之处&am…

前端工程化-03.环境准备

一.前端工程化 既然要实现前端工程化&#xff0c;那就要使用一些现成的工具来帮助我们实现&#xff0c;这个工具就是vue官方提供的脚手架 首先就要下载安装vue脚手架 二.什么是vue脚手架 三.安装NodeJS 1.先安装NodeJS才能安装vue脚手架。 Node.js — 在任何地方运行 Jav…

window.onload、$(document).ready()、Vue.created() 页面加载完成后执行方法

1、JavaScript 的 window.onload 方法 window.onload 方法是在页面所有元素&#xff08;包括图片、样式、链接等&#xff09;加载完成后触发的&#xff0c;在这个事件之前&#xff0c;页面上的所有资源都必须加载完成。因此&#xff0c;如果页面中包含大量的图片或其他资源&am…

【科目结转】财务科目结转

*&---------------------------------------------------------------------* *&程序名称 &#xff1a;ZFI134 *&程序描述 : 9003差异科目结转 &#xff08;批量操作 F.02 / 查询 FB03) *&申请单位 …

qt quick实现的水波纹特效:横向波纹、纵向波纹效果

qml实现的水波纹特效 1.横向波纹效果2.另一种效果&#xff08;纵向波纹&#xff09; 一直以来使用c qt如果要实现一些高级特效比如水波纹效果都难度比较大&#xff0c;但是使用qt quick难度就会小很多。这里借鉴一些网友的思路简单实现一下水波纹效果。主要思路就是波浪的形成是…