Русский язык в программировании. РУСИ++

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Статья посвящена системному программированию на русском языке с 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)

инструкции, метки, директивы

Бинарь

байты

готовая программа

Что изменено

Только первые два преобразования:

  1. Исходник → лексемы (лексер libcpp)

  2. Лексемы → 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%).

Полный вывод тестов

Источник: https://habr.com/ru/articles/1069968/


Интересные статьи

Интересные статьи

Банк России сообщил о рисках при работе с криптообменниками и онлайн‑казино. Нелегальные площадки используют схему, которая сводит покупателей и продавцов напрямую. Это может вовлечь лю...
Привет, меня зовут Евгений, я разработчик из Байовэр в компании НЛМК ИТ.Довелось мне тут столкнуться с разработкой системы опытно-промышленных испытаний на производстве, и если описать это к...
Сегодня создание и поддержка веб-сайта стали неотъемлемой частью бизнеса. Если вы занимаетесь электронной коммерцией, ведете блог или разрабатываете корпоративный портал, вам потребуется надежная и фу...
Передо мной стояла задача выбрать библиотеку для расчета на Python, использующего операции над матрицами. Я выбрал и протестировал несколько вариантов, как использующих видеокарту (GPU), так и работаю...
Недавно на проекте интегрировал модуль CRM Битрикса c виртуальной АТС Ростелеком. Делал по стандартной инструкции, где пошагово показано, какие поля заполнять. Оказалось, следование ей не гаран...