用 JavaScript 解析 JavaScript

周末我开始研究llamaduck - 一个简单的工具,旨在确定您的代码是否将在新发布的节点 0.6.0 上运行。最终,它可能也能够执行其他兼容性评估任务,但我首先关注的是简单的东西。

或者至少我认为这很简单。

自 0.4.x 以来的 API 更改列表似乎并不长,应该很容易消化。但事实证明,我几乎整个星期天都在研究如何将 javascript 变成漂亮的可分析的AST。

如果您不知道 AST 是什么——它是所谓的抽象语法树,这意味着无论实际语法是什么,它看起来都应该相同。虽然它会因不同的语言而有所不同。因此,CoffeeScript AST 应该看起来与 JavaScript 相同,但 Python 会有所不同。


JavaScript 变更和更改

有太多的 JavaScript 更改和更改可能需要在他们通过程序工作时进行,以至于对于某些人来说,准确地跟上他们应该做的事情来处理这样的事情无疑是一个挑战。

众所周知,JavaScript 的更改和更改可以在短期内使您的生活更具挑战性,但从长远来看,它会带来更好的产品。

我发现需要进行大量更改才能让 JavaScript 以我想要的方式排列。这种编码有很多小的和技术方面需要严格遵守,在有很多时间坐下来弄清楚他们需要什么之前,要做到这一点肯定不容易去做。

JavaScript 仍然是计算机程序员用来将他们的材料发布到 Internet 上的主要语言,因此开始使用它作为完成我自己的工作的主要工具对我来说很有意义。对于大大小小的项目,我继续依赖 JavaScript,而且我知道,我从中获得最大价值的唯一方法就是以对我的客户有效的方式使用它。

所有程序员越来越多地使用 JavaScript,这让我相信我必须继续努力,以充分利用我在这个程序上的时间,并且我所知道的技能可以以最好的方式得到应用我想要的结果。

在通过实际的 JavaScript 程序运行 JavaScript时,程序员经常会遇到一些常见的问题。有时可能会出现几行异常,并且还会出现其他情况,这些情况显然表明某段代码无法按预期工作。如果你遇到这个问题,只要知道这就是这个过程的全部内容。首先通过程序运行 JavaScript 是确保从最终产品中消除这些错误和遗漏的好方法。

仔细检查每一部分,确保你理解你在看什么。你可能会发现,毕竟你可以做更多的事情来帮助你的用户。

我的研究提出了 三个选择:

  1. 拿一个解析器生成器和一个 JavaScript 语法,希望最好
  2. JSLint有一个解析器……在 2000 行左右
  3. 据说 Uglify-JS 也有一个解析器

唯一可行的选择是 uglify-js。它是一个整齐打包的 node.js 模块,它做的比我需要的要多,但至少它有一个易于使用的解析器,带有暴露的 API 接口。

分数!

这是一个输出自己的 AST的文件示例,让您了解我在说什么:

var parser = require('uglify-js').parser;var util = require('util'); (function get_ast (path, callback) {    require('fs').readFile(path, 'utf-8', function (err, data) {        if (err) throw err;         callback(parser.parse(data));    });})('./example.js', function (data) {    console.log(util.inspect(data, true, null));});

该文件解析自身并输出编码为 javascript 数组的树(滚动过去,那里有更多文本):

[ 'toplevel',  [ [ 'var',      [ [ 'parser',          [ 'dot',            [ 'call',              [ 'name', 'require', [length]: 2 ],              [ [ 'string', 'uglify-js', [length]: 2 ],                [length]: 1 ],              [length]: 3 ],            'parser',            [length]: 3 ],          [length]: 2 ],        [length]: 1 ],      [length]: 2 ],    [ 'var',      [ [ 'util',          [ 'call',            [ 'name', 'require', [length]: 2 ],            [ [ 'string', 'util', [length]: 2 ], [length]: 1 ],            [length]: 3 ],          [length]: 2 ],        [length]: 1 ],      [length]: 2 ],    [ 'stat',      [ 'call',        [ 'function',          'get_ast',          [ 'path', 'callback', [length]: 2 ],          [ [ 'stat',              [ 'call',                [ 'dot',                  [ 'call',                    [ 'name', 'require', [length]: 2 ],                    [ [ 'string', 'fs', [length]: 2 ], [length]: 1 ],                    [length]: 3 ],                  'readFile',                  [length]: 3 ],                [ [ 'name', 'path', [length]: 2 ],                  [ 'string', 'utf-8', [length]: 2 ],                  [ 'function',                    null,                    [ 'err', 'data', [length]: 2 ],                    [ [ 'if',                        [ 'name', 'err', [length]: 2 ],                        [ 'throw',                          [ 'name', 'err', [length]: 2 ],                          [length]: 2 ],                        undefined,                        [length]: 4 ],                      [ 'stat',                        [ 'call',                          [ 'name', 'callback', [length]: 2 ],                          [ [ 'call',                              [ 'dot',                                [ 'name', 'parser', [length]: 2 ],                                'parse',                                [length]: 3 ],                              [ [ 'name', 'data', [length]: 2 ], [length]: 1 ],                              [length]: 3 ],                            [length]: 1 ],                          [length]: 3 ],                        [length]: 2 ],                      [length]: 2 ],                    [length]: 4 ],                  [length]: 3 ],                [length]: 3 ],              [length]: 2 ],            [length]: 1 ],          [length]: 4 ],        [ [ 'string', './example.js', [length]: 2 ],          [ 'function',            null,            [ 'data', [length]: 1 ],            [ [ 'stat',                [ 'call',                  [ 'dot',                    [ 'name', 'console', [length]: 2 ],                    'log',                    [length]: 3 ],                  [ [ 'call',                      [ 'dot',                        [ 'name', 'util', [length]: 2 ],                        'inspect',                        [length]: 3 ],                      [ [ 'name', 'data', [length]: 2 ],                        [ 'name', 'true', [length]: 2 ],                        [ 'name', 'null', [length]: 2 ],                        [length]: 3 ],                      [length]: 3 ],                    [length]: 1 ],                  [length]: 3 ],                [length]: 2 ],              [length]: 1 ],            [length]: 4 ],          [length]: 2 ],        [length]: 3 ],      [length]: 2 ],    [length]: 3 ],  [length]: 2 ]

结论

现在我们有了一个简单的树,我们可以 递归地分析和寻找不兼容的地方。但在做任何真正实用的事情之前,我需要弄清楚如何跟踪 变量范围。这真的很难,因为代码需要检查变量何时成为关键部分,然后确认它们确实最终以关键方式使用。

但是一旦那个坚果被破解, 美洲驼将成为一个对许多事情有用的整洁的小工具。

如果你有一些编码倾向,我很乐意在 llamaduck github repo提供帮助。

发表评论
留言与评论(共有 0 条评论) “”
   
验证码:

相关文章

推荐文章