JAVACC Как передать токен для 1 или более

#parsing #lexer #bnf #javacc

Вопрос:

Я недавно начал изучать синтаксический анализатор javacc. Меня попросили написать синтаксический анализатор, в котором токен принимает числа от 1 до многих, и другой токен, который принимает числа от 2 до многих, поэтому я придумал что-то вроде этого:

 TOKEN : {
<NUM1: <NUM> (<NUM>)* > //for one or more
<NUM2: (<NUM>) > //for two or more
<NUM :(["0"-"9"]) >





 // and in the function
    void calc():
    {}
    {
     (
      (<NUM1>) 
     (<NUM2>) 
     )* <EOF>
    }
 

Однако, даже если я передам текстовое значение БЕЗ цифр, оно будет успешно передано. Что я делаю не так в этом?

Ответ №1:

Синтаксис JavaCC для лексических маркеров позволяет использовать повторения элементов, заключенных в область () , за которой следует один из:

 ? - zero or one time
* - zero or more times
  - one or more times
 

В вашем случае вам понадобятся два жетона:

 TOKEN: 
{
  <NUM2: ["0"-"9"] (["0"-"9"]) > // for two or more
  <NUM1:           (["0"-"9"]) > // for one or more
}
 

Вы читаете это как:

  • названный токен NUM1 соответствует от одного до бесконечного числа цифр
  • маркер с именем NUM2 соответствует от двух до бесконечного числа цифр

Лексический механизм в JavaCC использует один символ из входного потока символов и пытается распознать токен. Эти два автомата выглядят следующим образом:

Автоматы Лексера

Лексер прогрессирует одновременно в обоих автоматах для обоих токенов. После того, как дальнейший прогресс невозможен, распознается последний найденный токен. Если возможно более одного типа токена, то распознается тот, который был объявлен первым. По этой причине NUM2 заявлено ранее NUM1 . Это означает, что для ввода 1 токен NUM2 не будет распознан, потому что для него требуется более одной цифры. В этом случае NUM1 будет только один тип токена, соответствующий этому входу. Для ввода 12 оба типа токенов будут принимать его, но NUM2 будут распознаны, потому что он объявлен первым. Это означает, что если вы закажете их NUM1 первыми, то NUM2 вы никогда не получите NUM2 токен, потому NUM1 что всегда будете «выигрывать» с его наивысшим приоритетом.

Чтобы использовать их, у вас могут быть две функции синтаксического анализа, подобные этим:

 void match_one_to_many_numbers() : {} { <NUM1> (" " <NUM1>)* <EOF> }
void match_two_to_many_numbers() : {} { <NUM2> (" " <NUM2>)* <EOF> }
 

Вы читаете это как:

  • функция match_one_to_many_numbers принимает от одного до бесконечного числа токенов NUM1 , разделенных пробелом, а затем входной поток должен заканчиваться EOF системным токеном
  • функция match_two_to_many_numbers та же, только из токена NUM2

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

Комментарии:

1. Слава богу, это работает!!!!

2. Поскольку <NUM1> совпадают только токены, содержащие одну цифру, вы могли бы написать просто <NUM1: (["0"-"9"])> . Если вы хотите сопоставить токен, содержащий 1 или более цифр, вы можете написать (<NUM1> | <NUM2>) в синтаксическом анализаторе.

3. Использование " " в синтаксическом анализаторе имеет смысл только в том случае, если у вас есть такое TOKEN объявление, как TOKEN:{ <SPACE : " ">} . Если вы пропускаете пробел, скажем, SKIP:{< " ">} с помощью,, то вам не нужно и не следует использовать «» в грамматике. Если для вас нет объявления ТОКЕНА или ПРОПУСКА " " , и вы используете " " правила грамматики, то JavaCC выведет TOKEN для вас объявление, которое может вам не понадобиться. Прочтите документацию и часто задаваемые вопросы для получения полного объяснения.

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

5. Первый комментарий содержал три утверждения. Что не соответствует действительности? (a) Строки из более чем одной цифры не приведут к появлению токенов NUM1. (b) Таким образом, определение NUM1 может быть упрощено. (c) Вы можете сопоставлять строки из 1 или более цифр, как показано на рисунке. Второй комментарий был предназначен для того, чтобы разъяснить ваше использование «» для спрашивающего. В его вопросе не было ничего, что указывало бы на то, что он хотел, чтобы «» было знаком. Поэтому я подумал, что стоит отметить, что ваша грамматика делает «» символом, что может быть неочевидно для новичка.