Skip to content

Add a fast variant of the brainfunct protect function #1

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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ compile-time interpreter. One example is as follows
#![recursion_limit = "18446744073709551615"]
#![feature(const_mut_refs)]

use program_protected_compile::brainfunct_protect;
use compile_protection::brainfunct_protect;

fn main() {
brainfunct_protect!("/,./+@", "1", "1");
Expand Down
175 changes: 168 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use proc_macro::{TokenStream};
use quote::{quote, ToTokens, TokenStreamExt, format_ident};
use syn::{LitStr, parse_macro_input, Token, Expr};
use proc_macro::TokenStream;
use proc_macro2::{Punct, Span};
use quote::{format_ident, quote, ToTokens, TokenStreamExt};
use syn::parse::{Parse, ParseStream};
use proc_macro2::Punct;
use syn::{parse_macro_input, Expr, LitStr, Token};

struct InputStruct {
program: LitStr,
Expand Down Expand Up @@ -37,14 +37,172 @@ impl Parse for InputStruct {
}
}

struct BrainfunctProgram {
operations: Vec<u8>,
functions: Vec<usize>,
main_index: usize,
input: Vec<u8>,
output: Vec<u8>,
}

impl Parse for BrainfunctProgram {
fn parse(input: ParseStream) -> syn::Result<Self> {
let p: LitStr = input.parse()?;
let p_string = p.value();
input.parse::<Token![,]>()?;
let input_str: LitStr = input.parse()?;
let input_string = input_str.value();
input.parse::<Token![,]>()?;
let output: LitStr = input.parse()?;
let output_string = output.value();
let mut temp = BrainfunctProgram {
operations: Vec::with_capacity(p_string.len()),
functions: vec![usize::MAX],

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

heh?

main_index: usize::MAX,
input: Vec::with_capacity(input_string.len()),
output: Vec::with_capacity(output_string.len()),
};
let mut func_index = 0;
for (index, c) in p.value().bytes().enumerate() {
temp.operations.push(match c {
b'>' | b'<' | b'+' | b'-' | b'.' | b',' | b'@' => c,
b'/' => {
temp.functions.push(temp.main_index);
temp.main_index = index;
func_index += 1;
c
}
_ => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't brainfuck actually permissive in characters allowed? i suppose that's different for brainfunct 🤔

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know that it's actually different. It's just the rules I'm implementing for this interpreter. i could always just have it do nothing.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to decide whether to ignore illegal characters or error.

return Err(syn::Error::new(
Span::call_site(),
"You had an invalid character in your program",
))
}
});

// The value is 256 because you can have 255 functions (and they start indexing at 1)
// and a main function which will cause the len to be 256 in the greatest case
if temp.functions.len() > 256 {
return Err(syn::Error::new(
Span::call_site(),
"You had more functions than are able to be called",
));
}
}

if func_index == 0 {
return Err(syn::Error::new(
Span::call_site(),
"You didn't define any functions at all",
));
}

for c in input_string.bytes() {
temp.input.push(c);
}

for c in output_string.bytes() {
temp.output.push(c);
}

Result::Ok(temp)
}
}

#[proc_macro]
pub fn fast_brainfunct_protect(input: TokenStream) -> TokenStream {
let program = parse_macro_input!(input as BrainfunctProgram);
let o = program.operations.iter();
let o_size = program.operations.len();
let f = program.functions.iter();
let f_size = program.functions.len();
let m = program.main_index;
let i = program.input.iter();
let i_size = program.input.len();
let out = program.output.iter();
let out_size = program.output.len();
let call_const = quote! {
const fn run_fast_brainfunct_protect(
operations: &[u8],
op_size: usize,
func_map: &[u8],
func_size: u8,
main_index: usize,
input: &[u8],
i_size: usize,
output: &[u8],
o_size: usize,
) {
const fn die() {
die()
}

let mut tape = [0u8; u16::MAX as usize];
let mut tape_ptr = 0usize;
let mut input_ptr = 0usize;
let mut output_ptr = 0usize;

let mut stack = [usize::MAX; u16::MAX as usize];
let mut stack_head = 0i32;
let mut current_index = main_index + 1usize;
loop {
if current_index >= op_size {
if stack_head != -1 {
// panic!("Your call stack wasn't completely emptied even though you have reached the end of the main function");
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Get rid of vestigial comments

die()
}
break;
}
match operations[current_index] {
b'>' => tape_ptr += 1,
b'<' => tape_ptr -= 1,
b'+' => tape[tape_ptr] += 1,
b'-' => tape[tape_ptr] -= 1,
b',' => {
if input_ptr >= i_size {
die();
}
tape[tape_ptr] = input[input_ptr];
input_ptr += 1
}
b'.' => {
if output[output_ptr] != tape[tape_ptr] || output_ptr >= o_size {
die()
}
output_ptr += 1
}
b'@' => {
if tape[tape_ptr] >= func_size {
// panic!("Undefined function {}", tape[tape_ptr])
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Get rid of vestigial comments

die();
}
stack[stack_head as usize] = current_index;
stack_head += 1;
current_index = (func_map[tape[tape_ptr] as usize] + 1) as usize;
}
b'/' => {
current_index = stack[stack_head as usize];
stack_head -= 1;
}
_ => die(),
}
current_index += 1
}
}

run_fast_brainfunct_protect([#(#o),*], #o_size, [#(#f),*], #f_size, #m, [#(#i),*], #i_size, [#(#out),*], #out_size);
};
call_const.into()
}

#[proc_macro]
pub fn brainfunct_protect(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as InputStruct);
let mut funcs = Vec::new();
for c in input.program.value().bytes() {
match c {
b'/' => funcs.push(Vec::new()),
op => funcs.last_mut().unwrap().push(op)
op => funcs.last_mut().unwrap().push(op),
}
}

Expand Down Expand Up @@ -92,7 +250,7 @@ pub fn brainfunct_protect(input: TokenStream) -> TokenStream {
inner.insert(0, "c(".into());
inner.push(")".into());
}
c => panic!("You've used an illegal character: {}", c)
c => panic!("You've used an illegal character: {}", c),
}
}
let merged = inner.join("");
Expand All @@ -103,7 +261,10 @@ pub fn brainfunct_protect(input: TokenStream) -> TokenStream {
let expected_bytes = expected.bytes();
let indices = 0..expected.len();
let input_str = input.input.value();
let input_iter = input_str.bytes().chain(std::iter::repeat(0)).take(u16::MAX as usize);
let input_iter = input_str
.bytes()
.chain(std::iter::repeat(0))
.take(u16::MAX as usize);

let main_func = format_ident!("func{}", funcs.len());
let tokens = quote! {
Expand Down