[UNVERIFIED] update to spec 1.10, parser rewrite.

This commit is contained in:
n3taeli 2024-02-06 01:48:44 +02:00
parent cff855b9cb
commit f2b551a586
Signed by: n3tael
GPG Key ID: F305925762F035A8
14 changed files with 182 additions and 70 deletions

View File

@ -1,5 +1,5 @@
# Labast
A zero-dependencies Labaski interpreter written in Rust. Fully supports Labashki specs 1.7.1.
A zero-dependencies Labaski interpreter written in Rust. Fully supports Labashki specs 1.10.
## Examples
See `scripts` directory.

26
scripts/calc.lb Normal file
View File

@ -0,0 +1,26 @@
; written by Labashki developer - aeris
@ 0
scan
scan
scan
|jmp 0
@ 1
add
meow
quit
@ 2
sub
meow dkdkd
quit
@ 3
mul
meow
quit
@ 4
div
meow
quit

View File

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

View File

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

View File

@ -12,6 +12,9 @@ pub enum RunError {
UnknownLabel(u16),
InvalidExpressionUnknownOperator(String),
MemoryEmpty,
AttemptToInsertHashOrStringInst,
AttemptToInsertEmptyConst,
AttemptToInsertALabel
}
impl Display for RunError {
@ -28,18 +31,21 @@ impl Display for RunError {
RunError::UnknownLabel(label_name) => write!(f, "Unknown label: {}", label_name),
RunError::InvalidExpressionUnknownOperator(name) => write!(f, "Invalid expression: unknown operator '{}'", name),
RunError::MemoryEmpty => write!(f, "No elements in memory!"),
RunError::AttemptToInsertHashOrStringInst => write!(f, "Attempt to insert a hash/string-instruction"),
RunError::AttemptToInsertEmptyConst => write!(f, "Attempt to insert an empty constant"),
RunError::AttemptToInsertALabel => write!(f, "Attempt to insert a label"),
}
}
}
pub enum ParseError {
DataNotAUInt(String, usize)
ArgumentNotRequired(String, usize, String)
}
impl Display for ParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ParseError::DataNotAUInt(name, line) => write!(f, "Argument for {0} at {1} isn't a number", name, line),
ParseError::ArgumentNotRequired(name, line_number, line) => write!(f, "ParseError:\n{1} | {2}\nerror: string argument for {0} not required", name, line_number, line),
}
}
}

View File

