#включить "руси.з" // Текущий обрабатываемый файл стат File *акт_файл; // Список всех входных файлов стат File **все_вх_файлы; стат цел ёмкость_вх_файлов = 0; стат цел счётчик_файлов = 0; // Флаг: текущая позиция находится в начале строки стат бул начало_строки; // Флаг: перед текущей позицией был пробельный символ стат бул был_пробел; // ===================================================================== // Механизм отслеживания выделенной памяти для токенов и файлов // ===================================================================== стат пусто **все_выделения = NULL; стат цел счётчик_выделений = 0; стат цел ёмкость_выделений = 0; стат пусто отследить_память(пусто *указ) { если (!указ) возврат; если (счётчик_выделений == ёмкость_выделений) { ёмкость_выделений = ёмкость_выделений ? ёмкость_выделений * 2 : 1024; все_выделения = realloc(все_выделения, ёмкость_выделений * размер(пусто *)); } все_выделения[счётчик_выделений++] = указ; } пусто free_all_tokens(пусто) { для (цел ш = 0; ш < счётчик_выделений; ш++) { free(все_выделения[ш]); } free(все_выделения); все_выделения = NULL; счётчик_выделений = 0; ёмкость_выделений = 0; free(все_вх_файлы); все_вх_файлы = NULL; ёмкость_вх_файлов = 0; счётчик_файлов = 0; } // ===================================================================== пусто error(символ *фмт, ...) { va_list арг; va_start(арг, фмт); vfprintf(stderr, фмт, арг); fprintf(stderr, "\n"); exit(1); } стат пусто формат_ошибки_в(символ *имя_файла, символ *исходник, цел номер_строки, символ *позиция, символ *фмт, va_list арг) { символ *нач_строки = позиция; пока (исходник < нач_строки && нач_строки[-1] != '\n') нач_строки--; символ *кон_строки = позиция; пока (*кон_строки && *кон_строки != '\n') кон_строки++; цел отступ = fprintf(stderr, "%s:%d: ", имя_файла, номер_строки); fprintf(stderr, "%.*s\n", (цел)(кон_строки - нач_строки), нач_строки); цел поз = display_width(нач_строки, позиция - нач_строки) + отступ; fprintf(stderr, "%*s", поз, ""); fprintf(stderr, "^ "); vfprintf(stderr, фмт, арг); fprintf(stderr, "\n"); } пусто error_at(символ *поз, символ *фмт, ...) { цел номер_строки = 1; для (символ *п = акт_файл->contents; п < поз; п++) если (*п == '\n') номер_строки++; va_list арг; va_start(арг, фмт); формат_ошибки_в(акт_файл->name, акт_файл->contents, номер_строки, поз, фмт, арг); exit(1); } пусто error_tok(Token *ток, символ *фмт, ...) { va_list арг; va_start(арг, фмт); формат_ошибки_в(ток->file->name, ток->file->contents, ток->line_no, ток->loc, фмт, арг); exit(1); } пусто warn_tok(Token *ток, символ *фмт, ...) { va_list арг; va_start(арг, фмт); формат_ошибки_в(ток->file->name, ток->file->contents, ток->line_no, ток->loc, фмт, арг); va_end(арг); } бул equal(Token *ток, символ *оп) { возврат memcmp(ток->loc, оп, ток->len) == 0 && оп[ток->len] == '\0'; } Token *skip(Token *ток, символ *оп) { если (!equal(ток, оп)) error_tok(ток, "ожидался '%s'", оп); возврат ток->next; } бул consume(Token **остаток, Token *ток, символ *стр) { если (equal(ток, стр)) { *остаток = ток->next; возврат истина; } *остаток = ток; возврат ложь; } стат Token *создать_токен(TokenKind вид, символ *начало, символ *конец) { Token *ток = calloc(1, размер(Token)); отследить_память(ток); ток->kind = вид; ток->loc = начало; ток->len = конец - начало; ток->file = акт_файл; ток->filename = акт_файл->display_name; ток->at_bol = начало_строки; ток->has_space = был_пробел; начало_строки = был_пробел = ложь; возврат ток; } стат бул есть_префикс(символ *стр, символ *преф) { возврат strncmp(стр, преф, strlen(преф)) == 0; } стат цел разбор_идентификатора(символ *начало) { символ *п = начало; uint32_t зн = decode_utf8(&п, п); если (!is_ident1(зн)) возврат 0; для (;;) { символ *след; зн = decode_utf8(&след, п); если (!is_ident2(зн)) возврат п - начало; п = след; } } стат цел знач_шестн_цифры(символ зн) { если ('0' <= зн && зн <= '9') возврат зн - '0'; если ('a' <= зн && зн <= 'f') возврат зн - 'a' + 10; возврат зн - 'A' + 10; } стат цел разбор_пунктуатора(символ *п) { стат символ *операторы[] = { "<<=", ">>=", "...", "==", "!=", "<=", ">=", "->", "+=", "-=", "*=", "/=", "++", "--", "%=", "&=", "|=", "^=", "&&", "||", "<<", ">>", "##", }; для (цел ш = 0; ш < размер(операторы) / размер(*операторы); ш++) если (есть_префикс(п, операторы[ш])) возврат strlen(операторы[ш]); возврат ispunct(*п) ? 1 : 0; } стат бул зарезервированное_слово(Token *ток) { стат ХешТаблица карта; если (карта.вместимость == 0) { стат символ *ключ_слова[] = { "return", "if", "else", "for", "while", "int", "sizeof", "char", "возврат", "если", "иначе", "для", "пока", "цел", "размер", "символ", "struct", "union", "short", "long", "void", "typedef", "_Bool", "структ", "объед", "корот", "длин", "пусто", "типдеф", "enum", "static", "goto", "break", "continue", "switch", "case", "переч", "стат", "перейти", "прерви", "продолжи", "выбор", "случай", "default", "extern", "_Alignof", "_Выравнивание_типа", "_Alignas", "_Выравнивание", "do", "signed", "_Generic", "_Обобщ", "умолч", "внеш", "делай", "знак", "unsigned", "const", "volatile", "auto", "register", "restrict", "беззнак", "конст", "летуч", "авто", "регистр", "огранич", "__restrict", "__restrict__", "_Noreturn", "_Безвозврат", "float", "double", "плав", "двойн", "typeof", "asm", "_Thread_local", "__thread", "_Atomic", "типоф", "асм", "__attribute__", }; для (цел ш = 0; ш < размер(ключ_слова) / размер(*ключ_слова); ш++) хеш_вставить(&карта, ключ_слова[ш], (пусто *)1); } возврат хеш_получить2(&карта, ток->loc, ток->len); } стат цел разбор_эскейп_посл(символ **новая_поз, символ *п) { если ('0' <= *п && *п <= '7') { цел зн = *п++ - '0'; если ('0' <= *п && *п <= '7') { зн = (зн << 3) + (*п++ - '0'); если ('0' <= *п && *п <= '7') зн = (зн << 3) + (*п++ - '0'); } *новая_поз = п; возврат зн; } если (*п == 'x') { п++; если (!isxdigit(*п)) error_at(п, "недопустимая шестнадцатеричная escape-последовательность"); цел зн = 0; для (; isxdigit(*п); п++) зн = (зн << 4) + знач_шестн_цифры(*п); *новая_поз = п; возврат зн; } *новая_поз = п + 1; выбор (*п) { случай 'a': возврат '\a'; случай 'b': возврат '\b'; случай 't': возврат '\t'; случай 'n': возврат '\n'; случай 'v': возврат '\v'; случай 'f': возврат '\f'; случай 'r': возврат '\r'; случай 'e': возврат 27; умолч: возврат *п; } } стат символ *найти_конец_строки(символ *п) { символ *начало = п; для (; *п != '"'; п++) { если (*п == '\n' || *п == '\0') error_at(начало, "незакрытый строковый литерал"); если (*п == '\\') п++; } возврат п; } стат Token *разбор_строкового_литерала(символ *начало, символ *кавычка) { символ *конец = найти_конец_строки(кавычка + 1); символ *буф = calloc(1, конец - кавычка); отследить_память(буф); цел длина = 0; для (символ *п = кавычка + 1; п < конец;) { если (*п == '\\') буф[длина++] = разбор_эскейп_посл(&п, п + 1); иначе буф[длина++] = *п++; } Token *ток = создать_токен(TK_STR, начало, конец + 1); ток->ty = array_of(ty_char, длина + 1); ток->str = буф; возврат ток; } стат Token *разбор_utf16_строки(символ *начало, символ *кавычка) { символ *конец = найти_конец_строки(кавычка + 1); uint16_t *буф = calloc(2, конец - начало); отследить_память(буф); цел длина = 0; для (символ *п = кавычка + 1; п < конец;) { если (*п == '\\') { буф[длина++] = разбор_эскейп_посл(&п, п + 1); продолжи; } uint32_t зн = decode_utf8(&п, п); если (зн < 0x10000) { буф[длина++] = зн; } иначе { зн -= 0x10000; буф[длина++] = 0xd800 + ((зн >> 10) & 0x3ff); буф[длина++] = 0xdc00 + (зн & 0x3ff); } } Token *ток = создать_токен(TK_STR, начало, конец + 1); ток->ty = array_of(ty_ushort, длина + 1); ток->str = (символ *)буф; возврат ток; } стат Token *разбор_utf32_строки(символ *начало, символ *кавычка, Type *тип) { символ *конец = найти_конец_строки(кавычка + 1); uint32_t *буф = calloc(4, конец - кавычка); отследить_память(буф); цел длина = 0; для (символ *п = кавычка + 1; п < конец;) { если (*п == '\\') буф[длина++] = разбор_эскейп_посл(&п, п + 1); иначе буф[длина++] = decode_utf8(&п, п); } Token *ток = создать_токен(TK_STR, начало, конец + 1); ток->ty = array_of(тип, длина + 1); ток->str = (символ *)буф; возврат ток; } стат Token *разбор_симв_литерала(символ *начало, символ *кавычка, Type *тип) { символ *п = кавычка + 1; если (*п == '\0') error_at(начало, "незакрытый символьный литерал"); цел зн; если (*п == '\\') зн = разбор_эскейп_посл(&п, п + 1); иначе зн = decode_utf8(&п, п); символ *конец = strchr(п, '\''); если (!конец) error_at(п, "незакрытый символьный литерал"); Token *ток = создать_токен(TK_NUM, начало, конец + 1); ток->val = зн; ток->ty = тип; возврат ток; } стат бул разбор_целого(Token *ток) { символ *п = ток->loc; цел основание = 10; если (!strncasecmp(п, "0x", 2) && isxdigit(п[2])) { п += 2; основание = 16; } иначе если (!strncasecmp(п, "0b", 2) && (п[2] == '0' || п[2] == '1')) { п += 2; основание = 2; } иначе если (*п == '0') { основание = 8; } int64_t знач = strtoul(п, &п, основание); бул суф_l = ложь; бул суф_u = ложь; если (есть_префикс(п, "LLU") || есть_префикс(п, "LLu") || есть_префикс(п, "llU") || есть_префикс(п, "llu") || есть_префикс(п, "ULL") || есть_префикс(п, "Ull") || есть_префикс(п, "uLL") || есть_префикс(п, "ull")) { п += 3; суф_l = суф_u = истина; } иначе если (!strncasecmp(п, "lu", 2) || !strncasecmp(п, "ul", 2)) { п += 2; суф_l = суф_u = истина; } иначе если (есть_префикс(п, "LL") || есть_префикс(п, "ll")) { п += 2; суф_l = истина; } иначе если (*п == 'L' || *п == 'l') { п++; суф_l = истина; } иначе если (*п == 'U' || *п == 'u') { п++; суф_u = истина; } если (п != ток->loc + ток->len) возврат ложь; Type *тип; если (основание == 10) { если (суф_l && суф_u) тип = ty_ulong; иначе если (суф_l) тип = ty_long; иначе если (суф_u) тип = (знач >> 32) ? ty_ulong : ty_uint; иначе тип = (знач >> 31) ? ty_long : ty_int; } иначе { если (суф_l && суф_u) тип = ty_ulong; иначе если (суф_l) тип = (знач >> 63) ? ty_ulong : ty_long; иначе если (суф_u) тип = (знач >> 32) ? ty_ulong : ty_uint; иначе если (знач >> 63) тип = ty_ulong; иначе если (знач >> 32) тип = ty_long; иначе если (знач >> 31) тип = ty_uint; иначе тип = ty_int; } ток->kind = TK_NUM; ток->val = знач; ток->ty = тип; возврат истина; } стат пусто преобразовать_пп_число(Token *ток) { если (разбор_целого(ток)) возврат; символ *конец; длин двойн знач = strtold(ток->loc, &конец); Type *тип; если (*конец == 'f' || *конец == 'F') { тип = ty_float; конец++; } иначе если (*конец == 'l' || *конец == 'L') { тип = ty_ldouble; конец++; } иначе { тип = ty_double; } если (ток->loc + ток->len != конец) error_tok(ток, "недопустимая числовая константа"); ток->kind = TK_NUM; ток->fval = знач; ток->ty = тип; } пусто convert_pp_tokens(Token *ток) { для (Token *т = ток; т->kind != TK_EOF; т = т->next) { если (зарезервированное_слово(т)) т->kind = TK_KEYWORD; иначе если (т->kind == TK_PP_NUM) преобразовать_пп_число(т); } } стат пусто назначить_номера_строк(Token *ток) { символ *п = акт_файл->contents; цел номер = 1; делай { если (п == ток->loc) { ток->line_no = номер; ток = ток->next; } если (*п == '\n') номер++; } пока (*п++); } Token *tokenize_string_literal(Token *ток, Type *базовый) { Token *рез; если (базовый->size == 2) рез = разбор_utf16_строки(ток->loc, ток->loc); иначе рез = разбор_utf32_строки(ток->loc, ток->loc, базовый); рез->next = ток->next; возврат рез; } Token *tokenize(File *файл) { акт_файл = файл; символ *п = файл->contents; Token голова = {}; Token *тек = &голова; начало_строки = истина; был_пробел = ложь; пока (*п) { если (есть_префикс(п, "//")) { п += 2; пока (*п != '\n') п++; был_пробел = истина; продолжи; } если (есть_префикс(п, "/*")) { символ *кон = strstr(п + 2, "*/"); если (!кон) error_at(п, "незакрытый блочный комментарий"); п = кон + 2; был_пробел = истина; продолжи; } если (*п == '\n') { п++; начало_строки = истина; был_пробел = ложь; продолжи; } если (isspace(*п)) { п++; был_пробел = истина; продолжи; } если (isdigit(*п) || (*п == '.' && isdigit(п[1]))) { символ *нач = п++; для (;;) { если (п[0] && п[1] && strchr("eEpP", п[0]) && strchr("+-", п[1])) п += 2; иначе если (isalnum(*п) || *п == '.') п++; иначе прерви; } тек = тек->next = создать_токен(TK_PP_NUM, нач, п); продолжи; } если (*п == '"') { тек = тек->next = разбор_строкового_литерала(п, п); п += тек->len; продолжи; } если (есть_префикс(п, "u8\"")) { тек = тек->next = разбор_строкового_литерала(п, п + 2); п += тек->len; продолжи; } если (есть_префикс(п, "u\"")) { тек = тек->next = разбор_utf16_строки(п, п + 1); п += тек->len; продолжи; } если (есть_префикс(п, "L\"")) { тек = тек->next = разбор_utf32_строки(п, п + 1, ty_int); п += тек->len; продолжи; } если (есть_префикс(п, "U\"")) { тек = тек->next = разбор_utf32_строки(п, п + 1, ty_uint); п += тек->len; продолжи; } если (*п == '\'') { тек = тек->next = разбор_симв_литерала(п, п, ty_int); тек->val = (символ)тек->val; п += тек->len; продолжи; } если (есть_префикс(п, "u'")) { тек = тек->next = разбор_симв_литерала(п, п + 1, ty_ushort); тек->val &= 0xffff; п += тек->len; продолжи; } если (есть_префикс(п, "L'")) { тек = тек->next = разбор_симв_литерала(п, п + 1, ty_int); п += тек->len; продолжи; } если (есть_префикс(п, "U'")) { тек = тек->next = разбор_симв_литерала(п, п + 1, ty_uint); п += тек->len; продолжи; } цел дл_ид = разбор_идентификатора(п); если (дл_ид) { тек = тек->next = создать_токен(TK_IDENT, п, п + дл_ид); п += тек->len; продолжи; } цел дл_пункт = разбор_пунктуатора(п); если (дл_пункт) { тек = тек->next = создать_токен(TK_PUNCT, п, п + дл_пункт); п += тек->len; продолжи; } error_at(п, "недопустимый токен"); } тек = тек->next = создать_токен(TK_EOF, п, п); назначить_номера_строк(голова.next); возврат голова.next; } стат символ *загрузить_файл(символ *путь) { FILE *фп; если (strcmp(путь, "-") == 0) { фп = stdin; } иначе { фп = fopen(путь, "r"); если (!фп) возврат NULL; } символ *буф; size_t разм_буф; FILE *вых = open_memstream(&буф, &разм_буф); для (;;) { символ врем_буф[4096]; цел прочитано = fread(врем_буф, 1, размер(врем_буф), фп); если (прочитано == 0) прерви; fwrite(врем_буф, 1, прочитано, вых); } если (фп != stdin) fclose(фп); fflush(вых); если (разм_буф == 0 || буф[разм_буф - 1] != '\n') fputc('\n', вых); fputc('\0', вых); fclose(вых); отследить_память(буф); возврат буф; } File **get_input_files(пусто) { возврат все_вх_файлы; } File *new_file(символ *имя, цел номер, символ *содержимое) { File *файл = calloc(1, размер(File)); отследить_память(файл); файл->name = имя; файл->display_name = имя; файл->file_no = номер; файл->contents = содержимое; возврат файл; } стат пусто нормализовать_переносы(символ *п) { цел ш = 0, щ = 0; пока (п[ш]) { если (п[ш] == '\r' && п[ш + 1] == '\n') { ш += 2; п[щ++] = '\n'; } иначе если (п[ш] == '\r') { ш++; п[щ++] = '\n'; } иначе { п[щ++] = п[ш++]; } } п[щ] = '\0'; } стат пусто убрать_продолжения_строк(символ *п) { цел ш = 0, щ = 0; цел убрано = 0; пока (п[ш]) { если (п[ш] == '\\' && п[ш + 1] == '\n') { ш += 2; убрано++; } иначе если (п[ш] == '\n') { п[щ++] = п[ш++]; для (; убрано > 0; убрано--) п[щ++] = '\n'; } иначе { п[щ++] = п[ш++]; } } для (; убрано > 0; убрано--) п[щ++] = '\n'; п[щ] = '\0'; } стат uint32_t разбор_универсального(символ *п, цел длина) { uint32_t зн = 0; для (цел ш = 0; ш < длина; ш++) { если (!isxdigit(п[ш])) возврат 0; зн = (зн << 4) | знач_шестн_цифры(п[ш]); } возврат зн; } стат пусто раскрыть_универсальные(символ *п) { символ *щ = п; пока (*п) { если (есть_префикс(п, "\\u")) { uint32_t зн = разбор_универсального(п + 2, 4); если (зн) { п += 6; щ += encode_utf8(щ, зн); } иначе { *щ++ = *п++; } } иначе если (есть_префикс(п, "\\U")) { uint32_t зн = разбор_универсального(п + 2, 8); если (зн) { п += 10; щ += encode_utf8(щ, зн); } иначе { *щ++ = *п++; } } иначе если (п[0] == '\\') { *щ++ = *п++; *щ++ = *п++; } иначе { *щ++ = *п++; } } *щ = '\0'; } Token *tokenize_file(символ *путь) { символ *содержимое = загрузить_файл(путь); если (!содержимое) возврат NULL; если (!memcmp(содержимое, "\xef\xbb\xbf", 3)) содержимое += 3; нормализовать_переносы(содержимое); убрать_продолжения_строк(содержимое); раскрыть_универсальные(содержимое); File *файл = new_file(путь, счётчик_файлов + 1, содержимое); если (счётчик_файлов + 2 > ёмкость_вх_файлов) { ёмкость_вх_файлов = ёмкость_вх_файлов ? ёмкость_вх_файлов * 2 : 16; все_вх_файлы = realloc(все_вх_файлы, размер(File *) * ёмкость_вх_файлов); } все_вх_файлы[счётчик_файлов] = файл; все_вх_файлы[счётчик_файлов + 1] = NULL; счётчик_файлов++; возврат tokenize(файл); }