需要正则表达式来完成语言解析器的基本递归功能(或帮助制作 Babel 插件)

Need regex to accomplish basic recursive feature of language parsers (or help making Babel plugin)

我有以下正则表达式:

/(?:this\.(\w+)\(([\s\S]*?)\))/g

它用来取这样的代码:

this.doSomething(foo, bar)

并将其替换为:

this.lookup('doSomething', [foo, bar])

对于该用例(这是最常见的)它可以正常工作,但如果在其中使用 this 则它不起作用:

this.doSomething(foo, bar, this.baz())

错误的结果是这样的:

this.lookup('doSomething', [foo, bar, this.baz(]))

应该是这样的:

this.lookup('doSomething', [foo, bar, this.baz()])

嗯,这是第一个问题。它实际上应该像this.doSomething一样进行转换,所以最终结果确实应该是:

this.lookup('doSomething', [foo, bar, this.lookup('baz', [])]);

基本上我的正则表达式假设 this.baz() 的右括号是 this.doSomething() 的右括号,并且也不会递归操作。我在这里需要某种递归 behavior/control 。

我听说过 xregexp,但我不确定它对我有何帮助。似乎真正的语言解析器可能是唯一的出路。我在那里没有太多经验,但我不怕弄脏我的手。似乎像 Esprima 这样的工具可以提供帮助?

在一天结束时,我希望在我的代码的构建步骤中进行较小的 language/syntax 更改,即与 Babel 完全一样。我实际上正在使用 Babel。也许某种 Babel 插件是一种选择?

无论如何,我对 quickfix 正则表达式技巧或更多 pro/robust 语言解析技术持开放态度。我也很好奇通常是如何解决这些问题的。扫描整个输入并匹配 open/closing braces/parentheses/etc 我假设??

下面是一个如何使用 Babel 插件执行此操作的示例:

var names = ['doSomething', 'baz'];

module.exports = function(context){
    var t = context.types;

    return {
        visitor: {
            CallExpression: function(path){
                var callee = path.get('callee');
                // Only process "this.*()" calls.
                if (!callee.isMemberExpression() ||
                    !callee.get('object').isThisExpression() ||
                    !callee.get('property').isIdentifier()) return;

                // Make sure the call is to one of your specific functions.
                if (names.indexOf(path.node.callee.property.name) === -1) return;

                // Build "this.lookup('<name>', [])".
                path.replaceWith(t.callExpression(
                    t.memberExpression(t.thisExpression(), t.identifier('lookup')),
                    [
                        t.stringLiteral(path.node.callee.property.name),
                        t.arrayExpression(path.node.arguments),
                    ]
                ));
            }
        }
    };
}

例如,如果将其放入 plugin.js 函数中,您可以创建一个 .babelrc 配置文件并确保 ./plugin.js 或指向它的任何路径在您的 plugins数组,例如

.babelrc

{
  "presets": ['es2015'],
  "plugins": ['./plugin']
}