阅读更多

1顶
0踩

Web前端

转载新闻 JavaScript的语法解析与抽象语法树

2015-07-10 16:43 by 副主编 mengyidan1988 评论(0) 有19342人浏览
本文转自:http://wwsun.me/

抽象语法树(Abstract Syntax Tree)也称为AST语法树,指的是源代码语法所对应的树状结构。也就是说,对于一种具体编程语言下的源代码,通过构建语法树的形式将源代码中的语句映射到树中的每一个节点上。

JavaScript语法解析
什么是语法树

可以通过一个简单的例子来看语法树具体长什么样子。有如下代码:



我们可以发现,程序代码本身可以被映射成为一棵语法树,而通过操纵语法树,我们能够精准的获得程序代码中的某个节点。例如声明语句,赋值语句,而这是用正则表达式所不能准确体现的地方。JavaScript的语法解析器Espsrima提供了一个在线解析的工具,你可以借助于这个工具,将JavaScript代码解析为一个JSON文件表示的树状结构。

有什么用

抽象语法树的作用非常的多,比如编译器、IDE、压缩优化代码等。在JavaScript中,虽然我们并不会常常与AST直接打交道,但却也会经常的涉及到它。例如使用UglifyJS来压缩代码,实际这背后就是在对JavaScript的抽象语法树进行操作。

在一些实际开发过程中,我们也会用到抽象语法树,下面通过一个小栗子来看看怎么进行JavaScript的语法解析以及对节点的遍历与操纵。
举个例子
小需求
我们将构建一个简单的静态分析器,它可以从命令行进行运行。它能够识别下面几部分内容:
  • 已声明但没有被调用的函数
  • 调用了未声明的函数
  • 被调用多次的函数

现在我们已经知道了可以将代码映射为AST进行语法解析,从而找到这些节点。但是,我们仍然需要一个语法解析器才能顺利的进行工作,在JavaScript的语法解析领域,一个流行的开源项目是Esprima,我们可以利用这个工具来完成任务。此外,我们需要借助Node来构建能够在命令行运行的JS代码。b

完整代码地址:
https://github.com/wwsun/awesome-javascript/tree/master/src/day05

准备工作

为了能够完成后面的工作,你需要确保安装了Node环境。首先创建项目的基本目录结构,以及初始化NPM。
mkdir esprima-tutorial
cd esprima-tutorial
npm install esprima --save

在根目录新建index.js文件,初试代码如下:
var fs = require('fs'),
    esprima = require('esprima');

function analyzeCode(code) {
    // 1
}

// 2
if (process.argv.length < 3) {
    console.log('Usage: index.js file.js');
    process.exit(1);
}

// 3
var filename = process.argv[2];
console.log('Reading ' + filename);
var code = fs.readFileSync(filename);

analyzeCode(code);
console.log('Done');

在上面的代码中:

1.函数analyzeCode用于执行主要的代码分析工作,这里我们暂时预留下来这部分工作待后面去解决。
2.我们需要确保用户在命令行中指定了分析文件的具体位置,这可以通过查看process.argv的长度来得到。为什么?你可以参考Node的官方文档:
引用
The first element will be ‘node’, the second element will be the name of the JavaScript file. The next elements will be any additional command line arguments.

3.获取文件,并将文件传入到analyzeCode函数中进行处理
解析代码和遍历AST

借助Esprima解析代码非常简单,只要使用一个方法即可:
var ast = esprima.parse(code);

esprima.parse()方法接收两种类型的参数:字符串或Node的Buffer对象,它也可以收附加的选项作为参数。解析后返回结果即为抽象语法树(AST),AST遵守Mozilla SpiderMonkey的解析器API。例如代码:
引用
6 * 7

解析后的结果为:
{
    "type": "Program",
    "body": [
        {
            "type": "ExpressionStatement",
            "expression": {
                "type": "BinaryExpression",
                "operator": "*",
                "left": {
                    "type": "Literal",
                    "value": 6,
                    "raw": "6"
                },
                "right": {
                    "type": "Literal",
                    "value": 7,
                    "raw": "7"
                }
            }
        }
    ]
}

我们可以发现每个节点都有一个type,根节点的type为Program。type也是所有节点都共有的,其他的属性依赖于节点的type。例如上面实例的程序中,我们可以发现根节点下面的子节点的类型为EspressionStatement,依此类推。

为了能够分析代码,我们需要对得到的AST进行遍历,我们可以借助Estraverse进行节点的遍历。执行如下命令进行安装该NPM包:
npm install estraverse --save

基本用法如下:
function analyzeCode(code) {
    var ast = esprima.parse(code);
    estraverse.traverse(ast, {
        enter: function (node) {
            console.log(node.type);
        }
    });
}

上面的代码会输出遇到的语法树上每个节点的类型。

获取分析数据

