当前位置: 首页 > news >正文

前端AST

前端AST

  • 1、什么是编译器
  • 2、什么是AST
  • 3、编译器的基本思路
    • 3.1 词法分析
    • 3.2 语法分析
    • 3.3 代码转换
    • 3.4 代码生成
    • 3.5 完整链路
  • 4、一个简单的编译器的实现
    • 4.1 词法分析
    • 4.2 语法分析
    • 4.3 代码转换
    • 4.4 代码生成
    • 4.5 完整流程

1、什么是编译器

定义:编译器就是个将当前语言转为其他语言的过程
从某种语法代码转为另一种编写方式的代码
本质上是字符串的操作
babel:它所做的事就是语法糖之类的转换,比如ES6/ES7/JSX转为ES5或者其他指定版本
其他常见编译器:

  • Less/Sass
  • TypeScript/coffeeScript
  • Eslint
  • etc…

2、什么是AST

抽象语法树,ast,全称是 Abstract Syntax Tree,是源代码的抽象语法结构的树状表现形式
dsl:领域特定语言,如SQL、CSS、HTML、JSX
低代码平台,也有对应的dsl

3、编译器的基本思路

编译器的工作过程一般分为三个阶段:
1、解析:将源代码转换为抽象语法树(AST)@babel/core parse(词法分析tokenizer、语法分析parser)
2、变换:对AST进行变换操作。@babel/traverse traverse
3、生成:根据变换后的AST生成目标代码。@babel/generator generate

整个可以这样理解:源代码我们把它看做字符串,目标代码亦是字符串,编译器的工作就是将源代码字符串转换为目标代码字符串
结合自然语言的理解:源码字符串->词法分析->语法分析->语义分析->目标代码字符串

3.1 词法分析

分词器:tokenizer(code)

词法分析将文本分割成一个个的“token”,例如:init、main、init、x、;、x、=、3、;、}等等。同时它可以去掉一些注释、空格、回车等等无效字符;

词法分析生成token的办法有2种:

  • 正则表达式
  • 自动机

正则表达式只适合一些简单的模板语法,真正复杂的语言并不合适,需要写大量的正则表达式,正则之间还有冲突需要处理,不容易维护,性能不高。

自动机可以很好的生成token;有穷状态自动机(finite state machine):在有限个输入的情况下,在这些状态中转移并期望最终达到终止状态。

有穷状态自动机根据确定性可以分为:
“确定有穷状态自动机”(DFA - Deterministic finite automaton)
在输入一个状态时,只得到一个固定的状态。DFA 可以认为是一种特殊的 NFA;
“非确定有穷自动机”(NFA - Non-deterministic finite automaton)
当输入一个字符或者条件得到一个状态机的集合。JavaScript 正则采用的是 NFA 引擎

3.2 语法分析

词法分析器:parser(tokens)
编译原理就是将一种语言转换为另一种语言, 语法分析的目的就是通过词法分析器拿到的token流 + 结合文法规则,通过一定算法得到一颗抽象语法树(AST)。
典型应用如babel插件,它的原理就是:es6代码 → Babylon.parse → AST → babel-traverse → 新的AST → es5代码。
从生成AST效率和实现难度上,前人总结主要有2种解析算法:自顶向下的分析方法和自底向上的分析方法。自顶向下算法实现相对简单,并且能够解析文法的范围也不错,所以一般的compiler都是采用深度优先索引的方式。

3.3 代码转换

转化器:transformer(ast)
在得到AST后,我们一般会先将AST转为另一种AST,目的是生成更符合预期的AST,这一步称为代码转换。

3.4 代码生成

生成器:generator(newAst)
在实际的代码处理过程中,可能会递归的分析()我们最终生成的AST,然后对于每种type都有个对应的函数处理,当然,这可能是最简单的做法。总之,我们的目标代码会在这一步输出,对于我们的目标语言,它就是HTML了。

3.5 完整链路

  1. input => tokenizer => tokens; // 词法分析
  2. tokens => parser => ast; // 语法分析,生成AST
  3. ast => transformer => newAst; // 中间层代码转换
  4. newAst => generator => output; // 生成目标代码

4、一个简单的编译器的实现

如果我们有两个函数 addsubtract 他们会写成这样:

类似 C

  • 2 + 2 : (加 2 2) 加 (2, 2)
  • 4 - 2 : (减 4 2) 减 (4, 2)
  • 2 + (4 - 2) : (加 2 (减 4 2)) 加 (2, 减 (4, 2))

对于以下语法:(加 2 (减 4 2))
词法分析后,生成tokens

tokens:[{ type: 'paren',  value: '('        },{ type: 'name',   value: 'add'      },{ type: 'number', value: '2'        },{ type: 'paren',  value: '('        },{ type: 'name',   value: 'subtract' },{ type: 'number', value: '4'        },{ type: 'number', value: '2'        },{ type: 'paren',  value: ')'        },{ type: 'paren',  value: ')'        },]

语法分析后,生成ast

ast:{type: 'Program',body: [{type: 'CallExpression',name: 'add',params: [{type: 'NumberLiteral',value: '2',}, {type: 'CallExpression',name: 'subtract',params: [{type: 'NumberLiteral',value: '4',}, {type: 'NumberLiteral',value: '2',}]}]}]}   

4.1 词法分析

function tokenizer(input) {let current = 0;let tokens = [];while (current < input.length) {let char = input[current];if (char === '(') {tokens.push({type: 'paren',value: '(',});current++;continue;}if (char === ')') {tokens.push({type: 'paren',value: ')',});current++;continue;}let WHITESPACE = /\s/;if (WHITESPACE.test(char)) {current++;continue;}let NUMBERS = /[0-9]/;if (NUMBERS.test(char)) {let value = '';while (NUMBERS.test(char)) {value += char;char = input[++current];}tokens.push({ type: 'number', value });continue;}if (char === '"') {let value = '';char = input[++current];while (char !== '"') {value += char;char = input[++current];}char = input[++current];tokens.push({ type: 'string', value });continue;}let LETTERS = /[a-z]/i;if (LETTERS.test(char)) {let value = '';while (LETTERS.test(char)) {value += char;char = input[++current];}tokens.push({ type: 'name', value });continue;}throw new TypeError('I dont know what this character is: ' + char);}return tokens;
}

4.2 语法分析

function parser(tokens) {let current = 0;function walk() {let token = tokens[current];if (token.type === 'number') {current++;return {type: 'NumberLiteral',value: token.value,};}if (token.type === 'string') {current++;return {type: 'StringLiteral',value: token.value,};}if (token.type === 'paren' && token.value === '(') {token = tokens[++current];let node = {type: 'CallExpression',name: token.value,params: [],};token = tokens[++current];while (token.type !== 'paren' || (token.type === 'paren' && token.value !== ')')) {node.params.push(walk());token = tokens[current];}current++;return node;}throw new TypeError(token.type);}let ast = {type: 'Program',body: [],};while (current < tokens.length) {ast.body.push(walk());}return ast;
}

4.3 代码转换

/*** ============================================================================*                                 ⌒(❀>◞౪◟<❀)⌒*                              代码转换方法!!!* ============================================================================*/function traverser(ast, visitor) {function traverseArray(array, parent) {array.forEach(child => {traverseNode(child, parent);});}function traverseNode(node, parent) {let methods = visitor[node.type];if (methods && methods.enter) {methods.enter(node, parent);}switch (node.type) {case 'Program':traverseArray(node.body, node);break;case 'CallExpression':traverseArray(node.params, node);break;case 'NumberLiteral':case 'StringLiteral':break;default:throw new TypeError(node.type);}if (methods && methods.exit) {methods.exit(node, parent);}}traverseNode(ast, null);
}/*** ============================================================================*                                   ⁽(◍˃̵͈̑ᴗ˂̵͈̑)⁽*                              代码转换!!!* ============================================================================*//**** ----------------------------------------------------------------------------*   Original AST                     |   Transformed AST* ----------------------------------------------------------------------------*   {                                |   {*     type: 'Program',               |     type: 'Program',*     body: [{                       |     body: [{*       type: 'CallExpression',      |       type: 'ExpressionStatement',*       name: 'add',                 |       expression: {*       params: [{                   |         type: 'CallExpression',*         type: 'NumberLiteral',     |         callee: {*         value: '2'                 |           type: 'Identifier',*       }, {                         |           name: 'add'*         type: 'CallExpression',    |         },*         name: 'subtract',          |         arguments: [{*         params: [{                 |           type: 'NumberLiteral',*           type: 'NumberLiteral',   |           value: '2'*           value: '4'               |         }, {*         }, {                       |           type: 'CallExpression',*           type: 'NumberLiteral',   |           callee: {*           value: '2'               |             type: 'Identifier',*         }]                         |             name: 'subtract'*       }]                           |           },*     }]                             |           arguments: [{*   }                                |             type: 'NumberLiteral',*                                    |             value: '4'* ---------------------------------- |           }, {*                                    |             type: 'NumberLiteral',*                                    |             value: '2'*                                    |           }]*  (sorry the other one is longer.)  |         }*                                    |       }*                                    |     }]*                                    |   }* ----------------------------------------------------------------------------*/function transformer(ast) {let newAst = {type: 'Program',body: [],};ast._context = newAst.body;traverser(ast, {NumberLiteral: {enter(node, parent) {parent._context.push({type: 'NumberLiteral',value: node.value,});},},StringLiteral: {enter(node, parent) {parent._context.push({type: 'StringLiteral',value: node.value,});},},CallExpression: {enter(node, parent) {let expression = {type: 'CallExpression',callee: {type: 'Identifier',name: node.name,},arguments: [],};node._context = expression.arguments;if (parent.type !== 'CallExpression') {expression = {type: 'ExpressionStatement',expression: expression,};}parent._context.push(expression);},},});return newAst;
}

