From 4a11b8357a532203ae3d75f8afed29f053b27c72 Mon Sep 17 00:00:00 2001 From: mzhoot Date: Tue, 21 Jul 2026 10:20:27 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A1=D1=82=D0=B0=D1=80=D1=82=D0=BE=D0=B2?= =?UTF-8?q?=D1=8B=D0=B9=20=D0=BA=D0=BE=D0=BC=D0=BC=D0=B8=D1=82:=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=BC=D0=BF=D0=B8=D0=BB=D1=8F=D1=82=D0=BE=D1=80=20=D0=A0?= =?UTF-8?q?=D1=83=D0=A1=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- LICENSE.md | 24 + Makefile | 64 + README.md | 762 ++++++++++ codegen.c | 1568 +++++++++++++++++++ hashmap.c | 320 ++++ include/common.c | 119 ++ include/rstdlib.h | 358 +++++ include/stdarg.h | 145 ++ include/stdbool.h | 45 + include/stddef.h | 82 + include/stdnoreturn.h | 49 + include/stdvv.c | 53 + include/stdvv.h | 25 + main.c | 875 +++++++++++ md2.o | Bin 0 -> 416 bytes parse.c | 2096 ++++++++++++++++++++++++++ preprocess.c | 1037 +++++++++++++ rusi.elf | Bin 0 -> 503480 bytes stage2/rusi.elf | Bin 0 -> 503480 bytes strings.c | 253 ++++ test/alignof.c | 50 + test/alloca.c | 29 + test/arith.c | 145 ++ test/asm.c | 24 + test/atomic.c | 64 + test/attribute.c | 40 + test/bitfield.c | 58 + test/builtin.c | 34 + test/cast.c | 60 + test/commonsym.c | 20 + test/compat.c | 20 + test/complit.c | 34 + test/const.c | 15 + test/constexpr.c | 56 + test/control.c | 101 ++ test/decl.c | 23 + test/driver.py | 987 ++++++++++++ test/enum.c | 19 + test/extern.c | 27 + test/float.c | 94 ++ test/function.c | 397 +++++ test/generic.c | 13 + test/include1.h | 6 + test/include2.h | 1 + test/include3.h | 1 + test/include4.h | 1 + test/initializer.c | 269 ++++ test/line.c | 23 + test/literal.c | 105 ++ test/offsetof.c | 20 + test/pointer.c | 46 + test/pragma-once.c | 10 + test/sizeof.c | 107 ++ test/stdhdr.c | 8 + test/string.c | 74 + test/struct.c | 68 + test/thirdparty/common | 9 + test/thirdparty/cpython.sh | 15 + test/thirdparty/git.sh | 7 + test/thirdparty/libpng.sh | 10 + test/thirdparty/sqlite.sh | 10 + test/thirdparty/tinycc.sh | 17 + test/tls.c | 43 + test/typedef.c | 19 + test/typeof.c | 15 + test/unicode.c | 106 ++ test/union.c | 25 + test/usualconv.c | 34 + test/varargs.c | 52 + test/variable.c | 74 + test/vla.c | 28 + test/тест.з | 42 + test_ru/#else.c | 10 + test_ru/alignas.c | 8 + test_ru/alignof.c | 8 + test_ru/asm.c | 9 + test_ru/asm2.c | 15 + test_ru/atomic.c | 10 + test_ru/auto.c | 8 + test_ru/bool.c | 10 + test_ru/break.c | 14 + test_ru/case.c | 21 + test_ru/char.c | 5 + test_ru/const.c | 8 + test_ru/continue.c | 16 + test_ru/counter_ru.c | 9 + test_ru/cyrillic_ident.c | 27 + test_ru/cyrillic_ptr.c | 55 + test_ru/date_ru.c | 11 + test_ru/default.c | 21 + test_ru/define.c | 10 + test_ru/defined.c | 10 + test_ru/do.c | 13 + test_ru/double.c | 5 + test_ru/elif.c | 12 + test_ru/else.c | 9 + test_ru/endif.c | 9 + test_ru/enum.c | 14 + test_ru/error | Bin 0 -> 16120 bytes test_ru/error.c | 11 + test_ru/extern.c | 10 + test_ru/file_ru.c | 10 + test_ru/float.c | 5 + test_ru/for.c | 12 + test_ru/func_ru.c | 8 + test_ru/function_ru.c | 8 + test_ru/generic.c | 8 + test_ru/goto.c | 11 + test_ru/if.c | 8 + test_ru/ifdef.c | 9 + test_ru/ifndef.c | 7 + test_ru/include.c | 10 + test_ru/inline.c | 12 + test_ru/int.c | 6 + test_ru/line.c | 7 + test_ru/line_ru.c | 9 + test_ru/long.c | 9 + test_ru/main.c | 9 + test_ru/noreturn.c | 11 + test_ru/once.c | 11 + test_ru/once_header.h | 4 + test_ru/register.c | 8 + test_ru/restrict.c | 9 + test_ru/return.c | 12 + test_ru/run_tests.py | 187 +++ test_ru/short.c | 5 + test_ru/signed.c | 8 + test_ru/sizeof.c | 9 + test_ru/static.c | 15 + test_ru/struct.c | 15 + test_ru/switch.c | 21 + test_ru/test_import.c | 7 + test_ru/test_ru.csv | 66 + test_ru/thread_local.c | 9 + test_ru/time_ru.c | 11 + test_ru/typedef.c | 10 + test_ru/typeof.c | 9 + test_ru/undef.c | 7 + test_ru/union.c | 14 + test_ru/unsigned.c | 8 + test_ru/void.c | 4 + test_ru/volatile.c | 8 + test_ru/while.c | 13 + test_ru/привет.з | 9 + test_ru/тест.рс | 7 + tokenize.c | 762 ++++++++++ type.c | 327 ++++ unicode.c | 558 +++++++ руси.з | 474 ++++++ ручные_тесты/fac.c | 35 + ручные_тесты/name.c | 23 + ручные_тесты/privertmir.c | 16 + 152 files changed, 14638 insertions(+) create mode 100644 LICENSE.md create mode 100644 Makefile create mode 100644 README.md create mode 100644 codegen.c create mode 100644 hashmap.c create mode 100644 include/common.c create mode 100644 include/rstdlib.h create mode 100644 include/stdarg.h create mode 100644 include/stdbool.h create mode 100644 include/stddef.h create mode 100644 include/stdnoreturn.h create mode 100644 include/stdvv.c create mode 100644 include/stdvv.h create mode 100644 main.c create mode 100644 md2.o create mode 100644 parse.c create mode 100644 preprocess.c create mode 100755 rusi.elf create mode 100755 stage2/rusi.elf create mode 100644 strings.c create mode 100644 test/alignof.c create mode 100644 test/alloca.c create mode 100644 test/arith.c create mode 100644 test/asm.c create mode 100644 test/atomic.c create mode 100644 test/attribute.c create mode 100644 test/bitfield.c create mode 100644 test/builtin.c create mode 100644 test/cast.c create mode 100644 test/commonsym.c create mode 100644 test/compat.c create mode 100644 test/complit.c create mode 100644 test/const.c create mode 100644 test/constexpr.c create mode 100644 test/control.c create mode 100644 test/decl.c create mode 100755 test/driver.py create mode 100644 test/enum.c create mode 100644 test/extern.c create mode 100644 test/float.c create mode 100644 test/function.c create mode 100644 test/generic.c create mode 100644 test/include1.h create mode 100644 test/include2.h create mode 100644 test/include3.h create mode 100644 test/include4.h create mode 100644 test/initializer.c create mode 100644 test/line.c create mode 100644 test/literal.c create mode 100644 test/offsetof.c create mode 100644 test/pointer.c create mode 100644 test/pragma-once.c create mode 100644 test/sizeof.c create mode 100644 test/stdhdr.c create mode 100644 test/string.c create mode 100644 test/struct.c create mode 100644 test/thirdparty/common create mode 100755 test/thirdparty/cpython.sh create mode 100755 test/thirdparty/git.sh create mode 100755 test/thirdparty/libpng.sh create mode 100755 test/thirdparty/sqlite.sh create mode 100755 test/thirdparty/tinycc.sh create mode 100644 test/tls.c create mode 100644 test/typedef.c create mode 100644 test/typeof.c create mode 100644 test/unicode.c create mode 100644 test/union.c create mode 100644 test/usualconv.c create mode 100644 test/varargs.c create mode 100644 test/variable.c create mode 100644 test/vla.c create mode 100644 test/тест.з create mode 100644 test_ru/#else.c create mode 100644 test_ru/alignas.c create mode 100644 test_ru/alignof.c create mode 100644 test_ru/asm.c create mode 100644 test_ru/asm2.c create mode 100644 test_ru/atomic.c create mode 100644 test_ru/auto.c create mode 100644 test_ru/bool.c create mode 100644 test_ru/break.c create mode 100644 test_ru/case.c create mode 100644 test_ru/char.c create mode 100644 test_ru/const.c create mode 100644 test_ru/continue.c create mode 100644 test_ru/counter_ru.c create mode 100644 test_ru/cyrillic_ident.c create mode 100644 test_ru/cyrillic_ptr.c create mode 100644 test_ru/date_ru.c create mode 100644 test_ru/default.c create mode 100644 test_ru/define.c create mode 100644 test_ru/defined.c create mode 100644 test_ru/do.c create mode 100644 test_ru/double.c create mode 100644 test_ru/elif.c create mode 100644 test_ru/else.c create mode 100644 test_ru/endif.c create mode 100644 test_ru/enum.c create mode 100755 test_ru/error create mode 100644 test_ru/error.c create mode 100644 test_ru/extern.c create mode 100644 test_ru/file_ru.c create mode 100644 test_ru/float.c create mode 100644 test_ru/for.c create mode 100644 test_ru/func_ru.c create mode 100644 test_ru/function_ru.c create mode 100644 test_ru/generic.c create mode 100644 test_ru/goto.c create mode 100644 test_ru/if.c create mode 100644 test_ru/ifdef.c create mode 100644 test_ru/ifndef.c create mode 100644 test_ru/include.c create mode 100644 test_ru/inline.c create mode 100644 test_ru/int.c create mode 100644 test_ru/line.c create mode 100644 test_ru/line_ru.c create mode 100644 test_ru/long.c create mode 100644 test_ru/main.c create mode 100644 test_ru/noreturn.c create mode 100644 test_ru/once.c create mode 100644 test_ru/once_header.h create mode 100644 test_ru/register.c create mode 100644 test_ru/restrict.c create mode 100644 test_ru/return.c create mode 100755 test_ru/run_tests.py create mode 100644 test_ru/short.c create mode 100644 test_ru/signed.c create mode 100644 test_ru/sizeof.c create mode 100644 test_ru/static.c create mode 100644 test_ru/struct.c create mode 100644 test_ru/switch.c create mode 100644 test_ru/test_import.c create mode 100644 test_ru/test_ru.csv create mode 100644 test_ru/thread_local.c create mode 100644 test_ru/time_ru.c create mode 100644 test_ru/typedef.c create mode 100644 test_ru/typeof.c create mode 100644 test_ru/undef.c create mode 100644 test_ru/union.c create mode 100644 test_ru/unsigned.c create mode 100644 test_ru/void.c create mode 100644 test_ru/volatile.c create mode 100644 test_ru/while.c create mode 100644 test_ru/привет.з create mode 100644 test_ru/тест.рс create mode 100644 tokenize.c create mode 100644 type.c create mode 100644 unicode.c create mode 100644 руси.з create mode 100644 ручные_тесты/fac.c create mode 100644 ручные_тесты/name.c create mode 100644 ручные_тесты/privertmir.c 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 0000000000000000000000000000000000000000..86da1f4de1cb9bfe121cb7d0c43a68e85a21e73f GIT binary patch literal 416 zcmb<-^>JfjWMqH=Mg}_u1P><4z>t7oGB|+Q4h*aeEDQ{K#TmsVMJ0(z40next; + если (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 0000000000000000000000000000000000000000..1736c2cec7f0c947630e86972de42cf37654f879 GIT binary patch literal 503480 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

