Как проанализировать вызовы выражений функций JavaScript с помощью ANTLR? - PullRequest
1 голос
/ 20 апреля 2011

Я создаю инструментарий JavaScript с помощью ANTLR, используя грамматику Патрика Хулсмейера EcmaScript 3 .

У меня проблема с анализом этой строки кода:

function(){}();

это прямой вызов выражения функции. Синтаксический анализатор распознает инструкцию как объявление функции, а затем завершается ошибкой, когда находит скобки после тела функции. Причина в том, что объявления функций распознаются с наибольшим приоритетом, чтобы избежать двусмысленности с выражениями функций.

Вот как грамматика распознает объявления функций:

sourceElement
options
{
    k = 1 ;
}
    : { input.LA(1) == FUNCTION }? functionDeclaration
    | statement
    ;

Я даже не уверен, что это допустимый оператор EcmaScript. Это так?
Я думаю, что правильнее было бы написать:

(function(){})();

, который на самом деле хорошо обрабатывается парсером.
Кстати, это не суть вопроса, потому что я не контролирую код для инструмента.

Я пытался исключить functionDeclaration из производства sourceElement и поместить его в statementstatementTail производство:

statementTail
    : variableStatement
    | emptyStatement
    | expressionStatement
    | functionDeclaration
    | ifStatement
    | ...
    ;

Но возникает ошибка сборки:

[роковое] правило statementTail имеет не LL (*) решение из-за рекурсивного правила вызова достижимы из Alts 3,4. Решить с помощью левого факторинга или используя синтаксические предикаты или используя backtrack=true опция.
| --->: variableStatement

, поскольку производство variableStatement содержит functionExpression в качестве потомка, что приводит к неоднозначности. Парсер не может выбирать между functionDeclaration и functionExpression, потому что они почти равны:

functionDeclaration
    : FUNCTION name=Identifier formalParameterList functionBody
    -> ^( FUNCTIONDECL $name formalParameterList functionBody )
    ;

functionExpression
    : FUNCTION name=Identifier? formalParameterList functionBody
    -> ^( FUNCTIONEXPR $name? formalParameterList functionBody )
    ;

Примечание. Я изменил исходные правила перезаписи, используя разные узлы дерева (FUNCTIONDECL и FUNCTIONEXPR), потому что он мне нужен во время прогулки по AST.

Как мне решить эту двусмысленность?

1 Ответ

2 голосов
/ 24 апреля 2011

Парсер вправе ожидать functionDeclaration, когда sourceElement начинается с ключевого слова function. Это фактически реализует следующее ограничение из Спецификации языка ECMAScript :

ExpressionStatement не может начаться с ключевым словом функции, потому что это может сделать это двусмысленным с FunctionDeclaration.

Таким образом, рассматриваемое утверждение недопустимо в соответствии с вышеуказанным ограничением, хотя на самом деле оно не является двусмысленным при создании грамматики: поскольку в нем опущен идентификатор функции, оно не может быть functionDeclaration. Заявление, разоблачающее синтаксическую двусмысленность, будет

function f(){}(42)

, который согласно спецификации ECMAScript является functionDeclaration, за которым следует expressionStatement.

Так что лучше всего спросить у провайдера этого кода правильный синтаксис. Вы говорили, что вам все равно нужно разобрать его, и, возможно, это можно сделать с помощью обратного отслеживания ANTLR. Убедитесь, что идентификатор функции является обязательным в functionDeclaration, и сделайте так, чтобы он попробовал functionDeclaration перед оператором. Но имейте в виду, что, даже если это поможет исходному утверждению, оно потерпит неудачу для

function f(){}()

потому что здесь функция functionDeclaration может быть успешно завершена, но после нее нет действительного оператора.

...