rusi/preprocess.c

1037 lines
39 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Этот файл реализует препроцессор языка 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(&macro_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;
хешставить(&macro_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 && хеш_получить(&macro_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("<built-in>", 1, buf));
register_macro(name, истина, tok);
}
пусто undef_macro(символ *name) {
хеш_удалить(&macro_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;
}