Статья посвящена системному программированию на русском языке с C-подобным синтаксисом. Компилятор для языков программирования РУСИ, РУСИ++ и объектный-РУСИ создан путём модификации и пересборки компилятора GCC 14й версии с превращением его в полноценный русско-английский билингвальный компилятор. Отдельно дана инструкция модификации русской раскладки клавиатуры для добавления в неё фигурных, угловых и квадратных скобок - дабы не переключаться и не терять поток.
Оглавление
Предыстория
Зачем это всё
Где взять и что внутри
Немного хулиганства
Теория: почему русский синтаксис работает
Сборка: от исходников до установки
Самосбор
Тесты из chibicc-rusi
Сравнительные сборки
РУСИ++: побочный продукт
Итог: форк chibicc породил три более совершенных языка
Перспективы применения
СПбГУ. RuC. Интерпретатор Терехова
Заключение
Ссылки
Бонус: фигурные скобки в русской раскладке
Спойлеры
Промпты для анализа статьи
Послесловие
Предыстория
В предыдущей статье был описан концепт билингвального самособирающегося компилятора языка Си, в котором кириллица уравнялась с оригинальным синтаксисом и можно писать на русском и английском Си одновременно. Код, напомню, выглядит так:
// Английский main с русским типом и оператором int main() { int x = 42; if (x == 42) { возврат 0; } return 1; } // Полностью русский синтаксис цел главн() { цел ъ = 42; если (ъ == 42) { возврат 0; } возврат 1; } // Смешанный стиль цел main() { printf("Легкоступов! Ты чего написал?"); возврат 0; }
Создан он был на базе chibicc для написания на нём кириллического ассемблера КВС https://xn–d1alm1bt.xn–p1ai/administrator/kvs. По сути, это был путь к своеобразной концептуальной полноте: кириллический ассемблер на кириллическом же Си. И для той задачи это было идеальным решением. Минималистичный русский компилятор под Linux. Ничего лишнего. Байты в бинарь складывать может. Задачу свою решает.
Но далее пошли предсказуемые комментарии со сравнениями с бухгалтерским программированием, предложением добавить падежи и пожеланиями не использовать это в продакшене. Обоснование последнего не осталось без внимания.
И действительно: во «взрослых» компиляторах есть ряд преимуществ как маркеров качества - множественное покрытие тестами и 40 лет ассемблерных оптимизаций. И как человек, пишущий свой ассемблер, я на это обратил внимание. Это тысячи человеко-лет работы, сотни оптимизационных проходов, отладка на миллионах строк кода.
Гипотетически: каким бы был полноценный продакшеновый русский Си?
Из спортивного интереса я решил попробовать. Смогу ли оседлать GCC и сделать его билингвом? Ранее мне поддались pascalABC.NET, Blaise и PHP. Вшивал я в них великий и могучий с целью поизучать на практике компиляторостроение. Отличный метод обучаться, кстати.
Получилось. Компилятор, в котором русские и английские ключевые слова - синонимы. Можно использовать любой язык или их гибрид. Реализовано так же, как в исходной статье. Даже тесты взял из русифицированного chibicc.
GCC сложнее, чем chibicc. Множество стандартов Си, поддерживаемых им, не могли не наложить свой отпечаток на его внутреннюю архитектуру.
Что получилось: RUSI на GCC 14.2.0. Русский Си, который компилируется в тот же самый машинный код, что и английский Си. С теми же оптимизациями. С той же производительностью.
Побочными продуктами получились РУСИ++ и объектный-РУСИ.
Зачем это всё
Эрик Рэймонд, «Собор и Базар» (1997), предисловие:
«Every good work of software starts by scratching a developer’s personal itch.»
Точный перевод: «Каждая хорошая программа начинается с того, что разработчик почесал свою личную нужду».
И где-то в России программист решил создать то, что считает нужным. И создаёт.
Линус Торвальдс, предисловие к книге Пекки Химанена «Хакерская этика и дух информационной эры» (2001):
«Linux был в значительной степени хобби (но серьёзным, лучшим из всех).»
Это не «продукт». Не «стартап». Не «проект для галочки». Хобби, но серьёзное. Работа, которая является игрой. Не потому, что «надо», а потому что интересно. Внутренний ребёнок радуется в процессе творчества.
Химанен в книге формулирует суть этики так:
«Хакер - это тот, кто решает проблему, а не тот, кто пользуется готовым решением. Если машина не делает то, что нужно, - хакер открывает её. Если система неудобна - хакер меняет её. Не потому, что заплатили, а потому что интересно. Не потому, что велели, а потому что сам захотел.»
И ведь можно открыть и переделать всё. Компилятор, раскладку, стандарт, технологию, чьё-то мнение.
RUSI - ровно это. Личное творчество. Захотел - переделал. Сделал. Сам захотел - и получилось.
Частично взгляды описаны в предыдущей статье Программисты на Руси программируют на Си. RUSI: Концепт билингвального компилятора.
В конце статьи есть список промптов для анализа текста статьи через призму различных подходов и взглядов.
Где взять и что внутри
Репозиторий: https://github.com/artradeskz/rgcc
Что внутри: GCC 14.2.0 с патчами для русского синтаксиса. Полный diff огромен - здесь не привожу. Но он есть в конце статьи под спойлером.
Краткая выжимка: что именно изменено и зачем.
1. UTF-8 в лексере (libcpp/lex.cc)
Традиционно работая с исходниками, GCC читает не по буквам, а по байтам. Так сложилось исторически. В интернете можно встретить утверждение, что GCC умеет работать с идентификаторами на UTF-8. Про то, что он может так же работать со служебными словами, там не будет сказано. Одна английская буква - один байт. Кириллица же в UTF-8 - это 2 байта на букву. И прежние алгоритмы чтения уже не сработают. Для русификации добавлена обработка UTF-8-последовательностей в forms_identifier_p, чтобы лексер парсил посимвольно, а не побайтово.
if (*buffer->cur == 0xD0 || *buffer->cur == 0xD1) { unsigned char first = *buffer->cur; unsigned char second = *(buffer->cur + 1); if (first == 0xD0 && second >= 0x90 && second <= 0xBF) { buffer->cur += 2; return true; } if (first == 0xD1 && second >= 0x80 && second <= 0x8F) { buffer->cur += 2; return true; } }
2. Синонимы ключевых слов (gcc/c-family/c-common.cc)
Добавлено ~250 русских синонимов в таблицу c_common_reswords[]:
{ "int", RID_INT, 0 }, { "цел", RID_INT_RU, 0 }, { "if", RID_IF, 0 }, { "если", RID_IF, 0 }, { "return", RID_RETURN, 0 }, { "возврат", RID_RETURN, 0 }, { "char", RID_CHAR, 0 }, { "символ", RID_CHAR, 0 },
3. Русские встроенные макросы (libcpp/init.cc)
B("__ФАЙЛ__", BT_FILE, false), B("__ЛИНИЯ__", BT_SPECLINE, true), B("__ДАТА__", BT_DATE, false), B("__СЧЁТЧИК__", BT_COUNTER, true), B("__ВРЕМЯ_ФАЙЛА__", BT_TIMESTAMP, false),
4. Русские директивы препроцессора (libcpp/directives.cc и libcpp/expr.cc)
Директивы препроцессора - это команды, которые начинаются с #. Они не являются частью языка C в том смысле, что парсер их не видит. Их обрабатывает libcpp до того, как парсер получит лексемы. Именно поэтому удалось добавить русские директивы без изменения парсера: достаточно было добавить синонимы в таблицу директив.
В libcpp/directives.cc есть таблица dtable[] - список всех директив. Туда добавлены русские аналоги:
{ do_define, UC"определить", sizeof "определить" - 1, KANDR, IN_I }, { do_include, UC"включить", sizeof "включить" - 1, KANDR, INCL | EXPAND }, { do_if, UC"если", sizeof "если" - 1, KANDR, COND | IF_COND | EXPAND }, { do_endif, UC"конецесли", sizeof "конецесли" - 1, KANDR, COND },
Теперь #включить <stdio.h> работает так же, как #include <stdio.h>. #определить - как #define. #если - как #if. И так далее.
Теперь можно писать:
#если определено(ОТЛАДКА) printf("Отладка включена\n"); #конецесли
Это полный русский аналог английского:
#if defined(DEBUG) printf("Отладка включена\n"); #endif
Немного хулиганства
Чтоб не перепутать стандартный и патченный компиляторы, было бы неплохо иметь возможность различать русифицированный GCC и обычный. Для этого можно доработать вывод команды с информацией о версии компилятора.
Где пропатчить команду gcc --version:
Файл: gcc/gcc.cc, 8900-я строка.
Там зашиты строка копирайта и текст про гарантию. Их можно заменить на свой текст.
if (print_version) { printf (_("%s %s%s\n"), progname, pkgversion_string, version_string); printf ("Copyright %s 2026 Coded by @artrades.\n", _("(C)")); fputs (_("Использовать можно и нужно. Но никто ни за что ответственность не несет(очевидно). This is free software; see the source for copying conditions. There is NO\n\ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"), stdout); if (! verbose_flag) return 0;
После сборки:
$ gcc --version gcc (GCC) 14.2.0 Copyright (C) 2026 Coded by @artrades. Использовать можно и нужно. Но никто ни за что ответственность не несет(очевидно). This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
В репозитории в данный участок кода - оригинальный. Текст можно менять на свое усмотрение.
Теория: почему русский синтаксис работает
Компилятор - это цепочка преобразований
Компилятор - это сложный многоэтапный механизм превращения слов в исполняемый файл. Это конвейер преобразователей, где каждый этап - это обособленная знаково-логическая система, словно свой отдельный язык программирования.
Упрощённая схема для любого компилятора высокого уровня:
Исходник → лексемы → AST → ассемблер → лексемы → AST → исполняемый файл
Между каждым этапом работает преобразователь со своей логикой.
Что такое «знаково-логическая система»
На каждом этапе есть:
Алфавит - набор допустимых символов
Правила - как из символов строить конструкции
Семантика - что эти конструкции означают
Примеры:
Этап | Алфавит | Что описывает |
|---|---|---|
Исходник | ASCII + UTF-8 | текст программы |
Лексемы | TK_INT, TK_IF, TK_RETURN | ключевые слова, операторы, идентификаторы |
AST | VarDecl, IfStmt, ReturnStmt | структура программы |
Ассемблер | mov, add, call | инструкции процессора |
Лексемы | MOV, ADD, CALL, REG_EAX, IMM, MEM | мнемоники, регистры, непосредственные значения, адреса памяти |
AST | Instruction(mov, src=REG, dst=REG), Label, Directive(.text) | инструкции, метки, директивы |
Бинарь | байты | готовая программа |
Что изменено
Только первые два преобразования:
Исходник → лексемы (лексер
libcpp)Лексемы → AST (парсер
c-parser.cc)
На этапе первого AST компилятору уже безразлично, как выглядели слова на человеческом языке.
Вот два исходника:
int x = 42;
цел x = 42;
После преобразований оба превращаются в один и тот же узел AST:
VarDecl(name="x", type=Int, init=42)
Дальше компилятор работает только с абстрактным синтаксическим деревом: оптимизирует его, преобразует в ассемблер, в объектный код. Информация о том, что ты написал int или цел, была актуальна лишь на этапе парсера.
Почему производительность идентична
Именно потому, что после AST всё одинаково:
int x = 42; ─┐ ├─ AST - ассемблер - объектный код - линкер - бинарь цел x = 42; ─┘
Русский и английский исходники дают идентичный AST, значит, идентичный ассемблер, идентичный объектный код, идентичный бинарь.
Это подтверждено экспериментально: секция .text (исполняемый код) русского и английского исходников побайтово идентична.
Ключевой вывод
RUSI - это не «новый язык». Это билингвальный фасад к существующему языку C.
GCC научили понимать русские слова на входе, на этапе лексера и парсера. Всё остальное - стандартный GCC 14.2.
Русский синтаксис не добавляет ни одной инструкции в машинный код. Потому что после парсера разницы между русским и английским C не существует, есть только AST.
Сборка: от исходников до установки
Проверено на Ubuntu 26 с установленным в систему GCC 12 или с GCC 15.
Качаем:
git clone --depth 1 https://github.com/artradeskz/rgcc.git
Зависимости:
sudo apt install flex bison libgmp-dev libmpfr-dev libmpc-dev
Или, если не хочется ставить dev-пакеты, используется встроенный скрипт:
cd rgcc ./contrib/download_prerequisites
Он скачает GMP, MPFR, MPC, ISL в дерево исходников:
2026-10-03 18:13:45 URL:https://gcc.gnu.org/pub/gcc/infrastructure/gettext-0.22.tar.gz [26105696/26105696] -> "gettext-0.22.tar.gz" [1] 2026-10-03 18:13:47 URL:http://gcc.gnu.org/pub/gcc/infrastructure/gmp-6.2.1.tar.bz2 [2493916/2493916] -> "gmp-6.2.1.tar.bz2" [1] 2026-10-03 18:13:49 URL:http://gcc.gnu.org/pub/gcc/infrastructure/mpfr-4.1.0.tar.bz2 [1747243/1747243] -> "mpfr-4.1.0.tar.bz2" [1] 2026-10-03 18:13:50 URL:http://gcc.gnu.org/pub/gcc/infrastructure/mpc-1.2.1.tar.gz [838731/838731] -> "mpc-1.2.1.tar.gz" [1] 2026-10-03 18:13:52 URL:http://gcc.gnu.org/pub/gcc/infrastructure/isl-0.24.tar.bz2 [2261594/2261594] -> "isl-0.24.tar.bz2" [1] gettext-0.22.tar.gz: OK gmp-6.2.1.tar.bz2: OK mpfr-4.1.0.tar.bz2: OK mpc-1.2.1.tar.gz: OK isl-0.24.tar.bz2: OK All prerequisites downloaded successfully.
Конфигурация:
mkdir build && cd build ../configure \ --enable-languages=c,c++ \ --disable-multilib \ --disable-nls \ --disable-libgomp \ --disable-libquadmath \ --disable-libssp \ --disable-libvtv \ --disable-libcc1 \ --disable-libmpx \ --disable-libcilkrts \ --disable-libsanitizer \ --disable-isl \ --disable-bootstrap \ --disable-graphite \ --prefix=/usr/local/gcc-rus
Пояснения:
--enable-languages=c,c++- собираем только C и C++--disable-isl- отключает ISL и Graphite--disable-bootstrap- отключен автоматический самосбор(об этом ниже)--prefix=/usr/local/gcc-rus- изолированная установка
Сборка (2 потока, можно и быстрее):
make -j2 2>&1 | tee build.log
На Aspire 5742G - около часа. На современной машине - 15–20 минут.
Установка:
sudo make install
Проверка:
/usr/local/gcc-rus/bin/gcc --version
Добавление в PATH:
echo 'export PATH=/usr/local/gcc-rus/bin:$PATH' >> ~/.bashrc source ~/.bashrc
Тесты из chibicc-rusi
Методика
Взяты тесты из chibicc-RUSI без изменений. Прогнаны на RUSI-GCC.
cd тесты python3 run_tests.py \ --compiler ~/Документы/RUSI++/rgcc/build-full/gcc/xgcc \ --build-dir ~/Документы/RUSI++/rgcc/build-full/gcc \ --dir test_ru \ --verbose
Результат: 63 из 65 (96.9%).
