Нельзя просто взять и создать плохой язык
Нельзя просто взять и создать плохой язык

Что Вы знаете про эзотерические языки программирования? Они кажутся вам странными? Смешными? Интересными? Этот язык не из таких – он не эзотерический. Если смех действительно продлевает жизнь, то после этой статьи Вы станете бессмертным.

Глава -2. Дисклеймер

Данная статья является саркастическим высмеиванием плохого кода. Она не ставит себе целью высмеять конкретного человека (ну разве что немного). К автору кода у меня есть личные претензии, но в данной статье Вы их не найдёте. Статья носит исключительно юмористический характер!

Глава -1. С чего всё началось

Помните ли вы эту статью? Нет? Ну а кого сейчас можно заинтересовать очередным убийцей Си? Но я эту статью помню, у меня есть причина помнить.

Если не углубляться в мою историю, про Rave я узнал примерно пару лет назад. Тогда язык назывался EPL (Easy Programming Language) и был высокоуровневым ассемблером. Я, конечно, посмеялся и языком совсем не заинтересовался. Так бы я и не вспомнил про этот язык, если бы один человек не напомнил мне.

А язык-тo! Язык как подрос! Он из высокоуровневого ассемблера превратился в то, что можно гордо назвать языком – в убийцу Си.

Глава -0.00000000. Компилятор

Компилятор языка можно собрать из исходного кода, который автор любезно выложил на GitHub. Обсуждая новый язык, не принято обсуждать его реализацию, потому что далеко не всем интересны детали устройства компилятора, но не в этом случае. Код компилятора Rave настолько прекрасен, что он быть может даже заслужил отдельную статью!

Компилятор Rave написан на C++ (раньше был на D, да), что безусловно инкрементирует его крутость. При этом, будучи написанным на столь сложном языке, компилятор почти всегда работает, что несомненно является великим достижением.

Начать обзор кода хотелось бы с сего прекрасного синглтона:

namespace Compiler {
    extern std::string linkString;
    extern std::string outFile;
    extern std::string outType;
    extern genSettings settings;
    extern nlohmann::json options;
    extern double lexTime;
    extern double parseTime;
    extern double genTime;
    extern std::vector<std::string> files;
    extern std::vector<std::string> toImport;
    extern bool debugMode;
    extern std::string features;

    extern void error(std::string message);
    extern void initialize(std::string outFile, std::string outType, genSettings settings, std::vector<std::string> files);
    extern void clearAll();
    extern void compile(std::string file);
    extern void compileAll();
}

О да, детка, это то, чего мы заслужили! Вот ради такого кода и развивался сектор разработки ПО! Посмотрите на это филигранно расставленные extern'ы! Использование пространств имён вместо классов – воистину гениальное решение!

В продолжение хотелось бы перейти к наипрекраснейшему лексеру:

Много кода
std::string Lexer::getIdentifier() {
    std::string buffer = "";
    while(
        peek() != '+' &&
        peek() != '-' &&
        peek() != '*' &&
        peek() != '/' &&
        peek() != '>' &&
        peek() != '<' &&
        peek() != ',' &&
        peek() != '.' &&
        peek() != ';' &&
        peek() != '(' &&
        peek() != ')' &&
        peek() != '[' &&
        peek() != ']' &&
        peek() != '&' &&
        peek() != '\'' &&
        peek() != '"' &&
        peek() != '~' &&
        peek() != '=' &&
        peek() != '{' &&
        peek() != '}' &&
        peek() != '!' &&
        peek() != ' ' &&
        peek() != '\n' &&
        peek() != '\r' &&
        peek() != '\r' &&
        peek() != ':' &&
        peek() != '@'
    ) {
        buffer += peek();
        idx += 1;
        if(peek() == ':' && this->text[this->idx+1] == ':') {
            buffer += "::";
            idx += 2;
        }
    }
    return buffer;
}

Эта точность исполнения! А посмотрите на уровень оптимизации! Доступ к памяти – операция таки медленная, поэтому компилятор Рейва старается лишний раз не сохранять значения в переменные, чтобы ускорить компиляцию кода!

Величие парсера прекрасно показывает следующий фрагмент кода:

