Пока индустрия движется в сторону усложнения компиляторов, я задался вопросом: можно ли создать инструмент, который дает безопасность Rust, гибкость C и при этом не весит сотни мегабайт?
Так появился Flame — системный язык с фронтэндом в 260 КБ, который реализует управление памятью через статический анализ AST и предлагает альтернативный взгляд на обработку ошибок через патчинг дерева токенов.
Почему это не «очередной клон Си»?
Основные проблемы системных языков сегодня — это либо риск утечек (C/C++), либо избыточная сложность анализаторов (Rust). В Flame я реализовал три концепции, которые решают эти задачи иначе:
1. Автоудаление помеченных переменных в куче
Вместо сложного Borrow Checker как в Rust, Flame использует статический анализ AST по специальному алгоритму и вставляет инструкцию удаления. Алгоритм работает так:
Компилятор сканирует с конца;
Когда находит инструкцию, где использованная помеченная
autodel— вставляет после этой инструкции удаление;
Циклы считаются как одна инструкция, если переменная используется в последний раз в цикле и объявлен вне цикла — удалится после цикла. Если объявлена внутри цикла — удалится внутри цикла. А ветвления в виде if/else проверяются, и вставляется удаление в каждый блок где использована в последний раз. При передачи переменной по возврату функции или метода — наследуется autodel, но если при передаче используется только значение а не сам адрес — то создастся промежуточная переменная, потом вставится удаление, потом промежуточная передастся в возврат. Данная возможность не до конца проверена, и будет укрепляться и развиваться.
2. Meta-exceptions: патчинг токенов
Вместо тяжёлых try/catch, Flame полуил свой подход. В языке исключение описывается в специальной структуре, где объявляются: места где может появиться исключение, и проверка/замена. Когда компилятор видит последовательность токенов которые описаны каким то исключением — он обращается к этой структуре, и либо откатывается до завершаения прошлой инструкции (';' или '{' или '}'), и вставляет проверку, либо полностью вырезает эту инструкцию и вставляет замену (при описании замены можно вставлять исходную инструкцию с помощью $source). Это позволяет исключить необходимость try/catch и не засорять код if/else, но сохранить возможность перехватывать любые исключения.
Устройство компилятора
Компилятор (по словам GitHub) содержит: 83.6% кода на Си, и 16.4% Shell и LLVM (отладка и тестирование). Сам фронтэнд весит 260 КБ.
В процесс компиляции входят несколько этапов:
1. Препроцессор (#include, #ifdef и прочее)
2. Лексер (разбирает текст на токены)
3. Препарсер (работает с исключениями)
4. Парсер (разбирает токены на AST)
5. Прегенератор (работает с безопасностью памяти)
6. Генератор (разбирает AST на LLVM IR и компилирует в.o, линкует через GCC)
Cсылка на GitHub с исходным кодом
Примеры кода
В основном синтаксис один в один как в Си, но переменные и поля классов объявляются с ключевым словом var, а методы и функции через func.
Прмер объявления класса:
class Name { var int a; //числа имеют свой суффикс типа: 5 - int; 5s - short; 5l - long; var short b = 55s; func void c(int ab, int bb) { self->a = ab + bb; } new() { //конструктор self->a = 5 + 5; } delete() { //деструктор self->a = 0; } }
Пример автоудаления:
func int main() { var autodel Name *obj = new(); obj->c(5, 5); // <- тут будет delete() obj; return 0; }
Пример описания исключений:
exception DivByZero { var int op; instruction { %n / %:op //%n - любое число, %i - идентификатор } //%k - ключевое слово, %: - присваивание значения в переменную check { //есть replace, он полностью заменяет инструкцию на то что if (op == 0) { //op инициализируется в коде автоматически //... } } }
Остальные можно найти в README.md в GitHub
Будущее Flame
Flame будет активно разваиваться, улучшать совместимость с другими языками (через настройки компилятора @callconv, который позволит менять последовательность регистров для вызова функции) и добавлять новые фичи (Hot Reload или Restart через функцию из стандартной библеотеки которая через общую память и родительский процесс прогонит данные в новый процесс через таблицу имён). Скоро выйдет документация по языку.
Заключение
Flame — это язык, где я реализовал концепцию язык, который удобный, но мощный системный язык. Язык решает проблемы которые есть в других языках: перегруженность, сложность, медлительность, опасность. Но я не могу сказать, что язык не имеет минусов. Впереди ещё много работ над стабильностью и развитием!
