// Этот файл реализует препроцессор языка C. #включить "руси.з" // Параметр макроса типдеф структ MacroParam MacroParam; структ MacroParam { MacroParam *next; символ *name; }; // Аргумент макроса типдеф структ MacroArg MacroArg; структ MacroArg { MacroArg *next; символ *name; бул is_va_args; Token *tok; }; // Тип функции-обработчика встроенного макроса типдеф Token *macro_handler_fn(Token *); // Структура макроса типдеф структ Macro Macro; структ Macro { символ *name; бул is_objlike; MacroParam *params; символ *va_args_name; Token *body; macro_handler_fn *handler; }; // Директива #если может быть вложенной типдеф структ CondIncl CondIncl; структ CondIncl { CondIncl *next; переч { IN_THEN, IN_ELIF, IN_ELSE } ctx; Token *tok; бул included; }; // Набор скрытия типдеф структ Hideset Hideset; структ Hideset { Hideset *next; символ *name; }; // Глобальные переменные препроцессора стат ХешТаблица macro_registry; стат CondIncl *conditional_stack; стат ХешТаблица pragma_once_registry; стат цел include_search_position; // Вынесенные хеш-таблицы для возможности их очистки стат ХешТаблица include_paths_cache; стат ХешТаблица include_guards; // ===================================================================== // Механизм отслеживания выделенной памяти в препроцессоре // ===================================================================== стат пусто **pp_allocs = NULL; стат цел pp_alloc_count = 0; стат цел pp_alloc_capacity = 0; стат пусто *track_pp(пусто *ptr) { если (!ptr) возврат NULL; если (pp_alloc_count == pp_alloc_capacity) { pp_alloc_capacity = pp_alloc_capacity ? pp_alloc_capacity * 2 : 1024; pp_allocs = realloc(pp_allocs, pp_alloc_capacity * размер(пусто *)); } pp_allocs[pp_alloc_count++] = ptr; возврат ptr; } // Функция для полного освобождения памяти, занятой препроцессором. // Вызывайте её в конце каждой итерации теста. пусто free_all_preprocess(пусто) { для (цел i = 0; i < pp_alloc_count; i++) { free(pp_allocs[i]); } free(pp_allocs); pp_allocs = NULL; pp_alloc_count = 0; pp_alloc_capacity = 0; // Сброс глобального состояния conditional_stack = NULL; include_search_position = 0; // Очистка хеш-таблиц (обнуляем структуры, чтобы избежать dangling pointers) macro_registry = (ХешТаблица){0}; pragma_once_registry = (ХешТаблица){0}; include_paths_cache = (ХешТаблица){0}; include_guards = (ХешТаблица){0}; } // ===================================================================== // Прямые объявления функций стат Token *preprocess_internal(Token *tok); стат Macro *lookup_macro(Token *tok); стат бул is_directive_start(Token *tok) { возврат tok->at_bol && equal(tok, "#"); } стат Token *skip_to_end_of_line(Token *tok) { если (tok->at_bol) возврат tok; warn_tok(tok, "лишний токен"); пока (tok->at_bol) tok = tok->next; возврат tok; } стат Token *duplicate_token(Token *tok) { Token *t = track_pp(calloc(1, размер(Token))); *t = *tok; t->next = NULL; возврат t; } стат Token *create_eof_token(Token *tok) { Token *t = duplicate_token(tok); t->kind = TK_EOF; t->len = 0; возврат t; } стат Hideset *create_hideset(символ *name) { Hideset *hs = track_pp(calloc(1, размер(Hideset))); hs->name = name; возврат hs; } стат Hideset *merge_hidesets(Hideset *hs1, Hideset *hs2) { Hideset head = {}; Hideset *cur = &head; для (; hs1; hs1 = hs1->next) cur = cur->next = create_hideset(hs1->name); cur->next = hs2; возврат head.next; } стат бул hideset_has_element(Hideset *hs, символ *s, цел len) { для (; hs; hs = hs->next) если (strlen(hs->name) == len && !strncmp(hs->name, s, len)) возврат истина; возврат ложь; } стат Hideset *intersect_hidesets(Hideset *hs1, Hideset *hs2) { Hideset head = {}; Hideset *cur = &head; для (; hs1; hs1 = hs1->next) если (hideset_has_element(hs2, hs1->name, strlen(hs1->name))) cur = cur->next = create_hideset(hs1->name); возврат head.next; } стат Token *attach_hideset(Token *tok, Hideset *hs) { Token head = {}; Token *cur = &head; для (; tok; tok = tok->next) { Token *t = duplicate_token(tok); t->hideset = merge_hidesets(t->hideset, hs); cur = cur->next = t; } возврат head.next; } стат Token *concatenate_tokens(Token *tok1, Token *tok2) { если (tok1->kind == TK_EOF) возврат tok2; Token head = {}; Token *cur = &head; для (; tok1->kind != TK_EOF; tok1 = tok1->next) cur = cur->next = duplicate_token(tok1); cur->next = tok2; возврат head.next; } стат Token *skip_nested_conditionals(Token *tok) { пока (tok->kind != TK_EOF) { если (is_directive_start(tok) && (equal(tok->next, "if") || equal(tok->next, "ifdef") || equal(tok->next, "ifndef") || equal(tok->next, "если") || equal(tok->next, "еслиопр") || equal(tok->next, "еслинеопр"))) { tok = skip_nested_conditionals(tok->next->next); продолжи; } если (is_directive_start(tok) && (equal(tok->next, "endif") || equal(tok->next, "конецесли"))) возврат tok->next->next; tok = tok->next; } возврат tok; } стат Token *skip_to_conditional_branch(Token *tok) { пока (tok->kind != TK_EOF) { если (is_directive_start(tok) && (equal(tok->next, "if") || equal(tok->next, "ifdef") || equal(tok->next, "ifndef") || equal(tok->next, "если") || equal(tok->next, "еслиопр") || equal(tok->next, "еслинеопр"))) { tok = skip_nested_conditionals(tok->next->next); продолжи; } если (is_directive_start(tok) && (equal(tok->next, "elif") || equal(tok->next, "else") || equal(tok->next, "endif") || equal(tok->next, "конецесли") || equal(tok->next, "иначеесли") || equal(tok->next, "иначе"))) прерви; tok = tok->next; } возврат tok; } стат символ *escape_string_literal(символ *str) { цел bufsize = 3; для (цел i = 0; str[i]; i++) { если (str[i] == '\'' || str[i] == '"') bufsize++; bufsize++; } символ *buf = track_pp(calloc(1, bufsize)); символ *p = buf; *p++ = '"'; для (цел i = 0; str[i]; i++) { если (str[i] == '\'' || str[i] == '"') *p++ = '\\'; *p++ = str[i]; } *p++ = '"'; *p++ = '\0'; возврат buf; } стат Token *create_string_token(символ *str, Token *tmpl) { символ *buf = escape_string_literal(str); возврат tokenize(new_file(tmpl->file->name, tmpl->file->file_no, buf)); } стат Token *extract_line_tokens(Token **rest, Token *tok) { Token head = {}; Token *cur = &head; для (; !tok->at_bol; tok = tok->next) cur = cur->next = duplicate_token(tok); cur->next = create_eof_token(tok); *rest = tok; возврат head.next; } стат Token *create_number_token(цел val, Token *tmpl) { символ *buf = track_pp(формат("%d\n", val)); возврат tokenize(new_file(tmpl->file->name, tmpl->file->file_no, buf)); } стат Token *parse_constant_expression(Token **rest, Token *tok) { tok = extract_line_tokens(rest, tok); Token head = {}; Token *cur = &head; пока (tok->kind != TK_EOF) { если ((equal(tok, "defined") || equal(tok, "определено"))) { Token *start = tok; бул has_paren = consume(&tok, tok->next, "("); если (tok->kind != TK_IDENT) error_tok(start, "имя макроса должно быть идентификатором"); Macro *m = lookup_macro(tok); tok = tok->next; если (has_paren) tok = skip(tok, ")"); cur = cur->next = create_number_token(m ? 1 : 0, start); продолжи; } cur = cur->next = tok; tok = tok->next; } cur->next = tok; возврат head.next; } стат длин evaluate_constant_expression(Token **rest, Token *tok) { Token *start = tok; Token *expr = parse_constant_expression(rest, tok->next); expr = preprocess_internal(expr); если (expr->kind == TK_EOF) error_tok(start, "нет выражения"); для (Token *t = expr; t->kind != TK_EOF; t = t->next) { если (t->kind == TK_IDENT) { Token *next = t->next; *t = *create_number_token(0, t); t->next = next; } } convert_pp_tokens(expr); Token *rest2; длин val = const_expr(&rest2, expr); если (rest2->kind != TK_EOF) error_tok(rest2, "лишний токен"); возврат val; } стат CondIncl *push_conditional_context(Token *tok, бул included) { CondIncl *ci = track_pp(calloc(1, размер(CondIncl))); ci->next = conditional_stack; ci->ctx = IN_THEN; ci->tok = tok; ci->included = included; conditional_stack = ci; возврат ci; } стат Macro *lookup_macro(Token *tok) { если (tok->kind != TK_IDENT) возврат NULL; возврат хеш_получить2(¯o_registry, tok->loc, tok->len); } стат Macro *register_macro(символ *name, бул is_objlike, Token *body) { Macro *m = track_pp(calloc(1, размер(Macro))); m->name = name; m->is_objlike = is_objlike; m->body = body; хеш_вставить(¯o_registry, name, m); возврат m; } стат MacroParam *parse_macro_parameters(Token **rest, Token *tok, символ **va_args_name) { MacroParam head = {}; MacroParam *cur = &head; пока (!equal(tok, ")")) { если (cur != &head) tok = skip(tok, ","); если (equal(tok, "...")) { *va_args_name = "__VA_ARGS__"; *rest = skip(tok->next, ")"); возврат head.next; } если (tok->kind != TK_IDENT) error_tok(tok, "ожидался идентификатор"); если (equal(tok->next, "...")) { *va_args_name = track_pp(strndup(tok->loc, tok->len)); *rest = skip(tok->next->next, ")"); возврат head.next; } MacroParam *m = track_pp(calloc(1, размер(MacroParam))); m->name = track_pp(strndup(tok->loc, tok->len)); cur = cur->next = m; tok = tok->next; } *rest = tok->next; возврат head.next; } стат пусто parse_macro_definition(Token **rest, Token *tok) { если (tok->kind != TK_IDENT) error_tok(tok, "имя макроса должно быть идентификатором"); символ *name = track_pp(strndup(tok->loc, tok->len)); tok = tok->next; если (!tok->has_space && equal(tok, "(")) { символ *va_args_name = NULL; MacroParam *params = parse_macro_parameters(&tok, tok->next, &va_args_name); Macro *m = register_macro(name, ложь, extract_line_tokens(rest, tok)); m->params = params; m->va_args_name = va_args_name; } иначе { register_macro(name, истина, extract_line_tokens(rest, tok)); } } стат MacroArg *parse_single_macro_argument(Token **rest, Token *tok, бул read_rest) { Token head = {}; Token *cur = &head; цел level = 0; для (;;) { если (level == 0 && equal(tok, ")")) прерви; если (level == 0 && !read_rest && equal(tok, ",")) прерви; если (tok->kind == TK_EOF) error_tok(tok, "преждевременный конец ввода"); если (equal(tok, "(")) level++; иначе если (equal(tok, ")")) level--; cur = cur->next = duplicate_token(tok); tok = tok->next; } cur->next = create_eof_token(tok); MacroArg *arg = track_pp(calloc(1, размер(MacroArg))); arg->tok = head.next; *rest = tok; возврат arg; } стат MacroArg *parse_macro_arguments(Token **rest, Token *tok, MacroParam *params, символ *va_args_name) { Token *start = tok; tok = tok->next->next; MacroArg head = {}; MacroArg *cur = &head; MacroParam *pp = params; для (; pp; pp = pp->next) { если (cur != &head) tok = skip(tok, ","); cur = cur->next = parse_single_macro_argument(&tok, tok, ложь); cur->name = pp->name; } если (va_args_name) { MacroArg *arg; если (equal(tok, ")")) { arg = track_pp(calloc(1, размер(MacroArg))); arg->tok = create_eof_token(tok); } иначе { если (pp != params) tok = skip(tok, ","); arg = parse_single_macro_argument(&tok, tok, истина); } arg->name = va_args_name; arg->is_va_args = истина; cur = cur->next = arg; } иначе если (pp) { error_tok(start, "слишком много аргументов"); } skip(tok, ")"); *rest = tok; возврат head.next; } стат MacroArg *lookup_macro_argument(MacroArg *args, Token *tok) { для (MacroArg *ap = args; ap; ap = ap->next) если (tok->len == strlen(ap->name) && !strncmp(tok->loc, ap->name, tok->len)) возврат ap; возврат NULL; } стат символ *merge_token_sequence(Token *tok, Token *end) { цел len = 1; для (Token *t = tok; t != end && t->kind != TK_EOF; t = t->next) { если (t != tok && t->has_space) len++; len += t->len; } символ *buf = track_pp(calloc(1, len)); цел pos = 0; для (Token *t = tok; t != end && t->kind != TK_EOF; t = t->next) { если (t != tok && t->has_space) buf[pos++] = ' '; strncpy(buf + pos, t->loc, t->len); pos += t->len; } buf[pos] = '\0'; возврат buf; } стат Token *convert_to_string_literal(Token *hash, Token *arg) { символ *s = merge_token_sequence(arg, NULL); возврат create_string_token(s, hash); } стат Token *concatenate_adjacent_tokens(Token *lhs, Token *rhs) { символ *buf = track_pp(формат("%.*s%.*s", lhs->len, lhs->loc, rhs->len, rhs->loc)); Token *tok = tokenize(new_file(lhs->file->name, lhs->file->file_no, buf)); если (tok->next->kind != TK_EOF) error_tok(lhs, "склейка образует '%s', недопустимый токен", buf); возврат tok; } стат бул contains_variadic_arguments(MacroArg *args) { для (MacroArg *ap = args; ap; ap = ap->next) если (!strcmp(ap->name, "__VA_ARGS__")) возврат ap->tok->kind != TK_EOF; возврат ложь; } стат Token *substitute_parameters(Token *tok, MacroArg *args) { Token head = {}; Token *cur = &head; пока (tok->kind != TK_EOF) { если (equal(tok, "#")) { MacroArg *arg = lookup_macro_argument(args, tok->next); если (!arg) error_tok(tok->next, "'#' не следует за параметром макроса"); cur = cur->next = convert_to_string_literal(tok, arg->tok); tok = tok->next->next; продолжи; } если (equal(tok, ",") && equal(tok->next, "##")) { MacroArg *arg = lookup_macro_argument(args, tok->next->next); если (arg && arg->is_va_args) { если (arg->tok->kind == TK_EOF) { tok = tok->next->next->next; } иначе { cur = cur->next = duplicate_token(tok); tok = tok->next->next; } продолжи; } } если (equal(tok, "##")) { если (cur == &head) error_tok(tok, "'##' не может быть в начале подстановки макроса"); если (tok->next->kind == TK_EOF) error_tok(tok, "'##' не может быть в конце подстановки макроса"); MacroArg *arg = lookup_macro_argument(args, tok->next); если (arg) { если (arg->tok->kind != TK_EOF) { *cur = *concatenate_adjacent_tokens(cur, arg->tok); для (Token *t = arg->tok->next; t->kind != TK_EOF; t = t->next) cur = cur->next = duplicate_token(t); } tok = tok->next->next; продолжи; } *cur = *concatenate_adjacent_tokens(cur, tok->next); tok = tok->next->next; продолжи; } MacroArg *arg = lookup_macro_argument(args, tok); если (arg && equal(tok->next, "##")) { Token *rhs = tok->next->next; если (arg->tok->kind == TK_EOF) { MacroArg *arg2 = lookup_macro_argument(args, rhs); если (arg2) { для (Token *t = arg2->tok; t->kind != TK_EOF; t = t->next) cur = cur->next = duplicate_token(t); } иначе { cur = cur->next = duplicate_token(rhs); } tok = rhs->next; продолжи; } для (Token *t = arg->tok; t->kind != TK_EOF; t = t->next) cur = cur->next = duplicate_token(t); tok = tok->next; продолжи; } если (equal(tok, "__VA_OPT__") && equal(tok->next, "(")) { MacroArg *arg = parse_single_macro_argument(&tok, tok->next->next, истина); если (contains_variadic_arguments(args)) для (Token *t = arg->tok; t->kind != TK_EOF; t = t->next) cur = cur->next = t; tok = skip(tok, ")"); продолжи; } если (arg) { Token *t = preprocess_internal(arg->tok); t->at_bol = tok->at_bol; t->has_space = tok->has_space; для (; t->kind != TK_EOF; t = t->next) cur = cur->next = duplicate_token(t); tok = tok->next; продолжи; } cur = cur->next = duplicate_token(tok); tok = tok->next; продолжи; } cur->next = tok; возврат head.next; } стат бул perform_macro_expansion(Token **rest, Token *tok) { если (hideset_has_element(tok->hideset, tok->loc, tok->len)) возврат ложь; Macro *m = lookup_macro(tok); если (!m) возврат ложь; если (m->handler) { *rest = m->handler(tok); (*rest)->next = tok->next; возврат истина; } если (m->is_objlike) { Hideset *hs = merge_hidesets(tok->hideset, create_hideset(m->name)); Token *body = attach_hideset(m->body, hs); для (Token *t = body; t->kind != TK_EOF; t = t->next) t->origin = tok; *rest = concatenate_tokens(body, tok->next); (*rest)->at_bol = tok->at_bol; (*rest)->has_space = tok->has_space; возврат истина; } если (!equal(tok->next, "(")) возврат ложь; Token *macro_token = tok; MacroArg *args = parse_macro_arguments(&tok, tok, m->params, m->va_args_name); Token *rparen = tok; Hideset *hs = intersect_hidesets(macro_token->hideset, rparen->hideset); hs = merge_hidesets(hs, create_hideset(m->name)); Token *body = substitute_parameters(m->body, args); body = attach_hideset(body, hs); для (Token *t = body; t->kind != TK_EOF; t = t->next) t->origin = macro_token; *rest = concatenate_tokens(body, tok->next); (*rest)->at_bol = macro_token->at_bol; (*rest)->has_space = macro_token->has_space; возврат истина; } символ *search_include_paths(символ *filename) { если (filename[0] == '/') возврат filename; символ *cached = хеш_получить(&include_paths_cache, filename); если (cached) возврат cached; для (цел i = 0; i < include_paths.len; i++) { символ *path = track_pp(формат("%s/%s", include_paths.data[i], filename)); если (!file_exists(path)) продолжи; хеш_вставить(&include_paths_cache, filename, path); include_search_position = i + 1; возврат path; } возврат NULL; } стат символ *find_next_include_path(символ *filename) { для (; include_search_position < include_paths.len; include_search_position++) { символ *path = track_pp(формат("%s/%s", include_paths.data[include_search_position], filename)); если (file_exists(path)) возврат path; } возврат NULL; } стат символ *extract_include_filename(Token **rest, Token *tok, бул *is_dquote) { если (tok->kind == TK_STR) { *is_dquote = истина; *rest = skip_to_end_of_line(tok->next); возврат track_pp(strndup(tok->loc + 1, tok->len - 2)); } если (equal(tok, "<")) { Token *start = tok; для (; !equal(tok, ">"); tok = tok->next) если (tok->at_bol || tok->kind == TK_EOF) error_tok(tok, "ожидался '>'"); *is_dquote = ложь; *rest = skip_to_end_of_line(tok->next); возврат merge_token_sequence(start->next, tok); } если (tok->kind == TK_IDENT) { Token *tok2 = preprocess_internal(extract_line_tokens(rest, tok)); возврат extract_include_filename(&tok2, tok2, is_dquote); } error_tok(tok, "ожидалось имя файла"); } стат символ *identify_include_guard(Token *tok) { если (!is_directive_start(tok) || !(equal(tok->next, "ifndef") || equal(tok->next, "еслинеопр"))) возврат NULL; tok = tok->next->next; если (tok->kind != TK_IDENT) возврат NULL; символ *macro = track_pp(strndup(tok->loc, tok->len)); tok = tok->next; если (!is_directive_start(tok) || !(equal(tok->next, "define") || equal(tok->next, "определить")) || !(equal(tok->next->next, macro))) возврат NULL; пока (tok->kind != TK_EOF) { если (!is_directive_start(tok)) { tok = tok->next; продолжи; } если ((equal(tok->next, "endif") || equal(tok->next, "конецесли")) && tok->next->next->kind == TK_EOF) возврат macro; если (equal(tok, "if") || equal(tok->next, "ifdef") || equal(tok->next, "ifndef") || equal(tok, "если") || equal(tok->next, "еслиопр") || equal(tok->next, "еслинеопр")) tok = skip_to_conditional_branch(tok->next); иначе tok = tok->next; } возврат NULL; } стат Token *process_include_directive(Token *tok, символ *path, Token *filename_tok) { если (хеш_получить(&pragma_once_registry, path)) возврат tok; символ *guard_name = хеш_получить(&include_guards, path); если (guard_name && хеш_получить(¯o_registry, guard_name)) возврат tok; Token *tok2 = tokenize_file(path); если (!tok2) error_tok(filename_tok, "%s: не удалось открыть файл: %s", path, strerror(errno)); guard_name = identify_include_guard(tok2); если (guard_name) хеш_вставить(&include_guards, path, guard_name); возврат concatenate_tokens(tok2, tok); } стат пусто parse_line_directive(Token **rest, Token *tok) { Token *start = tok; tok = preprocess(extract_line_tokens(rest, tok)); если (tok->kind != TK_NUM || tok->ty->kind != TY_INT) error_tok(tok, "недопустимый маркер строки"); start->file->line_delta = tok->val - start->line_no; tok = tok->next; если (tok->kind == TK_EOF) возврат; если (tok->kind != TK_STR) error_tok(tok, "ожидалось имя файла"); start->file->display_name = tok->str; } стат Token *preprocess_internal(Token *tok) { Token head = {}; Token *cur = &head; пока (tok->kind != TK_EOF) { если (perform_macro_expansion(&tok, tok)) продолжи; если (!is_directive_start(tok)) { tok->line_delta = tok->file->line_delta; tok->filename = tok->file->display_name; cur = cur->next = tok; tok = tok->next; продолжи; } Token *start = tok; tok = tok->next; если ((equal(tok, "include") || equal(tok, "включить"))) { бул is_dquote; символ *filename = extract_include_filename(&tok, tok->next, &is_dquote); если (filename[0] != '/' && is_dquote) { символ *dir = track_pp(strdup(start->file->name)); символ *path = track_pp(формат("%s/%s", dirname(dir), filename)); если (file_exists(path)) { tok = process_include_directive(tok, path, start->next->next); продолжи; } } символ *path = search_include_paths(filename); tok = process_include_directive(tok, path ? path : filename, start->next->next); продолжи; } если ((equal(tok, "include_next") || equal(tok, "включить_след"))) { бул ignore; символ *filename = extract_include_filename(&tok, tok->next, &ignore); символ *path = find_next_include_path(filename); tok = process_include_directive(tok, path ? path : filename, start->next->next); продолжи; } если ((equal(tok, "define") || equal(tok, "определить"))) { parse_macro_definition(&tok, tok->next); продолжи; } если ((equal(tok, "undef") || equal(tok, "отменить"))) { tok = tok->next; если (tok->kind != TK_IDENT) error_tok(tok, "имя макроса должно быть идентификатором"); undef_macro(track_pp(strndup(tok->loc, tok->len))); tok = skip_to_end_of_line(tok->next); продолжи; } если ((equal(tok, "if") || equal(tok, "если"))) { длин val = evaluate_constant_expression(&tok, tok); push_conditional_context(start, val); если (!val) tok = skip_to_conditional_branch(tok); продолжи; } если ((equal(tok, "ifdef") || equal(tok, "еслиопр"))) { бул defined = lookup_macro(tok->next); push_conditional_context(tok, defined); tok = skip_to_end_of_line(tok->next->next); если (!defined) tok = skip_to_conditional_branch(tok); продолжи; } если ((equal(tok, "ifndef") || equal(tok, "еслинеопр"))) { бул defined = lookup_macro(tok->next); push_conditional_context(tok, !defined); tok = skip_to_end_of_line(tok->next->next); если (defined) tok = skip_to_conditional_branch(tok); продолжи; } если ((equal(tok, "elif") || equal(tok, "иначеесли"))) { если (!conditional_stack || conditional_stack->ctx == IN_ELSE) error_tok(start, "#elif не к месту"); conditional_stack->ctx = IN_ELIF; если (!conditional_stack->included && evaluate_constant_expression(&tok, tok)) conditional_stack->included = истина; иначе tok = skip_to_conditional_branch(tok); продолжи; } если ((equal(tok, "else") || equal(tok, "иначе"))) { если (!conditional_stack || conditional_stack->ctx == IN_ELSE) error_tok(start, "#else не к месту"); conditional_stack->ctx = IN_ELSE; tok = skip_to_end_of_line(tok->next); если (conditional_stack->included) tok = skip_to_conditional_branch(tok); продолжи; } если ((equal(tok, "endif") || equal(tok, "конецесли"))) { если (!conditional_stack) error_tok(start, "#endif не к месту"); conditional_stack = conditional_stack->next; tok = skip_to_end_of_line(tok->next); продолжи; } если ((equal(tok, "line") || equal(tok, "строка"))) { parse_line_directive(&tok, tok->next); продолжи; } если (tok->kind == TK_PP_NUM) { parse_line_directive(&tok, tok); продолжи; } если ((equal(tok, "pragma") || equal(tok, "прагма")) && (equal(tok->next, "once") || equal(tok->next, "однократно"))) { хеш_вставить(&pragma_once_registry, tok->file->name, (пусто *)1); tok = skip_to_end_of_line(tok->next->next); продолжи; } если ((equal(tok, "pragma") || equal(tok, "прагма"))) { делай { tok = tok->next; } пока (!tok->at_bol); продолжи; } если ((equal(tok, "error") || equal(tok, "ошибка"))) error_tok(tok, "ошибка"); если (tok->at_bol) продолжи; error_tok(tok, "недопустимая директива препроцессора"); } cur->next = tok; возврат head.next; } пусто define_macro(символ *name, символ *buf) { Token *tok = tokenize(new_file("", 1, buf)); register_macro(name, истина, tok); } пусто undef_macro(символ *name) { хеш_удалить(¯o_registry, name); } стат Macro *register_builtin_macro(символ *name, macro_handler_fn *fn) { Macro *m = register_macro(name, истина, NULL); m->handler = fn; возврат m; } стат Token *expand_file_macro(Token *tmpl) { пока (tmpl->origin) tmpl = tmpl->origin; возврат create_string_token(tmpl->file->display_name, tmpl); } стат Token *expand_line_macro(Token *tmpl) { пока (tmpl->origin) tmpl = tmpl->origin; цел i = tmpl->line_no + tmpl->file->line_delta; возврат create_number_token(i, tmpl); } стат Token *expand_counter_macro(Token *tmpl) { стат цел i = 0; возврат create_number_token(i++, tmpl); } стат Token *expand_timestamp_macro(Token *tmpl) { структ stat st; если (stat(tmpl->file->name, &st) != 0) возврат create_string_token("??? ??? ?? ??:??:?? ????", tmpl); символ buf[30]; ctime_r(&st.st_mtime, buf); buf[24] = '\0'; возврат create_string_token(buf, tmpl); } стат Token *expand_base_file_macro(Token *tmpl) { возврат create_string_token(base_file, tmpl); } стат символ *format_date_string(структ tm *tm) { стат символ mon[][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", }; возврат track_pp(формат("\"%s %2d %d\"", mon[tm->tm_mon], tm->tm_mday, tm->tm_year + 1900)); } стат символ *format_time_string(структ tm *tm) { возврат track_pp(формат("\"%02d:%02d:%02d\"", tm->tm_hour, tm->tm_min, tm->tm_sec)); } // Инициализация предопределённых макросов пусто init_macros(пусто) { // Определение предопределённых макросов define_macro("_LP64", "1"); define_macro("__C99_MACRO_WITH_VA_ARGS", "1"); define_macro("__ELF__", "1"); define_macro("__LP64__", "1"); define_macro("__SIZEOF_DOUBLE__", "8"); define_macro("__SIZEOF_FLOAT__", "4"); define_macro("__SIZEOF_INT__", "4"); define_macro("__SIZEOF_LONG_DOUBLE__", "8"); define_macro("__SIZEOF_LONG_LONG__", "8"); define_macro("__SIZEOF_LONG__", "8"); define_macro("__SIZEOF_POINTER__", "8"); define_macro("__SIZEOF_PTRDIFF_T__", "8"); define_macro("__SIZEOF_SHORT__", "2"); define_macro("__SIZEOF_SIZE_T__", "8"); define_macro("__SIZE_TYPE__", "unsigned long"); define_macro("__STDC_HOSTED__", "1"); define_macro("__STDC_NO_COMPLEX__", "1"); define_macro("__STDC_UTF_16__", "1"); define_macro("__STDC_UTF_32__", "1"); define_macro("__STDC_VERSION__", "201112L"); define_macro("__STDC__", "1"); define_macro("__USER_LABEL_PREFIX__", ""); define_macro("__alignof__", "_Alignof"); define_macro("__amd64", "1"); define_macro("__amd64__", "1"); define_macro("__rusi__", "1"); define_macro("__const__", "const"); define_macro("__gnu_linux__", "1"); define_macro("__inline__", "inline"); define_macro("__linux", "1"); define_macro("__linux__", "1"); define_macro("__signed__", "signed"); define_macro("__typeof__", "typeof"); define_macro("__unix", "1"); define_macro("__unix__", "1"); define_macro("__volatile__", "volatile"); define_macro("__x86_64", "1"); define_macro("__x86_64__", "1"); define_macro("linux", "1"); define_macro("unix", "1"); register_builtin_macro("__FILE__", expand_file_macro); register_builtin_macro("__LINE__", expand_line_macro); register_builtin_macro("__COUNTER__", expand_counter_macro); register_builtin_macro("__TIMESTAMP__", expand_timestamp_macro); register_builtin_macro("__BASE_FILE__", expand_base_file_macro); register_builtin_macro("__ФАЙЛ__", expand_file_macro); register_builtin_macro("__ЛИНИЯ__", expand_line_macro); register_builtin_macro("__СЧЕТЧИК__", expand_counter_macro); register_builtin_macro("__ВРЕМЯ_ФАЙЛА__", expand_timestamp_macro); register_builtin_macro("__БАЗОВЫЙ_ФАЙЛ__", expand_base_file_macro); time_t now = time(NULL); структ tm *tm = localtime(&now); define_macro("__DATE__", format_date_string(tm)); define_macro("__TIME__", format_time_string(tm)); define_macro("__ДАТА__", format_date_string(tm)); define_macro("__ВРЕМЯ__", format_time_string(tm)); } типдеф переч { STR_NONE, STR_UTF8, STR_UTF16, STR_UTF32, STR_WIDE, } StringKind; стат StringKind determine_string_encoding(Token *tok) { если (!strcmp(tok->loc, "u8")) возврат STR_UTF8; выбор (tok->loc[0]) { случай '"': возврат STR_NONE; случай 'u': возврат STR_UTF16; случай 'U': возврат STR_UTF32; случай 'L': возврат STR_WIDE; } unreachable(); } стат пусто merge_consecutive_strings(Token *tok) { для (Token *tok1 = tok; tok1->kind != TK_EOF;) { если (tok1->kind != TK_STR || tok1->next->kind != TK_STR) { tok1 = tok1->next; продолжи; } StringKind kind = determine_string_encoding(tok1); Type *basety = tok1->ty->base; для (Token *t = tok1->next; t->kind == TK_STR; t = t->next) { StringKind k = determine_string_encoding(t); если (kind == STR_NONE) { kind = k; basety = t->ty->base; } иначе если (k != STR_NONE && kind != k) { error_tok(t, "неподдерживаемая нестандартная конкатенация строковых литералов"); } } если (basety->size > 1) для (Token *t = tok1; t->kind == TK_STR; t = t->next) если (t->ty->base->size == 1) *t = *tokenize_string_literal(t, basety); пока (tok1->kind == TK_STR) tok1 = tok1->next; } для (Token *tok1 = tok; tok1->kind != TK_EOF;) { если (tok1->kind != TK_STR || tok1->next->kind != TK_STR) { tok1 = tok1->next; продолжи; } Token *tok2 = tok1->next; пока (tok2->kind == TK_STR) tok2 = tok2->next; цел len = tok1->ty->array_len; для (Token *t = tok1->next; t != tok2; t = t->next) len = len + t->ty->array_len - 1; символ *buf = track_pp(calloc(tok1->ty->base->size, len)); цел i = 0; для (Token *t = tok1; t != tok2; t = t->next) { memcpy(buf + i, t->str, t->ty->size); i = i + t->ty->size - t->ty->base->size; } *tok1 = *duplicate_token(tok1); tok1->ty = array_of(tok1->ty->base, len); tok1->str = buf; tok1->next = tok2; tok1 = tok2; } } Token *preprocess(Token *tok) { tok = preprocess_internal(tok); если (conditional_stack) error_tok(conditional_stack->tok, "незавершённая условная директива"); convert_pp_tokens(tok); merge_consecutive_strings(tok); для (Token *t = tok; t; t = t->next) t->line_no += t->line_delta; возврат tok; }