Много кода
std::vector<Node*> Parser::parseFuncCallArgs() {
    std::vector<Node*> buffer;
    if(this->peek()->type == TokType::Rpar) this->next();
    else this->error("expected token '('!");
    while(this->peek()->type != TokType::Lpar) {
        if(this->peek()->type == TokType::Identifier) {
            if(this->peek()->value == "bool" || this->peek()->value == "char" || this->peek()->value == "uchar"
             ||this->peek()->value == "short" || this->peek()->value == "ushort" || this->peek()->value == "int"
             ||this->peek()->value == "uint" || this->peek()->value == "long" || this->peek()->value == "ulong"
             ||this->peek()->value == "cent" || this->peek()->value == "ucent" || this->peek()->value == "void"
             ||this->peek()->value == "half" || this->peek()->value == "bhalf" || this->peek()->value == "int4"
             ||this->peek()->value == "float8") {
                if(this->isDefinedLambda()) buffer.push_back(this->parseLambda());
                else buffer.push_back(new NodeType(this->parseType(), this->peek()->line));
            }
            else if(this->isDefinedLambda()) buffer.push_back(this->parseLambda());
            else buffer.push_back(this->parseExpr());
        }
        else buffer.push_back(this->parseExpr());
        if(this->peek()->type == TokType::Comma) this->next();
    } this->next();
    return buffer;
}

Как же это cache friendly! Мы не сохраняем никакой лишней информации, чтобы не замедлять нашу компиляцию! Готов поспорить, что Эндрю Келли ускорял компилятор Zig'а именно вдохновившись этим кодом! А это new! Словно энтерпрайз на Джаве!

Весь ли это код? Конечно, нет! Я рекомендую вам лично ознакомиться с кодом Rave, дабы в полной мере прочувствовать его величие!

Глава 1. Арифметика.

Важно помнить, что язык является ориентированным на скорость (см. официальный сайт). В связи с этим, с недавних пор Rave из коробки включает fastmath, что позволяет значительно ускорить программу. Но почему же другие языки так не делают? Очевидно, потому что никто просто не додумался включать fastmath из коробки!

Арифметика языка Rave настолько велика, что обычная математика не способна совладать с ней, что можно увидеть не следующем примере:

import <std/io>

void main {
    std::println(42 / 0);
}

По непонятной причине, математики решили запретить деление на ноль, но Rave исправляет этот недостаток! Теперь деление на ноль вполне себе определено: x/0 будет 1. Остальные операции тоже не отстают!

import <std/io> <std/math>

void main {
    std::println(std::math::pow(-4.0, 1.5));
}

Выводом, как и ожидается, будет число 16.

import <std/io> <std/math>

void main {
  std::println(0.0 / 0.0);
}

Выводит, очевидно:

-0.00000000
import <std/io> <std/math>

void main {
  std::println(std::math::INFINITY);
}

А этот код, выведет, конечно же:

0.00000000

Глава 1. Строки и хэширование.

Строки в Си – стандарт проверенный временем. Именно поэтому строки в Rave являются так же простой ссылкой на последовательный набор символов с нулевым байтом на конце. Но не одними же сишными строками! Rave, конечно, имеет и свой std::string.

Одно из важных отличий std::string от сишных строк – переопределённая функция хэширования:

import <std/io> <std/string>

void main {
    std::string str1 = "aboba";
    std::string str2 = "baoab";
    int hash1 = str1.hash();
    int hash2 = str2.hash();
    std::println(hash1);
    std::println(hash2);
}

Выводит данный код, как и ожидается, хэши строк:

26
26

Сам автор описывает алгоритм хэширования как "простой и эффективный алгоритм", с чем нельзя не согласиться!

Глава 3. Борьба за хороший код

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

import <std/io> <std/string>

void main {
    int hash1 = std::string("aboba").hash();
    int hash2 = std::string("baoab").hash();
    std::println(hash1);
    std::println(hash2);
}

В таком случае компилятор вежливо попросит программиста вынести строки в отдельные переменные, дабы код было проще читать:

Error in '/root/trash/rave/second.rave' file at 4 line: Structure 'void' doesn't exist! (getType)

Глава 4. Хэш-мапы.

Язык имеет реализацию хэш-мап в std. Работать с ними достаточно просто:

import <std/io> <std/map>

void main {
  std::hashmap<std::string, std::string> map = std::hashmap<std::string, std::string>();
  map.set(std::string("aboba"), std::string("bebra"));
  std::println(map.get(std::string("aboba")))
}

Выводит данный код ни что иное, как ожидаемую нами строку:

bebra

Следующий же код, очевидно, вызовет ошибку времени выполнения:

import <std/io> <std/map>

void main {
  std::hashmap<std::string, std::string> map = std::hashmap<std::string, std::string>();
  map.set(std::string("aboba"), std::string("bebra"));
  std::println(map.get(std::string("baoab")));
}

