Я недавно писал парсер для языка на основе C. Я использую CUP (Yacc для Java).Как проблема с идентификатором typedef-name - identifier разрешена в C?
Я хочу реализовать «лексером хак» (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 где «был ли видел спецификатор типа, после того, как спецификатор типа, имя ЬурейеЕ является идентификатором для переобъявить (_ts или _nots).»
От declspecs_ts мы можем достичь
typespec_nonreserved_nonattr:
TYPENAME
...
;
На первый взгляд, я не могу поверить, как сдвиг/свёртка не появляется! setspecs
пуст, поэтому у нас есть declspecs_ts
, за которым следует declarator
, так что мы можем ожидать, что парсер следует смутить, является ли TYPENAME
от declspecs_ts
или от declarator
.
Может кто-нибудь объяснить это кратко (или даже точно). Заранее спасибо!
EDIT: Полезные ссылки: http://www.gnu.org/software/bison/manual/bison.html#Semantic-Tokens
Вы правы, но в этом случае происходит что-то другое. Вы можете прочитать определение 'setspec' в ссылке выше фрагмента кода. Я выглядел немного глубже в этом коде. 'declspecs_ts' содержит ТОЧНО ОДИН' TYPENAME' и часто некоторые другие спецификаторы (такие как 'INLINE' и т. д.). Существуют также другие варианты, такие как 'declspecs_nots', и мы должны комбинировать разные' declspecs' с 'after_type_declarator' и' notype_declaraor' для поддержки всех возможных комбинаций. Это еще более сложно (из-за атрибутов), тем не менее он организован/разделен достаточно умным, чтобы предотвратить конфликты. – Grzes
Добро пожаловать в производственные грамматики, которые имеют не только «стандартные» конструкторы langauge, но и все другие барахлы, которые специфический компилятор/диалект (например, GCC, MS, GreenHills) бросаются в кучу. Когда вы добавляете все расширения, которые делают люди, грамматики, как правило, перестают быть простыми. Если вам повезет, грамматика по-прежнему «организована/расколота достаточно умна» для управления. Анализаторы GLR на самом деле делают это для больших сложных грамматик довольно легко, потому что не нужно запутывать такие вещи, как TYPEDEF, проверяя собственно грамматику. –
Вот как я это вижу: 'declspecs_ts after_type_declaration' для случая, когда имяТип будет повторно объявлен в качестве переменного (возможно, некоторых других typename2), например: ' ЬурейиЙ INT myType1; typedef float myType2; {myType1 myType2;/* variable myType2 (name) типа myType1 * /} ' ' declspecs_nots notype_declaration' для простого 'int foo;' – Grzes