Различные способы объявления LOOKAHEAD в JAVACC

Я понял грамматику Javacc, чтобы написать парсер, где я нашел строку, которая говорит как

Options : { LOOKAHEAD=3; }

Мне было интересно, что такое LOOKAHEAD и есть ли другие способы объявить lookahead?


person sarath kumar    schedule 23.04.2020    source источник


Ответы (2)


Параметры LOOKAHEAD - это количество токенов по умолчанию, которые будут использоваться для принятия решения о том, какой путь будет выбран в каждой точке выбора.

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

Значение по умолчанию для параметра LOOKAHEAD равно 1. Поэтому важно написать грамматику, которая подходит для LOOKAHEAD = 1, что означает, что выбор будет решаться путем просмотра на один токен вперед.

Например :

Поскольку JavaCC является парсером LL, если левый токен производственного правила повторяется, возникает конфликт выбора:

void PhoneNumber() : {} {​

(LocalNumber() | CountryNumber()) <EOF> ​

}​

void LocalNumber() : {} { ​

AreaCode() "-" <FOUR_DIGITS>  ​

}​

void CountryNumber() : {} {  ​

AreaCode() "-" <THREE_DIGIT> "-" <FOUR_DIGITS>   ​

}​

void AreaCode() : {} {   ​

 <THREE_DIGIT> ​

}

Обратите внимание, что и CountryNumber, и LocalNumber начинаются с терминала <THREE_DIGIT>, что вызывает конфликт выбора,

Это можно переписать с помощью подхода, называемого «левый факторинг».

void PhoneNumber() : {} {​

AreaCode() "-" (LocalNumber() | CountryNumber()) <EOF> ​

}​

void LocalNumber() : {} { ​

<FOUR_DIGITS>  ​

}​

void CountryNumber() : {} {  ​

<THREE_DIGIT> "-" <FOUR_DIGITS>   ​

}​

void AreaCode() : {} {   ​

 <THREE_DIGIT> ​

}

В случае, когда это невозможно сделать, используется спецификация Lookahead.

Спецификации Lookahead бывают пяти типов:

  • Множественный токен LOOKAHEAD
  • Синтаксический LOOKAHEAD
  • Комбинация множественных токенов и синтаксиса LOOKAHEAD
  • Семантический взгляд
  • Вложенный просмотр вперед

Множественный токен LOOKAHEAD

Это можно использовать с целым числом, переданным в методе LOOKAHEAD (k). Это можно сделать,

  • Местный LOOKAHEAD

    void PhoneNumber (): {} {(LOOKAHEAD (3) LocalNumber () | CountryNumber ())}

  • Глобальный LOOKAHEAD

    Варианты: {LOOKAHEAD = 3; }

Синтаксический LOOKAHEAD

Спецификация синтаксического просмотра вперед использует синтаксическую конструкцию в качестве преобразователя выбора.

При указании синтаксического упреждения без множественных токенов на самом деле вы указываете синтаксический упреждающий просмотр с бесконечным множеством токенов.

Например: LOOKAHEAD (2147483647, LocalNumber ())

void PhoneNumber() : {} {​

(​
LOOKAHEAD(("A"|"B")+ AreaCode() "-" <FOUR_DIGITS>) LocalNumber() ​

| CountryNumber()​

) <EOF> ​

}

Комбинация множественного токена и синтаксиса LOOKAHEAD

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

void PhoneNumber() : {} {​

(​
LOOKAHEAD(10, ("A"|"B")+ AreaCode() "-" <FOUR_DIGITS>) LocalNumber() ​

| CountryNumber()​

) <EOF> ​

}

Семантический взгляд

Семантический просмотр предполагает встраивание логического выражения Java в грамматику в точке выбора.

Если логическое выражение истинно, выбирается текущее раскрытие.

void PhoneNumber() : {} {​

(​
LOOKAHEAD({getToken(1).image.equals("123")}) ​
KeysvilleNumber() ​

| FarmvilleNumber()​

) <EOF> ​

}

Вложенный просмотр вперед

Вложенный просмотр вперед происходит, когда одна директива просмотра вперед перекрывает другую.В JavaCC вложенный синтаксический просмотр вперед игнорируется, но не вложенный семантический просмотр вперед.

void Start() : {} {​

(​
LOOKAHEAD(Fullname()) Fullname() ​

| Douglas()​

) <EOF> ​

}​

void Fullname() : {} {​

( LOOKAHEAD(Douglas() Munro()) ​

 Douglas()​

| Douglas() Albert()​

)​

Munro() ​

}​

void Douglas() : {} { "Douglas" } ​

void Albert() : {} { "Albert" }}​

void Munro() : {} { "Munro" }}

Все примеры взяты из замечательной книги Создание парсеров с помощью JavaCC Тома Коупленда

Надеюсь, это будет полезно, спасибо.

person sarath kumar    schedule 23.04.2020
comment
JavaCC создает нисходящие рекурсивные анализаторы спуска, которые являются LL, а не LR. Это объясняет, почему объявления LOOKAHEAD часто необходимы, и почему их относительно легко реализовать. - person rici; 23.04.2020

В настоящее время самой продвинутой версией JavaCC является JavaCC 21.

JavaCC21 предлагает новый способ указания опережающего просмотра, который намного удобнее и менее подвержен ошибкам, чем тот, который есть в унаследованном проекте JavaCC. Я имею в виду разделитель до сих пор.

Если у вас есть такая продукция, как:

void FooBar() : {}
{
   "foo" "bar" Baz()
}

и вы хотите просмотреть вперед 2 токена, чтобы увидеть, вводить ли FooBar (), в устаревшей версии JavaCC вы пишете:

LOOKAHEAD(2) FooBar()

каждый раз, когда вы используете продукцию FooBar (). Или, в качестве альтернативы, вы можете написать:

LOOKAHEAD("foo" "bar") FooBar()

В JavaCC21 вы можете написать производство следующим образом:

 void FooBar() : {}
 {
    "foo" "bar" =>|| Baz()
 }

=>|| называется разделителем до-сюда и означает, что мы просматриваем до этого момента. Если вы напишете продукцию, как указано выше, вы можете просто написать:

 FooBar()
 |
 SomethingElse()

и сгенерированный синтаксический анализатор сканирует до точки, указанной в производстве, при принятии решения, следует ли вводить FooBar ().

Разделитель до-сюда часто позволяет вам выражать вещи гораздо менее громоздко. Например, в устаревшем инструменте вы могли бы написать:

 LOOKAHEAD (Foo() Bar()) Foo() Bar() Baz()

в то время как в JavaCC21 вы можете написать:

 Foo() Bar() =>|| Baz()

JavaCC21 также имеет (необязательный) оптимизированный синтаксис, поэтому вы можете более кратко написать приведенную выше продукцию FooBar как:

  FooBar : "foo" "bar" =>|| Baz ;

Конечно, синтаксис * up-to-here - далеко не единственная новая функция JavaCC21, которой нет в устаревшем инструменте. Здесь слишком много того, что можно описать. Важным моментом является то, что вложенный синтаксический просмотр вперед теперь работает в JavaCC21. См. здесь.

person revusky    schedule 08.09.2020