Приветствую всех читателей данной статьи.
Тут я постараюсь описать год усердной, тяжёлой работы на моим собственным компилятором языка программирования Rave.
Предыстория
Я уже много раз делал различные языки программирования - их создания стало для меня чем-то вроде хобби после учёбы.
Однако все мои языки программирования вплоть до Rave были не очень удачными - некоторые были слишком сложны в использовании, некоторые имели неудобный синтаксис и низкую скорость работы.
В итоге, когда я всё переосмыслил, я решил надёжно подготовиться к созданию нового компилятора, синтаксис и устройство которого я попытался разметить перед написанием кода.
Что получилось, то получилось.
Основные языковые конструкции и синтаксис
В языке программирования Rave весь синтаксис, в целом, похож на синтаксис языка С.
Это было сделано, чтобы С и С++ программисты могли за пару-дней полностью освоить Rave и свободно на нём писать.
Вот пример программы, которая выводит "Hello, world!" в консоль на Rave:
import <std/io> // Импортируем файл io из глобальной папки std
void main { // Если функция без аргументов, скобки указывать необязательно
std::println("Hello, world!");
}
Как можно заметить, препроцессора в Rave нет.
Было решено полностью отказаться от него ещё в середине проектирования из-за его недостатков, которые перевешивали возможные преимущества его использования.
Также, вместо С функции printf и С++ функции cout у Rave есть своя функция - println.
В неё, как и в cout, можно вводить значения по-порядку, без указания типов аргументов в первой строке:
import <std/io>
void main {
std::println("Текущий год - ",2023,".");
}
Но, если вы предпочитаете использовать printf, вы можете вызвать std::printf:
import <std/io>
void main {
std::printf("Текущий год: %dn",2023);
}
Одна из причин, почему я сам начал писать свои остальные проекты на Rave - это красивые лямбды(как бы смешно это не звучало):
import <std/io>
void bow {
std::println("Bow");
}
void main {
void() func = bow;
void() func2 = void() {std::println("Func2");};
func();
}
Как и в С, в Rave есть указатели и арифметика указателей.
Она чуть более многословная, чем в С, однако и более безопасная, за счёт наличия runtime-проверок(отключаемых через флаг компилятора) и compile-time проверок:
void main {
void* a; // Переменные автоматически инициализируются в null(можно отключить)
a = itop(void*,0); // Ошибка
int b;
std::scanf("%d",&b);
a = itop(void*,b); // Если b == 0 - runtime ошибка
b = ptoi(a);
std::assert(b == 1, "b != 1");
}
Обработка ошибок также своя - она более легковесная, чем работа с исключениями, однако может показаться некоторым несколько неудобной:
import <std/error>
// Оператор => является сокращением вызова команды return в конце блока функции
std::error<int> func => std::error<int>(10,"",0);
// Первый аргумент - значение
// Второй - сообщение, которое нужно вывести при ошибке
// Третий - код ошибки
void main {
// Все вызовы функций с возвращаемым типом std::error
// в try автоматически проверяются на ошибку, и возвращают
// исходный тип, который указан в std::error
try {
int base = func();
}
// base виден во всей функции
auto base2 = func();
base2.catch();
// Также, std::error поддерживает свой обработчик ошибки
base2.catch(void(char* msg,int code) {
std::println("Error: '",msg,"', code: ",code);
std::exit(1);
});
}
Как я уже сказал, в Rave нету препроцессора, однако, есть система compile-time, популярная среди новых языков программирования:
@if(__RAVE_OS != "LINUX") {
@errorln("Данная библиотека не поддерживает платформу ",__RAVE_OS,".");
}
Через эти команды реализована обработка переменного количества аргументов.
Также, эти команды позволяют управлять флагами компилятора(вроде включения/выключения runtime-проверок), что может пригодится, если вы уверены, что участок кода не будет порождать ошибки и баги.
Rave не имеет классов, но структуры имеют все возможности классов - наследование(пока-что без возможности приведения дочерней структуры к родительской и наоборот), наличие методов и так далее.
Все эти концепции разрабатывались и писались в конце 2021 и на протяжении всего 2022 года.
Хоть это - лишь часть всего, что есть в Rave, я считаю, что этих примеров достаточно, чтобы оценить уровень работы, которую провёл лично я и участники моей команды.
Возможные преимущества
Преимуществ перед тем же С у Rave предостаточно:
-
Наличие базовой работы с ООП;
-
Наличие улучшенной безопасности работы с указателями и улучшенной обработки ошибок;
-
Отсутствие многих недостатков С, С++ и прочих языков(вроде того же препроцессора, хоть помечать его как недостаток довольно спорно), и так далее.
Возможные недостатки
Так как Rave разрабатывает довольно небольшое количество людей, в компиляторе есть не найденные баги, а также не лучшая обработка ошибок(в самом компиляторе).
По этой самой причине стандартная библиотека не развита на уровне схожих языков программирования(вроде Zig), хоть и имеет весь базовый(и даже расширенный) функционал, необходимый для создания программ и библиотек.
Эпилог
Разработка Rave для меня и моей команды является полезным опытом в конструировании компиляторов, что может в будущем пригодится каждому из нас.
Если вы хотите поддержать наш проект или присоединиться к его пользователям, то вот ссылка на наш сайт и GitHub.
Спасибо за прочтение статьи. До встречи!
Автор: Тимофей