1- pub fn brain_luck ( code : & str , input : Vec < u8 > ) -> Vec < u8 > {
1+ //create error type for my lib and use it for brain_luck function
2+ use std:: error:: Error ;
3+
4+ #[ derive( Debug , PartialEq , Eq , PartialOrd , Ord ) ]
5+ pub enum BrainLuckError {
6+ UnexpectedCharInCode ( char ) ,
7+ UnexpectedEndOfInput ,
8+ UnbalancedBrackets ,
9+ }
10+
11+ impl std:: fmt:: Display for BrainLuckError {
12+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
13+ let message = match self {
14+ BrainLuckError :: UnexpectedCharInCode ( c) => {
15+ format ! ( "unexpected char {} occured in code." , c)
16+ }
17+ BrainLuckError :: UnexpectedEndOfInput => String :: from ( "unexpected end of input." ) ,
18+ BrainLuckError :: UnbalancedBrackets => String :: from ( "unbalanced brackets." ) ,
19+ } ;
20+ f. write_str ( & message)
21+ }
22+ }
23+
24+ impl Error for BrainLuckError { }
25+
26+ pub fn brain_luck ( code : & str , input : Vec < u8 > ) -> Result < Vec < u8 > , BrainLuckError > {
227 let mut output = Vec :: new ( ) ;
328 let mut input = input. iter ( ) ;
429 let mut memory = vec ! [ 0u8 ; 1000 ] ;
@@ -8,54 +33,29 @@ pub fn brain_luck(code: &str, input: Vec<u8>) -> Vec<u8> {
833 let insts = code. chars ( ) . collect :: < Vec < _ > > ( ) ;
934 while let Some ( c) = insts. get ( ins_ptr) {
1035 match c {
11- '>' => {
12- mem_ptr += 1 ;
13- ins_ptr += 1 ;
14- }
15- '<' => {
16- mem_ptr -= 1 ;
17- ins_ptr += 1 ;
18- }
19- '+' => {
20- memory[ mem_ptr] = memory[ mem_ptr] . wrapping_add ( 1 ) ;
21- ins_ptr += 1 ;
22- }
23- '-' => {
24- memory[ mem_ptr] = memory[ mem_ptr] . wrapping_sub ( 1 ) ;
25- ins_ptr += 1 ;
26- }
27- '.' => {
28- output. push ( memory[ mem_ptr] ) ;
29- ins_ptr += 1 ;
30- }
31- ',' => {
32- let b = input. next ( ) . unwrap ( ) ;
33- memory[ mem_ptr] = * b;
34- ins_ptr += 1 ;
35- }
36+ '>' => mem_ptr += 1 ,
37+ '<' => mem_ptr -= 1 ,
38+ //TODO: add handling for mem_ptr going out of bounds (or accessing memory outside of bounds)
39+ '+' => memory[ mem_ptr] = memory[ mem_ptr] . wrapping_add ( 1 ) ,
40+ '-' => memory[ mem_ptr] = memory[ mem_ptr] . wrapping_sub ( 1 ) ,
41+ '.' => output. push ( memory[ mem_ptr] ) ,
42+ ',' => memory[ mem_ptr] = * input. next ( ) . ok_or ( BrainLuckError :: UnexpectedEndOfInput ) ?,
3643 '[' => {
37- let b = memory[ mem_ptr] ;
38- if b == 0 {
39- //jump to next after closing bracket
40- ins_ptr = after_matching_closing_bracket ( & insts, ins_ptr, Bracket :: LBracket ) ;
41- } else {
42- ins_ptr += 1 ;
44+ if memory[ mem_ptr] == 0 {
45+ ins_ptr = matching_closing_bracket_ptr ( & insts, ins_ptr, Bracket :: LBracket ) ?;
4346 }
4447 }
4548 ']' => {
46- let b = memory[ mem_ptr] ;
47- if b != 0 {
48- //jump to previous after closing bracket
49- ins_ptr = after_matching_closing_bracket ( & insts, ins_ptr, Bracket :: RBracket ) ;
50- } else {
51- ins_ptr += 1 ;
49+ if memory[ mem_ptr] != 0 {
50+ ins_ptr = matching_closing_bracket_ptr ( & insts, ins_ptr, Bracket :: RBracket ) ?;
5251 }
5352 }
54- c if c. is_whitespace ( ) => ins_ptr += 1 ,
55- unexpected_char => panic ! ( "Unexpected char {} occured in code." , unexpected_char) ,
53+ c if c. is_whitespace ( ) => ( ) ,
54+ unexpected_char => return Err ( BrainLuckError :: UnexpectedCharInCode ( * unexpected_char) ) ,
5655 }
56+ ins_ptr += 1 ;
5757 }
58- output
58+ Ok ( output)
5959}
6060
6161enum Bracket {
@@ -66,14 +66,18 @@ enum Bracket {
6666macro_rules! add {
6767 ( $u: ident, $s: ident) => {
6868 if $s < 0 {
69- $u - ( -$s) as usize
69+ $u. checked_sub ( ( -$s) as usize )
7070 } else {
71- $u + $s as usize
71+ Some ( $u + $s as usize )
7272 }
7373 } ;
7474}
7575
76- fn after_matching_closing_bracket ( insts : & [ char ] , ins_ptr : usize , b : Bracket ) -> usize {
76+ fn matching_closing_bracket_ptr (
77+ insts : & [ char ] ,
78+ ins_ptr : usize ,
79+ b : Bracket ,
80+ ) -> Result < usize , BrainLuckError > {
7781 let direction = match b {
7882 Bracket :: LBracket => 1 ,
7983 Bracket :: RBracket => -1 ,
@@ -84,18 +88,22 @@ fn after_matching_closing_bracket(insts: &[char], ins_ptr: usize, b: Bracket) ->
8488 loop {
8589 let c = insts. get ( ptr) ;
8690 match c {
87- None => panic ! ( "Unbalanced brackets(EOF)!" ) ,
91+ None => return Err ( BrainLuckError :: UnbalancedBrackets ) ,
8892 Some ( '[' ) => counter += direction,
8993 Some ( ']' ) => counter -= direction,
9094 Some ( _) => ( ) ,
9195 }
9296 if counter < 0 {
93- panic ! ( "Unbalanced brackets!" )
97+ return Err ( BrainLuckError :: UnbalancedBrackets ) ;
9498 }
9599 if counter == 0 {
96- return ptr;
100+ return Ok ( ptr) ;
97101 }
98- ptr = add ! ( ptr, direction) ;
102+ ptr = match add ! ( ptr, direction) {
103+ Some ( p) => p,
104+ None if direction == -1 => return Err ( BrainLuckError :: UnbalancedBrackets ) ,
105+ None => unreachable ! ( ) ,
106+ } ;
99107 }
100108}
101109
@@ -108,7 +116,7 @@ mod tests {
108116 fn echo_until_byte_255_encountered ( ) {
109117 // Echo until byte 255 encountered
110118 assert ! (
111- String :: from_utf8( brain_luck( ",+[-.,+]" , ez_vec( "Codewars" , 255 ) ) ) . unwrap( )
119+ String :: from_utf8( brain_luck( ",+[-.,+]" , ez_vec( "Codewars" , 255 ) ) . unwrap ( ) ) . unwrap( )
112120 == "Codewars"
113121 ) ;
114122 }
@@ -117,14 +125,84 @@ mod tests {
117125 fn echo_until_byte_0_encountered ( ) {
118126 // Echo until byte 0 encountered
119127 assert ! (
120- String :: from_utf8( brain_luck( ",[.[-],]" , ez_vec( "Codewars" , 0 ) ) ) . unwrap( ) == "Codewars"
128+ String :: from_utf8( brain_luck( ",[.[-],]" , ez_vec( "Codewars" , 0 ) ) . unwrap( ) ) . unwrap( )
129+ == "Codewars"
121130 ) ;
122131 }
123132
124133 #[ test]
125134 fn multiply_two_numbers ( ) {
126135 // Multiply two numbers
127- assert ! ( brain_luck( ",>,<[>[->+>+<<]>>[-<<+>>]<<<-]>>." , vec![ 8 , 9 ] ) == vec![ 72 ] ) ;
136+ assert ! ( brain_luck( ",>,<[>[->+>+<<]>>[-<<+>>]<<<-]>>." , vec![ 8 , 9 ] ) . unwrap( ) == vec![ 72 ] ) ;
137+ }
138+
139+ #[ test]
140+ fn unexpected_char_in_code ( ) {
141+ // Unexpected character in code
142+ assert ! ( matches!(
143+ brain_luck( ",[.[-]a,]" , ez_vec( "Hello" , 0 ) ) ,
144+ Err ( BrainLuckError :: UnexpectedCharInCode ( 'a' ) )
145+ ) ) ;
146+ }
147+
148+ #[ test]
149+ fn unbalanced_brackets ( ) {
150+ // Unbalanced brackets
151+ assert ! (
152+ brain_luck( "[.[-]," , ez_vec( "Hello" , 0 ) ) == Err ( BrainLuckError :: UnbalancedBrackets )
153+ ) ;
154+ }
155+
156+ #[ test]
157+ fn unexpected_end_of_input ( ) {
158+ // Unexpected end of input
159+ assert ! ( matches!(
160+ brain_luck( ",[.[-],]" , vec![ 72 , 101 , 108 , 108 ] ) ,
161+ Err ( BrainLuckError :: UnexpectedEndOfInput )
162+ ) ) ;
163+ }
164+
165+ #[ test]
166+ fn add_two_numbers ( ) {
167+ // Add two numbers
168+ assert ! ( brain_luck( ",>,<[->+<]>." , vec![ 5 , 10 ] ) . unwrap( ) == vec![ 15 ] ) ;
169+ }
170+
171+ #[ test]
172+ fn subtract_two_numbers ( ) {
173+ // Subtract two numbers
174+ assert ! ( brain_luck( ">,>,[<[->]<]>>[[<+>-]>>]<<<." , vec![ 10 , 5 ] ) . unwrap( ) == vec![ 5 ] ) ;
175+ }
176+
177+ #[ test]
178+ fn matching_closing_bracket_ptr_balanced ( ) {
179+ // Balanced brackets
180+ let code = "++[--[++]]" ;
181+ let insts = code. chars ( ) . collect :: < Vec < _ > > ( ) ;
182+ assert ! ( matching_closing_bracket_ptr( & insts, 2 , Bracket :: LBracket ) . unwrap( ) == 9 ) ;
183+ assert ! ( matching_closing_bracket_ptr( & insts, 9 , Bracket :: RBracket ) . unwrap( ) == 2 ) ;
184+ }
185+
186+ #[ test]
187+ fn matching_closing_bracket_ptr_unbalanced_left ( ) {
188+ // Unbalanced brackets (left)
189+ let code = "++[--[++]" ;
190+ let insts = code. chars ( ) . collect :: < Vec < _ > > ( ) ;
191+ assert ! ( matches!(
192+ matching_closing_bracket_ptr( & insts, 2 , Bracket :: LBracket ) ,
193+ Err ( BrainLuckError :: UnbalancedBrackets )
194+ ) ) ;
195+ }
196+
197+ #[ test]
198+ fn matching_closing_bracket_ptr_unbalanced_right ( ) {
199+ // Unbalanced brackets (right)
200+ let code = "++[--[++]]]" ;
201+ let insts = code. chars ( ) . collect :: < Vec < _ > > ( ) ;
202+ assert ! ( matches!(
203+ matching_closing_bracket_ptr( & insts, 10 , Bracket :: RBracket ) ,
204+ Err ( BrainLuckError :: UnbalancedBrackets )
205+ ) ) ;
128206 }
129207
130208 // Takes a static string and a terminating byte and returns an owned Vec<u8> for convenience
0 commit comments