为了完成需求,我们需要遍历语法树,并统计每个函数调用和声明的次数。因此,我们需要知道两种节点类型。首先是函数声明:
{
    "type": "FunctionDeclaration",
    "id": {
        "type": "Identifier",
        "name": "myAwesomeFunction"
    },
    "params": [
        ...
    ],
    "body": {
        "type": "BlockStatement",
        "body": [
            ...
        ]
    }
}

对函数声明而言,其节点类型为FunctionDeclaration,函数的标识符(即函数名)存放在id节点中,其中name子属性即为函数名。params和body分别为函数的参数列表和函数体。

我们再来看函数调用:
"expression": {
    "type": "CallExpression",
    "callee": {
        "type": "Identifier",
        "name": "myAwesomeFunction"
    },
    "arguments": []
}

对函数调用而言,即节点类型为CallExpression,callee指向被调用的函数。有了上面的了解,我们可以继续完成我们的程序如下:
function analyzeCode(code) {
    var ast = esprima.parse(code);

    var functionsStats = {}; //1

    var addStatsEntry = function (funcName) { //2
        if (!functionsStats[funcName]) {
            functionsStats[funcName] = { calls: 0, declarations: 0 };
        }
    };

	// 3
    estraverse.traverse(ast, {
        enter: function (node) {
            if (node.type === 'FunctionDeclaration') {
                addStatsEntry(node.id.name); //4
                functionsStats[node.id.name].declarations++;
            } else if (node.type === 'CallExpression' && node.callee.type === 'Identifier') {
                addStatsEntry(node.callee.name);
                functionsStats[node.callee.name].calls++; //5
            }
        }
    });
}

1.我们创建了一个对象functionStats用来存放函数的调用和声明的统计信息,函数名作为key。
2.函数addStatsEntry用于实现存放统计信息。
3.遍历AST
4.如果发现了函数声明,增加一次函数声明
5.如果发现了函数调用,增加一次函数调用
处理结果

最后进行结果的处理,我们只需要遍历查看functionStats中的信息就可以得到结果。创建结果处理函数如下:
function processResults(results) {
    for (var name in results) {
        if (results.hasOwnProperty(name)) {
            var stats = results[name];
            if (stats.declarations === 0) {
                console.log('Function', name, 'undeclared');
            } else if (stats.declarations > 1) {
                console.log('Function', name, 'decalred multiple times');
            } else if (stats.calls === 0) {
                console.log('Function', name, 'declared but not called');
            }
        }
    }
}

然后,在analyzeCode函数的末尾调用该函数即可,如下:
processResults(functionsStats);

测试

创建测试文件demo.js如下:
function declaredTwice() {
}

function main() {
    undeclared();
}

function unused() {
}

function declaredTwice() {
}

main();

执行如下命令:
$ node index.js demo.js

你将得到如下的处理结果:
引用

Reading test.js
Function declaredTwice decalred multiple times
Function undeclared undeclared
Function unused declared but not called
Done

小结
是时候进行小结,本文主要介绍了如何对JavaScript代码使用Esprima进行解析,解析后的结果是一棵符合SpiderMonkey解析API的语法树,然后我们使用Estraverse进行遍历,在遍历过程中我们通过识别节点类型来判断代码位置。最后,如果你需要修改节点,你可以参考[3]。

References
1.Esprima tutorial
2.Fun with Esprima and Static Analysis
3.Parsing and modifying JavaScript code with Esprima and Scodegen

本文转自:http://wwsun.me/
  • 大小: 84.2 KB
来自: wwsun.me
1
0
评论 共 0 条 请登录后发表评论

发表评论

您还没有登录,请您登录后再发表评论

