Недавно я писал парсер для языка на основе C. Я использую CUP (Yacc для Java).
Я хочу реализовать «взлом лексера» (http://eli.thegreenplace.net/2011/05/02/the-context-sensitivity-of-c%E2%80%99s-grammar-revisited/ или https://en.wikipedia.org/wiki/The_lexer_hack), чтобы различать имена typedef и переменные/ имена функций и т. д. Чтобы разрешить объявление переменных с тем же именем, что и объявленный ранее тип (пример из первой ссылки):
typedef int AA;
void foo() {
AA aa; /* OK - define variable aa of type AA */
float AA; /* OK - define variable AA of type float */
}
мы должны ввести несколько новых производств, где имя переменной/функции может быть либо IDENTIFIER
, либо TYPENAME
. И вот тут-то и возникают трудности — конфликты в грамматике.
Я пытался не использовать эту запутанную грамматику Yacc для gcc 3.4 (http://yaxx.googlecode.com/svn-history/r2/trunk/gcc-3.4.0/gcc/c-parse.y), но на этот раз у меня нет идея, как разрешать конфликты самостоятельно. Я взглянул на грамматику Yacc:
declarator:
after_type_declarator
| notype_declarator
;
after_type_declarator:
...
| TYPENAME
;
notype_declarator:
...
| IDENTIFIER
;
fndef:
declspecs_ts setspecs declarator
// some action code
// the rest of production
...
setspecs: /* empty */
// some action code
declspecs_ts
означает Declaration_specifiers, где "был ли замечен спецификатор типа; после спецификатора типа имя typedef является идентификатором для повторного объявления (_ts или _nots)".
Из declspecs_ts мы можем добраться
typespec_nonreserved_nonattr:
TYPENAME
...
;
На первый взгляд я не могу поверить, как не возникают конфликты сдвига/уменьшения! setspecs
пусто, поэтому у нас есть declspecs_ts
, за которым следует declarator
, так что мы можем ожидать, что синтаксический анализатор должен запутаться, происходит ли TYPENAME
из declspecs_ts
или из declarator
.
Может ли кто-нибудь объяснить это кратко (или даже точно). Заранее спасибо!
РЕДАКТИРОВАТЬ: полезная ссылка: http://www.gnu.org/software/bison/manual/bison.html#Semantic-Tokens