【问题标题】:Enumerate regular expressions via UglifyJS通过 UglifyJS 枚举正则表达式
【发布时间】:2015-12-30 07:05:06
【问题描述】:

我有一些 JavaScript 代码,我需要从中找到每个正则表达式的开始和结束索引。

如何从 UglifyJS 中提取这些信息?

var uglify = require('uglify-js');
var code = "func(1/2, /hello/);";
var parsed = uglify.parse(code);

我进入变量parsed 的结构非常复杂。我所需要的只是每个文字正则表达式的[{startIdx, endIdx}, {startIdx, endIdx}] 数组。

附:如果有人认为同样的任务可以用比 UglifyJS 更好的方式完成,欢迎提出建议!

更新

我知道如果我深入挖掘解析后的结构,那么对于每个正则表达式我都能找到对象:

AST_Token {
     raw: '/hello/',
     file: null,
     comments_before: [],
     nlb: false,
     endpos: 17,
     endcol: 17,
     endline: 1,
     pos: 10,
     col: 10,
     line: 1,
     value: /hello/,
     type: 'regexp'
}

我需要弄清楚如何从解析的结构中提取所有此类对象,以便编译位置索引列表。

【问题讨论】:

  • 在提取这些正则表达式或字符串或它们的索引后,您是否会告诉我们您将如何处理它们?
  • @torazaburo 它是另一个已经完成的解析器的一部分,除了正确支持正则表达式。我已经设法隔离了大约 99% 的案例,但如果没有完整的表达式评估,最后 1% 似乎是不可能的。我只需要知道正则表达式在任何给定代码行中的位置。

标签: javascript uglifyjs


【解决方案1】:

我得到了这个最终有用的link to the UglifyJS author's blog post,它为我指明了正确的方向。基于该博客,我能够将我的枚举代码修改为以下内容:

function enumRegEx(parsed) {
    var result = [];
    parsed.walk(new uglify.TreeWalker(function (obj) {
        if (obj instanceof uglify.AST_RegExp) {
            result.push({
                startIdx: obj.end.col,
                endIdx: obj.end.endcol
            });
        }
    }));
    return result;
}

这个东西不仅更短,工作方式相同,而且处理速度几乎是瞬间的,在 10 毫秒内,这让之前的结果(430 毫秒)相形见绌。

这就是我想要的结果! :)

更新:最后,我发现对于这个特定的任务,esprima 是一个更好的选择。与 UglifyJS 不同,它速度更快并且完全支持 ES6。

通过esprima 完成的任务完全相同,感谢Ariya Hidayat 的大力支持:

function parseRegEx(originalCode) {
    var result = [];
    esprima.tokenize(originalCode, {loc: true, range: true}, function (obj) {
        if (obj.type === 'RegularExpression') {
            result.push({
                startIdx: obj.range[0],
                endIdx: obj.range[1]
            });
        }
    });
    return result;
}

如您所见,使用esprima,您甚至不需要解析代码,而是传入原始代码,esprima 只会标记化,这样更快。

【讨论】:

  • @HenriqueBarcelos StackOverflow 不允许在回答后 2 天内接受自己的回答。
  • 但 esprima 被明确建议为您发布的相关问题中的最佳解决方案,而 IIRC 您认为它过于矫枉过正。
  • @torazaburo 相关问题中的建议根本没有帮助,它不够具体,甚至没有接近。但感谢您在此处立即对这个问题投反对票。
  • 请不要试图改写历史。 cmets 中的建议不需要是成熟的解决方案。 Esprima 不是火箭科学,一旦有了使用解析器的想法,弄清楚如何用它做你想做的事就很简单了。如果您还记得的话,您拒绝使用它的想法,不是因为该建议不具体,而是因为您不想使用解析器,将其描述为“矫枉过正”。无论如何,我很高兴你终于发现你确实需要一个解析器。
  • 我刚才就您对另一个问题的回答发表了评论,我认为这更合适;)
【解决方案2】:

由于还没有人回答,我设法提出了一个有效的正面解决方案,尽管可能不是最好的。

function enumRegEx(parsed) {
    var result = [];

    function loop(obj) {

        if (obj && typeof obj === 'object') {
            if (obj.used) {
                return;
            } else {
                obj.used = true;
            }
            if (obj instanceof Array) {
                obj.forEach(function (d) {
                    loop(d);
                });
            } else {
                if (obj instanceof uglify.AST_Node) {
                    for (var v in obj) {
                        loop(obj[v]);
                    }
                } else {
                    if (obj instanceof uglify.AST_Token) {
                        if (obj.type === 'regexp') {
                            result.push({
                                startIdx: obj.col,
                                endIdx: obj.endcol
                            });
                        }
                    }
                }
            }
        }
    }

    loop(parsed);
    return result;
}

我不喜欢这种方法的地方:

  • 我将它用于处理一个巨大的 30,000 行 JavaScript 文件,该文件由 UglifyJS 在 240 毫秒内解析,然后我的算法需要另外 430 毫秒来枚举正则表达式。这似乎效率很低。

  • 我必须修改具有属性used 的原始对象,因为解析的结构使用相互引用,否则会导致无限循环并耗尽调用堆栈。虽然我不太担心这一点,因为我没有将解析后的数据用于其他任何事情。

如果您知道更好的方法 - 请提出来!在这一点上,我最感兴趣的是提高我的枚举的性能,与实际的解析相比,这目前相当慢。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-21
    • 2019-06-23
    • 1970-01-01
    • 2021-02-22
    • 1970-01-01
    • 2012-11-21
    相关资源
    最近更新 更多