diff --git a/Cargo.lock b/Cargo.lock index 3e63598..b88f3be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,22 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + [[package]] name = "labast" -version = "1.0.2" +version = "1.0.3" +dependencies = [ + "fastrand", + "pico-args", +] + +[[package]] +name = "pico-args" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" diff --git a/Cargo.toml b/Cargo.toml index 63f1dba..b85db34 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "labast" -version = "1.0.2" +version = "1.0.3" authors = [ "n3tael" ] edition = "2021" description = "A zero-dependencies Labaski interpreter written in Rust." @@ -17,3 +17,5 @@ lto = true panic = "abort" [dependencies] +fastrand = "2.0.1" +pico-args = "0.5.0" diff --git a/README.md b/README.md index 0ef8dc7..f50517d 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.10. +A Labaski interpreter written in Rust. Fully supports Labashki specs 1.12. ## Examples See `scripts` directory. \ No newline at end of file diff --git a/npp_labaski.xml b/npp_labaski.xml new file mode 100644 index 0000000..1df2735 --- /dev/null +++ b/npp_labaski.xml @@ -0,0 +1,64 @@ + + + + + + + + 00; 01 02 03 04 + + + + + + + + ^ | + + + + + [ + + ] + + + + #insert + push pop dup swap pick add sub mul div mod putc getc scan meow args dump maxsize size quit exit _unix_random + #print $print #exec #expr + and or xor nand not band bor bxor bnand bnot + > $ : \ + - + jmp jnz jz kjnz kjz + @ + nop + 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/hello-world-old.lb b/scripts/hello-world-old.lb index 922a4ff..bad7307 100644 --- a/scripts/hello-world-old.lb +++ b/scripts/hello-world-old.lb @@ -27,4 +27,4 @@ putc push 105 ; i putc push 33 ; ! -putc \ No newline at end of file +putc \ No newline at end of file diff --git a/src/execute.rs b/src/execute.rs index 49f3813..79f9416 100644 --- a/src/execute.rs +++ b/src/execute.rs @@ -102,12 +102,14 @@ pub fn execute(stack: &mut Stack, mut origin_stack: Option<&mut Stack>, mod_name "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), + "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), + "#println" => instructions::println::println(&instruction.arg), + "$println" => instructions::println::println(&instruction.arg), // Modules "args" => instructions::args::args(&mut stack.memory, &mut origin_stack, &instruction.data), @@ -126,18 +128,21 @@ pub fn execute(stack: &mut Stack, mut origin_stack: Option<&mut Stack>, mod_name }, // Special - "dump" => instructions::dump::dump(&mut stack.memory), - "size" => instructions::size::size(&mut stack.memory), + "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), + "#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), + // Labast-specific + "random" => instructions::random::random(&mut stack.memory), + _ => { eprintln!("Unknown instruction: {}", instruction.name); std::process::exit(2); diff --git a/src/instructions/_unix_random.rs b/src/instructions/_unix_random.rs index 79258c1..75fa8b0 100644 --- a/src/instructions/_unix_random.rs +++ b/src/instructions/_unix_random.rs @@ -6,9 +6,9 @@ use crate::RunError; pub fn _unix_random(memory: &mut Vec) { let mut rng = File::open("/dev/urandom").unwrap(); - let mut buffer = [0u8; 1]; + let mut buffer = [0u8; 2]; rng.read_exact(&mut buffer).unwrap(); let data = &memory.pop().expect(&format!("{}", RunError::MemoryEmpty)); - memory.push((buffer[0] as u16) % (*data - 1)); + memory.push((u16::from_ne_bytes(buffer)) % (*data - 1)); } \ No newline at end of file diff --git a/src/instructions/maxsize.rs b/src/instructions/maxsize.rs index 5d7769d..4a3f1b7 100644 --- a/src/instructions/maxsize.rs +++ b/src/instructions/maxsize.rs @@ -1,3 +1,3 @@ pub fn maxsize(memory: &mut Vec) { - memory.push(65535); + memory.push(u16::MAX); } \ No newline at end of file diff --git a/src/instructions/mod.rs b/src/instructions/mod.rs index 70ea4c9..516a864 100644 --- a/src/instructions/mod.rs +++ b/src/instructions/mod.rs @@ -36,4 +36,6 @@ pub mod not; pub mod kjnz; pub mod kjz; pub mod print; -pub mod insert; \ No newline at end of file +pub mod println; +pub mod insert; +pub mod random; \ No newline at end of file diff --git a/src/instructions/print.rs b/src/instructions/print.rs index 86e8e56..679d7ca 100644 --- a/src/instructions/print.rs +++ b/src/instructions/print.rs @@ -1,3 +1,3 @@ pub fn print(arg: &String) { - println!("{}", arg); + print!("{}", arg); } \ No newline at end of file diff --git a/src/instructions/println.rs b/src/instructions/println.rs new file mode 100644 index 0000000..927f406 --- /dev/null +++ b/src/instructions/println.rs @@ -0,0 +1,3 @@ +pub fn println(arg: &String) { + println!("{}", arg); +} \ No newline at end of file diff --git a/src/instructions/random.rs b/src/instructions/random.rs new file mode 100644 index 0000000..a63b609 --- /dev/null +++ b/src/instructions/random.rs @@ -0,0 +1,7 @@ +use fastrand; +use crate::RunError; + +pub fn random(memory: &mut Vec) { + let data = &memory.pop().expect(&format!("{}", RunError::MemoryEmpty)); + memory.push(fastrand::u16(..) % (*data - 1)); +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 6d1850c..edf6a4d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ -use std::{env, fs, io::{BufRead, Write}}; +use std::{fs, io::{BufRead, Write}}; + use errors::RunError; use execute::execute; use parse::parse; @@ -10,17 +11,28 @@ mod execute; mod instructions; mod errors; +const HELP: &str = "\ +labast - Labaski interpreter written in Rust. + +usage: labast [options] [ file.lb ] + +options: + -h, --help prints help information + -v, --version prints version +"; + fn main() { - let args: Vec = env::args().collect(); + let args = match parse_args() { + Ok(v) => v, + Err(e) => { + eprintln!("Error: {}.", e); + std::process::exit(1); + } + }; - if args.get(2) == Some(&String::from("-h")) - || args.get(2) == Some(&String::from("--help")) { - eprintln!("Usage: {0} [file]", args[0]); - return; - } + let mut stack = Stack::new(); - if args.len() < 2 { - let mut stack = Stack::new(); + if args.input.is_none() { let mut line = String::new(); loop { @@ -37,12 +49,41 @@ fn main() { parse(&mut stack, &line.trim()); execute(&mut stack, None, &"inline".to_string()); } + } else { + let input = &args.input.expect("error: no input file"); + let lines = fs::read_to_string(&input).expect(&format!("{}", RunError::FailToReadFile)); + + parse(&mut stack, &lines); + execute(&mut stack, None, &String::from(input.file_name().expect("Failed to read file name.").to_str().expect("Failed to convert file name to string"))); + } +} + +#[derive(Debug)] +struct AppArgs { + input: Option, +} + +fn parse_args() -> Result { + let mut pargs = pico_args::Arguments::from_env(); + + if pargs.contains(["-h", "--help"]) { + print!("{}", HELP); + std::process::exit(0); } - let mut stack = Stack::new(); - let lines = fs::read_to_string(&args[1]).expect(&format!("{}", RunError::FailToReadFile)); + if pargs.contains(["-v", "--version"]) { + println!("labast version {}", option_env!("CARGO_PKG_VERSION").unwrap_or("unknown")); + std::process::exit(0); + } - parse(&mut stack, &lines); + let args = AppArgs { + input: pargs.opt_free_from_str()?, + }; - execute(&mut stack, None, &args[1]); + let remaining = pargs.finish(); + if !remaining.is_empty() { + eprintln!("Warning: unused arguments left: {:?}.", remaining); + } + + return Ok(args); } \ No newline at end of file