diff --git a/README.md b/README.md index 1db5ed0..aeb882b 100644 --- a/README.md +++ b/README.md @@ -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"); diff --git a/src/lib.rs b/src/lib.rs index 8aa2a58..9e0e734 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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, @@ -37,6 +37,164 @@ impl Parse for InputStruct { } } +struct BrainfunctProgram { + operations: Vec, + functions: Vec, + main_index: usize, + input: Vec, + output: Vec, +} + +impl Parse for BrainfunctProgram { + fn parse(input: ParseStream) -> syn::Result { + let p: LitStr = input.parse()?; + let p_string = p.value(); + input.parse::()?; + let input_str: LitStr = input.parse()?; + let input_string = input_str.value(); + input.parse::()?; + 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], + 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 + } + _ => { + 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"); + 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]) + 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); @@ -44,7 +202,7 @@ pub fn brainfunct_protect(input: TokenStream) -> TokenStream { 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), } } @@ -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(""); @@ -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! {