Язык программирования Rave

3974f9d723902edb2b75cd40bb7e0b41

Приветствую всех читателей данной статьи.

Тут я постараюсь описать год усердной, тяжёлой работы на моим собственным компилятором языка программирования Rave.

Предыстория

Я уже много раз делал различные языки программирования — их создания стало для меня чем-то вроде хобби после учёбы.

Однако все мои языки программирования вплоть до Rave были не очень удачными — некоторые были слишком сложны в использовании, некоторые имели неудобный синтаксис и низкую скорость работы.

В итоге, когда я всё переосмыслил, я решил надёжно подготовиться к созданию нового компилятора, синтаксис и устройство которого я попытался разметить перед написанием кода.

Что получилось, то получилось.

Основные языковые конструкции и синтаксис

В языке программирования Rave весь синтаксис, в целом, похож на синтаксис языка С.
Это было сделано, чтобы С и С++ программисты могли за пару-дней полностью освоить Rave и свободно на нём писать.

Вот пример программы, которая выводит «Hello, world!» в консоль на Rave:

import  // Импортируем файл io из глобальной папки std

void main { // Если функция без аргументов, скобки указывать необязательно
    std::println("Hello, world!");
}

Как можно заметить, препроцессора в Rave нет.

Было решено полностью отказаться от него ещё в середине проектирования из-за его недостатков, которые перевешивали возможные преимущества его использования.

Также, вместо С функции printf и С++ функции cout у Rave есть своя функция — println.
В неё, как и в cout, можно вводить значения по-порядку, без указания типов аргументов в первой строке:

import 

void main {
    std::println("Текущий год - ",2023,".");
}

Но, если вы предпочитаете использовать printf, вы можете вызвать std: printf:

import 

void main {
    std::printf("Текущий год: %d\n",2023);
}

Одна из причин, почему я сам начал писать свои остальные проекты на Rave — это красивые лямбды (как бы смешно это не звучало):

import 

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 

// Оператор => является сокращением вызова команды return в конце блока функции
std::error func => std::error(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.

Спасибо за прочтение статьи. До встречи!

© Habrahabr.ru