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
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/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 e4c1ea0..0fd8788 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,28 @@ 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")
+ };
+ if !flags.sebek.iter().any(|val| val.is_some()) {
+ flags.sebek = preprocessor.sebek;
+ };
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..641a106
--- /dev/null
+++ b/the-golden/src/interpreter/preprocessor.rs
@@ -0,0 +1,79 @@
+use regex::Regex;
+
+use crate::Utils;
+
+#[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 sebek: [Option; 3],
+ pub version: Option,
+}
+
+impl Preprocessor {
+ pub fn new() -> Self {
+ Self {
+ disabled_warnings: Warnings { too_left_pointer: false },
+
+ no_console: false,
+ sebek: [None, None, None],
+ 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;
+ }
+ 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" | "no_console" => {
+ if args_count < 2 {
+ self.no_console = true;
+ continue;
+ }
+ self.no_console = args[1].to_lowercase() != "false";
+ }
+ "disablewarnings" | "disable-warnings" | "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;
+ }
+ _ => {}
+ }
+ }
+ "sebek" => {
+ if args_count < 2 {
+ continue;
+ }
+ self.sebek = Utils::parse_sebek(args[1]);
+ }
+ _ => {}
+ }
+ }
+ }
+}
diff --git a/the-golden/src/interpreter/versions/handler.rs b/the-golden/src/interpreter/versions/handler.rs
index 496926c..2ca27b4 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]);
@@ -32,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)
}
@@ -118,13 +121,18 @@ impl Handler {
};
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));
+ };
+ v0_3_0::Runner::new(code, code_path, flags, ansi_enabled).run()
+ }
_ => {
println!(
"{}Couldn't run version {}",
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/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