@ -68,43 +68,46 @@ pub fn execute(stack: &mut Stack, mut origin_stack: Option<&mut Stack>, mod_name
match instruction.name.as_str() {
// Stack operations
"push" => instructions::push::push(&mut stack.memory, &instruction.data),
"pick" => instructions::pick::pick(&mut stack.memory, &instruction.data),
"pop" => instructions::pop::pop(&mut stack.memory),
"dup" => instructions::dup::dup(&mut stack.memory),
"swap" => instructions::swap::swap(&mut stack.memory),
"meow" => instructions::meow::meow(&mut stack.memory),
"dump" => instructions::dump::dump(&mut stack.memory),
"size" => instructions::size::size(&mut stack.memory),
"maxsize" => instructions::maxsize::maxsize(&mut stack.memory),
"pick" => instructions::pick::pick(&mut stack.memory, &instruction.data),
"#expr" => instructions::expr::expr(&mut stack.memory, &instruction.arg),
// Labels
"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),
"jz" => instructions::jz::jz(&mut stack.memory, &mut stack.labels, &mut stack.program_counter, &instruction.data),
// Math operations
// Math
"add" => instructions::add::add(&mut stack.memory),
"sub" => instructions::sub::sub(&mut stack.memory),
"mul" => instructions::mul::mul(&mut stack.memory),
"div" => instructions::div::div(&mut stack.memory),
"mod" => instructions::modulo::modulo(&mut stack.memory),
// Logic
"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),
"not" => instructions::not::not(&mut stack.memory),
// Bitwise
"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),
"bnot" => instructions::not::not(&mut stack.memory),
// Console operations
"putc" => instructions::putc::putc(&mut stack.memory),
"getc" => instructions::getc::getc(&mut stack.memory),
"scan" => instructions::scan::scan(&mut stack.memory),
// Labels
"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),
"jz" => instructions::jz::jz(&mut stack.memory, &mut stack.labels, &mut stack.program_counter, &instruction.data),
"kjnz" => instructions::kjnz::kjnz(&mut stack.memory, &mut stack.labels, &mut stack.program_counter, &instruction.data),
"kjz" => instructions::kjz::kjz(&mut stack.memory, &mut stack.labels, &mut stack.program_counter, &instruction.data),
// Input/Output
"putc" => instructions::putc::putc(&mut stack.memory),
"getc" => instructions::getc::getc(&mut stack.memory),
"scan" => instructions::scan::scan(&mut stack.memory),
"meow" => instructions::meow::meow(&mut stack.memory),
"#print" => instructions::print::print(&instruction.arg),
"$print" => instructions::print::print(&instruction.arg),
// Modules
"args" => instructions::args::args(&mut stack.memory, &mut origin_stack, &instruction.data),
@ -113,14 +116,28 @@ pub fn execute(stack: &mut Stack, mut origin_stack: Option<&mut Stack>, mod_name
instructions::exec::exec(stack, &arg, &mod_name);
}
// Self-modifying
"#insert" => {
let arg = instruction.arg.clone();
let data = instruction.data.clone();
instructions::insert::insert(&mut stack.program, &arg, &data)
},
// Special
"dump" => instructions::dump::dump(&mut stack.memory),
"size" => instructions::size::size(&mut stack.memory),
"maxsize" => instructions::maxsize::maxsize(&mut stack.memory),
"#expr" => instructions::expr::expr(&mut stack.memory, &instruction.arg),
"nop" => continue,
"quit" => std::process::exit(0),
"exit" => instructions::exit::exit(&instruction.data),
// Platform-specific
#[cfg(target_family = "unix")]
"_unix_random" => instructions::_unix_random::_unix_random(&mut stack.memory),
// ?
"nop" => continue,
"exit" => instructions::exit::exit(&instruction.data),
"quit" => std::process::exit(0),
_ => {
eprintln!("Unknown instruction: {}", instruction.name);
std::process::exit(2);

View File

@ -0,0 +1,26 @@
use crate::{errors::RunError, stack::Instruction};
pub fn insert(program: &mut Vec<Instruction>, arg: &String, data: &u16) {
let args: Vec<char> = arg.chars().collect();
if args[0] == '#' || args[0] == '$' {
eprintln!("{}", RunError::AttemptToInsertHashOrStringInst);
std::process::exit(2);
}
if args[0] == '\n' {
eprintln!("{}", RunError::AttemptToInsertEmptyConst);
std::process::exit(2);
}
if args[0] == '@' || args[1] == ' ' {
eprintln!("{}", RunError::AttemptToInsertALabel);
std::process::exit(2);
}
program.push(Instruction {
name: arg.to_string(),
arg: "\n".to_string(),
data: *data
});
}

View File

@ -6,7 +6,7 @@ pub fn jnz(memory: &mut Vec<u16>, labels: &mut [Option<i16>; 256], program_count
std::process::exit(2);
}
if memory.last() != Some(&0) {
if memory.pop() != Some(0) {
*program_counter = (labels[*data as usize].unwrap() - 1) as u16;
}
}

View File

@ -6,7 +6,7 @@ pub fn jz(memory: &mut Vec<u16>, labels: &mut [Option<i16>; 256], program_counte
std::process::exit(2);
}
if memory.last() == Some(&0) {
if memory.pop() == Some(0) {
*program_counter = (labels[*data as usize].unwrap() - 1) as u16;
}
}

12
src/instructions/kjnz.rs Normal file
View File

@ -0,0 +1,12 @@
use crate::errors::RunError;
pub fn kjnz(memory: &mut Vec<u16>, labels: &mut [Option<i16>; 256], program_counter: &mut u16, data: &u16) {
if labels[*data as usize].is_none() {
eprintln!("{}", RunError::UnknownLabel(*data));
std::process::exit(2);
}
if memory.last() != Some(&0) {
*program_counter = (labels[*data as usize].unwrap() - 1) as u16;
}
}

12
src/instructions/kjz.rs Normal file
View File

@ -0,0 +1,12 @@
use crate::errors::RunError;
pub fn kjz(memory: &mut Vec<u16>, labels: &mut [Option<i16>; 256], program_counter: &mut u16, data: &u16) {
if labels[*data as usize].is_none() {
eprintln!("{}", RunError::UnknownLabel(*data));
std::process::exit(2);
}
if memory.last() == Some(&0) {
*program_counter = (labels[*data as usize].unwrap() - 1) as u16;
}
}

View File

@ -33,3 +33,7 @@ pub mod bor;
pub mod bxor;
pub mod bnand;
pub mod not;
pub mod kjnz;
pub mod kjz;
pub mod print;
pub mod insert;

View File

@ -0,0 +1,3 @@
pub fn print(arg: &String) {
println!("{}", arg);
}

View File

@ -13,7 +13,9 @@ pub fn parse(stack: &mut Stack, file_content: &str) {
inst_line = line.trim().chars().collect();
}
let command: Vec<String> = inst_line.split_whitespace().map(String::from).collect();
let command: [String; 2] = inst_line.split_once(char::is_whitespace)
.map_or_else(|| [inst_line.to_string(), "".to_string()],
|(first, second)| [first.to_string(), second.to_string()]);
let name: String;
let mut arg: String = String::new();
@ -21,16 +23,19 @@ pub fn parse(stack: &mut Stack, file_content: &str) {
name = command[0].clone();
if command.len() >= 2 {
match name.chars().nth(0) {
Some('#') => arg = command[1].to_string(),
_ => {
if command[1] == "-1" { // required for _UNIX_RANDOM instruction
data = 65535;
} else {
data = command[1].parse().expect(&format!("{}", ParseError::DataNotAUInt(command[0].to_string(), i + 1)))
}
}
if !command[1].is_empty() {
if name.chars().nth(0) == Some('#') || name.chars().nth(1) == Some('#') {
let splited_data: Vec<String> = command[1].split_whitespace().map(String::from).collect();
arg = splited_data.get(0).unwrap().clone();
} else if name.chars().nth(0) == Some('$') || name.chars().nth(1) == Some('$') {
arg = command[1].clone();
} else if command[1] == "-1" { // required for
data = 65535; // _unix_random
} else if let Ok(number) = command[1].parse() {
data = number;
} else {
eprintln!("{}", ParseError::ArgumentNotRequired(command[0].to_string(), i + 1, line.to_string()));
std::process::exit(2);
}
}