В первой статье мы рассказывали про сферу, компилируемый язык, где всё построено на русском языке: ключевые слова, типы, стандартная библиотека. Компилятор написан на Rust и выдаёт машинный код через LLVM 18. С тех пор прошло несколько версий, и накопилось достаточно, чтобы рассказать отдельно. Сразу предупредим, что статья длинная: здесь и смена синтаксиса, и сборки под все платформы, и Android‑приложение с компилятором внутри, и сайт, который написан на сфере.

Репозиторий: github.com/nikitosklim/Sfera, лицензия MIT. Сайт с уроками и загрузками: языксфера.рф.

Что не давало покоя: русские слова и не русская пунктуация

Любой, кто пробовал писать на сфере, упирался в одно и то же. Слова русские, а скобки {}, стрелка ->, &&, <>, [] и $ приходилось набирать на английской раскладке. Язык, который заставлял писать код на двух раскладках сразу. Так быть не должно, поэтому пришлось менять привычные {} скобки на иной подход.

функция главная() -> целое {
    Список<целое> числа = Список<целое>();
    числа.добавить(10);
    если (числа.получить(0) > 5 && !(числа.размер() == 0)) {
        вывести("ok");
    }
    вернуть 0;
}

Стало:

функция главная(): целое
    Список из целых числа = Список из целых();
    числа.добавить(10);
    если (числа.получить(0) больше 5 и не (числа.размер() == 0))
        вывести("ok");
    конец
    вернуть 0;
конец

Что именно поменялось:

  • Блок закрывается словом конец. Открывающего слова нет: блок начинается сразу после заголовка. Цепочка если … иначе если … иначе закрывается одним конец.

  • Тип возврата пишется после двоеточия, стрелка не нужна.

  • Обобщённые типы пишутся словами: Список из целых, Словарь из текста и целого, результат из целого, канал из текста.

  • Операторы словами: и, или, не, меньше, больше, не меньше, не больше.

  • Подстановка в строках: "Сумма: %сумма%, вдвое: %(сумма * 2)%" вместо $имя.

  • Побитовые операции стали функциями: включить_биты(1, сдвинуть_влево(1, 4)) даёт 17.

Если вы считаете что данное обновление может приносить дискомфорт или это не удобно, или же у вас есть предложения как можно сделать лучше, то пожалуйста оставьте свой коментарий под этой статьей. Будем вместе думать и вносить изменения, чтобы добится лучшего варианта!

Старая запись продолжает работать, даже в одном проекте. А для переезда есть конвертер:

запустить --в-русский файл.код            # результат в стандартный вывод
запустить --в-русский файл.код --на-месте # перезаписать файл

Он не заменяет текст регулярками. Конвертер разбирает программу настоящим парсером, запоминает, какие токены перевести, а после преобразования проверяет, что дерево программы не изменилось. Если проверка не прошла, файл остаётся как был. Так мы и перевели весь репозиторий: примеры, тесты, стандартную библиотеку, сайт и панель разработчика.

Цена у этого решения тоже есть:

  • Слова конец, из, и, или, не, меньше, больше теперь зарезервированы. У меня в собственной панели нашлась переменная с именем в и несколько с именем и, пришлось переименовывать.

  • %имя% внутри любой строки теперь подстановка. Если нужен буквальный процент, пишется %%.

  • Операторы и и или не вычисляют правую часть лениво. Если вы привыкли писать х != 0 && 10 / х > 1, здесь так делать нельзя, нужны вложенные если. Я наступил на это сам: программа падала там, где в другом языке всё было бы в порядке. Это в списке того, что надо исправить.

  • Фраза «иначе если» превращается в цепочку только тогда, когда оба слова стоят на одной строке. Иначе парсер не отличит её от вложенного условия.

  • Если обобщённый тип вложен в другой и стоит не последним аргументом, пока остаются угловые скобки.

Один язык — пять платформ

