update 1.0.2

This commit is contained in:
n3taeli 2024-02-05 17:13:32 +02:00
parent 317fa500b8
commit a6fbe841f1
Signed by: n3tael
GPG Key ID: F305925762F035A8
27 changed files with 351 additions and 196 deletions

View File

@ -1,5 +1,5 @@
# Labast # Labast
A zero-dependencies Labaski interpreter written in Rust. Fully supports [Labashki specs 1.4.1](./SPECIFICATION.md). A zero-dependencies Labaski interpreter written in Rust. Fully supports Labashki specs 1.7.1.
## Examples ## Examples
See `scripts` directory. See `scripts` directory.

View File

@ -1,51 +0,0 @@
# Labaski specification
Last update: 2024-02-05
Программа состоит из команд в формате `ИНСТРУКЦИЯ АРГУМЕНТ` (assembler-like)
Аргумент всегда должен быть `uint16` (`unsigned short`, `u16`)
Все данные которые программа использует хранятся в стеке, сам стек это массив из этих `unsigned short`'ов
Размер стека без разницы, но желательно минимум 256
Выполнение программы начинается либо с начала файла, либо с лейбла 0, если он есть
Почти каждая инструкция которые читает что-то стека должна POP'ать прочитаннное значение
Если название инструкции начинается с #, то парсер программы должен прочитать аргумент к инструкции как строку(до первого пробела)
## Инструкции
| Инструкция | Аргумент | Описание |
|-------------|----------------|----------------------------------------------------------------------|
| PUSH | `uint16` | Добавить в стек |
| POP | - | Удалить из стека |
| DUP | - | Дублировать последнее значение в стеке |
| SWAP | - | Поменять местами последние 2 значения в стеке |
| ADD | - | Прибавление последних 2-х значений из стека |
| SUB | - | Вычитание последних 2-х значений из стека |
| MUL | - | Умножение последних 2-х значений из стека |
| DIV | - | Деление последних 2-х значений из стека |
| JMP | `uint16` лейбл | Перейти к лейблу <имя> |
| JNZ | `uint16` лейбл | Перейти к лейблу <имя> если значение с верхушки стека не 0 |
| JZ | `uint16` лейбл | Перейти к лейблу <имя> если значение с верхушки стека 0 |
| NOP | - | Ничего не делает |
| EXIT | - | Выход из программы с кодом 0 |
| PUTC | - | Вывести символ в консоль |
| GETC | - | Получить символ с консоли, добавить символ в стек |
| MEOW | - | Принт, POP'ает со стека |
| DUMP | - | Вывести весь стек в консоль, понятное дело не попает со стека |
| SCAN | - | Сканирует ввод из консоли на `short`'ы и пушит в стек |
| #EXEC | arg | Выполнить код из файла, указанного в аргументе* |
| ARGS | `uint16` | POP'ает аргумент значений с основного стека и пушит их в стек модуля, если указать аргумент 0, то нужное кол-во аргументов она возьмет(попнув) с оригинального стека |
| SIZE | | Пушит в стек значение размера стека |
| QUIT | `uint16` | Полностью завершает выполнение программы. Как аргумент попает exit-код. |
> \*путь относительно места вызова интерпретатора. Создает отдельный стек для модуля, но передает основной для того чтобы взять оттуда аргументы. После того как модуль выполнился, пушит весь стек модуля в основной.
## Лейблы
Лейблы могут быть только `unsigned short`'ами
От 0 (main-лейбл) до минимум 256
Если юзер высрал два лейбла с одинаковыми идентификаторами, то интерпретатор/компилятор должен выдать ошибку

View File

@ -1,7 +1,7 @@
; written by Labashki developer - aeris ; written by Labashki developer - aeris
@ 0 @ 0
#EXPR >++:+\ #expr >++:+\
DUMP dump
; Expected output ; Expected output
; ;

View File

@ -1,30 +1,30 @@
PUSH 72 ; H push 72 ; H
PUTC ; Print to console putc ; Print to console
PUSH 101 ; e push 101 ; e
PUTC putc
PUSH 108 ; l push 108 ; l
PUTC putc
PUSH 108 ; l push 108 ; l
PUTC putc
PUSH 111 ; o push 111 ; o
PUTC putc
PUSH 32 ; <space> push 32 ; <space>
PUTC putc
PUSH 76 ; L push 76 ; L
PUTC putc
PUSH 97 ; a push 97 ; a
PUTC putc
PUSH 98 ; b push 98 ; b
PUTC putc
PUSH 97 ; a push 97 ; a
PUTC putc
PUSH 115 ; s push 115 ; s
PUTC putc
PUSH 104 ; h push 104 ; h
PUTC putc
PUSH 107 ; k push 107 ; k
PUTC putc
PUSH 105 ; i push 105 ; i
PUTC putc
PUSH 33 ; ! push 33 ; !
PUTC putc

