From a742c70db5126fddc06b33458bea1fc010d92489 Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Wed, 31 Aug 2022 10:28:23 +0200 Subject: [PATCH 1/9] Start version 0.4.0 development --- .../src/interpreter/versions/handler.rs | 15 +- .../versions/v0-4-0/brackets_matcher.rs | 77 ++++ .../src/interpreter/versions/v0-4-0/lexer.rs | 69 +++ .../src/interpreter/versions/v0-4-0/main.rs | 421 ++++++++++++++++++ .../src/interpreter/versions/v0-4-0/parser.rs | 35 ++ .../interpreter/versions/v0-4-0/validator.rs | 37 ++ 6 files changed, 651 insertions(+), 3 deletions(-) create mode 100644 the-golden/src/interpreter/versions/v0-4-0/brackets_matcher.rs create mode 100644 the-golden/src/interpreter/versions/v0-4-0/lexer.rs create mode 100644 the-golden/src/interpreter/versions/v0-4-0/main.rs create mode 100644 the-golden/src/interpreter/versions/v0-4-0/parser.rs create mode 100644 the-golden/src/interpreter/versions/v0-4-0/validator.rs diff --git a/the-golden/src/interpreter/versions/handler.rs b/the-golden/src/interpreter/versions/handler.rs index 2ca27b4..4b7d47e 100644 --- a/the-golden/src/interpreter/versions/handler.rs +++ b/the-golden/src/interpreter/versions/handler.rs @@ -6,6 +6,8 @@ mod v0_1_0; mod v0_2_0; #[path = "./v0-3-0/main.rs"] mod v0_3_0; +#[path = "./v0-4-0/main.rs"] +mod v0_4_0; pub struct Handler { versions: Versions, @@ -19,6 +21,7 @@ impl Handler { Version::new(String::from("1"), vec![Version::new(String::from("0"), vec![])]), Version::new(String::from("2"), vec![Version::new(String::from("0"), vec![])]), Version::new(String::from("3"), vec![Version::new(String::from("0"), vec![])]), + Version::new(String::from("4"), vec![Version::new(String::from("0"), vec![])]), ], ); let versions = Versions::new(vec![versions_0]); @@ -111,22 +114,28 @@ impl Handler { match version.as_str() { "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)); + println!("{}Running version {}", crate::Utils::ansi_escape_text("94", "DEBUG", v0_1_0::INFO_PREFIX_LENGTH, ansi_enabled), version); }; 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)); + println!("{}Running version {}", crate::Utils::ansi_escape_text("94", "DEBUG", v0_2_0::INFO_PREFIX_LENGTH, ansi_enabled), version); }; v0_2_0::Runner::new(code, code_path, flags, ansi_enabled).run() } "0.3.0" => { if flags.debug { - println!("{}Running version 0.3.0", crate::Utils::ansi_escape_text("94", "DEBUG", v0_3_0::INFO_PREFIX_LENGTH, ansi_enabled)); + println!("{}Running version {}", crate::Utils::ansi_escape_text("94", "DEBUG", v0_3_0::INFO_PREFIX_LENGTH, ansi_enabled), version); }; v0_3_0::Runner::new(code, code_path, flags, ansi_enabled).run() } + "0.4.0" => { + if flags.debug { + println!("{}Running version {}", crate::Utils::ansi_escape_text("94", "DEBUG", v0_4_0::INFO_PREFIX_LENGTH, ansi_enabled), version); + }; + v0_4_0::Runner::new(code, code_path, flags, ansi_enabled).run() + } _ => { println!( "{}Couldn't run version {}", diff --git a/the-golden/src/interpreter/versions/v0-4-0/brackets_matcher.rs b/the-golden/src/interpreter/versions/v0-4-0/brackets_matcher.rs new file mode 100644 index 0000000..4519556 --- /dev/null +++ b/the-golden/src/interpreter/versions/v0-4-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-4-0/lexer.rs b/the-golden/src/interpreter/versions/v0-4-0/lexer.rs new file mode 100644 index 0000000..93b6f24 --- /dev/null +++ b/the-golden/src/interpreter/versions/v0-4-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-4-0/main.rs b/the-golden/src/interpreter/versions/v0-4-0/main.rs new file mode 100644 index 0000000..fddf898 --- /dev/null +++ b/the-golden/src/interpreter/versions/v0-4-0/main.rs @@ -0,0 +1,421 @@ +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; + +pub const INFO_PREFIX_LENGTH: usize = 12; + +pub struct Runner { + flags: Flags, + ansi_enabled: bool, + + 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, ansi_enabled: bool) -> 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 + Regex::new(crate::PREPROCESSOR_REGEX).unwrap(), //preprocessor regex + ]; + Self { + flags, + ansi_enabled, + + 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!("{}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, self.ansi_enabled); + if let Err(e) = validator_result { + 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, 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, self.ansi_enabled), e); + return; + } + if self.flags.debug { + 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, self.ansi_enabled), + 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.ansi_enabled), + self.brackets + ); + } + if self.flags.debug { + 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]; + 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, self.ansi_enabled), e); + break; + } + }; + } + if self.flags.debug { + 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, 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); + } + } + + 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 ^ 1][main_memory_pointers[main_active_memory ^ 1]], + "-" => main_memory[main_active_memory][main_memory_pointers[main_active_memory]] -= main_memory[main_active_memory ^ 1][main_memory_pointers[main_active_memory ^ 1]], + "*" => main_memory[main_active_memory][main_memory_pointers[main_active_memory]] *= main_memory[main_active_memory ^ 1][main_memory_pointers[main_active_memory ^ 1]], + "/" => { + let divisor = main_memory[main_active_memory ^ 1][main_memory_pointers[main_active_memory ^ 1]]; + let divident = &mut main_memory[main_active_memory][main_memory_pointers[main_active_memory]]; + if divisor == 0.0 { + let mut i = 0; + if *divident >= 0.0 { + i += 1; + } + if *divident > 0.0 { + i += 1; + } + let val = self.flags.sebek[i]; + if let Some(res) = val { + *divident = res; + } else { + return Err(format!( + "Mr. Sebek would support you. Attempted division by 0 for {}. You can set up custom values for division by 0 with the --sebek flag.", + divident + )); + } + } else { + *divident /= divisor + } + } + "`" => 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, self.ansi_enabled)); + } + } 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 ^= 1, + "$." => { + 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, 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, self.ansi_enabled), 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 ^ 1; + 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 ^ 1; + 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 ^ 1; + 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, 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, self.ansi_enabled), + is_local + ); + 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-4-0/parser.rs b/the-golden/src/interpreter/versions/v0-4-0/parser.rs new file mode 100644 index 0000000..7724194 --- /dev/null +++ b/the-golden/src/interpreter/versions/v0-4-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-4-0/validator.rs b/the-golden/src/interpreter/versions/v0-4-0/validator.rs new file mode 100644 index 0000000..2cbb93e --- /dev/null +++ b/the-golden/src/interpreter/versions/v0-4-0/validator.rs @@ -0,0 +1,37 @@ +use regex::Regex; + +use super::INFO_PREFIX_LENGTH; +use crate::Utils; + +pub struct Validator {} + +impl Validator { + 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, 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, ansi_enabled), 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) + } +} From eaa8ad5f7171b974d2e39497a80f1127ccf1cb32 Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Wed, 31 Aug 2022 10:33:55 +0200 Subject: [PATCH 2/9] Add no-brainfuck flag and preprocessor statement This will disable brainfuck compatibility, which would bring some behaviour changes --- the-golden/src/flags.rs | 3 +++ the-golden/src/interpreter/interpreter.rs | 2 ++ the-golden/src/interpreter/preprocessor.rs | 3 +++ 3 files changed, 8 insertions(+) diff --git a/the-golden/src/flags.rs b/the-golden/src/flags.rs index f44b059..cc7eeab 100644 --- a/the-golden/src/flags.rs +++ b/the-golden/src/flags.rs @@ -13,6 +13,7 @@ pub struct Flags { pub code_path: Option, pub debug: bool, pub debug_heavy: bool, + pub no_brainfuck: bool, pub no_console: bool, pub raw_code_to_run: Option, pub sebek: [Option; 3], @@ -28,6 +29,7 @@ impl Flags { code_path: None, debug: false, debug_heavy: false, + no_brainfuck: false, no_console: false, raw_code_to_run: None, sebek: [None, None, None], @@ -55,6 +57,7 @@ impl Flags { } "--disable-warnings" => self.disabled_warnings = Warnings { too_left_pointer: true }, "--disable-too-left-pointer-warning" => self.disabled_warnings.too_left_pointer = true, + "--no-brainfuck" => self.no_brainfuck = true, "--sebek" => { if i + 1 < args_count { self.sebek = Utils::parse_sebek(&args[i + 1]); diff --git a/the-golden/src/interpreter/interpreter.rs b/the-golden/src/interpreter/interpreter.rs index 0fd8788..09e84ca 100644 --- a/the-golden/src/interpreter/interpreter.rs +++ b/the-golden/src/interpreter/interpreter.rs @@ -19,6 +19,7 @@ impl Interpreter { pub fn new(version: Option, code: String, code_path: std::path::PathBuf, mut flags: Flags, ansi_enabled: bool) -> Self { let mut preprocessor = preprocessor::Preprocessor::new(); preprocessor.run(&code); + flags.no_brainfuck |= preprocessor.no_brainfuck; flags.no_console |= preprocessor.no_console; let final_version = if let Some(ver) = version { ver @@ -30,6 +31,7 @@ impl Interpreter { if !flags.sebek.iter().any(|val| val.is_some()) { flags.sebek = preprocessor.sebek; }; + println!("{:?}", flags); let versions_handler = versions_handler::Handler::new(); let parsed_version = versions_handler.parse_version(final_version, ansi_enabled); diff --git a/the-golden/src/interpreter/preprocessor.rs b/the-golden/src/interpreter/preprocessor.rs index 641a106..a1bedb2 100644 --- a/the-golden/src/interpreter/preprocessor.rs +++ b/the-golden/src/interpreter/preprocessor.rs @@ -11,6 +11,7 @@ pub struct Warnings { pub struct Preprocessor { pub disabled_warnings: Warnings, + pub no_brainfuck: bool, pub no_console: bool, pub sebek: [Option; 3], pub version: Option, @@ -21,6 +22,7 @@ impl Preprocessor { Self { disabled_warnings: Warnings { too_left_pointer: false }, + no_brainfuck: false, no_console: false, sebek: [None, None, None], version: None, @@ -48,6 +50,7 @@ impl Preprocessor { } self.version = Some(args[1].to_string()); } + "nobrainfuck" | "no-brainfuck" | "no_brainfuck" => self.no_brainfuck = true, "noconsole" | "no-console" | "no_console" => { if args_count < 2 { self.no_console = true; From 3fe24f2cf72f973afcce66bb41189dd8c0f97ff4 Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Wed, 31 Aug 2022 14:04:42 +0200 Subject: [PATCH 3/9] Apply cargo clippy suggestions --- the-golden/src/flags.rs | 6 +++++ .../src/interpreter/versions/handler.rs | 22 +++++++++---------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/the-golden/src/flags.rs b/the-golden/src/flags.rs index cc7eeab..2af24c2 100644 --- a/the-golden/src/flags.rs +++ b/the-golden/src/flags.rs @@ -20,6 +20,12 @@ pub struct Flags { pub version: Option, } +impl Default for Flags { + fn default() -> Self { + Self::new() + } +} + impl Flags { pub fn new() -> Self { Self { diff --git a/the-golden/src/interpreter/versions/handler.rs b/the-golden/src/interpreter/versions/handler.rs index 4b7d47e..21fd6da 100644 --- a/the-golden/src/interpreter/versions/handler.rs +++ b/the-golden/src/interpreter/versions/handler.rs @@ -84,19 +84,19 @@ impl Handler { } version_parsed.push(current_subversion.value.clone()); } - 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; + let prerelease = match (prerelease, !current_subversion.sub.is_empty()) { + (Some(ver), true) => { + 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()) } - 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() + _ => String::new(), }; let version_final = format!("{}{}", version_parsed.join("."), prerelease); if version_original != version_final && version_original.to_lowercase() != "latest" { From 27193b43825fb036c9c880bb04770c2a583a29db Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Wed, 31 Aug 2022 14:05:00 +0200 Subject: [PATCH 4/9] Change the version to 0.4.0 --- the-golden/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/the-golden/Cargo.toml b/the-golden/Cargo.toml index 3278517..cf2dda5 100644 --- a/the-golden/Cargo.toml +++ b/the-golden/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "the-golden" -version = "0.3.0" +version = "0.4.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html From c6a7182f7a05f8c3671b72eff0b1d40b3e9c7b86 Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Wed, 31 Aug 2022 14:19:32 +0200 Subject: [PATCH 5/9] Bring compatibility with brainfuck You can now paste in brainfuck code and it will run it --- the-golden/src/interpreter/interpreter.rs | 1 - the-golden/src/interpreter/preprocessor.rs | 3 +++ .../src/interpreter/versions/v0-4-0/main.rs | 22 +++++++++++-------- .../src/interpreter/versions/v0-4-0/parser.rs | 17 +++++--------- .../interpreter/versions/v0-4-0/validator.rs | 14 ------------ the-golden/src/main.rs | 2 +- 6 files changed, 23 insertions(+), 36 deletions(-) diff --git a/the-golden/src/interpreter/interpreter.rs b/the-golden/src/interpreter/interpreter.rs index 09e84ca..38ad617 100644 --- a/the-golden/src/interpreter/interpreter.rs +++ b/the-golden/src/interpreter/interpreter.rs @@ -31,7 +31,6 @@ impl Interpreter { if !flags.sebek.iter().any(|val| val.is_some()) { flags.sebek = preprocessor.sebek; }; - println!("{:?}", flags); let versions_handler = versions_handler::Handler::new(); let parsed_version = versions_handler.parse_version(final_version, ansi_enabled); diff --git a/the-golden/src/interpreter/preprocessor.rs b/the-golden/src/interpreter/preprocessor.rs index a1bedb2..4ed4559 100644 --- a/the-golden/src/interpreter/preprocessor.rs +++ b/the-golden/src/interpreter/preprocessor.rs @@ -38,6 +38,9 @@ impl Preprocessor { if statement.ends_with(':') { statement_chars.next_back(); } + if statement.ends_with('#') { + statement_chars.next_back(); + } let args = statement_chars.as_str().split(' ').collect::>(); if args.is_empty() { continue; diff --git a/the-golden/src/interpreter/versions/v0-4-0/main.rs b/the-golden/src/interpreter/versions/v0-4-0/main.rs index fddf898..a7a33f1 100644 --- a/the-golden/src/interpreter/versions/v0-4-0/main.rs +++ b/the-golden/src/interpreter/versions/v0-4-0/main.rs @@ -60,16 +60,16 @@ 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"^'?\$,").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(r"^(:|:?\r?\n)").unwrap(), // end of line Regex::new("^\"[^\"]*\"").unwrap(), // comments Regex::new(r"^[ \t\f\v]").unwrap(), // whitespace Regex::new(crate::PREPROCESSOR_REGEX).unwrap(), //preprocessor regex @@ -160,6 +160,10 @@ impl Runner { 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]]; + if !self.flags.no_brainfuck { + self.memory[1][0] = 1.0; + local_memory[1][0] = 1.0; + } let mut local_memory_pointers: [usize; 2] = [0, 0]; let mut active_local_memory: usize = 0; let program_length = parser.commands.len(); @@ -273,7 +277,7 @@ 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 ^= 1, - "$." => { + "$," => { if self.input_cache.is_none() { self.input_cache = Some(Utils::get_input_line()); } @@ -286,7 +290,7 @@ impl Runner { } } } - "$," => { + "," => { if self.input_cache.is_none() { self.input_cache = Some(Utils::get_input_line()); } @@ -295,13 +299,13 @@ impl Runner { 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, self.ansi_enabled), e); } } - "\\," => match char::from_u32(main_memory[main_active_memory][main_memory_pointers[main_active_memory]].floor() as u32) { + "." => 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() { diff --git a/the-golden/src/interpreter/versions/v0-4-0/parser.rs b/the-golden/src/interpreter/versions/v0-4-0/parser.rs index 7724194..126c059 100644 --- a/the-golden/src/interpreter/versions/v0-4-0/parser.rs +++ b/the-golden/src/interpreter/versions/v0-4-0/parser.rs @@ -1,18 +1,18 @@ -use regex::Regex; - pub struct Parser { pub commands: Vec, - pub commands_info: Vec<(String, usize, usize, std::path::PathBuf)> + pub commands_info: Vec<(String, usize, usize, std::path::PathBuf)>, } impl Parser { pub fn new() -> Self { - Self { commands: vec![], commands_info: vec![] } + 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(); @@ -20,16 +20,11 @@ impl Parser { 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-4-0/validator.rs b/the-golden/src/interpreter/versions/v0-4-0/validator.rs index 2cbb93e..76389bf 100644 --- a/the-golden/src/interpreter/versions/v0-4-0/validator.rs +++ b/the-golden/src/interpreter/versions/v0-4-0/validator.rs @@ -1,5 +1,3 @@ -use regex::Regex; - use super::INFO_PREFIX_LENGTH; use crate::Utils; @@ -11,9 +9,7 @@ impl Validator { if heavy_debug { 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, ansi_enabled), t); @@ -22,16 +18,6 @@ impl Validator { 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) } } diff --git a/the-golden/src/main.rs b/the-golden/src/main.rs index 0e4e9cc..a9fd3ea 100644 --- a/the-golden/src/main.rs +++ b/the-golden/src/main.rs @@ -12,7 +12,7 @@ mod utils; pub use utils::Utils; pub const INFO_PREFIX_LENGTH: usize = 12; -pub const PREPROCESSOR_REGEX: &str = "#[^:]*:\r?\n?"; +pub const PREPROCESSOR_REGEX: &str = "#[^#\r\n]*(#|#?\r?\n)"; fn main() { dotenv().ok(); From 0619a42590e6e48c82b6648912caaaf04c70aa5a Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Wed, 31 Aug 2022 16:09:15 +0200 Subject: [PATCH 6/9] Change how you access the local memory Now ' is used to toggle the use of local memory, instead of denoting that the following command will use it --- .../src/interpreter/versions/v0-4-0/main.rs | 53 ++++++++++--------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/the-golden/src/interpreter/versions/v0-4-0/main.rs b/the-golden/src/interpreter/versions/v0-4-0/main.rs index a7a33f1..9d2c7fb 100644 --- a/the-golden/src/interpreter/versions/v0-4-0/main.rs +++ b/the-golden/src/interpreter/versions/v0-4-0/main.rs @@ -35,6 +35,7 @@ pub struct Runner { program_pointer: usize, + on_local: bool, loops: Vec, memory: [Vec; 2], memory_pointers: [usize; 2], @@ -46,29 +47,30 @@ pub struct Runner { impl Runner { 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 - 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"^(\|-?[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 between local and global memory + 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 @@ -99,6 +101,7 @@ impl Runner { program_pointer: 0, + on_local: false, loops: vec![], memory: [vec![0.0], vec![0.0]], memory_pointers: [0, 0], @@ -189,9 +192,8 @@ 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 is_local = self.on_local; 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), @@ -257,6 +259,7 @@ impl Runner { *divident /= divisor } } + "'" => self.on_local = !self.on_local, "`" => main_memory[main_active_memory][main_memory_pointers[main_active_memory]] = rand::thread_rng().gen(), ">" => { main_memory_pointers[main_active_memory] += 1; From d99cad3ce1d64e9f75791f0b23c3c5cfabdbd5bb Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Wed, 31 Aug 2022 21:05:44 +0200 Subject: [PATCH 7/9] (Lexer) Fix column counting --- the-golden/src/interpreter/versions/v0-4-0/lexer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/the-golden/src/interpreter/versions/v0-4-0/lexer.rs b/the-golden/src/interpreter/versions/v0-4-0/lexer.rs index 93b6f24..e3877f1 100644 --- a/the-golden/src/interpreter/versions/v0-4-0/lexer.rs +++ b/the-golden/src/interpreter/versions/v0-4-0/lexer.rs @@ -50,7 +50,7 @@ impl Lexer { self.position += command_length; if command.contains('\n') { self.line += command.matches('\n').count(); - self.column += command.split('\n').last().unwrap().len(); + self.column = command.split('\n').last().unwrap().len() + 1; } else { self.column += command_length; } From d35370709aff982b13fd845aa464febd035bf6f2 Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Thu, 1 Sep 2022 06:58:12 +0200 Subject: [PATCH 8/9] Update the README and examples to match the new release --- README.md | 131 ++++++++++++++++++++++--------------- examples/fibonacci.au | 2 +- examples/fibonacci_zero.au | 2 +- examples/hello_world.au | 2 +- 4 files changed, 83 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index 9920a5d..6a1102b 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,8 @@ ## Table of contents -1. [Basic info](#basic-info) +1. [Basic info](#basic-info)
+ 1.1 [Brainfuck compatibility](#basic-info-bf-compatibility) 2. [How to run your code](#run-code)
2.1 [Arguments](#run-code-args)
2.2 [Flags](#run-code-flags) @@ -14,14 +15,25 @@ 7.1 [Using it](#preprocessor-using-it)
7.2 [Supported statements](#preprocessor-supported-statements) 8. [Incoming features](#incoming-features) -9. [Examples](#examples) +9. [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. +This language is a fairly good superset of 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, while preserving backwards compatibility (unless you explicitly remove it).
Its purpose is to let people make Brainfuck-styled programs less painfully. +### Brainfuck compatibility +In the version 0.4.0, the language was made compatible with Brainfuck code, meaning you can paste in Brainfuck code, add The Golden features, and it will work. It has one limit though - the `+` and `-` commands will only work if the active cell in the inactive memory is `1`. This is true by default and as long as you don't change the value during runtime (which isn't possible in Brainfuck programs, so we consider it enough) or you don't use the `--no-brainfuck` flag or `#no-brainfuck` preprocessor statement, which set all memory cells to 0.
+An example of such code is below:
+Brainfuck: +``` +>++++++++[<+++++++++>-]<.>++++[<+++++++>-]<+.+++++++..+++.>>++++++[<+++++++>-]<++.------------.>++++++[<+++++++++>-]<+.<.+++.------.--------.>>>++++[<++++++++>-]<+. +``` +Brainfuck with The Golden: +``` +>|8|+[<|9|+>-]<.>++++[<|7|+>-]<+.|7|+..+++.>>|6|+[<|7|+>-]<++.|12|-.>|6|+[<|9|+>-]<+.<.+++.|6|-.|8|-.>>>++++[<|8|+>-]<+. +``` ## How to run your code 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. @@ -37,23 +49,24 @@ You can run the code with some arguments including: ### 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-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 | -| --sebek | `--sebek -1|0|1` | Specify the results for division by 0. First number is for dividing a number < 0, second for dividing 0 itself, and the third is for dividing a number > 0 | -| --version | `--version 0.1.0` | Run the code using a specific version of the interpreter | +| 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-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 | +| --no-brainfuck | `--no-brainfuck` | Set first cells of secondary memories to 0, removing the compatibility with Brainfuck | +| --sebek | `--sebek -1\|0\|1` | Specify the results for division by 0. First number is for dividing a number < 0, second for dividing 0 itself, and the third is for dividing a number > 0 | +| --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! @@ -72,38 +85,48 @@ If you go into memory index -1, a 0 is added at that position and the whole memo 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. +| 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 (also adds one to the current cell in brainfuck compatible mode, because the inactive cell is 1) | `+` | Yes | Yes | +| - | Subtracts the cell in the inactive row from the cell in the active row (also subtracts one from the current cell in brainfuck compatible mode, because the inactive cell is 1) | `-` | 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 | +| ; | Switches the values of the active global cell and the active local cell | `;` | No | Yes | +| ' | Toggle if you are working with local or global memory | `'` | No | Yes | +| "" | Comments | `"This is a comment"` | No | No | ## Preprocessor @@ -112,18 +135,24 @@ The values parsed by the preprocessor are overridden by the flags passed in from ### Using it -The statements are put into the code file and begin with a `#`. +The statements are put into the code file and begin with a `#`. They can either end by a new line, a `#` and a new line, or just a `#`: +``` +#version 0.4.0 +#sebek -1|0|1# +#no-brainfuck#'!!!'$.'$.^|5|!$. "a" +``` ### Supported statements Statement names are case-insensitive, so `version` is the same as `VERSION` and `VerSIoN`. However, this may not be true for other parts of the statement. -| Statement | Aliases | Arguments | Explanation | Example | -| :----------------- | :------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------- | :----------------------------------- | -| `version` | None | None | Specifies the version of the interpreter to launch | `#version 0.3.0` | -| `no-console` | `noconsole`, `no_console` | None | Hides the console when running the code | `#no-console` | -| `disable-warnings` | `disablewarnings`, `disable_warnings` | The warning to disable: `too-left-pointer` (`tooleftpointer`) | Disables the specified warning | `#disable-warnings too-left-pointer` | -| `sebek` | None | The results of division by zero for negative numbers (``), zero itself (``), and positive numbers (`

`), separated by `\|`: `\|\|

` | Sets the result of division by zero to the specified number depending on the value of the number being divided | `sebek -1\|0\|1` (if a negative number was divided by 0 the result would be -1, if 0 was divided by 0 the result would be 0, and if a positive number was divided by 0 the result would be 1) | +| Statement | Aliases | Arguments | Explanation | Example | +| :----------------- | :------------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `version` | None | None | Specifies the version of the interpreter to launch | `#version 0.3.0` | +| `no-console` | `noconsole`, `no_console` | None | Hides the console when running the code | `#no-console` | +| `no-brainfuck` | `brainfuck`, `no_brainfuck` | None | Sets first cells of secondary memories to 0, removing the compatibility with Brainfuck, but preserving old memory values | `#no-brainfuck` | +| `disable-warnings` | `disablewarnings`, `disable_warnings` | The warning to disable: `too-left-pointer` (`tooleftpointer`) | Disables the specified warning | `#disable-warnings too-left-pointer` | +| `sebek` | None | The results of division by zero for negative numbers (``), zero itself (``), and positive numbers (`

`), separated by `\|`: `\|\|

` | Sets the result of division by zero to the specified number depending on the value of the number being divided | `sebek -1\|0\|1` (if a negative number was divided by 0 the result would be -1, if 0 was divided by 0 the result would be 0, and if a positive number was divided by 0 the result would be 1) | ## Incoming features @@ -137,13 +166,13 @@ Here are some examples written in this language:
"Hello, world!": ``` -|72|!\,|29|!\,|7|!\,\,|3|!\,|67|~\,|12|~\,|87|!\,|8|~\,|3|!\,|6|~\,|8|~\,|67|~\,: +|72|!.|29|!.|7|!..|3|!.|67|~.|12|~.|87|!.|8|~.|3|!.|6|~.|8|~.|67|~. ``` Fibonacci sequence: ``` -!>|10|!<^>|10|!<[@^+\.>\,<@]: +^~^!>|10|!<^>|10|!<[@^+$.>.<@] ``` You can find all the examples in the [examples folder](https://github.com/Pandicon/The-Golden/tree/main/examples). \ No newline at end of file diff --git a/examples/fibonacci.au b/examples/fibonacci.au index 5db292b..2fb7b7b 100644 --- a/examples/fibonacci.au +++ b/examples/fibonacci.au @@ -1 +1 @@ -!>|10|!<^>|10|!<[@^+\.>\,<@]: \ No newline at end of file +^~^!>|10|!<^>|10|!<[@^+$.>.<@] \ No newline at end of file diff --git a/examples/fibonacci_zero.au b/examples/fibonacci_zero.au index 223b828..615a21c 100644 --- a/examples/fibonacci_zero.au +++ b/examples/fibonacci_zero.au @@ -1 +1 @@ -!>|10|!<^>|10|!<\.>\,<[@^+\.>\,<@]: \ No newline at end of file +^~^!>|10|!<^>|10|!<$.>.<[@^+$.>.<@] \ No newline at end of file diff --git a/examples/hello_world.au b/examples/hello_world.au index f4fa2bc..d815b78 100644 --- a/examples/hello_world.au +++ b/examples/hello_world.au @@ -1 +1 @@ -|72|!\,|29|!\,|7|!\,\,|3|!\,|67|~\,|12|~\,|87|!\,|8|~\,|3|!\,|6|~\,|8|~\,|67|~\,: \ No newline at end of file +|72|!.|29|!.|7|!..|3|!.|67|~.|12|~.|87|!.|8|~.|3|!.|6|~.|8|~.|67|~. \ No newline at end of file From c5e9958fee14d5240458a1653a545161942f2a18 Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Thu, 1 Sep 2022 07:01:21 +0200 Subject: [PATCH 9/9] Make input chainable --- README.md | 4 ++-- the-golden/src/interpreter/versions/v0-4-0/main.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6a1102b..39f1f93 100644 --- a/README.md +++ b/README.md @@ -114,8 +114,8 @@ The magic of Brainfuck-like syntax is that it is easy and extremely difficult at | ^ | 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 | +| $. | Output the cell as a number (if the cell value is 69, 69 will be printed) | `$.` | Yes | Yes | +| . | Output the cell as a character (if the cell value is 69, E will be printed) | `.` | Yes | Yes | | [ | Start a while loop | `[` | No | Yes | | ] | End a while loop | `]` | No | Yes | | [@ | Start a do-while loop | `[@` | No | Yes | diff --git a/the-golden/src/interpreter/versions/v0-4-0/main.rs b/the-golden/src/interpreter/versions/v0-4-0/main.rs index 9d2c7fb..62eade8 100644 --- a/the-golden/src/interpreter/versions/v0-4-0/main.rs +++ b/the-golden/src/interpreter/versions/v0-4-0/main.rs @@ -64,8 +64,8 @@ impl Runner { 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(), // output number + Regex::new(r"^(\|-?[0-9]*\|)*\.").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