Разбор параметра int (x)

Вот простая функция с одним параметром int:

void f(int x) {}

f(42);

И вот еще одна функция с одним параметром int:

void g(int(x)) {}

g(42);

Теперь определим x как тип:

typedef int x;
void h(int(x)) {}

h(42);
// warning: passing argument 1 of ‘h’ makes pointer from integer without a cast

(Это поведение, которое я наблюдаю с помощью gcc 4.8.2)

Как писатели-парсеры справляются с этой ситуацией?

Кажется, классический трубопровод Lexer → Parser → Semantic Checker → ... здесь не работает.

Ответ 1

Вы эффективно определили h как:

void h(int(int)) {}

Параметр интерпретируется как неназванный указатель функции, который принимает int и возвращает int. Когда вы пытаетесь передать ему 42, компилятор жалуется, что вы пытаетесь сделать указатель на функцию из целого числа.

Я думаю, что вы просите, как компиляторы обрабатывают (неназванные) типы указателей функций и их, возможно, двусмысленные парсы. Ваш вопрос связан с самым досадным анализом на С++.

Там они решили, что всякий раз, когда возникает неоднозначность между типом указателя функции и другим способом анализа, он интерпретируется как указатель на функцию. Они сделали это, потому что есть другие способы устранения неоднозначности, если вы не хотите, чтобы он был указателем на функцию (например, - окружал его в круглых скобках, использовал {} синтаксис инициализатора и т.д.).

Вникая в специфику того, как автор синтаксического анализа может справиться с этим анализом, здесь лексический анализатор и грамматика для C11: http://quut.com/c/ANSI-C-grammar-l-2011.html В ваш пример, до typedef, x будет токеном IDENTIFIER, а после, это будет токен TYPEDEF_NAME, потому что анализатор информируется через таблицу символов, что x теперь является типом. В этом конкретном случае разбор недвусмыслен. "Обратная связь с конвейером", о которой вы, похоже, ссылаетесь, происходит через таблицу символов в этом случае, когда лексический анализатор информируется о контексте более высокими уровнями, которые влияют на его вывод по мере продвижения компиляции.

EDIT: Эти три статьи, найденные OP, описывают эту проблему и как она решается некоторыми C-синтаксическими анализаторами/компиляторами очень красиво. В принципе, контекстная свободная грамматика (CFG), которая только принимает/генерирует легальный синтаксис C, может быть почти указана. С появлением таблицы поиска по областям, которая позволяет лексическому анализатору правильно различать идентификаторы и имена typedef, тогда CFG [и, что более важно, LALR (1) parser (например, - yacc generated)], который принимает или генерирует легальный C может быть указан синтаксис.

Здесь еще более страшный пример, чем OP:

typedef int x;

int main() { x x = 5; return x; }  /* crazily enough this is legal C syntax and a well formed C program */

Ответ 2

После введения typedef

typedef int x;

функция имеет следующее определение

void h(int( int ) ) {}

то есть его параметр объявлен как имеющий тип функции int( int ), который настроен на указатель на функцию.

Вы вызываете функцию, подающую целое число:

h(42);

Нет никакого неявного преобразования из целого числа в указатель функции.

Я не вижу проблемы с

Кажется, классический трубопровод Lexer → Parser → Semantic Checker → ... здесь не работает.

Параметр заменяется на typedef.
x имеет атрибут компилятора типа. Поэтому он считает запись вроде

type-specifier h(type-specifier( type-name ) ) {}