View File

@ -1,20 +1,20 @@
@ 1 @ 1
PUSH 115 ; s push 115 ; s
PUTC putc
PUSH 104 ; h push 104 ; h
PUTC putc
PUSH 107 ; k push 107 ; k
PUTC putc
PUSH 105 ; i push 105 ; i
PUTC putc
EXIT exit
@ 0 @ 0
PUSH 76 ; L push 76 ; L
PUTC putc
PUSH 97 ; a push 97 ; a
PUTC putc
PUSH 98 ; b push 98 ; b
PUTC putc
PUSH 97 ; a push 97 ; a
PUTC putc
JMP 1 jmp 1

View File

@ -1,23 +1,23 @@
; Addition ; Addition
PUSH 2 ; a = 2 push 2 ; a = 2
PUSH 3 ; b = 2 push 3 ; b = 2
ADD ; a + b add ; a + b
MEOW ; = 5 meow ; = 5
; Subtraction ; Subtraction
PUSH 9 ; a = 9 push 9 ; a = 9
PUSH 4 ; b = 4 push 4 ; b = 4
SUB ; b - a sub ; b - a
MEOW ; = 5 meow ; = 5
; Multiplication ; Multiplication
PUSH 9 ; a = 9 push 9 ; a = 9
PUSH 5 ; b = 5 push 5 ; b = 5
MUL ; b * a mul ; b * a
MEOW ; = 45 meow ; = 45
; Division ; Division
PUSH 81; a = 81 push 81; a = 81
PUSH 9 ; b = 9 push 9 ; b = 9
DIV ; b / a div ; b / a
MEOW ; = 9 meow ; = 9

View File

@ -1,12 +1,12 @@
PUSH 76 ; L push 76 ; L
PUTC putc
PUSH 97 ; a push 97 ; a
PUTC putc
PUSH 98 ; b push 98 ; b
PUTC putc
PUSH 97 ; a push 97 ; a
PUTC putc
#EXEC second_part.lb #exec second_part.lb
PUSH 33 ; ! push 33 ; !
PUTC putc
QUIT quit

View File

@ -1,8 +1,8 @@
PUSH 115 ; s push 115 ; s
PUTC putc
PUSH 104 ; h push 104 ; h
PUTC putc
PUSH 107 ; k push 107 ; k
PUTC putc
PUSH 105 ; i push 105 ; i
PUTC putc

View File

@ -1,11 +1,11 @@
; written by Labashki developer - aeris ; written by Labashki developer - aeris
@ 0 @ 0
PUSH 2 push 2
PUSH 3 push 3
PUSH 4 push 4
PUSH 1 push 1
PICK pick
DUMP dump
; Expected output ; Expected output
; ;

View File

@ -1,9 +1,9 @@
@ 0 @ 0
PUSH 1 push 1
PUSH 2 push 2
PUSH 3 push 3
SIZE size
MEOW meow
; Expected output ; Expected output
; ;

View File

@ -1,4 +1,4 @@
; Labashki standard library ; Labashki standard library
; Written by Labashki developer - aeris ; Written by Labashki developer - aeris
ARGS 0 args 0

View File

@ -1,4 +1,4 @@
; Labashki standard library ; Labashki standard library
; Written by Labashki developer - aeris ; Written by Labashki developer - aeris
ARGS 3 args 3

View File

@ -1,17 +1,17 @@
; Labashki standard library ; Labashki standard library
; Written by Labashki developer - aeris ; Written by Labashki developer - aeris
MAXSIZE maxsize
ARGS 1 ; Насколько сварить носок args 1 ; Насколько сварить носок
SUB sub
@ 1 @ 1
JNZ 2 jnz 2
QUIT quit
@ 2 @ 2
#EXPR - #expr -
PUSH -1 push -1
_UNIX_RANDOM _unix_random
SWAP swap
JMP 1 jmp 1

View File

@ -1,2 +1,2 @@
SIZE size
#EXEC std\unix\ВАРЁНЫЙНОСОК.lb #exec std\unix\ВАРЁНЫЙНОСОК.lb

