[Rust] Небольшой язык программирования и его разработка
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Как всё началосьСидел я на третьем курсе колледжа и ничего не делал, получая пятерки. Материал я усваивал быстро (спасибо форумам и хабру), но чего-то мне не хватало и тогда я взялся за изучение операционных систем, надеясь что-то сделать к диплому. Время шло, практика и кончился третий курс.Переходя в следующий курс я начал активно изучать всё что касалось ОСи, но толком никуда не продвинулся. Тогда и родилась у меня идея создать свой ЯП. Времени было мало, а делать было что-то нужно и я в свободное от удалёнки (с серой ЗП) что-то писал.Язык я решил назвать The Gorge.Часть первая. Как работает язык и где его найтиБыло принято решение разместить язык на платформе гитхаб и создать новый профиль.
На тот момент я имел в распоряжении старый подаренный мне акк, но в последствии всё-таки создал свой и сейчас его можно найти так: (просто допишите сайт)/pcPowerJG/natural-network.В папке src в файле lib.rs мы можем увидеть чудо, язык написан почти полностью на раст (почему почти? к сожалению в далёкие времена 2019 года раст не давал открыть файл на моей любимой манжаре и пришлось открывать его через Си). Ну так вот, первое что мне было необходимо сделать - создать словарь используемых ключевых слов.
words.push("object".to_string());//1 // используется для создания объекта, который хранит значения в памяти
words.push("if".to_string());//2 // оператор условия, нужен для сравнения ДВУХ параметров
words.push("exit_()".to_string());//3//выход из приложения
words.push("func".to_string()); //4//инициализация функции
words.push("print".to_string());//5 // вывод на консоль
words.push("remove".to_string());//6 //удаление
words.push("array".to_string());//7 // создание массива
words.push("struct".to_string()); //8 // создание структуры
words.push("end".to_string());//9//end operation
words.push("end_func".to_string()); // 10 // конец функции
words.push("return".to_string()); // 11
words.push("!eq".to_string());// 12
words.push(">".to_string()); // 13
words.push("<".to_string()); // 14
words.push("loop".to_string());// 15
words.push("end_loop".to_string());// 16
words.push("_".to_string()); // 17 // просто в качестве НЕ ключевого слова
words.push("break".to_string()); // 18
words.push("true".to_string()); // 19
words.push("false".to_string()); // 20
Как мы видим у слов есть определённая нумерация (и не с нуля. это важно).Следующая главная функция это функция старт.
pub fn start_(&mut self, text: String) -> u8
В ней происходит вся магия обработки текста и перевод его в действие. Сразу оговорюсь, мы пишем интерпретатор.Так как мы пишем интерпретатор, нам необходимо как-то это обрабатывать, для этого необходимо создавать карту действий, но мы ограничимся простым массивом.Так же создадим переменные для временного хранения информации (тело цикла к примеру или математическое выражение, имя функции и т.д.).Привожу код.
let mut temp_values: String = String::new(); // ВРЕМЕННЫЕ ПЕРЕМЕННЫЕ
let mut temp_name: String = String::new(); // ...
let mut temp_buffer: String = String::new(); // ...
let mut func_text: String = String::new();
let mut last_op: [usize; 3] = [0; 3]; // храним три последних действия
// ----------------------------------------------
let mut if_count: usize = 0;
let mut if_result: bool = true; // ответ на условие
let mut struct_flag: bool = false; // это структура или условие
let mut function_inactive_flag: bool = false; // если функция не активна
let mut loop_flag: bool = false; // попали на цикл
let mut index_loop: usize = 0; // количество циклов (для цикла в цикле)
Так же смотря код можно заметить много флагов, они нужны как-раз таки для нормального функционирования.Всего наш код делиться на три блока
if ch == ' ' || ch == '\t' {
//...................
} else if ch == '\n' {
//...................
} else if ch == '=' {
//...................
}
} else {
temp_values.push(ch);
}
В первом мы выполняем действия при разделении кода (то есть сразу смотрим что за ключевое слово или переменная и записываем в карту действий). Второй выполняем после символа окончания строки, тут мы выполняем действия. И третий выполняется при присваивании.
Блок else нужен только для записи переменной.Дальше ныряем по коду, предлагаю посмотреть каждый блок отдельно.
if function_inactive_flag {
// ...
}
if loop_flag {
// ...
}
match temp_values.trim() {
// ...
}
Мы видим в блоке ещё три блока. Первый блок связан с функциями, в нём происходит запись их в переменную (мы храним имя функции как переменную).Второй связан с циклом, запись текста в переменную и выполнение цикла.
Третий же обнаруживает действия и отправляет их в карту.Всё остальное же идёт согласно законам логики построения программы.К примеру: a = b + c
Преобразуется в: last_op[0] = 1 last_op[1] = 17
И выполниться: в функции math_work .МатематикаФункция преобразования переменных в значения:
fn math_work(&self, text: String) -> String {
let text: String = Words::trim(text.clone());
let mut result_string: String = String::new();
let mut temp_string: String = String::new();
for ch in text.chars() {
match ch {
'+' | '-' | '/' | '*' | '(' | ')' | '&' | '|' | '!' | '=' | '<' | '>' => {
if Words::is_digit(temp_string.clone()) {
result_string += temp_string.clone().as_str();
} else {
result_string += self.search_var(temp_string).0.clone().as_str();
}
result_string.push(ch.clone());
temp_string = String::new();
},
_ => {
temp_string.push(ch.clone());
},
}
}
let (value, type_, _temp) = self.search_var(temp_string.clone());
if _temp {
result_string += value.as_str();
} else {
result_string += temp_string.clone().as_str();
} result_string
}
Всё достаточно просто, если символ не является буквой (или другим символом используемым в именах переменных) - сразу пишем его, если является ищем конец переменной (обычно между переменными есть два других спецсимвола, либо символ конца строки) и заменяем её имя значением.Передаём всё в следующую функцию:
fn eval(str_: Vec<char>) -> f32 {
let mut i: usize = 0;
Words::expr(str_, &mut i)
}
Вся математическая магия
fn eval(str_: Vec<char>) -> f32 {
let mut i: usize = 0;
Words::expr(str_, &mut i)
}
fn plus_one(u: &mut usize) {
*u += 1;
}
fn number(ch_: Vec<char>, idx: &mut usize) -> f32 {
let mut result: f32 = 0.0;
//float result = 0.0;
let mut div: f32 = 10.0;
let mut sign: f32 = 1.0;
if ch_[*idx] == '-'{
sign = -1.0;
*idx += 1;
}
while *idx < ch_.len() &&
match ch_[*idx] {
'0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => { true },
_ => { false }
}
{
result = result * 10.0 + (f32::from_str(&ch_[*idx].to_string()).expect("не удалось форматировать строку"));
*idx += 1;
}
if *idx < ch_.len() && (ch_[*idx] == '.'){
*idx += 1;
while *idx < ch_.len() &&
match ch_[*idx] {
'0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => { true },
_ => { false }
}
{
result = result + (f32::from_str(&ch_[*idx].to_string()).expect("не удалось форматировать строку")) / div;
div *= 10.0;
*idx += 1;
}
}
sign * result
}
fn expr(ch_: Vec<char>, idx: &mut usize) -> f32 {
let mut result: f32 = Words::term(ch_.clone(), idx);
while *idx < ch_.len() && (ch_[*idx] == '+' || ch_[*idx] == '-') {
match ch_[*idx] {
'+' => {
*idx += 1;
result += Words::term(ch_.clone(), idx);
},
'-' => {
*idx += 1;
result -= Words::term(ch_.clone(), idx);
},
_ => {},
}
} result
}
fn term(ch_: Vec<char>, idx: &mut usize) -> f32 {
let mut result: f32 = Words::factor(ch_.clone(), idx);
let mut div: f32 = 0.0;
while *idx < ch_.len() && (ch_[*idx] == '*' || ch_[*idx] == '/') {
match ch_[*idx] {
'*' => {
*idx += 1;
result *= Words::factor(ch_.clone(), idx);
},
'/' => {
*idx += 1;
div = Words::factor(ch_.clone(), idx);
if (div != 0.0) {
result /= div;
} else {
panic!("Division by zero!\n");
}
},
_ => {},
}
} result
}
fn factor(ch_: Vec<char>, idx: &mut usize) -> f32 {
let mut result: f32 = 0.0;
let mut sign: f32 = 1.0;
if (ch_[*idx] == '-') {
sign = -1.0;
*idx += 1;
}
if (ch_[*idx] == '(') {
*idx += 1;
result = Words::expr(ch_.clone(), idx);
if (ch_[*idx] != ')') {
panic!("Brackets unbalanced!\n");
}
*idx += 1;
} else { result = Words::number(ch_, idx); }
sign * result
}
ПеременныеТеперь настало время поговорить о переменных. Реализованы они крайне слабо и их хранение занимает огромную кучу места в оперативной памяти, ситуация возможно скоро исправиться.Все переменные занимают место сразу в двух массивах, массив имён и типов:
object_buffer: Vec<(String, usize)>
Массив значений:
value_buffer: Vec<String>
Для добавления новой переменной используется функция add_vars:
fn add_vars(&mut self, vars_name: String, mut vars_value: String, vars_type: usize) {
//object_buffer: Vec<(String, usize)>
//value_buffer: Vec<String>
if vars_value.clone().split('"').collect::<Vec<&str>>().len() > 1 {
vars_value = vars_value.split('"').collect::<Vec<&str>>()[1].to_string();
} else {
vars_value = vars_value.clone().trim().to_string();
}
self.object_buffer.push((vars_name, vars_type));
self.value_buffer.push(vars_value);
}
В ней всего одна проверка, есть ли кавычки (что в кавычках мы считаем текстом и не отрезаем пробелы).Для удаления переменной:
fn remove_vars(&mut self, vars_name: String) {
for i in 0..self.object_buffer.len() {
if self.object_buffer[i].0.clone() == vars_name {
self.object_buffer.remove(i);
self.value_buffer.remove(i);
return;
}
}
}
Запись значения и поиск:
fn set_value(&mut self, vars_name: String, mut vars_value: String) {
for i in 0..self.object_buffer.len() {
if self.object_buffer[i].0 == vars_name {
if vars_value.clone().split('"').collect::<Vec<&str>>().len() > 1 {
vars_value = vars_value.split('"').collect::<Vec<&str>>()[1].to_string();
} else {
vars_value = vars_value.clone().trim().to_string();
}
self.value_buffer[i] = vars_value.clone();
return;
}
}
}
pub fn search_var(&self, vars_name: String) -> (String, usize, bool) {
for i in 0..self.object_buffer.len() {
if self.object_buffer[i].0 == vars_name {
let value: String = self.value_buffer[i].clone();
let type_: usize = self.object_buffer[i].1.clone();
return (value, type_, true);
}
}
(String::new(), 0, false)
}
Так же планируется импортировать из старой версии языка поддержку внешних библиотек.
import("/lib.so")
extern_func("lib.so", func_name)
extern_func("lib.so", func_name, arg1, arg2)
close_import("lib.so")
Я не показал весь код из соображений удобочитаемости статьи. Весь код (особенно в функции обработки условий (боже не заходите туда, исправлю как руки дойдут), циклов и функций) занял бы слишком много экранного пространства, а я хочу передать суть и поделиться. Возможно кому-то будет легче написать свой ЯП по моим наработкам, а в целом вэлком.Что нужно исправить и добавить?
- Исправить функцию обработки условий (не заходите туда, я серьезно);
- Зачем нам всё хранить в тексте? Исправить на байты;
- Импортировать поддержу сишных библиотек из старой версии языка
Спасибо за внимание.
===========
Источник:
habr.com
===========
Похожие новости:
- [Информационная безопасность, Программирование, GitHub, Rust] Разработчик показал, как можно получить SSH-ключ через VSCode при открытии жертвой приложения в редакторе
- Демонстрация атаки на редакторы кода, приводящей к утечке файлов при открытии исходных текстов
- [Open source, Программирование, Системное программирование, Компиляторы, Rust] Планирование редакции Rust 2021 (перевод)
- [Разработка веб-сайтов, CSS] CSS – строго типизированный язык программирования (перевод)
- Выпуск языка программирования Rust 1.52
- [Криптография, Rust] Улучшаем Кузнечик на Rust
- Facebook присоединился к организации Rust Foundation
- [JavaScript, C, Rust, WebAssembly] Оптимизируем производительность: JavaScript (V8) vs AssemblyScript (WebAssembly) (перевод)
- [Программирование, Haskell, Функциональное программирование, Rust] Как мы выбираем языки программирования в Typeable
- [Программирование, Rust] С лёгким налётом ржавчины или немного о владении (перевод)
Теги для поиска: #_rust, #_jazyk_programmirovanija (язык программирования), #_rust
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 23-Ноя 00:03
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Как всё началосьСидел я на третьем курсе колледжа и ничего не делал, получая пятерки. Материал я усваивал быстро (спасибо форумам и хабру), но чего-то мне не хватало и тогда я взялся за изучение операционных систем, надеясь что-то сделать к диплому. Время шло, практика и кончился третий курс.Переходя в следующий курс я начал активно изучать всё что касалось ОСи, но толком никуда не продвинулся. Тогда и родилась у меня идея создать свой ЯП. Времени было мало, а делать было что-то нужно и я в свободное от удалёнки (с серой ЗП) что-то писал.Язык я решил назвать The Gorge.Часть первая. Как работает язык и где его найтиБыло принято решение разместить язык на платформе гитхаб и создать новый профиль. На тот момент я имел в распоряжении старый подаренный мне акк, но в последствии всё-таки создал свой и сейчас его можно найти так: (просто допишите сайт)/pcPowerJG/natural-network.В папке src в файле lib.rs мы можем увидеть чудо, язык написан почти полностью на раст (почему почти? к сожалению в далёкие времена 2019 года раст не давал открыть файл на моей любимой манжаре и пришлось открывать его через Си). Ну так вот, первое что мне было необходимо сделать - создать словарь используемых ключевых слов. words.push("object".to_string());//1 // используется для создания объекта, который хранит значения в памяти
words.push("if".to_string());//2 // оператор условия, нужен для сравнения ДВУХ параметров words.push("exit_()".to_string());//3//выход из приложения words.push("func".to_string()); //4//инициализация функции words.push("print".to_string());//5 // вывод на консоль words.push("remove".to_string());//6 //удаление words.push("array".to_string());//7 // создание массива words.push("struct".to_string()); //8 // создание структуры words.push("end".to_string());//9//end operation words.push("end_func".to_string()); // 10 // конец функции words.push("return".to_string()); // 11 words.push("!eq".to_string());// 12 words.push(">".to_string()); // 13 words.push("<".to_string()); // 14 words.push("loop".to_string());// 15 words.push("end_loop".to_string());// 16 words.push("_".to_string()); // 17 // просто в качестве НЕ ключевого слова words.push("break".to_string()); // 18 words.push("true".to_string()); // 19 words.push("false".to_string()); // 20 pub fn start_(&mut self, text: String) -> u8
let mut temp_values: String = String::new(); // ВРЕМЕННЫЕ ПЕРЕМЕННЫЕ
let mut temp_name: String = String::new(); // ... let mut temp_buffer: String = String::new(); // ... let mut func_text: String = String::new(); let mut last_op: [usize; 3] = [0; 3]; // храним три последних действия // ---------------------------------------------- let mut if_count: usize = 0; let mut if_result: bool = true; // ответ на условие let mut struct_flag: bool = false; // это структура или условие let mut function_inactive_flag: bool = false; // если функция не активна let mut loop_flag: bool = false; // попали на цикл let mut index_loop: usize = 0; // количество циклов (для цикла в цикле) if ch == ' ' || ch == '\t' {
//................... } else if ch == '\n' { //................... } else if ch == '=' { //................... } } else { temp_values.push(ch); } Блок else нужен только для записи переменной.Дальше ныряем по коду, предлагаю посмотреть каждый блок отдельно. if function_inactive_flag {
// ... } if loop_flag { // ... } match temp_values.trim() { // ... } Третий же обнаруживает действия и отправляет их в карту.Всё остальное же идёт согласно законам логики построения программы.К примеру: a = b + c Преобразуется в: last_op[0] = 1 last_op[1] = 17 И выполниться: в функции math_work .МатематикаФункция преобразования переменных в значения: fn math_work(&self, text: String) -> String {
let text: String = Words::trim(text.clone()); let mut result_string: String = String::new(); let mut temp_string: String = String::new(); for ch in text.chars() { match ch { '+' | '-' | '/' | '*' | '(' | ')' | '&' | '|' | '!' | '=' | '<' | '>' => { if Words::is_digit(temp_string.clone()) { result_string += temp_string.clone().as_str(); } else { result_string += self.search_var(temp_string).0.clone().as_str(); } result_string.push(ch.clone()); temp_string = String::new(); }, _ => { temp_string.push(ch.clone()); }, } } let (value, type_, _temp) = self.search_var(temp_string.clone()); if _temp { result_string += value.as_str(); } else { result_string += temp_string.clone().as_str(); } result_string } fn eval(str_: Vec<char>) -> f32 {
let mut i: usize = 0; Words::expr(str_, &mut i) } fn eval(str_: Vec<char>) -> f32 {
let mut i: usize = 0; Words::expr(str_, &mut i) } fn plus_one(u: &mut usize) { *u += 1; } fn number(ch_: Vec<char>, idx: &mut usize) -> f32 { let mut result: f32 = 0.0; //float result = 0.0; let mut div: f32 = 10.0; let mut sign: f32 = 1.0; if ch_[*idx] == '-'{ sign = -1.0; *idx += 1; } while *idx < ch_.len() && match ch_[*idx] { '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => { true }, _ => { false } } { result = result * 10.0 + (f32::from_str(&ch_[*idx].to_string()).expect("не удалось форматировать строку")); *idx += 1; } if *idx < ch_.len() && (ch_[*idx] == '.'){ *idx += 1; while *idx < ch_.len() && match ch_[*idx] { '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => { true }, _ => { false } } { result = result + (f32::from_str(&ch_[*idx].to_string()).expect("не удалось форматировать строку")) / div; div *= 10.0; *idx += 1; } } sign * result } fn expr(ch_: Vec<char>, idx: &mut usize) -> f32 { let mut result: f32 = Words::term(ch_.clone(), idx); while *idx < ch_.len() && (ch_[*idx] == '+' || ch_[*idx] == '-') { match ch_[*idx] { '+' => { *idx += 1; result += Words::term(ch_.clone(), idx); }, '-' => { *idx += 1; result -= Words::term(ch_.clone(), idx); }, _ => {}, } } result } fn term(ch_: Vec<char>, idx: &mut usize) -> f32 { let mut result: f32 = Words::factor(ch_.clone(), idx); let mut div: f32 = 0.0; while *idx < ch_.len() && (ch_[*idx] == '*' || ch_[*idx] == '/') { match ch_[*idx] { '*' => { *idx += 1; result *= Words::factor(ch_.clone(), idx); }, '/' => { *idx += 1; div = Words::factor(ch_.clone(), idx); if (div != 0.0) { result /= div; } else { panic!("Division by zero!\n"); } }, _ => {}, } } result } fn factor(ch_: Vec<char>, idx: &mut usize) -> f32 { let mut result: f32 = 0.0; let mut sign: f32 = 1.0; if (ch_[*idx] == '-') { sign = -1.0; *idx += 1; } if (ch_[*idx] == '(') { *idx += 1; result = Words::expr(ch_.clone(), idx); if (ch_[*idx] != ')') { panic!("Brackets unbalanced!\n"); } *idx += 1; } else { result = Words::number(ch_, idx); } sign * result } object_buffer: Vec<(String, usize)>
value_buffer: Vec<String>
fn add_vars(&mut self, vars_name: String, mut vars_value: String, vars_type: usize) {
//object_buffer: Vec<(String, usize)> //value_buffer: Vec<String> if vars_value.clone().split('"').collect::<Vec<&str>>().len() > 1 { vars_value = vars_value.split('"').collect::<Vec<&str>>()[1].to_string(); } else { vars_value = vars_value.clone().trim().to_string(); } self.object_buffer.push((vars_name, vars_type)); self.value_buffer.push(vars_value); } fn remove_vars(&mut self, vars_name: String) {
for i in 0..self.object_buffer.len() { if self.object_buffer[i].0.clone() == vars_name { self.object_buffer.remove(i); self.value_buffer.remove(i); return; } } } fn set_value(&mut self, vars_name: String, mut vars_value: String) {
for i in 0..self.object_buffer.len() { if self.object_buffer[i].0 == vars_name { if vars_value.clone().split('"').collect::<Vec<&str>>().len() > 1 { vars_value = vars_value.split('"').collect::<Vec<&str>>()[1].to_string(); } else { vars_value = vars_value.clone().trim().to_string(); } self.value_buffer[i] = vars_value.clone(); return; } } } pub fn search_var(&self, vars_name: String) -> (String, usize, bool) { for i in 0..self.object_buffer.len() { if self.object_buffer[i].0 == vars_name { let value: String = self.value_buffer[i].clone(); let type_: usize = self.object_buffer[i].1.clone(); return (value, type_, true); } } (String::new(), 0, false) } import("/lib.so")
extern_func("lib.so", func_name) extern_func("lib.so", func_name, arg1, arg2) close_import("lib.so")
=========== Источник: habr.com =========== Похожие новости:
|
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 23-Ноя 00:03
Часовой пояс: UTC + 5