From 9118490733e4cb08e8c84641f65a5bc4587608b8 Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Sun, 24 Jul 2022 20:01:08 +0200 Subject: [PATCH 01/30] Start a rewrite in Rust --- the-golden/.gitignore | 14 +++++ the-golden/Cargo.toml | 8 +++ the-golden/maumivu.au | 1 + the-golden/src/interpreter/interpreter.rs | 19 +++++++ .../src/interpreter/versions/handler.rs | 26 ++++++++++ .../versions/v0-1-0/brackets_matcher.rs | 52 +++++++++++++++++++ .../src/interpreter/versions/v0-1-0/main.rs | 21 ++++++++ the-golden/src/main.rs | 31 +++++++++++ 8 files changed, 172 insertions(+) create mode 100644 the-golden/.gitignore create mode 100644 the-golden/Cargo.toml create mode 100644 the-golden/maumivu.au create mode 100644 the-golden/src/interpreter/interpreter.rs create mode 100644 the-golden/src/interpreter/versions/handler.rs create mode 100644 the-golden/src/interpreter/versions/v0-1-0/brackets_matcher.rs create mode 100644 the-golden/src/interpreter/versions/v0-1-0/main.rs create mode 100644 the-golden/src/main.rs diff --git a/the-golden/.gitignore b/the-golden/.gitignore new file mode 100644 index 0000000..6985cf1 --- /dev/null +++ b/the-golden/.gitignore @@ -0,0 +1,14 @@ +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb diff --git a/the-golden/Cargo.toml b/the-golden/Cargo.toml new file mode 100644 index 0000000..295c601 --- /dev/null +++ b/the-golden/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "the-golden" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/the-golden/maumivu.au b/the-golden/maumivu.au new file mode 100644 index 0000000..5c1e82e --- /dev/null +++ b/the-golden/maumivu.au @@ -0,0 +1 @@ +++---+: \ No newline at end of file diff --git a/the-golden/src/interpreter/interpreter.rs b/the-golden/src/interpreter/interpreter.rs new file mode 100644 index 0000000..f008abe --- /dev/null +++ b/the-golden/src/interpreter/interpreter.rs @@ -0,0 +1,19 @@ +#[path = "./versions/handler.rs"] mod versions_handler; + +pub struct Interpreter { + version: String, + versions_handler: versions_handler::Handler, + code: String +} + +impl Interpreter { + pub fn new(mut version: String, code: String) -> Self { + let versions_handler = versions_handler::Handler::new(); + version = versions_handler.parse_version(version); + Self { code, version, versions_handler } + } + + pub fn run(&self) { + self.versions_handler.run(self.version.clone(), self.code.clone()); + } +} \ No newline at end of file diff --git a/the-golden/src/interpreter/versions/handler.rs b/the-golden/src/interpreter/versions/handler.rs new file mode 100644 index 0000000..9745600 --- /dev/null +++ b/the-golden/src/interpreter/versions/handler.rs @@ -0,0 +1,26 @@ +#[path = "./v0-1-0/main.rs"] mod v0_1_0; + +pub struct Handler { + versions: [String; 1] +} + +impl Handler { + pub fn new() -> Self { + Self { versions: [String::from("0.1.0")] } + } + + pub fn parse_version(&self, mut version: String) -> String { + version = version.to_lowercase(); + if version == *"latest" || !self.versions.contains(&version) { + version = self.versions.last().unwrap().to_string(); + } + version + } + + pub fn run(&self, version: String, code: String) { + match version.as_str() { + "0.1.0" => v0_1_0::Runner::new(code).run(), + _ => panic!("Couldn't launch version {}", &version) + } + } +} \ No newline at end of file diff --git a/the-golden/src/interpreter/versions/v0-1-0/brackets_matcher.rs b/the-golden/src/interpreter/versions/v0-1-0/brackets_matcher.rs new file mode 100644 index 0000000..ae71bfc --- /dev/null +++ b/the-golden/src/interpreter/versions/v0-1-0/brackets_matcher.rs @@ -0,0 +1,52 @@ +use std::collections::HashMap; + +pub struct BracketsMatcher { + pub brackets: HashMap>, + brackets_mem: Vec<(String, usize, usize)>, + bracket_keys: HashMap, + ending_brackets_keys: HashMap +} + +impl BracketsMatcher { + pub fn new() -> Self { + Self { + brackets: HashMap::from([ + ("while".to_string(), HashMap::new()), + ("do_while".to_string(), HashMap::new()), + ("while_local".to_string(), HashMap::new()), + ("do_while_local".to_string(), HashMap::new()) + ]), + brackets_mem: vec![], + bracket_keys: HashMap::from([ + ("[".to_string(), "while".to_string()), + ("]".to_string(), "while".to_string()), + ("[@".to_string(), "do_while".to_string()), + ("@]".to_string(), "do_while".to_string()), + ("'[".to_string(), "while_local".to_string()), + ("']".to_string(), "while_local".to_string()), + ("'[@".to_string(), "do_while_local".to_string()), + ("'@]".to_string(), "do_while_local".to_string()), + ]), + ending_brackets_keys: HashMap::from([ + ("]".to_string(), "while".to_string()), + ("@]".to_string(), "do_while".to_string()), + ("']".to_string(), "while_local".to_string()), + ("'@]".to_string(), "do_while_local".to_string()), + ]) + } + } + + pub fn match_brackets(&self, code: Vec) { + + } + + fn num_equals(&self, left: String, right: String) -> i8 { + if self.bracket_keys.get(&left) != self.bracket_keys.get(&right) { + return 0; + } + if left == right { + return 1; + } + return -1; + } +} \ No newline at end of file diff --git a/the-golden/src/interpreter/versions/v0-1-0/main.rs b/the-golden/src/interpreter/versions/v0-1-0/main.rs new file mode 100644 index 0000000..4232e61 --- /dev/null +++ b/the-golden/src/interpreter/versions/v0-1-0/main.rs @@ -0,0 +1,21 @@ +#[path = "./brackets_matcher.rs"] mod brackets_matcher; +use brackets_matcher::BracketsMatcher; + +pub struct Runner { + brackets_matcher: BracketsMatcher, + raw_code: String +} + +impl Runner { + pub fn new(raw_code: String) -> Self { + Self { + brackets_matcher: BracketsMatcher::new(), + raw_code + } + } + + pub fn run(&self) { + println!("Running version 0.1.0"); + println!("Raw code: {}", self.raw_code); + } +} \ No newline at end of file diff --git a/the-golden/src/main.rs b/the-golden/src/main.rs new file mode 100644 index 0000000..303a810 --- /dev/null +++ b/the-golden/src/main.rs @@ -0,0 +1,31 @@ +#[path = "./interpreter/interpreter.rs"] mod interpreter; +use interpreter::Interpreter; + +fn main() { + let args: Vec = std::env::args().collect(); + let actions = [String::from("run")]; + let mut action = String::new(); + let mut version = String::from("latest"); + for arg in &args { + if arg.starts_with("--version:") { + version = arg.split_once("--version:").unwrap().1.to_string(); + } else if actions.contains(&arg.to_lowercase()) { + action = arg.to_lowercase(); + } + } + if action == *"run" { + let mut path = std::path::PathBuf::from(args[0].clone()); + path.pop(); + if args.len() > 2 { + path.push(args[2].clone()); + } + path.push("maumivu.au"); + let code = match std::fs::read_to_string(path) { + Ok(c) => c, + Err(e) => panic!("{}", e) + }; + Interpreter::new(version, code).run(); + } else { + todo!() + } +} From b21f5b414127cfca1ca235da221b9b014ae5d1c7 Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Tue, 26 Jul 2022 07:20:47 +0200 Subject: [PATCH 02/30] Add a lexer and a syntax validator --- the-golden/Cargo.toml | 8 +++ the-golden/maumivu.au | 2 +- the-golden/src/interpreter/interpreter.rs | 9 ++-- .../src/interpreter/versions/handler.rs | 4 +- .../versions/v0-1-0/brackets_matcher.rs | 2 +- .../src/interpreter/versions/v0-1-0/lexer.rs | 53 +++++++++++++++++++ .../src/interpreter/versions/v0-1-0/main.rs | 31 +++++++++-- .../interpreter/versions/v0-1-0/validator.rs | 23 ++++++++ the-golden/src/main.rs | 27 ++++++++-- 9 files changed, 145 insertions(+), 14 deletions(-) create mode 100644 the-golden/src/interpreter/versions/v0-1-0/lexer.rs create mode 100644 the-golden/src/interpreter/versions/v0-1-0/validator.rs diff --git a/the-golden/Cargo.toml b/the-golden/Cargo.toml index 295c601..b85f41a 100644 --- a/the-golden/Cargo.toml +++ b/the-golden/Cargo.toml @@ -6,3 +6,11 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +dotenv = "0.15.0" +lazy_static = "1.4.0" +regex = "1.6.0" +tracing = "0.1.35" +tracing-subscriber = "0.3.15" + +[target.'cfg(windows)'.dependencies] +winconsole = { version = "0.11.1", features = ["window"] } diff --git a/the-golden/maumivu.au b/the-golden/maumivu.au index 5c1e82e..5aeac87 100644 --- a/the-golden/maumivu.au +++ b/the-golden/maumivu.au @@ -1 +1 @@ -++---+: \ No newline at end of file +!!"a": \ No newline at end of file diff --git a/the-golden/src/interpreter/interpreter.rs b/the-golden/src/interpreter/interpreter.rs index f008abe..7394a28 100644 --- a/the-golden/src/interpreter/interpreter.rs +++ b/the-golden/src/interpreter/interpreter.rs @@ -3,17 +3,18 @@ pub struct Interpreter { version: String, versions_handler: versions_handler::Handler, - code: String + code: String, + code_path: std::path::PathBuf } impl Interpreter { - pub fn new(mut version: String, code: String) -> Self { + pub fn new(mut version: String, code: String, code_path: std::path::PathBuf) -> Self { let versions_handler = versions_handler::Handler::new(); version = versions_handler.parse_version(version); - Self { code, version, versions_handler } + Self { code, version, versions_handler, code_path } } pub fn run(&self) { - self.versions_handler.run(self.version.clone(), self.code.clone()); + self.versions_handler.run(self.version.clone(), self.code.clone(), self.code_path.clone()); } } \ No newline at end of file diff --git a/the-golden/src/interpreter/versions/handler.rs b/the-golden/src/interpreter/versions/handler.rs index 9745600..d48e80b 100644 --- a/the-golden/src/interpreter/versions/handler.rs +++ b/the-golden/src/interpreter/versions/handler.rs @@ -17,9 +17,9 @@ impl Handler { version } - pub fn run(&self, version: String, code: String) { + pub fn run(&self, version: String, code: String, code_path: std::path::PathBuf) { match version.as_str() { - "0.1.0" => v0_1_0::Runner::new(code).run(), + "0.1.0" => v0_1_0::Runner::new(code, code_path).run(), _ => panic!("Couldn't launch version {}", &version) } } diff --git a/the-golden/src/interpreter/versions/v0-1-0/brackets_matcher.rs b/the-golden/src/interpreter/versions/v0-1-0/brackets_matcher.rs index ae71bfc..b57de47 100644 --- a/the-golden/src/interpreter/versions/v0-1-0/brackets_matcher.rs +++ b/the-golden/src/interpreter/versions/v0-1-0/brackets_matcher.rs @@ -47,6 +47,6 @@ impl BracketsMatcher { if left == right { return 1; } - return -1; + -1 } } \ No newline at end of file diff --git a/the-golden/src/interpreter/versions/v0-1-0/lexer.rs b/the-golden/src/interpreter/versions/v0-1-0/lexer.rs new file mode 100644 index 0000000..db37473 --- /dev/null +++ b/the-golden/src/interpreter/versions/v0-1-0/lexer.rs @@ -0,0 +1,53 @@ +use lazy_static::lazy_static; +use regex::Regex; + +lazy_static! { + static ref COMMENT_REGEX: Regex = Regex::new("^\"").unwrap(); + static ref NEW_LINE_REGEX: Regex = Regex::new(r"^\r?\n").unwrap(); +} + +pub struct Lexer { + text: String, + rules: Vec, + line: usize, + column: usize, + comment: bool, + file_path: std::path::PathBuf, + position: usize +} + +impl Lexer { + pub fn new(text: String, rules: Vec, file_path: std::path::PathBuf) -> Self { + Self { text, rules, line: 1, column: 1, comment: false, file_path, position: 0 } + } + + pub fn next(&mut self) -> Result, String> { + let text = &self.text.as_str()[self.position..]; + if text.len() < 1 { + return Ok(None); + } + if text == "\"" { + self.comment = !self.comment; + } + if self.comment { + return Ok(None); + } + for rule in &self.rules { + if let Some(captures) = rule.captures(text) { + if let Some(capture) = captures.get(0) { + let command = capture.as_str(); + let command_length = capture.end() - capture.start(); + self.position += command_length; + if command.contains("\n") { + self.line += command.matches("\n").count(); + self.column += command.split("\n").last().unwrap().len(); + } else { + self.column += command_length; + } + return Ok(Some((command.to_string(), self.line, self.column, self.file_path.clone()))); + } + } + } + Err(format!("Syntax error at {}:{} in {:?} ({:?})", self.line, self.column, self.file_path.file_name().unwrap(), self.file_path.as_path())) + } +} \ No newline at end of file diff --git a/the-golden/src/interpreter/versions/v0-1-0/main.rs b/the-golden/src/interpreter/versions/v0-1-0/main.rs index 4232e61..c301e12 100644 --- a/the-golden/src/interpreter/versions/v0-1-0/main.rs +++ b/the-golden/src/interpreter/versions/v0-1-0/main.rs @@ -1,21 +1,46 @@ +use regex::Regex; + #[path = "./brackets_matcher.rs"] mod brackets_matcher; use brackets_matcher::BracketsMatcher; +#[path = "./lexer.rs"] mod lexer; +pub use lexer::Lexer; +#[path = "./validator.rs"] mod validator; +use validator::Validator; pub struct Runner { brackets_matcher: BracketsMatcher, - raw_code: String + + raw_code: String, + rules: Vec, + code_path: std::path::PathBuf } impl Runner { - pub fn new(raw_code: String) -> Self { + pub fn new(raw_code: String, code_path: std::path::PathBuf) -> Self { + let rules = vec![ + Regex::new(r"^!").unwrap(), + Regex::new(r"^:\r?\n?").unwrap(), + Regex::new("\"[^\"]*\"").unwrap() + ]; Self { brackets_matcher: BracketsMatcher::new(), - raw_code + raw_code, + rules, + code_path } } pub fn run(&self) { println!("Running version 0.1.0"); println!("Raw code: {}", self.raw_code); + let validator_result = Validator::run(Lexer::new(self.raw_code.clone(), self.rules.clone(), self.code_path.clone())); + match validator_result { + Err(e) => { + println!("{}", e); + return; + }, + _ => {} + } + println!("Valid code!"); } } \ No newline at end of file diff --git a/the-golden/src/interpreter/versions/v0-1-0/validator.rs b/the-golden/src/interpreter/versions/v0-1-0/validator.rs new file mode 100644 index 0000000..6f85625 --- /dev/null +++ b/the-golden/src/interpreter/versions/v0-1-0/validator.rs @@ -0,0 +1,23 @@ +use regex::Regex; + +pub struct Validator {} + +impl Validator { + pub fn run(mut lexer: super::Lexer) -> Result { + let mut t = lexer.next(); + let mut p = t.clone(); + while t.is_ok() && t.clone().unwrap().is_some() { + p = t; + t = lexer.next(); + } + match t { + Err(e) => return Err(e), + _ => {} + } + let (command, line, column, file_path) = p.unwrap().unwrap(); + if !Regex::new(r":\n?\r?").unwrap().is_match(&command) { + return Err(format!("Syntax error at {}:{} in {:?} ({:?}) - ':' expected", line, column, file_path.file_name().unwrap(), file_path.as_path())); + } + return Ok(0); + } +} \ No newline at end of file diff --git a/the-golden/src/main.rs b/the-golden/src/main.rs index 303a810..5f36267 100644 --- a/the-golden/src/main.rs +++ b/the-golden/src/main.rs @@ -1,7 +1,28 @@ +use dotenv::dotenv; +use std::env; + #[path = "./interpreter/interpreter.rs"] mod interpreter; use interpreter::Interpreter; fn main() { + dotenv().ok(); + if env::var("RUST_LOG").is_err() { + env::set_var("RUST_LOG", "INFO"); + } + match env::var("LOGS") { + Ok(val) => { + if val.to_lowercase() != "on" && cfg!(target_os = "windows") { + winconsole::window::hide(); + } + } + Err(_) => { + if cfg!(target_os = "windows") { + winconsole::window::hide(); + } + } + } + tracing_subscriber::fmt::init(); + let args: Vec = std::env::args().collect(); let actions = [String::from("run")]; let mut action = String::new(); @@ -19,12 +40,12 @@ fn main() { if args.len() > 2 { path.push(args[2].clone()); } - path.push("maumivu.au"); - let code = match std::fs::read_to_string(path) { + path.set_file_name("maumivu.au"); + let code = match std::fs::read_to_string(&path) { Ok(c) => c, Err(e) => panic!("{}", e) }; - Interpreter::new(version, code).run(); + Interpreter::new(version, code, path).run(); } else { todo!() } From 81583fbd51331481605b2fd8c43a1cac318bc540 Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Tue, 26 Jul 2022 23:36:00 +0200 Subject: [PATCH 03/30] Add a brackets matcher This matches brackets together into pair, making it easy with loops to jump from the end to the start and the other way around --- the-golden/maumivu.au | 2 +- .../versions/v0-1-0/brackets_matcher.rs | 43 +++++++++++++++---- .../src/interpreter/versions/v0-1-0/lexer.rs | 12 +++--- .../src/interpreter/versions/v0-1-0/main.rs | 26 +++++++---- .../src/interpreter/versions/v0-1-0/parser.rs | 35 +++++++++++++++ .../interpreter/versions/v0-1-0/validator.rs | 7 ++- 6 files changed, 98 insertions(+), 27 deletions(-) create mode 100644 the-golden/src/interpreter/versions/v0-1-0/parser.rs diff --git a/the-golden/maumivu.au b/the-golden/maumivu.au index 5aeac87..b054e1f 100644 --- a/the-golden/maumivu.au +++ b/the-golden/maumivu.au @@ -1 +1 @@ -!!"a": \ No newline at end of file +!!"a"[!![@![!!!@]!]!!!!!]: \ No newline at end of file diff --git a/the-golden/src/interpreter/versions/v0-1-0/brackets_matcher.rs b/the-golden/src/interpreter/versions/v0-1-0/brackets_matcher.rs index b57de47..4519556 100644 --- a/the-golden/src/interpreter/versions/v0-1-0/brackets_matcher.rs +++ b/the-golden/src/interpreter/versions/v0-1-0/brackets_matcher.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; pub struct BracketsMatcher { pub brackets: HashMap>, - brackets_mem: Vec<(String, usize, usize)>, + brackets_mem: Vec<(String, usize, isize)>, bracket_keys: HashMap, ending_brackets_keys: HashMap } @@ -28,20 +28,45 @@ impl BracketsMatcher { ("'@]".to_string(), "do_while_local".to_string()), ]), ending_brackets_keys: HashMap::from([ - ("]".to_string(), "while".to_string()), - ("@]".to_string(), "do_while".to_string()), - ("']".to_string(), "while_local".to_string()), - ("'@]".to_string(), "do_while_local".to_string()), + ("while".to_string(), "]".to_string()), + ("do_while".to_string(), "@]".to_string()), + ("while_local".to_string(), "']".to_string()), + ("do_while_local".to_string(), "'@]".to_string()), ]) } } - pub fn match_brackets(&self, code: Vec) { - + pub fn match_brackets(&mut self, code: &[String]) { + let starting_brackets = ["[", "[@", "'[", "'[@", ]; + let ending_brackets = ["]", "@]", "']", "'@]"]; + for (i, command) in code.iter().enumerate() { + let command_str = command.as_str(); + if !starting_brackets.contains(&command_str) && !ending_brackets.contains(&command_str) { + continue; + } + if starting_brackets.contains(&command_str) { + self.brackets_mem.push((command.clone(), i, 0)); + } + let mut keys_to_remove = vec![]; + for key in 0..self.brackets_mem.len() { + self.brackets_mem[key].2 += self.num_equals(&self.brackets_mem[key].0, command); + let wanted_end = self.ending_brackets_keys.get(self.bracket_keys.get(&self.brackets_mem[key].0).unwrap()).unwrap(); + if self.brackets_mem[key].2 == 0 && command == wanted_end { + let category = self.bracket_keys.get(wanted_end).unwrap(); + let sub_map = self.brackets.get_mut(category).unwrap(); + sub_map.insert(self.brackets_mem[key].1, i); + sub_map.insert(i, self.brackets_mem[key].1); + keys_to_remove.push(key); + } + } + for key in keys_to_remove { + self.brackets_mem.remove(key); + } + } } - fn num_equals(&self, left: String, right: String) -> i8 { - if self.bracket_keys.get(&left) != self.bracket_keys.get(&right) { + fn num_equals(&self, left: &String, right: &String) -> isize { + if self.bracket_keys.get(left) != self.bracket_keys.get(right) { return 0; } if left == right { diff --git a/the-golden/src/interpreter/versions/v0-1-0/lexer.rs b/the-golden/src/interpreter/versions/v0-1-0/lexer.rs index db37473..68b3a4c 100644 --- a/the-golden/src/interpreter/versions/v0-1-0/lexer.rs +++ b/the-golden/src/interpreter/versions/v0-1-0/lexer.rs @@ -6,6 +6,7 @@ lazy_static! { static ref NEW_LINE_REGEX: Regex = Regex::new(r"^\r?\n").unwrap(); } +#[derive(Clone)] pub struct Lexer { text: String, rules: Vec, @@ -23,7 +24,7 @@ impl Lexer { pub fn next(&mut self) -> Result, String> { let text = &self.text.as_str()[self.position..]; - if text.len() < 1 { + if text.is_empty() { return Ok(None); } if text == "\"" { @@ -35,16 +36,17 @@ impl Lexer { for rule in &self.rules { if let Some(captures) = rule.captures(text) { if let Some(capture) = captures.get(0) { + let (command_line, command_column) = (self.line, self.column); let command = capture.as_str(); let command_length = capture.end() - capture.start(); self.position += command_length; - if command.contains("\n") { - self.line += command.matches("\n").count(); - self.column += command.split("\n").last().unwrap().len(); + if command.contains('\n') { + self.line += command.matches('\n').count(); + self.column += command.split('\n').last().unwrap().len(); } else { self.column += command_length; } - return Ok(Some((command.to_string(), self.line, self.column, self.file_path.clone()))); + return Ok(Some((command.to_string(), command_line, command_column, self.file_path.clone()))); } } } diff --git a/the-golden/src/interpreter/versions/v0-1-0/main.rs b/the-golden/src/interpreter/versions/v0-1-0/main.rs index c301e12..ea2dd83 100644 --- a/the-golden/src/interpreter/versions/v0-1-0/main.rs +++ b/the-golden/src/interpreter/versions/v0-1-0/main.rs @@ -4,6 +4,8 @@ use regex::Regex; use brackets_matcher::BracketsMatcher; #[path = "./lexer.rs"] mod lexer; pub use lexer::Lexer; +#[path = "./parser.rs"] mod parser; +pub use parser::Parser; #[path = "./validator.rs"] mod validator; use validator::Validator; @@ -19,6 +21,8 @@ impl Runner { pub fn new(raw_code: String, code_path: std::path::PathBuf) -> Self { let rules = vec![ Regex::new(r"^!").unwrap(), + Regex::new(r"^\[@?").unwrap(), + Regex::new(r"^@?\]").unwrap(), Regex::new(r"^:\r?\n?").unwrap(), Regex::new("\"[^\"]*\"").unwrap() ]; @@ -30,17 +34,23 @@ impl Runner { } } - pub fn run(&self) { + pub fn run(&mut self) { println!("Running version 0.1.0"); println!("Raw code: {}", self.raw_code); - let validator_result = Validator::run(Lexer::new(self.raw_code.clone(), self.rules.clone(), self.code_path.clone())); - match validator_result { - Err(e) => { - println!("{}", e); - return; - }, - _ => {} + let lexer = Lexer::new(self.raw_code.clone(), self.rules.clone(), self.code_path.clone()); + let validator_result = Validator::run(lexer.clone()); + if let Err(e) = validator_result { + println!("{}", e); + return; } println!("Valid code!"); + let mut parser = Parser::new(); + let parser_result = parser.run(lexer); + if let Err(e) = parser_result { + println!("{}", e); + return; + } + self.brackets_matcher.match_brackets(&parser.commands); + println!("{:?}", self.brackets_matcher.brackets); } } \ No newline at end of file diff --git a/the-golden/src/interpreter/versions/v0-1-0/parser.rs b/the-golden/src/interpreter/versions/v0-1-0/parser.rs new file mode 100644 index 0000000..7724194 --- /dev/null +++ b/the-golden/src/interpreter/versions/v0-1-0/parser.rs @@ -0,0 +1,35 @@ +use regex::Regex; + +pub struct Parser { + pub commands: Vec, + pub commands_info: Vec<(String, usize, usize, std::path::PathBuf)> +} + +impl Parser { + pub fn new() -> Self { + Self { commands: vec![], commands_info: vec![] } + } + + pub fn run(&mut self, mut lexer: super::Lexer) -> Result { + let mut t = lexer.next(); + let mut p = t.clone(); + while t.is_ok() && t.clone().unwrap().is_some() { + let val = t.clone().unwrap().unwrap(); + let (command, ..) = val.clone(); + if !((command.starts_with('"') && command.ends_with('"')) || command.contains(':')) { + self.commands.push(command); + self.commands_info.push(val); + } + p = t; + t = lexer.next(); + } + if let Err(e) = t { + return Err(e); + } + let (command, line, column, file_path) = p.unwrap().unwrap(); + if !Regex::new(r":\n?\r?").unwrap().is_match(&command) { + return Err(format!("Syntax error at {}:{} in {:?} ({:?}) - ':' expected", line, column, file_path.file_name().unwrap(), file_path.as_path())); + } + Ok(0) + } +} \ No newline at end of file diff --git a/the-golden/src/interpreter/versions/v0-1-0/validator.rs b/the-golden/src/interpreter/versions/v0-1-0/validator.rs index 6f85625..c37ff3f 100644 --- a/the-golden/src/interpreter/versions/v0-1-0/validator.rs +++ b/the-golden/src/interpreter/versions/v0-1-0/validator.rs @@ -10,14 +10,13 @@ impl Validator { p = t; t = lexer.next(); } - match t { - Err(e) => return Err(e), - _ => {} + if let Err(e) = t { + return Err(e); } let (command, line, column, file_path) = p.unwrap().unwrap(); if !Regex::new(r":\n?\r?").unwrap().is_match(&command) { return Err(format!("Syntax error at {}:{} in {:?} ({:?}) - ':' expected", line, column, file_path.file_name().unwrap(), file_path.as_path())); } - return Ok(0); + Ok(0) } } \ No newline at end of file From 4e96c36e06f59b610bc8f8d5f90adf774edb9794 Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Wed, 27 Jul 2022 20:36:19 +0200 Subject: [PATCH 04/30] Add a flags parser --- the-golden/src/flags.rs | 57 +++++++++++++++++++ .../src/interpreter/versions/v0-1-0/main.rs | 12 +++- the-golden/src/main.rs | 40 +++++++------ 3 files changed, 91 insertions(+), 18 deletions(-) create mode 100644 the-golden/src/flags.rs diff --git a/the-golden/src/flags.rs b/the-golden/src/flags.rs new file mode 100644 index 0000000..e05fd87 --- /dev/null +++ b/the-golden/src/flags.rs @@ -0,0 +1,57 @@ +#[derive(Debug)] +pub struct Flags { + pub action: Option, + pub code_path: Option, + pub debug: Option, + pub debug_heavy: Option, + pub raw_code_to_run: Option, + pub version: Option +} + +impl Flags { + pub fn new() -> Self { + Self { + action: None, + code_path: None, + debug: None, + debug_heavy: None, + raw_code_to_run: None, + version: None + } + } + + pub fn parse(&mut self, args: &[String]) { + let args_count = args.len(); + let mut i = 0; + while i < args_count { + let argument = &args[i]; + let argument_lowercase = argument.to_lowercase(); + match argument_lowercase.as_str() { + "--debug" => if self.debug.is_none() { + self.debug = Some(true); + }, + "--debug-heavy" => if self.debug_heavy.is_none() { + self.debug_heavy = Some(true); + }, + "--version" => if self.version.is_none() && i + 1 < args_count { + self.version = Some(args[i+1].clone()); + }, + "-" => if self.raw_code_to_run.is_none() && i + 1 < args_count { + self.raw_code_to_run = Some(args[i+1].clone()); + }, + "run" => if self.action.is_none() { + self.action = Some(String::from("run")); + if self.code_path.is_none() && i + 1 < args_count && !args[i+1].starts_with('-') { + let mut path = std::path::PathBuf::from(args[0].clone()); + path.pop(); + path.push(args[i+1].clone()); + path.set_file_name("maumivu.au"); + self.code_path = Some(path); + } + } + _ => {} + } + i += 1; + } + } +} \ No newline at end of file diff --git a/the-golden/src/interpreter/versions/v0-1-0/main.rs b/the-golden/src/interpreter/versions/v0-1-0/main.rs index ea2dd83..e28a4aa 100644 --- a/the-golden/src/interpreter/versions/v0-1-0/main.rs +++ b/the-golden/src/interpreter/versions/v0-1-0/main.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use regex::Regex; #[path = "./brackets_matcher.rs"] mod brackets_matcher; @@ -14,7 +16,9 @@ pub struct Runner { raw_code: String, rules: Vec, - code_path: std::path::PathBuf + code_path: std::path::PathBuf, + + brackets: HashMap> } impl Runner { @@ -28,9 +32,12 @@ impl Runner { ]; Self { brackets_matcher: BracketsMatcher::new(), + raw_code, rules, - code_path + code_path, + + brackets: HashMap::new() } } @@ -51,6 +58,7 @@ impl Runner { return; } self.brackets_matcher.match_brackets(&parser.commands); + self.brackets = self.brackets_matcher.brackets.clone(); println!("{:?}", self.brackets_matcher.brackets); } } \ No newline at end of file diff --git a/the-golden/src/main.rs b/the-golden/src/main.rs index 5f36267..c241f66 100644 --- a/the-golden/src/main.rs +++ b/the-golden/src/main.rs @@ -1,6 +1,8 @@ use dotenv::dotenv; use std::env; +#[path = "./flags.rs"] mod flags; +pub use flags::Flags; #[path = "./interpreter/interpreter.rs"] mod interpreter; use interpreter::Interpreter; @@ -24,28 +26,34 @@ fn main() { tracing_subscriber::fmt::init(); let args: Vec = std::env::args().collect(); - let actions = [String::from("run")]; + + let mut flags_handler = Flags::new(); + flags_handler.parse(&args); + println!("{:?}", flags_handler); + let mut action = String::new(); let mut version = String::from("latest"); - for arg in &args { - if arg.starts_with("--version:") { - version = arg.split_once("--version:").unwrap().1.to_string(); - } else if actions.contains(&arg.to_lowercase()) { - action = arg.to_lowercase(); - } + let mut code = String::new(); + let mut code_path = std::path::PathBuf::new(); + + if let Some(a) = flags_handler.action { + action = a; } - if action == *"run" { - let mut path = std::path::PathBuf::from(args[0].clone()); - path.pop(); - if args.len() > 2 { - path.push(args[2].clone()); - } - path.set_file_name("maumivu.au"); - let code = match std::fs::read_to_string(&path) { + if let Some(path) = flags_handler.code_path { + code = match std::fs::read_to_string(&path) { Ok(c) => c, Err(e) => panic!("{}", e) }; - Interpreter::new(version, code, path).run(); + code_path = path; + } else if let Some(code_to_run) = flags_handler.raw_code_to_run { + code = code_to_run; + code_path.set_file_name(""); + } + if let Some(v) = flags_handler.version { + version = v; + } + if action == *"run" { + Interpreter::new(version, code, code_path).run(); } else { todo!() } From 215884fd2d0d0a0895b4afb818d55f9cac5b971f Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Wed, 27 Jul 2022 20:53:29 +0200 Subject: [PATCH 05/30] Make use of debug and heavy debug modes Only print out debug information with at least one of those modes being activated (heavy prints almost everything, normal debug only quite useful information) --- the-golden/src/flags.rs | 19 ++++++------ the-golden/src/interpreter/interpreter.rs | 10 +++++-- .../src/interpreter/versions/handler.rs | 6 ++-- .../src/interpreter/versions/v0-1-0/main.rs | 30 +++++++++++++++---- .../interpreter/versions/v0-1-0/validator.rs | 8 ++++- the-golden/src/main.rs | 12 ++++---- 6 files changed, 57 insertions(+), 28 deletions(-) diff --git a/the-golden/src/flags.rs b/the-golden/src/flags.rs index e05fd87..1d27dcb 100644 --- a/the-golden/src/flags.rs +++ b/the-golden/src/flags.rs @@ -1,9 +1,9 @@ -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct Flags { pub action: Option, pub code_path: Option, - pub debug: Option, - pub debug_heavy: Option, + pub debug: bool, + pub debug_heavy: bool, pub raw_code_to_run: Option, pub version: Option } @@ -13,8 +13,8 @@ impl Flags { Self { action: None, code_path: None, - debug: None, - debug_heavy: None, + debug: false, + debug_heavy: false, raw_code_to_run: None, version: None } @@ -27,11 +27,10 @@ impl Flags { let argument = &args[i]; let argument_lowercase = argument.to_lowercase(); match argument_lowercase.as_str() { - "--debug" => if self.debug.is_none() { - self.debug = Some(true); - }, - "--debug-heavy" => if self.debug_heavy.is_none() { - self.debug_heavy = Some(true); + "--debug" => self.debug = true, + "--debug-heavy" => { + self.debug = true; + self.debug_heavy = true; }, "--version" => if self.version.is_none() && i + 1 < args_count { self.version = Some(args[i+1].clone()); diff --git a/the-golden/src/interpreter/interpreter.rs b/the-golden/src/interpreter/interpreter.rs index 7394a28..31c22ad 100644 --- a/the-golden/src/interpreter/interpreter.rs +++ b/the-golden/src/interpreter/interpreter.rs @@ -1,6 +1,10 @@ +use crate::Flags; + #[path = "./versions/handler.rs"] mod versions_handler; pub struct Interpreter { + flags: Flags, + version: String, versions_handler: versions_handler::Handler, code: String, @@ -8,13 +12,13 @@ pub struct Interpreter { } impl Interpreter { - pub fn new(mut version: String, code: String, code_path: std::path::PathBuf) -> Self { + pub fn new(mut version: String, code: String, code_path: std::path::PathBuf, flags: Flags) -> Self { let versions_handler = versions_handler::Handler::new(); version = versions_handler.parse_version(version); - Self { code, version, versions_handler, code_path } + Self { flags, code, version, versions_handler, code_path } } pub fn run(&self) { - self.versions_handler.run(self.version.clone(), self.code.clone(), self.code_path.clone()); + self.versions_handler.run(self.version.clone(), self.code.clone(), self.code_path.clone(), self.flags.clone()); } } \ No newline at end of file diff --git a/the-golden/src/interpreter/versions/handler.rs b/the-golden/src/interpreter/versions/handler.rs index d48e80b..5074cb5 100644 --- a/the-golden/src/interpreter/versions/handler.rs +++ b/the-golden/src/interpreter/versions/handler.rs @@ -1,3 +1,5 @@ +use crate::Flags; + #[path = "./v0-1-0/main.rs"] mod v0_1_0; pub struct Handler { @@ -17,9 +19,9 @@ impl Handler { version } - pub fn run(&self, version: String, code: String, code_path: std::path::PathBuf) { + pub fn run(&self, version: String, code: String, code_path: std::path::PathBuf, flags: Flags) { match version.as_str() { - "0.1.0" => v0_1_0::Runner::new(code, code_path).run(), + "0.1.0" => v0_1_0::Runner::new(code, code_path, flags).run(), _ => panic!("Couldn't launch version {}", &version) } } diff --git a/the-golden/src/interpreter/versions/v0-1-0/main.rs b/the-golden/src/interpreter/versions/v0-1-0/main.rs index e28a4aa..4d11da1 100644 --- a/the-golden/src/interpreter/versions/v0-1-0/main.rs +++ b/the-golden/src/interpreter/versions/v0-1-0/main.rs @@ -1,5 +1,6 @@ use std::collections::HashMap; +use crate::Flags; use regex::Regex; #[path = "./brackets_matcher.rs"] mod brackets_matcher; @@ -12,6 +13,8 @@ pub use parser::Parser; use validator::Validator; pub struct Runner { + flags: Flags, + brackets_matcher: BracketsMatcher, raw_code: String, @@ -22,7 +25,7 @@ pub struct Runner { } impl Runner { - pub fn new(raw_code: String, code_path: std::path::PathBuf) -> Self { + pub fn new(raw_code: String, code_path: std::path::PathBuf, flags: Flags) -> Self { let rules = vec![ Regex::new(r"^!").unwrap(), Regex::new(r"^\[@?").unwrap(), @@ -31,6 +34,8 @@ impl Runner { Regex::new("\"[^\"]*\"").unwrap() ]; Self { + flags, + brackets_matcher: BracketsMatcher::new(), raw_code, @@ -42,23 +47,36 @@ impl Runner { } pub fn run(&mut self) { - println!("Running version 0.1.0"); - println!("Raw code: {}", self.raw_code); + if self.flags.debug { + println!("Running version 0.1.0"); + println!("Raw code: {}", self.raw_code); + } let lexer = Lexer::new(self.raw_code.clone(), self.rules.clone(), self.code_path.clone()); - let validator_result = Validator::run(lexer.clone()); + let validator_result = Validator::run(lexer.clone(), self.flags.debug_heavy); if let Err(e) = validator_result { println!("{}", e); return; } - println!("Valid code!"); + if self.flags.debug { + println!("Valid code!"); + } let mut parser = Parser::new(); let parser_result = parser.run(lexer); if let Err(e) = parser_result { println!("{}", e); return; } + if self.flags.debug { + println!("Parsed commands: {:?}", parser.commands); + } self.brackets_matcher.match_brackets(&parser.commands); self.brackets = self.brackets_matcher.brackets.clone(); - println!("{:?}", self.brackets_matcher.brackets); + if self.flags.debug_heavy { + println!("Matched brackets: {:?}", self.brackets_matcher.brackets); + } + + if self.flags.debug { + println!("----- START OF CODE EXECUTION -----"); + } } } \ No newline at end of file diff --git a/the-golden/src/interpreter/versions/v0-1-0/validator.rs b/the-golden/src/interpreter/versions/v0-1-0/validator.rs index c37ff3f..821170d 100644 --- a/the-golden/src/interpreter/versions/v0-1-0/validator.rs +++ b/the-golden/src/interpreter/versions/v0-1-0/validator.rs @@ -3,12 +3,18 @@ use regex::Regex; pub struct Validator {} impl Validator { - pub fn run(mut lexer: super::Lexer) -> Result { + pub fn run(mut lexer: super::Lexer, heavy_debug: bool) -> Result { let mut t = lexer.next(); + if heavy_debug { + println!("Matched command: {:?}", t); + } let mut p = t.clone(); while t.is_ok() && t.clone().unwrap().is_some() { p = t; t = lexer.next(); + if heavy_debug { + println!("Matched command: {:?}", t); + } } if let Err(e) = t { return Err(e); diff --git a/the-golden/src/main.rs b/the-golden/src/main.rs index c241f66..f2b4369 100644 --- a/the-golden/src/main.rs +++ b/the-golden/src/main.rs @@ -29,31 +29,31 @@ fn main() { let mut flags_handler = Flags::new(); flags_handler.parse(&args); - println!("{:?}", flags_handler); let mut action = String::new(); let mut version = String::from("latest"); let mut code = String::new(); let mut code_path = std::path::PathBuf::new(); - if let Some(a) = flags_handler.action { + let cloned_flags = flags_handler.clone(); + if let Some(a) = cloned_flags.action { action = a; } - if let Some(path) = flags_handler.code_path { + if let Some(path) = cloned_flags.code_path { code = match std::fs::read_to_string(&path) { Ok(c) => c, Err(e) => panic!("{}", e) }; code_path = path; - } else if let Some(code_to_run) = flags_handler.raw_code_to_run { + } else if let Some(code_to_run) = cloned_flags.raw_code_to_run { code = code_to_run; code_path.set_file_name(""); } - if let Some(v) = flags_handler.version { + if let Some(v) = cloned_flags.version { version = v; } if action == *"run" { - Interpreter::new(version, code, code_path).run(); + Interpreter::new(version, code, code_path, flags_handler).run(); } else { todo!() } From 2c2d8b1b397584ce6b5fb3102e3fd540794dda2f Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Thu, 28 Jul 2022 22:48:39 +0200 Subject: [PATCH 06/30] Highlight errors in red --- .../src/interpreter/versions/v0-1-0/main.rs | 34 ++++++++++++++----- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/the-golden/src/interpreter/versions/v0-1-0/main.rs b/the-golden/src/interpreter/versions/v0-1-0/main.rs index 4d11da1..3d973a2 100644 --- a/the-golden/src/interpreter/versions/v0-1-0/main.rs +++ b/the-golden/src/interpreter/versions/v0-1-0/main.rs @@ -16,20 +16,22 @@ pub struct Runner { flags: Flags, brackets_matcher: BracketsMatcher, + brackets_categorised: HashMap>, + brackets: HashMap, raw_code: String, rules: Vec, code_path: std::path::PathBuf, - brackets: HashMap> + program_pointer: usize } impl Runner { pub fn new(raw_code: String, code_path: std::path::PathBuf, flags: Flags) -> Self { let rules = vec![ - Regex::new(r"^!").unwrap(), - Regex::new(r"^\[@?").unwrap(), - Regex::new(r"^@?\]").unwrap(), + Regex::new(r"^'?!").unwrap(), + Regex::new(r"^'?\[@?").unwrap(), + Regex::new(r"^'?@?\]").unwrap(), Regex::new(r"^:\r?\n?").unwrap(), Regex::new("\"[^\"]*\"").unwrap() ]; @@ -38,11 +40,13 @@ impl Runner { brackets_matcher: BracketsMatcher::new(), + brackets: HashMap::new(), + brackets_categorised: HashMap::new(), raw_code, rules, code_path, - brackets: HashMap::new() + program_pointer: 0 } } @@ -54,7 +58,7 @@ impl Runner { let lexer = Lexer::new(self.raw_code.clone(), self.rules.clone(), self.code_path.clone()); let validator_result = Validator::run(lexer.clone(), self.flags.debug_heavy); if let Err(e) = validator_result { - println!("{}", e); + println!("\x1b[91mERROR\x1b[00m {}", e); return; } if self.flags.debug { @@ -63,20 +67,32 @@ impl Runner { let mut parser = Parser::new(); let parser_result = parser.run(lexer); if let Err(e) = parser_result { - println!("{}", e); + println!("\x1b[91mERROR\x1b[00m {}", e); return; } if self.flags.debug { println!("Parsed commands: {:?}", parser.commands); } self.brackets_matcher.match_brackets(&parser.commands); - self.brackets = self.brackets_matcher.brackets.clone(); + self.brackets_categorised = self.brackets_matcher.brackets.clone(); if self.flags.debug_heavy { println!("Matched brackets: {:?}", self.brackets_matcher.brackets); } - + for loop_type in self.brackets_categorised.keys() { + let map = self.brackets_categorised.get(loop_type).unwrap(); + for (key, value) in map.iter() { + self.brackets.insert(*key, *value); + } + } + if self.flags.debug_heavy { + println!("Matched brackets uncategorised: {:?}", self.brackets); + } if self.flags.debug { println!("----- START OF CODE EXECUTION -----"); } + let program_length = parser.commands.len(); + while self.program_pointer < program_length { + let command = &parser.commands[self.program_pointer]; + } } } \ No newline at end of file From e4e9c15cc1b1e0513dd4fd94aa27dff65cef6e65 Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Thu, 28 Jul 2022 22:53:34 +0200 Subject: [PATCH 07/30] Don't hide the console window by default on Windows You can hide it with a flag or an environmental variable, but it won't hide by default (since it is mostly a CLI tool, having the CL is useful) --- the-golden/src/flags.rs | 3 +++ the-golden/src/main.rs | 20 ++++++++------------ 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/the-golden/src/flags.rs b/the-golden/src/flags.rs index 1d27dcb..b1b6209 100644 --- a/the-golden/src/flags.rs +++ b/the-golden/src/flags.rs @@ -4,6 +4,7 @@ pub struct Flags { pub code_path: Option, pub debug: bool, pub debug_heavy: bool, + pub no_console: bool, pub raw_code_to_run: Option, pub version: Option } @@ -15,6 +16,7 @@ impl Flags { code_path: None, debug: false, debug_heavy: false, + no_console: false, raw_code_to_run: None, version: None } @@ -32,6 +34,7 @@ impl Flags { self.debug = true; self.debug_heavy = true; }, + "--hide-console" => self.no_console = true, "--version" => if self.version.is_none() && i + 1 < args_count { self.version = Some(args[i+1].clone()); }, diff --git a/the-golden/src/main.rs b/the-golden/src/main.rs index f2b4369..03f79fd 100644 --- a/the-golden/src/main.rs +++ b/the-golden/src/main.rs @@ -11,18 +11,6 @@ fn main() { if env::var("RUST_LOG").is_err() { env::set_var("RUST_LOG", "INFO"); } - match env::var("LOGS") { - Ok(val) => { - if val.to_lowercase() != "on" && cfg!(target_os = "windows") { - winconsole::window::hide(); - } - } - Err(_) => { - if cfg!(target_os = "windows") { - winconsole::window::hide(); - } - } - } tracing_subscriber::fmt::init(); let args: Vec = std::env::args().collect(); @@ -52,6 +40,14 @@ fn main() { if let Some(v) = cloned_flags.version { version = v; } + if let Ok(val) = env::var("LOGS") { + if val.to_lowercase() == "off" && cfg!(target_os = "windows") { + winconsole::window::hide(); + } + } + if cloned_flags.no_console && cfg!(target_os = "windows") { + winconsole::window::hide(); + } if action == *"run" { Interpreter::new(version, code, code_path, flags_handler).run(); } else { From 1daa834f7dc994b4c9cc0f00a5931000b2fbe7b0 Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Thu, 28 Jul 2022 23:39:41 +0200 Subject: [PATCH 08/30] Add basic command evaluation and logs highlighting --- .../src/interpreter/versions/v0-1-0/main.rs | 80 ++++++++++++++++--- .../interpreter/versions/v0-1-0/validator.rs | 7 +- the-golden/src/main.rs | 2 + the-golden/src/utils.rs | 12 +++ 4 files changed, 88 insertions(+), 13 deletions(-) create mode 100644 the-golden/src/utils.rs diff --git a/the-golden/src/interpreter/versions/v0-1-0/main.rs b/the-golden/src/interpreter/versions/v0-1-0/main.rs index 3d973a2..af1ef76 100644 --- a/the-golden/src/interpreter/versions/v0-1-0/main.rs +++ b/the-golden/src/interpreter/versions/v0-1-0/main.rs @@ -9,9 +9,12 @@ use brackets_matcher::BracketsMatcher; pub use lexer::Lexer; #[path = "./parser.rs"] mod parser; pub use parser::Parser; +use crate::Utils; #[path = "./validator.rs"] mod validator; use validator::Validator; +const INFO_PREFIX_LENGTH: usize = 12; + pub struct Runner { flags: Flags, @@ -23,7 +26,12 @@ pub struct Runner { rules: Vec, code_path: std::path::PathBuf, - program_pointer: usize + program_pointer: usize, + + loops: Vec, + memory: [Vec; 2], + memory_pointers: [usize; 2], + active_memory: usize } impl Runner { @@ -46,37 +54,42 @@ impl Runner { rules, code_path, - program_pointer: 0 + program_pointer: 0, + + loops: vec![], + memory: [vec![0.0], vec![0.0]], + memory_pointers: [0, 0], + active_memory: 0 } } pub fn run(&mut self) { if self.flags.debug { - println!("Running version 0.1.0"); - println!("Raw code: {}", self.raw_code); + println!("{}Running version 0.1.0", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH)); + println!("{}Raw code: {}", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH), self.raw_code); } let lexer = Lexer::new(self.raw_code.clone(), self.rules.clone(), self.code_path.clone()); let validator_result = Validator::run(lexer.clone(), self.flags.debug_heavy); if let Err(e) = validator_result { - println!("\x1b[91mERROR\x1b[00m {}", e); + println!("{}{}", Utils::ansi_escape_text("91", "ERROR", INFO_PREFIX_LENGTH), e); return; } if self.flags.debug { - println!("Valid code!"); + println!("{}Valid code!", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH)); } let mut parser = Parser::new(); let parser_result = parser.run(lexer); if let Err(e) = parser_result { - println!("\x1b[91mERROR\x1b[00m {}", e); + println!("{}{}", Utils::ansi_escape_text("91", "ERROR", INFO_PREFIX_LENGTH), e); return; } if self.flags.debug { - println!("Parsed commands: {:?}", parser.commands); + println!("{}Parsed commands: {:?}", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH), parser.commands); } self.brackets_matcher.match_brackets(&parser.commands); self.brackets_categorised = self.brackets_matcher.brackets.clone(); if self.flags.debug_heavy { - println!("Matched brackets: {:?}", self.brackets_matcher.brackets); + println!("{}Matched brackets: {:?}", Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), self.brackets_matcher.brackets); } for loop_type in self.brackets_categorised.keys() { let map = self.brackets_categorised.get(loop_type).unwrap(); @@ -85,14 +98,59 @@ impl Runner { } } if self.flags.debug_heavy { - println!("Matched brackets uncategorised: {:?}", self.brackets); + println!("{}Matched brackets uncategorised: {:?}", Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), self.brackets); } if self.flags.debug { - println!("----- START OF CODE EXECUTION -----"); + println!("{}----- START OF CODE EXECUTION -----", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH)); } + let mut local_memory: [Vec; 2] = [vec![0.0], vec![0.0]]; + let mut local_memory_pointers: [usize; 2] = [0, 0]; + let mut active_local_memory: usize = 0; let program_length = parser.commands.len(); while self.program_pointer < program_length { let command = &parser.commands[self.program_pointer]; + active_local_memory = self.evaluate_command(command, &mut local_memory, &mut local_memory_pointers, active_local_memory); + } + if self.flags.debug { + println!("\n{}----- END OF CODE EXECUTION -----", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH)); + } + if self.flags.debug { + println!("{}Main memory:", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH)); + println!("{}{:?}", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH), self.memory); + println!("{}Local memory:", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH)); + println!("{}{:?}", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH), local_memory); + } + } + + pub fn evaluate_command(&mut self, command: &String, local_memory: &mut [Vec; 2], local_memory_pointers: &mut [usize; 2], active_local_memory: usize) -> usize { + let is_local = command.starts_with('\''); + let command = if is_local { + &command[1..] + } else { + command.as_str() + }; + let [(main_memory, main_memory_pointers, main_active_memory), (local_memory, local_memory_pointers, active_local_memory)] = if is_local { + [(local_memory, local_memory_pointers, active_local_memory), (&mut self.memory, &mut self.memory_pointers, self.active_memory)] + } else { + [(&mut self.memory, &mut self.memory_pointers, self.active_memory), (local_memory, local_memory_pointers, active_local_memory)] + }; + match command { + "!" => main_memory[main_active_memory][main_memory_pointers[main_active_memory]] += 1.0, + "~" => main_memory[main_active_memory][main_memory_pointers[main_active_memory]] -= 1.0, + "+" => main_memory[main_active_memory][main_memory_pointers[main_active_memory]] += main_memory[(main_active_memory as isize-1).abs() as usize][main_memory_pointers[(main_active_memory as isize-1).abs() as usize]], + "-" => main_memory[main_active_memory][main_memory_pointers[main_active_memory]] -= main_memory[(main_active_memory as isize-1).abs() as usize][main_memory_pointers[(main_active_memory as isize-1).abs() as usize]], + _ => {} + } + self.program_pointer += 1; + self.active_memory = if is_local { + active_local_memory + } else { + main_active_memory + }; + return if is_local { + main_active_memory + } else { + active_local_memory } } } \ No newline at end of file diff --git a/the-golden/src/interpreter/versions/v0-1-0/validator.rs b/the-golden/src/interpreter/versions/v0-1-0/validator.rs index 821170d..08de9f1 100644 --- a/the-golden/src/interpreter/versions/v0-1-0/validator.rs +++ b/the-golden/src/interpreter/versions/v0-1-0/validator.rs @@ -1,19 +1,22 @@ use regex::Regex; +use crate::Utils; +use super::INFO_PREFIX_LENGTH; + pub struct Validator {} impl Validator { pub fn run(mut lexer: super::Lexer, heavy_debug: bool) -> Result { let mut t = lexer.next(); if heavy_debug { - println!("Matched command: {:?}", t); + println!("{}Matched command: {:?}", Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), t); } let mut p = t.clone(); while t.is_ok() && t.clone().unwrap().is_some() { p = t; t = lexer.next(); if heavy_debug { - println!("Matched command: {:?}", t); + println!("{}Matched command: {:?}", Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), t); } } if let Err(e) = t { diff --git a/the-golden/src/main.rs b/the-golden/src/main.rs index 03f79fd..c716ad5 100644 --- a/the-golden/src/main.rs +++ b/the-golden/src/main.rs @@ -5,6 +5,8 @@ use std::env; pub use flags::Flags; #[path = "./interpreter/interpreter.rs"] mod interpreter; use interpreter::Interpreter; +#[path = "./utils.rs"] mod utils; +pub use utils::Utils; fn main() { dotenv().ok(); diff --git a/the-golden/src/utils.rs b/the-golden/src/utils.rs new file mode 100644 index 0000000..743cc34 --- /dev/null +++ b/the-golden/src/utils.rs @@ -0,0 +1,12 @@ +pub struct Utils {} + +impl Utils { + pub fn ansi_escape_text(style: &str, text: &str, min_length: usize) -> String { + let mut res = format!("\x1b[{}m{}", style, text); + for _ in 0..min_length - text.len() { + res += " "; + } + res += "\x1b[0m"; + res + } +} \ No newline at end of file From ce912848270270e4809bb5519340421ed05d20c0 Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Thu, 28 Jul 2022 23:41:22 +0200 Subject: [PATCH 09/30] Apply some clippy suggestions --- the-golden/src/interpreter/versions/v0-1-0/main.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/the-golden/src/interpreter/versions/v0-1-0/main.rs b/the-golden/src/interpreter/versions/v0-1-0/main.rs index af1ef76..2c84229 100644 --- a/the-golden/src/interpreter/versions/v0-1-0/main.rs +++ b/the-golden/src/interpreter/versions/v0-1-0/main.rs @@ -122,12 +122,12 @@ impl Runner { } } - pub fn evaluate_command(&mut self, command: &String, local_memory: &mut [Vec; 2], local_memory_pointers: &mut [usize; 2], active_local_memory: usize) -> usize { + pub fn evaluate_command(&mut self, command: &str, local_memory: &mut [Vec; 2], local_memory_pointers: &mut [usize; 2], active_local_memory: usize) -> usize { let is_local = command.starts_with('\''); let command = if is_local { &command[1..] } else { - command.as_str() + command }; let [(main_memory, main_memory_pointers, main_active_memory), (local_memory, local_memory_pointers, active_local_memory)] = if is_local { [(local_memory, local_memory_pointers, active_local_memory), (&mut self.memory, &mut self.memory_pointers, self.active_memory)] @@ -147,7 +147,7 @@ impl Runner { } else { main_active_memory }; - return if is_local { + if is_local { main_active_memory } else { active_local_memory From 56b9d48ef4178e1fdd19750b07e7e4f09a21f345 Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Thu, 28 Jul 2022 23:42:44 +0200 Subject: [PATCH 10/30] Add autoformating rules --- the-golden/rustfmt.toml | 8 +++ .../src/interpreter/versions/v0-1-0/main.rs | 68 ++++++++++++------- 2 files changed, 50 insertions(+), 26 deletions(-) create mode 100644 the-golden/rustfmt.toml diff --git a/the-golden/rustfmt.toml b/the-golden/rustfmt.toml new file mode 100644 index 0000000..b037110 --- /dev/null +++ b/the-golden/rustfmt.toml @@ -0,0 +1,8 @@ +hard_tabs = true +max_width = 200 +reorder_imports = true +reorder_modules = true +reorder_impl_items = false +report_fixme = "Always" +report_todo = "Always" +edition = "2021" diff --git a/the-golden/src/interpreter/versions/v0-1-0/main.rs b/the-golden/src/interpreter/versions/v0-1-0/main.rs index 2c84229..15fba26 100644 --- a/the-golden/src/interpreter/versions/v0-1-0/main.rs +++ b/the-golden/src/interpreter/versions/v0-1-0/main.rs @@ -3,14 +3,18 @@ use std::collections::HashMap; use crate::Flags; use regex::Regex; -#[path = "./brackets_matcher.rs"] mod brackets_matcher; +#[path = "./brackets_matcher.rs"] +mod brackets_matcher; use brackets_matcher::BracketsMatcher; -#[path = "./lexer.rs"] mod lexer; +#[path = "./lexer.rs"] +mod lexer; pub use lexer::Lexer; -#[path = "./parser.rs"] mod parser; -pub use parser::Parser; +#[path = "./parser.rs"] +mod parser; use crate::Utils; -#[path = "./validator.rs"] mod validator; +pub use parser::Parser; +#[path = "./validator.rs"] +mod validator; use validator::Validator; const INFO_PREFIX_LENGTH: usize = 12; @@ -20,7 +24,7 @@ pub struct Runner { brackets_matcher: BracketsMatcher, brackets_categorised: HashMap>, - + brackets: HashMap, raw_code: String, rules: Vec, @@ -31,7 +35,7 @@ pub struct Runner { loops: Vec, memory: [Vec; 2], memory_pointers: [usize; 2], - active_memory: usize + active_memory: usize, } impl Runner { @@ -41,7 +45,7 @@ impl Runner { Regex::new(r"^'?\[@?").unwrap(), Regex::new(r"^'?@?\]").unwrap(), Regex::new(r"^:\r?\n?").unwrap(), - Regex::new("\"[^\"]*\"").unwrap() + Regex::new("\"[^\"]*\"").unwrap(), ]; Self { flags, @@ -59,7 +63,7 @@ impl Runner { loops: vec![], memory: [vec![0.0], vec![0.0]], memory_pointers: [0, 0], - active_memory: 0 + active_memory: 0, } } @@ -89,7 +93,11 @@ impl Runner { self.brackets_matcher.match_brackets(&parser.commands); self.brackets_categorised = self.brackets_matcher.brackets.clone(); if self.flags.debug_heavy { - println!("{}Matched brackets: {:?}", Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), self.brackets_matcher.brackets); + println!( + "{}Matched brackets: {:?}", + Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), + self.brackets_matcher.brackets + ); } for loop_type in self.brackets_categorised.keys() { let map = self.brackets_categorised.get(loop_type).unwrap(); @@ -98,7 +106,11 @@ impl Runner { } } if self.flags.debug_heavy { - println!("{}Matched brackets uncategorised: {:?}", Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), self.brackets); + println!( + "{}Matched brackets uncategorised: {:?}", + Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), + self.brackets + ); } if self.flags.debug { println!("{}----- START OF CODE EXECUTION -----", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH)); @@ -124,33 +136,37 @@ impl Runner { pub fn evaluate_command(&mut self, command: &str, local_memory: &mut [Vec; 2], local_memory_pointers: &mut [usize; 2], active_local_memory: usize) -> usize { let is_local = command.starts_with('\''); - let command = if is_local { - &command[1..] - } else { - command - }; + let command = if is_local { &command[1..] } else { command }; let [(main_memory, main_memory_pointers, main_active_memory), (local_memory, local_memory_pointers, active_local_memory)] = if is_local { - [(local_memory, local_memory_pointers, active_local_memory), (&mut self.memory, &mut self.memory_pointers, self.active_memory)] + [ + (local_memory, local_memory_pointers, active_local_memory), + (&mut self.memory, &mut self.memory_pointers, self.active_memory), + ] } else { - [(&mut self.memory, &mut self.memory_pointers, self.active_memory), (local_memory, local_memory_pointers, active_local_memory)] + [ + (&mut self.memory, &mut self.memory_pointers, self.active_memory), + (local_memory, local_memory_pointers, active_local_memory), + ] }; match command { "!" => main_memory[main_active_memory][main_memory_pointers[main_active_memory]] += 1.0, "~" => main_memory[main_active_memory][main_memory_pointers[main_active_memory]] -= 1.0, - "+" => main_memory[main_active_memory][main_memory_pointers[main_active_memory]] += main_memory[(main_active_memory as isize-1).abs() as usize][main_memory_pointers[(main_active_memory as isize-1).abs() as usize]], - "-" => main_memory[main_active_memory][main_memory_pointers[main_active_memory]] -= main_memory[(main_active_memory as isize-1).abs() as usize][main_memory_pointers[(main_active_memory as isize-1).abs() as usize]], + "+" => { + main_memory[main_active_memory][main_memory_pointers[main_active_memory]] += + main_memory[(main_active_memory as isize - 1).abs() as usize][main_memory_pointers[(main_active_memory as isize - 1).abs() as usize]] + } + "-" => { + main_memory[main_active_memory][main_memory_pointers[main_active_memory]] -= + main_memory[(main_active_memory as isize - 1).abs() as usize][main_memory_pointers[(main_active_memory as isize - 1).abs() as usize]] + } _ => {} } self.program_pointer += 1; - self.active_memory = if is_local { - active_local_memory - } else { - main_active_memory - }; + self.active_memory = if is_local { active_local_memory } else { main_active_memory }; if is_local { main_active_memory } else { active_local_memory } } -} \ No newline at end of file +} From 998ac27dde81274ae3df5d55fe1b718bc225c274 Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Fri, 29 Jul 2022 21:00:24 +0200 Subject: [PATCH 11/30] Add repetition |5|! will now increment 5 times, and |-3|! will decrement 3 times --- .../src/interpreter/versions/v0-1-0/main.rs | 46 ++++++++++++++----- 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/the-golden/src/interpreter/versions/v0-1-0/main.rs b/the-golden/src/interpreter/versions/v0-1-0/main.rs index 15fba26..725a948 100644 --- a/the-golden/src/interpreter/versions/v0-1-0/main.rs +++ b/the-golden/src/interpreter/versions/v0-1-0/main.rs @@ -24,6 +24,7 @@ pub struct Runner { brackets_matcher: BracketsMatcher, brackets_categorised: HashMap>, + opposite_commands: HashMap, brackets: HashMap, raw_code: String, @@ -41,7 +42,7 @@ pub struct Runner { impl Runner { pub fn new(raw_code: String, code_path: std::path::PathBuf, flags: Flags) -> Self { let rules = vec![ - Regex::new(r"^'?!").unwrap(), + Regex::new(r"^'?(\|-?[0-9]*\|)*!").unwrap(), // increment Regex::new(r"^'?\[@?").unwrap(), Regex::new(r"^'?@?\]").unwrap(), Regex::new(r"^:\r?\n?").unwrap(), @@ -54,6 +55,8 @@ impl Runner { brackets: HashMap::new(), brackets_categorised: HashMap::new(), + opposite_commands: HashMap::from([("!".to_string(), "~".to_string())]), + raw_code, rules, code_path, @@ -148,18 +151,39 @@ impl Runner { (local_memory, local_memory_pointers, active_local_memory), ] }; - match command { - "!" => main_memory[main_active_memory][main_memory_pointers[main_active_memory]] += 1.0, - "~" => main_memory[main_active_memory][main_memory_pointers[main_active_memory]] -= 1.0, - "+" => { - main_memory[main_active_memory][main_memory_pointers[main_active_memory]] += - main_memory[(main_active_memory as isize - 1).abs() as usize][main_memory_pointers[(main_active_memory as isize - 1).abs() as usize]] + let split_command = command.split("|").collect::>(); + let (command, repeat) = if split_command.len() == 3 { + let count_str = split_command[1]; + let num = if count_str.is_empty() { + main_memory[main_active_memory][main_memory_pointers[main_active_memory]].floor() as i128 + } else if let Ok(val) = count_str.parse::() { + val + } else { + 1 + }; + let new_command = split_command[2]; + if num < 0 { + (self.opposite_commands.get(new_command).unwrap().as_str(), num * -1) + } else { + (new_command, num) } - "-" => { - main_memory[main_active_memory][main_memory_pointers[main_active_memory]] -= - main_memory[(main_active_memory as isize - 1).abs() as usize][main_memory_pointers[(main_active_memory as isize - 1).abs() as usize]] + } else { + (command, 1) + }; + for _ in 0..repeat { + match command { + "!" => main_memory[main_active_memory][main_memory_pointers[main_active_memory]] += 1.0, + "~" => main_memory[main_active_memory][main_memory_pointers[main_active_memory]] -= 1.0, + "+" => { + main_memory[main_active_memory][main_memory_pointers[main_active_memory]] += + main_memory[(main_active_memory as isize - 1).abs() as usize][main_memory_pointers[(main_active_memory as isize - 1).abs() as usize]] + } + "-" => { + main_memory[main_active_memory][main_memory_pointers[main_active_memory]] -= + main_memory[(main_active_memory as isize - 1).abs() as usize][main_memory_pointers[(main_active_memory as isize - 1).abs() as usize]] + } + _ => {} } - _ => {} } self.program_pointer += 1; self.active_memory = if is_local { active_local_memory } else { main_active_memory }; From dbc1f84b93bdd5698a8ca9608996cdcd60337402 Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Fri, 29 Jul 2022 23:18:35 +0200 Subject: [PATCH 12/30] Support more commands --- the-golden/Cargo.toml | 1 + .../src/interpreter/versions/v0-1-0/main.rs | 67 ++++++++++++++++--- 2 files changed, 60 insertions(+), 8 deletions(-) diff --git a/the-golden/Cargo.toml b/the-golden/Cargo.toml index b85f41a..fdd2aa0 100644 --- a/the-golden/Cargo.toml +++ b/the-golden/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" [dependencies] dotenv = "0.15.0" lazy_static = "1.4.0" +rand = "0.8.5" regex = "1.6.0" tracing = "0.1.35" tracing-subscriber = "0.3.15" diff --git a/the-golden/src/interpreter/versions/v0-1-0/main.rs b/the-golden/src/interpreter/versions/v0-1-0/main.rs index 725a948..bfe0dec 100644 --- a/the-golden/src/interpreter/versions/v0-1-0/main.rs +++ b/the-golden/src/interpreter/versions/v0-1-0/main.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use crate::Flags; +use rand::Rng; use regex::Regex; #[path = "./brackets_matcher.rs"] @@ -42,11 +43,22 @@ pub struct Runner { impl Runner { pub fn new(raw_code: String, code_path: std::path::PathBuf, flags: Flags) -> Self { let rules = vec![ - Regex::new(r"^'?(\|-?[0-9]*\|)*!").unwrap(), // increment - Regex::new(r"^'?\[@?").unwrap(), - Regex::new(r"^'?@?\]").unwrap(), - Regex::new(r"^:\r?\n?").unwrap(), - Regex::new("\"[^\"]*\"").unwrap(), + Regex::new(r"^'?(\|-?[0-9]*\|)*!").unwrap(), // increment + Regex::new(r"^'?(\|-?[0-9]*\|)*~").unwrap(), // decrement + Regex::new(r"^'?(\|-?[0-9]*\|)*\+").unwrap(), // add + Regex::new(r"^'?(\|-?[0-9]*\|)*-").unwrap(), // subtract + Regex::new(r"^'?(\|-?[0-9]*\|)*\*").unwrap(), // multiply + Regex::new(r"^'?(\|-?[0-9]*\|)*/").unwrap(), // divide + Regex::new(r"^'?`").unwrap(), // generate a random number from 0 (inclusive) to 1 (exclusive) + Regex::new(r"^'?>").unwrap(), // move right + Regex::new(r"^'?<").unwrap(), // move left + Regex::new(r"^'?_").unwrap(), // floor + Regex::new(r"^'?&").unwrap(), // ceil + Regex::new(r"^'?\^").unwrap(), // switch active memory + Regex::new(r"^'?\[@?").unwrap(), // (do-)while start + Regex::new(r"^'?@?\]").unwrap(), // (do-)while end + Regex::new(r"^:\r?\n?").unwrap(), // end of line + Regex::new("\"[^\"]*\"").unwrap(), // whitespace ]; Self { flags, @@ -55,7 +67,16 @@ impl Runner { brackets: HashMap::new(), brackets_categorised: HashMap::new(), - opposite_commands: HashMap::from([("!".to_string(), "~".to_string())]), + opposite_commands: HashMap::from([ + ("!".to_string(), "~".to_string()), + ("~".to_string(), "!".to_string()), + ("+".to_string(), "-".to_string()), + ("-".to_string(), "+".to_string()), + ("*".to_string(), "/".to_string()), + ("/".to_string(), "*".to_string()), + (">".to_string(), "<".to_string()), + ("<".to_string(), ">".to_string()), + ]), raw_code, rules, @@ -140,7 +161,7 @@ impl Runner { pub fn evaluate_command(&mut self, command: &str, local_memory: &mut [Vec; 2], local_memory_pointers: &mut [usize; 2], active_local_memory: usize) -> usize { let is_local = command.starts_with('\''); let command = if is_local { &command[1..] } else { command }; - let [(main_memory, main_memory_pointers, main_active_memory), (local_memory, local_memory_pointers, active_local_memory)] = if is_local { + let [(main_memory, main_memory_pointers, mut main_active_memory), (local_memory, local_memory_pointers, active_local_memory)] = if is_local { [ (local_memory, local_memory_pointers, active_local_memory), (&mut self.memory, &mut self.memory_pointers, self.active_memory), @@ -163,7 +184,11 @@ impl Runner { }; let new_command = split_command[2]; if num < 0 { - (self.opposite_commands.get(new_command).unwrap().as_str(), num * -1) + if let Some(opposite_command) = self.opposite_commands.get(new_command) { + (opposite_command.as_str(), num * -1) + } else { + (new_command, 0) + } } else { (new_command, num) } @@ -182,6 +207,32 @@ impl Runner { main_memory[main_active_memory][main_memory_pointers[main_active_memory]] -= main_memory[(main_active_memory as isize - 1).abs() as usize][main_memory_pointers[(main_active_memory as isize - 1).abs() as usize]] } + "*" => { + main_memory[main_active_memory][main_memory_pointers[main_active_memory]] *= + main_memory[(main_active_memory as isize - 1).abs() as usize][main_memory_pointers[(main_active_memory as isize - 1).abs() as usize]] + } + "/" => { + main_memory[main_active_memory][main_memory_pointers[main_active_memory]] /= + main_memory[(main_active_memory as isize - 1).abs() as usize][main_memory_pointers[(main_active_memory as isize - 1).abs() as usize]] + } + "`" => main_memory[main_active_memory][main_memory_pointers[main_active_memory]] = rand::thread_rng().gen(), + ">" => { + main_memory_pointers[main_active_memory] += 1; + if main_memory_pointers[main_active_memory] >= main_memory[main_active_memory].len() { + main_memory[main_active_memory].push(0.0); + } + } + "<" => { + if main_memory_pointers[main_active_memory] <= 0 { + main_memory[main_active_memory].insert(0, 0.0); + println!("{}You moved to the -1 index in memory. This will not crash the program, but should generally be avoided (you can use the --disable-warnings flag to disable all warnings or --disable-too-left-pointer-warning to disable this particular warning)", Utils::ansi_escape_text("93", "WARNING", INFO_PREFIX_LENGTH)); + } else { + main_memory_pointers[main_active_memory] -= 1; + } + } + "_" => main_memory[main_active_memory][main_memory_pointers[main_active_memory]] = main_memory[main_active_memory][main_memory_pointers[main_active_memory]].floor(), + "&" => main_memory[main_active_memory][main_memory_pointers[main_active_memory]] = main_memory[main_active_memory][main_memory_pointers[main_active_memory]].ceil(), + "^" => main_active_memory = (main_active_memory as isize - 1).abs() as usize, _ => {} } } From 5fa32f85bbce8e287d888a62bf21ae189e9513eb Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Sun, 31 Jul 2022 09:10:05 +0200 Subject: [PATCH 13/30] Add input and output --- the-golden/maumivu.au | 2 +- .../src/interpreter/versions/v0-1-0/main.rs | 72 ++++++++++++++++--- the-golden/src/utils.rs | 21 +++++- 3 files changed, 83 insertions(+), 12 deletions(-) diff --git a/the-golden/maumivu.au b/the-golden/maumivu.au index b054e1f..d92806c 100644 --- a/the-golden/maumivu.au +++ b/the-golden/maumivu.au @@ -1 +1 @@ -!!"a"[!![@![!!!@]!]!!!!!]: \ No newline at end of file +!|5|!"a"'[!'|-1|![@![!!!@]!]!!'!!'!']""+/`><<_&^|2|!^||~|3|!/&|-3|+$.>$,>$,\.<\,<\,: \ No newline at end of file diff --git a/the-golden/src/interpreter/versions/v0-1-0/main.rs b/the-golden/src/interpreter/versions/v0-1-0/main.rs index bfe0dec..8976d14 100644 --- a/the-golden/src/interpreter/versions/v0-1-0/main.rs +++ b/the-golden/src/interpreter/versions/v0-1-0/main.rs @@ -38,6 +38,8 @@ pub struct Runner { memory: [Vec; 2], memory_pointers: [usize; 2], active_memory: usize, + + input_cache: Option, } impl Runner { @@ -57,6 +59,10 @@ impl Runner { Regex::new(r"^'?\^").unwrap(), // switch active memory Regex::new(r"^'?\[@?").unwrap(), // (do-)while start Regex::new(r"^'?@?\]").unwrap(), // (do-)while end + Regex::new(r"^'?\$\.").unwrap(), // input number + Regex::new(r"^'?\$,").unwrap(), // input character + Regex::new(r"^'?\\\.").unwrap(), // output number + Regex::new(r"^'?\\,").unwrap(), // output character Regex::new(r"^:\r?\n?").unwrap(), // end of line Regex::new("\"[^\"]*\"").unwrap(), // whitespace ]; @@ -88,6 +94,8 @@ impl Runner { memory: [vec![0.0], vec![0.0]], memory_pointers: [0, 0], active_memory: 0, + + input_cache: None, } } @@ -145,7 +153,13 @@ impl Runner { let program_length = parser.commands.len(); while self.program_pointer < program_length { let command = &parser.commands[self.program_pointer]; - active_local_memory = self.evaluate_command(command, &mut local_memory, &mut local_memory_pointers, active_local_memory); + active_local_memory = match self.evaluate_command(command, &mut local_memory, &mut local_memory_pointers, active_local_memory) { + Ok(val) => val, + Err(e) => { + println!("{}{}", Utils::ansi_escape_text("91", "ERROR", INFO_PREFIX_LENGTH), e); + break; + } + }; } if self.flags.debug { println!("\n{}----- END OF CODE EXECUTION -----", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH)); @@ -158,7 +172,7 @@ impl Runner { } } - pub fn evaluate_command(&mut self, command: &str, local_memory: &mut [Vec; 2], local_memory_pointers: &mut [usize; 2], active_local_memory: usize) -> usize { + pub fn evaluate_command(&mut self, command: &str, local_memory: &mut [Vec; 2], local_memory_pointers: &mut [usize; 2], active_local_memory: usize) -> Result { let is_local = command.starts_with('\''); let command = if is_local { &command[1..] } else { command }; let [(main_memory, main_memory_pointers, mut main_active_memory), (local_memory, local_memory_pointers, active_local_memory)] = if is_local { @@ -172,7 +186,7 @@ impl Runner { (local_memory, local_memory_pointers, active_local_memory), ] }; - let split_command = command.split("|").collect::>(); + let split_command = command.split('|').collect::>(); let (command, repeat) = if split_command.len() == 3 { let count_str = split_command[1]; let num = if count_str.is_empty() { @@ -185,7 +199,7 @@ impl Runner { let new_command = split_command[2]; if num < 0 { if let Some(opposite_command) = self.opposite_commands.get(new_command) { - (opposite_command.as_str(), num * -1) + (opposite_command.as_str(), -num) } else { (new_command, 0) } @@ -223,7 +237,7 @@ impl Runner { } } "<" => { - if main_memory_pointers[main_active_memory] <= 0 { + if main_memory_pointers[main_active_memory] == 0 { main_memory[main_active_memory].insert(0, 0.0); println!("{}You moved to the -1 index in memory. This will not crash the program, but should generally be avoided (you can use the --disable-warnings flag to disable all warnings or --disable-too-left-pointer-warning to disable this particular warning)", Utils::ansi_escape_text("93", "WARNING", INFO_PREFIX_LENGTH)); } else { @@ -233,15 +247,53 @@ impl Runner { "_" => main_memory[main_active_memory][main_memory_pointers[main_active_memory]] = main_memory[main_active_memory][main_memory_pointers[main_active_memory]].floor(), "&" => main_memory[main_active_memory][main_memory_pointers[main_active_memory]] = main_memory[main_active_memory][main_memory_pointers[main_active_memory]].ceil(), "^" => main_active_memory = (main_active_memory as isize - 1).abs() as usize, + "$." => { + if self.input_cache.is_none() { + self.input_cache = Some(Utils::get_input_line()); + } + let input = &self.input_cache.clone().unwrap(); + self.input_cache = None; + main_memory[main_active_memory][main_memory_pointers[main_active_memory]] = match input.parse::() { + Ok(val) => val, + Err(e) => { + return Err(format!("Failed to convert {} from input to a number: {}", input, e)); + } + } + } + "$," => { + if self.input_cache.is_none() { + self.input_cache = Some(Utils::get_input_line()); + } + let input = &self.input_cache.clone().unwrap(); + let (char, remainder) = Utils::next_char(input); + self.input_cache = if !remainder.is_empty() { Some(remainder.to_string()) } else { None }; + main_memory[main_active_memory][main_memory_pointers[main_active_memory]] = (char as u32) as f64; + } + "\\." => { + print!("{}", main_memory[main_active_memory][main_memory_pointers[main_active_memory]]); + if let Err(e) = Utils::flush_console() { + println!("{}{}", Utils::ansi_escape_text("91", "ERROR", INFO_PREFIX_LENGTH), e); + } + } + "\\," => match char::from_u32(main_memory[main_active_memory][main_memory_pointers[main_active_memory]].floor() as u32) { + Some(c) => { + print!("{}", c); + if let Err(e) = Utils::flush_console() { + println!("{}{}", Utils::ansi_escape_text("91", "ERROR", INFO_PREFIX_LENGTH), e); + } + } + None => { + return Err(format!( + "Failed to convert {} from memory to a character", + main_memory[main_active_memory][main_memory_pointers[main_active_memory]].floor() + )); + } + }, _ => {} } } self.program_pointer += 1; self.active_memory = if is_local { active_local_memory } else { main_active_memory }; - if is_local { - main_active_memory - } else { - active_local_memory - } + Ok(if is_local { main_active_memory } else { active_local_memory }) } } diff --git a/the-golden/src/utils.rs b/the-golden/src/utils.rs index 743cc34..9c800d2 100644 --- a/the-golden/src/utils.rs +++ b/the-golden/src/utils.rs @@ -1,3 +1,5 @@ +use std::io::Write; + pub struct Utils {} impl Utils { @@ -9,4 +11,21 @@ impl Utils { res += "\x1b[0m"; res } -} \ No newline at end of file + + pub fn get_input_line() -> String { + let mut input = String::new(); + std::io::stdin().read_line(&mut input).unwrap(); + input.trim().to_string() + } + + pub fn next_char(s: &str) -> (char, &str) { + match s.chars().next() { + Some(c) => (c, s.split_at(c.len_utf8()).1), + None => ('\0', s.split_at(0).1), + } + } + + pub fn flush_console() -> std::io::Result<()> { + std::io::stdout().flush() + } +} From eda0ddda4e224de077afdcfe9ac21ff9b1d197b7 Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Fri, 5 Aug 2022 12:28:01 +0200 Subject: [PATCH 14/30] Add loops support --- .../src/interpreter/versions/v0-1-0/main.rs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/the-golden/src/interpreter/versions/v0-1-0/main.rs b/the-golden/src/interpreter/versions/v0-1-0/main.rs index 8976d14..7d24fa4 100644 --- a/the-golden/src/interpreter/versions/v0-1-0/main.rs +++ b/the-golden/src/interpreter/versions/v0-1-0/main.rs @@ -289,6 +289,35 @@ impl Runner { )); } }, + "[" => { + if main_memory[main_active_memory][main_memory_pointers[main_active_memory]] == 0.0 { + if let Some(index) = self.loops.iter().position(|value| *value == self.program_pointer) { + self.loops.remove(index); + } + self.program_pointer = *self.brackets.get(&self.program_pointer).unwrap(); + } else if !self.loops.contains(&self.program_pointer) { + self.loops.push(self.program_pointer); + } + } + "]" | "@]" => { + if main_memory[main_active_memory][main_memory_pointers[main_active_memory]] == 0.0 { + if let Some(index) = self.loops.iter().position(|value| *value == self.program_pointer) { + self.loops.remove(index); + } + } else { + self.program_pointer = *self.brackets.get(&self.program_pointer).unwrap(); + } + } + "[@" => { + if main_memory[main_active_memory][main_memory_pointers[main_active_memory]] == 0.0 && self.loops.contains(&self.program_pointer) { + if let Some(index) = self.loops.iter().position(|value| *value == self.program_pointer) { + self.loops.remove(index); + } + self.program_pointer = *self.brackets.get(&self.program_pointer).unwrap(); + } else if !self.loops.contains(&self.program_pointer) { + self.loops.push(self.program_pointer); + } + } _ => {} } } From c68eefe5929d1069083757de282fe000dd28fbdd Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Fri, 5 Aug 2022 16:37:27 +0200 Subject: [PATCH 15/30] Finish up commands rules and execution --- .../src/interpreter/versions/v0-1-0/lexer.rs | 26 ++++-- .../src/interpreter/versions/v0-1-0/main.rs | 84 ++++++++++++++----- 2 files changed, 81 insertions(+), 29 deletions(-) diff --git a/the-golden/src/interpreter/versions/v0-1-0/lexer.rs b/the-golden/src/interpreter/versions/v0-1-0/lexer.rs index 68b3a4c..93b6f24 100644 --- a/the-golden/src/interpreter/versions/v0-1-0/lexer.rs +++ b/the-golden/src/interpreter/versions/v0-1-0/lexer.rs @@ -2,8 +2,8 @@ use lazy_static::lazy_static; use regex::Regex; lazy_static! { - static ref COMMENT_REGEX: Regex = Regex::new("^\"").unwrap(); - static ref NEW_LINE_REGEX: Regex = Regex::new(r"^\r?\n").unwrap(); + static ref COMMENT_REGEX: Regex = Regex::new("^\"").unwrap(); + static ref NEW_LINE_REGEX: Regex = Regex::new(r"^\r?\n").unwrap(); } #[derive(Clone)] @@ -14,12 +14,20 @@ pub struct Lexer { column: usize, comment: bool, file_path: std::path::PathBuf, - position: usize + position: usize, } impl Lexer { pub fn new(text: String, rules: Vec, file_path: std::path::PathBuf) -> Self { - Self { text, rules, line: 1, column: 1, comment: false, file_path, position: 0 } + Self { + text, + rules, + line: 1, + column: 1, + comment: false, + file_path, + position: 0, + } } pub fn next(&mut self) -> Result, String> { @@ -50,6 +58,12 @@ impl Lexer { } } } - Err(format!("Syntax error at {}:{} in {:?} ({:?})", self.line, self.column, self.file_path.file_name().unwrap(), self.file_path.as_path())) + Err(format!( + "Syntax error at {}:{} in {:?} ({:?})", + self.line, + self.column, + self.file_path.file_name().unwrap(), + self.file_path.as_path() + )) } -} \ No newline at end of file +} diff --git a/the-golden/src/interpreter/versions/v0-1-0/main.rs b/the-golden/src/interpreter/versions/v0-1-0/main.rs index 7d24fa4..de0b3c4 100644 --- a/the-golden/src/interpreter/versions/v0-1-0/main.rs +++ b/the-golden/src/interpreter/versions/v0-1-0/main.rs @@ -45,26 +45,32 @@ pub struct Runner { impl Runner { pub fn new(raw_code: String, code_path: std::path::PathBuf, flags: Flags) -> Self { let rules = vec![ - Regex::new(r"^'?(\|-?[0-9]*\|)*!").unwrap(), // increment - Regex::new(r"^'?(\|-?[0-9]*\|)*~").unwrap(), // decrement - Regex::new(r"^'?(\|-?[0-9]*\|)*\+").unwrap(), // add - Regex::new(r"^'?(\|-?[0-9]*\|)*-").unwrap(), // subtract - Regex::new(r"^'?(\|-?[0-9]*\|)*\*").unwrap(), // multiply - Regex::new(r"^'?(\|-?[0-9]*\|)*/").unwrap(), // divide - Regex::new(r"^'?`").unwrap(), // generate a random number from 0 (inclusive) to 1 (exclusive) - Regex::new(r"^'?>").unwrap(), // move right - Regex::new(r"^'?<").unwrap(), // move left - Regex::new(r"^'?_").unwrap(), // floor - Regex::new(r"^'?&").unwrap(), // ceil - Regex::new(r"^'?\^").unwrap(), // switch active memory - Regex::new(r"^'?\[@?").unwrap(), // (do-)while start - Regex::new(r"^'?@?\]").unwrap(), // (do-)while end - Regex::new(r"^'?\$\.").unwrap(), // input number - Regex::new(r"^'?\$,").unwrap(), // input character - Regex::new(r"^'?\\\.").unwrap(), // output number - Regex::new(r"^'?\\,").unwrap(), // output character - Regex::new(r"^:\r?\n?").unwrap(), // end of line - Regex::new("\"[^\"]*\"").unwrap(), // whitespace + Regex::new(r"^'?(\|-?[0-9]*\|)*!").unwrap(), // increment + Regex::new(r"^'?(\|-?[0-9]*\|)*~").unwrap(), // decrement + Regex::new(r"^'?(\|-?[0-9]*\|)*\+").unwrap(), // add + Regex::new(r"^'?(\|-?[0-9]*\|)*-").unwrap(), // subtract + Regex::new(r"^'?(\|-?[0-9]*\|)*\*").unwrap(), // multiply + Regex::new(r"^'?(\|-?[0-9]*\|)*/").unwrap(), // divide + Regex::new(r"^'?`").unwrap(), // generate a random number from 0 (inclusive) to 1 (exclusive) + Regex::new(r"^'?(\|-?[0-9]*\|)*>").unwrap(), // move right + Regex::new(r"^'?(\|-?[0-9]*\|)*<").unwrap(), // move left + Regex::new(r"^'?_").unwrap(), // floor + Regex::new(r"^'?&").unwrap(), // ceil + Regex::new(r"^'?\^").unwrap(), // switch active memory + Regex::new(r"^'?\[@?").unwrap(), // (do-)while start + Regex::new(r"^'?@?\]").unwrap(), // (do-)while end + Regex::new(r"^'?\$\.").unwrap(), // input number + Regex::new(r"^'?\$,").unwrap(), // input character + Regex::new(r"^'?\\\.").unwrap(), // output number + Regex::new(r"^'?\\,").unwrap(), // output character + Regex::new(r"^'?(\|-?[0-9]*\|)*\?=").unwrap(), // break if active memory address is equal to inactive memory address + Regex::new(r"^'?(\|-?[0-9]*\|)*\?>").unwrap(), // break if active memory address is greater than inactive memory address + Regex::new(r"^'?(\|-?[0-9]*\|)*\?<").unwrap(), // break if active memory address is less than inactive memory address + Regex::new(r"^'?\?\?").unwrap(), // set current active memory address to its index + Regex::new(r"^'?;").unwrap(), // swap main and local memory addresses + Regex::new(r"^:\r?\n?").unwrap(), // end of line + Regex::new("^\"[^\"]*\"").unwrap(), // comments + Regex::new(r"^[ \t\f\v]").unwrap(), // whitespace ]; Self { flags, @@ -175,7 +181,7 @@ impl Runner { pub fn evaluate_command(&mut self, command: &str, local_memory: &mut [Vec; 2], local_memory_pointers: &mut [usize; 2], active_local_memory: usize) -> Result { let is_local = command.starts_with('\''); let command = if is_local { &command[1..] } else { command }; - let [(main_memory, main_memory_pointers, mut main_active_memory), (local_memory, local_memory_pointers, active_local_memory)] = if is_local { + let [(main_memory, main_memory_pointers, mut main_active_memory), (local_memory, local_memory_pointers, local_active_memory)] = if is_local { [ (local_memory, local_memory_pointers, active_local_memory), (&mut self.memory, &mut self.memory_pointers, self.active_memory), @@ -318,11 +324,43 @@ impl Runner { self.loops.push(self.program_pointer); } } + "?=" => { + let inactive_memory = (main_active_memory as isize - 1).abs() as usize; + if main_memory[main_active_memory][main_memory_pointers[main_active_memory]] == main_memory[inactive_memory][main_memory_pointers[inactive_memory]] { + if let Some(current_loop) = self.loops.pop() { + self.program_pointer = *self.brackets.get(¤t_loop).unwrap(); + } + } + } + "?>" => { + let inactive_memory = (main_active_memory as isize - 1).abs() as usize; + if main_memory[main_active_memory][main_memory_pointers[main_active_memory]] > main_memory[inactive_memory][main_memory_pointers[inactive_memory]] { + if let Some(current_loop) = self.loops.pop() { + self.program_pointer = *self.brackets.get(¤t_loop).unwrap(); + } + } + } + "?<" => { + let inactive_memory = (main_active_memory as isize - 1).abs() as usize; + if main_memory[main_active_memory][main_memory_pointers[main_active_memory]] < main_memory[inactive_memory][main_memory_pointers[inactive_memory]] { + if let Some(current_loop) = self.loops.pop() { + self.program_pointer = *self.brackets.get(¤t_loop).unwrap(); + } + } + } + "??" => { + main_memory[main_active_memory][main_memory_pointers[main_active_memory]] = main_memory_pointers[main_active_memory] as f64; + } + ";" => { + let holder = local_memory[local_active_memory][local_memory_pointers[local_active_memory]]; + local_memory[local_active_memory][local_memory_pointers[local_active_memory]] = main_memory[main_active_memory][main_memory_pointers[main_active_memory]]; + main_memory[main_active_memory][main_memory_pointers[main_active_memory]] = holder; + } _ => {} } } self.program_pointer += 1; - self.active_memory = if is_local { active_local_memory } else { main_active_memory }; - Ok(if is_local { main_active_memory } else { active_local_memory }) + self.active_memory = if is_local { local_active_memory } else { main_active_memory }; + Ok(if is_local { main_active_memory } else { local_active_memory }) } } From 24f594ed82586f494a978e51c81db3d4da52060f Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Fri, 5 Aug 2022 16:47:57 +0200 Subject: [PATCH 16/30] Add heavy debug logs --- .../src/interpreter/versions/v0-1-0/main.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/the-golden/src/interpreter/versions/v0-1-0/main.rs b/the-golden/src/interpreter/versions/v0-1-0/main.rs index de0b3c4..55fac2f 100644 --- a/the-golden/src/interpreter/versions/v0-1-0/main.rs +++ b/the-golden/src/interpreter/versions/v0-1-0/main.rs @@ -180,6 +180,7 @@ impl Runner { pub fn evaluate_command(&mut self, command: &str, local_memory: &mut [Vec; 2], local_memory_pointers: &mut [usize; 2], active_local_memory: usize) -> Result { let is_local = command.starts_with('\''); + let raw_command = command; let command = if is_local { &command[1..] } else { command }; let [(main_memory, main_memory_pointers, mut main_active_memory), (local_memory, local_memory_pointers, local_active_memory)] = if is_local { [ @@ -361,6 +362,20 @@ impl Runner { } self.program_pointer += 1; self.active_memory = if is_local { local_active_memory } else { main_active_memory }; + if self.flags.debug_heavy { + println!("\n{}Raw command: {:?}", Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), raw_command); + println!("{}Command executed: {:?}", Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), command); + println!( + "{}Command was executed on local memory: {:?}", + Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), + is_local + ); + println!("{}Command repetitions: {:?}", Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), repeat); + println!("{}Global memory: {:?}", Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), self.memory); + println!("{}Global memory pointers: {:?}", Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), self.memory_pointers); + println!("{}Active global memory: {:?}", Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), self.active_memory); + std::thread::sleep(std::time::Duration::from_millis(500)); + } Ok(if is_local { main_active_memory } else { local_active_memory }) } } From 2d7e0a53ac4568dd22b5bd1f2466398500a30b57 Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Sat, 6 Aug 2022 11:56:30 +0200 Subject: [PATCH 17/30] Add an option to disable warnings --- the-golden/src/flags.rs | 53 ++++++++++++------- .../src/interpreter/versions/v0-1-0/main.rs | 4 +- the-golden/src/main.rs | 21 ++++---- 3 files changed, 48 insertions(+), 30 deletions(-) diff --git a/the-golden/src/flags.rs b/the-golden/src/flags.rs index b1b6209..4fab175 100644 --- a/the-golden/src/flags.rs +++ b/the-golden/src/flags.rs @@ -1,24 +1,33 @@ +#[derive(Clone, Debug)] +pub struct Warnings { + pub too_left_pointer: bool, +} + #[derive(Clone, Debug)] pub struct Flags { + pub disabled_warnings: Warnings, + pub action: Option, pub code_path: Option, pub debug: bool, pub debug_heavy: bool, pub no_console: bool, pub raw_code_to_run: Option, - pub version: Option + pub version: Option, } impl Flags { pub fn new() -> Self { Self { + disabled_warnings: Warnings { too_left_pointer: false }, + action: None, code_path: None, debug: false, debug_heavy: false, no_console: false, raw_code_to_run: None, - version: None + version: None, } } @@ -33,22 +42,30 @@ impl Flags { "--debug-heavy" => { self.debug = true; self.debug_heavy = true; - }, + } "--hide-console" => self.no_console = true, - "--version" => if self.version.is_none() && i + 1 < args_count { - self.version = Some(args[i+1].clone()); - }, - "-" => if self.raw_code_to_run.is_none() && i + 1 < args_count { - self.raw_code_to_run = Some(args[i+1].clone()); - }, - "run" => if self.action.is_none() { - self.action = Some(String::from("run")); - if self.code_path.is_none() && i + 1 < args_count && !args[i+1].starts_with('-') { - let mut path = std::path::PathBuf::from(args[0].clone()); - path.pop(); - path.push(args[i+1].clone()); - path.set_file_name("maumivu.au"); - self.code_path = Some(path); + "--version" => { + if self.version.is_none() && i + 1 < args_count { + self.version = Some(args[i + 1].clone()); + } + } + "--disable-warnings" => self.disabled_warnings = Warnings { too_left_pointer: true }, + "--disable-too-left-pointer-warning" => self.disabled_warnings.too_left_pointer = true, + "-" => { + if self.raw_code_to_run.is_none() && i + 1 < args_count { + self.raw_code_to_run = Some(args[i + 1].clone()); + } + } + "run" => { + if self.action.is_none() { + self.action = Some(String::from("run")); + if self.code_path.is_none() && i + 1 < args_count && !args[i + 1].starts_with('-') { + let mut path = std::path::PathBuf::from(args[0].clone()); + path.pop(); + path.push(args[i + 1].clone()); + path.set_file_name("maumivu.au"); + self.code_path = Some(path); + } } } _ => {} @@ -56,4 +73,4 @@ impl Flags { i += 1; } } -} \ No newline at end of file +} diff --git a/the-golden/src/interpreter/versions/v0-1-0/main.rs b/the-golden/src/interpreter/versions/v0-1-0/main.rs index 55fac2f..4b8fcbb 100644 --- a/the-golden/src/interpreter/versions/v0-1-0/main.rs +++ b/the-golden/src/interpreter/versions/v0-1-0/main.rs @@ -246,7 +246,9 @@ impl Runner { "<" => { if main_memory_pointers[main_active_memory] == 0 { main_memory[main_active_memory].insert(0, 0.0); - println!("{}You moved to the -1 index in memory. This will not crash the program, but should generally be avoided (you can use the --disable-warnings flag to disable all warnings or --disable-too-left-pointer-warning to disable this particular warning)", Utils::ansi_escape_text("93", "WARNING", INFO_PREFIX_LENGTH)); + if !self.flags.disabled_warnings.too_left_pointer { + println!("{}You moved to the -1 index in memory. This will not crash the program, but should generally be avoided (you can use the --disable-warnings flag to disable all warnings or --disable-too-left-pointer-warning to disable this particular warning)", Utils::ansi_escape_text("93", "WARNING", INFO_PREFIX_LENGTH)); + } } else { main_memory_pointers[main_active_memory] -= 1; } diff --git a/the-golden/src/main.rs b/the-golden/src/main.rs index c716ad5..b5d47f0 100644 --- a/the-golden/src/main.rs +++ b/the-golden/src/main.rs @@ -1,11 +1,14 @@ use dotenv::dotenv; use std::env; -#[path = "./flags.rs"] mod flags; +#[path = "./flags.rs"] +mod flags; pub use flags::Flags; -#[path = "./interpreter/interpreter.rs"] mod interpreter; +#[path = "./interpreter/interpreter.rs"] +mod interpreter; use interpreter::Interpreter; -#[path = "./utils.rs"] mod utils; +#[path = "./utils.rs"] +mod utils; pub use utils::Utils; fn main() { @@ -20,19 +23,19 @@ fn main() { let mut flags_handler = Flags::new(); flags_handler.parse(&args); - let mut action = String::new(); + let mut _action = String::new(); let mut version = String::from("latest"); let mut code = String::new(); let mut code_path = std::path::PathBuf::new(); let cloned_flags = flags_handler.clone(); if let Some(a) = cloned_flags.action { - action = a; + _action = a; } if let Some(path) = cloned_flags.code_path { code = match std::fs::read_to_string(&path) { Ok(c) => c, - Err(e) => panic!("{}", e) + Err(e) => panic!("{}", e), }; code_path = path; } else if let Some(code_to_run) = cloned_flags.raw_code_to_run { @@ -50,9 +53,5 @@ fn main() { if cloned_flags.no_console && cfg!(target_os = "windows") { winconsole::window::hide(); } - if action == *"run" { - Interpreter::new(version, code, code_path, flags_handler).run(); - } else { - todo!() - } + Interpreter::new(version, code, code_path, flags_handler).run(); } From 69153846b7222fa29dae38a4a9786218f6e773d3 Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Sat, 6 Aug 2022 11:58:21 +0200 Subject: [PATCH 18/30] Use the standard library implementation for swapping values --- the-golden/src/interpreter/versions/v0-1-0/main.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/the-golden/src/interpreter/versions/v0-1-0/main.rs b/the-golden/src/interpreter/versions/v0-1-0/main.rs index 4b8fcbb..fdd39ef 100644 --- a/the-golden/src/interpreter/versions/v0-1-0/main.rs +++ b/the-golden/src/interpreter/versions/v0-1-0/main.rs @@ -355,9 +355,10 @@ impl Runner { main_memory[main_active_memory][main_memory_pointers[main_active_memory]] = main_memory_pointers[main_active_memory] as f64; } ";" => { - let holder = local_memory[local_active_memory][local_memory_pointers[local_active_memory]]; - local_memory[local_active_memory][local_memory_pointers[local_active_memory]] = main_memory[main_active_memory][main_memory_pointers[main_active_memory]]; - main_memory[main_active_memory][main_memory_pointers[main_active_memory]] = holder; + std::mem::swap( + &mut local_memory[local_active_memory][local_memory_pointers[local_active_memory]], + &mut main_memory[main_active_memory][main_memory_pointers[main_active_memory]], + ); } _ => {} } From d0a023eca2da912878dbf7fda1ff06846e9abb5f Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Sat, 6 Aug 2022 12:04:21 +0200 Subject: [PATCH 19/30] Remove the ?? command from the first version This is to match the original interpreter with the versions, ?? came in the 2nd one --- the-golden/src/interpreter/versions/v0-1-0/main.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/the-golden/src/interpreter/versions/v0-1-0/main.rs b/the-golden/src/interpreter/versions/v0-1-0/main.rs index fdd39ef..d2d5694 100644 --- a/the-golden/src/interpreter/versions/v0-1-0/main.rs +++ b/the-golden/src/interpreter/versions/v0-1-0/main.rs @@ -66,7 +66,6 @@ impl Runner { Regex::new(r"^'?(\|-?[0-9]*\|)*\?=").unwrap(), // break if active memory address is equal to inactive memory address Regex::new(r"^'?(\|-?[0-9]*\|)*\?>").unwrap(), // break if active memory address is greater than inactive memory address Regex::new(r"^'?(\|-?[0-9]*\|)*\?<").unwrap(), // break if active memory address is less than inactive memory address - Regex::new(r"^'?\?\?").unwrap(), // set current active memory address to its index Regex::new(r"^'?;").unwrap(), // swap main and local memory addresses Regex::new(r"^:\r?\n?").unwrap(), // end of line Regex::new("^\"[^\"]*\"").unwrap(), // comments @@ -351,9 +350,6 @@ impl Runner { } } } - "??" => { - main_memory[main_active_memory][main_memory_pointers[main_active_memory]] = main_memory_pointers[main_active_memory] as f64; - } ";" => { std::mem::swap( &mut local_memory[local_active_memory][local_memory_pointers[local_active_memory]], From 1349006f0457daf1bf3604bad39d4ee549a92395 Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Sat, 6 Aug 2022 12:04:38 +0200 Subject: [PATCH 20/30] Add version 0.2.0 with the ?? command --- .../src/interpreter/versions/handler.rs | 16 +- .../versions/v0-2-0/brackets_matcher.rs | 77 ++++ .../src/interpreter/versions/v0-2-0/lexer.rs | 69 ++++ .../src/interpreter/versions/v0-2-0/main.rs | 384 ++++++++++++++++++ .../src/interpreter/versions/v0-2-0/parser.rs | 35 ++ .../interpreter/versions/v0-2-0/validator.rs | 31 ++ 6 files changed, 607 insertions(+), 5 deletions(-) create mode 100644 the-golden/src/interpreter/versions/v0-2-0/brackets_matcher.rs create mode 100644 the-golden/src/interpreter/versions/v0-2-0/lexer.rs create mode 100644 the-golden/src/interpreter/versions/v0-2-0/main.rs create mode 100644 the-golden/src/interpreter/versions/v0-2-0/parser.rs create mode 100644 the-golden/src/interpreter/versions/v0-2-0/validator.rs diff --git a/the-golden/src/interpreter/versions/handler.rs b/the-golden/src/interpreter/versions/handler.rs index 5074cb5..213fa95 100644 --- a/the-golden/src/interpreter/versions/handler.rs +++ b/the-golden/src/interpreter/versions/handler.rs @@ -1,14 +1,19 @@ use crate::Flags; -#[path = "./v0-1-0/main.rs"] mod v0_1_0; +#[path = "./v0-1-0/main.rs"] +mod v0_1_0; +#[path = "./v0-2-0/main.rs"] +mod v0_2_0; pub struct Handler { - versions: [String; 1] + versions: [String; 2], } impl Handler { pub fn new() -> Self { - Self { versions: [String::from("0.1.0")] } + Self { + versions: [String::from("0.1.0"), String::from("0.2.0")], + } } pub fn parse_version(&self, mut version: String) -> String { @@ -22,7 +27,8 @@ impl Handler { pub fn run(&self, version: String, code: String, code_path: std::path::PathBuf, flags: Flags) { match version.as_str() { "0.1.0" => v0_1_0::Runner::new(code, code_path, flags).run(), - _ => panic!("Couldn't launch version {}", &version) + "0.2.0" => v0_2_0::Runner::new(code, code_path, flags).run(), + _ => panic!("Couldn't launch version {}", &version), } } -} \ No newline at end of file +} diff --git a/the-golden/src/interpreter/versions/v0-2-0/brackets_matcher.rs b/the-golden/src/interpreter/versions/v0-2-0/brackets_matcher.rs new file mode 100644 index 0000000..4519556 --- /dev/null +++ b/the-golden/src/interpreter/versions/v0-2-0/brackets_matcher.rs @@ -0,0 +1,77 @@ +use std::collections::HashMap; + +pub struct BracketsMatcher { + pub brackets: HashMap>, + brackets_mem: Vec<(String, usize, isize)>, + bracket_keys: HashMap, + ending_brackets_keys: HashMap +} + +impl BracketsMatcher { + pub fn new() -> Self { + Self { + brackets: HashMap::from([ + ("while".to_string(), HashMap::new()), + ("do_while".to_string(), HashMap::new()), + ("while_local".to_string(), HashMap::new()), + ("do_while_local".to_string(), HashMap::new()) + ]), + brackets_mem: vec![], + bracket_keys: HashMap::from([ + ("[".to_string(), "while".to_string()), + ("]".to_string(), "while".to_string()), + ("[@".to_string(), "do_while".to_string()), + ("@]".to_string(), "do_while".to_string()), + ("'[".to_string(), "while_local".to_string()), + ("']".to_string(), "while_local".to_string()), + ("'[@".to_string(), "do_while_local".to_string()), + ("'@]".to_string(), "do_while_local".to_string()), + ]), + ending_brackets_keys: HashMap::from([ + ("while".to_string(), "]".to_string()), + ("do_while".to_string(), "@]".to_string()), + ("while_local".to_string(), "']".to_string()), + ("do_while_local".to_string(), "'@]".to_string()), + ]) + } + } + + pub fn match_brackets(&mut self, code: &[String]) { + let starting_brackets = ["[", "[@", "'[", "'[@", ]; + let ending_brackets = ["]", "@]", "']", "'@]"]; + for (i, command) in code.iter().enumerate() { + let command_str = command.as_str(); + if !starting_brackets.contains(&command_str) && !ending_brackets.contains(&command_str) { + continue; + } + if starting_brackets.contains(&command_str) { + self.brackets_mem.push((command.clone(), i, 0)); + } + let mut keys_to_remove = vec![]; + for key in 0..self.brackets_mem.len() { + self.brackets_mem[key].2 += self.num_equals(&self.brackets_mem[key].0, command); + let wanted_end = self.ending_brackets_keys.get(self.bracket_keys.get(&self.brackets_mem[key].0).unwrap()).unwrap(); + if self.brackets_mem[key].2 == 0 && command == wanted_end { + let category = self.bracket_keys.get(wanted_end).unwrap(); + let sub_map = self.brackets.get_mut(category).unwrap(); + sub_map.insert(self.brackets_mem[key].1, i); + sub_map.insert(i, self.brackets_mem[key].1); + keys_to_remove.push(key); + } + } + for key in keys_to_remove { + self.brackets_mem.remove(key); + } + } + } + + fn num_equals(&self, left: &String, right: &String) -> isize { + if self.bracket_keys.get(left) != self.bracket_keys.get(right) { + return 0; + } + if left == right { + return 1; + } + -1 + } +} \ No newline at end of file diff --git a/the-golden/src/interpreter/versions/v0-2-0/lexer.rs b/the-golden/src/interpreter/versions/v0-2-0/lexer.rs new file mode 100644 index 0000000..93b6f24 --- /dev/null +++ b/the-golden/src/interpreter/versions/v0-2-0/lexer.rs @@ -0,0 +1,69 @@ +use lazy_static::lazy_static; +use regex::Regex; + +lazy_static! { + static ref COMMENT_REGEX: Regex = Regex::new("^\"").unwrap(); + static ref NEW_LINE_REGEX: Regex = Regex::new(r"^\r?\n").unwrap(); +} + +#[derive(Clone)] +pub struct Lexer { + text: String, + rules: Vec, + line: usize, + column: usize, + comment: bool, + file_path: std::path::PathBuf, + position: usize, +} + +impl Lexer { + pub fn new(text: String, rules: Vec, file_path: std::path::PathBuf) -> Self { + Self { + text, + rules, + line: 1, + column: 1, + comment: false, + file_path, + position: 0, + } + } + + pub fn next(&mut self) -> Result, String> { + let text = &self.text.as_str()[self.position..]; + if text.is_empty() { + return Ok(None); + } + if text == "\"" { + self.comment = !self.comment; + } + if self.comment { + return Ok(None); + } + for rule in &self.rules { + if let Some(captures) = rule.captures(text) { + if let Some(capture) = captures.get(0) { + let (command_line, command_column) = (self.line, self.column); + let command = capture.as_str(); + let command_length = capture.end() - capture.start(); + self.position += command_length; + if command.contains('\n') { + self.line += command.matches('\n').count(); + self.column += command.split('\n').last().unwrap().len(); + } else { + self.column += command_length; + } + return Ok(Some((command.to_string(), command_line, command_column, self.file_path.clone()))); + } + } + } + Err(format!( + "Syntax error at {}:{} in {:?} ({:?})", + self.line, + self.column, + self.file_path.file_name().unwrap(), + self.file_path.as_path() + )) + } +} diff --git a/the-golden/src/interpreter/versions/v0-2-0/main.rs b/the-golden/src/interpreter/versions/v0-2-0/main.rs new file mode 100644 index 0000000..fdd39ef --- /dev/null +++ b/the-golden/src/interpreter/versions/v0-2-0/main.rs @@ -0,0 +1,384 @@ +use std::collections::HashMap; + +use crate::Flags; +use rand::Rng; +use regex::Regex; + +#[path = "./brackets_matcher.rs"] +mod brackets_matcher; +use brackets_matcher::BracketsMatcher; +#[path = "./lexer.rs"] +mod lexer; +pub use lexer::Lexer; +#[path = "./parser.rs"] +mod parser; +use crate::Utils; +pub use parser::Parser; +#[path = "./validator.rs"] +mod validator; +use validator::Validator; + +const INFO_PREFIX_LENGTH: usize = 12; + +pub struct Runner { + flags: Flags, + + brackets_matcher: BracketsMatcher, + brackets_categorised: HashMap>, + opposite_commands: HashMap, + + brackets: HashMap, + raw_code: String, + rules: Vec, + code_path: std::path::PathBuf, + + program_pointer: usize, + + loops: Vec, + memory: [Vec; 2], + memory_pointers: [usize; 2], + active_memory: usize, + + input_cache: Option, +} + +impl Runner { + pub fn new(raw_code: String, code_path: std::path::PathBuf, flags: Flags) -> Self { + let rules = vec![ + Regex::new(r"^'?(\|-?[0-9]*\|)*!").unwrap(), // increment + Regex::new(r"^'?(\|-?[0-9]*\|)*~").unwrap(), // decrement + Regex::new(r"^'?(\|-?[0-9]*\|)*\+").unwrap(), // add + Regex::new(r"^'?(\|-?[0-9]*\|)*-").unwrap(), // subtract + Regex::new(r"^'?(\|-?[0-9]*\|)*\*").unwrap(), // multiply + Regex::new(r"^'?(\|-?[0-9]*\|)*/").unwrap(), // divide + Regex::new(r"^'?`").unwrap(), // generate a random number from 0 (inclusive) to 1 (exclusive) + Regex::new(r"^'?(\|-?[0-9]*\|)*>").unwrap(), // move right + Regex::new(r"^'?(\|-?[0-9]*\|)*<").unwrap(), // move left + Regex::new(r"^'?_").unwrap(), // floor + Regex::new(r"^'?&").unwrap(), // ceil + Regex::new(r"^'?\^").unwrap(), // switch active memory + Regex::new(r"^'?\[@?").unwrap(), // (do-)while start + Regex::new(r"^'?@?\]").unwrap(), // (do-)while end + Regex::new(r"^'?\$\.").unwrap(), // input number + Regex::new(r"^'?\$,").unwrap(), // input character + Regex::new(r"^'?\\\.").unwrap(), // output number + Regex::new(r"^'?\\,").unwrap(), // output character + Regex::new(r"^'?(\|-?[0-9]*\|)*\?=").unwrap(), // break if active memory address is equal to inactive memory address + Regex::new(r"^'?(\|-?[0-9]*\|)*\?>").unwrap(), // break if active memory address is greater than inactive memory address + Regex::new(r"^'?(\|-?[0-9]*\|)*\?<").unwrap(), // break if active memory address is less than inactive memory address + Regex::new(r"^'?\?\?").unwrap(), // set current active memory address to its index + Regex::new(r"^'?;").unwrap(), // swap main and local memory addresses + Regex::new(r"^:\r?\n?").unwrap(), // end of line + Regex::new("^\"[^\"]*\"").unwrap(), // comments + Regex::new(r"^[ \t\f\v]").unwrap(), // whitespace + ]; + Self { + flags, + + brackets_matcher: BracketsMatcher::new(), + + brackets: HashMap::new(), + brackets_categorised: HashMap::new(), + opposite_commands: HashMap::from([ + ("!".to_string(), "~".to_string()), + ("~".to_string(), "!".to_string()), + ("+".to_string(), "-".to_string()), + ("-".to_string(), "+".to_string()), + ("*".to_string(), "/".to_string()), + ("/".to_string(), "*".to_string()), + (">".to_string(), "<".to_string()), + ("<".to_string(), ">".to_string()), + ]), + + raw_code, + rules, + code_path, + + program_pointer: 0, + + loops: vec![], + memory: [vec![0.0], vec![0.0]], + memory_pointers: [0, 0], + active_memory: 0, + + input_cache: None, + } + } + + pub fn run(&mut self) { + if self.flags.debug { + println!("{}Running version 0.1.0", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH)); + println!("{}Raw code: {}", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH), self.raw_code); + } + let lexer = Lexer::new(self.raw_code.clone(), self.rules.clone(), self.code_path.clone()); + let validator_result = Validator::run(lexer.clone(), self.flags.debug_heavy); + if let Err(e) = validator_result { + println!("{}{}", Utils::ansi_escape_text("91", "ERROR", INFO_PREFIX_LENGTH), e); + return; + } + if self.flags.debug { + println!("{}Valid code!", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH)); + } + let mut parser = Parser::new(); + let parser_result = parser.run(lexer); + if let Err(e) = parser_result { + println!("{}{}", Utils::ansi_escape_text("91", "ERROR", INFO_PREFIX_LENGTH), e); + return; + } + if self.flags.debug { + println!("{}Parsed commands: {:?}", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH), parser.commands); + } + self.brackets_matcher.match_brackets(&parser.commands); + self.brackets_categorised = self.brackets_matcher.brackets.clone(); + if self.flags.debug_heavy { + println!( + "{}Matched brackets: {:?}", + Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), + self.brackets_matcher.brackets + ); + } + for loop_type in self.brackets_categorised.keys() { + let map = self.brackets_categorised.get(loop_type).unwrap(); + for (key, value) in map.iter() { + self.brackets.insert(*key, *value); + } + } + if self.flags.debug_heavy { + println!( + "{}Matched brackets uncategorised: {:?}", + Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), + self.brackets + ); + } + if self.flags.debug { + println!("{}----- START OF CODE EXECUTION -----", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH)); + } + let mut local_memory: [Vec; 2] = [vec![0.0], vec![0.0]]; + let mut local_memory_pointers: [usize; 2] = [0, 0]; + let mut active_local_memory: usize = 0; + let program_length = parser.commands.len(); + while self.program_pointer < program_length { + let command = &parser.commands[self.program_pointer]; + active_local_memory = match self.evaluate_command(command, &mut local_memory, &mut local_memory_pointers, active_local_memory) { + Ok(val) => val, + Err(e) => { + println!("{}{}", Utils::ansi_escape_text("91", "ERROR", INFO_PREFIX_LENGTH), e); + break; + } + }; + } + if self.flags.debug { + println!("\n{}----- END OF CODE EXECUTION -----", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH)); + } + if self.flags.debug { + println!("{}Main memory:", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH)); + println!("{}{:?}", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH), self.memory); + println!("{}Local memory:", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH)); + println!("{}{:?}", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH), local_memory); + } + } + + pub fn evaluate_command(&mut self, command: &str, local_memory: &mut [Vec; 2], local_memory_pointers: &mut [usize; 2], active_local_memory: usize) -> Result { + let is_local = command.starts_with('\''); + let raw_command = command; + let command = if is_local { &command[1..] } else { command }; + let [(main_memory, main_memory_pointers, mut main_active_memory), (local_memory, local_memory_pointers, local_active_memory)] = if is_local { + [ + (local_memory, local_memory_pointers, active_local_memory), + (&mut self.memory, &mut self.memory_pointers, self.active_memory), + ] + } else { + [ + (&mut self.memory, &mut self.memory_pointers, self.active_memory), + (local_memory, local_memory_pointers, active_local_memory), + ] + }; + let split_command = command.split('|').collect::>(); + let (command, repeat) = if split_command.len() == 3 { + let count_str = split_command[1]; + let num = if count_str.is_empty() { + main_memory[main_active_memory][main_memory_pointers[main_active_memory]].floor() as i128 + } else if let Ok(val) = count_str.parse::() { + val + } else { + 1 + }; + let new_command = split_command[2]; + if num < 0 { + if let Some(opposite_command) = self.opposite_commands.get(new_command) { + (opposite_command.as_str(), -num) + } else { + (new_command, 0) + } + } else { + (new_command, num) + } + } else { + (command, 1) + }; + for _ in 0..repeat { + match command { + "!" => main_memory[main_active_memory][main_memory_pointers[main_active_memory]] += 1.0, + "~" => main_memory[main_active_memory][main_memory_pointers[main_active_memory]] -= 1.0, + "+" => { + main_memory[main_active_memory][main_memory_pointers[main_active_memory]] += + main_memory[(main_active_memory as isize - 1).abs() as usize][main_memory_pointers[(main_active_memory as isize - 1).abs() as usize]] + } + "-" => { + main_memory[main_active_memory][main_memory_pointers[main_active_memory]] -= + main_memory[(main_active_memory as isize - 1).abs() as usize][main_memory_pointers[(main_active_memory as isize - 1).abs() as usize]] + } + "*" => { + main_memory[main_active_memory][main_memory_pointers[main_active_memory]] *= + main_memory[(main_active_memory as isize - 1).abs() as usize][main_memory_pointers[(main_active_memory as isize - 1).abs() as usize]] + } + "/" => { + main_memory[main_active_memory][main_memory_pointers[main_active_memory]] /= + main_memory[(main_active_memory as isize - 1).abs() as usize][main_memory_pointers[(main_active_memory as isize - 1).abs() as usize]] + } + "`" => main_memory[main_active_memory][main_memory_pointers[main_active_memory]] = rand::thread_rng().gen(), + ">" => { + main_memory_pointers[main_active_memory] += 1; + if main_memory_pointers[main_active_memory] >= main_memory[main_active_memory].len() { + main_memory[main_active_memory].push(0.0); + } + } + "<" => { + if main_memory_pointers[main_active_memory] == 0 { + main_memory[main_active_memory].insert(0, 0.0); + if !self.flags.disabled_warnings.too_left_pointer { + println!("{}You moved to the -1 index in memory. This will not crash the program, but should generally be avoided (you can use the --disable-warnings flag to disable all warnings or --disable-too-left-pointer-warning to disable this particular warning)", Utils::ansi_escape_text("93", "WARNING", INFO_PREFIX_LENGTH)); + } + } else { + main_memory_pointers[main_active_memory] -= 1; + } + } + "_" => main_memory[main_active_memory][main_memory_pointers[main_active_memory]] = main_memory[main_active_memory][main_memory_pointers[main_active_memory]].floor(), + "&" => main_memory[main_active_memory][main_memory_pointers[main_active_memory]] = main_memory[main_active_memory][main_memory_pointers[main_active_memory]].ceil(), + "^" => main_active_memory = (main_active_memory as isize - 1).abs() as usize, + "$." => { + if self.input_cache.is_none() { + self.input_cache = Some(Utils::get_input_line()); + } + let input = &self.input_cache.clone().unwrap(); + self.input_cache = None; + main_memory[main_active_memory][main_memory_pointers[main_active_memory]] = match input.parse::() { + Ok(val) => val, + Err(e) => { + return Err(format!("Failed to convert {} from input to a number: {}", input, e)); + } + } + } + "$," => { + if self.input_cache.is_none() { + self.input_cache = Some(Utils::get_input_line()); + } + let input = &self.input_cache.clone().unwrap(); + let (char, remainder) = Utils::next_char(input); + self.input_cache = if !remainder.is_empty() { Some(remainder.to_string()) } else { None }; + main_memory[main_active_memory][main_memory_pointers[main_active_memory]] = (char as u32) as f64; + } + "\\." => { + print!("{}", main_memory[main_active_memory][main_memory_pointers[main_active_memory]]); + if let Err(e) = Utils::flush_console() { + println!("{}{}", Utils::ansi_escape_text("91", "ERROR", INFO_PREFIX_LENGTH), e); + } + } + "\\," => match char::from_u32(main_memory[main_active_memory][main_memory_pointers[main_active_memory]].floor() as u32) { + Some(c) => { + print!("{}", c); + if let Err(e) = Utils::flush_console() { + println!("{}{}", Utils::ansi_escape_text("91", "ERROR", INFO_PREFIX_LENGTH), e); + } + } + None => { + return Err(format!( + "Failed to convert {} from memory to a character", + main_memory[main_active_memory][main_memory_pointers[main_active_memory]].floor() + )); + } + }, + "[" => { + if main_memory[main_active_memory][main_memory_pointers[main_active_memory]] == 0.0 { + if let Some(index) = self.loops.iter().position(|value| *value == self.program_pointer) { + self.loops.remove(index); + } + self.program_pointer = *self.brackets.get(&self.program_pointer).unwrap(); + } else if !self.loops.contains(&self.program_pointer) { + self.loops.push(self.program_pointer); + } + } + "]" | "@]" => { + if main_memory[main_active_memory][main_memory_pointers[main_active_memory]] == 0.0 { + if let Some(index) = self.loops.iter().position(|value| *value == self.program_pointer) { + self.loops.remove(index); + } + } else { + self.program_pointer = *self.brackets.get(&self.program_pointer).unwrap(); + } + } + "[@" => { + if main_memory[main_active_memory][main_memory_pointers[main_active_memory]] == 0.0 && self.loops.contains(&self.program_pointer) { + if let Some(index) = self.loops.iter().position(|value| *value == self.program_pointer) { + self.loops.remove(index); + } + self.program_pointer = *self.brackets.get(&self.program_pointer).unwrap(); + } else if !self.loops.contains(&self.program_pointer) { + self.loops.push(self.program_pointer); + } + } + "?=" => { + let inactive_memory = (main_active_memory as isize - 1).abs() as usize; + if main_memory[main_active_memory][main_memory_pointers[main_active_memory]] == main_memory[inactive_memory][main_memory_pointers[inactive_memory]] { + if let Some(current_loop) = self.loops.pop() { + self.program_pointer = *self.brackets.get(¤t_loop).unwrap(); + } + } + } + "?>" => { + let inactive_memory = (main_active_memory as isize - 1).abs() as usize; + if main_memory[main_active_memory][main_memory_pointers[main_active_memory]] > main_memory[inactive_memory][main_memory_pointers[inactive_memory]] { + if let Some(current_loop) = self.loops.pop() { + self.program_pointer = *self.brackets.get(¤t_loop).unwrap(); + } + } + } + "?<" => { + let inactive_memory = (main_active_memory as isize - 1).abs() as usize; + if main_memory[main_active_memory][main_memory_pointers[main_active_memory]] < main_memory[inactive_memory][main_memory_pointers[inactive_memory]] { + if let Some(current_loop) = self.loops.pop() { + self.program_pointer = *self.brackets.get(¤t_loop).unwrap(); + } + } + } + "??" => { + main_memory[main_active_memory][main_memory_pointers[main_active_memory]] = main_memory_pointers[main_active_memory] as f64; + } + ";" => { + std::mem::swap( + &mut local_memory[local_active_memory][local_memory_pointers[local_active_memory]], + &mut main_memory[main_active_memory][main_memory_pointers[main_active_memory]], + ); + } + _ => {} + } + } + self.program_pointer += 1; + self.active_memory = if is_local { local_active_memory } else { main_active_memory }; + if self.flags.debug_heavy { + println!("\n{}Raw command: {:?}", Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), raw_command); + println!("{}Command executed: {:?}", Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), command); + println!( + "{}Command was executed on local memory: {:?}", + Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), + is_local + ); + println!("{}Command repetitions: {:?}", Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), repeat); + println!("{}Global memory: {:?}", Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), self.memory); + println!("{}Global memory pointers: {:?}", Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), self.memory_pointers); + println!("{}Active global memory: {:?}", Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), self.active_memory); + std::thread::sleep(std::time::Duration::from_millis(500)); + } + Ok(if is_local { main_active_memory } else { local_active_memory }) + } +} diff --git a/the-golden/src/interpreter/versions/v0-2-0/parser.rs b/the-golden/src/interpreter/versions/v0-2-0/parser.rs new file mode 100644 index 0000000..7724194 --- /dev/null +++ b/the-golden/src/interpreter/versions/v0-2-0/parser.rs @@ -0,0 +1,35 @@ +use regex::Regex; + +pub struct Parser { + pub commands: Vec, + pub commands_info: Vec<(String, usize, usize, std::path::PathBuf)> +} + +impl Parser { + pub fn new() -> Self { + Self { commands: vec![], commands_info: vec![] } + } + + pub fn run(&mut self, mut lexer: super::Lexer) -> Result { + let mut t = lexer.next(); + let mut p = t.clone(); + while t.is_ok() && t.clone().unwrap().is_some() { + let val = t.clone().unwrap().unwrap(); + let (command, ..) = val.clone(); + if !((command.starts_with('"') && command.ends_with('"')) || command.contains(':')) { + self.commands.push(command); + self.commands_info.push(val); + } + p = t; + t = lexer.next(); + } + if let Err(e) = t { + return Err(e); + } + let (command, line, column, file_path) = p.unwrap().unwrap(); + if !Regex::new(r":\n?\r?").unwrap().is_match(&command) { + return Err(format!("Syntax error at {}:{} in {:?} ({:?}) - ':' expected", line, column, file_path.file_name().unwrap(), file_path.as_path())); + } + Ok(0) + } +} \ No newline at end of file diff --git a/the-golden/src/interpreter/versions/v0-2-0/validator.rs b/the-golden/src/interpreter/versions/v0-2-0/validator.rs new file mode 100644 index 0000000..08de9f1 --- /dev/null +++ b/the-golden/src/interpreter/versions/v0-2-0/validator.rs @@ -0,0 +1,31 @@ +use regex::Regex; + +use crate::Utils; +use super::INFO_PREFIX_LENGTH; + +pub struct Validator {} + +impl Validator { + pub fn run(mut lexer: super::Lexer, heavy_debug: bool) -> Result { + let mut t = lexer.next(); + if heavy_debug { + println!("{}Matched command: {:?}", Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), t); + } + let mut p = t.clone(); + while t.is_ok() && t.clone().unwrap().is_some() { + p = t; + t = lexer.next(); + if heavy_debug { + println!("{}Matched command: {:?}", Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), t); + } + } + if let Err(e) = t { + return Err(e); + } + let (command, line, column, file_path) = p.unwrap().unwrap(); + if !Regex::new(r":\n?\r?").unwrap().is_match(&command) { + return Err(format!("Syntax error at {}:{} in {:?} ({:?}) - ':' expected", line, column, file_path.file_name().unwrap(), file_path.as_path())); + } + Ok(0) + } +} \ No newline at end of file From f6fd9d69a4935c38307cc5b8cac11787f5db9f9c Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Sat, 6 Aug 2022 12:36:19 +0200 Subject: [PATCH 21/30] Check if ANSI is supported before using it --- the-golden/Cargo.toml | 1 + the-golden/src/interpreter/interpreter.rs | 22 +++-- .../src/interpreter/versions/handler.rs | 16 +++- .../src/interpreter/versions/v0-1-0/main.rs | 81 ++++++++++++------- .../interpreter/versions/v0-1-0/validator.rs | 18 +++-- .../src/interpreter/versions/v0-2-0/main.rs | 81 ++++++++++++------- .../interpreter/versions/v0-2-0/validator.rs | 18 +++-- the-golden/src/main.rs | 5 +- the-golden/src/utils.rs | 8 +- 9 files changed, 168 insertions(+), 82 deletions(-) diff --git a/the-golden/Cargo.toml b/the-golden/Cargo.toml index fdd2aa0..772eb30 100644 --- a/the-golden/Cargo.toml +++ b/the-golden/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] dotenv = "0.15.0" +enable-ansi-support = "0.1.2" lazy_static = "1.4.0" rand = "0.8.5" regex = "1.6.0" diff --git a/the-golden/src/interpreter/interpreter.rs b/the-golden/src/interpreter/interpreter.rs index 31c22ad..1930ad1 100644 --- a/the-golden/src/interpreter/interpreter.rs +++ b/the-golden/src/interpreter/interpreter.rs @@ -1,24 +1,34 @@ use crate::Flags; -#[path = "./versions/handler.rs"] mod versions_handler; +#[path = "./versions/handler.rs"] +mod versions_handler; pub struct Interpreter { flags: Flags, + ansi_enabled: bool, version: String, versions_handler: versions_handler::Handler, code: String, - code_path: std::path::PathBuf + code_path: std::path::PathBuf, } impl Interpreter { - pub fn new(mut version: String, code: String, code_path: std::path::PathBuf, flags: Flags) -> Self { + pub fn new(mut version: String, code: String, code_path: std::path::PathBuf, flags: Flags, ansi_enabled: bool) -> Self { let versions_handler = versions_handler::Handler::new(); version = versions_handler.parse_version(version); - Self { flags, code, version, versions_handler, code_path } + Self { + flags, + ansi_enabled, + code, + version, + versions_handler, + code_path, + } } pub fn run(&self) { - self.versions_handler.run(self.version.clone(), self.code.clone(), self.code_path.clone(), self.flags.clone()); + self.versions_handler + .run(self.version.clone(), self.code.clone(), self.code_path.clone(), self.flags.clone(), self.ansi_enabled); } -} \ No newline at end of file +} diff --git a/the-golden/src/interpreter/versions/handler.rs b/the-golden/src/interpreter/versions/handler.rs index 213fa95..f337719 100644 --- a/the-golden/src/interpreter/versions/handler.rs +++ b/the-golden/src/interpreter/versions/handler.rs @@ -24,10 +24,20 @@ impl Handler { version } - pub fn run(&self, version: String, code: String, code_path: std::path::PathBuf, flags: Flags) { + pub fn run(&self, version: String, code: String, code_path: std::path::PathBuf, flags: Flags, ansi_enabled: bool) { match version.as_str() { - "0.1.0" => v0_1_0::Runner::new(code, code_path, flags).run(), - "0.2.0" => v0_2_0::Runner::new(code, code_path, flags).run(), + "0.1.0" => { + if flags.debug { + println!("{}Running version 0.1.0", crate::Utils::ansi_escape_text("94", "DEBUG", v0_1_0::INFO_PREFIX_LENGTH, ansi_enabled)); + }; + v0_1_0::Runner::new(code, code_path, flags, ansi_enabled).run() + } + "0.2.0" => { + if flags.debug { + println!("{}Running version 0.2.0", crate::Utils::ansi_escape_text("94", "DEBUG", v0_2_0::INFO_PREFIX_LENGTH, ansi_enabled)); + }; + v0_2_0::Runner::new(code, code_path, flags, ansi_enabled).run() + } _ => panic!("Couldn't launch version {}", &version), } } diff --git a/the-golden/src/interpreter/versions/v0-1-0/main.rs b/the-golden/src/interpreter/versions/v0-1-0/main.rs index d2d5694..68f4ee1 100644 --- a/the-golden/src/interpreter/versions/v0-1-0/main.rs +++ b/the-golden/src/interpreter/versions/v0-1-0/main.rs @@ -18,10 +18,11 @@ pub use parser::Parser; mod validator; use validator::Validator; -const INFO_PREFIX_LENGTH: usize = 12; +pub const INFO_PREFIX_LENGTH: usize = 12; pub struct Runner { flags: Flags, + ansi_enabled: bool, brackets_matcher: BracketsMatcher, brackets_categorised: HashMap>, @@ -43,7 +44,7 @@ pub struct Runner { } impl Runner { - pub fn new(raw_code: String, code_path: std::path::PathBuf, flags: Flags) -> Self { + pub fn new(raw_code: String, code_path: std::path::PathBuf, flags: Flags, ansi_enabled: bool) -> Self { let rules = vec![ Regex::new(r"^'?(\|-?[0-9]*\|)*!").unwrap(), // increment Regex::new(r"^'?(\|-?[0-9]*\|)*~").unwrap(), // decrement @@ -73,6 +74,7 @@ impl Runner { ]; Self { flags, + ansi_enabled, brackets_matcher: BracketsMatcher::new(), @@ -106,33 +108,36 @@ impl Runner { pub fn run(&mut self) { if self.flags.debug { - println!("{}Running version 0.1.0", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH)); - println!("{}Raw code: {}", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH), self.raw_code); + println!("{}Raw code: {}", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH, self.ansi_enabled), self.raw_code); } let lexer = Lexer::new(self.raw_code.clone(), self.rules.clone(), self.code_path.clone()); - let validator_result = Validator::run(lexer.clone(), self.flags.debug_heavy); + let validator_result = Validator::run(lexer.clone(), self.flags.debug_heavy, self.ansi_enabled); if let Err(e) = validator_result { - println!("{}{}", Utils::ansi_escape_text("91", "ERROR", INFO_PREFIX_LENGTH), e); + println!("{}{}", Utils::ansi_escape_text("91", "ERROR", INFO_PREFIX_LENGTH, self.ansi_enabled), e); return; } if self.flags.debug { - println!("{}Valid code!", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH)); + println!("{}Valid code!", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH, self.ansi_enabled)); } let mut parser = Parser::new(); let parser_result = parser.run(lexer); if let Err(e) = parser_result { - println!("{}{}", Utils::ansi_escape_text("91", "ERROR", INFO_PREFIX_LENGTH), e); + println!("{}{}", Utils::ansi_escape_text("91", "ERROR", INFO_PREFIX_LENGTH, self.ansi_enabled), e); return; } if self.flags.debug { - println!("{}Parsed commands: {:?}", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH), parser.commands); + println!( + "{}Parsed commands: {:?}", + Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH, self.ansi_enabled), + parser.commands + ); } self.brackets_matcher.match_brackets(&parser.commands); self.brackets_categorised = self.brackets_matcher.brackets.clone(); if self.flags.debug_heavy { println!( "{}Matched brackets: {:?}", - Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), + Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH, self.ansi_enabled), self.brackets_matcher.brackets ); } @@ -145,12 +150,12 @@ impl Runner { if self.flags.debug_heavy { println!( "{}Matched brackets uncategorised: {:?}", - Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), + Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH, self.ansi_enabled), self.brackets ); } if self.flags.debug { - println!("{}----- START OF CODE EXECUTION -----", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH)); + println!("{}----- START OF CODE EXECUTION -----", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH, self.ansi_enabled)); } let mut local_memory: [Vec; 2] = [vec![0.0], vec![0.0]]; let mut local_memory_pointers: [usize; 2] = [0, 0]; @@ -161,19 +166,19 @@ impl Runner { active_local_memory = match self.evaluate_command(command, &mut local_memory, &mut local_memory_pointers, active_local_memory) { Ok(val) => val, Err(e) => { - println!("{}{}", Utils::ansi_escape_text("91", "ERROR", INFO_PREFIX_LENGTH), e); + println!("{}{}", Utils::ansi_escape_text("91", "ERROR", INFO_PREFIX_LENGTH, self.ansi_enabled), e); break; } }; } if self.flags.debug { - println!("\n{}----- END OF CODE EXECUTION -----", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH)); + println!("\n{}----- END OF CODE EXECUTION -----", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH, self.ansi_enabled)); } if self.flags.debug { - println!("{}Main memory:", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH)); - println!("{}{:?}", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH), self.memory); - println!("{}Local memory:", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH)); - println!("{}{:?}", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH), local_memory); + println!("{}Main memory:", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH, self.ansi_enabled)); + println!("{}{:?}", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH, self.ansi_enabled), self.memory); + println!("{}Local memory:", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH, self.ansi_enabled)); + println!("{}{:?}", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH, self.ansi_enabled), local_memory); } } @@ -246,7 +251,7 @@ impl Runner { if main_memory_pointers[main_active_memory] == 0 { main_memory[main_active_memory].insert(0, 0.0); if !self.flags.disabled_warnings.too_left_pointer { - println!("{}You moved to the -1 index in memory. This will not crash the program, but should generally be avoided (you can use the --disable-warnings flag to disable all warnings or --disable-too-left-pointer-warning to disable this particular warning)", Utils::ansi_escape_text("93", "WARNING", INFO_PREFIX_LENGTH)); + println!("{}You moved to the -1 index in memory. This will not crash the program, but should generally be avoided (you can use the --disable-warnings flag to disable all warnings or --disable-too-left-pointer-warning to disable this particular warning)", Utils::ansi_escape_text("93", "WARNING", INFO_PREFIX_LENGTH, self.ansi_enabled)); } } else { main_memory_pointers[main_active_memory] -= 1; @@ -280,14 +285,14 @@ impl Runner { "\\." => { print!("{}", main_memory[main_active_memory][main_memory_pointers[main_active_memory]]); if let Err(e) = Utils::flush_console() { - println!("{}{}", Utils::ansi_escape_text("91", "ERROR", INFO_PREFIX_LENGTH), e); + println!("{}{}", Utils::ansi_escape_text("91", "ERROR", INFO_PREFIX_LENGTH, self.ansi_enabled), e); } } "\\," => match char::from_u32(main_memory[main_active_memory][main_memory_pointers[main_active_memory]].floor() as u32) { Some(c) => { print!("{}", c); if let Err(e) = Utils::flush_console() { - println!("{}{}", Utils::ansi_escape_text("91", "ERROR", INFO_PREFIX_LENGTH), e); + println!("{}{}", Utils::ansi_escape_text("91", "ERROR", INFO_PREFIX_LENGTH, self.ansi_enabled), e); } } None => { @@ -362,17 +367,37 @@ impl Runner { self.program_pointer += 1; self.active_memory = if is_local { local_active_memory } else { main_active_memory }; if self.flags.debug_heavy { - println!("\n{}Raw command: {:?}", Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), raw_command); - println!("{}Command executed: {:?}", Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), command); + println!( + "\n{}Raw command: {:?}", + Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH, self.ansi_enabled), + raw_command + ); + println!("{}Command executed: {:?}", Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH, self.ansi_enabled), command); println!( "{}Command was executed on local memory: {:?}", - Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), + Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH, self.ansi_enabled), is_local ); - println!("{}Command repetitions: {:?}", Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), repeat); - println!("{}Global memory: {:?}", Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), self.memory); - println!("{}Global memory pointers: {:?}", Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), self.memory_pointers); - println!("{}Active global memory: {:?}", Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), self.active_memory); + println!( + "{}Command repetitions: {:?}", + Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH, self.ansi_enabled), + repeat + ); + println!( + "{}Global memory: {:?}", + Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH, self.ansi_enabled), + self.memory + ); + println!( + "{}Global memory pointers: {:?}", + Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH, self.ansi_enabled), + self.memory_pointers + ); + println!( + "{}Active global memory: {:?}", + Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH, self.ansi_enabled), + self.active_memory + ); std::thread::sleep(std::time::Duration::from_millis(500)); } Ok(if is_local { main_active_memory } else { local_active_memory }) diff --git a/the-golden/src/interpreter/versions/v0-1-0/validator.rs b/the-golden/src/interpreter/versions/v0-1-0/validator.rs index 08de9f1..2cbb93e 100644 --- a/the-golden/src/interpreter/versions/v0-1-0/validator.rs +++ b/the-golden/src/interpreter/versions/v0-1-0/validator.rs @@ -1,22 +1,22 @@ use regex::Regex; -use crate::Utils; use super::INFO_PREFIX_LENGTH; +use crate::Utils; pub struct Validator {} impl Validator { - pub fn run(mut lexer: super::Lexer, heavy_debug: bool) -> Result { + pub fn run(mut lexer: super::Lexer, heavy_debug: bool, ansi_enabled: bool) -> Result { let mut t = lexer.next(); if heavy_debug { - println!("{}Matched command: {:?}", Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), t); + println!("{}Matched command: {:?}", Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH, ansi_enabled), t); } let mut p = t.clone(); while t.is_ok() && t.clone().unwrap().is_some() { p = t; t = lexer.next(); if heavy_debug { - println!("{}Matched command: {:?}", Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), t); + println!("{}Matched command: {:?}", Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH, ansi_enabled), t); } } if let Err(e) = t { @@ -24,8 +24,14 @@ impl Validator { } let (command, line, column, file_path) = p.unwrap().unwrap(); if !Regex::new(r":\n?\r?").unwrap().is_match(&command) { - return Err(format!("Syntax error at {}:{} in {:?} ({:?}) - ':' expected", line, column, file_path.file_name().unwrap(), file_path.as_path())); + return Err(format!( + "Syntax error at {}:{} in {:?} ({:?}) - ':' expected", + line, + column, + file_path.file_name().unwrap(), + file_path.as_path() + )); } Ok(0) } -} \ No newline at end of file +} diff --git a/the-golden/src/interpreter/versions/v0-2-0/main.rs b/the-golden/src/interpreter/versions/v0-2-0/main.rs index fdd39ef..7d225d2 100644 --- a/the-golden/src/interpreter/versions/v0-2-0/main.rs +++ b/the-golden/src/interpreter/versions/v0-2-0/main.rs @@ -18,10 +18,11 @@ pub use parser::Parser; mod validator; use validator::Validator; -const INFO_PREFIX_LENGTH: usize = 12; +pub const INFO_PREFIX_LENGTH: usize = 12; pub struct Runner { flags: Flags, + ansi_enabled: bool, brackets_matcher: BracketsMatcher, brackets_categorised: HashMap>, @@ -43,7 +44,7 @@ pub struct Runner { } impl Runner { - pub fn new(raw_code: String, code_path: std::path::PathBuf, flags: Flags) -> Self { + pub fn new(raw_code: String, code_path: std::path::PathBuf, flags: Flags, ansi_enabled: bool) -> Self { let rules = vec![ Regex::new(r"^'?(\|-?[0-9]*\|)*!").unwrap(), // increment Regex::new(r"^'?(\|-?[0-9]*\|)*~").unwrap(), // decrement @@ -74,6 +75,7 @@ impl Runner { ]; Self { flags, + ansi_enabled, brackets_matcher: BracketsMatcher::new(), @@ -107,33 +109,36 @@ impl Runner { pub fn run(&mut self) { if self.flags.debug { - println!("{}Running version 0.1.0", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH)); - println!("{}Raw code: {}", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH), self.raw_code); + println!("{}Raw code: {}", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH, self.ansi_enabled), self.raw_code); } let lexer = Lexer::new(self.raw_code.clone(), self.rules.clone(), self.code_path.clone()); - let validator_result = Validator::run(lexer.clone(), self.flags.debug_heavy); + let validator_result = Validator::run(lexer.clone(), self.flags.debug_heavy, self.ansi_enabled); if let Err(e) = validator_result { - println!("{}{}", Utils::ansi_escape_text("91", "ERROR", INFO_PREFIX_LENGTH), e); + println!("{}{}", Utils::ansi_escape_text("91", "ERROR", INFO_PREFIX_LENGTH, self.ansi_enabled), e); return; } if self.flags.debug { - println!("{}Valid code!", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH)); + println!("{}Valid code!", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH, self.ansi_enabled)); } let mut parser = Parser::new(); let parser_result = parser.run(lexer); if let Err(e) = parser_result { - println!("{}{}", Utils::ansi_escape_text("91", "ERROR", INFO_PREFIX_LENGTH), e); + println!("{}{}", Utils::ansi_escape_text("91", "ERROR", INFO_PREFIX_LENGTH, self.ansi_enabled), e); return; } if self.flags.debug { - println!("{}Parsed commands: {:?}", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH), parser.commands); + println!( + "{}Parsed commands: {:?}", + Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH, self.ansi_enabled), + parser.commands + ); } self.brackets_matcher.match_brackets(&parser.commands); self.brackets_categorised = self.brackets_matcher.brackets.clone(); if self.flags.debug_heavy { println!( "{}Matched brackets: {:?}", - Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), + Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH, self.ansi_enabled), self.brackets_matcher.brackets ); } @@ -146,12 +151,12 @@ impl Runner { if self.flags.debug_heavy { println!( "{}Matched brackets uncategorised: {:?}", - Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), + Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH, self.ansi_enabled), self.brackets ); } if self.flags.debug { - println!("{}----- START OF CODE EXECUTION -----", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH)); + println!("{}----- START OF CODE EXECUTION -----", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH, self.ansi_enabled)); } let mut local_memory: [Vec; 2] = [vec![0.0], vec![0.0]]; let mut local_memory_pointers: [usize; 2] = [0, 0]; @@ -162,19 +167,19 @@ impl Runner { active_local_memory = match self.evaluate_command(command, &mut local_memory, &mut local_memory_pointers, active_local_memory) { Ok(val) => val, Err(e) => { - println!("{}{}", Utils::ansi_escape_text("91", "ERROR", INFO_PREFIX_LENGTH), e); + println!("{}{}", Utils::ansi_escape_text("91", "ERROR", INFO_PREFIX_LENGTH, self.ansi_enabled), e); break; } }; } if self.flags.debug { - println!("\n{}----- END OF CODE EXECUTION -----", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH)); + println!("\n{}----- END OF CODE EXECUTION -----", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH, self.ansi_enabled)); } if self.flags.debug { - println!("{}Main memory:", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH)); - println!("{}{:?}", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH), self.memory); - println!("{}Local memory:", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH)); - println!("{}{:?}", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH), local_memory); + println!("{}Main memory:", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH, self.ansi_enabled)); + println!("{}{:?}", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH, self.ansi_enabled), self.memory); + println!("{}Local memory:", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH, self.ansi_enabled)); + println!("{}{:?}", Utils::ansi_escape_text("94", "DEBUG", INFO_PREFIX_LENGTH, self.ansi_enabled), local_memory); } } @@ -247,7 +252,7 @@ impl Runner { if main_memory_pointers[main_active_memory] == 0 { main_memory[main_active_memory].insert(0, 0.0); if !self.flags.disabled_warnings.too_left_pointer { - println!("{}You moved to the -1 index in memory. This will not crash the program, but should generally be avoided (you can use the --disable-warnings flag to disable all warnings or --disable-too-left-pointer-warning to disable this particular warning)", Utils::ansi_escape_text("93", "WARNING", INFO_PREFIX_LENGTH)); + println!("{}You moved to the -1 index in memory. This will not crash the program, but should generally be avoided (you can use the --disable-warnings flag to disable all warnings or --disable-too-left-pointer-warning to disable this particular warning)", Utils::ansi_escape_text("93", "WARNING", INFO_PREFIX_LENGTH, self.ansi_enabled)); } } else { main_memory_pointers[main_active_memory] -= 1; @@ -281,14 +286,14 @@ impl Runner { "\\." => { print!("{}", main_memory[main_active_memory][main_memory_pointers[main_active_memory]]); if let Err(e) = Utils::flush_console() { - println!("{}{}", Utils::ansi_escape_text("91", "ERROR", INFO_PREFIX_LENGTH), e); + println!("{}{}", Utils::ansi_escape_text("91", "ERROR", INFO_PREFIX_LENGTH, self.ansi_enabled), e); } } "\\," => match char::from_u32(main_memory[main_active_memory][main_memory_pointers[main_active_memory]].floor() as u32) { Some(c) => { print!("{}", c); if let Err(e) = Utils::flush_console() { - println!("{}{}", Utils::ansi_escape_text("91", "ERROR", INFO_PREFIX_LENGTH), e); + println!("{}{}", Utils::ansi_escape_text("91", "ERROR", INFO_PREFIX_LENGTH, self.ansi_enabled), e); } } None => { @@ -366,17 +371,37 @@ impl Runner { self.program_pointer += 1; self.active_memory = if is_local { local_active_memory } else { main_active_memory }; if self.flags.debug_heavy { - println!("\n{}Raw command: {:?}", Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), raw_command); - println!("{}Command executed: {:?}", Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), command); + println!( + "\n{}Raw command: {:?}", + Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH, self.ansi_enabled), + raw_command + ); + println!("{}Command executed: {:?}", Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH, self.ansi_enabled), command); println!( "{}Command was executed on local memory: {:?}", - Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), + Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH, self.ansi_enabled), is_local ); - println!("{}Command repetitions: {:?}", Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), repeat); - println!("{}Global memory: {:?}", Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), self.memory); - println!("{}Global memory pointers: {:?}", Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), self.memory_pointers); - println!("{}Active global memory: {:?}", Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), self.active_memory); + println!( + "{}Command repetitions: {:?}", + Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH, self.ansi_enabled), + repeat + ); + println!( + "{}Global memory: {:?}", + Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH, self.ansi_enabled), + self.memory + ); + println!( + "{}Global memory pointers: {:?}", + Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH, self.ansi_enabled), + self.memory_pointers + ); + println!( + "{}Active global memory: {:?}", + Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH, self.ansi_enabled), + self.active_memory + ); std::thread::sleep(std::time::Duration::from_millis(500)); } Ok(if is_local { main_active_memory } else { local_active_memory }) diff --git a/the-golden/src/interpreter/versions/v0-2-0/validator.rs b/the-golden/src/interpreter/versions/v0-2-0/validator.rs index 08de9f1..2cbb93e 100644 --- a/the-golden/src/interpreter/versions/v0-2-0/validator.rs +++ b/the-golden/src/interpreter/versions/v0-2-0/validator.rs @@ -1,22 +1,22 @@ use regex::Regex; -use crate::Utils; use super::INFO_PREFIX_LENGTH; +use crate::Utils; pub struct Validator {} impl Validator { - pub fn run(mut lexer: super::Lexer, heavy_debug: bool) -> Result { + pub fn run(mut lexer: super::Lexer, heavy_debug: bool, ansi_enabled: bool) -> Result { let mut t = lexer.next(); if heavy_debug { - println!("{}Matched command: {:?}", Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), t); + println!("{}Matched command: {:?}", Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH, ansi_enabled), t); } let mut p = t.clone(); while t.is_ok() && t.clone().unwrap().is_some() { p = t; t = lexer.next(); if heavy_debug { - println!("{}Matched command: {:?}", Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH), t); + println!("{}Matched command: {:?}", Utils::ansi_escape_text("34", "HEAVY DEBUG", INFO_PREFIX_LENGTH, ansi_enabled), t); } } if let Err(e) = t { @@ -24,8 +24,14 @@ impl Validator { } let (command, line, column, file_path) = p.unwrap().unwrap(); if !Regex::new(r":\n?\r?").unwrap().is_match(&command) { - return Err(format!("Syntax error at {}:{} in {:?} ({:?}) - ':' expected", line, column, file_path.file_name().unwrap(), file_path.as_path())); + return Err(format!( + "Syntax error at {}:{} in {:?} ({:?}) - ':' expected", + line, + column, + file_path.file_name().unwrap(), + file_path.as_path() + )); } Ok(0) } -} \ No newline at end of file +} diff --git a/the-golden/src/main.rs b/the-golden/src/main.rs index b5d47f0..ad04ba3 100644 --- a/the-golden/src/main.rs +++ b/the-golden/src/main.rs @@ -1,4 +1,5 @@ use dotenv::dotenv; +use enable_ansi_support; use std::env; #[path = "./flags.rs"] @@ -17,7 +18,7 @@ fn main() { env::set_var("RUST_LOG", "INFO"); } tracing_subscriber::fmt::init(); - + let ansi_enabled = enable_ansi_support::enable_ansi_support().is_ok(); let args: Vec = std::env::args().collect(); let mut flags_handler = Flags::new(); @@ -53,5 +54,5 @@ fn main() { if cloned_flags.no_console && cfg!(target_os = "windows") { winconsole::window::hide(); } - Interpreter::new(version, code, code_path, flags_handler).run(); + Interpreter::new(version, code, code_path, flags_handler, ansi_enabled).run(); } diff --git a/the-golden/src/utils.rs b/the-golden/src/utils.rs index 9c800d2..d7772e0 100644 --- a/the-golden/src/utils.rs +++ b/the-golden/src/utils.rs @@ -3,12 +3,14 @@ use std::io::Write; pub struct Utils {} impl Utils { - pub fn ansi_escape_text(style: &str, text: &str, min_length: usize) -> String { - let mut res = format!("\x1b[{}m{}", style, text); + pub fn ansi_escape_text(style: &str, text: &str, min_length: usize, enabled_ansi: bool) -> String { + let mut res = format!("{}{}", if enabled_ansi { format!("\x1b[{}m", style) } else { String::new() }, text); for _ in 0..min_length - text.len() { res += " "; } - res += "\x1b[0m"; + if enabled_ansi { + res += "\x1b[0m"; + } res } From 1256361047920938e95bc1fe73497ce164478594 Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Sat, 6 Aug 2022 12:47:39 +0200 Subject: [PATCH 22/30] Add support for absolute paths to code --- the-golden/src/flags.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/the-golden/src/flags.rs b/the-golden/src/flags.rs index 4fab175..53767a6 100644 --- a/the-golden/src/flags.rs +++ b/the-golden/src/flags.rs @@ -60,10 +60,20 @@ impl Flags { if self.action.is_none() { self.action = Some(String::from("run")); if self.code_path.is_none() && i + 1 < args_count && !args[i + 1].starts_with('-') { - let mut path = std::path::PathBuf::from(args[0].clone()); - path.pop(); - path.push(args[i + 1].clone()); - path.set_file_name("maumivu.au"); + let mut path = if args[i + 1].starts_with('.') || args[i + 1].starts_with('/') { + let mut p = std::path::PathBuf::from(args[0].clone()); + p.pop(); + p.push(args[i + 1].clone()); + p + } else { + std::path::PathBuf::from(args[i + 1].clone()) + }; + if path.is_file() { + path.set_file_name("maumivu.au"); + } else { + path.push("maumivu.au"); + } + println!("{path:?}"); self.code_path = Some(path); } } From fb9f6a3b06232d89691317c7f06edae062b7c4ea Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Sat, 6 Aug 2022 12:50:57 +0200 Subject: [PATCH 23/30] Get rid of the file path log --- the-golden/src/flags.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/the-golden/src/flags.rs b/the-golden/src/flags.rs index 53767a6..7e8e8e3 100644 --- a/the-golden/src/flags.rs +++ b/the-golden/src/flags.rs @@ -73,7 +73,6 @@ impl Flags { } else { path.push("maumivu.au"); } - println!("{path:?}"); self.code_path = Some(path); } } From 63e1104904b77f2153229cf3b6054c7e3a17c845 Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Mon, 8 Aug 2022 17:38:48 +0200 Subject: [PATCH 24/30] Add better versions parsing --- .../src/interpreter/versions/handler.rs | 83 +++++++++++++++++-- 1 file changed, 76 insertions(+), 7 deletions(-) diff --git a/the-golden/src/interpreter/versions/handler.rs b/the-golden/src/interpreter/versions/handler.rs index f337719..c910b88 100644 --- a/the-golden/src/interpreter/versions/handler.rs +++ b/the-golden/src/interpreter/versions/handler.rs @@ -6,22 +6,70 @@ mod v0_1_0; mod v0_2_0; pub struct Handler { - versions: [String; 2], + versions: Versions, } impl Handler { pub fn new() -> Self { - Self { - versions: [String::from("0.1.0"), String::from("0.2.0")], - } + let versions_0 = Version::new( + String::from("0"), + vec![ + Version::new(String::from("1"), vec![Version::new(String::from("0"), vec![])]), + Version::new(String::from("2"), vec![Version::new(String::from("0"), vec![])]), + ], + ); + let versions = Versions::new(vec![versions_0]); + Self { versions } } pub fn parse_version(&self, mut version: String) -> String { version = version.to_lowercase(); - if version == *"latest" || !self.versions.contains(&version) { - version = self.versions.last().unwrap().to_string(); + if version == *"latest" { + version = "x.x.x".to_string(); + } + let mut parts = version.split('.').map(|x| x.to_string()).collect::>(); + parts.truncate(3); + for _ in 0..3 - parts.len() { + parts.push(String::from("x")); + } + let mut version_parsed: Vec = vec![]; + let mut current_subversion = if parts[0].parse::().is_err() { + self.versions.versions.last().unwrap() + } else { + let mut to_return: Option<&Version> = None; + for subversion in &self.versions.versions { + if subversion.value == parts[0] { + to_return = Some(subversion); + break; + } + } + if to_return.is_some() { + to_return.unwrap() + } else { + self.versions.versions.last().unwrap() + } + }; + version_parsed.push(current_subversion.value.clone()); + for ver in &parts[1..3] { + if ver.parse::().is_err() { + current_subversion = current_subversion.sub.last().unwrap(); + } else { + let mut to_return: Option<&Version> = None; + for subversion in current_subversion.sub.as_ref() { + if subversion.value == *ver { + to_return = Some(subversion); + break; + } + } + if to_return.is_some() { + current_subversion = to_return.unwrap() + } else { + current_subversion = current_subversion.sub.last().unwrap() + } + } + version_parsed.push(current_subversion.value.clone()); } - version + version_parsed.join(".") } pub fn run(&self, version: String, code: String, code_path: std::path::PathBuf, flags: Flags, ansi_enabled: bool) { @@ -42,3 +90,24 @@ impl Handler { } } } + +struct Versions { + versions: Vec, +} + +impl Versions { + fn new(versions: Vec) -> Self { + Self { versions } + } +} + +struct Version { + sub: Box>, + value: String, +} + +impl Version { + fn new(value: String, sub: Vec) -> Self { + Self { sub: Box::new(sub), value } + } +} From 031add68f3c27584ef4669f194ada9cffad514b1 Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Mon, 8 Aug 2022 17:43:28 +0200 Subject: [PATCH 25/30] Apply some clippy suggestions --- the-golden/src/interpreter/versions/handler.rs | 16 ++++++---------- the-golden/src/main.rs | 1 - 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/the-golden/src/interpreter/versions/handler.rs b/the-golden/src/interpreter/versions/handler.rs index c910b88..46cd2f9 100644 --- a/the-golden/src/interpreter/versions/handler.rs +++ b/the-golden/src/interpreter/versions/handler.rs @@ -43,8 +43,8 @@ impl Handler { break; } } - if to_return.is_some() { - to_return.unwrap() + if let Some(to_return) = to_return { + to_return } else { self.versions.versions.last().unwrap() } @@ -55,17 +55,13 @@ impl Handler { current_subversion = current_subversion.sub.last().unwrap(); } else { let mut to_return: Option<&Version> = None; - for subversion in current_subversion.sub.as_ref() { + for subversion in ¤t_subversion.sub { if subversion.value == *ver { to_return = Some(subversion); break; } } - if to_return.is_some() { - current_subversion = to_return.unwrap() - } else { - current_subversion = current_subversion.sub.last().unwrap() - } + current_subversion = if let Some(to_return) = to_return { to_return } else { current_subversion.sub.last().unwrap() } } version_parsed.push(current_subversion.value.clone()); } @@ -102,12 +98,12 @@ impl Versions { } struct Version { - sub: Box>, + sub: Vec, value: String, } impl Version { fn new(value: String, sub: Vec) -> Self { - Self { sub: Box::new(sub), value } + Self { sub, value } } } diff --git a/the-golden/src/main.rs b/the-golden/src/main.rs index ad04ba3..0495d43 100644 --- a/the-golden/src/main.rs +++ b/the-golden/src/main.rs @@ -1,5 +1,4 @@ use dotenv::dotenv; -use enable_ansi_support; use std::env; #[path = "./flags.rs"] From 71b05f26c5cfc2253d740e436c4fd816de0a684e Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Tue, 9 Aug 2022 18:13:54 +0200 Subject: [PATCH 26/30] Take prereleases into account when parsing versions --- the-golden/src/interpreter/interpreter.rs | 2 +- .../src/interpreter/versions/handler.rs | 54 +++++++++++++++++-- 2 files changed, 50 insertions(+), 6 deletions(-) diff --git a/the-golden/src/interpreter/interpreter.rs b/the-golden/src/interpreter/interpreter.rs index 1930ad1..e4c1ea0 100644 --- a/the-golden/src/interpreter/interpreter.rs +++ b/the-golden/src/interpreter/interpreter.rs @@ -16,7 +16,7 @@ pub struct Interpreter { impl Interpreter { pub fn new(mut version: String, code: String, code_path: std::path::PathBuf, flags: Flags, ansi_enabled: bool) -> Self { let versions_handler = versions_handler::Handler::new(); - version = versions_handler.parse_version(version); + version = versions_handler.parse_version(version, ansi_enabled); Self { flags, ansi_enabled, diff --git a/the-golden/src/interpreter/versions/handler.rs b/the-golden/src/interpreter/versions/handler.rs index 46cd2f9..fc5ac66 100644 --- a/the-golden/src/interpreter/versions/handler.rs +++ b/the-golden/src/interpreter/versions/handler.rs @@ -22,9 +22,9 @@ impl Handler { Self { versions } } - pub fn parse_version(&self, mut version: String) -> String { - version = version.to_lowercase(); - if version == *"latest" { + pub fn parse_version(&self, mut version: String, ansi_enabled: bool) -> String { + let version_original = version.clone(); + if version.to_lowercase() == *"latest" { version = "x.x.x".to_string(); } let mut parts = version.split('.').map(|x| x.to_string()).collect::>(); @@ -32,6 +32,19 @@ impl Handler { for _ in 0..3 - parts.len() { parts.push(String::from("x")); } + let s = parts[2].split("-").map(|x| x.to_string()).collect::>(); + let (parts, prerelease, _build_metadata) = if s.len() > 1 { + let s_joined = s[1..].join("-"); + let p = s_joined.clone(); + let s2 = p.split("+").map(|x| x.to_string()).collect::>(); + if s2.len() > 1 { + (vec![parts[0].clone(), parts[1].clone(), s[0].clone()], Some(s2[0].clone()), Some(s2[1..].join("+").clone())) + } else { + (vec![parts[0].clone(), parts[1].clone(), s[0].clone()], Some(s_joined), None) + } + } else { + (parts, None, None) + }; let mut version_parsed: Vec = vec![]; let mut current_subversion = if parts[0].parse::().is_err() { self.versions.versions.last().unwrap() @@ -65,7 +78,30 @@ impl Handler { } version_parsed.push(current_subversion.value.clone()); } - version_parsed.join(".") + let prerelease = if prerelease.is_some() && !current_subversion.sub.is_empty() { + let ver = prerelease.unwrap(); + let mut to_return: Option<&Version> = None; + for subversion in ¤t_subversion.sub { + if subversion.value == ver { + to_return = Some(subversion); + break; + } + } + current_subversion = if let Some(to_return) = to_return { to_return } else { current_subversion.sub.last().unwrap() }; + format!("-{}", current_subversion.value.clone()) + } else { + String::new() + }; + let version_final = format!("{}{}", version_parsed.join("."), prerelease); + if version_original != version_final { + println!( + "{}Could not find version {}, instead found {}", + crate::Utils::ansi_escape_text("93", "WARNING", v0_1_0::INFO_PREFIX_LENGTH, ansi_enabled), + version_original, + version_final + ); + } + version_final } pub fn run(&self, version: String, code: String, code_path: std::path::PathBuf, flags: Flags, ansi_enabled: bool) { @@ -82,7 +118,14 @@ impl Handler { }; v0_2_0::Runner::new(code, code_path, flags, ansi_enabled).run() } - _ => panic!("Couldn't launch version {}", &version), + _ => { + println!( + "{}Couldn't run version {}", + crate::Utils::ansi_escape_text("91", "ERROR", v0_1_0::INFO_PREFIX_LENGTH, ansi_enabled), + version + ); + return; + } } } } @@ -97,6 +140,7 @@ impl Versions { } } +#[derive(Debug)] struct Version { sub: Vec, value: String, From 23429b7d5dc138fd8c00d8c7ed8088d8f5ef2f7a Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Tue, 9 Aug 2022 18:25:14 +0200 Subject: [PATCH 27/30] Get rid of panic!s and instead use custom error messages --- the-golden/src/interpreter/versions/handler.rs | 2 +- the-golden/src/main.rs | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/the-golden/src/interpreter/versions/handler.rs b/the-golden/src/interpreter/versions/handler.rs index fc5ac66..496926c 100644 --- a/the-golden/src/interpreter/versions/handler.rs +++ b/the-golden/src/interpreter/versions/handler.rs @@ -93,7 +93,7 @@ impl Handler { String::new() }; let version_final = format!("{}{}", version_parsed.join("."), prerelease); - if version_original != version_final { + if version_original != version_final && version_original.to_lowercase() != "latest" { println!( "{}Could not find version {}, instead found {}", crate::Utils::ansi_escape_text("93", "WARNING", v0_1_0::INFO_PREFIX_LENGTH, ansi_enabled), diff --git a/the-golden/src/main.rs b/the-golden/src/main.rs index 0495d43..46540a9 100644 --- a/the-golden/src/main.rs +++ b/the-golden/src/main.rs @@ -11,6 +11,8 @@ use interpreter::Interpreter; mod utils; pub use utils::Utils; +pub const INFO_PREFIX_LENGTH: usize = 12; + fn main() { dotenv().ok(); if env::var("RUST_LOG").is_err() { @@ -35,13 +37,27 @@ fn main() { if let Some(path) = cloned_flags.code_path { code = match std::fs::read_to_string(&path) { Ok(c) => c, - Err(e) => panic!("{}", e), + Err(e) => { + println!( + "{}Couldn't open a maumivu.au file from the provided path: {}", + Utils::ansi_escape_text("91", "ERROR", INFO_PREFIX_LENGTH, ansi_enabled), + e + ); + return; + } }; code_path = path; } else if let Some(code_to_run) = cloned_flags.raw_code_to_run { code = code_to_run; code_path.set_file_name(""); } + if code.is_empty() { + println!( + "{}No code provided - either provide a path to the maumivu.au file, or use the '- ' flag to run code from the command line directly", + Utils::ansi_escape_text("91", "ERROR", INFO_PREFIX_LENGTH, ansi_enabled) + ); + return; + } if let Some(v) = cloned_flags.version { version = v; } From b507d004e523f4a1befeab911e765335db991da6 Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Tue, 9 Aug 2022 18:36:54 +0200 Subject: [PATCH 28/30] Fix path parsing for "./..." paths --- the-golden/src/flags.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/the-golden/src/flags.rs b/the-golden/src/flags.rs index 7e8e8e3..5067129 100644 --- a/the-golden/src/flags.rs +++ b/the-golden/src/flags.rs @@ -60,7 +60,7 @@ impl Flags { if self.action.is_none() { self.action = Some(String::from("run")); if self.code_path.is_none() && i + 1 < args_count && !args[i + 1].starts_with('-') { - let mut path = if args[i + 1].starts_with('.') || args[i + 1].starts_with('/') { + let mut path = if args[i + 1] != "." && (args[i + 1].starts_with('.') || args[i + 1].starts_with('/')) { let mut p = std::path::PathBuf::from(args[0].clone()); p.pop(); p.push(args[i + 1].clone()); From 1d63484a1e5bc96524c3d723bdb8e841a31e31b9 Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Tue, 9 Aug 2022 18:43:56 +0200 Subject: [PATCH 29/30] Rename the "interpreter" folder --- python-interpreter-old/README.md | 137 ++++++++++++++++++ .../main.py | 0 2 files changed, 137 insertions(+) create mode 100644 python-interpreter-old/README.md rename {interpreter => python-interpreter-old}/main.py (100%) diff --git a/python-interpreter-old/README.md b/python-interpreter-old/README.md new file mode 100644 index 0000000..29238aa --- /dev/null +++ b/python-interpreter-old/README.md @@ -0,0 +1,137 @@ +# The Golden + +## Note +This was the first version of the interpreter left here in case it is needed for whatever reason. It will likely not be developped anymore in favour of the Rust one, which can be compiled and ran without Python installed. + +## Table of contents + +1. [Basic info](#basic-info) +2. [How to run your code](#run-code)
+ 2.1 [Arguments](#run-code-args)
+ 2.2 [Flags](#run-code-flags) +3. [Main features](#main-features) +4. [Important notes](#important-notes) +5. [Mechanics](#mechanics) +6. [Syntax](#syntax) +7. [Incoming features](#incoming-features) +8. [Examples](#examples) + +## Basic info + +The language is currently in development and there is no stable release yet. There may be a lot of breaking changes introduced in updates.
+This language is a fairly good language inspired by a faily popular language called Brainfuck. It takes the beauty of not using any letters in the code from it but also provides some handy features, like printing output as numbers instead of characters and more. +
Its purpose is to let people make Brainfuck-styled programs less painfully. + +## How to run your code + +All you need to do it run the interpreter file with Python 3.5 and higher. + +### Arguments + +You can run the code with some arguments including: + +- The maumivu.au file location (you will be asked to provide it if you don't include it in the arguments) +- Some flags + +### Flags + +See the table below for some flags you can provide when running your code. +| Flag | Usage | Effect | +|:-----|:------|:------| +| - \ | `- '!!![~]:` | You can provide some code to be ran by the interpreter - no need to have a maumivu.au file | +| --debug | `--debug` | Enabled debug mode - print parsed commands, which command was ran and the memory state at the end of execution | +| --debug-heavy | `--debug-heavy` | Enabled heavy debug mode - print all the things printed in debug mode + stop for 0.5 seconds after each command and print the memory state | +| --disable-warnings | `--disable-warnings` | Disable all warnings | +| --disable-path-warning | `--disable-path-warning` | Disable the "No path provided" warning (fired when running code from args) | +| --disable-too-left-pointer-warning | `--disable-too-left-pointer-warning` | Disable the warning fired when you go to the -1 index in memory | + +## Main features + +How good or bad the features of this language are is completely subjective, but here are some of them: + +- Brainfuck-like syntax - other will have no idea wth your code does +- Easy operations chaining - forget code looking like `>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>.<<<<<<<<<<<<<<<<<<<<<<<<<<`, now you can do `|49|>\,|26|<` to achieve the exact same result +- Easy arithmetics - tired of multiplication in O(N^2) time? The solution is here! Just do `*` and you are done in ~O(1) +- Decimal numbers - pretty self-explanatory, but now you can use decimal numbers in your code +- And much more! + +## Important notes + +This language uses multiple memory rows - you have access to 2 global memory rows and 2 local memory rows. Both with global and local memory, you are always using just one row at a time. I will be referring to those rows as `active` (the one you are using) and `inactive` (the one you are not using).
+Unless said otherwise, `cell` is referring to the selected cell (and also if not said otherwise in the active global memory row).
+ +## Mechanics + +The main file has to be named `maumivu.au`. This isn't required with command-line-provided code (obviously). +When converting numbers to characters and vice versa, the ASCII table is used.
+The memory has unlimited size and consists of double-precision numbers. When you go to an unexisting index (to the right) that cell is created with the value of 0.
+If you go into memory index -1, a 0 is added at that position and the whole memory is shifted one cell to the right. While this is allowed, I would discourage you from doing it since it can be fairly slow (compared to other operations). That's why it will fire a warning.
+Loops function the exact same way as in Brainfuck - they only run if the current cell value isn't 0. This language also offers do-while loops, which ignore the check the first time.
+You can chain commands by putting `||` in front of them. You can also put a number between those pipes. If you decide to put a number in there, the command right after it will run `floor(the number)` times. If you leave it empty, the code will run `floor(cell value)` times. If the value is negative, the opposite command will be ran (see the table below). If the value is 0, it won't be ran at all.
+| Command | Opposite command | +|:-----|:------| +| ! | ~ | +| ~ | ! | +| + | - | +| - | + | +| _ | / | +| / | _ | +| > | < | +| < | > | + +## Syntax + +The magic of Brainfuck-like syntax is that it is easy and extremely difficult at the same time. Here are all the commands the interpreter will understand: +| Command | Explanation | Showcase | Chainable? | Usable on local memory? | +|:-----|:------|:------|:------|:------| +| ! | Adds one to the current cell | `!` | Yes | Yes | +| ~ | Subtracts one from the current cell | `~` | Yes | Yes | +| + | Adds the cell in the inactive row to the cell in the active row | `+` | Yes | Yes | +| - | Subtracts the cell in the inactive row from the cell in the active row | `-` | Yes | Yes | +| * | Multiplies the cell in the active row by the cell in the inactive row | `*`| Yes | Yes | +| / | Divides the cell in the active row by the cell in the inactive row |`/`| Yes | Yes | +| _ | Floors the current cell value (towards -infinity) |`_`| No | Yes | +| & | Ceils the current cell value (towards +infinity) |`&`| No | Yes | +| ` | Sets the cell to a random number from 0 (inclusive) to 1 (exclusive) | ` | No | Yes | +| > | Move the cell pointer one to the right | `>`| Yes | Yes | +| < | Move the cell pointer one to the left |`<`| Yes | Yes | +| ^ | Switch active memory (sets the active as inactive and the inactive as active) |`^`| No | Yes | +| $. | Sets the cell to the value of user input as a number (if they input 69, the cell value will be 69) |`$.` | No | Yes | +| $, | Sets the cell to the value of user input as a character (if they input E, the cell value will be 69) | `$,`| No | Yes | +| \\. | Output the cell as a number (if the cell value is 69, 69 will be printed) |`\.`| No | Yes | +| \\, | Output the cell as a character (if the cell value is 69, E will be printed) |`\,`| No | Yes | +| [ | Start a while loop |`[` | No | Yes | +| ] | End a while loop | `]` | No | Yes | +| [@ | Start a do-while loop | `[@` | No | Yes | +| @] | End a do-while loop | `@]` | No | Yes | +| ?? | Sets the cell value to its index | `??` | No | Yes | +| ?= | If the cells in the active and inactive rows have the same value, break the loop | `[?=]` | Yes | Yes | +| ?< | If the cell in the active row has a lower value than the cell in the inactive row, break the loop | `[?<]` | Yes | Yes | +| ?> | If the cell in the active row has a higher value than the cell in the inactive row, break the loop | `[?>]` | Yes | Yes | +| ; | Switch the values of the active global cell and the active local cell | `;` | No | Yes | +| "" | Comments | `"This is a comment"` | No | No | + +Each line has to end with a punctuation (`:`) or else the program will crash. + +## Incoming features + +- Functions +- Running other files +- A compiled interpreter + +## Examples + +Here are some examples written in this language:
+"Hello, world!": + +``` +|72|!\,|29|!\,|7|!\,\,|3|!\,|67|~\,|12|~\,|87|!\,|8|~\,|3|!\,|6|~\,|8|~\,|67|~\,: +``` + +Fibonacci sequence: + +``` +!>|10|!<^>|10|!<[@^+\.>\,<@]: +``` + +You can find all the examples in the [examples folder](https://github.com/Pandicon/The-Golden/tree/main/examples). diff --git a/interpreter/main.py b/python-interpreter-old/main.py similarity index 100% rename from interpreter/main.py rename to python-interpreter-old/main.py From 8a46fd533a6f241d38ef068678bb55d510db3935 Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Tue, 9 Aug 2022 18:46:40 +0200 Subject: [PATCH 30/30] Update the repository README Fix some typos and add details about the compiled interpreter --- README.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 9a72278..6aaa43b 100644 --- a/README.md +++ b/README.md @@ -21,13 +21,14 @@ This language is a fairly good language inspired by a faily popular language cal ## How to run your code -All you need to do it run the interpreter file with Python 3.5 and higher. +All you need to do it run the interpreter file with the `run` argument and a path to the maumivu.au file (for example `the-golden run .`). You will have to download a binary from one of the [releases](https://github.com/Pandicon/The-Golden/releases) (I recommend using the [latest one](https://github.com/Pandicon/The-Golden/releases/latest/)). Then you will have to set it up in a way you want - you can either run the binary from a specific place, or set it up as a custom command. ### Arguments You can run the code with some arguments including: -- The maumivu.au file location (you will be asked to provide it if you don't include it in the arguments) +- The maumivu.au file location +- Code to run if you don't provide the maumivu.au file location - Some flags ### Flags @@ -39,15 +40,16 @@ See the table below for some flags you can provide when running your code. | --debug | `--debug` | Enabled debug mode - print parsed commands, which command was ran and the memory state at the end of execution | | --debug-heavy | `--debug-heavy` | Enabled heavy debug mode - print all the things printed in debug mode + stop for 0.5 seconds after each command and print the memory state | | --disable-warnings | `--disable-warnings` | Disable all warnings | -| --disable-path-warning | `--disable-path-warning` | Disable the "No path provided" warning (fired when running code from args) | | --disable-too-left-pointer-warning | `--disable-too-left-pointer-warning` | Disable the warning fired when you go to the -1 index in memory | +| --hide-console | `--hide-console` | Hide the console when running the code | +| --version | `--version 0.1.0` | Run the code using a specific version of the interpreter | ## Main features How good or bad the features of this language are is completely subjective, but here are some of them: - Brainfuck-like syntax - other will have no idea wth your code does -- Easy operations chaining - forget code looking like `>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>.<<<<<<<<<<<<<<<<<<<<<<<<<<`, now you can do `|49|\,|26|<` to achieve the exact same result +- Easy operations chaining - forget code looking like `>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>.<<<<<<<<<<<<<<<<<<<<<<<<<<`, now you can do `|49|>\,|26|<` to achieve the exact same result - Easy arithmetics - tired of multiplication in O(N^2) time? The solution is here! Just do `*` and you are done in ~O(1) - Decimal numbers - pretty self-explanatory, but now you can use decimal numbers in your code - And much more! @@ -87,16 +89,16 @@ The magic of Brainfuck-like syntax is that it is easy and extremely difficult at | - | Subtracts the cell in the inactive row from the cell in the active row | `-` | Yes | Yes | | * | Multiplies the cell in the active row by the cell in the inactive row | `*`| Yes | Yes | | / | Divides the cell in the active row by the cell in the inactive row |`/`| Yes | Yes | -| _ | Floors the current cell value (towards -infinity) |`_`| No | Yes | +| _ | Floors the current cell value (towards -infinity) |`\_`| No | Yes | | & | Ceils the current cell value (towards +infinity) |`&`| No | Yes | | ` | Sets the cell to a random number from 0 (inclusive) to 1 (exclusive) | ` | No | Yes | | > | Move the cell pointer one to the right | `>`| Yes | Yes | | < | Move the cell pointer one to the left |`<`| Yes | Yes | | ^ | Switch active memory (sets the active as inactive and the inactive as active) |`^`| No | Yes | -| $. | Sets the cell to the value of user input as a number (if they input 69, the cell value will be 69) |`$.` | No | Yes | +| $. | Sets the cell to the value of user input as a number (if they input 69, the cell value will be 69) | `$.` | No | Yes | | $, | Sets the cell to the value of user input as a character (if they input E, the cell value will be 69) | `$,`| No | Yes | -| \. | Output the cell as a number (if the cell value is 69, 69 will be printed) |`\.`| No | Yes | -| \, | Output the cell as a character (if the cell value is 69, E will be printed) |`\,`| No | Yes | +| \\. | Output the cell as a number (if the cell value is 69, 69 will be printed) |`\.`| No | Yes | +| \\, | Output the cell as a character (if the cell value is 69, E will be printed) |`\,`| No | Yes | | [ | Start a while loop |`[` | No | Yes | | ] | End a while loop | `]` | No | Yes | | [@ | Start a do-while loop | `[@` | No | Yes | @@ -114,7 +116,7 @@ Each line has to end with a punctuation (`:`) or else the program will crash. - Functions - Running other files -- A compiled interpreter +- A compiled interpreter ✔️ ## Examples