View File

@ -1,4 +1,6 @@
use crate::{instructions, stack::Stack}; use std::io::Read;
use crate::{errors::RunError, instructions, stack::{Instruction, Stack}};
fn find_labels(stack: &mut Stack) { fn find_labels(stack: &mut Stack) {
while stack.program_counter < (stack.program.len() as u16) { while stack.program_counter < (stack.program.len() as u16) {
@ -29,62 +31,102 @@ fn find_labels(stack: &mut Stack) {
} }
} }
fn find_prefix_operators(instruction: &mut Instruction, memory: &mut Vec<u16>) {
match instruction.name.chars().nth(0) {
Some('|') => {
instruction.name = instruction.name.chars().skip(1).collect();
instruction.data = memory.pop().expect(&format!("{}", RunError::MemoryEmpty));
},
Some('^') => {
instruction.name = instruction.name.chars().skip(1).collect();
//dbg!(&instruction);
if instruction.name.chars().nth(0) != Some('#') {
println!("{0} {1}", instruction.name, instruction.data.to_string());
} else if instruction.arg.is_empty() {
println!("{}", instruction.name);
} else {
println!("{0} {1}", instruction.name, instruction.arg);
}
}
_ => {}
}
}
pub fn execute(stack: &mut Stack, mut origin_stack: Option<&mut Stack>, mod_name: &String) { pub fn execute(stack: &mut Stack, mut origin_stack: Option<&mut Stack>, mod_name: &String) {
find_labels(stack); find_labels(stack);
while let Some(instruction) = stack.program.get(stack.program_counter as usize) { while let Some(mut instruction) = stack.program.get_mut(stack.program_counter as usize) {
stack.program_counter += 1; stack.program_counter += 1;
find_prefix_operators(&mut instruction, &mut stack.memory);
if instruction.name.starts_with('@') { if instruction.name.starts_with('@') {
continue; continue;
} }
match instruction.name.as_str() { match instruction.name.as_str() {
// Stack operations // Stack operations
"PUSH" => instructions::push::push(&mut stack.memory, &instruction.data), "push" => instructions::push::push(&mut stack.memory, &instruction.data),
"PICK" => instructions::pick::pick(&mut stack.memory, &instruction.data), "pick" => instructions::pick::pick(&mut stack.memory, &instruction.data),
"POP" => instructions::pop::pop(&mut stack.memory), "pop" => instructions::pop::pop(&mut stack.memory),
"DUP" => instructions::dup::dup(&mut stack.memory), "dup" => instructions::dup::dup(&mut stack.memory),
"SWAP" => instructions::swap::swap(&mut stack.memory), "swap" => instructions::swap::swap(&mut stack.memory),
"MEOW" => instructions::meow::meow(&mut stack.memory), "meow" => instructions::meow::meow(&mut stack.memory),
"DUMP" => instructions::dump::dump(&mut stack.memory), "dump" => instructions::dump::dump(&mut stack.memory),
"SIZE" => instructions::size::size(&mut stack.memory), "size" => instructions::size::size(&mut stack.memory),
"MAXSIZE" => instructions::maxsize::maxsize(&mut stack.memory), "maxsize" => instructions::maxsize::maxsize(&mut stack.memory),
"#EXPR" => instructions::expr::expr(&mut stack.memory, &instruction.arg), "#expr" => instructions::expr::expr(&mut stack.memory, &instruction.arg),
// Labels // Labels
"JMP" => instructions::jmp::jmp(&mut stack.labels, &mut stack.program_counter, &instruction.data), "jmp" => instructions::jmp::jmp(&mut stack.labels, &mut stack.program_counter, &instruction.data),
"JNZ" => instructions::jnz::jnz(&mut stack.memory, &mut stack.labels, &mut stack.program_counter, &instruction.data), "jnz" => instructions::jnz::jnz(&mut stack.memory, &mut stack.labels, &mut stack.program_counter, &instruction.data),
"JZ" => instructions::jz::jz(&mut stack.memory, &mut stack.labels, &mut stack.program_counter, &instruction.data), "jz" => instructions::jz::jz(&mut stack.memory, &mut stack.labels, &mut stack.program_counter, &instruction.data),
// Math operations // Math operations
"ADD" => instructions::add::add(&mut stack.memory), "add" => instructions::add::add(&mut stack.memory),
"SUB" => instructions::sub::sub(&mut stack.memory), "sub" => instructions::sub::sub(&mut stack.memory),
"MUL" => instructions::mul::mul(&mut stack.memory), "mul" => instructions::mul::mul(&mut stack.memory),
"DIV" => instructions::div::div(&mut stack.memory), "div" => instructions::div::div(&mut stack.memory),
"mod" => instructions::modulo::modulo(&mut stack.memory),
"and" => instructions::and::and(&mut stack.memory),
"or" => instructions::or::or(&mut stack.memory),
"xor" => instructions::xor::xor(&mut stack.memory),
"nand" => instructions::nand::nand(&mut stack.memory),
"band" => instructions::band::band(&mut stack.memory),
"bor" => instructions::bor::bor(&mut stack.memory),
"bxor" => instructions::bxor::bxor(&mut stack.memory),
"bnand" => instructions::bnand::bnand(&mut stack.memory),
"not" => instructions::not::not(&mut stack.memory),
// Console operations // Console operations
"PUTC" => instructions::putc::putc(&mut stack.memory), "putc" => instructions::putc::putc(&mut stack.memory),
"GETC" => instructions::getc::getc(&mut stack.memory), "getc" => instructions::getc::getc(&mut stack.memory),
"SCAN" => instructions::scan::scan(&mut stack.memory), "scan" => instructions::scan::scan(&mut stack.memory),
// Modules // Modules
"ARGS" => instructions::args::args(&mut stack.memory, &mut origin_stack, &instruction.data), "args" => instructions::args::args(&mut stack.memory, &mut origin_stack, &instruction.data),
"#EXEC" => instructions::exec::exec(stack, &instruction.arg.clone(), &mod_name), "#exec" => {
let arg = instruction.arg.clone();
instructions::exec::exec(stack, &arg, &mod_name);
}
#[cfg(target_family = "unix")] #[cfg(target_family = "unix")]
"_UNIX_RANDOM" => instructions::_unix_random::_unix_random(&mut stack.memory), "_unix_random" => instructions::_unix_random::_unix_random(&mut stack.memory),
// ? // ?
"NOP" => continue, "nop" => continue,
"EXIT" => instructions::exit::exit(&instruction.data), "exit" => instructions::exit::exit(&instruction.data),
"QUIT" => std::process::exit(0), "quit" => std::process::exit(0),
_ => { _ => {
eprintln!("Unknown instruction: {}", instruction.name); eprintln!("Unknown instruction: {}", instruction.name);
std::process::exit(2); std::process::exit(2);
} }
} }
std::io::stdin().bytes();
} }
} }

14
src/instructions/and.rs Normal file
View File

@ -0,0 +1,14 @@
use crate::errors::RunError;
pub fn and(memory: &mut Vec<u16>) {
let a: bool = memory.pop().expect(&format!("{}", RunError::MemoryEmpty)) == 1;
let b: bool = memory.pop().expect(&format!("{}", RunError::MemoryEmpty)) == 1;
let c: u16 = if a && b {
1
} else {
0
};
memory.push(c);
}

14
src/instructions/band.rs Normal file
View File

@ -0,0 +1,14 @@
use crate::errors::RunError;
pub fn band(memory: &mut Vec<u16>) {
let a: bool = memory.pop().expect(&format!("{}", RunError::MemoryEmpty)) == 1;
let b: bool = memory.pop().expect(&format!("{}", RunError::MemoryEmpty)) == 1;
let c: u16 = if a & b {
1
} else {
0
};
memory.push(c);
}

14
src/instructions/bnand.rs Normal file
View File

@ -0,0 +1,14 @@
use crate::errors::RunError;
pub fn bnand(memory: &mut Vec<u16>) {
let a: bool = memory.pop().expect(&format!("{}", RunError::MemoryEmpty)) == 1;
let b: bool = memory.pop().expect(&format!("{}", RunError::MemoryEmpty)) == 1;
let c: u16 = if !(a & b) {
1
} else {
0
};
memory.push(c);
}

14
src/instructions/bor.rs Normal file
View File

@ -0,0 +1,14 @@
use crate::errors::RunError;
pub fn bor(memory: &mut Vec<u16>) {
let a: bool = memory.pop().expect(&format!("{}", RunError::MemoryEmpty)) == 1;
let b: bool = memory.pop().expect(&format!("{}", RunError::MemoryEmpty)) == 1;
let c: u16 = if a | b {
1
} else {
0
};
memory.push(c);
}

14
src/instructions/bxor.rs Normal file
View File

@ -0,0 +1,14 @@
use crate::errors::RunError;
pub fn bxor(memory: &mut Vec<u16>) {
let a: bool = memory.pop().expect(&format!("{}", RunError::MemoryEmpty)) == 1;
let b: bool = memory.pop().expect(&format!("{}", RunError::MemoryEmpty)) == 1;
let c: u16 = if a ^ b {
1
} else {
0
};
memory.push(c);
}

View File

@ -23,3 +23,13 @@ pub mod exit;
pub mod maxsize; pub mod maxsize;
#[cfg(target_family = "unix")] #[cfg(target_family = "unix")]
pub mod _unix_random; pub mod _unix_random;
pub mod modulo;
pub mod and;
pub mod or;
pub mod xor;
pub mod nand;
pub mod band;
pub mod bor;
pub mod bxor;
pub mod bnand;
pub mod not;

View File

@ -0,0 +1,8 @@
use crate::errors::RunError;
pub fn modulo(memory: &mut Vec<u16>) {
let a: u16 = memory.pop().expect(&format!("{}", RunError::MemoryEmpty));
let b: u16 = memory.pop().expect(&format!("{}", RunError::MemoryEmpty));
memory.push(b % a);
}

14
src/instructions/nand.rs Normal file
View File

@ -0,0 +1,14 @@
use crate::errors::RunError;
pub fn nand(memory: &mut Vec<u16>) {
let a: bool = memory.pop().expect(&format!("{}", RunError::MemoryEmpty)) == 1;
let b: bool = memory.pop().expect(&format!("{}", RunError::MemoryEmpty)) == 1;
let c: u16 = if !(a && b) {
1
} else {
0
};
memory.push(c);
}

13
src/instructions/not.rs Normal file
View File

@ -0,0 +1,13 @@
use crate::errors::RunError;
pub fn not(memory: &mut Vec<u16>) {
let a: bool = memory.pop().expect(&format!("{}", RunError::MemoryEmpty)) == 1;
let c: u16 = if !a {
1
} else {
0
};
memory.push(c);
}

14
src/instructions/or.rs Normal file
View File

@ -0,0 +1,14 @@
use crate::errors::RunError;
pub fn or(memory: &mut Vec<u16>) {
let a: bool = memory.pop().expect(&format!("{}", RunError::MemoryEmpty)) == 1;
let b: bool = memory.pop().expect(&format!("{}", RunError::MemoryEmpty)) == 1;
let c: u16 = if a || b {
1
} else {
0
};
memory.push(c);
}

14
src/instructions/xor.rs Normal file
View File

@ -0,0 +1,14 @@
use crate::errors::RunError;
pub fn xor(memory: &mut Vec<u16>) {
let a: bool = memory.pop().expect(&format!("{}", RunError::MemoryEmpty)) == 1;
let b: bool = memory.pop().expect(&format!("{}", RunError::MemoryEmpty)) == 1;
let c: u16 = if !a != !b {
1
} else {
0
};
memory.push(c);
}

View File

@ -1,4 +1,4 @@
use std::{env, fs}; use std::{env, fs, io::{BufRead, Write}};
use errors::RunError; use errors::RunError;
use execute::execute; use execute::execute;
use parse::parse; use parse::parse;
@ -12,12 +12,33 @@ mod errors;
fn main() { fn main() {
let args: Vec<String> = env::args().collect(); let args: Vec<String> = env::args().collect();
if args.len() < 2 { if args.get(2) == Some(&String::from("-h"))
eprintln!("Usage: {0} [FILE]", args[0]); || args.get(2) == Some(&String::from("--help")) {
eprintln!("Usage: {0} [file]", args[0]);
return; return;
} }
if args.len() < 2 {
let mut stack = Stack::new();
let mut line = String::new();
loop {
line.clear();
stack.program.clear();
let stdin = std::io::stdin();
print!("> ");
std::io::stdout().flush().expect("Cannot flush the buffer.");
stdin.lock().read_line(&mut line).unwrap();
parse(&mut stack, &line.trim());
execute(&mut stack, None, &"inline".to_string());
}
}
let mut stack = Stack::new(); let mut stack = Stack::new();
let lines = fs::read_to_string(&args[1]).expect(&format!("{}", RunError::FailToReadFile)); let lines = fs::read_to_string(&args[1]).expect(&format!("{}", RunError::FailToReadFile));