Вывод:

Segmentation fault 

Несмотря на то, что хэши строк "aboba" и "baoab" совпадают, хэш-мапа всё равно способна их различить! Данную технологию в сообществе Rave принято называть safe collisions.

Глава 5. Ошибки

Ошибки – одна из важнейших вещей в компиляторе. Rave в этом плане придерживается принципа KISS: его ошибки настолько просты, насколько это возможно.

Возьмём следующий код:

import <std/io> <std/map>

void main {
  std::hashmap map;
  map.set(std::string("aboba"), std::string("bebra"));
  std::println(map.get(std::string("baoab")));
}

В нём происходит создание локальной переменной типа std::hasmap без указания аргументов шаблона, о чём компилятор незамедлительно сообщит, уйдя в бесконечный цикл.

import <std/io> <std/map>

void main {
  std::hashmap<void, void> map;
  map.set(std::string("aboba"), std::string("bebra"));
  std::println(map.get(std::string("baoab")));
}

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

Длинный вывод
GEP into unsized type!
  %NodeGet_generate_Iden_preload23 = getelementptr inbounds %"std::hashmap::Entry<void,void>", %"std::hashmap::Entry<void,void>"* %NodeGet_checkStructure_load, i32 0, i32 2
Error in 'examples/second.rave' file at 146 line: LLVM errors into the function '~std::hashmap<void,void>'! Content:
define linkonce_odr void @"_RaveF24~std::hashmap<void,void>"(%"std::hashmap<void,void>"* %0) comdat {
entry:
  %old = alloca %"std::hashmap::Entry<void,void>"*, align 8
  %entry4 = alloca %"std::hashmap::Entry<void,void>"*, align 8
  %i = alloca i32, align 4
  %NodeGet_generate_Iden_preload = getelementptr inbounds %"std::hashmap<void,void>", %"std::hashmap<void,void>"* %0, i32 0, i32 0
  %NodeGet_generate_Iden_load = load %"std::hashmap::Entry<void,void>"**, %"std::hashmap::Entry<void,void>"*** %NodeGet_generate_Iden_preload, align 8
  %comparePtoIOne = ptrtoint %"std::hashmap::Entry<void,void>"** %NodeGet_generate_Iden_load to i64
  %compareINEQ = icmp ne %"std::hashmap::Entry<void,void>"** %NodeGet_generate_Iden_load, null
  br i1 %compareINEQ, label %then, label %else

then:                                             ; preds = %entry
  %scopeGetLoad = load i32, i32* %i, align 4
  %scopeGetLoad1 = load i32, i32* %i, align 4
  store i32 0, i32* %i, align 4
  br label %cond

else:                                             ; preds = %entry
  br label %end

end:                                              ; preds = %else, %exit2
  br label %exit

cond:                                             ; preds = %exit14, %then
  %scopeGetLoad3 = load i32, i32* %i, align 4
  %compareILS = icmp slt i32 %scopeGetLoad3, 64
  br i1 %compareILS, label %while, label %exit2

while:                                            ; preds = %cond
  %scopeGetLoad5 = load %"std::hashmap::Entry<void,void>"*, %"std::hashmap::Entry<void,void>"** %entry4, align 8
  %NodeGet_generate_Iden_ptr = getelementptr inbounds %"std::hashmap<void,void>", %"std::hashmap<void,void>"* %0, i32 0, i32 0
  %NodeIndex_NodeGet_load149_ = load %"std::hashmap::Entry<void,void>"**, %"std::hashmap::Entry<void,void>"*** %NodeGet_generate_Iden_ptr, align 8
  %scopeGetLoad6 = load i32, i32* %i, align 4
  %gep3_byIndex = getelementptr %"std::hashmap::Entry<void,void>"*, %"std::hashmap::Entry<void,void>"** %NodeIndex_NodeGet_load149_, i32 %scopeGetLoad6
  %NodeIndex_NodeGet149_ = load %"std::hashmap::Entry<void,void>"*, %"std::hashmap::Entry<void,void>"** %gep3_byIndex, align 8
  %scopeGetLoad7 = load %"std::hashmap::Entry<void,void>"*, %"std::hashmap::Entry<void,void>"** %entry4, align 8
  %scopeGetLoad8 = load %"std::hashmap::Entry<void,void>"*, %"std::hashmap::Entry<void,void>"** %entry4, align 8
  store %"std::hashmap::Entry<void,void>"* %NodeIndex_NodeGet149_, %"std::hashmap::Entry<void,void>"** %entry4, align 8
  %scopeGetLoad9 = load %"std::hashmap::Entry<void,void>"*, %"std::hashmap::Entry<void,void>"** %old, align 8
  %scopeGetLoad10 = load %"std::hashmap::Entry<void,void>"*, %"std::hashmap::Entry<void,void>"** %old, align 8
  %scopeGetLoad11 = load %"std::hashmap::Entry<void,void>"*, %"std::hashmap::Entry<void,void>"** %old, align 8
  store %"std::hashmap::Entry<void,void>"* null, %"std::hashmap::Entry<void,void>"** %old, align 8
  br label %cond12

exit2:                                            ; preds = %cond
  %NodeGet_generate_Iden_preload30 = getelementptr inbounds %"std::hashmap<void,void>", %"std::hashmap<void,void>"* %0, i32 0, i32 0
  %NodeGet_generate_Iden_load31 = load %"std::hashmap::Entry<void,void>"**, %"std::hashmap::Entry<void,void>"*** %NodeGet_generate_Iden_preload30, align 8
  %NodeCast_ptop32 = bitcast %"std::hashmap::Entry<void,void>"** %NodeGet_generate_Iden_load31 to i8*
  call void @free(i8* %NodeCast_ptop32)
  %NodeGet_generate_Iden_ptr33 = getelementptr inbounds %"std::hashmap<void,void>", %"std::hashmap<void,void>"* %0, i32 0, i32 0
  %NodeGet_generate_Iden_ptr34 = getelementptr inbounds %"std::hashmap<void,void>", %"std::hashmap<void,void>"* %0, i32 0, i32 0
  store %"std::hashmap::Entry<void,void>"** null, %"std::hashmap::Entry<void,void>"*** %NodeGet_generate_Iden_ptr34, align 8
  br label %end

cond12:                                           ; preds = %while13, %while
  %scopeGetLoad15 = load %"std::hashmap::Entry<void,void>"*, %"std::hashmap::Entry<void,void>"** %entry4, align 8
  %comparePtoIOne16 = ptrtoint %"std::hashmap::Entry<void,void>"* %scopeGetLoad15 to i64
  %compareINEQ17 = icmp ne %"std::hashmap::Entry<void,void>"* %scopeGetLoad15, null
  br i1 %compareINEQ17, label %while13, label %exit14

while13:                                          ; preds = %cond12
  %scopeGetLoad18 = load %"std::hashmap::Entry<void,void>"*, %"std::hashmap::Entry<void,void>"** %old, align 8
  %scopeGetLoad19 = load %"std::hashmap::Entry<void,void>"*, %"std::hashmap::Entry<void,void>"** %entry4, align 8
  %scopeGetLoad20 = load %"std::hashmap::Entry<void,void>"*, %"std::hashmap::Entry<void,void>"** %old, align 8
  %scopeGetLoad21 = load %"std::hashmap::Entry<void,void>"*, %"std::hashmap::Entry<void,void>"** %old, align 8
  store %"std::hashmap::Entry<void,void>"* %scopeGetLoad19, %"std::hashmap::Entry<void,void>"** %old, align 8
  %scopeGetLoad22 = load %"std::hashmap::Entry<void,void>"*, %"std::hashmap::Entry<void,void>"** %entry4, align 8
  %NodeGet_checkStructure_load = load %"std::hashmap::Entry<void,void>"*, %"std::hashmap::Entry<void,void>"** %entry4, align 8
  %NodeGet_generate_Iden_preload23 = getelementptr inbounds %"std::hashmap::Entry<void,void>", %"std::hashmap::Entry<void,void>"* %NodeGet_checkStructure_load, i32 0, i32 2
  %NodeGet_generate_Iden_load24 = load %"std::hashmap::Entry<void,void>"*, %"std::hashmap::Entry<void,void>"** %NodeGet_generate_Iden_preload23, align 8
  %scopeGetLoad25 = load %"std::hashmap::Entry<void,void>"*, %"std::hashmap::Entry<void,void>"** %entry4, align 8
  store %"std::hashmap::Entry<void,void>"* %NodeGet_generate_Iden_load24, %"std::hashmap::Entry<void,void>"** %entry4, align 8
  %scopeGetLoad26 = load %"std::hashmap::Entry<void,void>"*, %"std::hashmap::Entry<void,void>"** %old, align 8
  %NodeCast_ptop = bitcast %"std::hashmap::Entry<void,void>"* %scopeGetLoad26 to i8*
  call void @free(i8* %NodeCast_ptop)
  br label %cond12

exit14:                                           ; preds = %cond12
  %scopeGetLoad27 = load i32, i32* %i, align 4
  %scopeGetLoad28 = load i32, i32* %i, align 4
  %sum = add i32 %scopeGetLoad28, 1
  %scopeGetLoad29 = load i32, i32* %i, align 4
  store i32 %sum, i32* %i, align 4
  br label %cond

exit:                                             ; preds = %end
  ret void
}

