Почему многострочные комментарии в flex / bison такие уклончивые? - PullRequest
14 голосов
/ 10 ноября 2010

Я пытаюсь проанализировать многострочные комментарии в стиле C в моем файле flex (.l):

%s ML_COMMENT
%%

...

<INITIAL>"/*"                   BEGIN(ML_COMMENT);
<ML_COMMENT>"*/"                BEGIN(INITIAL);  
<ML_COMMENT>[.\n]+              { }

Я не возвращаю токен, и моя грамматика (.y) не 'комментировать комментарии любым способом.

Когда я запускаю свой исполняемый файл, я получаю ошибку разбора:

$ ./a.out
/*
abc 
def
Parse error: parse error
$ echo "/* foo */" | ./a.out
Parse error: parse error

(Моя функция yyerror выполняет printf ("Ошибка разбора:% s \ n"), откуда берется первая половина избыточного сообщения об ошибке).

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

Также сбивает с толку:

$ ./a.out
/* foo */
a = b;
Parse error: parse error

В этом случае комментарий закрывается до фактического действительного ввода (который безкомментарий, разбирает просто отлично).Ошибка фактически возникает после разбора «a», а не после попытки анализа присваивания «a = b;».Если я введу «а» в отдельной строке, он все равно выдаст ошибку.

Учитывая, что сообщение об ошибке является ошибкой синтаксического анализатора, а не ошибки сканера, есть ли что-то важное, что я упускаю в своем файле .y?Или я что-то не так делаю в правилах своего сканера, которые распространяются на парсер?

РЕДАКТИРОВАТЬ: По предложению @ Руди я включил отладку и обнаружил:

$ ./a.out
Starting parse
Entering state 0
Reading a token: /*
foo
Next token is 44 (IDENTIFER)
Shifting token 44 (IDENTIFER), Entering state 4
Reducing via rule 5 (line 130), IDENTIFER  -> identifier
state stack now 0
Entering state 5

Я отключил отладку и обнаружил, что /* foo */ = bar; действительно анализирует так же, как foo = bar;.Я использую Flex 2.5.4;он не дает мне никаких предупреждений о правилах с состоянием, которые я пытаюсь использовать.

Ответы [ 4 ]

5 голосов
/ 10 ноября 2010

Я думаю, вам нужно объявить ваше начальное условие ML_COMMENT как эксклюзивное стартовое условие, чтобы были активны только правила ML_COMMENT. %x ML_COMMENT вместо %s ML_COMMENT

В противном случае также действуют правила без условий запуска.

5 голосов
/ 10 ноября 2010

Анализ комментариев таким способом может привести к ошибкам, потому что:

  • вам нужно добавить условия ко всем вашим правилам lex
  • это становится еще более сложным, если вы также хотите обрабатывать// comments
  • у вас все еще есть риск, что yacc / bison объединит два комментария, включая все, что находится между

В моем парсере я обрабатываю комментарии, подобные этомуСначала определите правила lex для начала комментария, например:

\/\*     {
         if (!SkipComment())
            return(-1);
         }

\/\/     {
         if (!SkipLine())
            return(-1);
         }

, затем напишите функции SkipComment и SkipLine.Они должны использовать весь ввод, пока не будет найден конец комментария (это довольно старый код, так что простите мне несколько архаичные конструкции):

bool SkipComment (void)
{
int Key;

Key=!EOF;
while (true)
   {
   if (Key==EOF)
      {
      /* yyerror("Unexpected EOF within comment."); */
      break;
      }
   switch ((char)Key)
      {
      case '*' :
         Key=input();
         if (char)Key=='/') return true;
         else               continue;
         break;
      case '\n' :
         ++LineNr;
         break;
      }
   Key=input();
   }

return false;
}

bool SkipLine (void)
{
int Key;

Key=!EOF;
while (true)
   {
   if (Key==EOF)
      return true;
   switch ((char)Key)
      {
      case '\n' :
         unput('\n');
         return true;
         break;
      }
   Key=input();
   }

return false;
}
1 голос
/ 12 февраля 2013

Я нашел это описание грамматики языка Си (на самом деле просто лексер) очень полезным. Я думаю, что это в основном совпадает с ответом Патрика, но немного отличается.

http://www.lysator.liu.se/c/ANSI-C-grammar-l.html

1 голос
/ 10 ноября 2010

Помимо проблемы с %x против %s, у вас также есть проблема, что . в [.\n] соответствует (только) литералу ., а не «любому символу, кроме новой строки», как голый . делает. Вы хотите правило, подобное

<ML_COMMENT>.|"\n"     { /* do nothing */ }

вместо

...