返回顶部
首页 > 资讯 > 精选 >JS如何实现AST抽象语法树
  • 476
分享到

JS如何实现AST抽象语法树

2023-06-06 10:06:31 476人浏览 八月长安
摘要

小编给大家分享一下js如何实现AST抽象语法树,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!前端中的AST抽象语法树问题四则运算正则表达式词法分析语法分析完整代码

小编给大家分享一下js如何实现AST抽象语法树,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!

前端中的AST抽象语法树问题

四则运算

首先明确,此次的代码都是基于LL的语法分析来实现的,实现的是四则混合运算的功能,先看下定义:
TokenNumber:
· 1 2 3 4 5 6 7 8 9 0 的组合
Operator:
+ - * / 之一
WhiteSpace:
<SP>
LineTerminator:
<LF> <CR>

看下产生式:
JS如何实现AST抽象语法树

正则表达式

我们首先实现正则表达式的匹配原则:

<script>    var regexp = /([0-9\.]+)|([ \t]+)|([\r\n]+)|(\*)|(\/)|(\+)|(\-)/g    var dictionary = ["Number", "Whitespace", "LineTerminator", "*", "/", "+", "-"];    function tokenize(source) {        var result = null;        while(true) {            result = regexp.exec(source);            if(!result) break;            for(var i = 1; i <= dictionary.length; i ++) {                if(result[i])                    console.log(dictionary[i - 1]);            }            console.log(result);        }    }    tokenize("1024 + 10 * 25");</script>

此时我们看一下页面的运行打印结果:
JS如何实现AST抽象语法树
值得一提的是这里用到了exec方法,exec() 方法用于检索字符串中的正则表达式的匹配。
我们看一下它的语法:
RegExpObject.exec(string)

如果 exec() 找到了匹配的文本,则返回一个结果数组。否则,返回 null。此数组的第 0 个元素是与正则表达式相匹配的文本,第 1 个元素是与 RegExpObject 的第 1 个子表达式相匹配的文本(如果有的话),第 2 个元素是与 RegExpObject 的第 2 个子表达式相匹配的文本(如果有的话),以此类推。除了数组元素和 length 属性之外,exec() 方法还返回两个属性。index 属性声明的是匹配文本的第一个字符的位置。input 属性则存放的是被检索的字符串 string。我们可以看得出,在调用非全局的 RegExp 对象的 exec() 方法时,返回的数组与调用方法 String.match() 返回的数组是相同的。

但是,当 RegExpObject 是一个全局正则表达式时,exec() 的行为就稍微复杂一些。它会在 RegExpObject 的 lastIndex 属性指定的字符处开始检索字符串 string。当 exec() 找到了与表达式相匹配的文本时,在匹配后,它将把 RegExpObject 的 lastIndex 属性设置为匹配文本的最后一个字符的下一个位置。这就是说,您可以通过反复调用 exec() 方法来遍历字符串中的所有匹配文本。当 exec() 再也找不到匹配的文本时,它将返回 null,并把 lastIndex 属性重置为 0。

词法分析

我们在这一部分对上面的代码做优化
首先是刚才提到的:
当 RegExpObject 是一个全局正则表达式时,exec() 的行为就稍微复杂一些。它会在 RegExpObject 的 lastIndex 属性指定的字符处开始检索字符串 string。当 exec() 找到了与表达式相匹配的文本时,在匹配后,它将把 RegExpObject 的 lastIndex 属性设置为匹配文本的最后一个字符的下一个位置。
那么我们就要考虑到没有匹配上字符的情况,做一个判断处理:

<script>    var regexp = /([0-9\.]+)|([ \t]+)|([\r\n]+)|(\*)|(\/)|(\+)|(\-)/g    var dictionary = ["Number", "Whitespace", "LineTerminator", "*", "/", "+", "-"];    function* tokenize(source) {        var result = null;        var lastIndex = 0;        while(true) {            lastIndex = regexp.lastIndex;            result = regexp.exec(source);            if(!result) break;            if(regexp.lastIndex - lastIndex > result[0].length)                break;                        let token = {                type: null,                value: null            }            for(var i = 1; i <= dictionary.length; i ++) {                if(result[i])                    token.type = dictionary[i - 1];            }            token.value = result[0];            yield token        }        yield {            type: 'EOF'        }    }    for (let token of tokenize("1024 + 10 * 25")) {        console.log(token)    }</script>

如上,我们对regexp.lastIndex - lastIndexresult[0] 的长度进行比较,判断是否有字符串没有匹配上。
将整个函数改成generator函数的形式,我们看下运行的结果:
JS如何实现AST抽象语法树