Ну и следующий код тоже не является валидным, о чём компилятор не менее доходчиво сообщит:

import <std/io> <std/string> <std/matrix>

void main {
  std::matrix<float> m = std::matrix<float>(3, 3);
  for (float i = 1; i <= 3; i += 1) {
    for (float j = 1; j <= 3; j += 1) {
      m.set(i, j, i*j);
    }
  }
  std::matrix<float> n = m.multiply(m, m);
}

Компилятор выведет следующую ошибку этапа компиляции:

Illegal instruction 

Глава 6. Переопределяем мироздание

В Rave местами прослеживается след такого прекрасного языка как DreamBerd. Например Rave позволяет переопределять встроенные типы:

import <std/string>

void main {
  auto void = std::string;
}

Не менее просто можно переопределить даже ключевые слова или целые конструкции:

void main {
  auto import <std/io> = "std/string.rave";
}

Более того, язык позволяет переопределять даже числа!

int 42 {
  return = 5;
}

void main {
}

Правда стоит отметить, что вызвать такую функцию нельзя:

import <std/io>

int 42 {
  return = 5;
}

void main {
  std::println(42());
}

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

Error in '/root/trash/rave/second.rave' file at 8 line: a call of this kind is temporarily unavailable!

Глава 7. Свежий взгляд