相关推荐

  • treehugger:JavaScript AST(抽象语法树)转换工具

    现在: Javascript:一个(基于的)解析器,分析重建类型结构并首先尝试进行类型推断。 lib/treehugger/js/*.js 该项目依赖进行库加载。 AST表示 ast.js使用一些简单的数据结构来表示AST,并使用文本表示形式来...

  • JavaScript语法解析与抽象语法树(AST)----Espsrima的使用方法

    前言 最近在分析微信小程序,...JavaScript语法解析 首先来看一下什么是抽象语法树。抽象语法树(Abstract Syntax Tree)也称为AST语法树,指的是源代码语法所对应的树状结构。也就是说,对于一种具体编程语言下的源...

  • 【前端源码解析】AST 抽象语法树

    AST 抽象语法树

  • JavaScript中的 抽象语法树 AST

    抽象语法树(Abstract Syntax Tree)也称为AST语法树,指的是源代码语法所对应的树状结构。也就是说,一种编程语言的源代码,通过构建语法树的形式将源代码中的语句映射到树中的每一个节点上。 而在JavaScript中,...

  • AST抽象语法树

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

  • 转JavaScript的语法解析与抽象语法树

    JavaScript的语法解析与抽象语法树 Jul 10, 2015 抽象语法树(Abstract Syntax Tree)也称为AST语法树,指的是源代码语法所对应的树状结构。也就是说,对于一种具体编程语言下的源代码,通过构建语法树的形式将源...

  • php ast 抽象语法树,抽象语法树(AST)

    抽象语法树入门到放弃?抽象语法树(Abstract syntax tree AST)在计算机科学中,抽象语法和抽象语法树其实是源代码的抽象语法结构的树状表现形式为什么是抽象的?前端工程化,离不了 AST虽然AST是编译原理中的基础...

  • AST 抽象语法树

    ast抽象语法树是对象,这个对象就是对源代码的描述,通过JS Parser把代码转化成一个抽象的语法树,这个数定义了代码的结构,通过操纵这课树,我们可以精准的定位到声明语句,赋值语句,运算语句等等,实现对代码的...

  • html转换成抽象语法树,了解JavaScript抽象语法树

    本篇文章给大家谈谈JavaScript抽象语法树。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。前言babel是现在几乎每个项目中必备的一个东西,但是其工作原理避不开对js的解析在生成的过程,babel有...

  • java抽象语法树(ast),【你应该了解的】抽象语法树AST

    本文介绍的抽象语法树,就是他们用到的技术,是不是应该了解一下呢?本文没有晦涩难懂的理论,也没有大段大段的代码,完全从零开始,小白阅读也无任何障碍。通过本文的阅读,您将会了解AST的基本原理以及使用方法。...

  • vue源码解析---AST抽象语法树

    在计算机科学中,抽象语法树(abstract syntax tree或者缩写为AST),或者语法树(syntax tree),是源代码的抽象语法结构的树状表现形式,这里特指编程语言的源代码。我们可以理解为:把 template(模板)解析成一...

  • python 抽象语法树_抽象语法树(Abstract Syntax Tree)

    一般来说,程序中的一段源代码在执行之前...2 解析/语法分析这个过程是将词法单元流(数组)转换成一个由元素逐级嵌套所组成的代表了程序语法结构的树,这个树就叫“抽象语法树”(AST)。全称 Abstract Syntax Tree。...

  • 抽象语法树 -Abstract Syntax Tree

    抽象语法树

  • cst:JavaScript具体语法树

    与AST (抽象语法树)不同, CST包含JavaScript源文件中的所有信息:空格,标点符号,注释。 此信息对于代码样式检查器和其他代码查询非常有用。 当您需要在保留初始文件格式的同时对现有JavaScript文件应用修改的...

  • node-v16.12.0-darwin-x64.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

  • 试用Dev Containers的示例项目-Go

    计算机技术是指评价计算机系统的各种知识和技能的总称。它涵盖了计算机硬件、软件、网络和信息安全等方面。计算机技术的发展使我们能够进行高效的数据处理、信息存储和传输。现代计算机技术包括操作系统、数据库管理、编程语言、算法设计等。同时,人工智能、云计算和大数据等新兴技术也在不断推动计算机技术的进步。计算机技术的应用广泛,涵盖了各个领域,如商业、医疗、教育和娱乐等。随着计算机技术的不断革新,我们可以更加高效地实现预期自动化、标准化

  • NTsky新闻发布v1.0测试版(提供JavaBean).zip

    ### 内容概要: 《NTsky新闻发布v1.0测试版》是一款基于 Java 开发的新闻发布系统的测试版本,旨在为新闻机构和媒体提供一个简单易用的新闻发布平台。该系统具有基本的新闻发布和管理功能,包括新闻分类、新闻编辑、新闻发布等核心功能。此外,该版本还提供了 JavaBean,使开发人员能够方便地将系统集成到自己的项目中,快速实现新闻发布的功能。 ### 适用人群: 本测试版本适用于新闻机构、媒体从业者以及Java开发人员。如果你是一家新闻机构或媒体,希望拥有一个简单易用的新闻发布平台,方便快捷地发布和管理新闻,那么这个测试版本将为你提供一个初步的体验。同时,如果你是一名Java开发人员,希望学习和掌握新闻发布系统的开发技术,并且对新闻行业有一定的了解,那么通过这个测试版本,你可以获取到一些JavaBean,并且可以参考系统的设计和实现,为你的项目开发提供参考和借鉴。无论是从业务需求还是技术学习的角度,该测试版本都将为你提供一定的帮助和支持。

  • JavaScript介绍.zip

    javascript,JavaScript 最初由 Netscape 公司的 Brendan Eich 在 1995 年开发,用于 Netscape Navigator 浏览器。随着时间的推移,JavaScript 成为了网页开发中不可或缺的一部分,并且其应用范围已经远远超出了浏览器,成为了全栈开发的重要工具。

  • 15-21.php

    15-21.php

Global site tag (gtag.js) - Google Analytics