ПРОГНОЗЫ для литерала массива JavaScript/ECMAScript

В настоящее время я реализую парсер JavaScript/ECMAScript 5.1 с JavaCC и имеют проблемы с производством ArrayLiteral.

ArrayLiteral :
    [ Elision_opt ]
    [ ElementList ]
    [ ElementList , Elision_opt ]

ElementList :
    Elision_opt AssignmentExpression
    ElementList , Elision_opt AssignmentExpression

Elision :
    ,
    Elision ,

У меня есть три вопроса, я задам их один за другим.

Это второй.


Я упростил это производство до следующей формы:

ArrayLiteral:
    "[" ("," | AssignmentExpression ",") * AssignmentExpression ? "]"

Пожалуйста, посмотрите первый вопрос о том, правильно это или нет:

Как упростить буквальное создание массива JavaScript/ECMAScript?

Теперь я попытался реализовать его в JavaCC следующим образом:

void ArrayLiteral() :
{
}
{
    "["
    (
        ","
    |   AssignmentExpression()
        ","
    ) *
    (
        AssignmentExpression()
    ) ?
    "]"
}

JavaCC жалуется на неоднозначное , или AssignmentExpression (его содержимое). Очевидно, что требуется спецификация LOOKAHEAD. Я потратил много времени, пытаясь понять LOOKAHEAD, пробовал разные вещи, такие как

  • LOOKAHEAD (AssignmentExpression() ",") in (...)*
  • LOOKAHEAD (AssignmentExpression() "]") in (...)?

и несколько других вариантов, но мне не удалось избавиться от предупреждения JavaCC.

Я не понимаю, почему это не работает:

void ArrayLiteral() :
{
}
{
    "["
    (
        LOOKAHEAD ("," | AssignmentExpression() ",")
        ","
    |   AssignmentExpression()
        ","
    ) *
    (
        LOOKAHEAD (AssignmentExpression() "]")
        AssignmentExpression()
    ) ?
    "]"
}

Хорошо, AssignmentExpression() само по себе неоднозначно, но конечные "," или "]" в LOOKAHEADs должны прояснить, какой из вариантов следует выбрать - или я ошибаюсь?

Как должна выглядеть правильная спецификация LOOKAHEAD для этой продукции?

Обновить

Это не сработало, к сожалению:

void ArrayLiteral() :
{
}
{
    "["
    (
        ","
    |
        LOOKAHEAD (AssignmentExpression() ",")
        AssignmentExpression()
        ","
    ) *
    (
        AssignmentExpression()
    ) ?
    "]"
}

Предупреждение:

Warning: Choice conflict in (...)* construct at line 6, column 5.
         Expansion nested within construct and expansion following construct
         have common prefixes, one of which is: "function"
         Consider using a lookahead of 2 or more for nested expansion.

