From f2b551a586bb07132eddb75211c3f0126a020705 Mon Sep 17 00:00:00 2001 From: n3taeli Date: Tue, 6 Feb 2024 01:48:44 +0200 Subject: [PATCH] [UNVERIFIED] update to spec 1.10, parser rewrite. --- README.md | 2 +- scripts/calc.lb | 26 ++++++++++++++++ scripts/hello-world-old.lb | 30 ++++++++++++++++++ scripts/hello-world.lb | 31 +------------------ src/errors.rs | 10 ++++-- src/execute.rs | 63 ++++++++++++++++++++++++-------------- src/instructions/insert.rs | 26 ++++++++++++++++ src/instructions/jnz.rs | 2 +- src/instructions/jz.rs | 2 +- src/instructions/kjnz.rs | 12 ++++++++ src/instructions/kjz.rs | 12 ++++++++ src/instructions/mod.rs | 6 +++- src/instructions/print.rs | 3 ++ src/parse.rs | 27 +++++++++------- 14 files changed, 182 insertions(+), 70 deletions(-) create mode 100644 scripts/calc.lb create mode 100644 scripts/hello-world-old.lb create mode 100644 src/instructions/insert.rs create mode 100644 src/instructions/kjnz.rs create mode 100644 src/instructions/kjz.rs create mode 100644 src/instructions/print.rs diff --git a/README.md b/README.md index 09e5688..0ef8dc7 100644 --- a/README.md +++ b/README.md @@ -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. \ No newline at end of file diff --git a/scripts/calc.lb b/scripts/calc.lb new file mode 100644 index 0000000..be58127 --- /dev/null +++ b/scripts/calc.lb @@ -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 \ No newline at end of file diff --git a/scripts/hello-world-old.lb b/scripts/hello-world-old.lb new file mode 100644 index 0000000..922a4ff --- /dev/null +++ b/scripts/hello-world-old.lb @@ -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 ; +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 \ No newline at end of file diff --git a/scripts/hello-world.lb b/scripts/hello-world.lb index 922a4ff..0ffed19 100644 --- a/scripts/hello-world.lb +++ b/scripts/hello-world.lb @@ -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 ; -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 \ No newline at end of file +$print Hello Labashki! \ No newline at end of file diff --git a/src/errors.rs b/src/errors.rs index 12d58de..4209d4f 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -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), } } } \ No newline at end of file diff --git a/src/execute.rs b/src/execute.rs index 9b22fae..49f3813 100644 --- a/src/execute.rs +++ b/src/execute.rs @@ -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); diff --git a/src/instructions/insert.rs b/src/instructions/insert.rs new file mode 100644 index 0000000..c5b5367 --- /dev/null +++ b/src/instructions/insert.rs @@ -0,0 +1,26 @@ +use crate::{errors::RunError, stack::Instruction}; + +pub fn insert(program: &mut Vec, arg: &String, data: &u16) { + let args: Vec = 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 + }); +} \ No newline at end of file diff --git a/src/instructions/jnz.rs b/src/instructions/jnz.rs index 4216cd4..2093234 100644 --- a/src/instructions/jnz.rs +++ b/src/instructions/jnz.rs @@ -6,7 +6,7 @@ pub fn jnz(memory: &mut Vec, labels: &mut [Option; 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; } } \ No newline at end of file diff --git a/src/instructions/jz.rs b/src/instructions/jz.rs index cc2fcba..f85838d 100644 --- a/src/instructions/jz.rs +++ b/src/instructions/jz.rs @@ -6,7 +6,7 @@ pub fn jz(memory: &mut Vec, labels: &mut [Option; 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; } } \ No newline at end of file diff --git a/src/instructions/kjnz.rs b/src/instructions/kjnz.rs new file mode 100644 index 0000000..e5aec9e --- /dev/null +++ b/src/instructions/kjnz.rs @@ -0,0 +1,12 @@ +use crate::errors::RunError; + +pub fn kjnz(memory: &mut Vec, labels: &mut [Option; 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; + } +} \ No newline at end of file diff --git a/src/instructions/kjz.rs b/src/instructions/kjz.rs new file mode 100644 index 0000000..94329b5 --- /dev/null +++ b/src/instructions/kjz.rs @@ -0,0 +1,12 @@ +use crate::errors::RunError; + +pub fn kjz(memory: &mut Vec, labels: &mut [Option; 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; + } +} \ No newline at end of file diff --git a/src/instructions/mod.rs b/src/instructions/mod.rs index 2239540..70ea4c9 100644 --- a/src/instructions/mod.rs +++ b/src/instructions/mod.rs @@ -32,4 +32,8 @@ pub mod band; pub mod bor; pub mod bxor; pub mod bnand; -pub mod not; \ No newline at end of file +pub mod not; +pub mod kjnz; +pub mod kjz; +pub mod print; +pub mod insert; \ No newline at end of file diff --git a/src/instructions/print.rs b/src/instructions/print.rs new file mode 100644 index 0000000..86e8e56 --- /dev/null +++ b/src/instructions/print.rs @@ -0,0 +1,3 @@ +pub fn print(arg: &String) { + println!("{}", arg); +} \ No newline at end of file diff --git a/src/parse.rs b/src/parse.rs index 96dc0e0..bde2e41 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -13,7 +13,9 @@ pub fn parse(stack: &mut Stack, file_content: &str) { inst_line = line.trim().chars().collect(); } - let command: Vec = 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 = 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); } }