Ответ 1
Я думаю, вам нужно объявить условие запуска ML_COMMENT как исключительное условие запуска, поэтому активны только правила ML_COMMENT. %x ML_COMMENT
вместо %s ML_COMMENT
В противном случае также действуют правила без начальных условий.
Я пытаюсь проанализировать многострочные комментарии 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; он не дает мне никаких предупреждений о правилах состояния, которые я пытаюсь использовать.
Я думаю, вам нужно объявить условие запуска ML_COMMENT как исключительное условие запуска, поэтому активны только правила ML_COMMENT. %x ML_COMMENT
вместо %s ML_COMMENT
В противном случае также действуют правила без начальных условий.
Анализ комментариев таким образом может привести к ошибкам, потому что:
В моем парсере я обрабатываю такие комментарии. Сначала определите правила 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;
}
Помимо проблемы с %x
vs %s
у вас также возникает проблема, заключающаяся в том, что .
in [.\n]
соответствует (только) литералу .
, а не "любому символу, отличному от новой строки", .
делает. Вам нужно правило вроде
<ML_COMMENT>.|"\n" { /* do nothing */ }
вместо
Я нашел это описание грамматики языка C (фактически только лексера) очень полезным. Я думаю, что это в основном то же самое, что и Патрик, но немного другое.