Как typedef-name - проблема идентификатора решена в C?

Недавно я писал парсер для языка, основанного на 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 означает объявление_спецификаторов, где "виден ли спецификатор типа; после спецификатора типа имя определения типа является идентификатором для повторного объявления (_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

1 ответ

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

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

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

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

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

Другие вопросы по тегам