├── .gitignore ├── examples ├── hello_world.fr ├── return.fr ├── basic_ptr.fr ├── cprint.fr ├── free.fr ├── input.fr ├── smpl.fr └── reverse_polish_notation.fr ├── src ├── lib.rs ├── env.rs ├── bin.rs ├── simplify.rs ├── parser.lalrpop ├── ir.rs └── compile.rs ├── Cargo.toml ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | a.out 5 | *.c 6 | *.go 7 | *.smpl 8 | .vscode -------------------------------------------------------------------------------- /examples/hello_world.fr: -------------------------------------------------------------------------------- 1 | #[enable(brainfuck)] 2 | 3 | fn start() { 4 | println("Hello, world!"); 5 | } 6 | -------------------------------------------------------------------------------- /examples/return.fr: -------------------------------------------------------------------------------- 1 | #[enable(brainfuck)] 2 | 3 | fn start() { 4 | if test() { 5 | println("True!"); 6 | } else { 7 | println("False!"); 8 | } 9 | } 10 | 11 | fn test() { 12 | return 1; 13 | } -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate lazy_static; 3 | 4 | pub mod compile; 5 | pub use compile::*; 6 | pub mod env; 7 | pub use env::*; 8 | pub mod ir; 9 | pub use ir::*; 10 | pub mod parser; 11 | pub use parser::*; 12 | pub mod simplify; 13 | pub use simplify::*; 14 | -------------------------------------------------------------------------------- /examples/basic_ptr.fr: -------------------------------------------------------------------------------- 1 | 2 | fn start() { 3 | def a = 5; 4 | // print `a` as a digit by adding ascii code for 0 5 | println(add(a, 48)); 6 | inc(&a); 7 | // print `a` as a digit by adding ascii code for 0 8 | println(add(a, 48)); 9 | } 10 | 11 | fn inc(ptr) { 12 | *ptr = add(*ptr, 1); 13 | } -------------------------------------------------------------------------------- /examples/cprint.fr: -------------------------------------------------------------------------------- 1 | 2 | fn start() { 3 | def str = alloc(16); 4 | 5 | if 0 { 6 | *str = "True!\0"; 7 | } else { 8 | *str = "False!\0"; 9 | } 10 | 11 | cprint(str); 12 | } 13 | 14 | fn cprint(str) { 15 | def counter = 0; 16 | def running = *add(str, counter); 17 | while running { 18 | print(running); 19 | counter = add(counter, 1); 20 | running = *add(str, counter); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /examples/free.fr: -------------------------------------------------------------------------------- 1 | 2 | fn start() { 3 | // Allocate 128 bytes of memory and store the pointer to that block in `str` 4 | def size = 128; 5 | def str = alloc(size); 6 | free(str, size); 7 | } 8 | 9 | // free_byte only frees a single cell, so free must be implemented manually 10 | fn free(ptr, size) { 11 | while size { 12 | size = sub(size, 1); 13 | // free_byte is built in 14 | free_byte(add(ptr, size)); 15 | } 16 | 17 | // Store 0 in the return register 18 | return 0; 19 | } -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fr" 3 | version = "0.1.1" 4 | authors = ["adam-mcdaniel "] 5 | edition = "2018" 6 | description = "A programming language with an unusual compiler backend" 7 | homepage = "https://github.com/adam-mcdaniel/free" 8 | website = "https://github.com/adam-mcdaniel/free" 9 | readme = "README.md" 10 | license = "MIT" 11 | 12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 13 | 14 | [[bin]] 15 | name = "fr" 16 | path = "src/bin.rs" 17 | 18 | [build-dependencies] 19 | lalrpop = "0.17.2" 20 | 21 | [dependencies] 22 | lazy_static = "1.4" 23 | lalrpop = "0.17.2" 24 | lalrpop-util = "0.17.2" 25 | regex = "1.3" 26 | comment = "0.1" 27 | rand = "0.7" 28 | clap = "2.33" 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Adam McDaniel 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /examples/input.fr: -------------------------------------------------------------------------------- 1 | 2 | 3 | fn start() { 4 | def str = getline("Testing: "); 5 | 6 | print("You said: `"); 7 | cprint(str); 8 | print("`"); 9 | 10 | free(str, 128); 11 | } 12 | 13 | fn neq(ch1, ch2) { 14 | if sub(ch1, ch2) { return 1; } 15 | else { return 0; } 16 | } 17 | 18 | fn mul(m, n) { 19 | def result = m; 20 | while n { 21 | result = add(result, m); 22 | n = sub(n, 1); 23 | } 24 | return result; 25 | } 26 | 27 | 28 | fn getline(prompt) { 29 | print(prompt); 30 | def str = alloc(128); 31 | 32 | def counter = 0; 33 | def input = getch(); 34 | def running = mul(neq(input, '\n'), neq(input, 0)); 35 | while running { 36 | *add(str, counter) = input; 37 | input = getch(); 38 | counter = add(counter, 1); 39 | running = mul(neq(input, '\n'), neq(input, 0)); 40 | } 41 | 42 | return str; 43 | } 44 | 45 | // free_byte only frees a single cell, so free must be implemented manually 46 | fn free(ptr, size) { 47 | while size { 48 | size = sub(size, 1); 49 | // free_byte is built in 50 | free_byte(add(ptr, size)); 51 | } 52 | 53 | // Store 0 in the return register 54 | return 0; 55 | } -------------------------------------------------------------------------------- /src/env.rs: -------------------------------------------------------------------------------- 1 | use crate::{add_to_compiled, Error, Value}; 2 | use std::collections::HashMap; 3 | 4 | #[derive(Debug, Clone)] 5 | pub struct Env { 6 | scope: HashMap, 7 | } 8 | 9 | impl Env { 10 | pub fn new() -> Self { 11 | Self { 12 | scope: HashMap::new(), 13 | } 14 | } 15 | 16 | pub fn define(&mut self, name: impl ToString, value: Value) -> Result<(), Error> { 17 | if let Ok(previous_value) = self.get(name.to_string()) { 18 | previous_value.free(); 19 | } 20 | 21 | self.scope.insert(name.to_string(), value.copy()?); 22 | Ok(()) 23 | } 24 | 25 | pub fn define_no_cp(&mut self, name: impl ToString, value: Value) { 26 | if let Ok(previous_value) = self.get(name.to_string()) { 27 | previous_value.free(); 28 | } 29 | 30 | self.scope.insert(name.to_string(), value); 31 | } 32 | 33 | pub fn get(&mut self, name: impl ToString) -> Result { 34 | match self.scope.get(&name.to_string()) { 35 | Some(val) => Ok(*val), 36 | None => Err(Error::VariableNotDefined(name.to_string(), self.clone())), 37 | } 38 | } 39 | 40 | pub fn free(&mut self) { 41 | for value in self.scope.values() { 42 | // value.free(); 43 | if !value.is_ref() { 44 | value.free(); 45 | } else { 46 | add_to_compiled(format!("NOT FREEING {:#?}", value)); 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /examples/smpl.fr: -------------------------------------------------------------------------------- 1 | // 2 | // Compiler for the SMPL programming language written in free 3 | // 4 | 5 | 6 | fn start() { 7 | def program = alloc(8192); 8 | def counter = 0; 9 | def input = getch(); 10 | while input { 11 | *add(counter, program) = input; 12 | counter = add(counter, 1); 13 | input = getch(); 14 | } 15 | 16 | compile(program); 17 | } 18 | 19 | // @sig fn(char*) 20 | fn compile(program) { 21 | print("#include \nconst int TAPE_SIZE = 24576;\nconst int REF_TAPE_SIZE = 256;\nunsigned short tape[24576];\nunsigned int ref_tape[24576];\nunsigned int ptr = 0;\nunsigned int ref_ptr = 0;\nunsigned int allocate() {\n\tunsigned int size = tape[ptr];\n\tint cons_empty_spaces = 0;\n\tfor (int i=TAPE_SIZE-1; i>0; i--) {\n\t\tif (tape[i] == 0) { cons_empty_spaces++; }\n\t\telse { cons_empty_spaces = 0; }\n\t\tif (cons_empty_spaces == size) { return i; }\n\t}\n\treturn 0;\n}\nvoid plus(int n) {\ntape[ptr] += n;\n}\nvoid minus(int n) {\n\ttape[ptr] -= n;\n}\nvoid set(int n) {\n\ttape[ptr] = n;\n}\nvoid left(int n) {\n\tptr -= n;\n}\nvoid right(int n) {\n\tptr += n;\n}\nvoid deref() {\n\tref_tape[ref_ptr++ % REF_TAPE_SIZE] = ptr;\n\tptr = tape[ptr];\n}\nvoid refer() {\n\tptr = ref_tape[--ref_ptr % REF_TAPE_SIZE];\n}\nint main() {\n"); 22 | 23 | def n = 0; 24 | def running = *add(program, n); 25 | while running { 26 | compile_op(running); 27 | 28 | n = add(n, 1); 29 | running = *add(program, n); 30 | } 31 | 32 | print("\n}"); 33 | } 34 | 35 | 36 | // @sig fn(char) 37 | fn compile_op(ch) { 38 | if is_char(ch, '+') { println("plus(1);"); } 39 | if is_char(ch, '-') { println("minus(1);"); } 40 | if is_char(ch, '<') { println("left(1);"); } 41 | if is_char(ch, '>') { println("right(1);"); } 42 | if is_char(ch, '[') { println("while (tape[ptr]) {"); } 43 | if is_char(ch, ']') { println("}"); } 44 | if is_char(ch, '.') { println("printf(\"%c\",(char)(tape[ptr]%256));"); } 45 | if is_char(ch, ',') { println("scanf(\"%c\", (char*)&tape[ptr]);"); } 46 | if is_char(ch, '&') { println("refer();"); } 47 | if is_char(ch, '*') { println("deref();"); } 48 | if is_char(ch, '?') { println("allocate();"); } 49 | } 50 | 51 | 52 | // @sig fn(char, char) -> bool 53 | fn is_char(ch1, ch2) { 54 | if sub(ch1, ch2) { return 0; } else { return 1; } 55 | } 56 | -------------------------------------------------------------------------------- /src/bin.rs: -------------------------------------------------------------------------------- 1 | use clap::{clap_app, crate_version, AppSettings}; 2 | use fr::{Error, Program, Simplify, C}; 3 | use std::{ 4 | fs::{read_to_string, write}, 5 | process::exit, 6 | }; 7 | 8 | enum Target { 9 | C, 10 | BrainFuck, 11 | } 12 | 13 | fn main() -> Result<(), Error> { 14 | let matches = clap_app!(fr => 15 | (version: crate_version!()) 16 | (author: "Adam McDaniel ") 17 | (about: "Compiles code written in the Free programming language") 18 | (@arg input: +takes_value +required "Path to free file to compile") 19 | (@arg output: +takes_value "Path to output file") 20 | // (@arg leak_check: --leakcheck "Add memory leak checks") 21 | (@group target => 22 | (@arg c: -c --c "Compile to C") 23 | (@arg bf: -b --bf "Compile to SMPL/Brainfuck") 24 | ) 25 | ) 26 | .setting(AppSettings::ArgRequiredElseHelp) 27 | .get_matches(); 28 | 29 | let target = if matches.is_present("bf") { 30 | Target::BrainFuck 31 | } else { 32 | Target::C 33 | }; 34 | 35 | let optimization = 40; 36 | 37 | // let leak_check = matches.is_present("leak_check"); 38 | 39 | let output_file = match matches.value_of("output") { 40 | Some(file) => file, 41 | None => match target { 42 | Target::C => "out.c", 43 | Target::BrainFuck => "out.smpl", 44 | }, 45 | }; 46 | 47 | if let Some(file) = matches.value_of("input") { 48 | if let Ok(contents) = read_to_string(file) { 49 | let compiled = optimize( 50 | match Program::from(contents).compile() { 51 | Ok(c) => c, 52 | Err(e) => { 53 | println!("Could not compile program: {:#?}", e); 54 | exit(1); 55 | } 56 | }, 57 | optimization, 58 | ); 59 | 60 | let output_contents = match target { 61 | Target::C => C::simplify(compiled), 62 | Target::BrainFuck => compiled, 63 | }; 64 | 65 | if let Ok(_) = write(&output_file, &output_contents) { 66 | println!("Successfully compiled program to {}", output_file); 67 | } 68 | } 69 | } 70 | 71 | Ok(()) 72 | } 73 | 74 | pub fn optimize(s: impl ToString, level: usize) -> String { 75 | let mut compiled = s 76 | .to_string() 77 | .chars() 78 | .filter(|ch| ['>', '<', ',', '.', '[', ']', '+', '-', '*', '?', '&'].contains(ch)) 79 | .collect::(); 80 | let original_len = compiled.len(); 81 | 82 | for n in 1..level + 1 { 83 | let to = ">".repeat(n); 84 | let back = "<".repeat(n); 85 | 86 | let move1 = to.clone() + &back; 87 | let move2 = back + &to; 88 | for _ in 0..10 { 89 | compiled = compiled.replace(&move1, "").replace(&move2, "").replace("*&", ""); 90 | } 91 | } 92 | 93 | compiled 94 | } 95 | -------------------------------------------------------------------------------- /src/simplify.rs: -------------------------------------------------------------------------------- 1 | use crate::Program; 2 | 3 | pub trait Simplify { 4 | fn prelude() -> String; 5 | fn postlude() -> String; 6 | fn simplify(s: impl ToString) -> String; 7 | } 8 | 9 | pub struct C; 10 | impl C { 11 | pub fn new() -> Self { 12 | Self 13 | } 14 | } 15 | 16 | impl Simplify for C { 17 | fn prelude() -> String { 18 | format!( 19 | r#"#include 20 | const int TAPE_SIZE = {TAPE_SIZE}; 21 | const int REF_TAPE_SIZE = {REF_TAPE_SIZE}; 22 | 23 | unsigned short tape[{TAPE_SIZE}]; 24 | unsigned int ref_tape[{TAPE_SIZE}]; 25 | unsigned int ptr = 0; 26 | unsigned int ref_ptr = 0; 27 | 28 | unsigned int allocate() {{ 29 | unsigned int size = tape[ptr]; 30 | int cons_empty_spaces = 0; 31 | for (int i=TAPE_SIZE-1; i>0; i--) {{ 32 | if (tape[i] == 0) {{ cons_empty_spaces++; }} 33 | else {{ cons_empty_spaces = 0; }} 34 | if (cons_empty_spaces == size) {{ return i; }} 35 | }} 36 | return 0; 37 | }} 38 | 39 | 40 | void plus(int n) {{ 41 | tape[ptr] += n; 42 | }} 43 | 44 | void minus(int n) {{ 45 | tape[ptr] -= n; 46 | }} 47 | 48 | void set(int n) {{ 49 | tape[ptr] = n; 50 | }} 51 | 52 | void left(int n) {{ 53 | ptr -= n; 54 | }} 55 | 56 | void right(int n) {{ 57 | ptr += n; 58 | }} 59 | 60 | void deref() {{ 61 | ref_tape[ref_ptr++ % REF_TAPE_SIZE] = ptr; 62 | ptr = tape[ptr]; 63 | }} 64 | 65 | void refer() {{ 66 | ptr = ref_tape[--ref_ptr % REF_TAPE_SIZE]; 67 | }} 68 | 69 | int main() {{ 70 | "#, 71 | TAPE_SIZE = Program::tape_size(), 72 | REF_TAPE_SIZE = 256 73 | ) 74 | } 75 | 76 | fn postlude() -> String { 77 | String::from("}") 78 | } 79 | 80 | fn simplify(s: impl ToString) -> String { 81 | let mut result = Self::prelude(); 82 | let mut repeated = 0; 83 | let mut last = '\0'; 84 | 85 | let mut filtered = s 86 | .to_string() 87 | .chars() 88 | .filter(|c| ['>', '<', '+', '-', '*', '&', '?', '[', ']', '.', ','].contains(c)) 89 | .collect::(); 90 | filtered = filtered.replace("[-]", "0"); 91 | 92 | for ch in filtered.chars() { 93 | if ch == last { 94 | repeated += 1; 95 | } else { 96 | let line = match last { 97 | '>' => format!("right({});", repeated), 98 | '<' => format!("left({});", repeated), 99 | '+' => format!("plus({});", repeated), 100 | '-' => format!("minus({});", repeated), 101 | '0' => "set(0);".repeat(repeated), 102 | '*' => "deref();".repeat(repeated), 103 | '&' => "refer();".repeat(repeated), 104 | '?' => "tape[ptr] = allocate();".repeat(repeated), 105 | '[' => "while (tape[ptr]) {".repeat(repeated), 106 | ']' => "}\n".repeat(repeated), 107 | '.' => "printf(\"%c\",(char)(tape[ptr]%256));".repeat(repeated), 108 | ',' => "scanf(\"%c\", (char*)&tape[ptr]);".repeat(repeated), 109 | _ => String::new(), 110 | }; 111 | result += &(line + "\n"); 112 | repeated = 1; 113 | last = ch; 114 | } 115 | } 116 | 117 | result + &Self::postlude() 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/parser.lalrpop: -------------------------------------------------------------------------------- 1 | use crate::compile::*; 2 | 3 | grammar; 4 | 5 | 6 | extern { 7 | type Location = usize; 8 | type Error = String; 9 | } 10 | 11 | 12 | pub Str: String = => String::from(&s[1..s.len()-1]).replace("\\n","\n").replace("\\r","\r").replace("\\t","\t").replace("\\0","\0").replace("\\\"", "\""); 13 | pub Char: char = => String::from(&s[1..s.len()-1]).replace("\\n","\n").replace("\\r","\r").replace("\\t","\t").replace("\\0","\0").replace("\\\"", "\"").chars().next().unwrap(); 14 | 15 | pub Num: Literal = { 16 | r"([0-9]+([.][0-9]*)?|[.][0-9]+)" => match <>.parse::() { 17 | Ok(val) => Literal::byte_int(val), 18 | Err(_) => Literal::unsigned_short(<>.parse::().unwrap()), 19 | }, 20 | } 21 | 22 | pub Flag: Flag = { 23 | "#" "[" "]" => <> 24 | } 25 | 26 | pub FlagName: Flag = { 27 | "enable" "(" "brainfuck" ")" => Flag::EnableBrainFuck, 28 | "enable" "(" "size_warn" ")" => Flag::EnableSizeWarn 29 | } 30 | 31 | pub Value: Eval = { 32 | "(" ")" => <>, 33 | "*" => Eval::Deref(Deref::new(name)), 34 | => Eval::Load(Load::new(name)), 35 | "&" => Eval::Refer(Refer::new(Eval::Load(Load::new(name)))), 36 | > => Eval::Call(Call::new(name, args)), 37 | => Eval::Literal(<>) 38 | } 39 | 40 | pub Expr: Expr = { 41 | => <>, 42 | => <>, 43 | ";" => <>, 44 | ";" => Expr::Eval(<>), 45 | "return" ";" => Expr::Return(Return::new(ret)) 46 | } 47 | 48 | pub Assign: Expr = { 49 | "=" => Expr::Assign(Assign::new(lhs, rhs)), 50 | "def" "=" => Expr::Define(Define::new(lhs, rhs)), 51 | } 52 | 53 | pub Program: Program = { 54 | => Program::new(flags, fndefs) 55 | } 56 | 57 | pub Body: Vec = Expr* => <>; 58 | 59 | pub Ident: String = { 60 | r"[a-zA-Z_][a-zA-Z0-9_]*" => <>.to_string() 61 | } 62 | 63 | pub WhileLoop: Expr = { 64 | "while" "{" "}" => Expr::While(While::new(Eval::Load(Load::new(condition)), body)) 65 | } 66 | 67 | pub IfStatement: Expr = { 68 | "if" "{" "}" "}")?> => { 69 | match else_clause { 70 | Some(clause) => { 71 | Expr::If(If::new(condition, then_body, clause)) 72 | } 73 | None => { 74 | Expr::If(If::new(condition, then_body, vec![])) 75 | } 76 | } 77 | } 78 | } 79 | 80 | FunctionDef: UserFn = "fn" > "{" "}" => UserFn::new(name, args, body); 81 | 82 | 83 | Literal: Literal = { 84 | => Literal::string(<>), 85 | => Literal::character(<>), 86 | => <> 87 | } 88 | 89 | Infix: (First, Operator, Second) = { 90 | => { 91 | (<>) 92 | } 93 | } 94 | 95 | List: Vec = { 96 | )*> => { 97 | match end { 98 | None => list.iter().map(|(v, s)| v.clone()).collect(), 99 | Some(val) => { 100 | let mut list: Vec<_> = list.iter().map(|(v, s)| v.clone()).collect(); 101 | list.push(val); 102 | list 103 | } 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /examples/reverse_polish_notation.fr: -------------------------------------------------------------------------------- 1 | fn start() { 2 | def expr = "1 1 + 2 * 3 + 2 *\0"; 3 | def result = eval(&expr); 4 | 5 | print("Ans = "); 6 | iprint(result); 7 | println(""); 8 | } 9 | 10 | // Computes value of given RPN-expression. 11 | // 12 | // @sig: fn(string) -> int 13 | fn eval(expr) { 14 | def stack = stack_new(32); 15 | def parsing = *expr; 16 | 17 | while parsing { 18 | def ch = *expr; 19 | 20 | if is_whitespace(ch) { 21 | eval_process_whitespace(&expr); 22 | } else { 23 | if is_digit(ch) { 24 | eval_process_number(stack, &expr); 25 | } else { 26 | if is_operator(ch) { 27 | eval_process_operator(stack, &expr); 28 | } else { 29 | def msg = "expression contains an unknown character\0"; 30 | panic(&msg); 31 | } 32 | } 33 | } 34 | 35 | parsing = *expr; 36 | } 37 | 38 | if neq(stack_len(stack), 1) { 39 | def msg = "expression is unbalanced\0"; 40 | panic(&msg); 41 | } 42 | 43 | def result = stack_pop(stack); 44 | 45 | stack_free(stack); 46 | 47 | return result; 48 | } 49 | 50 | // @vis: private 51 | // @sig: fn(&&char) 52 | fn eval_process_whitespace(expr) { 53 | // We're just skipping all the whitespaces 54 | *expr = add(*expr, 1); 55 | } 56 | 57 | // @vis: private 58 | // @sig: fn(&stack, &&char) 59 | fn eval_process_number(stack, expr) { 60 | def number = alloc(8); 61 | def number_ptr = number; 62 | 63 | def parsing = 1; 64 | 65 | while parsing { 66 | *number_ptr = **expr; 67 | number_ptr = add(number_ptr, 1); 68 | 69 | *expr = add(*expr, 1); 70 | 71 | parsing = is_digit(**expr); 72 | } 73 | 74 | *number_ptr = 0; 75 | 76 | stack_push(stack, atoi(number)); 77 | free(&number, 8); 78 | } 79 | 80 | // @vis: private 81 | // @sig: fn(&stack, &&char) 82 | fn eval_process_operator(stack, expr) { 83 | def b = stack_pop(stack); 84 | def a = stack_pop(stack); 85 | def c = 0; 86 | 87 | def op = **expr; 88 | 89 | if eq(op, "+") { 90 | c = add(a, b); 91 | } 92 | 93 | if eq(op, "-") { 94 | c = sub(a, b); 95 | } 96 | 97 | if eq(op, "*") { 98 | c = mul(a, b); 99 | } 100 | 101 | if eq(op, "/") { 102 | c = div(a, b); 103 | } 104 | 105 | if eq(op, "%") { 106 | c = mod(a, b); 107 | } 108 | 109 | stack_push(stack, c); 110 | 111 | *expr = add(*expr, 1); 112 | } 113 | 114 | // Returns whether given character is a whitespace. 115 | // 116 | // @sig: fn(char) -> bool 117 | fn is_whitespace(ch) { 118 | def whitespaces = " \t\0"; 119 | 120 | return strcnt(&whitespaces, ch); 121 | } 122 | 123 | // Returns whether given character is a digit. 124 | // 125 | // @sig: fn(char) -> bool 126 | fn is_digit(ch) { 127 | def digits = "0123456789\0"; 128 | 129 | return strcnt(&digits, ch); 130 | } 131 | 132 | // Returns whether given character is an operator. 133 | // 134 | // @sig: fn(char) -> bool 135 | fn is_operator(ch) { 136 | def operators = "+-*/%\0"; 137 | 138 | return strcnt(&operators, ch); 139 | } 140 | 141 | // ------------------------ // 142 | // Stack-oriented functions // 143 | 144 | // Creates a new, empty stack and returns a pointer to it. 145 | // 146 | // Since Free as-is doesn't support data types, we're modelling stack as a 147 | // pointer into memory that we manually process as such: 148 | // 149 | // [ stack ] [ stack + 1 ] [ stack + 2 ] [ ... ] 150 | // | | | ---- from this point on we've got actual 151 | // | | stack's items 152 | // | | 153 | // | ^ this contains stack's maximum size 154 | // | 155 | // ^ this contains stack's length 156 | // 157 | // More or less, it's: 158 | // 159 | // ``` 160 | // struct stack { 161 | // len: int, 162 | // size: int, 163 | // items: [int; int], 164 | // } 165 | // ``` 166 | // 167 | // @sig: fn(int) -> &stack 168 | fn stack_new(size) { 169 | // We're adding two bytes to the stack's size, because we're going to utilize 170 | // stack's first and second byte to store its length and size 171 | def stack = alloc(add(size, 2)); 172 | 173 | *stack_len_ptr(stack) = 0; 174 | *stack_size_ptr(stack) = size; 175 | 176 | return stack; 177 | } 178 | 179 | // Releases memory associated with given stack. 180 | // 181 | // @sig: fn(&stack) 182 | fn stack_free(stack) { 183 | free(stack, add(stack_len(stack), 2)); 184 | } 185 | 186 | // Pushes given value onto the stack. 187 | // 188 | // @sig: fn(&stack, int) 189 | fn stack_push(stack, value) { 190 | if stack_is_full(stack) { 191 | def msg = "stack overflowed\0"; 192 | panic(&msg); 193 | } 194 | 195 | def len_ptr = stack_len_ptr(stack); 196 | def size_ptr = stack_size_ptr(stack); 197 | def value_ptr = stack_value_ptr(stack, *len_ptr); 198 | 199 | // Store value inside the stack 200 | *value_ptr = value; 201 | 202 | // Increase stack's size 203 | *len_ptr = add(*len_ptr, 1); 204 | } 205 | 206 | // Pops value from the top of given stack. 207 | // 208 | // @sig: fn(&stack) -> int 209 | fn stack_pop(stack) { 210 | if stack_is_empty(stack) { 211 | def msg = "stack underflowed\0"; 212 | panic(&msg); 213 | } 214 | 215 | def len_ptr = stack_len_ptr(stack); 216 | 217 | // Decrease stack's size 218 | *len_ptr = sub(*len_ptr, 1); 219 | 220 | // Load value from the stack 221 | def value_ptr = stack_value_ptr(stack, *len_ptr); 222 | 223 | return *value_ptr; 224 | } 225 | 226 | // Returns number of elements inside given stack right now. 227 | // 228 | // @sig: fn(&stack) -> int 229 | fn stack_len(stack) { 230 | def len_ptr = stack_len_ptr(stack); 231 | 232 | return *len_ptr; 233 | } 234 | 235 | // Returns whether given stack is empty. 236 | // 237 | // @sig: fn(&stack) -> bool 238 | fn stack_is_empty(stack) { 239 | def len_ptr = stack_len_ptr(stack); 240 | 241 | return eq(*len_ptr, 0); 242 | } 243 | 244 | // Returns whether given stack is full. 245 | // 246 | // @sig: fn(&stack) -> bool 247 | fn stack_is_full(stack) { 248 | def len_ptr = stack_len_ptr(stack); 249 | def size_ptr = stack_size_ptr(stack); 250 | 251 | return gte(*len_ptr, *size_ptr); 252 | } 253 | 254 | // Returns a pointer to an integer indicating the total number of elements 255 | // inside given stack right now. 256 | // 257 | // @vis: private 258 | // @sig: fn(&stack) -> int 259 | fn stack_len_ptr(stack) { 260 | return add(stack, 0); 261 | } 262 | 263 | // Returns a pointer to an integer indicating the total number of elements 264 | // given stack can contain. 265 | // 266 | // @vis: private 267 | // @sig: fn(&stack) -> int 268 | fn stack_size_ptr(stack) { 269 | return add(stack, 1); 270 | } 271 | 272 | // Returns a pointer to given stack's value. 273 | // 274 | // @vis: private 275 | // @sig: fn(&stack) -> &T 276 | fn stack_value_ptr(stack, idx) { 277 | return add(stack, add(idx, 2)); 278 | } 279 | 280 | // ------------------------- // 281 | // String-oriented functions // 282 | 283 | // Returns number of characters inside given null-terminated string. 284 | // 285 | // @sig: fn(&char) -> int 286 | fn strlen(str) { 287 | def len = 0; 288 | def running = *str; 289 | 290 | while running { 291 | len = add(len, 1); 292 | str = add(str, 1); 293 | running = *str; 294 | } 295 | 296 | return len; 297 | } 298 | 299 | // Reverses given null-terminated string in-place. 300 | // 301 | // @sig: fn(&char) 302 | fn strrev(str) { 303 | def len = strlen(str); 304 | def running = gte(len, 2); 305 | 306 | if running { 307 | def len_half = div(len, 2); 308 | 309 | def idx_a = 0; 310 | def idx_b = sub(len, 1); 311 | 312 | while running { 313 | pswap( 314 | add(str, idx_a), 315 | add(str, idx_b), 316 | ); 317 | 318 | idx_a = add(idx_a, 1); 319 | idx_b = sub(idx_b, 1); 320 | 321 | running = neq(idx_a, len_half); 322 | } 323 | } 324 | } 325 | 326 | // Returns whether given null-terminated string contains given character. 327 | // Similar to `strpos(str, ch) > 0`, but simpler. 328 | // 329 | // @sig: fn(&char, char) -> bool 330 | fn strcnt(str, ch) { 331 | def result = 0; 332 | def running = *str; 333 | 334 | while running { 335 | if eq(*str, ch) { 336 | result = 1; 337 | running = 0; 338 | } else { 339 | str = add(str, 1); 340 | running = *str; 341 | } 342 | } 343 | 344 | return result; 345 | } 346 | 347 | // Converts given integer into a null-terminated string. 348 | // 349 | // Given buffer must be large enough to contain the entire number and the null 350 | // terminator. 351 | // 352 | // @sig: fn(int, &char) 353 | fn itoa(num, buf) { 354 | def ptr = buf; 355 | def len = 0; 356 | 357 | while num { 358 | *ptr = add(48, mod(num, 10)); 359 | num = div(num, 10); 360 | 361 | ptr = add(ptr, 1); 362 | len = add(len, 1); 363 | } 364 | 365 | if eq(len, 0) { 366 | *ptr = 48; 367 | ptr = add(ptr, 1); 368 | } 369 | 370 | *ptr = 0; 371 | 372 | strrev(buf); 373 | } 374 | 375 | // Converts given null-terminated string into an integer. 376 | // 377 | // If given string contains non-digit characters, the behavior is undefined. 378 | // 379 | // @sig: fn(&char) -> int 380 | fn atoi(str) { 381 | def res = 0; 382 | def running = *str; 383 | 384 | while running { 385 | res = mul(res, 10); 386 | res = add(res, sub(*str, 48)); 387 | 388 | str = add(str, 1); 389 | running = *str; 390 | } 391 | 392 | return res; 393 | } 394 | 395 | // Prints a null-terminated string onto the stdout. 396 | // 397 | // @sig: fn(&char) 398 | fn sprint(str) { 399 | def running = *str; 400 | 401 | while running { 402 | print(*str); 403 | str = add(str, 1); 404 | running = *str; 405 | } 406 | } 407 | 408 | // -------------------------- // 409 | // Pointer-oriented functions // 410 | 411 | // Swaps values under given pointers. 412 | // 413 | // @sig: fn(&T, &T) 414 | fn pswap(a, b) { 415 | def tmp = *a; 416 | *a = *b; 417 | *b = tmp; 418 | } 419 | 420 | // Frees memory under given pointer. 421 | // 422 | // @sig: fn(&T, int) 423 | fn free(ptr, size) { 424 | while size { 425 | size = sub(size, 1); 426 | free_byte(add(ptr, size)); 427 | } 428 | } 429 | 430 | // -------------------------- // 431 | // Integer-oriented functions // 432 | 433 | // Prints an integer onto the stdout. 434 | // 435 | // @sig: fn(int) 436 | fn iprint(num) { 437 | def buf = alloc(16); 438 | itoa(num, buf); 439 | sprint(buf); 440 | } 441 | 442 | // Multiplies both numbers and returns the result. 443 | // 444 | // @sig: fn(int, int) -> int 445 | fn mul(a, b) { 446 | def res = 0; 447 | 448 | while b { 449 | res = add(res, a); 450 | b = sub(b, 1); 451 | } 452 | 453 | return res; 454 | } 455 | 456 | // Divides both numbers and returns the result. 457 | // 458 | // It utilizes repeated subtraction and thus is totally inefficient for bigger 459 | // numbers. 460 | // 461 | // @sig: fn(int, int) -> int 462 | fn div(a, b) { 463 | def res = 0; 464 | def running = 1; 465 | 466 | while running { 467 | if gte(a, b) { 468 | a = sub(a, b); 469 | res = add(res, 1); 470 | } else { 471 | running = 0; 472 | } 473 | } 474 | 475 | return res; 476 | } 477 | 478 | // Returns modulo of both numbers. 479 | // 480 | // It utilizes repeated subtraction and thus is totally inefficient for bigger 481 | // numbers. 482 | // 483 | // @sig: fn(int, int) -> int 484 | fn mod(a, b) { 485 | def res = 0; 486 | 487 | while a { 488 | if gte(a, b) { 489 | a = sub(a, b); 490 | } else { 491 | res = a; 492 | a = 0; 493 | } 494 | } 495 | 496 | return res; 497 | } 498 | 499 | // Returns whether both numbers are equal. 500 | // 501 | // @sig: fn(int, int) -> bool 502 | fn eq(a, b) { 503 | if sub(a, b) { 504 | return 0; 505 | } else { 506 | return 1; 507 | } 508 | } 509 | 510 | // Returns whether both numbers are different. 511 | // 512 | // @sig: fn(int, int) -> bool 513 | fn neq(a, b) { 514 | return sub(1, eq(a, b)); 515 | } 516 | 517 | // Returns whether first number is greater than or equal to the second one. 518 | // 519 | // Since Free doesn't provide any built-ins that would allow us to compare 520 | // numbers, what we're doing here is basically decreasing numbers by one until 521 | // either one (or both) eventually are zeroed-out. 522 | // 523 | // @sig: fn(int, int) -> bool 524 | fn gte(a, b) { 525 | def res = 0; 526 | def running = 1; 527 | 528 | while running { 529 | if a { } else { 530 | running = 0; 531 | res = 0; 532 | } 533 | 534 | if b { } else { 535 | running = 0; 536 | res = 1; 537 | } 538 | 539 | a = sub(a, 1); 540 | b = sub(b, 1); 541 | } 542 | 543 | return res; 544 | } 545 | 546 | // --------------- // 547 | // Other functions // 548 | 549 | // @sig: fn(&char) 550 | fn panic(msg) { 551 | print("panic: "); 552 | sprint(msg); 553 | println(""); 554 | 555 | def true = 1; 556 | 557 | while true { 558 | // 559 | } 560 | } 561 | -------------------------------------------------------------------------------- /src/ir.rs: -------------------------------------------------------------------------------- 1 | use crate::{Error, Eval, Literal, Lower, Program}; 2 | use core::fmt; 3 | use std::sync::Mutex; 4 | 5 | lazy_static! { 6 | static ref COMPILED: Mutex = Mutex::new(String::new()); 7 | static ref CONTROL_REGISTERS: Mutex> = Mutex::new(Vec::new()); 8 | 9 | /// This variable is responsible for keeping track of each statically allocated variable 10 | /// For example, if a variable `test` is allocated statically with size 4, the STACK_PTR 11 | /// will be allocated by 4, and the next variable will be allocated at the STACK_PTR 12 | pub static ref STACK_PTR: Mutex = Mutex::new(0); 13 | pub static ref RETURN: Value = Value::new(1).unwrap(); 14 | pub static ref TEMP0: Value = Value::new(1).unwrap(); 15 | pub static ref TEMP1: Value = Value::new(1).unwrap(); 16 | pub static ref TEMP2: Value = Value::new(1).unwrap(); 17 | pub static ref TEMP3: Value = Value::new(1).unwrap(); 18 | pub static ref TEMP4: Value = Value::new(1).unwrap(); 19 | pub static ref TEMP5: Value = Value::new(1).unwrap(); 20 | pub static ref TEMP6: Value = Value::new(1).unwrap(); 21 | 22 | pub static ref STACK_SIZE: Mutex = Mutex::new(16384); 23 | pub static ref HEAP_SIZE: Mutex = Mutex::new(8192); 24 | } 25 | 26 | pub fn increment_stack(allocation_size: u32) -> Result<(), Error> { 27 | let mut stack_ptr = STACK_PTR.lock().unwrap(); 28 | *stack_ptr += allocation_size; 29 | if *stack_ptr > *STACK_SIZE.lock().unwrap() { 30 | Err(Error::StackOverflow) 31 | } else { 32 | Ok(()) 33 | } 34 | } 35 | 36 | pub fn set_stack(stack_size: u32) -> Result<(), Error> { 37 | let mut stack_ptr = STACK_PTR.lock().unwrap(); 38 | *stack_ptr = stack_size; 39 | if *stack_ptr > *STACK_SIZE.lock().unwrap() { 40 | Err(Error::StackOverflow) 41 | } else { 42 | Ok(()) 43 | } 44 | } 45 | 46 | pub fn add_to_compiled(s: impl ToString) { 47 | let mut c = COMPILED.lock().unwrap(); 48 | (*c) += &s.to_string(); 49 | } 50 | 51 | #[allow(unused_must_use)] 52 | pub fn init() { 53 | let f = |_: &Value| {}; 54 | f(&RETURN); 55 | f(&TEMP0); 56 | f(&TEMP1); 57 | f(&TEMP2); 58 | f(&TEMP3); 59 | f(&TEMP4); 60 | f(&TEMP5); 61 | f(&TEMP6); 62 | 63 | add_to_compiled(format!( 64 | "STARTING STACK PTR IS {} ", 65 | *STACK_PTR.lock().unwrap() 66 | )); 67 | } 68 | 69 | pub fn compile() -> String { 70 | RETURN.free(); 71 | TEMP0.free(); 72 | TEMP1.free(); 73 | TEMP2.free(); 74 | TEMP3.free(); 75 | TEMP4.free(); 76 | TEMP5.free(); 77 | TEMP6.free(); 78 | 79 | add_to_compiled(format!( 80 | "FINAL STACK PTR IS {} ", 81 | *STACK_PTR.lock().unwrap() 82 | )); 83 | 84 | COMPILED.lock().unwrap().clone() 85 | } 86 | 87 | pub struct Control; 88 | impl Control { 89 | pub fn if_begin(var: Value) -> Result<(), Error> { 90 | add_to_compiled("\nIF BEGIN\n"); 91 | TEMP5.assign(var)?; 92 | TEMP6.assign(Eval::Literal(Literal::byte_int(1)).lower()?)?; 93 | 94 | // CONTROL_REGISTERS.lock().unwrap().push(var); 95 | 96 | Self::while_begin(*TEMP5); 97 | add_to_compiled("\nTHEN CODE BEGIN\n"); 98 | Ok(()) 99 | } 100 | 101 | pub fn else_begin() -> Result<(), Error> { 102 | add_to_compiled("\nTHEN CODE END\n"); 103 | TEMP5.zero(); 104 | TEMP6.zero(); 105 | Self::while_end(); 106 | Self::while_begin(*TEMP6); 107 | add_to_compiled("\nELSE CODE BEGIN\n"); 108 | // TEMP0.zero(); 109 | // let var = CONTROL_REGISTERS.lock().unwrap().pop().unwrap(); 110 | // TEMP1.assign(var)?; 111 | // var.zero(); 112 | // add_to_compiled(var.to() + "]" + &var.from()); 113 | // var.assign(*TEMP1)?; 114 | // TEMP1.zero(); 115 | Ok(()) 116 | } 117 | 118 | pub fn if_end() -> Result<(), Error> { 119 | add_to_compiled("\nELSE CODE END\n"); 120 | TEMP6.zero(); 121 | Self::while_end(); 122 | // TEMP0.zero(); 123 | // let var = CONTROL_REGISTERS.lock().unwrap().pop().unwrap(); 124 | // TEMP1.assign(var)?; 125 | // var.zero(); 126 | // add_to_compiled(var.to() + "]" + &var.from()); 127 | // var.assign(*TEMP1)?; 128 | // TEMP1.zero(); 129 | add_to_compiled("\nIF END\n"); 130 | Ok(()) 131 | } 132 | 133 | pub fn while_begin(var: Value) { 134 | add_to_compiled("\nWHILE BEGIN\n"); 135 | // TEMP0.zero(); 136 | CONTROL_REGISTERS.lock().unwrap().push(var); 137 | add_to_compiled(var.to() + "[" + &var.from()); 138 | add_to_compiled("\nCODE BEGIN\n"); 139 | } 140 | 141 | pub fn while_end() { 142 | add_to_compiled("\nCODE END\n"); 143 | let var = CONTROL_REGISTERS.lock().unwrap().pop().unwrap(); 144 | add_to_compiled(var.to() + "]" + &var.from()); 145 | add_to_compiled("\nWHILE END\n"); 146 | } 147 | } 148 | 149 | pub struct Stdin; 150 | impl Stdin { 151 | pub fn getch(var: Value) { 152 | let mut result = String::new(); 153 | 154 | result += &var.to(); 155 | result += ","; 156 | result += &var.from(); 157 | 158 | add_to_compiled("\nPRINT CELL\n"); 159 | add_to_compiled(&result); 160 | add_to_compiled("\nDONE\n"); 161 | } 162 | } 163 | 164 | pub struct Stdout; 165 | impl Stdout { 166 | /// This prints a Value according to its size. 167 | /// Basically, this prints each cell that the Value owns. 168 | pub fn print(var: Value) { 169 | let mut result = String::new(); 170 | 171 | result += &var.to(); 172 | for _ in 0..var.size() { 173 | result += ".>"; 174 | } 175 | for _ in 0..var.size() { 176 | result += "<"; 177 | } 178 | result += &var.from(); 179 | 180 | add_to_compiled("\nPRINT CELL\n"); 181 | add_to_compiled(&result); 182 | add_to_compiled("\nDONE\n"); 183 | } 184 | 185 | 186 | /// This prints a pointer to a value like a CString 187 | /// This requires brainfuck compatibility mode to be disabled. 188 | pub fn print_cstr(var: Value) -> Result<(), Error> { 189 | let mut result = String::new(); 190 | 191 | // Dereference value and print string 192 | result += &var.to(); 193 | result += "*[.>]&"; 194 | // Refer back to home 195 | result += &var.from(); 196 | 197 | add_to_compiled("\nPRINT CELL\n"); 198 | add_to_compiled(&result); 199 | add_to_compiled("\nDONE\n"); 200 | 201 | if Program::brainfuck_enabled() { 202 | Err(Error::CannotUsePointersInBrainFuckMode) 203 | } else { 204 | Ok(()) 205 | } 206 | } 207 | } 208 | 209 | /// This object represents a value stored on the tape. 210 | /// Objects can be stored indirectly or directly. 211 | /// In brainfuck compatibility mode, dereferencing 212 | /// or referencing an object is illegal. 213 | #[derive(Copy, Clone, PartialEq, Eq, PartialOrd)] 214 | pub struct Value { 215 | pub offset: u32, 216 | pub reference_depth: u32, 217 | pub number_cells: u32, 218 | } 219 | 220 | /// This is for debugging. 221 | impl fmt::Debug for Value { 222 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 223 | write!( 224 | f, 225 | "(to `{}` | from `{}` | {} wide)", 226 | self.to(), 227 | self.from(), 228 | self.size() 229 | ) 230 | } 231 | } 232 | 233 | impl Value { 234 | pub fn new(size: u32) -> Result { 235 | let result = Self { 236 | offset: *STACK_PTR.lock().unwrap(), 237 | reference_depth: 0, 238 | number_cells: size, 239 | }; 240 | 241 | result.zero(); 242 | 243 | increment_stack(size)?; 244 | Ok(result) 245 | } 246 | 247 | pub fn alloc(size: u32) -> Result { 248 | let mut result = Self::new(1)?; 249 | result.number_cells = size; 250 | 251 | add_to_compiled(format!("\nALLOCATING {} CELLS\n", size)); 252 | add_to_compiled(result.to()); 253 | add_to_compiled("+".repeat(size as usize)); 254 | add_to_compiled("?*"); 255 | add_to_compiled("+>".repeat(size as usize)); 256 | add_to_compiled("&"); 257 | add_to_compiled(result.from()); 258 | add_to_compiled("\nDONE\n"); 259 | 260 | if Program::brainfuck_enabled() { 261 | Err(Error::CannotUsePointersInBrainFuckMode) 262 | } else { 263 | Ok(result) 264 | } 265 | } 266 | 267 | pub fn variable_alloc(size: Self) -> Result { 268 | let result = Self::new(1)?; 269 | 270 | result.assign(size)?; 271 | 272 | add_to_compiled("\nALLOCATING CELLS\n"); 273 | add_to_compiled(&result.to()); 274 | add_to_compiled("?"); 275 | add_to_compiled(&result.from()); 276 | add_to_compiled("\nDONE\n"); 277 | 278 | if Program::brainfuck_enabled() { 279 | Err(Error::CannotUsePointersInBrainFuckMode) 280 | } else { 281 | Ok(result) 282 | } 283 | } 284 | 285 | pub fn copy(&self) -> Result { 286 | let val = Self::new(self.number_cells)?; 287 | val.assign(*self)?; 288 | Ok(val) 289 | } 290 | 291 | pub fn is_ref(&self) -> bool { 292 | self.reference_depth > 0 293 | } 294 | 295 | pub fn zero(&self) { 296 | add_to_compiled(self.to()); 297 | for _ in 0..self.number_cells { 298 | add_to_compiled("[-]>"); 299 | } 300 | for _ in 0..self.number_cells { 301 | add_to_compiled("<"); 302 | } 303 | add_to_compiled(self.from()); 304 | } 305 | 306 | pub fn free(&self) { 307 | add_to_compiled(&format!( 308 | "\nFREEING CELLS {}~{}\n", 309 | self.offset, 310 | self.offset + self.size() 311 | )); 312 | add_to_compiled(&self.to()); 313 | 314 | for _ in 0..self.size() { 315 | add_to_compiled("[-]>"); 316 | } 317 | 318 | for _ in 0..self.size() { 319 | add_to_compiled("<"); 320 | } 321 | 322 | add_to_compiled(self.from()); 323 | 324 | add_to_compiled("\nDONE\n"); 325 | } 326 | 327 | pub fn set(&self, val: impl Into) { 328 | add_to_compiled(&self.to()); 329 | add_to_compiled("[-]"); 330 | add_to_compiled("+".repeat(val.into())); 331 | add_to_compiled(self.from()); 332 | } 333 | 334 | pub fn assign(&self, val: Self) -> Result<(), Error> { 335 | if val == *self { 336 | return Ok(()); 337 | } 338 | 339 | if Program::brainfuck_enabled() && val.size() > self.size() { 340 | return Err(Error::CannotAssignLargerValueToSmallerValueInBrainFuckMode); 341 | } else if Program::size_warn_enabled() && val.size() > self.size() { 342 | eprintln!("Warning: assigning larger value to smaller value"); 343 | } 344 | 345 | TEMP0.zero(); 346 | 347 | for cell in 0..val.size() { 348 | let cell_to = val.to() + &">".repeat(cell as usize); 349 | let cell_from = "<".repeat(cell as usize) + &val.from(); 350 | let this_to = self.to() + &">".repeat(cell as usize); 351 | let this_from = "<".repeat(cell as usize) + &self.from(); 352 | add_to_compiled(this_to.clone() + "[-]" + &this_from); 353 | add_to_compiled( 354 | cell_to.clone() 355 | + "[" 356 | + &cell_from 357 | + &this_to 358 | + "+" 359 | + &this_from 360 | + &TEMP0.to() 361 | + "+" 362 | + &TEMP0.from() 363 | + &cell_to 364 | + "-" 365 | + &cell_from 366 | + &cell_to 367 | + "]" 368 | + &cell_from, 369 | ); 370 | add_to_compiled( 371 | TEMP0.to() 372 | + "[" 373 | + &TEMP0.from() 374 | + &cell_to 375 | + "+" 376 | + &cell_from 377 | + &TEMP0.to() 378 | + "-" 379 | + &TEMP0.from() 380 | + &TEMP0.to() 381 | + "]" 382 | + &TEMP0.from(), 383 | ); 384 | } 385 | Ok(()) 386 | } 387 | 388 | pub fn plus_eq(&self, val: Self) { 389 | TEMP0.zero(); 390 | 391 | for cell in 0..val.size() { 392 | let cell_to = val.to() + &">".repeat(cell as usize); 393 | let cell_from = "<".repeat(cell as usize) + &val.from(); 394 | let this_to = self.to() + &">".repeat(cell as usize); 395 | let this_from = "<".repeat(cell as usize) + &self.from(); 396 | add_to_compiled( 397 | cell_to.clone() 398 | + "[" 399 | + &cell_from 400 | + &this_to 401 | + "+" 402 | + &this_from 403 | + &TEMP0.to() 404 | + "+" 405 | + &TEMP0.from() 406 | + &cell_to 407 | + "-" 408 | + &cell_from 409 | + &cell_to 410 | + "]" 411 | + &cell_from, 412 | ); 413 | add_to_compiled( 414 | TEMP0.to() 415 | + "[" 416 | + &TEMP0.from() 417 | + &cell_to 418 | + "+" 419 | + &cell_from 420 | + &TEMP0.to() 421 | + "-" 422 | + &TEMP0.from() 423 | + &TEMP0.to() 424 | + "]" 425 | + &TEMP0.from(), 426 | ); 427 | } 428 | } 429 | 430 | pub fn minus_eq(&self, val: Self) { 431 | TEMP0.zero(); 432 | 433 | for cell in 0..val.size() { 434 | let cell_to = val.to() + &">".repeat(cell as usize); 435 | let cell_from = "<".repeat(cell as usize) + &val.from(); 436 | let this_to = self.to() + &">".repeat(cell as usize); 437 | let this_from = "<".repeat(cell as usize) + &self.from(); 438 | add_to_compiled( 439 | cell_to.clone() 440 | + "[" 441 | + &cell_from 442 | + &this_to 443 | + "-" 444 | + &this_from 445 | + &TEMP0.to() 446 | + "+" 447 | + &TEMP0.from() 448 | + &cell_to 449 | + "-" 450 | + &cell_from 451 | + &cell_to 452 | + "]" 453 | + &cell_from, 454 | ); 455 | add_to_compiled( 456 | TEMP0.to() 457 | + "[" 458 | + &TEMP0.from() 459 | + &cell_to 460 | + "+" 461 | + &cell_from 462 | + &TEMP0.to() 463 | + "-" 464 | + &TEMP0.from() 465 | + &TEMP0.to() 466 | + "]" 467 | + &TEMP0.from(), 468 | ); 469 | } 470 | } 471 | 472 | pub fn byte_int(value: u8) -> Result { 473 | let result = Self::new(1)?; 474 | add_to_compiled(result.to()); 475 | add_to_compiled("+".repeat(value as usize)); 476 | add_to_compiled(result.from()); 477 | Ok(result) 478 | } 479 | 480 | pub fn unsigned_short(value: u16) -> Result { 481 | let result = Self::new(1)?; 482 | add_to_compiled(result.to()); 483 | add_to_compiled("+".repeat(value as usize)); 484 | add_to_compiled(result.from()); 485 | 486 | if Program::brainfuck_enabled() { 487 | Err(Error::CannotUseUnsignedShortsInBrainFuckMode) 488 | } else { 489 | Ok(result) 490 | } 491 | } 492 | 493 | pub fn character(value: char) -> Result { 494 | let result = Self::new(1)?; 495 | add_to_compiled(result.to()); 496 | add_to_compiled("+".repeat(value as usize)); 497 | add_to_compiled(result.from()); 498 | Ok(result) 499 | } 500 | 501 | pub fn string(value: impl ToString) -> Result { 502 | let result = Self::new((value.to_string().len() + 1) as u32)?; 503 | 504 | add_to_compiled(result.to()); 505 | for ch in value.to_string().chars() { 506 | add_to_compiled("+".repeat(ch as usize) + ">"); 507 | } 508 | 509 | for _ in value.to_string().chars() { 510 | add_to_compiled("<"); 511 | } 512 | add_to_compiled(result.from()); 513 | 514 | Ok(result) 515 | } 516 | 517 | pub fn to(&self) -> String { 518 | ">".repeat(self.offset as usize).to_string() + &"*".repeat(self.reference_depth as usize) 519 | } 520 | 521 | pub fn from(&self) -> String { 522 | "&".repeat(self.reference_depth as usize) + &"<".repeat(self.offset as usize).to_string() 523 | } 524 | 525 | pub fn size(&self) -> u32 { 526 | self.number_cells 527 | } 528 | 529 | pub fn deref(&self) -> Result { 530 | let mut result = Self::new(1)?; 531 | 532 | result.reference_depth = self.reference_depth + 1; 533 | result.offset = self.offset; 534 | 535 | if Program::brainfuck_enabled() { 536 | Err(Error::CannotUsePointersInBrainFuckMode) 537 | } else { 538 | Ok(result) 539 | } 540 | } 541 | 542 | pub fn refer(&self) -> Result { 543 | let result = Self::new(1)?; 544 | 545 | add_to_compiled(result.to()); 546 | add_to_compiled("+".repeat(self.offset as usize)); 547 | add_to_compiled(result.from()); 548 | 549 | if Program::brainfuck_enabled() { 550 | Err(Error::CannotUsePointersInBrainFuckMode) 551 | } else { 552 | Ok(result) 553 | } 554 | } 555 | } 556 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # free 2 | 3 | A terrible programming language that targets an even worse programming language. 4 | 5 | I decided to name this programming language free because there are absolutely no memory constraints whatsoever. It's basically impossible for a free program to segfault or anything like that: you can arbitrarily assign to any memory location. This isn't by design. It's just a byproduct of the target programming language: [SMPL](https://github.com/adam-mcdaniel/smpl). 6 | 7 | # SMPL 8 | 9 | [SMPL](https://github.com/adam-mcdaniel/smpl), pronounced "simple", is a programming language almost identical to brainfuck. It's ridiculously easy to implement; SMPL is just a superset of brainfuck with 3 additional operators. These operators, `&`, `*`, and `?`, give SMPL the ability to manage memory dynamically rather than statically, which is impossible in brainfuck. Consider the following problem. 10 | 11 | In brainfuck, it's incredibly easy to represent an array of numbers. They can just be stored on the tape like so. 12 | ```c 13 | char array[6] 14 | | 15 | v 16 | [0, 0, 0, 1, 2, 3, 4, 5, 0, 0, 0, ...] 17 | ``` 18 | 19 | But what happens when the array needs to grow? Or if you want to represent a pointer to an element of the array? 20 | 21 | There is no real elegant solution to representing pointers in brainfuck. 22 | 23 | However, enabled by the powers of SMPL we can represent pointer operations rather elegantly. 24 | 25 | | SMPL Operator | Description | 26 | |---------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------| 27 | | > | Move the pointer to the right | 28 | | < | Move the pointer to the left | 29 | | + | Increment the memory cell under the pointer | 30 | | - | Decrement the memory cell under the pointer | 31 | | . | Output the character stored at the cell under the pointer | 32 | | , | Input a character and store it in the cell under the pointer | 33 | | [ | Jump past the matching `]` if the cell under the pointer is zero | 34 | | ] | Jump back to the matching `[` if the cell under the pointer is not zero | 35 | | * | Set the pointer equal to the value of the current cell | 36 | | & | Set the pointer back to the value it was before the last `*` | 37 | | ? | With the value of the cell under the pointer, store the address of the first instance of that many consecutive zeros from the left of the tape at the current cell | 38 | 39 | # Compilation Process 40 | 41 | After the input code has been parsed and the AST has been generated, the compilation can begin. 42 | 43 | Honestly, I'm not sure I still understand the complete scope of how the entire compiler works. A lot of it seemingly works by magic. Nevertheless, I'll still try to give a meaningful explanation. **_Do not trust this software because I honestly have no idea why it works_**. 44 | 45 | 1. Initialize the stack and the heap 46 | - When compilation begins, the compiler sets asside memory that it **KNOWS** will be necessary. This memory consists of the _return_ register, and six other registers for math. Although it may appear that I chose six registers because most CPUs have six registers, I actually chose six because that is the minimum number of temporary cells required for any atomic math algorithm in brainfuck listed on esolangs.org. 47 | 48 | - After the stack memory is allocated, the total memory allocated for the combined stack and heap is calculated. 49 | 50 | 2. Inline function calls 51 | - Essentially, all functions' bodies are copied and pasted into where they are called. 52 | 53 | - Even though functions are inlined, they still have stackframes which allocate and deallocate function memory before and after calls. 54 | 55 | 3. Statically compute stack allocations 56 | - This is probably the **_worst_** part of the compilation process. It's **entirely** unnecessary and I probably should've just made a predetermined stack limit such as `8192` bytes or something. 57 | 58 | - When a scope is instantiated, a new scope is pushed onto the current scope hierarchy. When variables are defined in the scope, they are allocated in the environment's stackframe. When the scope ends, every variable in the scope is deallocated. Notice how I never mentioned free's stack pointer in the list of registers? That's because there **_is no stack pointer_**. All stack memory is accounted for **during compile time**. That means that for every scope, the storage capacity for the stack grows **exactly** as much as is necessary. The compiler can compute exactly how much memory will be allocated on the stack at any arbitrary point of time in the runtime. 59 | 60 | - I hate myself for implementing the stack this way. Never do this. Just implement `push` and `pop` and be done with it. **_Don't be like me, you'll regret your stupidity_**. 61 | 62 | 4. Convert atomic operations into SMPL/brainfuck 63 | - I used esolangs.org page on brainfuck algorithms for this and it was incredibly handy. Building an intermediate representation as a superset of brainfuck is actually a really great way to have a bunch of pre-built algorithms at your disposal. I found this part extremely simple and easy to implement. The hardest portion was implementing pointers **correctly**. There were very many times where my test programs would have inexplicable errors, and it was _always_ the algorithms responsible for representing pointers in SMPL. In fact, there are probably a reasonable number of memory bugs **still** in the compiler. 64 | 65 | - The best way to avoid errors is honestly just to enable brainfuck compatibility mode. Not only will this allow you to run your code with industrial strength brainfuck compilers and interpreters, it will free you from banging your head against a wall over weird memory behavior. 66 | 67 | 5. Optimize SMPL/brainfuck 68 | - Because SMPL and brainfuck are so minimal, optimizations are extremely effective. Little things like zeroing deallocated memory are _technically_ unnecessary, and can be optimized away if needed. 69 | 70 | - This is the last step in the compilation process before SMPL is optionally converted to C. 71 | 72 | After all these steps, your compiled `free` program is ready to execute. 73 | 74 | # Syntax and Flags 75 | 76 | The syntax of free is heavily inspired by rust. This is because of the objective fact that rust is the best programming language ever made. 77 | 78 | Every free program contains a `start` function (like the `main` function). 79 | 80 | ```rust 81 | fn start() { 82 | // This is a variable declaration 83 | def str = "Hello world!"; 84 | println(str); 85 | } 86 | ``` 87 | 88 | I **_REALLY_** wanted to use the `let` keyword because it's so pretty, but no variable in free is constant. I used `def` because it's less misleading, and because `var` is uglier in my opinion. 89 | 90 | free also has two different control flow structures: the while loop, and the if-else statement. 91 | 92 | The if-else statement functions the same way as an if statement in any other language. There are no else-if expressions, though. 93 | 94 | ```rust 95 | fn start() { 96 | if test() { 97 | // this code will run if test() is non-zero 98 | println("True!"); 99 | } else { 100 | // this code will run if test() is zero 101 | println("False!"); 102 | } 103 | } 104 | 105 | fn test() { 106 | return 1; 107 | } 108 | ``` 109 | 110 | While loops are much different, however. Because of the way free's memory is managed, it isn't possible to use a function call as the condition to a while loop. Variables **must** be used to store the condition of a while loop because their position in the memory tape is always constant within their scope. 111 | 112 | ```rust 113 | fn start() { 114 | def running = 1; 115 | while running { 116 | println("running!"); 117 | } 118 | } 119 | ``` 120 | 121 | For the same reason that while loops require variables for their conditions, only variables can be referenced. Any value can be dereferenced, though. 122 | 123 | ```rust 124 | fn start() { 125 | def a = 5; 126 | // print `a` as a digit by adding ascii code for 0 127 | println(add(a, 48)); 128 | inc(&a); 129 | // print `a` as a digit by adding ascii code for 0 130 | println(add(a, 48)); 131 | } 132 | 133 | // No type is needed for `ptr`. All variables are typeless because type checking is hard. 134 | fn inc(ptr) { 135 | *ptr = add(*ptr, 1); 136 | } 137 | ``` 138 | 139 | Using the `alloc` function, now we can use dynamic memory allocation! 140 | 141 | ```rust 142 | fn start() { 143 | // Allocate 16 bytes of memory and store the pointer to that block in `str` 144 | def str = alloc(16); 145 | 146 | if 1 { 147 | *str = "True!\n\0"; 148 | } else { 149 | *str = "False!\n\0"; 150 | } 151 | 152 | cprint(str); 153 | } 154 | 155 | fn cprint(str) { 156 | def counter = 0; 157 | def running = *add(str, counter); 158 | while running { 159 | print(running); 160 | counter = add(counter, 1); 161 | running = *add(str, counter); 162 | } 163 | } 164 | ``` 165 | 166 | We also need to be able to free memory that we allocate. 167 | 168 | 169 | ```rust 170 | fn start() { 171 | // Allocate 128 bytes of memory and store the pointer to that block in `str` 172 | def size = 128; 173 | def str = alloc(size); 174 | free(str, size); 175 | } 176 | 177 | // free_byte only frees a single cell, so free must be implemented manually 178 | fn free(ptr, size) { 179 | while size { 180 | size = sub(size, 1); 181 | // free_byte is built in 182 | free_byte(add(ptr, size)); 183 | } 184 | 185 | // Store 0 in the return register 186 | return 0; 187 | } 188 | ``` 189 | 190 | # Sample Output 191 | 192 | Now to show you some god awful output code. 193 | 194 | Here is hello world in free. 195 | ```rust 196 | // This flag enables brainfuck compatibility mode. 197 | // This disables pointer operations and any number literal greater than 255 198 | #[enable(brainfuck)] 199 | 200 | fn start() { 201 | println("Hello, world!"); 202 | } 203 | ``` 204 | 205 | This gets compiled to the following. 206 | 207 | **_Forgive me for what I've created._** 208 | 209 | ```brainfuck 210 | [-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]<<<<<<<<<<<<<++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++>+<>>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[-]>>>>>>>>>>>>>>>>>>>>>[-]<<<<<<<<<<<<<<[>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<+>>>>>>>-]<<<<<<<[>>>>>>>+<<<<<<<-]>>>>>>>>>>>>>>>>>>>>>>[-]<<<<<<<<<<<<<<[>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>-]<<<<<<<<[>>>>>>>>+<<<<<<<<-]>>>>>>>>>>>>>>>>>>>>>>>[-]<<<<<<<<<<<<<<[>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>-]<<<<<<<<<[>>>>>>>>>+<<<<<<<<<-]>>>>>>>>>>>>>>>>>>>>>>>>[-]<<<<<<<<<<<<<<<>[<>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>-<>]<<<<<<<<<<[>>>>>>>>>>+<<<<<<<<<<-]>>>>>>>>>>>>>>>>>>>>>>>>>[-]<<<<<<<<<<<<<<[>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>-]<<<<<<<<<<<[>>>>>>>>>>>+<<<<<<<<<<<-]>>>>>>>>>>>>>>>>>>>>>>>>>>[-]<<<<<<<<<<<<<<<>[<>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>-<>]<<<<<<<<<<<<[>>>>>>>>>>>>+<<<<<<<<<<<<-]>>>>>>>>>>>>>>>>>>>>>>>>>>>[-]<<<<<<<<<<<<<<[>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>-]<<<<<<<<<<<<<[>>>>>>>>>>>>>+<<<<<<<<<<<<<-]>>>>>>>>>>>>>>>>>>>>>>>>>>>>[-]<<<<<<<<<<<<<<<>[<>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>-<>]<<<<<<<<<<<<<<[>>>>>>>>>>>>>>+<<<<<<<<<<<<<<-]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[-]<<<<<<<<<<<<<<[>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>-]<<<<<<<<<<<<<<<[>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<-]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[-]<<<<<<<<<<<<<<<>[<>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>-<>]<<<<<<<<<<<<<<<<[>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<-]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[-]<<<<<<<<<<<<<<[>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>-]<<<<<<<<<<<<<<<<<[>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<-]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[-]<<<<<<<<<<<<<<<>[<>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>-<>]<<<<<<<<<<<<<<<<<<[>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<-]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[-]<<<<<<<<<<<<<<[>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>-]<<<<<<<<<<<<<<<<<<<[>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<-]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[-]<<<<<<<<<<<<<<<>[<>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>-<>]<<<<<<<<<<<<<<<<<<<<[>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<-]>>>>>>>>>>>>>>>>>>>>>.>.>.>.>.>.>.>.>.>.>.>.>.>.<<>>>[-]++++++++++.<<<<<<<<<<<<<<[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]<<>>>[-][-]<<<<<<<<<<<<<<<<<<<<<<<<<<<<[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]<<<<<<<<<<<<<[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]<<<<<<<<<<<<<<<<<<<<<[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]<<<<<<< 211 | ``` 212 | 213 | **Yikes** that's bad. It does, however, fairly simply show how the compilation process works. Let me break it down. 214 | 215 | This is the first bit of output code from the compiler, and it's the simplest part to grasp. Here, the compiler is allocating and zeroing the register cells in addition to the cells that will be used for storing the string literal on the stack. 216 | 217 | ```brainfuck 218 | [-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]<<<<<<<<<<<<< 219 | ``` 220 | 221 | Then, the compiler actually assigns the data from the string literal to the memory location on the stack. 222 | 223 | 224 | ```brainfuck 225 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++>+< 226 | ``` 227 | 228 | Then, the compiler copies the string over to a new memory location for the `println` function to manipulate. 229 | 230 | 231 | ```brainfuck 232 | >>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[-]>>>>>>>>>>>>>>>>>>>>>[-]<<<<<<<<<<<<<<[>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<+>>>>>>>-]<<<<<<<[>>>>>>>+<<<<<<<-]>>>>>>>>>>>>>>>>>>>>>>[-]<<<<<<<<<<<<<<[>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>-]<<<<<<<<[>>>>>>>>+<<<<<<<<-]>>>>>>>>>>>>>>>>>>>>>>>[-]<<<<<<<<<<<<<<[>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>-]<<<<<<<<<[>>>>>>>>>+<<<<<<<<<-]>>>>>>>>>>>>>>>>>>>>>>>>[-]<<<<<<<<<<<<<<<>[<>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>-<>]<<<<<<<<<<[>>>>>>>>>>+<<<<<<<<<<-]>>>>>>>>>>>>>>>>>>>>>>>>>[-]<<<<<<<<<<<<<<[>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>-]<<<<<<<<<<<[>>>>>>>>>>>+<<<<<<<<<<<-]>>>>>>>>>>>>>>>>>>>>>>>>>>[-]<<<<<<<<<<<<<<<>[<>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>-<>]<<<<<<<<<<<<[>>>>>>>>>>>>+<<<<<<<<<<<<-]>>>>>>>>>>>>>>>>>>>>>>>>>>>[-]<<<<<<<<<<<<<<[>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>-]<<<<<<<<<<<<<[>>>>>>>>>>>>>+<<<<<<<<<<<<<-]>>>>>>>>>>>>>>>>>>>>>>>>>>>>[-]<<<<<<<<<<<<<<<>[<>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>-<>]<<<<<<<<<<<<<<[>>>>>>>>>>>>>>+<<<<<<<<<<<<<<-]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[-]<<<<<<<<<<<<<<[>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>-]<<<<<<<<<<<<<<<[>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<-]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[-]<<<<<<<<<<<<<<<>[<>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>-<>]<<<<<<<<<<<<<<<<[>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<-]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[-]<<<<<<<<<<<<<<[>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>-]<<<<<<<<<<<<<<<<<[>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<-]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[-]<<<<<<<<<<<<<<<>[<>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>-<>]<<<<<<<<<<<<<<<<<<[>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<-]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[-]<<<<<<<<<<<<<<[>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>-]<<<<<<<<<<<<<<<<<<<[>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<-]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[-]<<<<<<<<<<<<<<<>[<>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>-<>]<<<<<<<<<<<<<<<<<<<<[>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<-] 233 | ``` 234 | 235 | And lastly, the program prints out the string and cleans up the stack. 236 | 237 | ```brainfuck 238 | [-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]<<>>>[-][-]<<<<<<<<<<<<<<<<<<<<<<<<<<<<[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]<<<<<<<<<<<<<[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]<<<<<<<<<<<<<<<<<<<<<[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]<<<<<<< 239 | ``` 240 | 241 | Although most of this output code is understandable, there are still a few sections that are confusing to me. 242 | 243 | Like I'm not completely sure why `>+<` is there when the program is storing the `Hello world!` string, but whatever. It works, and I've drained my capacity to care why. 244 | 245 | 246 | # Usage and Installation 247 | 248 | The best way to install free is with the rust package manager. 249 | 250 | ```bash 251 | cargo install -f fr 252 | ``` 253 | 254 | Then, free files can be compiled with the `fr` binary. 255 | 256 | ```bash 257 | fr in.fr 258 | gcc out.c 259 | ./a.out 260 | ``` 261 | -------------------------------------------------------------------------------- /src/compile.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | add_to_compiled, compile, init, set_stack, Control, Env, ProgramParser, Stdout, Stdin, Value, 3 | HEAP_SIZE, RETURN, STACK_PTR, STACK_SIZE, 4 | }; 5 | use comment::rust::strip; 6 | use rand::distributions::Alphanumeric; 7 | use rand::{thread_rng, Rng}; 8 | use std::{ 9 | collections::HashMap, 10 | sync::{Arc, Mutex}, 11 | }; 12 | 13 | lazy_static! { 14 | /// This is used to manage variable definitions within scopes. 15 | /// When a function is called, a scope is pushed on the scope stack, 16 | /// and all new definitions are contained in the new scope. 17 | /// When the function call finishes, the scope is popped and freed. 18 | pub static ref SCOPE_STACK: Mutex> = Mutex::new(vec![Env::new()]); 19 | 20 | /// If the user enables brainfuck compatibility mode, this flag is set 21 | static ref ENABLE_BRAINFUCK: Mutex = Mutex::new(false); 22 | /// If the user enables size warnings, this flag is set 23 | static ref ENABLE_SIZE_WARN: Mutex = Mutex::new(false); 24 | 25 | /// This hashmap contains all the user defined functions for the program 26 | static ref FN_DEFS: Mutex> = Mutex::new(HashMap::new()); 27 | /// This hashmap contains all the compiler defined functions for the program 28 | static ref FOREIGN_FN_DEFS: Mutex> = Mutex::new(HashMap::new()); 29 | } 30 | 31 | /// Generate a random string, used for naming temporary variables 32 | fn rand_str() -> String { 33 | thread_rng().sample_iter(&Alphanumeric).take(30).collect() 34 | } 35 | 36 | /// This object manages compiling the program, and setting the enabled flags. 37 | #[derive(Clone, Debug, PartialOrd, PartialEq)] 38 | pub struct Program(Vec, Vec); 39 | 40 | impl From for Program { 41 | fn from(t: T) -> Self { 42 | match ProgramParser::new().parse(&strip(t.to_string()).unwrap()) { 43 | Ok(val) => val, 44 | Err(e) => panic!("{:#?}", e), 45 | } 46 | } 47 | } 48 | 49 | impl Program { 50 | /// Create new Program object 51 | pub fn new(flags: Vec, funs: Vec) -> Self { 52 | // Set the flags for the compiler 53 | for flag in &flags { 54 | match flag { 55 | Flag::EnableBrainFuck => *ENABLE_BRAINFUCK.lock().unwrap() = true, 56 | Flag::EnableSizeWarn => *ENABLE_SIZE_WARN.lock().unwrap() = true, 57 | } 58 | } 59 | // Return self 60 | Self(flags, funs) 61 | } 62 | 63 | /// Instantiate compiler functions 64 | fn prelude() { 65 | init(); 66 | 67 | // Add lhs and rhs 68 | deforfun("add", &["a", "b"], || { 69 | get("a")?.plus_eq(get("b")?); 70 | set_return(get("a")?)?; 71 | Ok(()) 72 | }); 73 | 74 | // Subtract rhs from lhs 75 | deforfun("sub", &["a", "b"], || { 76 | get("a")?.minus_eq(get("b")?); 77 | set_return(get("a")?)?; 78 | Ok(()) 79 | }); 80 | 81 | // Print function 82 | deforfun("print", &["a"], || { 83 | Stdout::print(get("a")?); 84 | Ok(()) 85 | }); 86 | 87 | // Println function 88 | deforfun("println", &["a"], || { 89 | Stdout::print(get("a")?); 90 | Stdout::print(Eval::Literal(Literal::character('\n')).lower()?); 91 | Ok(()) 92 | }); 93 | 94 | // Print function 95 | deforfun("cprint", &["ptr"], || { 96 | Stdout::print_cstr(get("ptr")?)?; 97 | Ok(()) 98 | }); 99 | 100 | // Println function 101 | deforfun("cprintln", &["ptr"], || { 102 | Stdout::print_cstr(get("ptr")?)?; 103 | Stdout::print(Eval::Literal(Literal::character('\n')).lower()?); 104 | Ok(()) 105 | }); 106 | 107 | // Allocate `size` number of bytes 108 | deforfun("alloc", &["size"], || { 109 | define("ptr", Eval::Value(Value::variable_alloc(get("size")?)?))?; 110 | set_return(get("ptr")?)?; 111 | Ok(()) 112 | }); 113 | 114 | // Free a byte at ptr 115 | deforfun("free_byte", &["ptr"], || { 116 | get("ptr")?.deref()?.free(); 117 | Ok(()) 118 | }); 119 | 120 | // Free a byte at ptr 121 | deforfun("getch", &[], || { 122 | define("ch", Eval::Value(Value::character('\0')?))?; 123 | Stdin::getch(get("ch")?); 124 | set_return(get("ch")?)?; 125 | Ok(()) 126 | }); 127 | } 128 | 129 | /// Is brainfuck compatibility mode enabled? 130 | pub fn brainfuck_enabled() -> bool { 131 | *ENABLE_BRAINFUCK.lock().unwrap() 132 | } 133 | 134 | /// Are size warnings enabled? 135 | pub fn size_warn_enabled() -> bool { 136 | *ENABLE_SIZE_WARN.lock().unwrap() 137 | } 138 | 139 | /// Get the stack size 140 | pub fn stack_size() -> u32 { 141 | *STACK_SIZE.lock().unwrap() 142 | } 143 | 144 | /// Get the heap size 145 | pub fn heap_size() -> u32 { 146 | *HEAP_SIZE.lock().unwrap() 147 | } 148 | 149 | /// Get the tape size 150 | pub fn tape_size() -> u32 { 151 | Self::stack_size() + Self::heap_size() 152 | } 153 | 154 | /// Compile the code 155 | pub fn compile(self) -> Result { 156 | // Add the compiler functions 157 | Self::prelude(); 158 | 159 | // Get function definitions 160 | let Program(_, funs) = self; 161 | 162 | // Compile 163 | for fun in funs { 164 | fun.compile(); 165 | } 166 | 167 | call("start", &vec![])?; 168 | 169 | // Return compiled code 170 | Ok(compile()) 171 | } 172 | } 173 | 174 | /// The possible flags to be returned by the parser 175 | #[derive(Clone, Debug, PartialOrd, PartialEq)] 176 | pub enum Flag { 177 | EnableBrainFuck, 178 | EnableSizeWarn, 179 | } 180 | 181 | /// The possible compiler errors 182 | #[derive(Clone, Debug)] 183 | pub enum Error { 184 | StackOverflow, 185 | MustReturnSingleByte, 186 | CannotReferenceAReference, 187 | CannotUsePointersInBrainFuckMode, 188 | CannotUseUnsignedShortsInBrainFuckMode, 189 | CannotAssignLargerValueToSmallerValueInBrainFuckMode, 190 | FunctionNotDefined(String), 191 | VariableNotDefined(String, Env), 192 | } 193 | 194 | /// This trait describes objects that are lowered 195 | /// into values rather than expressions, such as function calls 196 | /// literals, variables, etc.. 197 | pub trait Lower { 198 | fn lower(&self) -> Result; 199 | } 200 | 201 | /// A value can be `lowered` into a value 202 | impl Lower for Value { 203 | fn lower(&self) -> Result { 204 | Ok(*self) 205 | } 206 | } 207 | 208 | /// This trait is for objects that compile into expressions 209 | /// rather than values. This is applicable for definitions, 210 | /// assignments, return expressions, while loops, etc.. 211 | pub trait Compile { 212 | fn compile(&self) -> Result<(), Error>; 213 | } 214 | 215 | /// This function creates a new scope on the scope stack. 216 | /// THIS IS ONLY TO BE USED BY FUNCTION DEFINITIONS 217 | fn push_scope(env: Env) { 218 | SCOPE_STACK.lock().unwrap().push(env); 219 | } 220 | 221 | /// This function destroys a scope on the scope stack. 222 | /// THIS IS ONLY TO BE USED BY FUNCTION DEFINITIONS 223 | fn pop_scope() -> Env { 224 | SCOPE_STACK.lock().unwrap().pop().unwrap() 225 | } 226 | 227 | /// This sets the RETURN value object to a specific value 228 | pub fn set_return(val: Value) -> Result<(), Error> { 229 | if val.size() > 1 { 230 | Err(Error::MustReturnSingleByte) 231 | } else { 232 | RETURN.assign(val)?; 233 | Ok(()) 234 | } 235 | } 236 | 237 | /// This retreives the last value returned by a function 238 | pub fn get_return() -> Result { 239 | // let val = Eval::Value(*RETURN); 240 | // let name = format!("%TEMP_RETURN{}%", *STACK_PTR.lock().unwrap()); 241 | // define(&name, val)?; 242 | // get(name) 243 | Ok(*RETURN) 244 | } 245 | 246 | /// This represents a value that can be evaluated. Variables, 247 | /// literals, function calls, value dereferences, variable references, 248 | /// and values (this is a cheat for letting the compiler easily use 249 | /// values in place of long Evals) can all be evaluated into a Value 250 | #[derive(Clone, Debug, PartialOrd, PartialEq)] 251 | pub enum Eval { 252 | Load(Load), 253 | Literal(Literal), 254 | Call(Call), 255 | Deref(Deref), 256 | Refer(Refer), 257 | Value(Value), 258 | } 259 | 260 | /// An Eval expression is evaluated by the expression it contains 261 | impl Lower for Eval { 262 | fn lower(&self) -> Result { 263 | // Lower the contained expression 264 | match self { 265 | Self::Load(l) => l.lower(), 266 | Self::Literal(l) => l.lower(), 267 | Self::Deref(r) => r.lower(), 268 | Self::Call(c) => c.lower(), 269 | Self::Refer(v) => v.lower(), 270 | Self::Value(v) => v.lower(), 271 | } 272 | } 273 | } 274 | 275 | /// This represents a statement as opposed to a value. 276 | /// A value can also be a statement, though. 277 | #[derive(Clone, Debug, PartialOrd, PartialEq)] 278 | pub enum Expr { 279 | If(If), 280 | While(While), 281 | Eval(Eval), 282 | Define(Define), 283 | Assign(Assign), 284 | Return(Return), 285 | } 286 | 287 | /// An Expression is evaluated by the expression it contains 288 | impl Compile for Expr { 289 | fn compile(&self) -> Result<(), Error> { 290 | match self { 291 | Self::If(l) => l.compile()?, 292 | Self::Eval(e) => { 293 | e.lower()?; // Dont return value from lower 294 | } 295 | Self::Define(def) => def.compile()?, 296 | Self::Assign(a) => a.compile()?, 297 | Self::While(w) => w.compile()?, 298 | Self::Return(r) => r.compile()?, 299 | } 300 | Ok(()) 301 | } 302 | } 303 | 304 | /// This sets the RETURN register to an Eval 305 | #[derive(Clone, Debug, PartialOrd, PartialEq)] 306 | pub struct Return(Eval); 307 | 308 | impl Return { 309 | pub fn new(val: Eval) -> Self { 310 | Return(val) 311 | } 312 | } 313 | 314 | impl Compile for Return { 315 | fn compile(&self) -> Result<(), Error> { 316 | let Return(val) = self; 317 | set_return(val.lower()?)?; 318 | Ok(()) 319 | } 320 | } 321 | 322 | #[derive(Clone, Debug, PartialOrd, PartialEq)] 323 | pub struct Call(String, Vec); 324 | 325 | impl Call { 326 | pub fn new(name: impl ToString, args: Vec) -> Self { 327 | Self(name.to_string(), args) 328 | } 329 | } 330 | 331 | impl Lower for Call { 332 | fn lower(&self) -> Result { 333 | let Call(name, args) = self; 334 | add_to_compiled(format!("CALLING {}", name)); 335 | call(name, args)?; 336 | add_to_compiled("DONE"); 337 | get_return() 338 | } 339 | } 340 | 341 | #[derive(Clone, Debug, PartialOrd, PartialEq)] 342 | pub struct Deref(Arc); 343 | 344 | impl Deref { 345 | pub fn new(refer: Eval) -> Self { 346 | Self(Arc::new(refer)) 347 | } 348 | } 349 | 350 | impl Lower for Deref { 351 | fn lower(&self) -> Result { 352 | let Deref(refer) = self; 353 | Ok(refer.lower()?.deref()?) 354 | } 355 | } 356 | 357 | #[derive(Clone, Debug, PartialOrd, PartialEq)] 358 | pub struct Refer(Arc); 359 | 360 | impl Refer { 361 | pub fn new(var: Eval) -> Self { 362 | Self(Arc::new(var)) 363 | } 364 | } 365 | 366 | impl Lower for Refer { 367 | fn lower(&self) -> Result { 368 | let Refer(var) = self; 369 | Ok(var.lower()?.refer()?) 370 | } 371 | } 372 | 373 | #[derive(Clone, Debug, PartialOrd, PartialEq)] 374 | pub struct Load(String); 375 | 376 | impl Load { 377 | pub fn new(s: impl ToString) -> Self { 378 | Self(s.to_string()) 379 | } 380 | } 381 | 382 | impl Lower for Load { 383 | fn lower(&self) -> Result { 384 | let Load(name) = self; 385 | get(name) 386 | } 387 | } 388 | 389 | #[derive(Clone, Debug, PartialOrd, PartialEq)] 390 | pub enum Literal { 391 | String(String), 392 | Character(char), 393 | ByteInt(u8), 394 | UnsignedShort(u16), 395 | } 396 | 397 | impl Literal { 398 | pub fn string(s: impl ToString) -> Self { 399 | Self::String(s.to_string()) 400 | } 401 | 402 | pub fn character(ch: char) -> Self { 403 | Self::Character(ch) 404 | } 405 | 406 | pub fn byte_int(b: u8) -> Self { 407 | Self::ByteInt(b) 408 | } 409 | 410 | pub fn unsigned_short(ui: u16) -> Self { 411 | Self::UnsignedShort(ui) 412 | } 413 | } 414 | 415 | impl Lower for Literal { 416 | fn lower(&self) -> Result { 417 | let name; 418 | match self { 419 | Self::String(s) => { 420 | name = format!("%TEMP_STR_LITERAL_{}%", rand_str()); 421 | define_no_cp(&name, Eval::Value(Value::string(s)?))?; 422 | } 423 | Self::Character(ch) => { 424 | name = format!("%TEMP_CHAR_LITERAL_{}%", rand_str()); 425 | define_no_cp(&name, Eval::Value(Value::character(*ch)?))?; 426 | } 427 | Self::ByteInt(byte) => { 428 | name = format!("%TEMP_BYTE_LITERAL_{}%", rand_str()); 429 | define_no_cp(&name, Eval::Value(Value::byte_int(*byte)?))?; 430 | } 431 | Self::UnsignedShort(ui) => { 432 | name = format!("%TEMP_U16_LITERAL_{}%", rand_str()); 433 | define_no_cp(&name, Eval::Value(Value::unsigned_short(*ui)?))?; 434 | } 435 | } 436 | get(name) 437 | } 438 | } 439 | 440 | pub fn deforfun(name: impl ToString, args: &[&'static str], fun: fn() -> Result<(), Error>) { 441 | FOREIGN_FN_DEFS 442 | .lock() 443 | .unwrap() 444 | .insert(name.to_string(), ForeignFn::new(args.to_vec(), fun)); 445 | } 446 | 447 | #[derive(Clone, Debug, PartialOrd, PartialEq)] 448 | pub struct UserFn { 449 | name: String, 450 | parameters: Vec, 451 | body: Vec, 452 | } 453 | 454 | impl UserFn { 455 | pub fn new(name: impl ToString, parameters: Vec, body: Vec) -> Self { 456 | Self { 457 | name: name.to_string(), 458 | parameters: parameters.iter().map(ToString::to_string).collect(), 459 | body, 460 | } 461 | } 462 | 463 | pub fn compile(self) { 464 | FN_DEFS.lock().unwrap().insert(self.name.clone(), self); 465 | } 466 | 467 | pub fn call(&self, args: &Vec) -> Result<(), Error> { 468 | let stack_frame; 469 | stack_frame = *STACK_PTR.lock().unwrap(); 470 | 471 | let mut env = Env::new(); 472 | 473 | for (i, p) in self.parameters.iter().enumerate() { 474 | env.define(p.to_string(), args[i].lower()?)?; 475 | } 476 | 477 | push_scope(env); 478 | 479 | for instruction in &self.body { 480 | instruction.compile()?; 481 | } 482 | 483 | pop_scope().free(); 484 | set_stack(stack_frame)?; 485 | 486 | Ok(()) 487 | } 488 | } 489 | 490 | pub fn call(name: impl ToString, args: &Vec) -> Result<(), Error> { 491 | let table = FN_DEFS.lock().unwrap(); 492 | if let Some(f_ref) = table.get(&name.to_string()) { 493 | let fun = f_ref.clone(); 494 | drop(table); 495 | fun.call(args)?; 496 | return Ok(()); 497 | } else { 498 | drop(table) 499 | } 500 | 501 | let table = FOREIGN_FN_DEFS.lock().unwrap(); 502 | if let Some(f_ref) = table.get(&name.to_string()) { 503 | let fun = f_ref.clone(); 504 | drop(table); 505 | fun.call(args)?; 506 | return Ok(()); 507 | } 508 | 509 | Err(Error::FunctionNotDefined(name.to_string())) 510 | } 511 | 512 | pub fn define(name: impl ToString, val: Eval) -> Result<(), Error> { 513 | let temp_name = format!("%TEMP_DEFINE_{}%", rand_str()); 514 | Define::new(&temp_name, val).compile()?; 515 | Define::new(name, Eval::Load(Load::new(temp_name))).compile()?; 516 | Ok(()) 517 | } 518 | 519 | pub fn define_no_cp(final_name: impl ToString, value: Eval) -> Result<(), Error> { 520 | let name = format!("%TEMP_DEFINE_{}%", rand_str()); 521 | 522 | let val = value.lower()?; 523 | let mut scope_stack = SCOPE_STACK.lock().unwrap(); 524 | let scope = scope_stack.last_mut().unwrap(); 525 | scope.define_no_cp(&name, val); 526 | drop(scope_stack); 527 | 528 | let val = get(name)?; 529 | let mut scope_stack = SCOPE_STACK.lock().unwrap(); 530 | let scope = scope_stack.last_mut().unwrap(); 531 | scope.define_no_cp(final_name, val); 532 | drop(scope_stack); 533 | 534 | Ok(()) 535 | } 536 | 537 | pub fn get(name: impl ToString) -> Result { 538 | let mut scope_stack = SCOPE_STACK.lock().unwrap(); 539 | let scope = scope_stack.last_mut().unwrap(); 540 | scope.get(name.to_string()) 541 | } 542 | 543 | #[derive(Clone, Debug, PartialOrd, PartialEq)] 544 | pub struct Define(String, Eval); 545 | 546 | impl Define { 547 | pub fn new(var: impl ToString, value: Eval) -> Self { 548 | Self(var.to_string(), value) 549 | } 550 | } 551 | 552 | impl Compile for Define { 553 | fn compile(&self) -> Result<(), Error> { 554 | let Define(name, value) = self; 555 | add_to_compiled(format!("DEFINING {}", name)); 556 | 557 | let val = value.lower()?; 558 | let mut scope_stack = SCOPE_STACK.lock().unwrap(); 559 | let scope = scope_stack.last_mut().unwrap(); 560 | scope.define(&name, val)?; 561 | drop(scope_stack); 562 | 563 | add_to_compiled("DONE"); 564 | 565 | Ok(()) 566 | } 567 | } 568 | 569 | #[derive(Clone, Debug, PartialOrd, PartialEq)] 570 | pub struct Assign(Eval, Eval); 571 | 572 | impl Assign { 573 | pub fn new(lhs: Eval, rhs: Eval) -> Self { 574 | Self(lhs, rhs) 575 | } 576 | } 577 | 578 | impl Compile for Assign { 579 | fn compile(&self) -> Result<(), Error> { 580 | let Assign(lhs, rhs) = self; 581 | lhs.lower()?.assign(rhs.lower()?)?; 582 | Ok(()) 583 | } 584 | } 585 | 586 | #[derive(Clone, Debug, PartialOrd, PartialEq)] 587 | pub struct If(Eval, Vec, Vec); 588 | 589 | impl If { 590 | pub fn new(condition: Eval, then: Vec, otherwise: Vec) -> Self { 591 | Self(condition, then, otherwise) 592 | } 593 | } 594 | 595 | impl Compile for If { 596 | fn compile(&self) -> Result<(), Error> { 597 | let If(condition, then, otherwise) = self; 598 | Control::if_begin(condition.lower()?)?; 599 | for exp in then { 600 | exp.compile()?; 601 | } 602 | Control::else_begin()?; 603 | for exp in otherwise { 604 | exp.compile()?; 605 | } 606 | Control::if_end()?; 607 | Ok(()) 608 | } 609 | } 610 | 611 | #[derive(Clone, Debug, PartialOrd, PartialEq)] 612 | pub struct While(Eval, Vec); 613 | 614 | impl While { 615 | pub fn new(condition: Eval, then: Vec) -> Self { 616 | Self(condition, then) 617 | } 618 | } 619 | 620 | impl Compile for While { 621 | fn compile(&self) -> Result<(), Error> { 622 | let While(condition, then) = self; 623 | Control::while_begin(condition.lower()?); 624 | { 625 | for exp in then { 626 | exp.compile()?; 627 | } 628 | } 629 | Control::while_end(); 630 | Ok(()) 631 | } 632 | } 633 | 634 | /// This class is only used for foreign functions. Do not use for regular functions. 635 | #[derive(Clone)] 636 | pub struct ForeignFn { 637 | parameters: Vec, 638 | body: fn() -> Result<(), Error>, 639 | } 640 | 641 | impl ForeignFn { 642 | pub fn new(parameters: Vec, body: fn() -> Result<(), Error>) -> Self { 643 | Self { 644 | parameters: parameters.iter().map(ToString::to_string).collect(), 645 | body, 646 | } 647 | } 648 | 649 | pub fn define(name: impl ToString, args: Vec, fun: fn() -> Result<(), Error>) { 650 | FOREIGN_FN_DEFS.lock().unwrap().insert( 651 | name.to_string(), 652 | Self::new(args.iter().map(ToString::to_string).collect(), fun), 653 | ); 654 | } 655 | 656 | pub fn call(&self, args: &Vec) -> Result<(), Error> { 657 | let stack_frame; 658 | stack_frame = *STACK_PTR.lock().unwrap(); 659 | 660 | let mut env = Env::new(); 661 | 662 | for (i, p) in self.parameters.iter().enumerate() { 663 | env.define(p.to_string(), args[i].lower()?)?; 664 | } 665 | 666 | push_scope(env); 667 | 668 | (self.body)()?; 669 | 670 | pop_scope().free(); 671 | set_stack(stack_frame)?; 672 | 673 | Ok(()) 674 | } 675 | } 676 | --------------------------------------------------------------------------------