2013-06-19 1 views
3

Я недавно писал парсер для языка на основе 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

ответ

1

Я не могу говорить за конкретный код.

Но основной трюк заключается в том, что C lexer проверяет каждый IDENTIFIER и решает, может ли быть имя typedef. Если это так, то он изменяет тип lexeme на TYPEDEF и передает его парсеру.

Как лексер должен знать, какие идентификаторы являются typedefs? Синтаксический анализатор должен, по сути, сказать об этом, захватив информацию typedef по мере ее запуска. Где-то в грамматике, связанной с декларациями, должно быть предпринято действие по предоставлению этой информации. Я бы предположил, что он будет привязан к правилам грамматики для объявлений типа typedef.

Вы не показали, что сделал «setspec»; возможно, это место. Общий трюк, используемый с генераторами парсеров LR, заключается в том, чтобы ввести правило грамматики E с пустой правой рукой (ваш пример «setspec»?), Который будет вызываться в середине какого-либо другого правила грамматики (ваш пример «fndef»), чтобы включить доступ к семантическому действию в середине обработки этого правила.

Весь этот трюк состоит в том, чтобы обойти неоднозначность парсинга, если вы не можете указать typedefs из других идентификаторов. Если ваш синтаксический анализатор допускает двусмысленность, вам вообще не нужен этот хак; просто разобрать и построить АСТ с обоими (суб) анализами. После приобретения АСТ, древовидная прогулка может найти информацию о типе и устранить непоследовательные подпараметры. Мы делаем это с помощью GLR для C и C++, и он прекрасно отделяет синтаксический анализ от разрешения имен.

+0

Вы правы, но в этом случае происходит что-то другое. Вы можете прочитать определение 'setspec' в ссылке выше фрагмента кода. Я выглядел немного глубже в этом коде. 'declspecs_ts' содержит ТОЧНО ОДИН' TYPENAME' и часто некоторые другие спецификаторы (такие как 'INLINE' и т. д.). Существуют также другие варианты, такие как 'declspecs_nots', и мы должны комбинировать разные' declspecs' с 'after_type_declarator' и' notype_declaraor' для поддержки всех возможных комбинаций. Это еще более сложно (из-за атрибутов), тем не менее он организован/разделен достаточно умным, чтобы предотвратить конфликты. – Grzes

+1

Добро пожаловать в производственные грамматики, которые имеют не только «стандартные» конструкторы langauge, но и все другие барахлы, которые специфический компилятор/диалект (например, GCC, MS, GreenHills) бросаются в кучу. Когда вы добавляете все расширения, которые делают люди, грамматики, как правило, перестают быть простыми. Если вам повезет, грамматика по-прежнему «организована/расколота достаточно умна» для управления. Анализаторы GLR на самом деле делают это для больших сложных грамматик довольно легко, потому что не нужно запутывать такие вещи, как TYPEDEF, проверяя собственно грамматику. –

+0

Вот как я это вижу: 'declspecs_ts after_type_declaration' для случая, когда имяТип будет повторно объявлен в качестве переменного (возможно, некоторых других typename2), например: ' ЬурейиЙ INT myType1; typedef float myType2; {myType1 myType2;/* variable myType2 (name) типа myType1 * /} ' ' declspecs_nots notype_declaration' для простого 'int foo;' – Grzes