当前位置: 首页 > 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个父节点及任意个子节点, 前提: 根节点除…...

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

设计模式和设计原则回顾

设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...

java_网络服务相关_gateway_nacos_feign区别联系

1. spring-cloud-starter-gateway 作用&#xff1a;作为微服务架构的网关&#xff0c;统一入口&#xff0c;处理所有外部请求。 核心能力&#xff1a; 路由转发&#xff08;基于路径、服务名等&#xff09;过滤器&#xff08;鉴权、限流、日志、Header 处理&#xff09;支持负…...

React第五十七节 Router中RouterProvider使用详解及注意事项

前言 在 React Router v6.4 中&#xff0c;RouterProvider 是一个核心组件&#xff0c;用于提供基于数据路由&#xff08;data routers&#xff09;的新型路由方案。 它替代了传统的 <BrowserRouter>&#xff0c;支持更强大的数据加载和操作功能&#xff08;如 loader 和…...

【Linux】C语言执行shell指令

在C语言中执行Shell指令 在C语言中&#xff0c;有几种方法可以执行Shell指令&#xff1a; 1. 使用system()函数 这是最简单的方法&#xff0c;包含在stdlib.h头文件中&#xff1a; #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)

概述 在 Swift 开发语言中&#xff0c;各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过&#xff0c;在涉及到多个子类派生于基类进行多态模拟的场景下&#xff0c;…...

【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表

1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...

css的定位(position)详解:相对定位 绝对定位 固定定位

在 CSS 中&#xff0c;元素的定位通过 position 属性控制&#xff0c;共有 5 种定位模式&#xff1a;static&#xff08;静态定位&#xff09;、relative&#xff08;相对定位&#xff09;、absolute&#xff08;绝对定位&#xff09;、fixed&#xff08;固定定位&#xff09;和…...

如何在网页里填写 PDF 表格?

有时候&#xff0c;你可能希望用户能在你的网站上填写 PDF 表单。然而&#xff0c;这件事并不简单&#xff0c;因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件&#xff0c;但原生并不支持编辑或填写它们。更糟的是&#xff0c;如果你想收集表单数据&#xff…...

网站指纹识别

网站指纹识别 网站的最基本组成&#xff1a;服务器&#xff08;操作系统&#xff09;、中间件&#xff08;web容器&#xff09;、脚本语言、数据厍 为什么要了解这些&#xff1f;举个例子&#xff1a;发现了一个文件读取漏洞&#xff0c;我们需要读/etc/passwd&#xff0c;如…...