diff --git a/CHANGELOG.md b/CHANGELOG.md index 30f211c..095487c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.13.0] - 2023-12-10 + +### Added + +- Function creation support (no arguments). +- Unknown names now raise errors. + +### Changed + +- (**dev notes**): Migrated to `clap` API. + ## [0.12.0] - 2023-10-30 ### Added @@ -155,7 +166,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Simple function and string parsing -[unreleased]: https://github.com/ArnabRollin/dwn/compare/v0.12.0...HEAD +[unreleased]: https://github.com/ArnabRollin/dwn/compare/v0.13.0...HEAD + +[0.13.0]: https://github.com/ArnabRollin/dwn/compare/v0.12.0...v0.13.0 [0.12.0]: https://github.com/ArnabRollin/dwn/compare/v0.11.0...v0.12.0 diff --git a/Cargo.lock b/Cargo.lock index 4790f51..0b19487 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,15 +2,229 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "anstream" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "clap" +version = "4.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + [[package]] name = "dwn" -version = "0.12.0" +version = "0.13.0" dependencies = [ + "clap", "lazy_static", ] +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "proc-macro2" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "2.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" diff --git a/Cargo.toml b/Cargo.toml index c68f61e..e881c62 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,10 @@ [package] name = "dwn" -version = "0.12.0" +version = "0.13.0" edition = "2021" [dependencies] +clap = { version = "4.4.11", features = ["derive"] } lazy_static = "1.4.0" [target.x86_64-unknown-linux-gnu] diff --git a/src/argparser.rs b/src/argparser.rs deleted file mode 100644 index ee36c78..0000000 --- a/src/argparser.rs +++ /dev/null @@ -1,153 +0,0 @@ -//! Argument parser for Dawn (dwn). - -use std::{collections::HashMap, env::Args}; - -/// Parses the arguments. -/// -/// Examples: -/// -/// ```rust -/// let mut args = std::env::args(); -/// args.next(); -/// let args = argparse(args); -/// -/// assert_eq!(args.options, vec![String::from("dog")]) -/// assert_eq!(args.flags, vec![String::from("f")]) -/// assert_eq!(args.arguments, vec![String::from("dig")]) -/// assert_eq!(args.command, String::from("lawn")) -/// ``` -/// ```console -/// % cargo run -- --dog -f lawn dig -/// ``` - -pub fn argparse(mut args: Args) -> Arguments { - let mut options: Vec = vec![]; - let mut flags: Vec = vec![]; - let mut arguments: Vec = vec![]; - let mut variables: HashMap = HashMap::new(); - - while let Some(arg) = args.next() { - if arg.starts_with("--") { - let varsplit: Vec<&str> = arg.split('=').collect(); - - if varsplit[..varsplit.len() - 1].is_empty() { - options.extend( - arg.trim_start_matches("--") - .split('-') - .map(|s| s.to_string()), - ); - } else { - variables.insert( - varsplit[0].trim_start_matches("--").to_string(), - varsplit[1].to_string(), - ); - } - } else if arg.starts_with("-") { - flags.extend(arg.chars().skip(1).map(|c| c.to_string())); - } else { - arguments.push(arg); - } - } - - let command = if arguments.len() > 0 { - arguments.remove(0) - } else { - String::new() - }; - - Arguments { - options, - flags, - command, - arguments, - variables, - } -} - -/// Arguments Struct. -/// -/// Examples: -/// ```rust -/// Arguments { -/// options: vec![String::from("dog")], -/// flags: vec![String::from("f")], -/// command: String::from("lawn"), -/// arguments: vec![String::from("dig")], -/// variables: vec![], -/// } -/// ``` -pub struct Arguments { - pub options: Vec, - pub flags: Vec, - pub command: String, - pub arguments: Vec, - pub variables: HashMap, -} - -#[test] -fn argument_parser() { - fn argparse(args: Vec) -> Arguments { - let mut options: Vec = vec![]; - let mut flags: Vec = vec![]; - let mut arguments: Vec = vec![]; - let mut variables: HashMap = HashMap::new(); - - let mut args = args.iter(); - - while let Some(arg) = args.next() { - if arg.starts_with("--") { - let varsplit: Vec<&str> = arg.split('=').collect(); - - if varsplit[..varsplit.len() - 1].is_empty() { - options.extend( - arg.trim_start_matches("--") - .split('-') - .map(|s| s.to_string()), - ); - } else { - variables.insert( - varsplit[0].trim_start_matches("--").to_string(), - varsplit[1].to_string(), - ); - } - } else if arg.starts_with("-") { - flags.extend(arg.chars().skip(1).map(|c| c.to_string())); - } else { - arguments.push(arg.to_string()); - } - } - - let command = if arguments.len() > 0 { - arguments.remove(0) - } else { - String::new() - }; - - Arguments { - options, - flags, - command, - arguments, - variables, - } - } - let args = argparse(vec![ - "klein".to_string(), - "--c-c-c".to_string(), - "-dd".to_string(), - "cey".to_string(), - "--level=1".to_string(), - ]); - - let mut variables_assert = HashMap::new(); - variables_assert.insert("level".to_string(), "1".to_string()); - - assert_eq!(args.command, "klein".to_string()); - assert_eq!(args.arguments, vec!["cey".to_string()]); - assert_eq!( - args.options, - vec!["c".to_string(), "c".to_string(), "c".to_string()] - ); - assert_eq!(args.flags, vec!["d".to_string(), "d".to_string()]); - assert_eq!(args.variables, variables_assert); -} diff --git a/src/bytecode.rs b/src/bytecode.rs index 86296ec..781875e 100644 --- a/src/bytecode.rs +++ b/src/bytecode.rs @@ -29,34 +29,13 @@ lazy_static! { } /// The function used to bytecode compile files. -pub fn bytecode_compile_file(file: Option<&String>, level: Option<&String>) { - if file.is_none() { - eprintln!("Error: Please provide a file to compile!"); - exit(1); - } - - let file = file.unwrap(); - +pub fn bytecode_compile_file(file: String, level: i32) { let max_level = 1; - let level = if level.is_none() { - max_level - } else { - let level = level.unwrap(); - if level.to_lowercase() == "latest".to_string() { - max_level - } else { - match level.parse::() { - Ok(level) => level, - Err(_) => { - eprintln!("Error: Level must be a number or 'latest' !"); - exit(1); - } - } - } - }; + + let level = if level < 0 { max_level } else { level }; let reader = - BufReader::new(File::open(file).expect(format!("Cannot open file `{}`", file).as_str())); + BufReader::new(File::open(&file).expect(format!("Cannot open file `{}`", file).as_str())); match level { 1 => bytec_lvl1(reader, file), @@ -67,7 +46,7 @@ pub fn bytecode_compile_file(file: Option<&String>, level: Option<&String>) { } } -fn bytec_lvl1(reader: BufReader, file: &String) { +fn bytec_lvl1(reader: BufReader, file: String) { let mut scope = 0; let mut in_scope = false; let mut scope_token = String::new(); @@ -179,16 +158,9 @@ fn bytec_lvl1(reader: BufReader, file: &String) { } } -pub fn bytecode_run(bytecode_file: Option<&String>) { - if bytecode_file.is_none() { - eprintln!("Error: Please provide a file to compile!"); - exit(1); - } - - let bytecode_file = bytecode_file.unwrap(); - +pub fn bytecode_run(bytecode_file: String) { let mut reader = BufReader::new( - File::open(bytecode_file).expect(format!("Cannot open file `{}`", bytecode_file).as_str()), + File::open(&bytecode_file).expect(format!("Cannot open file `{}`", bytecode_file).as_str()), ); let mut level = String::new(); diff --git a/src/interpreter.rs b/src/interpreter.rs index 02772d9..cc56f52 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -2,23 +2,14 @@ use std::fs::File; use std::io::{BufRead, BufReader}; -use std::process::exit; use crate::dwn::{get_funcs, Metadata}; use crate::runner::run; /// The function used to interpret files. -pub fn interpret_file(file: Option<&String>) { - let file_default = &String::new(); - let file = file.unwrap_or(file_default); - - if file == file_default { - eprintln!("Error: Please provide a file to run!"); - exit(1); - } - +pub fn interpret_file(file: String) { let reader = - BufReader::new(File::open(file).expect(format!("Cannot open file `{}`", file).as_str())); + BufReader::new(File::open(&file).expect(format!("Cannot open file `{}`", file).as_str())); let mut scope = 0; let mut in_scope = false; diff --git a/src/main.rs b/src/main.rs index b61da09..b92ad8b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,16 +2,12 @@ #[macro_use] extern crate lazy_static; -use about::{about, help}; -use argparser::argparse; use bytecode::{bytecode_compile_file, bytecode_run}; +use clap::{Parser, Subcommand}; use framework::make_framework; use idle::idle; use interpreter::interpret_file; -use std::{env::args, process::exit}; -mod about; -mod argparser; mod bytecode; mod dwn; mod framework; @@ -20,45 +16,38 @@ mod interpreter; mod lexer; mod runner; -fn main() { - let mut args = args(); - - // Skip program name - args.next(); - - let arguments = argparse(args); - - if arguments.options.is_empty() - && arguments.flags.is_empty() - && arguments.arguments.is_empty() - && (arguments.command == String::new()) - { - about(); - exit(1) - } - - if arguments.options.contains(&"help".to_string()) || arguments.flags.contains(&"h".to_string()) - { - about(); - exit(0); - } +/// Dawn (`dwn`) is the interpreter and bytecode compiler for the Dawn Programming Language. +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +struct Args { + #[command(subcommand)] + command: Commands, +} - if arguments.options.contains(&"version".to_string()) - || arguments.flags.contains(&"v".to_string()) - { - println!("0.12.0"); - exit(0); - } +#[derive(Subcommand, Debug)] +enum Commands { + /// Runs a Dawn project file. + #[command(alias = "r")] + Run { file: String }, + /// Bytecode compiles a Dawn project file. + Bytec { file: String, level: Option }, + /// Runs a Dawn bytecode file. + Byterun { file: String }, + /// Starts the Integrated Development and Learning Environment (IDLE) + Idle, + /// Creates a framework for Dawn Programming Language extensions. + #[command(alias = "fw")] + Framework, +} - match arguments.command.as_str() { - "help" => help(arguments.arguments.get(0)), - "run" | "r" => interpret_file(arguments.arguments.get(0)), - "bytec" | "bytecodec" | "bytc" => { - bytecode_compile_file(arguments.arguments.get(0), arguments.variables.get("level")) - } - "byterun" | "bytecoderun" | "bytrun" => bytecode_run(arguments.arguments.get(0)), - "idle" => idle(), - "framework" | "fw" => make_framework(), - unknown_command => eprintln!("Unknown command: {}", unknown_command), +fn main() { + let args = Args::parse(); + + match args.command { + Commands::Run { file } => interpret_file(file), + Commands::Bytec { file, level } => bytecode_compile_file(file, level.unwrap_or(-1)), + Commands::Byterun { file } => bytecode_run(file), + Commands::Idle => idle(), + Commands::Framework => make_framework(), } }