From 499f09b7e52cf76a24cf655a5e724d880c6fb0c7 Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Tue, 9 Aug 2022 21:13:44 +0200 Subject: [PATCH 1/9] Start version 0.3.0 development --- the-golden/Cargo.toml | 2 +- .../src/interpreter/versions/handler.rs | 9 + .../versions/v0-3-0/brackets_matcher.rs | 77 ++++ .../src/interpreter/versions/v0-3-0/lexer.rs | 69 +++ .../src/interpreter/versions/v0-3-0/main.rs | 409 ++++++++++++++++++ .../src/interpreter/versions/v0-3-0/parser.rs | 35 ++ .../interpreter/versions/v0-3-0/validator.rs | 37 ++ 7 files changed, 637 insertions(+), 1 deletion(-) create mode 100644 the-golden/src/interpreter/versions/v0-3-0/brackets_matcher.rs create mode 100644 the-golden/src/interpreter/versions/v0-3-0/lexer.rs create mode 100644 the-golden/src/interpreter/versions/v0-3-0/main.rs create mode 100644 the-golden/src/interpreter/versions/v0-3-0/parser.rs create mode 100644 the-golden/src/interpreter/versions/v0-3-0/validator.rs diff --git a/the-golden/Cargo.toml b/the-golden/Cargo.toml index 772eb30..3278517 100644 --- a/the-golden/Cargo.toml +++ b/the-golden/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "the-golden" -version = "0.1.0" +version = "0.3.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/the-golden/src/interpreter/versions/handler.rs b/the-golden/src/interpreter/versions/handler.rs index 496926c..03ac399 100644 --- a/the-golden/src/interpreter/versions/handler.rs +++ b/the-golden/src/interpreter/versions/handler.rs @@ -4,6 +4,8 @@ use crate::Flags; mod v0_1_0; #[path = "./v0-2-0/main.rs"] mod v0_2_0; +#[path = "./v0-3-0/main.rs"] +mod v0_3_0; pub struct Handler { versions: Versions, @@ -16,6 +18,7 @@ impl Handler { 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![])]), + Version::new(String::from("3"), vec![Version::new(String::from("0"), vec![])]), ], ); let versions = Versions::new(vec![versions_0]); @@ -118,6 +121,12 @@ impl Handler { }; v0_2_0::Runner::new(code, code_path, flags, ansi_enabled).run() } + "0.3.0" => { + if flags.debug { + println!("{}Running version 0.2.0", crate::Utils::ansi_escape_text("94", "DEBUG", v0_3_0::INFO_PREFIX_LENGTH, ansi_enabled)); + }; + v0_3_0::Runner::new(code, code_path, flags, ansi_enabled).run() + } _ => { println!( "{}Couldn't run version {}", diff --git a/the-golden/src/interpreter/versions/v0-3-0/brackets_matcher.rs b/the-golden/src/interpreter/versions/v0-3-0/brackets_matcher.rs new file mode 100644 index 0000000..4519556 --- /dev/null +++ b/the-golden/src/interpreter/versions/v0-3-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-3-0/lexer.rs b/the-golden/src/interpreter/versions/v0-3-0/lexer.rs new file mode 100644 index 0000000..93b6f24 --- /dev/null +++ b/the-golden/src/interpreter/versions/v0-3-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-3-0/main.rs b/the-golden/src/interpreter/versions/v0-3-0/main.rs new file mode 100644 index 0000000..7d225d2 --- /dev/null +++ b/the-golden/src/interpreter/versions/v0-3-0/main.rs @@ -0,0 +1,409 @@ +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 + ]; + 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 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, 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 = (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, 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 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, 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-3-0/parser.rs b/the-golden/src/interpreter/versions/v0-3-0/parser.rs new file mode 100644 index 0000000..7724194 --- /dev/null +++ b/the-golden/src/interpreter/versions/v0-3-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-3-0/validator.rs b/the-golden/src/interpreter/versions/v0-3-0/validator.rs new file mode 100644 index 0000000..2cbb93e --- /dev/null +++ b/the-golden/src/interpreter/versions/v0-3-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 b4597aa5106cb13bf5c3f6ac6c2baae99d7c3d66 Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Tue, 9 Aug 2022 21:19:41 +0200 Subject: [PATCH 2/9] Make the active memory switch better Converted it from "(main_active_memory as isize - 1).abs() as usize" to "main_active_memory ^ 1" --- .../src/interpreter/versions/v0-3-0/main.rs | 28 ++++++------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/the-golden/src/interpreter/versions/v0-3-0/main.rs b/the-golden/src/interpreter/versions/v0-3-0/main.rs index 7d225d2..b405019 100644 --- a/the-golden/src/interpreter/versions/v0-3-0/main.rs +++ b/the-golden/src/interpreter/versions/v0-3-0/main.rs @@ -225,22 +225,10 @@ impl Runner { 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]] += 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]], + "/" => 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]] = rand::thread_rng().gen(), ">" => { main_memory_pointers[main_active_memory] += 1; @@ -260,7 +248,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 = (main_active_memory as isize - 1).abs() as usize, + "^" => main_active_memory ^= 1, "$." => { if self.input_cache.is_none() { self.input_cache = Some(Utils::get_input_line()); @@ -333,7 +321,7 @@ impl Runner { } } "?=" => { - let inactive_memory = (main_active_memory as isize - 1).abs() as usize; + 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(); @@ -341,7 +329,7 @@ impl Runner { } } "?>" => { - let inactive_memory = (main_active_memory as isize - 1).abs() as usize; + 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(); @@ -349,7 +337,7 @@ impl Runner { } } "?<" => { - let inactive_memory = (main_active_memory as isize - 1).abs() as usize; + 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(); From cb143893441a4658a6309c497b4956bb96ef832f Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Tue, 30 Aug 2022 10:26:41 +0200 Subject: [PATCH 3/9] Add a basic preprocessor This allows for specifying the interpreter version in the code file itself, which ensures it will run on the correct version without relying on the user to pass in correct flags --- the-golden/src/interpreter/interpreter.rs | 19 ++++- the-golden/src/interpreter/preprocessor.rs | 71 +++++++++++++++++++ .../src/interpreter/versions/handler.rs | 9 ++- .../src/interpreter/versions/v0-3-0/main.rs | 53 +++++++------- the-golden/src/main.rs | 5 +- 5 files changed, 121 insertions(+), 36 deletions(-) create mode 100644 the-golden/src/interpreter/preprocessor.rs diff --git a/the-golden/src/interpreter/interpreter.rs b/the-golden/src/interpreter/interpreter.rs index e4c1ea0..a55207b 100644 --- a/the-golden/src/interpreter/interpreter.rs +++ b/the-golden/src/interpreter/interpreter.rs @@ -1,5 +1,7 @@ use crate::Flags; +#[path = "./preprocessor.rs"] +mod preprocessor; #[path = "./versions/handler.rs"] mod versions_handler; @@ -14,14 +16,25 @@ pub struct Interpreter { } impl Interpreter { - pub fn new(mut version: String, code: String, code_path: std::path::PathBuf, flags: Flags, ansi_enabled: bool) -> Self { + 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_console |= preprocessor.no_console; + let final_version = if let Some(ver) = version { + ver + } else if let Some(ver) = preprocessor.version { + ver + } else { + String::from("latest") + }; let versions_handler = versions_handler::Handler::new(); - version = versions_handler.parse_version(version, ansi_enabled); + let parsed_version = versions_handler.parse_version(final_version, ansi_enabled); + Self { flags, ansi_enabled, code, - version, + version: parsed_version, versions_handler, code_path, } diff --git a/the-golden/src/interpreter/preprocessor.rs b/the-golden/src/interpreter/preprocessor.rs new file mode 100644 index 0000000..a26106f --- /dev/null +++ b/the-golden/src/interpreter/preprocessor.rs @@ -0,0 +1,71 @@ +use regex::Regex; + +#[derive(Clone, Debug)] +pub struct Warnings { + pub too_left_pointer: bool, +} + +#[derive(Clone, Debug)] +pub struct Preprocessor { + pub disabled_warnings: Warnings, + + pub no_console: bool, + pub version: Option, +} + +impl Preprocessor { + pub fn new() -> Self { + Self { + disabled_warnings: Warnings { too_left_pointer: false }, + + no_console: false, + version: None, + } + } + + pub fn run(&mut self, code: &str) { + let rule = Regex::new(crate::PREPROCESSOR_REGEX).unwrap(); + let statements = rule.find_iter(code).map(|m| m.as_str().trim()).collect::>(); + for &statement in &statements { + let mut statement_chars = statement.chars(); + statement_chars.next(); + if statement.ends_with(':') { + statement_chars.next_back(); + } + let args = statement_chars.as_str().split(' ').collect::>(); + if args.is_empty() { + continue; + } + println!("{args:?}"); + let args_count = args.len(); + match args[0].to_lowercase().as_str() { + "version" => { + if args_count < 2 { + continue; + } + self.version = Some(args[1].to_string()); + } + "noconsole" | "no-console" => { + if args_count < 2 { + self.no_console = true; + continue; + } + self.no_console = args[1].to_lowercase() != "false"; + } + "disablewarnings" | "disable-warnings" => { + if args_count < 2 { + continue; + } + match args[1].to_lowercase().as_str() { + "too-left-pointer" | "tooleftpointer" => { + self.disabled_warnings.too_left_pointer = true; + } + _ => {} + } + } + _ => {} + } + } + println!("{:?}", self); + } +} diff --git a/the-golden/src/interpreter/versions/handler.rs b/the-golden/src/interpreter/versions/handler.rs index 03ac399..2ca27b4 100644 --- a/the-golden/src/interpreter/versions/handler.rs +++ b/the-golden/src/interpreter/versions/handler.rs @@ -35,13 +35,13 @@ 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 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::>(); + 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())) + (vec![parts[0].clone(), parts[1].clone(), s[0].clone()], Some(s2[0].clone()), Some(s2[1..].join("+"))) } else { (vec![parts[0].clone(), parts[1].clone(), s[0].clone()], Some(s_joined), None) } @@ -123,7 +123,7 @@ impl Handler { } "0.3.0" => { if flags.debug { - println!("{}Running version 0.2.0", crate::Utils::ansi_escape_text("94", "DEBUG", v0_3_0::INFO_PREFIX_LENGTH, ansi_enabled)); + println!("{}Running version 0.3.0", crate::Utils::ansi_escape_text("94", "DEBUG", v0_3_0::INFO_PREFIX_LENGTH, ansi_enabled)); }; v0_3_0::Runner::new(code, code_path, flags, ansi_enabled).run() } @@ -133,7 +133,6 @@ impl Handler { crate::Utils::ansi_escape_text("91", "ERROR", v0_1_0::INFO_PREFIX_LENGTH, ansi_enabled), version ); - return; } } } diff --git a/the-golden/src/interpreter/versions/v0-3-0/main.rs b/the-golden/src/interpreter/versions/v0-3-0/main.rs index b405019..3deda6d 100644 --- a/the-golden/src/interpreter/versions/v0-3-0/main.rs +++ b/the-golden/src/interpreter/versions/v0-3-0/main.rs @@ -46,32 +46,33 @@ 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"^:\r?\n?").unwrap(), // end of line - Regex::new("^\"[^\"]*\"").unwrap(), // comments - Regex::new(r"^[ \t\f\v]").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 + Regex::new(crate::PREPROCESSOR_REGEX).unwrap(), //preprocessor regex ]; Self { flags, diff --git a/the-golden/src/main.rs b/the-golden/src/main.rs index 46540a9..0e4e9cc 100644 --- a/the-golden/src/main.rs +++ b/the-golden/src/main.rs @@ -12,6 +12,7 @@ mod utils; pub use utils::Utils; pub const INFO_PREFIX_LENGTH: usize = 12; +pub const PREPROCESSOR_REGEX: &str = "#[^:]*:\r?\n?"; fn main() { dotenv().ok(); @@ -26,7 +27,7 @@ fn main() { flags_handler.parse(&args); let mut _action = String::new(); - let mut version = String::from("latest"); + let mut version = None; let mut code = String::new(); let mut code_path = std::path::PathBuf::new(); @@ -59,7 +60,7 @@ fn main() { return; } if let Some(v) = cloned_flags.version { - version = v; + version = Some(v); } if let Ok(val) = env::var("LOGS") { if val.to_lowercase() == "off" && cfg!(target_os = "windows") { From f718245a4c6c7e897a6be07e3e023e56128b7512 Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Tue, 30 Aug 2022 15:09:53 +0200 Subject: [PATCH 4/9] Add a --sebek flag --- the-golden/src/flags.rs | 9 +++++++++ the-golden/src/interpreter/interpreter.rs | 3 +++ the-golden/src/interpreter/preprocessor.rs | 12 ++++++++++-- the-golden/src/utils.rs | 12 ++++++++++++ 4 files changed, 34 insertions(+), 2 deletions(-) diff --git a/the-golden/src/flags.rs b/the-golden/src/flags.rs index 5067129..f44b059 100644 --- a/the-golden/src/flags.rs +++ b/the-golden/src/flags.rs @@ -1,3 +1,5 @@ +use crate::Utils; + #[derive(Clone, Debug)] pub struct Warnings { pub too_left_pointer: bool, @@ -13,6 +15,7 @@ pub struct Flags { pub debug_heavy: bool, pub no_console: bool, pub raw_code_to_run: Option, + pub sebek: [Option; 3], pub version: Option, } @@ -27,6 +30,7 @@ impl Flags { debug_heavy: false, no_console: false, raw_code_to_run: None, + sebek: [None, None, None], version: None, } } @@ -51,6 +55,11 @@ impl Flags { } "--disable-warnings" => self.disabled_warnings = Warnings { too_left_pointer: true }, "--disable-too-left-pointer-warning" => self.disabled_warnings.too_left_pointer = true, + "--sebek" => { + if i + 1 < args_count { + self.sebek = Utils::parse_sebek(&args[i + 1]); + } + } "-" => { if self.raw_code_to_run.is_none() && i + 1 < args_count { self.raw_code_to_run = Some(args[i + 1].clone()); diff --git a/the-golden/src/interpreter/interpreter.rs b/the-golden/src/interpreter/interpreter.rs index a55207b..71eb448 100644 --- a/the-golden/src/interpreter/interpreter.rs +++ b/the-golden/src/interpreter/interpreter.rs @@ -27,6 +27,9 @@ impl Interpreter { } else { String::from("latest") }; + if flags.sebek.iter().filter(|val| val.is_some()).collect::>>().is_empty() { + flags.sebek = preprocessor.sebek; + }; 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 a26106f..d1e81fe 100644 --- a/the-golden/src/interpreter/preprocessor.rs +++ b/the-golden/src/interpreter/preprocessor.rs @@ -1,5 +1,7 @@ use regex::Regex; +use crate::Utils; + #[derive(Clone, Debug)] pub struct Warnings { pub too_left_pointer: bool, @@ -10,6 +12,7 @@ pub struct Preprocessor { pub disabled_warnings: Warnings, pub no_console: bool, + pub sebek: [Option; 3], pub version: Option, } @@ -19,6 +22,7 @@ impl Preprocessor { disabled_warnings: Warnings { too_left_pointer: false }, no_console: false, + sebek: [None, None, None], version: None, } } @@ -36,7 +40,6 @@ impl Preprocessor { if args.is_empty() { continue; } - println!("{args:?}"); let args_count = args.len(); match args[0].to_lowercase().as_str() { "version" => { @@ -63,9 +66,14 @@ impl Preprocessor { _ => {} } } + "sebek" => { + if args_count < 2 { + continue; + } + self.sebek = Utils::parse_sebek(args[1]); + } _ => {} } } - println!("{:?}", self); } } diff --git a/the-golden/src/utils.rs b/the-golden/src/utils.rs index d7772e0..275e9b2 100644 --- a/the-golden/src/utils.rs +++ b/the-golden/src/utils.rs @@ -30,4 +30,16 @@ impl Utils { pub fn flush_console() -> std::io::Result<()> { std::io::stdout().flush() } + + pub fn parse_sebek(input: &str) -> [Option; 3] { + let mut sebek = [None, None, None]; + let args_vec = input.split("|").filter_map(|val| val.parse::().ok()).collect::>(); + for (i, &val) in args_vec.iter().enumerate() { + if i >= sebek.len() { + break; + } + sebek[i] = Some(val); + } + sebek + } } From c4e7d82f733543c4e636e75ea2b7214be6b96c68 Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Tue, 30 Aug 2022 15:42:15 +0200 Subject: [PATCH 5/9] Use the --sebek flag values in the interpreter --- .../src/interpreter/versions/v0-3-0/main.rs | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/the-golden/src/interpreter/versions/v0-3-0/main.rs b/the-golden/src/interpreter/versions/v0-3-0/main.rs index 3deda6d..fddf898 100644 --- a/the-golden/src/interpreter/versions/v0-3-0/main.rs +++ b/the-golden/src/interpreter/versions/v0-3-0/main.rs @@ -229,7 +229,30 @@ impl Runner { "+" => 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]], - "/" => 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; From 63cbacdf4a28206499ddb31c96c8525cad229076 Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Tue, 30 Aug 2022 15:45:47 +0200 Subject: [PATCH 6/9] Apply cargo clippy suggestions --- the-golden/src/interpreter/interpreter.rs | 3 ++- the-golden/src/utils.rs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/the-golden/src/interpreter/interpreter.rs b/the-golden/src/interpreter/interpreter.rs index 71eb448..98aea6a 100644 --- a/the-golden/src/interpreter/interpreter.rs +++ b/the-golden/src/interpreter/interpreter.rs @@ -27,9 +27,10 @@ impl Interpreter { } else { String::from("latest") }; - if flags.sebek.iter().filter(|val| val.is_some()).collect::>>().is_empty() { + if !flags.sebek.iter().any(|val| val.is_some()) { flags.sebek = preprocessor.sebek; }; + println!("{:?}", flags.sebek); let versions_handler = versions_handler::Handler::new(); let parsed_version = versions_handler.parse_version(final_version, ansi_enabled); diff --git a/the-golden/src/utils.rs b/the-golden/src/utils.rs index 275e9b2..1bc431a 100644 --- a/the-golden/src/utils.rs +++ b/the-golden/src/utils.rs @@ -33,7 +33,7 @@ impl Utils { pub fn parse_sebek(input: &str) -> [Option; 3] { let mut sebek = [None, None, None]; - let args_vec = input.split("|").filter_map(|val| val.parse::().ok()).collect::>(); + let args_vec = input.split('|').filter_map(|val| val.parse::().ok()).collect::>(); for (i, &val) in args_vec.iter().enumerate() { if i >= sebek.len() { break; From f0c6f6eee4241c6153de70958704daafdf4c9b33 Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Tue, 30 Aug 2022 15:46:04 +0200 Subject: [PATCH 7/9] Get rid of a print --- the-golden/src/interpreter/interpreter.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/the-golden/src/interpreter/interpreter.rs b/the-golden/src/interpreter/interpreter.rs index 98aea6a..0fd8788 100644 --- a/the-golden/src/interpreter/interpreter.rs +++ b/the-golden/src/interpreter/interpreter.rs @@ -30,7 +30,6 @@ impl Interpreter { if !flags.sebek.iter().any(|val| val.is_some()) { flags.sebek = preprocessor.sebek; }; - println!("{:?}", flags.sebek); let versions_handler = versions_handler::Handler::new(); let parsed_version = versions_handler.parse_version(final_version, ansi_enabled); From 381210758ec316f8d0d8414d915921a8def95258 Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Tue, 30 Aug 2022 21:47:18 +0200 Subject: [PATCH 8/9] (preprocessor) Add an option to use _ instead of - Since the case of names is ignored, you can have NO_CONSOLE, which looks better than NO-CONSOLE --- the-golden/src/interpreter/preprocessor.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/the-golden/src/interpreter/preprocessor.rs b/the-golden/src/interpreter/preprocessor.rs index d1e81fe..641a106 100644 --- a/the-golden/src/interpreter/preprocessor.rs +++ b/the-golden/src/interpreter/preprocessor.rs @@ -48,14 +48,14 @@ impl Preprocessor { } self.version = Some(args[1].to_string()); } - "noconsole" | "no-console" => { + "noconsole" | "no-console" | "no_console" => { if args_count < 2 { self.no_console = true; continue; } self.no_console = args[1].to_lowercase() != "false"; } - "disablewarnings" | "disable-warnings" => { + "disablewarnings" | "disable-warnings" | "disable_warnings" => { if args_count < 2 { continue; } From 99b5cfc1d273e28b315285671c45b5ba35294c6a Mon Sep 17 00:00:00 2001 From: Pandicon <70060103+Pandicon@users.noreply.github.com> Date: Tue, 30 Aug 2022 22:21:26 +0200 Subject: [PATCH 9/9] (README) Add version 0.3.0 documentation --- README.md | 45 +++++++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 6aaa43b..9920a5d 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,11 @@ 4. [Important notes](#important-notes) 5. [Mechanics](#mechanics) 6. [Syntax](#syntax) -7. [Incoming features](#incoming-features) -8. [Examples](#examples) +7. [Preprocessor](#preprocessor)
+ 7.1 [Using it](#preprocessor-using-it)
+ 7.2 [Supported statements](#preprocessor-supported-statements) +8. [Incoming features](#incoming-features) +9. [Examples](#examples) ## Basic info @@ -42,6 +45,7 @@ See the table below for some flags you can provide when running your code. | --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 | ## Main features @@ -87,19 +91,8 @@ The magic of Brainfuck-like syntax is that it is easy and extremely difficult at | ~ | 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 | +| _ | 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 | @@ -112,6 +105,26 @@ The magic of Brainfuck-like syntax is that it is easy and extremely difficult at Each line has to end with a punctuation (`:`) or else the program will crash. +## Preprocessor + +The preprocessor allows you to include flags into the code itself, so you don't have to rely on the user to run the code with the correct flags.
+The values parsed by the preprocessor are overridden by the flags passed in from the command line. + +### Using it + +The statements are put into the code file and begin with 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) | + ## Incoming features - Functions @@ -133,4 +146,4 @@ Fibonacci sequence: !>|10|!<^>|10|!<[@^+\.>\,<@]: ``` -You can find all the examples in the [examples folder](https://github.com/Pandicon/The-Golden/tree/main/examples). +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