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

Тут я постараюсь описать год усердной, тяжёлой работы на моим собственным компилятором языка программирования 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("Текущий год: %d\n",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.

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

Комментарии (44)


  1. Dynasaur
    22.01.2023 11:57
    +14

    Один вопрос - зачем? Кому-то мало имеющегося зоопарка ЯП?


    1. shasoftX
      22.01.2023 12:13
      +9

      Судя по всему ответ в эпилоге


    1. whoisking
      22.01.2023 12:49
      +5

      Конечно, вероятность что из новых языков что-то станет популярным очень низкая, но в таких языках вполне можно проводить какие-то эксперименты, которые в случае успеха можно завезти в язык более популярный и востребованный. Кто-то просто созданием языков прокачивает свои личные навыки, кому-то по фану


      1. perfect_genius
        22.01.2023 13:43
        -7

        Надо было это написать в самом начале текста, чтобы заранее настроить читателя. А не в конце.


  1. iliazeus
    22.01.2023 12:05
    +4

    Есть ли какие-то особенности, которые бы выгодно отличали язык от других "убийц C" - Go, D, Zig, ...? В первую очередь, наверное, интересно сравнение с D, учитывая, что это язык, на котором написан компилятор.


  1. aaabramenko
    22.01.2023 12:21
    +6

    Есть yaml, а нужен ещë yapl - yet another programming language.


  1. vasyakolobok77
    22.01.2023 12:44
    +7

    Возможно в чем-то ошибаюсь и неверно пониманию, но выскажу свое видение.

    Представленный ЯП недалеко ушел от C/C++/D. Он больше похож на обертку, которая в паре мест накладывает свои правила на написание кода. Может быть в этом и была цель.

    Но.. как по мне, новый ЯП в сравнении с референсными ЯП должен:

    – Не слишком сильно отличаться по синтаксису от референсных ЯП, чтобы не отталкивать тех кто входит в разработку / переходит на ваш стек;

    – Заменять шаблонный (boilerplate) код, который был в референсных ЯП, на более простые и очевидные конструкции;

    – Давать бОльшую безопасность работы, чем в референсных ЯП;

    – Давать бОльшую гибкость в разработке, чем в референсных ЯП.

    Каких идиом не хватает как по мне:

    – generics взамен или совместно с template подходом;

    – обработка исключений / классов исключений вместо кодов ошибок;

    – лямбды / замыкания / функции первого порядка;

    – атомики / объекты синхрониазции / управление потоками / асинхронные механизмы;

    – аннотации / параметры / способы верхнеуровневого программирования, кодогенерации, аспектно-ориентированного программирования


  1. 0Bannon
    22.01.2023 12:54
    +1

    А можно ещё от точки с запятой избавиться?


  1. perfect_genius
    22.01.2023 13:49
    +8

    Ничего с этим не поделать, молодёжь так и будет пытаться изобрести очередной язык и опишет это на Хабре. Надо смириться, похоже :) Ну хоть для них самих это будет полезно в плане развития.


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


  1. Rio
    22.01.2023 13:56
    +2

    Попытки сделать "улучшенный Си" мне интересны, и название языка вы классное выбрали, мне нравится (но, кажется, в контексте ЯП оно несколько, ээ, двусмысленное).

    Есть несколько вопросов, а поскольку я не нашёл ответов ни в документации на сайте (как-то совсем уж там мало), ни на гитхабе, поэтому поинтересуюсь тут.

          void* a; // Переменные автоматически инициализируются в null(можно отключить)
          a = itop(void*,0); // Ошибка
          int b;
          std::scanf("%d",&b);
          a = itop(void*,b); // Если b == 0 - runtime ошибка
          b = ptoi(a);

    Конструкции языка itop и ptoi это что за сущности? Выглядят как функции, но itop, например, принимает первым параметром тип, и при этом как-то узнаёт адрес переменной, переданной вторым параметром значению (может, это ссылка)? Или это всё-таки не функции в обычном понимании, а спец. конструкции самого языка?

    std::error<int> func => std::error<int>(10,"",0);

    Я так понимаю, что сущность std::error<> предполагается возвращать всем функциям, предполагающим возможность фейла в процессе выполнения. Это известный подход к выдаче и обработке ошибок, и он мне в целом импонирует, но мне кажется название неудачное выбрано. Может, что-то нейтральное, навроде std::result было бы лучше, ведь предполагается, что ф-я всё-таки не ошибку возвращать должна, а результат (который да, может быть не посчитан из-за ошибки) .

        try {
            int base = func();
        }
        // base виден во всей функции

    Я правильно понимаю, что этот случай (блок try) является исключением из правила "переменная объявленная внутри блока, видна только там"? Вообще, обработка ошибок мне не очень понятна, try/catch смущают, наверное: вроде как исключение ловим, но не исключение, дальше уровнем оно не пробрасывается, соответственно, не очень понятно вообще, зачем пытаться быть похожим на обработку исключений, в том числе и ключевые слова копируя. Лично мне наверное зашло бы что-то попроще, в духе:

    auto result = func();
    
    if (!result.err) doSomethingWith(result.val);

    и если бы проверки не было, а result.val использовался, вот тогда бы генерировался runtime error.


    1. Makcimka132 Автор
      22.01.2023 17:16

      "itop"/"ptoi" являются встроенными инструкциями, которые соответствуют тем же инструкциям в LLVM. Для удобства, "itop" принимает первый аргумент в виде типа, чтобы не пришлось лишний раз прописывать "cast(type)".

      Как вы верно подметили - try является исключением из правила "переменная объявленная внутри блока, видна только там". В основном, он используется для тех мест, где не так важна пользовательская отладка ошибок - в таком случае, пользователь может поручить компилятору автоматически проверять вызовы таких функций на ошибку.

      Сам пользователь может собственноручно проверить, есть ли ошибка при вызове функции через catch:

      import <std/error> <std/io>
      
      std::error<int> foo => std::error<int>(0,"Error!\n",100);
      
      void main {
          std::error<int> result = foo();
          result.catch();
          result.catch(void(char* msg, int code) {
              std::println(msg,", code: ",code);
          });
      }


      1. Rio
        23.01.2023 12:40

        То есть, предполагается, что программист должен окружать блоком try любой код, где ошибки не проверяются вручную, если он хочет, чтобы программа завершалась "красиво", c runtime error в месте вызова? Но учитывая, что исключений нет, получается, блоком try вообще все функции закрывать желательно. Почему бы тогда просто не сделать это поведение всегда активным по умолчанию, и добавить спецификатор для функции, которым можно пользоваться, если дефолтные проверки не нужны? Тогда и исключения из правил для блока try не нужно будет, и самого try. Или, как альтернативу, заместо try можно блок а-ля unsafe добавить, который работает строго наоборот. Мне кажется, это как-то более правильно было бы.


        1. Makcimka132 Автор
          23.01.2023 16:57

          "try" нужен для автоматизации ловли ошибок при вызовах функций с возвращаемым типом std::error<type>.

          Как вы хотите сделать это по-умолчанию - я, честно сказать, не понимаю. Возможно, вы имели ввиду переход на полноценные исключения.


          1. Rio
            23.01.2023 18:12

            Я имел в виду что-то вроде такого: компилятор автоматически вставляет ловлю ошибки, если функция возвращает std::error<type>, и при этом компилятор не видит обработки ошибки пользователем перед использованием значения (а также опциональное отключение такого поведения под ответственность программиста).

            std::error<int> foo => std::error<int>(0,"Error!\n",100);
            
            void bar()
            {
                std::error<int> result = foo();
                // Здесь нет проверки на ошибку перед использованием результата,
                // и компилятор здесь вставил её сам, не требуя от программиста try.
                ... тут result используется  ...
            
                result = foo();
                result.catch();
                ...
                // А здесь есть проверка. Компилятор ничего от себя не добавлял.      
                ... тут result используется  ...
            
                unsafe 
                {
                    result = foo();
                    // Здесь нет проверки на ошибку перед использованием результата,
                    // но программист явно указал компилятору не проверять.
                    ... тут result используется  ...
                }
            }

            Идея в том, что проверка на ошибку, всегда добавляемая по умолчанию, лучше чем отсутствие проверки по умолчанию: программист не забудет добавить try где нужно. Если же программист точно уверен, что никакая проверка не нужна, он указывает это явно.


            1. Makcimka132 Автор
              23.01.2023 18:45

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


  1. ris58h
    22.01.2023 15:51
    +1

    Эх, название многообещающее. Думал party hard будет, а тут обёртка над C/C++.


  1. gatoazul
    22.01.2023 16:02
    +3

    Автору рекомендуется расширить свои познания в области языков программирования. Такое впечатление, что кроме С и его друзей он ни о чем больше никогда и не слыхал, и не в состоянии вырваться из узкого туннеля.


  1. KongEnGe
    22.01.2023 16:40

    У котлина над джавой продоминировать получилось лучше.


    1. zagayevskiy
      22.01.2023 21:49
      +2

      Котлин лет 5 только проектировали, до первого релиза.


  1. SibirAy
    22.01.2023 17:52

    мне кажется что яп -ёпла- точно не хватает


  1. import_all
    22.01.2023 18:33
    +3

    Чел харош


  1. JustKenux
    22.01.2023 18:48
    +3

    Расскажу своё (никому не нужное) мнение о языке.

    Знаю много, так как видел своими глазами, как язык рождался и развивался, а также принимал частичное участие в разработке, а также помощи с поиском и исправлением багов.

    Сам по себе язык неплохой, несмотря на баги, (хоть серьезные уже давно исправлены), и на безумно малый std. Язык привносит runtime проверки (да да, я упрашивал автора добавить их наверно месяц :D), которые позволяют уменьшить к-во segmentation fault'ов и в целом улучшить жизнь; имеются методы в структурах (что, технически, является syntax sugar - методы во время компиляции выносятся, названия манглятся), которые очень удобны в разработке и вносят хотя бы намек на ООП, имеется ряд syntax sugar'ов, таких как лямбды и => в return, что опять уменьшает количество сурсов и +100 к читаемости. Говорю это все как разработчик на Rave (писал разные библиотеки уже - биндинги, врапперы над биндингами, свои собственные - например парсер json, хотя он и заброшен; программы).

    Язык я не считаю прямо нужным - есть целых зоопарк из Go, Rust, D, C++ (и многих других), которые тоже хорошо справляются с задачей general purpose. Автор говорит, что язык хорошо подходит для низкоуровневой работы, и тут я не могу не согласиться, но опять же, есть те же С/C++. Синтаксис конечно приятнее, но тут уже у каждого своё мнение.

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

    (Это, к слову, мой первый комментарий, и тем более, я зарегистрировал аккаунт только ради этого комментария :D. Передаю привет Правителю!, бебрарику, плюшевому, зедикловну, не очень сексуальному рабу и всем остальным rave'анутым!)


  1. DungeonLords
    22.01.2023 19:56

    Смею предложить автору написать "улучшенный Си" для микроконтроллеров. Там есть где развернуться. Например, сделать наконец чтобы язык знал, что вот это прерывание и не надо переменные которые в нем используются выкидывать (см. "Скользкая дорожка для поклонников volatile")


    1. Makcimka132 Автор
      22.01.2023 20:44
      +1

      Спасибо за идею, возможно реализую.


    1. flx0
      23.01.2023 08:26

      Например, сделать наконец чтобы язык знал, что вот это прерывание

      Это есть в HolyC. Но увы, там развитие языка остановилось когда автора убили агенты ЦРУ.


      1. DungeonLords
        23.01.2023 10:48

        Я посмотрел про HolyC и помоему вы ошибаетесь. Где там про прерывания?


        1. flx0
          23.01.2023 11:14

          Там есть атрибут interrupt для функции, который позволяет ее использовать как обработчик прерывания. Не уверен, как именно хендлятся используемые в них переменные по отношению к вашим проблемам с volatile, но вряд ли выбрасываются, Терри Дэвис не особо заморачивался с такими оптимизациями. Сказано сходить в память, оно сходит в память.
          Впрочем, самой полной документацией на HolyC является Библия, так что о поведении программ на нем рассуждать довольно сложно.


  1. Zedikon
    22.01.2023 19:56

    Высказываю очень важное и конструктивно объективное мнение касаемо языка программирования раве (Rave), могу сказать от лица ветерана всего этого языка, который был с самого зарождения языка и до самого его релиза. В целом могу сказать то, что язык является полезным и имеет своим применения. Безусловно, кто-то может считать, что это очередной мусорный язык, от которого нету никакого толку - но, если так подумать, почти любой язык является бессмысленным. В-общем, не буду устраивать длиннейшие дискуссии, ибо это может затянуться на очень длительное время, скажу лишь, что у языка есть множество преимуществ над другими, основным из которого, является очень позитивное и дружелюбное сообщество, с которым связано историй больше, чем с самим языком
    (Привет всем любителям jamal'ов, и ценителям кода на epl).


  1. ildarin
    23.01.2023 10:28
    +2

    Как профессиональный программист, который тратит 90% работы на гугление и копипасту со стаковерфлоу, тем не менее скажу пару замечаний насчет синтаксиса как этого, так и других языков.

    Много символьные конструкции из символов: это ужасно.

    Допустим, [::] - это 3 нажатия: [shift+;+;]. По сравнению с тем же js - где достаточно одной [.] - это в 3 раза больше. -> в c++ - это [-+shift+.]. К-к-к-омбо! Лямбды в js - [_=>], [()=>{}] тоже громоздкие комбо из символов. Мы скованы историческим расположением и символами кнопок на клавиатуре, но можно же сделать проще? В идеале - 1 семантическая единица - 1 нажатие клавиши. В этом смысле мне очень нравится система типов в Rust, где они максимально сокращены до 1 символа.

    Доп. символы не имеющие смысловой нагрузки - это кошмарно.

    [;] - в конце строки, какой в этом смысл? в этом плане мне нравится гибкость JS - там это не обязательно, хотя и можно использовать тчк-зпт как разделитель. import <std/io> - кавычки это ужас, когда можно и без них вообще. Перекладывать работу с бездушного и всеядного синтаксического анализатора на разработчика - это отвратительно. Мы тут автоматизируем работу человека или чем занимаемся вообще? Если хочется красоты - для этого есть IDE, можно хоть каждую точку в букет из анимированных роз превратить и каждую букву заставить плясать чечетку.

    Язык должен быть лаконичным настолько, насколько это вообще возможно. Благо, в разработке синтетических языков мы можем избежать исторических атавизмов вроде ру или eng, и забить болт на запятые и большие буквы с красной строки, сделав язык удобным для работы. Нужна точка с запятой для устранения неоднозначности? А точно ли она нужна, разве из контекста нельзя определить смысл? Это то, что и должен уметь хороший язык программирования - читать мысли разработчика.


    1. ildarin
      23.01.2023 10:43
      -1

      Как-то давно и неправда делал свой язык, и как раз размышлял над лаконичностью. В том числе были несколько идей:

      (до Rust) - типы данных в 1 символ, с размерностью числом после. q - бит (question/bool), q64 - 64 бита, 64q - массив из 64. Индексация без []. [.] - любой номер, т.е.

      64q a //массив bool 64
      a.=1 a3=0 a5.15=0 //все значения 1, кроме как по индексу 3 и от 5 до 15

      Все кавычки - [], т.к. для них не нужен shift.

      Каждый оператор максимум в 1 символ и без нажатия shift.


      1. ildarin
        23.01.2023 11:15

        32q a a.=1 a3=0 a.=+5-a2 q64 b=255

        В этом конкретном примере, нужен ли разделитель, кроме пробела?

        Чтобы доказать, что да - нужно доказать, что есть такая последовательность команд, которая будет неоднозначно воспринята. Какие операторы могут идти после [=]? Допустим, только [a-z/0-1] затем [+,-,*,/] (или наоборот). А может ли другая команда начинаться с этого символа? Если нет - то и разделитель не нужен. И вот этой работой разработчики языков себе голову особо не забивают, а следовало бы. Красивым язык может быть в своем изяществе.

        А читабельность можно отдать на откуп IDE, т.к. в блокноте уже давно никто не работает. А уже она раскрасит и подсветит.


        1. unclegluk
          24.01.2023 01:44

          По записи напоминает Forth, но наоборот. Такая же белиберда, если не обладаешь сакральным знанием.


    1. Makcimka132 Автор
      23.01.2023 17:06
      +1

      Спасибо, что высказали мне свою точку зрения на синтаксис.

      Акцент был сделан на то, что эти операторы и конструкции итак находятся в "обороте", и пользователям будет легче принять и использовать новый функционал.

      import, как я считаю, не должен давать излишней функциональности. Все, что требуется пользователю - импортирования файлов проекта, файлов библиотек и std. И import с этой задачей справляется. А насчёт кавычек - они были выбраны для той же удобности людям, которые привыкли писать на С/С++.

      ';' в конце строки находится для отладки компилятором и некоторых... "трюков" в парсинге языка.


      1. ildarin
        23.01.2023 17:23

        Ну я так и понял, что язык расчитан на мягкий переход для плюсников. Это и есть "исторические" ограничения. Т.е. костыли, которые поддерживаются потому-что кто-то не подумав изначально так решил. Типа слова йод, а не ёд, k в слове knife и таких вот конструкций в плюсах.


  1. DungeonLords
    23.01.2023 12:02

    Чего мне не хватает в Си?

    Было бы здорово, если бы функции можно было указать тип массива и
    количество элементов, при попытке подсунуть не массив или массив другой
    длины чтобы при компиляции выдавалась ошибка. Кто не хочет пользоваться
    таким механизмом - не указывайте кол-во элементов при объявлении
    массива-аргумента функции
    void f(int a[8]){//в аргументе функции указан массив с определенном количеством элементов
    }
    int main()
    {
    int b[4];
    f(b); //no error why?
    return 0;
    }


    1. Makcimka132 Автор
      23.01.2023 16:33

      Если речь идёт в контексте Rave, то массивы по-умолчанию передаются по значению, что автоматически обязывает компилятор проверять типы.


    1. SalazarMAX
      23.01.2023 21:37

      gcc так умеет (-Wstringop-overflow) начиная с 11 версии, но только при передаче массивов фиксированных размеров меньшей длины в функцию, которая ожидает массив большей длины. Но для других случаев при наличии в языке VLA сделать такую проверку в compile time невозможно.


  1. domix32
    23.01.2023 12:21

    Что с константностью переменных? Есть ли иммутабельность по-умолчанию? (гуглить better deafaults)
    ```
    let x = 3; //не даст измениться
    let mut y = 123; // кто-то может это поменять
    ```

    Исправлена ли болезнь Си/Си++ со спиральностью типов. Например, Саттер предлагает сделать униформный порядок в своём суперсете плюсов.

    Можно ли на системе макросов сделать самосборку проекта? Чтобы никах симейков не нужно было. Пока что мне известно только про jai Джона Блоу, который может сам себе быть сборочным скриптом.

    Компилятор уже Self hosted?

    Наличие базовой работы с ООП;

    А где примеры? Есть ли интерфейсы? Что с наследованием? Что с перегрузками типов функций?

    Наличие улучшенной безопасности работы с указателями и улучшенной обработки ошибок;

    Опять же - какие-то примеры было нелпохо увидеть. Значит ли это что по умолчанию везде толстые указатели, хранящие размер типа рядом?

    Как устроен ABI между языком и каким-нибудь Си?

    Отсутствие многих недостатков С, С++ и прочих языков(вроде того же
    препроцессора, хоть помечать его как недостаток довольно спорно), и так
    далее.

    Препроцессор в сях/плюсах это меньшая из проблем. Оно только усложняет отладку, но в среднем жить с этим можно. А какие ещё проблемы были решены?

    P.S. У вас серт на ваш домен остутвует.


    1. Makcimka132 Автор
      23.01.2023 16:55

      По поводу константности - присутствует "const(type)":

      const(char)* str = "Hi!";

      Про самосборку - не весь нужный функционал имеется, но постепенно будем добавлять чего не хватает.

      Нет, на данный момент компилятор не self-hosted, но это в ближайших планах.

      Примеры есть в данной статье, а также в GitHub-репозитории. На данный момент их там мало, так что я напишу вас интересующие:

      struct Bow {
          float data;
      }
      
      struct Foo {
          void method {
              data = 0.0;
          }
      
          void method(int a) {
              data = cast(float)a;
          }
      }
      
      void main {
          Foo foo;
          foo.method();
          foo.method(10);
      }

      Наследование сейчас реализовано, скажем так, не лучшим образом, но для простого предотвращения копирования элементов структуры в другие структуры множество раз руками вполне сойдёт.

      Про отладку ошибок - примеры приведены в статье. Но вкратце - itop/ptoi проверяются на нулевое значение, а std::error<type> позволяет удобно получать результат выполнения функции с возможностью отследить возможные ошибки.

      Про ABI - сейчас оно плохо задокументировано, каюсь.

      Но в целом, если вам нужно, скажем, импортировать функцию из С-библиотеки, вы можете использовать linkname:

      extern(linkname: "foo") void raveFoo();

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

      По поводу решения проблем из C/C++ - в данной области до сих пор ведётся активная разработка, так что точно сказать не могу. Но в целом, акцент ведётся на улучшенную безопасность работы с памятью и указателями - тому пример структура SafePtr(которая недавно была переведена в std/memutil):

      import <std/memutil>
      
      void main {
          auto ptr = SafePtr<int>(10);
      
          int[10] array;
          ptr.pointer = &array;
      }

      Работа с SafePtr позволяет частично облегчить отладку ошибок, связанных с попыткой доступа за грани указателя.

      P.S. Насчёт сертификата знаю...(


      1. domix32
        23.01.2023 19:09

        По поводу решения проблем из C/C++

        Но какие-то постулаты к которым вы как разработчики стремитесь должны же быть в каком-то виде? Снижение когнитивной нагрузки при чтении кода по идее улучшает безопасность.

        А ещё забыл спросить про всякие UB, как то разыменование нулевых указателей, обращение к неинициализированным переменным, вызов унарных операторов в качестве параметров функции и вот это вот всё - в каком-то видео оно обсуждалось/документировалось?
        И про время жизни - по-умолчанию везде RAII или есть механизмы управления временем жизни?

        И я так понял в примере выше где-то опечатка ибо есть Bow c членом и Foo без них, но с методами, который пытается получить доступ к члену.
        Там я так понимаю должно было быть что-то вроде?

        struct Foo: Bow { ... }
        using blabla = name::space::sub::name::space;
        blabla::something(); 

        алиасинг пространств имён уже есть в каком-то виде?


        1. Makcimka132 Автор
          23.01.2023 19:48

          Да, там небольшая опечатка.

          Наследование происходит через ':'.

          struct Bow {}
          struct Foo : Bow {}

          В минимальном виде присутствует.