Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Version 0.3.0 #2

Merged
merged 9 commits into from
Aug 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 29 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)<br>
7.1 [Using it](#preprocessor-using-it)<br>
7.2 [Supported statements](#preprocessor-supported-statements)
8. [Incoming features](#incoming-features)
9. [Examples](#examples)

## Basic info <a name="basic-info"></a>

Expand Down Expand Up @@ -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 <a name="main-features"></a>
Expand Down Expand Up @@ -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) | <code>`</code> | 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) | <code>`</code> | 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 |
Expand All @@ -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 <a name="preprocessor"></a>

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.<br>
The values parsed by the preprocessor are overridden by the flags passed in from the command line.

### Using it <a name="preprocessor-using-it"></a>

The statements are put into the code file and begin with a `#`.

### Supported statements <a name="preprocessor-supported-statements"></a>

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 (`<n>`), zero itself (`<z>`), and positive numbers (`<p>`), separated by `\|`: `<n>\|<z>\|<p>` | 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 <a name="incoming-features"></a>

- Functions
Expand All @@ -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).
2 changes: 1 addition & 1 deletion the-golden/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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
Expand Down
9 changes: 9 additions & 0 deletions the-golden/src/flags.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use crate::Utils;

#[derive(Clone, Debug)]
pub struct Warnings {
pub too_left_pointer: bool,
Expand All @@ -13,6 +15,7 @@ pub struct Flags {
pub debug_heavy: bool,
pub no_console: bool,
pub raw_code_to_run: Option<String>,
pub sebek: [Option<f64>; 3],
pub version: Option<String>,
}

Expand All @@ -27,6 +30,7 @@ impl Flags {
debug_heavy: false,
no_console: false,
raw_code_to_run: None,
sebek: [None, None, None],
version: None,
}
}
Expand All @@ -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());
Expand Down
22 changes: 19 additions & 3 deletions the-golden/src/interpreter/interpreter.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use crate::Flags;

#[path = "./preprocessor.rs"]
mod preprocessor;
#[path = "./versions/handler.rs"]
mod versions_handler;

Expand All @@ -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<String>, 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,
}
Expand Down
79 changes: 79 additions & 0 deletions the-golden/src/interpreter/preprocessor.rs
Original file line number Diff line number Diff line change
@@ -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<f64>; 3],
pub version: Option<String>,
}

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::<Vec<&str>>();
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::<Vec<&str>>();
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]);
}
_ => {}
}
}
}
}
16 changes: 12 additions & 4 deletions the-golden/src/interpreter/versions/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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]);
Expand All @@ -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::<Vec<String>>();
let s = parts[2].split('-').map(|x| x.to_string()).collect::<Vec<String>>();
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::<Vec<String>>();
let s2 = p.split('+').map(|x| x.to_string()).collect::<Vec<String>>();
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)
}
Expand Down Expand Up @@ -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;
}
}
}
Expand Down
77 changes: 77 additions & 0 deletions the-golden/src/interpreter/versions/v0-3-0/brackets_matcher.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
use std::collections::HashMap;

pub struct BracketsMatcher {
pub brackets: HashMap<String, HashMap<usize, usize>>,
brackets_mem: Vec<(String, usize, isize)>,
bracket_keys: HashMap<String, String>,
ending_brackets_keys: HashMap<String, String>
}

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
}
}
Loading