Раньше «Сфера» жила у нас на MacBook или Windows. Теперь у каждой системы есть своя сборка:

  • Windows. Установщик и панель разработчика. Пришлось переписать сокеты на Winsock, переключать консоль в UTF-8 и подменять недостающие POSIX‑вызовы (snprintf, clock_gettime, потоки). На Windows было потрачено больше времени, чем на что‑либо другое: подряд идёт десяток коммитов с названием «Fix» про линковку, LLVM‑символы и условную компиляцию.

  • macOS. Отдельные сборки для Intel (собираемые у себя на MacBook) и Apple Silicon (собирается в GitHub Actions).

  • Linux. Панель и компилятор в архиве.

  • Android. Об этом отдельный рассказ ниже.

Среда разработки на MacOS
Среда разработки на MacOS

Линукс, Apple Silicon и Android собирает GitHub Actions: запуск вручную или при выпуске тега версии. Сборки для Intel Mac и Windows пока делаются скриптами. Дальше все файлы выкладываются на сайт, и рядом с каждым показана контрольная сумма SHA-256.

Компилятор внутри Android‑приложения

Это самое неожиданное, что получилось. Идея в том, чтобы писать и запускать код сферы на телефоне, без root, без Termux и без компьютера.

Среда разработки на Андроид устройстве
Среда разработки на Андроид устройстве

Устроено так. В APK лежит сам компилятор, собранный под ARM64, линковщик ld.lld и небольшая замена clang, которая нужна для линковки. Всё это приложение вызывает как обычные программы: компилирует код в объектный файл, линкует и запускает получившийся бинарник в собственной песочнице.

Пришлось решить несколько неприятных задач:

  • Откуда запускать бинарники. Android запрещает исполнять файлы из домашней папки приложения, начиная с API 29. Поэтому инструменты лежат в APK под видом библиотек lib*.so: система сама распаковывает их в каталог, откуда запуск разрешён. А для собранных на телефоне программ приходится собирать приложение с targetSdk 28. Из‑за этого в Google Play такое приложение не попадёт, поэтому распространяется оно через сайт.

  • Целевая тройка. LLVM, который я собрал под Android, не знал нужную тройку, пришлось указывать её явно.

  • Путь к библиотекам. Приложение определяет каталог с библиотеками при первой подготовке и сохраняет его в файл. Сначала я вычислял его на лету, и фоновый запуск терял цепочку родительских процессов: пользователь видел сообщение «В этом APK нет компилятора», хотя компилятор там был.

Редактор кода на Андроид
Редактор кода на Андроид

Сама среда разработки тоже написана на сфере: редактор с подсветкой синтаксиса, файлы, справка, уроки и примеры внутри приложения, консоль с выводом. Графика идёт через SDL по FFI. Приложение подписывается постоянным ключом, значок и версия берутся из Cargo.toml.

Проверка на живом телефоне выглядит так: открываю многопоточность.код, нажимаю «Запустить», в консоли появляется «сборка, готово, запуск, 4, код выхода: 0». Программа компилируется и исполняется на устройстве.

функция главная(): целое
    канал из целого канал_результата = создать_канал();

    параллельно
        целое вычислено = 2 + 2;
        канал_результата.отправить(вычислено);
    конец

    целое значение = канал_результата.получить();
    вывести(целое_в_текст(значение));
    вернуть 0;
конец

Сайт, написанный на сфере

Сайт обязательно должен был быть написан на нашем языке программирования. Это хороший способ найти его слабые места: если чего‑то не хватает для настоящего сервера, это сразу видно.

Что внутри:

  • HTTP‑сервер из стандартной библиотеки: пул из 16 потоков, каждый принимает подключения с общего слушающего сокета. Загрузка большого файла занимает только один поток.

  • Раздача файлов с докачкой (Range), ETag и ответами 304.

  • Собственный разбор Markdown (модуль разметка.код), оглавление и подсветка кода.

  • Поиск по всем материалам: индекс отдаётся одним текстом, поиск делает страница.

  • Загрузка установщиков на сервер через PUT по токену прямо потоком в файл, для этого есть скрипт upload-release.sh.

  • Счётчик посещений, карта сайта, robots.txt.