4.4 代码生成

function codeGenerator(node) {switch (node.type) {case 'Program':return node.body.map(codeGenerator).join('\n');case 'ExpressionStatement':return (codeGenerator(node.expression) + ';' // << (...because we like to code the *correct* way));case 'CallExpression':return codeGenerator(node.callee) + '(' + node.arguments.map(codeGenerator).join(', ') + ')';case 'Identifier':return node.name;case 'NumberLiteral':return node.value;case 'StringLiteral':return '"' + node.value + '"';default:throw new TypeError(node.type);}
}

4.5 完整流程

/****   1. input  => tokenizer   => tokens*   2. tokens => parser      => ast*   3. ast    => transformer => newAst*   4. newAst => generator   => output*/function compiler(input) {let tokens = tokenizer(input);let ast = parser(tokens);let newAst = transformer(ast);let output = codeGenerator(newAst);return output;
}

相关文章:

前端AST

前端AST 1、什么是编译器2、什么是AST3、编译器的基本思路3.1 词法分析3.2 语法分析3.3 代码转换3.4 代码生成3.5 完整链路 4、一个简单的编译器的实现4.1 词法分析4.2 语法分析4.3 代码转换4.4 代码生成4.5 完整流程 1、什么是编译器 定义&#xff1a;编译器就是个将当前语言…...

基于EPS32C3电脑远程开机模块设计

基于EPS32C3电脑远程开机模块设计 前言 缘起&#xff0c;手头资料太多了&#xff0c;所以想组一台NAS放在家里存储数据。在咸鱼淘了一套J3160主板加机箱&#xff0c;加上几块硬盘组建NAS。 对于NAS&#xff0c;我的需求是不用的时候关机(节省功耗)&#xff0c;要用的时候开机…...

深度解析 Netty 性能卓越的背后原因

一、引言 在当今数字化时代&#xff0c;构建高性能、高可靠的网络应用成为了技术领域的关键需求。Netty 作为一款备受推崇的网络应用框架&#xff0c;以其出色的性能在众多框架中脱颖而出。深入探究 Netty 性能卓越的原因&#xff0c;不仅能够帮助开发者更好地理解和运用这一框…...

虚幻引擎(Unreal Engine)技术使得《黑神话悟空传》大火,现在重视C++的开始吃香了,JAVA,Go,Unity都不能和C++相媲美!

虚幻引擎&#xff08;Unreal Engine&#xff09;火了黑神话游戏。 往后&#xff0c;会有大批量的公司开始模仿这个赛道&#xff01; C 的虚拟引擎技术通常指的是使用 C 语言开发的游戏引擎&#xff0c;如虚幻引擎&#xff08;Unreal Engine&#xff09;等。以下是对 C 虚拟引…...

华为-2022-测试面试题

文章目录 一、源数组a&#xff0c;将a中所有元素乘以2之后组成一个新数组&#xff0c;则这个新数组就叫双倍数组&#xff0c;给你一个数组a&#xff0c;判断它是不是双倍数组&#xff0c;如果是则输出源数组&#xff0c;不是则输出空数组。二、如果想把一个文件移动到另一个文件…...

Linux-(系统启动、用户管理)

目录 前言 关机&重启命令 基本介绍 注意细节 用户登录和注销 注意&#xff1a; 用户管理 基本介绍 添加用户 指定/修改密码 删除用户 查询用户信息 切换用户 查看当前用户登录用户 用户组 新增组 删除组 查看所有组 修改用户所属组 创建用户时指定用户…...

机器学习:opencv--图像形态学

目录 前言 一、常用形态学操作 二、腐蚀和膨胀 1.图像腐蚀 2.图形膨胀 三、开运算和闭运算 1.开运算 2.闭运算 四、顶帽和黑帽 1.顶帽 2.黑帽 五、梯度运算 总结 前言 图像形态学是一种用于处理和分析图像形状和结构的技术。 一、常用形态学操作 膨胀&#xff08…...

网络基础入门指南(一)

前言 在这个高度互联的世界里&#xff0c;互联网已成为日常生活不可或缺的一部分。然而&#xff0c;对于许多人来说&#xff0c;网络是如何工作的仍然是个谜。本文旨在为那些对网络基础知识感兴趣的朋友提供一个简单的介绍&#xff0c;帮助大家更好地理解互联网的基本原理和技…...

【项目】云备份

云备份 云备份概述框架 功能演示服务端客户端 公共模块文件操作模块目录操作模块 服务端模块功能划分功能细分模块数据管理热点管理 客户端模块功能划分功能细分模块数据管理目录检查文件备份 云备份 概述 自动将本地计算机上指定文件夹中需要备份的文件上传备份到服务器中。…...

WebGL系列教程二(环境搭建及初始化Shader)

目录 1 前言2 新建html页面3 着色器介绍3.1 顶点着色器、片元着色器与光栅化的概念3.2 声明顶点着色器3.3 声明片元着色器 4 坐标系(右手系)介绍5 着色器初始化5.1 给一个画布canvas5.2 获取WebGL对象5.3 创建着色器对象5.4 获取着色器对象的源5.5 绑定着色器的源5.6 编译着色器…...

keepalive和nginx高可用集群

keepalived 和 nginx 高可用集群搭建 主备模式 zyj86主机和zyj87主机安装nginx和keepalived yum install nginx keepalived -y systemctl enable --now nginx.service keepalived.service主调度器配置 编辑zyj86主机&#xff08;主&#xff09;配置文件 vi /etc/keepalived…...

二分查找题总结

二分查找题总结 hot100搜索插入位置搜索二维矩阵在排序数组中查找元素的第一个和最后一个位置搜索旋转排序数组寻找旋转排序数组中的最小值寻找两个正序数组的中位数 hot100 搜索插入位置 题目链接&#xff1a; 35.搜索插入位置 代码&#xff1a; class Solution {public in…...

仕考网:公务员面试流程介绍

通知进面信息——资格审查——面试签到——抽签候考 面试形式&#xff1a; 面试分为结构化和无领导小组两种形式 1.在结构化面试中&#xff0c;当轮到某位考生时&#xff0c;引导员将在候考室宣布其编号&#xff0c;随后考生跟随引导人员前往考场入口。考生在开始考试时需回…...

(十五)SpringCloudAlibaba-Sentinel持久化到Nacos

前言 在前面我们已经将Sentinel配置的规则持久化到系统的文件中。本章节我们将Sentinel持久化到Nacos中; 传送门(Sentinel数据持久化到文件)https://blog.csdn.net/weixin_45876411/article/details/140742963 默认情况下 Sentinel 只能接收到 Nacos 推送的消息&#xff0c;但…...

GitHub图床

GitHub图床 文章目录 GitHub图床图床介绍Github访问GitHub手动修改hostsgithub520 加速器创建账户创建仓库创建token PicGoTypora 图床介绍 图床 存放图片的地方 为什么设置图床呢 在我认识图床之前, 有一个问题 [^放在typora上面的图片, 其实是一个链接, 并且将图片存放在本地…...

记一次高版本view-design的组件迁移到自身项目的低版本

背景 npm i -S view-design当前老项目使用view-design这个组件库&#xff0c;但是当我们去官网查看该组件库最新版本&#xff0c;竟然发现没有博主想用的image/ImagePreivew这两个基础组件 说实话&#xff0c;有点离谱了哈&#xff01;&#xff01; 自己造轮子&#xff1f; …...

QT运行ROS工程

文章目录 使用QT创建ROS工程项目配置修改cmake环境配置运行设置 运行 使用QT创建ROS工程 工程名字和路径 下一步(直接选择默认选项就可以&#xff09;->完成 完成之后 是这样的 接下来在工作空间里面创建功能包 鼠标选中src点击右键->添加新文件 name::功能包的名字…...

电脑技巧:如何在Win11电脑上调整设置,让屏幕更加护眼?

目录 一、调整屏幕亮度 二、启用夜间模式 三、调整色彩设置 四、使用第三方护眼软件 五、保持良好的用眼习惯 总结 随着长时间使用电脑的人越来越多,护眼问题也变得越来越重要。Win11作为更新的操作系统,提供了更多的设置选项来帮助我们保护眼睛。本文将详细介绍如何在…...

【数据结构】排序算法篇二

【数据结构】排序算法篇二 1. 快速排序&#xff08;hoare版本&#xff09;&#xff08;1&#xff09;基本思想&#xff1a;&#xff08;2&#xff09;动态图解&#xff1a;&#xff08;3&#xff09;代码实现&#xff1a;&#xff08;4&#xff09;特性总结&#xff1a; 2. 快速…...

python进阶篇-day09-数据结构与算法(非线性结构与排序算法)

非线性结构(树状结构) 特点: 每个节点都可以有n个子节点(后继节点) 和 n个父节点(前驱节点) 代表: 树, 图...... 概述 属于数据结构之 非线性结构的一种, 父节点可以有多个子节点(后续节点) 特点 有且只有1个根节点 每个节点都可以有1个父节点及任意个子节点, 前提: 根节点除…...

RestClient

什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端&#xff0c;它允许HTTP与Elasticsearch 集群通信&#xff0c;而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级&#xff…...

C++_核心编程_多态案例二-制作饮品

#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为&#xff1a;煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例&#xff0c;提供抽象制作饮品基类&#xff0c;提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...

stm32G473的flash模式是单bank还是双bank?

今天突然有人stm32G473的flash模式是单bank还是双bank&#xff1f;由于时间太久&#xff0c;我真忘记了。搜搜发现&#xff0c;还真有人和我一样。见下面的链接&#xff1a;https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...

如何在看板中体现优先级变化

在看板中有效体现优先级变化的关键措施包括&#xff1a;采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中&#xff0c;设置任务排序规则尤其重要&#xff0c;因为它让看板视觉上直观地体…...

Opencv中的addweighted函数

一.addweighted函数作用 addweighted&#xff08;&#xff09;是OpenCV库中用于图像处理的函数&#xff0c;主要功能是将两个输入图像&#xff08;尺寸和类型相同&#xff09;按照指定的权重进行加权叠加&#xff08;图像融合&#xff09;&#xff0c;并添加一个标量值&#x…...

ardupilot 开发环境eclipse 中import 缺少C++

目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...

算法岗面试经验分享-大模型篇

文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer &#xff08;1&#xff09;资源 论文&a…...

基于Java+MySQL实现(GUI)客户管理系统

客户资料管理系统的设计与实现 第一章 需求分析 1.1 需求总体介绍 本项目为了方便维护客户信息为了方便维护客户信息&#xff0c;对客户进行统一管理&#xff0c;可以把所有客户信息录入系统&#xff0c;进行维护和统计功能。可通过文件的方式保存相关录入数据&#xff0c;对…...

基于IDIG-GAN的小样本电机轴承故障诊断

目录 🔍 核心问题 一、IDIG-GAN模型原理 1. 整体架构 2. 核心创新点 (1) ​梯度归一化(Gradient Normalization)​​ (2) ​判别器梯度间隙正则化(Discriminator Gradient Gap Regularization)​​ (3) ​自注意力机制(Self-Attention)​​ 3. 完整损失函数 二…...

华为OD机考-机房布局

import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...