1037 lines
39 KiB
C
1037 lines
39 KiB
C
|
|
// Этот файл реализует препроцессор языка 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("<built-in>", 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;
|
|||
|
|
}
|