forked from n3tael/labast
[UNVERIFIED] update to spec 1.10, parser rewrite.
This commit is contained in:
parent
cff855b9cb
commit
f2b551a586
@ -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
26
scripts/calc.lb
Normal 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
|
30
scripts/hello-world-old.lb
Normal file
30
scripts/hello-world-old.lb
Normal 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
|
@ -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!
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
26
src/instructions/insert.rs
Normal file
26
src/instructions/insert.rs
Normal 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
|
||||
});
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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
12
src/instructions/kjnz.rs
Normal 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
12
src/instructions/kjz.rs
Normal 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;
|
||||
}
|
||||
}
|
@ -32,4 +32,8 @@ pub mod band;
|
||||
pub mod bor;
|
||||
pub mod bxor;
|
||||
pub mod bnand;
|
||||
pub mod not;
|
||||
pub mod not;
|
||||
pub mod kjnz;
|
||||
pub mod kjz;
|
||||
pub mod print;
|
||||
pub mod insert;
|
3
src/instructions/print.rs
Normal file
3
src/instructions/print.rs
Normal file
@ -0,0 +1,3 @@
|
||||
pub fn print(arg: &String) {
|
||||
println!("{}", arg);
|
||||
}
|
27
src/parse.rs
27
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<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);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user