Требуется регулярное выражение для выполнения основной рекурсивной функции парсеров языка (или помощь в создании плагина Babel)

У меня есть следующее регулярное выражение:

/(?: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() а также не работает рекурсивно. Мне нужно какое-то рекурсивное поведение / контроль здесь.

Я слышал о xregexp, но я не уверен, как это может мне помочь. Также кажется, что настоящий синтаксический анализатор языка - единственный путь. У меня там мало опыта, но я не боюсь испачкать руки. Кажется, такие инструменты, как Esprima, могут помочь?

В конце дня я собираюсь внести небольшие изменения в язык / синтаксис на этапе сборки моего кода, то есть точно так же, как это делает Babel. Я на самом деле использую Бабель. Может быть, какой-то плагин Babel является вариантом?

В любом случае, я открыт как для быстрого исправления трюка с регулярными выражениями, так и для более про / надежных методов синтаксического анализа языка. Мне также просто любопытно, как обычно решаются такие проблемы. Сканирование по всему вводу и сопоставление открытых / закрывающих скобок / скобок / и т. Д. Я предполагаю??

1 ответ

Решение

Вот пример того, как вы можете сделать это с помощью плагина 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']
}
Другие вопросы по тегам