语法分析

首先编写分块的产生式,我们看一下总的代码结构:

<script>    var regexp = /([0-9\.]+)|([ \t]+)|([\r\n]+)|(\*)|(\/)|(\+)|(\-)/g    var dictionary = ["Number", "Whitespace", "LineTerminator", "*", "/", "+", "-"];    function* tokenize(source) {        var result = null;        var lastIndex = 0;        while(true) {            lastIndex = regexp.lastIndex;            result = regexp.exec(source);            if(!result) break;            if(regexp.lastIndex - lastIndex > result[0].length)                break;                        let token = {                type: null,                value: null            }            for(var i = 1; i <= dictionary.length; i ++) {                if(result[i])                    token.type = dictionary[i - 1];            }            token.value = result[0];            yield token        }        yield {            type: 'EOF'        }    }    let source = [];    for(let token of tokenize("10 * 25")) {        if (token.type !== "Whitespace" && token.type !== "LineTerminator")            source.push(token);    }    function Expression(tokens) {    }    function AdditiveExpression(source){    }    function MultiplicativeExpresson(source) {        console.log(source);    }    MultiplicativeExpresson("10 * 25")</script>

我们先从MultiplicativeExpresson来进行研究,它分为四种情况:

function MultiplicativeExpresson(source) {//如果是数字则进行封装     if(source[0].type === "Number") {         let node = {             type: "MultiplicativeExpresson",             children:[source[0]]         }         source[0] = node;         return MultiplicativeExpresson(source)     }     //如果是乘号或者除号,则将三项出栈,进行重组     if(source[0].type === "MultiplicativeExpresson" && source[1] && source[1].type === "*") {         let node = {             type: "MultiplicativeExpresson",             operator: "*",             children: []         }         node.children.push(source.shift());         node.children.push(source.shift());         node.children.push(source.shift());         source.unshift(node);         return MultiplicativeExpresson(source)     }     if(source[0].type === "MultiplicativeExpresson" && source[1] && source[1].type === "/") {         let node = {             type: "MultiplicativeExpresson",             operator: "*",             children: []         }         node.children.push(source.shift());         node.children.push(source.shift());         node.children.push(source.shift());         source.unshift(node);         return MultiplicativeExpresson(source)     }     //递归结束的条件     if(source[0].type === "MultiplicativeExpresson")         return source[0];     return MultiplicativeExpresson(source); }

我们看一下当source为"10 * 25 / 2"时调用console.log(MultiplicativeExpresson(source))最后运行的结果:
JS如何实现AST抽象语法树
接下来看AdditiveExpression 本质上和MultiplicativeExpresson没有什么不同,差异点已经标注在代码当中了:

    function AdditiveExpression(source){        if(source[0].type === "MultiplicativeExpresson") {            let node = {                type: "AdditiveExpression",                children:[source[0]]            }            source[0] = node;            return AdditiveExpression(source)        }        //如果是乘号或者除号,则将三项出栈,进行重组        if(source[0].type === "AdditiveExpression" && source[1] && source[1].type === "+") {            let node = {                type: "AdditiveExpression",                operator: "+",                children: []            }            node.children.push(source.shift());            node.children.push(source.shift());            //考虑到第三个数可能时Number 需要在这里再次调用一下 MultiplicativeExpresson 做处理            MultiplicativeExpresson(source);            node.children.push(source.shift());            source.unshift(node);            return AdditiveExpression(source)        }        if(source[0].type === "AdditiveExpression" && source[1] && source[1].type === "-") {            let node = {                type: "AdditiveExpression",                operator: "-",                children: []            }            node.children.push(source.shift());            node.children.push(source.shift());            MultiplicativeExpresson(source);            node.children.push(source.shift());            source.unshift(node);            return AdditiveExpression(source)        }        //递归结束的条件        if(source[0].type === "AdditiveExpression")            return source[0];        //第一次进循环 调用        MultiplicativeExpresson(source);        return AdditiveExpression(source);    }

我们看一下当source为"10 * 25 / 2"时调用console.log(AdditiveExpression(source))最后运行的结果:
JS如何实现AST抽象语法树
那么Expression的代码逻辑就很好表达了:

function Expression(tokens) {     if(source[0].type === "AdditiveExpression" && source[1] && source[1].type === "EOF") {         let node = {             type: "Expression",             children: [source.shift(), source.shift()]         }         source.unshift(node);         return node;     }     AdditiveExpression(source);     return Expression(source); }

看下运行后的结果:
JS如何实现AST抽象语法树
以上就是所有的js解析抽象语法树的代码。

完整代码

<script>    var regexp = /([0-9\.]+)|([ \t]+)|([\r\n]+)|(\*)|(\/)|(\+)|(\-)/g    var dictionary = ["Number", "Whitespace", "LineTerminator", "*", "/", "+", "-"];    function* tokenize(source) {        var result = null;        var lastIndex = 0;        while(true) {            lastIndex = regexp.lastIndex;            result = regexp.exec(source);            if(!result) break;            if(regexp.lastIndex - lastIndex > result[0].length)                break;                        let token = {                type: null,                value: null            }            for(var i = 1; i <= dictionary.length; i ++) {                if(result[i])                    token.type = dictionary[i - 1];            }            token.value = result[0];            yield token        }        yield {            type: 'EOF'        }    }    let source = [];    for(let token of tokenize("10 * 25 / 2")) {        if (token.type !== "Whitespace" && token.type !== "LineTerminator")            source.push(token);    }    function Expression(tokens) {        if(source[0].type === "AdditiveExpression" && source[1] && source[1].type === "EOF") {            let node = {                type: "Expression",                children: [source.shift(), source.shift()]            }            source.unshift(node);            return node;        }        AdditiveExpression(source);        return Expression(source);    }    function AdditiveExpression(source){        if(source[0].type === "MultiplicativeExpresson") {            let node = {                type: "AdditiveExpression",                children:[source[0]]            }            source[0] = node;            return AdditiveExpression(source)        }        //如果是乘号或者除号,则将三项出栈,进行重组        if(source[0].type === "AdditiveExpression" && source[1] && source[1].type === "+") {            let node = {                type: "AdditiveExpression",                operator: "+",                children: []            }            node.children.push(source.shift());            node.children.push(source.shift());            //考虑到第三个数可能时Number 需要在这里再次调用一下 MultiplicativeExpresson 做处理            MultiplicativeExpresson(source);            node.children.push(source.shift());            source.unshift(node);            return AdditiveExpression(source)        }        if(source[0].type === "AdditiveExpression" && source[1] && source[1].type === "-") {            let node = {                type: "AdditiveExpression",                operator: "-",                children: []            }            node.children.push(source.shift());            node.children.push(source.shift());            MultiplicativeExpresson(source);            node.children.push(source.shift());            source.unshift(node);            return AdditiveExpression(source)        }        //递归结束的条件        if(source[0].type === "AdditiveExpression")            return source[0];        //第一次进循环 调用        MultiplicativeExpresson(source);        return AdditiveExpression(source);    }    function MultiplicativeExpresson(source) {        if(source[0].type === "Number") {            let node = {                type: "MultiplicativeExpresson",                children:[source[0]]            }            source[0] = node;            return MultiplicativeExpresson(source)        }        //如果是乘号或者除号,则将三项出栈,进行重组        if(source[0].type === "MultiplicativeExpresson" && source[1] && source[1].type === "*") {            let node = {                type: "MultiplicativeExpresson",                operator: "*",                children: []            }            node.children.push(source.shift());            node.children.push(source.shift());            node.children.push(source.shift());            source.unshift(node);            return MultiplicativeExpresson(source)        }        if(source[0].type === "MultiplicativeExpresson" && source[1] && source[1].type === "/") {            let node = {                type: "MultiplicativeExpresson",                operator: "*",                children: []            }            node.children.push(source.shift());            node.children.push(source.shift());            node.children.push(source.shift());            source.unshift(node);            return MultiplicativeExpresson(source)        }        //递归结束的条件        if(source[0].type === "MultiplicativeExpresson")            return source[0];        return MultiplicativeExpresson(source);    }    console.log(Expression(source))</script>

以上是“JS如何实现AST抽象语法树”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注编程网精选频道!

--结束END--

本文标题: JS如何实现AST抽象语法树

本文链接: https://lsjlt.com/news/246496.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • JS如何实现AST抽象语法树
    小编给大家分享一下JS如何实现AST抽象语法树,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!前端中的AST抽象语法树问题四则运算正则表达式词法分析语法分析完整代码...
    99+
    2023-06-06
  • 浅析AST抽象语法树及Python代码实现
    在计算机科学中,抽象语法树(abstract syntax tree或者缩写为AST),或者语法树(syntax tree),是源代码的抽象语法结构的树状表现形式,这里特指编程语言的源代码。树上的每个节点都...
    99+
    2022-06-04
    抽象 语法 代码
  • Vue编译器AST抽象语法树源码分析
    目录引言baseCompile主要核心代码如何写一个程序来识别 Tokenparse 函数解析模板字符串引言 接上篇  Vue编译器源码分析compile 解析 baseC...
    99+
    2024-04-02
  • 详解使用抽象语法树AST实现一个AOP切面逻辑
    目录开篇一、实现目的1、work.js2、aop.js二、利用语法树添加切面事件三、总结与思考开篇 AST 功能很灵活,可以通过改变一些自定义结构便可以输入自定义的功能,下面简单的...
    99+
    2023-05-14
    AST抽象语法树实现AOP AOP切面逻辑
  • 怎么使用抽象语法树AST实现一个AOP切面逻辑
    这篇文章主要介绍了怎么使用抽象语法树AST实现一个AOP切面逻辑的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇怎么使用抽象语法树AST实现一个AOP切面逻辑文章都会有所收获,下面我们一起来看看吧。一、实现目的将...
    99+
    2023-07-05
  • Python Ast抽象语法树的介绍及应用详解
    目录引言1. AST简介 2. 创建AST2.1 Compile函数2.2 生成ast 3. 遍历AST3.1 ast.NodeTransfer3.2 ast.N...
    99+
    2024-04-02
  • JS中抽象工厂模式如何实现
    这篇文章主要介绍了JS中抽象工厂模式如何实现,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。抽象工厂模式(Abstract Factory)就...
    99+
    2024-04-02
  • JS如何实现的二叉树算法
    这篇文章给大家分享的是有关JS如何实现的二叉树算法的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。具体如下:<!DOCTYPE HTML> <head&...
    99+
    2024-04-02
  • js如何实现抽奖效果
    小编给大家分享一下js如何实现抽奖效果,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!效果图:代码如下:<!DOCTYPE&...
    99+
    2024-04-02
  • c++如何实现抽象工厂
    今天小编给大家分享一下c++如何实现抽象工厂的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。代码示例利用者代码:这段代码中创建...
    99+
    2023-06-19
  • js如何实现数组转树
    本篇内容介绍了“js如何实现数组转树”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!原生 封装工具函数 getTree1 定义-映射对象 ma...
    99+
    2023-07-02
  • vue抽象语法树和虚拟dom的区别有哪些
    本篇内容主要讲解“vue抽象语法树和虚拟dom的区别有哪些”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“vue抽象语法树和虚拟dom的区别有哪些”吧! ...
    99+
    2024-04-02
  • Python如何实现抽象工厂模式
    这篇文章主要介绍“Python如何实现抽象工厂模式”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Python如何实现抽象工厂模式”文章能帮助大家解决问题。实 现下面是实现Abstract Facto...
    99+
    2023-06-19
  • JS如何实现简单九宫格抽奖
    这篇文章主要介绍了JS如何实现简单九宫格抽奖的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇JS如何实现简单九宫格抽奖文章都会有所收获,下面我们一起来看看吧。HTML文件:<body><div&...
    99+
    2023-07-02
  • java中抽象类、抽象方法、接口与实现接口实例详解
    前言对于java中的抽象类,抽象方法,接口,实现接口等具体的概念就不在这里详细的说明了,网上书本都有很多解释,主要是我懒,下面通过一个例子来说明其中的精髓要点,能不能练成绝世武功,踏上封王之路,就看自己的的啦(不要误会,我指的只是我自己啦啦...
    99+
    2023-05-30
    java 抽象类 接口
  • JS如何实现简单的九宫格抽奖
    本篇内容介绍了“JS如何实现简单的九宫格抽奖”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!结构<body>  ...
    99+
    2023-07-02
  • php如何调用抽象方法
    在 PHP 中,抽象方法是一种不能在抽象类中实现但必须在其实现类中实现的方法。抽象方法可以定义在抽象类或接口中,并且其定义方式类似于普通方法,但以 abstract 关键字开头并省略方法体实现。抽象方法必须在子类中覆写实现。如果您想学习如何...
    99+
    2023-05-14
  • java抽象类如何实例化
    Java抽象类不能直接实例化,因为抽象类是一种不完整的类,其中可能包含抽象方法,这些方法没有实现。所以,不能直接使用抽象类来创建对象...
    99+
    2023-10-22
    java
  • js中如何实现对象拷贝
    小编给大家分享一下js中如何实现对象拷贝,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!   原生深克隆方法JSON.parse...
    99+
    2024-04-02
  • java如何实现抽象工厂模式Abstract Factory
    这篇文章给大家分享的是有关java如何实现抽象工厂模式Abstract Factory的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。在工厂类模式中,如果需要新的类时,需要修改工厂类,这样显然不利于扩展。在抽象工厂...
    99+
    2023-06-04
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作