Как 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++, и он прекрасно отделяет разбор от разрешения имен.