![Новый убийца Си опроверг арифметику - 1 Нельзя просто взять и создать плохой язык](https://www.pvsm.ru/images/2024/07/24/novyi-ubiica-si-oproverg-arifmetiku.jpg)
Что Вы знаете про эзотерические языки программирования? Они кажутся вам странными? Смешными? Интересными? Этот язык не из таких – он не эзотерический. Если смех действительно продлевает жизнь, то после этой статьи Вы станете бессмертным.
Глава -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 = [
'', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', ''
];
std::cstring::ltos(@getCurrArg(long), cast(char*)&nBuffer);
std::sprintBuffer.appendC(cast(char*)&nBuffer);
};
@if(@tEquals(@getCurrArgType(), cent)) {
nBuffer = [
'', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', ''
];
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] != ''; 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 = [
'', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', ''
];
std::cstring::dtos(@getCurrArg(double), 8, &nBuffer);
std::sprintBuffer.appendC(cast(char*)&nBuffer);
};
};
@if(@tEquals(@getCurrArgType(), float)) {
nBuffer = [
'', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', ''
];
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 = [
'', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', ''
];
std::cstring::ltos(@getCurrArg(long), cast(char*)&nBuffer);
std::sprintBuffer.appendC(cast(char*)&nBuffer);
};
@if(@tEquals(@getCurrArgType(), cent)) {
nBuffer = [
'', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', ''
];
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] != ''; 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 = [
'', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', ''
];
std::cstring::dtos(@getCurrArg(double), 8, &nBuffer);
std::sprintBuffer.appendC(cast(char*)&nBuffer);
};
};
@if(@tEquals(@getCurrArgType(), float)) {
nBuffer = [
'', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', ''
];
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
Автор: pluffie