Маршрутизация выглядит так:

функция маршрутизировать(целое32 клиент, Запрос з): целое
    текст сегмент = первый_сегмент(з.путь);
    текст остаток = остаток_пути(з.путь);
    ...
    текст раздел = раздел_по_сегменту(сегмент);
    если (длина(раздел) больше 0)
        текст русский_раздел = раздел_в_адрес(раздел);
        если (длина(остаток) == 0)
            если (сегмент != русский_раздел)
                вернуть на_русский_адрес(клиент, з, "/" + русский_раздел);
            конец
            вернуть показать_каталог(клиент, раздел);
        конец
        ...

Домен у сайта тоже русский: языксфера.рф. Адреса страниц между прочем тоже на русском языке: /уроки/быстрый-старт, /документация/многопоточность, /загрузки, а служебные пути называются /статика и /загрузить. Старые латинские адреса, которые мы писали вначале разработки сайта, отвечают кодом 301 и перенаправляют на русские, так что ссылки не сломались.

Работает всё на обычном VPS: nginx с сертификатом Let's Encrypt проксирует запросы на сервер на «Сфере», который запущен как systemd‑служба. Релиз сайта собирает GitHub Actions, на сервере его разворачивает один скрипт.

На сайте сейчас 32 раздела документации, семь уроков (от первой программы до своего веб‑сервера), 11 примеров и поиск. Можете перейти и глянуть, я думаю это удобный способ просмотра того как устроен язык, всё разделено по разделам и также имеется поиск по словам, так что можно найти любую учебную информацию для справки, по тому как устроен язык. Обязательно перейдите на сайт и сами зацените, если есть пожелания, то пожалуйста напишите их в комментарии.

Что нашлось по нашему пути

Писать большой проект на своём языке полезно в основном из‑за ошибок, которые он вытаскивает наружу. Из недавних:

  • Проверка обязательного возврата из функций и методов работала неправильно. Бесконечный цикл пока (да) теперь считается возвратом, а ошибочные срабатывания исправлены.

  • Структуры с текстовыми полями копируют строку, поэтому нельзя хранить в них указатель на буфер, который может содержать нулевые байты: он обрезается. Для дескрипторов я использовал целые числа.

  • Через переменную, объявленную внутри цикла, на каждой итерации утекал стек. Это одна из самых неприятных ошибок за всё время: после полумиллиона итераций программа падала без единого предупреждения. Теперь все переменные размещаются в начале функции.

  • Подробный список найденных и исправленных use‑after‑free и double‑free есть в дневнике разработки в репозитории.

Чего ещё нет

Честно о слабых местах:

  • Язык молодой (версия 0.3.3), совместимость между версиями пока не гарантируется.

  • В отладчике нельзя посмотреть значение переменной по имени, а точки останова и шаги работают в обычных lldb и gdb.

  • Установщик для Windows не подписан, система покажет предупреждение.

  • Операторы и и или не ленивые, об этом выше.

  • Сообщества почти нет: нужна заинтересованность людей в нашем проекте.

Что дальше

  • Пакетный индекс для менеджера пакетов сфера_пакеты.

  • Значения переменных в отладчике.

  • Ленивые и и или.

  • Подписанные установщики и автоматическая сборка для Windows.

  • Больше уроков и примеров, раздел с проектами сообщества на сайте.

Сборки сферы под разные устройства
Сборки сферы под разные устройства
  1. Скачайте сборку для своей системы на языксфера.рф/загрузки. Для каждой системы там есть короткая инструкция.

  2. Пройдите быстрый старт, это около десяти минут.

  3. Или соберите из исходников: cargo build --release (нужен LLVM 18).

Если попробуете и что‑то сломается, напишите в issues. Такая обратная связь для нашего проекте ценнее всего! А если проект кажется полезным, его можно поддержать или же рассказать о нём знакомым.