Почему многострочные комментарии в flex/bison настолько уклончивы?

Я пытаюсь проанализировать многострочные комментарии 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;". Если я вхожу в "a" в свою собственную строку, он все равно выдает ошибку.

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

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

$ ./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; он не дает мне никаких предупреждений о правилах состояния, которые я пытаюсь использовать.

Ответы

Ответ 1

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

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

Ответ 2

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

  • вам нужно добавить условия ко всем вашим правилам lex.
  • он становится еще более сложным, если вы также хотите обрабатывать//комментарии
  • у вас все еще есть риск того, что 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;
}

Ответ 3

Помимо проблемы с %x vs %s у вас также возникает проблема, заключающаяся в том, что . in [.\n] соответствует (только) литералу ., а не "любому символу, отличному от новой строки", . делает. Вам нужно правило вроде

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

вместо

Ответ 4

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

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