kvkg+}i35f;TupO#gU&@x_! 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 literal 0 HcmV?d00001 diff --git a/stage2/rusi.elf b/stage2/rusi.elf new file mode 100755 index 0000000000000000000000000000000000000000..f424bf74e67c8b610198e45e21eddc0331b85517 GIT binary patch literal 503480 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

kvkg+}i35f;TupO#gU&@x_! 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 rw%u62*c05=eR2Fx+D!bO-zlb@%a7NNmW // стандартная библиотека +#включить // стандартная библиотека +#включить // стандартная библиотека +#включить // стандартная библиотека + +// ============================================================ +// Управление динамическим массивом строк +// ============================================================ + +пусто массив_строк_добавить(МассивСтрок *массив, символ *строка) { + если (!массив) возврат; + + // Инициализация массива при первом использовании + если (!массив->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 0000000000000000000000000000000000000000..c1d7121a29247c1885ff70a31325fc1299af714f GIT binary patch literal 16120 zcmeHOU2Ggz6~41hVy7hb+DWREKr<4eDnFjJUDsHZ+KyvyJeKUlBsPSHWV%0Nd&TaK zH9H&GK?NH(f^A=ngrcYqC@-K&2nmTGMTms0f@=BzTEhdpAXycaf)a`;e^wD$&bjxT z$#`l*l?Q~-TzT&Kx#zn--@P+?*L&wH>G6{ZO%t3F;yHnqo;0OPF@Xt}MYaj6-frLj{=6nNnf-F15Esl5t1} z;wA|f93nIcszWC!|F#e8?MP_8a?FANTJ5A+)q*XWbY;~20FTpOdOrbS{& z)X~RyE=L>7F(Fm7Y-L>7#&V1eU+DXw32|LJ1V0HreFwj=Z5UzBuJ{?xw@aBqS)h#r z91x@9V<(2~KI5r=2%9arWeT&gq6GC(6NkWJp09u73M{{b_$alt`MfDLT$9*0PFo>A zbHQb?j2Va-h#80(h#80(h#80(h#80(_}^sU&mE7yXRX|8vtHTz`;-vY<+`6}HmsGO zw%wK;#`4}jjMdgJShcsTmEUbnPNjS6y>D7;Yx_D)(LC~9K>cOL=s5mT5P$oDxq4&J zG==pn=Ki4@7=lgg{|P#p8#Al7XC4H195=-+8Nd9F6tzw3^>?1PUf*i5v^T8v`+jFo zLo3w~#bbY$p8Y&@N>mR`TeY|ETxvF(Z|qHjM>~J}eeGfV-Ip=YTE4f6-K#lUYnSH% zgq>Zx?1R=L{oh*v)mpiXB5Bo1OV&zy>FZYQMYyV~R&DyKRhzgrUQ53=UYq#-yB(eB z8<0)ZA)EdgWak@@IlqCdv;kT54rDKGT5EM|=x!@1R_)$-pgx$FU?wmsPB8;912F?J z12F?J12F?J12F?J12F?J12F>+$ACt^WF98CcIk}J7LwYr1FdbV@Pi-zE{MOyZ~d{^ zd;()eT9fF*uf&Hpn@#zfI{M=jGY~TnGY~TnGY~TnGY~TnGY~TnGY~WI|IGmW2#l#g z=c)OOD4r?BbU+FCB56wPAlX+5_ZZoHF8UbRd_MWZWLtC&8o!HT;&MEni^*&;rTpvt zri;osvibXIooqbEifMuDN0cc(L-Io;kCHq>k_De#*7umw#r?qV!kUww<~tSN_NqcSp67Z-PMv7ppID~K zk3wkT2p}jh#c`=Qx8$VYukp>st;UbmR~kQV{IGFz{j2M*q#Cb+>w4ox<7VRrsloN- z_2pDY z-3xTTQ8>J*q5P{6e;@hz{^fWJ6iqxT`rw=fCOH`(r=;E~_DAPoTuyhl{wxylMdmSP)s4cpVZ%RM>SBv`375xenY&)8ecwHP3 zM+Va@50cAr4MXa|rfqxm`a zH29M+FL*vCoSp%HSK@$}r1|CNUJLqRUUc%~eSzr=)e{}p1&ZhUPDKlm13&gN+OCW7 z33zH!{TwfHp5JS@@ZLctTd@7iEIKNF6^{GlMBee7*+Rv4JlikXxuRQkDiE1>?b)K6 z%@pmt?|KzGQ(X`_w=`dLd?#=8>>Odw6v_oV<9V4y+bR3rqL}eACCAQJOQl7Cge)5( z{cWMPee%rEMA}ZD8nJC*k9^_O(8SoVut&#FpBNgqPoF$_Ha%re4V@TILvi#?3p4zF zg_FY_q;I5uJ0JL<*G67 zx$}?C!3uwrU`64I^4#O7)tcMazGx(frbrx`$@+>SK zG7IGym-S3G>p2(Mg13Jh)`5~>nLez7`xIFE@XS+bBVkHqgB-k1kxs}kHk>{N2JR&o zFIr4htUmmNuB;&EeFi)5b-XZMC%nE3o}V9#$LBjfBe;E&@FwAT|FVw&+&1)rnJ};9iUIydPU2JoXP0j!}b)+vk3L4tyBO_&W8!PI%tWL5NIP zM>t&o1NUN#=l$0f;kn-|IgaHQ0grn-wt0W2UcwVXr^cfFC%3o+3UbMK-cRa8Fz+Y1 z{pk2#0xXV+%Y)}HZjK=FK3ZAyZXg>VUxq7;=g$Y7@E0S5kvphiHG=2Q!Ke%GS^fgtVSKPp>QV80pK%?GWBKa{p7%RP3D4i#qIkA{3kuf7{pWp1AK`;_ zO#q^U;o>dO&`05cpYh#+M=6YFc^BNQheKbW3ZwNV!$F|Gw+nuifV2z(RJ5&q;E4J# z_!i+|=ejLLV-N3w|0Ut0>pzOu2~XicTaWfC^=#l#3gg3)@4ay7JmK$9;p0%4gp1b= kuPgkX%yS>cpX*mvl^deMhzWb+o$)_^S^+*40YpskFY@J}TL1t6 literal 0 HcmV?d00001 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