762 lines
27 KiB
C
762 lines
27 KiB
C
|
|
#включить "руси.з"
|
|||
|
|
|
|||
|
|
// Текущий обрабатываемый файл
|
|||
|
|
стат 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(файл);
|
|||
|
|
}
|