Надеюсь, мне удалось показать вам, насколько прекрасен Rave! Первая статья на тему данного языка вышла достаточно давно, поэтому мне хотелось бы слегка обновить её, дабы она лучше показывала всё возможности современного Rave.

Автор оригинала: @Makcimka132


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

void main {
  void (inline) import <std/io> {
    void void = import <std/io> foo;
    (I`m no gay, bro) void {;
    (not) defined variable;
    int + 5;
    42();
    import <shitcore???> ();

Как вы могли заметить, если функция не имеет аргументов, то скобки указывать не обязательно:

void main {
  void import <std/io> {
    42;

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

Очень много кода
    (ctargs) std::string gsprint {
        std::thread::spinlock::lock(&std::sprintBufferSL);

        if(!std::sbInitialized) {
            std::sprintBuffer = std::string(128);
            std::sbInitialized = true;
        }
        else std::sprintBuffer.length = 0;

        char[40] nBuffer;

        @foreachArgs() {
            @if(@tNequals(@getCurrArgType(), float)) {
                @if(@tNequals(@getCurrArgType(), double)) {
                    @if(@tNequals(@getCurrArgType(), std::string)) {
                        @if(@tNequals(@getCurrArgType(), char)) {
                            @if(@tEquals(@getCurrArgType(), bool)) {
                                std::sprintBuffer.appendC(std::cstring::fromBool(@getCurrArg(bool)));
                            };
                            @if(@tEquals(@getCurrArgType(), short) || @tEquals(@getCurrArgType(), int) || @tEquals(@getCurrArgType(), long)) {
                                nBuffer = [
                                    '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
                                    '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
                                    '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
                                    '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
                                    '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'
                                ];
                                std::cstring::ltos(@getCurrArg(long), cast(char*)&nBuffer);
                                std::sprintBuffer.appendC(cast(char*)&nBuffer);
                            };
                            @if(@tEquals(@getCurrArgType(), cent)) {
                                nBuffer = [
                                    '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
                                    '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
                                    '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
                                    '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
                                    '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'
                                ];
                                std::cstring::ctos(@getCurrArg(cent), cast(char*)&nBuffer);
                                std::sprintBuffer.appendC(cast(char*)&nBuffer);
                            };
                            @if(@tEquals(@getCurrArgType(), std::u32string)) {
                                std::u32string us = @getCurrArg(std::u32string);
                                for(int i=0; i<us.length; i+=1) {
                                    char[4] buf;
                                    int n = std::utf::encode(us.data[i], &buf[0]);
                                    if(n == 1) std::sprintBuffer.add(buf[0]);
                                    else if(n == 2) {
                                        std::sprintBuffer.add(buf[0]);
                                        std::sprintBuffer.add(buf[1]);
                                    }
                                    else if(n == 3) {
                                        std::sprintBuffer.add(buf[0]);
                                        std::sprintBuffer.add(buf[1]);
                                        std::sprintBuffer.add(buf[2]);
                                    }
                                    else if(n == 4) {
                                        std::sprintBuffer.add(buf[0]);
                                        std::sprintBuffer.add(buf[1]);
                                        std::sprintBuffer.add(buf[2]);
                                        std::sprintBuffer.add(buf[3]);
                                    }
                                }
                            };
                            @if(@tEquals(@getCurrArgType(), std::u8string)) {
                                std::u8string u8s = @getCurrArg(std::u8string);
                                for(int i=0; i<u8s.bytes; i+=1) {
                                    std::sprintBuffer.add(u8s.data[i]);
                                }
                            };
                            @if(@tEquals(@getCurrArgType(), int*)) {
                                uint* utf8Text = @getCurrArg(uint*);
                                for(int i=0; utf8Text[i] != '\0'; i+=1) {
                                    char[4] buf;
                                    int n = std::utf::encode(utf8Text[i], &buf[0]);
                                    if(n == 1) std::sprintBuffer.add(buf[0]);
                                    else if(n == 2) {
                                        std::sprintBuffer.add(buf[0]);
                                        std::sprintBuffer.add(buf[1]);
                                    }
                                    else if(n == 3) {
                                        std::sprintBuffer.add(buf[0]);
                                        std::sprintBuffer.add(buf[1]);
                                        std::sprintBuffer.add(buf[2]);
                                    }
                                    else if(n == 4) {
                                        std::sprintBuffer.add(buf[0]);
                                        std::sprintBuffer.add(buf[1]);
                                        std::sprintBuffer.add(buf[2]);
                                        std::sprintBuffer.add(buf[3]);
                                    }
                                }
                            };
                            @if(@tEquals(@getCurrArgType(), char*)) {
                                std::sprintBuffer.appendC(@getCurrArg(char*));
                            };
                        };
                        @if(@tEquals(@getCurrArgType(), char)) {
                            std::sprintBuffer.add(@getCurrArg(char));
                        };
                    };
                    @if(@tEquals(@getCurrArgType(), std::string)) {
                        std::string s = @getCurrArg(std::string);
                        std::sprintBuffer.append(s);
                    };
                };
                @if(@tEquals(@getCurrArgType(), double)) {
                    nBuffer = [
                        '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
                        '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
                        '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
                        '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
                        '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'
                    ];
                    std::cstring::dtos(@getCurrArg(double), 8, &nBuffer);
                    std::sprintBuffer.appendC(cast(char*)&nBuffer);
                };
            };
            @if(@tEquals(@getCurrArgType(), float)) {
                nBuffer = [
                    '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
                    '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
                    '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
                    '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
                    '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'
                ];
                std::cstring::dtos(@getCurrArg(double), 7, &nBuffer);
                std::sprintBuffer.appendC(cast(char*)&nBuffer);
            };
        };
        std::thread::spinlock::unlock(&std::sprintBufferSL);
    } => std::sprintBuffer;

    (ctargs) std::string sprint {
        std::string result = "";
        
        char[40] buffer;

        @foreachArgs() {
            @if(@tNequals(@getCurrArgType(), float)) {
                @if(@tNequals(@getCurrArgType(), double)) {
                    @if(@tNequals(@getCurrArgType(), std::string)) {
                        @if(@tNequals(@getCurrArgType(), char)) {
                            @if(@tEquals(@getCurrArgType(), bool)) {
                                result.appendC(std::cstring::fromBool(@getCurrArg(bool)));
                            };
                            @if(@tEquals(@getCurrArgType(), short) || @tEquals(@getCurrArgType(), int) || @tEquals(@getCurrArgType(), long)) {
                                nBuffer = [
                                    '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
                                    '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
                                    '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
                                    '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
                                    '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'
                                ];
                                std::cstring::ltos(@getCurrArg(long), cast(char*)&nBuffer);
                                std::sprintBuffer.appendC(cast(char*)&nBuffer);
                            };
                            @if(@tEquals(@getCurrArgType(), cent)) {
                                nBuffer = [
                                    '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
                                    '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
                                    '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
                                    '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
                                    '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'
                                ];
                                std::cstring::ctos(@getCurrArg(cent), cast(char*)&nBuffer);
                                std::sprintBuffer.appendC(cast(char*)&nBuffer);
                            };
                            @if(@tEquals(@getCurrArgType(), std::u32string)) {
                                std::u32string us = @getCurrArg(std::u32string);
                                for(int i=0; i<us.length; i+=1) {
                                    char[4] buf;
                                    int n = std::utf::encode(us.data[i], &buf[0]);
                                    if(n == 1) result.add(buf[0]);
                                    else if(n == 2) {
                                        result.add(buf[0]);
                                        result.add(buf[1]);
                                    }
                                    else if(n == 3) {
                                        result.add(buf[0]);
                                        result.add(buf[1]);
                                        result.add(buf[2]);
                                    }
                                    else if(n == 4) {
                                        result.add(buf[0]);
                                        result.add(buf[1]);
                                        result.add(buf[2]);
                                        result.add(buf[3]);
                                    }
                                }
                            };
                            @if(@tEquals(@getCurrArgType(), std::u8string)) {
                                std::u8string u8s = @getCurrArg(std::u8string);
                                for(int i=0; i<u8s.bytes; i+=1) {
                                    result.add(u8s.data[i]);
                                }
                            };
                            @if(@tEquals(@getCurrArgType(), int*)) {
                                uint* utf8Text = @getCurrArg(uint*);
                                for(int i=0; utf8Text[i] != '\0'; i+=1) {
                                    char[4] buf;
                                    int n = std::utf::encode(utf8Text[i], &buf[0]);
                                    if(n == 1) result.add(buf[0]);
                                    else if(n == 2) {
                                        result.add(buf[0]);
                                        result.add(buf[1]);
                                    }
                                    else if(n == 3) {
                                        result.add(buf[0]);
                                        result.add(buf[1]);
                                        result.add(buf[2]);
                                    }
                                    else if(n == 4) {
                                        result.add(buf[0]);
                                        result.add(buf[1]);
                                        result.add(buf[2]);
                                        result.add(buf[3]);
                                    }
                                }
                            };
                            @if(@tEquals(@getCurrArgType(), char*)) {
                                result.appendC(@getCurrArg(void*));
                            };
                        };
                        @if(@tEquals(@getCurrArgType(), char)) {
                            result.add(@getCurrArg(char));
                        };
                    };
                    @if(@tEquals(@getCurrArgType(), std::string)) {
                        std::string s = @getCurrArg(std::string);
                        result.append(s);
                    };
                };
                @if(@tEquals(@getCurrArgType(), double)) {
                    nBuffer = [
                        '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
                        '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
                        '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
                        '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
                        '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'
                    ];
                    std::cstring::dtos(@getCurrArg(double), 8, &nBuffer);
                    std::sprintBuffer.appendC(cast(char*)&nBuffer);
                };
            };
            @if(@tEquals(@getCurrArgType(), float)) {
                nBuffer = [
                    '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
                    '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
                    '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
                    '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
                    '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'
                ];
                std::cstring::dtos(@getCurrArg(double), 7, &nBuffer);
                std::sprintBuffer.appendC(cast(char*)&nBuffer);
            };
        };

    } => result;

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

Error in '/root/trash/rave/first.rave' file at -1 line: expected a number, true/false, char, variable or expression. Got: '' on -1 line.

Глава 8. Заключительные истории

А теперь эпилог, или лучше сказать концовка. Как я сказал ранее, статья носит исключительно юмористический характер. В Rave так или иначе было вложено много усилий, что несложно понять, прочитав эту статью. Надеюсь, вы продлили себе жизнь, посмеявшись от души. Хороших снов.

P.S. Если вам не верится в реальность некоторых примеров кода, вы всегда можете проверить всё сами! К вашим услугам репозиторий. Правда будьте готовы, что для сборки кода понадобится руками ставить зависимости и редачить Makefile, а также нужно не забыSegmentation fault

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


  1. Daniel217D
    24.07.2024 07:26
    +17

    Почему именно высмеивание, а не структурированная критика? Личная неприязнь здесь явно прослеживается


    1. pluffie Автор
      24.07.2024 07:26
      +9

      Критика была, не только от меня, кстати. Больше года назад ему говорили, что бинарный логарифм можно ускорить, реализовав его с помощью процессорной инструкции - игнор. Ещё когда компилятор был на D, ему говорили, что лучше повторяющиеся вызовы в if'ах вынести в перменные - код переписан с нуля, а проблема та же. Про ужасный компайл тайм ему говорили, а я даже варианты исправления предлагал - не помню уже, что в ответ получили. Про хэш ему говорили - ответ в статье.

      Нет, иногда автор критику слушает, но слушает он её как-то выборочно и, иногда, понимает как-то по-своему. Да и многие проблемы вызваны банально тем, что он не тестировал код, или бежал что-то реализовывать, даже не попытавшись разобраться в теме. А, ну и его коронное оправдание: "Я пишу компилятор в одиночку".

      Критику тоже заслужить нужно. Когда сам автор к коду наплевательски относится, неудивительно, что другие будут над ним только смеяться. Личная неприязнь всё-таки есть, да.


      1. tyomitch
        24.07.2024 07:26

        лучше повторяющиеся вызовы в if'ах вынести в перменные

        Компилятор C++ достаточно умный, что в сгенерированном коде разницы не будет, скорее всего.


        1. pluffie Автор
          24.07.2024 07:26
          +2

          Да это понятно, но легче прочитать просто название переменной, чем цепочку вызовов, что в нынешнем коде. Претензия чисто стилистическая.


  1. crackedmind
    24.07.2024 07:26
    +20

    Арифметика настолько впечатлила, что немного проникла в статью.

    Глава 1. Арифметика.

    Глава 1. Строки и хэширование.


    1. Squoworode
      24.07.2024 07:26
      +25

      То есть, главы -2, -1 и -0.00000000 вас не смутили?


  1. tigreavecdesailes
    24.07.2024 07:26
    +29

    Надеюсь, вы продлили себя жизнь, посмеявшись от души.

    Осилил пару экранов, не смешно. Какая-то личная вендетта... стоило ли ради этого регаться на хабре?


  1. 40oleg
    24.07.2024 07:26
    +19

    Язык делает один человек, имеет пока всего 50 звезд на гитхабе, а автор статьи уже поставил язык в одну линейку с с++ и требует того же. Ради чего?


    1. pluffie Автор
      24.07.2024 07:26
      +3

      Чего того же? Правильной арифметики? Или отсутствия багов в парсере компилятора, который пишется два года? (версии на плюсах меньше года, ладно)

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

      Одно дело баги, которые сложно найти, а другое - бесконечный цикл, если забыть указать аргументы шаблона.


      1. vendelieu
        24.07.2024 07:26
        +3

        Странный аргумент :/

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


        1. pluffie Автор
          24.07.2024 07:26
          +1

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


          1. boris768
            24.07.2024 07:26
            +5

            так может лучше завести баги на гитхабе и вести себя профессионально, чем разводить клоунаду?

            И что за непоследовательность - буквально цитирую - "И я имею право это требовать" - кто дал право, в каком договоре прописано?

            Хотите критиковать - берите пример с pvs-studio, они грамотно рекламируя свой продукт, выпускают хорошие статьи без излишнего сарказма.


            1. pluffie Автор
              24.07.2024 07:26
              +1

              так может лучше завести баги на гитхабе и вести себя профессионально, чем разводить клоунаду?

              Ну я заинтересован не сколько в улучшении проекта, сколько в высмеивании плохого кода. Плохие у меня интересы, да.

              И что за непоследовательность - буквально цитирую - "И я имею право это требовать" - кто дал право, в каком договоре прописано?

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


            1. eptr
              24.07.2024 07:26
              +2

              так может лучше завести баги на гитхабе и вести себя профессионально, чем разводить клоунаду?

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

              Хотя, глумиться, конечно, не следовало.


          1. mishapetelev
            24.07.2024 07:26

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

            Если ты такой умный напиши Rave 1.(9) без недостатков


  1. Dmitry_604
    24.07.2024 07:26
    +13

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


  1. Crystal_Development
    24.07.2024 07:26
    +5

    Все хотят убить C. Но C не убивается.


    1. Vitimbo
      24.07.2024 07:26
      +2

      \s Но чего стоит такая жизнь...


    1. rbdr
      24.07.2024 07:26

      "А вдруг получится"? :)

      Тогда сразу же ж "жизнь удалась".


  1. KvanTTT
    24.07.2024 07:26
    +4

    Забавно, но такое лучше бы зашло на 1 апреля.


    1. pluffie Автор
      24.07.2024 07:26

      Вы про статью, или про язык?


      1. KvanTTT
        24.07.2024 07:26
        +2

        Про статью, т.к. ее я только что прочитал и узнал о языке Rave. Что творится в голове у автора языка я не знаю, поэтому не знаю чем он руководствовался при создании этого языка, и как он к нему относится.


  1. gohrytt
    24.07.2024 07:26

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

    Но да, названию соответствует