commit 4a11b8357a532203ae3d75f8afed29f053b27c72 Author: mzhoot Date: Tue Jul 21 10:20:27 2026 +0300 Стартовый коммит: компилятор РуСи diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..c187b8d --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,24 @@ +Copyright (c) 2020-2024, Rui Ueyama +Copyright (c) 2026, Kirichenko Viktor + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..55aaab7 --- /dev/null +++ b/Makefile @@ -0,0 +1,64 @@ +# Исполняемый файл компилятора +COMPILER = ./rusi.elf + +# Флаги компиляции +COMPILER_FLAGS = + +# Исходные файлы +SOURCE_FILES = $(wildcard *.c) + +# Директория для stage2 +STAGE2_DIR = stage2 +STAGE2_BINARY = $(STAGE2_DIR)/rusi.elf + +# ============================================================ +# Сборка по умолчанию (stage2 — самосборка) +# ============================================================ + +all: stage2 + +# ============================================================ +# Тесты +# ============================================================ + +test: $(COMPILER) + @echo "=== Запуск стандартных тестов ===" + python3 test/driver.py $(COMPILER) + +test-ru: $(COMPILER) + @echo "=== Запуск русских тестов ===" + python3 test_ru/run_tests.py + +test-all: test test-ru + +# ============================================================ +# Самосборка (stage2) +# ============================================================ + +$(STAGE2_BINARY): $(COMPILER) $(SOURCE_FILES) руси.з + @mkdir -p $(STAGE2_DIR) + $(COMPILER) $(COMPILER_FLAGS) -o $@ $(SOURCE_FILES) + @if cmp -s $(COMPILER) $@; then \ + echo "OK: stage2 побайтово идентичен rusi.elf"; \ + else \ + echo "OK: stage2 собран (отличается от rusi.elf)"; \ + fi + +stage2: $(STAGE2_BINARY) + +test-stage2: $(STAGE2_BINARY) + @echo "=== Запуск тестов на stage2 ===" + python3 test/driver.py $(STAGE2_BINARY) + +# ============================================================ +# Очистка +# ============================================================ + +clean: + rm -rf $(STAGE2_DIR) stage3 + rm -f *.o + rm -f test/*.o test/*.elf test/*.s + rm -f test_ru/*.o test_ru/*.elf test_ru/*.s + find . -name '*~' -delete + +.PHONY: all test test-ru test-all stage2 test-stage2 clean \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e54b229 --- /dev/null +++ b/README.md @@ -0,0 +1,762 @@ +```markdown +# Руси: двуязычный компилятор C с русским синтаксисом + +**Руси** — это компилятор языка C, поддерживающий **полноценный двуязычный синтаксис**. +Вы можете писать программы на C, используя как **английские**, так и **русские** ключевые слова, типы данных, операторы и директивы препроцессора — в любом сочетании, в одном файле. + +Название проекта — **Руси** — говорит само за себя: это язык C, заговоривший по-русски, но при этом сохранивший родной английский синтаксис для тех, кому он привычнее. + +Проект вдохновлен двумя реализациями: +- [chibicc](https://github.com/rui314/chibicc) — небольшой, но мощный C11 компилятор, способный компилировать реальные проекты.(Собственно и сделан на его основе) +- [c89cc.sh](https://gist.github.com/alganet/2b89c4368f8d23d033961d8a3deb5c19) — минималистичный компилятор C89/ELF64 на чистом shell, демонстрирующий невероятную простоту реализации. + +Проект **Руси** разработан для портирования компилятора [КВС (kvs)](https://github.com/artradeskz/kvs) — ассемблера с русским синтаксисом, написанного на Python. Вместо того чтобы использовать интерпретатор Python для сборки, **Руси** позволит скомпилировать этот ассемблер в нативный бинарный файл, значительно повысив производительность и удобство использования. + +## Двуязычность: как это работает + +**Руси** одновременно понимает два языка: английский C и русский C. Это означает, что: + +- Вы можете писать код **полностью на английском** — как в любом стандартном C. +- Вы можете писать код **полностью на русском** — используя русские ключевые слова, типы и операторы. +- Вы можете **смешивать** английский и русский синтаксис в одном файле. + +Это делает компилятор идеальным инструментом для обучения, миграции проектов и команд с разными языковыми предпочтениями. + +### Примеры смешанного синтаксиса + +```c +// Английский main с русским типом и оператором +int главн() { + цел x = 42; + if (x == 42) { + возврат 0; + } + return 1; +} + +// Русский +цел главн() { + цел ы = 42; + если (ы == 42) { + возврат 0; + } + возврат 1; +} + +// Полностью смешанный стиль +цел main() { + int счетчик = 0; + для (счетчик = 0; счетчик < 10; счетчик = счетчик + 1) { + if (счетчик % 2 == 0) { + printf("Четное: %d\n", счетчик); + } + } + возврат 0; +} +``` + +## Полный список русских слов для программирования + +### Типы данных (13) + +| Английский | Русский | Пример | +|------------|---------|--------| +| `int` | `цел` | `цел x = 42;` | +| `char` | `символ` | `символ c = 'A';` | +| `short` | `корот` | `корот s = 123;` | +| `long` | `длин` | `длин l = 456789;` | +| `void` | `пусто` | `пусто функция() {}` | +| `float` | `плав` | `плав f = 3.14;` | +| `double` | `двойн` | `двойн d = 2.71;` | +| `struct` | `структ` | `структ Точка {};` | +| `union` | `объед` | `объед Данные {};` | +| `enum` | `переч` | `переч Цвет {};` | +| `_Bool` | `бул` | `бул flag = истина;` | +| `unsigned` | `беззнак` | `беззнак цел u = 42;` | +| `signed` | `знак` | `знак цел s = -42;` | + +### Ключевые слова (16) + +| Английский | Русский | Пример | +|------------|---------|--------| +| `typedef` | `типдеф` | `типдеф цел МойЦел;` | +| `static` | `стат` | `стат цел счетчик = 0;` | +| `extern` | `внеш` | `внеш цел глобальная;` | +| `inline` | `инлайн` | `инлайн цел квадрат() {}` | +| `const` | `конст` | `конст цел x = 42;` | +| `volatile` | `летуч` | `летуч цел x;` | +| `auto` | `авто` | `авто цел x = 42;` | +| `register` | `регистр` | `регистр цел x;` | +| `restrict` | `огранич` | `цел * огранич ptr;` | +| `_Alignas` | `_Выравнивание` | `_Выравнивание(16) символ buf[32];` | +| `_Alignof` | `_Выравнивание_типа` | `_Выравнивание_типа(цел);` | +| `_Noreturn` | `_Безвозврат` | `_Безвозврат пусто функция() {}` | +| `_Generic` | `_Обобщ` | `_Обобщ(42, цел: 1, умолч: 0);` | +| `_Atomic` | `_Атом` | `_Атом цел x;` | +| `_Thread_local` | `_Поток_лок` | `_Поток_лок цел x;` | +| `__thread` | `__поток` | `__поток цел x;` | + +### Операторы (18) + +| Английский | Русский | Пример | +|------------|---------|--------| +| `return` | `возврат` | `возврат 0;` | +| `if` | `если` | `если (x == 10) {}` | +| `else` | `иначе` | `иначе {}` | +| `for` | `для` | `для (i = 0; i < 10; i++) {}` | +| `while` | `пока` | `пока (i < 10) {}` | +| `do` | `делай` | `делай {} пока (i < 10);` | +| `switch` | `выбор` | `выбор (x) {}` | +| `case` | `случай` | `случай 1:` | +| `default` | `умолч` | `умолч:` | +| `goto` | `перейти` | `перейти метка;` | +| `break` | `прерви` | `прерви;` | +| `continue` | `продолжи` | `продолжи;` | +| `sizeof` | `размер` | `размер(цел);` | +| `typeof` | `типоф` | `типоф(x) y;` | +| `asm` | `асм` | `асм("nop");` | +| `&&` | Оставлено как есть | `x && y` | +| `\|\|` | Оставлено как есть | `x \|\| y` | +| `!` | Оставлено как есть | `!x` | + +### Директивы препроцессора (15) + +| Английский | Русский | Пример | +|------------|---------|--------| +| `#include` | `#включить` | `#включить ` | +| `#include_next` | `#включить_след` | `#включить_след "file.h"` | +| `#define` | `#определить` | `#определить ПИ 3.14` | +| `#undef` | `#отменить` | `#отменить ПИ` | +| `#if` | `#если` | `#если ВЕРСИЯ == 2` | +| `#ifdef` | `#еслиопр` | `#еслиопр ТЕСТ` | +| `#ifndef` | `#еслинеопр` | `#еслинеопр ТЕСТ` | +| `#elif` | `#иначеесли` | `#иначеесли ВЕРСИЯ == 2` | +| `#else` | `#иначе` | `#иначе` | +| `#endif` | `#конецесли` | `#конецесли` | +| `#line` | `#строка` | `#строка 100 "file.c"` | +| `#error` | `#ошибка` | `#ошибка "Сообщение"` | +| `#pragma` | `#прагма` | `#прагма once` | +| `defined` | `определено` | `#если определено(ТЕСТ)` | +| `once` | `однократно` | `#прагма однократно` | + +### Встроенные макросы (9) + +| Английский | Русский | Пример | +|------------|---------|--------| +| `__FILE__` | `__ФАЙЛ__` | `символ *f = __ФАЙЛ__;` | +| `__LINE__` | `__ЛИНИЯ__` | `цел n = __ЛИНИЯ__;` | +| `__func__` | `__функ__` | `символ *f = __функ__;` | +| `__FUNCTION__` | `__ФУНКЦИЯ__` | `символ *f = __ФУНКЦИЯ__;` | +| `__COUNTER__` | `__СЧЕТЧИК__` | `цел n = __СЧЕТЧИК__;` | +| `__TIMESTAMP__` | `__ВРЕМЯ_ФАЙЛА__` | `символ *t = __ВРЕМЯ_ФАЙЛА__;` | +| `__DATE__` | `__ДАТА__` | `символ *d = __ДАТА__;` | +| `__TIME__` | `__ВРЕМЯ__` | `символ *t = __ВРЕМЯ__;` | +| `__BASE_FILE__` | `__БАЗОВЫЙ_ФАЙЛ__` | `символ *b = __БАЗОВЫЙ_ФАЙЛ__;` | + +### Функции (1) + +| Английский | Русский | Пример | +|------------|---------|--------| +| `main` | `главн` | `цел главн() {}` | + +## Особенности + +- **Двуязычный синтаксис**: все ключевые слова, типы, операторы и директивы препроцессора доступны как на английском, так и на русском языке. +- **Смешивание языков**: английский и русский синтаксис можно использовать в одном файле в любых сочетаниях. +- **Русские идентификаторы**: можно называть переменные, функции и структуры по-русски. +- **Русские имена файлов и расширения**: компилятор работает с файлами `программа.си` и заголовками `привет.з`. +- **Полная поддержка C11**: включая препроцессор, битовые поля, VLA, атомарные и thread-local переменные. +- **70+ переведенных конструкций**: все ключевые слова, операторы, макросы и директивы покрыты тестами. + +## Пример программы (смешанный синтаксис) + +```c +#включить +#включить + +структ Точка { + цел x; + цел y; +}; + +цел сумма_координат(структ Точка точка) { + возврат точка.x + точка.y; +} + +цел главн() { + цел массив[10]; + for (цел i = 0; i < 10; i = i + 1) { + массив[i] = i * 2; + } + + int сумма = 0; + для (int i = 0; i < 10; i = i + 1) { + сумма = сумма + массив[i]; + } + + если (sum == 90) { + printf("Сумма: %d\n", сумма); + } else { + printf("Ошибка!\n"); + возврат 1; + } + + struct Точка точка = {10, 20}; + цел результат = сумма_координат(точка); + + если (результат == 30) { + printf("OK\n"); + возврат 0; + } + + return 1; +} +``` + +## Импорт файлов с русскими именами + +```c +// привет.з - русский заголовочный файл +#еслинеопр ПРИВЕТ_З +#определить ПРИВЕТ_З + +#включить +#define ПЕЧАТЬ printf + +#endif +``` + +```c +// программа.рс - основная программа +#включить "привет.з" + +цел главн() { + ПЕЧАТЬ("Привет, мир!\n"); + возврат 0; +} +``` + +## Компиляция русских файлов + +```bash +./руси программа.рс -o программа +./программа +# Вывод: Привет, мир! +``` + +## Внутреннее устройство + +Компилятор состоит из четырех этапов: +1. **Токенизация** — разбиение исходного кода на токены (с поддержкой UTF-8 и кириллицы). +2. **Препроцессинг** — обработка директив и макросов (с русскими синонимами). +3. **Парсинг** — построение AST с использованием рекурсивного спуска. +4. **Генерация кода** — создание ассемблерного текста для x86-64 (ELF64). + +## Тестирование и самосборка + +Проект имеет две независимые системы тестирования: + +### 1. Стандартные тесты (английский синтаксис) + +Запускаются командой `make test`. Проверяют базовую функциональность компилятора на оригинальном C. + +### 2. Русские тесты + +Запускаются командой `make test-ru`. Написаны на Python (`test_ru/run_tests.py`) и проверяют все русские синонимы. 70+ тестов покрывают все переведенные конструкции: + +```bash +make test-ru +# Вывод: 🎉 Все тесты пройдены! +``` + +### 3. Самосборка (Stage 2) + +Компилятор способен скомпилировать сам себя. Этот процесс называется **самосборкой** (self-hosting) и подтверждает, что компилятор достаточно зрелый для работы с собственным кодом. + +```bash +make test-stage2 +``` + +Эта команда: +1. Компилирует компилятор с помощью самого себя (stage2) +2. Проверяет, что stage2-версия проходит все тесты +3. Подтверждает, что компилятор способен воспроизвести себя + +Самосборка — важный этап разработки компилятора, доказывающий его надежность и полноту. + +## Планы по миграции исходного кода на русский синтаксис + +Текущая версия компилятора уже **полностью понимает русский синтаксис** и успешно проходит все тесты (включая 70+ русских тестов). Следующий логический шаг — **переписать сам исходный код компилятора на русском синтаксисе**. + +### Что это даст? + +1. **Полная русификация**: исходный код компилятора будет написан на том же языке, который он компилирует. +2. **Демонстрация возможностей**: сам компилятор станет лучшим примером использования русского синтаксиса. +3. **Образовательная ценность**: русскоязычные студенты смогут изучать устройство компилятора без языкового барьера. +4. **Двуязычная кодовая база**: английская и русская версии будут существовать параллельно. + +### Как это будет сделано + +1. **Поэтапная миграция**: исходный код будет переписан на русский синтаксис постепенно, файл за файлом. +2. **Сохранение двуязычности**: компилятор продолжит понимать оба синтаксиса. +3. **Тестирование**: после каждого этапа будет запускаться полный набор тестов. +4. **Самосборка**: после завершения миграции компилятор сможет скомпилировать себя из русскоязычного исходного кода. + +### Статус + +- ✅ **Компилятор понимает русский синтаксис** — полностью готово. +- ✅ **Функция main переименована в главн** — сделано. +- ⏳ **Миграция исходного кода на русский синтаксис** — в планах. +- ⏳ **Самосборка из русскоязычного исходного кода** — следующий этап. + +### Пример того, как будет выглядеть код компилятора на русском + +```c +// Токенизатор на русском синтаксисе (планируется) +структ Токен { + переч ВидТокена вид; + Токен *следующий; + цел значение; + символ *позиция; + цел длина; +}; + +цел прочитать_идентификатор(символ *начало) { + символ *p = начало; + цел c = декодировать_utf8(&p, p); + если (!это_идентификатор1(c)) { + возврат 0; + } + пока (истина) { + символ *q; + c = декодировать_utf8(&q, p); + если (!это_идентификатор2(c)) { + возврат p - начало; + } + p = q; + } +} +``` + +## Портирование КВС (kvs) с Python на C + +Проект **Руси** разработан с прицелом на портирование компилятора [КВС (kvs)](https://github.com/artradeskz/kvs) — ассемблера с русским синтаксисом для x86-64, написанного на Python. + +### Зачем это нужно? + +КВС (расшифровывается как "Код В Силу" или просто "КВС") — это образовательный ассемблер, который использует русскую лексику для мнемоник инструкций, имён регистров и директив. Однако он написан на Python и работает как интерпретатор, что ограничивает его производительность и распространение. + +**Руси** позволит: + +1. **Скомпилировать КВС в нативный бинарный файл** — вместо интерпретации Python-скриптов, ассемблер будет работать как обычный исполняемый файл. +2. **Значительно повысить производительность** — компилированный код работает на порядок быстрее интерпретируемого. +3. **Распространять как самостоятельный бинарник** — не требуется устанавливать Python и зависимости. +4. **Улучшить интеграцию** — бинарный файл легче встраивать в различные системы и инструменты. + +### Как будет происходить портирование + +1. **Анализ архитектуры КВС**: КВС состоит из шести проходов: лексер, парсер, 4 прохода разрешения меток и констант, финальная сборка ELF. +2. **Переписывание на C с русским синтаксисом**: каждый модуль будет переписан на C, используя русские ключевые слова и идентификаторы. +3. **Сохранение логики**: шестипроходная архитектура останется, но будет оптимизирована для нативного выполнения. +4. **Полная совместимость**: бинарный файл должен компилировать те же `.квс` файлы, что и оригинальный Python-скрипт. + +### Пример: лексер КВС на русском C + +```c +// планируемый код лексера КВС на русском C +структ Токен { + переч ВидТокена вид; + символ *текст; + цел длина; + Токен *следующий; +}; + +Токен *лексический_анализ(символ *исходник) { + Токен *голова = нуль; + Токен *текущий = нуль; + символ *позиция = исходник; + + пока (*позиция != '\0') { + пропустить_пробелы(&позиция); + если (*позиция == '\0') { + прерви; + } + + если (*позиция == ';') { + пропустить_комментарий(&позиция); + продолжи; + } + + Токен *токен = выделить_токен(&позиция); + если (голова == нуль) { + голова = токен; + } иначе { + текущий->следующий = токен; + } + текущий = токен; + } + + возврат голова; +} +``` + +### Преимущества портирования для КВС + +| Аспект | Python-версия (оригинал) | C-версия (план) | +|--------|--------------------------|-----------------| +| **Скорость сборки** | ~0.5-1.0 сек для простых программ | ~0.01-0.05 сек | +| **Установка** | Требуется Python 3.7+ | Ничего не требуется | +| **Распространение** | Исходники или .deb | Один бинарник | +| **Зависимости** | Стандартная библиотека Python | Только libc | +| **Интеграция** | Только как скрипт | Может встраиваться в любую систему | + +### План работ по портированию + +1. **Этап 1**: Портирование лексера (`квс_лексер.py` → `лексер.рс`) +2. **Этап 2**: Портирование парсера (`квс_парсер.py` → `парсер.рс`) +3. **Этап 3**: Портирование четырех проходов (`квс_проход*.py` → `проход*.рс`) +4. **Этап 4**: Портирование финальной сборки ELF (`квс_финсбор.py` → `финсбор.рс`) +5. **Этап 5**: Интеграция в единый бинарник (`квс_сборка.рс`) +6. **Этап 6**: Тестирование на всех 26 тестах КВС + +## Подсветка синтаксиса для VS Code / VS Codium + +В планах разработка **расширения для подсветки синтаксиса** русского C для редакторов VS Code и VS Codium. + +### Что будет поддерживаться + +- **Русские ключевые слова**: подсветка всех переведенных конструкций (`цел`, `если`, `возврат` и др.) +- **Русские идентификаторы**: правильное распознавание переменных и функций с кириллицей +- **Русские директивы препроцессора**: подсветка `#включить`, `#определить` и др. +- **Русские имена файлов**: корректная работа с файлами `.рс` и `.з` + +### Планируемые функции расширения + +- **Автодополнение**: подсказки для русских ключевых слов +- **Фрагменты кода**: готовые шаблоны для `если`, `для`, `структ` и др. +- **Форматирование**: автоматическое форматирование кода с русским синтаксисом +- **Проверка синтаксиса**: интеграция с компилятором **Руси** для проверки ошибок + +### Статус + +- ⏳ **Разработка расширения для подсветки синтаксиса** — в планах. +- ⏳ **Интеграция с компилятором** — следующий этап. + +## Планы по русификации стандартной библиотеки + +В планах создание **русскоязычной стандартной библиотеки** — заголовочных файлов с русскими именами для всех основных функций и констант. + +### Зачем это нужно? + +Русификация стандартной библиотеки сделает русский синтаксис по-настоящему завершенным. Программист сможет не только писать ключевые слова на русском, но и использовать функции и константы с русскими именами: + +```c +#включить <стандарт.з> + +цел главн() { + // Вместо printf("Привет!\n"); + печать("Привет!\n"); + + // Вместо strlen("текст"); + цел длина = длина_строки("текст"); + + // Вместо malloc(100); + пусто *память = выделить(100); + + // Вместо free(память); + освободить(память); + + возврат 0; +} +``` + +### Какие функции будут переведены + +| Английское имя | Русское имя | Заголовочный файл | +|----------------|-------------|-------------------| +| `printf` | `печать` | `стандарт.з` (stdio.h) | +| `scanf` | `ввод` | `стандарт.з` (stdio.h) | +| `puts` | `вывод_строки` | `стандарт.з` (stdio.h) | +| `gets` | `получить_строку` | `стандарт.з` (stdio.h) | +| `malloc` | `выделить` | `стандарт.з` (stdlib.h) | +| `free` | `освободить` | `стандарт.з` (stdlib.h) | +| `calloc` | `выделить_нули` | `стандарт.з` (stdlib.h) | +| `realloc` | `перевыделить` | `стандарт.з` (stdlib.h) | +| `strlen` | `длина_строки` | `строки.з` (string.h) | +| `strcpy` | `копировать_строку` | `строки.з` (string.h) | +| `strcmp` | `сравнить_строки` | `строки.з` (string.h) | +| `strcat` | `склеить_строки` | `строки.з` (string.h) | +| `strchr` | `найти_в_строке` | `строки.з` (string.h) | +| `strstr` | `найти_подстроку` | `строки.з` (string.h) | +| `memcpy` | `копировать_память` | `память.з` (string.h) | +| `memset` | `заполнить_память` | `память.з` (string.h) | +| `memcmp` | `сравнить_память` | `память.з` (string.h) | +| `exit` | `завершить` | `стандарт.з` (stdlib.h) | +| `rand` | `случайное` | `стандарт.з` (stdlib.h) | +| `srand` | `инициализировать_случайное` | `стандарт.з` (stdlib.h) | +| `time` | `время` | `время.з` (time.h) | +| `sqrt` | `корень` | `математика.з` (math.h) | +| `sin` | `синус` | `математика.з` (math.h) | +| `cos` | `косинус` | `математика.з` (math.h) | +| `tan` | `тангенс` | `математика.з` (math.h) | +| `pow` | `степень` | `математика.з` (math.h) | +| `abs` | `модуль` | `стандарт.з` (stdlib.h) | + +### Как это будет реализовано + +1. **Создание заголовочных файлов с русскими именами**: например, `стандарт.з` (аналог stdio.h + stdlib.h), `строки.з` (string.h), `математика.з` (math.h). +2. **Использование макросов**: русские имена будут определены через `#define` или через `typedef`. +3. **Полная совместимость**: можно будет использовать как русские, так и английские имена в одной программе. + +### Пример заголовочного файла `стандарт.з` + +```c +// стандарт.з - русская стандартная библиотека +#еслинеопр СТАНДАРТ_З +#определить СТАНДАРТ_З + +#включить +#включить +#включить +#включить + +// Ввод-вывод +#определить печать printf +#определить ввод scanf +#определить вывод_строки puts +#определить получить_строку gets +#определить вывод_ошибки fprintf + +// Управление памятью +#определить выделить malloc +#определить освободить free +#определить выделить_нули calloc +#определить перевыделить realloc + +// Завершение программы +#определить завершить exit + +// Работа со строками +#определить длина_строки strlen +#определить копировать_строку strcpy +#определить сравнить_строки strcmp +#определить склеить_строки strcat +#определить найти_в_строке strchr +#определить найти_подстроку strstr + +// Работа с памятью +#определить копировать_память memcpy +#определить заполнить_память memset +#определить сравнить_память memcmp + +// Случайные числа +#определить случайное rand +#определить инициализировать_случайное srand + +// Математика +#определить корень sqrt +#определить синус sin +#определить косинус cos +#определить тангенс tan +#определить степень pow + +// Константы +#определить ПИ 3.14159265358979323846 +#определить Е 2.71828182845904523536 + +#конецесли +``` + +### Пример программы с русской стандартной библиотекой + +```c +#включить "стандарт.з" + +цел главн() { + печать("Введите число: "); + цел x; + ввод("%d", &x); + + цел квадрат = степень(x, 2); + печать("Квадрат числа %d равен %d\n", x, квадрат); + + печать("Синус числа: %f\n", синус(x)); + + символ *сообщение = выделить(100); + если (сообщение != нуль) { + копировать_строку(сообщение, "Привет, мир!"); + печать("%s\n", сообщение); + освободить(сообщение); + } + + завершить(0); +} +``` + +### Преимущества для образования + +Русскоязычная стандартная библиотека станет мощным инструментом для обучения программированию, так как: + +1. **Снижает порог входа**: студентам не нужно запоминать английские названия функций. +2. **Ускоряет обучение**: можно сразу переходить к логике, а не тратить время на запоминание терминов. +3. **Сохраняет совместимость**: при необходимости можно использовать английские имена. + +### Статус + +- ⏳ **Русификация стандартной библиотеки** — в планах. +- ⏳ **Создание заголовочных файлов с русскими именами** — следующий этап. +- ⏳ **Интеграция с компилятором** — после создания библиотеки. + +## Настройка русской раскладки для Linux + +В планах создание **настройки для русской раскладки клавиатуры в Linux**, чтобы программистам не приходилось переключаться между русской и английской раскладками при вводе знаков препинания. + +### Проблема + +При написании кода на русском языке программисты сталкиваются с необходимостью часто переключать раскладку: +- Русская раскладка: буквы на русском, но знаки препинания (`.` , `,` , `;` , `:` , `[` , `]` , `{` , `}` , `(` , `)` , `!` , `?` , `@` , `#` , `$` , `%` , `^` , `&` , `*` , `-` , `_` , `+` , `=` , `~` , `|` , `\` , `/` , `<` , `>` ) находятся в других местах или недоступны. +- Английская раскладка: знаки препинания на привычных местах, но буквы на английском. + +Постоянное переключение раскладки замедляет написание кода и отвлекает от процесса. + +### Решение + +Создание **специальной настройки (раскладки или модификации)** для Linux, которая: + +1. **Сохраняет расположение букв как в русской раскладке** (ЙЦУКЕН или ЙЦУКЕНГ). +2. **Размещает знаки препинания на тех же клавишах, что и в английской раскладке** (`.` на той же клавише, где в английском, `,` на той же и т.д.). + +В результате программист может: +- Писать буквы на русском (без переключения). +- Вводить знаки препинания на тех же клавишах, к которым привык в английской раскладке. + +### Как это будет реализовано + +1. **Создание файла настройки XKB (xkb)** — стандартный способ настройки раскладок в Linux (Xorg и Wayland). +2. **Интеграция с системой** — установка через скрипт или пакет. + +### Пример файла `ru_prog` (русская программистская раскладка) + +```xkb +// ru_prog - русская раскладка со знаками препинания как в английской +xkb_symbols "prog" { + include "ru(ru)" + name[Group1] = "Russian (programming)"; + + // Переопределяем знаки препинания + key { [ apostrophe, asciitilde ] }; // ` ~ + key { [ 1, exclam ] }; // 1 ! + key { [ 2, at ] }; // 2 @ + key { [ 3, numbersign ] }; // 3 # + key { [ 4, dollar ] }; // 4 $ + key { [ 5, percent ] }; // 5 % + key { [ 6, asciicircum ] }; // 6 ^ + key { [ 7, ampersand ] }; // 7 & + key { [ 8, asterisk ] }; // 8 * + key { [ 9, parenleft ] }; // 9 ( + key { [ 0, parenright ] }; // 0 ) + key { [ minus, underscore ] }; // - _ + key { [ equal, plus ] }; // = + + + key { [ q, Q ] }; // q + key { [ w, W ] }; // w + key { [ e, E ] }; // e + key { [ r, R ] }; // r + key { [ t, T ] }; // t + key { [ y, Y ] }; // y + key { [ u, U ] }; // u + key { [ i, I ] }; // i + key { [ o, O ] }; // o + key { [ p, P ] }; // p + key { [ bracketleft, braceleft ] }; // [ { + key { [ bracketright, braceright ] }; // ] } + + key { [ a, A ] }; // a + key { [ s, S ] }; // s + key { [ d, D ] }; // d + key { [ f, F ] }; // f + key { [ g, G ] }; // g + key { [ h, H ] }; // h + key { [ j, J ] }; // j + key { [ k, K ] }; // k + key { [ l, L ] }; // l + key { [ semicolon, colon ] }; // ; : + key { [ apostrophe, quotedbl ] }; // ' " + + key { [ z, Z ] }; // z + key { [ x, X ] }; // x + key { [ c, C ] }; // c + key { [ v, V ] }; // v + key { [ b, B ] }; // b + key { [ n, N ] }; // n + key { [ m, M ] }; // m + key { [ comma, less ] }; // , < + key { [ period, greater ] }; // . > + key { [ slash, question ] }; // / ? +}; +``` + +### Альтернативный подход: использование `setxkbmap` + +Для быстрой настройки можно использовать команду: + +```bash +setxkbmap -layout us,ru -variant ,prog -option grp:alt_shift_toggle +``` + +### Планируемые функции + +1. **Установка через скрипт**: `./настроить_раскладку.sh` — автоматически добавляет раскладку в систему. +2. **Переключение по горячим клавишам**: например, `Alt+Shift` переключает между стандартной русской и программистской русской раскладкой. +3. **Интеграция с редакторами**: автоматическое переключение раскладки при открытии `.рс` файлов. +4. **Документация**: инструкции для различных дистрибутивов Linux (Ubuntu, Fedora, Arch, etc.) + +### Статус + +- ⏳ **Создание файла XKB для русской программистской раскладки** — в планах. +- ⏳ **Скрипт автоматической установки** — следующий этап. +- ⏳ **Документация для различных дистрибутивов** — в планах. + +## Сборка и тестирование + +```bash +# Сборка компилятора +make + +# Запуск стандартных тестов +make test + +# Запуск русских тестов +make test-ru + +# Запуск всех тестов (стандартные + русские) +make test-all + +# Самосборка и тестирование stage2 +make test-stage2 + +# Очистка собранных файлов +make clean +``` + +## Благодарности + +Особая благодарность: +- **Rui Ueyama** за создание [chibicc](https://github.com/rui314/chibicc) — образцово-простого и образовательного компилятора. +- **Alexandre Gaigalas** за [c89cc.sh](https://gist.github.com/alganet/2b89c4368f8d23d033961d8a3deb5c19) — демонстрацию минимализма в реализации компилятора. + +## Статус + +- ✅ Все основные конструкции переведены и протестированы. +- ✅ Двуязычный синтаксис полностью работоспособен. +- ✅ Поддержка русского синтаксиса и смешанного стиля успешно проходит 70+ тестов. +- ✅ Русские имена файлов и расширений поддерживаются. +- ✅ Самосборка (self-hosting) успешно работает. +- ✅ Функция `main` переименована в `главн`. +- ⏳ Миграция исходного кода на русский синтаксис — в процессе. +- ⏳ Портирование КВС с Python на C — в планах. +- ⏳ Расширение для подсветки синтаксиса VS Code / VS Codium — в планах. +- ⏳ Русификация стандартной библиотеки — в планах. +- ⏳ Настройка русской раскладки для Linux — в планах. + +``` \ No newline at end of file diff --git a/codegen.c b/codegen.c new file mode 100644 index 0000000..c2355fb --- /dev/null +++ b/codegen.c @@ -0,0 +1,1568 @@ +#включить "руси.з" + +// Максимальное количество аргументов в регистрах общего назначения и XMM +#определить GP_MAX 6 +#определить FP_MAX 8 + +стат FILE *выходной_файл; +стат цел глубина_стека; +стат символ *арг_рег8[] = {"%dil", "%sil", "%dl", "%cl", "%r8b", "%r9b"}; +стат символ *арг_рег16[] = {"%di", "%si", "%dx", "%cx", "%r8w", "%r9w"}; +стат символ *арг_рег32[] = {"%edi", "%esi", "%edx", "%ecx", "%r8d", "%r9d"}; +стат символ *арг_рег64[] = {"%rdi", "%rsi", "%rdx", "%rcx", "%r8", "%r9"}; +стат Obj *текущая_функция; + +стат пусто сгенерировать_выражение(Node *узел); +стат пусто сгенерировать_оператор(Node *узел); + +// Вывод строки в выходной файл с форматированием +__attribute__((format(printf, 1, 2))) +стат пусто вывести_строку(символ *fmt, ...) { + va_list args; + va_start(args, fmt); + vfprintf(выходной_файл, fmt, args); + va_end(args); + fprintf(выходной_файл, "\n"); +} + +// Генератор уникальных номеров меток +стат цел сгенерировать_номер_метки(пусто) { + стат цел счетчик_меток = 1; + возврат счетчик_меток++; +} + +// Поместить значение из RAX в стек +стат пусто положить_в_стек(пусто) { + вывести_строку(" push %%rax"); + глубина_стека++; +} + +// Извлечь значение из стека в указанный регистр +стат пусто извлечь_из_стека(символ *рег) { + вывести_строку(" pop %s", рег); + глубина_стека--; +} + +// Поместить значение из XMM0 в стек (для чисел с плавающей точкой) +стат пусто положить_дробное_в_стек(пусто) { + вывести_строку(" sub $8, %%rsp"); + вывести_строку(" movsd %%xmm0, (%%rsp)"); + глубина_стека++; +} + +// Извлечь значение из стека в XMM регистр +стат пусто извлечь_дробное_из_стека(цел номер_рег) { + вывести_строку(" movsd (%%rsp), %%xmm%d", номер_рег); + вывести_строку(" add $8, %%rsp"); + глубина_стека--; +} + +// Округление числа вверх до ближайшего кратного align +цел align_to(цел число, цел выравнивание) { + возврат (число + выравнивание - 1) / выравнивание * выравнивание; +} + +// Получить имя регистра DX в зависимости от размера +стат символ *получить_рег_dx(цел разм) { + выбор (разм) { + случай 1: возврат "%dl"; + случай 2: возврат "%dx"; + случай 4: возврат "%edx"; + случай 8: возврат "%rdx"; + } + unreachable(); +} + +// Получить имя регистра AX в зависимости от размера +стат символ *получить_рег_ax(цел разм) { + выбор (разм) { + случай 1: возврат "%al"; + случай 2: возврат "%ax"; + случай 4: возврат "%eax"; + случай 8: возврат "%rax"; + } + unreachable(); +} + +// Генерация абсолютного адреса для узла +стат пусто сгенерировать_адрес(Node *узел) { + выбор (узел->kind) { + случай ND_VAR: + если (узел->var->ty->kind == TY_VLA) { + вывести_строку(" mov %d(%%rbp), %%rax", узел->var->offset); + возврат; + } + + если (узел->var->is_local) { + вывести_строку(" lea %d(%%rbp), %%rax", узел->var->offset); + возврат; + } + + если (opt_fpic) { + если (узел->var->is_tls) { + вывести_строку(" data16 lea %s@tlsgd(%%rip), %%rdi", узел->var->name); + вывести_строку(" .value 0x6666"); + вывести_строку(" rex64"); + вывести_строку(" call __tls_get_addr@PLT"); + возврат; + } + + вывести_строку(" mov %s@GOTPCREL(%%rip), %%rax", узел->var->name); + возврат; + } + + если (узел->var->is_tls) { + вывести_строку(" mov %%fs:0, %%rax"); + вывести_строку(" add $%s@tpoff, %%rax", узел->var->name); + возврат; + } + + если (узел->ty->kind == TY_FUNC) { + если (узел->var->is_definition) + вывести_строку(" lea %s(%%rip), %%rax", узел->var->name); + иначе + вывести_строку(" mov %s@GOTPCREL(%%rip), %%rax", узел->var->name); + возврат; + } + + вывести_строку(" lea %s(%%rip), %%rax", узел->var->name); + возврат; + + случай ND_DEREF: + сгенерировать_выражение(узел->lhs); + возврат; + + случай ND_COMMA: + сгенерировать_выражение(узел->lhs); + сгенерировать_адрес(узел->rhs); + возврат; + + случай ND_MEMBER: + сгенерировать_адрес(узел->lhs); + вывести_строку(" add $%d, %%rax", узел->member->offset); + возврат; + + случай ND_FUNCALL: + если (узел->ret_buffer) { + сгенерировать_выражение(узел); + возврат; + } + прерви; + + случай ND_ASSIGN: + случай ND_COND: + если (узел->ty->kind == TY_STRUCT || узел->ty->kind == TY_UNION) { + сгенерировать_выражение(узел); + возврат; + } + прерви; + + случай ND_VLA_PTR: + вывести_строку(" lea %d(%%rbp), %%rax", узел->var->offset); + возврат; + } + + error_tok(узел->tok, "not an lvalue"); +} + +// Загрузка значения из памяти по адресу в RAX +стат пусто загрузить_значение(Type *тип) { + выбор (тип->kind) { + случай TY_ARRAY: + случай TY_STRUCT: + случай TY_UNION: + случай TY_FUNC: + случай TY_VLA: + возврат; + + случай TY_FLOAT: + вывести_строку(" movss (%%rax), %%xmm0"); + возврат; + + случай TY_DOUBLE: + вывести_строку(" movsd (%%rax), %%xmm0"); + возврат; + + случай TY_LDOUBLE: + вывести_строку(" fldt (%%rax)"); + возврат; + } + + символ *инструкция = тип->is_unsigned ? "movz" : "movs"; + + если (тип->size == 1) + вывести_строку(" %sbl (%%rax), %%eax", инструкция); + иначе если (тип->size == 2) + вывести_строку(" %swl (%%rax), %%eax", инструкция); + иначе если (тип->size == 4) + вывести_строку(" movsxd (%%rax), %%rax"); + иначе + вывести_строку(" mov (%%rax), %%rax"); +} + +// Сохранение значения из RAX по адресу на вершине стека +стат пусто сохранить_значение(Type *тип) { + извлечь_из_стека("%rdi"); + + выбор (тип->kind) { + случай TY_STRUCT: + случай TY_UNION: + для (цел i = 0; i < тип->size; i++) { + вывести_строку(" mov %d(%%rax), %%r8b", i); + вывести_строку(" mov %%r8b, %d(%%rdi)", i); + } + возврат; + + случай TY_FLOAT: + вывести_строку(" movss %%xmm0, (%%rdi)"); + возврат; + + случай TY_DOUBLE: + вывести_строку(" movsd %%xmm0, (%%rdi)"); + возврат; + + случай TY_LDOUBLE: + вывести_строку(" fstpt (%%rdi)"); + возврат; + } + + если (тип->size == 1) + вывести_строку(" mov %%al, (%%rdi)"); + иначе если (тип->size == 2) + вывести_строку(" mov %%ax, (%%rdi)"); + иначе если (тип->size == 4) + вывести_строку(" mov %%eax, (%%rdi)"); + иначе + вывести_строку(" mov %%rax, (%%rdi)"); +} + +// Сравнение значения с нулем +стат пусто сравнить_с_нулем(Type *тип) { + выбор (тип->kind) { + случай TY_FLOAT: + вывести_строку(" xorps %%xmm1, %%xmm1"); + вывести_строку(" ucomiss %%xmm1, %%xmm0"); + возврат; + + случай TY_DOUBLE: + вывести_строку(" xorpd %%xmm1, %%xmm1"); + вывести_строку(" ucomisd %%xmm1, %%xmm0"); + возврат; + + случай TY_LDOUBLE: + вывести_строку(" fldz"); + вывести_строку(" fucomip"); + вывести_строку(" fstp %%st(0)"); + возврат; + } + + если (is_integer(тип) && тип->size <= 4) + вывести_строку(" cmp $0, %%eax"); + иначе + вывести_строку(" cmp $0, %%rax"); +} + +// Идентификаторы типов для таблицы преобразований +переч { I8, I16, I32, I64, U8, U16, U32, U64, F32, F64, F80 }; + +// Получение идентификатора типа +стат цел получить_ид_типа(Type *тип) { + выбор (тип->kind) { + случай TY_CHAR: + возврат тип->is_unsigned ? U8 : I8; + случай TY_SHORT: + возврат тип->is_unsigned ? U16 : I16; + случай TY_INT: + возврат тип->is_unsigned ? U32 : I32; + случай TY_LONG: + возврат тип->is_unsigned ? U64 : I64; + случай TY_FLOAT: + возврат F32; + случай TY_DOUBLE: + возврат F64; + случай TY_LDOUBLE: + возврат F80; + } + возврат U64; +} + +// Таблица инструкций для преобразования типов +стат символ i32i8[] = "movsbl %al, %eax"; +стат символ i32u8[] = "movzbl %al, %eax"; +стат символ i32i16[] = "movswl %ax, %eax"; +стат символ i32u16[] = "movzwl %ax, %eax"; +стат символ i32f32[] = "cvtsi2ssl %eax, %xmm0"; +стат символ i32i64[] = "movsxd %eax, %rax"; +стат символ i32f64[] = "cvtsi2sdl %eax, %xmm0"; +стат символ i32f80[] = "mov %eax, -4(%rsp); fildl -4(%rsp)"; + +стат символ u32f32[] = "mov %eax, %eax; cvtsi2ssq %rax, %xmm0"; +стат символ u32i64[] = "mov %eax, %eax"; +стат символ u32f64[] = "mov %eax, %eax; cvtsi2sdq %rax, %xmm0"; +стат символ u32f80[] = "mov %eax, %eax; mov %rax, -8(%rsp); fildll -8(%rsp)"; + +стат символ i64f32[] = "cvtsi2ssq %rax, %xmm0"; +стат символ i64f64[] = "cvtsi2sdq %rax, %xmm0"; +стат символ i64f80[] = "movq %rax, -8(%rsp); fildll -8(%rsp)"; + +стат символ u64f32[] = "cvtsi2ssq %rax, %xmm0"; +стат символ u64f64[] = + "test %rax,%rax; js 1f; pxor %xmm0,%xmm0; cvtsi2sd %rax,%xmm0; jmp 2f; " + "1: mov %rax,%rdi; and $1,%eax; pxor %xmm0,%xmm0; shr %rdi; " + "or %rax,%rdi; cvtsi2sd %rdi,%xmm0; addsd %xmm0,%xmm0; 2:"; +стат символ u64f80[] = + "mov %rax, -8(%rsp); fildq -8(%rsp); test %rax, %rax; jns 1f;" + "mov $1602224128, %eax; mov %eax, -4(%rsp); fadds -4(%rsp); 1:"; + +стат символ f32i8[] = "cvttss2sil %xmm0, %eax; movsbl %al, %eax"; +стат символ f32u8[] = "cvttss2sil %xmm0, %eax; movzbl %al, %eax"; +стат символ f32i16[] = "cvttss2sil %xmm0, %eax; movswl %ax, %eax"; +стат символ f32u16[] = "cvttss2sil %xmm0, %eax; movzwl %ax, %eax"; +стат символ f32i32[] = "cvttss2sil %xmm0, %eax"; +стат символ f32u32[] = "cvttss2siq %xmm0, %rax"; +стат символ f32i64[] = "cvttss2siq %xmm0, %rax"; +стат символ f32u64[] = "cvttss2siq %xmm0, %rax"; +стат символ f32f64[] = "cvtss2sd %xmm0, %xmm0"; +стат символ f32f80[] = "movss %xmm0, -4(%rsp); flds -4(%rsp)"; + +стат символ f64i8[] = "cvttsd2sil %xmm0, %eax; movsbl %al, %eax"; +стат символ f64u8[] = "cvttsd2sil %xmm0, %eax; movzbl %al, %eax"; +стат символ f64i16[] = "cvttsd2sil %xmm0, %eax; movswl %ax, %eax"; +стат символ f64u16[] = "cvttsd2sil %xmm0, %eax; movzwl %ax, %eax"; +стат символ f64i32[] = "cvttsd2sil %xmm0, %eax"; +стат символ f64u32[] = "cvttsd2siq %xmm0, %rax"; +стат символ f64i64[] = "cvttsd2siq %xmm0, %rax"; +стат символ f64u64[] = "cvttsd2siq %xmm0, %rax"; +стат символ f64f32[] = "cvtsd2ss %xmm0, %xmm0"; +стат символ f64f80[] = "movsd %xmm0, -8(%rsp); fldl -8(%rsp)"; + +#определить FROM_F80_1 \ + "fnstcw -10(%rsp); movzwl -10(%rsp), %eax; or $12, %ah; " \ + "mov %ax, -12(%rsp); fldcw -12(%rsp); " + +#определить FROM_F80_2 " -24(%rsp); fldcw -10(%rsp); " + +стат символ f80i8[] = FROM_F80_1 "fistps" FROM_F80_2 "movsbl -24(%rsp), %eax"; +стат символ f80u8[] = FROM_F80_1 "fistps" FROM_F80_2 "movzbl -24(%rsp), %eax"; +стат символ f80i16[] = FROM_F80_1 "fistps" FROM_F80_2 "movzbl -24(%rsp), %eax"; +стат символ f80u16[] = FROM_F80_1 "fistpl" FROM_F80_2 "movswl -24(%rsp), %eax"; +стат символ f80i32[] = FROM_F80_1 "fistpl" FROM_F80_2 "mov -24(%rsp), %eax"; +стат символ f80u32[] = FROM_F80_1 "fistpl" FROM_F80_2 "mov -24(%rsp), %eax"; +стат символ f80i64[] = FROM_F80_1 "fistpq" FROM_F80_2 "mov -24(%rsp), %rax"; +стат символ f80u64[] = FROM_F80_1 "fistpq" FROM_F80_2 "mov -24(%rsp), %rax"; +стат символ f80f32[] = "fstps -8(%rsp); movss -8(%rsp), %xmm0"; +стат символ f80f64[] = "fstpl -8(%rsp); movsd -8(%rsp), %xmm0"; + +// Таблица преобразований типов +стат символ *таблица_преобразований[][11] = { + {NULL, NULL, NULL, i32i64, i32u8, i32u16, NULL, i32i64, i32f32, i32f64, i32f80}, + {i32i8, NULL, NULL, i32i64, i32u8, i32u16, NULL, i32i64, i32f32, i32f64, i32f80}, + {i32i8, i32i16, NULL, i32i64, i32u8, i32u16, NULL, i32i64, i32f32, i32f64, i32f80}, + {i32i8, i32i16, NULL, NULL, i32u8, i32u16, NULL, NULL, i64f32, i64f64, i64f80}, + {i32i8, NULL, NULL, i32i64, NULL, NULL, NULL, i32i64, i32f32, i32f64, i32f80}, + {i32i8, i32i16, NULL, i32i64, i32u8, NULL, NULL, i32i64, i32f32, i32f64, i32f80}, + {i32i8, i32i16, NULL, u32i64, i32u8, i32u16, NULL, u32i64, u32f32, u32f64, u32f80}, + {i32i8, i32i16, NULL, NULL, i32u8, i32u16, NULL, NULL, u64f32, u64f64, u64f80}, + {f32i8, f32i16, f32i32, f32i64, f32u8, f32u16, f32u32, f32u64, NULL, f32f64, f32f80}, + {f64i8, f64i16, f64i32, f64i64, f64u8, f64u16, f64u32, f64u64, f64f32, NULL, f64f80}, + {f80i8, f80i16, f80i32, f80i64, f80u8, f80u16, f80u32, f80u64, f80f32, f80f64, NULL}, +}; + +// Генерация кода для преобразования типа +стат пусто сгенерировать_приведение_типа(Type *от_типа, Type *к_типу) { + если (к_типу->kind == TY_VOID) + возврат; + + если (к_типу->kind == TY_BOOL) { + сравнить_с_нулем(от_типа); + вывести_строку(" setne %%al"); + вывести_строку(" movzx %%al, %%eax"); + возврат; + } + + цел ид_источника = получить_ид_типа(от_типа); + цел ид_назначения = получить_ид_типа(к_типу); + если (таблица_преобразований[ид_источника][ид_назначения]) + вывести_строку(" %s", таблица_преобразований[ид_источника][ид_назначения]); +} + +// Проверка, содержит ли структура только вещественные члены в заданном диапазоне байт +стат бул только_дробные_члены(Type *тип, цел начало, цел конец, цел смещение) { + если (тип->kind == TY_STRUCT || тип->kind == TY_UNION) { + для (Member *член = тип->members; член; член = член->next) + если (!только_дробные_члены(член->ty, начало, конец, смещение + член->offset)) + возврат ложь; + возврат истина; + } + + если (тип->kind == TY_ARRAY) { + для (цел i = 0; i < тип->array_len; i++) + если (!только_дробные_члены(тип->base, начало, конец, смещение + тип->base->size * i)) + возврат ложь; + возврат истина; + } + + возврат смещение < начало || конец <= смещение || тип->kind == TY_FLOAT || тип->kind == TY_DOUBLE; +} + +стат бул первые_8_байт_дробные(Type *тип) { + возврат только_дробные_члены(тип, 0, 8, 0); +} + +стат бул вторые_8_байт_дробные(Type *тип) { + возврат только_дробные_члены(тип, 8, 16, 0); +} + +// Поместить структуру в стек +стат пусто положить_структуру_в_стек(Type *тип) { + цел выровненный_разм = align_to(тип->size, 8); + вывести_строку(" sub $%d, %%rsp", выровненный_разм); + глубина_стека += выровненный_разм / 8; + + для (цел i = 0; i < тип->size; i++) { + вывести_строку(" mov %d(%%rax), %%r10b", i); + вывести_строку(" mov %%r10b, %d(%%rsp)", i); + } +} + +// Рекурсивная обработка аргументов функции +стат пусто обработать_аргументы_функции(Node *арг, бул первый_проход) { + если (!арг) + возврат; + обработать_аргументы_функции(арг->next, первый_проход); + + если ((первый_проход && !арг->pass_by_stack) || (!первый_проход && арг->pass_by_stack)) + возврат; + + сгенерировать_выражение(арг); + + выбор (арг->ty->kind) { + случай TY_STRUCT: + случай TY_UNION: + положить_структуру_в_стек(арг->ty); + прерви; + случай TY_FLOAT: + случай TY_DOUBLE: + положить_дробное_в_стек(); + прерви; + случай TY_LDOUBLE: + вывести_строку(" sub $16, %%rsp"); + вывести_строку(" fstpt (%%rsp)"); + глубина_стека += 2; + прерви; + умолч: + положить_в_стек(); + } +} + +// Подготовка аргументов функции согласно x86-64 ABI +стат цел подготовить_аргументы_функции(Node *узел) { + цел стек_арг = 0, gp_счет = 0, fp_счет = 0; + + если (узел->ret_buffer && узел->ty->size > 16) + gp_счет++; + + для (Node *арг = узел->args; арг; арг = арг->next) { + Type *тип_арг = арг->ty; + + выбор (тип_арг->kind) { + случай TY_STRUCT: + случай TY_UNION: + если (тип_арг->size > 16) { + арг->pass_by_stack = истина; + стек_арг += align_to(тип_арг->size, 8) / 8; + } иначе { + бул fp1 = первые_8_байт_дробные(тип_арг); + бул fp2 = вторые_8_байт_дробные(тип_арг); + + если (fp_счет + fp1 + fp2 < FP_MAX && gp_счет + !fp1 + !fp2 < GP_MAX) { + fp_счет = fp_счет + fp1 + fp2; + gp_счет = gp_счет + !fp1 + !fp2; + } иначе { + арг->pass_by_stack = истина; + стек_арг += align_to(тип_арг->size, 8) / 8; + } + } + прерви; + + случай TY_FLOAT: + случай TY_DOUBLE: + если (fp_счет++ >= FP_MAX) { + арг->pass_by_stack = истина; + стек_арг++; + } + прерви; + + случай TY_LDOUBLE: + арг->pass_by_stack = истина; + стек_арг += 2; + прерви; + + умолч: + если (gp_счет++ >= GP_MAX) { + арг->pass_by_stack = истина; + стек_арг++; + } + } + } + + если ((глубина_стека + стек_арг) % 2 == 1) { + вывести_строку(" sub $8, %%rsp"); + глубина_стека++; + стек_арг++; + } + + обработать_аргументы_функции(узел->args, true); + обработать_аргументы_функции(узел->args, false); + + если (узел->ret_buffer && узел->ty->size > 16) { + вывести_строку(" lea %d(%%rbp), %%rax", узел->ret_buffer->offset); + положить_в_стек(); + } + + возврат стек_арг; +} + +// Копирование возвращаемого значения структуры в буфер +стат пусто скопировать_буфер_возврата(Obj *перем) { + Type *тип_перем = перем->ty; + цел gp_счет = 0, fp_счет = 0; + + если (первые_8_байт_дробные(тип_перем)) { + assert(тип_перем->size == 4 || 8 <= тип_перем->size); + если (тип_перем->size == 4) + вывести_строку(" movss %%xmm0, %d(%%rbp)", перем->offset); + иначе + вывести_строку(" movsd %%xmm0, %d(%%rbp)", перем->offset); + fp_счет++; + } иначе { + для (цел i = 0; i < MIN(8, тип_перем->size); i++) { + вывести_строку(" mov %%al, %d(%%rbp)", перем->offset + i); + вывести_строку(" shr $8, %%rax"); + } + gp_счет++; + } + + если (тип_перем->size > 8) { + если (вторые_8_байт_дробные(тип_перем)) { + assert(тип_перем->size == 12 || тип_перем->size == 16); + если (тип_перем->size == 12) + вывести_строку(" movss %%xmm%d, %d(%%rbp)", fp_счет, перем->offset + 8); + иначе + вывести_строку(" movsd %%xmm%d, %d(%%rbp)", fp_счет, перем->offset + 8); + } иначе { + символ *рег1 = (gp_счет == 0) ? "%al" : "%dl"; + символ *рег2 = (gp_счет == 0) ? "%rax" : "%rdx"; + для (цел i = 8; i < MIN(16, тип_перем->size); i++) { + вывести_строку(" mov %s, %d(%%rbp)", рег1, перем->offset + i); + вывести_строку(" shr $8, %s", рег2); + } + } + } +} + +// Копирование структуры в регистры для возврата +стат пусто скопировать_структуру_в_регистры(пусто) { + Type *тип_возврата = текущая_функция->ty->return_ty; + цел gp_счет = 0, fp_счет = 0; + + вывести_строку(" mov %%rax, %%rdi"); + + если (только_дробные_члены(тип_возврата, 0, 8, 0)) { + assert(тип_возврата->size == 4 || 8 <= тип_возврата->size); + если (тип_возврата->size == 4) + вывести_строку(" movss (%%rdi), %%xmm0"); + иначе + вывести_строку(" movsd (%%rdi), %%xmm0"); + fp_счет++; + } иначе { + вывести_строку(" mov $0, %%rax"); + для (цел i = MIN(8, тип_возврата->size) - 1; i >= 0; i--) { + вывести_строку(" shl $8, %%rax"); + вывести_строку(" mov %d(%%rdi), %%al", i); + } + gp_счет++; + } + + если (тип_возврата->size > 8) { + если (только_дробные_члены(тип_возврата, 8, 16, 0)) { + assert(тип_возврата->size == 12 || тип_возврата->size == 16); + если (тип_возврата->size == 4) + вывести_строку(" movss 8(%%rdi), %%xmm%d", fp_счет); + иначе + вывести_строку(" movsd 8(%%rdi), %%xmm%d", fp_счет); + } иначе { + символ *рег1 = (gp_счет == 0) ? "%al" : "%dl"; + символ *рег2 = (gp_счет == 0) ? "%rax" : "%rdx"; + вывести_строку(" mov $0, %s", рег2); + для (цел i = MIN(16, тип_возврата->size) - 1; i >= 8; i--) { + вывести_строку(" shl $8, %s", рег2); + вывести_строку(" mov %d(%%rdi), %s", i, рег1); + } + } + } +} + +// Копирование структуры в память для возврата +стат пусто скопировать_структуру_в_память(пусто) { + Type *тип_возврата = текущая_функция->ty->return_ty; + Obj *парам = текущая_функция->params; + + вывести_строку(" mov %d(%%rbp), %%rdi", парам->offset); + + для (цел i = 0; i < тип_возврата->size; i++) { + вывести_строку(" mov %d(%%rax), %%dl", i); + вывести_строку(" mov %%dl, %d(%%rdi)", i); + } +} + +// Встроенная функция alloca +стат пусто сгенерировать_alloca(пусто) { + вывести_строку(" add $15, %%rdi"); + вывести_строку(" and $0xfffffff0, %%edi"); + + вывести_строку(" mov %d(%%rbp), %%rcx", текущая_функция->alloca_bottom->offset); + вывести_строку(" sub %%rsp, %%rcx"); + вывести_строку(" mov %%rsp, %%rax"); + вывести_строку(" sub %%rdi, %%rsp"); + вывести_строку(" mov %%rsp, %%rdx"); + вывести_строку("1:"); + вывести_строку(" cmp $0, %%rcx"); + вывести_строку(" je 2f"); + вывести_строку(" mov (%%rax), %%r8b"); + вывести_строку(" mov %%r8b, (%%rdx)"); + вывести_строку(" inc %%rdx"); + вывести_строку(" inc %%rax"); + вывести_строку(" dec %%rcx"); + вывести_строку(" jmp 1b"); + вывести_строку("2:"); + + вывести_строку(" mov %d(%%rbp), %%rax", текущая_функция->alloca_bottom->offset); + вывести_строку(" sub %%rdi, %%rax"); + вывести_строку(" mov %%rax, %d(%%rbp)", текущая_функция->alloca_bottom->offset); +} + +// Генерация кода для выражения +стат пусто сгенерировать_выражение(Node *узел) { + вывести_строку(" .loc %d %d", узел->tok->file->file_no, узел->tok->line_no); + + выбор (узел->kind) { + случай ND_NULL_EXPR: + возврат; + + случай ND_NUM: { + выбор (узел->ty->kind) { + случай TY_FLOAT: { + объед { плав f32; uint32_t u32; } u = { узел->fval }; + вывести_строку(" mov $%u, %%eax # float %Lf", u.u32, узел->fval); + вывести_строку(" movq %%rax, %%xmm0"); + возврат; + } + случай TY_DOUBLE: { + объед { двойн f64; uint64_t u64; } u = { узел->fval }; + вывести_строку(" mov $%lu, %%rax # double %Lf", u.u64, узел->fval); + вывести_строку(" movq %%rax, %%xmm0"); + возврат; + } + случай TY_LDOUBLE: { + объед { длин двойн f80; uint64_t u64[2]; } u; + memset(&u, 0, размер(u)); + u.f80 = узел->fval; + вывести_строку(" mov $%lu, %%rax # long double %Lf", u.u64[0], узел->fval); + вывести_строку(" mov %%rax, -16(%%rsp)"); + вывести_строку(" mov $%lu, %%rax", u.u64[1]); + вывести_строку(" mov %%rax, -8(%%rsp)"); + вывести_строку(" fldt -16(%%rsp)"); + возврат; + } + } + + вывести_строку(" mov $%ld, %%rax", узел->val); + возврат; + } + + случай ND_NEG: + сгенерировать_выражение(узел->lhs); + + выбор (узел->ty->kind) { + случай TY_FLOAT: + вывести_строку(" mov $1, %%rax"); + вывести_строку(" shl $31, %%rax"); + вывести_строку(" movq %%rax, %%xmm1"); + вывести_строку(" xorps %%xmm1, %%xmm0"); + возврат; + случай TY_DOUBLE: + вывести_строку(" mov $1, %%rax"); + вывести_строку(" shl $63, %%rax"); + вывести_строку(" movq %%rax, %%xmm1"); + вывести_строку(" xorpd %%xmm1, %%xmm0"); + возврат; + случай TY_LDOUBLE: + вывести_строку(" fchs"); + возврат; + } + + вывести_строку(" neg %%rax"); + возврат; + + случай ND_VAR: + сгенерировать_адрес(узел); + загрузить_значение(узел->ty); + возврат; + + случай ND_MEMBER: { + сгенерировать_адрес(узел); + загрузить_значение(узел->ty); + + Member *член = узел->member; + если (член->is_bitfield) { + вывести_строку(" shl $%d, %%rax", 64 - член->bit_width - член->bit_offset); + если (член->ty->is_unsigned) + вывести_строку(" shr $%d, %%rax", 64 - член->bit_width); + иначе + вывести_строку(" sar $%d, %%rax", 64 - член->bit_width); + } + возврат; + } + + случай ND_DEREF: + сгенерировать_выражение(узел->lhs); + загрузить_значение(узел->ty); + возврат; + + случай ND_ADDR: + сгенерировать_адрес(узел->lhs); + возврат; + + случай ND_ASSIGN: + сгенерировать_адрес(узел->lhs); + положить_в_стек(); + сгенерировать_выражение(узел->rhs); + + если (узел->lhs->kind == ND_MEMBER && узел->lhs->member->is_bitfield) { + вывести_строку(" mov %%rax, %%r8"); + + Member *член = узел->lhs->member; + вывести_строку(" mov %%rax, %%rdi"); + вывести_строку(" and $%ld, %%rdi", (1L << член->bit_width) - 1); + вывести_строку(" shl $%d, %%rdi", член->bit_offset); + + вывести_строку(" mov (%%rsp), %%rax"); + загрузить_значение(член->ty); + + длин маска = ((1L << член->bit_width) - 1) << член->bit_offset; + вывести_строку(" mov $%ld, %%r9", ~маска); + вывести_строку(" and %%r9, %%rax"); + вывести_строку(" or %%rdi, %%rax"); + сохранить_значение(узел->ty); + вывести_строку(" mov %%r8, %%rax"); + возврат; + } + + сохранить_значение(узел->ty); + возврат; + + случай ND_STMT_EXPR: + для (Node *n = узел->body; n; n = n->next) + сгенерировать_оператор(n); + возврат; + + случай ND_COMMA: + сгенерировать_выражение(узел->lhs); + сгенерировать_выражение(узел->rhs); + возврат; + + случай ND_CAST: + сгенерировать_выражение(узел->lhs); + сгенерировать_приведение_типа(узел->lhs->ty, узел->ty); + возврат; + + случай ND_MEMZERO: + вывести_строку(" mov $%d, %%rcx", узел->var->ty->size); + вывести_строку(" lea %d(%%rbp), %%rdi", узел->var->offset); + вывести_строку(" mov $0, %%al"); + вывести_строку(" rep stosb"); + возврат; + + случай ND_COND: { + цел метка = сгенерировать_номер_метки(); + сгенерировать_выражение(узел->cond); + сравнить_с_нулем(узел->cond->ty); + вывести_строку(" je .L.else.%d", метка); + сгенерировать_выражение(узел->then); + вывести_строку(" jmp .L.end.%d", метка); + вывести_строку(".L.else.%d:", метка); + сгенерировать_выражение(узел->els); + вывести_строку(".L.end.%d:", метка); + возврат; + } + + случай ND_NOT: + сгенерировать_выражение(узел->lhs); + сравнить_с_нулем(узел->lhs->ty); + вывести_строку(" sete %%al"); + вывести_строку(" movzx %%al, %%rax"); + возврат; + + случай ND_BITNOT: + сгенерировать_выражение(узел->lhs); + вывести_строку(" not %%rax"); + возврат; + + случай ND_LOGAND: { + цел метка = сгенерировать_номер_метки(); + сгенерировать_выражение(узел->lhs); + сравнить_с_нулем(узел->lhs->ty); + вывести_строку(" je .L.false.%d", метка); + сгенерировать_выражение(узел->rhs); + сравнить_с_нулем(узел->rhs->ty); + вывести_строку(" je .L.false.%d", метка); + вывести_строку(" mov $1, %%rax"); + вывести_строку(" jmp .L.end.%d", метка); + вывести_строку(".L.false.%d:", метка); + вывести_строку(" mov $0, %%rax"); + вывести_строку(".L.end.%d:", метка); + возврат; + } + + случай ND_LOGOR: { + цел метка = сгенерировать_номер_метки(); + сгенерировать_выражение(узел->lhs); + сравнить_с_нулем(узел->lhs->ty); + вывести_строку(" jne .L.true.%d", метка); + сгенерировать_выражение(узел->rhs); + сравнить_с_нулем(узел->rhs->ty); + вывести_строку(" jne .L.true.%d", метка); + вывести_строку(" mov $0, %%rax"); + вывести_строку(" jmp .L.end.%d", метка); + вывести_строку(".L.true.%d:", метка); + вывести_строку(" mov $1, %%rax"); + вывести_строку(".L.end.%d:", метка); + возврат; + } + + случай ND_FUNCALL: { + если (узел->lhs->kind == ND_VAR && !strcmp(узел->lhs->var->name, "alloca")) { + сгенерировать_выражение(узел->args); + вывести_строку(" mov %%rax, %%rdi"); + сгенерировать_alloca(); + возврат; + } + + цел стек_арг = подготовить_аргументы_функции(узел); + сгенерировать_выражение(узел->lhs); + + цел gp_счет = 0, fp_счет = 0; + + если (узел->ret_buffer && узел->ty->size > 16) + извлечь_из_стека(арг_рег64[gp_счет++]); + + для (Node *арг = узел->args; арг; арг = арг->next) { + Type *тип_арг = арг->ty; + + выбор (тип_арг->kind) { + случай TY_STRUCT: + случай TY_UNION: + если (тип_арг->size > 16) + продолжи; + + бул fp1 = первые_8_байт_дробные(тип_арг); + бул fp2 = вторые_8_байт_дробные(тип_арг); + + если (fp_счет + fp1 + fp2 < FP_MAX && gp_счет + !fp1 + !fp2 < GP_MAX) { + если (fp1) + извлечь_дробное_из_стека(fp_счет++); + иначе + извлечь_из_стека(арг_рег64[gp_счет++]); + + если (тип_арг->size > 8) { + если (fp2) + извлечь_дробное_из_стека(fp_счет++); + иначе + извлечь_из_стека(арг_рег64[gp_счет++]); + } + } + прерви; + + случай TY_FLOAT: + случай TY_DOUBLE: + если (fp_счет < FP_MAX) + извлечь_дробное_из_стека(fp_счет++); + прерви; + + случай TY_LDOUBLE: + прерви; + + умолч: + если (gp_счет < GP_MAX) + извлечь_из_стека(арг_рег64[gp_счет++]); + } + } + + вывести_строку(" mov %%rax, %%r10"); + вывести_строку(" mov $%d, %%rax", fp_счет); + вывести_строку(" call *%%r10"); + вывести_строку(" add $%d, %%rsp", стек_арг * 8); + + глубина_стека -= стек_арг; + + выбор (узел->ty->kind) { + случай TY_BOOL: + вывести_строку(" movzx %%al, %%eax"); + возврат; + случай TY_CHAR: + если (узел->ty->is_unsigned) + вывести_строку(" movzbl %%al, %%eax"); + иначе + вывести_строку(" movsbl %%al, %%eax"); + возврат; + случай TY_SHORT: + если (узел->ty->is_unsigned) + вывести_строку(" movzwl %%ax, %%eax"); + иначе + вывести_строку(" movswl %%ax, %%eax"); + возврат; + } + + если (узел->ret_buffer && узел->ty->size <= 16) { + скопировать_буфер_возврата(узел->ret_buffer); + вывести_строку(" lea %d(%%rbp), %%rax", узел->ret_buffer->offset); + } + + возврат; + } + + случай ND_LABEL_VAL: + вывести_строку(" lea %s(%%rip), %%rax", узел->unique_label); + возврат; + + случай ND_CAS: { + сгенерировать_выражение(узел->cas_addr); + положить_в_стек(); + сгенерировать_выражение(узел->cas_new); + положить_в_стек(); + сгенерировать_выражение(узел->cas_old); + вывести_строку(" mov %%rax, %%r8"); + загрузить_значение(узел->cas_old->ty->base); + извлечь_из_стека("%rdx"); + извлечь_из_стека("%rdi"); + + цел разм = узел->cas_addr->ty->base->size; + вывести_строку(" lock cmpxchg %s, (%%rdi)", получить_рег_dx(разм)); + вывести_строку(" sete %%cl"); + вывести_строку(" je 1f"); + вывести_строку(" mov %s, (%%r8)", получить_рег_ax(разм)); + вывести_строку("1:"); + вывести_строку(" movzbl %%cl, %%eax"); + возврат; + } + + случай ND_EXCH: { + сгенерировать_выражение(узел->lhs); + положить_в_стек(); + сгенерировать_выражение(узел->rhs); + извлечь_из_стека("%rdi"); + + цел разм = узел->lhs->ty->base->size; + вывести_строку(" xchg %s, (%%rdi)", получить_рег_ax(разм)); + возврат; + } + } + + // Арифметические операции с плавающей точкой + выбор (узел->lhs->ty->kind) { + случай TY_FLOAT: + случай TY_DOUBLE: { + сгенерировать_выражение(узел->rhs); + положить_дробное_в_стек(); + сгенерировать_выражение(узел->lhs); + извлечь_дробное_из_стека(1); + + символ *суффикс = (узел->lhs->ty->kind == TY_FLOAT) ? "ss" : "sd"; + + выбор (узел->kind) { + случай ND_ADD: + вывести_строку(" add%s %%xmm1, %%xmm0", суффикс); + возврат; + случай ND_SUB: + вывести_строку(" sub%s %%xmm1, %%xmm0", суффикс); + возврат; + случай ND_MUL: + вывести_строку(" mul%s %%xmm1, %%xmm0", суффикс); + возврат; + случай ND_DIV: + вывести_строку(" div%s %%xmm1, %%xmm0", суффикс); + возврат; + случай ND_EQ: + случай ND_NE: + случай ND_LT: + случай ND_LE: + вывести_строку(" ucomi%s %%xmm0, %%xmm1", суффикс); + + если (узел->kind == ND_EQ) { + вывести_строку(" sete %%al"); + вывести_строку(" setnp %%dl"); + вывести_строку(" and %%dl, %%al"); + } иначе если (узел->kind == ND_NE) { + вывести_строку(" setne %%al"); + вывести_строку(" setp %%dl"); + вывести_строку(" or %%dl, %%al"); + } иначе если (узел->kind == ND_LT) { + вывести_строку(" seta %%al"); + } иначе { + вывести_строку(" setae %%al"); + } + + вывести_строку(" and $1, %%al"); + вывести_строку(" movzb %%al, %%rax"); + возврат; + } + + error_tok(узел->tok, "invalid expression"); + } + + случай TY_LDOUBLE: { + сгенерировать_выражение(узел->lhs); + сгенерировать_выражение(узел->rhs); + + выбор (узел->kind) { + случай ND_ADD: + вывести_строку(" faddp"); + возврат; + случай ND_SUB: + вывести_строку(" fsubrp"); + возврат; + случай ND_MUL: + вывести_строку(" fmulp"); + возврат; + случай ND_DIV: + вывести_строку(" fdivrp"); + возврат; + случай ND_EQ: + случай ND_NE: + случай ND_LT: + случай ND_LE: + вывести_строку(" fcomip"); + вывести_строку(" fstp %%st(0)"); + + если (узел->kind == ND_EQ) + вывести_строку(" sete %%al"); + иначе если (узел->kind == ND_NE) + вывести_строку(" setne %%al"); + иначе если (узел->kind == ND_LT) + вывести_строку(" seta %%al"); + иначе + вывести_строку(" setae %%al"); + + вывести_строку(" movzb %%al, %%rax"); + возврат; + } + + error_tok(узел->tok, "invalid expression"); + } + } + + // Целочисленные арифметические операции + сгенерировать_выражение(узел->rhs); + положить_в_стек(); + сгенерировать_выражение(узел->lhs); + извлечь_из_стека("%rdi"); + + символ *ax_рег, *di_рег, *dx_рег; + + если (узел->lhs->ty->kind == TY_LONG || узел->lhs->ty->base) { + ax_рег = "%rax"; + di_рег = "%rdi"; + dx_рег = "%rdx"; + } иначе { + ax_рег = "%eax"; + di_рег = "%edi"; + dx_рег = "%edx"; + } + + выбор (узел->kind) { + случай ND_ADD: + вывести_строку(" add %s, %s", di_рег, ax_рег); + возврат; + случай ND_SUB: + вывести_строку(" sub %s, %s", di_рег, ax_рег); + возврат; + случай ND_MUL: + вывести_строку(" imul %s, %s", di_рег, ax_рег); + возврат; + случай ND_DIV: + случай ND_MOD: + если (узел->ty->is_unsigned) { + вывести_строку(" mov $0, %s", dx_рег); + вывести_строку(" div %s", di_рег); + } иначе { + если (узел->lhs->ty->size == 8) + вывести_строку(" cqo"); + иначе + вывести_строку(" cdq"); + вывести_строку(" idiv %s", di_рег); + } + + если (узел->kind == ND_MOD) + вывести_строку(" mov %%rdx, %%rax"); + возврат; + случай ND_BITAND: + вывести_строку(" and %s, %s", di_рег, ax_рег); + возврат; + случай ND_BITOR: + вывести_строку(" or %s, %s", di_рег, ax_рег); + возврат; + случай ND_BITXOR: + вывести_строку(" xor %s, %s", di_рег, ax_рег); + возврат; + случай ND_EQ: + случай ND_NE: + случай ND_LT: + случай ND_LE: + вывести_строку(" cmp %s, %s", di_рег, ax_рег); + + если (узел->kind == ND_EQ) { + вывести_строку(" sete %%al"); + } иначе если (узел->kind == ND_NE) { + вывести_строку(" setne %%al"); + } иначе если (узел->kind == ND_LT) { + если (узел->lhs->ty->is_unsigned) + вывести_строку(" setb %%al"); + иначе + вывести_строку(" setl %%al"); + } иначе если (узел->kind == ND_LE) { + если (узел->lhs->ty->is_unsigned) + вывести_строку(" setbe %%al"); + иначе + вывести_строку(" setle %%al"); + } + + вывести_строку(" movzb %%al, %%rax"); + возврат; + случай ND_SHL: + вывести_строку(" mov %%rdi, %%rcx"); + вывести_строку(" shl %%cl, %s", ax_рег); + возврат; + случай ND_SHR: + вывести_строку(" mov %%rdi, %%rcx"); + если (узел->lhs->ty->is_unsigned) + вывести_строку(" shr %%cl, %s", ax_рег); + иначе + вывести_строку(" sar %%cl, %s", ax_рег); + возврат; + } + + error_tok(узел->tok, "invalid expression"); +} + +// Генерация кода для оператора +стат пусто сгенерировать_оператор(Node *узел) { + вывести_строку(" .loc %d %d", узел->tok->file->file_no, узел->tok->line_no); + + выбор (узел->kind) { + случай ND_IF: { + цел метка = сгенерировать_номер_метки(); + сгенерировать_выражение(узел->cond); + сравнить_с_нулем(узел->cond->ty); + вывести_строку(" je .L.else.%d", метка); + сгенерировать_оператор(узел->then); + вывести_строку(" jmp .L.end.%d", метка); + вывести_строку(".L.else.%d:", метка); + если (узел->els) + сгенерировать_оператор(узел->els); + вывести_строку(".L.end.%d:", метка); + возврат; + } + + случай ND_FOR: { + цел метка = сгенерировать_номер_метки(); + если (узел->init) + сгенерировать_оператор(узел->init); + вывести_строку(".L.begin.%d:", метка); + если (узел->cond) { + сгенерировать_выражение(узел->cond); + сравнить_с_нулем(узел->cond->ty); + вывести_строку(" je %s", узел->brk_label); + } + сгенерировать_оператор(узел->then); + вывести_строку("%s:", узел->cont_label); + если (узел->inc) + сгенерировать_выражение(узел->inc); + вывести_строку(" jmp .L.begin.%d", метка); + вывести_строку("%s:", узел->brk_label); + возврат; + } + + случай ND_DO: { + цел метка = сгенерировать_номер_метки(); + вывести_строку(".L.begin.%d:", метка); + сгенерировать_оператор(узел->then); + вывести_строку("%s:", узел->cont_label); + сгенерировать_выражение(узел->cond); + сравнить_с_нулем(узел->cond->ty); + вывести_строку(" jne .L.begin.%d", метка); + вывести_строку("%s:", узел->brk_label); + возврат; + } + + случай ND_SWITCH: + сгенерировать_выражение(узел->cond); + + для (Node *n = узел->case_next; n; n = n->case_next) { + символ *ax_рег = (узел->cond->ty->size == 8) ? "%rax" : "%eax"; + символ *di_рег = (узел->cond->ty->size == 8) ? "%rdi" : "%edi"; + + если (n->begin == n->end) { + вывести_строку(" cmp $%ld, %s", n->begin, ax_рег); + вывести_строку(" je %s", n->label); + продолжи; + } + + вывести_строку(" mov %s, %s", ax_рег, di_рег); + вывести_строку(" sub $%ld, %s", n->begin, di_рег); + вывести_строку(" cmp $%ld, %s", n->end - n->begin, di_рег); + вывести_строку(" jbe %s", n->label); + } + + если (узел->default_case) + вывести_строку(" jmp %s", узел->default_case->label); + + вывести_строку(" jmp %s", узел->brk_label); + сгенерировать_оператор(узел->then); + вывести_строку("%s:", узел->brk_label); + возврат; + + случай ND_CASE: + вывести_строку("%s:", узел->label); + сгенерировать_оператор(узел->lhs); + возврат; + + случай ND_BLOCK: + для (Node *n = узел->body; n; n = n->next) + сгенерировать_оператор(n); + возврат; + + случай ND_GOTO: + вывести_строку(" jmp %s", узел->unique_label); + возврат; + + случай ND_GOTO_EXPR: + сгенерировать_выражение(узел->lhs); + вывести_строку(" jmp *%%rax"); + возврат; + + случай ND_LABEL: + вывести_строку("%s:", узел->unique_label); + сгенерировать_оператор(узел->lhs); + возврат; + + случай ND_RETURN: + если (узел->lhs) { + сгенерировать_выражение(узел->lhs); + Type *тип_возврата = узел->lhs->ty; + + выбор (тип_возврата->kind) { + случай TY_STRUCT: + случай TY_UNION: + если (тип_возврата->size <= 16) + скопировать_структуру_в_регистры(); + иначе + скопировать_структуру_в_память(); + прерви; + } + } + + вывести_строку(" jmp .L.return.%s", текущая_функция->name); + возврат; + + случай ND_EXPR_STMT: + сгенерировать_выражение(узел->lhs); + возврат; + + случай ND_ASM: + вывести_строку(" %s", узел->asm_str); + возврат; + } + + error_tok(узел->tok, "invalid statement"); +} + +// Назначение смещений локальным переменным +стат пусто назначить_смещения_локальным(Obj *программа) { + для (Obj *функция = программа; функция; функция = функция->next) { + если (!функция->is_function) + продолжи; + + цел верх = 16; + цел низ = 0; + + цел gp_счет = 0, fp_счет = 0; + + для (Obj *перем = функция->params; перем; перем = перем->next) { + Type *тип_перем = перем->ty; + + выбор (тип_перем->kind) { + случай TY_STRUCT: + случай TY_UNION: + если (тип_перем->size <= 16) { + бул fp1 = только_дробные_члены(тип_перем, 0, 8, 0); + бул fp2 = только_дробные_члены(тип_перем, 8, 16, 8); + если (fp_счет + fp1 + fp2 < FP_MAX && gp_счет + !fp1 + !fp2 < GP_MAX) { + fp_счет = fp_счет + fp1 + fp2; + gp_счет = gp_счет + !fp1 + !fp2; + продолжи; + } + } + прерви; + + случай TY_FLOAT: + случай TY_DOUBLE: + если (fp_счет++ < FP_MAX) + продолжи; + прерви; + + случай TY_LDOUBLE: + прерви; + + умолч: + если (gp_счет++ < GP_MAX) + продолжи; + } + + верх = align_to(верх, 8); + перем->offset = верх; + верх += перем->ty->size; + } + + для (Obj *перем = функция->locals; перем; перем = перем->next) { + если (перем->offset) + продолжи; + + цел выравнивание = (перем->ty->kind == TY_ARRAY && перем->ty->size >= 16) + ? MAX(16, перем->align) : перем->align; + + низ += перем->ty->size; + низ = align_to(низ, выравнивание); + перем->offset = -низ; + } + + функция->stack_size = align_to(низ, 16); + } +} + +// Генерация секции данных +стат пусто создать_секцию_данных(Obj *программа) { + для (Obj *перем = программа; перем; перем = перем->next) { + если (перем->is_function || !перем->is_definition) + продолжи; + + если (перем->is_static) + вывести_строку(" .local %s", перем->name); + иначе + вывести_строку(" .globl %s", перем->name); + + цел выравнивание = (перем->ty->kind == TY_ARRAY && перем->ty->size >= 16) + ? MAX(16, перем->align) : перем->align; + + если (opt_fcommon && перем->is_tentative) { + вывести_строку(" .comm %s, %d, %d", перем->name, перем->ty->size, выравнивание); + продолжи; + } + + если (перем->init_data) { + если (перем->is_tls) + вывести_строку(" .section .tdata,\"awT\",@progbits"); + иначе + вывести_строку(" .data"); + + вывести_строку(" .type %s, @object", перем->name); + вывести_строку(" .size %s, %d", перем->name, перем->ty->size); + вывести_строку(" .align %d", выравнивание); + вывести_строку("%s:", перем->name); + + Relocation *рел = перем->rel; + цел позиция = 0; + пока (позиция < перем->ty->size) { + если (рел && рел->offset == позиция) { + вывести_строку(" .quad %s%+ld", *рел->label, рел->addend); + рел = рел->next; + позиция += 8; + } иначе { + вывести_строку(" .byte %d", перем->init_data[позиция++]); + } + } + продолжи; + } + + если (перем->is_tls) + вывести_строку(" .section .tbss,\"awT\",@nobits"); + иначе + вывести_строку(" .bss"); + + вывести_строку(" .align %d", выравнивание); + вывести_строку("%s:", перем->name); + вывести_строку(" .zero %d", перем->ty->size); + } +} + +// Сохранение значения из XMM регистра в стек +стат пусто сохранить_дробный_регистр(цел номер_рег, цел смещение, цел разм) { + выбор (разм) { + случай 4: + вывести_строку(" movss %%xmm%d, %d(%%rbp)", номер_рег, смещение); + возврат; + случай 8: + вывести_строку(" movsd %%xmm%d, %d(%%rbp)", номер_рег, смещение); + возврат; + } + unreachable(); +} + +// Сохранение значения из GP регистра в стек +стат пусто сохранить_gp_регистр(цел номер_рег, цел смещение, цел разм) { + выбор (разм) { + случай 1: + вывести_строку(" mov %s, %d(%%rbp)", арг_рег8[номер_рег], смещение); + возврат; + случай 2: + вывести_строку(" mov %s, %d(%%rbp)", арг_рег16[номер_рег], смещение); + возврат; + случай 4: + вывести_строку(" mov %s, %d(%%rbp)", арг_рег32[номер_рег], смещение); + возврат; + случай 8: + вывести_строку(" mov %s, %d(%%rbp)", арг_рег64[номер_рег], смещение); + возврат; + умолч: + для (цел i = 0; i < разм; i++) { + вывести_строку(" mov %s, %d(%%rbp)", арг_рег8[номер_рег], смещение + i); + вывести_строку(" shr $8, %s", арг_рег64[номер_рег]); + } + возврат; + } +} + +// Генерация секции кода +стат пусто создать_секцию_кода(Obj *программа) { + для (Obj *функция = программа; функция; функция = функция->next) { + если (!функция->is_function || !функция->is_definition) + продолжи; + + если (!функция->is_live) + продолжи; + + если (функция->is_static) + вывести_строку(" .local %s", функция->name); + иначе + вывести_строку(" .globl %s", функция->name); + + если (strcmp(функция->name, "главн") == 0) + вывести_строку(" .globl main"); + + вывести_строку(" .text"); + вывести_строку(" .type %s, @function", функция->name); + вывести_строку("%s:", функция->name); + + если (strcmp(функция->name, "главн") == 0) + вывести_строку("main:"); + + текущая_функция = функция; + + вывести_строку(" push %%rbp"); + вывести_строку(" mov %%rsp, %%rbp"); + вывести_строку(" sub $%d, %%rsp", функция->stack_size); + вывести_строку(" mov %%rsp, %d(%%rbp)", функция->alloca_bottom->offset); + + если (функция->va_area) { + цел gp_счет = 0, fp_счет = 0; + для (Obj *перем = функция->params; перем; перем = перем->next) { + если (is_flonum(перем->ty)) + fp_счет++; + иначе + gp_счет++; + } + + цел смещение = функция->va_area->offset; + + вывести_строку(" movl $%d, %d(%%rbp)", gp_счет * 8, смещение); + вывести_строку(" movl $%d, %d(%%rbp)", fp_счет * 8 + 48, смещение + 4); + вывести_строку(" movq %%rbp, %d(%%rbp)", смещение + 8); + вывести_строку(" addq $16, %d(%%rbp)", смещение + 8); + вывести_строку(" movq %%rbp, %d(%%rbp)", смещение + 16); + вывести_строку(" addq $%d, %d(%%rbp)", смещение + 24, смещение + 16); + + вывести_строку(" movq %%rdi, %d(%%rbp)", смещение + 24); + вывести_строку(" movq %%rsi, %d(%%rbp)", смещение + 32); + вывести_строку(" movq %%rdx, %d(%%rbp)", смещение + 40); + вывести_строку(" movq %%rcx, %d(%%rbp)", смещение + 48); + вывести_строку(" movq %%r8, %d(%%rbp)", смещение + 56); + вывести_строку(" movq %%r9, %d(%%rbp)", смещение + 64); + вывести_строку(" movsd %%xmm0, %d(%%rbp)", смещение + 72); + вывести_строку(" movsd %%xmm1, %d(%%rbp)", смещение + 80); + вывести_строку(" movsd %%xmm2, %d(%%rbp)", смещение + 88); + вывести_строку(" movsd %%xmm3, %d(%%rbp)", смещение + 96); + вывести_строку(" movsd %%xmm4, %d(%%rbp)", смещение + 104); + вывести_строку(" movsd %%xmm5, %d(%%rbp)", смещение + 112); + вывести_строку(" movsd %%xmm6, %d(%%rbp)", смещение + 120); + вывести_строку(" movsd %%xmm7, %d(%%rbp)", смещение + 128); + } + + цел gp_счет = 0, fp_счет = 0; + для (Obj *перем = функция->params; перем; перем = перем->next) { + если (перем->offset > 0) + продолжи; + + Type *тип_перем = перем->ty; + + выбор (тип_перем->kind) { + случай TY_STRUCT: + случай TY_UNION: + assert(тип_перем->size <= 16); + если (только_дробные_члены(тип_перем, 0, 8, 0)) + сохранить_дробный_регистр(fp_счет++, перем->offset, MIN(8, тип_перем->size)); + иначе + сохранить_gp_регистр(gp_счет++, перем->offset, MIN(8, тип_перем->size)); + + если (тип_перем->size > 8) { + если (только_дробные_члены(тип_перем, 8, 16, 0)) + сохранить_дробный_регистр(fp_счет++, перем->offset + 8, тип_перем->size - 8); + иначе + сохранить_gp_регистр(gp_счет++, перем->offset + 8, тип_перем->size - 8); + } + прерви; + + случай TY_FLOAT: + случай TY_DOUBLE: + сохранить_дробный_регистр(fp_счет++, перем->offset, тип_перем->size); + прерви; + + умолч: + сохранить_gp_регистр(gp_счет++, перем->offset, тип_перем->size); + } + } + + сгенерировать_оператор(функция->body); + assert(глубина_стека == 0); + + если (strcmp(функция->name, "main") == 0 || strcmp(функция->name, "главн") == 0) + вывести_строку(" mov $0, %%rax"); + + вывести_строку(".L.return.%s:", функция->name); + вывести_строку(" mov %%rbp, %%rsp"); + вывести_строку(" pop %%rbp"); + вывести_строку(" ret"); + } +} + +// Главная функция генерации кода +пусто codegen(Obj *программа, FILE *выход) { + выходной_файл = выход; + + File **файлы = get_input_files(); + для (цел i = 0; файлы[i]; i++) + вывести_строку(" .file %d \"%s\"", файлы[i]->file_no, файлы[i]->name); + + назначить_смещения_локальным(программа); + создать_секцию_данных(программа); + создать_секцию_кода(программа); +} \ No newline at end of file diff --git a/hashmap.c b/hashmap.c new file mode 100644 index 0000000..c1ea29f --- /dev/null +++ b/hashmap.c @@ -0,0 +1,320 @@ +// ============================================================ +// Реализация хеш-таблицы с открытой адресацией +// ============================================================ + +#включить "руси.з" +#включить // стандартная библиотека +#включить // стандартная библиотека +#включить // стандартная библиотека +#включить // стандартная библиотека +#включить // стандартная библиотека + +// ============================================================ +// Константы +// ============================================================ + +#определить НАЧАЛЬНЫЙ_РАЗМЕР 16 // Начальный размер таблицы +#определить ВЕРХНИЙ_ПОРОГ 70 // Перестройка при заполнении > 70% +#определить НИЖНИЙ_ПОРОГ 50 // Целевое заполнение после перестройки < 50% +#определить УДАЛЕНО ((пусто *)-1) // Маркер удаленного элемента + +// ============================================================ +// Хеш-функция FNV-1a +// ============================================================ + +стат uint64_t хеш_fnv(конст символ *данные, цел длина) { + uint64_t хеш = 0xcbf29ce484222325ULL; // FNV offset basis + + для (цел ш = 0; ш < длина; ш++) { + хеш *= 0x100000001b3ULL; // FNV prime + хеш ^= (беззнак символ)данные[ш]; + } + + возврат хеш; +} + +// ============================================================ +// Проверка совпадения ключей +// ============================================================ + +стат бул ключи_совпадают(ЗаписьХеша *запись, конст символ *ключ, цел длина_ключа) { + если (!запись->ключ || запись->ключ == УДАЛЕНО) { + возврат ложь; + } + + возврат (запись->длина_ключа == длина_ключа) && + (memcmp(запись->ключ, ключ, длина_ключа) == 0); +} + +// ============================================================ +// Перестройка хеш-таблицы +// ============================================================ + +стат пусто перестроить(ХешТаблица *таблица) { + если (!таблица) возврат; + + // Подсчитываем количество активных записей + цел активных = 0; + для (цел ш = 0; ш < таблица->вместимость; ш++) { + пусто *ключ = таблица->корзины[ш].ключ; + если (ключ && ключ != УДАЛЕНО) { + активных++; + } + } + + // Вычисляем новый размер + цел новая_вместимость = таблица->вместимость; + пока ((активных * 100) / новая_вместимость >= НИЖНИЙ_ПОРОГ) { + новая_вместимость *= 2; + } + assert(новая_вместимость > 0); + + // Создаем новую таблицу + ХешТаблица новая = {0}; + новая.корзины = (ЗаписьХеша *)calloc(новая_вместимость, размер(ЗаписьХеша)); + новая.вместимость = новая_вместимость; + + // Копируем все активные записи + для (цел ш = 0; ш < таблица->вместимость; ш++) { + ЗаписьХеша *запись = &таблица->корзины[ш]; + если (запись->ключ && запись->ключ != УДАЛЕНО) { + хеш_вставить2(&новая, запись->ключ, запись->длина_ключа, запись->значение); + } + } + + assert(новая.использовано == активных); + + // Освобождаем старую таблицу + если (таблица->корзины) { + free(таблица->корзины); + } + + // Копируем новую таблицу + *таблица = новая; +} + +// ============================================================ +// Поиск записи по ключу +// ============================================================ + +стат ЗаписьХеша *найти_запись(ХешТаблица *таблица, конст символ *ключ, цел длина_ключа) { + если (!таблица || !таблица->корзины) { + возврат NULL; + } + + uint64_t хеш = хеш_fnv(ключ, длина_ключа); + + для (цел ш = 0; ш < таблица->вместимость; ш++) { + цел индекс = (хеш + ш) % таблица->вместимость; + ЗаписьХеша *запись = &таблица->корзины[индекс]; + + если (ключи_совпадают(запись, ключ, длина_ключа)) { + возврат запись; + } + + если (запись->ключ == NULL) { + возврат NULL; + } + } + + unreachable(); + возврат NULL; +} + +// ============================================================ +// Поиск или создание записи +// ============================================================ + +стат ЗаписьХеша *найти_или_создать(ХешТаблица *таблица, конст символ *ключ, цел длина_ключа) { + // Инициализация при первом использовании + если (!таблица->корзины) { + таблица->корзины = (ЗаписьХеша *)calloc(НАЧАЛЬНЫЙ_РАЗМЕР, размер(ЗаписьХеша)); + таблица->вместимость = НАЧАЛЬНЫЙ_РАЗМЕР; + если (!таблица->корзины) { + возврат NULL; + } + } + + // Перестройка при переполнении + если ((таблица->использовано * 100) / таблица->вместимость >= ВЕРХНИЙ_ПОРОГ) { + перестроить(таблица); + } + + uint64_t хеш = хеш_fnv(ключ, длина_ключа); + + для (цел ш = 0; ш < таблица->вместимость; ш++) { + цел индекс = (хеш + ш) % таблица->вместимость; + ЗаписьХеша *запись = &таблица->корзины[индекс]; + + // Нашли существующую запись + если (ключи_совпадают(запись, ключ, длина_ключа)) { + возврат запись; + } + + // Нашли tombstone - используем его + если (запись->ключ == УДАЛЕНО) { + запись->ключ = (символ *)ключ; + запись->длина_ключа = длина_ключа; + возврат запись; + } + + // Нашли пустую запись + если (запись->ключ == NULL) { + запись->ключ = (символ *)ключ; + запись->длина_ключа = длина_ключа; + таблица->использовано++; + возврат запись; + } + } + + unreachable(); + возврат NULL; +} + +// ============================================================ +// Публичные функции +// ============================================================ + +пусто *хеш_получить(ХешТаблица *таблица, символ *ключ) { + если (!таблица || !ключ) возврат NULL; + возврат хеш_получить2(таблица, ключ, (цел)strlen(ключ)); +} + +пусто *хеш_получить2(ХешТаблица *таблица, символ *ключ, цел длина_ключа) { + если (!таблица || !ключ || длина_ключа <= 0) возврат NULL; + + ЗаписьХеша *запись = найти_запись(таблица, ключ, длина_ключа); + возврат запись ? запись->значение : NULL; +} + +пусто хеш_вставить(ХешТаблица *таблица, символ *ключ, пусто *значение) { + если (!таблица || !ключ) возврат; + хеш_вставить2(таблица, ключ, (цел)strlen(ключ), значение); +} + +пусто хеш_вставить2(ХешТаблица *таблица, символ *ключ, цел длина_ключа, пусто *значение) { + если (!таблица || !ключ || длина_ключа <= 0) возврат; + + ЗаписьХеша *запись = найти_или_создать(таблица, ключ, длина_ключа); + если (запись) { + запись->значение = значение; + } +} + +пусто хеш_удалить(ХешТаблица *таблица, символ *ключ) { + если (!таблица || !ключ) возврат; + хеш_удалить2(таблица, ключ, (цел)strlen(ключ)); +} + +пусто хеш_удалить2(ХешТаблица *таблица, символ *ключ, цел длина_ключа) { + если (!таблица || !ключ || длина_ключа <= 0) возврат; + + ЗаписьХеша *запись = найти_запись(таблица, ключ, длина_ключа); + если (запись) { + запись->ключ = УДАЛЕНО; + } +} + +// ============================================================ +// Тестирование хеш-таблицы +// ============================================================ + +пусто хеш_тест(пусто) { + ХешТаблица *таблица = (ХешТаблица *)calloc(1, размер(ХешТаблица)); + если (!таблица) { + fprintf(stderr, "Failed to allocate hashmap for testing\n"); + возврат; + } + + printf("Testing hashmap...\n"); + + // Вставка 5000 записей + для (цел ш = 0; ш < 5000; ш++) { + символ *ключ = формат("key %d", ш); + хеш_вставить(таблица, ключ, (пусто *)(intptr_t)ш); + } + + // Удаление записей 1000-1999 + для (цел ш = 1000; ш < 2000; ш++) { + символ *ключ = формат("key %d", ш); + хеш_удалить(таблица, ключ); + } + + // Повторная вставка 1500-1599 + для (цел ш = 1500; ш < 1600; ш++) { + символ *ключ = формат("key %d", ш); + хеш_вставить(таблица, ключ, (пусто *)(intptr_t)ш); + } + + // Вставка 6000-6999 + для (цел ш = 6000; ш < 7000; ш++) { + символ *ключ = формат("key %d", ш); + хеш_вставить(таблица, ключ, (пусто *)(intptr_t)ш); + } + + // Проверка записей 0-999 + для (цел ш = 0; ш < 1000; ш++) { + символ *ключ = формат("key %d", ш); + пусто *значение = хеш_получить(таблица, ключ); + assert((intptr_t)значение == ш); + } + + // Проверка удаленных записей 1000-1499 + для (цел ш = 1000; ш < 1500; ш++) { + символ *ключ = формат("key %d", ш); + пусто *значение = хеш_получить(таблица, ключ); + assert(значение == NULL); + } + + // Проверка восстановленных записей 1500-1599 + для (цел ш = 1500; ш < 1600; ш++) { + символ *ключ = формат("key %d", ш); + пусто *значение = хеш_получить(таблица, ключ); + assert((intptr_t)значение == ш); + } + + // Проверка удаленных записей 1600-1999 + для (цел ш = 1600; ш < 2000; ш++) { + символ *ключ = формат("key %d", ш); + пусто *значение = хеш_получить(таблица, ключ); + assert(значение == NULL); + } + + // Проверка записей 2000-4999 + для (цел ш = 2000; ш < 5000; ш++) { + символ *ключ = формат("key %d", ш); + пусто *значение = хеш_получить(таблица, ключ); + assert((intptr_t)значение == ш); + } + + // Проверка несуществующих записей 5000-5999 + для (цел ш = 5000; ш < 6000; ш++) { + символ *ключ = формат("key %d", ш); + пусто *значение = хеш_получить(таблица, ключ); + assert(значение == NULL); + } + + // Обновление записей 6000-6999 + для (цел ш = 6000; ш < 7000; ш++) { + символ *ключ = формат("key %d", ш); + хеш_вставить(таблица, ключ, (пусто *)(intptr_t)(ш * 2)); + } + + // Проверка обновленных записей + для (цел ш = 6000; ш < 7000; ш++) { + символ *ключ = формат("key %d", ш); + пусто *значение = хеш_получить(таблица, ключ); + assert((intptr_t)значение == ш * 2); + } + + // Проверка несуществующего ключа + assert(хеш_получить(таблица, "no such key") == NULL); + + // Освобождаем память + если (таблица->корзины) { + free(таблица->корзины); + } + free(таблица); + + printf("All hashmap tests passed!\n"); +} \ No newline at end of file diff --git a/include/common.c b/include/common.c new file mode 100644 index 0000000..83dbf24 --- /dev/null +++ b/include/common.c @@ -0,0 +1,119 @@ +#include +#include +#include +/* +void assert(int expected, int actual, char *code) { + if (expected == actual) { + printf("%s => %d\n", code, actual); + } else { + printf("%s => %d expected but got %d\n", code, expected, actual); + exit(1); + } +} +*/ +static int static_fn() { return 5; } +int ext1 = 5; +int *ext2 = &ext1; +int ext3 = 7; +int ext_fn1(int x) { return x; } +int ext_fn2(int x) { return x; } +int common_ext2 = 3; +static int common_local; + +int false_fn() { return 512; } +int true_fn() { return 513; } +int char_fn() { return (2<<8)+3; } +int short_fn() { return (2<<16)+5; } + +int uchar_fn() { return (2<<10)-1-4; } +int ushort_fn() { return (2<<20)-1-7; } + +int schar_fn() { return (2<<10)-1-4; } +int sshort_fn() { return (2<<20)-1-7; } + +int add_all(int n, ...) { + va_list ap; + va_start(ap, n); + + int sum = 0; + for (int i = 0; i < n; i++) + sum += va_arg(ap, int); + return sum; +} + +float add_float(float x, float y) { + return x + y; +} + +double add_double(double x, double y) { + return x + y; +} + +int add10_int(int x1, int x2, int x3, int x4, int x5, int x6, int x7, int x8, int x9, int x10) { + return x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10; +} + +float add10_float(float x1, float x2, float x3, float x4, float x5, float x6, float x7, float x8, float x9, float x10) { + return x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10; +} + +double add10_double(double x1, double x2, double x3, double x4, double x5, double x6, double x7, double x8, double x9, double x10) { + return x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10; +} + +typedef struct { int a,b; short c; char d; } Ty4; +typedef struct { int a; float b; double c; } Ty5; +typedef struct { unsigned char a[3]; } Ty6; +typedef struct { long a, b, c; } Ty7; + +int struct_test4(Ty4 x, int n) { + switch (n) { + case 0: return x.a; + case 1: return x.b; + case 2: return x.c; + default: return x.d; + } +} + +int struct_test5(Ty5 x, int n) { + switch (n) { + case 0: return x.a; + case 1: return x.b; + default: return x.c; + } +} + +int struct_test6(Ty6 x, int n) { + return x.a[n]; +} + +int struct_test7(Ty7 x, int n) { + switch (n) { + case 0: return x.a; + case 1: return x.b; + default: return x.c; + } +} + +Ty4 struct_test24(void) { + return (Ty4){10, 20, 30, 40}; +} + +Ty5 struct_test25(void) { + return (Ty5){10, 20, 30}; +} + +Ty6 struct_test26(void) { + return (Ty6){10, 20, 30}; +} + +typedef struct { unsigned char a[10]; } Ty20; +typedef struct { unsigned char a[20]; } Ty21; + +Ty20 struct_test27(void) { + return (Ty20){10, 20, 30, 40, 50, 60, 70, 80, 90, 100}; +} + +Ty21 struct_test28(void) { + return (Ty21){1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20}; +} diff --git a/include/rstdlib.h b/include/rstdlib.h new file mode 100644 index 0000000..78c4729 --- /dev/null +++ b/include/rstdlib.h @@ -0,0 +1,358 @@ +// ============================================================ +// СТАНДАРТНАЯ БИБЛИОТЕКА РУСИ +// ============================================================ +// Объединение всех стандартных заголовочных файлов C11 +// в единый модуль с русскими синонимами. +// +// Включает: +// - float.h — характеристики типов с плавающей точкой +// - stdalign.h — макросы для работы с выравниванием типов +// - stdarg.h — макросы для работы с переменным числом аргументов +// - stdatomic.h — атомарные операции и типы +// - stdbool.h — булевый тип данных +// - stddef.h — стандартные определения типов и макросов +// - stdnoreturn.h — спецификатор невозвратных функций +// ============================================================ + +#ifndef _STDLIB_RU +#define _STDLIB_RU + +// ============================================================ +// float.h - Характеристики типов с плавающей точкой +// ============================================================ + +// Общие параметры представления чисел с плавающей точкой +#define DECIMAL_DIG 21 +#define FLT_EVAL_METHOD 0 +#define FLT_RADIX 2 +#define FLT_ROUNDS 1 + +// Характеристики типа float (одинарная точность, 32 бита) +#define FLT_DIG 6 +#define FLT_EPSILON 0x1p-23 +#define FLT_MANT_DIG 24 +#define FLT_MAX 0x1.fffffep+127 +#define FLT_MAX_10_EXP 38 +#define FLT_MAX_EXP 128 +#define FLT_MIN 0x1p-126 +#define FLT_MIN_10_EXP -37 +#define FLT_MIN_EXP -125 +#define FLT_TRUE_MIN 0x1p-149 + +// Характеристики типа double (двойная точность, 64 бита) +#define DBL_DIG 15 +#define DBL_EPSILON 0x1p-52 +#define DBL_MANT_DIG 53 +#define DBL_MAX 0x1.fffffffffffffp+1023 +#define DBL_MAX_10_EXP 308 +#define DBL_MAX_EXP 1024 +#define DBL_MIN 0x1p-1022 +#define DBL_MIN_10_EXP -307 +#define DBL_MIN_EXP -1021 +#define DBL_TRUE_MIN 0x0.0000000000001p-1022 + +// Характеристики типа long double (расширенная точность, 80/128 бит) +#define LDBL_DIG 15 +#define LDBL_EPSILON 0x1p-52 +#define LDBL_MANT_DIG 53 +#define LDBL_MAX 0x1.fffffffffffffp+1023 +#define LDBL_MAX_10_EXP 308 +#define LDBL_MAX_EXP 1024 +#define LDBL_MIN 0x1p-1022 +#define LDBL_MIN_10_EXP -307 +#define LDBL_MIN_EXP -1021 +#define LDBL_TRUE_MIN 0x0.0000000000001p-1022 + +// Русские синонимы для float.h +#define ДЕСЯТИЧНЫХ_ЦИФР DECIMAL_DIG +#define МЕТОД_ОЦЕНКИ FLT_EVAL_METHOD +#define ОСНОВАНИЕ FLT_RADIX +#define РЕЖИМ_ОКРУГЛЕНИЯ FLT_ROUNDS + +#define ОДИНАР_ЦИФР FLT_DIG +#define ОДИНАР_ЭПСИЛОН FLT_EPSILON +#define ОДИНАР_МАНТИССА FLT_MANT_DIG +#define ОДИНАР_МАКС FLT_MAX +#define ОДИНАР_МАКС_10_СТЕП FLT_MAX_10_EXP +#define ОДИНАР_МАКС_СТЕП FLT_MAX_EXP +#define ОДИНАР_МИН FLT_MIN +#define ОДИНАР_МИН_10_СТЕП FLT_MIN_10_EXP +#define ОДИНАР_МИН_СТЕП FLT_MIN_EXP +#define ОДИНАР_МИН_ДЕНОРМ FLT_TRUE_MIN + +#define ДВОЙН_ЦИФР DBL_DIG +#define ДВОЙН_ЭПСИЛОН DBL_EPSILON +#define ДВОЙН_МАНТИССА DBL_MANT_DIG +#define ДВОЙН_МАКС DBL_MAX +#define ДВОЙН_МАКС_10_СТЕП DBL_MAX_10_EXP +#define ДВОЙН_МАКС_СТЕП DBL_MAX_EXP +#define ДВОЙН_МИН DBL_MIN +#define ДВОЙН_МИН_10_СТЕП DBL_MIN_10_EXP +#define ДВОЙН_МИН_СТЕП DBL_MIN_EXP +#define ДВОЙН_МИН_ДЕНОРМ DBL_TRUE_MIN + +#define ДЛИН_ЦИФР LDBL_DIG +#define ДЛИН_ЭПСИЛОН LDBL_EPSILON +#define ДЛИН_МАНТИССА LDBL_MANT_DIG +#define ДЛИН_МАКС LDBL_MAX +#define ДЛИН_МАКС_10_СТЕП LDBL_MAX_10_EXP +#define ДЛИН_МАКС_СТЕП LDBL_MAX_EXP +#define ДЛИН_МИН LDBL_MIN +#define ДЛИН_МИН_10_СТЕП LDBL_MIN_10_EXP +#define ДЛИН_МИН_СТЕП LDBL_MIN_EXP +#define ДЛИН_МИН_ДЕНОРМ LDBL_TRUE_MIN + +// ============================================================ +// stdalign.h - Макросы для работы с выравниванием типов +// ============================================================ + +#define alignas _Alignas +#define alignof _Alignof +#define __alignas_is_defined 1 +#define __alignof_is_defined 1 + +// Русские синонимы для stdalign.h +#define выравн _Alignas +#define выравн_размер _Alignof + +// ============================================================ +// stdarg.h - Макросы для работы с переменным числом аргументов +// ============================================================ + +typedef struct { + unsigned int gp_offset; + unsigned int fp_offset; + void *overflow_arg_area; + void *reg_save_area; +} __va_state; + +typedef __va_state va_list[1]; + +#define va_start(ap, last) \ + do { *(ap) = *(__va_state *)__va_area__; } while (0) + +#define va_end(ap) + +static void *__va_read_from_stack(__va_state *ap, int sz, int align) { + void *p = ap->overflow_arg_area; + if (align > 8) + p = (p + 15) / 16 * 16; + ap->overflow_arg_area = ((unsigned long)p + sz + 7) / 8 * 8; + return p; +} + +static void *__va_read_from_gp(__va_state *ap, int sz, int align) { + if (ap->gp_offset >= 48) + return __va_read_from_stack(ap, sz, align); + + void *r = ap->reg_save_area + ap->gp_offset; + ap->gp_offset += 8; + return r; +} + +static void *__va_read_from_fp(__va_state *ap, int sz, int align) { + if (ap->fp_offset >= 112) + return __va_read_from_stack(ap, sz, align); + + void *r = ap->reg_save_area + ap->fp_offset; + ap->fp_offset += 8; + return r; +} + +#define va_arg(ap, ty) \ + ({ \ + int klass = __builtin_reg_class(ty); \ + *(ty *)(klass == 0 ? __va_read_from_gp(ap, sizeof(ty), _Alignof(ty)) : \ + klass == 1 ? __va_read_from_fp(ap, sizeof(ty), _Alignof(ty)) : \ + __va_read_from_stack(ap, sizeof(ty), _Alignof(ty))); \ + }) + +#define va_copy(dest, src) ((dest)[0] = (src)[0]) + +#define __GNUC_VA_LIST 1 +typedef va_list __gnuc_va_list; + +// Русские синонимы для stdarg.h +#define аргументы va_list +#define арг_начало va_start +#define арг_конец va_end +#define арг_взять va_arg +#define арг_копия va_copy + +// ============================================================ +// stdatomic.h - Атомарные операции и типы +// ============================================================ + +#define ATOMIC_BOOL_LOCK_FREE 1 +#define ATOMIC_CHAR_LOCK_FREE 1 +#define ATOMIC_CHAR16_T_LOCK_FREE 1 +#define ATOMIC_CHAR32_T_LOCK_FREE 1 +#define ATOMIC_WCHAR_T_LOCK_FREE 1 +#define ATOMIC_SHORT_LOCK_FREE 1 +#define ATOMIC_INT_LOCK_FREE 1 +#define ATOMIC_LONG_LOCK_FREE 1 +#define ATOMIC_LLONG_LOCK_FREE 1 +#define ATOMIC_POINTER_LOCK_FREE 1 + +typedef enum { + memory_order_relaxed, + memory_order_consume, + memory_order_acquire, + memory_order_release, + memory_order_acq_rel, + memory_order_seq_cst, +} memory_order; + +#define порядок_ослабленный memory_order_relaxed +#define порядок_потребление memory_order_consume +#define порядок_приобретение memory_order_acquire +#define порядок_освобождение memory_order_release +#define порядок_приобретение_освобождение memory_order_acq_rel +#define порядок_последовательный memory_order_seq_cst + +#define ATOMIC_FLAG_INIT(x) (x) +#define atomic_init(addr, val) (*(addr) = (val)) +#define kill_dependency(x) (x) +#define atomic_thread_fence(order) +#define atomic_signal_fence(order) +#define atomic_is_lock_free(x) 1 + +#define atomic_load(addr) (*(addr)) +#define atomic_store(addr, val) (*(addr) = (val)) +#define atomic_load_explicit(addr, order) (*(addr)) +#define atomic_store_explicit(addr, val, order) (*(addr) = (val)) + +#define атом_загрузить(addr) atomic_load(addr) +#define атом_сохранить(addr, val) atomic_store(addr, val) +#define атом_загрузить_явно(addr, order) atomic_load_explicit(addr, order) +#define атом_сохранить_явно(addr, val, order) atomic_store_explicit(addr, val, order) + +#define atomic_fetch_add(obj, val) (*(obj) += (val)) +#define atomic_fetch_sub(obj, val) (*(obj) -= (val)) +#define atomic_fetch_or(obj, val) (*(obj) |= (val)) +#define atomic_fetch_xor(obj, val) (*(obj) ^= (val)) +#define atomic_fetch_and(obj, val) (*(obj) &= (val)) + +#define atomic_fetch_add_explicit(obj, val, order) (*(obj) += (val)) +#define atomic_fetch_sub_explicit(obj, val, order) (*(obj) -= (val)) +#define atomic_fetch_or_explicit(obj, val, order) (*(obj) |= (val)) +#define atomic_fetch_xor_explicit(obj, val, order) (*(obj) ^= (val)) +#define atomic_fetch_and_explicit(obj, val, order) (*(obj) &= (val)) + +#define атом_прибавить(obj, val) atomic_fetch_add(obj, val) +#define атом_вычесть(obj, val) atomic_fetch_sub(obj, val) +#define атом_или(obj, val) atomic_fetch_or(obj, val) +#define атом_исключ_или(obj, val) atomic_fetch_xor(obj, val) +#define атом_и(obj, val) atomic_fetch_and(obj, val) + +#define атом_прибавить_явно(obj, val, order) atomic_fetch_add_explicit(obj, val, order) +#define атом_вычесть_явно(obj, val, order) atomic_fetch_sub_explicit(obj, val, order) +#define атом_или_явно(obj, val, order) atomic_fetch_or_explicit(obj, val, order) +#define атом_исключ_или_явно(obj, val, order) atomic_fetch_xor_explicit(obj, val, order) +#define атом_и_явно(obj, val, order) atomic_fetch_and_explicit(obj, val, order) + +#define atomic_compare_exchange_weak(p, old, new) \ + __builtin_compare_and_swap((p), (old), (new)) + +#define atomic_compare_exchange_strong(p, old, new) \ + __builtin_compare_and_swap((p), (old), (new)) + +#define atomic_exchange(obj, val) __builtin_atomic_exchange((obj), (val)) +#define atomic_exchange_explicit(obj, val, order) __builtin_atomic_exchange((obj), (val)) + +#define атом_сравнить_обменять_слабо(p, old, new) atomic_compare_exchange_weak(p, old, new) +#define атом_сравнить_обменять(p, old, new) atomic_compare_exchange_strong(p, old, new) +#define атом_обменять(obj, val) atomic_exchange(obj, val) +#define атом_обменять_явно(obj, val, order) atomic_exchange_explicit(obj, val, order) + +#define atomic_flag_test_and_set(obj) atomic_exchange((obj), 1) +#define atomic_flag_test_and_set_explicit(obj, order) atomic_exchange((obj), 1) +#define atomic_flag_clear(obj) (*(obj) = 0) +#define atomic_flag_clear_explicit(obj, order) (*(obj) = 0) + +#define атом_флаг_проверить_установить(obj) atomic_flag_test_and_set(obj) +#define атом_флаг_проверить_установить_явно(obj, order) atomic_flag_test_and_set_explicit(obj, order) +#define атом_флаг_очистить(obj) atomic_flag_clear(obj) +#define атом_флаг_очистить_явно(obj, order) atomic_flag_clear_explicit(obj, order) + +typedef _Atomic _Bool atomic_flag; +typedef _Atomic _Bool atomic_bool; +typedef _Atomic char atomic_char; +typedef _Atomic signed char atomic_schar; +typedef _Atomic unsigned char atomic_uchar; +typedef _Atomic short atomic_short; +typedef _Atomic unsigned short atomic_ushort; +typedef _Atomic int atomic_int; +typedef _Atomic unsigned int atomic_uint; +typedef _Atomic long atomic_long; +typedef _Atomic unsigned long atomic_ulong; +typedef _Atomic long long atomic_llong; +typedef _Atomic unsigned long long atomic_ullong; +typedef _Atomic unsigned short atomic_char16_t; +typedef _Atomic unsigned atomic_char32_t; +typedef _Atomic unsigned atomic_wchar_t; +typedef _Atomic signed char atomic_int_least8_t; +typedef _Atomic unsigned char atomic_uint_least8_t; +typedef _Atomic short atomic_int_least16_t; +typedef _Atomic unsigned short atomic_uint_least16_t; +typedef _Atomic int atomic_int_least32_t; +typedef _Atomic unsigned int atomic_uint_least32_t; +typedef _Atomic long atomic_int_least64_t; +typedef _Atomic unsigned long atomic_uint_least64_t; +typedef _Atomic signed char atomic_int_fast8_t; +typedef _Atomic unsigned char atomic_uint_fast8_t; +typedef _Atomic short atomic_int_fast16_t; +typedef _Atomic unsigned short atomic_uint_fast16_t; +typedef _Atomic int atomic_int_fast32_t; +typedef _Atomic unsigned int atomic_uint_fast32_t; +typedef _Atomic long atomic_int_fast64_t; +typedef _Atomic unsigned long atomic_uint_fast64_t; +typedef _Atomic long atomic_intptr_t; +typedef _Atomic unsigned long atomic_uintptr_t; +typedef _Atomic unsigned long atomic_size_t; +typedef _Atomic long atomic_ptrdiff_t; +typedef _Atomic long atomic_intmax_t; +typedef _Atomic unsigned long atomic_uintmax_t; + +// ============================================================ +// stdbool.h - Булевый тип данных +// ============================================================ + +#define bool _Bool +#define true 1 +#define false 0 + +#define бул _Bool +#define истина 1 +#define ложь 0 + +#define __bool_true_false_are_defined 1 + +// ============================================================ +// stddef.h - Стандартные определения типов и макросов +// ============================================================ + +#define NULL ((void *)0) + +typedef unsigned long size_t; +typedef long ptrdiff_t; +typedef unsigned int wchar_t; +typedef long max_align_t; + +#define offsetof(type, member) ((size_t)&(((type *)0)->member)) + +#define НУЛЬ NULL +typedef size_t размер_t; +typedef ptrdiff_t разность_t; +typedef wchar_t шсимвол_t; +typedef max_align_t макс_выравн_t; +#define смещение(type, member) offsetof(type, member) + +// ============================================================ +// stdnoreturn.h - Спецификатор невозвратных функций +// ============================================================ + +#define noreturn _Noreturn +#define безвозврат _Noreturn + +#endif // _STDLIB_RU \ No newline at end of file diff --git a/include/stdarg.h b/include/stdarg.h new file mode 100644 index 0000000..26d85fe --- /dev/null +++ b/include/stdarg.h @@ -0,0 +1,145 @@ +// stdarg.h - Макросы для работы с переменным числом аргументов функций +// +// Этот заголовочный файл реализует механизм передачи переменного числа +// аргументов (variadic functions) согласно ABI System V для x86-64. +// +// Структура __va_state отражает внутреннее представление области +// сохранения регистров, создаваемой компилятором для variadic функций. +// +// Поддерживаются как стандартные макросы (va_start, va_arg, va_end, va_copy), +// так и русские синонимы (арг_начало, арг_взять, арг_конец, арг_копия). + +#ifndef _STDARG_H_RU +#define _STDARG_H_RU + +// ============================================================ +// Внутреннее состояние списка аргументов +// ============================================================ + +// Структура описывает состояние итератора по аргументам variadic функции. +// Она отражает ABI x86-64 System V: +// - gp_offset: смещение в области сохранения регистров общего назначения +// - fp_offset: смещение в области сохранения регистров с плавающей точкой +// - overflow_arg_area: указатель на аргументы, переданные через стек +// - reg_save_area: указатель на область сохранения регистров +typedef struct { + unsigned int gp_offset; + unsigned int fp_offset; + void *overflow_arg_area; + void *reg_save_area; +} __va_state; + +// Список аргументов — массив из одного элемента __va_state. +// Такое представление позволяет передавать список по ссылке +// и модифицировать его внутри функций. +typedef __va_state va_list[1]; + +// ============================================================ +// Инициализация и завершение работы с аргументами +// ============================================================ + +// Инициализация списка аргументов. +// Копирует состояние из специальной области __va_area__, +// которая создаётся компилятором для каждой variadic функции. +#define va_start(ap, last) \ + do { *(ap) = *(__va_state *)__va_area__; } while (0) + +// Завершение работы со списком аргументов. +// В данной реализации не требует никаких действий. +#define va_end(ap) + +// ============================================================ +// Внутренние функции извлечения аргументов +// ============================================================ + +// Извлечение аргумента из области переполнения (переданного через стек). +// Выравнивает указатель согласно требованиям типа и продвигает +// область переполнения вперёд с учётом размера и выравнивания. +static void *__va_read_from_stack(__va_state *ap, int sz, int align) { + void *p = ap->overflow_arg_area; + if (align > 8) + p = (p + 15) / 16 * 16; + ap->overflow_arg_area = ((unsigned long)p + sz + 7) / 8 * 8; + return p; +} + +// Извлечение аргумента из области сохранения регистров общего назначения. +// Если регистры общего назначения исчерпаны (gp_offset >= 48), +// переключается на извлечение из стека. +static void *__va_read_from_gp(__va_state *ap, int sz, int align) { + if (ap->gp_offset >= 48) + return __va_read_from_stack(ap, sz, align); + + void *r = ap->reg_save_area + ap->gp_offset; + ap->gp_offset += 8; + return r; +} + +// Извлечение аргумента из области сохранения регистров с плавающей точкой. +// Если регистры с плавающей точкой исчерпаны (fp_offset >= 112), +// переключается на извлечение из стека. +static void *__va_read_from_fp(__va_state *ap, int sz, int align) { + if (ap->fp_offset >= 112) + return __va_read_from_stack(ap, sz, align); + + void *r = ap->reg_save_area + ap->fp_offset; + ap->fp_offset += 8; + return r; +} + +// ============================================================ +// Извлечение следующего аргумента +// ============================================================ + +// Извлечение следующего аргумента заданного типа из списка. +// Использует встроенную функцию __builtin_reg_class для определения +// класса регистра (0 - общего назначения, 1 - плавающая точка, 2 - память) +// и вызывает соответствующую функцию извлечения. +#define va_arg(ap, ty) \ + ({ \ + int klass = __builtin_reg_class(ty); \ + *(ty *)(klass == 0 ? __va_read_from_gp(ap, sizeof(ty), _Alignof(ty)) : \ + klass == 1 ? __va_read_from_fp(ap, sizeof(ty), _Alignof(ty)) : \ + __va_read_from_stack(ap, sizeof(ty), _Alignof(ty))); \ + }) + +// ============================================================ +// Копирование списка аргументов +// ============================================================ + +// Копирование состояния списка аргументов. +// Позволяет итерироваться по аргументам независимо в разных местах. +#define va_copy(dest, src) ((dest)[0] = (src)[0]) + +// ============================================================ +// Совместимость с GCC +// ============================================================ + +// Макрос, указывающий, что va_list совместим с GCC +#define __GNUC_VA_LIST 1 + +// Альтернативное имя типа для совместимости с GCC +typedef va_list __gnuc_va_list; + +// ============================================================ +// Русские синонимы для удобства +// ============================================================ + +// Тип списка аргументов +#define аргументы va_list + +// Инициализация списка аргументов +// Пример: арг_начало(args, последний); +#define арг_начало va_start + +// Завершение работы со списком аргументов +#define арг_конец va_end + +// Извлечение следующего аргумента +// Пример: цел x = арг_взять(args, цел); +#define арг_взять va_arg + +// Копирование списка аргументов +#define арг_копия va_copy + +#endif // _STDARG_H_RU \ No newline at end of file diff --git a/include/stdbool.h b/include/stdbool.h new file mode 100644 index 0000000..f22fdcd --- /dev/null +++ b/include/stdbool.h @@ -0,0 +1,45 @@ +// stdbool.h - Заголовочный файл для работы с булевым типом данных +// +// Этот файл определяет макросы для использования булевых значений +// в программах на языке C. Поддерживаются как стандартные английские +// идентификаторы (bool, true, false), так и русские синонимы +// (бул, истина, ложь). + +#ifndef _STDBOOL_H_INCLUDED +#define _STDBOOL_H_INCLUDED + +// ============================================================ +// Стандартные макросы C99 +// ============================================================ + +// Тип булевой переменной (встроенный тип _Bool) +#define bool _Bool + +// Логическая истина +#define true 1 + +// Логическая ложь +#define false 0 + +// ============================================================ +// Русские синонимы для булевых значений +// ============================================================ + +// Тип булевой переменной (русский синоним) +#define бул _Bool + +// Логическая истина (русский синоним) +#define истина 1 + +// Логическая ложь (русский синоним) +#define ложь 0 + +// ============================================================ +// Служебные макросы +// ============================================================ + +// Макрос, указывающий, что данный заголовочный файл включён +// и макросы bool/true/false определены +#define __bool_true_false_are_defined 1 + +#endif // _STDBOOL_H_INCLUDED \ No newline at end of file diff --git a/include/stddef.h b/include/stddef.h new file mode 100644 index 0000000..16f9b1b --- /dev/null +++ b/include/stddef.h @@ -0,0 +1,82 @@ +// stddef.h - Стандартные определения типов и макросов +// +// Этот заголовочный файл определяет фундаментальные типы данных и макросы, +// используемые в программах на языке C. Все размеры и типы соответствуют +// стандарту C11 для архитектуры x86-64 (LP64 модель данных). +// +// Поддерживаются как стандартные идентификаторы (NULL, size_t, offsetof), +// так и русские синонимы (НУЛЬ, размер, смещение). + +#ifndef _STDDEF_H_RU +#define _STDDEF_H_RU + +// ============================================================ +// Нулевой указатель +// ============================================================ + +// Константа нулевого указателя. +// Используется для обозначения недействительного или неинициализированного +// указателя. Может быть приведён к любому типу указателя. +#define NULL ((void *)0) + +// ============================================================ +// Фундаментальные типы данных +// ============================================================ + +// Тип для представления размеров объектов в байтах. +// Беззнаковое целое, способное вместить размер любого объекта. +// На x86-64 это 64-битное беззнаковое целое. +typedef unsigned long size_t; + +// Тип для представления разности двух указателей. +// Знаковое целое, способное вместить разность любых двух указателей +// в пределах одного массива. На x86-64 это 64-битное знаковое целое. +typedef long ptrdiff_t; + +// Тип для представления широких символов (wide characters). +// Используется для работы с Unicode в кодировках UTF-16/UTF-32. +// На x86-64 Linux это 32-битное беззнаковое целое (UTF-32). +typedef unsigned int wchar_t; + +// Тип с максимальным требованием выравнивания. +// Гарантирует, что любой объект этого типа будет выровнен +// по максимальной границе, требуемой любым базовым типом. +typedef long max_align_t; + +// ============================================================ +// Макрос для вычисления смещения члена структуры +// ============================================================ + +// Возвращает смещение в байтах указанного члена внутри структуры. +// Использует технику создания указателя по нулевому адресу +// для вычисления смещения без создания реального объекта. +// +// Пример использования: +// struct Point { int x; int y; }; +// size_t offset = offsetof(struct Point, y); // вернёт 4 +#define offsetof(type, member) ((size_t)&(((type *)0)->member)) + +// ============================================================ +// Русские синонимы для удобства +// ============================================================ + +// Нулевой указатель (русский синоним) +#define НУЛЬ NULL + +// Тип для размеров (русский синоним) +typedef size_t размер_t; + +// Тип для разности указателей (русский синоним) +typedef ptrdiff_t разность_t; + +// Тип для широких символов (русский синоним) +typedef wchar_t шсимвол_t; + +// Тип с максимальным выравниванием (русский синоним) +typedef max_align_t макс_выравн_t; + +// Макрос вычисления смещения (русский синоним) +// Пример: размер_t смещ = смещение(struct Точка, y); +#define смещение(type, member) offsetof(type, member) + +#endif // _STDDEF_H_RU \ No newline at end of file diff --git a/include/stdnoreturn.h b/include/stdnoreturn.h new file mode 100644 index 0000000..3882967 --- /dev/null +++ b/include/stdnoreturn.h @@ -0,0 +1,49 @@ +// stdnoreturn.h - Спецификатор невозвратных функций +// +// Этот заголовочный файл определяет макрос noreturn, который является +// удобным синонимом ключевого слова _Noreturn из стандарта C11. +// +// Спецификатор noreturn сообщает компилятору, что функция никогда +// не возвращает управление вызывающему коду. Это позволяет компилятору +// выполнять дополнительные оптимизации и выдавать более точные +// предупреждения о недостижимом коде. +// +// Типичные примеры невозвратных функций: +// - exit() — завершение программы +// - abort() — аварийное завершение +// - _Exit() — немедленное завершение без очистки +// - quick_exit() — быстрое завершение +// +// Поддерживается как стандартный идентификатор (noreturn), +// так и русский синоним (безвозврат). + +#ifndef _STDNORETURN_H_RU +#define _STDNORETURN_H_RU + +// ============================================================ +// Стандартный макрос C11 +// ============================================================ + +// Спецификатор невозвратной функции. +// Применяется к объявлениям функций, которые не возвращают управление. +// +// Пример использования: +// #include +// noreturn void завершить_программу(цел код) { +// exit(код); +// } +#define noreturn _Noreturn + +// ============================================================ +// Русский синоним для удобства +// ============================================================ + +// Спецификатор невозвратной функции (русский синоним). +// +// Пример использования: +// безвозврат пусто аварийный_выход(цел код) { +// выход(код); +// } +#define безвозврат _Noreturn + +#endif // _STDNORETURN_H_RU \ No newline at end of file diff --git a/include/stdvv.c b/include/stdvv.c new file mode 100644 index 0000000..a367167 --- /dev/null +++ b/include/stdvv.c @@ -0,0 +1,53 @@ +// stdvv.c - реализация русского ввода/вывода +#include "stdvv.h" + + + +long syscall3(long number, long arg1, long arg2, long arg3) { + asm("mov %rcx, %r10"); + asm("mov %rdi, %rax"); + asm("mov %rsi, %rdi"); + asm("mov %rdx, %rsi"); + asm("mov %r10, %rdx"); + asm("syscall"); +} + +long длина_строки(const char *s) { + long len = 0; + while (s[len]) len++; + return len; +} + +int печатьф(const char *формат, ...) { + long len = длина_строки(формат); + return (int)syscall3(1, 1, (long)формат, len); +} + +int печать(const char *строка) { + long len = длина_строки(строка); + return (int)syscall3(1, 1, (long)строка, len); +} + +int вывод_строки(const char *строка) { + int result = печать(строка); + симв_вывод('\n'); + return result; +} + +int симв_вывод(int c) { + char ch = (char)c; + return (int)syscall3(1, 1, (long)&ch, 1); +} + +void завершение(int код) { + syscall3(60, код, 0, 0); + for (;;) {} +} + +// assert - проверка условия для тестов +void assert(int expected, int actual, char *code) { + if (expected != actual) { + printf("ОШИБКА: ожидалось %d, получено %d (%s)\n", expected, actual, code); + завершение(1); + } +} \ No newline at end of file diff --git a/include/stdvv.h b/include/stdvv.h new file mode 100644 index 0000000..45b6b39 --- /dev/null +++ b/include/stdvv.h @@ -0,0 +1,25 @@ +// stdvv.h - стандартный ввод/вывод для русского C +#ifndef _STDVV_H +#define _STDVV_H + +#define NULL ((void*)0) + +// Системные вызовы +long syscall3(long number, long arg1, long arg2, long arg3); + +// Функции вывода +int печатьф(const char *формат, ...); +int печать(const char *строка); +int симв_вывод(int c); +int вывод_строки(const char *строка); + +// Завершение программы +void завершение(int код); + +// Длина строки +long длина_строки(const char *s); + +// assert для тестов +void assert(int expected, int actual, char *code); + +#endif \ No newline at end of file diff --git a/main.c b/main.c new file mode 100644 index 0000000..94fbe7d --- /dev/null +++ b/main.c @@ -0,0 +1,875 @@ +#включить "руси.з" + +// Типы входных файлов +типдеф переч { + FILE_NONE, FILE_C, FILE_ASM, FILE_OBJ, FILE_AR, FILE_DSO, +} FileType; + +// Глобальные переменные (публичные интерфейсы) +МассивСтрок include_paths; // было StringArray +бул opt_fcommon = истина; +бул opt_fpic; + +символ *base_file; + +// Статические переменные для опций командной строки +стат FileType source_type_override; +стат МассивСтрок forced_includes; // было StringArray +стат бул option_preprocess_only; +стат бул option_make_depend; +стат бул option_make_depend_with_compile; +стат бул option_make_depend_only; +стат бул option_phony_targets; +стат бул option_assembly_only; +стат бул option_compile_only; +стат бул option_cc1_mode; +стат бул option_verbose_commands; +стат бул option_static_link; +стат бул option_shared_lib; +стат символ *option_depend_file; +стат символ *option_target_name; +стат символ *option_output_file; + +стат МассивСтрок linker_extra_args; // было StringArray +стат МассивСтрок standard_include_paths; // было StringArray + +стат символ *compiler_output_path; + +стат МассивСтрок input_files; // было StringArray +стат МассивСтрок temporary_files; // было StringArray + +// Вывод справки по использованию +стат пусто show_usage(цел status) { + fprintf(stderr, "rusi [ -o ] \n"); + exit(status); +} + +// Проверка, требует ли опция аргумент +стат бул option_requires_argument(символ *arg) { + символ *options_with_args[] = { + "-o", "-I", "-idirafter", "-include", "-x", "-MF", "-MT", "-Xlinker", + }; + + для (цел i = 0; i < размер(options_with_args) / размер(*options_with_args); i++) + если (!strcmp(arg, options_with_args[i])) + возврат истина; + возврат ложь; +} + +// Добавление стандартных путей поиска include-файлов +стат пусто setup_default_include_paths(символ *executable_path) { + // Ожидаем, что include-файлы rusi установлены в ./include относительно argv[0] + массив_строк_добавить(&include_paths, формат("%s/include", dirname(strdup(executable_path)))); + + // Добавляем стандартные системные пути + массив_строк_добавить(&include_paths, "/usr/local/include"); + массив_строк_добавить(&include_paths, "/usr/include/x86_64-linux-gnu"); + массив_строк_добавить(&include_paths, "/usr/include"); + + // Сохраняем копию стандартных путей для опции -MMD + для (цел i = 0; i < include_paths.len; i++) + массив_строк_добавить(&standard_include_paths, include_paths.data[i]); +} + +// Обработка определения макроса из командной строки +стат пусто process_macro_definition(символ *str) { + символ *equals = strchr(str, '='); + если (equals) + define_macro(strndup(str, equals - str), equals + 1); + иначе + define_macro(str, "1"); +} + +// Разбор значения опции -x (тип исходного файла) +стат FileType parse_source_type(символ *type_str) { + если (!strcmp(type_str, "c")) + возврат FILE_C; + если (!strcmp(type_str, "assembler")) + возврат FILE_ASM; + если (!strcmp(type_str, "none")) + возврат FILE_NONE; + error(": unknown argument for -x: %s", type_str); +} + +// Экранирование строки для использования в Makefile +стат символ *escape_for_makefile(символ *input) { + символ *buffer = calloc(1, strlen(input) * 2 + 1); + + для (цел i = 0, j = 0; input[i]; i++) { + выбор (input[i]) { + случай '$': + // Символ $ экранируется как $$ + buffer[j++] = '$'; + buffer[j++] = '$'; + прерви; + случай '#': + // Символ # экранируется как \# + buffer[j++] = '\\'; + buffer[j++] = '#'; + прерви; + случай ' ': + случай '\t': + // Пробелы и табуляции экранируются обратными слешами + для (цел k = i - 1; k >= 0 && input[k] == '\\'; k--) + buffer[j++] = '\\'; + buffer[j++] = '\\'; + buffer[j++] = input[i]; + прерви; + умолч: + buffer[j++] = input[i]; + прерви; + } + } + возврат buffer; +} + +// Разбор аргументов командной строки +стат пусто parse_command_line(цел argc, символ **argv) { + // Проверяем, что все опции, требующие аргумента, его имеют + для (цел i = 1; i < argc; i++) + если (option_requires_argument(argv[i])) + если (!argv[++i]) + show_usage(1); + + МассивСтрок deferred_includes = {}; // было StringArray + + для (цел i = 1; i < argc; i++) { + // Опция ###: вывод команд подпроцессов + если (!strcmp(argv[i], "-###")) { + option_verbose_commands = истина; + продолжи; + } + + // Опция -cc1: прямой вызов компилятора + если (!strcmp(argv[i], "-cc1")) { + option_cc1_mode = истина; + продолжи; + } + + // Справка + если (!strcmp(argv[i], "--help")) + show_usage(0); + + // Опция -o: выходной файл + если (!strcmp(argv[i], "-o")) { + option_output_file = argv[++i]; + продолжи; + } + + если (!strncmp(argv[i], "-o", 2)) { + option_output_file = argv[i] + 2; + продолжи; + } + + // Опция -S: только ассемблер + если (!strcmp(argv[i], "-S")) { + option_assembly_only = истина; + продолжи; + } + + // Опции -fcommon и -fno-common + если (!strcmp(argv[i], "-fcommon")) { + opt_fcommon = истина; + продолжи; + } + + если (!strcmp(argv[i], "-fno-common")) { + opt_fcommon = ложь; + продолжи; + } + + // Опция -c: только компиляция + если (!strcmp(argv[i], "-c")) { + option_compile_only = истина; + продолжи; + } + + // Опция -E: только препроцессор + если (!strcmp(argv[i], "-E")) { + option_preprocess_only = истина; + продолжи; + } + + // Опция -I: путь поиска include + если (!strncmp(argv[i], "-I", 2)) { + массив_строк_добавить(&include_paths, argv[i] + 2); + продолжи; + } + + // Опция -D: определение макроса + если (!strcmp(argv[i], "-D")) { + process_macro_definition(argv[++i]); + продолжи; + } + + если (!strncmp(argv[i], "-D", 2)) { + process_macro_definition(argv[i] + 2); + продолжи; + } + + // Опция -U: отмена определения макроса + если (!strcmp(argv[i], "-U")) { + undef_macro(argv[++i]); + продолжи; + } + + если (!strncmp(argv[i], "-U", 2)) { + undef_macro(argv[i] + 2); + продолжи; + } + + // Опция -include: принудительное включение файла + если (!strcmp(argv[i], "-include")) { + массив_строк_добавить(&forced_includes, argv[++i]); + продолжи; + } + + // Опция -x: указание типа исходного файла + если (!strcmp(argv[i], "-x")) { + source_type_override = parse_source_type(argv[++i]); + продолжи; + } + + если (!strncmp(argv[i], "-x", 2)) { + source_type_override = parse_source_type(argv[i] + 2); + продолжи; + } + + // Опции -l и -Wl,: передача линкеру + если (!strncmp(argv[i], "-l", 2) || !strncmp(argv[i], "-Wl,", 4)) { + массив_строк_добавить(&input_files, argv[i]); + продолжи; + } + + // Опция -Xlinker: передача аргумента линкеру + если (!strcmp(argv[i], "-Xlinker")) { + массив_строк_добавить(&linker_extra_args, argv[++i]); + продолжи; + } + + // Опция -s: удаление символов + если (!strcmp(argv[i], "-s")) { + массив_строк_добавить(&linker_extra_args, "-s"); + продолжи; + } + + // Опции генерации зависимостей + если (!strcmp(argv[i], "-M")) { + option_make_depend = истина; + продолжи; + } + + если (!strcmp(argv[i], "-MF")) { + option_depend_file = argv[++i]; + продолжи; + } + + если (!strcmp(argv[i], "-MP")) { + option_phony_targets = истина; + продолжи; + } + + если (!strcmp(argv[i], "-MT")) { + если (option_target_name == NULL) + option_target_name = argv[++i]; + иначе + option_target_name = формат("%s %s", option_target_name, argv[++i]); + продолжи; + } + + если (!strcmp(argv[i], "-MD")) { + option_make_depend_with_compile = истина; + продолжи; + } + + если (!strcmp(argv[i], "-MQ")) { + если (option_target_name == NULL) + option_target_name = escape_for_makefile(argv[++i]); + иначе + option_target_name = формат("%s %s", option_target_name, escape_for_makefile(argv[++i])); + продолжи; + } + + если (!strcmp(argv[i], "-MMD")) { + option_make_depend_with_compile = option_make_depend_only = истина; + продолжи; + } + + // Опции -fpic и -fPIC: position-independent code + если (!strcmp(argv[i], "-fpic") || !strcmp(argv[i], "-fPIC")) { + opt_fpic = истина; + продолжи; + } + + // Опции cc1: входной и выходной файлы + если (!strcmp(argv[i], "-cc1-input")) { + base_file = argv[++i]; + продолжи; + } + + если (!strcmp(argv[i], "-cc1-output")) { + compiler_output_path = argv[++i]; + продолжи; + } + + // Опция -idirafter: отложенный поиск include + если (!strcmp(argv[i], "-idirafter")) { + массив_строк_добавить(&deferred_includes, argv[i++]); + продолжи; + } + + // Опции -static и -shared: тип линковки + если (!strcmp(argv[i], "-static")) { + option_static_link = истина; + массив_строк_добавить(&linker_extra_args, "-static"); + продолжи; + } + + если (!strcmp(argv[i], "-shared")) { + option_shared_lib = истина; + массив_строк_добавить(&linker_extra_args, "-shared"); + продолжи; + } + + // Опция -L: путь поиска библиотек + если (!strcmp(argv[i], "-L")) { + массив_строк_добавить(&linker_extra_args, "-L"); + массив_строк_добавить(&linker_extra_args, argv[++i]); + продолжи; + } + + если (!strncmp(argv[i], "-L", 2)) { + массив_строк_добавить(&linker_extra_args, "-L"); + массив_строк_добавить(&linker_extra_args, argv[i] + 2); + продолжи; + } + + // Тестовая опция для hashmap + если (!strcmp(argv[i], "-hashmap-test")) { + хеш_тест(); + exit(0); + } + + // Игнорируемые опции (для совместимости с GCC) + если (!strncmp(argv[i], "-O", 2) || + !strncmp(argv[i], "-W", 2) || + !strncmp(argv[i], "-g", 2) || + !strncmp(argv[i], "-std=", 5) || + !strcmp(argv[i], "-ffreestanding") || + !strcmp(argv[i], "-fno-builtin") || + !strcmp(argv[i], "-fno-omit-frame-pointer") || + !strcmp(argv[i], "-fno-stack-protector") || + !strcmp(argv[i], "-fno-strict-aliasing") || + !strcmp(argv[i], "-m64") || + !strcmp(argv[i], "-mno-red-zone") || + !strcmp(argv[i], "-w")) + продолжи; + + // Проверка на неизвестную опцию + если (argv[i][0] == '-' && argv[i][1] != '\0') + error("unknown argument: %s", argv[i]); + + // Добавление входного файла + массив_строк_добавить(&input_files, argv[i]); + } + + // Добавляем отложенные include-пути в конец + для (цел i = 0; i < deferred_includes.len; i++) + массив_строк_добавить(&include_paths, deferred_includes.data[i]); + + если (input_files.len == 0) + error("no input files"); + + // -E подразумевает, что входной файл - это C код + если (option_preprocess_only) + source_type_override = FILE_C; +} + +// Открытие файла для записи +стат FILE *open_output_file(символ *path) { + если (!path || strcmp(path, "-") == 0) + возврат stdout; + + FILE *out = fopen(path, "w"); + если (!out) + error("cannot open output file: %s: %s", path, strerror(errno)); + возврат out; +} + +// Проверка, заканчивается ли строка указанным суффиксом +стат бул string_ends_with(символ *str, символ *suffix) { + цел str_len = strlen(str); + цел suffix_len = strlen(suffix); + возврат (str_len >= suffix_len) && !strcmp(str + str_len - suffix_len, suffix); +} + +// Замена расширения файла +стат символ *change_extension(символ *template, символ *new_ext) { + символ *filename = basename(strdup(template)); + символ *dot = strrchr(filename, '.'); + если (dot) + *dot = '\0'; + возврат формат("%s%s", filename, new_ext); +} + +// Очистка временных файлов при выходе +стат пусто cleanup_temporary_files(пусто) { + для (цел i = 0; i < temporary_files.len; i++) + unlink(temporary_files.data[i]); +} + +// Создание временного файла +стат символ *create_temporary_file(пусто) { + символ *path = strdup("/tmp/rusi-XXXXXX"); + цел fd = mkstemp(path); + если (fd == -1) + error("mkstemp failed: %s", strerror(errno)); + close(fd); + + массив_строк_добавить(&temporary_files, path); + возврат path; +} + +// Запуск подпроцесса +стат пусто execute_subprocess(символ **argv) { + // Если указана опция -###, выводим команду подпроцесса + если (option_verbose_commands) { + fprintf(stderr, "%s", argv[0]); + для (цел i = 1; argv[i]; i++) + fprintf(stderr, " %s", argv[i]); + fprintf(stderr, "\n"); + } + + если (fork() == 0) { + // Дочерний процесс: выполняем команду + execvp(argv[0], argv); + fprintf(stderr, "exec failed: %s: %s\n", argv[0], strerror(errno)); + _exit(1); + } + + // Родительский процесс: ждём завершения дочернего + цел status; + пока (wait(&status) > 0); + если (status != 0) + exit(1); +} + +// Запуск компилятора в режиме cc1 +стат пусто invoke_compiler_cc1(цел argc, символ **argv, символ *input, символ *output) { + символ **args = calloc(argc + 10, размер(символ *)); + memcpy(args, argv, argc * размер(символ *)); + args[argc++] = "-cc1"; + + если (input) { + args[argc++] = "-cc1-input"; + args[argc++] = input; + } + + если (output) { + args[argc++] = "-cc1-output"; + args[argc++] = output; + } + + execute_subprocess(args); +} + +// Вывод токенов в stdout (для опции -E) +стат пусто output_preprocessed_tokens(Token *tok) { + FILE *out = open_output_file(option_output_file ? option_output_file : "-"); + + цел line_num = 1; + для (; tok->kind != TK_EOF; tok = tok->next) { + если (line_num > 1 && tok->at_bol) + fprintf(out, "\n"); + если (tok->has_space && !tok->at_bol) + fprintf(out, " "); + fprintf(out, "%.*s", tok->len, tok->loc); + line_num++; + } + fprintf(out, "\n"); +} + +// Проверка, находится ли путь в стандартных include-путях +стат бул is_standard_include_path(символ *path) { + для (цел i = 0; i < standard_include_paths.len; i++) { + символ *dir = standard_include_paths.data[i]; + цел dir_len = strlen(dir); + если (strncmp(dir, path, dir_len) == 0 && path[dir_len] == '/') + возврат истина; + } + возврат ложь; +} + +// Вывод зависимостей файлов для make (опции -M, -MD) +стат пусто output_file_dependencies(пусто) { + символ *output_path; + если (option_depend_file) + output_path = option_depend_file; + иначе если (option_make_depend_with_compile) + output_path = change_extension(option_output_file ? option_output_file : base_file, ".d"); + иначе если (option_output_file) + output_path = option_output_file; + иначе + output_path = "-"; + + FILE *out = open_output_file(output_path); + если (option_target_name) + fprintf(out, "%s:", option_target_name); + иначе + fprintf(out, "%s:", escape_for_makefile(change_extension(base_file, ".o"))); + + File **files = get_input_files(); + + для (цел i = 0; files[i]; i++) { + если (option_make_depend_only && is_standard_include_path(files[i]->name)) + продолжи; + fprintf(out, " \\\n %s", files[i]->name); + } + + fprintf(out, "\n\n"); + + // Создание фиктивных целей для каждой зависимости + если (option_phony_targets) { + для (цел i = 1; files[i]; i++) { + если (option_make_depend_only && is_standard_include_path(files[i]->name)) + продолжи; + fprintf(out, "%s:\n\n", escape_for_makefile(files[i]->name)); + } + } +} + +// Токенизация файла с обработкой ошибок +стат Token *tokenize_input_file(символ *path) { + Token *tok = tokenize_file(path); + если (!tok) + error("%s: %s", path, strerror(errno)); + возврат tok; +} + +// Объединение двух списков токенов +стат Token *concatenate_token_lists(Token *list1, Token *list2) { + если (!list1 || list1->kind == TK_EOF) + возврат list2; + + Token *current = list1; + пока (current->next->kind != TK_EOF) + current = current->next; + current->next = list2; + возврат list1; +} + +// Основная функция компиляции (режим cc1) +стат пусто compile_cc1(пусто) { + Token *tokens = NULL; + + // Обработка опции -include: принудительное включение файлов + для (цел i = 0; i < forced_includes.len; i++) { + символ *include_file = forced_includes.data[i]; + символ *path; + если (file_exists(include_file)) { + path = include_file; + } иначе { + path = search_include_paths(include_file); + если (!path) + error("-include: %s: %s", include_file, strerror(errno)); + } + Token *file_tokens = tokenize_input_file(path); + tokens = concatenate_token_lists(tokens, file_tokens); + } + + // Токенизация и парсинг основного файла + Token *main_tokens = tokenize_input_file(base_file); + tokens = concatenate_token_lists(tokens, main_tokens); + tokens = preprocess(tokens); + + // Если указаны опции -M или -MD, выводим зависимости + если (option_make_depend || option_make_depend_with_compile) { + output_file_dependencies(); + если (option_make_depend) { + free_all_types(); + free_all_tokens(); + free_all_preprocess(); + возврат; + } + } + + // Если указана опция -E, выводим результат препроцессора + если (option_preprocess_only) { + output_preprocessed_tokens(tokens); + free_all_types(); + free_all_tokens(); + free_all_preprocess(); + возврат; + } + + // Парсинг в AST + Obj *program = parse(tokens); + + // Открываем временный буфер для вывода + символ *buffer; + size_t buffer_size; + FILE *output_buffer = open_memstream(&buffer, &buffer_size); + + // Обход AST и генерация ассемблера + codegen(program, output_buffer); + fclose(output_buffer); + + // Запись ассемблера в файл + FILE *out = open_output_file(compiler_output_path); + fwrite(buffer, buffer_size, 1, out); + fclose(out); + + free(buffer); + free_all_types(); + free_all_tokens(); + free_all_preprocess(); +} + +// Запуск ассемблера +стат пусто run_assembler(символ *input, символ *output) { + символ *cmd[] = {"as", "-c", input, "-o", output, NULL}; + execute_subprocess(cmd); +} + +// Поиск файла по шаблону (glob) +стат символ *find_matching_file(символ *pattern) { + символ *path = NULL; + glob_t buf = {}; + glob(pattern, 0, NULL, &buf); + если (buf.gl_pathc > 0) + path = strdup(buf.gl_pathv[buf.gl_pathc - 1]); + globfree(&buf); + возврат path; +} + +// Проверка существования файла +бул file_exists(символ *path) { + структ stat st; + возврат !stat(path, &st); +} + +// Поиск пути к системным библиотекам +стат символ *locate_library_path(пусто) { + если (file_exists("/usr/lib/x86_64-linux-gnu/crti.o")) + возврат "/usr/lib/x86_64-linux-gnu"; + если (file_exists("/usr/lib64/crti.o")) + возврат "/usr/lib64"; + error("library path is not found"); +} + +// Поиск пути к библиотекам GCC +стат символ *locate_gcc_library_path(пусто) { + символ *search_patterns[] = { + "/usr/lib/gcc/x86_64-linux-gnu/*/crtbegin.o", + "/usr/lib/gcc/x86_64-pc-linux-gnu/*/crtbegin.o", // Для Gentoo + "/usr/lib/gcc/x86_64-redhat-linux/*/crtbegin.o", // Для Fedora + }; + + для (цел i = 0; i < размер(search_patterns) / размер(*search_patterns); i++) { + символ *path = find_matching_file(search_patterns[i]); + если (path) + возврат dirname(path); + } + + error("gcc library path is not found"); +} + +// Запуск линкера +стат пусто invoke_linker(МассивСтрок *inputs, символ *output) { // было StringArray + МассивСтрок args = {}; // было StringArray + + массив_строк_добавить(&args, "ld"); + массив_строк_добавить(&args, "-o"); + массив_строк_добавить(&args, output); + массив_строк_добавить(&args, "-m"); + массив_строк_добавить(&args, "elf_x86_64"); + + символ *lib_path = locate_library_path(); + символ *gcc_lib_path = locate_gcc_library_path(); + + // Добавляем стартовые объекты в зависимости от типа линковки + если (option_shared_lib) { + массив_строк_добавить(&args, формат("%s/crti.o", lib_path)); + массив_строк_добавить(&args, формат("%s/crtbeginS.o", gcc_lib_path)); + } иначе { + массив_строк_добавить(&args, формат("%s/crt1.o", lib_path)); + массив_строк_добавить(&args, формат("%s/crti.o", lib_path)); + массив_строк_добавить(&args, формат("%s/crtbegin.o", gcc_lib_path)); + } + + // Добавляем пути поиска библиотек + массив_строк_добавить(&args, формат("-L%s", gcc_lib_path)); + массив_строк_добавить(&args, "-L/usr/lib/x86_64-linux-gnu"); + массив_строк_добавить(&args, "-L/usr/lib64"); + массив_строк_добавить(&args, "-L/lib64"); + массив_строк_добавить(&args, "-L/usr/lib/x86_64-linux-gnu"); + массив_строк_добавить(&args, "-L/usr/lib/x86_64-pc-linux-gnu"); + массив_строк_добавить(&args, "-L/usr/lib/x86_64-redhat-linux"); + массив_строк_добавить(&args, "-L/usr/lib"); + массив_строк_добавить(&args, "-L/lib"); + + // Добавляем динамический линкер для не-статической линковки + если (!option_static_link) { + массив_строк_добавить(&args, "-dynamic-linker"); + массив_строк_добавить(&args, "/lib64/ld-linux-x86-64.so.2"); + } + + // Добавляем дополнительные аргументы линкера + для (цел i = 0; i < linker_extra_args.len; i++) + массив_строк_добавить(&args, linker_extra_args.data[i]); + + // Добавляем входные файлы + для (цел i = 0; i < inputs->len; i++) + массив_строк_добавить(&args, inputs->data[i]); + + // Добавляем стандартные библиотеки + если (option_static_link) { + массив_строк_добавить(&args, "--start-group"); + массив_строк_добавить(&args, "-lgcc"); + массив_строк_добавить(&args, "-lgcc_eh"); + массив_строк_добавить(&args, "-lc"); + массив_строк_добавить(&args, "--end-group"); + } иначе { + массив_строк_добавить(&args, "-lc"); + массив_строк_добавить(&args, "-lgcc"); + массив_строк_добавить(&args, "--as-needed"); + массив_строк_добавить(&args, "-lgcc_s"); + массив_строк_добавить(&args, "--no-as-needed"); + } + + // Добавляем конечные объекты + если (option_shared_lib) + массив_строк_добавить(&args, формат("%s/crtendS.o", gcc_lib_path)); + иначе + массив_строк_добавить(&args, формат("%s/crtend.o", gcc_lib_path)); + + массив_строк_добавить(&args, формат("%s/crtn.o", lib_path)); + массив_строк_добавить(&args, NULL); + + execute_subprocess(args.data); +} + +// Определение типа файла по расширению +стат FileType determine_file_type(символ *filename) { + если (source_type_override != FILE_NONE) + возврат source_type_override; + + если (string_ends_with(filename, ".a")) + возврат FILE_AR; + если (string_ends_with(filename, ".so")) + возврат FILE_DSO; + если (string_ends_with(filename, ".o")) + возврат FILE_OBJ; + если (string_ends_with(filename, ".c") || string_ends_with(filename, ".рс") || string_ends_with(filename, ".си")) + возврат FILE_C; + если (string_ends_with(filename, ".s")) + возврат FILE_ASM; + + error(": unknown file extension: %s", filename); +} + +// Главная функция программы +цел главн(цел argc, символ **argv) { + // Регистрируем функцию очистки временных файлов + atexit(cleanup_temporary_files); + init_macros(); + parse_command_line(argc, argv); + + // Режим прямого вызова компилятора (cc1) + если (option_cc1_mode) { + setup_default_include_paths(argv[0]); + compile_cc1(); + free_all_types(); + free_all_tokens(); + free_all_preprocess(); + возврат 0; + } + + // Проверка несовместимых опций + если (input_files.len > 1 && option_output_file && (option_compile_only || option_assembly_only || option_preprocess_only)) + error("cannot specify '-o' with '-c,' '-S' or '-E' with multiple files"); + + МассивСтрок linker_args = {}; // было StringArray + + // Обработка всех входных файлов + для (цел i = 0; i < input_files.len; i++) { + символ *input = input_files.data[i]; + + // Обработка опций -l (библиотеки) + если (!strncmp(input, "-l", 2)) { + массив_строк_добавить(&linker_args, input); + продолжи; + } + + // Обработка опций -Wl, (передача линкеру) + если (!strncmp(input, "-Wl,", 4)) { + символ *s = strdup(input + 4); + символ *arg = strtok(s, ","); + пока (arg) { + массив_строк_добавить(&linker_args, arg); + arg = strtok(NULL, ","); + } + продолжи; + } + + // Определение выходного файла + символ *output; + если (option_output_file) + output = option_output_file; + иначе если (option_assembly_only) + output = change_extension(input, ".s"); + иначе + output = change_extension(input, ".o"); + + FileType file_type = determine_file_type(input); + + // Обработка объектных файлов и архивов + если (file_type == FILE_OBJ || file_type == FILE_AR || file_type == FILE_DSO) { + массив_строк_добавить(&linker_args, input); + продолжи; + } + + // Обработка ассемблерных файлов + если (file_type == FILE_ASM) { + если (!option_assembly_only) + run_assembler(input, output); + продолжи; + } + + // Дальше предполагаем, что это C файл + assert(file_type == FILE_C); + + // Только препроцессор + если (option_preprocess_only || option_make_depend) { + invoke_compiler_cc1(argc, argv, input, NULL); + продолжи; + } + + // Только компиляция в ассемблер + если (option_assembly_only) { + invoke_compiler_cc1(argc, argv, input, output); + продолжи; + } + + // Компиляция и ассемблирование + если (option_compile_only) { + символ *temp = create_temporary_file(); + invoke_compiler_cc1(argc, argv, input, temp); + run_assembler(temp, output); + продолжи; + } + + // Полный цикл: компиляция, ассемблирование и линковка + символ *temp_asm = create_temporary_file(); + символ *temp_obj = create_temporary_file(); + invoke_compiler_cc1(argc, argv, input, temp_asm); + run_assembler(temp_asm, temp_obj); + массив_строк_добавить(&linker_args, temp_obj); + продолжи; + } + + // Запуск линкера, если есть что линковать + если (linker_args.len > 0) + invoke_linker(&linker_args, option_output_file ? option_output_file : "a.out"); + возврат 0; +} \ No newline at end of file diff --git a/md2.o b/md2.o new file mode 100644 index 0000000..86da1f4 Binary files /dev/null and b/md2.o differ diff --git a/parse.c b/parse.c new file mode 100644 index 0000000..402d202 --- /dev/null +++ b/parse.c @@ -0,0 +1,2096 @@ +// Этот файл содержит рекурсивный спуск-парсер для языка C. +#включить "руси.з" + + +// Вспомогательные функции для поддержки русских синонимов +стат Token *пропустить_любой(Token *ток, символ *с1, символ *с2) { + если (equal(ток, с1)) возврат ток->next; + если (equal(ток, с2)) возврат ток->next; + возврат NULL; +} + +стат Token *поглотить_любой(Token **остаток, Token *ток, символ *с1, символ *с2) { + Token *след = пропустить_любой(ток, с1, с2); + если (след) { + *остаток = след; + возврат след; + } + возврат NULL; +} + +типдеф структ { + Obj *перем; + Type *определение_типа; + Type *тип_переч; + цел знач_переч; +} ОблПерем; + +типдеф структ Область Область; +структ Область { + Область *след; + ХешТаблица перем; + ХешТаблица теги; +}; + +типдеф структ { + бул это_типдеф; + бул это_статик; + бул это_внеш; + бул это_инлайн; + бул это_tls; + цел выравн; +} АтрПерем; + +типдеф структ Инициализатор Инициализатор; +структ Инициализатор { + Инициализатор *след; + Type *тип; + Token *ток; + бул гибкий; + Node *выр; + Инициализатор **дочерние; + Member *член; +}; + +типдеф структ Десигнатор Десигнатор; +структ Десигнатор { + Десигнатор *след; + цел инд; + Member *член; + Obj *перем; +}; + +стат Obj *лок_перем; +стат Obj *глоб_перем; +стат Область *тек_обл = &(Область){}; +стат Obj *акт_функция; +стат Node *ожид_goto; +стат Node *опред_метки; +стат символ *цель_прерви; +стат символ *цель_продолжи; +стат Node *акт_выбор; +стат Obj *встр_alloca; + +стат бул это_имя_типа(Token *ток); +стат Type *спецификатор_типа(Token **остаток, Token *ток, АтрПерем *атр); +стат Type *имя_типа(Token **остаток, Token *ток); +стат Type *спец_переч(Token **остаток, Token *ток); +стат Type *спец_типоф(Token **остаток, Token *ток); +стат Type *суффикс_типа(Token **остаток, Token *ток, Type *тип); +стат Type *декларатор(Token **остаток, Token *ток, Type *тип); +стат Node *объявление(Token **остаток, Token *ток, Type *баз_тип, АтрПерем *атр); +стат пусто иниц_массива2(Token **остаток, Token *ток, Инициализатор *инит, цел ш); +стат пусто иниц_структуры2(Token **остаток, Token *ток, Инициализатор *инит, Member *член); +стат пусто инициализатор2(Token **остаток, Token *ток, Инициализатор *инит); +стат Инициализатор *инициализатор(Token **остаток, Token *ток, Type *тип, Type **нов_тип); +стат Node *иниц_лок_перем(Token **остаток, Token *ток, Obj *перем); +стат пусто иниц_глоб_перем(Token *остаток, Token *ток, Obj *перем); +стат Node *составной_оператор(Token **остаток, Token *ток); +стат Node *оператор(Token **остаток, Token *ток); +стат Node *выр_оператор(Token **остаток, Token *ток); +стат Node *выражение(Token **остаток, Token *ток); +стат int64_t вычислить(Node *узел); +стат int64_t вычислить2(Node *узел, символ ***метка); +стат int64_t вычислить_rval(Node *узел, символ ***метка); +стат бул это_конст_выр(Node *узел); +стат Node *присваивание(Token **остаток, Token *ток); +стат Node *лог_или(Token **остаток, Token *ток); +стат двойн выч_дробное(Node *узел); +стат Node *условное(Token **остаток, Token *ток); +стат Node *лог_и(Token **остаток, Token *ток); +стат Node *побит_или(Token **остаток, Token *ток); +стат Node *побит_искл_или(Token **остаток, Token *ток); +стат Node *побит_и(Token **остаток, Token *ток); +стат Node *равенство(Token **остаток, Token *ток); +стат Node *сравнение(Token **остаток, Token *ток); +стат Node *сдвиг(Token **остаток, Token *ток); +стат Node *сложение(Token **остаток, Token *ток); +стат Node *нов_слож(Node *лев, Node *прав, Token *ток); +стат Node *нов_выч(Node *лев, Node *прав, Token *ток); +стат Node *умножение(Token **остаток, Token *ток); +стат Node *приведение(Token **остаток, Token *ток); +стат Member *найти_член_стр(Type *тип, Token *ток); +стат Type *объяв_структ(Token **остаток, Token *ток); +стат Type *объяв_объед(Token **остаток, Token *ток); +стат Node *постфикс(Token **остаток, Token *ток); +стат Node *вызов_фн(Token **остаток, Token *ток, Node *узел); +стат Node *унарный(Token **остаток, Token *ток); +стат Node *первичный(Token **остаток, Token *ток); +стат Token *разбор_типдеф(Token *ток, Type *баз_тип); +стат бул это_функция(Token *ток); +стат Token *функция(Token *ток, Type *баз_тип, АтрПерем *атр); +стат Token *глоб_перем_объяв(Token *ток, Type *баз_тип, АтрПерем *атр); + +стат цел округлить_вниз(цел ч, цел выр) { + возврат align_to(ч - выр + 1, выр); +} + +стат пусто войти_в_обл(пусто) { + Область *обл = calloc(1, размер(Область)); + обл->след = тек_обл; + тек_обл = обл; +} + +стат пусто выйти_из_обл(пусто) { + тек_обл = тек_обл->след; +} + +стат ОблПерем *найти_перем(Token *ток) { + для (Область *обл = тек_обл; обл; обл = обл->след) { + ОблПерем *обл2 = хеш_получить2(&обл->перем, ток->loc, ток->len); + если (обл2) + возврат обл2; + } + возврат NULL; +} + +стат Type *найти_тег(Token *ток) { + для (Область *обл = тек_обл; обл; обл = обл->след) { + Type *тип = хеш_получить2(&обл->теги, ток->loc, ток->len); + если (тип) + возврат тип; + } + возврат NULL; +} + +стат Node *создать_узел(NodeKind вид, Token *ток) { + Node *узел = calloc(1, размер(Node)); + узел->kind = вид; + узел->tok = ток; + возврат узел; +} + +стат Node *создать_бин_узел(NodeKind вид, Node *лев, Node *прав, Token *ток) { + Node *узел = создать_узел(вид, ток); + узел->lhs = лев; + узел->rhs = прав; + возврат узел; +} + +стат Node *создать_ун_узел(NodeKind вид, Node *выр, Token *ток) { + Node *узел = создать_узел(вид, ток); + узел->lhs = выр; + возврат узел; +} + +стат Node *создать_число(int64_t знач, Token *ток) { + Node *узел = создать_узел(ND_NUM, ток); + узел->val = знач; + возврат узел; +} + +стат Node *создать_длин_число(int64_t знач, Token *ток) { + Node *узел = создать_узел(ND_NUM, ток); + узел->val = знач; + узел->ty = ty_long; + возврат узел; +} + +стат Node *создать_бузн_длин_число(длин знач, Token *ток) { + Node *узел = создать_узел(ND_NUM, ток); + узел->val = знач; + узел->ty = ty_ulong; + возврат узел; +} + +стат Node *создать_узел_перем(Obj *перем, Token *ток) { + Node *узел = создать_узел(ND_VAR, ток); + узел->var = перем; + возврат узел; +} + +стат Node *создать_узел_vla(Obj *перем, Token *ток) { + Node *узел = создать_узел(ND_VLA_PTR, ток); + узел->var = перем; + возврат узел; +} + +Node *new_cast(Node *выр, Type *тип) { + add_type(выр); + Node *узел = calloc(1, размер(Node)); + узел->kind = ND_CAST; + узел->tok = выр->tok; + узел->lhs = выр; + узел->ty = copy_type(тип); + возврат узел; +} + +стат ОблПерем *зарег_перем(символ *имя) { + ОблПерем *обл = calloc(1, размер(ОблПерем)); + хеш_вставить(&тек_обл->перем, имя, обл); + возврат обл; +} + +стат Инициализатор *создать_инит(Type *тип, бул гибкий) { + Инициализатор *инит = calloc(1, размер(Инициализатор)); + инит->тип = тип; + если (тип->kind == TY_ARRAY) { + если (гибкий && тип->size < 0) { + инит->гибкий = истина; + возврат инит; + } + инит->дочерние = calloc(тип->array_len, размер(Инициализатор *)); + для (цел ш = 0; ш < тип->array_len; ш++) + инит->дочерние[ш] = создать_инит(тип->base, ложь); + возврат инит; + } + если (тип->kind == TY_STRUCT || тип->kind == TY_UNION) { + цел длина = 0; + для (Member *член = тип->members; член; член = член->next) + длина++; + инит->дочерние = calloc(длина, размер(Инициализатор *)); + для (Member *член = тип->members; член; член = член->next) { + если (гибкий && тип->is_flexible && !член->next) { + Инициализатор *доч = calloc(1, размер(Инициализатор)); + доч->тип = член->ty; + доч->гибкий = истина; + инит->дочерние[член->idx] = доч; + } иначе { + инит->дочерние[член->idx] = создать_инит(член->ty, ложь); + } + } + возврат инит; + } + возврат инит; +} + +стат Obj *создать_перем(символ *имя, Type *тип) { + Obj *перем = calloc(1, размер(Obj)); + перем->name = имя; + перем->ty = тип; + перем->align = тип->align; + зарег_перем(имя)->перем = перем; + возврат перем; +} + +стат Obj *создать_лок_перем(символ *имя, Type *тип) { + Obj *перем = создать_перем(имя, тип); + перем->is_local = истина; + перем->next = лок_перем; + лок_перем = перем; + возврат перем; +} + +стат Obj *создать_глоб_перем(символ *имя, Type *тип) { + Obj *перем = создать_перем(имя, тип); + перем->next = глоб_перем; + перем->is_static = истина; + перем->is_definition = истина; + глоб_перем = перем; + возврат перем; +} + +стат символ *создать_уник_метку(пусто) { + стат цел щ = 0; + возврат формат(".L..%d", щ++); +} + +стат Obj *создать_аноним_глоб(Type *тип) { + возврат создать_глоб_перем(создать_уник_метку(), тип); +} + +стат Obj *создать_строк_литерал(символ *стр, Type *тип) { + Obj *перем = создать_аноним_глоб(тип); + перем->init_data = стр; + возврат перем; +} + +стат символ *извлечь_ид(Token *ток) { + если (ток->kind != TK_IDENT) + error_tok(ток, "ожидался идентификатор"); + возврат strndup(ток->loc, ток->len); +} + +стат Type *найти_типдеф(Token *ток) { + если (ток->kind == TK_IDENT) { + ОблПерем *обл = найти_перем(ток); + если (обл) + возврат обл->определение_типа; + } + возврат NULL; +} + +стат пусто зарег_тег(Token *ток, Type *тип) { + хеш_вставить2(&тек_обл->теги, ток->loc, ток->len, тип); +} + +стат Type *спецификатор_типа(Token **остаток, Token *ток, АтрПерем *атр) { + переч { + VOID = 1 << 0, BOOL = 1 << 2, CHAR = 1 << 4, + SHORT = 1 << 6, INT = 1 << 8, LONG = 1 << 10, + FLOAT = 1 << 12, DOUBLE = 1 << 14, OTHER = 1 << 16, + SIGNED = 1 << 17, UNSIGNED = 1 << 18, + }; + Type *тип = ty_int; + цел счет = 0; + бул атом = ложь; + пока (это_имя_типа(ток)) { + если ((equal(ток, "typedef") || equal(ток, "типдеф")) || (equal(ток, "static") || equal(ток, "стат")) || + (equal(ток, "extern") || equal(ток, "внеш")) || (equal(ток, "inline") || equal(ток, "инлайн")) || + equal(ток, "_Thread_local") || equal(ток, "__thread")) { + если (!атр) + error_tok(ток, "спецификатор класса памяти не допускается в данном контексте"); + если ((equal(ток, "typedef") || equal(ток, "типдеф"))) атр->это_типдеф = истина; + иначе если ((equal(ток, "static") || equal(ток, "стат"))) атр->это_статик = истина; + иначе если ((equal(ток, "extern") || equal(ток, "внеш"))) атр->это_внеш = истина; + иначе если ((equal(ток, "inline") || equal(ток, "инлайн"))) атр->это_инлайн = истина; + иначе атр->это_tls = истина; + если (атр->это_типдеф && атр->это_статик + атр->это_внеш + атр->это_инлайн + атр->это_tls > 1) + error_tok(ток, "типдеф не может использоваться вместе с стат, внеш, инлайн, __thread или _Thread_local"); + ток = ток->next; + продолжи; + } + если (поглотить_любой(&ток, ток, "const", "конст") || поглотить_любой(&ток, ток, "volatile", "летуч") || + поглотить_любой(&ток, ток, "auto", "авто") || поглотить_любой(&ток, ток, "register", "регистр") || + поглотить_любой(&ток, ток, "restrict", "огранич") || consume(&ток, ток, "__restrict") || + consume(&ток, ток, "__restrict__") || consume(&ток, ток, "_Noreturn") || consume(&ток, ток, "_Безвозврат")) + продолжи; + если (equal(ток, "_Atomic") || equal(ток, "_Атом")) { + ток = ток->next; + если (equal(ток, "(")) { + тип = имя_типа(&ток, ток->next); + ток = skip(ток, ")"); + } + атом = истина; + продолжи; + } + если (equal(ток, "_Alignas") || equal(ток, "_Выравнивание")) { + если (!атр) error_tok(ток, "_Alignas (или _Выравнивание) не допускается в данном контексте"); + ток = skip(ток->next, "("); + если (это_имя_типа(ток)) атр->выравн = имя_типа(&ток, ток)->align; + иначе атр->выравн = const_expr(&ток, ток); + ток = skip(ток, ")"); + продолжи; + } + Type *т2 = найти_типдеф(ток); + если ((equal(ток, "struct") || equal(ток, "структ")) || (equal(ток, "union") || equal(ток, "объед")) || + (equal(ток, "enum") || equal(ток, "переч")) || (equal(ток, "typeof") || equal(ток, "типоф")) || т2) { + если (счет) прерви; + если ((equal(ток, "struct") || equal(ток, "структ"))) тип = объяв_структ(&ток, ток->next); + иначе если ((equal(ток, "union") || equal(ток, "объед"))) тип = объяв_объед(&ток, ток->next); + иначе если ((equal(ток, "enum") || equal(ток, "переч"))) тип = спец_переч(&ток, ток->next); + иначе если ((equal(ток, "typeof") || equal(ток, "типоф"))) тип = спец_типоф(&ток, ток->next); + иначе { тип = т2; ток = ток->next; } + счет += OTHER; + продолжи; + } + если ((equal(ток, "void") || equal(ток, "пусто"))) счет += VOID; + иначе если (equal(ток, "_Bool")) счет += BOOL; + иначе если ((equal(ток, "char") || equal(ток, "символ"))) счет += CHAR; + иначе если ((equal(ток, "short") || equal(ток, "корот"))) счет += SHORT; + иначе если ((equal(ток, "int") || equal(ток, "цел"))) счет += INT; + иначе если ((equal(ток, "long") || equal(ток, "длин"))) счет += LONG; + иначе если ((equal(ток, "float") || equal(ток, "плав"))) счет += FLOAT; + иначе если ((equal(ток, "double") || equal(ток, "двойн"))) счет += DOUBLE; + иначе если ((equal(ток, "signed") || equal(ток, "знак"))) счет |= SIGNED; + иначе если ((equal(ток, "unsigned") || equal(ток, "беззнак"))) счет |= UNSIGNED; + иначе unreachable(); + выбор (счет) { + случай VOID: тип = ty_void; прерви; + случай BOOL: тип = ty_bool; прерви; + случай CHAR: случай SIGNED + CHAR: тип = ty_char; прерви; + случай UNSIGNED + CHAR: тип = ty_uchar; прерви; + случай SHORT: случай SHORT + INT: случай SIGNED + SHORT: случай SIGNED + SHORT + INT: тип = ty_short; прерви; + случай UNSIGNED + SHORT: случай UNSIGNED + SHORT + INT: тип = ty_ushort; прерви; + случай INT: случай SIGNED: случай SIGNED + INT: тип = ty_int; прерви; + случай UNSIGNED: случай UNSIGNED + INT: тип = ty_uint; прерви; + случай LONG: случай LONG + INT: случай LONG + LONG: случай LONG + LONG + INT: + случай SIGNED + LONG: случай SIGNED + LONG + INT: случай SIGNED + LONG + LONG: случай SIGNED + LONG + LONG + INT: + тип = ty_long; прерви; + случай UNSIGNED + LONG: случай UNSIGNED + LONG + INT: случай UNSIGNED + LONG + LONG: случай UNSIGNED + LONG + LONG + INT: + тип = ty_ulong; прерви; + случай FLOAT: тип = ty_float; прерви; + случай DOUBLE: тип = ty_double; прерви; + случай LONG + DOUBLE: тип = ty_ldouble; прерви; + умолч: error_tok(ток, "недопустимый тип"); + } + ток = ток->next; + } + если (атом) { тип = copy_type(тип); тип->is_atomic = истина; } + *остаток = ток; + возврат тип; +} + +стат Type *разбор_парам(Token **остаток, Token *ток, Type *тип) { + если ((equal(ток, "void") || equal(ток, "пусто")) && equal(ток->next, ")")) { + *остаток = ток->next->next; + возврат func_type(тип); + } + Type голова = {}; + Type *тек = &голова; + бул вариад = ложь; + пока (!equal(ток, ")")) { + если (тек != &голова) ток = skip(ток, ","); + если (equal(ток, "...")) { вариад = истина; ток = ток->next; skip(ток, ")"); прерви; } + Type *т2 = спецификатор_типа(&ток, ток, NULL); + т2 = декларатор(&ток, ток, т2); + Token *имя = т2->name; + если (т2->kind == TY_ARRAY) { т2 = pointer_to(т2->base); т2->name = имя; } + иначе если (т2->kind == TY_FUNC) { т2 = pointer_to(т2); т2->name = имя; } + тек = тек->next = copy_type(т2); + } + если (тек == &голова) вариад = истина; + тип = func_type(тип); + тип->params = голова.next; + тип->is_variadic = вариад; + *остаток = ток->next; + возврат тип; +} + +стат Type *разбор_изм_масс(Token **остаток, Token *ток, Type *тип) { + пока ((equal(ток, "static") || equal(ток, "стат")) || (equal(ток, "restrict") || equal(ток, "огранич"))) + ток = ток->next; + если (equal(ток, "]")) { + тип = суффикс_типа(остаток, ток->next, тип); + возврат array_of(тип, -1); + } + Node *выр = условное(&ток, ток); + ток = skip(ток, "]"); + тип = суффикс_типа(остаток, ток, тип); + если (тип->kind == TY_VLA || !это_конст_выр(выр)) + возврат vla_of(тип, выр); + возврат array_of(тип, вычислить(выр)); +} + +стат Type *суффикс_типа(Token **остаток, Token *ток, Type *тип) { + если (equal(ток, "(")) возврат разбор_парам(остаток, ток->next, тип); + если (equal(ток, "[")) возврат разбор_изм_масс(остаток, ток->next, тип); + *остаток = ток; + возврат тип; +} + +стат Type *разбор_указ(Token **остаток, Token *ток, Type *тип) { + пока (consume(&ток, ток, "*")) { + тип = pointer_to(тип); + пока ((equal(ток, "const") || equal(ток, "конст")) || (equal(ток, "volatile") || equal(ток, "летуч")) || + (equal(ток, "restrict") || equal(ток, "огранич")) || equal(ток, "__restrict") || equal(ток, "__restrict__")) + ток = ток->next; + } + *остаток = ток; + возврат тип; +} + +стат Type *декларатор(Token **остаток, Token *ток, Type *тип) { + тип = разбор_указ(&ток, ток, тип); + если (equal(ток, "(")) { + Token *старт = ток; + Type загл = {}; + декларатор(&ток, старт->next, &загл); + ток = skip(ток, ")"); + тип = суффикс_типа(остаток, ток, тип); + возврат декларатор(&ток, старт->next, тип); + } + Token *имя = NULL; + Token *поз_имени = ток; + если (ток->kind == TK_IDENT) { имя = ток; ток = ток->next; } + тип = суффикс_типа(остаток, ток, тип); + тип->name = имя; + тип->name_pos = поз_имени; + возврат тип; +} + +стат Type *абстр_декл(Token **остаток, Token *ток, Type *тип) { + тип = разбор_указ(&ток, ток, тип); + если (equal(ток, "(")) { + Token *старт = ток; + Type загл = {}; + абстр_декл(&ток, старт->next, &загл); + ток = skip(ток, ")"); + тип = суффикс_типа(остаток, ток, тип); + возврат абстр_декл(&ток, старт->next, тип); + } + возврат суффикс_типа(остаток, ток, тип); +} + +стат Type *имя_типа(Token **остаток, Token *ток) { + Type *тип = спецификатор_типа(&ток, ток, NULL); + возврат абстр_декл(остаток, ток, тип); +} + +стат бул это_заверш(Token *ток) { + возврат equal(ток, "}") || (equal(ток, ",") && equal(ток->next, "}")); +} + +стат бул погл_заверш(Token **остаток, Token *ток) { + если (equal(ток, "}")) { *остаток = ток->next; возврат истина; } + если (equal(ток, ",") && equal(ток->next, "}")) { *остаток = ток->next->next; возврат истина; } + возврат ложь; +} + +стат Type *спец_переч(Token **остаток, Token *ток) { + Type *тип = enum_type(); + Token *тег = NULL; + если (ток->kind == TK_IDENT) { тег = ток; ток = ток->next; } + если (тег && !equal(ток, "{")) { + Type *т = найти_тег(тег); + если (!т) error_tok(тег, "неизвестный тип перечисления"); + если (т->kind != TY_ENUM) error_tok(тег, "не является тегом перечисления"); + *остаток = ток; + возврат т; + } + ток = skip(ток, "{"); + цел ш = 0; + цел знач = 0; + пока (!погл_заверш(остаток, ток)) { + если (ш++ > 0) ток = skip(ток, ","); + символ *имя = извлечь_ид(ток); + ток = ток->next; + если (equal(ток, "=")) знач = const_expr(&ток, ток->next); + ОблПерем *обл = зарег_перем(имя); + обл->тип_переч = тип; + обл->знач_переч = знач++; + } + если (тег) зарег_тег(тег, тип); + возврат тип; +} + +стат Type *спец_типоф(Token **остаток, Token *ток) { + ток = skip(ток, "("); + Type *тип; + если (это_имя_типа(ток)) { + тип = имя_типа(&ток, ток); + } иначе { + Node *узел = выражение(&ток, ток); + add_type(узел); + тип = узел->ty; + } + *остаток = skip(ток, ")"); + возврат тип; +} + +стат Node *создать_выч_vla(Type *тип, Token *ток) { + Node *узел = создать_узел(ND_NULL_EXPR, ток); + если (тип->base) узел = создать_бин_узел(ND_COMMA, узел, создать_выч_vla(тип->base, ток), ток); + если (тип->kind != TY_VLA) возврат узел; + Node *баз_разм; + если (тип->base->kind == TY_VLA) баз_разм = создать_узел_перем(тип->base->vla_size, ток); + иначе баз_разм = создать_число(тип->base->size, ток); + тип->vla_size = создать_лок_перем("", ty_ulong); + Node *выр = создать_бин_узел(ND_ASSIGN, создать_узел_перем(тип->vla_size, ток), + создать_бин_узел(ND_MUL, тип->vla_len, баз_разм, ток), ток); + возврат создать_бин_узел(ND_COMMA, узел, выр, ток); +} + +стат Node *создать_узел_alloca(Node *разм) { + Node *узел = создать_ун_узел(ND_FUNCALL, создать_узел_перем(встр_alloca, разм->tok), разм->tok); + узел->func_ty = встр_alloca->ty; + узел->ty = встр_alloca->ty->return_ty; + узел->args = разм; + add_type(разм); + возврат узел; +} + +стат Node *объявление(Token **остаток, Token *ток, Type *баз_тип, АтрПерем *атр) { + Node голова = {}; + Node *тек = &голова; + цел ш = 0; + пока (!equal(ток, ";")) { + если (ш++ > 0) ток = skip(ток, ","); + Type *тип = декларатор(&ток, ток, баз_тип); + если (тип->kind == TY_VOID) error_tok(ток, "переменная объявлена как пусто (void)"); + если (!тип->name) error_tok(тип->name_pos, "имя переменной опущено"); + если (атр && атр->это_статик) { + Obj *перем = создать_аноним_глоб(тип); + зарег_перем(извлечь_ид(тип->name))->перем = перем; + если (equal(ток, "=")) иниц_глоб_перем(&ток, ток->next, перем); + продолжи; + } + тек = тек->next = создать_ун_узел(ND_EXPR_STMT, создать_выч_vla(тип, ток), ток); + если (тип->kind == TY_VLA) { + если (equal(ток, "=")) error_tok(ток, "объект переменного размера не может быть инициализирован"); + Obj *перем = создать_лок_перем(извлечь_ид(тип->name), тип); + Token *т = тип->name; + Node *выр = создать_бин_узел(ND_ASSIGN, создать_узел_vla(перем, т), + создать_узел_alloca(создать_узел_перем(тип->vla_size, т)), т); + тек = тек->next = создать_ун_узел(ND_EXPR_STMT, выр, т); + продолжи; + } + Obj *перем = создать_лок_перем(извлечь_ид(тип->name), тип); + если (атр && атр->выравн) перем->align = атр->выравн; + если (equal(ток, "=")) { + Node *выр = иниц_лок_перем(&ток, ток->next, перем); + тек = тек->next = создать_ун_узел(ND_EXPR_STMT, выр, ток); + } + если (перем->ty->size < 0) error_tok(тип->name, "переменная имеет неполный тип"); + если (перем->ty->kind == TY_VOID) error_tok(тип->name, "переменная объявлена как пусто (void)"); + } + Node *узел = создать_узел(ND_BLOCK, ток); + узел->body = голова.next; + *остаток = ток->next; + возврат узел; +} + +стат Token *проп_лишний_инит(Token *ток) { + если (equal(ток, "{")) { ток = проп_лишний_инит(ток->next); возврат skip(ток, "}"); } + присваивание(&ток, ток); + возврат ток; +} + +стат пусто разбор_стр_инит(Token **остаток, Token *ток, Инициализатор *инит) { + если (инит->гибкий) + *инит = *создать_инит(array_of(инит->тип->base, ток->ty->array_len), ложь); + цел длина = MIN(инит->тип->array_len, ток->ty->array_len); + выбор (инит->тип->base->size) { + случай 1: { символ *стр = ток->str; для (цел ш = 0; ш < длина; ш++) инит->дочерние[ш]->выр = создать_число(стр[ш], ток); прерви; } + случай 2: { uint16_t *стр = (uint16_t *)ток->str; для (цел ш = 0; ш < длина; ш++) инит->дочерние[ш]->выр = создать_число(стр[ш], ток); прерви; } + случай 4: { uint32_t *стр = (uint32_t *)ток->str; для (цел ш = 0; ш < длина; ш++) инит->дочерние[ш]->выр = создать_число(стр[ш], ток); прерви; } + умолч: unreachable(); + } + *остаток = ток->next; +} + +стат пусто разбор_десиг_масс(Token **остаток, Token *ток, Type *тип, цел *начало, цел *конец) { + *начало = const_expr(&ток, ток->next); + если (*начало >= тип->array_len) error_tok(ток, "индекс десигнатора массива выходит за его границы"); + если (equal(ток, "...")) { + *конец = const_expr(&ток, ток->next); + если (*конец >= тип->array_len) error_tok(ток, "индекс десигнатора массива выходит за его границы"); + если (*конец < *начало) error_tok(ток, "диапазон десигнатора массива [%d, %d] пуст", *начало, *конец); + } иначе { *конец = *начало; } + *остаток = skip(ток, "]"); +} + +стат Member *разбор_десиг_стр(Token **остаток, Token *ток, Type *тип) { + Token *старт = ток; + ток = skip(ток, "."); + если (ток->kind != TK_IDENT) error_tok(ток, "ожидался десигнатор поля"); + для (Member *член = тип->members; член; член = член->next) { + если (член->ty->kind == TY_STRUCT && !член->name) { + если (найти_член_стр(член->ty, ток)) { *остаток = старт; возврат член; } + продолжи; + } + если (член->name->len == ток->len && !strncmp(член->name->loc, ток->loc, ток->len)) { + *остаток = ток->next; + возврат член; + } + } + error_tok(ток, "в структуре нет такого члена"); +} + +стат пусто разбор_обозн(Token **остаток, Token *ток, Инициализатор *инит) { + если (equal(ток, "[")) { + если (инит->тип->kind != TY_ARRAY) error_tok(ток, "индекс массива в инициализаторе не-массива"); + цел начало, конец; + разбор_десиг_масс(&ток, ток, инит->тип, &начало, &конец); + Token *т2; + для (цел ш = начало; ш <= конец; ш++) разбор_обозн(&т2, ток, инит->дочерние[ш]); + иниц_массива2(остаток, т2, инит, начало + 1); + возврат; + } + если (equal(ток, ".") && инит->тип->kind == TY_STRUCT) { + Member *член = разбор_десиг_стр(&ток, ток, инит->тип); + разбор_обозн(&ток, ток, инит->дочерние[член->idx]); + инит->выр = NULL; + иниц_структуры2(остаток, ток, инит, член->next); + возврат; + } + если (equal(ток, ".") && инит->тип->kind == TY_UNION) { + Member *член = разбор_десиг_стр(&ток, ток, инит->тип); + инит->член = член; + разбор_обозн(остаток, ток, инит->дочерние[член->idx]); + возврат; + } + если (equal(ток, ".")) error_tok(ток, "имя поля не в инициализаторе структуры или объединения"); + если (equal(ток, "=")) ток = ток->next; + инициализатор2(остаток, ток, инит); +} + +стат цел подсч_эл_инит(Token *ток, Type *тип) { + бул первый = истина; + Инициализатор *загл = создать_инит(тип->base, истина); + цел ш = 0, макс = 0; + пока (!погл_заверш(&ток, ток)) { + если (!первый) ток = skip(ток, ","); + первый = ложь; + если (equal(ток, "[")) { + ш = const_expr(&ток, ток->next); + если (equal(ток, "...")) ш = const_expr(&ток, ток->next); + ток = skip(ток, "]"); + разбор_обозн(&ток, ток, загл); + } иначе { инициализатор2(&ток, ток, загл); } + ш++; + макс = MAX(макс, ш); + } + возврат макс; +} + +стат пусто разбор_иниц_масс(Token **остаток, Token *ток, Инициализатор *инит) { + ток = skip(ток, "{"); + если (инит->гибкий) { + цел длина = подсч_эл_инит(ток, инит->тип); + *инит = *создать_инит(array_of(инит->тип->base, длина), ложь); + } + бул первый = истина; + если (инит->гибкий) { + цел длина = подсч_эл_инит(ток, инит->тип); + *инит = *создать_инит(array_of(инит->тип->base, длина), ложь); + } + для (цел ш = 0; !погл_заверш(остаток, ток); ш++) { + если (!первый) ток = skip(ток, ","); + первый = ложь; + если (equal(ток, "[")) { + цел начало, конец; + разбор_десиг_масс(&ток, ток, инит->тип, &начало, &конец); + Token *т2; + для (цел щ = начало; щ <= конец; щ++) разбор_обозн(&т2, ток, инит->дочерние[щ]); + ток = т2; + ш = конец; + продолжи; + } + если (ш < инит->тип->array_len) инициализатор2(&ток, ток, инит->дочерние[ш]); + иначе ток = проп_лишний_инит(ток); + } +} + +стат пусто иниц_массива2(Token **остаток, Token *ток, Инициализатор *инит, цел ш) { + если (инит->гибкий) { + цел длина = подсч_эл_инит(ток, инит->тип); + *инит = *создать_инит(array_of(инит->тип->base, длина), ложь); + } + для (; ш < инит->тип->array_len && !это_заверш(ток); ш++) { + Token *старт = ток; + если (ш > 0) ток = skip(ток, ","); + если (equal(ток, "[") || equal(ток, ".")) { *остаток = старт; возврат; } + инициализатор2(&ток, ток, инит->дочерние[ш]); + } + *остаток = ток; +} + +стат пусто разбор_иниц_стр(Token **остаток, Token *ток, Инициализатор *инит) { + ток = skip(ток, "{"); + Member *член = инит->тип->members; + бул первый = истина; + пока (!погл_заверш(остаток, ток)) { + если (!первый) ток = skip(ток, ","); + первый = ложь; + если (equal(ток, ".")) { + член = разбор_десиг_стр(&ток, ток, инит->тип); + разбор_обозн(&ток, ток, инит->дочерние[член->idx]); + член = член->next; + продолжи; + } + если (член) { инициализатор2(&ток, ток, инит->дочерние[член->idx]); член = член->next; } + иначе ток = проп_лишний_инит(ток); + } +} + +стат пусто иниц_структуры2(Token **остаток, Token *ток, Инициализатор *инит, Member *член) { + бул первый = истина; + для (; член && !это_заверш(ток); член = член->next) { + Token *старт = ток; + если (!первый) ток = skip(ток, ","); + первый = ложь; + если (equal(ток, "[") || equal(ток, ".")) { *остаток = старт; возврат; } + инициализатор2(&ток, ток, инит->дочерние[член->idx]); + } + *остаток = ток; +} + +стат пусто разбор_иниц_объед(Token **остаток, Token *ток, Инициализатор *инит) { + если (equal(ток, "{") && equal(ток->next, ".")) { + Member *член = разбор_десиг_стр(&ток, ток->next, инит->тип); + инит->член = член; + разбор_обозн(&ток, ток, инит->дочерние[член->idx]); + *остаток = skip(ток, "}"); + возврат; + } + инит->член = инит->тип->members; + если (equal(ток, "{")) { + инициализатор2(&ток, ток->next, инит->дочерние[0]); + consume(&ток, ток, ","); + *остаток = skip(ток, "}"); + } иначе { инициализатор2(остаток, ток, инит->дочерние[0]); } +} + +стат пусто инициализатор2(Token **остаток, Token *ток, Инициализатор *инит) { + если (инит->тип->kind == TY_ARRAY && ток->kind == TK_STR) { разбор_стр_инит(остаток, ток, инит); возврат; } + если (инит->тип->kind == TY_ARRAY) { + если (equal(ток, "{")) разбор_иниц_масс(остаток, ток, инит); + иначе иниц_массива2(остаток, ток, инит, 0); + возврат; + } + если (инит->тип->kind == TY_STRUCT) { + если (equal(ток, "{")) { разбор_иниц_стр(остаток, ток, инит); возврат; } + Node *выр = присваивание(остаток, ток); + add_type(выр); + если (выр->ty->kind == TY_STRUCT) { инит->выр = выр; возврат; } + иниц_структуры2(остаток, ток, инит, инит->тип->members); + возврат; + } + если (инит->тип->kind == TY_UNION) { разбор_иниц_объед(остаток, ток, инит); возврат; } + если (equal(ток, "{")) { инициализатор2(&ток, ток->next, инит); *остаток = skip(ток, "}"); возврат; } + инит->выр = присваивание(остаток, ток); +} + +стат Type *продубл_стр(Type *тип) { + тип = copy_type(тип); + Member голова = {}; + Member *тек = &голова; + для (Member *член = тип->members; член; член = член->next) { + Member *м = calloc(1, размер(Member)); + *м = *член; + тек = тек->next = м; + } + тип->members = голова.next; + возврат тип; +} + +стат Инициализатор *инициализатор(Token **остаток, Token *ток, Type *тип, Type **нов_тип) { + Инициализатор *инит = создать_инит(тип, истина); + инициализатор2(остаток, ток, инит); + если ((тип->kind == TY_STRUCT || тип->kind == TY_UNION) && тип->is_flexible) { + тип = продубл_стр(тип); + Member *член = тип->members; + пока (член->next) член = член->next; + член->ty = инит->дочерние[член->idx]->тип; + тип->size += член->ty->size; + *нов_тип = тип; + возврат инит; + } + *нов_тип = инит->тип; + возврат инит; +} + +стат Node *постр_выр_десиг(Десигнатор *дес, Token *ток) { + если (дес->перем) возврат создать_узел_перем(дес->перем, ток); + если (дес->член) { + Node *узел = создать_ун_узел(ND_MEMBER, постр_выр_десиг(дес->след, ток), ток); + узел->member = дес->член; + возврат узел; + } + Node *лев = постр_выр_десиг(дес->след, ток); + Node *прав = создать_число(дес->инд, ток); + возврат создать_ун_узел(ND_DEREF, нов_слож(лев, прав, ток), ток); +} + +стат Node *постр_лок_инит(Инициализатор *инит, Type *тип, Десигнатор *дес, Token *ток) { + если (тип->kind == TY_ARRAY) { + Node *узел = создать_узел(ND_NULL_EXPR, ток); + для (цел ш = 0; ш < тип->array_len; ш++) { + Десигнатор д2 = {дес, ш}; + Node *прав = постр_лок_инит(инит->дочерние[ш], тип->base, &д2, ток); + узел = создать_бин_узел(ND_COMMA, узел, прав, ток); + } + возврат узел; + } + если (тип->kind == TY_STRUCT && !инит->выр) { + Node *узел = создать_узел(ND_NULL_EXPR, ток); + для (Member *член = тип->members; член; член = член->next) { + Десигнатор д2 = {дес, 0, член}; + Node *прав = постр_лок_инит(инит->дочерние[член->idx], член->ty, &д2, ток); + узел = создать_бин_узел(ND_COMMA, узел, прав, ток); + } + возврат узел; + } + если (тип->kind == TY_UNION) { + Member *член = инит->член ? инит->член : тип->members; + Десигнатор д2 = {дес, 0, член}; + возврат постр_лок_инит(инит->дочерние[член->idx], член->ty, &д2, ток); + } + если (!инит->выр) возврат создать_узел(ND_NULL_EXPR, ток); + Node *лев = постр_выр_десиг(дес, ток); + возврат создать_бин_узел(ND_ASSIGN, лев, инит->выр, ток); +} + +стат Node *иниц_лок_перем(Token **остаток, Token *ток, Obj *перем) { + Инициализатор *инит = инициализатор(остаток, ток, перем->ty, &перем->ty); + Десигнатор дес = {NULL, 0, NULL, перем}; + Node *лев = создать_узел(ND_MEMZERO, ток); + лев->var = перем; + Node *прав = постр_лок_инит(инит, перем->ty, &дес, ток); + возврат создать_бин_узел(ND_COMMA, лев, прав, ток); +} + +стат uint64_t проч_буф(символ *буф, цел разм) { + если (разм == 1) возврат *буф; + если (разм == 2) возврат *(uint16_t *)буф; + если (разм == 4) возврат *(uint32_t *)буф; + если (разм == 8) возврат *(uint64_t *)буф; + unreachable(); +} + +стат пусто запиш_буф(символ *буф, uint64_t знач, цел разм) { + если (разм == 1) *буф = знач; + иначе если (разм == 2) *(uint16_t *)буф = знач; + иначе если (разм == 4) *(uint32_t *)буф = знач; + иначе если (разм == 8) *(uint64_t *)буф = знач; + иначе unreachable(); +} + +стат Relocation * +сериал_глоб(Relocation *тек, Инициализатор *инит, Type *тип, символ *буф, цел смещ) { + если (тип->kind == TY_ARRAY) { + цел разм = тип->base->size; + для (цел ш = 0; ш < тип->array_len; ш++) + тек = сериал_глоб(тек, инит->дочерние[ш], тип->base, буф, смещ + разм * ш); + возврат тек; + } + если (тип->kind == TY_STRUCT) { + для (Member *член = тип->members; член; член = член->next) { + если (член->is_bitfield) { + Node *выр = инит->дочерние[член->idx]->выр; + если (!выр) прерви; + символ *поз = буф + смещ + член->offset; + uint64_t стар = проч_буф(поз, член->ty->size); + uint64_t нов = вычислить(выр); + uint64_t маска = (1L << член->bit_width) - 1; + uint64_t объед_знач = стар | ((нов & маска) << член->bit_offset); + запиш_буф(поз, объед_знач, член->ty->size); + } иначе { + тек = сериал_глоб(тек, инит->дочерние[член->idx], член->ty, буф, смещ + член->offset); + } + } + возврат тек; + } + если (тип->kind == TY_UNION) { + если (!инит->член) возврат тек; + возврат сериал_глоб(тек, инит->дочерние[инит->член->idx], инит->член->ty, буф, смещ); + } + если (!инит->выр) возврат тек; + если (тип->kind == TY_FLOAT) { *(плав *)(буф + смещ) = выч_дробное(инит->выр); возврат тек; } + если (тип->kind == TY_DOUBLE) { *(двойн *)(буф + смещ) = выч_дробное(инит->выр); возврат тек; } + символ **метка = NULL; + uint64_t знач = вычислить2(инит->выр, &метка); + если (!метка) { запиш_буф(буф + смещ, знач, тип->size); возврат тек; } + Relocation *рел = calloc(1, размер(Relocation)); + рел->offset = смещ; + рел->label = метка; + рел->addend = знач; + тек->next = рел; + возврат тек->next; +} + +стат пусто иниц_глоб_перем(Token **остаток, Token *ток, Obj *перем) { + Инициализатор *инит = инициализатор(остаток, ток, перем->ty, &перем->ty); + Relocation голова = {}; + символ *буф = calloc(1, перем->ty->size); + сериал_глоб(&голова, инит, перем->ty, буф, 0); + перем->init_data = буф; + перем->rel = голова.next; +} + +стат бул это_имя_типа(Token *ток) { + стат ХешТаблица карта; + если (карта.вместимость == 0) { + стат символ *кл[] = { + "void", "_Bool", "char", "short", "int", "long", "struct", "union", + "пусто", "символ", "корот", "цел", "длин", "структ", "объед", + "typedef", "enum", "static", "extern", "_Alignas", "_Выравнивание", "signed", "unsigned", + "типдеф", "переч", "стат", "внеш", "знак", "беззнак", + "const", "volatile", "auto", "register", "restrict", "__restrict", + "конст", "летуч", "авто", "регистр", "огранич", + "__restrict__", "_Noreturn", "_Безвозврат", "float", "double", "typeof", "inline", + "плав", "двойн", "типоф", "инлайн", + "_Thread_local", "__thread", "_Atomic", + }; + для (цел ш = 0; ш < размер(кл) / размер(*кл); ш++) + хеш_вставить(&карта, кл[ш], (пусто *)1); + } + возврат хеш_получить2(&карта, ток->loc, ток->len) || найти_типдеф(ток); +} + +стат Node *разбор_асм(Token **остаток, Token *ток) { + Node *узел = создать_узел(ND_ASM, ток); + ток = ток->next; + пока ((equal(ток, "volatile") || equal(ток, "летуч")) || (equal(ток, "inline") || equal(ток, "инлайн"))) + ток = ток->next; + ток = skip(ток, "("); + если (ток->kind != TK_STR || ток->ty->base->kind != TY_CHAR) + error_tok(ток, "ожидался строковый литерал"); + узел->asm_str = ток->str; + *остаток = skip(ток->next, ")"); + возврат узел; +} + +стат Node *оператор(Token **остаток, Token *ток) { + если ((equal(ток, "return") || equal(ток, "возврат"))) { + Node *узел = создать_узел(ND_RETURN, ток); + если (consume(остаток, ток->next, ";")) возврат узел; + Node *выр = выражение(&ток, ток->next); + *остаток = skip(ток, ";"); + add_type(выр); + Type *тип = акт_функция->ty->return_ty; + если (тип->kind != TY_STRUCT && тип->kind != TY_UNION) + выр = new_cast(выр, акт_функция->ty->return_ty); + узел->lhs = выр; + возврат узел; + } + если ((equal(ток, "if") || equal(ток, "если"))) { + Node *узел = создать_узел(ND_IF, ток); + ток = skip(ток->next, "("); + узел->cond = выражение(&ток, ток); + ток = skip(ток, ")"); + узел->then = оператор(&ток, ток); + если ((equal(ток, "else") || equal(ток, "иначе"))) + узел->els = оператор(&ток, ток->next); + *остаток = ток; + возврат узел; + } + если ((equal(ток, "switch") || equal(ток, "выбор"))) { + Node *узел = создать_узел(ND_SWITCH, ток); + ток = skip(ток->next, "("); + узел->cond = выражение(&ток, ток); + ток = skip(ток, ")"); + Node *пред = акт_выбор; + акт_выбор = узел; + символ *пр_цель = цель_прерви; + цель_прерви = узел->brk_label = создать_уник_метку(); + узел->then = оператор(остаток, ток); + акт_выбор = пред; + цель_прерви = пр_цель; + возврат узел; + } + если ((equal(ток, "case") || equal(ток, "случай"))) { + если (!акт_выбор) error_tok(ток, "бесхозный случай (case) вне выбора"); + Node *узел = создать_узел(ND_CASE, ток); + цел начало = const_expr(&ток, ток->next); + цел конец; + если (equal(ток, "...")) { + конец = const_expr(&ток, ток->next); + если (конец < начало) error_tok(ток, "указан пустой диапазон случая"); + } иначе { конец = начало; } + ток = skip(ток, ":"); + узел->label = создать_уник_метку(); + узел->lhs = оператор(остаток, ток); + узел->begin = начало; + узел->end = конец; + узел->case_next = акт_выбор->case_next; + акт_выбор->case_next = узел; + возврат узел; + } + если ((equal(ток, "default") || equal(ток, "умолч"))) { + если (!акт_выбор) error_tok(ток, "бесхозный умолч (default) вне выбора"); + Node *узел = создать_узел(ND_CASE, ток); + ток = skip(ток->next, ":"); + узел->label = создать_уник_метку(); + узел->lhs = оператор(остаток, ток); + акт_выбор->default_case = узел; + возврат узел; + } + если ((equal(ток, "for") || equal(ток, "для"))) { + Node *узел = создать_узел(ND_FOR, ток); + ток = skip(ток->next, "("); + войти_в_обл(); + символ *пр_цель = цель_прерви; + символ *прд_цель = цель_продолжи; + цель_прерви = узел->brk_label = создать_уник_метку(); + цель_продолжи = узел->cont_label = создать_уник_метку(); + если (это_имя_типа(ток)) { + Type *баз = спецификатор_типа(&ток, ток, NULL); + узел->init = объявление(&ток, ток, баз, NULL); + } иначе { узел->init = выр_оператор(&ток, ток); } + если (!equal(ток, ";")) узел->cond = выражение(&ток, ток); + ток = skip(ток, ";"); + если (!equal(ток, ")")) узел->inc = выражение(&ток, ток); + ток = skip(ток, ")"); + узел->then = оператор(остаток, ток); + выйти_из_обл(); + цель_прерви = пр_цель; + цель_продолжи = прд_цель; + возврат узел; + } + если ((equal(ток, "while") || equal(ток, "пока"))) { + Node *узел = создать_узел(ND_FOR, ток); + ток = skip(ток->next, "("); + узел->cond = выражение(&ток, ток); + ток = skip(ток, ")"); + символ *пр_цель = цель_прерви; + символ *прд_цель = цель_продолжи; + цель_прерви = узел->brk_label = создать_уник_метку(); + цель_продолжи = узел->cont_label = создать_уник_метку(); + узел->then = оператор(остаток, ток); + цель_прерви = пр_цель; + цель_продолжи = прд_цель; + возврат узел; + } + если ((equal(ток, "do") || equal(ток, "делай"))) { + Node *узел = создать_узел(ND_DO, ток); + символ *пр_цель = цель_прерви; + символ *прд_цель = цель_продолжи; + цель_прерви = узел->brk_label = создать_уник_метку(); + цель_продолжи = узел->cont_label = создать_уник_метку(); + узел->then = оператор(&ток, ток->next); + цель_прерви = пр_цель; + цель_продолжи = прд_цель; + ток = пропустить_любой(ток, "while", "пока"); + ток = skip(ток, "("); + узел->cond = выражение(&ток, ток); + ток = skip(ток, ")"); + *остаток = skip(ток, ";"); + возврат узел; + } + если ((equal(ток, "asm") || equal(ток, "асм"))) возврат разбор_асм(остаток, ток); + если ((equal(ток, "goto") || equal(ток, "перейти"))) { + если (equal(ток->next, "*")) { + Node *узел = создать_узел(ND_GOTO_EXPR, ток); + узел->lhs = выражение(&ток, ток->next->next); + *остаток = skip(ток, ";"); + возврат узел; + } + Node *узел = создать_узел(ND_GOTO, ток); + узел->label = извлечь_ид(ток->next); + узел->goto_next = ожид_goto; + ожид_goto = узел; + *остаток = skip(ток->next->next, ";"); + возврат узел; + } + если ((equal(ток, "break") || equal(ток, "прерви"))) { + если (!цель_прерви) error_tok(ток, "бесхозный прерви (break) вне цикла или выбора"); + Node *узел = создать_узел(ND_GOTO, ток); + узел->unique_label = цель_прерви; + *остаток = skip(ток->next, ";"); + возврат узел; + } + если ((equal(ток, "continue") || equal(ток, "продолжи"))) { + если (!цель_продолжи) error_tok(ток, "бесхозный продолжи (continue) вне цикла"); + Node *узел = создать_узел(ND_GOTO, ток); + узел->unique_label = цель_продолжи; + *остаток = skip(ток->next, ";"); + возврат узел; + } + если (ток->kind == TK_IDENT && equal(ток->next, ":")) { + Node *узел = создать_узел(ND_LABEL, ток); + узел->label = strndup(ток->loc, ток->len); + узел->unique_label = создать_уник_метку(); + узел->lhs = оператор(остаток, ток->next->next); + узел->goto_next = опред_метки; + опред_метки = узел; + возврат узел; + } + если (equal(ток, "{")) возврат составной_оператор(остаток, ток->next); + возврат выр_оператор(остаток, ток); +} + +стат Node *составной_оператор(Token **остаток, Token *ток) { + Node *узел = создать_узел(ND_BLOCK, ток); + Node голова = {}; + Node *тек = &голова; + войти_в_обл(); + пока (!equal(ток, "}")) { + если (это_имя_типа(ток) && !equal(ток->next, ":")) { + АтрПерем атр = {}; + Type *баз = спецификатор_типа(&ток, ток, &атр); + если (атр.это_типдеф) { ток = разбор_типдеф(ток, баз); продолжи; } + если (это_функция(ток)) { ток = функция(ток, баз, &атр); продолжи; } + если (атр.это_внеш) { ток = глоб_перем_объяв(ток, баз, &атр); продолжи; } + тек = тек->next = объявление(&ток, ток, баз, &атр); + } иначе { тек = тек->next = оператор(&ток, ток); } + add_type(тек); + } + выйти_из_обл(); + узел->body = голова.next; + *остаток = ток->next; + возврат узел; +} + +стат Node *выр_оператор(Token **остаток, Token *ток) { + если (equal(ток, ";")) { *остаток = ток->next; возврат создать_узел(ND_BLOCK, ток); } + Node *узел = создать_узел(ND_EXPR_STMT, ток); + узел->lhs = выражение(&ток, ток); + *остаток = skip(ток, ";"); + возврат узел; +} + +стат Node *выражение(Token **остаток, Token *ток) { + Node *узел = присваивание(&ток, ток); + если (equal(ток, ",")) возврат создать_бин_узел(ND_COMMA, узел, выражение(остаток, ток->next), ток); + *остаток = ток; + возврат узел; +} + +стат int64_t вычислить(Node *узел) { возврат вычислить2(узел, NULL); } + +стат int64_t вычислить2(Node *узел, символ ***метка) { + add_type(узел); + если (is_flonum(узел->ty)) возврат выч_дробное(узел); + выбор (узел->kind) { + случай ND_ADD: возврат вычислить2(узел->lhs, метка) + вычислить(узел->rhs); + случай ND_SUB: возврат вычислить2(узел->lhs, метка) - вычислить(узел->rhs); + случай ND_MUL: возврат вычислить(узел->lhs) * вычислить(узел->rhs); + случай ND_DIV: + если (узел->ty->is_unsigned) возврат (uint64_t)вычислить(узел->lhs) / вычислить(узел->rhs); + возврат вычислить(узел->lhs) / вычислить(узел->rhs); + случай ND_NEG: возврат -вычислить(узел->lhs); + случай ND_MOD: + если (узел->ty->is_unsigned) возврат (uint64_t)вычислить(узел->lhs) % вычислить(узел->rhs); + возврат вычислить(узел->lhs) % вычислить(узел->rhs); + случай ND_BITAND: возврат вычислить(узел->lhs) & вычислить(узел->rhs); + случай ND_BITOR: возврат вычислить(узел->lhs) | вычислить(узел->rhs); + случай ND_BITXOR: возврат вычислить(узел->lhs) ^ вычислить(узел->rhs); + случай ND_SHL: возврат вычислить(узел->lhs) << вычислить(узел->rhs); + случай ND_SHR: + если (узел->ty->is_unsigned && узел->ty->size == 8) возврат (uint64_t)вычислить(узел->lhs) >> вычислить(узел->rhs); + возврат вычислить(узел->lhs) >> вычислить(узел->rhs); + случай ND_EQ: возврат вычислить(узел->lhs) == вычислить(узел->rhs); + случай ND_NE: возврат вычислить(узел->lhs) != вычислить(узел->rhs); + случай ND_LT: + если (узел->lhs->ty->is_unsigned) возврат (uint64_t)вычислить(узел->lhs) < вычислить(узел->rhs); + возврат вычислить(узел->lhs) < вычислить(узел->rhs); + случай ND_LE: + если (узел->lhs->ty->is_unsigned) возврат (uint64_t)вычислить(узел->lhs) <= вычислить(узел->rhs); + возврат вычислить(узел->lhs) <= вычислить(узел->rhs); + случай ND_COND: возврат вычислить(узел->cond) ? вычислить2(узел->then, метка) : вычислить2(узел->els, метка); + случай ND_COMMA: возврат вычислить2(узел->rhs, метка); + случай ND_NOT: возврат !вычислить(узел->lhs); + случай ND_BITNOT: возврат ~вычислить(узел->lhs); + случай ND_LOGAND: возврат вычислить(узел->lhs) && вычислить(узел->rhs); + случай ND_LOGOR: возврат вычислить(узел->lhs) || вычислить(узел->rhs); + случай ND_CAST: { + int64_t знач = вычислить2(узел->lhs, метка); + если (is_integer(узел->ty)) { + выбор (узел->ty->size) { + случай 1: возврат узел->ty->is_unsigned ? (uint8_t)знач : (int8_t)знач; + случай 2: возврат узел->ty->is_unsigned ? (uint16_t)знач : (int16_t)знач; + случай 4: возврат узел->ty->is_unsigned ? (uint32_t)знач : (int32_t)знач; + } + } + возврат знач; + } + случай ND_ADDR: возврат вычислить_rval(узел->lhs, метка); + случай ND_LABEL_VAL: *метка = &узел->unique_label; возврат 0; + случай ND_MEMBER: + если (!метка) error_tok(узел->tok, "не является константой времени компиляции"); + если (узел->ty->kind != TY_ARRAY) error_tok(узел->tok, "недопустимый инициализатор"); + возврат вычислить_rval(узел->lhs, метка) + узел->member->offset; + случай ND_VAR: + если (!метка) error_tok(узел->tok, "не является константой времени компиляции"); + если (узел->var->ty->kind != TY_ARRAY && узел->var->ty->kind != TY_FUNC) + error_tok(узел->tok, "недопустимый инициализатор"); + *метка = &узел->var->name; + возврат 0; + случай ND_NUM: возврат узел->val; + } + error_tok(узел->tok, "не является константой времени компиляции"); +} + +стат int64_t вычислить_rval(Node *узел, символ ***метка) { + выбор (узел->kind) { + случай ND_VAR: + если (узел->var->is_local) error_tok(узел->tok, "не является константой времени компиляции"); + *метка = &узел->var->name; + возврат 0; + случай ND_DEREF: возврат вычислить2(узел->lhs, метка); + случай ND_MEMBER: возврат вычислить_rval(узел->lhs, метка) + узел->member->offset; + } + error_tok(узел->tok, "недопустимый инициализатор"); +} + +стат бул это_конст_выр(Node *узел) { + add_type(узел); + выбор (узел->kind) { + случай ND_ADD: случай ND_SUB: случай ND_MUL: случай ND_DIV: + случай ND_BITAND: случай ND_BITOR: случай ND_BITXOR: + случай ND_SHL: случай ND_SHR: + случай ND_EQ: случай ND_NE: случай ND_LT: случай ND_LE: + случай ND_LOGAND: случай ND_LOGOR: + возврат это_конст_выр(узел->lhs) && это_конст_выр(узел->rhs); + случай ND_COND: + если (!это_конст_выр(узел->cond)) возврат ложь; + возврат это_конст_выр(вычислить(узел->cond) ? узел->then : узел->els); + случай ND_COMMA: возврат это_конст_выр(узел->rhs); + случай ND_NEG: случай ND_NOT: случай ND_BITNOT: случай ND_CAST: + возврат это_конст_выр(узел->lhs); + случай ND_NUM: возврат истина; + } + возврат ложь; +} + +int64_t const_expr(Token **остаток, Token *ток) { + Node *узел = условное(остаток, ток); + возврат вычислить(узел); +} + +стат двойн выч_дробное(Node *узел) { + add_type(узел); + если (is_integer(узел->ty)) { + если (узел->ty->is_unsigned) возврат (беззнак длин)вычислить(узел); + возврат вычислить(узел); + } + выбор (узел->kind) { + случай ND_ADD: возврат выч_дробное(узел->lhs) + выч_дробное(узел->rhs); + случай ND_SUB: возврат выч_дробное(узел->lhs) - выч_дробное(узел->rhs); + случай ND_MUL: возврат выч_дробное(узел->lhs) * выч_дробное(узел->rhs); + случай ND_DIV: возврат выч_дробное(узел->lhs) / выч_дробное(узел->rhs); + случай ND_NEG: возврат -выч_дробное(узел->lhs); + случай ND_COND: возврат выч_дробное(узел->cond) ? выч_дробное(узел->then) : выч_дробное(узел->els); + случай ND_COMMA: возврат выч_дробное(узел->rhs); + случай ND_CAST: + если (is_flonum(узел->lhs->ty)) возврат выч_дробное(узел->lhs); + возврат вычислить(узел->lhs); + случай ND_NUM: возврат узел->fval; + } + error_tok(узел->tok, "не является константой времени компиляции"); +} + +стат Node *преобр_в_присв(Node *бин) { + add_type(бин->lhs); + add_type(бин->rhs); + Token *ток = бин->tok; + если (бин->lhs->kind == ND_MEMBER) { + Obj *перем = создать_лок_перем("", pointer_to(бин->lhs->lhs->ty)); + Node *в1 = создать_бин_узел(ND_ASSIGN, создать_узел_перем(перем, ток), + создать_ун_узел(ND_ADDR, бин->lhs->lhs, ток), ток); + Node *в2 = создать_ун_узел(ND_MEMBER, + создать_ун_узел(ND_DEREF, создать_узел_перем(перем, ток), ток), ток); + в2->member = бин->lhs->member; + Node *в3 = создать_ун_узел(ND_MEMBER, + создать_ун_узел(ND_DEREF, создать_узел_перем(перем, ток), ток), ток); + в3->member = бин->lhs->member; + Node *в4 = создать_бин_узел(ND_ASSIGN, в2, + создать_бин_узел(бин->kind, в3, бин->rhs, ток), ток); + возврат создать_бин_узел(ND_COMMA, в1, в4, ток); + } + если (бин->lhs->ty->is_atomic) { + Node голова = {}; + Node *тек = &голова; + Obj *адр = создать_лок_перем("", pointer_to(бин->lhs->ty)); + Obj *знач = создать_лок_перем("", бин->rhs->ty); + Obj *стар = создать_лок_перем("", бин->lhs->ty); + Obj *нов = создать_лок_перем("", бин->lhs->ty); + тек = тек->next = создать_ун_узел(ND_EXPR_STMT, + создать_бин_узел(ND_ASSIGN, создать_узел_перем(адр, ток), + создать_ун_узел(ND_ADDR, бин->lhs, ток), ток), ток); + тек = тек->next = создать_ун_узел(ND_EXPR_STMT, + создать_бин_узел(ND_ASSIGN, создать_узел_перем(знач, ток), бин->rhs, ток), ток); + тек = тек->next = создать_ун_узел(ND_EXPR_STMT, + создать_бин_узел(ND_ASSIGN, создать_узел_перем(стар, ток), + создать_ун_узел(ND_DEREF, создать_узел_перем(адр, ток), ток), ток), ток); + Node *цикл = создать_узел(ND_DO, ток); + цикл->brk_label = создать_уник_метку(); + цикл->cont_label = создать_уник_метку(); + Node *тело = создать_бин_узел(ND_ASSIGN, создать_узел_перем(нов, ток), + создать_бин_узел(бин->kind, создать_узел_перем(стар, ток), создать_узел_перем(знач, ток), ток), ток); + цикл->then = создать_узел(ND_BLOCK, ток); + цикл->then->body = создать_ун_узел(ND_EXPR_STMT, тело, ток); + Node *cas = создать_узел(ND_CAS, ток); + cas->cas_addr = создать_узел_перем(адр, ток); + cas->cas_old = создать_ун_узел(ND_ADDR, создать_узел_перем(стар, ток), ток); + cas->cas_new = создать_узел_перем(нов, ток); + цикл->cond = создать_ун_узел(ND_NOT, cas, ток); + тек = тек->next = цикл; + тек = тек->next = создать_ун_узел(ND_EXPR_STMT, создать_узел_перем(нов, ток), ток); + Node *узел = создать_узел(ND_STMT_EXPR, ток); + узел->body = голова.next; + возврат узел; + } + Obj *перем = создать_лок_перем("", pointer_to(бин->lhs->ty)); + Node *в1 = создать_бин_узел(ND_ASSIGN, создать_узел_перем(перем, ток), + создать_ун_узел(ND_ADDR, бин->lhs, ток), ток); + Node *в2 = создать_бин_узел(ND_ASSIGN, + создать_ун_узел(ND_DEREF, создать_узел_перем(перем, ток), ток), + создать_бин_узел(бин->kind, + создать_ун_узел(ND_DEREF, создать_узел_перем(перем, ток), ток), + бин->rhs, ток), ток); + возврат создать_бин_узел(ND_COMMA, в1, в2, ток); +} + +стат Node *присваивание(Token **остаток, Token *ток) { + Node *узел = условное(&ток, ток); + если (equal(ток, "=")) возврат создать_бин_узел(ND_ASSIGN, узел, присваивание(остаток, ток->next), ток); + если (equal(ток, "+=")) возврат преобр_в_присв(нов_слож(узел, присваивание(остаток, ток->next), ток)); + если (equal(ток, "-=")) возврат преобр_в_присв(нов_выч(узел, присваивание(остаток, ток->next), ток)); + если (equal(ток, "*=")) возврат преобр_в_присв(создать_бин_узел(ND_MUL, узел, присваивание(остаток, ток->next), ток)); + если (equal(ток, "/=")) возврат преобр_в_присв(создать_бин_узел(ND_DIV, узел, присваивание(остаток, ток->next), ток)); + если (equal(ток, "%=")) возврат преобр_в_присв(создать_бин_узел(ND_MOD, узел, присваивание(остаток, ток->next), ток)); + если (equal(ток, "&=")) возврат преобр_в_присв(создать_бин_узел(ND_BITAND, узел, присваивание(остаток, ток->next), ток)); + если (equal(ток, "|=")) возврат преобр_в_присв(создать_бин_узел(ND_BITOR, узел, присваивание(остаток, ток->next), ток)); + если (equal(ток, "^=")) возврат преобр_в_присв(создать_бин_узел(ND_BITXOR, узел, присваивание(остаток, ток->next), ток)); + если (equal(ток, "<<=")) возврат преобр_в_присв(создать_бин_узел(ND_SHL, узел, присваивание(остаток, ток->next), ток)); + если (equal(ток, ">>=")) возврат преобр_в_присв(создать_бин_узел(ND_SHR, узел, присваивание(остаток, ток->next), ток)); + *остаток = ток; + возврат узел; +} + +стат Node *условное(Token **остаток, Token *ток) { + Node *усл = лог_или(&ток, ток); + если (!equal(ток, "?")) { *остаток = ток; возврат усл; } + если (equal(ток->next, ":")) { + add_type(усл); + Obj *перем = создать_лок_перем("", усл->ty); + Node *лев = создать_бин_узел(ND_ASSIGN, создать_узел_перем(перем, ток), усл, ток); + Node *прав = создать_узел(ND_COND, ток); + прав->cond = создать_узел_перем(перем, ток); + прав->then = создать_узел_перем(перем, ток); + прав->els = условное(остаток, ток->next->next); + возврат создать_бин_узел(ND_COMMA, лев, прав, ток); + } + Node *узел = создать_узел(ND_COND, ток); + узел->cond = усл; + узел->then = выражение(&ток, ток->next); + ток = skip(ток, ":"); + узел->els = условное(остаток, ток); + возврат узел; +} + +стат Node *лог_или(Token **остаток, Token *ток) { + Node *узел = лог_и(&ток, ток); + пока (equal(ток, "||")) { Token *старт = ток; узел = создать_бин_узел(ND_LOGOR, узел, лог_и(&ток, ток->next), старт); } + *остаток = ток; + возврат узел; +} + +стат Node *лог_и(Token **остаток, Token *ток) { + Node *узел = побит_или(&ток, ток); + пока (equal(ток, "&&")) { Token *старт = ток; узел = создать_бин_узел(ND_LOGAND, узел, побит_или(&ток, ток->next), старт); } + *остаток = ток; + возврат узел; +} + +стат Node *побит_или(Token **остаток, Token *ток) { + Node *узел = побит_искл_или(&ток, ток); + пока (equal(ток, "|")) { Token *старт = ток; узел = создать_бин_узел(ND_BITOR, узел, побит_искл_или(&ток, ток->next), старт); } + *остаток = ток; + возврат узел; +} + +стат Node *побит_искл_или(Token **остаток, Token *ток) { + Node *узел = побит_и(&ток, ток); + пока (equal(ток, "^")) { Token *старт = ток; узел = создать_бин_узел(ND_BITXOR, узел, побит_и(&ток, ток->next), старт); } + *остаток = ток; + возврат узел; +} + +стат Node *побит_и(Token **остаток, Token *ток) { + Node *узел = равенство(&ток, ток); + пока (equal(ток, "&")) { Token *старт = ток; узел = создать_бин_узел(ND_BITAND, узел, равенство(&ток, ток->next), старт); } + *остаток = ток; + возврат узел; +} + +стат Node *равенство(Token **остаток, Token *ток) { + Node *узел = сравнение(&ток, ток); + для (;;) { + Token *старт = ток; + если (equal(ток, "==")) { узел = создать_бин_узел(ND_EQ, узел, сравнение(&ток, ток->next), старт); продолжи; } + если (equal(ток, "!=")) { узел = создать_бин_узел(ND_NE, узел, сравнение(&ток, ток->next), старт); продолжи; } + *остаток = ток; + возврат узел; + } +} + +стат Node *сравнение(Token **остаток, Token *ток) { + Node *узел = сдвиг(&ток, ток); + для (;;) { + Token *старт = ток; + если (equal(ток, "<")) { узел = создать_бин_узел(ND_LT, узел, сдвиг(&ток, ток->next), старт); продолжи; } + если (equal(ток, "<=")) { узел = создать_бин_узел(ND_LE, узел, сдвиг(&ток, ток->next), старт); продолжи; } + если (equal(ток, ">")) { узел = создать_бин_узел(ND_LT, сдвиг(&ток, ток->next), узел, старт); продолжи; } + если (equal(ток, ">=")) { узел = создать_бин_узел(ND_LE, сдвиг(&ток, ток->next), узел, старт); продолжи; } + *остаток = ток; + возврат узел; + } +} + +стат Node *сдвиг(Token **остаток, Token *ток) { + Node *узел = сложение(&ток, ток); + для (;;) { + Token *старт = ток; + если (equal(ток, "<<")) { узел = создать_бин_узел(ND_SHL, узел, сложение(&ток, ток->next), старт); продолжи; } + если (equal(ток, ">>")) { узел = создать_бин_узел(ND_SHR, узел, сложение(&ток, ток->next), старт); продолжи; } + *остаток = ток; + возврат узел; + } +} + +стат Node *нов_слож(Node *лев, Node *прав, Token *ток) { + add_type(лев); + add_type(прав); + если (is_numeric(лев->ty) && is_numeric(прав->ty)) возврат создать_бин_узел(ND_ADD, лев, прав, ток); + если (лев->ty->base && прав->ty->base) error_tok(ток, "недопустимые операнды"); + если (!лев->ty->base && прав->ty->base) { Node *вр = лев; лев = прав; прав = вр; } + если (лев->ty->base->kind == TY_VLA) { + прав = создать_бин_узел(ND_MUL, прав, создать_узел_перем(лев->ty->base->vla_size, ток), ток); + возврат создать_бин_узел(ND_ADD, лев, прав, ток); + } + прав = создать_бин_узел(ND_MUL, прав, создать_длин_число(лев->ty->base->size, ток), ток); + возврат создать_бин_узел(ND_ADD, лев, прав, ток); +} + +стат Node *нов_выч(Node *лев, Node *прав, Token *ток) { + add_type(лев); + add_type(прав); + если (is_numeric(лев->ty) && is_numeric(прав->ty)) возврат создать_бин_узел(ND_SUB, лев, прав, ток); + если (лев->ty->base->kind == TY_VLA) { + прав = создать_бин_узел(ND_MUL, прав, создать_узел_перем(лев->ty->base->vla_size, ток), ток); + add_type(прав); + Node *узел = создать_бин_узел(ND_SUB, лев, прав, ток); + узел->ty = лев->ty; + возврат узел; + } + если (лев->ty->base && is_integer(прав->ty)) { + прав = создать_бин_узел(ND_MUL, прав, создать_длин_число(лев->ty->base->size, ток), ток); + add_type(прав); + Node *узел = создать_бин_узел(ND_SUB, лев, прав, ток); + узел->ty = лев->ty; + возврат узел; + } + если (лев->ty->base && прав->ty->base) { + Node *узел = создать_бин_узел(ND_SUB, лев, прав, ток); + узел->ty = ty_long; + возврат создать_бин_узел(ND_DIV, узел, создать_число(лев->ty->base->size, ток), ток); + } + error_tok(ток, "недопустимые операнды"); +} + +стат Node *сложение(Token **остаток, Token *ток) { + Node *узел = умножение(&ток, ток); + для (;;) { + Token *старт = ток; + если (equal(ток, "+")) { узел = нов_слож(узел, умножение(&ток, ток->next), старт); продолжи; } + если (equal(ток, "-")) { узел = нов_выч(узел, умножение(&ток, ток->next), старт); продолжи; } + *остаток = ток; + возврат узел; + } +} + +стат Node *умножение(Token **остаток, Token *ток) { + Node *узел = приведение(&ток, ток); + для (;;) { + Token *старт = ток; + если (equal(ток, "*")) { узел = создать_бин_узел(ND_MUL, узел, приведение(&ток, ток->next), старт); продолжи; } + если (equal(ток, "/")) { узел = создать_бин_узел(ND_DIV, узел, приведение(&ток, ток->next), старт); продолжи; } + если (equal(ток, "%")) { узел = создать_бин_узел(ND_MOD, узел, приведение(&ток, ток->next), старт); продолжи; } + *остаток = ток; + возврат узел; + } +} + +стат Node *приведение(Token **остаток, Token *ток) { + если (equal(ток, "(") && это_имя_типа(ток->next)) { + Token *старт = ток; + Type *тип = имя_типа(&ток, ток->next); + ток = skip(ток, ")"); + если (equal(ток, "{")) возврат унарный(остаток, старт); + Node *узел = new_cast(приведение(остаток, ток), тип); + узел->tok = старт; + возврат узел; + } + возврат унарный(остаток, ток); +} + +стат Node *унарный(Token **остаток, Token *ток) { + если (equal(ток, "+")) возврат приведение(остаток, ток->next); + если (equal(ток, "-")) возврат создать_ун_узел(ND_NEG, приведение(остаток, ток->next), ток); + если (equal(ток, "&")) { + Node *лев = приведение(остаток, ток->next); + add_type(лев); + если (лев->kind == ND_MEMBER && лев->member->is_bitfield) + error_tok(ток, "невозможно взять адрес битового поля"); + возврат создать_ун_узел(ND_ADDR, лев, ток); + } + если (equal(ток, "*")) { + Node *узел = приведение(остаток, ток->next); + add_type(узел); + если (узел->ty->kind == TY_FUNC) возврат узел; + возврат создать_ун_узел(ND_DEREF, узел, ток); + } + если (equal(ток, "!")) возврат создать_ун_узел(ND_NOT, приведение(остаток, ток->next), ток); + если (equal(ток, "~")) возврат создать_ун_узел(ND_BITNOT, приведение(остаток, ток->next), ток); + если (equal(ток, "++")) возврат преобр_в_присв(нов_слож(унарный(остаток, ток->next), создать_число(1, ток), ток)); + если (equal(ток, "--")) возврат преобр_в_присв(нов_выч(унарный(остаток, ток->next), создать_число(1, ток), ток)); + если (equal(ток, "&&")) { + Node *узел = создать_узел(ND_LABEL_VAL, ток); + узел->label = извлечь_ид(ток->next); + узел->goto_next = ожид_goto; + ожид_goto = узел; + *остаток = ток->next->next; + возврат узел; + } + возврат постфикс(остаток, ток); +} + +стат пусто разбор_членов_стр(Token **остаток, Token *ток, Type *тип) { + Member голова = {}; + Member *тек = &голова; + цел инд = 0; + пока (!equal(ток, "}")) { + АтрПерем атр = {}; + Type *баз = спецификатор_типа(&ток, ток, &атр); + бул первый = истина; + если ((баз->kind == TY_STRUCT || баз->kind == TY_UNION) && consume(&ток, ток, ";")) { + Member *член = calloc(1, размер(Member)); + член->ty = баз; + член->idx = инд++; + член->align = атр.выравн ? атр.выравн : член->ty->align; + тек = тек->next = член; + продолжи; + } + пока (!consume(&ток, ток, ";")) { + если (!первый) ток = skip(ток, ","); + первый = ложь; + Member *член = calloc(1, размер(Member)); + член->ty = декларатор(&ток, ток, баз); + член->name = член->ty->name; + член->idx = инд++; + член->align = атр.выравн ? атр.выравн : член->ty->align; + если (consume(&ток, ток, ":")) { член->is_bitfield = истина; член->bit_width = const_expr(&ток, ток); } + тек = тек->next = член; + } + } + если (тек != &голова && тек->ty->kind == TY_ARRAY && тек->ty->array_len < 0) { + тек->ty = array_of(тек->ty->base, 0); + тип->is_flexible = истина; + } + *остаток = ток->next; + тип->members = голова.next; +} + +стат Token *разбор_атриб(Token *ток, Type *тип) { + пока (consume(&ток, ток, "__attribute__")) { + ток = skip(ток, "("); + ток = skip(ток, "("); + бул первый = истина; + пока (!consume(&ток, ток, ")")) { + если (!первый) ток = skip(ток, ","); + первый = ложь; + если (consume(&ток, ток, "packed")) { тип->is_packed = истина; продолжи; } + если (consume(&ток, ток, "aligned")) { ток = skip(ток, "("); тип->align = const_expr(&ток, ток); ток = skip(ток, ")"); продолжи; } + error_tok(ток, "неизвестный атрибут"); + } + ток = skip(ток, ")"); + } + возврат ток; +} + +стат Type *разбор_стр_объед(Token **остаток, Token *ток) { + Type *тип = struct_type(); + ток = разбор_атриб(ток, тип); + Token *тег = NULL; + если (ток->kind == TK_IDENT) { тег = ток; ток = ток->next; } + если (тег && !equal(ток, "{")) { + *остаток = ток; + Type *т2 = найти_тег(тег); + если (т2) возврат т2; + тип->size = -1; + зарег_тег(тег, тип); + возврат тип; + } + ток = skip(ток, "{"); + разбор_членов_стр(&ток, ток, тип); + *остаток = разбор_атриб(ток, тип); + если (тег) { + Type *т2 = хеш_получить2(&тек_обл->теги, тег->loc, тег->len); + если (т2) { *т2 = *тип; возврат т2; } + зарег_тег(тег, тип); + } + возврат тип; +} + +стат Type *объяв_структ(Token **остаток, Token *ток) { + Type *тип = разбор_стр_объед(остаток, ток); + тип->kind = TY_STRUCT; + если (тип->size < 0) возврат тип; + цел биты = 0; + для (Member *член = тип->members; член; член = член->next) { + если (член->is_bitfield && член->bit_width == 0) { + биты = align_to(биты, член->ty->size * 8); + } иначе если (член->is_bitfield) { + цел разм = член->ty->size; + если (биты / (разм * 8) != (биты + член->bit_width - 1) / (разм * 8)) + биты = align_to(биты, разм * 8); + член->offset = округлить_вниз(биты / 8, разм); + член->bit_offset = биты % (разм * 8); + биты += член->bit_width; + } иначе { + если (!тип->is_packed) биты = align_to(биты, член->align * 8); + член->offset = биты / 8; + биты += член->ty->size * 8; + } + если (!тип->is_packed && тип->align < член->align) тип->align = член->align; + } + тип->size = align_to(биты, тип->align * 8) / 8; + возврат тип; +} + +стат Type *объяв_объед(Token **остаток, Token *ток) { + Type *тип = разбор_стр_объед(остаток, ток); + тип->kind = TY_UNION; + если (тип->size < 0) возврат тип; + для (Member *член = тип->members; член; член = член->next) { + если (тип->align < член->align) тип->align = член->align; + если (тип->size < член->ty->size) тип->size = член->ty->size; + } + тип->size = align_to(тип->size, тип->align); + возврат тип; +} + +стат Member *найти_член(Type *тип, Token *ток) { + для (Member *член = тип->members; член; член = член->next) { + если ((член->ty->kind == TY_STRUCT || член->ty->kind == TY_UNION) && !член->name) { + если (найти_член(член->ty, ток)) возврат член; + продолжи; + } + если (член->name->len == ток->len && !strncmp(член->name->loc, ток->loc, ток->len)) + возврат член; + } + возврат NULL; +} + +стат Member *найти_член_стр(Type *тип, Token *ток) { возврат найти_член(тип, ток); } + +стат Node *создать_доступ_к_члену(Node *узел, Token *ток) { + add_type(узел); + если (узел->ty->kind != TY_STRUCT && узел->ty->kind != TY_UNION) + error_tok(узел->tok, "не является структурой или объединением"); + Type *тип = узел->ty; + для (;;) { + Member *член = найти_член(тип, ток); + если (!член) error_tok(ток, "нет такого члена"); + узел = создать_ун_узел(ND_MEMBER, узел, ток); + узел->member = член; + если (член->name) прерви; + тип = член->ty; + } + возврат узел; +} + +стат Node *создать_инкрем_декрем(Node *узел, Token *ток, цел доб) { + add_type(узел); + возврат new_cast(нов_слож(преобр_в_присв(нов_слож(узел, создать_число(доб, ток), ток)), + создать_число(-доб, ток), ток), узел->ty); +} + +стат Node *постфикс(Token **остаток, Token *ток) { + если (equal(ток, "(") && это_имя_типа(ток->next)) { + Token *старт = ток; + Type *тип = имя_типа(&ток, ток->next); + ток = skip(ток, ")"); + если (тек_обл->след == NULL) { + Obj *перем = создать_аноним_глоб(тип); + иниц_глоб_перем(остаток, ток, перем); + возврат создать_узел_перем(перем, старт); + } + Obj *перем = создать_лок_перем("", тип); + Node *лев = иниц_лок_перем(остаток, ток, перем); + Node *прав = создать_узел_перем(перем, ток); + возврат создать_бин_узел(ND_COMMA, лев, прав, старт); + } + Node *узел = первичный(&ток, ток); + для (;;) { + если (equal(ток, "(")) { узел = вызов_фн(&ток, ток->next, узел); продолжи; } + если (equal(ток, "[")) { + Token *старт = ток; + Node *инд = выражение(&ток, ток->next); + ток = skip(ток, "]"); + узел = создать_ун_узел(ND_DEREF, нов_слож(узел, инд, старт), старт); + продолжи; + } + если (equal(ток, ".")) { узел = создать_доступ_к_члену(узел, ток->next); ток = ток->next->next; продолжи; } + если (equal(ток, "->")) { узел = создать_ун_узел(ND_DEREF, узел, ток); узел = создать_доступ_к_члену(узел, ток->next); ток = ток->next->next; продолжи; } + если (equal(ток, "++")) { узел = создать_инкрем_декрем(узел, ток, 1); ток = ток->next; продолжи; } + если (equal(ток, "--")) { узел = создать_инкрем_декрем(узел, ток, -1); ток = ток->next; продолжи; } + *остаток = ток; + возврат узел; + } +} + +стат Node *вызов_фн(Token **остаток, Token *ток, Node *фн) { + add_type(фн); + если (фн->ty->kind != TY_FUNC && (фн->ty->kind != TY_PTR || фн->ty->base->kind != TY_FUNC)) + error_tok(фн->tok, "не является функцией"); + Type *тип = (фн->ty->kind == TY_FUNC) ? фн->ty : фн->ty->base; + Type *тип_парам = тип->params; + Node голова = {}; + Node *тек = &голова; + пока (!equal(ток, ")")) { + если (тек != &голова) ток = skip(ток, ","); + Node *арг = присваивание(&ток, ток); + add_type(арг); + если (!тип_парам && !тип->is_variadic) error_tok(ток, "слишком много аргументов"); + если (тип_парам) { + если (тип_парам->kind != TY_STRUCT && тип_парам->kind != TY_UNION) + арг = new_cast(арг, тип_парам); + тип_парам = тип_парам->next; + } иначе если (арг->ty->kind == TY_FLOAT) { арг = new_cast(арг, ty_double); } + тек = тек->next = арг; + } + если (тип_парам) error_tok(ток, "слишком мало аргументов"); + *остаток = skip(ток, ")"); + Node *узел = создать_ун_узел(ND_FUNCALL, фн, ток); + узел->func_ty = тип; + узел->ty = тип->return_ty; + узел->args = голова.next; + если (узел->ty->kind == TY_STRUCT || узел->ty->kind == TY_UNION) + узел->ret_buffer = создать_лок_перем("", узел->ty); + возврат узел; +} + +стат Node *разбор_обобщ(Token **остаток, Token *ток) { + Token *старт = ток; + ток = skip(ток, "("); + Node *упр = присваивание(&ток, ток); + add_type(упр); + Type *т1 = упр->ty; + если (т1->kind == TY_FUNC) т1 = pointer_to(т1); + иначе если (т1->kind == TY_ARRAY) т1 = pointer_to(т1->base); + Node *рез = NULL; + пока (!consume(остаток, ток, ")")) { + ток = skip(ток, ","); + если ((equal(ток, "default") || equal(ток, "умолч"))) { + ток = skip(ток->next, ":"); + Node *узел = присваивание(&ток, ток); + если (!рез) рез = узел; + продолжи; + } + Type *т2 = имя_типа(&ток, ток); + ток = skip(ток, ":"); + Node *узел = присваивание(&ток, ток); + если (is_compatible(т1, т2)) рез = узел; + } + если (!рез) error_tok(старт, "тип управляющего выражения не совместим ни с одним типом обобщенной ассоциации"); + возврат рез; +} + +стат Node *первичный(Token **остаток, Token *ток) { + Token *старт = ток; + если (equal(ток, "(") && equal(ток->next, "{")) { + Node *узел = создать_узел(ND_STMT_EXPR, ток); + узел->body = составной_оператор(&ток, ток->next->next)->body; + *остаток = skip(ток, ")"); + возврат узел; + } + если (equal(ток, "(")) { Node *узел = выражение(&ток, ток->next); *остаток = skip(ток, ")"); возврат узел; } + если ((equal(ток, "sizeof") || equal(ток, "размер")) && equal(ток->next, "(") && это_имя_типа(ток->next->next)) { + Type *тип = имя_типа(&ток, ток->next->next); + *остаток = skip(ток, ")"); + если (тип->kind == TY_VLA) { + если (тип->vla_size) возврат создать_узел_перем(тип->vla_size, ток); + Node *лев = создать_выч_vla(тип, ток); + Node *прав = создать_узел_перем(тип->vla_size, ток); + возврат создать_бин_узел(ND_COMMA, лев, прав, ток); + } + возврат создать_бузн_длин_число(тип->size, старт); + } + если ((equal(ток, "sizeof") || equal(ток, "размер"))) { + Node *узел = унарный(остаток, ток->next); + add_type(узел); + если (узел->ty->kind == TY_VLA) возврат создать_узел_перем(узел->ty->vla_size, ток); + возврат создать_бузн_длин_число(узел->ty->size, ток); + } + если ((equal(ток, "_Alignof") || equal(ток, "_Выравнивание_типа")) && equal(ток->next, "(") && это_имя_типа(ток->next->next)) { + Type *тип = имя_типа(&ток, ток->next->next); + *остаток = skip(ток, ")"); + возврат создать_бузн_длин_число(тип->align, ток); + } + если (equal(ток, "_Alignof") || equal(ток, "_Выравнивание_типа")) { + Node *узел = унарный(остаток, ток->next); + add_type(узел); + возврат создать_бузн_длин_число(узел->ty->align, ток); + } + если (equal(ток, "_Generic") || equal(ток, "_Обобщ")) возврат разбор_обобщ(остаток, ток->next); + если (equal(ток, "__builtin_types_compatible_p")) { + ток = skip(ток->next, "("); + Type *т1 = имя_типа(&ток, ток); + ток = skip(ток, ","); + Type *т2 = имя_типа(&ток, ток); + *остаток = skip(ток, ")"); + возврат создать_число(is_compatible(т1, т2), старт); + } + если (equal(ток, "__builtin_reg_class")) { + ток = skip(ток->next, "("); + Type *тип = имя_типа(&ток, ток); + *остаток = skip(ток, ")"); + если (is_integer(тип) || тип->kind == TY_PTR) возврат создать_число(0, старт); + если (is_flonum(тип)) возврат создать_число(1, старт); + возврат создать_число(2, старт); + } + если (equal(ток, "__builtin_compare_and_swap")) { + Node *узел = создать_узел(ND_CAS, ток); + ток = skip(ток->next, "("); + узел->cas_addr = присваивание(&ток, ток); + ток = skip(ток, ","); + узел->cas_old = присваивание(&ток, ток); + ток = skip(ток, ","); + узел->cas_new = присваивание(&ток, ток); + *остаток = skip(ток, ")"); + возврат узел; + } + если (equal(ток, "__builtin_atomic_exchange")) { + Node *узел = создать_узел(ND_EXCH, ток); + ток = skip(ток->next, "("); + узел->lhs = присваивание(&ток, ток); + ток = skip(ток, ","); + узел->rhs = присваивание(&ток, ток); + *остаток = skip(ток, ")"); + возврат узел; + } + если (ток->kind == TK_IDENT) { + ОблПерем *обл = найти_перем(ток); + *остаток = ток->next; + если (обл && обл->перем && обл->перем->is_function) { + если (акт_функция) массив_строк_добавить(&акт_функция->refs, обл->перем->name); + иначе обл->перем->is_root = истина; + } + если (обл) { + если (обл->перем) возврат создать_узел_перем(обл->перем, ток); + если (обл->тип_переч) возврат создать_число(обл->знач_переч, ток); + } + если (equal(ток->next, "(")) error_tok(ток, "неявное объявление функции"); + error_tok(ток, "неопределенная переменная"); + } + если (ток->kind == TK_STR) { + Obj *перем = создать_строк_литерал(ток->str, ток->ty); + *остаток = ток->next; + возврат создать_узел_перем(перем, ток); + } + если (ток->kind == TK_NUM) { + Node *узел; + если (is_flonum(ток->ty)) { узел = создать_узел(ND_NUM, ток); узел->fval = ток->fval; } + иначе { узел = создать_число(ток->val, ток); } + узел->ty = ток->ty; + *остаток = ток->next; + возврат узел; + } + error_tok(ток, "ожидалось выражение"); +} + +стат Token *разбор_типдеф(Token *ток, Type *баз_тип) { + бул первый = истина; + пока (!consume(&ток, ток, ";")) { + если (!первый) ток = skip(ток, ","); + первый = ложь; + Type *тип = декларатор(&ток, ток, баз_тип); + если (!тип->name) error_tok(тип->name_pos, "имя типдеф опущено"); + зарег_перем(извлечь_ид(тип->name))->определение_типа = тип; + } + возврат ток; +} + +стат пусто зарег_парам(Type *парам) { + если (парам) { + зарег_парам(парам->next); + если (!парам->name) error_tok(парам->name_pos, "имя параметра опущено"); + создать_лок_перем(извлечь_ид(парам->name), парам); + } +} + +стат пусто связать_метки(пусто) { + для (Node *x = ожид_goto; x; x = x->goto_next) { + для (Node *y = опред_метки; y; y = y->goto_next) { + если (!strcmp(x->label, y->label)) { x->unique_label = y->unique_label; прерви; } + } + если (x->unique_label == NULL) error_tok(x->tok->next, "использование необъявленной метки"); + } + ожид_goto = опред_метки = NULL; +} + +стат Obj *найти_функцию(символ *имя) { + Область *обл = тек_обл; + пока (обл->след) обл = обл->след; + ОблПерем *обл2 = хеш_получить(&обл->перем, имя); + если (обл2 && обл2->перем && обл2->перем->is_function) возврат обл2->перем; + возврат NULL; +} + +стат пусто пометить_живой(Obj *перем) { + если (!перем->is_function || перем->is_live) возврат; + перем->is_live = истина; + для (цел ш = 0; ш < перем->refs.len; ш++) { + Obj *фн = найти_функцию(перем->refs.data[ш]); + если (фн) пометить_живой(фн); + } +} + +стат Token *функция(Token *ток, Type *баз_тип, АтрПерем *атр) { + Type *тип = декларатор(&ток, ток, баз_тип); + если (!тип->name) error_tok(тип->name_pos, "имя функции опущено"); + символ *имя_стр = извлечь_ид(тип->name); + Obj *фн = найти_функцию(имя_стр); + если (фн) { + если (!фн->is_function) error_tok(ток, "переобъявлено как символ другого вида"); + если (фн->is_definition && equal(ток, "{")) error_tok(ток, "переопределение %s", имя_стр); + если (!фн->is_static && атр->это_статик) error_tok(ток, "статическое объявление следует за нестатическим"); + фн->is_definition = фн->is_definition || equal(ток, "{"); + } иначе { + фн = создать_глоб_перем(имя_стр, тип); + фн->is_function = истина; + фн->is_definition = equal(ток, "{"); + фн->is_static = атр->это_статик || (атр->это_инлайн && !атр->это_внеш); + фн->is_inline = атр->это_инлайн; + } + фн->is_root = !(фн->is_static && фн->is_inline); + если (consume(&ток, ток, ";")) возврат ток; + акт_функция = фн; + лок_перем = NULL; + войти_в_обл(); + зарег_парам(тип->params); + Type *rty = тип->return_ty; + если ((rty->kind == TY_STRUCT || rty->kind == TY_UNION) && rty->size > 16) + создать_лок_перем("", pointer_to(rty)); + фн->params = лок_перем; + если (тип->is_variadic) + фн->va_area = создать_лок_перем("__va_area__", array_of(ty_char, 136)); + фн->alloca_bottom = создать_лок_перем("__alloca_size__", pointer_to(ty_char)); + ток = skip(ток, "{"); + зарег_перем("__func__")->перем = создать_строк_литерал(фн->name, array_of(ty_char, strlen(фн->name) + 1)); + зарег_перем("__FUNCTION__")->перем = создать_строк_литерал(фн->name, array_of(ty_char, strlen(фн->name) + 1)); + зарег_перем("__функ__")->перем = создать_строк_литерал(фн->name, array_of(ty_char, strlen(фн->name) + 1)); + зарег_перем("__ФУНКЦИЯ__")->перем = создать_строк_литерал(фн->name, array_of(ty_char, strlen(фн->name) + 1)); + фн->body = составной_оператор(&ток, ток); + фн->locals = лок_перем; + выйти_из_обл(); + связать_метки(); + возврат ток; +} + +стат Token *глоб_перем_объяв(Token *ток, Type *баз_тип, АтрПерем *атр) { + бул первый = истина; + пока (!consume(&ток, ток, ";")) { + если (!первый) ток = skip(ток, ","); + первый = ложь; + Type *тип = декларатор(&ток, ток, баз_тип); + если (!тип->name) error_tok(тип->name_pos, "имя переменной опущено"); + Obj *перем = создать_глоб_перем(извлечь_ид(тип->name), тип); + перем->is_definition = !атр->это_внеш; + перем->is_static = атр->это_статик; + перем->is_tls = атр->это_tls; + если (атр->выравн) перем->align = атр->выравн; + если (equal(ток, "=")) иниц_глоб_перем(&ток, ток->next, перем); + иначе если (!атр->это_внеш && !атр->это_tls) перем->is_tentative = истина; + } + возврат ток; +} + +стат бул это_функция(Token *ток) { + если (equal(ток, ";")) возврат ложь; + Type загл = {}; + Type *тип = декларатор(&ток, ток, &загл); + возврат тип->kind == TY_FUNC; +} + +стат пусто убрать_лишние_тендат(пусто) { + Obj голова; + Obj *тек = &голова; + для (Obj *перем = глоб_перем; перем; перем = перем->next) { + если (!перем->is_tentative) { тек = тек->next = перем; продолжи; } + Obj *п2 = глоб_перем; + для (; п2; п2 = п2->next) + если (перем != п2 && п2->is_definition && !strcmp(перем->name, п2->name)) прерви; + если (!п2) тек = тек->next = перем; + } + тек->next = NULL; + глоб_перем = голова.next; +} + +стат пусто зарег_встр_фн(пусто) { + Type *тип = func_type(pointer_to(ty_void)); + тип->params = copy_type(ty_int); + встр_alloca = создать_глоб_перем("alloca", тип); + встр_alloca->is_definition = ложь; +} + +Obj *parse(Token *ток) { + зарег_встр_фн(); + глоб_перем = NULL; + пока (ток->kind != TK_EOF) { + АтрПерем атр = {}; + Type *баз = спецификатор_типа(&ток, ток, &атр); + если (атр.это_типдеф) { ток = разбор_типдеф(ток, баз); продолжи; } + если (это_функция(ток)) { ток = функция(ток, баз, &атр); продолжи; } + ток = глоб_перем_объяв(ток, баз, &атр); + } + для (Obj *перем = глоб_перем; перем; перем = перем->next) + если (перем->is_root) пометить_живой(перем); + убрать_лишние_тендат(); + возврат глоб_перем; +} \ No newline at end of file diff --git a/preprocess.c b/preprocess.c new file mode 100644 index 0000000..d5d4714 --- /dev/null +++ b/preprocess.c @@ -0,0 +1,1037 @@ +// Этот файл реализует препроцессор языка C. +#включить "руси.з" + +// Параметр макроса +типдеф структ MacroParam MacroParam; +структ MacroParam { + MacroParam *next; + символ *name; +}; + +// Аргумент макроса +типдеф структ MacroArg MacroArg; +структ MacroArg { + MacroArg *next; + символ *name; + бул is_va_args; + Token *tok; +}; + +// Тип функции-обработчика встроенного макроса +типдеф Token *macro_handler_fn(Token *); + +// Структура макроса +типдеф структ Macro Macro; +структ Macro { + символ *name; + бул is_objlike; + MacroParam *params; + символ *va_args_name; + Token *body; + macro_handler_fn *handler; +}; + +// Директива #если может быть вложенной +типдеф структ CondIncl CondIncl; +структ CondIncl { + CondIncl *next; + переч { IN_THEN, IN_ELIF, IN_ELSE } ctx; + Token *tok; + бул included; +}; + +// Набор скрытия +типдеф структ Hideset Hideset; +структ Hideset { + Hideset *next; + символ *name; +}; + +// Глобальные переменные препроцессора +стат ХешТаблица macro_registry; +стат CondIncl *conditional_stack; +стат ХешТаблица pragma_once_registry; +стат цел include_search_position; + +// Вынесенные хеш-таблицы для возможности их очистки +стат ХешТаблица include_paths_cache; +стат ХешТаблица include_guards; + +// ===================================================================== +// Механизм отслеживания выделенной памяти в препроцессоре +// ===================================================================== +стат пусто **pp_allocs = NULL; +стат цел pp_alloc_count = 0; +стат цел pp_alloc_capacity = 0; + +стат пусто *track_pp(пусто *ptr) { + если (!ptr) возврат NULL; + если (pp_alloc_count == pp_alloc_capacity) { + pp_alloc_capacity = pp_alloc_capacity ? pp_alloc_capacity * 2 : 1024; + pp_allocs = realloc(pp_allocs, pp_alloc_capacity * размер(пусто *)); + } + pp_allocs[pp_alloc_count++] = ptr; + возврат ptr; +} + +// Функция для полного освобождения памяти, занятой препроцессором. +// Вызывайте её в конце каждой итерации теста. +пусто free_all_preprocess(пусто) { + для (цел i = 0; i < pp_alloc_count; i++) { + free(pp_allocs[i]); + } + free(pp_allocs); + pp_allocs = NULL; + pp_alloc_count = 0; + pp_alloc_capacity = 0; + + // Сброс глобального состояния + conditional_stack = NULL; + include_search_position = 0; + + // Очистка хеш-таблиц (обнуляем структуры, чтобы избежать dangling pointers) + macro_registry = (ХешТаблица){0}; + pragma_once_registry = (ХешТаблица){0}; + include_paths_cache = (ХешТаблица){0}; + include_guards = (ХешТаблица){0}; +} +// ===================================================================== + +// Прямые объявления функций +стат Token *preprocess_internal(Token *tok); +стат Macro *lookup_macro(Token *tok); + +стат бул is_directive_start(Token *tok) { + возврат tok->at_bol && equal(tok, "#"); +} + +стат Token *skip_to_end_of_line(Token *tok) { + если (tok->at_bol) + возврат tok; + warn_tok(tok, "лишний токен"); + пока (tok->at_bol) + tok = tok->next; + возврат tok; +} + +стат Token *duplicate_token(Token *tok) { + Token *t = track_pp(calloc(1, размер(Token))); + *t = *tok; + t->next = NULL; + возврат t; +} + +стат Token *create_eof_token(Token *tok) { + Token *t = duplicate_token(tok); + t->kind = TK_EOF; + t->len = 0; + возврат t; +} + +стат Hideset *create_hideset(символ *name) { + Hideset *hs = track_pp(calloc(1, размер(Hideset))); + hs->name = name; + возврат hs; +} + +стат Hideset *merge_hidesets(Hideset *hs1, Hideset *hs2) { + Hideset head = {}; + Hideset *cur = &head; + для (; hs1; hs1 = hs1->next) + cur = cur->next = create_hideset(hs1->name); + cur->next = hs2; + возврат head.next; +} + +стат бул hideset_has_element(Hideset *hs, символ *s, цел len) { + для (; hs; hs = hs->next) + если (strlen(hs->name) == len && !strncmp(hs->name, s, len)) + возврат истина; + возврат ложь; +} + +стат Hideset *intersect_hidesets(Hideset *hs1, Hideset *hs2) { + Hideset head = {}; + Hideset *cur = &head; + для (; hs1; hs1 = hs1->next) + если (hideset_has_element(hs2, hs1->name, strlen(hs1->name))) + cur = cur->next = create_hideset(hs1->name); + возврат head.next; +} + +стат Token *attach_hideset(Token *tok, Hideset *hs) { + Token head = {}; + Token *cur = &head; + для (; tok; tok = tok->next) { + Token *t = duplicate_token(tok); + t->hideset = merge_hidesets(t->hideset, hs); + cur = cur->next = t; + } + возврат head.next; +} + +стат Token *concatenate_tokens(Token *tok1, Token *tok2) { + если (tok1->kind == TK_EOF) + возврат tok2; + Token head = {}; + Token *cur = &head; + для (; tok1->kind != TK_EOF; tok1 = tok1->next) + cur = cur->next = duplicate_token(tok1); + cur->next = tok2; + возврат head.next; +} + +стат Token *skip_nested_conditionals(Token *tok) { + пока (tok->kind != TK_EOF) { + если (is_directive_start(tok) && + (equal(tok->next, "if") || equal(tok->next, "ifdef") || + equal(tok->next, "ifndef") || equal(tok->next, "если") || + equal(tok->next, "еслиопр") || equal(tok->next, "еслинеопр"))) { + tok = skip_nested_conditionals(tok->next->next); + продолжи; + } + если (is_directive_start(tok) && (equal(tok->next, "endif") || equal(tok->next, "конецесли"))) + возврат tok->next->next; + tok = tok->next; + } + возврат tok; +} + +стат Token *skip_to_conditional_branch(Token *tok) { + пока (tok->kind != TK_EOF) { + если (is_directive_start(tok) && + (equal(tok->next, "if") || equal(tok->next, "ifdef") || + equal(tok->next, "ifndef") || equal(tok->next, "если") || + equal(tok->next, "еслиопр") || equal(tok->next, "еслинеопр"))) { + tok = skip_nested_conditionals(tok->next->next); + продолжи; + } + если (is_directive_start(tok) && + (equal(tok->next, "elif") || equal(tok->next, "else") || + equal(tok->next, "endif") || equal(tok->next, "конецесли") || + equal(tok->next, "иначеесли") || equal(tok->next, "иначе"))) + прерви; + tok = tok->next; + } + возврат tok; +} + +стат символ *escape_string_literal(символ *str) { + цел bufsize = 3; + для (цел i = 0; str[i]; i++) { + если (str[i] == '\'' || str[i] == '"') + bufsize++; + bufsize++; + } + символ *buf = track_pp(calloc(1, bufsize)); + символ *p = buf; + *p++ = '"'; + для (цел i = 0; str[i]; i++) { + если (str[i] == '\'' || str[i] == '"') + *p++ = '\\'; + *p++ = str[i]; + } + *p++ = '"'; + *p++ = '\0'; + возврат buf; +} + +стат Token *create_string_token(символ *str, Token *tmpl) { + символ *buf = escape_string_literal(str); + возврат tokenize(new_file(tmpl->file->name, tmpl->file->file_no, buf)); +} + +стат Token *extract_line_tokens(Token **rest, Token *tok) { + Token head = {}; + Token *cur = &head; + для (; !tok->at_bol; tok = tok->next) + cur = cur->next = duplicate_token(tok); + cur->next = create_eof_token(tok); + *rest = tok; + возврат head.next; +} + +стат Token *create_number_token(цел val, Token *tmpl) { + символ *buf = track_pp(формат("%d\n", val)); + возврат tokenize(new_file(tmpl->file->name, tmpl->file->file_no, buf)); +} + +стат Token *parse_constant_expression(Token **rest, Token *tok) { + tok = extract_line_tokens(rest, tok); + Token head = {}; + Token *cur = &head; + пока (tok->kind != TK_EOF) { + если ((equal(tok, "defined") || equal(tok, "определено"))) { + Token *start = tok; + бул has_paren = consume(&tok, tok->next, "("); + если (tok->kind != TK_IDENT) + error_tok(start, "имя макроса должно быть идентификатором"); + Macro *m = lookup_macro(tok); + tok = tok->next; + если (has_paren) + tok = skip(tok, ")"); + cur = cur->next = create_number_token(m ? 1 : 0, start); + продолжи; + } + cur = cur->next = tok; + tok = tok->next; + } + cur->next = tok; + возврат head.next; +} + +стат длин evaluate_constant_expression(Token **rest, Token *tok) { + Token *start = tok; + Token *expr = parse_constant_expression(rest, tok->next); + expr = preprocess_internal(expr); + если (expr->kind == TK_EOF) + error_tok(start, "нет выражения"); + + для (Token *t = expr; t->kind != TK_EOF; t = t->next) { + если (t->kind == TK_IDENT) { + Token *next = t->next; + *t = *create_number_token(0, t); + t->next = next; + } + } + convert_pp_tokens(expr); + Token *rest2; + длин val = const_expr(&rest2, expr); + если (rest2->kind != TK_EOF) + error_tok(rest2, "лишний токен"); + возврат val; +} + +стат CondIncl *push_conditional_context(Token *tok, бул included) { + CondIncl *ci = track_pp(calloc(1, размер(CondIncl))); + ci->next = conditional_stack; + ci->ctx = IN_THEN; + ci->tok = tok; + ci->included = included; + conditional_stack = ci; + возврат ci; +} + +стат Macro *lookup_macro(Token *tok) { + если (tok->kind != TK_IDENT) + возврат NULL; + возврат хеш_получить2(¯o_registry, tok->loc, tok->len); +} + +стат Macro *register_macro(символ *name, бул is_objlike, Token *body) { + Macro *m = track_pp(calloc(1, размер(Macro))); + m->name = name; + m->is_objlike = is_objlike; + m->body = body; + хеш_вставить(¯o_registry, name, m); + возврат m; +} + +стат MacroParam *parse_macro_parameters(Token **rest, Token *tok, символ **va_args_name) { + MacroParam head = {}; + MacroParam *cur = &head; + пока (!equal(tok, ")")) { + если (cur != &head) + tok = skip(tok, ","); + если (equal(tok, "...")) { + *va_args_name = "__VA_ARGS__"; + *rest = skip(tok->next, ")"); + возврат head.next; + } + если (tok->kind != TK_IDENT) + error_tok(tok, "ожидался идентификатор"); + если (equal(tok->next, "...")) { + *va_args_name = track_pp(strndup(tok->loc, tok->len)); + *rest = skip(tok->next->next, ")"); + возврат head.next; + } + MacroParam *m = track_pp(calloc(1, размер(MacroParam))); + m->name = track_pp(strndup(tok->loc, tok->len)); + cur = cur->next = m; + tok = tok->next; + } + *rest = tok->next; + возврат head.next; +} + +стат пусто parse_macro_definition(Token **rest, Token *tok) { + если (tok->kind != TK_IDENT) + error_tok(tok, "имя макроса должно быть идентификатором"); + символ *name = track_pp(strndup(tok->loc, tok->len)); + tok = tok->next; + если (!tok->has_space && equal(tok, "(")) { + символ *va_args_name = NULL; + MacroParam *params = parse_macro_parameters(&tok, tok->next, &va_args_name); + Macro *m = register_macro(name, ложь, extract_line_tokens(rest, tok)); + m->params = params; + m->va_args_name = va_args_name; + } иначе { + register_macro(name, истина, extract_line_tokens(rest, tok)); + } +} + +стат MacroArg *parse_single_macro_argument(Token **rest, Token *tok, бул read_rest) { + Token head = {}; + Token *cur = &head; + цел level = 0; + для (;;) { + если (level == 0 && equal(tok, ")")) + прерви; + если (level == 0 && !read_rest && equal(tok, ",")) + прерви; + если (tok->kind == TK_EOF) + error_tok(tok, "преждевременный конец ввода"); + если (equal(tok, "(")) + level++; + иначе если (equal(tok, ")")) + level--; + cur = cur->next = duplicate_token(tok); + tok = tok->next; + } + cur->next = create_eof_token(tok); + MacroArg *arg = track_pp(calloc(1, размер(MacroArg))); + arg->tok = head.next; + *rest = tok; + возврат arg; +} + +стат MacroArg *parse_macro_arguments(Token **rest, Token *tok, MacroParam *params, символ *va_args_name) { + Token *start = tok; + tok = tok->next->next; + MacroArg head = {}; + MacroArg *cur = &head; + MacroParam *pp = params; + для (; pp; pp = pp->next) { + если (cur != &head) + tok = skip(tok, ","); + cur = cur->next = parse_single_macro_argument(&tok, tok, ложь); + cur->name = pp->name; + } + если (va_args_name) { + MacroArg *arg; + если (equal(tok, ")")) { + arg = track_pp(calloc(1, размер(MacroArg))); + arg->tok = create_eof_token(tok); + } иначе { + если (pp != params) + tok = skip(tok, ","); + arg = parse_single_macro_argument(&tok, tok, истина); + } + arg->name = va_args_name; + arg->is_va_args = истина; + cur = cur->next = arg; + } иначе если (pp) { + error_tok(start, "слишком много аргументов"); + } + skip(tok, ")"); + *rest = tok; + возврат head.next; +} + +стат MacroArg *lookup_macro_argument(MacroArg *args, Token *tok) { + для (MacroArg *ap = args; ap; ap = ap->next) + если (tok->len == strlen(ap->name) && !strncmp(tok->loc, ap->name, tok->len)) + возврат ap; + возврат NULL; +} + +стат символ *merge_token_sequence(Token *tok, Token *end) { + цел len = 1; + для (Token *t = tok; t != end && t->kind != TK_EOF; t = t->next) { + если (t != tok && t->has_space) + len++; + len += t->len; + } + символ *buf = track_pp(calloc(1, len)); + цел pos = 0; + для (Token *t = tok; t != end && t->kind != TK_EOF; t = t->next) { + если (t != tok && t->has_space) + buf[pos++] = ' '; + strncpy(buf + pos, t->loc, t->len); + pos += t->len; + } + buf[pos] = '\0'; + возврат buf; +} + +стат Token *convert_to_string_literal(Token *hash, Token *arg) { + символ *s = merge_token_sequence(arg, NULL); + возврат create_string_token(s, hash); +} + +стат Token *concatenate_adjacent_tokens(Token *lhs, Token *rhs) { + символ *buf = track_pp(формат("%.*s%.*s", lhs->len, lhs->loc, rhs->len, rhs->loc)); + Token *tok = tokenize(new_file(lhs->file->name, lhs->file->file_no, buf)); + если (tok->next->kind != TK_EOF) + error_tok(lhs, "склейка образует '%s', недопустимый токен", buf); + возврат tok; +} + +стат бул contains_variadic_arguments(MacroArg *args) { + для (MacroArg *ap = args; ap; ap = ap->next) + если (!strcmp(ap->name, "__VA_ARGS__")) + возврат ap->tok->kind != TK_EOF; + возврат ложь; +} + +стат Token *substitute_parameters(Token *tok, MacroArg *args) { + Token head = {}; + Token *cur = &head; + пока (tok->kind != TK_EOF) { + если (equal(tok, "#")) { + MacroArg *arg = lookup_macro_argument(args, tok->next); + если (!arg) + error_tok(tok->next, "'#' не следует за параметром макроса"); + cur = cur->next = convert_to_string_literal(tok, arg->tok); + tok = tok->next->next; + продолжи; + } + если (equal(tok, ",") && equal(tok->next, "##")) { + MacroArg *arg = lookup_macro_argument(args, tok->next->next); + если (arg && arg->is_va_args) { + если (arg->tok->kind == TK_EOF) { + tok = tok->next->next->next; + } иначе { + cur = cur->next = duplicate_token(tok); + tok = tok->next->next; + } + продолжи; + } + } + если (equal(tok, "##")) { + если (cur == &head) + error_tok(tok, "'##' не может быть в начале подстановки макроса"); + если (tok->next->kind == TK_EOF) + error_tok(tok, "'##' не может быть в конце подстановки макроса"); + MacroArg *arg = lookup_macro_argument(args, tok->next); + если (arg) { + если (arg->tok->kind != TK_EOF) { + *cur = *concatenate_adjacent_tokens(cur, arg->tok); + для (Token *t = arg->tok->next; t->kind != TK_EOF; t = t->next) + cur = cur->next = duplicate_token(t); + } + tok = tok->next->next; + продолжи; + } + *cur = *concatenate_adjacent_tokens(cur, tok->next); + tok = tok->next->next; + продолжи; + } + MacroArg *arg = lookup_macro_argument(args, tok); + если (arg && equal(tok->next, "##")) { + Token *rhs = tok->next->next; + если (arg->tok->kind == TK_EOF) { + MacroArg *arg2 = lookup_macro_argument(args, rhs); + если (arg2) { + для (Token *t = arg2->tok; t->kind != TK_EOF; t = t->next) + cur = cur->next = duplicate_token(t); + } иначе { + cur = cur->next = duplicate_token(rhs); + } + tok = rhs->next; + продолжи; + } + для (Token *t = arg->tok; t->kind != TK_EOF; t = t->next) + cur = cur->next = duplicate_token(t); + tok = tok->next; + продолжи; + } + если (equal(tok, "__VA_OPT__") && equal(tok->next, "(")) { + MacroArg *arg = parse_single_macro_argument(&tok, tok->next->next, истина); + если (contains_variadic_arguments(args)) + для (Token *t = arg->tok; t->kind != TK_EOF; t = t->next) + cur = cur->next = t; + tok = skip(tok, ")"); + продолжи; + } + если (arg) { + Token *t = preprocess_internal(arg->tok); + t->at_bol = tok->at_bol; + t->has_space = tok->has_space; + для (; t->kind != TK_EOF; t = t->next) + cur = cur->next = duplicate_token(t); + tok = tok->next; + продолжи; + } + cur = cur->next = duplicate_token(tok); + tok = tok->next; + продолжи; + } + cur->next = tok; + возврат head.next; +} + +стат бул perform_macro_expansion(Token **rest, Token *tok) { + если (hideset_has_element(tok->hideset, tok->loc, tok->len)) + возврат ложь; + Macro *m = lookup_macro(tok); + если (!m) + возврат ложь; + если (m->handler) { + *rest = m->handler(tok); + (*rest)->next = tok->next; + возврат истина; + } + если (m->is_objlike) { + Hideset *hs = merge_hidesets(tok->hideset, create_hideset(m->name)); + Token *body = attach_hideset(m->body, hs); + для (Token *t = body; t->kind != TK_EOF; t = t->next) + t->origin = tok; + *rest = concatenate_tokens(body, tok->next); + (*rest)->at_bol = tok->at_bol; + (*rest)->has_space = tok->has_space; + возврат истина; + } + если (!equal(tok->next, "(")) + возврат ложь; + Token *macro_token = tok; + MacroArg *args = parse_macro_arguments(&tok, tok, m->params, m->va_args_name); + Token *rparen = tok; + Hideset *hs = intersect_hidesets(macro_token->hideset, rparen->hideset); + hs = merge_hidesets(hs, create_hideset(m->name)); + Token *body = substitute_parameters(m->body, args); + body = attach_hideset(body, hs); + для (Token *t = body; t->kind != TK_EOF; t = t->next) + t->origin = macro_token; + *rest = concatenate_tokens(body, tok->next); + (*rest)->at_bol = macro_token->at_bol; + (*rest)->has_space = macro_token->has_space; + возврат истина; +} + +символ *search_include_paths(символ *filename) { + если (filename[0] == '/') + возврат filename; + символ *cached = хеш_получить(&include_paths_cache, filename); + если (cached) + возврат cached; + + для (цел i = 0; i < include_paths.len; i++) { + символ *path = track_pp(формат("%s/%s", include_paths.data[i], filename)); + если (!file_exists(path)) + продолжи; + хеш_вставить(&include_paths_cache, filename, path); + include_search_position = i + 1; + возврат path; + } + возврат NULL; +} + +стат символ *find_next_include_path(символ *filename) { + для (; include_search_position < include_paths.len; include_search_position++) { + символ *path = track_pp(формат("%s/%s", include_paths.data[include_search_position], filename)); + если (file_exists(path)) + возврат path; + } + возврат NULL; +} + +стат символ *extract_include_filename(Token **rest, Token *tok, бул *is_dquote) { + если (tok->kind == TK_STR) { + *is_dquote = истина; + *rest = skip_to_end_of_line(tok->next); + возврат track_pp(strndup(tok->loc + 1, tok->len - 2)); + } + если (equal(tok, "<")) { + Token *start = tok; + для (; !equal(tok, ">"); tok = tok->next) + если (tok->at_bol || tok->kind == TK_EOF) + error_tok(tok, "ожидался '>'"); + *is_dquote = ложь; + *rest = skip_to_end_of_line(tok->next); + возврат merge_token_sequence(start->next, tok); + } + если (tok->kind == TK_IDENT) { + Token *tok2 = preprocess_internal(extract_line_tokens(rest, tok)); + возврат extract_include_filename(&tok2, tok2, is_dquote); + } + error_tok(tok, "ожидалось имя файла"); +} + +стат символ *identify_include_guard(Token *tok) { + если (!is_directive_start(tok) || !(equal(tok->next, "ifndef") || equal(tok->next, "еслинеопр"))) + возврат NULL; + tok = tok->next->next; + если (tok->kind != TK_IDENT) + возврат NULL; + символ *macro = track_pp(strndup(tok->loc, tok->len)); + tok = tok->next; + если (!is_directive_start(tok) || !(equal(tok->next, "define") || equal(tok->next, "определить")) || !(equal(tok->next->next, macro))) + возврат NULL; + пока (tok->kind != TK_EOF) { + если (!is_directive_start(tok)) { + tok = tok->next; + продолжи; + } + если ((equal(tok->next, "endif") || equal(tok->next, "конецесли")) && tok->next->next->kind == TK_EOF) + возврат macro; + если (equal(tok, "if") || equal(tok->next, "ifdef") || equal(tok->next, "ifndef") || + equal(tok, "если") || equal(tok->next, "еслиопр") || equal(tok->next, "еслинеопр")) + tok = skip_to_conditional_branch(tok->next); + иначе + tok = tok->next; + } + возврат NULL; +} + +стат Token *process_include_directive(Token *tok, символ *path, Token *filename_tok) { + если (хеш_получить(&pragma_once_registry, path)) + возврат tok; + + символ *guard_name = хеш_получить(&include_guards, path); + если (guard_name && хеш_получить(¯o_registry, guard_name)) + возврат tok; + + Token *tok2 = tokenize_file(path); + если (!tok2) + error_tok(filename_tok, "%s: не удалось открыть файл: %s", path, strerror(errno)); + + guard_name = identify_include_guard(tok2); + если (guard_name) + хеш_вставить(&include_guards, path, guard_name); + + возврат concatenate_tokens(tok2, tok); +} + +стат пусто parse_line_directive(Token **rest, Token *tok) { + Token *start = tok; + tok = preprocess(extract_line_tokens(rest, tok)); + если (tok->kind != TK_NUM || tok->ty->kind != TY_INT) + error_tok(tok, "недопустимый маркер строки"); + start->file->line_delta = tok->val - start->line_no; + tok = tok->next; + если (tok->kind == TK_EOF) + возврат; + если (tok->kind != TK_STR) + error_tok(tok, "ожидалось имя файла"); + start->file->display_name = tok->str; +} + +стат Token *preprocess_internal(Token *tok) { + Token head = {}; + Token *cur = &head; + пока (tok->kind != TK_EOF) { + если (perform_macro_expansion(&tok, tok)) + продолжи; + если (!is_directive_start(tok)) { + tok->line_delta = tok->file->line_delta; + tok->filename = tok->file->display_name; + cur = cur->next = tok; + tok = tok->next; + продолжи; + } + Token *start = tok; + tok = tok->next; + если ((equal(tok, "include") || equal(tok, "включить"))) { + бул is_dquote; + символ *filename = extract_include_filename(&tok, tok->next, &is_dquote); + если (filename[0] != '/' && is_dquote) { + символ *dir = track_pp(strdup(start->file->name)); + символ *path = track_pp(формат("%s/%s", dirname(dir), filename)); + если (file_exists(path)) { + tok = process_include_directive(tok, path, start->next->next); + продолжи; + } + } + символ *path = search_include_paths(filename); + tok = process_include_directive(tok, path ? path : filename, start->next->next); + продолжи; + } + если ((equal(tok, "include_next") || equal(tok, "включить_след"))) { + бул ignore; + символ *filename = extract_include_filename(&tok, tok->next, &ignore); + символ *path = find_next_include_path(filename); + tok = process_include_directive(tok, path ? path : filename, start->next->next); + продолжи; + } + если ((equal(tok, "define") || equal(tok, "определить"))) { + parse_macro_definition(&tok, tok->next); + продолжи; + } + если ((equal(tok, "undef") || equal(tok, "отменить"))) { + tok = tok->next; + если (tok->kind != TK_IDENT) + error_tok(tok, "имя макроса должно быть идентификатором"); + undef_macro(track_pp(strndup(tok->loc, tok->len))); + tok = skip_to_end_of_line(tok->next); + продолжи; + } + если ((equal(tok, "if") || equal(tok, "если"))) { + длин val = evaluate_constant_expression(&tok, tok); + push_conditional_context(start, val); + если (!val) + tok = skip_to_conditional_branch(tok); + продолжи; + } + если ((equal(tok, "ifdef") || equal(tok, "еслиопр"))) { + бул defined = lookup_macro(tok->next); + push_conditional_context(tok, defined); + tok = skip_to_end_of_line(tok->next->next); + если (!defined) + tok = skip_to_conditional_branch(tok); + продолжи; + } + если ((equal(tok, "ifndef") || equal(tok, "еслинеопр"))) { + бул defined = lookup_macro(tok->next); + push_conditional_context(tok, !defined); + tok = skip_to_end_of_line(tok->next->next); + если (defined) + tok = skip_to_conditional_branch(tok); + продолжи; + } + если ((equal(tok, "elif") || equal(tok, "иначеесли"))) { + если (!conditional_stack || conditional_stack->ctx == IN_ELSE) + error_tok(start, "#elif не к месту"); + conditional_stack->ctx = IN_ELIF; + если (!conditional_stack->included && evaluate_constant_expression(&tok, tok)) + conditional_stack->included = истина; + иначе + tok = skip_to_conditional_branch(tok); + продолжи; + } + если ((equal(tok, "else") || equal(tok, "иначе"))) { + если (!conditional_stack || conditional_stack->ctx == IN_ELSE) + error_tok(start, "#else не к месту"); + conditional_stack->ctx = IN_ELSE; + tok = skip_to_end_of_line(tok->next); + если (conditional_stack->included) + tok = skip_to_conditional_branch(tok); + продолжи; + } + если ((equal(tok, "endif") || equal(tok, "конецесли"))) { + если (!conditional_stack) + error_tok(start, "#endif не к месту"); + conditional_stack = conditional_stack->next; + tok = skip_to_end_of_line(tok->next); + продолжи; + } + если ((equal(tok, "line") || equal(tok, "строка"))) { + parse_line_directive(&tok, tok->next); + продолжи; + } + если (tok->kind == TK_PP_NUM) { + parse_line_directive(&tok, tok); + продолжи; + } + если ((equal(tok, "pragma") || equal(tok, "прагма")) && (equal(tok->next, "once") || equal(tok->next, "однократно"))) { + хеш_вставить(&pragma_once_registry, tok->file->name, (пусто *)1); + tok = skip_to_end_of_line(tok->next->next); + продолжи; + } + если ((equal(tok, "pragma") || equal(tok, "прагма"))) { + делай { + tok = tok->next; + } пока (!tok->at_bol); + продолжи; + } + если ((equal(tok, "error") || equal(tok, "ошибка"))) + error_tok(tok, "ошибка"); + если (tok->at_bol) + продолжи; + error_tok(tok, "недопустимая директива препроцессора"); + } + cur->next = tok; + возврат head.next; +} + +пусто define_macro(символ *name, символ *buf) { + Token *tok = tokenize(new_file("", 1, buf)); + register_macro(name, истина, tok); +} + +пусто undef_macro(символ *name) { + хеш_удалить(¯o_registry, name); +} + +стат Macro *register_builtin_macro(символ *name, macro_handler_fn *fn) { + Macro *m = register_macro(name, истина, NULL); + m->handler = fn; + возврат m; +} + +стат Token *expand_file_macro(Token *tmpl) { + пока (tmpl->origin) + tmpl = tmpl->origin; + возврат create_string_token(tmpl->file->display_name, tmpl); +} + +стат Token *expand_line_macro(Token *tmpl) { + пока (tmpl->origin) + tmpl = tmpl->origin; + цел i = tmpl->line_no + tmpl->file->line_delta; + возврат create_number_token(i, tmpl); +} + +стат Token *expand_counter_macro(Token *tmpl) { + стат цел i = 0; + возврат create_number_token(i++, tmpl); +} + +стат Token *expand_timestamp_macro(Token *tmpl) { + структ stat st; + если (stat(tmpl->file->name, &st) != 0) + возврат create_string_token("??? ??? ?? ??:??:?? ????", tmpl); + символ buf[30]; + ctime_r(&st.st_mtime, buf); + buf[24] = '\0'; + возврат create_string_token(buf, tmpl); +} + +стат Token *expand_base_file_macro(Token *tmpl) { + возврат create_string_token(base_file, tmpl); +} + +стат символ *format_date_string(структ tm *tm) { + стат символ mon[][4] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", + }; + возврат track_pp(формат("\"%s %2d %d\"", mon[tm->tm_mon], tm->tm_mday, tm->tm_year + 1900)); +} + +стат символ *format_time_string(структ tm *tm) { + возврат track_pp(формат("\"%02d:%02d:%02d\"", tm->tm_hour, tm->tm_min, tm->tm_sec)); +} + + +// Инициализация предопределённых макросов +пусто init_macros(пусто) { + // Определение предопределённых макросов + define_macro("_LP64", "1"); + define_macro("__C99_MACRO_WITH_VA_ARGS", "1"); + define_macro("__ELF__", "1"); + define_macro("__LP64__", "1"); + define_macro("__SIZEOF_DOUBLE__", "8"); + define_macro("__SIZEOF_FLOAT__", "4"); + define_macro("__SIZEOF_INT__", "4"); + define_macro("__SIZEOF_LONG_DOUBLE__", "8"); + define_macro("__SIZEOF_LONG_LONG__", "8"); + define_macro("__SIZEOF_LONG__", "8"); + define_macro("__SIZEOF_POINTER__", "8"); + define_macro("__SIZEOF_PTRDIFF_T__", "8"); + define_macro("__SIZEOF_SHORT__", "2"); + define_macro("__SIZEOF_SIZE_T__", "8"); + define_macro("__SIZE_TYPE__", "unsigned long"); + define_macro("__STDC_HOSTED__", "1"); + define_macro("__STDC_NO_COMPLEX__", "1"); + define_macro("__STDC_UTF_16__", "1"); + define_macro("__STDC_UTF_32__", "1"); + define_macro("__STDC_VERSION__", "201112L"); + define_macro("__STDC__", "1"); + define_macro("__USER_LABEL_PREFIX__", ""); + define_macro("__alignof__", "_Alignof"); + define_macro("__amd64", "1"); + define_macro("__amd64__", "1"); + define_macro("__rusi__", "1"); + define_macro("__const__", "const"); + define_macro("__gnu_linux__", "1"); + define_macro("__inline__", "inline"); + define_macro("__linux", "1"); + define_macro("__linux__", "1"); + define_macro("__signed__", "signed"); + define_macro("__typeof__", "typeof"); + define_macro("__unix", "1"); + define_macro("__unix__", "1"); + define_macro("__volatile__", "volatile"); + define_macro("__x86_64", "1"); + define_macro("__x86_64__", "1"); + define_macro("linux", "1"); + define_macro("unix", "1"); + + register_builtin_macro("__FILE__", expand_file_macro); + register_builtin_macro("__LINE__", expand_line_macro); + register_builtin_macro("__COUNTER__", expand_counter_macro); + register_builtin_macro("__TIMESTAMP__", expand_timestamp_macro); + register_builtin_macro("__BASE_FILE__", expand_base_file_macro); + + register_builtin_macro("__ФАЙЛ__", expand_file_macro); + register_builtin_macro("__ЛИНИЯ__", expand_line_macro); + register_builtin_macro("__СЧЕТЧИК__", expand_counter_macro); + register_builtin_macro("__ВРЕМЯ_ФАЙЛА__", expand_timestamp_macro); + register_builtin_macro("__БАЗОВЫЙ_ФАЙЛ__", expand_base_file_macro); + + time_t now = time(NULL); + структ tm *tm = localtime(&now); + define_macro("__DATE__", format_date_string(tm)); + define_macro("__TIME__", format_time_string(tm)); + + define_macro("__ДАТА__", format_date_string(tm)); + define_macro("__ВРЕМЯ__", format_time_string(tm)); +} + +типдеф переч { + STR_NONE, STR_UTF8, STR_UTF16, STR_UTF32, STR_WIDE, +} StringKind; + +стат StringKind determine_string_encoding(Token *tok) { + если (!strcmp(tok->loc, "u8")) + возврат STR_UTF8; + выбор (tok->loc[0]) { + случай '"': возврат STR_NONE; + случай 'u': возврат STR_UTF16; + случай 'U': возврат STR_UTF32; + случай 'L': возврат STR_WIDE; + } + unreachable(); +} + +стат пусто merge_consecutive_strings(Token *tok) { + для (Token *tok1 = tok; tok1->kind != TK_EOF;) { + если (tok1->kind != TK_STR || tok1->next->kind != TK_STR) { + tok1 = tok1->next; + продолжи; + } + StringKind kind = determine_string_encoding(tok1); + Type *basety = tok1->ty->base; + для (Token *t = tok1->next; t->kind == TK_STR; t = t->next) { + StringKind k = determine_string_encoding(t); + если (kind == STR_NONE) { + kind = k; + basety = t->ty->base; + } иначе если (k != STR_NONE && kind != k) { + error_tok(t, "неподдерживаемая нестандартная конкатенация строковых литералов"); + } + } + если (basety->size > 1) + для (Token *t = tok1; t->kind == TK_STR; t = t->next) + если (t->ty->base->size == 1) + *t = *tokenize_string_literal(t, basety); + пока (tok1->kind == TK_STR) + tok1 = tok1->next; + } + + для (Token *tok1 = tok; tok1->kind != TK_EOF;) { + если (tok1->kind != TK_STR || tok1->next->kind != TK_STR) { + tok1 = tok1->next; + продолжи; + } + Token *tok2 = tok1->next; + пока (tok2->kind == TK_STR) + tok2 = tok2->next; + цел len = tok1->ty->array_len; + для (Token *t = tok1->next; t != tok2; t = t->next) + len = len + t->ty->array_len - 1; + символ *buf = track_pp(calloc(tok1->ty->base->size, len)); + цел i = 0; + для (Token *t = tok1; t != tok2; t = t->next) { + memcpy(buf + i, t->str, t->ty->size); + i = i + t->ty->size - t->ty->base->size; + } + *tok1 = *duplicate_token(tok1); + tok1->ty = array_of(tok1->ty->base, len); + tok1->str = buf; + tok1->next = tok2; + tok1 = tok2; + } +} + +Token *preprocess(Token *tok) { + tok = preprocess_internal(tok); + если (conditional_stack) + error_tok(conditional_stack->tok, "незавершённая условная директива"); + convert_pp_tokens(tok); + merge_consecutive_strings(tok); + для (Token *t = tok; t; t = t->next) + t->line_no += t->line_delta; + возврат tok; +} \ No newline at end of file diff --git a/rusi.elf b/rusi.elf new file mode 100755 index 0000000..1736c2c Binary files /dev/null and b/rusi.elf differ diff --git a/stage2/rusi.elf b/stage2/rusi.elf new file mode 100755 index 0000000..f424bf7 Binary files /dev/null and b/stage2/rusi.elf differ diff --git a/strings.c b/strings.c new file mode 100644 index 0000000..c1e9d9c --- /dev/null +++ b/strings.c @@ -0,0 +1,253 @@ +// ============================================================ +// Управление динамическим массивом строк +// ============================================================ + +#включить "руси.з" +#включить // стандартная библиотека +#включить // стандартная библиотека +#включить // стандартная библиотека +#включить // стандартная библиотека + +// ============================================================ +// Управление динамическим массивом строк +// ============================================================ + +пусто массив_строк_добавить(МассивСтрок *массив, символ *строка) { + если (!массив) возврат; + + // Инициализация массива при первом использовании + если (!массив->data) { + массив->data = (символ **)calloc(8, размер(символ *)); + если (!массив->data) { + // Ошибка выделения памяти - выходим + возврат; + } + массив->capacity = 8; + массив->len = 0; + } + + // Расширение массива при необходимости + если (массив->capacity == массив->len) { + size_t новая_ёмкость = массив->capacity * 2; + символ **новые_данные = (символ **)realloc(массив->data, размер(символ *) * новая_ёмкость); + если (!новые_данные) { + // Ошибка перевыделения памяти + возврат; + } + + массив->data = новые_данные; + массив->capacity = новая_ёмкость; + + // Инициализация новых элементов нулями + для (size_t ш = массив->len; ш < массив->capacity; ш++) { + массив->data[ш] = NULL; + } + } + + // Добавление строки + массив->data[массив->len++] = строка; +} + +// ============================================================ +// Форматирование строки в стиле printf +// ============================================================ + +символ *формат(символ *строка_формата, ...) { + если (!строка_формата) возврат NULL; + + символ *буфер = NULL; + size_t разм_буфера = 0; + + // Открываем поток в память + FILE *поток = open_memstream(&буфер, &разм_буфера); + если (!поток) { + возврат NULL; + } + + // Форматируем вывод + va_list арг; + va_start(арг, строка_формата); + цел рез = vfprintf(поток, строка_формата, арг); + va_end(арг); + + // Закрываем поток (это освобождает внутренние ресурсы) + fclose(поток); + + // Проверяем успешность записи + если (рез < 0) { + free(буфер); + возврат NULL; + } + + возврат буфер; +} + +// ============================================================ +// Дополнительные полезные функции для работы со строками +// ============================================================ + +// Создает копию строки с выделением памяти +символ *строка_дублировать(конст символ *исходная) { + если (!исходная) возврат NULL; + + size_t длина = strlen(исходная) + 1; + символ *копия = (символ *)malloc(длина); + если (!копия) возврат NULL; + + memcpy(копия, исходная, длина); + возврат копия; +} + +// Объединяет две строки в новую +символ *строка_склеить(конст символ *первая, конст символ *вторая) { + если (!первая) возврат строка_дублировать(вторая); + если (!вторая) возврат строка_дублировать(первая); + + size_t дл1 = strlen(первая); + size_t дл2 = strlen(вторая); + символ *рез = (символ *)malloc(дл1 + дл2 + 1); + если (!рез) возврат NULL; + + memcpy(рез, первая, дл1); + memcpy(рез + дл1, вторая, дл2 + 1); + возврат рез; +} + +// Объединяет три строки в новую +символ *строка_склеить3(конст символ *первая, конст символ *вторая, конст символ *третья) { + если (!первая && !вторая && !третья) возврат NULL; + если (!первая) возврат строка_склеить(вторая, третья); + если (!вторая) возврат строка_склеить(первая, третья); + если (!третья) возврат строка_склеить(первая, вторая); + + size_t дл1 = strlen(первая); + size_t дл2 = strlen(вторая); + size_t дл3 = strlen(третья); + символ *рез = (символ *)malloc(дл1 + дл2 + дл3 + 1); + если (!рез) возврат NULL; + + memcpy(рез, первая, дл1); + memcpy(рез + дл1, вторая, дл2); + memcpy(рез + дл1 + дл2, третья, дл3 + 1); + возврат рез; +} + +// Освобождает массив строк +пусто массив_строк_освободить(МассивСтрок *массив) { + если (!массив) возврат; + + если (массив->data) { + // Освобождаем каждую строку + для (size_t ш = 0; ш < массив->len; ш++) { + если (массив->data[ш]) { + free(массив->data[ш]); + массив->data[ш] = NULL; + } + } + // Освобождаем массив указателей + free(массив->data); + массив->data = NULL; + } + + массив->len = 0; + массив->capacity = 0; +} + +// Очищает массив строк без освобождения памяти под него +пусто массив_строк_очистить(МассивСтрок *массив) { + если (!массив || !массив->data) возврат; + + // Освобождаем только строки, но не массив указателей + для (size_t ш = 0; ш < массив->len; ш++) { + если (массив->data[ш]) { + free(массив->data[ш]); + массив->data[ш] = NULL; + } + } + массив->len = 0; +} + +// Получает строку по индексу с проверкой границ +символ *массив_строк_получить(конст МассивСтрок *массив, size_t инд) { + если (!массив || !массив->data || инд >= массив->len) { + возврат NULL; + } + возврат массив->data[инд]; +} + +// Устанавливает строку по индексу с проверкой границ +бул массив_строк_установить(МассивСтрок *массив, size_t инд, символ *строка) { + если (!массив || !массив->data || инд >= массив->len) { + возврат ложь; + } + + // Освобождаем старую строку + если (массив->data[инд]) { + free(массив->data[инд]); + } + + массив->data[инд] = строка; + возврат истина; +} + +// Вставляет строку в указанную позицию +бул массив_строк_вставить(МассивСтрок *массив, size_t поз, символ *строка) { + если (!массив || !строка) возврат ложь; + если (поз > массив->len) возврат ложь; + + // Добавляем в конец + если (поз == массив->len) { + массив_строк_добавить(массив, строка); + возврат истина; + } + + // Вставляем в середину - сначала добавляем элемент в конец + массив_строк_добавить(массив, NULL); + если (!массив->data) возврат ложь; + + // Сдвигаем элементы вправо + для (size_t ш = массив->len - 1; ш > поз; ш--) { + массив->data[ш] = массив->data[ш - 1]; + } + + массив->data[поз] = строка; + возврат истина; +} + +// Удаляет строку по индексу +бул массив_строк_удалить(МассивСтрок *массив, size_t инд) { + если (!массив || !массив->data || инд >= массив->len) { + возврат ложь; + } + + // Освобождаем удаляемую строку + если (массив->data[инд]) { + free(массив->data[инд]); + } + + // Сдвигаем элементы влево + для (size_t ш = инд; ш < массив->len - 1; ш++) { + массив->data[ш] = массив->data[ш + 1]; + } + + массив->len--; + массив->data[массив->len] = NULL; + возврат истина; +} + +// Ищет строку в массиве +ssize_t массив_строк_найти(конст МассивСтрок *массив, конст символ *строка) { + если (!массив || !массив->data || !строка) возврат -1; + + для (size_t ш = 0; ш < массив->len; ш++) { + если (массив->data[ш] && strcmp(массив->data[ш], строка) == 0) { + возврат (ssize_t)ш; + } + } + возврат -1; +} + +// Проверяет, содержит ли массив строку +бул массив_строк_содержит(конст МассивСтрок *массив, конст символ *строка) { + возврат массив_строк_найти(массив, строка) >= 0; +} \ No newline at end of file diff --git a/test/alignof.c b/test/alignof.c new file mode 100644 index 0000000..2086783 --- /dev/null +++ b/test/alignof.c @@ -0,0 +1,50 @@ +#включить "тест.з" +#включить "stdvv.c" + +цел _Выравнивание(512) г1; +цел _Выравнивание(512) г2; +символ г3; +цел г4; +длин г5; +символ г6; + +цел главн() { + ПРОВЕРИТЬ(1, _Выравнивание_типа(символ)); + ПРОВЕРИТЬ(2, _Выравнивание_типа(short)); + ПРОВЕРИТЬ(4, _Выравнивание_типа(цел)); + ПРОВЕРИТЬ(8, _Выравнивание_типа(длин)); + ПРОВЕРИТЬ(8, _Выравнивание_типа(длин длин)); + ПРОВЕРИТЬ(1, _Выравнивание_типа(символ[3])); + ПРОВЕРИТЬ(4, _Выравнивание_типа(цел[3])); + ПРОВЕРИТЬ(1, _Выравнивание_типа(структ {символ а; символ б;}[2])); + ПРОВЕРИТЬ(8, _Выравнивание_типа(структ {символ а; длин б;}[2])); + + ПРОВЕРИТЬ(1, ({ _Выравнивание(символ) символ х, у; &у-&х; })); + ПРОВЕРИТЬ(8, ({ _Выравнивание(длин) символ х, у; &у-&х; })); + ПРОВЕРИТЬ(32, ({ _Выравнивание(32) символ х, у; &у-&х; })); + ПРОВЕРИТЬ(32, ({ _Выравнивание(32) цел *х, *у; ((символ *)&у)-((символ *)&х); })); + ПРОВЕРИТЬ(16, ({ структ { _Выравнивание(16) символ х, у; } а; &а.у-&а.х; })); + ПРОВЕРИТЬ(8, ({ структ T { _Выравнивание(8) символ а; }; _Выравнивание_типа(структ T); })); + + ПРОВЕРИТЬ(0, (длин)(символ *)&г1 % 512); + ПРОВЕРИТЬ(0, (длин)(символ *)&г2 % 512); + ПРОВЕРИТЬ(0, (длин)(символ *)&г4 % 4); + ПРОВЕРИТЬ(0, (длин)(символ *)&г5 % 8); + + ПРОВЕРИТЬ(1, ({ символ х; _Выравнивание_типа(х); })); + ПРОВЕРИТЬ(4, ({ цел х; _Выравнивание_типа(х); })); + ПРОВЕРИТЬ(1, ({ символ х; _Выравнивание_типа х; })); + ПРОВЕРИТЬ(4, ({ цел х; _Выравнивание_типа х; })); + + ПРОВЕРИТЬ(1, _Выравнивание_типа(символ) << 31 >> 31); + ПРОВЕРИТЬ(1, _Выравнивание_типа(символ) << 63 >> 63); + ПРОВЕРИТЬ(1, ({ символ х; _Выравнивание_типа(х) << 63 >> 63; })); + + ПРОВЕРИТЬ(0, ({ символ х[16]; (беззнак длин)&х % 16; })); + ПРОВЕРИТЬ(0, ({ символ х[17]; (беззнак длин)&х % 16; })); + ПРОВЕРИТЬ(0, ({ символ х[100]; (беззнак длин)&х % 16; })); + ПРОВЕРИТЬ(0, ({ символ х[101]; (беззнак длин)&х % 16; })); + + печатьф("OK\n"); + возврат 0; +} diff --git a/test/alloca.c b/test/alloca.c new file mode 100644 index 0000000..8f8c360 --- /dev/null +++ b/test/alloca.c @@ -0,0 +1,29 @@ +#включить "тест.з" +#включить "stdvv.c" + +пусто *функция(цел x, пусто *p, цел y) { возврат p; } + +цел главн() { + цел i = 0; + + символ *p1 = alloca(16); + символ *p2 = alloca(16); + символ *p3 = 1 + (символ *)alloca(3) + 1; + p3 -= 2; + символ *p4 = функция(1, alloca(16), 3); + + ПРОВЕРИТЬ(16, p1 - p2); + ПРОВЕРИТЬ(16, p2 - p3); + ПРОВЕРИТЬ(16, p3 - p4); + + memcpy(p1, "0123456789abcdef", 16); + memcpy(p2, "ghijklmnopqrstuv", 16); + memcpy(p3, "wxy", 3); + + ПРОВЕРИТЬ(0, memcmp(p1, "0123456789abcdef", 16)); + ПРОВЕРИТЬ(0, memcmp(p2, "ghijklmnopqrstuv", 16)); + ПРОВЕРИТЬ(0, memcmp(p3, "wxy", 3)); + + печатьф("OK\n"); + возврат 0; +} \ No newline at end of file diff --git a/test/arith.c b/test/arith.c new file mode 100644 index 0000000..4012820 --- /dev/null +++ b/test/arith.c @@ -0,0 +1,145 @@ +#включить "тест.з" +#включить "stdvv.c" + +цел главн() { + ПРОВЕРИТЬ(0, 0); + ПРОВЕРИТЬ(42, 42); + ПРОВЕРИТЬ(21, 5+20-4); + ПРОВЕРИТЬ(41, 12 + 34 - 5 ); + ПРОВЕРИТЬ(47, 5+6*7); + ПРОВЕРИТЬ(15, 5*(9-6)); + ПРОВЕРИТЬ(4, (3+5)/2); + ПРОВЕРИТЬ(10, -10+20); + ПРОВЕРИТЬ(10, - -10); + ПРОВЕРИТЬ(10, - - +10); + + ПРОВЕРИТЬ(0, 0==1); + ПРОВЕРИТЬ(1, 42==42); + ПРОВЕРИТЬ(1, 0!=1); + ПРОВЕРИТЬ(0, 42!=42); + + ПРОВЕРИТЬ(1, 0<1); + ПРОВЕРИТЬ(0, 1<1); + ПРОВЕРИТЬ(0, 2<1); + ПРОВЕРИТЬ(1, 0<=1); + ПРОВЕРИТЬ(1, 1<=1); + ПРОВЕРИТЬ(0, 2<=1); + + ПРОВЕРИТЬ(1, 1>0); + ПРОВЕРИТЬ(0, 1>1); + ПРОВЕРИТЬ(0, 1>2); + ПРОВЕРИТЬ(1, 1>=0); + ПРОВЕРИТЬ(1, 1>=1); + ПРОВЕРИТЬ(0, 1>=2); + + ПРОВЕРИТЬ(0, 1073741824 * 100 / 100); + + ПРОВЕРИТЬ(7, ({ цел и=2; и+=5; и; })); + ПРОВЕРИТЬ(7, ({ цел и=2; и+=5; })); + ПРОВЕРИТЬ(3, ({ цел и=5; и-=2; и; })); + ПРОВЕРИТЬ(3, ({ цел и=5; и-=2; })); + ПРОВЕРИТЬ(6, ({ цел и=3; и*=2; и; })); + ПРОВЕРИТЬ(6, ({ цел и=3; и*=2; })); + ПРОВЕРИТЬ(3, ({ цел и=6; и/=2; и; })); + ПРОВЕРИТЬ(3, ({ цел и=6; и/=2; })); + + ПРОВЕРИТЬ(3, ({ цел и=2; ++и; })); + ПРОВЕРИТЬ(2, ({ цел а[3]; а[0]=0; а[1]=1; а[2]=2; цел *п=а+1; ++*п; })); + ПРОВЕРИТЬ(0, ({ цел а[3]; а[0]=0; а[1]=1; а[2]=2; цел *п=а+1; --*п; })); + + ПРОВЕРИТЬ(2, ({ цел и=2; и++; })); + ПРОВЕРИТЬ(2, ({ цел и=2; и--; })); + ПРОВЕРИТЬ(3, ({ цел и=2; и++; и; })); + ПРОВЕРИТЬ(1, ({ цел и=2; и--; и; })); + ПРОВЕРИТЬ(1, ({ цел а[3]; а[0]=0; а[1]=1; а[2]=2; цел *п=а+1; *п++; })); + ПРОВЕРИТЬ(1, ({ цел а[3]; а[0]=0; а[1]=1; а[2]=2; цел *п=а+1; *п--; })); + + ПРОВЕРИТЬ(0, ({ цел а[3]; а[0]=0; а[1]=1; а[2]=2; цел *п=а+1; (*п++)--; а[0]; })); + ПРОВЕРИТЬ(0, ({ цел а[3]; а[0]=0; а[1]=1; а[2]=2; цел *п=а+1; (*(п--))--; а[1]; })); + ПРОВЕРИТЬ(2, ({ цел а[3]; а[0]=0; а[1]=1; а[2]=2; цел *п=а+1; (*п)--; а[2]; })); + ПРОВЕРИТЬ(2, ({ цел а[3]; а[0]=0; а[1]=1; а[2]=2; цел *п=а+1; (*п)--; п++; *п; })); + + ПРОВЕРИТЬ(0, ({ цел а[3]; а[0]=0; а[1]=1; а[2]=2; цел *п=а+1; (*п++)--; а[0]; })); + ПРОВЕРИТЬ(0, ({ цел а[3]; а[0]=0; а[1]=1; а[2]=2; цел *п=а+1; (*п++)--; а[1]; })); + ПРОВЕРИТЬ(2, ({ цел а[3]; а[0]=0; а[1]=1; а[2]=2; цел *п=а+1; (*п++)--; а[2]; })); + ПРОВЕРИТЬ(2, ({ цел а[3]; а[0]=0; а[1]=1; а[2]=2; цел *п=а+1; (*п++)--; *п; })); + + ПРОВЕРИТЬ(0, !1); + ПРОВЕРИТЬ(0, !2); + ПРОВЕРИТЬ(1, !0); + ПРОВЕРИТЬ(1, !(символ)0); + ПРОВЕРИТЬ(0, !(длин)3); + ПРОВЕРИТЬ(4, размер(!(символ)0)); + ПРОВЕРИТЬ(4, размер(!(длин)0)); + + ПРОВЕРИТЬ(-1, ~0); + ПРОВЕРИТЬ(0, ~-1); + + ПРОВЕРИТЬ(5, 17%6); + ПРОВЕРИТЬ(5, ((длин)17)%6); + ПРОВЕРИТЬ(2, ({ цел и=10; и%=4; и; })); + ПРОВЕРИТЬ(2, ({ длин и=10; и%=4; и; })); + + ПРОВЕРИТЬ(0, 0&1); + ПРОВЕРИТЬ(1, 3&1); + ПРОВЕРИТЬ(3, 7&3); + ПРОВЕРИТЬ(10, -1&10); + + ПРОВЕРИТЬ(1, 0|1); + ПРОВЕРИТЬ(0b10011, 0b10000|0b00011); + + ПРОВЕРИТЬ(0, 0^0); + ПРОВЕРИТЬ(0, 0b1111^0b1111); + ПРОВЕРИТЬ(0b110100, 0b111000^0b001100); + + ПРОВЕРИТЬ(2, ({ цел и=6; и&=3; и; })); + ПРОВЕРИТЬ(7, ({ цел и=6; и|=3; и; })); + ПРОВЕРИТЬ(10, ({ цел и=15; и^=5; и; })); + + ПРОВЕРИТЬ(1, 1<<0); + ПРОВЕРИТЬ(8, 1<<3); + ПРОВЕРИТЬ(10, 5<<1); + ПРОВЕРИТЬ(2, 5>>1); + ПРОВЕРИТЬ(-1, -1>>1); + ПРОВЕРИТЬ(1, ({ цел и=1; и<<=0; и; })); + ПРОВЕРИТЬ(8, ({ цел и=1; и<<=3; и; })); + ПРОВЕРИТЬ(10, ({ цел и=5; и<<=1; и; })); + ПРОВЕРИТЬ(2, ({ цел и=5; и>>=1; и; })); + ПРОВЕРИТЬ(-1, -1); + ПРОВЕРИТЬ(-1, ({ цел и=-1; и; })); + ПРОВЕРИТЬ(-1, ({ цел и=-1; и>>=1; и; })); + + ПРОВЕРИТЬ(2, 0?1:2); + ПРОВЕРИТЬ(1, 1?1:2); + ПРОВЕРИТЬ(-1, 0?-2:-1); + ПРОВЕРИТЬ(-2, 1?-2:-1); + ПРОВЕРИТЬ(4, размер(0?1:2)); + ПРОВЕРИТЬ(8, размер(0?(длин)1:(длин)2)); + ПРОВЕРИТЬ(-1, 0?(длин)-2:-1); + ПРОВЕРИТЬ(-1, 0?-2:(длин)-1); + ПРОВЕРИТЬ(-2, 1?(длин)-2:-1); + ПРОВЕРИТЬ(-2, 1?-2:(длин)-1); + + 1 ? -2 : (пусто)-1; + + ПРОВЕРИТЬ(20, ({ цел х; цел *п=&х; п+20-п; })); + ПРОВЕРИТЬ(1, ({ цел х; цел *п=&х; п+20-п>0; })); + ПРОВЕРИТЬ(-20, ({ цел х; цел *п=&х; п-20-п; })); + ПРОВЕРИТЬ(1, ({ цел х; цел *п=&х; п-20-п<0; })); + + ПРОВЕРИТЬ(15, (символ *)0xffffffffffffffff - (символ *)0xfffffffffffffff0); + ПРОВЕРИТЬ(-15, (символ *)0xfffffffffffffff0 - (символ *)0xffffffffffffffff); + ПРОВЕРИТЬ(1, (пусто *)0xffffffffffffffff > (пусто *)0); + + ПРОВЕРИТЬ(3, 3?:5); + ПРОВЕРИТЬ(5, 0?:5); + ПРОВЕРИТЬ(4, ({ цел и = 3; ++и?:10; })); + + ПРОВЕРИТЬ(3, (длин двойн)3); + ПРОВЕРИТЬ(5, (длин двойн)3+2); + ПРОВЕРИТЬ(6, (длин двойн)3*2); + ПРОВЕРИТЬ(5, (длин двойн)3+2.0); + + printf("OK\n"); + возврат 0; +} \ No newline at end of file diff --git a/test/asm.c b/test/asm.c new file mode 100644 index 0000000..258a881 --- /dev/null +++ b/test/asm.c @@ -0,0 +1,24 @@ +#включить "тест.з" +#включить "stdvv.c" + +символ *асм_функция1(пусто) { + асм("mov $50, %rax\n\t" + "mov %rbp, %rsp\n\t" + "pop %rbp\n\t" + "ret"); +} + +символ *асм_функция2(пусто) { + асм inline volatile("mov $55, %rax\n\t" + "mov %rbp, %rsp\n\t" + "pop %rbp\n\t" + "ret"); +} + +цел главн() { + ПРОВЕРИТЬ(50, асм_функция1()); + ПРОВЕРИТЬ(55, асм_функция2()); + + printf("OK\n"); + возврат 0; +} \ No newline at end of file diff --git a/test/atomic.c b/test/atomic.c new file mode 100644 index 0000000..72c60cd --- /dev/null +++ b/test/atomic.c @@ -0,0 +1,64 @@ +#включить "тест.з" +#включить "stdvv.c" +#include +#include + +static int incr(_Atomic int *p) { + int oldval = *p; + int newval; + do { + newval = oldval + 1; + } while (!atomic_compare_exchange_weak(p, &oldval, newval)); + return newval; +} + +static int add1(void *arg) { + _Atomic int *x = arg; + for (int i = 0; i < 1000*1000; i++) + incr(x); + return 0; +} + +static int add2(void *arg) { + _Atomic int *x = arg; + for (int i = 0; i < 1000*1000; i++) + (*x)++; + return 0; +} + +static int add3(void *arg) { + _Atomic int *x = arg; + for (int i = 0; i < 1000*1000; i++) + *x += 5; + return 0; +} + +static int add_millions(void) { + _Atomic int x = 0; + + pthread_t thr1; + pthread_t thr2; + pthread_t thr3; + + pthread_create(&thr1, NULL, add1, &x); + pthread_create(&thr2, NULL, add2, &x); + pthread_create(&thr3, NULL, add3, &x); + + for (int i = 0; i < 1000*1000; i++) + x--; + + pthread_join(thr1, NULL); + pthread_join(thr2, NULL); + pthread_join(thr3, NULL); + return x; +} + +int main() { + ASSERT(6*1000*1000, add_millions()); + + ASSERT(3, ({ int x=3; atomic_exchange(&x, 5); })); + ASSERT(5, ({ int x=3; atomic_exchange(&x, 5); x; })); + + printf("OK\n"); + return 0; +} diff --git a/test/attribute.c b/test/attribute.c new file mode 100644 index 0000000..1c01597 --- /dev/null +++ b/test/attribute.c @@ -0,0 +1,40 @@ +#включить "тест.з" +#включить "stdvv.c" +#включить "rstdlib.h" + +цел главн() { + ПРОВЕРИТЬ(5, ({ структ { символ a; цел b; } __attribute__((packed)) x; размер(x); })); + ПРОВЕРИТЬ(0, offsetof(структ __attribute__((packed)) { символ a; цел b; }, a)); + ПРОВЕРИТЬ(1, offsetof(структ __attribute__((packed)) { символ a; цел b; }, b)); + + ПРОВЕРИТЬ(5, ({ структ __attribute__((packed)) { символ a; цел b; } x; размер(x); })); + ПРОВЕРИТЬ(0, offsetof(структ { символ a; цел b; } __attribute__((packed)), a)); + ПРОВЕРИТЬ(1, offsetof(структ { символ a; цел b; } __attribute__((packed)), b)); + + ПРОВЕРИТЬ(9, ({ типдеф структ { символ a; цел b[2]; } __attribute__((packed)) T; размер(T); })); + ПРОВЕРИТЬ(9, ({ типдеф структ __attribute__((packed)) { символ a; цел b[2]; } T; размер(T); })); + + ПРОВЕРИТЬ(1, offsetof(структ __attribute__((packed)) T { символ a; цел b[2]; }, b)); + ПРОВЕРИТЬ(1, _Выравнивание_типа(структ __attribute__((packed)) { символ a; цел b[2]; })); + + ПРОВЕРИТЬ(8, ({ структ __attribute__((aligned(8))) { цел a; } x; _Выравнивание_типа(x); })); + ПРОВЕРИТЬ(8, ({ структ { цел a; } __attribute__((aligned(8))) x; _Выравнивание_типа(x); })); + + ПРОВЕРИТЬ(8, ({ структ __attribute__((aligned(8), packed)) { символ a; цел b; } x; _Выравнивание_типа(x); })); + ПРОВЕРИТЬ(8, ({ структ { символ a; цел b; } __attribute__((aligned(8), packed)) x; _Выравнивание_типа(x); })); + ПРОВЕРИТЬ(1, offsetof(структ __attribute__((aligned(8), packed)) { символ a; цел b; }, b)); + ПРОВЕРИТЬ(1, offsetof(структ { символ a; цел b; } __attribute__((aligned(8), packed)), b)); + + ПРОВЕРИТЬ(8, ({ структ __attribute__((aligned(8))) __attribute__((packed)) { символ a; цел b; } x; _Выравнивание_типа(x); })); + ПРОВЕРИТЬ(8, ({ структ { символ a; цел b; } __attribute__((aligned(8))) __attribute__((packed)) x; _Выравнивание_типа(x); })); + ПРОВЕРИТЬ(1, offsetof(структ __attribute__((aligned(8))) __attribute__((packed)) { символ a; цел b; }, b)); + ПРОВЕРИТЬ(1, offsetof(структ { символ a; цел b; } __attribute__((aligned(8))) __attribute__((packed)), b)); + + ПРОВЕРИТЬ(8, ({ структ __attribute__((aligned(8))) { символ a; цел b; } __attribute__((packed)) x; _Выравнивание_типа(x); })); + ПРОВЕРИТЬ(1, offsetof(структ __attribute__((aligned(8))) { символ a; цел b; } __attribute__((packed)), b)); + + ПРОВЕРИТЬ(16, ({ структ __attribute__((aligned(8+8))) { символ a; цел b; } x; _Выравнивание_типа(x); })); + + printf("OK\n"); + возврат 0; +} \ No newline at end of file diff --git a/test/bitfield.c b/test/bitfield.c new file mode 100644 index 0000000..785f8f0 --- /dev/null +++ b/test/bitfield.c @@ -0,0 +1,58 @@ +#включить "тест.з" +#включить "stdvv.c" + +структ { + символ а; + цел б : 5; + цел в : 10; +} г45 = {1, 2, 3}, г46={}; + +цел главн() { + ПРОВЕРИТЬ(4, размер(структ {цел х:1; })); + ПРОВЕРИТЬ(8, размер(структ {длин х:1; })); + + структ бит1 { + корот а; + символ б; + цел в : 2; + цел г : 3; + цел д : 3; + }; + + ПРОВЕРИТЬ(4, размер(структ бит1)); + ПРОВЕРИТЬ(1, ({ структ бит1 х; х.а=1; х.б=2; х.в=3; х.г=4; х.д=5; х.а; })); + ПРОВЕРИТЬ(1, ({ структ бит1 х={1,2,3,4,5}; х.а; })); + ПРОВЕРИТЬ(2, ({ структ бит1 х={1,2,3,4,5}; х.б; })); + ПРОВЕРИТЬ(-1, ({ структ бит1 х={1,2,3,4,5}; х.в; })); + ПРОВЕРИТЬ(-4, ({ структ бит1 х={1,2,3,4,5}; х.г; })); + ПРОВЕРИТЬ(-3, ({ структ бит1 х={1,2,3,4,5}; х.д; })); + + ПРОВЕРИТЬ(1, г45.а); + ПРОВЕРИТЬ(2, г45.б); + ПРОВЕРИТЬ(3, г45.в); + + ПРОВЕРИТЬ(0, г46.а); + ПРОВЕРИТЬ(0, г46.б); + ПРОВЕРИТЬ(0, г46.в); + + типдеф структ { + цел а : 10; + цел б : 10; + цел в : 10; + } Т3; + + ПРОВЕРИТЬ(1, ({ Т3 х={1,2,3}; х.а++; })); + ПРОВЕРИТЬ(2, ({ Т3 х={1,2,3}; х.б++; })); + ПРОВЕРИТЬ(3, ({ Т3 х={1,2,3}; х.в++; })); + + ПРОВЕРИТЬ(2, ({ Т3 х={1,2,3}; ++х.а; })); + ПРОВЕРИТЬ(3, ({ Т3 х={1,2,3}; ++х.б; })); + ПРОВЕРИТЬ(4, ({ Т3 х={1,2,3}; ++х.в; })); + + ПРОВЕРИТЬ(4, размер(структ {цел а:3; цел в:1; цел в:5;})); + ПРОВЕРИТЬ(8, размер(структ {цел а:3; цел:0; цел в:5;})); + ПРОВЕРИТЬ(4, размер(структ {цел а:3; цел:0;})); + + printf("OK\n"); + возврат 0; +} \ No newline at end of file diff --git a/test/builtin.c b/test/builtin.c new file mode 100644 index 0000000..83bf0bf --- /dev/null +++ b/test/builtin.c @@ -0,0 +1,34 @@ +#включить "тест.з" +#включить "stdvv.c" + +цел главн() { + ПРОВЕРИТЬ(1, __builtin_types_compatible_p(цел, цел)); + ПРОВЕРИТЬ(1, __builtin_types_compatible_p(двойн, двойн)); + ПРОВЕРИТЬ(0, __builtin_types_compatible_p(цел, длин)); + ПРОВЕРИТЬ(0, __builtin_types_compatible_p(длин, плав)); + ПРОВЕРИТЬ(1, __builtin_types_compatible_p(цел *, цел *)); + ПРОВЕРИТЬ(0, __builtin_types_compatible_p(корот *, цел *)); + ПРОВЕРИТЬ(0, __builtin_types_compatible_p(цел **, цел *)); + ПРОВЕРИТЬ(1, __builtin_types_compatible_p(конст цел, цел)); + ПРОВЕРИТЬ(0, __builtin_types_compatible_p(беззнак, цел)); + ПРОВЕРИТЬ(1, __builtin_types_compatible_p(знак, цел)); + ПРОВЕРИТЬ(0, __builtin_types_compatible_p(структ {цел а;}, структ {цел а;})); + + ПРОВЕРИТЬ(1, __builtin_types_compatible_p(цел (*)(пусто), цел (*)(пусто))); + ПРОВЕРИТЬ(1, __builtin_types_compatible_p(пусто (*)(цел), пусто (*)(цел))); + ПРОВЕРИТЬ(1, __builtin_types_compatible_p(пусто (*)(цел, двойн), пусто (*)(цел, двойн))); + ПРОВЕРИТЬ(1, __builtin_types_compatible_p(цел (*)(плав, двойн), цел (*)(плав, двойн))); + ПРОВЕРИТЬ(0, __builtin_types_compatible_p(цел (*)(плав, двойн), цел)); + ПРОВЕРИТЬ(0, __builtin_types_compatible_p(цел (*)(плав, двойн), цел (*)(плав))); + ПРОВЕРИТЬ(0, __builtin_types_compatible_p(цел (*)(плав, двойн), цел (*)(плав, двойн, цел))); + ПРОВЕРИТЬ(1, __builtin_types_compatible_p(двойн (*)(...), двойн (*)(...))); + ПРОВЕРИТЬ(0, __builtin_types_compatible_p(двойн (*)(...), двойн (*)(пусто))); + + ПРОВЕРИТЬ(1, ({ типдеф структ {цел а;} Т; __builtin_types_compatible_p(Т, Т); })); + ПРОВЕРИТЬ(1, ({ типдеф структ {цел а;} Т; __builtin_types_compatible_p(Т, конст Т); })); + + ПРОВЕРИТЬ(1, ({ структ {цел а; цел б;} х; __builtin_types_compatible_p(типоф(х.а), типоф(х.б)); })); + + printf("OK\n"); + возврат 0; +} \ No newline at end of file diff --git a/test/cast.c b/test/cast.c new file mode 100644 index 0000000..56e08cb --- /dev/null +++ b/test/cast.c @@ -0,0 +1,60 @@ +#включить "тест.з" +#включить "stdvv.c" + +цел главн() { + ПРОВЕРИТЬ(131585, (цел)8590066177); + ПРОВЕРИТЬ(513, (корот)8590066177); + ПРОВЕРИТЬ(1, (символ)8590066177); + ПРОВЕРИТЬ(1, (длин)1); + ПРОВЕРИТЬ(0, (длин)&*(цел *)0); + ПРОВЕРИТЬ(513, ({ цел х=512; *(символ *)&х=1; х; })); + ПРОВЕРИТЬ(5, ({ цел х=5; длин у=(длин)&х; *(цел*)у; })); + + (пусто)1; + + ПРОВЕРИТЬ(-1, (символ)255); + ПРОВЕРИТЬ(-1, (знак символ)255); + ПРОВЕРИТЬ(255, (беззнак символ)255); + ПРОВЕРИТЬ(-1, (корот)65535); + ПРОВЕРИТЬ(65535, (беззнак корот)65535); + ПРОВЕРИТЬ(-1, (цел)0xffffffff); + ПРОВЕРИТЬ(0xffffffff, (беззнак)0xffffffff); + + ПРОВЕРИТЬ(1, -1<1); + ПРОВЕРИТЬ(0, -1<(беззнак)1); + ПРОВЕРИТЬ(254, (символ)127+(символ)127); + ПРОВЕРИТЬ(65534, (корот)32767+(корот)32767); + ПРОВЕРИТЬ(-1, -1>>1); + ПРОВЕРИТЬ(-1, (беззнак длин)-1); + ПРОВЕРИТЬ(2147483647, ((беззнак)-1)>>1); + ПРОВЕРИТЬ(-50, (-100)/2); + ПРОВЕРИТЬ(2147483598, ((беззнак)-100)/2); + ПРОВЕРИТЬ(9223372036854775758, ((беззнак длин)-100)/2); + ПРОВЕРИТЬ(0, ((длин)-1)/(беззнак)100); + ПРОВЕРИТЬ(-2, (-100)%7); + ПРОВЕРИТЬ(2, ((беззнак)-100)%7); + ПРОВЕРИТЬ(6, ((беззнак длин)-100)%9); + + ПРОВЕРИТЬ(65535, (цел)(беззнак корот)65535); + ПРОВЕРИТЬ(65535, ({ беззнак корот х = 65535; х; })); + ПРОВЕРИТЬ(65535, ({ беззнак корот х = 65535; (цел)х; })); + + ПРОВЕРИТЬ(-1, ({ типдеф корот Т; Т х = 65535; (цел)х; })); + ПРОВЕРИТЬ(65535, ({ типдеф беззнак корот Т; Т х = 65535; (цел)х; })); + + ПРОВЕРИТЬ(0, (_Bool)0.0); + ПРОВЕРИТЬ(1, (_Bool)0.1); + ПРОВЕРИТЬ(3, (символ)3.0); + ПРОВЕРИТЬ(1000, (корот)1000.3); + ПРОВЕРИТЬ(3, (цел)3.99); + ПРОВЕРИТЬ(2000000000000000, (длин)2e15); + ПРОВЕРИТЬ(3, (плав)3.5); + ПРОВЕРИТЬ(5, (двойн)(плав)5.5); + ПРОВЕРИТЬ(3, (плав)3); + ПРОВЕРИТЬ(3, (двойн)3); + ПРОВЕРИТЬ(3, (плав)3L); + ПРОВЕРИТЬ(3, (двойн)3L); + + printf("OK\n"); + возврат 0; +} \ No newline at end of file diff --git a/test/commonsym.c b/test/commonsym.c new file mode 100644 index 0000000..11dc534 --- /dev/null +++ b/test/commonsym.c @@ -0,0 +1,20 @@ +#включить "тест.з" +#включить "stdvv.c" + +цел x; +цел x = 5; +цел y = 7; +цел y; +цел common_ext1; +цел common_ext2 = 3; // <-- ДОБАВИТЬ ИНИЦИАЛИЗАЦИЮ! +стат цел common_local; + +цел главн() { + ПРОВЕРИТЬ(5, x); + ПРОВЕРИТЬ(7, y); + ПРОВЕРИТЬ(0, common_ext1); + ПРОВЕРИТЬ(3, common_ext2); + + печатьф("OK\n"); + возврат 0; +} \ No newline at end of file diff --git a/test/compat.c b/test/compat.c new file mode 100644 index 0000000..a9bbe2a --- /dev/null +++ b/test/compat.c @@ -0,0 +1,20 @@ +#включить "тест.з" +#включить "stdvv.c" + +_Noreturn noreturn_fn(int restrict x) { + exit(0); +} + +void funcy_type(int arg[restrict static 3]) {} + +int main() { + { volatile x; } + { int volatile x; } + { volatile int x; } + { volatile int volatile volatile x; } + { int volatile * volatile volatile x; } + { auto ** restrict __restrict __restrict__ const volatile *x; } + + printf("OK\n"); + return 0; +} diff --git a/test/complit.c b/test/complit.c new file mode 100644 index 0000000..9f14512 --- /dev/null +++ b/test/complit.c @@ -0,0 +1,34 @@ +#включить "тест.з" +#включить "stdvv.c" + +типдеф структ Дерево { + цел значение; + структ Дерево *левый; + структ Дерево *правый; +} Дерево; + +Дерево *дерево = &(Дерево){ + 1, + &(Дерево){ + 2, + &(Дерево){ 3, 0, 0 }, + &(Дерево){ 4, 0, 0 } + }, + 0 +}; + +цел главн() { + ПРОВЕРИТЬ(1, (цел){1}); + ПРОВЕРИТЬ(2, ((цел[]){0,1,2})[2]); + ПРОВЕРИТЬ('a', ((структ {символ а; цел б;}){'a', 3}).а); + ПРОВЕРИТЬ(3, ({ цел х=3; (цел){х}; })); + (цел){3} = 5; + + ПРОВЕРИТЬ(1, дерево->значение); + ПРОВЕРИТЬ(2, дерево->левый->значение); + ПРОВЕРИТЬ(3, дерево->левый->левый->значение); + ПРОВЕРИТЬ(4, дерево->левый->правый->значение); + + printf("OK\n"); + возврат 0; +} \ No newline at end of file diff --git a/test/const.c b/test/const.c new file mode 100644 index 0000000..52f505a --- /dev/null +++ b/test/const.c @@ -0,0 +1,15 @@ +#включить "тест.з" +#включить "stdvv.c" + +цел главн() { + { конст х; } + { цел конст х; } + { конст цел х; } + { конст цел конст конст х; } + ПРОВЕРИТЬ(5, ({ конст х = 5; х; })); + ПРОВЕРИТЬ(8, ({ конст х = 8; цел *конст у=&х; *у; })); + ПРОВЕРИТЬ(6, ({ конст х = 6; *(конст * конст)&х; })); + + printf("OK\n"); + возврат 0; +} \ No newline at end of file diff --git a/test/constexpr.c b/test/constexpr.c new file mode 100644 index 0000000..c2d92c8 --- /dev/null +++ b/test/constexpr.c @@ -0,0 +1,56 @@ +#включить "тест.з" +#включить "stdvv.c" + +плав г40 = 1.5; +двойн г41 = 0.0 ? 55 : (0, 1 + 1 * 5.0 / 2 * (двойн)2 * (цел)2.0); + +цел главн() { + ПРОВЕРИТЬ(10, ({ переч { десять=1+2+3+4 }; десять; })); + ПРОВЕРИТЬ(1, ({ цел и=0; выбор(3) { случай 5-2+0*3: и++; } и; })); + ПРОВЕРИТЬ(8, ({ цел х[1+1]; размер(х); })); + ПРОВЕРИТЬ(6, ({ символ х[8-2]; размер(х); })); + ПРОВЕРИТЬ(6, ({ символ х[2*3]; размер(х); })); + ПРОВЕРИТЬ(3, ({ символ х[12/4]; размер(х); })); + ПРОВЕРИТЬ(2, ({ символ х[12%10]; размер(х); })); + ПРОВЕРИТЬ(0b100, ({ символ х[0b110&0b101]; размер(х); })); + ПРОВЕРИТЬ(0b111, ({ символ х[0b110|0b101]; размер(х); })); + ПРОВЕРИТЬ(0b110, ({ символ х[0b111^0b001]; размер(х); })); + ПРОВЕРИТЬ(4, ({ символ х[1<<2]; размер(х); })); + ПРОВЕРИТЬ(2, ({ символ х[4>>1]; размер(х); })); + ПРОВЕРИТЬ(2, ({ символ х[(1==1)+1]; размер(х); })); + ПРОВЕРИТЬ(1, ({ символ х[(1!=1)+1]; размер(х); })); + ПРОВЕРИТЬ(1, ({ символ х[(1<1)+1]; размер(х); })); + ПРОВЕРИТЬ(2, ({ символ х[(1<=1)+1]; размер(х); })); + ПРОВЕРИТЬ(2, ({ символ х[1?2:3]; размер(х); })); + ПРОВЕРИТЬ(3, ({ символ х[0?2:3]; размер(х); })); + ПРОВЕРИТЬ(3, ({ символ х[(1,3)]; размер(х); })); + ПРОВЕРИТЬ(2, ({ символ х[!0+1]; размер(х); })); + ПРОВЕРИТЬ(1, ({ символ х[!1+1]; размер(х); })); + ПРОВЕРИТЬ(2, ({ символ х[~-3]; размер(х); })); + ПРОВЕРИТЬ(2, ({ символ х[(5||6)+1]; размер(х); })); + ПРОВЕРИТЬ(1, ({ символ х[(0||0)+1]; размер(х); })); + ПРОВЕРИТЬ(2, ({ символ х[(1&&1)+1]; размер(х); })); + ПРОВЕРИТЬ(1, ({ символ х[(1&&0)+1]; размер(х); })); + ПРОВЕРИТЬ(3, ({ символ х[(цел)3]; размер(х); })); + ПРОВЕРИТЬ(15, ({ символ х[(символ)0xffffff0f]; размер(х); })); + ПРОВЕРИТЬ(0x10f, ({ символ х[(корот)0xffff010f]; размер(х); })); + ПРОВЕРИТЬ(4, ({ символ х[(цел)0xfffffffffff+5]; размер(х); })); + ПРОВЕРИТЬ(8, ({ символ х[(цел*)0+2]; размер(х); })); + ПРОВЕРИТЬ(12, ({ символ х[(цел*)16-1]; размер(х); })); + ПРОВЕРИТЬ(3, ({ символ х[(цел*)16-(цел*)4]; размер(х); })); + + ПРОВЕРИТЬ(4, ({ символ х[(-1>>31)+5]; размер(х); })); + ПРОВЕРИТЬ(255, ({ символ х[(беззнак символ)0xffffffff]; размер(х); })); + ПРОВЕРИТЬ(0x800f, ({ символ х[(беззнак корот)0xffff800f]; размер(х); })); + ПРОВЕРИТЬ(1, ({ символ х[(беззнак цел)0xfffffffffff>>31]; размер(х); })); + ПРОВЕРИТЬ(1, ({ символ х[(длин)-1/((длин)1<<62)+1]; размер(х); })); + ПРОВЕРИТЬ(4, ({ символ х[(беззнак длин)-1/((длин)1<<62)+1]; размер(х); })); + ПРОВЕРИТЬ(1, ({ символ х[(беззнак)1<-1]; размер(х); })); + ПРОВЕРИТЬ(1, ({ символ х[(беззнак)1<=-1]; размер(х); })); + + ПРОВЕРИТЬ(1, г40==1.5); + ПРОВЕРИТЬ(1, г41==11); + + printf("OK\n"); + возврат 0; +} \ No newline at end of file diff --git a/test/control.c b/test/control.c new file mode 100644 index 0000000..8b2e657 --- /dev/null +++ b/test/control.c @@ -0,0 +1,101 @@ +#включить "тест.з" +#включить "stdvv.c" + +/* + * Это блочный комментарий. + */ + +цел главн() { + ПРОВЕРИТЬ(3, ({ цел х; если (0) х=2; иначе х=3; х; })); + ПРОВЕРИТЬ(3, ({ цел х; если (1-1) х=2; иначе х=3; х; })); + ПРОВЕРИТЬ(2, ({ цел х; если (1) х=2; иначе х=3; х; })); + ПРОВЕРИТЬ(2, ({ цел х; если (2-1) х=2; иначе х=3; х; })); + + ПРОВЕРИТЬ(55, ({ цел и=0; цел й=0; для (и=0; и<=10; и=и+1) й=и+й; й; })); + + ПРОВЕРИТЬ(10, ({ цел и=0; пока(и<10) и=и+1; и; })); + + ПРОВЕРИТЬ(3, ({ 1; {2;} 3; })); + ПРОВЕРИТЬ(5, ({ ;;; 5; })); + + ПРОВЕРИТЬ(10, ({ цел и=0; пока(и<10) и=и+1; и; })); + ПРОВЕРИТЬ(55, ({ цел и=0; цел й=0; пока(и<=10) {й=и+й; и=и+1;} й; })); + + ПРОВЕРИТЬ(3, (1,2,3)); + ПРОВЕРИТЬ(5, ({ цел и=2, й=3; (и=5,й)=6; и; })); + ПРОВЕРИТЬ(6, ({ цел и=2, й=3; (и=5,й)=6; й; })); + + ПРОВЕРИТЬ(55, ({ цел й=0; для (цел и=0; и<=10; и=и+1) й=й+и; й; })); + ПРОВЕРИТЬ(3, ({ цел и=3; цел й=0; для (цел и=0; и<=10; и=и+1) й=й+и; и; })); + + ПРОВЕРИТЬ(1, 0||1); + ПРОВЕРИТЬ(1, 0||(2-2)||5); + ПРОВЕРИТЬ(0, 0||0); + ПРОВЕРИТЬ(0, 0||(2-2)); + + ПРОВЕРИТЬ(0, 0&&1); + ПРОВЕРИТЬ(0, (2-2)&&5); + ПРОВЕРИТЬ(1, 1&&5); + + ПРОВЕРИТЬ(3, ({ цел и=0; перейти а; а: и++; б: и++; в: и++; и; })); + ПРОВЕРИТЬ(2, ({ цел и=0; перейти е; г: и++; д: и++; е: и++; ж: и++; и; })); + ПРОВЕРИТЬ(1, ({ цел и=0; перейти и; з: и++; к: и++; и: и++; и; })); + + ПРОВЕРИТЬ(1, ({ типдеф цел фу; перейти фу; фу:; 1; })); + + ПРОВЕРИТЬ(3, ({ цел и=0; для(;и<10;и++) { если (и == 3) прерви; } и; })); + ПРОВЕРИТЬ(4, ({ цел и=0; пока (1) { если (и++ == 3) прерви; } и; })); + ПРОВЕРИТЬ(3, ({ цел и=0; для(;и<10;и++) { для (;;) прерви; если (и == 3) прерви; } и; })); + ПРОВЕРИТЬ(4, ({ цел и=0; пока (1) { пока(1) прерви; если (и++ == 3) прерви; } и; })); + + ПРОВЕРИТЬ(10, ({ цел и=0; цел й=0; для (;и<10;и++) { если (и>5) продолжи; й++; } и; })); + ПРОВЕРИТЬ(6, ({ цел и=0; цел й=0; для (;и<10;и++) { если (и>5) продолжи; й++; } й; })); + ПРОВЕРИТЬ(10, ({ цел и=0; цел й=0; для(;!и;) { для (;й!=10;й++) продолжи; прерви; } й; })); + ПРОВЕРИТЬ(11, ({ цел и=0; цел й=0; пока (и++<10) { если (и>5) продолжи; й++; } и; })); + ПРОВЕРИТЬ(5, ({ цел и=0; цел й=0; пока (и++<10) { если (и>5) продолжи; й++; } й; })); + ПРОВЕРИТЬ(11, ({ цел и=0; цел й=0; пока(!и) { пока (й++!=10) продолжи; прерви; } й; })); + + ПРОВЕРИТЬ(5, ({ цел и=0; выбор(0) { случай 0:и=5;прерви; случай 1:и=6;прерви; случай 2:и=7;прерви; } и; })); + ПРОВЕРИТЬ(6, ({ цел и=0; выбор(1) { случай 0:и=5;прерви; случай 1:и=6;прерви; случай 2:и=7;прерви; } и; })); + ПРОВЕРИТЬ(7, ({ цел и=0; выбор(2) { случай 0:и=5;прерви; случай 1:и=6;прерви; случай 2:и=7;прерви; } и; })); + ПРОВЕРИТЬ(0, ({ цел и=0; выбор(3) { случай 0:и=5;прерви; случай 1:и=6;прерви; случай 2:и=7;прерви; } и; })); + ПРОВЕРИТЬ(5, ({ цел и=0; выбор(0) { случай 0:и=5;прерви; умолч:и=7; } и; })); + ПРОВЕРИТЬ(7, ({ цел и=0; выбор(1) { случай 0:и=5;прерви; умолч:и=7; } и; })); + ПРОВЕРИТЬ(2, ({ цел и=0; выбор(1) { случай 0: 0; случай 1: 0; случай 2: 0; и=2; } и; })); + ПРОВЕРИТЬ(0, ({ цел и=0; выбор(3) { случай 0: 0; случай 1: 0; случай 2: 0; и=2; } и; })); + + ПРОВЕРИТЬ(3, ({ цел и=0; выбор(-1) { случай 0xffffffff: и=3; прерви; } и; })); + + ПРОВЕРИТЬ(7, ({ цел и=0; цел й=0; делай { й++; } пока (и++ < 6); й; })); + ПРОВЕРИТЬ(4, ({ цел и=0; цел й=0; цел к=0; делай { если (++й > 3) прерви; продолжи; к++; } пока (1); й; })); + + ПРОВЕРИТЬ(0, 0.0 && 0.0); + ПРОВЕРИТЬ(0, 0.0 && 0.1); + ПРОВЕРИТЬ(0, 0.3 && 0.0); + ПРОВЕРИТЬ(1, 0.3 && 0.5); + ПРОВЕРИТЬ(0, 0.0 || 0.0); + ПРОВЕРИТЬ(1, 0.0 || 0.1); + ПРОВЕРИТЬ(1, 0.3 || 0.0); + ПРОВЕРИТЬ(1, 0.3 || 0.5); + ПРОВЕРИТЬ(5, ({ цел х; если (0.0) х=3; иначе х=5; х; })); + ПРОВЕРИТЬ(3, ({ цел х; если (0.1) х=3; иначе х=5; х; })); + ПРОВЕРИТЬ(5, ({ цел х=5; если (0.0) х=3; х; })); + ПРОВЕРИТЬ(3, ({ цел х=5; если (0.1) х=3; х; })); + ПРОВЕРИТЬ(10, ({ двойн и=10.0; цел й=0; для (; и; и--, й++); й; })); + ПРОВЕРИТЬ(10, ({ двойн и=10.0; цел й=0; делай й++; пока(--и); й; })); + + ПРОВЕРИТЬ(2, ({ цел и=0; выбор(7) { случай 0 ... 5: и=1; прерви; случай 6 ... 20: и=2; прерви; } и; })); + ПРОВЕРИТЬ(1, ({ цел и=0; выбор(7) { случай 0 ... 7: и=1; прерви; случай 8 ... 10: и=2; прерви; } и; })); + ПРОВЕРИТЬ(1, ({ цел и=0; выбор(7) { случай 0: и=1; прерви; случай 7 ... 7: и=1; прерви; } и; })); + + ПРОВЕРИТЬ(3, ({ пусто *п = &&в11; цел и=0; перейти *п; в11:и++; в12:и++; в13:и++; и; })); + ПРОВЕРИТЬ(2, ({ пусто *п = &&в22; цел и=0; перейти *п; в21:и++; в22:и++; в23:и++; и; })); + ПРОВЕРИТЬ(1, ({ пусто *п = &&в33; цел и=0; перейти *п; в31:и++; в32:и++; в33:и++; и; })); + + ПРОВЕРИТЬ(3, ({ стат пусто *п[]={&&в41,&&в42,&&в43}; цел и=0; перейти *п[0]; в41:и++; в42:и++; в43:и++; и; })); + ПРОВЕРИТЬ(2, ({ стат пусто *п[]={&&в52,&&в52,&&в53}; цел и=0; перейти *п[1]; в51:и++; в52:и++; в53:и++; и; })); + ПРОВЕРИТЬ(1, ({ стат пусто *п[]={&&в62,&&в62,&&в63}; цел и=0; перейти *п[2]; в61:и++; в62:и++; в63:и++; и; })); + + printf("OK\n"); + возврат 0; +} \ No newline at end of file diff --git a/test/decl.c b/test/decl.c new file mode 100644 index 0000000..4066bbd --- /dev/null +++ b/test/decl.c @@ -0,0 +1,23 @@ +#включить "тест.з" +#включить "stdvv.c" + +цел главн() { + ПРОВЕРИТЬ(1, ({ символ х; размер(х); })); + ПРОВЕРИТЬ(2, ({ корот цел х; размер(х); })); + ПРОВЕРИТЬ(2, ({ цел корот х; размер(х); })); + ПРОВЕРИТЬ(4, ({ цел х; размер(х); })); + ПРОВЕРИТЬ(8, ({ длин цел х; размер(х); })); + ПРОВЕРИТЬ(8, ({ цел длин х; размер(х); })); + + ПРОВЕРИТЬ(8, ({ длин длин х; размер(х); })); + + ПРОВЕРИТЬ(0, ({ _Bool х=0; х; })); + ПРОВЕРИТЬ(1, ({ _Bool х=1; х; })); + ПРОВЕРИТЬ(1, ({ _Bool х=2; х; })); + ПРОВЕРИТЬ(1, (_Bool)1); + ПРОВЕРИТЬ(1, (_Bool)2); + ПРОВЕРИТЬ(0, (_Bool)(символ)256); + + printf("OK\n"); + возврат 0; +} \ No newline at end of file diff --git a/test/driver.py b/test/driver.py new file mode 100755 index 0000000..1661b0f --- /dev/null +++ b/test/driver.py @@ -0,0 +1,987 @@ +#!/usr/bin/env python3 +import os +import sys +import tempfile +import shutil +import subprocess +import re +import atexit +import signal +import traceback +import glob + +# Цвета для терминала +GREEN = '\033[92m' +RED = '\033[91m' +YELLOW = '\033[93m' +BLUE = '\033[94m' +RESET = '\033[0m' +BOLD = '\033[1m' + +# Глобальные переменные для состояния тестов +_TESTS_PASSED = 0 +_TESTS_FAILED = 0 +_TESTS_TOTAL = 0 +_TEST_RESULTS: list = [] + +def main(): + # Проверяем, что передан путь к исполняемому файлу rusi + if len(sys.argv) < 2: + print("Использование: {} <путь-к-rusi>".format(sys.argv[0]), file=sys.stderr) + sys.exit(1) + + rusi = sys.argv[1] # Путь к тестируемому компилятору + + # Создаем временную папку для тестов + tmp = tempfile.mkdtemp(prefix='rusi-test-') + + def cleanup(): + # Удаляем временную папку после завершения тестов + shutil.rmtree(tmp, ignore_errors=True) + + atexit.register(cleanup) # Регистрируем функцию очистки + + # Обработчики сигналов для корректного завершения + signal.signal(signal.SIGINT, lambda s, f: завершить_тесты()) + signal.signal(signal.SIGTERM, lambda s, f: завершить_тесты()) + if hasattr(signal, 'SIGHUP'): + signal.signal(signal.SIGHUP, lambda s, f: завершить_тесты()) + + # Создаем пустой C-файл для тестирования + tmp_c = os.path.join(tmp, 'tmp.c') + with open(tmp_c, 'w') as f: + f.write('') + + # Запускаем все тесты + try: + запустить_тесты(rusi, tmp, tmp_c) + except (AssertionError, subprocess.CalledProcessError, Exception) as e: + # При падении любого теста выводим отчет и выходим с ошибкой + вывести_отчет() + print(f"\n{RED}❌ ТЕСТЫ НЕ ПРОЙДЕНЫ: {str(e)}{RESET}") + sys.exit(1) + + # Если все тесты прошли успешно + вывести_отчет() + if _TESTS_FAILED == 0: + print(f"\n{GREEN}{BOLD}🎉 ВСЕ ТЕСТЫ ПРОЙДЕНЫ УСПЕШНО!{RESET}") + sys.exit(0) + else: + print(f"\n{RED}{BOLD}❌ НЕ ПРОЙДЕНО {_TESTS_FAILED} ТЕСТОВ ИЗ {_TESTS_TOTAL}{RESET}") + sys.exit(1) + +def завершить_тесты(): + """Принудительное завершение с выводом отчета""" + вывести_отчет() + sys.exit(1) + +def запустить_тесты(rusi: str, tmp: str, tmp_c: str): + """Запускает все тесты""" + global _TESTS_TOTAL + + # Список всех тестов с их описаниями + тесты = [ + ("-o", lambda: тест_o(rusi, tmp, tmp_c)), + ("--help", lambda: тест_help(rusi)), + ("-S", lambda: тест_S(rusi)), + ("имя выходного файла по умолчанию", lambda: тест_имя_выходного_файла(rusi, tmp)), + ("несколько входных файлов", lambda: тест_несколько_входных(rusi, tmp)), + ("линковщик", lambda: тест_линковщик(rusi, tmp)), + ("a.out", lambda: тест_a_out(rusi, tmp)), + ("-E", lambda: тест_E(rusi, tmp)), + ("-E и -o", lambda: тест_E_o(rusi, tmp)), + ("-I", lambda: тест_I(rusi, tmp)), + ("-D", lambda: тест_D(rusi)), + ("-D со значением", lambda: тест_D_значение(rusi)), + ("-U", lambda: тест_U(rusi)), + ("игнорируемые опции", lambda: тест_игнорируемые(rusi, tmp_c)), + ("маркер BOM", lambda: тест_BOM(rusi)), + ("inline", lambda: тест_inline(rusi, tmp)), + ("-idirafter", lambda: тест_idirafter(rusi, tmp)), + ("-fcommon (по умолчанию)", lambda: тест_fcommon_по_умолчанию(rusi)), + ("-fcommon", lambda: тест_fcommon(rusi)), + ("-fno-common", lambda: тест_fno_common(rusi)), + ("-include", lambda: тест_include(rusi, tmp)), + ("-include stdio.h", lambda: тест_include_stdio(rusi)), + ("-xc", lambda: тест_xc(rusi, tmp)), + ("-x assembler", lambda: тест_x_assembler(rusi, tmp)), + ("-x none", lambda: тест_x_none(rusi, tmp)), + ("-E без -xc", lambda: тест_E_без_xc(rusi)), + (".a", lambda: тест_a_архив(rusi, tmp)), + (".so", lambda: тест_so_библиотека(rusi, tmp)), + ("хеш-таблица", lambda: тест_хеш_таблица(rusi)), + ("-M", lambda: тест_M(rusi, tmp)), + ("-MF", lambda: тест_MF(rusi, tmp)), + ("-MP", lambda: тест_MP(rusi, tmp)), + ("-MT", lambda: тест_MT(rusi, tmp)), + ("-MT несколько целей", lambda: тест_MT_несколько(rusi, tmp)), + ("-MD", lambda: тест_MD(rusi, tmp)), + ("-MD с -MF", lambda: тест_MD_MF(rusi, tmp)), + ("-fPIC", lambda: тест_fPIC(rusi, tmp)), + ("#include_next", lambda: тест_include_next(rusi, tmp)), + ("-static", lambda: тест_static(rusi, tmp)), + ("-shared", lambda: тест_shared(rusi, tmp)), + ("-L", lambda: тест_L(rusi, tmp)), + ("-Wl,", lambda: тест_Wl(rusi, tmp)), + ("-Xlinker", lambda: тест_Xlinker(rusi, tmp)), + # ============================================================ + # НОВЫЕ ТЕСТЫ: Сборка и запуск .c файлов из папки test/ + # ============================================================ + ("Сборка тестов .c", lambda: тест_сборка_c_файлов(rusi)), + ] + + _TESTS_TOTAL = len(тесты) + + # Вычисляем максимальную длину названия теста для выравнивания + макс_длина = 0 + for название, _ in тесты: + макс_длина = max(макс_длина, len(название)) + + for название, тест_функция in тесты: + выполнить_тест(название, тест_функция, макс_длина) + +def выполнить_тест(название: str, тест_функция, макс_длина: int): + """Выполняет отдельный тест - при ошибке сразу прерывает выполнение""" + global _TESTS_PASSED, _TESTS_FAILED + + try: + тест_функция() + _TESTS_PASSED += 1 + _TEST_RESULTS.append((название, True, None)) + print(f"{GREEN}✓{RESET} {название:<{макс_длина}} ... {GREEN}ПРОЙДЕНО{RESET}") + except AssertionError as e: + _TESTS_FAILED += 1 + ошибка = str(e) if str(e) else "проверка не пройдена" + _TEST_RESULTS.append((название, False, ошибка)) + print(f"{RED}✗{RESET} {название:<{макс_длина}} ... {RED}НЕ ПРОЙДЕНО{RESET}: {ошибка}") + raise # Прерываем выполнение при падении теста + except subprocess.CalledProcessError as e: + _TESTS_FAILED += 1 + ошибка = f"команда завершилась с кодом {e.returncode}" + _TEST_RESULTS.append((название, False, ошибка)) + print(f"{RED}✗{RESET} {название:<{макс_длина}} ... {RED}НЕ ПРОЙДЕНО{RESET}: {ошибка}") + raise # Прерываем выполнение при падении теста + except Exception as e: + _TESTS_FAILED += 1 + ошибка = f"{type(e).__name__}: {str(e)}" + _TEST_RESULTS.append((название, False, ошибка)) + print(f"{RED}✗{RESET} {название:<{макс_длина}} ... {RED}НЕ ПРОЙДЕНО{RESET}: {ошибка}") + traceback.print_exc() + raise # Прерываем выполнение при падении теста + +def вывести_отчет(): + """Выводит итоговый отчет о тестировании""" + global _TESTS_PASSED, _TESTS_FAILED, _TESTS_TOTAL + + print("\n" + "=" * 60) + print(f"{BOLD}ИТОГОВЫЙ ОТЧЕТ О ТЕСТИРОВАНИИ{RESET}") + print("=" * 60) + + if _TEST_RESULTS: + print("\nПОДРОБНЫЕ РЕЗУЛЬТАТЫ:") + for название, успешно, ошибка in _TEST_RESULTS: + if успешно: + статус = f"{GREEN}✓ ПРОЙДЕНО{RESET}" + else: + статус = f"{RED}✗ НЕ ПРОЙДЕНО{RESET}" + строка = f" {статус} {название}" + if not успешно and ошибка: + строка += f" ({RED}ошибка: {ошибка}{RESET})" + print(строка) + + print("\n" + "-" * 60) + print(f"ВСЕГО ТЕСТОВ: {_TESTS_TOTAL}") + print(f"ПРОЙДЕНО: {GREEN}{_TESTS_PASSED}{RESET}") + print(f"НЕ ПРОЙДЕНО: {RED}{_TESTS_FAILED}{RESET}") + print("=" * 60) + +def проверка_существования_файла(путь: str, сообщение: str = ""): + """Вспомогательная функция для проверки существования файла""" + if not os.path.exists(путь): + raise AssertionError(f"файл {путь} не создан {сообщение}") + +def проверка_содержимого(текст: str, паттерн, сообщение: str = ""): + """Вспомогательная функция для проверки содержимого""" + if isinstance(паттерн, str): + if паттерн not in текст: + raise AssertionError(f"'{паттерн}' не найден в выводе {сообщение}") + else: # регулярное выражение + if not паттерн.search(текст): + raise AssertionError(f"паттерн не найден в выводе {сообщение}") + +def run_rusi(rusi: str, *args, **kwargs): + """Запускает rusi с заданными аргументами""" + cmd = [rusi] + list(args) + if 'input' in kwargs: + input_text = kwargs.pop('input') + kwargs['input'] = input_text + if 'capture_output' not in kwargs and 'stdout' not in kwargs and 'stderr' not in kwargs: + kwargs['capture_output'] = True + kwargs['text'] = True + return subprocess.run(cmd, **kwargs) + +# ============================================================================ +# НОВАЯ ФУНКЦИЯ: Сборка и запуск .c файлов из папки test/ +# ============================================================================ + +def тест_сборка_c_файлов(rusi: str): + """Собирает и запускает все .c файлы из папки test/, проверяя вывод на OK""" + + # Находим все .c файлы в папке test/ + test_dir = "test" + if not os.path.exists(test_dir): + raise AssertionError(f"Папка {test_dir} не найдена") + + c_files = glob.glob(os.path.join(test_dir, "*.c")) + + if not c_files: + raise AssertionError("В папке test/ нет .c файлов") + + # Файлы, которые нужно исключить (требуют специальной линковки или не являются тестами) + исключения = [] # atomic.c теперь работает + + # Файлы, которые требуют stdvv.c + stdvv = os.path.join(test_dir, "stdvv.c") + if not os.path.exists(stdvv): + stdvv = None + + # Файлы, которые требуют test.c (если есть) + test_c = os.path.join(test_dir, "test.c") + if os.path.exists(test_c): + runtime = test_c + elif stdvv: + runtime = stdvv + else: + runtime = None + + # Счетчики для отчета + всего_тестов = 0 + пройдено_тестов = 0 + пропущено_тестов = 0 + + # Вычисляем максимальную длину имени файла для выравнивания + макс_длина = 0 + for c_file in c_files: + имя = os.path.basename(c_file) + if имя not in ["stdvv.c", "test.c", "тест.з"]: + макс_длина = max(макс_длина, len(имя)) + + print(f"\n{BOLD}=== ЗАПУСК СЕМАНТИЧЕСКИХ ТЕСТОВ ==={RESET}") + print() + + # Проходим по всем .c файлам + for c_file in sorted(c_files): + имя_файла = os.path.basename(c_file) + имя_без_расш = os.path.splitext(имя_файла)[0] + + # Пропускаем исключения + if имя_файла in исключения: + пропущено_тестов += 1 + print(f"{YELLOW}⚠{RESET} {имя_файла:<{макс_длина}} ... {YELLOW}ПРОПУЩЕН{RESET} (требует специальной линковки)") + continue + + # Пропускаем runtime файлы (не тесты) + if имя_файла in ["stdvv.c", "test.c", "тест.з"]: + continue + + всего_тестов += 1 + + # Определяем, нужен ли runtime для этого теста + with open(c_file, 'r', encoding='utf-8', errors='ignore') as f: + содержимое = f.read() + нужен_runtime = 'ASSERT' in содержимое or 'ПРОВЕРИТЬ' in содержимое or 'printf' not in содержимое + + # Собираем команду компиляции + выходной_файл = os.path.join(test_dir, f"test_{имя_без_расш}") + аргументы = [rusi, "-o", выходной_файл, c_file] + + # Добавляем runtime если нужен + if нужен_runtime and runtime and os.path.exists(runtime): + аргументы.append(runtime) + + # Собираем + try: + subprocess.run(аргументы, capture_output=True, text=True, check=True) + except subprocess.CalledProcessError as e: + raise AssertionError(f"Ошибка компиляции {имя_файла}: {e.stderr}") + + # Проверяем, что исполняемый файл создан + if not os.path.exists(выходной_файл): + raise AssertionError(f"Исполняемый файл не создан для {имя_файла}") + + # Запускаем и проверяем вывод + try: + результат = subprocess.run([выходной_файл], capture_output=True, text=True, timeout=5) + except subprocess.TimeoutExpired: + raise AssertionError(f"Таймаут при выполнении {имя_файла}") + + # Проверяем, что программа завершилась успешно + if результат.returncode != 0: + raise AssertionError(f"{имя_файла} завершилась с кодом {результат.returncode}: {результат.stderr}") + + # Проверяем, что вывод содержит OK + if "OK" not in результат.stdout: + raise AssertionError(f"{имя_файла} не вывела OK. Вывод: {результат.stdout}") + + # Тест пройден! + пройдено_тестов += 1 + print(f"{GREEN}✓{RESET} {имя_файла:<{макс_длина}} ... {GREEN}ПРОЙДЕНО{RESET}") + + # Удаляем исполняемый файл + try: + os.unlink(выходной_файл) + except: + pass + + # Выводим итоговый отчет по семантическим тестам + print() + print(f"{BOLD}=== ИТОГ СЕМАНТИЧЕСКИХ ТЕСТОВ ==={RESET}") + print(f" Всего тестов: {всего_тестов}") + print(f" {GREEN}Пройдено:{RESET} {пройдено_тестов}") + if пропущено_тестов > 0: + print(f" {YELLOW}Пропущено:{RESET} {пропущено_тестов}") + print() + +# ============================================================================ +# ОСТАЛЬНЫЕ ТЕСТЫ (без изменений) +# ============================================================================ + +def тест_o(rusi: str, tmp: str, tmp_c: str): + """Тест опции -o""" + out_file = os.path.join(tmp, 'out') + if os.path.exists(out_file): + os.unlink(out_file) + subprocess.run([rusi, '-c', '-o', out_file, tmp_c], check=True) + проверка_существования_файла(out_file) + +def тест_help(rusi: str): + """Тест --help""" + result = subprocess.run([rusi, '--help'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True) + if not result.stdout.strip(): + raise AssertionError("пустой вывод справки") + +def тест_S(rusi: str): + """Тест -S""" + result = subprocess.run([rusi, '-S', '-o-', '-xc', '-'], + input='int main() {}', capture_output=True, text=True) + проверка_содержимого(result.stdout, 'main:', "метка main не найдена в ассемблерном коде") + +def тест_имя_выходного_файла(rusi: str, tmp: str): + """Тест имени выходного файла по умолчанию""" + out_c = os.path.join(tmp, 'out.c') + with open(out_c, 'w') as f: + f.write('int main() {}') + + out_o = os.path.join(tmp, 'out.o') + out_s = os.path.join(tmp, 'out.s') + for f in [out_o, out_s]: + if os.path.exists(f): + os.unlink(f) + + old_pwd = os.getcwd() + os.chdir(tmp) + try: + subprocess.run([os.path.join(old_pwd, rusi), '-c', 'out.c'], check=True) + проверка_существования_файла(out_o) + finally: + os.chdir(old_pwd) + + os.chdir(tmp) + try: + subprocess.run([os.path.join(old_pwd, rusi), '-c', '-S', 'out.c'], check=True) + проверка_существования_файла(out_s) + finally: + os.chdir(old_pwd) + +def тест_несколько_входных(rusi: str, tmp: str): + """Тест с несколькими входными файлами""" + foo_o = os.path.join(tmp, 'foo.o') + bar_o = os.path.join(tmp, 'bar.o') + for f in [foo_o, bar_o]: + if os.path.exists(f): + os.unlink(f) + + foo_c = os.path.join(tmp, 'foo.c') + bar_c = os.path.join(tmp, 'bar.c') + with open(foo_c, 'w') as f: + f.write('int x;') + with open(bar_c, 'w') as f: + f.write('int y;') + + old_pwd = os.getcwd() + os.chdir(tmp) + try: + subprocess.run([os.path.join(old_pwd, rusi), '-c', foo_c, bar_c], check=True) + проверка_существования_файла(foo_o) + проверка_существования_файла(bar_o) + finally: + os.chdir(old_pwd) + + foo_s = os.path.join(tmp, 'foo.s') + bar_s = os.path.join(tmp, 'bar.s') + for f in [foo_s, bar_s]: + if os.path.exists(f): + os.unlink(f) + + os.chdir(tmp) + try: + subprocess.run([os.path.join(old_pwd, rusi), '-c', '-S', foo_c, bar_c], check=True) + проверка_существования_файла(foo_s) + проверка_существования_файла(bar_s) + finally: + os.chdir(old_pwd) + +def тест_линковщик(rusi: str, tmp: str): + """Тест линковщика""" + foo_exe = os.path.join(tmp, 'foo') + if os.path.exists(foo_exe): + os.unlink(foo_exe) + + subprocess.run([rusi, '-o', foo_exe, '-xc', '-xc', '-'], + input='int main() { return 0; }', text=True, check=True) + result = subprocess.run([foo_exe]) + if result.returncode != 0: + raise AssertionError(f"программа вернула {result.returncode}, ожидалось 0") + + if os.path.exists(foo_exe): + os.unlink(foo_exe) + + foo_c = os.path.join(tmp, 'foo.c') + bar_c = os.path.join(tmp, 'bar.c') + with open(foo_c, 'w') as f: + f.write('int bar(); int main() { return bar(); }') + with open(bar_c, 'w') as f: + f.write('int bar() { return 42; }') + + subprocess.run([rusi, '-o', foo_exe, foo_c, bar_c], check=True) + result = subprocess.run([foo_exe]) + if result.returncode != 42: + raise AssertionError(f"программа вернула {result.returncode}, ожидалось 42") + +def тест_a_out(rusi: str, tmp: str): + """Тест a.out""" + a_out = os.path.join(tmp, 'a.out') + if os.path.exists(a_out): + os.unlink(a_out) + + foo_c = os.path.join(tmp, 'foo.c') + with open(foo_c, 'w') as f: + f.write('int main() {}') + + old_pwd = os.getcwd() + os.chdir(tmp) + try: + subprocess.run([os.path.join(old_pwd, rusi), 'foo.c'], check=True) + проверка_существования_файла(a_out) + finally: + os.chdir(old_pwd) + +def тест_E(rusi: str, tmp: str): + """Тест -E""" + out_file = os.path.join(tmp, 'out') + with open(out_file, 'w') as f: + f.write('foo') + + result = subprocess.run([rusi, '-E', '-xc', '-'], + input=f'#include "{out_file}"\n', capture_output=True, text=True) + проверка_содержимого(result.stdout, 'foo', "препроцессор не вставил содержимое файла") + +def тест_E_o(rusi: str, tmp: str): + """Тест -E и -o""" + out1 = os.path.join(tmp, 'out1') + with open(out1, 'w') as f: + f.write('foo') + + out2 = os.path.join(tmp, 'out2') + subprocess.run([rusi, '-E', '-o', out2, '-xc', '-'], + input=f'#include "{out1}"\n', text=True, check=True) + with open(out2) as f: + content = f.read() + проверка_содержимого(content, 'foo', "результат препроцессинга не записан в файл") + +def тест_I(rusi: str, tmp: str): + """Тест -I""" + dir_path = os.path.join(tmp, 'dir') + os.makedirs(dir_path, exist_ok=True) + i_test = os.path.join(dir_path, 'i-option-test') + with open(i_test, 'w') as f: + f.write('foo') + + result = subprocess.run([rusi, f'-I{dir_path}', '-E', '-xc', '-'], + input='#include "i-option-test"\n', capture_output=True, text=True) + проверка_содержимого(result.stdout, 'foo', "файл не найден в указанной папке") + +def тест_D(rusi: str): + """Тест -D""" + result = subprocess.run([rusi, '-Dfoo', '-E', '-xc', '-'], + input='foo', capture_output=True, text=True) + проверка_содержимого(result.stdout, '1', "макрос без значения не подставлен как 1") + +def тест_D_значение(rusi: str): + """Тест -D со значением""" + result = subprocess.run([rusi, '-Dfoo=bar', '-E', '-xc', '-'], + input='foo', capture_output=True, text=True) + проверка_содержимого(result.stdout, 'bar', "макрос не подставлен с указанным значением") + +def тест_U(rusi: str): + """Тест -U""" + result = subprocess.run([rusi, '-Dfoo=bar', '-Ufoo', '-E', '-xc', '-'], + input='foo', capture_output=True, text=True) + проверка_содержимого(result.stdout, 'foo', "макрос не остался неопределенным") + +def тест_игнорируемые(rusi: str, tmp_c: str): + """Тест игнорируемых опций""" + subprocess.run([rusi, '-c', '-O', '-Wall', '-g', '-std=c11', '-ffreestanding', + '-fno-builtin', '-fno-omit-frame-pointer', '-fno-stack-protector', + '-fno-strict-aliasing', '-m64', '-mno-red-zone', '-w', + '-o', '/dev/null', tmp_c], check=True) + +def тест_BOM(rusi: str): + """Тест маркера BOM""" + result = subprocess.run([rusi, '-E', '-o-', '-xc', '-'], + input='\ufeffxyz\n', capture_output=True, text=True) + проверка_содержимого(result.stdout, re.compile(r'^xyz', re.MULTILINE), "BOM не удален") + +def тест_inline(rusi: str, tmp: str): + """Тесты inline-функций""" + inline1 = os.path.join(tmp, 'inline1.c') + inline2 = os.path.join(tmp, 'inline2.c') + inline3 = os.path.join(tmp, 'inline3.c') + + # Две одинаковые inline-функции в разных файлах + with open(inline1, 'w') as f: + f.write('inline void foo() {}') + with open(inline2, 'w') as f: + f.write('inline void foo() {}') + with open(inline3, 'w') as f: + f.write('int main() { return 0; }') + + subprocess.run([rusi, '-o', '/dev/null', inline1, inline2, inline3], check=True) + + # extern inline + with open(inline1, 'w') as f: + f.write('extern inline void foo() {}') + with open(inline2, 'w') as f: + f.write('int foo(); int main() { foo(); }') + + subprocess.run([rusi, '-o', '/dev/null', inline1, inline2], check=True) + + # static inline не используется + result = subprocess.run([rusi, '-o-', '-S', '-xc', '-'], + input='static inline void f1() {}', capture_output=True, text=True) + if 'f1:' in result.stdout: + raise AssertionError("static inline функция сгенерировала код без использования") + + # static inline используется + result = subprocess.run([rusi, '-o-', '-S', '-xc', '-'], + input='static inline void f1() {} void foo() { f1(); }', + capture_output=True, text=True) + проверка_содержимого(result.stdout, 'f1:', "static inline функция не сгенерировала код при использовании") + + # Дополнительные проверки inline + test_input = 'static inline void f1() {} static inline void f2() { f1(); } void foo() { f1(); }' + result = subprocess.run([rusi, '-o-', '-S', '-xc', '-'], + input=test_input, capture_output=True, text=True) + проверка_содержимого(result.stdout, 'f1:', "f1 должна быть сгенерирована") + if 'f2:' in result.stdout: + raise AssertionError("f2 сгенерирована, хотя не используется") + +def тест_idirafter(rusi: str, tmp: str): + """Тест -idirafter""" + dir1 = os.path.join(tmp, 'dir1') + dir2 = os.path.join(tmp, 'dir2') + os.makedirs(dir1, exist_ok=True) + os.makedirs(dir2, exist_ok=True) + + idirafter1 = os.path.join(dir1, 'idirafter') + idirafter2 = os.path.join(dir2, 'idirafter') + with open(idirafter1, 'w') as f: + f.write('foo') + with open(idirafter2, 'w') as f: + f.write('bar') + + # Обычная -I ищет в первой папке + result = subprocess.run([rusi, f'-I{dir1}', f'-I{dir2}', '-E', '-xc', '-'], + input='#include "idirafter"\n', capture_output=True, text=True) + проверка_содержимого(result.stdout, 'foo', "-I не нашла файл в первой папке") + + # -idirafter ищет после -I + result = subprocess.run([rusi, f'-idirafter', dir1, f'-I{dir2}', '-E', '-xc', '-'], + input='#include "idirafter"\n', capture_output=True, text=True) + проверка_содержимого(result.stdout, 'bar', "-idirafter не нашла файл после -I") + +def тест_fcommon_по_умолчанию(rusi: str): + """Тест -fcommon (по умолчанию)""" + result = subprocess.run([rusi, '-S', '-o-', '-xc', '-'], + input='int foo;', capture_output=True, text=True) + проверка_содержимого(result.stdout, '.comm foo', "директива .comm не сгенерирована") + +def тест_fcommon(rusi: str): + """Тест -fcommon""" + result = subprocess.run([rusi, '-fcommon', '-S', '-o-', '-xc', '-'], + input='int foo;', capture_output=True, text=True) + проверка_содержимого(result.stdout, '.comm foo', "директива .comm не сгенерирована с -fcommon") + +def тест_fno_common(rusi: str): + """Тест -fno-common""" + result = subprocess.run([rusi, '-fno-common', '-S', '-o-', '-xc', '-'], + input='int foo;', capture_output=True, text=True) + проверка_содержимого(result.stdout, re.compile(r'^foo:', re.MULTILINE), "foo не сгенерирована как обычная переменная") + +def тест_include(rusi: str, tmp: str): + """Тест -include""" + out_h = os.path.join(tmp, 'out.h') + with open(out_h, 'w') as f: + f.write('foo') + + result = subprocess.run([rusi, '-include', out_h, '-E', '-o-', '-xc', '-'], + input='bar', capture_output=True, text=True) + проверка_содержимого(result.stdout, 'foo', "файл не включен через -include") + проверка_содержимого(result.stdout, 'bar', "входной текст отсутствует после -include") + +def тест_include_stdio(rusi: str): + """Тест -include stdio.h""" + result = subprocess.run([rusi, '-Iinclude', '-include', 'stdio.h', '-E', '-o-', '-xc', '-'], + input='NULL', capture_output=True, text=True) + проверка_содержимого(result.stdout, '0', "NULL не определен через -include stdio.h") + +def тест_xc(rusi: str, tmp: str): + """Тест -xc""" + foo_o = os.path.join(tmp, 'foo.o') + subprocess.run([rusi, '-c', '-xc', '-o', foo_o, '-'], + input='int x;', text=True, check=True) + +def тест_x_assembler(rusi: str, tmp: str): + """Тест -x assembler""" + foo_o = os.path.join(tmp, 'foo.o') + subprocess.run([rusi, '-c', '-x', 'assembler', '-o', foo_o, '-'], + input='x:', text=True, check=True) + +def тест_x_none(rusi: str, tmp: str): + """Тест -x none""" + foo_o = os.path.join(tmp, 'foo.o') + foo_c = os.path.join(tmp, 'foo.c') + with open(foo_c, 'w') as f: + f.write('int x;') + subprocess.run([rusi, '-c', '-x', 'assembler', '-x', 'none', '-o', foo_o, foo_c], check=True) + +def тест_E_без_xc(rusi: str): + """Тест -E без -xc""" + result = subprocess.run([rusi, '-E', '-'], + input='foo', capture_output=True, text=True) + проверка_содержимого(result.stdout, 'foo', "препроцессинг без -xc не прошел") + +def тест_a_архив(rusi: str, tmp: str): + """Тест работы с архивными файлами (.a)""" + foo_o = os.path.join(tmp, 'foo.o') + bar_o = os.path.join(tmp, 'bar.o') + + subprocess.run([rusi, '-c', '-xc', '-o', foo_o, '-'], + input='void foo() {}', text=True, check=True) + subprocess.run([rusi, '-c', '-xc', '-o', bar_o, '-'], + input='void bar() {}', text=True, check=True) + + foo_a = os.path.join(tmp, 'foo.a') + subprocess.run(['ar', 'rcs', foo_a, foo_o, bar_o], check=True) + + main_c = os.path.join(tmp, 'main.c') + with open(main_c, 'w') as f: + f.write('void foo(); void bar(); int main() { foo(); bar(); }') + + foo_exe = os.path.join(tmp, 'foo') + subprocess.run([rusi, '-o', foo_exe, main_c, foo_a], check=True) + +def тест_so_библиотека(rusi: str, tmp: str): + """Тест работы с динамическими библиотеками (.so)""" + foo_o = os.path.join(tmp, 'foo.o') + bar_o = os.path.join(tmp, 'bar.o') + + subprocess.run(['cc', '-fPIC', '-c', '-xc', '-o', foo_o, '-'], + input='void foo() {}', text=True, check=True) + subprocess.run(['cc', '-fPIC', '-c', '-xc', '-o', bar_o, '-'], + input='void bar() {}', text=True, check=True) + + foo_so = os.path.join(tmp, 'foo.so') + subprocess.run(['cc', '-shared', '-o', foo_so, foo_o, bar_o], check=True) + + main_c = os.path.join(tmp, 'main.c') + with open(main_c, 'w') as f: + f.write('void foo(); void bar(); int main() { foo(); bar(); }') + + foo_exe = os.path.join(tmp, 'foo') + subprocess.run([rusi, '-o', foo_exe, main_c, foo_so], check=True) + +def тест_хеш_таблица(rusi: str): + """Тест хеш-таблицы""" + subprocess.run([rusi, '-hashmap-test'], check=True) + +def тест_M(rusi: str, tmp: str): + """Тест -M""" + out_c = os.path.join(tmp, 'out.c') + with open(out_c, 'w') as f: + f.write('#include "out2.h"\n') + f.write('#include "out3.h"\n') + + out2_h = os.path.join(tmp, 'out2.h') + out3_h = os.path.join(tmp, 'out3.h') + for f in [out2_h, out3_h]: + with open(f, 'w') as fh: + fh.write('') + + result = subprocess.run([rusi, '-M', f'-I{tmp}', out_c], + capture_output=True, text=True) + проверка_содержимого(result.stdout, 'out.o:', "цель out.o не найдена") + проверка_содержимого(result.stdout, 'out.c', "зависимость out.c не найдена") + проверка_содержимого(result.stdout, 'out2.h', "зависимость out2.h не найдена") + проверка_содержимого(result.stdout, 'out3.h', "зависимость out3.h не найдена") + +def тест_MF(rusi: str, tmp: str): + """Тест -MF""" + out_c = os.path.join(tmp, 'out.c') + with open(out_c, 'w') as f: + f.write('#include "out2.h"\n') + f.write('#include "out3.h"\n') + + out2_h = os.path.join(tmp, 'out2.h') + out3_h = os.path.join(tmp, 'out3.h') + for f in [out2_h, out3_h]: + with open(f, 'w') as fh: + fh.write('') + + mf_file = os.path.join(tmp, 'mf') + subprocess.run([rusi, '-MF', mf_file, '-M', f'-I{tmp}', out_c], check=True) + with open(mf_file) as f: + content = f.read() + проверка_содержимого(content, 'out.o:', "цель out.o не найдена в MF") + проверка_содержимого(content, 'out.c', "зависимость out.c не найдена в MF") + проверка_содержимого(content, 'out2.h', "зависимость out2.h не найдена в MF") + проверка_содержимого(content, 'out3.h', "зависимость out3.h не найдена в MF") + +def тест_MP(rusi: str, tmp: str): + """Тест -MP""" + out_c = os.path.join(tmp, 'out.c') + with open(out_c, 'w') as f: + f.write('#include "out2.h"\n') + f.write('#include "out3.h"\n') + + out2_h = os.path.join(tmp, 'out2.h') + out3_h = os.path.join(tmp, 'out3.h') + for f in [out2_h, out3_h]: + with open(f, 'w') as fh: + fh.write('') + + mp_file = os.path.join(tmp, 'mp') + subprocess.run([rusi, '-MF', mp_file, '-MP', '-M', f'-I{tmp}', out_c], check=True) + with open(mp_file) as f: + content = f.read() + if not re.search(r'out2\.h:', content): + raise AssertionError("цель out2.h не найдена в MP") + if not re.search(r'out3\.h:', content): + raise AssertionError("цель out3.h не найдена в MP") + +def тест_MT(rusi: str, tmp: str): + """Тест -MT""" + out_c = os.path.join(tmp, 'out.c') + with open(out_c, 'w') as f: + f.write('#include "out2.h"\n') + + out2_h = os.path.join(tmp, 'out2.h') + with open(out2_h, 'w') as f: + f.write('') + + result = subprocess.run([rusi, '-MT', 'foo', '-M', f'-I{tmp}', out_c], + capture_output=True, text=True) + if not result.stdout.startswith('foo:'): + raise AssertionError("цель не изменена на foo") + +def тест_MT_несколько(rusi: str, tmp: str): + """Тест -MT с несколькими целями""" + out_c = os.path.join(tmp, 'out.c') + with open(out_c, 'w') as f: + f.write('#include "out2.h"\n') + + out2_h = os.path.join(tmp, 'out2.h') + with open(out2_h, 'w') as f: + f.write('') + + result = subprocess.run([rusi, '-MT', 'foo', '-MT', 'bar', '-M', f'-I{tmp}', out_c], + capture_output=True, text=True) + if not result.stdout.startswith('foo bar:'): + raise AssertionError("несколько целей не установлены") + +def тест_MD(rusi: str, tmp: str): + """Тест -MD""" + md2_c = os.path.join(tmp, 'md2.c') + md3_c = os.path.join(tmp, 'md3.c') + with open(md2_c, 'w') as f: + f.write('#include "out2.h"\n') + with open(md3_c, 'w') as f: + f.write('#include "out3.h"\n') + + out2_h = os.path.join(tmp, 'out2.h') + out3_h = os.path.join(tmp, 'out3.h') + for f in [out2_h, out3_h]: + with open(f, 'w') as fh: + fh.write('') + + old_pwd = os.getcwd() + os.chdir(tmp) + try: + subprocess.run([os.path.join(old_pwd, rusi), '-c', '-MD', '-I.', 'md2.c', 'md3.c'], check=True) + finally: + os.chdir(old_pwd) + + md2_d = os.path.join(tmp, 'md2.d') + md3_d = os.path.join(tmp, 'md3.d') + with open(md2_d) as f: + content = f.read() + проверка_содержимого(content, 'md2.o:', "цель md2.o не найдена в MD") + проверка_содержимого(content, 'md2.c', "зависимость md2.c не найдена в MD") + проверка_содержимого(content, 'out2.h', "зависимость out2.h не найдена в MD") + + with open(md3_d) as f: + content = f.read() + проверка_содержимого(content, 'md3.o:', "цель md3.o не найдена в MD") + проверка_содержимого(content, 'md3.c', "зависимость md3.c не найдена в MD") + проверка_содержимого(content, 'out3.h', "зависимость out3.h не найдена в MD") + +def тест_MD_MF(rusi: str, tmp: str): + """Тест -MD с -MF""" + md2_c = os.path.join(tmp, 'md2.c') + with open(md2_c, 'w') as f: + f.write('#include "out2.h"\n') + + out2_h = os.path.join(tmp, 'out2.h') + with open(out2_h, 'w') as f: + f.write('') + + md_mf_d = os.path.join(tmp, 'md-mf.d') + subprocess.run([rusi, '-c', '-MD', '-MF', md_mf_d, '-I.', md2_c], check=True) + with open(md_mf_d) as f: + content = f.read() + проверка_содержимого(content, 'md2.o:', "цель md2.o не найдена в MD+MF") + проверка_содержимого(content, 'md2.c', "зависимость md2.c не найдена в MD+MF") + проверка_содержимого(content, 'out2.h', "зависимость out2.h не найдена в MD+MF") + +def тест_fPIC(rusi: str, tmp: str): + """Тест -fPIC""" + foo_o = os.path.join(tmp, 'foo.o') + subprocess.run([rusi, '-fPIC', '-xc', '-c', '-o', foo_o, '-'], + input='extern int bar; int foo() { return bar; }', text=True, check=True) + + foo_so = os.path.join(tmp, 'foo.so') + subprocess.run(['cc', '-shared', '-o', foo_so, foo_o], check=True) + + main_c = os.path.join(tmp, 'main.c') + with open(main_c, 'w') as f: + f.write('int foo(); int bar=3; int main() { foo(); }') + + foo_exe = os.path.join(tmp, 'foo') + subprocess.run([rusi, '-o', foo_exe, main_c, foo_so], check=True) + +def тест_include_next(rusi: str, tmp: str): + """Тест #include_next""" + next1 = os.path.join(tmp, 'next1') + next2 = os.path.join(tmp, 'next2') + next3 = os.path.join(tmp, 'next3') + os.makedirs(next1, exist_ok=True) + os.makedirs(next2, exist_ok=True) + os.makedirs(next3, exist_ok=True) + + file_c = os.path.join(tmp, 'file.c') + with open(file_c, 'w') as f: + f.write('#include "file1.h"\n') + + with open(os.path.join(next1, 'file1.h'), 'w') as f: + f.write('#include_next "file1.h"\n') + + with open(os.path.join(next2, 'file1.h'), 'w') as f: + f.write('#include_next "file2.h"\n') + + with open(os.path.join(next3, 'file2.h'), 'w') as f: + f.write('foo') + + result = subprocess.run([rusi, f'-I{next1}', f'-I{next2}', f'-I{next3}', '-E', file_c], + capture_output=True, text=True) + проверка_содержимого(result.stdout, 'foo', "цепочка include_next не привела к foo") + +def тест_static(rusi: str, tmp: str): + """Тест -static""" + foo_c = os.path.join(tmp, 'foo.c') + bar_c = os.path.join(tmp, 'bar.c') + with open(foo_c, 'w') as f: + f.write('extern int bar; int foo() { return bar; }') + with open(bar_c, 'w') as f: + f.write('int foo(); int bar=3; int main() { foo(); }') + + foo_exe = os.path.join(tmp, 'foo') + subprocess.run([rusi, '-static', '-o', foo_exe, foo_c, bar_c], check=True) + + result = subprocess.run(['file', foo_exe], capture_output=True, text=True) + if 'statically linked' not in result.stdout: + raise AssertionError("файл не является статически слинкованным") + +def тест_shared(rusi: str, tmp: str): + """Тест -shared""" + foo_so = os.path.join(tmp, 'foo.so') + foo_c = os.path.join(tmp, 'foo.c') + bar_c = os.path.join(tmp, 'bar.c') + + with open(foo_c, 'w') as f: + f.write('extern int bar; int foo() { return bar; }') + with open(bar_c, 'w') as f: + f.write('int foo(); int bar=3; int main() { foo(); }') + + subprocess.run([rusi, '-fPIC', '-shared', '-o', foo_so, foo_c, bar_c], check=True) + +def тест_L(rusi: str, tmp: str): + """Тест -L""" + foo_c = os.path.join(tmp, 'foo.c') + with open(foo_c, 'w') as f: + f.write('extern int bar; int foo() { return bar; }') + + libfoobar_so = os.path.join(tmp, 'libfoobar.so') + subprocess.run([rusi, '-fPIC', '-shared', '-o', libfoobar_so, foo_c], check=True) + + bar_c = os.path.join(tmp, 'bar.c') + with open(bar_c, 'w') as f: + f.write('int foo(); int bar=3; int main() { foo(); }') + + foo_exe = os.path.join(tmp, 'foo') + subprocess.run([rusi, '-o', foo_exe, bar_c, f'-L{tmp}', '-lfoobar'], check=True) + +def тест_Wl(rusi: str, tmp: str): + """Тест -Wl,""" + foo_o = os.path.join(tmp, 'foo.o') + bar_o = os.path.join(tmp, 'bar.o') + baz_o = os.path.join(tmp, 'baz.o') + + subprocess.run([rusi, '-c', '-o', foo_o, '-xc', '-'], + input='int foo() {}', text=True, check=True) + subprocess.run([rusi, '-c', '-o', bar_o, '-xc', '-'], + input='int foo() {}', text=True, check=True) + subprocess.run([rusi, '-c', '-o', baz_o, '-xc', '-'], + input='int main() {}', text=True, check=True) + + foo_exe = os.path.join(tmp, 'foo') + subprocess.run(['cc', '-Wl,-z,muldefs,--gc-sections', '-o', foo_exe, foo_o, bar_o, baz_o], check=True) + +def тест_Xlinker(rusi: str, tmp: str): + """Тест -Xlinker""" + foo_o = os.path.join(tmp, 'foo.o') + bar_o = os.path.join(tmp, 'bar.o') + baz_o = os.path.join(tmp, 'baz.o') + + subprocess.run([rusi, '-c', '-o', foo_o, '-xc', '-'], + input='int foo() {}', text=True, check=True) + subprocess.run([rusi, '-c', '-o', bar_o, '-xc', '-'], + input='int foo() {}', text=True, check=True) + subprocess.run([rusi, '-c', '-o', baz_o, '-xc', '-'], + input='int main() {}', text=True, check=True) + + foo_exe = os.path.join(tmp, 'foo') + subprocess.run(['cc', '-Xlinker', '-z', '-Xlinker', 'muldefs', '-Xlinker', '--gc-sections', + '-o', foo_exe, foo_o, bar_o, baz_o], check=True) + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/test/enum.c b/test/enum.c new file mode 100644 index 0000000..3103d8c --- /dev/null +++ b/test/enum.c @@ -0,0 +1,19 @@ +#включить "тест.з" +#включить "stdvv.c" + +цел главн() { + ПРОВЕРИТЬ(0, ({ переч { ноль, один, два }; ноль; })); + ПРОВЕРИТЬ(1, ({ переч { ноль, один, два }; один; })); + ПРОВЕРИТЬ(2, ({ переч { ноль, один, два }; два; })); + ПРОВЕРИТЬ(5, ({ переч { пять=5, шесть, семь }; пять; })); + ПРОВЕРИТЬ(6, ({ переч { пять=5, шесть, семь }; шесть; })); + ПРОВЕРИТЬ(0, ({ переч { ноль, пять=5, три=3, четыре }; ноль; })); + ПРОВЕРИТЬ(5, ({ переч { ноль, пять=5, три=3, четыре }; пять; })); + ПРОВЕРИТЬ(3, ({ переч { ноль, пять=5, три=3, четыре }; три; })); + ПРОВЕРИТЬ(4, ({ переч { ноль, пять=5, три=3, четыре }; четыре; })); + ПРОВЕРИТЬ(4, ({ переч { ноль, один, два } х; размер(х); })); + ПРОВЕРИТЬ(4, ({ переч т { ноль, один, два }; переч т у; размер(у); })); + + printf("OK\n"); + возврат 0; +} \ No newline at end of file diff --git a/test/extern.c b/test/extern.c new file mode 100644 index 0000000..ee9a1bc --- /dev/null +++ b/test/extern.c @@ -0,0 +1,27 @@ +#включить "тест.з" +#включить "stdvv.c" +#include "common.c" + +extern int ext1; +extern int *ext2; + +inline int inline_fn(void) { + return 3; +} + +int main() { + ASSERT(5, ext1); + ASSERT(5, *ext2); + + extern int ext3; + ASSERT(7, ext3); + + int ext_fn1(int x); + ASSERT(5, ext_fn1(5)); + + extern int ext_fn2(int x); + ASSERT(8, ext_fn2(8)); + + printf("OK\n"); + return 0; +} diff --git a/test/float.c b/test/float.c new file mode 100644 index 0000000..08b9f29 --- /dev/null +++ b/test/float.c @@ -0,0 +1,94 @@ +#включить "тест.з" +#включить "stdvv.c" + +цел главн() { + ПРОВЕРИТЬ(35, (плав)(символ)35); + ПРОВЕРИТЬ(35, (плав)(корот)35); + ПРОВЕРИТЬ(35, (плав)(цел)35); + ПРОВЕРИТЬ(35, (плав)(длин)35); + ПРОВЕРИТЬ(35, (плав)(беззнак символ)35); + ПРОВЕРИТЬ(35, (плав)(беззнак корот)35); + ПРОВЕРИТЬ(35, (плав)(беззнак цел)35); + ПРОВЕРИТЬ(35, (плав)(беззнак длин)35); + + ПРОВЕРИТЬ(35, (двойн)(символ)35); + ПРОВЕРИТЬ(35, (двойн)(корот)35); + ПРОВЕРИТЬ(35, (двойн)(цел)35); + ПРОВЕРИТЬ(35, (двойн)(длин)35); + ПРОВЕРИТЬ(35, (двойн)(беззнак символ)35); + ПРОВЕРИТЬ(35, (двойн)(беззнак корот)35); + ПРОВЕРИТЬ(35, (двойн)(беззнак цел)35); + ПРОВЕРИТЬ(35, (двойн)(беззнак длин)35); + + ПРОВЕРИТЬ(35, (символ)(плав)35); + ПРОВЕРИТЬ(35, (корот)(плав)35); + ПРОВЕРИТЬ(35, (цел)(плав)35); + ПРОВЕРИТЬ(35, (длин)(плав)35); + ПРОВЕРИТЬ(35, (беззнак символ)(плав)35); + ПРОВЕРИТЬ(35, (беззнак корот)(плав)35); + ПРОВЕРИТЬ(35, (беззнак цел)(плав)35); + ПРОВЕРИТЬ(35, (беззнак длин)(плав)35); + + ПРОВЕРИТЬ(35, (символ)(двойн)35); + ПРОВЕРИТЬ(35, (корот)(двойн)35); + ПРОВЕРИТЬ(35, (цел)(двойн)35); + ПРОВЕРИТЬ(35, (длин)(двойн)35); + ПРОВЕРИТЬ(35, (беззнак символ)(двойн)35); + ПРОВЕРИТЬ(35, (беззнак корот)(двойн)35); + ПРОВЕРИТЬ(35, (беззнак цел)(двойн)35); + ПРОВЕРИТЬ(35, (беззнак длин)(двойн)35); + + ПРОВЕРИТЬ(-2147483648, (двойн)(беззнак длин)(длин)-1); + + ПРОВЕРИТЬ(1, 2e3==2e3); + ПРОВЕРИТЬ(0, 2e3==2e5); + ПРОВЕРИТЬ(1, 2.0==2); + ПРОВЕРИТЬ(0, 5.1<5); + ПРОВЕРИТЬ(0, 5.0<5); + ПРОВЕРИТЬ(1, 4.9<5); + ПРОВЕРИТЬ(0, 5.1<=5); + ПРОВЕРИТЬ(1, 5.0<=5); + ПРОВЕРИТЬ(1, 4.9<=5); + + ПРОВЕРИТЬ(1, 2e3f==2e3); + ПРОВЕРИТЬ(0, 2e3f==2e5); + ПРОВЕРИТЬ(1, 2.0f==2); + ПРОВЕРИТЬ(0, 5.1f<5); + ПРОВЕРИТЬ(0, 5.0f<5); + ПРОВЕРИТЬ(1, 4.9f<5); + ПРОВЕРИТЬ(0, 5.1f<=5); + ПРОВЕРИТЬ(1, 5.0f<=5); + ПРОВЕРИТЬ(1, 4.9f<=5); + + ПРОВЕРИТЬ(6, 2.3+3.8); + ПРОВЕРИТЬ(-1, 2.3-3.8); + ПРОВЕРИТЬ(-3, -3.8); + ПРОВЕРИТЬ(13, 3.3*4); + ПРОВЕРИТЬ(2, 5.0/2); + + ПРОВЕРИТЬ(6, 2.3f+3.8f); + ПРОВЕРИТЬ(6, 2.3f+3.8); + ПРОВЕРИТЬ(-1, 2.3f-3.8); + ПРОВЕРИТЬ(-3, -3.8f); + ПРОВЕРИТЬ(13, 3.3f*4); + ПРОВЕРИТЬ(2, 5.0f/2); + + ПРОВЕРИТЬ(0, 0.0/0.0 == 0.0/0.0); + ПРОВЕРИТЬ(1, 0.0/0.0 != 0.0/0.0); + + ПРОВЕРИТЬ(0, 0.0/0.0 < 0); + ПРОВЕРИТЬ(0, 0.0/0.0 <= 0); + ПРОВЕРИТЬ(0, 0.0/0.0 > 0); + ПРОВЕРИТЬ(0, 0.0/0.0 >= 0); + + ПРОВЕРИТЬ(0, !3.); + ПРОВЕРИТЬ(1, !0.); + ПРОВЕРИТЬ(0, !3.f); + ПРОВЕРИТЬ(1, !0.f); + + ПРОВЕРИТЬ(5, 0.0 ? 3 : 5); + ПРОВЕРИТЬ(3, 1.2 ? 3 : 5); + + printf("OK\n"); + возврат 0; +} \ No newline at end of file diff --git a/test/function.c b/test/function.c new file mode 100644 index 0000000..60dc9bf --- /dev/null +++ b/test/function.c @@ -0,0 +1,397 @@ +#включить "тест.з" +#включить "stdvv.c" +#include "common.c" + + + +int ret3(void) { + return 3; + return 5; +} + +int add2(int x, int y) { + return x + y; +} + +int sub2(int x, int y) { + return x - y; +} + +int add6(int a, int b, int c, int d, int e, int f) { + return a + b + c + d + e + f; +} + +int addx(int *x, int y) { + return *x + y; +} + +int sub_char(char a, char b, char c) { + return a - b - c; +} + +int fib(int x) { + if (x<=1) + return 1; + return fib(x-1) + fib(x-2); +} + +int sub_long(long a, long b, long c) { + return a - b - c; +} + +int sub_short(short a, short b, short c) { + return a - b - c; +} + +int g1; + +int *g1_ptr(void) { return &g1; } +char int_to_char(int x) { return x; } + +int div_long(long a, long b) { + return a / b; +} + +_Bool bool_fn_add(_Bool x) { return x + 1; } +_Bool bool_fn_sub(_Bool x) { return x - 1; } + +int param_decay(int x[]) { return x[0]; } + +int counter() { + static int i; + static int j = 1+1; + return i++ + j++; +} + +void ret_none() { + return; +} + +_Bool true_fn(); +_Bool false_fn(); +char char_fn(); +short short_fn(); + +unsigned char uchar_fn(); +unsigned short ushort_fn(); + +char schar_fn(); +short sshort_fn(); + +int add_all(int n, ...); + +typedef struct { + int gp_offset; + int fp_offset; + void *overflow_arg_area; + void *reg_save_area; +} __va_elem; + +typedef __va_elem va_list[1]; + +int add_all(int n, ...); +int sprintf(char *buf, char *fmt, ...); +int vsprintf(char *buf, char *fmt, va_list ap); + +char *fmt(char *buf, char *fmt, ...) { + va_list ap; + *ap = *(__va_elem *)__va_area__; + vsprintf(buf, fmt, ap); +} + +double add_double(double x, double y); +float add_float(float x, float y); + +float add_float3(float x, float y, float z) { + return x + y + z; +} + +double add_double3(double x, double y, double z) { + return x + y + z; +} + +int (*fnptr(int (*fn)(int n, ...)))(int, ...) { + return fn; +} + +int param_decay2(int x()) { return x(); } + +char *func_fn(void) { + return __func__; +} + +char *function_fn(void) { + return __FUNCTION__; +} + +int add10_int(int x1, int x2, int x3, int x4, int x5, int x6, int x7, int x8, int x9, int x10); +float add10_float(float x1, float x2, float x3, float x4, float x5, float x6, float x7, float x8, float x9, float x10); +double add10_double(double x1, double x2, double x3, double x4, double x5, double x6, double x7, double x8, double x9, double x10); + +int many_args1(int a, int b, int c, int d, int e, int f, int g, int h) { + return g / h; +} + +double many_args2(double a, double b, double c, double d, double e, + double f, double g, double h, double i, double j) { + return i / j; +} + +int many_args3(int a, double b, int c, int d, double e, int f, + double g, int h, double i, double j, double k, + double l, double m, int n, int o, double p) { + return o / p; +} + +typedef struct { int a,b; short c; char d; } Ty4; +typedef struct { int a; float b; double c; } Ty5; +typedef struct { unsigned char a[3]; } Ty6; +typedef struct { long a, b, c; } Ty7; + +int struct_test5(Ty5 x, int n); +int struct_test4(Ty4 x, int n); +int struct_test6(Ty6 x, int n); +int struct_test7(Ty7 x, int n); + +int struct_test14(Ty4 x, int n) { + switch (n) { + case 0: return x.a; + case 1: return x.b; + case 2: return x.c; + default: return x.d; + } +} + +int struct_test15(Ty5 x, int n) { + switch (n) { + case 0: return x.a; + case 1: return x.b; + default: return x.c; + } +} + +typedef struct { unsigned char a[10]; } Ty20; +typedef struct { unsigned char a[20]; } Ty21; + +Ty4 struct_test24(void); +Ty5 struct_test25(void); +Ty6 struct_test26(void); +Ty20 struct_test27(void); +Ty21 struct_test28(void); + +Ty4 struct_test34(void) { + return (Ty4){10, 20, 30, 40}; +} + +Ty5 struct_test35(void) { + return (Ty5){10, 20, 30}; +} + +Ty6 struct_test36(void) { + return (Ty6){10, 20, 30}; +} + +Ty20 struct_test37(void) { + return (Ty20){10, 20, 30, 40, 50, 60, 70, 80, 90, 100}; +} + +Ty21 struct_test38(void) { + return (Ty21){1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20}; +} + +inline int inline_fn(void) { + return 3; +} + +double to_double(long double x) { + return x; +} + +long double to_ldouble(int x) { + return x; +} + +int main() { + ASSERT(3, ret3()); + ASSERT(8, add2(3, 5)); + ASSERT(2, sub2(5, 3)); + ASSERT(21, add6(1,2,3,4,5,6)); + ASSERT(66, add6(1,2,add6(3,4,5,6,7,8),9,10,11)); + ASSERT(136, add6(1,2,add6(3,add6(4,5,6,7,8,9),10,11,12,13),14,15,16)); + + ASSERT(7, add2(3,4)); + ASSERT(1, sub2(4,3)); + ASSERT(55, fib(9)); + + ASSERT(1, ({ sub_char(7, 3, 3); })); + + ASSERT(1, sub_long(7, 3, 3)); + ASSERT(1, sub_short(7, 3, 3)); + + g1 = 3; + + ASSERT(3, *g1_ptr()); + ASSERT(5, int_to_char(261)); + ASSERT(5, int_to_char(261)); + ASSERT(-5, div_long(-10, 2)); + + ASSERT(1, bool_fn_add(3)); + ASSERT(0, bool_fn_sub(3)); + ASSERT(1, bool_fn_add(-3)); + ASSERT(0, bool_fn_sub(-3)); + ASSERT(1, bool_fn_add(0)); + ASSERT(1, bool_fn_sub(0)); + + ASSERT(5, static_fn()); + + ASSERT(3, ({ int x[2]; x[0]=3; param_decay(x); })); + + ASSERT(2, counter()); + ASSERT(4, counter()); + ASSERT(6, counter()); + + ret_none(); + + // Используем значения из common.c (без переопределения функций) + ASSERT(513, true_fn()); + ASSERT(512, false_fn()); + ASSERT(515, char_fn()); // (2<<8)+3 = 515 + ASSERT(131077, short_fn()); // (2<<16)+5 = 131077 + + ASSERT(6, add_all(3,1,2,3)); + ASSERT(5, add_all(4,1,2,3,-1)); + + { char buf[100]; fmt(buf, "%d %d %s", 1, 2, "foo"); printf("%s\n", buf); } + + ASSERT(0, ({ char buf[100]; sprintf(buf, "%d %d %s", 1, 2, "foo"); strcmp("1 2 foo", buf); })); + + ASSERT(0, ({ char buf[100]; fmt(buf, "%d %d %s", 1, 2, "foo"); strcmp("1 2 foo", buf); })); + + ASSERT(2043, uchar_fn()); + ASSERT(2097144, ushort_fn()); + ASSERT(2043, schar_fn()); + ASSERT(2097144, sshort_fn()); + + ASSERT(6, add_float(2.3, 3.8)); + ASSERT(6, add_double(2.3, 3.8)); + + ASSERT(7, add_float3(2.5, 2.5, 2.5)); + ASSERT(7, add_double3(2.5, 2.5, 2.5)); + + ASSERT(0, ({ char buf[100]; sprintf(buf, "%.1f", (float)3.5); strcmp(buf, "3.5"); })); + + ASSERT(0, ({ char buf[100]; fmt(buf, "%.1f", (float)3.5); strcmp(buf, "3.5"); })); + + ASSERT(5, (add2)(2,3)); + ASSERT(5, (&add2)(2,3)); + ASSERT(7, ({ int (*fn)(int,int) = add2; fn(2,5); })); + ASSERT(6, fnptr(add_all)(3, 1, 2, 3)); + + ASSERT(3, param_decay2(ret3)); + + ASSERT(5, sizeof(__func__)); + ASSERT(0, strcmp("main", __func__)); + ASSERT(0, strcmp("func_fn", func_fn())); + ASSERT(0, strcmp("main", __FUNCTION__)); + ASSERT(0, strcmp("function_fn", function_fn())); + + + ASSERT(55, add10_int(1,2,3,4,5,6,7,8,9,10)); + ASSERT(55, add10_float(1,2,3,4,5,6,7,8,9,10)); + ASSERT(55, add10_double(1,2,3,4,5,6,7,8,9,10)); + + ASSERT(0, ({ char buf[200]; sprintf(buf, "%d %.1f %.1f %.1f %d %d %.1f %d %d %d %d %.1f %d %d %.1f %.1f %.1f %.1f %d", 1, 1.0, 1.0, 1.0, 1, 1, 1.0, 1, 1, 1, 1, 1.0, 1, 1, 1.0, 1.0, 1.0, 1.0, 1); strcmp("1 1.0 1.0 1.0 1 1 1.0 1 1 1 1 1.0 1 1 1.0 1.0 1.0 1.0 1", buf); })); + + ASSERT(4, many_args1(1,2,3,4,5,6,40,10)); + ASSERT(4, many_args2(1,2,3,4,5,6,7,8,40,10)); + ASSERT(8, many_args3(1,2,3,4,5,6,7,8,9,10,11,12,13,14,80,10)); + + ASSERT(10, ({ Ty4 x={10,20,30,40}; struct_test4(x, 0); })); + ASSERT(20, ({ Ty4 x={10,20,30,40}; struct_test4(x, 1); })); + ASSERT(30, ({ Ty4 x={10,20,30,40}; struct_test4(x, 2); })); + ASSERT(40, ({ Ty4 x={10,20,30,40}; struct_test4(x, 3); })); + + ASSERT(10, ({ Ty5 x={10,20,30}; struct_test5(x, 0); })); + ASSERT(20, ({ Ty5 x={10,20,30}; struct_test5(x, 1); })); + ASSERT(30, ({ Ty5 x={10,20,30}; struct_test5(x, 2); })); + + ASSERT(10, ({ Ty6 x={10,20,30}; struct_test6(x, 0); })); + ASSERT(20, ({ Ty6 x={10,20,30}; struct_test6(x, 1); })); + ASSERT(30, ({ Ty6 x={10,20,30}; struct_test6(x, 2); })); + + ASSERT(10, ({ Ty7 x={10,20,30}; struct_test7(x, 0); })); + ASSERT(20, ({ Ty7 x={10,20,30}; struct_test7(x, 1); })); + ASSERT(30, ({ Ty7 x={10,20,30}; struct_test7(x, 2); })); + + ASSERT(10, ({ Ty4 x={10,20,30,40}; struct_test14(x, 0); })); + ASSERT(20, ({ Ty4 x={10,20,30,40}; struct_test14(x, 1); })); + ASSERT(30, ({ Ty4 x={10,20,30,40}; struct_test14(x, 2); })); + ASSERT(40, ({ Ty4 x={10,20,30,40}; struct_test14(x, 3); })); + + ASSERT(10, ({ Ty5 x={10,20,30}; struct_test15(x, 0); })); + ASSERT(20, ({ Ty5 x={10,20,30}; struct_test15(x, 1); })); + ASSERT(30, ({ Ty5 x={10,20,30}; struct_test15(x, 2); })); + + ASSERT(10, struct_test24().a); + ASSERT(20, struct_test24().b); + ASSERT(30, struct_test24().c); + ASSERT(40, struct_test24().d); + + ASSERT(10, struct_test25().a); + ASSERT(20, struct_test25().b); + ASSERT(30, struct_test25().c); + + ASSERT(10, struct_test26().a[0]); + ASSERT(20, struct_test26().a[1]); + ASSERT(30, struct_test26().a[2]); + + ASSERT(10, struct_test27().a[0]); + ASSERT(60, struct_test27().a[5]); + ASSERT(100, struct_test27().a[9]); + + ASSERT(1, struct_test28().a[0]); + ASSERT(5, struct_test28().a[4]); + ASSERT(10, struct_test28().a[9]); + ASSERT(15, struct_test28().a[14]); + ASSERT(20, struct_test28().a[19]); + + ASSERT(10, struct_test34().a); + ASSERT(20, struct_test34().b); + ASSERT(30, struct_test34().c); + ASSERT(40, struct_test34().d); + + ASSERT(10, struct_test35().a); + ASSERT(20, struct_test35().b); + ASSERT(30, struct_test35().c); + + ASSERT(10, struct_test36().a[0]); + ASSERT(20, struct_test36().a[1]); + ASSERT(30, struct_test36().a[2]); + + ASSERT(10, struct_test37().a[0]); + ASSERT(60, struct_test37().a[5]); + ASSERT(100, struct_test37().a[9]); + + ASSERT(1, struct_test38().a[0]); + ASSERT(5, struct_test38().a[4]); + ASSERT(10, struct_test38().a[9]); + ASSERT(15, struct_test38().a[14]); + ASSERT(20, struct_test38().a[19]); + + ASSERT(5, (***add2)(2,3)); + + ASSERT(3, inline_fn()); + + ASSERT(0, ({ char buf[100]; sprintf(buf, "%Lf", (long double)12.3); strncmp(buf, "12.3", 4); })); + + ASSERT(1, to_double(3.5) == 3.5); + ASSERT(0, to_double(3.5) == 3); + + ASSERT(1, (long double)5.0 == (long double)5.0); + ASSERT(0, (long double)5.0 == (long double)5.2); + + ASSERT(1, to_ldouble(5.0) == 5.0); + ASSERT(0, to_ldouble(5.0) == 5.2); + + печатьф("OK\n"); +} \ No newline at end of file diff --git a/test/generic.c b/test/generic.c new file mode 100644 index 0000000..4759219 --- /dev/null +++ b/test/generic.c @@ -0,0 +1,13 @@ +#включить "тест.з" +#включить "stdvv.c" + +цел главн() { + ПРОВЕРИТЬ(1, _Обобщ(100.0, двойн: 1, цел *: 2, цел: 3, плав: 4)); + ПРОВЕРИТЬ(2, _Обобщ((цел *)0, двойн: 1, цел *: 2, цел: 3, плав: 4)); + ПРОВЕРИТЬ(2, _Обобщ((цел[3]){}, двойн: 1, цел *: 2, цел: 3, плав: 4)); + ПРОВЕРИТЬ(3, _Обобщ(100, двойн: 1, цел *: 2, цел: 3, плав: 4)); + ПРОВЕРИТЬ(4, _Обобщ(100f, двойн: 1, цел *: 2, цел: 3, плав: 4)); + + printf("OK\n"); + возврат 0; +} \ No newline at end of file diff --git a/test/include1.h b/test/include1.h new file mode 100644 index 0000000..9381511 --- /dev/null +++ b/test/include1.h @@ -0,0 +1,6 @@ +#include "include2.h" + +char *include1_filename = __FILE__; +int include1_line = __LINE__; + +int include1 = 5; diff --git a/test/include2.h b/test/include2.h new file mode 100644 index 0000000..1a5ee9d --- /dev/null +++ b/test/include2.h @@ -0,0 +1 @@ +int include2 = 7; diff --git a/test/include3.h b/test/include3.h new file mode 100644 index 0000000..820d4a6 --- /dev/null +++ b/test/include3.h @@ -0,0 +1 @@ +#define foo 3 diff --git a/test/include4.h b/test/include4.h new file mode 100644 index 0000000..1fea1a2 --- /dev/null +++ b/test/include4.h @@ -0,0 +1 @@ +#define foo 4 diff --git a/test/initializer.c b/test/initializer.c new file mode 100644 index 0000000..5567748 --- /dev/null +++ b/test/initializer.c @@ -0,0 +1,269 @@ +#включить "тест.з" +#включить "stdvv.c" +#include "common.c" + +char g3 = 3; +short g4 = 4; +int g5 = 5; +long g6 = 6; +int g9[3] = {0, 1, 2}; +struct {char a; int b;} g11[2] = {{1, 2}, {3, 4}}; +struct {int a[2];} g12[2] = {{{1, 2}}}; +union { int a; char b[8]; } g13[2] = {0x01020304, 0x05060708}; +char g17[] = "foobar"; +char g18[10] = "foobar"; +char g19[3] = "foobar"; +char *g20 = g17+0; +char *g21 = g17+3; +char *g22 = &g17-3; +char *g23[] = {g17+0, g17+3, g17-3}; +int g24=3; +int *g25=&g24; +int g26[3] = {1, 2, 3}; +int *g27 = g26 + 1; +int *g28 = &g11[1].a; +long g29 = (long)(long)g26; +struct { struct { int a[3]; } a; } g30 = {{{1,2,3}}}; +int *g31=g30.a.a; +struct {int a[2];} g40[2] = {{1, 2}, 3, 4}; +struct {int a[2];} g41[2] = {1, 2, 3, 4}; +char g43[][4] = {'f', 'o', 'o', 0, 'b', 'a', 'r', 0}; +char *g44 = {"foo"}; +union { int a; char b[4]; } g50 = {.b[2]=0x12}; +union { int a; } g51[2] = {}; + +typedef char T60[]; +T60 g60 = {1, 2, 3}; +T60 g61 = {1, 2, 3, 4, 5, 6}; + +typedef struct { char a, b[]; } T65; +T65 g65 = {'f','o','o',0}; +T65 g66 = {'f','o','o','b','a','r',0}; + +int main() { + ASSERT(1, ({ int x[3]={1,2,3}; x[0]; })); + ASSERT(2, ({ int x[3]={1,2,3}; x[1]; })); + ASSERT(3, ({ int x[3]={1,2,3}; x[2]; })); + ASSERT(3, ({ int x[3]={1,2,3}; x[2]; })); + + ASSERT(2, ({ int x[2][3]={{1,2,3},{4,5,6}}; x[0][1]; })); + ASSERT(4, ({ int x[2][3]={{1,2,3},{4,5,6}}; x[1][0]; })); + ASSERT(6, ({ int x[2][3]={{1,2,3},{4,5,6}}; x[1][2]; })); + + ASSERT(0, ({ int x[3]={}; x[0]; })); + ASSERT(0, ({ int x[3]={}; x[1]; })); + ASSERT(0, ({ int x[3]={}; x[2]; })); + + ASSERT(2, ({ int x[2][3]={{1,2}}; x[0][1]; })); + ASSERT(0, ({ int x[2][3]={{1,2}}; x[1][0]; })); + ASSERT(0, ({ int x[2][3]={{1,2}}; x[1][2]; })); + + ASSERT('a', ({ char x[4]="abc"; x[0]; })); + ASSERT('c', ({ char x[4]="abc"; x[2]; })); + ASSERT(0, ({ char x[4]="abc"; x[3]; })); + ASSERT('a', ({ char x[2][4]={"abc","def"}; x[0][0]; })); + ASSERT(0, ({ char x[2][4]={"abc","def"}; x[0][3]; })); + ASSERT('d', ({ char x[2][4]={"abc","def"}; x[1][0]; })); + ASSERT('f', ({ char x[2][4]={"abc","def"}; x[1][2]; })); + + ASSERT(4, ({ int x[]={1,2,3,4}; x[3]; })); + ASSERT(16, ({ int x[]={1,2,3,4}; sizeof(x); })); + ASSERT(4, ({ char x[]="foo"; sizeof(x); })); + + ASSERT(4, ({ typedef char T[]; T x="foo"; T y="x"; sizeof(x); })); + ASSERT(2, ({ typedef char T[]; T x="foo"; T y="x"; sizeof(y); })); + ASSERT(2, ({ typedef char T[]; T x="x"; T y="foo"; sizeof(x); })); + ASSERT(4, ({ typedef char T[]; T x="x"; T y="foo"; sizeof(y); })); + + ASSERT(1, ({ struct {int a; int b; int c;} x={1,2,3}; x.a; })); + ASSERT(2, ({ struct {int a; int b; int c;} x={1,2,3}; x.b; })); + ASSERT(3, ({ struct {int a; int b; int c;} x={1,2,3}; x.c; })); + ASSERT(1, ({ struct {int a; int b; int c;} x={1}; x.a; })); + ASSERT(0, ({ struct {int a; int b; int c;} x={1}; x.b; })); + ASSERT(0, ({ struct {int a; int b; int c;} x={1}; x.c; })); + + ASSERT(1, ({ struct {int a; int b;} x[2]={{1,2},{3,4}}; x[0].a; })); + ASSERT(2, ({ struct {int a; int b;} x[2]={{1,2},{3,4}}; x[0].b; })); + ASSERT(3, ({ struct {int a; int b;} x[2]={{1,2},{3,4}}; x[1].a; })); + ASSERT(4, ({ struct {int a; int b;} x[2]={{1,2},{3,4}}; x[1].b; })); + + ASSERT(0, ({ struct {int a; int b;} x[2]={{1,2}}; x[1].b; })); + + ASSERT(0, ({ struct {int a; int b;} x={}; x.a; })); + ASSERT(0, ({ struct {int a; int b;} x={}; x.b; })); + + ASSERT(5, ({ typedef struct {int a,b,c,d,e,f;} T; T x={1,2,3,4,5,6}; T y; y=x; y.e; })); + ASSERT(2, ({ typedef struct {int a,b;} T; T x={1,2}; T y, z; z=y=x; z.b; })); + + ASSERT(1, ({ typedef struct {int a,b;} T; T x={1,2}; T y=x; y.a; })); + + ASSERT(4, ({ union { int a; char b[4]; } x={0x01020304}; x.b[0]; })); + ASSERT(3, ({ union { int a; char b[4]; } x={0x01020304}; x.b[1]; })); + + ASSERT(0x01020304, ({ union { struct { char a,b,c,d; } e; int f; } x={{4,3,2,1}}; x.f; })); + + ASSERT(3, g3); + ASSERT(4, g4); + ASSERT(5, g5); + ASSERT(6, g6); + + ASSERT(0, g9[0]); + ASSERT(1, g9[1]); + ASSERT(2, g9[2]); + + ASSERT(1, g11[0].a); + ASSERT(2, g11[0].b); + ASSERT(3, g11[1].a); + ASSERT(4, g11[1].b); + + ASSERT(1, g12[0].a[0]); + ASSERT(2, g12[0].a[1]); + ASSERT(0, g12[1].a[0]); + ASSERT(0, g12[1].a[1]); + + ASSERT(4, g13[0].b[0]); + ASSERT(3, g13[0].b[1]); + ASSERT(8, g13[1].b[0]); + ASSERT(7, g13[1].b[1]); + + ASSERT(7, sizeof(g17)); + ASSERT(10, sizeof(g18)); + ASSERT(3, sizeof(g19)); + + ASSERT(0, memcmp(g17, "foobar", 7)); + ASSERT(0, memcmp(g18, "foobar\0\0\0", 10)); + ASSERT(0, memcmp(g19, "foo", 3)); + + ASSERT(0, strcmp(g20, "foobar")); + ASSERT(0, strcmp(g21, "bar")); + ASSERT(0, strcmp(g22+3, "foobar")); + + ASSERT(0, strcmp(g23[0], "foobar")); + ASSERT(0, strcmp(g23[1], "bar")); + ASSERT(0, strcmp(g23[2]+3, "foobar")); + + ASSERT(3, g24); + ASSERT(3, *g25); + ASSERT(2, *g27); + ASSERT(3, *g28); + ASSERT(1, *(int *)g29); + + ASSERT(1, g31[0]); + ASSERT(2, g31[1]); + ASSERT(3, g31[2]); + + ASSERT(1, g40[0].a[0]); + ASSERT(2, g40[0].a[1]); + ASSERT(3, g40[1].a[0]); + ASSERT(4, g40[1].a[1]); + + ASSERT(1, g41[0].a[0]); + ASSERT(2, g41[0].a[1]); + ASSERT(3, g41[1].a[0]); + ASSERT(4, g41[1].a[1]); + + ASSERT(0, ({ int x[2][3]={0,1,2,3,4,5}; x[0][0]; })); + ASSERT(3, ({ int x[2][3]={0,1,2,3,4,5}; x[1][0]; })); + + ASSERT(0, ({ struct {int a; int b;} x[2]={0,1,2,3}; x[0].a; })); + ASSERT(2, ({ struct {int a; int b;} x[2]={0,1,2,3}; x[1].a; })); + + ASSERT(0, strcmp(g43[0], "foo")); + ASSERT(0, strcmp(g43[1], "bar")); + ASSERT(0, strcmp(g44, "foo")); + + ASSERT(3, ({ int a[]={1,2,3,}; a[2]; })); + ASSERT(1, ({ struct {int a,b,c;} x={1,2,3,}; x.a; })); + ASSERT(1, ({ union {int a; char b;} x={1,}; x.a; })); + ASSERT(2, ({ enum {x,y,z,}; z; })); + + ASSERT(3, sizeof(g60)); + ASSERT(6, sizeof(g61)); + + ASSERT(4, sizeof(g65)); + ASSERT(7, sizeof(g66)); + ASSERT(0, strcmp(g65.b, "oo")); + ASSERT(0, strcmp(g66.b, "oobar")); + + ASSERT(4, ({ int x[3]={1, 2, 3, [0]=4, 5}; x[0]; })); + ASSERT(5, ({ int x[3]={1, 2, 3, [0]=4, 5}; x[1]; })); + ASSERT(3, ({ int x[3]={1, 2, 3, [0]=4, 5}; x[2]; })); + + ASSERT(10, ({ int x[2][3]={1,2,3,4,5,6,[0][1]=7,8,[0]=9,[0]=10,11,[1][0]=12}; x[0][0]; })); + ASSERT(11, ({ int x[2][3]={1,2,3,4,5,6,[0][1]=7,8,[0]=9,[0]=10,11,[1][0]=12}; x[0][1]; })); + ASSERT(8, ({ int x[2][3]={1,2,3,4,5,6,[0][1]=7,8,[0]=9,[0]=10,11,[1][0]=12}; x[0][2]; })); + ASSERT(12, ({ int x[2][3]={1,2,3,4,5,6,[0][1]=7,8,[0]=9,[0]=10,11,[1][0]=12}; x[1][0]; })); + ASSERT(5, ({ int x[2][3]={1,2,3,4,5,6,[0][1]=7,8,[0]=9,[0]=10,11,[1][0]=12}; x[1][1]; })); + ASSERT(6, ({ int x[2][3]={1,2,3,4,5,6,[0][1]=7,8,[0]=9,[0]=10,11,[1][0]=12}; x[1][2]; })); + + ASSERT(7, ({ int x[2][3]={1,2,3,4,5,6,[0]={7,8},9,10}; x[0][0]; })); + ASSERT(8, ({ int x[2][3]={1,2,3,4,5,6,[0]={7,8},9,10}; x[0][1]; })); + ASSERT(3, ({ int x[2][3]={1,2,3,4,5,6,[0]={7,8},9,10}; x[0][2]; })); + ASSERT(9, ({ int x[2][3]={1,2,3,4,5,6,[0]={7,8},9,10}; x[1][0]; })); + ASSERT(10, ({ int x[2][3]={1,2,3,4,5,6,[0]={7,8},9,10}; x[1][1]; })); + ASSERT(6, ({ int x[2][3]={1,2,3,4,5,6,[0]={7,8},9,10}; x[1][2]; })); + + ASSERT(7, ((int[10]){ [3]=7 })[3]); + ASSERT(0, ((int[10]){ [3]=7 })[4]); + + ASSERT(10, ({ char x[]={[10-3]=1,2,3}; sizeof(x); })); + ASSERT(20, ({ char x[][2]={[8][1]=1,2}; sizeof(x); })); + + ASSERT(3, sizeof(g60)); + ASSERT(6, sizeof(g61)); + + ASSERT(4, sizeof(g65)); + ASSERT(7, sizeof(g66)); + ASSERT(0, strcmp(g65.b, "oo")); + ASSERT(0, strcmp(g66.b, "oobar")); + + ASSERT(7, ((int[10]){ [3] 7 })[3]); + ASSERT(0, ((int[10]){ [3] 7 })[4]); + + ASSERT(4, ({ struct { int a,b; } x={1,2,.b=3,.a=4}; x.a; })); + ASSERT(3, ({ struct { int a,b; } x={1,2,.b=3,.a=4}; x.b; })); + + ASSERT(1, ({ struct { struct { int a,b; } c; } x={.c=1,2}; x.c.a; })); + ASSERT(2, ({ struct { struct { int a,b; } c; } x={.c=1,2}; x.c.b; })); + + ASSERT(0, ({ struct { struct { int a,b; } c; } x={.c.b=1}; x.c.a; })); + ASSERT(1, ({ struct { struct { int a,b; } c; } x={.c.b=1}; x.c.b; })); + + ASSERT(1, ({ struct { int a[2]; } x={.a=1,2}; x.a[0]; })); + ASSERT(2, ({ struct { int a[2]; } x={.a=1,2}; x.a[1]; })); + + ASSERT(0, ({ struct { int a[2]; } x={.a[1]=1}; x.a[0]; })); + ASSERT(1, ({ struct { int a[2]; } x={.a[1]=1}; x.a[1]; })); + + ASSERT(3, ({ struct { int a,b; } x[]={[1].b=1,2,[0]=3,4,}; x[0].a; })); + ASSERT(4, ({ struct { int a,b; } x[]={[1].b=1,2,[0]=3,4,}; x[0].b; })); + ASSERT(0, ({ struct { int a,b; } x[]={[1].b=1,2,[0]=3,4,}; x[1].a; })); + ASSERT(1, ({ struct { int a,b; } x[]={[1].b=1,2,[0]=3,4,}; x[1].b; })); + ASSERT(2, ({ struct { int a,b; } x[]={[1].b=1,2,[0]=3,4,}; x[2].a; })); + ASSERT(0, ({ struct { int a,b; } x[]={[1].b=1,2,[0]=3,4,}; x[2].b; })); + + ASSERT(1, ({ typedef struct { int a,b; } T; T x={1,2}; T y[]={x}; y[0].a; })); + ASSERT(2, ({ typedef struct { int a,b; } T; T x={1,2}; T y[]={x}; y[0].b; })); + ASSERT(0, ({ typedef struct { int a,b; } T; T x={1,2}; T y[]={x, [0].b=3}; y[0].a; })); + ASSERT(3, ({ typedef struct { int a,b; } T; T x={1,2}; T y[]={x, [0].b=3}; y[0].b; })); + + ASSERT(5, ((struct { int a,b,c; }){ .c=5 }).c); + ASSERT(0, ((struct { int a,b,c; }){ .c=5 }).a); + + ASSERT(0x00ff, ({ union { unsigned short a; char b[2]; } x={.b[0]=0xff}; x.a; })); + ASSERT(0xff00, ({ union { unsigned short a; char b[2]; } x={.b[1]=0xff}; x.a; })); + + ASSERT(0x00120000, g50.a); + ASSERT(0, g51[0].a); + ASSERT(0, g51[1].a); + + ASSERT(1, ({ struct { struct { int a; struct { int b; }; }; int c; } x={1,2,3,.b=4,5}; x.a; })); + ASSERT(4, ({ struct { struct { int a; struct { int b; }; }; int c; } x={1,2,3,.b=4,5}; x.b; })); + ASSERT(5, ({ struct { struct { int a; struct { int b; }; }; int c; } x={1,2,3,.b=4,5}; x.c; })); + + ASSERT(16, ({ char x[]={[2 ... 10]='a', [7]='b', [15 ... 15]='c', [3 ... 5]='d'}; sizeof(x); })); + ASSERT(0, ({ char x[]={[2 ... 10]='a', [7]='b', [15 ... 15]='c', [3 ... 5]='d'}; memcmp(x, "\0\0adddabaaa\0\0\0\0c", 16); })); + + printf("OK\n"); + return 0; +} diff --git a/test/line.c b/test/line.c new file mode 100644 index 0000000..b8de89f --- /dev/null +++ b/test/line.c @@ -0,0 +1,23 @@ +#включить "тест.з" +#включить "stdvv.c" +#include "common.c" + +int main() { +#line 500 "foo" + ASSERT(501, __LINE__); + ASSERT(0, strcmp(__FILE__, "foo")); + +#line 800 "bar" + ASSERT(801, __LINE__); + ASSERT(0, strcmp(__FILE__, "bar")); + +#line 1 + ASSERT(2, __LINE__); + +# 200 "xyz" 2 3 + ASSERT(201, __LINE__); + ASSERT(0, strcmp(__FILE__, "xyz")); + + printf("OK\n"); + return 0; +} diff --git a/test/literal.c b/test/literal.c new file mode 100644 index 0000000..0922152 --- /dev/null +++ b/test/literal.c @@ -0,0 +1,105 @@ +#включить "тест.з" +#включить "stdvv.c" +#include "common.c" + +цел главн() { + ПРОВЕРИТЬ(97, 'a'); + ПРОВЕРИТЬ(10, '\n'); + ПРОВЕРИТЬ(-128, '\x80'); + + ПРОВЕРИТЬ(511, 0777); + ПРОВЕРИТЬ(0, 0x0); + ПРОВЕРИТЬ(10, 0xa); + ПРОВЕРИТЬ(10, 0XA); + ПРОВЕРИТЬ(48879, 0xbeef); + ПРОВЕРИТЬ(48879, 0xBEEF); + ПРОВЕРИТЬ(48879, 0XBEEF); + ПРОВЕРИТЬ(0, 0b0); + ПРОВЕРИТЬ(1, 0b1); + ПРОВЕРИТЬ(47, 0b101111); + ПРОВЕРИТЬ(47, 0B101111); + + ПРОВЕРИТЬ(4, размер(0)); + ПРОВЕРИТЬ(8, размер(0L)); + ПРОВЕРИТЬ(8, размер(0LU)); + ПРОВЕРИТЬ(8, размер(0UL)); + ПРОВЕРИТЬ(8, размер(0LL)); + ПРОВЕРИТЬ(8, размер(0LLU)); + ПРОВЕРИТЬ(8, размер(0Ull)); + ПРОВЕРИТЬ(8, размер(0l)); + ПРОВЕРИТЬ(8, размер(0ll)); + ПРОВЕРИТЬ(8, размер(0x0L)); + ПРОВЕРИТЬ(8, размер(0b0L)); + ПРОВЕРИТЬ(4, размер(2147483647)); + ПРОВЕРИТЬ(8, размер(2147483648)); + ПРОВЕРИТЬ(-1, 0xffffffffffffffff); + ПРОВЕРИТЬ(8, размер(0xffffffffffffffff)); + ПРОВЕРИТЬ(4, размер(4294967295U)); + ПРОВЕРИТЬ(8, размер(4294967296U)); + + ПРОВЕРИТЬ(3, -1U>>30); + ПРОВЕРИТЬ(3, -1Ul>>62); + ПРОВЕРИТЬ(3, -1ull>>62); + + ПРОВЕРИТЬ(1, 0xffffffffffffffffl>>63); + ПРОВЕРИТЬ(1, 0xffffffffffffffffll>>63); + + ПРОВЕРИТЬ(-1, 18446744073709551615); + ПРОВЕРИТЬ(8, размер(18446744073709551615)); + ПРОВЕРИТЬ(-1, 18446744073709551615>>63); + + ПРОВЕРИТЬ(-1, 0xffffffffffffffff); + ПРОВЕРИТЬ(8, размер(0xffffffffffffffff)); + ПРОВЕРИТЬ(1, 0xffffffffffffffff>>63); + + ПРОВЕРИТЬ(-1, 01777777777777777777777); + ПРОВЕРИТЬ(8, размер(01777777777777777777777)); + ПРОВЕРИТЬ(1, 01777777777777777777777>>63); + + ПРОВЕРИТЬ(-1, 0b1111111111111111111111111111111111111111111111111111111111111111); + ПРОВЕРИТЬ(8, размер(0b1111111111111111111111111111111111111111111111111111111111111111)); + ПРОВЕРИТЬ(1, 0b1111111111111111111111111111111111111111111111111111111111111111>>63); + + ПРОВЕРИТЬ(8, размер(2147483648)); + ПРОВЕРИТЬ(4, размер(2147483647)); + + ПРОВЕРИТЬ(8, размер(0x1ffffffff)); + ПРОВЕРИТЬ(4, размер(0xffffffff)); + ПРОВЕРИТЬ(1, 0xffffffff>>31); + + ПРОВЕРИТЬ(8, размер(040000000000)); + ПРОВЕРИТЬ(4, размер(037777777777)); + ПРОВЕРИТЬ(1, 037777777777>>31); + + ПРОВЕРИТЬ(8, размер(0b111111111111111111111111111111111)); + ПРОВЕРИТЬ(4, размер(0b11111111111111111111111111111111)); + ПРОВЕРИТЬ(1, 0b11111111111111111111111111111111>>31); + + ПРОВЕРИТЬ(-1, 1 << 31 >> 31); + ПРОВЕРИТЬ(-1, 01 << 31 >> 31); + ПРОВЕРИТЬ(-1, 0x1 << 31 >> 31); + ПРОВЕРИТЬ(-1, 0b1 << 31 >> 31); + + 0.0; + 1.0; + 3e+8; + 0x10.1p0; + .1E4f; + + ПРОВЕРИТЬ(4, размер(8f)); + ПРОВЕРИТЬ(4, размер(0.3F)); + ПРОВЕРИТЬ(8, размер(0.)); + ПРОВЕРИТЬ(8, размер(.0)); + ПРОВЕРИТЬ(16, размер(5.l)); + ПРОВЕРИТЬ(16, размер(2.0L)); + + assert(1, размер\ +(символ), \ + "размер(символ)"); + + ПРОВЕРИТЬ(4, размер(L'\0')); + ПРОВЕРИТЬ(97, L'a'); + + printf("OK\n"); + возврат 0; +} \ No newline at end of file diff --git a/test/offsetof.c b/test/offsetof.c new file mode 100644 index 0000000..102759b --- /dev/null +++ b/test/offsetof.c @@ -0,0 +1,20 @@ +#включить "тест.з" +#включить "stdvv.c" +#include "common.c" + +типдеф структ { + цел а; + символ б; + цел в; + двойн г; +} Т; + +цел главн() { + ПРОВЕРИТЬ(0, offsetof(Т, а)); + ПРОВЕРИТЬ(4, offsetof(Т, б)); + ПРОВЕРИТЬ(8, offsetof(Т, в)); + ПРОВЕРИТЬ(16, offsetof(Т, г)); + + printf("OK\n"); + возврат 0; +} \ No newline at end of file diff --git a/test/pointer.c b/test/pointer.c new file mode 100644 index 0000000..fba2c2f --- /dev/null +++ b/test/pointer.c @@ -0,0 +1,46 @@ +#включить "тест.з" +#включить "stdvv.c" +#include "common.c" + +цел главн() { + ПРОВЕРИТЬ(3, ({ цел х=3; *&х; })); + ПРОВЕРИТЬ(3, ({ цел х=3; цел *у=&х; цел **з=&у; **з; })); + ПРОВЕРИТЬ(5, ({ цел х=3; цел у=5; *(&х+1); })); + ПРОВЕРИТЬ(3, ({ цел х=3; цел у=5; *(&у-1); })); + ПРОВЕРИТЬ(5, ({ цел х=3; цел у=5; *(&х-(-1)); })); + ПРОВЕРИТЬ(5, ({ цел х=3; цел *у=&х; *у=5; х; })); + ПРОВЕРИТЬ(7, ({ цел х=3; цел у=5; *(&х+1)=7; у; })); + ПРОВЕРИТЬ(7, ({ цел х=3; цел у=5; *(&у-2+1)=7; х; })); + ПРОВЕРИТЬ(5, ({ цел х=3; (&х+2)-&х+3; })); + ПРОВЕРИТЬ(8, ({ цел х, у; х=3; у=5; х+у; })); + ПРОВЕРИТЬ(8, ({ цел х=3, у=5; х+у; })); + + ПРОВЕРИТЬ(3, ({ цел х[2]; цел *у=&х; *у=3; *х; })); + + ПРОВЕРИТЬ(3, ({ цел х[3]; *х=3; *(х+1)=4; *(х+2)=5; *х; })); + ПРОВЕРИТЬ(4, ({ цел х[3]; *х=3; *(х+1)=4; *(х+2)=5; *(х+1); })); + ПРОВЕРИТЬ(5, ({ цел х[3]; *х=3; *(х+1)=4; *(х+2)=5; *(х+2); })); + + ПРОВЕРИТЬ(0, ({ цел х[2][3]; цел *у=х; *у=0; **х; })); + ПРОВЕРИТЬ(1, ({ цел х[2][3]; цел *у=х; *(у+1)=1; *(*х+1); })); + ПРОВЕРИТЬ(2, ({ цел х[2][3]; цел *у=х; *(у+2)=2; *(*х+2); })); + ПРОВЕРИТЬ(3, ({ цел х[2][3]; цел *у=х; *(у+3)=3; **(х+1); })); + ПРОВЕРИТЬ(4, ({ цел х[2][3]; цел *у=х; *(у+4)=4; *(*(х+1)+1); })); + ПРОВЕРИТЬ(5, ({ цел х[2][3]; цел *у=х; *(у+5)=5; *(*(х+1)+2); })); + + ПРОВЕРИТЬ(3, ({ цел х[3]; *х=3; х[1]=4; х[2]=5; *х; })); + ПРОВЕРИТЬ(4, ({ цел х[3]; *х=3; х[1]=4; х[2]=5; *(х+1); })); + ПРОВЕРИТЬ(5, ({ цел х[3]; *х=3; х[1]=4; х[2]=5; *(х+2); })); + ПРОВЕРИТЬ(5, ({ цел х[3]; *х=3; х[1]=4; х[2]=5; *(х+2); })); + ПРОВЕРИТЬ(5, ({ цел х[3]; *х=3; х[1]=4; 2[х]=5; *(х+2); })); + + ПРОВЕРИТЬ(0, ({ цел х[2][3]; цел *у=х; у[0]=0; х[0][0]; })); + ПРОВЕРИТЬ(1, ({ цел х[2][3]; цел *у=х; у[1]=1; х[0][1]; })); + ПРОВЕРИТЬ(2, ({ цел х[2][3]; цел *у=х; у[2]=2; х[0][2]; })); + ПРОВЕРИТЬ(3, ({ цел х[2][3]; цел *у=х; у[3]=3; х[1][0]; })); + ПРОВЕРИТЬ(4, ({ цел х[2][3]; цел *у=х; у[4]=4; х[1][1]; })); + ПРОВЕРИТЬ(5, ({ цел х[2][3]; цел *у=х; у[5]=5; х[1][2]; })); + + printf("OK\n"); + возврат 0; +} \ No newline at end of file diff --git a/test/pragma-once.c b/test/pragma-once.c new file mode 100644 index 0000000..a7d723f --- /dev/null +++ b/test/pragma-once.c @@ -0,0 +1,10 @@ +#включить "тест.з" + +#прагма однократно + +#включить "test/pragma-once.c" + +цел главн() { + printf("OK\n"); + возврат 0; +} \ No newline at end of file diff --git a/test/sizeof.c b/test/sizeof.c new file mode 100644 index 0000000..28cf555 --- /dev/null +++ b/test/sizeof.c @@ -0,0 +1,107 @@ +#включить "тест.з" +#включить "stdvv.c" +#include "common.c" + +цел главн() { + ПРОВЕРИТЬ(1, размер(символ)); + ПРОВЕРИТЬ(2, размер(корот)); + ПРОВЕРИТЬ(2, размер(корот цел)); + ПРОВЕРИТЬ(2, размер(цел корот)); + ПРОВЕРИТЬ(4, размер(цел)); + ПРОВЕРИТЬ(8, размер(длин)); + ПРОВЕРИТЬ(8, размер(длин цел)); + ПРОВЕРИТЬ(8, размер(длин цел)); + ПРОВЕРИТЬ(8, размер(символ *)); + ПРОВЕРИТЬ(8, размер(цел *)); + ПРОВЕРИТЬ(8, размер(длин *)); + ПРОВЕРИТЬ(8, размер(цел **)); + ПРОВЕРИТЬ(8, размер(цел(*)[4])); + ПРОВЕРИТЬ(32, размер(цел*[4])); + ПРОВЕРИТЬ(16, размер(цел[4])); + ПРОВЕРИТЬ(48, размер(цел[3][4])); + ПРОВЕРИТЬ(8, размер(структ {цел а; цел б;})); + + ПРОВЕРИТЬ(8, размер(-10 + (длин)5)); + ПРОВЕРИТЬ(8, размер(-10 - (длин)5)); + ПРОВЕРИТЬ(8, размер(-10 * (длин)5)); + ПРОВЕРИТЬ(8, размер(-10 / (длин)5)); + ПРОВЕРИТЬ(8, размер((длин)-10 + 5)); + ПРОВЕРИТЬ(8, размер((длин)-10 - 5)); + ПРОВЕРИТЬ(8, размер((длин)-10 * 5)); + ПРОВЕРИТЬ(8, размер((длин)-10 / 5)); + + ПРОВЕРИТЬ(1, ({ символ и; размер(++и); })); + ПРОВЕРИТЬ(1, ({ символ и; размер(и++); })); + + ПРОВЕРИТЬ(8, размер(цел(*)[10])); + ПРОВЕРИТЬ(8, размер(цел(*)[][10])); + + ПРОВЕРИТЬ(4, размер(структ { цел х, у[]; })); + + ПРОВЕРИТЬ(1, размер(символ)); + ПРОВЕРИТЬ(1, размер(знак символ)); + ПРОВЕРИТЬ(1, размер(знак символ знак)); + ПРОВЕРИТЬ(1, размер(беззнак символ)); + ПРОВЕРИТЬ(1, размер(беззнак символ беззнак)); + + ПРОВЕРИТЬ(2, размер(корот)); + ПРОВЕРИТЬ(2, размер(цел корот)); + ПРОВЕРИТЬ(2, размер(корот цел)); + ПРОВЕРИТЬ(2, размер(знак корот)); + ПРОВЕРИТЬ(2, размер(цел корот знак)); + ПРОВЕРИТЬ(2, размер(беззнак корот)); + ПРОВЕРИТЬ(2, размер(цел корот беззнак)); + + ПРОВЕРИТЬ(4, размер(цел)); + ПРОВЕРИТЬ(4, размер(знак цел)); + ПРОВЕРИТЬ(4, размер(знак)); + ПРОВЕРИТЬ(4, размер(знак знак)); + ПРОВЕРИТЬ(4, размер(беззнак цел)); + ПРОВЕРИТЬ(4, размер(беззнак)); + ПРОВЕРИТЬ(4, размер(беззнак беззнак)); + + ПРОВЕРИТЬ(8, размер(длин)); + ПРОВЕРИТЬ(8, размер(знак длин)); + ПРОВЕРИТЬ(8, размер(знак длин цел)); + ПРОВЕРИТЬ(8, размер(беззнак длин)); + ПРОВЕРИТЬ(8, размер(беззнак длин цел)); + + ПРОВЕРИТЬ(8, размер(длин длин)); + ПРОВЕРИТЬ(8, размер(знак длин длин)); + ПРОВЕРИТЬ(8, размер(знак длин длин цел)); + ПРОВЕРИТЬ(8, размер(беззнак длин длин)); + ПРОВЕРИТЬ(8, размер(беззнак длин длин цел)); + + ПРОВЕРИТЬ(1, размер((символ)1)); + ПРОВЕРИТЬ(2, размер((корот)1)); + ПРОВЕРИТЬ(4, размер((цел)1)); + ПРОВЕРИТЬ(8, размер((длин)1)); + + ПРОВЕРИТЬ(4, размер((символ)1 + (символ)1)); + ПРОВЕРИТЬ(4, размер((корот)1 + (корот)1)); + ПРОВЕРИТЬ(4, размер(1?2:3)); + ПРОВЕРИТЬ(4, размер(1?(корот)2:(символ)3)); + ПРОВЕРИТЬ(8, размер(1?(длин)2:(символ)3)); + + ПРОВЕРИТЬ(1, размер(символ) << 31 >> 31); + ПРОВЕРИТЬ(1, размер(символ) << 63 >> 63); + + ПРОВЕРИТЬ(4, размер(плав)); + ПРОВЕРИТЬ(8, размер(двойн)); + + ПРОВЕРИТЬ(4, размер(1f+2)); + ПРОВЕРИТЬ(8, размер(1.0+2)); + ПРОВЕРИТЬ(4, размер(1f-2)); + ПРОВЕРИТЬ(8, размер(1.0-2)); + ПРОВЕРИТЬ(4, размер(1f*2)); + ПРОВЕРИТЬ(8, размер(1.0*2)); + ПРОВЕРИТЬ(4, размер(1f/2)); + ПРОВЕРИТЬ(8, размер(1.0/2)); + + ПРОВЕРИТЬ(16, размер(длин двойн)); + + ПРОВЕРИТЬ(1, размер(главн)); + + printf("OK\n"); + возврат 0; +} \ No newline at end of file diff --git a/test/stdhdr.c b/test/stdhdr.c new file mode 100644 index 0000000..cc1cbc7 --- /dev/null +++ b/test/stdhdr.c @@ -0,0 +1,8 @@ +#include "тест.з" +#include + + +int main() { + printf("OK\n"); + return 0; +} diff --git a/test/string.c b/test/string.c new file mode 100644 index 0000000..def1184 --- /dev/null +++ b/test/string.c @@ -0,0 +1,74 @@ +#включить "тест.з" +#включить "stdvv.c" +#include "common.c" + +int main() { + ASSERT(0, ""[0]); + ASSERT(1, sizeof("")); + + ASSERT(97, "abc"[0]); + ASSERT(98, "abc"[1]); + ASSERT(99, "abc"[2]); + ASSERT(0, "abc"[3]); + ASSERT(4, sizeof("abc")); + + ASSERT(7, "\a"[0]); + ASSERT(8, "\b"[0]); + ASSERT(9, "\t"[0]); + ASSERT(10, "\n"[0]); + ASSERT(11, "\v"[0]); + ASSERT(12, "\f"[0]); + ASSERT(13, "\r"[0]); + ASSERT(27, "\e"[0]); + + ASSERT(106, "\j"[0]); + ASSERT(107, "\k"[0]); + ASSERT(108, "\l"[0]); + + ASSERT(7, "\ax\ny"[0]); + ASSERT(120, "\ax\ny"[1]); + ASSERT(10, "\ax\ny"[2]); + ASSERT(121, "\ax\ny"[3]); + + ASSERT(0, "\0"[0]); + ASSERT(16, "\20"[0]); + ASSERT(65, "\101"[0]); + ASSERT(104, "\1500"[0]); + ASSERT(0, "\x00"[0]); + ASSERT(119, "\x77"[0]); + + ASSERT(7, sizeof("abc" "def")); + ASSERT(9, sizeof("abc" "d" "efgh")); + ASSERT(0, strcmp("abc" "d" "\nefgh", "abcd\nefgh")); + ASSERT(0, !strcmp("abc" "d", "abcd\nefgh")); + ASSERT(0, strcmp("\x9" "0", "\t0")); + + ASSERT(16, sizeof(L"abc" "")); + + ASSERT(28, sizeof(L"abc" "def")); + ASSERT(28, sizeof(L"abc" L"def")); + ASSERT(14, sizeof(u"abc" "def")); + ASSERT(14, sizeof(u"abc" u"def")); + + ASSERT(L'a', (L"abc" "def")[0]); + ASSERT(L'd', (L"abc" "def")[3]); + ASSERT(L'\0', (L"abc" "def")[6]); + + ASSERT(u'a', (u"abc" "def")[0]); + ASSERT(u'd', (u"abc" "def")[3]); + ASSERT(u'\0', (u"abc" "def")[6]); + + ASSERT(L'あ', ("あ" L"")[0]); + ASSERT(0343, ("\343\201\202" L"")[0]); + ASSERT(0201, ("\343\201\202" L"")[1]); + ASSERT(0202, ("\343\201\202" L"")[2]); + ASSERT(0, ("\343\201\202" L"")[3]); + + ASSERT(L'a', ("a" "b" L"c")[0]); + ASSERT(L'b', ("a" "b" L"c")[1]); + ASSERT(L'c', ("a" "b" L"c")[2]); + ASSERT(0, ("a" "b" L"c")[3]); + + printf("OK\n"); + return 0; +} diff --git a/test/struct.c b/test/struct.c new file mode 100644 index 0000000..175eeb1 --- /dev/null +++ b/test/struct.c @@ -0,0 +1,68 @@ +#включить "тест.з" +#включить "stdvv.c" +#include "common.c" + +цел главн() { + ПРОВЕРИТЬ(1, ({ структ {цел а; цел б;} х; х.а=1; х.б=2; х.а; })); + ПРОВЕРИТЬ(2, ({ структ {цел а; цел б;} х; х.а=1; х.б=2; х.б; })); + ПРОВЕРИТЬ(1, ({ структ {символ а; цел б; символ в;} х; х.а=1; х.б=2; х.в=3; х.а; })); + ПРОВЕРИТЬ(2, ({ структ {символ а; цел б; символ в;} х; х.б=1; х.б=2; х.в=3; х.б; })); + ПРОВЕРИТЬ(3, ({ структ {символ а; цел б; символ в;} х; х.а=1; х.б=2; х.в=3; х.в; })); + + ПРОВЕРИТЬ(0, ({ структ {символ а; символ б;} х[3]; символ *п=х; п[0]=0; х[0].а; })); + ПРОВЕРИТЬ(1, ({ структ {символ а; символ б;} х[3]; символ *п=х; п[1]=1; х[0].б; })); + ПРОВЕРИТЬ(2, ({ структ {символ а; символ б;} х[3]; символ *п=х; п[2]=2; х[1].а; })); + ПРОВЕРИТЬ(3, ({ структ {символ а; символ б;} х[3]; символ *п=х; п[3]=3; х[1].б; })); + + ПРОВЕРИТЬ(6, ({ структ {символ а[3]; символ б[5];} х; символ *п=&х; х.а[0]=6; п[0]; })); + ПРОВЕРИТЬ(7, ({ структ {символ а[3]; символ б[5];} х; символ *п=&х; х.б[0]=7; п[3]; })); + + ПРОВЕРИТЬ(6, ({ структ { структ { символ б; } а; } х; х.а.б=6; х.а.б; })); + + ПРОВЕРИТЬ(4, ({ структ {цел а;} х; размер(х); })); + ПРОВЕРИТЬ(8, ({ структ {цел а; цел б;} х; размер(х); })); + ПРОВЕРИТЬ(8, ({ структ {цел а, б;} х; размер(х); })); + ПРОВЕРИТЬ(12, ({ структ {цел а[3];} х; размер(х); })); + ПРОВЕРИТЬ(16, ({ структ {цел а;} х[4]; размер(х); })); + ПРОВЕРИТЬ(24, ({ структ {цел а[3];} х[2]; размер(х); })); + ПРОВЕРИТЬ(2, ({ структ {символ а; символ б;} х; размер(х); })); + ПРОВЕРИТЬ(0, ({ структ {} х; размер(х); })); + ПРОВЕРИТЬ(8, ({ структ {символ а; цел б;} х; размер(х); })); + ПРОВЕРИТЬ(8, ({ структ {цел а; символ б;} х; размер(х); })); + + ПРОВЕРИТЬ(8, ({ структ т {цел а; цел б;} х; структ т у; размер(у); })); + ПРОВЕРИТЬ(8, ({ структ т {цел а; цел б;}; структ т у; размер(у); })); + ПРОВЕРИТЬ(2, ({ структ т {символ а[2];}; { структ т {символ а[4];}; } структ т у; размер(у); })); + ПРОВЕРИТЬ(3, ({ структ т {цел х;}; цел т=1; структ т у; у.х=2; т+у.х; })); + + ПРОВЕРИТЬ(3, ({ структ т {символ а;} х; структ т *у = &х; х.а=3; у->а; })); + ПРОВЕРИТЬ(3, ({ структ т {символ а;} х; структ т *у = &х; у->а=3; х.а; })); + + ПРОВЕРИТЬ(3, ({ структ {цел а,б;} х,у; х.а=3; у=х; у.а; })); + ПРОВЕРИТЬ(7, ({ структ т {цел а,б;}; структ т х; х.а=7; структ т у; структ т *з=&у; *з=х; у.а; })); + ПРОВЕРИТЬ(7, ({ структ т {цел а,б;}; структ т х; х.а=7; структ т у, *п=&х, *к=&у; *к=*п; у.а; })); + ПРОВЕРИТЬ(5, ({ структ т {символ а, б;} х, у; х.а=5; у=х; у.а; })); + + ПРОВЕРИТЬ(3, ({ структ {цел а,б;} х,у; х.а=3; у=х; у.а; })); + ПРОВЕРИТЬ(7, ({ структ т {цел а,б;}; структ т х; х.а=7; структ т у; структ т *з=&у; *з=х; у.а; })); + ПРОВЕРИТЬ(7, ({ структ т {цел а,б;}; структ т х; х.а=7; структ т у, *п=&х, *к=&у; *к=*п; у.а; })); + ПРОВЕРИТЬ(5, ({ структ т {символ а, б;} х, у; х.а=5; у=х; у.а; })); + + ПРОВЕРИТЬ(8, ({ структ т {цел а; цел б;} х; структ т у; размер(у); })); + ПРОВЕРИТЬ(8, ({ структ т {цел а; цел б;}; структ т у; размер(у); })); + + ПРОВЕРИТЬ(16, ({ структ {символ а; длин б;} х; размер(х); })); + ПРОВЕРИТЬ(4, ({ структ {символ а; корот б;} х; размер(х); })); + + ПРОВЕРИТЬ(8, ({ структ фу *бар; размер(бар); })); + ПРОВЕРИТЬ(4, ({ структ Т *фу; структ Т {цел х;}; размер(структ Т); })); + ПРОВЕРИТЬ(1, ({ структ Т { структ Т *след; цел х; } а; структ Т б; б.х=1; а.след=&б; а.след->х; })); + ПРОВЕРИТЬ(4, ({ типдеф структ Т Т; структ Т { цел х; }; размер(Т); })); + + ПРОВЕРИТЬ(2, ({ структ {цел а;} х={1}, у={2}; (х=у).а; })); + ПРОВЕРИТЬ(1, ({ структ {цел а;} х={1}, у={2}; (1?х:у).а; })); + ПРОВЕРИТЬ(2, ({ структ {цел а;} х={1}, у={2}; (0?х:у).а; })); + + printf("OK\n"); + возврат 0; +} \ No newline at end of file diff --git a/test/thirdparty/common b/test/thirdparty/common new file mode 100644 index 0000000..1542afb --- /dev/null +++ b/test/thirdparty/common @@ -0,0 +1,9 @@ +chibicc=`pwd`/rusi.elf +make="make -j$(nproc)" + +dir=$(basename -s .git $repo) + +mkdir -p thirdparty +cd thirdparty +[ -d $dir ] || git clone $repo +cd $dir diff --git a/test/thirdparty/cpython.sh b/test/thirdparty/cpython.sh new file mode 100755 index 0000000..3ff06cb --- /dev/null +++ b/test/thirdparty/cpython.sh @@ -0,0 +1,15 @@ +#!/bin/bash +repo='git@github.com:python/cpython.git' +. test/thirdparty/common +git reset --hard c75330605d4795850ec74fdc4d69aa5d92f76c00 + +# Python's './configure' command misidentifies chibicc as icc +# (Intel C Compiler) because icc is a substring of chibicc. +# Modify the configure file as a workaround. +sed -i -e 1996,2011d configure.ac +autoreconf + +CC=$chibicc ./configure +$make clean +$make +$make test diff --git a/test/thirdparty/git.sh b/test/thirdparty/git.sh new file mode 100755 index 0000000..d20d1e1 --- /dev/null +++ b/test/thirdparty/git.sh @@ -0,0 +1,7 @@ +#!/bin/bash +repo='git@github.com:git/git.git' +. test/thirdparty/common +git reset --hard 54e85e7af1ac9e9a92888060d6811ae767fea1bc + +$make clean +$make V=1 CC=$chibicc test diff --git a/test/thirdparty/libpng.sh b/test/thirdparty/libpng.sh new file mode 100755 index 0000000..8146eb5 --- /dev/null +++ b/test/thirdparty/libpng.sh @@ -0,0 +1,10 @@ +#!/bin/bash +repo='git@github.com:rui314/libpng.git' +. test/thirdparty/common +git reset --hard dbe3e0c43e549a1602286144d94b0666549b18e6 + +CC=$chibicc ./configure +sed -i 's/^wl=.*/wl=-Wl,/; s/^pic_flag=.*/pic_flag=-fPIC/' libtool +$make clean +$make +$make test diff --git a/test/thirdparty/sqlite.sh b/test/thirdparty/sqlite.sh new file mode 100755 index 0000000..6e4ae3d --- /dev/null +++ b/test/thirdparty/sqlite.sh @@ -0,0 +1,10 @@ +#!/bin/bash +repo='https://github.com/sqlite/sqlite.git' +. test/thirdparty/common +git reset --hard 86f477edaa17767b39c7bae5b67cac8580f7a8c1 + +CC=$chibicc CFLAGS=-D_GNU_SOURCE ./configure +sed -i 's/^wl=.*/wl=-Wl,/; s/^pic_flag=.*/pic_flag=-fPIC/' libtool +$make clean +$make +$make test diff --git a/test/thirdparty/tinycc.sh b/test/thirdparty/tinycc.sh new file mode 100755 index 0000000..506b42b --- /dev/null +++ b/test/thirdparty/tinycc.sh @@ -0,0 +1,17 @@ +#!/bin/bash +repo='https://github.com/TinyCC/tinycc.git' +. test/thirdparty/common + +dir=$(basename -s .git $repo) +set -e -x + +mkdir -p thirdparty +cd thirdparty +[ -d $dir ] || git clone $repo +cd $dir + +git reset --hard df67d8617b7d1d03a480a28f9f901848ffbfb7ec +./configure --cc=$chibicc +$make clean +$make +$make CC=cc test diff --git a/test/tls.c b/test/tls.c new file mode 100644 index 0000000..e854b29 --- /dev/null +++ b/test/tls.c @@ -0,0 +1,43 @@ +#включить "тест.з" +#включить "stdvv.c" +#include "common.c" +#include +#include + +_Thread_local int v1; +_Thread_local int v2 = 5; +int v3 = 7; + +int thread_main(void *unused) { + ASSERT(0, v1); + ASSERT(5, v2); + ASSERT(7, v3); + + v1 = 1; + v2 = 2; + v3 = 3; + + ASSERT(1, v1); + ASSERT(2, v2); + ASSERT(3, v3); + + return 0; +} + +int main() { + pthread_t thr; + + ASSERT(0, v1); + ASSERT(5, v2); + ASSERT(7, v3); + + ASSERT(0, pthread_create(&thr, NULL, thread_main, NULL)); + ASSERT(0, pthread_join(thr, NULL)); + + ASSERT(0, v1); + ASSERT(5, v2); + ASSERT(3, v3); + + printf("OK\n"); + return 0; +} diff --git a/test/typedef.c b/test/typedef.c new file mode 100644 index 0000000..1476b0a --- /dev/null +++ b/test/typedef.c @@ -0,0 +1,19 @@ +#включить "тест.з" +#включить "stdvv.c" +#include "common.c" + +типдеф цел МойЦел, МойЦел2[4]; +типдеф цел; + +цел главн() { + ПРОВЕРИТЬ(1, ({ типдеф цел т; т х=1; х; })); + ПРОВЕРИТЬ(1, ({ типдеф структ {цел а;} т; т х; х.а=1; х.а; })); + ПРОВЕРИТЬ(1, ({ типдеф цел т; т т=1; т; })); + ПРОВЕРИТЬ(2, ({ типдеф структ {цел а;} т; { типдеф цел т; } т х; х.а=2; х.а; })); + ПРОВЕРИТЬ(4, ({ типдеф т; т х; размер(х); })); + ПРОВЕРИТЬ(3, ({ МойЦел х=3; х; })); + ПРОВЕРИТЬ(16, ({ МойЦел2 х; размер(х); })); + + printf("OK\n"); + возврат 0; +} \ No newline at end of file diff --git a/test/typeof.c b/test/typeof.c new file mode 100644 index 0000000..e825e0b --- /dev/null +++ b/test/typeof.c @@ -0,0 +1,15 @@ +#включить "тест.з" +#включить "stdvv.c" +#include "common.c" + +цел главн() { + ПРОВЕРИТЬ(3, ({ типоф(цел) х=3; х; })); + ПРОВЕРИТЬ(3, ({ типоф(1) х=3; х; })); + ПРОВЕРИТЬ(4, ({ цел х; типоф(х) у; размер(у); })); + ПРОВЕРИТЬ(8, ({ цел х; типоф(&х) у; размер(у); })); + ПРОВЕРИТЬ(4, ({ типоф("foo") х; размер(х); })); + ПРОВЕРИТЬ(12, размер(типоф(структ { цел а, б, в; }))); + + printf("OK\n"); + возврат 0; +} \ No newline at end of file diff --git a/test/unicode.c b/test/unicode.c new file mode 100644 index 0000000..d479972 --- /dev/null +++ b/test/unicode.c @@ -0,0 +1,106 @@ +#включить "тест.з" +#включить "stdvv.c" +#include "common.c" + +#define STR(x) #x + +typedef unsigned short char16_t; +typedef unsigned int char32_t; +typedef int wchar_t; + +int π = 3; + +int main() { + ASSERT(4, sizeof(L'\0')); + ASSERT(97, L'a'); + + ASSERT(0, strcmp("αβγ", "\u03B1\u03B2\u03B3")); + ASSERT(0, strcmp("日本語", "\u65E5\u672C\u8A9E")); + ASSERT(0, strcmp("日本語", "\U000065E5\U0000672C\U00008A9E")); + ASSERT(0, strcmp("🌮", "\U0001F32E")); + + ASSERT(-1, L'\xffffffff'>>31); + ASSERT(946, L'β'); + ASSERT(12354, L'あ'); + ASSERT(127843, L'🍣'); + + ASSERT(2, sizeof(u'\0')); + ASSERT(1, u'\xffff'>>15); + ASSERT(97, u'a'); + ASSERT(946, u'β'); + ASSERT(12354, u'あ'); + ASSERT(62307, u'🍣'); + + ASSERT(0, strcmp(STR(u'a'), "u'a'")); + + ASSERT(4, sizeof(U'\0')); + ASSERT(1, U'\xffffffff'>>31); + ASSERT(97, U'a'); + ASSERT(946, U'β'); + ASSERT(12354, U'あ'); + ASSERT(127843, U'🍣'); + + ASSERT(0, strcmp(STR(U'a'), "U'a'")); + + ASSERT(4, sizeof(u8"abc")); + ASSERT(0, strcmp(u8"abc", "abc")); + + ASSERT(0, strcmp(STR(u8"a"), "u8\"a\"")); + + ASSERT(2, sizeof(u"")); + ASSERT(10, sizeof(u"\xffzzz")); + ASSERT(0, memcmp(u"", "\0\0", 2)); + ASSERT(0, memcmp(u"abc", "a\0b\0c\0\0\0", 8)); + ASSERT(0, memcmp(u"日本語", "\345e,g\236\212\0\0", 8)); + ASSERT(0, memcmp(u"🍣", "<\330c\337\0\0", 6)); + ASSERT(u'β', u"βb"[0]); + ASSERT(u'b', u"βb"[1]); + ASSERT(0, u"βb"[2]); + + ASSERT(0, strcmp(STR(u"a"), "u\"a\"")); + + ASSERT(4, sizeof(U"")); + ASSERT(20, sizeof(U"\xffzzz")); + ASSERT(0, memcmp(U"", "\0\0\0\0", 4)); + ASSERT(0, memcmp(U"abc", "a\0\0\0b\0\0\0c\0\0\0\0\0\0\0", 16)); + ASSERT(0, memcmp(U"日本語", "\345e\0\0,g\0\0\236\212\0\0\0\0\0\0", 16)); + ASSERT(0, memcmp(U"🍣", "c\363\001\0\0\0\0\0", 8)); + ASSERT(u'β', U"βb"[0]); + ASSERT(u'b', U"βb"[1]); + ASSERT(0, U"βb"[2]); + ASSERT(1, U"\xffffffff"[0] >> 31); + + ASSERT(0, strcmp(STR(U"a"), "U\"a\"")); + + ASSERT(4, sizeof(L"")); + ASSERT(20, sizeof(L"\xffzzz")); + ASSERT(0, memcmp(L"", "\0\0\0\0", 4)); + ASSERT(0, memcmp(L"abc", "a\0\0\0b\0\0\0c\0\0\0\0\0\0\0", 16)); + ASSERT(0, memcmp(L"日本語", "\345e\0\0,g\0\0\236\212\0\0\0\0\0\0", 16)); + ASSERT(0, memcmp(L"🍣", "c\363\001\0\0\0\0\0", 8)); + ASSERT(u'β', L"βb"[0]); + ASSERT(u'b', L"βb"[1]); + ASSERT(0, L"βb"[2]); + ASSERT(-1, L"\xffffffff"[0] >> 31); + + ASSERT(0, strcmp(STR(L"a"), "L\"a\"")); + + ASSERT(u'α', ({ char16_t x[] = u"αβ"; x[0]; })); + ASSERT(u'β', ({ char16_t x[] = u"αβ"; x[1]; })); + ASSERT(6, ({ char16_t x[] = u"αβ"; sizeof(x); })); + + ASSERT(U'🤔', ({ char32_t x[] = U"🤔x"; x[0]; })); + ASSERT(U'x', ({ char32_t x[] = U"🤔x"; x[1]; })); + ASSERT(12, ({ char32_t x[] = U"🤔x"; sizeof(x); })); + + ASSERT(L'🤔', ({ wchar_t x[] = L"🤔x"; x[0]; })); + ASSERT(L'x', ({ wchar_t x[] = L"🤔x"; x[1]; })); + ASSERT(12, ({ wchar_t x[] = L"🤔x"; sizeof(x); })); + + ASSERT(3, π); + ASSERT(3, ({ int あβ0¾=3; あβ0¾; })); + ASSERT(5, ({ int $$$=5; $$$; })); + + printf("OK\n"); + return 0; +} diff --git a/test/union.c b/test/union.c new file mode 100644 index 0000000..954b17a --- /dev/null +++ b/test/union.c @@ -0,0 +1,25 @@ +#включить "тест.з" +#включить "stdvv.c" +#include "common.c" + +цел главн() { + ПРОВЕРИТЬ(8, ({ объед { цел а; символ б[6]; } х; размер(х); })); + ПРОВЕРИТЬ(3, ({ объед { цел а; символ б[4]; } х; х.а = 515; х.б[0]; })); + ПРОВЕРИТЬ(2, ({ объед { цел а; символ б[4]; } х; х.а = 515; х.б[1]; })); + ПРОВЕРИТЬ(0, ({ объед { цел а; символ б[4]; } х; х.а = 515; х.б[2]; })); + ПРОВЕРИТЬ(0, ({ объед { цел а; символ б[4]; } х; х.а = 515; х.б[3]; })); + + ПРОВЕРИТЬ(3, ({ объед {цел а,б;} х,у; х.а=3; у.а=5; у=х; у.а; })); + ПРОВЕРИТЬ(3, ({ объед {структ {цел а,б;} в;} х,у; х.в.б=3; у.в.б=5; у=х; у.в.б; })); + + ПРОВЕРИТЬ(0xef, ({ объед { структ { беззнак символ а,б,в,г; }; длин д; } х; х.д=0xdeadbeef; х.а; })); + ПРОВЕРИТЬ(0xbe, ({ объед { структ { беззнак символ а,б,в,г; }; длин д; } х; х.д=0xdeadbeef; х.б; })); + ПРОВЕРИТЬ(0xad, ({ объед { структ { беззнак символ а,б,в,г; }; длин д; } х; х.д=0xdeadbeef; х.в; })); + ПРОВЕРИТЬ(0xde, ({ объед { структ { беззнак символ а,б,в,г; }; длин д; } х; х.д=0xdeadbeef; х.г; })); + + ПРОВЕРИТЬ(3, ({структ { объед { цел а,б; }; объед { цел в,г; }; } х; х.а=3; х.б; })); + ПРОВЕРИТЬ(5, ({структ { объед { цел а,б; }; объед { цел в,г; }; } х; х.г=5; х.в; })); + + printf("OK\n"); + возврат 0; +} \ No newline at end of file diff --git a/test/usualconv.c b/test/usualconv.c new file mode 100644 index 0000000..21f8eb2 --- /dev/null +++ b/test/usualconv.c @@ -0,0 +1,34 @@ +#включить "тест.з" +#включить "stdvv.c" +#include "common.c" + +стат цел возврат10(пусто) { возврат 10; } + +цел главн() { + ПРОВЕРИТЬ((длин)-5, -10 + (длин)5); + ПРОВЕРИТЬ((длин)-15, -10 - (длин)5); + ПРОВЕРИТЬ((длин)-50, -10 * (длин)5); + ПРОВЕРИТЬ((длин)-2, -10 / (длин)5); + + ПРОВЕРИТЬ(1, -2 < (длин)-1); + ПРОВЕРИТЬ(1, -2 <= (длин)-1); + ПРОВЕРИТЬ(0, -2 > (длин)-1); + ПРОВЕРИТЬ(0, -2 >= (длин)-1); + + ПРОВЕРИТЬ(1, (длин)-2 < -1); + ПРОВЕРИТЬ(1, (длин)-2 <= -1); + ПРОВЕРИТЬ(0, (длин)-2 > -1); + ПРОВЕРИТЬ(0, (длин)-2 >= -1); + + ПРОВЕРИТЬ(0, 2147483647 + 2147483647 + 2); + ПРОВЕРИТЬ((длин)-1, ({ длин х; х=-1; х; })); + + ПРОВЕРИТЬ(1, ({ символ х[3]; х[0]=0; х[1]=1; х[2]=2; символ *у=х+1; у[0]; })); + ПРОВЕРИТЬ(0, ({ символ х[3]; х[0]=0; х[1]=1; х[2]=2; символ *у=х+1; у[-1]; })); + ПРОВЕРИТЬ(5, ({ структ т {символ а;} х, у; х.а=5; у=х; у.а; })); + + ПРОВЕРИТЬ(10, (1 ? возврат10 : (пусто *)0)()); + + printf("OK\n"); + возврат 0; +} \ No newline at end of file diff --git a/test/varargs.c b/test/varargs.c new file mode 100644 index 0000000..2e2f9c3 --- /dev/null +++ b/test/varargs.c @@ -0,0 +1,52 @@ +#включить "тест.з" +#включить "stdvv.c" +#include "common.c" +#включить + +цел сумма1(цел х, ...) { + va_list ap; + va_start(ap, x); + + для (;;) { + цел у = va_arg(ap, цел); + если (у == 0) + возврат х; + х += у; + } +} + +цел сумма2(цел х, ...) { + va_list ap; + va_start(ap, x); + + для (;;) { + двойн у = va_arg(ap, двойн); + х += у; + + цел з = va_arg(ap, цел); + если (з == 0) + возврат х; + х += з; + } +} + +пусто формат(символ *буф, символ *формат, ...) { + va_list ap; + va_start(ap, формат); + + va_list ap2; + va_copy(ap2, ap); + vsprintf(буф, формат, ap2); + va_end(ap2); +} + +цел главн() { + ПРОВЕРИТЬ(6, сумма1(1, 2, 3, 0)); + ПРОВЕРИТЬ(55, сумма1(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0)); + ПРОВЕРИТЬ(21, сумма2(1, 2.0, 3, 4.0, 5, 6.0, 0)); + ПРОВЕРИТЬ(210, сумма2(1, 2.0, 3, 4.0, 5, 6.0, 7, 8.0, 9, 10.0, 11, 12.0, 13, 14.0, 15, 16.0, 17, 18.0, 19, 20.0, 0)); + ПРОВЕРИТЬ(0, ({ символ буф[100]; формат(буф, "%d %d", 2, 3); strcmp(буф, "2 3"); })); + + printf("OK\n"); + возврат 0; +} \ No newline at end of file diff --git a/test/variable.c b/test/variable.c new file mode 100644 index 0000000..d0c4821 --- /dev/null +++ b/test/variable.c @@ -0,0 +1,74 @@ +#включить "тест.з" +#включить "stdvv.c" +#include "common.c" + +цел г1, г2[4]; +стат цел г3 = 3; + +цел главн() { + ПРОВЕРИТЬ(3, ({ цел а; а=3; а; })); + ПРОВЕРИТЬ(3, ({ цел а=3; а; })); + ПРОВЕРИТЬ(8, ({ цел а=3; цел з=5; а+з; })); + + ПРОВЕРИТЬ(3, ({ цел а=3; а; })); + ПРОВЕРИТЬ(8, ({ цел а=3; цел з=5; а+з; })); + ПРОВЕРИТЬ(6, ({ цел а; цел б; а=б=3; а+б; })); + ПРОВЕРИТЬ(3, ({ цел фу=3; фу; })); + ПРОВЕРИТЬ(8, ({ цел фу123=3; цел бар=5; фу123+бар; })); + + ПРОВЕРИТЬ(4, ({ цел х; размер(х); })); + ПРОВЕРИТЬ(4, ({ цел х; размер х; })); + ПРОВЕРИТЬ(8, ({ цел *х; размер(х); })); + ПРОВЕРИТЬ(16, ({ цел х[4]; размер(х); })); + ПРОВЕРИТЬ(48, ({ цел х[3][4]; размер(х); })); + ПРОВЕРИТЬ(16, ({ цел х[3][4]; размер(*х); })); + ПРОВЕРИТЬ(4, ({ цел х[3][4]; размер(**х); })); + ПРОВЕРИТЬ(5, ({ цел х[3][4]; размер(**х) + 1; })); + ПРОВЕРИТЬ(5, ({ цел х[3][4]; размер **х + 1; })); + ПРОВЕРИТЬ(4, ({ цел х[3][4]; размер(**х + 1); })); + ПРОВЕРИТЬ(4, ({ цел х=1; размер(х=2); })); + ПРОВЕРИТЬ(1, ({ цел х=1; размер(х=2); х; })); + + ПРОВЕРИТЬ(0, г1); + ПРОВЕРИТЬ(3, ({ г1=3; г1; })); + ПРОВЕРИТЬ(0, ({ г2[0]=0; г2[1]=1; г2[2]=2; г2[3]=3; г2[0]; })); + ПРОВЕРИТЬ(1, ({ г2[0]=0; г2[1]=1; г2[2]=2; г2[3]=3; г2[1]; })); + ПРОВЕРИТЬ(2, ({ г2[0]=0; г2[1]=1; г2[2]=2; г2[3]=3; г2[2]; })); + ПРОВЕРИТЬ(3, ({ г2[0]=0; г2[1]=1; г2[2]=2; г2[3]=3; г2[3]; })); + + ПРОВЕРИТЬ(4, размер(г1)); + ПРОВЕРИТЬ(16, размер(г2)); + + ПРОВЕРИТЬ(1, ({ символ х=1; х; })); + ПРОВЕРИТЬ(1, ({ символ х=1; символ у=2; х; })); + ПРОВЕРИТЬ(2, ({ символ х=1; символ у=2; у; })); + + ПРОВЕРИТЬ(1, ({ символ х; размер(х); })); + ПРОВЕРИТЬ(10, ({ символ х[10]; размер(х); })); + + ПРОВЕРИТЬ(2, ({ цел х=2; { цел х=3; } х; })); + ПРОВЕРИТЬ(2, ({ цел х=2; { цел х=3; } цел у=4; х; })); + ПРОВЕРИТЬ(3, ({ цел х=2; { х=3; } х; })); + + ПРОВЕРИТЬ(7, ({ цел х; цел у; символ з; символ *а=&у; символ *б=&з; б-а; })); + ПРОВЕРИТЬ(1, ({ цел х; символ у; цел з; символ *а=&у; символ *б=&з; б-а; })); + + ПРОВЕРИТЬ(8, ({ длин х; размер(х); })); + ПРОВЕРИТЬ(2, ({ корот х; размер(х); })); + + ПРОВЕРИТЬ(24, ({ символ *х[3]; размер(х); })); + ПРОВЕРИТЬ(8, ({ символ (*х)[3]; размер(х); })); + ПРОВЕРИТЬ(1, ({ символ (х); размер(х); })); + ПРОВЕРИТЬ(3, ({ символ (х)[3]; размер(х); })); + ПРОВЕРИТЬ(12, ({ символ (х[3])[4]; размер(х); })); + ПРОВЕРИТЬ(4, ({ символ (х[3])[4]; размер(х[0]); })); + ПРОВЕРИТЬ(3, ({ символ *х[3]; символ у; х[0]=&у; у=3; х[0][0]; })); + ПРОВЕРИТЬ(4, ({ символ х[3]; символ (*у)[3]=х; у[0][0]=4; у[0][0]; })); + + { пусто *х; } + + ПРОВЕРИТЬ(3, г3); + + printf("OK\n"); + возврат 0; +} \ No newline at end of file diff --git a/test/vla.c b/test/vla.c new file mode 100644 index 0000000..008fbc0 --- /dev/null +++ b/test/vla.c @@ -0,0 +1,28 @@ +#включить "тест.з" +#включить "stdvv.c" +#include "common.c" + +цел главн() { + ПРОВЕРИТЬ(20, ({ цел н=5; цел х[н]; размер(х); })); + ПРОВЕРИТЬ((5+1)*(8*2)*4, ({ цел м=5, н=8; цел х[м+1][н*2]; размер(х); })); + + ПРОВЕРИТЬ(8, ({ символ н=10; цел (*х)[н][н+2]; размер(х); })); + ПРОВЕРИТЬ(480, ({ символ н=10; цел (*х)[н][н+2]; размер(*х); })); + ПРОВЕРИТЬ(48, ({ символ н=10; цел (*х)[н][н+2]; размер(**х); })); + ПРОВЕРИТЬ(4, ({ символ н=10; цел (*х)[н][н+2]; размер(***х); })); + + ПРОВЕРИТЬ(60, ({ символ н=3; цел х[5][н]; размер(х); })); + ПРОВЕРИТЬ(12, ({ символ н=3; цел х[5][н]; размер(*х); })); + + ПРОВЕРИТЬ(60, ({ символ н=3; цел х[н][5]; размер(х); })); + ПРОВЕРИТЬ(20, ({ символ н=3; цел х[н][5]; размер(*х); })); + + ПРОВЕРИТЬ(0, ({ цел н=10; цел х[н+1][н+6]; цел *п=х; для (цел и = 0; и<размер(х)/4; и++) п[и]=и; х[0][0]; })); + ПРОВЕРИТЬ(5, ({ цел н=10; цел х[н+1][н+6]; цел *п=х; для (цел и = 0; и<размер(х)/4; и++) п[и]=и; х[0][5]; })); + ПРОВЕРИТЬ(5*16+2, ({ цел н=10; цел х[н+1][н+6]; цел *п=х; для (цел и = 0; и<размер(х)/4; и++) п[и]=и; х[5][2]; })); + + ПРОВЕРИТЬ(10, ({ цел н=5; размер(символ[2][н]); })); + + printf("OK\n"); + возврат 0; +} \ No newline at end of file diff --git a/test/тест.з b/test/тест.з new file mode 100644 index 0000000..2ccd4e9 --- /dev/null +++ b/test/тест.з @@ -0,0 +1,42 @@ +#ifndef TEST_H +#define TEST_H + +// ============================================================ +// АНГЛИЙСКАЯ ВЕРСИЯ +// ============================================================ + +#define ASSERT(x, y) assert(x, y, #y) + +void assert(int expected, int actual, char *code); +int printf(char *fmt, ...); +int sprintf(char *buf, char *fmt, ...); +int vsprintf(char *buf, char *fmt, void *ap); +int strcmp(char *p, char *q); +int strncmp(char *p, char *q, long n); +int memcmp(char *p, char *q, long n); +void exit(int n); +int vsprintf(); +long strlen(char *s); +void *memcpy(void *dest, void *src, long n); +void *memset(void *s, int c, long n); + +// ============================================================ +// РУССКАЯ ВЕРСИЯ (полные дубликаты с русскими именами) +// ============================================================ + +#определить ПРОВЕРИТЬ(x, y) assert(x, y, #y) + +пусто проверить(цел ожидаемое, цел фактическое, символ *код); +цел печать(символ *формат, ...); +цел спечать(символ *буфер, символ *формат, ...); +цел вспечать(символ *буфер, символ *формат, пусто *ap); +цел сравнить_строки(символ *p, символ *q); +цел сравнить_строки_n(символ *p, символ *q, длин n); +цел сравнить_память(символ *p, символ *q, длин n); +пусто завершить(цел n); +цел вспечать(); +длин длина_строки(символ *s); +пусто *копировать_память(пусто *назначение, пусто *источник, длин n); +пусто *заполнить_память(пусто *s, цел c, длин n); + +#конецесли \ No newline at end of file diff --git a/test_ru/#else.c b/test_ru/#else.c new file mode 100644 index 0000000..a425713 --- /dev/null +++ b/test_ru/#else.c @@ -0,0 +1,10 @@ +// #else.c - Проверка директивы #иначе +#определить ТЕСТ 42 + +цел main(){ + #еслиопр ТЕСТ + возврат 0; + #иначе + возврат 1; + #endif +} \ No newline at end of file diff --git a/test_ru/alignas.c b/test_ru/alignas.c new file mode 100644 index 0000000..fc72639 --- /dev/null +++ b/test_ru/alignas.c @@ -0,0 +1,8 @@ +// выравнивание.c - Проверка _Выравнивание +цел главн(){ + _Выравнивание(16) символ буфер[32]; + если ((беззнак длин)буфер % 16 == 0) { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/alignof.c b/test_ru/alignof.c new file mode 100644 index 0000000..47d2ddb --- /dev/null +++ b/test_ru/alignof.c @@ -0,0 +1,8 @@ +// alignof.c - Проверка _Выравнивание_типа +цел главн(){ + цел разм = _Выравнивание_типа(цел); + если (разм == 4) { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/asm.c b/test_ru/asm.c new file mode 100644 index 0000000..e1f0d80 --- /dev/null +++ b/test_ru/asm.c @@ -0,0 +1,9 @@ +// asm.c - Проверка оператора asm +цел главн(){ + цел x = 10; + асм("nop"); + если (x == 10) { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/asm2.c b/test_ru/asm2.c new file mode 100644 index 0000000..bd71fdc --- /dev/null +++ b/test_ru/asm2.c @@ -0,0 +1,15 @@ +// asm2.c - Проверка оператора asm с системным вызовом +длин сис_вызов(длин номер, длин арг1, длин арг2, длин арг3) { + асм("mov %rcx, %r10"); + асм("mov %rdi, %rax"); + асм("mov %rsi, %rdi"); + асм("mov %rdx, %rsi"); + асм("mov %r10, %rdx"); + асм("syscall"); +} + +цел главн(){ + // syscall 60 = exit, аргумент 0 + сис_вызов(60, 0, 0, 0); + возврат 1; // Сюда не должны попасть +} \ No newline at end of file diff --git a/test_ru/atomic.c b/test_ru/atomic.c new file mode 100644 index 0000000..2073e4a --- /dev/null +++ b/test_ru/atomic.c @@ -0,0 +1,10 @@ +// atomic.c - Проверка типа _Атом +#включить + +цел главн() { + _Атом цел значение = 42; + если (значение == 42) { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/auto.c b/test_ru/auto.c new file mode 100644 index 0000000..25d5399 --- /dev/null +++ b/test_ru/auto.c @@ -0,0 +1,8 @@ +// auto.c - Проверка ключевого слова auto +цел главн(){ + авто цел x = 42; + если (x == 42) { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/bool.c b/test_ru/bool.c new file mode 100644 index 0000000..237bc0f --- /dev/null +++ b/test_ru/bool.c @@ -0,0 +1,10 @@ +// bool.c - Проверка типа bool через макрос +#включить + +цел главн(){ + бул флаг = истина; + если (флаг) { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/break.c b/test_ru/break.c new file mode 100644 index 0000000..9c49317 --- /dev/null +++ b/test_ru/break.c @@ -0,0 +1,14 @@ +// break.c - Проверка оператора break +цел главн(){ + цел и = 0; + пока (1) { + и = и + 1; + если (и == 5) { + прерви; + } + } + если (и == 5) { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/case.c b/test_ru/case.c new file mode 100644 index 0000000..e87e90b --- /dev/null +++ b/test_ru/case.c @@ -0,0 +1,21 @@ +// case.c - Проверка оператора case +цел главн(){ + цел х = 1; + цел рез = 0; + + выбор (х) { + случай 1: + рез = 100; + прерви; + случай 2: + рез = 200; + прерви; + умолч: + рез = 300; + } + + если (рез == 100) { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/char.c b/test_ru/char.c new file mode 100644 index 0000000..222e718 --- /dev/null +++ b/test_ru/char.c @@ -0,0 +1,5 @@ +// 2_char.c - Проверка типа char +цел главн(){ + символ с = 'A'; + возврат с; +} \ No newline at end of file diff --git a/test_ru/const.c b/test_ru/const.c new file mode 100644 index 0000000..9feeefd --- /dev/null +++ b/test_ru/const.c @@ -0,0 +1,8 @@ +// const.c - Проверка ключевого слова const +цел главн(){ + конст цел х = 42; + если (х == 42) { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/continue.c b/test_ru/continue.c new file mode 100644 index 0000000..b45973a --- /dev/null +++ b/test_ru/continue.c @@ -0,0 +1,16 @@ +// contиnue.c - Проверка оператора contиnue +цел главн(){ + цел и; + цел сум = 0; + для (и = 0; и < 10; и = и + 1) { + если (и == 5) { + продолжи; + } + сум = сум + и; + } + // сум = 0+1+2+3+4+6+7+8+9 = 40 (пропустили 5) + если (сум == 40) { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/counter_ru.c b/test_ru/counter_ru.c new file mode 100644 index 0000000..63d3c62 --- /dev/null +++ b/test_ru/counter_ru.c @@ -0,0 +1,9 @@ +// counter_ru.c - Проверка макроса __СЧЕТЧИК__ +цел главн() { + цел счет1 = __СЧЕТЧИК__; + цел счет2 = __СЧЕТЧИК__; + если (счет1 == 0 && счет2 == 1) { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/cyrillic_ident.c b/test_ru/cyrillic_ident.c new file mode 100644 index 0000000..2eb4e7c --- /dev/null +++ b/test_ru/cyrillic_ident.c @@ -0,0 +1,27 @@ +// cyrillic_ident.c - Проверка кириллических идентификаторов +цел глобальная_переменная = 42; + +цел сумма(цел а, цел б) { + возврат а + б; +} + +структ Точка { + цел координата_x; + цел координата_y; +}; + +цел главн() { + цел локальная_переменная = 10; + цел результат = сумма(локальная_переменная, глобальная_переменная); + + структ Точка точка; + точка.координата_x = 5; + точка.координата_y = 7; + + цел итог = точка.координата_x + точка.координата_y + результат; + + если (итог == 64) { // 5 + 7 + 10 + 42 = 64 + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/cyrillic_ptr.c b/test_ru/cyrillic_ptr.c new file mode 100644 index 0000000..3167293 --- /dev/null +++ b/test_ru/cyrillic_ptr.c @@ -0,0 +1,55 @@ +// cyrillic_ptr.c - Проверка указателей и указателей на указатели с кириллическими идентификаторами +цел главн() { + // Обычный указатель + цел значение = 42; + цел *указатель = &значение; + + если (*указатель != 42) { + возврат 1; + } + + // Указатель на указатель + цел **указатель_на_указатель = &указатель; + + если (**указатель_на_указатель != 42) { + возврат 1; + } + + // Изменение через указатель + *указатель = 100; + если (значение != 100) { + возврат 1; + } + + // Изменение через указатель на указатель + **указатель_на_указатель = 200; + если (значение != 200) { + возврат 1; + } + + // Указатель на структуру + структ Данные { + цел поле; + }; + + структ Данные объект; + объект.поле = 123; + + структ Данные *указатель_на_структуру = &объект; + если (указатель_на_структуру->поле != 123) { + возврат 1; + } + + указатель_на_структуру->поле = 456; + если (объект.поле != 456) { + возврат 1; + } + + // Указатель на указатель на структуру + структ Данные **указатель_на_указатель_на_структуру = &указатель_на_структуру; + если ((*указатель_на_указатель_на_структуру)->поле != 456) { + возврат 1; + } + + возврат 0; +} \ No newline at end of file diff --git a/test_ru/date_ru.c b/test_ru/date_ru.c new file mode 100644 index 0000000..724af49 --- /dev/null +++ b/test_ru/date_ru.c @@ -0,0 +1,11 @@ +// date_ru.c - Проверка макроса __ДАТА__ +#включить + +цел главн() { + символ *дата = __ДАТА__; + // Просто проверяем, что строка не пустая + если (дата[0] != '\0') { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/default.c b/test_ru/default.c new file mode 100644 index 0000000..1cf68ae --- /dev/null +++ b/test_ru/default.c @@ -0,0 +1,21 @@ +// default.c - Проверка оператора default +цел главн(){ + цел x = 999; + цел рез = 0; + + выбор (x) { + случай 1: + рез = 100; + прерви; + случай 2: + рез = 200; + прерви; + умолч: + рез = 999; + } + + если (рез == 999) { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/define.c b/test_ru/define.c new file mode 100644 index 0000000..1e22a09 --- /dev/null +++ b/test_ru/define.c @@ -0,0 +1,10 @@ +// define.c - Проверка директивы #определить +#определить ПИ 3.14 + +цел главн(){ + плав pi = ПИ; + если ((цел)pi == 3) { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/defined.c b/test_ru/defined.c new file mode 100644 index 0000000..cf0de36 --- /dev/null +++ b/test_ru/defined.c @@ -0,0 +1,10 @@ +// defined.c - Проверка оператора defined +#определить ТЕСТ 42 + +цел главн(){ + #если определено(ТЕСТ) + возврат 0; + #иначе + возврат 1; + #конецесли +} \ No newline at end of file diff --git a/test_ru/do.c b/test_ru/do.c new file mode 100644 index 0000000..401a1c8 --- /dev/null +++ b/test_ru/do.c @@ -0,0 +1,13 @@ +// do.c - Проверка цикла do-whиle +цел главн(){ + цел и = 0; + цел сум = 0; + делай { + сум = сум + и; + и = и + 1; + } пока (и < 10); + если (сум == 45) { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/double.c b/test_ru/double.c new file mode 100644 index 0000000..34fbe42 --- /dev/null +++ b/test_ru/double.c @@ -0,0 +1,5 @@ +// double.c - Проверка типа double +цел главн(){ + двойн d = 2.71; + возврат (цел)d; +} \ No newline at end of file diff --git a/test_ru/elif.c b/test_ru/elif.c new file mode 100644 index 0000000..b8c6edf --- /dev/null +++ b/test_ru/elif.c @@ -0,0 +1,12 @@ +// elif.c - Проверка директивы #иначеесли +#определить ВЕРСИЯ 2 + +цел главн(){ + #если ВЕРСИЯ == 1 + возврат 1; + #иначеесли ВЕРСИЯ == 2 + возврат 0; + #иначе + возврат 2; + #конецесли +} \ No newline at end of file diff --git a/test_ru/else.c b/test_ru/else.c new file mode 100644 index 0000000..035bb83 --- /dev/null +++ b/test_ru/else.c @@ -0,0 +1,9 @@ +// else.c - Проверка оператора else +цел главн(){ + цел х = 5; + если (х == 10) { + возврат 1; + } иначе { + возврат 0; + } +} \ No newline at end of file diff --git a/test_ru/endif.c b/test_ru/endif.c new file mode 100644 index 0000000..cc499e7 --- /dev/null +++ b/test_ru/endif.c @@ -0,0 +1,9 @@ +// endif.c - Проверка директивы #конецесли +#определить ТЕСТ 1 + +цел главн(){ + #если ТЕСТ == 1 + возврат 0; + #конецесли + возврат 1; +} \ No newline at end of file diff --git a/test_ru/enum.c b/test_ru/enum.c new file mode 100644 index 0000000..c0eb371 --- /dev/null +++ b/test_ru/enum.c @@ -0,0 +1,14 @@ +// enum.c - Проверка типа enum +переч Цвет { + КРАСНЫЙ, + ЗЕЛЕНЫЙ, + СИНИЙ +}; + +цел главн(){ + переч Цвет c = ЗЕЛЕНЫЙ; + если (c == 1) { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/error b/test_ru/error new file mode 100755 index 0000000..c1d7121 Binary files /dev/null and b/test_ru/error differ diff --git a/test_ru/error.c b/test_ru/error.c new file mode 100644 index 0000000..bd15155 --- /dev/null +++ b/test_ru/error.c @@ -0,0 +1,11 @@ +// error.c - Проверка директивы #ошибка +#определить ТЕСТ 1 + +цел главн(){ + #если ТЕСТ != 1 + // Нормальный код + возврат 0; + #иначе + #ошибка "Тестовая ошибка - не должно произойти" + #конецесли +} \ No newline at end of file diff --git a/test_ru/extern.c b/test_ru/extern.c new file mode 100644 index 0000000..7e7d24e --- /dev/null +++ b/test_ru/extern.c @@ -0,0 +1,10 @@ +// extern.c - Проверка ключевого слова extern +внеш цел глобальная_переменная; +цел глобальная_переменная = 42; + +цел главн(){ + если (глобальная_переменная == 42) { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/file_ru.c b/test_ru/file_ru.c new file mode 100644 index 0000000..6b736a9 --- /dev/null +++ b/test_ru/file_ru.c @@ -0,0 +1,10 @@ +// file_ru.c - Проверка макроса __ФАЙЛ__ +#включить + +цел главн() { + символ *имя_файла = __ФАЙЛ__; + если (имя_файла[0] != '\0') { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/float.c b/test_ru/float.c new file mode 100644 index 0000000..883a721 --- /dev/null +++ b/test_ru/float.c @@ -0,0 +1,5 @@ +// float.c - Проверка типа float +цел главн(){ + плав ф = 3.14; + возврат (цел)ф; +} \ No newline at end of file diff --git a/test_ru/for.c b/test_ru/for.c new file mode 100644 index 0000000..049c5ef --- /dev/null +++ b/test_ru/for.c @@ -0,0 +1,12 @@ +// for.c - Проверка цикла for +цел главн(){ + цел и; + цел сум = 0; + для (и = 0; и < 10; и = и + 1) { + сум = сум + и; + } + если (сум == 45) { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/func_ru.c b/test_ru/func_ru.c new file mode 100644 index 0000000..39e0079 --- /dev/null +++ b/test_ru/func_ru.c @@ -0,0 +1,8 @@ +// func_ru.c - Проверка макроса __функ__ +цел главн() { + символ *имя = __функ__; + если (имя[0] != '\0') { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/function_ru.c b/test_ru/function_ru.c new file mode 100644 index 0000000..44b8448 --- /dev/null +++ b/test_ru/function_ru.c @@ -0,0 +1,8 @@ +// function_ru.c - Проверка макроса __ФУНКЦИЯ__ +цел главн() { + символ *имя = __ФУНКЦИЯ__; + если (имя[0] != '\0') { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/generic.c b/test_ru/generic.c new file mode 100644 index 0000000..93a8eb7 --- /dev/null +++ b/test_ru/generic.c @@ -0,0 +1,8 @@ +// generic.c - Проверка _Обобщ +цел главн(){ + цел x = _Обобщ(42, цел: 1, умолч: 0); + если (x == 1) { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/goto.c b/test_ru/goto.c new file mode 100644 index 0000000..d930ba2 --- /dev/null +++ b/test_ru/goto.c @@ -0,0 +1,11 @@ +// goto.c - Проверка оператора goto +цел главн(){ + цел х = 0; + перейти метка; + х = 1; // Эта строка должна быть пропущена +метка: + если (х == 0) { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/if.c b/test_ru/if.c new file mode 100644 index 0000000..c6c253e --- /dev/null +++ b/test_ru/if.c @@ -0,0 +1,8 @@ +// if.c - Проверка оператора if +цел главн(){ + цел х = 10; + если (х == 10) { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/ifdef.c b/test_ru/ifdef.c new file mode 100644 index 0000000..554c824 --- /dev/null +++ b/test_ru/ifdef.c @@ -0,0 +1,9 @@ +// ifdef.c - Проверка директивы #еслиопр +#определить ТЕСТ 42 + +цел главн(){ + #еслиопр ТЕСТ + возврат 0; + #конецесли + возврат 1; +} \ No newline at end of file diff --git a/test_ru/ifndef.c b/test_ru/ifndef.c new file mode 100644 index 0000000..df6b10b --- /dev/null +++ b/test_ru/ifndef.c @@ -0,0 +1,7 @@ +// ifndef.c - Проверка директивы #еслинеопр +цел главн(){ + #еслинеопр ТЕСТ + возврат 0; + #конецесли + возврат 1; +} \ No newline at end of file diff --git a/test_ru/include.c b/test_ru/include.c new file mode 100644 index 0000000..eb31e75 --- /dev/null +++ b/test_ru/include.c @@ -0,0 +1,10 @@ +// include.c - Проверка директивы #включить +#включить + +цел главн(){ + бул флаг = истина; + если (флаг) { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/inline.c b/test_ru/inline.c new file mode 100644 index 0000000..5fa52e8 --- /dev/null +++ b/test_ru/inline.c @@ -0,0 +1,12 @@ +// inline.c - Проверка ключевого слова inline +инлайн цел квадрат(цел х) { + возврат х * х; +} + +цел главн(){ + цел рез = квадрат(5); + если (рез == 25) { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/int.c b/test_ru/int.c new file mode 100644 index 0000000..84eea78 --- /dev/null +++ b/test_ru/int.c @@ -0,0 +1,6 @@ +//1_int.c + +цел главн(){ + цел и = 0; + возврат и; +} \ No newline at end of file diff --git a/test_ru/line.c b/test_ru/line.c new file mode 100644 index 0000000..83b5604 --- /dev/null +++ b/test_ru/line.c @@ -0,0 +1,7 @@ +// line.c - Проверка директивы #строка +#строка 100 "test_ru/line.c" + +цел главн(){ + // Это строка 102 в файле + возврат 0; +} \ No newline at end of file diff --git a/test_ru/line_ru.c b/test_ru/line_ru.c new file mode 100644 index 0000000..0ad3485 --- /dev/null +++ b/test_ru/line_ru.c @@ -0,0 +1,9 @@ +// line_ru.c - Проверка макроса __ЛИНИЯ__ +цел главн() { + цел номер_строки = __ЛИНИЯ__; + // Просто проверяем, что номер > 0 + если (номер_строки > 0) { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/long.c b/test_ru/long.c new file mode 100644 index 0000000..36c774b --- /dev/null +++ b/test_ru/long.c @@ -0,0 +1,9 @@ +// лong.c - Проверка типа лong +цел главн(){ + длин л = 456789; + длин о = 456789; + если (л == о) { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/main.c b/test_ru/main.c new file mode 100644 index 0000000..a97fa26 --- /dev/null +++ b/test_ru/main.c @@ -0,0 +1,9 @@ +// main.c - Проверка функции main (главн) +цел глобальная = 42; + +цел главн(){ + если (глобальная == 42) { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/noreturn.c b/test_ru/noreturn.c new file mode 100644 index 0000000..cb32f6f --- /dev/null +++ b/test_ru/noreturn.c @@ -0,0 +1,11 @@ +// noreturn.c - Проверка _Безвозврат +_Безвозврат пусто завершение() { + // Функция помечена как _Безвозврат, но на самом деле возвращается + // Это проверяет, что атрибут не вызывает ошибок компиляции + возврат; +} + +цел главн(){ + завершение(); + возврат 0; +} \ No newline at end of file diff --git a/test_ru/once.c b/test_ru/once.c new file mode 100644 index 0000000..d9259db --- /dev/null +++ b/test_ru/once.c @@ -0,0 +1,11 @@ +// once.c - Проверка #прагма once с двумя включениями +#включить "once_header.h" +#включить "once_header.h" + +цел главн(){ + #еслиопр ТЕСТ_ЗНАЧЕНИЕ + возврат 0; + #иначе + возврат 1; + #конецесли +} \ No newline at end of file diff --git a/test_ru/once_header.h b/test_ru/once_header.h new file mode 100644 index 0000000..cbafddc --- /dev/null +++ b/test_ru/once_header.h @@ -0,0 +1,4 @@ +// once_header.h - Проверка #прагма once +#прагма once + +#определить ТЕСТ_ЗНАЧЕНИЕ 42 \ No newline at end of file diff --git a/test_ru/register.c b/test_ru/register.c new file mode 100644 index 0000000..554d2b1 --- /dev/null +++ b/test_ru/register.c @@ -0,0 +1,8 @@ +// register.c - Проверка ключевого слова register +цел main(){ + регистр цел ж = 42; + если (ж == 42) { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/restrict.c b/test_ru/restrict.c new file mode 100644 index 0000000..6f8ab80 --- /dev/null +++ b/test_ru/restrict.c @@ -0,0 +1,9 @@ +// restrict.c - Проверка ключевого слова restrict +цел главн(){ + цел щ = 42; + цел * огранич указатель = &щ; + если (*указатель == 42) { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/return.c b/test_ru/return.c new file mode 100644 index 0000000..3d3e807 --- /dev/null +++ b/test_ru/return.c @@ -0,0 +1,12 @@ +// return.c - Проверка оператора return +цел получить_число(){ + возврат 42; +} + +цел главн(){ + цел ш = получить_число(); + если (ш == 42) { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/run_tests.py b/test_ru/run_tests.py new file mode 100755 index 0000000..219374e --- /dev/null +++ b/test_ru/run_tests.py @@ -0,0 +1,187 @@ +#!/usr/bin/env python3 +""" +run_tests.py - Запуск русских тестов для rusi +""" + +import os +import sys +import csv +import subprocess +from pathlib import Path + +RUSI = "./rusi.elf" +TEST_DIR = Path(__file__).parent + +GREEN = "\033[92m" +RED = "\033[91m" +YELLOW = "\033[93m" +BLUE = "\033[94m" +RESET = "\033[0m" +BOLD = "\033[1m" + + +def print_header(text): + print(f"\n{BOLD}{BLUE}{'='*60}{RESET}") + print(f"{BOLD}{BLUE}{text:^60}{RESET}") + print(f"{BOLD}{BLUE}{'='*60}{RESET}") + + +def load_tests(): + csv_file = TEST_DIR / "test_ru.csv" + tests = [] + + if not csv_file.exists(): + for f in sorted(TEST_DIR.glob("*.c")): + tests.append({ + "номер": f.stem.split('_')[0] if '_' in f.stem else "??", + "тест": f.stem, + "описание": "Без описания", + "ожидаемый_код": None, + "файл": f + }) + return tests + + with open(csv_file, 'r', encoding='utf-8-sig') as f: + reader = csv.DictReader(f) + for row in reader: + test_name = row['Тест'].strip() + test_file = TEST_DIR / f"{test_name}.c" + if test_file.exists(): + try: + expected = int(str(row['Ожидаемый_код']).strip()) + except (ValueError, KeyError): + expected = None + + tests.append({ + "номер": row['Номер'].strip(), + "тест": test_name, + "описание": row['Описание'].strip(), + "ожидаемый_код": expected, + "файл": test_file + }) + + return tests + + +def compile_test(test_name, test_file): + exe_file = TEST_DIR / test_name + + if not os.path.exists(RUSI): + return False, f"rusi не найден: {RUSI}" + + cmd = [RUSI, "-o", str(exe_file), str(test_file)] + result = subprocess.run(cmd, capture_output=True, text=True) + + if result.returncode != 0: + return False, result.stderr + + return True, str(exe_file) + + +def run_test(exe_file): + result = subprocess.run([exe_file], capture_output=True, text=True) + return result.returncode, result.stdout, result.stderr + + +def main(): + import argparse + + parser = argparse.ArgumentParser() + parser.add_argument("--keep", "-k", action="store_true") + parser.add_argument("--verbose", "-v", action="store_true") + parser.add_argument("--test", "-t") + args = parser.parse_args() + + print_header("ЗАПУСК РУССКИХ ТЕСТОВ ДЛЯ RUSI") + + tests = load_tests() + + if args.test: + tests = [t for t in tests if t["тест"] == args.test or t["номер"] == args.test] + if not tests: + print(f"{RED}ОШИБКА: Тест '{args.test}' не найден{RESET}") + sys.exit(1) + + if not tests: + print(f"{YELLOW}ВНИМАНИЕ: Тесты не найдены{RESET}") + sys.exit(0) + + print(f"\nНайдено тестов: {len(tests)}") + print(f"Компилятор: {RUSI}") + print(f"Папка тестов: {TEST_DIR}") + print() + + passed = 0 + failed = 0 + results = [] + + for test in tests: + test_name = test["тест"] + test_file = test["файл"] + expected = test["ожидаемый_код"] + exe_file = TEST_DIR / test_name + + ok, result = compile_test(test_name, test_file) + + if not ok: + print(f" {RED}✗{RESET} {test_name:<20} Ошибка компиляции") + if args.verbose: + print(f" {result}") + failed += 1 + results.append((test_name, False, "compile_error", None)) + continue + + code, stdout, stderr = run_test(str(exe_file)) + + if expected is not None: + if code == expected: + print(f" {GREEN}✓{RESET} {test_name:<20} ожидалось {expected}, получено {code}") + passed += 1 + results.append((test_name, True, "пройден", code)) + else: + print(f" {RED}✗{RESET} {test_name:<20} ожидалось {expected}, получено {code}") + if args.verbose: + if stdout: + print(f" Вывод: {stdout.strip()}") + if stderr: + print(f" Ошибки: {stderr.strip()}") + failed += 1 + results.append((test_name, False, f"expected_{expected}_got_{code}", code)) + else: + if code == 0: + print(f" {GREEN}✓{RESET} {test_name:<20} завершился успешно (код {code})") + passed += 1 + results.append((test_name, True, "пройден", code)) + else: + print(f" {RED}✗{RESET} {test_name:<20} завершился с ошибкой (код {code})") + failed += 1 + results.append((test_name, False, f"failed_with_{code}", code)) + + if not args.keep and exe_file.exists(): + os.remove(exe_file) + + print_header("РЕЗУЛЬТАТЫ ТЕСТИРОВАНИЯ") + + for test_name, ok, status, code in results: + if ok: + print(f" {GREEN}✓{RESET} {test_name:<20} {status}") + else: + print(f" {RED}✗{RESET} {test_name:<20} {status}") + + print(f"\n{BOLD}Итого:{RESET}") + print(f" {GREEN}Пройдено:{RESET} {passed}") + print(f" {RED}Провалено:{RESET} {failed}") + print(f" {BOLD}Всего:{RESET} {len(tests)}") + + if args.keep: + print(f"\n{YELLOW}Бинарники сохранены в {TEST_DIR}{RESET}") + + if failed > 0: + sys.exit(1) + else: + print(f"\n{GREEN}{BOLD}🎉 Все тесты пройдены!{RESET}") + sys.exit(0) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/test_ru/short.c b/test_ru/short.c new file mode 100644 index 0000000..2b230f3 --- /dev/null +++ b/test_ru/short.c @@ -0,0 +1,5 @@ +// short.c - Проверка типа short +цел главн(){ + корот к = 123; + возврат к; +} \ No newline at end of file diff --git a/test_ru/signed.c b/test_ru/signed.c new file mode 100644 index 0000000..8e8455a --- /dev/null +++ b/test_ru/signed.c @@ -0,0 +1,8 @@ +// зigned.c - Проверка типа зigned (знак) +цел главн(){ + знак цел з = -42; + если (з == -42) { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/sizeof.c b/test_ru/sizeof.c new file mode 100644 index 0000000..e0544c2 --- /dev/null +++ b/test_ru/sizeof.c @@ -0,0 +1,9 @@ +// sizeof.c - Проверка оператора размер +цел главн(){ + цел ч = 10; + цел д = размер(ч); + если (д == 4) { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/static.c b/test_ru/static.c new file mode 100644 index 0000000..3a18363 --- /dev/null +++ b/test_ru/static.c @@ -0,0 +1,15 @@ +// stаtic.c - Проверка ключевого слова stаtic +цел счетчик(){ + стат цел колво = 0; + колво = колво + 1; + возврат колво; +} + +цел главн(){ + цел а = счетчик(); + цел б = счетчик(); + если (а == 1 && б == 2) { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/struct.c b/test_ru/struct.c new file mode 100644 index 0000000..71fc009 --- /dev/null +++ b/test_ru/struct.c @@ -0,0 +1,15 @@ +// struct.c - Проверка типа struct +структ Точка { + цел икс; + цел игрек; +}; + +цел главн(){ + структ Точка ю; + ю.икс = 10; + ю.игрек = 20; + если (ю.икс == 10 && ю.игрек == 20) { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/switch.c b/test_ru/switch.c new file mode 100644 index 0000000..0765e76 --- /dev/null +++ b/test_ru/switch.c @@ -0,0 +1,21 @@ +// switch.c - Проверка оператора switch +цел главн(){ + цел икс = 2; + цел итог = 0; + + выбор (икс) { + случай 1: + итог = 10; + прерви; + случай 2: + итог = 20; + прерви; + умолч: + итог = 30; + } + + если (итог == 20) { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/test_import.c b/test_ru/test_import.c new file mode 100644 index 0000000..4325e81 --- /dev/null +++ b/test_ru/test_import.c @@ -0,0 +1,7 @@ +// test_import.c - Проверка импорта файла с русским именем и расширением +#включить "привет.з" + +цел главн() { + ПЕЧАТЬ("OK\n"); + возврат 0; +} \ No newline at end of file diff --git a/test_ru/test_ru.csv b/test_ru/test_ru.csv new file mode 100644 index 0000000..58292c9 --- /dev/null +++ b/test_ru/test_ru.csv @@ -0,0 +1,66 @@ +Номер,Тест,Описание,Ожидаемый_код +1,int,Проверка типа int (объявление и возврат),0 +2,char,Проверка типа char (символ 'A' = 65),65 +3,short,Проверка типа short (значение 123),123 +4,long,Проверка типа long (сравнение 456789),0 +5,void,Проверка типа void (функция без возврата),0 +6,float,Проверка типа float (приведение 3.14 к int),3 +7,double,Проверка типа double (приведение 2.71 к int),2 +8,struct,Проверка типа struct (поля структуры),0 +9,union,Проверка типа union (разделяемая память),0 +10,enum,Проверка типа enum (константы перечисления),0 +11,bool,Проверка типа bool (макрос из stdbool.h),0 +12,unsigned,Проверка типа unsigned (беззнак),0 +13,signed,Проверка типа signed (знак),0 +14,typedef,Проверка ключевого слова typedef,0 +15,static,Проверка ключевого слова static,0 +16,extern,Проверка ключевого слова extern,0 +17,inline,Проверка ключевого слова inline,0 +18,const,Проверка ключевого слова const,0 +19,volatile,Проверка ключевого слова volatile,0 +20,auto,Проверка ключевого слова auto,0 +21,register,Проверка ключевого слова register,0 +22,restrict,Проверка ключевого слова restrict,0 +23,return,Проверка оператора return,0 +24,if,Проверка оператора if,0 +25,else,Проверка оператора else,0 +26,for,Проверка цикла for,0 +27,while,Проверка цикла while,0 +28,do,Проверка цикла do-while,0 +29,switch,Проверка оператора switch,0 +30,case,Проверка оператора case,0 +31,default,Проверка оператора default,0 +32,goto,Проверка оператора goto,0 +33,break,Проверка оператора break,0 +34,continue,Проверка оператора continue,0 +35,sizeof,Проверка оператора sizeof,0 +36,typeof,Проверка оператора typeof,0 +37,asm,Проверка оператора asm,0 +38,asm2,Проверка asm с системным вызовом exit,0 +39,include,Проверка директивы #включить,0 +40,define,Проверка директивы #определить,0 +41,undef,Проверка директивы #отменить,0 +42,ifdef,Проверка директивы #еслиопр,0 +43,ifndef,Проверка директивы #еслинеопр,0 +44,elif,Проверка директивы #иначеесли,0 +45,#else,Проверка директивы #иначе,0 +46,endif,Проверка директивы #конецесли,0 +47,line,Проверка директивы #строка,0 +48,defined,Проверка оператора defined,0 +49,once,Проверка директивы #прагма once,0 +50,main,Проверка функции main (главная),0 +51,cyrillic_ident,Проверка кириллических идентификаторов (переменные функции структуры),0 +52,cyrillic_ptr,Проверка указателей и указателей на указатели с кириллическими идентификаторами,0 +56,file_ru,Проверка макроса __ФАЙЛ__,0 +57,line_ru,Проверка макроса __ЛИНИЯ__,0 +58,counter_ru,Проверка макроса __СЧЕТЧИК__,0 +59,date_ru,Проверка макроса __ДАТА__,0 +60,time_ru,Проверка макроса __ВРЕМЯ__,0 +61,func_ru,Проверка макроса __функ__,0 +62,function_ru,Проверка макроса __ФУНКЦИЯ__,0 +65,alignas,Проверка _Выравнивание,0 +66,alignof,Проверка _Выравнивание_типа,0 +67,noreturn,Проверка _Безвозврат,0 +68,generic,Проверка _Обобщ,0 +69,test_import,Проверка импорта файла с русским именем и расширением (.з),0 +70,test_compile_ru,Проверка компиляции файла с русским именем и расширением (.рс),0 \ No newline at end of file diff --git a/test_ru/thread_local.c b/test_ru/thread_local.c new file mode 100644 index 0000000..4dbfea8 --- /dev/null +++ b/test_ru/thread_local.c @@ -0,0 +1,9 @@ +// thread_local.c - Проверка _Поток_лок +_Поток_лок цел глобальная = 42; + +цел главн() { + если (глобальная == 42) { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/time_ru.c b/test_ru/time_ru.c new file mode 100644 index 0000000..63b9fe6 --- /dev/null +++ b/test_ru/time_ru.c @@ -0,0 +1,11 @@ +// time_ru.c - Проверка макроса __ВРЕМЯ__ +#включить + +цел главн() { + символ *время = __ВРЕМЯ__; + // Просто проверяем, что строка не пустая + если (время[0] != '\0') { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/typedef.c b/test_ru/typedef.c new file mode 100644 index 0000000..a9c1774 --- /dev/null +++ b/test_ru/typedef.c @@ -0,0 +1,10 @@ +// typedef.c - Проверка ключевого слова typedef +типдеф цел МойЦел; + +цел главн(){ + МойЦел x = 42; + если (x == 42) { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/typeof.c b/test_ru/typeof.c new file mode 100644 index 0000000..eee92fe --- /dev/null +++ b/test_ru/typeof.c @@ -0,0 +1,9 @@ +// tцpeof.c - Проверка оператора tцpeof +цел главн(){ + цел й = 42; + типоф(й) ц = 100; + если (ц == 100) { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/undef.c b/test_ru/undef.c new file mode 100644 index 0000000..c2fcb82 --- /dev/null +++ b/test_ru/undef.c @@ -0,0 +1,7 @@ +// undef.c - Проверка директивы #отменить +#определить ТЕСТ 42 +#отменить ТЕСТ + +цел главн(){ + возврат 0; +} \ No newline at end of file diff --git a/test_ru/union.c b/test_ru/union.c new file mode 100644 index 0000000..06d720e --- /dev/null +++ b/test_ru/union.c @@ -0,0 +1,14 @@ +// union.c - Проверка типа union +объед Данные { + цел i; + символ c; +}; + +цел главн(){ + объед Данные d; + d.i = 65; + если (d.c == 'A') { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/unsigned.c b/test_ru/unsigned.c new file mode 100644 index 0000000..9ef42e3 --- /dev/null +++ b/test_ru/unsigned.c @@ -0,0 +1,8 @@ +// unsigned.c - Проверка типа unsigned (беззнак) +цел главн(){ + беззнак цел u = 42; + если (u == 42) { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/void.c b/test_ru/void.c new file mode 100644 index 0000000..dd3cde7 --- /dev/null +++ b/test_ru/void.c @@ -0,0 +1,4 @@ +// void.c - Проверка типа void (функция без возврата) +пусто главн(){ + возврат 0; +} \ No newline at end of file diff --git a/test_ru/volatile.c b/test_ru/volatile.c new file mode 100644 index 0000000..96b6609 --- /dev/null +++ b/test_ru/volatile.c @@ -0,0 +1,8 @@ +// volatile.c - Проверка ключевого слова volatile +цел главн(){ + летуч цел x = 42; + если (x == 42) { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/while.c b/test_ru/while.c new file mode 100644 index 0000000..4690f92 --- /dev/null +++ b/test_ru/while.c @@ -0,0 +1,13 @@ +// while.c - Проверка цикла while +цел главн(){ + цел i = 0; + цел sum = 0; + пока (i < 10) { + sum = sum + i; + i = i + 1; + } + если (sum == 45) { + возврат 0; + } + возврат 1; +} \ No newline at end of file diff --git a/test_ru/привет.з b/test_ru/привет.з new file mode 100644 index 0000000..da9c5de --- /dev/null +++ b/test_ru/привет.з @@ -0,0 +1,9 @@ +// привет.з - Русский аналог stdio.h +#еслинеопр ПРИВЕТ_З +#определить ПРИВЕТ_З + +#включить + +#определить ПЕЧАТЬ printf + +#конецесли \ No newline at end of file diff --git a/test_ru/тест.рс b/test_ru/тест.рс new file mode 100644 index 0000000..61345c1 --- /dev/null +++ b/test_ru/тест.рс @@ -0,0 +1,7 @@ +// тест.рс - Тестовый файл на русском C +#включить + +цел главная() { + printf("Привет, мир!\n"); + возврат 0; +} \ No newline at end of file diff --git a/tokenize.c b/tokenize.c new file mode 100644 index 0000000..f950c1a --- /dev/null +++ b/tokenize.c @@ -0,0 +1,762 @@ +#включить "руси.з" + +// Текущий обрабатываемый файл +стат 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(файл); +} \ No newline at end of file diff --git a/type.c b/type.c new file mode 100644 index 0000000..493e252 --- /dev/null +++ b/type.c @@ -0,0 +1,327 @@ +#включить "руси.з" + +// ===================================================================== +// Механизм отслеживания выделенной памяти для типов +// ===================================================================== + +стат Type **все_типы = NULL; +стат цел счётчик_типов = 0; +стат цел ёмкость_типов = 0; + +стат пусто отследить_тип(Type *тип) { + если (счётчик_типов == ёмкость_типов) { + ёмкость_типов = ёмкость_типов ? ёмкость_типов * 2 : 1024; + все_типы = realloc(все_типы, ёмкость_типов * размер(Type *)); + } + все_типы[счётчик_типов++] = тип; +} + +пусто free_all_types(пусто) { + для (цел ш = 0; ш < счётчик_типов; ш++) { + free(все_типы[ш]); + } + free(все_типы); + + все_типы = NULL; + счётчик_типов = 0; + ёмкость_типов = 0; +} +// ===================================================================== + +Type *ty_void = &(Type){TY_VOID, 1, 1}; +Type *ty_bool = &(Type){TY_BOOL, 1, 1}; +Type *ty_char = &(Type){TY_CHAR, 1, 1}; +Type *ty_short = &(Type){TY_SHORT, 2, 2}; +Type *ty_int = &(Type){TY_INT, 4, 4}; +Type *ty_long = &(Type){TY_LONG, 8, 8}; + +Type *ty_uchar = &(Type){TY_CHAR, 1, 1, истина}; +Type *ty_ushort = &(Type){TY_SHORT, 2, 2, истина}; +Type *ty_uint = &(Type){TY_INT, 4, 4, истина}; +Type *ty_ulong = &(Type){TY_LONG, 8, 8, истина}; + +Type *ty_float = &(Type){TY_FLOAT, 4, 4}; +Type *ty_double = &(Type){TY_DOUBLE, 8, 8}; +Type *ty_ldouble = &(Type){TY_LDOUBLE, 16, 16}; + +стат Type *выделить_тип(TypeKind вид, цел разм, цел выравн) { + Type *тип = calloc(1, размер(Type)); + тип->kind = вид; + тип->size = разм; + тип->align = выравн; + + отследить_тип(тип); + возврат тип; +} + +бул is_integer(Type *тип) { + TypeKind вид = тип->kind; + возврат вид == TY_BOOL || вид == TY_CHAR || вид == TY_SHORT || + вид == TY_INT || вид == TY_LONG || вид == TY_ENUM; +} + +бул is_flonum(Type *тип) { + возврат тип->kind == TY_FLOAT || тип->kind == TY_DOUBLE || + тип->kind == TY_LDOUBLE; +} + +бул is_numeric(Type *тип) { + возврат is_integer(тип) || is_flonum(тип); +} + +бул is_compatible(Type *т1, Type *т2) { + если (т1 == т2) + возврат истина; + + если (т1->origin) + возврат is_compatible(т1->origin, т2); + если (т2->origin) + возврат is_compatible(т1, т2->origin); + + если (т1->kind != т2->kind) + возврат ложь; + + выбор (т1->kind) { + случай TY_CHAR: + случай TY_SHORT: + случай TY_INT: + случай TY_LONG: + возврат т1->is_unsigned == т2->is_unsigned; + случай TY_FLOAT: + случай TY_DOUBLE: + случай TY_LDOUBLE: + возврат истина; + случай TY_PTR: + возврат is_compatible(т1->base, т2->base); + случай TY_FUNC: { + если (!is_compatible(т1->return_ty, т2->return_ty)) + возврат ложь; + если (т1->is_variadic != т2->is_variadic) + возврат ложь; + + Type *пар1 = т1->params; + Type *пар2 = т2->params; + для (; пар1 && пар2; пар1 = пар1->next, пар2 = пар2->next) + если (!is_compatible(пар1, пар2)) + возврат ложь; + + возврат пар1 == NULL && пар2 == NULL; + } + случай TY_ARRAY: + если (!is_compatible(т1->base, т2->base)) + возврат ложь; + возврат т1->array_len < 0 && т2->array_len < 0 && + т1->array_len == т2->array_len; + } + возврат ложь; +} + +Type *copy_type(Type *тип) { + Type *копия = calloc(1, размер(Type)); + *копия = *тип; + копия->origin = тип; + + отследить_тип(копия); + возврат копия; +} + +Type *pointer_to(Type *база) { + Type *указ = выделить_тип(TY_PTR, 8, 8); + указ->base = база; + указ->is_unsigned = истина; + возврат указ; +} + +Type *func_type(Type *возвр_тип) { + Type *фн = выделить_тип(TY_FUNC, 1, 1); + фн->return_ty = возвр_тип; + возврат фн; +} + +Type *array_of(Type *база, цел длина) { + Type *масс = выделить_тип(TY_ARRAY, база->size * длина, база->align); + масс->base = база; + масс->array_len = длина; + возврат масс; +} + +Type *vla_of(Type *база, Node *узел_длины) { + Type *вла = выделить_тип(TY_VLA, 8, 8); + вла->base = база; + вла->vla_len = узел_длины; + возврат вла; +} + +Type *enum_type(пусто) { + возврат выделить_тип(TY_ENUM, 4, 4); +} + +Type *struct_type(пусто) { + возврат выделить_тип(TY_STRUCT, 0, 1); +} + +стат Type *общий_тип(Type *т1, Type *т2) { + если (т1->base) + возврат pointer_to(т1->base); + + если (т1->kind == TY_FUNC) + возврат pointer_to(т1); + если (т2->kind == TY_FUNC) + возврат pointer_to(т2); + + если (т1->kind == TY_LDOUBLE || т2->kind == TY_LDOUBLE) + возврат ty_ldouble; + если (т1->kind == TY_DOUBLE || т2->kind == TY_DOUBLE) + возврат ty_double; + если (т1->kind == TY_FLOAT || т2->kind == TY_FLOAT) + возврат ty_float; + + если (т1->size < 4) + т1 = ty_int; + если (т2->size < 4) + т2 = ty_int; + + если (т1->size != т2->size) + возврат (т1->size < т2->size) ? т2 : т1; + + если (т2->is_unsigned) + возврат т2; + возврат т1; +} + +стат пусто арифм_преобразование(Node **лев, Node **прав) { + Type *общий = общий_тип((*лев)->ty, (*прав)->ty); + *лев = new_cast(*лев, общий); + *прав = new_cast(*прав, общий); +} + +пусто add_type(Node *узел) { + если (!узел || узел->ty) + возврат; + + add_type(узел->lhs); + add_type(узел->rhs); + add_type(узел->cond); + add_type(узел->then); + add_type(узел->els); + add_type(узел->init); + add_type(узел->inc); + + для (Node *п = узел->body; п; п = п->next) + add_type(п); + для (Node *п = узел->args; п; п = п->next) + add_type(п); + + выбор (узел->kind) { + случай ND_NUM: + узел->ty = ty_int; + возврат; + случай ND_ADD: + случай ND_SUB: + случай ND_MUL: + случай ND_DIV: + случай ND_MOD: + случай ND_BITAND: + случай ND_BITOR: + случай ND_BITXOR: + арифм_преобразование(&узел->lhs, &узел->rhs); + узел->ty = узел->lhs->ty; + возврат; + случай ND_NEG: { + Type *рез_тип = общий_тип(ty_int, узел->lhs->ty); + узел->lhs = new_cast(узел->lhs, рез_тип); + узел->ty = рез_тип; + возврат; + } + случай ND_ASSIGN: + если (узел->lhs->ty->kind == TY_ARRAY) + error_tok(узел->lhs->tok, "не lvalue"); + если (узел->lhs->ty->kind != TY_STRUCT) + узел->rhs = new_cast(узел->rhs, узел->lhs->ty); + узел->ty = узел->lhs->ty; + возврат; + случай ND_EQ: + случай ND_NE: + случай ND_LT: + случай ND_LE: + арифм_преобразование(&узел->lhs, &узел->rhs); + узел->ty = ty_int; + возврат; + случай ND_FUNCALL: + узел->ty = узел->func_ty->return_ty; + возврат; + случай ND_NOT: + случай ND_LOGOR: + случай ND_LOGAND: + узел->ty = ty_int; + возврат; + случай ND_BITNOT: + случай ND_SHL: + случай ND_SHR: + узел->ty = узел->lhs->ty; + возврат; + случай ND_VAR: + случай ND_VLA_PTR: + узел->ty = узел->var->ty; + возврат; + случай ND_COND: + если (узел->then->ty->kind == TY_VOID || узел->els->ty->kind == TY_VOID) { + узел->ty = ty_void; + } иначе { + арифм_преобразование(&узел->then, &узел->els); + узел->ty = узел->then->ty; + } + возврат; + случай ND_COMMA: + узел->ty = узел->rhs->ty; + возврат; + случай ND_MEMBER: + узел->ty = узел->member->ty; + возврат; + случай ND_ADDR: { + Type *тип_операнда = узел->lhs->ty; + если (тип_операнда->kind == TY_ARRAY) + узел->ty = pointer_to(тип_операнда->base); + иначе + узел->ty = pointer_to(тип_операнда); + возврат; + } + случай ND_DEREF: + если (!узел->lhs->ty->base) + error_tok(узел->tok, "недопустимое разыменование указателя"); + если (узел->lhs->ty->base->kind == TY_VOID) + error_tok(узел->tok, "разыменование указателя на void"); + узел->ty = узел->lhs->ty->base; + возврат; + случай ND_STMT_EXPR: + если (узел->body) { + Node *оп = узел->body; + пока (оп->next) + оп = оп->next; + если (оп->kind == ND_EXPR_STMT) { + узел->ty = оп->lhs->ty; + возврат; + } + } + error_tok(узел->tok, "выражение-инструкция, возвращающая void, не поддерживается"); + возврат; + случай ND_LABEL_VAL: + узел->ty = pointer_to(ty_void); + возврат; + случай ND_CAS: + add_type(узел->cas_addr); + add_type(узел->cas_old); + add_type(узел->cas_new); + узел->ty = ty_bool; + если (узел->cas_addr->ty->kind != TY_PTR) + error_tok(узел->cas_addr->tok, "ожидался указатель"); + если (узел->cas_old->ty->kind != TY_PTR) + error_tok(узел->cas_old->tok, "ожидался указатель"); + возврат; + случай ND_EXCH: + если (узел->lhs->ty->kind != TY_PTR) + error_tok(узел->cas_addr->tok, "ожидался указатель"); + узел->ty = узел->lhs->ty->base; + возврат; + } +} \ No newline at end of file diff --git a/unicode.c b/unicode.c new file mode 100644 index 0000000..fb3dfcf --- /dev/null +++ b/unicode.c @@ -0,0 +1,558 @@ +#включить "руси.з" + +// Кодирует символ Unicode в последовательность UTF-8. +// Возвращает количество записанных байт (от 1 до 4). +цел encode_utf8(символ *буф, uint32_t зн) { + стат конст uint32_t МАСКИ[] = { + 0x00000000, + 0x0000007F, + 0x000007FF, + 0x0000FFFF, + 0x0010FFFF, + }; + + цел длина; + если (зн <= 0x7F) + длина = 1; + иначе если (зн <= 0x7FF) + длина = 2; + иначе если (зн <= 0xFFFF) + длина = 3; + иначе если (зн <= 0x10FFFF) + длина = 4; + иначе + возврат 0; + + если (зн >= 0xD800 && зн <= 0xDFFF) + возврат 0; + + символ врем[4]; + цел поз = 0; + + для (цел ш = 0; ш < длина - 1; ш++) { + врем[поз++] = 0x80 | (зн & 0x3F); + зн >>= 6; + } + + стат конст uint8_t МЕТКА_ПЕРВОГО[] = { + 0x00, + 0x00, + 0xC0, + 0xE0, + 0xF0, + }; + + врем[поз++] = МЕТКА_ПЕРВОГО[длина] | зн; + + для (цел ш = 0; ш < длина; ш++) + буф[ш] = врем[длина - 1 - ш]; + + возврат длина; +} + +uint32_t decode_utf8(символ **нов_поз, символ *п) { + беззнак символ первый = (беззнак символ)*п; + если (первый < 0x80) { + *нов_поз = п + 1; + возврат первый; + } + + цел длина; + uint32_t рез; + uint32_t мин_знач; + + если ((первый & 0xF8) == 0xF0) { + длина = 4; + рез = первый & 0x07; + мин_знач = 0x10000; + } иначе если ((первый & 0xF0) == 0xE0) { + длина = 3; + рез = первый & 0x0F; + мин_знач = 0x800; + } иначе если ((первый & 0xE0) == 0xC0) { + длина = 2; + рез = первый & 0x1F; + мин_знач = 0x80; + } иначе { + error_at(п, "некорректная UTF-8 последовательность"); + } + + для (цел ш = 1; ш < длина; ш++) { + беззнак символ байт = (беззнак символ)п[ш]; + + если ((байт & 0xC0) != 0x80) + error_at(п, "некорректная UTF-8 последовательность"); + + рез = (рез << 6) | (байт & 0x3F); + } + + если (рез < мин_знач) + error_at(п, "избыточная UTF-8 последовательность"); + + если (рез >= 0xD800 && рез <= 0xDFFF) + error_at(п, "суррогатные пары недопустимы в UTF-8"); + + если (рез > 0x10FFFF) + error_at(п, "кодпоинт превышает U+10FFFF"); + + *нов_поз = п + длина; + возврат рез; +} + +стат бул в_диапазоне(uint32_t *диап, uint32_t зн) { + для (цел ш = 0; диап[ш] != (uint32_t)-1; ш += 2) { + если (зн >= диап[ш] && зн <= диап[ш + 1]) + возврат истина; + } + возврат ложь; +} + +бул is_ident1(uint32_t зн) { + если (зн == '_' || зн == '$') + возврат истина; + если (зн >= 'a' && зн <= 'z') + возврат истина; + если (зн >= 'A' && зн <= 'Z') + возврат истина; + если (зн < 0x80) + возврат ложь; + + если (зн == 0x00A8 || зн == 0x00AA || зн == 0x00AD || зн == 0x00AF) + возврат истина; + если (зн >= 0x00B2 && зн <= 0x00B5) + возврат истина; + если (зн >= 0x00B7 && зн <= 0x00BA) + возврат истина; + если (зн >= 0x00BC && зн <= 0x00BE) + возврат истина; + если (зн >= 0x00C0 && зн <= 0x00D6) + возврат истина; + если (зн >= 0x00D8 && зн <= 0x00F6) + возврат истина; + если (зн >= 0x00F8 && зн <= 0x00FF) + возврат истина; + + если (зн >= 0x0100 && зн <= 0x02FF) + возврат истина; + если (зн >= 0x0370 && зн <= 0x167F) + возврат истина; + если (зн >= 0x1681 && зн <= 0x180D) + возврат истина; + если (зн >= 0x180F && зн <= 0x1DBF) + возврат истина; + если (зн >= 0x1E00 && зн <= 0x1FFF) + возврат истина; + + если (зн == 0x200B || зн == 0x200D) + возврат истина; + если (зн >= 0x202A && зн <= 0x202E) + возврат истина; + если (зн == 0x203F || зн == 0x2040 || зн == 0x2054) + возврат истина; + если (зн >= 0x2060 && зн <= 0x206F) + возврат истина; + + если (зн >= 0x2070 && зн <= 0x20CF) + возврат истина; + если (зн >= 0x2100 && зн <= 0x218F) + возврат истина; + если (зн >= 0x2460 && зн <= 0x24FF) + возврат истина; + если (зн >= 0x2776 && зн <= 0x2793) + возврат истина; + + если (зн >= 0x2C00 && зн <= 0x2DFF) + возврат истина; + если (зн >= 0x2E80 && зн <= 0x2FFF) + возврат истина; + если (зн == 0x3004 || зн == 0x3007) + возврат истина; + если (зн >= 0x3021 && зн <= 0x302F) + возврат истина; + если (зн >= 0x3031 && зн <= 0x303F) + возврат истина; + если (зн >= 0x3040 && зн <= 0xD7FF) + возврат истина; + если (зн >= 0xF900 && зн <= 0xFD3D) + возврат истина; + если (зн >= 0xFD40 && зн <= 0xFDCF) + возврат истина; + если (зн >= 0xFDF0 && зн <= 0xFE1F) + возврат истина; + если (зн >= 0xFE30 && зн <= 0xFE44) + возврат истина; + если (зн >= 0xFE47 && зн <= 0xFFFD) + возврат истина; + + если (зн >= 0x10000 && зн <= 0xEFFFD) { + стат конст uint32_t НАЧ_ПЛОСКОСТЕЙ[] = { + 0x10000, 0x20000, 0x30000, 0x40000, 0x50000, + 0x60000, 0x70000, 0x80000, 0x90000, 0xA0000, + 0xB0000, 0xC0000, 0xD0000, 0xE0000 + }; + стат конст цел ЧИСЛО_ПЛОСК = размер(НАЧ_ПЛОСКОСТЕЙ) / размер(НАЧ_ПЛОСКОСТЕЙ[0]); + + для (цел ш = 0; ш < ЧИСЛО_ПЛОСК; ш++) { + если (зн >= НАЧ_ПЛОСКОСТЕЙ[ш] && зн <= НАЧ_ПЛОСКОСТЕЙ[ш] + 0xFFFD) + возврат истина; + } + } + + если (зн >= 0x0400 && зн <= 0x04FF) + возврат истина; + если (зн >= 0x0500 && зн <= 0x052F) + возврат истина; + + возврат ложь; +} + +бул is_ident2(uint32_t зн) { + если (зн >= '0' && зн <= '9') + возврат истина; + + если (зн == '$') + возврат истина; + + если (is_ident1(зн)) + возврат истина; + + если (зн >= 0x0300 && зн <= 0x036F) + возврат истина; + если (зн >= 0x1DC0 && зн <= 0x1DFF) + возврат истина; + если (зн >= 0x20D0 && зн <= 0x20FF) + возврат истина; + если (зн >= 0xFE20 && зн <= 0xFE2F) + возврат истина; + + возврат ложь; +} + +стат цел ширина_символа(uint32_t зн) { + если (зн < 0x20) + возврат 0; + + если (зн == 0x7F) + возврат 0; + + если (зн == 0xA0) + возврат 0; + + если (зн >= 0x0300 && зн <= 0x036F) + возврат 0; + если (зн >= 0x0483 && зн <= 0x0489) + возврат 0; + если (зн >= 0x0591 && зн <= 0x05BD) + возврат 0; + если (зн == 0x05BF || зн == 0x05C1 || зн == 0x05C2 || зн == 0x05C4 || + зн == 0x05C5 || зн == 0x05C7) + возврат 0; + если (зн >= 0x0610 && зн <= 0x061A) + возврат 0; + если (зн >= 0x064B && зн <= 0x065F) + возврат 0; + если (зн == 0x0670) + возврат 0; + если (зн >= 0x06D6 && зн <= 0x06DC) + возврат 0; + если (зн >= 0x06DF && зн <= 0x06E4) + возврат 0; + если (зн == 0x06E7 || зн == 0x06E8 || зн == 0x06EA || зн == 0x06ED) + возврат 0; + если (зн == 0x0711) + возврат 0; + если (зн >= 0x0730 && зн <= 0x074A) + возврат 0; + если (зн >= 0x07A6 && зн <= 0x07B0) + возврат 0; + если (зн >= 0x0901 && зн <= 0x0902) + возврат 0; + если (зн == 0x093C) + возврат 0; + если (зн >= 0x0941 && зн <= 0x0948) + возврат 0; + если (зн == 0x094D) + возврат 0; + если (зн >= 0x0951 && зн <= 0x0954) + возврат 0; + если (зн == 0x0962 || зн == 0x0963) + возврат 0; + если (зн == 0x0981) + возврат 0; + если (зн == 0x09BC) + возврат 0; + если (зн == 0x09C1 || зн == 0x09C2 || зн == 0x09C3 || зн == 0x09C4) + возврат 0; + если (зн == 0x09CD) + возврат 0; + если (зн == 0x09E2 || зн == 0x09E3) + возврат 0; + если (зн == 0x0A01 || зн == 0x0A02) + возврат 0; + если (зн == 0x0A3C) + возврат 0; + если (зн == 0x0A41 || зн == 0x0A42) + возврат 0; + если (зн == 0x0A47 || зн == 0x0A48) + возврат 0; + если (зн >= 0x0A4B && зн <= 0x0A4D) + возврат 0; + если (зн == 0x0A70 || зн == 0x0A71) + возврат 0; + если (зн == 0x0A81 || зн == 0x0A82) + возврат 0; + если (зн == 0x0ABC) + возврат 0; + если (зн >= 0x0AC1 && зн <= 0x0AC5) + возврат 0; + если (зн == 0x0AC7 || зн == 0x0AC8) + возврат 0; + если (зн == 0x0ACD) + возврат 0; + если (зн == 0x0AE2 || зн == 0x0AE3) + возврат 0; + если (зн == 0x0B01) + возврат 0; + если (зн == 0x0B3C) + возврат 0; + если (зн == 0x0B3F) + возврат 0; + если (зн == 0x0B41 || зн == 0x0B42 || зн == 0x0B43) + возврат 0; + если (зн == 0x0B4D) + возврат 0; + если (зн == 0x0B56) + возврат 0; + если (зн == 0x0B82) + возврат 0; + если (зн == 0x0BC0) + возврат 0; + если (зн == 0x0BCD) + возврат 0; + если (зн >= 0x0C3E && зн <= 0x0C40) + возврат 0; + если (зн >= 0x0C46 && зн <= 0x0C48) + возврат 0; + если (зн >= 0x0C4A && зн <= 0x0C4D) + возврат 0; + если (зн == 0x0C55 || зн == 0x0C56) + возврат 0; + если (зн == 0x0CBC) + возврат 0; + если (зн == 0x0CBF) + возврат 0; + если (зн == 0x0CC6) + возврат 0; + если (зн == 0x0CCC || зн == 0x0CCD) + возврат 0; + если (зн == 0x0CE2 || зн == 0x0CE3) + возврат 0; + если (зн == 0x0D41 || зн == 0x0D42 || зн == 0x0D43) + возврат 0; + если (зн == 0x0D4D) + возврат 0; + если (зн == 0x0DCA) + возврат 0; + если (зн == 0x0DD2 || зн == 0x0DD3 || зн == 0x0DD4) + возврат 0; + если (зн == 0x0DD6) + возврат 0; + если (зн == 0x0E31) + возврат 0; + если (зн >= 0x0E34 && зн <= 0x0E3A) + возврат 0; + если (зн >= 0x0E47 && зн <= 0x0E4E) + возврат 0; + если (зн == 0x0EB1) + возврат 0; + если (зн >= 0x0EB4 && зн <= 0x0EB9) + возврат 0; + если (зн == 0x0EBB || зн == 0x0EBC) + возврат 0; + если (зн >= 0x0EC8 && зн <= 0x0ECD) + возврат 0; + если (зн == 0x0F18 || зн == 0x0F19) + возврат 0; + если (зн == 0x0F35) + возврат 0; + если (зн == 0x0F37) + возврат 0; + если (зн == 0x0F39) + возврат 0; + если (зн >= 0x0F71 && зн <= 0x0F7E) + возврат 0; + если (зн >= 0x0F80 && зн <= 0x0F84) + возврат 0; + если (зн == 0x0F86 || зн == 0x0F87) + возврат 0; + если (зн >= 0x0F90 && зн <= 0x0F97) + возврат 0; + если (зн >= 0x0F99 && зн <= 0x0FBC) + возврат 0; + если (зн == 0x0FC6) + возврат 0; + если (зн >= 0x102D && зн <= 0x1030) + возврат 0; + если (зн == 0x1032) + возврат 0; + если (зн == 0x1036 || зн == 0x1037) + возврат 0; + если (зн == 0x1039) + возврат 0; + если (зн == 0x1058 || зн == 0x1059) + возврат 0; + + если (зн >= 0x1160 && зн <= 0x11FF) + возврат 0; + если (зн == 0x135F) + возврат 0; + если (зн == 0x1712 || зн == 0x1713 || зн == 0x1714) + возврат 0; + если (зн == 0x1732 || зн == 0x1733 || зн == 0x1734) + возврат 0; + если (зн == 0x1752 || зн == 0x1753) + возврат 0; + если (зн == 0x1772 || зн == 0x1773) + возврат 0; + если (зн >= 0x17B4 && зн <= 0x17B5) + возврат 0; + если (зн >= 0x17B7 && зн <= 0x17BD) + возврат 0; + если (зн == 0x17C6) + возврат 0; + если (зн >= 0x17C9 && зн <= 0x17D3) + возврат 0; + если (зн == 0x17DD) + возврат 0; + если (зн >= 0x180B && зн <= 0x180D) + возврат 0; + если (зн == 0x18A9) + возврат 0; + если (зн == 0x1920 || зн == 0x1921 || зн == 0x1922) + возврат 0; + если (зн == 0x1927 || зн == 0x1928) + возврат 0; + если (зн == 0x1932) + возврат 0; + если (зн == 0x1939 || зн == 0x193A || зн == 0x193B) + возврат 0; + если (зн == 0x1A17 || зн == 0x1A18) + возврат 0; + если (зн >= 0x1B00 && зн <= 0x1B03) + возврат 0; + если (зн == 0x1B34) + возврат 0; + если (зн >= 0x1B36 && зн <= 0x1B3A) + возврат 0; + если (зн == 0x1B3C) + возврат 0; + если (зн == 0x1B42) + возврат 0; + если (зн >= 0x1B6B && зн <= 0x1B73) + возврат 0; + если (зн >= 0x1DC0 && зн <= 0x1DCA) + возврат 0; + если (зн == 0x1DFE || зн == 0x1DFF) + возврат 0; + + если (зн >= 0x200B && зн <= 0x200F) + возврат 0; + если (зн >= 0x202A && зн <= 0x202E) + возврат 0; + если (зн >= 0x2060 && зн <= 0x2063) + возврат 0; + если (зн >= 0x206A && зн <= 0x206F) + возврат 0; + + если (зн >= 0x20D0 && зн <= 0x20EF) + возврат 0; + если (зн >= 0x302A && зн <= 0x302F) + возврат 0; + если (зн == 0x3099 || зн == 0x309A) + возврат 0; + + если (зн == 0xA806) + возврат 0; + если (зн == 0xA80B) + возврат 0; + если (зн == 0xA825 || зн == 0xA826) + возврат 0; + если (зн == 0xFB1E) + возврат 0; + если (зн >= 0xFE00 && зн <= 0xFE0F) + возврат 0; + если (зн >= 0xFE20 && зн <= 0xFE23) + возврат 0; + + если (зн == 0xFEFF) + возврат 0; + если (зн >= 0xFFF9 && зн <= 0xFFFB) + возврат 0; + + если (зн == 0x10A01 || зн == 0x10A02 || зн == 0x10A03) + возврат 0; + если (зн == 0x10A05 || зн == 0x10A06) + возврат 0; + если (зн >= 0x10A0C && зн <= 0x10A0F) + возврат 0; + если (зн == 0x10A38 || зн == 0x10A39 || зн == 0x10A3A) + возврат 0; + если (зн == 0x10A3F) + возврат 0; + + если (зн >= 0x1D167 && зн <= 0x1D169) + возврат 0; + если (зн >= 0x1D173 && зн <= 0x1D182) + возврат 0; + если (зн >= 0x1D185 && зн <= 0x1D18B) + возврат 0; + если (зн >= 0x1D1AA && зн <= 0x1D1AD) + возврат 0; + если (зн >= 0x1D242 && зн <= 0x1D244) + возврат 0; + если (зн == 0xE0001) + возврат 0; + если (зн >= 0xE0020 && зн <= 0xE007F) + возврат 0; + если (зн >= 0xE0100 && зн <= 0xE01EF) + возврат 0; + + если (зн >= 0x1100 && зн <= 0x115F) + возврат 2; + если (зн == 0x2329 || зн == 0x232A) + возврат 2; + если (зн >= 0x2E80 && зн <= 0x303E) + возврат 2; + если (зн >= 0x3040 && зн <= 0xA4CF) + возврат 2; + если (зн >= 0xAC00 && зн <= 0xD7A3) + возврат 2; + если (зн >= 0xF900 && зн <= 0xFAFF) + возврат 2; + если (зн >= 0xFE10 && зн <= 0xFE19) + возврат 2; + если (зн >= 0xFE30 && зн <= 0xFE6F) + возврат 2; + если (зн >= 0xFF00 && зн <= 0xFF60) + возврат 2; + если (зн >= 0xFFE0 && зн <= 0xFFE6) + возврат 2; + если (зн >= 0x1F000 && зн <= 0x1F644) + возврат 2; + если (зн >= 0x20000 && зн <= 0x3FFFD) + возврат 2; + + возврат 1; +} + +цел display_width(символ *п, цел длина) { + символ *конец = п + длина; + цел шир = 0; + + пока (п < конец) { + uint32_t зн = decode_utf8(&п, п); + шир += ширина_символа(зн); + } + + возврат шир; +} \ No newline at end of file diff --git a/руси.з b/руси.з new file mode 100644 index 0000000..d8d761f --- /dev/null +++ b/руси.з @@ -0,0 +1,474 @@ +// ============================================================ +// Заголовочный файл компилятора RUSI +// ============================================================ + +#еслинеопр RUSI_H +#определить RUSI_H + +#определить _POSIX_C_SOURCE 200809L + +#включить // стандартная библиотека +#включить // стандартная библиотека +#включить // стандартная библиотека +#включить // стандартная библиотека +#включить // стандартная библиотека +#включить // стандартная библиотека +#включить // стандартная библиотека +#включить // стандартная библиотека +#включить // стандартная библиотека +#включить // стандартная библиотека +#включить // стандартная библиотека +#включить // стандартная библиотека +#включить // стандартная библиотека +#включить // стандартная библиотека +#включить // стандартная библиотека +#включить // стандартная библиотека +#включить // стандартная библиотека +#включить // стандартная библиотека + +пусто free_all_types(пусто); +пусто free_all_tokens(пусто); +пусто free_all_preprocess(пусто); + +// ============================================================ +// Общие макросы +// ============================================================ + +#определить MAX(a, b) ((a) < (b) ? (b) : (a)) +#определить MIN(a, b) ((a) < (b) ? (a) : (b)) + +#еслинеопр __GNUC__ +#определить __attribute__(x) +#конецесли + +// ============================================================ +// Предварительные объявления структур +// ============================================================ + +типдеф структ Type Type; +типдеф структ Node Node; +типдеф структ Member Member; +типдеф структ Relocation Relocation; +типдеф структ Hideset Hideset; + +// ============================================================ +// Модуль strings.c - Работа со строками +// ============================================================ + +типдеф структ { + символ **data; // Массив строк + цел capacity; // Вместимость + цел len; // Текущее количество +} МассивСтрок; + +пусто массив_строк_добавить(МассивСтрок *массив, символ *строка); +символ *формат(символ *строка_формата, ...) __attribute__((format(printf, 1, 2))); +пусто массив_строк_освободить(МассивСтрок *массив); +пусто массив_строк_очистить(МассивСтрок *массив); +символ *массив_строк_получить(конст МассивСтрок *массив, size_t инд); +бул массив_строк_установить(МассивСтрок *массив, size_t инд, символ *строка); +бул массив_строк_вставить(МассивСтрок *массив, size_t поз, символ *строка); +бул массив_строк_удалить(МассивСтрок *массив, size_t инд); +ssize_t массив_строк_найти(конст МассивСтрок *массив, конст символ *строка); +бул массив_строк_содержит(конст МассивСтрок *массив, конст символ *строка); + +// ============================================================ +// Модуль tokenize.c - Лексический анализ +// ============================================================ + +типдеф переч { + TK_IDENT, // Идентификаторы + TK_PUNCT, // Пунктуаторы + TK_KEYWORD, // Ключевые слова + TK_STR, // Строковые литералы + TK_NUM, // Числовые литералы + TK_PP_NUM, // Препроцессорные числа + TK_EOF, // Маркер конца файла +} TokenKind; + +типдеф структ { + символ *name; // Имя файла + цел file_no; // Номер файла + символ *contents; // Содержимое файла + символ *display_name; // Отображаемое имя (для #строка) + цел line_delta; // Смещение строк +} File; + +типдеф структ Token Token; +структ Token { + TokenKind kind; // Тип токена + Token *next; // Следующий токен + int64_t val; // Значение для TK_NUM + длин двойн fval; // Значение с плавающей точкой для TK_NUM + символ *loc; // Позиция в исходном коде + цел len; // Длина токена + Type *ty; // Тип (для TK_NUM или TK_STR) + символ *str; // Содержимое строкового литерала (с \0) + + File *file; // Файл источника + символ *filename; // Имя файла + цел line_no; // Номер строки + цел line_delta; // Смещение строки + бул at_bol; // В начале строки + бул has_space; // Есть пробел перед токеном + Hideset *hideset; // Для раскрытия макросов + Token *origin; // Исходный токен (если раскрыт из макроса) +}; + +// Ошибки и предупреждения +noreturn пусто error(символ *format, ...) __attribute__((format(printf, 1, 2))); +noreturn пусто error_at(символ *location, символ *format, ...) __attribute__((format(printf, 2, 3))); +noreturn пусто error_tok(Token *token, символ *format, ...) __attribute__((format(printf, 2, 3))); +пусто warn_tok(Token *token, символ *format, ...) __attribute__((format(printf, 2, 3))); + +// Работа с токенами +бул equal(Token *token, символ *operator); +Token *skip(Token *token, символ *operator); +бул consume(Token **rest, Token *token, символ *str); +пусто convert_pp_tokens(Token *tokens); + +// Работа с файлами +File **get_input_files(пусто); +File *new_file(символ *name, цел file_no, символ *contents); + +// Токенизация +Token *tokenize_string_literal(Token *token, Type *base_type); +Token *tokenize(File *file); +Token *tokenize_file(символ *filename); + +#определить unreachable() error("internal error at %s:%d", __ФАЙЛ__, __ЛИНИЯ__) + +// ============================================================ +// Модуль preprocess.c - Препроцессинг +// ============================================================ + +символ *search_include_paths(символ *filename); +пусто init_macros(пусто); +пусто define_macro(символ *name, символ *value); +пусто undef_macro(символ *name); +Token *preprocess(Token *tokens); + +// ============================================================ +// Модуль parse.c - Синтаксический анализ +// ============================================================ + +типдеф структ Obj Obj; +структ Obj { + Obj *next; + символ *name; // Имя переменной + Type *ty; // Тип + Token *tok; // Представительный токен + бул is_local; // Локальная или глобальная + цел align; // Выравнивание + + // Локальные переменные + цел offset; // Смещение в стеке + + // Глобальные переменные или функции + бул is_function; // Это функция + бул is_definition; // Это определение + бул is_static; // Статическая + + // Глобальные переменные + бул is_tentative; // Предварительное объявление + бул is_tls; // TLS переменная + символ *init_data; // Данные инициализации + Relocation *rel; // Релокации + + // Функции + бул is_inline; // Inline функция + Obj *params; // Параметры + Node *body; // Тело функции + Obj *locals; // Локальные переменные + Obj *va_area; // Область для va_list + Obj *alloca_bottom; // Нижняя граница alloca + цел stack_size; // Размер стека + + // Статические inline функции + бул is_live; // Используется + бул is_root; // Корневая + МассивСтрок refs; // Ссылки (было StringArray) +}; + +типдеф структ Relocation Relocation; +структ Relocation { + Relocation *next; + цел offset; // Смещение + символ **label; // Метка + длин addend; // Добавка +}; + +типдеф переч { + ND_NULL_EXPR, // Пустое выражение + ND_ADD, // Сложение + ND_SUB, // Вычитание + ND_MUL, // Умножение + ND_DIV, // Деление + ND_NEG, // Унарный минус + ND_MOD, // Остаток от деления + ND_BITAND, // Побитовое И + ND_BITOR, // Побитовое ИЛИ + ND_BITXOR, // Побитовое исключающее ИЛИ + ND_SHL, // Сдвиг влево + ND_SHR, // Сдвиг вправо + ND_EQ, // Равно + ND_NE, // Не равно + ND_LT, // Меньше + ND_LE, // Меньше или равно + ND_ASSIGN, // Присваивание + ND_COND, // Условный оператор ?: + ND_COMMA, // Оператор запятая + ND_MEMBER, // Доступ к члену структуры + ND_ADDR, // Взятие адреса + ND_DEREF, // Разыменование + ND_NOT, // Логическое НЕ + ND_BITNOT, // Побитовое НЕ + ND_LOGAND, // Логическое И + ND_LOGOR, // Логическое ИЛИ + ND_RETURN, // возврат + ND_IF, // если + ND_FOR, // для или пока + ND_DO, // делай-пока + ND_SWITCH, // выбор + ND_CASE, // случай + ND_BLOCK, // Блок { ... } + ND_GOTO, // перейти + ND_GOTO_EXPR, // перейти с выражением (GNU) + ND_LABEL, // Метка + ND_LABEL_VAL, // Метка как значение (GNU) + ND_FUNCALL, // Вызов функции + ND_EXPR_STMT, // Выражение-инструкция + ND_STMT_EXPR, // Инструкция-выражение (GNU) + ND_VAR, // Переменная + ND_VLA_PTR, // VLA дескриптор + ND_NUM, // Число + ND_CAST, // Приведение типа + ND_MEMZERO, // Обнуление стековой переменной + ND_ASM, // асм + ND_CAS, // Атомарная compare-and-swap + ND_EXCH, // Атомарный обмен +} NodeKind; + +структ Node { + NodeKind kind; // Тип узла + Node *next; // Следующий узел + Type *ty; // Тип + Token *tok; // Представительный токен + + Node *lhs; // Левый операнд + Node *rhs; // Правый операнд + + // Условные и циклические конструкции + Node *cond; // Условие + Node *then; // Ветвь then + Node *els; // Ветвь else + Node *init; // Инициализация + Node *inc; // Инкремент + + // Метки для прерви и продолжи + символ *brk_label; + символ *cont_label; + + // Блок или выражение-инструкция + Node *body; + + // Доступ к члену структуры + Member *member; + + // Вызов функции + Type *func_ty; // Тип функции + Node *args; // Аргументы + бул pass_by_stack; // Передача через стек + Obj *ret_buffer; // Буфер для возврата + + // перейти, метки и метки как значения + символ *label; + символ *unique_label; + Node *goto_next; + + // выбор + Node *case_next; + Node *default_case; + + // случай + длин begin; + длин end; + + // асм + символ *asm_str; + + // Атомарные операции + Node *cas_addr; + Node *cas_old; + Node *cas_new; + Obj *atomic_addr; + Node *atomic_expr; + + // Переменная + Obj *var; + + // Числовой литерал + int64_t val; + длин двойн fval; +}; + +Node *new_cast(Node *expression, Type *type); +int64_t const_expr(Token **rest, Token *token); +Obj *parse(Token *tokens); + +// ============================================================ +// Модуль type.c - Система типов +// ============================================================ + +типдеф переч { + TY_VOID, // пусто + TY_BOOL, // _Bool + TY_CHAR, // символ + TY_SHORT, // корот + TY_INT, // цел + TY_LONG, // длин + TY_FLOAT, // плав + TY_DOUBLE, // двойн + TY_LDOUBLE, // длин двойн + TY_ENUM, // переч + TY_PTR, // указатель + TY_FUNC, // функция + TY_ARRAY, // массив + TY_VLA, // массив переменной длины + TY_STRUCT, // структ + TY_UNION, // объед +} TypeKind; + +структ Type { + TypeKind kind; // Тип + цел size; // Размер в байтах + цел align; // Выравнивание + бул is_unsigned; // Беззнаковый + бул is_atomic; // _Atomic + Type *origin; // Исходный тип (для типдеф) + + // Для указателей и массивов + Type *base; + + // Имя (для объявлений) + Token *name; + Token *name_pos; + + // Для массивов + цел array_len; // Длина массива + Node *vla_len; // Длина VLA (выражение) + Obj *vla_size; // Размер VLA + + // Для структур/объединений + Member *members; + бул is_flexible; // Гибкий массив + бул is_packed; // Упакованный + + // Для функций + Type *return_ty; // Возвращаемый тип + Type *params; // Параметры + бул is_variadic; // Функция с переменным числом аргументов + Type *next; // Следующий параметр +}; + +структ Member { + Member *next; + Type *ty; + Token *tok; // Для сообщений об ошибках + Token *name; + цел idx; // Индекс + цел align; // Выравнивание + цел offset; // Смещение + + // Битовые поля + бул is_bitfield; + цел bit_offset; + цел bit_width; +}; + +// Базовые типы +внеш Type *ty_void; +внеш Type *ty_bool; +внеш Type *ty_char; +внеш Type *ty_short; +внеш Type *ty_int; +внеш Type *ty_long; +внеш Type *ty_uchar; +внеш Type *ty_ushort; +внеш Type *ty_uint; +внеш Type *ty_ulong; +внеш Type *ty_float; +внеш Type *ty_double; +внеш Type *ty_ldouble; + +// Проверка типов +бул is_integer(Type *type); +бул is_flonum(Type *type); +бул is_numeric(Type *type); +бул is_compatible(Type *first, Type *second); + +// Создание типов +Type *copy_type(Type *original); +Type *pointer_to(Type *base_type); +Type *func_type(Type *return_type); +Type *array_of(Type *base_type, цел size); +Type *vla_of(Type *base_type, Node *expr); +Type *enum_type(пусто); +Type *struct_type(пусто); + +// Вывод типов +пусто add_type(Node *node); + +// ============================================================ +// Модуль codegen.c - Генерация кода +// ============================================================ + +пусто codegen(Obj *program, FILE *output); +цел align_to(цел value, цел alignment); + +// ============================================================ +// Модуль unicode.c - Поддержка Unicode +// ============================================================ + +цел encode_utf8(символ *buffer, uint32_t code_point); +uint32_t decode_utf8(символ **new_position, символ *input); +бул is_ident1(uint32_t code_point); +бул is_ident2(uint32_t code_point); +цел display_width(символ *text, цел length); + +// ============================================================ +// Модуль hashmap.c - Хеш-таблица +// ============================================================ + +типдеф структ { + символ *ключ; // Ключ + цел длина_ключа; // Длина ключа + пусто *значение; // Значение +} ЗаписьХеша; + +типдеф структ { + ЗаписьХеша *корзины; // Массив корзин + цел вместимость; // Вместимость + цел использовано; // Использовано +} ХешТаблица; + +пусто *хеш_получить(ХешТаблица *таблица, символ *ключ); +пусто *хеш_получить2(ХешТаблица *таблица, символ *ключ, цел длина_ключа); +пусто хеш_вставить(ХешТаблица *таблица, символ *ключ, пусто *значение); +пусто хеш_вставить2(ХешТаблица *таблица, символ *ключ, цел длина_ключа, пусто *значение); +пусто хеш_удалить(ХешТаблица *таблица, символ *ключ); +пусто хеш_удалить2(ХешТаблица *таблица, символ *ключ, цел длина_ключа); +пусто хеш_тест(пусто); + +// ============================================================ +// Модуль main.c - Основная программа +// ============================================================ + +бул file_exists(символ *path); + +внеш МассивСтрок include_paths; // было StringArray +внеш бул opt_fpic; +внеш бул opt_fcommon; +внеш символ *base_file; + +#конецесли // RUSI_H \ No newline at end of file diff --git a/ручные_тесты/fac.c b/ручные_тесты/fac.c new file mode 100644 index 0000000..0285054 --- /dev/null +++ b/ручные_тесты/fac.c @@ -0,0 +1,35 @@ +// ============================================================ +// Вычисление факториала числа (простая версия) +// ============================================================ + +#включить + +цел главн(пусто) { + цел число, ш; + длин результат = 1; // используем long, т.к. факториал быстро растет + + printf("=== ВЫЧИСЛЕНИЕ ФАКТОРИАЛА ===\n"); + printf("Введите целое число (0-20): "); + scanf("%d", &число); + + // Проверка ввода + если (число < 0) { + printf("Ошибка: факториал определен только для неотрицательных чисел!\n"); + возврат 1; + } + + если (число > 20) { + printf("Предупреждение: результат может не поместиться в 64-битное число!\n"); + } + + // Вычисление факториала + для (ш = 2; ш <= число; ш++) { + результат *= ш; + } + + // Вывод результата + printf("%d! = %ld\n", число, результат); + // Убрал строку с размером, т.к. она вызывает ошибку + + возврат 0; +} \ No newline at end of file diff --git a/ручные_тесты/name.c b/ручные_тесты/name.c new file mode 100644 index 0000000..c39bf95 --- /dev/null +++ b/ручные_тесты/name.c @@ -0,0 +1,23 @@ +// ============================================================ +// Программа с вводом имени пользователя +// ============================================================ + +// ./rusi.elf -o ./ручные_тесты/name ./ручные_тесты/name.c +// ./ручные_тесты/name + +#включить + +цел главн(пусто) { + символ имя[100]; + + // Запрос имени + printf("Как вас зовут? "); + + // Ввод строки с клавиатуры + scanf("%99s", имя); + + // Приветствие + printf("Привет, %s! Добро пожаловать в RUSI!\n", имя); + + возврат 0; +} \ No newline at end of file diff --git a/ручные_тесты/privertmir.c b/ручные_тесты/privertmir.c new file mode 100644 index 0000000..0075b2b --- /dev/null +++ b/ручные_тесты/privertmir.c @@ -0,0 +1,16 @@ +// ============================================================ +// Программа "Привет, мир!" на русском синтаксисе RUSI +// ============================================================ + +// ./rusi.elf -o ./ручные_тесты/privetmir ./ручные_тесты/privertmir.c +// ./ручные_тесты/privetmir + +#включить // стандартный ввод/вывод + +цел главн(пусто) { + // Вывод приветствия в консоль + printf("Привет, мир!\n"); + + // Возвращаем 0 - успешное завершение + возврат 0; +} \ No newline at end of file