Что Вы знаете про эзотерические языки программирования? Они кажутся вам странными? Смешными? Интересными? Этот язык не из таких – он не эзотерический. Если смех действительно продлевает жизнь, то после этой статьи Вы станете бессмертным.
Глава -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
Комментарии (68)
crackedmind
24.07.2024 07:26+29Арифметика настолько впечатлила, что немного проникла в статью.
Глава 1. Арифметика.
Глава 1. Строки и хэширование.
tigreavecdesailes
24.07.2024 07:26+56Надеюсь, вы продлили себя жизнь, посмеявшись от души.
Осилил пару экранов, не смешно. Какая-то личная вендетта... стоило ли ради этого регаться на хабре?
40oleg
24.07.2024 07:26+32Язык делает один человек, имеет пока всего 50 звезд на гитхабе, а автор статьи уже поставил язык в одну линейку с с++ и требует того же. Ради чего?
pluffie Автор
24.07.2024 07:26+9Чего того же? Правильной арифметики? Или отсутствия багов в парсере компилятора, который пишется два года? (версии на плюсах меньше года, ладно)
Я вообще ничего не требую в данной статье, но если бы и требовал, то только банального отсутствия глупых багов. И я имею право это требовать, потому что язык-то уже релизнут.
Одно дело баги, которые сложно найти, а другое - бесконечный цикл, если забыть указать аргументы шаблона.
vendelieu
24.07.2024 07:26+18Странный аргумент :/
Автор языка, как я понимаю, не требует деньги, не заставляет людей писать на нем, это просто опенсорс проект который человек может писать в свое свободное время, а вы требуете от него чего-то :/
pluffie Автор
24.07.2024 07:26Ну я потому и не требую ничего, как я уже выше указал. В статье не требования, а просто шутки над плохим продуктом.
boris768
24.07.2024 07:26+27так может лучше завести баги на гитхабе и вести себя профессионально, чем разводить клоунаду?
И что за непоследовательность - буквально цитирую - "И я имею право это требовать" - кто дал право, в каком договоре прописано?
Хотите критиковать - берите пример с pvs-studio, они грамотно рекламируя свой продукт, выпускают хорошие статьи без излишнего сарказма.
pluffie Автор
24.07.2024 07:26так может лучше завести баги на гитхабе и вести себя профессионально, чем разводить клоунаду?
Ну я заинтересован не сколько в улучшении проекта, сколько в высмеивании плохого кода. Плохие у меня интересы, да.
И что за непоследовательность - буквально цитирую - "И я имею право это требовать" - кто дал право, в каком договоре прописано?
Ну я вот считаю, что если код помечается как релизный, то от него можно требовать отсутствия хотя бы банальных багов. Если я считаю неправильно, то да, права у меня действительно нет.
PsihXMak
24.07.2024 07:26+25в высмеивании плохого кода
Это не высмеивание, это уже кибербуллинг. Такое ощущение, что вы пытаетесь пользователей хабра натравить на автора.
eptr
24.07.2024 07:26+5так может лучше завести баги на гитхабе и вести себя профессионально, чем разводить клоунаду?
Это бессмысленно, потому что уровень создателя языка Rave как программиста потрясающе низок.
Хотя, глумиться, конечно, не следовало.
mishapetelev
24.07.2024 07:26+11Да ты ведь просто завидуешь, новичек создал свой язык программирования, а ты нет.
Если ты такой умный напиши Rave 1.(9) без недостатков
Only_god_can_judge_me
24.07.2024 07:26+4Мне кажется, это связано с личными качествами тимофея, такими как высокомерие, самоуверенность и порой закрытость к обратной связи, которые могут вызывать такие амбиции, как, например, сделать язык убийцей C(По крайне мере я это наблюдал, пока не разорвал с ним связи). Не удивлюсь, если он забанит на своём сервере кого-то, кто критикует его язык. Поэтому, да, эта статья отражает то, что автор статьи, возможно, ощущал при общении с тимофеем и наблюдении за разработкой языка.
Вот такую ответку автор сделал тимофею =D
Dmitry_604
24.07.2024 07:26+21Прочел заголовок, первая мысль - покушение на китайского лидера какое-то. Уже от потока полит. новостей деформация пошла :)
gohrytt
24.07.2024 07:26+4Самое страшное - я никогда не слышал про этот язык, но даже прочитав только эту статью я понимаю какие проблемы автор языка пытался решить и это вызывает уважение.
Но да, названию соответствует
Daniel217D
Почему именно высмеивание, а не структурированная критика? Личная неприязнь здесь явно прослеживается
pluffie Автор
Критика была, не только от меня, кстати. Больше года назад ему говорили, что бинарный логарифм можно ускорить, реализовав его с помощью процессорной инструкции - игнор. Ещё когда компилятор был на D, ему говорили, что лучше повторяющиеся вызовы в if'ах вынести в перменные - код переписан с нуля, а проблема та же. Про ужасный компайл тайм ему говорили, а я даже варианты исправления предлагал - не помню уже, что в ответ получили. Про хэш ему говорили - ответ в статье.
Нет, иногда автор критику слушает, но слушает он её как-то выборочно и, иногда, понимает как-то по-своему. Да и многие проблемы вызваны банально тем, что он не тестировал код, или бежал что-то реализовывать, даже не попытавшись разобраться в теме. А, ну и его коронное оправдание: "Я пишу компилятор в одиночку".
Критику тоже заслужить нужно. Когда сам автор к коду наплевательски относится, неудивительно, что другие будут над ним только смеяться. Личная неприязнь всё-таки есть, да.
tyomitch
Компилятор C++ достаточно умный, что в сгенерированном коде разницы не будет, скорее всего.
pluffie Автор
Да это понятно, но легче прочитать просто название переменной, чем цепочку вызовов, что в нынешнем коде. Претензия чисто стилистическая.
funny_falcon
Не факт.
С переменной выглядит приятнее.
FreeNickname
То есть Вам нужно, чтобы Вы на диване сидели и с дивана командовали, а автор языка по первому зову подпрыгивал и выполнял? А когда этого не происходит, у Вас возникает вселенская обида? Сдаётся мне, это не самая оптимальная позиция для того, чтобы прожить счастливую жизнь, но дело Ваше, конечно.
На всякий случай, вдруг это поможет (вряд ли, конечно): Вы можете не командовать с дивана. Никто не заставляет Вас. Вы свободны.
Нет.
P.S. Юмор в статье неплохой) Если бы не нужно было выуживать его из потока желчи, была бы хорошая статья.