Строка 6 - это ( перед первым LOOKAHEAD. Общий префикс "function" — это просто одно из возможных начал AssignmentExpression.


person lexicore    schedule 13.11.2014    source источник


Ответы (3)


Вот еще один подход. Его преимущество заключается в том, что он определяет, какие запятые обозначают неопределенные элементы, без использования каких-либо семантических действий.

void ArrayLiteral() : {} { "[" MoreArrayLiteral() }

void MoreArrayLiteral() : {} {
    "]"
|    "," /* undefined item */ MoreArrayLiteral()
|    AssignmentExpression() ( "]" |  "," MoreArrayLiteral() )
}
person Theodore Norvell    schedule 14.11.2014

JavaCC производит синтаксические анализаторы сверху вниз. Сразу скажу, что я не фанат генераторов нисходящих синтаксических анализаторов, поэтому я не эксперт по JavaCC, и у меня нет под рукой его для тестирования.

(Редактировать: я думал, что сработает что-то еще, но впоследствии я понял, что не понимаю, как JavaCC связывает предварительный просмотр с фактическим выбором; в случае ( A | B )* C на самом деле есть три возможных варианта: A, B и C. Я думал, что он рассмотрит все три из них, но возможно, что он будет рассматривать их сразу по два. Так что следующее предположение — еще одно предположение.)

Сказав это, я думаю, что следующее будет работать, но это включает в себя синтаксический анализ почти каждого AssignmentExpression() дважды.

{
    "["
    (
        ","
    |
        AssignmentExpression()
        ","
    ) *
    (
        LOOKAHEAD (AssignmentExpression() "]")
        AssignmentExpression()
    ) ?
    "]"
}

Как я указал в связанном вопросе, лучшим решением является переписать произведение иначе:

"[" AssignmentExpression ? ("," AssignmentExpression ?) * "]"

Это приводит к упреждающей грамматике с одним токеном, поэтому вам не понадобится объявление LOOKAHEAD для ее обработки.

person rici    schedule 13.11.2014
comment
ArrayLiteral -> '[' AssignmentExpression ? (',' AssignmentExpression ?) * ']' - Я думал об этом, но это сильно усложняет мой код. На самом деле я создаю ArrayLiteral , используя своего рода шаблон строителя. Так что на ',' здесь я не буду знать, добавлять ли элизион или выражение присваивания. Но разрешимо. - person lexicore; 13.11.2014
comment
@lexicore: если необязательный AssignmentExpression отсутствует, у вас есть исключение. В чем проблема? (Другими словами, вы не решаете, основываясь на запятой; вы решаете, основываясь на наличии или отсутствии AssignmentExpression) - person rici; 13.11.2014
comment
А, наверное, ничего. Я хочу добавить в свой массив как выражения присваивания, так и исключения. Так что, просто глядя на ',' в (...) *, я не могу решить, нужно ли мне добавлять здесь исключение или нет, я должен знать, что было последним шагом. Но это на самом деле тривиально, вы правы. Меня ввели в заблуждение собственные мысли. - person lexicore; 13.11.2014
comment
Я не совсем понимаю значение elisions. Однако я думаю, что @rici прав. "[" {builder.startArrayLiteral();} Item() ("," Item())* "]" {builder.endArrayLiteral();} и определить Item с помощью e=AssignmentExp() {builder.addExp(e);} | {builder.addElision();}. - person Theodore Norvell; 13.11.2014
comment
@rici Пожалуйста, посмотрите мое обновление и ответ - LOOKAHEADs, как вы написали, к сожалению, не сработало. Но у тебя переписывание сработало отлично! Мне просто нужна была еще одна переменная, чтобы отслеживать, был ли последний элемент AssignmentExpression. - person lexicore; 13.11.2014
comment
@TheodoreNorvell Допустим, [,1,] эффективно производит [undefined,1], а также [,1]. Я думаю, что то, что вы опубликовали, сработает. - person lexicore; 13.11.2014
comment
@rici Пожалуйста, посмотрите часть после Update в вопросе - я опробовал опубликованный вами код (с множеством вариантов), по какой-то причине я получаю опубликованное предупреждение. Я не понимаю, почему, для меня это выглядит вполне обоснованным. Может быть, Теодор мог бы просветить нас... - person lexicore; 14.11.2014
comment
@lexicore: Хорошо, я сделал еще одну попытку. - person rici; 14.11.2014

Вот как я это решил (благодаря ответу @rici):

JSArrayLiteral ArrayLiteral() : 
{
    boolean lastElementWasAssignmentExpression = false;
}
{
    "["
    (
        (
            AssignmentExpression()
            {
                // Do something with expression
                lastElementWasAssignmentExpression = true;
            }
        ) ?
        (
            ","
            {
                if (!lastElementWasAssignmentExpression)
                {
                    // Do something with elision
                }
            }
            (
                AssignmentExpression()
                {
                    // Do something with expression
                    lastElementWasAssignmentExpression = true;
                }
            ) ?
        ) *
    )
    "]"
    {
        // Do something with results
    }
}
person lexicore    schedule 13.11.2014