├── .gitignore ├── 1 - Lecture ├── 220327 - Make Interpreter Rust, Week 0.pdf ├── 220327 - Make Interpreter Rust, Week 1.pdf └── 220410 - Make Interpreter Rust, Week 2.pdf ├── 2 - Code ├── 220522 - Make Interpreter Rust, Week 3 │ ├── Cargo.toml │ └── src │ │ ├── bin │ │ └── main.rs │ │ ├── lexer │ │ ├── lexer.rs │ │ └── mod.rs │ │ ├── lib.rs │ │ └── token │ │ ├── mod.rs │ │ └── token.rs └── 220605 - Make Interpreter Rust, Week 4 │ ├── Cargo.toml │ └── src │ ├── bin │ └── main.rs │ ├── lexer │ ├── lexer.rs │ └── mod.rs │ ├── lib.rs │ └── token │ ├── mod.rs │ └── token.rs ├── 3 - Assignment ├── 220327 - Make Interpreter Rust, Week 1 │ ├── README.md │ ├── prob1 │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src │ │ │ └── lib.rs │ ├── prob2 │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src │ │ │ └── lib.rs │ └── prob3 │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src │ │ └── lib.rs ├── 220410 - Make Interpreter Rust, Week 2 │ ├── README.md │ ├── prob1 │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src │ │ │ └── lib.rs │ ├── prob2 │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src │ │ │ └── lib.rs │ ├── prob3 │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src │ │ │ └── lib.rs │ └── prob4 │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src │ │ ├── lib.rs │ │ └── linked_list.rs └── 220605 - Make Interpreter Rust, Week 4 │ ├── README.md │ ├── prob1 │ ├── Cargo.toml │ ├── README.md │ └── src │ │ ├── bin │ │ └── main.rs │ │ ├── lexer │ │ ├── lexer.rs │ │ └── mod.rs │ │ ├── lib.rs │ │ └── token │ │ ├── mod.rs │ │ └── token.rs │ └── prob2 │ ├── Cargo.toml │ ├── README.md │ └── src │ ├── bin │ └── main.rs │ ├── lexer │ ├── lexer.rs │ └── mod.rs │ ├── lib.rs │ └── token │ ├── mod.rs │ └── token.rs ├── 4 - Solution ├── 220327 - Make Interpreter Rust, Week 1 │ ├── README.md │ ├── prob1 │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src │ │ │ └── lib.rs │ ├── prob2 │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src │ │ │ └── lib.rs │ └── prob3 │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src │ │ └── lib.rs └── 220410 - Make Interpreter Rust, Week 2 │ ├── README.md │ ├── prob1 │ ├── Cargo.toml │ ├── README.md │ └── src │ │ └── lib.rs │ ├── prob2 │ ├── Cargo.toml │ ├── README.md │ └── src │ │ └── lib.rs │ ├── prob3 │ ├── Cargo.toml │ ├── README.md │ └── src │ │ └── lib.rs │ └── prob4 │ ├── Cargo.toml │ ├── README.md │ └── src │ ├── lib.rs │ └── linked_list.rs ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | **/target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | -------------------------------------------------------------------------------- /1 - Lecture/220327 - Make Interpreter Rust, Week 0.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/utilForever/2022-Make-Interpreter-Rust/98c99131bd4a090592a9011bf56c443abf2fa953/1 - Lecture/220327 - Make Interpreter Rust, Week 0.pdf -------------------------------------------------------------------------------- /1 - Lecture/220327 - Make Interpreter Rust, Week 1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/utilForever/2022-Make-Interpreter-Rust/98c99131bd4a090592a9011bf56c443abf2fa953/1 - Lecture/220327 - Make Interpreter Rust, Week 1.pdf -------------------------------------------------------------------------------- /1 - Lecture/220410 - Make Interpreter Rust, Week 2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/utilForever/2022-Make-Interpreter-Rust/98c99131bd4a090592a9011bf56c443abf2fa953/1 - Lecture/220410 - Make Interpreter Rust, Week 2.pdf -------------------------------------------------------------------------------- /2 - Code/220522 - Make Interpreter Rust, Week 3/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "monkey" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["Chris Ohk "] 6 | 7 | [dependencies] 8 | rustyline = { version = "9.1.2", optional = true } 9 | 10 | [[bin]] 11 | name = "monkey" 12 | path = "src/bin/main.rs" 13 | required-features = ["binaries"] 14 | 15 | [features] 16 | binaries = ["rustyline"] 17 | -------------------------------------------------------------------------------- /2 - Code/220522 - Make Interpreter Rust, Week 3/src/bin/main.rs: -------------------------------------------------------------------------------- 1 | extern crate monkey; 2 | extern crate rustyline; 3 | 4 | use monkey::lexer::lexer::Lexer; 5 | use monkey::token::token::Token; 6 | use rustyline::error::ReadlineError; 7 | use rustyline::Editor; 8 | 9 | fn main() { 10 | let mut rl = Editor::<()>::new(); 11 | 12 | println!("Hello! This is the Monkey programming language!"); 13 | println!("Feel free to type in commands\n"); 14 | 15 | loop { 16 | match rl.readline(">> ") { 17 | Ok(line) => { 18 | rl.add_history_entry(&line); 19 | 20 | let mut lexer = Lexer::new(&line); 21 | 22 | loop { 23 | let tok = lexer.next_token(); 24 | if tok == Token::Eof { 25 | break; 26 | } 27 | 28 | println!("{:?}", tok); 29 | } 30 | } 31 | Err(ReadlineError::Interrupted) => { 32 | println!("\nBye :)"); 33 | break; 34 | } 35 | Err(ReadlineError::Eof) => { 36 | println!(); 37 | break; 38 | } 39 | Err(err) => { 40 | println!("Error: {:?}", err); 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /2 - Code/220522 - Make Interpreter Rust, Week 3/src/lexer/lexer.rs: -------------------------------------------------------------------------------- 1 | use crate::token::token::Token; 2 | 3 | pub struct Lexer<'a> { 4 | input: &'a str, 5 | position: usize, 6 | read_position: usize, 7 | ch: u8, 8 | } 9 | 10 | impl<'a> Lexer<'a> { 11 | pub fn new(input: &'a str) -> Self { 12 | let mut lexer = Lexer { 13 | input, 14 | position: 0, 15 | read_position: 0, 16 | ch: 0, 17 | }; 18 | 19 | lexer.read_char(); 20 | lexer 21 | } 22 | 23 | fn read_char(&mut self) { 24 | if self.read_position >= self.input.len() { 25 | self.ch = 0; 26 | } else { 27 | self.ch = self.input.as_bytes()[self.read_position]; 28 | } 29 | 30 | self.position = self.read_position; 31 | self.read_position += 1; 32 | } 33 | 34 | pub fn next_token(&mut self) -> Token { 35 | let tok = match self.ch { 36 | b'=' => Token::Assign, 37 | b';' => Token::Semicolon, 38 | b'(' => Token::Lparen, 39 | b')' => Token::Rparen, 40 | b',' => Token::Comma, 41 | b'+' => Token::Plus, 42 | b'{' => Token::Lbrace, 43 | b'}' => Token::Rbrace, 44 | 0 => Token::Eof, 45 | _ => Token::Illegal, 46 | }; 47 | 48 | self.read_char(); 49 | 50 | tok 51 | } 52 | } 53 | 54 | #[cfg(test)] 55 | mod tests { 56 | use crate::lexer::lexer::Lexer; 57 | use crate::token::token::Token; 58 | 59 | #[test] 60 | fn test_next_token() { 61 | let input = r#"=+(){},;"#; 62 | let tests = vec![ 63 | Token::Assign, 64 | Token::Plus, 65 | Token::Lparen, 66 | Token::Rparen, 67 | Token::Lbrace, 68 | Token::Rbrace, 69 | Token::Comma, 70 | Token::Semicolon, 71 | Token::Eof, 72 | ]; 73 | 74 | let mut lexer = Lexer::new(input); 75 | 76 | for expect in tests { 77 | let tok = lexer.next_token(); 78 | assert_eq!(expect, tok); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /2 - Code/220522 - Make Interpreter Rust, Week 3/src/lexer/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod lexer; 2 | -------------------------------------------------------------------------------- /2 - Code/220522 - Make Interpreter Rust, Week 3/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod lexer; 2 | pub mod token; 3 | -------------------------------------------------------------------------------- /2 - Code/220522 - Make Interpreter Rust, Week 3/src/token/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod token; 2 | -------------------------------------------------------------------------------- /2 - Code/220522 - Make Interpreter Rust, Week 3/src/token/token.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, PartialEq)] 2 | pub enum Token { 3 | Illegal, 4 | Eof, 5 | 6 | // Identifiers + Literals 7 | Ident, 8 | Int, 9 | 10 | // Operators 11 | Assign, 12 | Plus, 13 | 14 | // Delimiters 15 | Comma, 16 | Semicolon, 17 | Lparen, 18 | Rparen, 19 | Lbrace, 20 | Rbrace, 21 | 22 | // Reserved Keywords 23 | Function, 24 | Let, 25 | } 26 | -------------------------------------------------------------------------------- /2 - Code/220605 - Make Interpreter Rust, Week 4/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "monkey" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["Chris Ohk "] 6 | 7 | [dependencies] 8 | rustyline = { version = "9.1.2", optional = true } 9 | 10 | [[bin]] 11 | name = "monkey" 12 | path = "src/bin/main.rs" 13 | required-features = ["binaries"] 14 | 15 | [features] 16 | binaries = ["rustyline"] 17 | -------------------------------------------------------------------------------- /2 - Code/220605 - Make Interpreter Rust, Week 4/src/bin/main.rs: -------------------------------------------------------------------------------- 1 | extern crate monkey; 2 | extern crate rustyline; 3 | 4 | use monkey::lexer::lexer::Lexer; 5 | use monkey::token::token::Token; 6 | use rustyline::error::ReadlineError; 7 | use rustyline::Editor; 8 | 9 | fn main() { 10 | let mut rl = Editor::<()>::new(); 11 | 12 | println!("Hello! This is the Monkey programming language!"); 13 | println!("Feel free to type in commands\n"); 14 | 15 | loop { 16 | match rl.readline(">> ") { 17 | Ok(line) => { 18 | rl.add_history_entry(&line); 19 | 20 | let mut lexer = Lexer::new(&line); 21 | 22 | loop { 23 | let tok = lexer.next_token(); 24 | if tok == Token::Eof { 25 | break; 26 | } 27 | 28 | println!("{:?}", tok); 29 | } 30 | } 31 | Err(ReadlineError::Interrupted) => { 32 | println!("\nBye :)"); 33 | break; 34 | } 35 | Err(ReadlineError::Eof) => { 36 | println!(); 37 | break; 38 | } 39 | Err(err) => { 40 | println!("Error: {:?}", err); 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /2 - Code/220605 - Make Interpreter Rust, Week 4/src/lexer/lexer.rs: -------------------------------------------------------------------------------- 1 | use crate::token::token::Token; 2 | 3 | pub struct Lexer<'a> { 4 | input: &'a str, 5 | position: usize, 6 | read_position: usize, 7 | ch: u8, 8 | } 9 | 10 | impl<'a> Lexer<'a> { 11 | pub fn new(input: &'a str) -> Self { 12 | let mut lexer = Lexer { 13 | input, 14 | position: 0, 15 | read_position: 0, 16 | ch: 0, 17 | }; 18 | 19 | lexer.read_char(); 20 | lexer 21 | } 22 | 23 | fn read_char(&mut self) { 24 | if self.read_position >= self.input.len() { 25 | self.ch = 0; 26 | } else { 27 | self.ch = self.input.as_bytes()[self.read_position]; 28 | } 29 | 30 | self.position = self.read_position; 31 | self.read_position += 1; 32 | } 33 | 34 | pub fn next_token(&mut self) -> Token { 35 | self.skip_whitespace(); 36 | 37 | let tok = match self.ch { 38 | b'=' => { 39 | if self.peek_char() == b'=' { 40 | self.read_char(); 41 | Token::Equal 42 | } else { 43 | Token::Assign 44 | } 45 | } 46 | b'+' => Token::Plus, 47 | b'-' => Token::Minus, 48 | b'!' => { 49 | if self.peek_char() == b'=' { 50 | self.read_char(); 51 | Token::NotEqual 52 | } else { 53 | Token::Bang 54 | } 55 | } 56 | b'*' => Token::Asterisk, 57 | b'/' => Token::Slash, 58 | b'<' => Token::LessThan, 59 | b'>' => Token::GreaterThan, 60 | b',' => Token::Comma, 61 | b';' => Token::Semicolon, 62 | b'(' => Token::Lparen, 63 | b')' => Token::Rparen, 64 | b'{' => Token::Lbrace, 65 | b'}' => Token::Rbrace, 66 | b'a'..=b'z' | b'A'..=b'Z' | b'_' => { 67 | return self.read_identifier(); 68 | } 69 | b'0'..=b'9' => { 70 | return self.read_number(); 71 | } 72 | 0 => Token::Eof, 73 | _ => Token::Illegal, 74 | }; 75 | 76 | self.read_char(); 77 | 78 | tok 79 | } 80 | 81 | fn skip_whitespace(&mut self) { 82 | loop { 83 | match self.ch { 84 | b' ' | b'\t' | b'\n' | b'\r' => { 85 | self.read_char(); 86 | } 87 | _ => { 88 | break; 89 | } 90 | } 91 | } 92 | } 93 | 94 | fn peek_char(&mut self) -> u8 { 95 | if self.read_position >= self.input.len() { 96 | 0 97 | } else { 98 | self.input.as_bytes()[self.read_position] 99 | } 100 | } 101 | 102 | fn read_identifier(&mut self) -> Token { 103 | let position = self.position; 104 | 105 | loop { 106 | match self.ch { 107 | b'a'..=b'z' | b'A'..=b'Z' | b'_' => { 108 | self.read_char(); 109 | } 110 | _ => { 111 | break; 112 | } 113 | } 114 | } 115 | 116 | let literal = &self.input[position..self.position]; 117 | 118 | match literal { 119 | "fn" => Token::Function, 120 | "let" => Token::Let, 121 | "true" => Token::Bool(true), 122 | "false" => Token::Bool(false), 123 | "if" => Token::If, 124 | "else" => Token::Else, 125 | "return" => Token::Return, 126 | _ => Token::Ident(String::from(literal)), 127 | } 128 | } 129 | 130 | fn read_number(&mut self) -> Token { 131 | let position = self.position; 132 | 133 | loop { 134 | match self.ch { 135 | b'0'..=b'9' => { 136 | self.read_char(); 137 | } 138 | _ => { 139 | break; 140 | } 141 | } 142 | } 143 | 144 | let literal = &self.input[position..self.position]; 145 | Token::Int(literal.parse::().unwrap()) 146 | } 147 | } 148 | 149 | #[cfg(test)] 150 | mod tests { 151 | use crate::lexer::lexer::Lexer; 152 | use crate::token::token::Token; 153 | 154 | #[test] 155 | fn test_next_token() { 156 | let input = r#"let five = 5; 157 | let ten = 10; 158 | 159 | let add = fn(x, y) { 160 | x + y; 161 | }; 162 | 163 | let result = add(five, ten); 164 | !-/*5; 165 | 5 < 10 > 5; 166 | 167 | if (5 < 10) { 168 | return true; 169 | } else { 170 | return false; 171 | } 172 | 173 | 10 == 10; 174 | 10 != 9; 175 | "#; 176 | let tests = vec![ 177 | Token::Let, 178 | Token::Ident(String::from("five")), 179 | Token::Assign, 180 | Token::Int(5), 181 | Token::Semicolon, 182 | Token::Let, 183 | Token::Ident(String::from("ten")), 184 | Token::Assign, 185 | Token::Int(10), 186 | Token::Semicolon, 187 | Token::Let, 188 | Token::Ident(String::from("add")), 189 | Token::Assign, 190 | Token::Function, 191 | Token::Lparen, 192 | Token::Ident(String::from("x")), 193 | Token::Comma, 194 | Token::Ident(String::from("y")), 195 | Token::Rparen, 196 | Token::Lbrace, 197 | Token::Ident(String::from("x")), 198 | Token::Plus, 199 | Token::Ident(String::from("y")), 200 | Token::Semicolon, 201 | Token::Rbrace, 202 | Token::Semicolon, 203 | Token::Let, 204 | Token::Ident(String::from("result")), 205 | Token::Assign, 206 | Token::Ident(String::from("add")), 207 | Token::Lparen, 208 | Token::Ident(String::from("five")), 209 | Token::Comma, 210 | Token::Ident(String::from("ten")), 211 | Token::Rparen, 212 | Token::Semicolon, 213 | Token::Bang, 214 | Token::Minus, 215 | Token::Slash, 216 | Token::Asterisk, 217 | Token::Int(5), 218 | Token::Semicolon, 219 | Token::Int(5), 220 | Token::LessThan, 221 | Token::Int(10), 222 | Token::GreaterThan, 223 | Token::Int(5), 224 | Token::Semicolon, 225 | Token::If, 226 | Token::Lparen, 227 | Token::Int(5), 228 | Token::LessThan, 229 | Token::Int(10), 230 | Token::Rparen, 231 | Token::Lbrace, 232 | Token::Return, 233 | Token::Bool(true), 234 | Token::Semicolon, 235 | Token::Rbrace, 236 | Token::Else, 237 | Token::Lbrace, 238 | Token::Return, 239 | Token::Bool(false), 240 | Token::Semicolon, 241 | Token::Rbrace, 242 | Token::Int(10), 243 | Token::Equal, 244 | Token::Int(10), 245 | Token::Semicolon, 246 | Token::Int(10), 247 | Token::NotEqual, 248 | Token::Int(9), 249 | Token::Semicolon, 250 | Token::Eof, 251 | ]; 252 | 253 | let mut lexer = Lexer::new(input); 254 | 255 | for expect in tests { 256 | let tok = lexer.next_token(); 257 | assert_eq!(expect, tok); 258 | } 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /2 - Code/220605 - Make Interpreter Rust, Week 4/src/lexer/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod lexer; 2 | -------------------------------------------------------------------------------- /2 - Code/220605 - Make Interpreter Rust, Week 4/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod lexer; 2 | pub mod token; 3 | -------------------------------------------------------------------------------- /2 - Code/220605 - Make Interpreter Rust, Week 4/src/token/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod token; 2 | -------------------------------------------------------------------------------- /2 - Code/220605 - Make Interpreter Rust, Week 4/src/token/token.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, PartialEq)] 2 | pub enum Token { 3 | Illegal, 4 | Eof, 5 | 6 | // Identifiers + Literals 7 | Ident(String), 8 | Int(i64), 9 | Bool(bool), 10 | 11 | // Operators 12 | Assign, 13 | Plus, 14 | Minus, 15 | Bang, 16 | Asterisk, 17 | Slash, 18 | 19 | Equal, 20 | NotEqual, 21 | LessThan, 22 | GreaterThan, 23 | 24 | // Delimiters 25 | Comma, 26 | Semicolon, 27 | Lparen, 28 | Rparen, 29 | Lbrace, 30 | Rbrace, 31 | 32 | // Reserved Keywords 33 | Function, 34 | Let, 35 | If, 36 | Else, 37 | Return, 38 | } 39 | -------------------------------------------------------------------------------- /3 - Assignment/220327 - Make Interpreter Rust, Week 1/README.md: -------------------------------------------------------------------------------- 1 | # Assignment 1 2 | 3 | You should pass all tests by running `cargo test`. 4 | 5 | 1. [Role Playing Game](./prob1) 6 | 7 | 2. [RPN Calculator](./prob2) 8 | 9 | 3. [Bowling](./prob3) -------------------------------------------------------------------------------- /3 - Assignment/220327 - Make Interpreter Rust, Week 1/prob1/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "prob1" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /3 - Assignment/220327 - Make Interpreter Rust, Week 1/prob1/README.md: -------------------------------------------------------------------------------- 1 | # Instructions 2 | 3 | You're working on implementing a role-playing game. The player's character is represented by the following: 4 | 5 | ```rust 6 | pub struct Player { 7 | health: u32, 8 | mana: Option, 9 | level: u32, 10 | } 11 | ``` 12 | 13 | Players in this game must reach level 10 before they unlock a mana pool so that they can start casting spells. Before that point, the Player's mana is `None`. 14 | 15 | You're working on two pieces of functionality in this game, the revive mechanic and the spell casting mechanic. 16 | 17 | The `revive` method should check to ensure that the Player is indeed dead (their health has reached 0), and if they are, the method should return a new Player instance with 100 health. 18 | If the Player's level is 10 or above, they should also be revived with 100 mana. 19 | If the Player's level is below 10, their mana should be `None`. The `revive` method should preserve the Player's level. 20 | 21 | ```rust 22 | let dead_player = Player { health: 0, mana: None, level: 2 }; 23 | dead_player.revive() 24 | // Returns Player { health: 100, mana: None, level: 2 } 25 | ``` 26 | 27 | If the `revive` method is called on a Player whose health is 1 or above, then the method should return `None`. 28 | 29 | ```rust 30 | let alive_player = Player { health: 1, mana: Some(15), level: 11 }; 31 | alive_player.revive() 32 | // Returns None 33 | ``` 34 | 35 | The `cast_spell` method takes a mutable reference to the Player as well as a `mana_cost` parameter indicating how much mana the spell costs. It returns the amount of damage that the cast spell performs, which will always be two times the mana cost of the spell if the spell is successfully cast. 36 | 37 | - If the player does not have access to a mana pool, attempting to cast the spell must decrease their health by the mana cost of the spell. The damage returned must be 0. 38 | 39 | ```rust 40 | let not_a_wizard_yet = Player { health: 79, mana: None, level: 9 }; 41 | assert_eq!(not_a_wizard_yet.cast_spell(5), 0) 42 | assert_eq!(not_a_wizard_yet.health, 74); 43 | assert_eq!(not_a_wizard_yet.mana, None); 44 | ``` 45 | 46 | - If the player has a mana pool but insufficient mana, the method should not affect the pool, but instead return 0 47 | 48 | ```rust 49 | let low_mana_wizard = Player { health: 93, mana: Some(3), level: 12 }; 50 | assert_eq!(low_mana_wizard.cast_spell(10), 0); 51 | assert_eq!(low_mana_wizard.health, 93); 52 | assert_eq!(low_mana_wizard.mana, Some(3)); 53 | ``` 54 | 55 | - Otherwise, the `mana_cost` should be deducted from the Player's mana pool and the appropriate amount of damage should be returned. 56 | 57 | ```rust 58 | let wizard = Player { health: 123, mana: Some(30), level: 18 }; 59 | assert_eq!(wizard.cast_spell(10), 20); 60 | assert_eq!(wizard.health, 123); 61 | assert_eq!(wizard.mana, Some(20)); 62 | ``` 63 | 64 | Have fun! 65 | -------------------------------------------------------------------------------- /3 - Assignment/220327 - Make Interpreter Rust, Week 1/prob1/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub struct Player { 2 | pub health: u32, 3 | pub mana: Option, 4 | pub level: u32, 5 | } 6 | 7 | impl Player { 8 | pub fn revive(&self) -> Option { 9 | unimplemented!("Revive this player") 10 | } 11 | 12 | pub fn cast_spell(&mut self, mana_cost: u32) -> u32 { 13 | unimplemented!("Cast a spell of cost {}", mana_cost) 14 | } 15 | } 16 | 17 | #[test] 18 | fn test_reviving_dead_player() { 19 | let dead_player = Player { 20 | health: 0, 21 | mana: Some(0), 22 | level: 34, 23 | }; 24 | let revived_player = dead_player 25 | .revive() 26 | .expect("reviving a dead player must return Some(player)"); 27 | assert_eq!(revived_player.health, 100); 28 | assert_eq!(revived_player.mana, Some(100)); 29 | assert_eq!(revived_player.level, dead_player.level); 30 | } 31 | 32 | #[test] 33 | fn test_reviving_dead_level9_player() { 34 | let dead_player = Player { 35 | health: 0, 36 | mana: None, 37 | level: 9, 38 | }; 39 | let revived_player = dead_player 40 | .revive() 41 | .expect("reviving a dead player must return Some(player)"); 42 | assert_eq!(revived_player.health, 100); 43 | assert_eq!(revived_player.mana, None); 44 | assert_eq!(revived_player.level, dead_player.level); 45 | } 46 | 47 | #[test] 48 | fn test_reviving_dead_level10_player() { 49 | let dead_player = Player { 50 | health: 0, 51 | mana: Some(0), 52 | level: 10, 53 | }; 54 | let revived_player = dead_player 55 | .revive() 56 | .expect("reviving a dead player must return Some(player)"); 57 | assert_eq!(revived_player.health, 100); 58 | assert_eq!(revived_player.mana, Some(100)); 59 | assert_eq!(revived_player.level, dead_player.level); 60 | } 61 | 62 | #[test] 63 | fn test_reviving_alive_player() { 64 | let alive_player = Player { 65 | health: 1, 66 | mana: None, 67 | level: 8, 68 | }; 69 | assert!(alive_player.revive().is_none()); 70 | } 71 | 72 | #[test] 73 | fn test_cast_spell_with_enough_mana() { 74 | const HEALTH: u32 = 99; 75 | const MANA: u32 = 100; 76 | const LEVEL: u32 = 100; 77 | const MANA_COST: u32 = 3; 78 | 79 | let mut accomplished_wizard = Player { 80 | health: HEALTH, 81 | mana: Some(MANA), 82 | level: LEVEL, 83 | }; 84 | 85 | assert_eq!(accomplished_wizard.cast_spell(MANA_COST), MANA_COST * 2); 86 | assert_eq!(accomplished_wizard.health, HEALTH); 87 | assert_eq!(accomplished_wizard.mana, Some(MANA - MANA_COST)); 88 | assert_eq!(accomplished_wizard.level, LEVEL); 89 | } 90 | 91 | #[test] 92 | fn test_cast_spell_with_insufficient_mana() { 93 | let mut no_mana_wizard = Player { 94 | health: 56, 95 | mana: Some(2), 96 | level: 22, 97 | }; 98 | 99 | let clone = Player { ..no_mana_wizard }; 100 | 101 | assert_eq!(no_mana_wizard.cast_spell(3), 0); 102 | assert_eq!(no_mana_wizard.health, clone.health); 103 | assert_eq!(no_mana_wizard.mana, clone.mana); 104 | assert_eq!(no_mana_wizard.level, clone.level); 105 | } 106 | 107 | #[test] 108 | fn test_cast_spell_with_no_mana_pool() { 109 | const MANA_COST: u32 = 10; 110 | 111 | let mut underleveled_player = Player { 112 | health: 87, 113 | mana: None, 114 | level: 6, 115 | }; 116 | 117 | let clone = Player { 118 | ..underleveled_player 119 | }; 120 | 121 | assert_eq!(underleveled_player.cast_spell(MANA_COST), 0); 122 | assert_eq!(underleveled_player.health, clone.health - MANA_COST); 123 | assert_eq!(underleveled_player.mana, clone.mana); 124 | assert_eq!(underleveled_player.level, clone.level); 125 | } 126 | 127 | #[test] 128 | fn test_cast_large_spell_with_no_mana_pool() { 129 | const MANA_COST: u32 = 30; 130 | 131 | let mut underleveled_player = Player { 132 | health: 20, 133 | mana: None, 134 | level: 6, 135 | }; 136 | 137 | assert_eq!(underleveled_player.cast_spell(MANA_COST), 0); 138 | assert_eq!(underleveled_player.health, 0); 139 | assert_eq!(underleveled_player.mana, None); 140 | assert_eq!(underleveled_player.level, 6); 141 | } 142 | -------------------------------------------------------------------------------- /3 - Assignment/220327 - Make Interpreter Rust, Week 1/prob2/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "prob2" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /3 - Assignment/220327 - Make Interpreter Rust, Week 1/prob2/README.md: -------------------------------------------------------------------------------- 1 | # Instructions 2 | 3 | ## 1. Overview 4 | 5 | [Reverse Polish notation](https://en.wikipedia.org/wiki/Reverse_Polish_notation) (RPN) is a way of writing mathematical expressions. 6 | Unlike in traditional infix notation, RPN operators *follow* their operands. 7 | For example, instead of writing: 8 | 9 | ``` 10 | 2 + 2 11 | ``` 12 | 13 | you would write: 14 | 15 | ``` 16 | 2 2 + 17 | ``` 18 | 19 | The major benefit of Reverse Polish notation is that it is much simpler to parse than infix notation. 20 | RPN eliminates the need for order of operations or parentheses in complex expressions. 21 | For example: 22 | 23 | ``` 24 | (4 + 8) / (7 - 5) 25 | ``` 26 | 27 | can be written as 28 | 29 | ``` 30 | 4 8 + 7 5 - / 31 | ``` 32 | 33 | In both cases, the expression evaluates to 6. 34 | 35 | ## 2. Example 36 | 37 | Let's manually evaluate that complex expression. 38 | As we learned in the introduction, evaluation of RPN requires a stack. 39 | This stack is used to hold numeric values that the operators operate on. 40 | We start our calculator with an empty stack and then evaluate each element one at a time. 41 | 42 | First, we encounter a `4`, 43 | so we push it onto our freshly created stack. 44 | 45 | ``` 46 | 4 47 | ``` 48 | 49 | Next, we encounter an `8`. 50 | We also push that onto the stack. 51 | 52 | ``` 53 | 4 8 54 | ``` 55 | 56 | Now, we encounter a `+`. 57 | We pop off the two topmost values (4 and 8), 58 | add them together, 59 | and push the sum back onto the stack. 60 | 61 | ``` 62 | 12 63 | ``` 64 | 65 | We do something similar for `7`, `5`, and `-`: 66 | 67 | ``` 68 | 12 7 69 | 12 7 5 70 | 12 2 71 | ``` 72 | 73 | Now we encounter a `/`. 74 | Even though we last encountered a `-`, 75 | there are two elements on the stack. 76 | We pop off the two elements, 77 | divide them, 78 | and push the result back onto the stack. 79 | 80 | ``` 81 | 6 82 | ``` 83 | 84 | Finally, since there is exactly one element on the stack, 85 | we can say the expression evaluated to 6. 86 | 87 | ## 3. Goal 88 | 89 | Your goal is to write a calculator to evaluate a list of inputs ordered by Reverse Polish notation and return the final element on the stack. 90 | 91 | If there is not exactly one element in the stack at the end, return `None`. 92 | 93 | If there is an operator with too few operands (such as the input `2 +`), return `None`. 94 | 95 | You are given the following enum and stubbed function as a starting point. 96 | 97 | ```rust 98 | #[derive(Debug)] 99 | pub enum CalculatorInput { 100 | Add, 101 | Subtract, 102 | Multiply, 103 | Divide, 104 | Value(i32), 105 | } 106 | 107 | pub fn evaluate(inputs: &[CalculatorInput]) -> Option { 108 | unimplemented!( 109 | "Given the inputs: {:?}, evaluate them as though they were a Reverse Polish notation expression", 110 | inputs, 111 | ); 112 | } 113 | ``` -------------------------------------------------------------------------------- /3 - Assignment/220327 - Make Interpreter Rust, Week 1/prob2/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | pub enum CalculatorInput { 3 | Add, 4 | Subtract, 5 | Multiply, 6 | Divide, 7 | Value(i32), 8 | } 9 | 10 | pub fn evaluate(inputs: &[CalculatorInput]) -> Option { 11 | unimplemented!( 12 | "Given the inputs: {:?}, evaluate them as though they were a Reverse Polish notation expression", 13 | inputs, 14 | ); 15 | } 16 | 17 | #[cfg(test)] 18 | fn calculator_input(s: &str) -> Vec { 19 | s.split_whitespace() 20 | .map(|s| match s { 21 | "+" => CalculatorInput::Add, 22 | "-" => CalculatorInput::Subtract, 23 | "*" => CalculatorInput::Multiply, 24 | "/" => CalculatorInput::Divide, 25 | n => CalculatorInput::Value(n.parse().unwrap()), 26 | }) 27 | .collect() 28 | } 29 | 30 | #[test] 31 | fn test_empty_input_returns_none() { 32 | let input = calculator_input(""); 33 | assert_eq!(evaluate(&input), None); 34 | } 35 | 36 | #[test] 37 | fn test_simple_value() { 38 | let input = calculator_input("10"); 39 | assert_eq!(evaluate(&input), Some(10)); 40 | } 41 | 42 | #[test] 43 | fn test_simple_addition() { 44 | let input = calculator_input("2 2 +"); 45 | assert_eq!(evaluate(&input), Some(4)); 46 | } 47 | 48 | #[test] 49 | fn test_simple_subtraction() { 50 | let input = calculator_input("7 11 -"); 51 | assert_eq!(evaluate(&input), Some(-4)); 52 | } 53 | 54 | #[test] 55 | fn test_simple_multiplication() { 56 | let input = calculator_input("6 9 *"); 57 | assert_eq!(evaluate(&input), Some(54)); 58 | } 59 | 60 | #[test] 61 | fn test_simple_division() { 62 | let input = calculator_input("57 19 /"); 63 | assert_eq!(evaluate(&input), Some(3)); 64 | } 65 | 66 | #[test] 67 | fn test_complex_operation() { 68 | let input = calculator_input("4 8 + 7 5 - /"); 69 | assert_eq!(evaluate(&input), Some(6)); 70 | } 71 | 72 | #[test] 73 | fn test_too_few_operands_returns_none() { 74 | let input = calculator_input("2 +"); 75 | assert_eq!(evaluate(&input), None); 76 | } 77 | 78 | #[test] 79 | fn test_too_many_operands_returns_none() { 80 | let input = calculator_input("2 2"); 81 | assert_eq!(evaluate(&input), None); 82 | } 83 | 84 | #[test] 85 | fn test_zero_operands_returns_none() { 86 | let input = calculator_input("+"); 87 | assert_eq!(evaluate(&input), None); 88 | } 89 | 90 | #[test] 91 | fn test_intermediate_error_returns_none() { 92 | let input = calculator_input("+ 2 2 *"); 93 | assert_eq!(evaluate(&input), None); 94 | } 95 | -------------------------------------------------------------------------------- /3 - Assignment/220327 - Make Interpreter Rust, Week 1/prob3/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "prob3" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /3 - Assignment/220327 - Make Interpreter Rust, Week 1/prob3/README.md: -------------------------------------------------------------------------------- 1 | # Instructions 2 | 3 | Score a bowling game. 4 | 5 | Bowling is a game where players roll a heavy ball to knock down pins 6 | arranged in a triangle. Write code to keep track of the score 7 | of a game of bowling. 8 | 9 | ## Scoring Bowling 10 | 11 | The game consists of 10 frames. A frame is composed of one or two ball 12 | throws with 10 pins standing at frame initialization. There are three 13 | cases for the tabulation of a frame. 14 | 15 | * An open frame is where a score of less than 10 is recorded for the 16 | frame. In this case the score for the frame is the number of pins 17 | knocked down. 18 | 19 | * A spare is where all ten pins are knocked down by the second 20 | throw. The total value of a spare is 10 plus the number of pins 21 | knocked down in their next throw. 22 | 23 | * A strike is where all ten pins are knocked down by the first 24 | throw. The total value of a strike is 10 plus the number of pins 25 | knocked down in the next two throws. If a strike is immediately 26 | followed by a second strike, then the value of the first strike 27 | cannot be determined until the ball is thrown one more time. 28 | 29 | Here is a three frame example: 30 | 31 | | Frame 1 | Frame 2 | Frame 3 | 32 | | :-------------: |:-------------:| :---------------------:| 33 | | X (strike) | 5/ (spare) | 9 0 (open frame) | 34 | 35 | Frame 1 is (10 + 5 + 5) = 20 36 | 37 | Frame 2 is (5 + 5 + 9) = 19 38 | 39 | Frame 3 is (9 + 0) = 9 40 | 41 | This means the current running total is 48. 42 | 43 | The tenth frame in the game is a special case. If someone throws a 44 | strike or a spare then they get a fill ball. Fill balls exist to 45 | calculate the total of the 10th frame. Scoring a strike or spare on 46 | the fill ball does not give the player more fill balls. The total 47 | value of the 10th frame is the total number of pins knocked down. 48 | 49 | For a tenth frame of X1/ (strike and a spare), the total value is 20. 50 | 51 | For a tenth frame of XXX (three strikes), the total value is 30. 52 | 53 | ## Requirements 54 | 55 | Write code to keep track of the score of a game of bowling. It should 56 | support two operations: 57 | 58 | * `roll(pins : int)` is called each time the player rolls a ball. The 59 | argument is the number of pins knocked down. 60 | * `score() : int` is called only at the very end of the game. It 61 | returns the total score for that game. -------------------------------------------------------------------------------- /3 - Assignment/220327 - Make Interpreter Rust, Week 1/prob3/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, PartialEq)] 2 | pub enum Error { 3 | NotEnoughPinsLeft, 4 | GameComplete, 5 | } 6 | 7 | pub struct BowlingGame {} 8 | 9 | impl BowlingGame { 10 | pub fn new() -> Self { 11 | unimplemented!(); 12 | } 13 | 14 | pub fn roll(&mut self, pins: u16) -> Result<(), Error> { 15 | unimplemented!("Record that {} pins have been scored", pins); 16 | } 17 | 18 | pub fn score(&self) -> Option { 19 | unimplemented!("Return the score if the game is complete, or None if not."); 20 | } 21 | } 22 | 23 | #[test] 24 | fn roll_returns_a_result() { 25 | let mut game = BowlingGame::new(); 26 | assert!(game.roll(0).is_ok()); 27 | } 28 | 29 | #[test] 30 | fn you_cannot_roll_more_than_ten_pins_in_a_single_roll() { 31 | let mut game = BowlingGame::new(); 32 | 33 | assert_eq!(game.roll(11), Err(Error::NotEnoughPinsLeft)); 34 | } 35 | 36 | #[test] 37 | fn a_game_score_is_some_if_ten_frames_have_been_rolled() { 38 | let mut game = BowlingGame::new(); 39 | 40 | for _ in 0..10 { 41 | let _ = game.roll(0); 42 | let _ = game.roll(0); 43 | } 44 | 45 | assert!(game.score().is_some()); 46 | } 47 | 48 | #[test] 49 | fn you_cannot_score_a_game_with_no_rolls() { 50 | let game = BowlingGame::new(); 51 | 52 | assert_eq!(game.score(), None); 53 | } 54 | 55 | #[test] 56 | fn a_game_score_is_none_if_fewer_than_ten_frames_have_been_rolled() { 57 | let mut game = BowlingGame::new(); 58 | 59 | for _ in 0..9 { 60 | let _ = game.roll(0); 61 | let _ = game.roll(0); 62 | } 63 | 64 | assert_eq!(game.score(), None); 65 | } 66 | 67 | #[test] 68 | fn a_roll_is_err_if_the_game_is_done() { 69 | let mut game = BowlingGame::new(); 70 | 71 | for _ in 0..10 { 72 | let _ = game.roll(0); 73 | let _ = game.roll(0); 74 | } 75 | 76 | assert_eq!(game.roll(0), Err(Error::GameComplete)); 77 | } 78 | 79 | #[test] 80 | fn twenty_zero_pin_rolls_scores_zero() { 81 | let mut game = BowlingGame::new(); 82 | 83 | for _ in 0..20 { 84 | let _ = game.roll(0); 85 | } 86 | 87 | assert_eq!(game.score(), Some(0)); 88 | } 89 | 90 | #[test] 91 | fn ten_frames_without_a_strike_or_spare() { 92 | let mut game = BowlingGame::new(); 93 | 94 | for _ in 0..10 { 95 | let _ = game.roll(3); 96 | let _ = game.roll(6); 97 | } 98 | 99 | assert_eq!(game.score(), Some(90)); 100 | } 101 | 102 | #[test] 103 | fn spare_in_the_first_frame_followed_by_zeros() { 104 | let mut game = BowlingGame::new(); 105 | 106 | let _ = game.roll(6); 107 | let _ = game.roll(4); 108 | 109 | for _ in 0..18 { 110 | let _ = game.roll(0); 111 | } 112 | 113 | assert_eq!(game.score(), Some(10)); 114 | } 115 | 116 | #[test] 117 | fn points_scored_in_the_roll_after_a_spare_are_counted_twice_as_a_bonus() { 118 | let mut game = BowlingGame::new(); 119 | 120 | let _ = game.roll(6); 121 | let _ = game.roll(4); 122 | let _ = game.roll(3); 123 | 124 | for _ in 0..17 { 125 | let _ = game.roll(0); 126 | } 127 | 128 | assert_eq!(game.score(), Some(16)); 129 | } 130 | 131 | #[test] 132 | fn consecutive_spares_each_get_a_one_roll_bonus() { 133 | let mut game = BowlingGame::new(); 134 | 135 | let _ = game.roll(5); 136 | let _ = game.roll(5); 137 | let _ = game.roll(3); 138 | let _ = game.roll(7); 139 | let _ = game.roll(4); 140 | 141 | for _ in 0..15 { 142 | let _ = game.roll(0); 143 | } 144 | 145 | assert_eq!(game.score(), Some(31)); 146 | } 147 | 148 | #[test] 149 | fn if_the_last_frame_is_a_spare_you_get_one_extra_roll_that_is_scored_once() { 150 | let mut game = BowlingGame::new(); 151 | 152 | for _ in 0..18 { 153 | let _ = game.roll(0); 154 | } 155 | 156 | let _ = game.roll(5); 157 | let _ = game.roll(5); 158 | let _ = game.roll(7); 159 | 160 | assert_eq!(game.score(), Some(17)); 161 | } 162 | 163 | #[test] 164 | fn a_strike_earns_ten_points_in_a_frame_with_a_single_roll() { 165 | let mut game = BowlingGame::new(); 166 | 167 | let _ = game.roll(10); 168 | 169 | for _ in 0..18 { 170 | let _ = game.roll(0); 171 | } 172 | 173 | assert_eq!(game.score(), Some(10)); 174 | } 175 | 176 | #[test] 177 | fn points_scored_in_the_two_rolls_after_a_strike_are_counted_twice_as_a_bonus() { 178 | let mut game = BowlingGame::new(); 179 | 180 | let _ = game.roll(10); 181 | let _ = game.roll(5); 182 | let _ = game.roll(3); 183 | 184 | for _ in 0..16 { 185 | let _ = game.roll(0); 186 | } 187 | 188 | assert_eq!(game.score(), Some(26)); 189 | } 190 | 191 | #[test] 192 | fn consecutive_strikes_each_get_the_two_roll_bonus() { 193 | let mut game = BowlingGame::new(); 194 | 195 | let _ = game.roll(10); 196 | let _ = game.roll(10); 197 | let _ = game.roll(10); 198 | let _ = game.roll(5); 199 | let _ = game.roll(3); 200 | 201 | for _ in 0..12 { 202 | let _ = game.roll(0); 203 | } 204 | 205 | assert_eq!(game.score(), Some(81)); 206 | } 207 | 208 | #[test] 209 | fn a_strike_in_the_last_frame_earns_a_two_roll_bonus_that_is_counted_once() { 210 | let mut game = BowlingGame::new(); 211 | 212 | for _ in 0..18 { 213 | let _ = game.roll(0); 214 | } 215 | 216 | let _ = game.roll(10); 217 | let _ = game.roll(7); 218 | let _ = game.roll(1); 219 | 220 | assert_eq!(game.score(), Some(18)); 221 | } 222 | 223 | #[test] 224 | fn a_spare_with_the_two_roll_bonus_does_not_get_a_bonus_roll() { 225 | let mut game = BowlingGame::new(); 226 | 227 | for _ in 0..18 { 228 | let _ = game.roll(0); 229 | } 230 | 231 | let _ = game.roll(10); 232 | let _ = game.roll(7); 233 | let _ = game.roll(3); 234 | 235 | assert_eq!(game.score(), Some(20)); 236 | } 237 | 238 | #[test] 239 | fn strikes_with_the_two_roll_bonus_do_not_get_a_bonus_roll() { 240 | let mut game = BowlingGame::new(); 241 | 242 | for _ in 0..18 { 243 | let _ = game.roll(0); 244 | } 245 | 246 | let _ = game.roll(10); 247 | let _ = game.roll(10); 248 | let _ = game.roll(10); 249 | 250 | assert_eq!(game.score(), Some(30)); 251 | } 252 | 253 | #[test] 254 | fn a_strike_with_the_one_roll_bonus_after_a_spare_in_the_last_frame_does_not_get_a_bonus() { 255 | let mut game = BowlingGame::new(); 256 | 257 | for _ in 0..18 { 258 | let _ = game.roll(0); 259 | } 260 | 261 | let _ = game.roll(7); 262 | let _ = game.roll(3); 263 | let _ = game.roll(10); 264 | 265 | assert_eq!(game.score(), Some(20)); 266 | } 267 | 268 | #[test] 269 | fn all_strikes_is_a_perfect_score_of_300() { 270 | let mut game = BowlingGame::new(); 271 | 272 | for _ in 0..12 { 273 | let _ = game.roll(10); 274 | } 275 | 276 | assert_eq!(game.score(), Some(300)); 277 | } 278 | 279 | #[test] 280 | fn you_cannot_roll_more_than_ten_pins_in_a_single_frame() { 281 | let mut game = BowlingGame::new(); 282 | 283 | assert!(game.roll(5).is_ok()); 284 | assert_eq!(game.roll(6), Err(Error::NotEnoughPinsLeft)); 285 | } 286 | 287 | #[test] 288 | fn first_bonus_ball_after_a_final_strike_cannot_score_an_invalid_number_of_pins() { 289 | let mut game = BowlingGame::new(); 290 | 291 | for _ in 0..18 { 292 | let _ = game.roll(0); 293 | } 294 | 295 | let _ = game.roll(10); 296 | 297 | assert_eq!(game.roll(11), Err(Error::NotEnoughPinsLeft)); 298 | } 299 | 300 | #[test] 301 | fn the_two_balls_after_a_final_strike_cannot_score_an_invalid_number_of_pins() { 302 | let mut game = BowlingGame::new(); 303 | 304 | for _ in 0..18 { 305 | let _ = game.roll(0); 306 | } 307 | 308 | let _ = game.roll(10); 309 | 310 | assert!(game.roll(5).is_ok()); 311 | assert_eq!(game.roll(6), Err(Error::NotEnoughPinsLeft)); 312 | } 313 | 314 | #[test] 315 | fn the_two_balls_after_a_final_strike_can_be_a_strike_and_non_strike() { 316 | let mut game = BowlingGame::new(); 317 | 318 | for _ in 0..18 { 319 | let _ = game.roll(0); 320 | } 321 | 322 | let _ = game.roll(10); 323 | 324 | assert!(game.roll(10).is_ok()); 325 | assert!(game.roll(6).is_ok()); 326 | } 327 | 328 | #[test] 329 | fn the_two_balls_after_a_final_strike_cannot_be_a_non_strike_followed_by_a_strike() { 330 | let mut game = BowlingGame::new(); 331 | 332 | for _ in 0..18 { 333 | let _ = game.roll(0); 334 | } 335 | 336 | let _ = game.roll(10); 337 | 338 | assert!(game.roll(6).is_ok()); 339 | assert_eq!(game.roll(10), Err(Error::NotEnoughPinsLeft)); 340 | } 341 | 342 | #[test] 343 | fn second_bonus_ball_after_a_final_strike_cannot_score_an_invalid_number_of_pins_even_if_first_is_strike( 344 | ) { 345 | let mut game = BowlingGame::new(); 346 | 347 | for _ in 0..18 { 348 | let _ = game.roll(0); 349 | } 350 | 351 | let _ = game.roll(10); 352 | 353 | assert!(game.roll(10).is_ok()); 354 | assert_eq!(game.roll(11), Err(Error::NotEnoughPinsLeft)); 355 | } 356 | 357 | #[test] 358 | fn if_the_last_frame_is_a_strike_you_cannot_score_before_the_extra_rolls_are_taken() { 359 | let mut game = BowlingGame::new(); 360 | 361 | for _ in 0..18 { 362 | let _ = game.roll(0); 363 | } 364 | 365 | let _ = game.roll(10); 366 | 367 | assert_eq!(game.score(), None); 368 | 369 | let _ = game.roll(10); 370 | 371 | assert_eq!(game.score(), None); 372 | 373 | let _ = game.roll(10); 374 | 375 | assert!(game.score().is_some()); 376 | } 377 | 378 | #[test] 379 | fn if_the_last_frame_is_a_spare_you_cannot_create_a_score_before_extra_roll_is_taken() { 380 | let mut game = BowlingGame::new(); 381 | 382 | for _ in 0..18 { 383 | let _ = game.roll(0); 384 | } 385 | 386 | let _ = game.roll(5); 387 | let _ = game.roll(5); 388 | 389 | assert_eq!(game.score(), None); 390 | 391 | let _ = game.roll(10); 392 | 393 | assert!(game.score().is_some()); 394 | } 395 | 396 | #[test] 397 | fn cannot_roll_after_bonus_roll_for_spare() { 398 | let mut game = BowlingGame::new(); 399 | 400 | for _ in 0..9 { 401 | let _ = game.roll(0); 402 | let _ = game.roll(0); 403 | } 404 | let _ = game.roll(7); 405 | let _ = game.roll(3); 406 | assert!(game.roll(2).is_ok()); 407 | 408 | assert_eq!(game.roll(2), Err(Error::GameComplete)); 409 | } 410 | 411 | #[test] 412 | fn cannot_roll_after_bonus_roll_for_strike() { 413 | let mut game = BowlingGame::new(); 414 | 415 | for _ in 0..9 { 416 | let _ = game.roll(0); 417 | let _ = game.roll(0); 418 | } 419 | let _ = game.roll(10); 420 | let _ = game.roll(3); 421 | assert!(game.roll(2).is_ok()); 422 | 423 | assert_eq!(game.roll(2), Err(Error::GameComplete)); 424 | } 425 | 426 | #[test] 427 | fn last_two_strikes_followed_by_only_last_bonus_with_non_strike_points() { 428 | let mut game = BowlingGame::new(); 429 | for _ in 0..16 { 430 | let _ = game.roll(0); 431 | } 432 | let _ = game.roll(10); 433 | let _ = game.roll(10); 434 | let _ = game.roll(0); 435 | let _ = game.roll(1); 436 | 437 | assert_eq!(game.score(), Some(31)); 438 | } 439 | -------------------------------------------------------------------------------- /3 - Assignment/220410 - Make Interpreter Rust, Week 2/README.md: -------------------------------------------------------------------------------- 1 | # Assignment 2 2 | 3 | You should pass all tests by running `cargo test`. 4 | 5 | 1. [Word Count](./prob1) 6 | 7 | 2. [Circular Buffer](./prob2) 8 | 9 | 3. [Simple Linked List](./prob3) 10 | 11 | 4. [Doubly Linked List](./prob4) -------------------------------------------------------------------------------- /3 - Assignment/220410 - Make Interpreter Rust, Week 2/prob1/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "prob1" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /3 - Assignment/220410 - Make Interpreter Rust, Week 2/prob1/README.md: -------------------------------------------------------------------------------- 1 | # Instructions 2 | 3 | Given a phrase, count the occurrences of each _word_ in that phrase. 4 | 5 | For the purposes of this exercise you can expect that a _word_ will always be one of: 6 | 7 | 1. A _number_ composed of one or more ASCII digits (i.e. "0" or "1234") OR 8 | 2. A _simple word_ composed of one or more ASCII letters (i.e. "a" or "they") OR 9 | 3. A _contraction_ of two _simple words_ joined by a single apostrophe (i.e. "it's" or "they're") 10 | 11 | When counting words you can assume the following rules: 12 | 13 | 1. The count is _case insensitive_ (i.e. "You", "you", and "YOU" are 3 uses of the same word) 14 | 2. The count is _unordered_; the tests will ignore how words and counts are ordered 15 | 3. Other than the apostrophe in a _contraction_ all forms of _punctuation_ are ignored 16 | 4. The words can be separated by _any_ form of whitespace (i.e. "\t", "\n", " "), or 17 | external punctuation. 18 | 19 | For example, for the phrase `"That's the password: 'PASSWORD 123'!", cried the Special Agent.\nSo I fled.` the count would be: 20 | 21 | ```text 22 | that's: 1 23 | the: 2 24 | password: 2 25 | 123: 1 26 | cried: 1 27 | special: 1 28 | agent: 1 29 | so: 1 30 | i: 1 31 | fled: 1 32 | ``` 33 | 34 | For the phrase `"one,two,three"` the count would be: 35 | 36 | ```text 37 | one: 1 38 | two: 1 39 | three: 1 40 | ``` 41 | -------------------------------------------------------------------------------- /3 - Assignment/220410 - Make Interpreter Rust, Week 2/prob1/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | /// Count occurrences of words. 4 | pub fn word_count(words: &str) -> HashMap { 5 | unimplemented!("Count of occurrences of words in {:?}", words); 6 | } 7 | 8 | #[cfg(test)] 9 | fn check_word_count(s: &str, pairs: &[(&str, u32)]) { 10 | // The reason for the awkward code in here is to ensure that the failure 11 | // message for assert_eq! is as informative as possible. A simpler 12 | // solution would simply check the length of the map, and then 13 | // check for the presence and value of each key in the given pairs vector. 14 | let mut m: HashMap = word_count(s); 15 | for &(k, v) in pairs.iter() { 16 | assert_eq!((k, m.remove(&k.to_string()).unwrap_or(0)), (k, v)); 17 | } 18 | // may fail with a message that clearly shows all extra pairs in the map 19 | assert_eq!(m.iter().collect::>(), vec![]); 20 | } 21 | 22 | #[test] 23 | fn test_count_one_word() { 24 | check_word_count("word", &[("word", 1)]); 25 | } 26 | 27 | #[test] 28 | fn test_count_one_of_each() { 29 | check_word_count("one of each", &[("one", 1), ("of", 1), ("each", 1)]); 30 | } 31 | 32 | #[test] 33 | fn test_count_multiple_occurrences() { 34 | check_word_count( 35 | "one fish two fish red fish blue fish", 36 | &[("one", 1), ("fish", 4), ("two", 1), ("red", 1), ("blue", 1)], 37 | ); 38 | } 39 | 40 | #[test] 41 | fn cramped_lists() { 42 | check_word_count("one,two,three", &[("one", 1), ("two", 1), ("three", 1)]); 43 | } 44 | 45 | #[test] 46 | fn expanded_lists() { 47 | check_word_count("one\ntwo\nthree", &[("one", 1), ("two", 1), ("three", 1)]); 48 | } 49 | 50 | #[test] 51 | fn test_ignore_punctuation() { 52 | check_word_count( 53 | "car : carpet as java : javascript!!&@$%^&", 54 | &[ 55 | ("car", 1), 56 | ("carpet", 1), 57 | ("as", 1), 58 | ("java", 1), 59 | ("javascript", 1), 60 | ], 61 | ); 62 | } 63 | 64 | #[test] 65 | fn test_include_numbers() { 66 | check_word_count( 67 | "testing, 1, 2 testing", 68 | &[("testing", 2), ("1", 1), ("2", 1)], 69 | ); 70 | } 71 | 72 | #[test] 73 | fn test_normalize_case() { 74 | check_word_count("go Go GO Stop stop", &[("go", 3), ("stop", 2)]); 75 | } 76 | 77 | #[test] 78 | fn with_apostrophes() { 79 | check_word_count( 80 | "First: don't laugh. Then: don't cry.", 81 | &[ 82 | ("first", 1), 83 | ("don't", 2), 84 | ("laugh", 1), 85 | ("then", 1), 86 | ("cry", 1), 87 | ], 88 | ); 89 | } 90 | 91 | #[test] 92 | fn with_quotations() { 93 | check_word_count( 94 | "Joe can't tell between 'large' and large.", 95 | &[ 96 | ("joe", 1), 97 | ("can't", 1), 98 | ("tell", 1), 99 | ("between", 1), 100 | ("large", 2), 101 | ("and", 1), 102 | ], 103 | ); 104 | } 105 | 106 | #[test] 107 | fn multiple_spaces_not_detected_as_a_word() { 108 | check_word_count( 109 | " multiple whitespaces", 110 | &[("multiple", 1), ("whitespaces", 1)], 111 | ); 112 | } -------------------------------------------------------------------------------- /3 - Assignment/220410 - Make Interpreter Rust, Week 2/prob2/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "prob2" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /3 - Assignment/220410 - Make Interpreter Rust, Week 2/prob2/README.md: -------------------------------------------------------------------------------- 1 | # Instructions 2 | 3 | A circular buffer, cyclic buffer or ring buffer is a data structure that 4 | uses a single, fixed-size buffer as if it were connected end-to-end. 5 | 6 | A circular buffer first starts empty and of some predefined length. For 7 | example, this is a 7-element buffer: 8 | 9 | [ ][ ][ ][ ][ ][ ][ ] 10 | 11 | Assume that a 1 is written into the middle of the buffer (exact starting 12 | location does not matter in a circular buffer): 13 | 14 | [ ][ ][ ][1][ ][ ][ ] 15 | 16 | Then assume that two more elements are added — 2 & 3 — which get 17 | appended after the 1: 18 | 19 | [ ][ ][ ][1][2][3][ ] 20 | 21 | If two elements are then removed from the buffer, the oldest values 22 | inside the buffer are removed. The two elements removed, in this case, 23 | are 1 & 2, leaving the buffer with just a 3: 24 | 25 | [ ][ ][ ][ ][ ][3][ ] 26 | 27 | If the buffer has 7 elements then it is completely full: 28 | 29 | [5][6][7][8][9][3][4] 30 | 31 | When the buffer is full an error will be raised, alerting the client 32 | that further writes are blocked until a slot becomes free. 33 | 34 | When the buffer is full, the client can opt to overwrite the oldest 35 | data with a forced write. In this case, two more elements — A & B — 36 | are added and they overwrite the 3 & 4: 37 | 38 | [5][6][7][8][9][A][B] 39 | 40 | 3 & 4 have been replaced by A & B making 5 now the oldest data in the 41 | buffer. Finally, if two elements are removed then what would be 42 | returned is 5 & 6 yielding the buffer: 43 | 44 | [ ][ ][7][8][9][A][B] 45 | 46 | Because there is space available, if the client again uses overwrite 47 | to store C & D then the space where 5 & 6 were stored previously will 48 | be used not the location of 7 & 8. 7 is still the oldest element and 49 | the buffer is once again full. 50 | 51 | [C][D][7][8][9][A][B] 52 | -------------------------------------------------------------------------------- /3 - Assignment/220410 - Make Interpreter Rust, Week 2/prob2/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | #[cfg(test)] 3 | use std::rc::Rc; 4 | 5 | pub struct CircularBuffer { 6 | // This field is here to make the template compile and not to 7 | // complain about unused type parameter 'T'. Once you start 8 | // solving the exercise, delete this field and the 'std::marker::PhantomData' 9 | // import. 10 | field: PhantomData, 11 | } 12 | 13 | #[derive(Debug, PartialEq)] 14 | pub enum Error { 15 | EmptyBuffer, 16 | FullBuffer, 17 | } 18 | 19 | impl CircularBuffer { 20 | pub fn new(capacity: usize) -> Self { 21 | unimplemented!( 22 | "Construct a new CircularBuffer with the capacity to hold {}.", 23 | match capacity { 24 | 1 => "1 element".to_string(), 25 | _ => format!("{} elements", capacity), 26 | } 27 | ); 28 | } 29 | 30 | pub fn write(&mut self, _element: T) -> Result<(), Error> { 31 | unimplemented!("Write the passed element to the CircularBuffer or return FullBuffer error if CircularBuffer is full."); 32 | } 33 | 34 | pub fn read(&mut self) -> Result { 35 | unimplemented!("Read the oldest element from the CircularBuffer or return EmptyBuffer error if CircularBuffer is empty."); 36 | } 37 | 38 | pub fn clear(&mut self) { 39 | unimplemented!("Clear the CircularBuffer."); 40 | } 41 | 42 | pub fn overwrite(&mut self, _element: T) { 43 | unimplemented!("Write the passed element to the CircularBuffer, overwriting the existing elements if CircularBuffer is full."); 44 | } 45 | } 46 | 47 | #[test] 48 | fn error_on_read_empty_buffer() { 49 | let mut buffer = CircularBuffer::::new(1); 50 | assert_eq!(Err(Error::EmptyBuffer), buffer.read()); 51 | } 52 | 53 | #[test] 54 | fn can_read_item_just_written() { 55 | let mut buffer = CircularBuffer::new(1); 56 | assert!(buffer.write('1').is_ok()); 57 | assert_eq!(Ok('1'), buffer.read()); 58 | } 59 | 60 | #[test] 61 | fn each_item_may_only_be_read_once() { 62 | let mut buffer = CircularBuffer::new(1); 63 | assert!(buffer.write('1').is_ok()); 64 | assert_eq!(Ok('1'), buffer.read()); 65 | assert_eq!(Err(Error::EmptyBuffer), buffer.read()); 66 | } 67 | 68 | #[test] 69 | fn items_are_read_in_the_order_they_are_written() { 70 | let mut buffer = CircularBuffer::new(2); 71 | assert!(buffer.write('1').is_ok()); 72 | assert!(buffer.write('2').is_ok()); 73 | assert_eq!(Ok('1'), buffer.read()); 74 | assert_eq!(Ok('2'), buffer.read()); 75 | assert_eq!(Err(Error::EmptyBuffer), buffer.read()); 76 | } 77 | 78 | #[test] 79 | fn full_buffer_cant_be_written_to() { 80 | let mut buffer = CircularBuffer::new(1); 81 | assert!(buffer.write('1').is_ok()); 82 | assert_eq!(Err(Error::FullBuffer), buffer.write('2')); 83 | } 84 | 85 | #[test] 86 | fn read_frees_up_capacity_for_another_write() { 87 | let mut buffer = CircularBuffer::new(1); 88 | assert!(buffer.write('1').is_ok()); 89 | assert_eq!(Ok('1'), buffer.read()); 90 | assert!(buffer.write('2').is_ok()); 91 | assert_eq!(Ok('2'), buffer.read()); 92 | } 93 | 94 | #[test] 95 | fn read_position_is_maintained_even_across_multiple_writes() { 96 | let mut buffer = CircularBuffer::new(3); 97 | assert!(buffer.write('1').is_ok()); 98 | assert!(buffer.write('2').is_ok()); 99 | assert_eq!(Ok('1'), buffer.read()); 100 | assert!(buffer.write('3').is_ok()); 101 | assert_eq!(Ok('2'), buffer.read()); 102 | assert_eq!(Ok('3'), buffer.read()); 103 | } 104 | 105 | #[test] 106 | fn items_cleared_out_of_buffer_cant_be_read() { 107 | let mut buffer = CircularBuffer::new(1); 108 | assert!(buffer.write('1').is_ok()); 109 | buffer.clear(); 110 | assert_eq!(Err(Error::EmptyBuffer), buffer.read()); 111 | } 112 | 113 | #[test] 114 | fn clear_frees_up_capacity_for_another_write() { 115 | let mut buffer = CircularBuffer::new(1); 116 | assert!(buffer.write('1').is_ok()); 117 | buffer.clear(); 118 | assert!(buffer.write('2').is_ok()); 119 | assert_eq!(Ok('2'), buffer.read()); 120 | } 121 | 122 | #[test] 123 | fn clear_does_nothing_on_empty_buffer() { 124 | let mut buffer = CircularBuffer::new(1); 125 | buffer.clear(); 126 | assert!(buffer.write('1').is_ok()); 127 | assert_eq!(Ok('1'), buffer.read()); 128 | } 129 | 130 | #[test] 131 | fn clear_actually_frees_up_its_elements() { 132 | let mut buffer = CircularBuffer::new(1); 133 | let element = Rc::new(()); 134 | assert!(buffer.write(Rc::clone(&element)).is_ok()); 135 | assert_eq!(Rc::strong_count(&element), 2); 136 | buffer.clear(); 137 | assert_eq!(Rc::strong_count(&element), 1); 138 | } 139 | 140 | #[test] 141 | fn overwrite_acts_like_write_on_non_full_buffer() { 142 | let mut buffer = CircularBuffer::new(2); 143 | assert!(buffer.write('1').is_ok()); 144 | buffer.overwrite('2'); 145 | assert_eq!(Ok('1'), buffer.read()); 146 | assert_eq!(Ok('2'), buffer.read()); 147 | assert_eq!(Err(Error::EmptyBuffer), buffer.read()); 148 | } 149 | 150 | #[test] 151 | fn overwrite_replaces_the_oldest_item_on_full_buffer() { 152 | let mut buffer = CircularBuffer::new(2); 153 | assert!(buffer.write('1').is_ok()); 154 | assert!(buffer.write('2').is_ok()); 155 | buffer.overwrite('A'); 156 | assert_eq!(Ok('2'), buffer.read()); 157 | assert_eq!(Ok('A'), buffer.read()); 158 | } 159 | 160 | #[test] 161 | fn overwrite_replaces_the_oldest_item_remaining_in_buffer_following_a_read() { 162 | let mut buffer = CircularBuffer::new(3); 163 | assert!(buffer.write('1').is_ok()); 164 | assert!(buffer.write('2').is_ok()); 165 | assert!(buffer.write('3').is_ok()); 166 | assert_eq!(Ok('1'), buffer.read()); 167 | assert!(buffer.write('4').is_ok()); 168 | buffer.overwrite('5'); 169 | assert_eq!(Ok('3'), buffer.read()); 170 | assert_eq!(Ok('4'), buffer.read()); 171 | assert_eq!(Ok('5'), buffer.read()); 172 | } 173 | 174 | #[test] 175 | fn integer_buffer() { 176 | let mut buffer = CircularBuffer::new(2); 177 | assert!(buffer.write(1).is_ok()); 178 | assert!(buffer.write(2).is_ok()); 179 | assert_eq!(Ok(1), buffer.read()); 180 | assert!(buffer.write(-1).is_ok()); 181 | assert_eq!(Ok(2), buffer.read()); 182 | assert_eq!(Ok(-1), buffer.read()); 183 | assert_eq!(Err(Error::EmptyBuffer), buffer.read()); 184 | } 185 | 186 | #[test] 187 | fn string_buffer() { 188 | let mut buffer = CircularBuffer::new(2); 189 | buffer.write("".to_string()).unwrap(); 190 | buffer.write("Testing".to_string()).unwrap(); 191 | assert_eq!(0, buffer.read().unwrap().len()); 192 | assert_eq!(Ok("Testing".to_string()), buffer.read()); 193 | } 194 | -------------------------------------------------------------------------------- /3 - Assignment/220410 - Make Interpreter Rust, Week 2/prob3/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "prob3" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /3 - Assignment/220410 - Make Interpreter Rust, Week 2/prob3/README.md: -------------------------------------------------------------------------------- 1 | # Instructions 2 | 3 | Write a simple linked list implementation that uses Elements and a List. 4 | 5 | The linked list is a fundamental data structure in computer science, 6 | often used in the implementation of other data structures. They're 7 | pervasive in functional programming languages, such as Clojure, Erlang, 8 | or Haskell, but far less common in imperative languages such as Ruby or 9 | Python. 10 | 11 | The simplest kind of linked list is a singly linked list. Each element in the 12 | list contains data and a "next" field pointing to the next element in the list 13 | of elements. 14 | 15 | This variant of linked lists is often used to represent sequences or 16 | push-down stacks (also called a LIFO stack; Last In, First Out). 17 | 18 | As a first take, lets create a singly linked list to contain the range (1..10), 19 | and provide functions to reverse a linked list and convert to and from arrays. 20 | 21 | When implementing this in a language with built-in linked lists, 22 | implement your own abstract data type. 23 | 24 |
25 | Implementation Hints 26 |
27 | Do not implement the struct `SimpleLinkedList` as a wrapper around a `Vec`. Instead, allocate nodes on the heap. 28 | This might be implemented as: 29 | ``` 30 | pub struct SimpleLinkedList { 31 | head: Option>>, 32 | } 33 | ``` 34 | The `head` field points to the first element (Node) of this linked list. 35 | This implementation also requires a struct `Node` with the following fields: 36 | ``` 37 | struct Node { 38 | data: T, 39 | next: Option>>, 40 | } 41 | ``` 42 | `data` contains the stored data, and `next` points to the following node (if available) or None. 43 | 44 | ## Why `Option>>` and not just `Option>`? 45 | Try it on your own. You will get the following error. 46 | 47 | ``` 48 | | struct Node 49 | | ^^^^^^^^^^^^^^ recursive type has infinite size 50 | ... 51 | | next: Option>, 52 | | --------------------- recursive without indirection 53 | ``` 54 | 55 | The problem is that at compile time the size of next must be known. 56 | Since `next` is recursive ("a node has a node has a node..."), the compiler does not know how much memory is to be allocated. 57 | In contrast, [Box](https://doc.rust-lang.org/std/boxed/) is a heap pointer with a defined 58 |
-------------------------------------------------------------------------------- /3 - Assignment/220410 - Make Interpreter Rust, Week 2/prob3/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::iter::FromIterator; 2 | 3 | pub struct SimpleLinkedList { 4 | // Delete this field 5 | // dummy is needed to avoid unused parameter error during compilation 6 | dummy: ::std::marker::PhantomData, 7 | } 8 | 9 | impl SimpleLinkedList { 10 | pub fn new() -> Self { 11 | unimplemented!() 12 | } 13 | 14 | // You may be wondering why it's necessary to have is_empty() 15 | // when it can easily be determined from len(). 16 | // It's good custom to have both because len() can be expensive for some types, 17 | // whereas is_empty() is almost always cheap. 18 | // (Also ask yourself whether len() is expensive for SimpleLinkedList) 19 | pub fn is_empty(&self) -> bool { 20 | unimplemented!() 21 | } 22 | 23 | pub fn len(&self) -> usize { 24 | unimplemented!() 25 | } 26 | 27 | pub fn push(&mut self, _element: T) { 28 | unimplemented!() 29 | } 30 | 31 | pub fn pop(&mut self) -> Option { 32 | unimplemented!() 33 | } 34 | 35 | pub fn peek(&self) -> Option<&T> { 36 | unimplemented!() 37 | } 38 | 39 | #[must_use] 40 | pub fn rev(self) -> SimpleLinkedList { 41 | unimplemented!() 42 | } 43 | } 44 | 45 | impl FromIterator for SimpleLinkedList { 46 | fn from_iter>(_iter: I) -> Self { 47 | unimplemented!() 48 | } 49 | } 50 | 51 | // In general, it would be preferable to implement IntoIterator for SimpleLinkedList 52 | // instead of implementing an explicit conversion to a vector. This is because, together, 53 | // FromIterator and IntoIterator enable conversion between arbitrary collections. 54 | // Given that implementation, converting to a vector is trivial: 55 | // 56 | // let vec: Vec<_> = simple_linked_list.into_iter().collect(); 57 | // 58 | // The reason this exercise's API includes an explicit conversion to Vec instead 59 | // of IntoIterator is that implementing that interface is fairly complicated, and 60 | // demands more of the student than we expect at this point in the track. 61 | 62 | impl From> for Vec { 63 | fn from(mut _linked_list: SimpleLinkedList) -> Vec { 64 | unimplemented!() 65 | } 66 | } 67 | 68 | #[test] 69 | fn test_new_list_is_empty() { 70 | let list: SimpleLinkedList = SimpleLinkedList::new(); 71 | assert_eq!(list.len(), 0, "list's length must be 0"); 72 | } 73 | 74 | #[test] 75 | fn test_push_increments_length() { 76 | let mut list: SimpleLinkedList = SimpleLinkedList::new(); 77 | list.push(1); 78 | assert_eq!(list.len(), 1, "list's length must be 1"); 79 | list.push(2); 80 | assert_eq!(list.len(), 2, "list's length must be 2"); 81 | } 82 | 83 | #[test] 84 | fn test_pop_decrements_length() { 85 | let mut list: SimpleLinkedList = SimpleLinkedList::new(); 86 | list.push(1); 87 | list.push(2); 88 | list.pop(); 89 | assert_eq!(list.len(), 1, "list's length must be 1"); 90 | list.pop(); 91 | assert_eq!(list.len(), 0, "list's length must be 0"); 92 | } 93 | 94 | #[test] 95 | fn test_is_empty() { 96 | let mut list: SimpleLinkedList = SimpleLinkedList::new(); 97 | assert!(list.is_empty(), "List wasn't empty on creation"); 98 | for inserts in 0..100 { 99 | for i in 0..inserts { 100 | list.push(i); 101 | assert!( 102 | !list.is_empty(), 103 | "List was empty after having inserted {}/{} elements", 104 | i, 105 | inserts 106 | ); 107 | } 108 | for i in 0..inserts { 109 | assert!( 110 | !list.is_empty(), 111 | "List was empty before removing {}/{} elements", 112 | i, 113 | inserts 114 | ); 115 | list.pop(); 116 | } 117 | assert!( 118 | list.is_empty(), 119 | "List wasn't empty after having removed {} elements", 120 | inserts 121 | ); 122 | } 123 | } 124 | 125 | #[test] 126 | fn test_pop_returns_head_element_and_removes_it() { 127 | let mut list: SimpleLinkedList = SimpleLinkedList::new(); 128 | list.push(1); 129 | list.push(2); 130 | assert_eq!(list.pop(), Some(2), "Element must be 2"); 131 | assert_eq!(list.pop(), Some(1), "Element must be 1"); 132 | assert_eq!(list.pop(), None, "No element should be contained in list"); 133 | } 134 | 135 | #[test] 136 | fn test_peek_returns_reference_to_head_element_but_does_not_remove_it() { 137 | let mut list: SimpleLinkedList = SimpleLinkedList::new(); 138 | assert_eq!(list.peek(), None, "No element should be contained in list"); 139 | list.push(2); 140 | assert_eq!(list.peek(), Some(&2), "Element must be 2"); 141 | assert_eq!(list.peek(), Some(&2), "Element must be still 2"); 142 | list.push(3); 143 | assert_eq!(list.peek(), Some(&3), "Head element is now 3"); 144 | assert_eq!(list.pop(), Some(3), "Element must be 3"); 145 | assert_eq!(list.peek(), Some(&2), "Head element is now 2"); 146 | assert_eq!(list.pop(), Some(2), "Element must be 2"); 147 | assert_eq!(list.peek(), None, "No element should be contained in list"); 148 | } 149 | 150 | #[test] 151 | fn test_from_slice() { 152 | let mut array = vec!["1", "2", "3", "4"]; 153 | let mut list: SimpleLinkedList<_> = array.drain(..).collect(); 154 | assert_eq!(list.pop(), Some("4")); 155 | assert_eq!(list.pop(), Some("3")); 156 | assert_eq!(list.pop(), Some("2")); 157 | assert_eq!(list.pop(), Some("1")); 158 | } 159 | 160 | #[test] 161 | fn test_reverse() { 162 | let mut list: SimpleLinkedList = SimpleLinkedList::new(); 163 | list.push(1); 164 | list.push(2); 165 | list.push(3); 166 | let mut rev_list = list.rev(); 167 | assert_eq!(rev_list.pop(), Some(1)); 168 | assert_eq!(rev_list.pop(), Some(2)); 169 | assert_eq!(rev_list.pop(), Some(3)); 170 | assert_eq!(rev_list.pop(), None); 171 | } 172 | 173 | #[test] 174 | fn test_into_vector() { 175 | let mut v = Vec::new(); 176 | let mut s = SimpleLinkedList::new(); 177 | for i in 1..4 { 178 | v.push(i); 179 | s.push(i); 180 | } 181 | let s_as_vec: Vec = s.into(); 182 | assert_eq!(v, s_as_vec); 183 | } 184 | -------------------------------------------------------------------------------- /3 - Assignment/220410 - Make Interpreter Rust, Week 2/prob4/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "prob4" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | 10 | # [features] 11 | # advanced = [] 12 | # default = [ "advanced" ] 13 | -------------------------------------------------------------------------------- /3 - Assignment/220410 - Make Interpreter Rust, Week 2/prob4/README.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Write a doubly linked list using unsafe Rust, including an iterator over the list 4 | and a cursor for efficient mutation. 5 | 6 | The doubly linked list is a fundamental data structure in computer science, 7 | often used in the implementation of other data structures. They're 8 | pervasive in functional programming languages, such as Clojure, Erlang, 9 | or Haskell, but far less common in imperative languages such as Ruby or 10 | Python. 11 | 12 | Each node in a doubly linked list contains data and pointers to the next 13 | and previous node, if they exist. 14 | 15 | New nodes can be efficiently added at any point in the list, if one already has 16 | a reference to the position. Likewise, all elements 17 | from another list can be inserted at any point in constant time. 18 | 19 | In Rust, linked lists are very rarely used, but occasionally they trip up 20 | newcomers, when they try implementing one. Often, they find it unexpectedly 21 | difficult to work with the yet unfamiliar borrow checker. 22 | 23 | ## A Note on `unsafe` 24 | Remember, the goal of unsafe Rust is to write safe code in cases where the compiler can't help us 25 | guarantee correctness. It must not be possible for a user to cause memory unsafety of any kind using 26 | only the safe interfaces we expose. 27 | 28 | Document the safety-critical invariants you need to uphold and comment each unsafe block explaining why it 29 | is safe. 30 | 31 | Any function where the caller has to maintain safety-critical invariants should be marked unsafe. This includes 32 | private functions. 33 | 34 | ## Step 1 35 | 36 | Implement the functionality for adding and removing elements (pushing and popping) 37 | at the front and back. This is enough to use the list as a double-ended queue. 38 | Also implement the `len` and `is_empty` functions. 39 | 40 | In the finished implementation, all modifications of the list should be done through the cursor struct 41 | to minimize duplication. The `push_*` and `pop_*` methods on `LinkedList` 42 | are defined in terms of required cursor methods in the module `linked_list`. If you wish, you 43 | can skip the `Cursor` struct for now and override the methods, but please revert them at the end. 44 | 45 | ## Step 2 46 | 47 | Implement iteration over the list from front to back with the `Iter` struct. 48 | 49 | ## Step 3 50 | 51 | Complete the functionality of the cursor. It should be able to move to any position and insert or remove elements there. 52 | 53 | ## Step 4 54 | 55 | Implement the `Drop` trait for your `LinkedList` to clean up resources. 56 | 57 | ## Step 5 (advanced and optional) 58 | 59 | The tests for these last two things are conditionally compiled via the feature flag `advanced`. 60 | Add the key `default = ["advanced"]` to the `Cargo.toml` file under `[features]` to activate them. 61 | 62 | To allow users of your structure maximum flexibility, make sure that your `LinkedList` is covariant over `T`. 63 | This means, for example, that a `LinkedList<&'static T>` can also be used as a `LinkedList<&'a T>`. 64 | See the [Rustonomicon](https://doc.rust-lang.org/nomicon/subtyping.html#subtyping-and-variance) for an explanation of variance in Rust. 65 | 66 | Make sure that your list is safe to send and share across thread boundaries 67 | and signal this to the type system by implementing `Send` and `Sync` manually. 68 | These traits are usually auto-derived, but aren't implemented here automatically, because of the use of 69 | raw pointers. See the docs for [`Send`](https://doc.rust-lang.org/std/marker/trait.Send.html) and [`Sync`](https://doc.rust-lang.org/std/marker/trait.Sync.html) and the [rustonomicon chapter](https://doc.rust-lang.org/nomicon/send-and-sync.html) on them for details on their significance. 70 | 71 |
72 | Hints 73 |
74 | * A doubly linked does not have a clear ownership hierarchy, which is why it requires either the use 75 | of unsafe or abstractions for shared ownership like `Rc`. The latter has some overhead that is unnecessary 76 | for this case. 77 | 78 | * Refer to the [Rustonomicon](https://doc.rust-lang.org/nomicon/) for details on how to use `unsafe {}` correctly. 79 | 80 | * Several functions require similar behaviour in different directions (towards front or back). Try not to duplicate 81 | shared code paths. 82 |
-------------------------------------------------------------------------------- /3 - Assignment/220410 - Make Interpreter Rust, Week 2/prob4/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod linked_list; 2 | 3 | pub struct LinkedList(std::marker::PhantomData); 4 | 5 | pub struct Cursor<'a, T>(std::marker::PhantomData<&'a mut T>); 6 | 7 | pub struct Iter<'a, T>(std::marker::PhantomData<&'a T>); 8 | 9 | impl LinkedList { 10 | pub fn new() -> Self { 11 | unimplemented!() 12 | } 13 | 14 | // You may be wondering why it's necessary to have is_empty() 15 | // when it can easily be determined from len(). 16 | // It's good custom to have both because len() can be expensive for some types, 17 | // whereas is_empty() is almost always cheap. 18 | // (Also ask yourself whether len() is expensive for LinkedList) 19 | pub fn is_empty(&self) -> bool { 20 | unimplemented!() 21 | } 22 | 23 | pub fn len(&self) -> usize { 24 | unimplemented!() 25 | } 26 | 27 | /// Return a cursor positioned on the front element 28 | pub fn cursor_front(&mut self) -> Cursor<'_, T> { 29 | unimplemented!() 30 | } 31 | 32 | /// Return a cursor positioned on the back element 33 | pub fn cursor_back(&mut self) -> Cursor<'_, T> { 34 | unimplemented!() 35 | } 36 | 37 | /// Return an iterator that moves from front to back 38 | pub fn iter(&self) -> Iter<'_, T> { 39 | unimplemented!() 40 | } 41 | } 42 | 43 | // the cursor is expected to act as if it is at the position of an element 44 | // and it also has to work with and be able to insert into an empty list. 45 | impl Cursor<'_, T> { 46 | /// Take a mutable reference to the current element 47 | pub fn peek_mut(&mut self) -> Option<&mut T> { 48 | unimplemented!() 49 | } 50 | 51 | /// Move one position forward (towards the back) and 52 | /// return a reference to the new position 53 | #[allow(clippy::should_implement_trait)] 54 | pub fn next(&mut self) -> Option<&mut T> { 55 | unimplemented!() 56 | } 57 | 58 | /// Move one position backward (towards the front) and 59 | /// return a reference to the new position 60 | pub fn prev(&mut self) -> Option<&mut T> { 61 | unimplemented!() 62 | } 63 | 64 | /// Remove and return the element at the current position and move the cursor 65 | /// to the neighboring element that's closest to the back. This can be 66 | /// either the next or previous position. 67 | pub fn take(&mut self) -> Option { 68 | unimplemented!() 69 | } 70 | 71 | pub fn insert_after(&mut self, _element: T) { 72 | unimplemented!() 73 | } 74 | 75 | pub fn insert_before(&mut self, _element: T) { 76 | unimplemented!() 77 | } 78 | } 79 | 80 | impl<'a, T> Iterator for Iter<'a, T> { 81 | type Item = &'a T; 82 | 83 | fn next(&mut self) -> Option<&'a T> { 84 | unimplemented!() 85 | } 86 | } 87 | 88 | #[test] 89 | fn is_generic() { 90 | struct Foo; 91 | LinkedList::::new(); 92 | } 93 | 94 | // ——————————————————————————————————————————————————————————— 95 | // Tests for Step 1: push / pop at front and back 96 | // ——————————————————————————————————————————————————————————— 97 | 98 | #[test] 99 | fn basics_empty_list() { 100 | let list: LinkedList = LinkedList::new(); 101 | assert_eq!(list.len(), 0); 102 | assert!(list.is_empty()); 103 | } 104 | 105 | // push / pop at back ———————————————————————————————————————— 106 | #[test] 107 | fn basics_single_element_back() { 108 | let mut list: LinkedList = LinkedList::new(); 109 | list.push_back(5); 110 | 111 | assert_eq!(list.len(), 1); 112 | assert!(!list.is_empty()); 113 | 114 | assert_eq!(list.pop_back(), Some(5)); 115 | 116 | assert_eq!(list.len(), 0); 117 | assert!(list.is_empty()); 118 | } 119 | 120 | #[test] 121 | fn basics_push_pop_at_back() { 122 | let mut list: LinkedList = LinkedList::new(); 123 | for i in 0..10 { 124 | list.push_back(i); 125 | assert_eq!(list.len(), i as usize + 1); 126 | assert!(!list.is_empty()); 127 | } 128 | for i in (0..10).rev() { 129 | assert_eq!(list.len(), i as usize + 1); 130 | assert!(!list.is_empty()); 131 | assert_eq!(i, list.pop_back().unwrap()); 132 | } 133 | assert_eq!(list.len(), 0); 134 | assert!(list.is_empty()); 135 | } 136 | 137 | // push / pop at front ——————————————————————————————————————— 138 | #[test] 139 | fn basics_single_element_front() { 140 | let mut list: LinkedList = LinkedList::new(); 141 | list.push_front(5); 142 | 143 | assert_eq!(list.len(), 1); 144 | assert!(!list.is_empty()); 145 | 146 | assert_eq!(list.pop_front(), Some(5)); 147 | 148 | assert_eq!(list.len(), 0); 149 | assert!(list.is_empty()); 150 | } 151 | 152 | #[test] 153 | fn basics_push_pop_at_front() { 154 | let mut list: LinkedList = LinkedList::new(); 155 | for i in 0..10 { 156 | list.push_front(i); 157 | assert_eq!(list.len(), i as usize + 1); 158 | assert!(!list.is_empty()); 159 | } 160 | for i in (0..10).rev() { 161 | assert_eq!(list.len(), i as usize + 1); 162 | assert!(!list.is_empty()); 163 | assert_eq!(i, list.pop_front().unwrap()); 164 | } 165 | assert_eq!(list.len(), 0); 166 | assert!(list.is_empty()); 167 | } 168 | 169 | // push / pop at mixed sides ————————————————————————————————— 170 | #[test] 171 | fn basics_push_front_pop_back() { 172 | let mut list: LinkedList = LinkedList::new(); 173 | for i in 0..10 { 174 | list.push_front(i); 175 | assert_eq!(list.len(), i as usize + 1); 176 | assert!(!list.is_empty()); 177 | } 178 | for i in 0..10 { 179 | assert_eq!(list.len(), 10 - i as usize); 180 | assert!(!list.is_empty()); 181 | assert_eq!(i, list.pop_back().unwrap()); 182 | } 183 | assert_eq!(list.len(), 0); 184 | assert!(list.is_empty()); 185 | } 186 | 187 | #[test] 188 | fn basics_push_back_pop_front() { 189 | let mut list: LinkedList = LinkedList::new(); 190 | for i in 0..10 { 191 | list.push_back(i); 192 | assert_eq!(list.len(), i as usize + 1); 193 | assert!(!list.is_empty()); 194 | } 195 | for i in 0..10 { 196 | assert_eq!(list.len(), 10 - i as usize); 197 | assert!(!list.is_empty()); 198 | assert_eq!(i, list.pop_front().unwrap()); 199 | } 200 | assert_eq!(list.len(), 0); 201 | assert!(list.is_empty()); 202 | } 203 | 204 | // ——————————————————————————————————————————————————————————— 205 | // Tests for Step 2: iteration 206 | // ——————————————————————————————————————————————————————————— 207 | 208 | #[test] 209 | fn iter() { 210 | let mut list: LinkedList = LinkedList::new(); 211 | for num in 0..10 { 212 | list.push_back(num); 213 | } 214 | 215 | for (num, &entered_num) in (0..10).zip(list.iter()) { 216 | assert_eq!(num, entered_num); 217 | } 218 | } 219 | 220 | // ——————————————————————————————————————————————————————————— 221 | // Tests for Step 3: full cursor functionality 222 | // ——————————————————————————————————————————————————————————— 223 | 224 | #[test] 225 | fn cursor_insert_before_on_empty_list() { 226 | // insert_after on empty list is already tested via push_back() 227 | let mut list = LinkedList::new(); 228 | list.cursor_front().insert_before(0); 229 | assert_eq!(Some(0), list.pop_front()); 230 | } 231 | 232 | #[test] 233 | fn cursor_insert_after_in_middle() { 234 | let mut list = (0..10).collect::>(); 235 | 236 | { 237 | let mut cursor = list.cursor_front(); 238 | let didnt_run_into_end = cursor.seek_forward(4); 239 | assert!(didnt_run_into_end); 240 | 241 | for n in (0..10).rev() { 242 | cursor.insert_after(n); 243 | } 244 | } 245 | 246 | assert_eq!(list.len(), 20); 247 | 248 | let expected = (0..5).chain(0..10).chain(5..10); 249 | 250 | assert!(expected.eq(list.iter().cloned())); 251 | } 252 | 253 | #[test] 254 | fn cursor_insert_before_in_middle() { 255 | let mut list = (0..10).collect::>(); 256 | 257 | { 258 | let mut cursor = list.cursor_back(); 259 | let didnt_run_into_end = cursor.seek_backward(4); 260 | assert!(didnt_run_into_end); 261 | 262 | for n in 0..10 { 263 | cursor.insert_before(n); 264 | } 265 | } 266 | 267 | assert_eq!(list.len(), 20); 268 | 269 | let expected = (0..5).chain(0..10).chain(5..10); 270 | 271 | assert!(expected.eq(list.iter().cloned())); 272 | } 273 | 274 | // "iterates" via next() and checks that it visits the right elements 275 | #[test] 276 | fn cursor_next_and_peek() { 277 | let mut list = (0..10).collect::>(); 278 | let mut cursor = list.cursor_front(); 279 | 280 | assert_eq!(cursor.peek_mut(), Some(&mut 0)); 281 | 282 | for n in 1..10 { 283 | let next = cursor.next().cloned(); 284 | assert_eq!(next, Some(n)); 285 | assert_eq!(next, cursor.peek_mut().cloned()); 286 | } 287 | } 288 | 289 | // "iterates" via prev() and checks that it visits the right elements 290 | #[test] 291 | fn cursor_prev_and_peek() { 292 | let mut list = (0..10).collect::>(); 293 | let mut cursor = list.cursor_back(); 294 | 295 | assert_eq!(cursor.peek_mut(), Some(&mut 9)); 296 | 297 | for n in (0..9).rev() { 298 | let prev = cursor.prev().cloned(); 299 | assert_eq!(prev, Some(n)); 300 | assert_eq!(prev, cursor.peek_mut().cloned()); 301 | } 302 | } 303 | 304 | // removes all elements starting from the middle 305 | #[test] 306 | fn cursor_take() { 307 | let mut list = (0..10).collect::>(); 308 | let mut cursor = list.cursor_front(); 309 | cursor.seek_forward(5); 310 | 311 | for expected in (5..10).chain((0..5).rev()) { 312 | assert_eq!(cursor.take(), Some(expected)); 313 | } 314 | } 315 | 316 | // ——————————————————————————————————————————————————————————— 317 | // Tests for Step 4: clean-up via `Drop` 318 | // ——————————————————————————————————————————————————————————— 319 | 320 | // The leak tests that are also for this step are separated into 321 | // their own files so that nothing else interferes with the allocator 322 | // whilst they run 323 | 324 | // checks number of drops 325 | // may pass for incorrect programs if double frees happen 326 | // exactly as often as destructor leaks 327 | #[test] 328 | fn drop_no_double_frees() { 329 | use std::cell::Cell; 330 | struct DropCounter<'a>(&'a Cell); 331 | 332 | impl<'a> Drop for DropCounter<'a> { 333 | fn drop(&mut self) { 334 | let num = self.0.get(); 335 | self.0.set(num + 1); 336 | } 337 | } 338 | 339 | const N: usize = 15; 340 | 341 | let counter = Cell::new(0); 342 | let list = std::iter::repeat_with(|| DropCounter(&counter)) 343 | .take(N) 344 | .collect::>(); 345 | 346 | assert_eq!(list.len(), N); 347 | drop(list); 348 | assert_eq!(counter.get(), N); 349 | } 350 | 351 | #[test] 352 | fn drop_large_list() { 353 | drop((0..2_000_000).collect::>()); 354 | } 355 | 356 | // ——————————————————————————————————————————————————————————— 357 | // Tests for Step 5 (advanced): covariance and Send/Sync 358 | // ——————————————————————————————————————————————————————————— 359 | 360 | // These are compile time tests. They won't compile unless your 361 | // code passes. 362 | 363 | #[cfg(feature = "advanced")] 364 | #[test] 365 | fn advanced_linked_list_is_send_sync() { 366 | trait AssertSend: Send {} 367 | trait AssertSync: Sync {} 368 | 369 | impl AssertSend for LinkedList {} 370 | impl AssertSync for LinkedList {} 371 | } 372 | 373 | #[cfg(feature = "advanced")] 374 | #[allow(dead_code)] 375 | #[test] 376 | fn advanced_is_covariant() { 377 | fn a<'a>(x: LinkedList<&'static str>) -> LinkedList<&'a str> { 378 | x 379 | } 380 | 381 | fn a_iter<'a>(i: Iter<'static, &'static str>) -> Iter<'a, &'a str> { 382 | i 383 | } 384 | } 385 | -------------------------------------------------------------------------------- /3 - Assignment/220410 - Make Interpreter Rust, Week 2/prob4/src/linked_list.rs: -------------------------------------------------------------------------------- 1 | use crate::{Cursor, LinkedList}; 2 | 3 | impl LinkedList { 4 | pub fn push_back(&mut self, element: T) { 5 | self.cursor_back().insert_after(element); 6 | } 7 | 8 | pub fn push_front(&mut self, element: T) { 9 | self.cursor_front().insert_before(element); 10 | } 11 | 12 | pub fn pop_back(&mut self) -> Option { 13 | self.cursor_back().take() 14 | } 15 | 16 | pub fn pop_front(&mut self) -> Option { 17 | self.cursor_front().take() 18 | } 19 | 20 | pub fn front(&self) -> Option<&T> { 21 | self.iter().next() 22 | } 23 | 24 | pub fn back(&self) -> Option<&T> { 25 | self.iter().last() 26 | } 27 | } 28 | 29 | impl std::iter::FromIterator for LinkedList { 30 | fn from_iter(iter: I) -> Self 31 | where 32 | I: IntoIterator, 33 | { 34 | let mut list = Self::new(); 35 | for elem in iter { 36 | list.push_back(elem); 37 | } 38 | list 39 | } 40 | } 41 | 42 | // seek methods, return false if end of list is reached prematurely 43 | impl Cursor<'_, T> { 44 | pub fn seek_forward(&mut self, n: usize) -> bool { 45 | (0..n).all(|_| self.next().is_some()) 46 | } 47 | 48 | pub fn seek_backward(&mut self, n: usize) -> bool { 49 | (0..n).all(|_| self.prev().is_some()) 50 | } 51 | } 52 | 53 | #[allow(unused)] 54 | #[cfg(feature = "advanced")] 55 | /// ```compile_fail 56 | /// use doubly_linked_list::LinkedList; 57 | /// trait AssertSend: Send {} 58 | /// impl AssertSend for LinkedList {} 59 | /// ``` 60 | pub struct IllegalSend; 61 | 62 | #[allow(unused)] 63 | #[cfg(feature = "advanced")] 64 | /// ```compile_fail 65 | /// use doubly_linked_list::LinkedList; 66 | /// trait AssertSync: Sync {} 67 | /// impl AssertSync for LinkedList {} 68 | /// ``` 69 | pub struct IllegalSync; 70 | -------------------------------------------------------------------------------- /3 - Assignment/220605 - Make Interpreter Rust, Week 4/README.md: -------------------------------------------------------------------------------- 1 | # Assignment 3 2 | 3 | You should pass all tests by running `cargo test`. 4 | 5 | 1. [Support operators for comparison](./prob1) 6 | 7 | 2. [Support floating point type](./prob2) 8 | -------------------------------------------------------------------------------- /3 - Assignment/220605 - Make Interpreter Rust, Week 4/prob1/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "monkey" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["Chris Ohk "] 6 | 7 | [dependencies] 8 | rustyline = { version = "9.1.2", optional = true } 9 | 10 | [[bin]] 11 | name = "monkey" 12 | path = "src/bin/main.rs" 13 | required-features = ["binaries"] 14 | 15 | [features] 16 | binaries = ["rustyline"] 17 | -------------------------------------------------------------------------------- /3 - Assignment/220605 - Make Interpreter Rust, Week 4/prob1/README.md: -------------------------------------------------------------------------------- 1 | # Instructions 2 | 3 | You should implement code to support operator `>=` and `<=`. -------------------------------------------------------------------------------- /3 - Assignment/220605 - Make Interpreter Rust, Week 4/prob1/src/bin/main.rs: -------------------------------------------------------------------------------- 1 | extern crate monkey; 2 | extern crate rustyline; 3 | 4 | use monkey::lexer::lexer::Lexer; 5 | use monkey::token::token::Token; 6 | use rustyline::error::ReadlineError; 7 | use rustyline::Editor; 8 | 9 | fn main() { 10 | let mut rl = Editor::<()>::new(); 11 | 12 | println!("Hello! This is the Monkey programming language!"); 13 | println!("Feel free to type in commands\n"); 14 | 15 | loop { 16 | match rl.readline(">> ") { 17 | Ok(line) => { 18 | rl.add_history_entry(&line); 19 | 20 | let mut lexer = Lexer::new(&line); 21 | 22 | loop { 23 | let tok = lexer.next_token(); 24 | if tok == Token::Eof { 25 | break; 26 | } 27 | 28 | println!("{:?}", tok); 29 | } 30 | } 31 | Err(ReadlineError::Interrupted) => { 32 | println!("\nBye :)"); 33 | break; 34 | } 35 | Err(ReadlineError::Eof) => { 36 | println!(); 37 | break; 38 | } 39 | Err(err) => { 40 | println!("Error: {:?}", err); 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /3 - Assignment/220605 - Make Interpreter Rust, Week 4/prob1/src/lexer/lexer.rs: -------------------------------------------------------------------------------- 1 | use crate::token::token::Token; 2 | 3 | pub struct Lexer<'a> { 4 | input: &'a str, 5 | position: usize, 6 | read_position: usize, 7 | ch: u8, 8 | } 9 | 10 | impl<'a> Lexer<'a> { 11 | pub fn new(input: &'a str) -> Self { 12 | let mut lexer = Lexer { 13 | input, 14 | position: 0, 15 | read_position: 0, 16 | ch: 0, 17 | }; 18 | 19 | lexer.read_char(); 20 | lexer 21 | } 22 | 23 | fn read_char(&mut self) { 24 | if self.read_position >= self.input.len() { 25 | self.ch = 0; 26 | } else { 27 | self.ch = self.input.as_bytes()[self.read_position]; 28 | } 29 | 30 | self.position = self.read_position; 31 | self.read_position += 1; 32 | } 33 | 34 | pub fn next_token(&mut self) -> Token { 35 | self.skip_whitespace(); 36 | 37 | let tok = match self.ch { 38 | b'=' => { 39 | if self.peek_char() == b'=' { 40 | self.read_char(); 41 | Token::Equal 42 | } else { 43 | Token::Assign 44 | } 45 | } 46 | b'+' => Token::Plus, 47 | b'-' => Token::Minus, 48 | b'!' => { 49 | if self.peek_char() == b'=' { 50 | self.read_char(); 51 | Token::NotEqual 52 | } else { 53 | Token::Bang 54 | } 55 | } 56 | b'*' => Token::Asterisk, 57 | b'/' => Token::Slash, 58 | b'<' => Token::LessThan, 59 | b'>' => Token::GreaterThan, 60 | b',' => Token::Comma, 61 | b';' => Token::Semicolon, 62 | b'(' => Token::Lparen, 63 | b')' => Token::Rparen, 64 | b'{' => Token::Lbrace, 65 | b'}' => Token::Rbrace, 66 | b'a'..=b'z' | b'A'..=b'Z' | b'_' => { 67 | return self.read_identifier(); 68 | } 69 | b'0'..=b'9' => { 70 | return self.read_number(); 71 | } 72 | 0 => Token::Eof, 73 | _ => Token::Illegal, 74 | }; 75 | 76 | self.read_char(); 77 | 78 | tok 79 | } 80 | 81 | fn skip_whitespace(&mut self) { 82 | loop { 83 | match self.ch { 84 | b' ' | b'\t' | b'\n' | b'\r' => { 85 | self.read_char(); 86 | } 87 | _ => { 88 | break; 89 | } 90 | } 91 | } 92 | } 93 | 94 | fn peek_char(&mut self) -> u8 { 95 | if self.read_position >= self.input.len() { 96 | 0 97 | } else { 98 | self.input.as_bytes()[self.read_position] 99 | } 100 | } 101 | 102 | fn read_identifier(&mut self) -> Token { 103 | let position = self.position; 104 | 105 | loop { 106 | match self.ch { 107 | b'a'..=b'z' | b'A'..=b'Z' | b'_' => { 108 | self.read_char(); 109 | } 110 | _ => { 111 | break; 112 | } 113 | } 114 | } 115 | 116 | let literal = &self.input[position..self.position]; 117 | 118 | match literal { 119 | "fn" => Token::Function, 120 | "let" => Token::Let, 121 | "true" => Token::Bool(true), 122 | "false" => Token::Bool(false), 123 | "if" => Token::If, 124 | "else" => Token::Else, 125 | "return" => Token::Return, 126 | _ => Token::Ident(String::from(literal)), 127 | } 128 | } 129 | 130 | fn read_number(&mut self) -> Token { 131 | let position = self.position; 132 | 133 | loop { 134 | match self.ch { 135 | b'0'..=b'9' => { 136 | self.read_char(); 137 | } 138 | _ => { 139 | break; 140 | } 141 | } 142 | } 143 | 144 | let literal = &self.input[position..self.position]; 145 | Token::Int(literal.parse::().unwrap()) 146 | } 147 | } 148 | 149 | #[cfg(test)] 150 | mod tests { 151 | use crate::lexer::lexer::Lexer; 152 | use crate::token::token::Token; 153 | 154 | #[test] 155 | fn test_next_token() { 156 | let input = r#"let five = 5; 157 | let ten = 10; 158 | 159 | let add = fn(x, y) { 160 | x + y; 161 | }; 162 | 163 | let result = add(five, ten); 164 | !-/*5; 165 | 5 < 10 > 5; 166 | 167 | if (5 < 10) { 168 | return true; 169 | } else { 170 | return false; 171 | } 172 | 173 | 10 == 10; 174 | 10 != 9; 175 | 10 <= 10; 176 | 10 >= 10; 177 | "#; 178 | let tests = vec![ 179 | Token::Let, 180 | Token::Ident(String::from("five")), 181 | Token::Assign, 182 | Token::Int(5), 183 | Token::Semicolon, 184 | Token::Let, 185 | Token::Ident(String::from("ten")), 186 | Token::Assign, 187 | Token::Int(10), 188 | Token::Semicolon, 189 | Token::Let, 190 | Token::Ident(String::from("add")), 191 | Token::Assign, 192 | Token::Function, 193 | Token::Lparen, 194 | Token::Ident(String::from("x")), 195 | Token::Comma, 196 | Token::Ident(String::from("y")), 197 | Token::Rparen, 198 | Token::Lbrace, 199 | Token::Ident(String::from("x")), 200 | Token::Plus, 201 | Token::Ident(String::from("y")), 202 | Token::Semicolon, 203 | Token::Rbrace, 204 | Token::Semicolon, 205 | Token::Let, 206 | Token::Ident(String::from("result")), 207 | Token::Assign, 208 | Token::Ident(String::from("add")), 209 | Token::Lparen, 210 | Token::Ident(String::from("five")), 211 | Token::Comma, 212 | Token::Ident(String::from("ten")), 213 | Token::Rparen, 214 | Token::Semicolon, 215 | Token::Bang, 216 | Token::Minus, 217 | Token::Slash, 218 | Token::Asterisk, 219 | Token::Int(5), 220 | Token::Semicolon, 221 | Token::Int(5), 222 | Token::LessThan, 223 | Token::Int(10), 224 | Token::GreaterThan, 225 | Token::Int(5), 226 | Token::Semicolon, 227 | Token::If, 228 | Token::Lparen, 229 | Token::Int(5), 230 | Token::LessThan, 231 | Token::Int(10), 232 | Token::Rparen, 233 | Token::Lbrace, 234 | Token::Return, 235 | Token::Bool(true), 236 | Token::Semicolon, 237 | Token::Rbrace, 238 | Token::Else, 239 | Token::Lbrace, 240 | Token::Return, 241 | Token::Bool(false), 242 | Token::Semicolon, 243 | Token::Rbrace, 244 | Token::Int(10), 245 | Token::Equal, 246 | Token::Int(10), 247 | Token::Semicolon, 248 | Token::Int(10), 249 | Token::NotEqual, 250 | Token::Int(9), 251 | Token::Semicolon, 252 | Token::Int(10), 253 | Token::LessThanEqual, 254 | Token::Int(10), 255 | Token::Semicolon, 256 | Token::Int(10), 257 | Token::GreaterThanEqual, 258 | Token::Int(10), 259 | Token::Semicolon, 260 | Token::Eof, 261 | ]; 262 | 263 | let mut lexer = Lexer::new(input); 264 | 265 | for expect in tests { 266 | let tok = lexer.next_token(); 267 | assert_eq!(expect, tok); 268 | } 269 | } 270 | } 271 | -------------------------------------------------------------------------------- /3 - Assignment/220605 - Make Interpreter Rust, Week 4/prob1/src/lexer/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod lexer; 2 | -------------------------------------------------------------------------------- /3 - Assignment/220605 - Make Interpreter Rust, Week 4/prob1/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod lexer; 2 | pub mod token; 3 | -------------------------------------------------------------------------------- /3 - Assignment/220605 - Make Interpreter Rust, Week 4/prob1/src/token/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod token; 2 | -------------------------------------------------------------------------------- /3 - Assignment/220605 - Make Interpreter Rust, Week 4/prob1/src/token/token.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, PartialEq)] 2 | pub enum Token { 3 | Illegal, 4 | Eof, 5 | 6 | // Identifiers + Literals 7 | Ident(String), 8 | Int(i64), 9 | Bool(bool), 10 | 11 | // Operators 12 | Assign, 13 | Plus, 14 | Minus, 15 | Bang, 16 | Asterisk, 17 | Slash, 18 | 19 | Equal, 20 | NotEqual, 21 | LessThan, 22 | GreaterThan, 23 | 24 | // Delimiters 25 | Comma, 26 | Semicolon, 27 | Lparen, 28 | Rparen, 29 | Lbrace, 30 | Rbrace, 31 | 32 | // Reserved Keywords 33 | Function, 34 | Let, 35 | If, 36 | Else, 37 | Return, 38 | } 39 | -------------------------------------------------------------------------------- /3 - Assignment/220605 - Make Interpreter Rust, Week 4/prob2/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "monkey" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["Chris Ohk "] 6 | 7 | [dependencies] 8 | rustyline = { version = "9.1.2", optional = true } 9 | 10 | [[bin]] 11 | name = "monkey" 12 | path = "src/bin/main.rs" 13 | required-features = ["binaries"] 14 | 15 | [features] 16 | binaries = ["rustyline"] 17 | -------------------------------------------------------------------------------- /3 - Assignment/220605 - Make Interpreter Rust, Week 4/prob2/README.md: -------------------------------------------------------------------------------- 1 | # Instructions 2 | 3 | You should implement code to support floating point types such as `float` and `double`. -------------------------------------------------------------------------------- /3 - Assignment/220605 - Make Interpreter Rust, Week 4/prob2/src/bin/main.rs: -------------------------------------------------------------------------------- 1 | extern crate monkey; 2 | extern crate rustyline; 3 | 4 | use monkey::lexer::lexer::Lexer; 5 | use monkey::token::token::Token; 6 | use rustyline::error::ReadlineError; 7 | use rustyline::Editor; 8 | 9 | fn main() { 10 | let mut rl = Editor::<()>::new(); 11 | 12 | println!("Hello! This is the Monkey programming language!"); 13 | println!("Feel free to type in commands\n"); 14 | 15 | loop { 16 | match rl.readline(">> ") { 17 | Ok(line) => { 18 | rl.add_history_entry(&line); 19 | 20 | let mut lexer = Lexer::new(&line); 21 | 22 | loop { 23 | let tok = lexer.next_token(); 24 | if tok == Token::Eof { 25 | break; 26 | } 27 | 28 | println!("{:?}", tok); 29 | } 30 | } 31 | Err(ReadlineError::Interrupted) => { 32 | println!("\nBye :)"); 33 | break; 34 | } 35 | Err(ReadlineError::Eof) => { 36 | println!(); 37 | break; 38 | } 39 | Err(err) => { 40 | println!("Error: {:?}", err); 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /3 - Assignment/220605 - Make Interpreter Rust, Week 4/prob2/src/lexer/lexer.rs: -------------------------------------------------------------------------------- 1 | use crate::token::token::Token; 2 | 3 | pub struct Lexer<'a> { 4 | input: &'a str, 5 | position: usize, 6 | read_position: usize, 7 | ch: u8, 8 | } 9 | 10 | impl<'a> Lexer<'a> { 11 | pub fn new(input: &'a str) -> Self { 12 | let mut lexer = Lexer { 13 | input, 14 | position: 0, 15 | read_position: 0, 16 | ch: 0, 17 | }; 18 | 19 | lexer.read_char(); 20 | lexer 21 | } 22 | 23 | fn read_char(&mut self) { 24 | if self.read_position >= self.input.len() { 25 | self.ch = 0; 26 | } else { 27 | self.ch = self.input.as_bytes()[self.read_position]; 28 | } 29 | 30 | self.position = self.read_position; 31 | self.read_position += 1; 32 | } 33 | 34 | pub fn next_token(&mut self) -> Token { 35 | self.skip_whitespace(); 36 | 37 | let tok = match self.ch { 38 | b'=' => { 39 | if self.peek_char() == b'=' { 40 | self.read_char(); 41 | Token::Equal 42 | } else { 43 | Token::Assign 44 | } 45 | } 46 | b'+' => Token::Plus, 47 | b'-' => Token::Minus, 48 | b'!' => { 49 | if self.peek_char() == b'=' { 50 | self.read_char(); 51 | Token::NotEqual 52 | } else { 53 | Token::Bang 54 | } 55 | } 56 | b'*' => Token::Asterisk, 57 | b'/' => Token::Slash, 58 | b'<' => Token::LessThan, 59 | b'>' => Token::GreaterThan, 60 | b',' => Token::Comma, 61 | b';' => Token::Semicolon, 62 | b'(' => Token::Lparen, 63 | b')' => Token::Rparen, 64 | b'{' => Token::Lbrace, 65 | b'}' => Token::Rbrace, 66 | b'a'..=b'z' | b'A'..=b'Z' | b'_' => { 67 | return self.read_identifier(); 68 | } 69 | b'0'..=b'9' => { 70 | return self.read_number(); 71 | } 72 | 0 => Token::Eof, 73 | _ => Token::Illegal, 74 | }; 75 | 76 | self.read_char(); 77 | 78 | tok 79 | } 80 | 81 | fn skip_whitespace(&mut self) { 82 | loop { 83 | match self.ch { 84 | b' ' | b'\t' | b'\n' | b'\r' => { 85 | self.read_char(); 86 | } 87 | _ => { 88 | break; 89 | } 90 | } 91 | } 92 | } 93 | 94 | fn peek_char(&mut self) -> u8 { 95 | if self.read_position >= self.input.len() { 96 | 0 97 | } else { 98 | self.input.as_bytes()[self.read_position] 99 | } 100 | } 101 | 102 | fn read_identifier(&mut self) -> Token { 103 | let position = self.position; 104 | 105 | loop { 106 | match self.ch { 107 | b'a'..=b'z' | b'A'..=b'Z' | b'_' => { 108 | self.read_char(); 109 | } 110 | _ => { 111 | break; 112 | } 113 | } 114 | } 115 | 116 | let literal = &self.input[position..self.position]; 117 | 118 | match literal { 119 | "fn" => Token::Function, 120 | "let" => Token::Let, 121 | "true" => Token::Bool(true), 122 | "false" => Token::Bool(false), 123 | "if" => Token::If, 124 | "else" => Token::Else, 125 | "return" => Token::Return, 126 | _ => Token::Ident(String::from(literal)), 127 | } 128 | } 129 | 130 | fn read_number(&mut self) -> Token { 131 | let position = self.position; 132 | 133 | loop { 134 | match self.ch { 135 | b'0'..=b'9' => { 136 | self.read_char(); 137 | } 138 | _ => { 139 | break; 140 | } 141 | } 142 | } 143 | 144 | let literal = &self.input[position..self.position]; 145 | Token::Int(literal.parse::().unwrap()) 146 | } 147 | } 148 | 149 | #[cfg(test)] 150 | mod tests { 151 | use crate::lexer::lexer::Lexer; 152 | use crate::token::token::Token; 153 | 154 | #[test] 155 | fn test_next_token() { 156 | let input = r#"let five = 5; 157 | let ten = 10; 158 | 159 | let add = fn(x, y) { 160 | x + y; 161 | }; 162 | 163 | let result = add(five, ten); 164 | !-/*5; 165 | 5 < 10 > 5; 166 | 167 | if (5 < 10) { 168 | return true; 169 | } else { 170 | return false; 171 | } 172 | 173 | 10 == 10; 174 | 10 != 9; 175 | 176 | let pi = 3.14; 177 | let e = 2.71828; 178 | 179 | let mul = fn(x, y) { 180 | x * y; 181 | }; 182 | 183 | let ret = mul(pi, e); 184 | "#; 185 | let tests = vec![ 186 | Token::Let, 187 | Token::Ident(String::from("five")), 188 | Token::Assign, 189 | Token::Int(5), 190 | Token::Semicolon, 191 | Token::Let, 192 | Token::Ident(String::from("ten")), 193 | Token::Assign, 194 | Token::Int(10), 195 | Token::Semicolon, 196 | Token::Let, 197 | Token::Ident(String::from("add")), 198 | Token::Assign, 199 | Token::Function, 200 | Token::Lparen, 201 | Token::Ident(String::from("x")), 202 | Token::Comma, 203 | Token::Ident(String::from("y")), 204 | Token::Rparen, 205 | Token::Lbrace, 206 | Token::Ident(String::from("x")), 207 | Token::Plus, 208 | Token::Ident(String::from("y")), 209 | Token::Semicolon, 210 | Token::Rbrace, 211 | Token::Semicolon, 212 | Token::Let, 213 | Token::Ident(String::from("result")), 214 | Token::Assign, 215 | Token::Ident(String::from("add")), 216 | Token::Lparen, 217 | Token::Ident(String::from("five")), 218 | Token::Comma, 219 | Token::Ident(String::from("ten")), 220 | Token::Rparen, 221 | Token::Semicolon, 222 | Token::Bang, 223 | Token::Minus, 224 | Token::Slash, 225 | Token::Asterisk, 226 | Token::Int(5), 227 | Token::Semicolon, 228 | Token::Int(5), 229 | Token::LessThan, 230 | Token::Int(10), 231 | Token::GreaterThan, 232 | Token::Int(5), 233 | Token::Semicolon, 234 | Token::If, 235 | Token::Lparen, 236 | Token::Int(5), 237 | Token::LessThan, 238 | Token::Int(10), 239 | Token::Rparen, 240 | Token::Lbrace, 241 | Token::Return, 242 | Token::Bool(true), 243 | Token::Semicolon, 244 | Token::Rbrace, 245 | Token::Else, 246 | Token::Lbrace, 247 | Token::Return, 248 | Token::Bool(false), 249 | Token::Semicolon, 250 | Token::Rbrace, 251 | Token::Int(10), 252 | Token::Equal, 253 | Token::Int(10), 254 | Token::Semicolon, 255 | Token::Int(10), 256 | Token::NotEqual, 257 | Token::Int(9), 258 | Token::Semicolon, 259 | Token::Let, 260 | Token::Ident(String::from("pi")), 261 | Token::Assign, 262 | Token::Double(3.14), 263 | Token::Semicolon, 264 | Token::Let, 265 | Token::Ident(String::from("e")), 266 | Token::Assign, 267 | Token::Double(2.71828), 268 | Token::Semicolon, 269 | Token::Let, 270 | Token::Ident(String::from("mul")), 271 | Token::Assign, 272 | Token::Function, 273 | Token::Lparen, 274 | Token::Ident(String::from("x")), 275 | Token::Comma, 276 | Token::Ident(String::from("y")), 277 | Token::Rparen, 278 | Token::Lbrace, 279 | Token::Ident(String::from("x")), 280 | Token::Asterisk, 281 | Token::Ident(String::from("y")), 282 | Token::Semicolon, 283 | Token::Rbrace, 284 | Token::Semicolon, 285 | Token::Let, 286 | Token::Ident(String::from("ret")), 287 | Token::Assign, 288 | Token::Ident(String::from("mul")), 289 | Token::Lparen, 290 | Token::Ident(String::from("pi")), 291 | Token::Comma, 292 | Token::Ident(String::from("e")), 293 | Token::Rparen, 294 | Token::Semicolon, 295 | Token::Eof, 296 | ]; 297 | 298 | let mut lexer = Lexer::new(input); 299 | 300 | for expect in tests { 301 | let tok = lexer.next_token(); 302 | assert_eq!(expect, tok); 303 | } 304 | } 305 | } 306 | -------------------------------------------------------------------------------- /3 - Assignment/220605 - Make Interpreter Rust, Week 4/prob2/src/lexer/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod lexer; 2 | -------------------------------------------------------------------------------- /3 - Assignment/220605 - Make Interpreter Rust, Week 4/prob2/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod lexer; 2 | pub mod token; 3 | -------------------------------------------------------------------------------- /3 - Assignment/220605 - Make Interpreter Rust, Week 4/prob2/src/token/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod token; 2 | -------------------------------------------------------------------------------- /3 - Assignment/220605 - Make Interpreter Rust, Week 4/prob2/src/token/token.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, PartialEq)] 2 | pub enum Token { 3 | Illegal, 4 | Eof, 5 | 6 | // Identifiers + Literals 7 | Ident(String), 8 | Int(i64), 9 | Bool(bool), 10 | 11 | // Operators 12 | Assign, 13 | Plus, 14 | Minus, 15 | Bang, 16 | Asterisk, 17 | Slash, 18 | 19 | Equal, 20 | NotEqual, 21 | LessThan, 22 | GreaterThan, 23 | 24 | // Delimiters 25 | Comma, 26 | Semicolon, 27 | Lparen, 28 | Rparen, 29 | Lbrace, 30 | Rbrace, 31 | 32 | // Reserved Keywords 33 | Function, 34 | Let, 35 | If, 36 | Else, 37 | Return, 38 | } 39 | -------------------------------------------------------------------------------- /4 - Solution/220327 - Make Interpreter Rust, Week 1/README.md: -------------------------------------------------------------------------------- 1 | # Assignment 1 - Solution 2 | 3 | You should pass all tests by running `cargo test`. 4 | 5 | 1. [Role Playing Game](./prob1) 6 | 7 | 2. [RPN Calculator](./prob2) 8 | 9 | 3. [Bowling](./prob3) -------------------------------------------------------------------------------- /4 - Solution/220327 - Make Interpreter Rust, Week 1/prob1/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "prob1" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /4 - Solution/220327 - Make Interpreter Rust, Week 1/prob1/README.md: -------------------------------------------------------------------------------- 1 | # Instructions 2 | 3 | You're working on implementing a role-playing game. The player's character is represented by the following: 4 | 5 | ```rust 6 | pub struct Player { 7 | health: u32, 8 | mana: Option, 9 | level: u32, 10 | } 11 | ``` 12 | 13 | Players in this game must reach level 10 before they unlock a mana pool so that they can start casting spells. Before that point, the Player's mana is `None`. 14 | 15 | You're working on two pieces of functionality in this game, the revive mechanic and the spell casting mechanic. 16 | 17 | The `revive` method should check to ensure that the Player is indeed dead (their health has reached 0), and if they are, the method should return a new Player instance with 100 health. 18 | If the Player's level is 10 or above, they should also be revived with 100 mana. 19 | If the Player's level is below 10, their mana should be `None`. The `revive` method should preserve the Player's level. 20 | 21 | ```rust 22 | let dead_player = Player { health: 0, mana: None, level: 2 }; 23 | dead_player.revive() 24 | // Returns Player { health: 100, mana: None, level: 2 } 25 | ``` 26 | 27 | If the `revive` method is called on a Player whose health is 1 or above, then the method should return `None`. 28 | 29 | ```rust 30 | let alive_player = Player { health: 1, mana: Some(15), level: 11 }; 31 | alive_player.revive() 32 | // Returns None 33 | ``` 34 | 35 | The `cast_spell` method takes a mutable reference to the Player as well as a `mana_cost` parameter indicating how much mana the spell costs. It returns the amount of damage that the cast spell performs, which will always be two times the mana cost of the spell if the spell is successfully cast. 36 | 37 | - If the player does not have access to a mana pool, attempting to cast the spell must decrease their health by the mana cost of the spell. The damage returned must be 0. 38 | 39 | ```rust 40 | let not_a_wizard_yet = Player { health: 79, mana: None, level: 9 }; 41 | assert_eq!(not_a_wizard_yet.cast_spell(5), 0) 42 | assert_eq!(not_a_wizard_yet.health, 74); 43 | assert_eq!(not_a_wizard_yet.mana, None); 44 | ``` 45 | 46 | - If the player has a mana pool but insufficient mana, the method should not affect the pool, but instead return 0 47 | 48 | ```rust 49 | let low_mana_wizard = Player { health: 93, mana: Some(3), level: 12 }; 50 | assert_eq!(low_mana_wizard.cast_spell(10), 0); 51 | assert_eq!(low_mana_wizard.health, 93); 52 | assert_eq!(low_mana_wizard.mana, Some(3)); 53 | ``` 54 | 55 | - Otherwise, the `mana_cost` should be deducted from the Player's mana pool and the appropriate amount of damage should be returned. 56 | 57 | ```rust 58 | let wizard = Player { health: 123, mana: Some(30), level: 18 }; 59 | assert_eq!(wizard.cast_spell(10), 20); 60 | assert_eq!(wizard.health, 123); 61 | assert_eq!(wizard.mana, Some(20)); 62 | ``` 63 | 64 | Have fun! 65 | -------------------------------------------------------------------------------- /4 - Solution/220327 - Make Interpreter Rust, Week 1/prob1/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub struct Player { 2 | pub health: u32, 3 | pub mana: Option, 4 | pub level: u32, 5 | } 6 | 7 | impl Player { 8 | pub fn revive(&self) -> Option { 9 | if self.health == 0 { 10 | return Some(Player { 11 | health: 100, 12 | mana: if self.level >= 10 { Some(100) } else { None }, 13 | level: self.level, 14 | }); 15 | } 16 | 17 | None 18 | } 19 | 20 | pub fn cast_spell(&mut self, mana_cost: u32) -> u32 { 21 | match self.mana { 22 | Some(ref mut mana) => { 23 | if *mana < mana_cost { 24 | return 0; 25 | } else { 26 | *mana -= mana_cost; 27 | return 2 * mana_cost; 28 | } 29 | } 30 | None => { 31 | if self.health <= mana_cost { 32 | self.health = 0; 33 | } else { 34 | self.health -= mana_cost; 35 | } 36 | return 0; 37 | } 38 | } 39 | } 40 | } 41 | 42 | #[test] 43 | fn test_reviving_dead_player() { 44 | let dead_player = Player { 45 | health: 0, 46 | mana: Some(0), 47 | level: 34, 48 | }; 49 | let revived_player = dead_player 50 | .revive() 51 | .expect("reviving a dead player must return Some(player)"); 52 | assert_eq!(revived_player.health, 100); 53 | assert_eq!(revived_player.mana, Some(100)); 54 | assert_eq!(revived_player.level, dead_player.level); 55 | } 56 | 57 | #[test] 58 | fn test_reviving_dead_level9_player() { 59 | let dead_player = Player { 60 | health: 0, 61 | mana: None, 62 | level: 9, 63 | }; 64 | let revived_player = dead_player 65 | .revive() 66 | .expect("reviving a dead player must return Some(player)"); 67 | assert_eq!(revived_player.health, 100); 68 | assert_eq!(revived_player.mana, None); 69 | assert_eq!(revived_player.level, dead_player.level); 70 | } 71 | 72 | #[test] 73 | fn test_reviving_dead_level10_player() { 74 | let dead_player = Player { 75 | health: 0, 76 | mana: Some(0), 77 | level: 10, 78 | }; 79 | let revived_player = dead_player 80 | .revive() 81 | .expect("reviving a dead player must return Some(player)"); 82 | assert_eq!(revived_player.health, 100); 83 | assert_eq!(revived_player.mana, Some(100)); 84 | assert_eq!(revived_player.level, dead_player.level); 85 | } 86 | 87 | #[test] 88 | fn test_reviving_alive_player() { 89 | let alive_player = Player { 90 | health: 1, 91 | mana: None, 92 | level: 8, 93 | }; 94 | assert!(alive_player.revive().is_none()); 95 | } 96 | 97 | #[test] 98 | fn test_cast_spell_with_enough_mana() { 99 | const HEALTH: u32 = 99; 100 | const MANA: u32 = 100; 101 | const LEVEL: u32 = 100; 102 | const MANA_COST: u32 = 3; 103 | 104 | let mut accomplished_wizard = Player { 105 | health: HEALTH, 106 | mana: Some(MANA), 107 | level: LEVEL, 108 | }; 109 | 110 | assert_eq!(accomplished_wizard.cast_spell(MANA_COST), MANA_COST * 2); 111 | assert_eq!(accomplished_wizard.health, HEALTH); 112 | assert_eq!(accomplished_wizard.mana, Some(MANA - MANA_COST)); 113 | assert_eq!(accomplished_wizard.level, LEVEL); 114 | } 115 | 116 | #[test] 117 | fn test_cast_spell_with_insufficient_mana() { 118 | let mut no_mana_wizard = Player { 119 | health: 56, 120 | mana: Some(2), 121 | level: 22, 122 | }; 123 | 124 | let clone = Player { ..no_mana_wizard }; 125 | 126 | assert_eq!(no_mana_wizard.cast_spell(3), 0); 127 | assert_eq!(no_mana_wizard.health, clone.health); 128 | assert_eq!(no_mana_wizard.mana, clone.mana); 129 | assert_eq!(no_mana_wizard.level, clone.level); 130 | } 131 | 132 | #[test] 133 | fn test_cast_spell_with_no_mana_pool() { 134 | const MANA_COST: u32 = 10; 135 | 136 | let mut underleveled_player = Player { 137 | health: 87, 138 | mana: None, 139 | level: 6, 140 | }; 141 | 142 | let clone = Player { 143 | ..underleveled_player 144 | }; 145 | 146 | assert_eq!(underleveled_player.cast_spell(MANA_COST), 0); 147 | assert_eq!(underleveled_player.health, clone.health - MANA_COST); 148 | assert_eq!(underleveled_player.mana, clone.mana); 149 | assert_eq!(underleveled_player.level, clone.level); 150 | } 151 | 152 | #[test] 153 | fn test_cast_large_spell_with_no_mana_pool() { 154 | const MANA_COST: u32 = 30; 155 | 156 | let mut underleveled_player = Player { 157 | health: 20, 158 | mana: None, 159 | level: 6, 160 | }; 161 | 162 | assert_eq!(underleveled_player.cast_spell(MANA_COST), 0); 163 | assert_eq!(underleveled_player.health, 0); 164 | assert_eq!(underleveled_player.mana, None); 165 | assert_eq!(underleveled_player.level, 6); 166 | } 167 | -------------------------------------------------------------------------------- /4 - Solution/220327 - Make Interpreter Rust, Week 1/prob2/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "prob2" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /4 - Solution/220327 - Make Interpreter Rust, Week 1/prob2/README.md: -------------------------------------------------------------------------------- 1 | # Instructions 2 | 3 | ## 1. Overview 4 | 5 | [Reverse Polish notation](https://en.wikipedia.org/wiki/Reverse_Polish_notation) (RPN) is a way of writing mathematical expressions. 6 | Unlike in traditional infix notation, RPN operators *follow* their operands. 7 | For example, instead of writing: 8 | 9 | ``` 10 | 2 + 2 11 | ``` 12 | 13 | you would write: 14 | 15 | ``` 16 | 2 2 + 17 | ``` 18 | 19 | The major benefit of Reverse Polish notation is that it is much simpler to parse than infix notation. 20 | RPN eliminates the need for order of operations or parentheses in complex expressions. 21 | For example: 22 | 23 | ``` 24 | (4 + 8) / (7 - 5) 25 | ``` 26 | 27 | can be written as 28 | 29 | ``` 30 | 4 8 + 7 5 - / 31 | ``` 32 | 33 | In both cases, the expression evaluates to 6. 34 | 35 | ## 2. Example 36 | 37 | Let's manually evaluate that complex expression. 38 | As we learned in the introduction, evaluation of RPN requires a stack. 39 | This stack is used to hold numeric values that the operators operate on. 40 | We start our calculator with an empty stack and then evaluate each element one at a time. 41 | 42 | First, we encounter a `4`, 43 | so we push it onto our freshly created stack. 44 | 45 | ``` 46 | 4 47 | ``` 48 | 49 | Next, we encounter an `8`. 50 | We also push that onto the stack. 51 | 52 | ``` 53 | 4 8 54 | ``` 55 | 56 | Now, we encounter a `+`. 57 | We pop off the two topmost values (4 and 8), 58 | add them together, 59 | and push the sum back onto the stack. 60 | 61 | ``` 62 | 12 63 | ``` 64 | 65 | We do something similar for `7`, `5`, and `-`: 66 | 67 | ``` 68 | 12 7 69 | 12 7 5 70 | 12 2 71 | ``` 72 | 73 | Now we encounter a `/`. 74 | Even though we last encountered a `-`, 75 | there are two elements on the stack. 76 | We pop off the two elements, 77 | divide them, 78 | and push the result back onto the stack. 79 | 80 | ``` 81 | 6 82 | ``` 83 | 84 | Finally, since there is exactly one element on the stack, 85 | we can say the expression evaluated to 6. 86 | 87 | ## 3. Goal 88 | 89 | Your goal is to write a calculator to evaluate a list of inputs ordered by Reverse Polish notation and return the final element on the stack. 90 | 91 | If there is not exactly one element in the stack at the end, return `None`. 92 | 93 | If there is an operator with too few operands (such as the input `2 +`), return `None`. 94 | 95 | You are given the following enum and stubbed function as a starting point. 96 | 97 | ```rust 98 | #[derive(Debug)] 99 | pub enum CalculatorInput { 100 | Add, 101 | Subtract, 102 | Multiply, 103 | Divide, 104 | Value(i32), 105 | } 106 | 107 | pub fn evaluate(inputs: &[CalculatorInput]) -> Option { 108 | unimplemented!( 109 | "Given the inputs: {:?}, evaluate them as though they were a Reverse Polish notation expression", 110 | inputs, 111 | ); 112 | } 113 | ``` -------------------------------------------------------------------------------- /4 - Solution/220327 - Make Interpreter Rust, Week 1/prob2/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | pub enum CalculatorInput { 3 | Add, 4 | Subtract, 5 | Multiply, 6 | Divide, 7 | Value(i32), 8 | } 9 | 10 | pub fn evaluate(inputs: &[CalculatorInput]) -> Option { 11 | use CalculatorInput::*; 12 | 13 | let mut stack: Vec = Vec::new(); 14 | 15 | for input in inputs { 16 | match input { 17 | Add => { 18 | if stack.len() < 2 { 19 | return None; 20 | } 21 | 22 | let num1 = stack.pop().unwrap(); 23 | let num2 = stack.pop().unwrap(); 24 | stack.push(num2 + num1); 25 | } 26 | Subtract => { 27 | if stack.len() < 2 { 28 | return None; 29 | } 30 | 31 | let num1 = stack.pop().unwrap(); 32 | let num2 = stack.pop().unwrap(); 33 | stack.push(num2 - num1); 34 | } 35 | Multiply => { 36 | if stack.len() < 2 { 37 | return None; 38 | } 39 | 40 | let num1 = stack.pop().unwrap(); 41 | let num2 = stack.pop().unwrap(); 42 | stack.push(num2 * num1); 43 | } 44 | Divide => { 45 | if stack.len() < 2 { 46 | return None; 47 | } 48 | 49 | let num1 = stack.pop().unwrap(); 50 | let num2 = stack.pop().unwrap(); 51 | stack.push(num2 / num1); 52 | } 53 | Value(val) => { 54 | stack.push(*val); 55 | } 56 | } 57 | } 58 | 59 | if stack.len() != 1 { 60 | None 61 | } else { 62 | Some(*stack.last().unwrap()) 63 | } 64 | } 65 | 66 | #[cfg(test)] 67 | fn calculator_input(s: &str) -> Vec { 68 | s.split_whitespace() 69 | .map(|s| match s { 70 | "+" => CalculatorInput::Add, 71 | "-" => CalculatorInput::Subtract, 72 | "*" => CalculatorInput::Multiply, 73 | "/" => CalculatorInput::Divide, 74 | n => CalculatorInput::Value(n.parse().unwrap()), 75 | }) 76 | .collect() 77 | } 78 | 79 | #[test] 80 | fn test_empty_input_returns_none() { 81 | let input = calculator_input(""); 82 | assert_eq!(evaluate(&input), None); 83 | } 84 | 85 | #[test] 86 | fn test_simple_value() { 87 | let input = calculator_input("10"); 88 | assert_eq!(evaluate(&input), Some(10)); 89 | } 90 | 91 | #[test] 92 | fn test_simple_addition() { 93 | let input = calculator_input("2 2 +"); 94 | assert_eq!(evaluate(&input), Some(4)); 95 | } 96 | 97 | #[test] 98 | fn test_simple_subtraction() { 99 | let input = calculator_input("7 11 -"); 100 | assert_eq!(evaluate(&input), Some(-4)); 101 | } 102 | 103 | #[test] 104 | fn test_simple_multiplication() { 105 | let input = calculator_input("6 9 *"); 106 | assert_eq!(evaluate(&input), Some(54)); 107 | } 108 | 109 | #[test] 110 | fn test_simple_division() { 111 | let input = calculator_input("57 19 /"); 112 | assert_eq!(evaluate(&input), Some(3)); 113 | } 114 | 115 | #[test] 116 | fn test_complex_operation() { 117 | let input = calculator_input("4 8 + 7 5 - /"); 118 | assert_eq!(evaluate(&input), Some(6)); 119 | } 120 | 121 | #[test] 122 | fn test_too_few_operands_returns_none() { 123 | let input = calculator_input("2 +"); 124 | assert_eq!(evaluate(&input), None); 125 | } 126 | 127 | #[test] 128 | fn test_too_many_operands_returns_none() { 129 | let input = calculator_input("2 2"); 130 | assert_eq!(evaluate(&input), None); 131 | } 132 | 133 | #[test] 134 | fn test_zero_operands_returns_none() { 135 | let input = calculator_input("+"); 136 | assert_eq!(evaluate(&input), None); 137 | } 138 | 139 | #[test] 140 | fn test_intermediate_error_returns_none() { 141 | let input = calculator_input("+ 2 2 *"); 142 | assert_eq!(evaluate(&input), None); 143 | } 144 | -------------------------------------------------------------------------------- /4 - Solution/220327 - Make Interpreter Rust, Week 1/prob3/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "prob3" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /4 - Solution/220327 - Make Interpreter Rust, Week 1/prob3/README.md: -------------------------------------------------------------------------------- 1 | # Instructions 2 | 3 | Score a bowling game. 4 | 5 | Bowling is a game where players roll a heavy ball to knock down pins 6 | arranged in a triangle. Write code to keep track of the score 7 | of a game of bowling. 8 | 9 | ## Scoring Bowling 10 | 11 | The game consists of 10 frames. A frame is composed of one or two ball 12 | throws with 10 pins standing at frame initialization. There are three 13 | cases for the tabulation of a frame. 14 | 15 | * An open frame is where a score of less than 10 is recorded for the 16 | frame. In this case the score for the frame is the number of pins 17 | knocked down. 18 | 19 | * A spare is where all ten pins are knocked down by the second 20 | throw. The total value of a spare is 10 plus the number of pins 21 | knocked down in their next throw. 22 | 23 | * A strike is where all ten pins are knocked down by the first 24 | throw. The total value of a strike is 10 plus the number of pins 25 | knocked down in the next two throws. If a strike is immediately 26 | followed by a second strike, then the value of the first strike 27 | cannot be determined until the ball is thrown one more time. 28 | 29 | Here is a three frame example: 30 | 31 | | Frame 1 | Frame 2 | Frame 3 | 32 | | :-------------: |:-------------:| :---------------------:| 33 | | X (strike) | 5/ (spare) | 9 0 (open frame) | 34 | 35 | Frame 1 is (10 + 5 + 5) = 20 36 | 37 | Frame 2 is (5 + 5 + 9) = 19 38 | 39 | Frame 3 is (9 + 0) = 9 40 | 41 | This means the current running total is 48. 42 | 43 | The tenth frame in the game is a special case. If someone throws a 44 | strike or a spare then they get a fill ball. Fill balls exist to 45 | calculate the total of the 10th frame. Scoring a strike or spare on 46 | the fill ball does not give the player more fill balls. The total 47 | value of the 10th frame is the total number of pins knocked down. 48 | 49 | For a tenth frame of X1/ (strike and a spare), the total value is 20. 50 | 51 | For a tenth frame of XXX (three strikes), the total value is 30. 52 | 53 | ## Requirements 54 | 55 | Write code to keep track of the score of a game of bowling. It should 56 | support two operations: 57 | 58 | * `roll(pins : int)` is called each time the player rolls a ball. The 59 | argument is the number of pins knocked down. 60 | * `score() : int` is called only at the very end of the game. It 61 | returns the total score for that game. -------------------------------------------------------------------------------- /4 - Solution/220327 - Make Interpreter Rust, Week 1/prob3/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, PartialEq)] 2 | pub enum Error { 3 | NotEnoughPinsLeft, 4 | GameComplete, 5 | } 6 | 7 | #[derive(Debug, Default)] 8 | pub struct BowlingGame { 9 | throws: Vec, 10 | second: bool, 11 | } 12 | 13 | impl BowlingGame { 14 | pub fn new() -> Self { 15 | Default::default() 16 | } 17 | 18 | pub fn roll(&mut self, pins: u16) -> Result<(), Error> { 19 | if pins > 10 || (self.second && pins + self.throws.last().unwrap() > 10) { 20 | Err(Error::NotEnoughPinsLeft) 21 | } else if self.score().is_some() { 22 | Err(Error::GameComplete) 23 | } else { 24 | self.throws.push(pins); 25 | self.second = if pins != 10 { !self.second } else { false }; 26 | 27 | Ok(()) 28 | } 29 | } 30 | 31 | pub fn score(&self) -> Option { 32 | let mut total = 0; 33 | let mut frame = 0; 34 | let throws = &self.throws; 35 | 36 | for _ in 0..10 { 37 | if let (Some(&first), Some(&second)) = (throws.get(frame), throws.get(frame + 1)) { 38 | total += first + second; 39 | 40 | if first == 10 || first + second == 10 { 41 | if let Some(&third) = throws.get(frame + 2) { 42 | total += third; 43 | } else { 44 | return None; 45 | } 46 | } 47 | 48 | frame += if first == 10 { 1 } else { 2 }; 49 | } else { 50 | return None; 51 | } 52 | } 53 | 54 | Some(total) 55 | } 56 | } 57 | 58 | #[test] 59 | fn roll_returns_a_result() { 60 | let mut game = BowlingGame::new(); 61 | assert!(game.roll(0).is_ok()); 62 | } 63 | 64 | #[test] 65 | fn you_cannot_roll_more_than_ten_pins_in_a_single_roll() { 66 | let mut game = BowlingGame::new(); 67 | 68 | assert_eq!(game.roll(11), Err(Error::NotEnoughPinsLeft)); 69 | } 70 | 71 | #[test] 72 | fn a_game_score_is_some_if_ten_frames_have_been_rolled() { 73 | let mut game = BowlingGame::new(); 74 | 75 | for _ in 0..10 { 76 | let _ = game.roll(0); 77 | let _ = game.roll(0); 78 | } 79 | 80 | assert!(game.score().is_some()); 81 | } 82 | 83 | #[test] 84 | fn you_cannot_score_a_game_with_no_rolls() { 85 | let game = BowlingGame::new(); 86 | 87 | assert_eq!(game.score(), None); 88 | } 89 | 90 | #[test] 91 | fn a_game_score_is_none_if_fewer_than_ten_frames_have_been_rolled() { 92 | let mut game = BowlingGame::new(); 93 | 94 | for _ in 0..9 { 95 | let _ = game.roll(0); 96 | let _ = game.roll(0); 97 | } 98 | 99 | assert_eq!(game.score(), None); 100 | } 101 | 102 | #[test] 103 | fn a_roll_is_err_if_the_game_is_done() { 104 | let mut game = BowlingGame::new(); 105 | 106 | for _ in 0..10 { 107 | let _ = game.roll(0); 108 | let _ = game.roll(0); 109 | } 110 | 111 | assert_eq!(game.roll(0), Err(Error::GameComplete)); 112 | } 113 | 114 | #[test] 115 | fn twenty_zero_pin_rolls_scores_zero() { 116 | let mut game = BowlingGame::new(); 117 | 118 | for _ in 0..20 { 119 | let _ = game.roll(0); 120 | } 121 | 122 | assert_eq!(game.score(), Some(0)); 123 | } 124 | 125 | #[test] 126 | fn ten_frames_without_a_strike_or_spare() { 127 | let mut game = BowlingGame::new(); 128 | 129 | for _ in 0..10 { 130 | let _ = game.roll(3); 131 | let _ = game.roll(6); 132 | } 133 | 134 | assert_eq!(game.score(), Some(90)); 135 | } 136 | 137 | #[test] 138 | fn spare_in_the_first_frame_followed_by_zeros() { 139 | let mut game = BowlingGame::new(); 140 | 141 | let _ = game.roll(6); 142 | let _ = game.roll(4); 143 | 144 | for _ in 0..18 { 145 | let _ = game.roll(0); 146 | } 147 | 148 | assert_eq!(game.score(), Some(10)); 149 | } 150 | 151 | #[test] 152 | fn points_scored_in_the_roll_after_a_spare_are_counted_twice_as_a_bonus() { 153 | let mut game = BowlingGame::new(); 154 | 155 | let _ = game.roll(6); 156 | let _ = game.roll(4); 157 | let _ = game.roll(3); 158 | 159 | for _ in 0..17 { 160 | let _ = game.roll(0); 161 | } 162 | 163 | assert_eq!(game.score(), Some(16)); 164 | } 165 | 166 | #[test] 167 | fn consecutive_spares_each_get_a_one_roll_bonus() { 168 | let mut game = BowlingGame::new(); 169 | 170 | let _ = game.roll(5); 171 | let _ = game.roll(5); 172 | let _ = game.roll(3); 173 | let _ = game.roll(7); 174 | let _ = game.roll(4); 175 | 176 | for _ in 0..15 { 177 | let _ = game.roll(0); 178 | } 179 | 180 | assert_eq!(game.score(), Some(31)); 181 | } 182 | 183 | #[test] 184 | fn if_the_last_frame_is_a_spare_you_get_one_extra_roll_that_is_scored_once() { 185 | let mut game = BowlingGame::new(); 186 | 187 | for _ in 0..18 { 188 | let _ = game.roll(0); 189 | } 190 | 191 | let _ = game.roll(5); 192 | let _ = game.roll(5); 193 | let _ = game.roll(7); 194 | 195 | assert_eq!(game.score(), Some(17)); 196 | } 197 | 198 | #[test] 199 | fn a_strike_earns_ten_points_in_a_frame_with_a_single_roll() { 200 | let mut game = BowlingGame::new(); 201 | 202 | let _ = game.roll(10); 203 | 204 | for _ in 0..18 { 205 | let _ = game.roll(0); 206 | } 207 | 208 | assert_eq!(game.score(), Some(10)); 209 | } 210 | 211 | #[test] 212 | fn points_scored_in_the_two_rolls_after_a_strike_are_counted_twice_as_a_bonus() { 213 | let mut game = BowlingGame::new(); 214 | 215 | let _ = game.roll(10); 216 | let _ = game.roll(5); 217 | let _ = game.roll(3); 218 | 219 | for _ in 0..16 { 220 | let _ = game.roll(0); 221 | } 222 | 223 | assert_eq!(game.score(), Some(26)); 224 | } 225 | 226 | #[test] 227 | fn consecutive_strikes_each_get_the_two_roll_bonus() { 228 | let mut game = BowlingGame::new(); 229 | 230 | let _ = game.roll(10); 231 | let _ = game.roll(10); 232 | let _ = game.roll(10); 233 | let _ = game.roll(5); 234 | let _ = game.roll(3); 235 | 236 | for _ in 0..12 { 237 | let _ = game.roll(0); 238 | } 239 | 240 | assert_eq!(game.score(), Some(81)); 241 | } 242 | 243 | #[test] 244 | fn a_strike_in_the_last_frame_earns_a_two_roll_bonus_that_is_counted_once() { 245 | let mut game = BowlingGame::new(); 246 | 247 | for _ in 0..18 { 248 | let _ = game.roll(0); 249 | } 250 | 251 | let _ = game.roll(10); 252 | let _ = game.roll(7); 253 | let _ = game.roll(1); 254 | 255 | assert_eq!(game.score(), Some(18)); 256 | } 257 | 258 | #[test] 259 | fn a_spare_with_the_two_roll_bonus_does_not_get_a_bonus_roll() { 260 | let mut game = BowlingGame::new(); 261 | 262 | for _ in 0..18 { 263 | let _ = game.roll(0); 264 | } 265 | 266 | let _ = game.roll(10); 267 | let _ = game.roll(7); 268 | let _ = game.roll(3); 269 | 270 | assert_eq!(game.score(), Some(20)); 271 | } 272 | 273 | #[test] 274 | fn strikes_with_the_two_roll_bonus_do_not_get_a_bonus_roll() { 275 | let mut game = BowlingGame::new(); 276 | 277 | for _ in 0..18 { 278 | let _ = game.roll(0); 279 | } 280 | 281 | let _ = game.roll(10); 282 | let _ = game.roll(10); 283 | let _ = game.roll(10); 284 | 285 | assert_eq!(game.score(), Some(30)); 286 | } 287 | 288 | #[test] 289 | fn a_strike_with_the_one_roll_bonus_after_a_spare_in_the_last_frame_does_not_get_a_bonus() { 290 | let mut game = BowlingGame::new(); 291 | 292 | for _ in 0..18 { 293 | let _ = game.roll(0); 294 | } 295 | 296 | let _ = game.roll(7); 297 | let _ = game.roll(3); 298 | let _ = game.roll(10); 299 | 300 | assert_eq!(game.score(), Some(20)); 301 | } 302 | 303 | #[test] 304 | fn all_strikes_is_a_perfect_score_of_300() { 305 | let mut game = BowlingGame::new(); 306 | 307 | for _ in 0..12 { 308 | let _ = game.roll(10); 309 | } 310 | 311 | assert_eq!(game.score(), Some(300)); 312 | } 313 | 314 | #[test] 315 | fn you_cannot_roll_more_than_ten_pins_in_a_single_frame() { 316 | let mut game = BowlingGame::new(); 317 | 318 | assert!(game.roll(5).is_ok()); 319 | assert_eq!(game.roll(6), Err(Error::NotEnoughPinsLeft)); 320 | } 321 | 322 | #[test] 323 | fn first_bonus_ball_after_a_final_strike_cannot_score_an_invalid_number_of_pins() { 324 | let mut game = BowlingGame::new(); 325 | 326 | for _ in 0..18 { 327 | let _ = game.roll(0); 328 | } 329 | 330 | let _ = game.roll(10); 331 | 332 | assert_eq!(game.roll(11), Err(Error::NotEnoughPinsLeft)); 333 | } 334 | 335 | #[test] 336 | fn the_two_balls_after_a_final_strike_cannot_score_an_invalid_number_of_pins() { 337 | let mut game = BowlingGame::new(); 338 | 339 | for _ in 0..18 { 340 | let _ = game.roll(0); 341 | } 342 | 343 | let _ = game.roll(10); 344 | 345 | assert!(game.roll(5).is_ok()); 346 | assert_eq!(game.roll(6), Err(Error::NotEnoughPinsLeft)); 347 | } 348 | 349 | #[test] 350 | fn the_two_balls_after_a_final_strike_can_be_a_strike_and_non_strike() { 351 | let mut game = BowlingGame::new(); 352 | 353 | for _ in 0..18 { 354 | let _ = game.roll(0); 355 | } 356 | 357 | let _ = game.roll(10); 358 | 359 | assert!(game.roll(10).is_ok()); 360 | assert!(game.roll(6).is_ok()); 361 | } 362 | 363 | #[test] 364 | fn the_two_balls_after_a_final_strike_cannot_be_a_non_strike_followed_by_a_strike() { 365 | let mut game = BowlingGame::new(); 366 | 367 | for _ in 0..18 { 368 | let _ = game.roll(0); 369 | } 370 | 371 | let _ = game.roll(10); 372 | 373 | assert!(game.roll(6).is_ok()); 374 | assert_eq!(game.roll(10), Err(Error::NotEnoughPinsLeft)); 375 | } 376 | 377 | #[test] 378 | fn second_bonus_ball_after_a_final_strike_cannot_score_an_invalid_number_of_pins_even_if_first_is_strike( 379 | ) { 380 | let mut game = BowlingGame::new(); 381 | 382 | for _ in 0..18 { 383 | let _ = game.roll(0); 384 | } 385 | 386 | let _ = game.roll(10); 387 | 388 | assert!(game.roll(10).is_ok()); 389 | assert_eq!(game.roll(11), Err(Error::NotEnoughPinsLeft)); 390 | } 391 | 392 | #[test] 393 | fn if_the_last_frame_is_a_strike_you_cannot_score_before_the_extra_rolls_are_taken() { 394 | let mut game = BowlingGame::new(); 395 | 396 | for _ in 0..18 { 397 | let _ = game.roll(0); 398 | } 399 | 400 | let _ = game.roll(10); 401 | 402 | assert_eq!(game.score(), None); 403 | 404 | let _ = game.roll(10); 405 | 406 | assert_eq!(game.score(), None); 407 | 408 | let _ = game.roll(10); 409 | 410 | assert!(game.score().is_some()); 411 | } 412 | 413 | #[test] 414 | fn if_the_last_frame_is_a_spare_you_cannot_create_a_score_before_extra_roll_is_taken() { 415 | let mut game = BowlingGame::new(); 416 | 417 | for _ in 0..18 { 418 | let _ = game.roll(0); 419 | } 420 | 421 | let _ = game.roll(5); 422 | let _ = game.roll(5); 423 | 424 | assert_eq!(game.score(), None); 425 | 426 | let _ = game.roll(10); 427 | 428 | assert!(game.score().is_some()); 429 | } 430 | 431 | #[test] 432 | fn cannot_roll_after_bonus_roll_for_spare() { 433 | let mut game = BowlingGame::new(); 434 | 435 | for _ in 0..9 { 436 | let _ = game.roll(0); 437 | let _ = game.roll(0); 438 | } 439 | let _ = game.roll(7); 440 | let _ = game.roll(3); 441 | assert!(game.roll(2).is_ok()); 442 | 443 | assert_eq!(game.roll(2), Err(Error::GameComplete)); 444 | } 445 | 446 | #[test] 447 | fn cannot_roll_after_bonus_roll_for_strike() { 448 | let mut game = BowlingGame::new(); 449 | 450 | for _ in 0..9 { 451 | let _ = game.roll(0); 452 | let _ = game.roll(0); 453 | } 454 | let _ = game.roll(10); 455 | let _ = game.roll(3); 456 | assert!(game.roll(2).is_ok()); 457 | 458 | assert_eq!(game.roll(2), Err(Error::GameComplete)); 459 | } 460 | 461 | #[test] 462 | fn last_two_strikes_followed_by_only_last_bonus_with_non_strike_points() { 463 | let mut game = BowlingGame::new(); 464 | for _ in 0..16 { 465 | let _ = game.roll(0); 466 | } 467 | let _ = game.roll(10); 468 | let _ = game.roll(10); 469 | let _ = game.roll(0); 470 | let _ = game.roll(1); 471 | 472 | assert_eq!(game.score(), Some(31)); 473 | } 474 | -------------------------------------------------------------------------------- /4 - Solution/220410 - Make Interpreter Rust, Week 2/README.md: -------------------------------------------------------------------------------- 1 | # Assignment 2 - Solution 2 | 3 | You should pass all tests by running `cargo test`. 4 | 5 | 1. [Word Count](./prob1) 6 | 7 | 2. [Circular Buffer](./prob2) 8 | 9 | 3. [Simple Linked List](./prob3) 10 | 11 | 4. [Doubly Linked List](./prob4) -------------------------------------------------------------------------------- /4 - Solution/220410 - Make Interpreter Rust, Week 2/prob1/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "prob1" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /4 - Solution/220410 - Make Interpreter Rust, Week 2/prob1/README.md: -------------------------------------------------------------------------------- 1 | # Instructions 2 | 3 | Given a phrase, count the occurrences of each _word_ in that phrase. 4 | 5 | For the purposes of this exercise you can expect that a _word_ will always be one of: 6 | 7 | 1. A _number_ composed of one or more ASCII digits (i.e. "0" or "1234") OR 8 | 2. A _simple word_ composed of one or more ASCII letters (i.e. "a" or "they") OR 9 | 3. A _contraction_ of two _simple words_ joined by a single apostrophe (i.e. "it's" or "they're") 10 | 11 | When counting words you can assume the following rules: 12 | 13 | 1. The count is _case insensitive_ (i.e. "You", "you", and "YOU" are 3 uses of the same word) 14 | 2. The count is _unordered_; the tests will ignore how words and counts are ordered 15 | 3. Other than the apostrophe in a _contraction_ all forms of _punctuation_ are ignored 16 | 4. The words can be separated by _any_ form of whitespace (i.e. "\t", "\n", " "), or 17 | external punctuation. 18 | 19 | For example, for the phrase `"That's the password: 'PASSWORD 123'!", cried the Special Agent.\nSo I fled.` the count would be: 20 | 21 | ```text 22 | that's: 1 23 | the: 2 24 | password: 2 25 | 123: 1 26 | cried: 1 27 | special: 1 28 | agent: 1 29 | so: 1 30 | i: 1 31 | fled: 1 32 | ``` 33 | 34 | For the phrase `"one,two,three"` the count would be: 35 | 36 | ```text 37 | one: 1 38 | two: 1 39 | three: 1 40 | ``` 41 | -------------------------------------------------------------------------------- /4 - Solution/220410 - Make Interpreter Rust, Week 2/prob1/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | /// Count occurrences of words. 4 | pub fn word_count(words: &str) -> HashMap { 5 | let mut ret = HashMap::new(); 6 | 7 | for word in words 8 | .split(|c: char| !c.is_alphanumeric() && c != '\'') 9 | .filter(|w| !w.is_empty()) 10 | .map(|w| w.trim_matches('\'').to_lowercase()) 11 | { 12 | *ret.entry(word).or_insert(0) += 1; 13 | } 14 | 15 | ret 16 | } 17 | 18 | #[cfg(test)] 19 | fn check_word_count(s: &str, pairs: &[(&str, u32)]) { 20 | // The reason for the awkward code in here is to ensure that the failure 21 | // message for assert_eq! is as informative as possible. A simpler 22 | // solution would simply check the length of the map, and then 23 | // check for the presence and value of each key in the given pairs vector. 24 | let mut m: HashMap = word_count(s); 25 | for &(k, v) in pairs.iter() { 26 | assert_eq!((k, m.remove(&k.to_string()).unwrap_or(0)), (k, v)); 27 | } 28 | // may fail with a message that clearly shows all extra pairs in the map 29 | assert_eq!(m.iter().collect::>(), vec![]); 30 | } 31 | 32 | #[test] 33 | fn test_count_one_word() { 34 | check_word_count("word", &[("word", 1)]); 35 | } 36 | 37 | #[test] 38 | fn test_count_one_of_each() { 39 | check_word_count("one of each", &[("one", 1), ("of", 1), ("each", 1)]); 40 | } 41 | 42 | #[test] 43 | fn test_count_multiple_occurrences() { 44 | check_word_count( 45 | "one fish two fish red fish blue fish", 46 | &[("one", 1), ("fish", 4), ("two", 1), ("red", 1), ("blue", 1)], 47 | ); 48 | } 49 | 50 | #[test] 51 | fn cramped_lists() { 52 | check_word_count("one,two,three", &[("one", 1), ("two", 1), ("three", 1)]); 53 | } 54 | 55 | #[test] 56 | fn expanded_lists() { 57 | check_word_count("one\ntwo\nthree", &[("one", 1), ("two", 1), ("three", 1)]); 58 | } 59 | 60 | #[test] 61 | fn test_ignore_punctuation() { 62 | check_word_count( 63 | "car : carpet as java : javascript!!&@$%^&", 64 | &[ 65 | ("car", 1), 66 | ("carpet", 1), 67 | ("as", 1), 68 | ("java", 1), 69 | ("javascript", 1), 70 | ], 71 | ); 72 | } 73 | 74 | #[test] 75 | fn test_include_numbers() { 76 | check_word_count( 77 | "testing, 1, 2 testing", 78 | &[("testing", 2), ("1", 1), ("2", 1)], 79 | ); 80 | } 81 | 82 | #[test] 83 | fn test_normalize_case() { 84 | check_word_count("go Go GO Stop stop", &[("go", 3), ("stop", 2)]); 85 | } 86 | 87 | #[test] 88 | fn with_apostrophes() { 89 | check_word_count( 90 | "First: don't laugh. Then: don't cry.", 91 | &[ 92 | ("first", 1), 93 | ("don't", 2), 94 | ("laugh", 1), 95 | ("then", 1), 96 | ("cry", 1), 97 | ], 98 | ); 99 | } 100 | 101 | #[test] 102 | fn with_quotations() { 103 | check_word_count( 104 | "Joe can't tell between 'large' and large.", 105 | &[ 106 | ("joe", 1), 107 | ("can't", 1), 108 | ("tell", 1), 109 | ("between", 1), 110 | ("large", 2), 111 | ("and", 1), 112 | ], 113 | ); 114 | } 115 | 116 | #[test] 117 | fn multiple_spaces_not_detected_as_a_word() { 118 | check_word_count( 119 | " multiple whitespaces", 120 | &[("multiple", 1), ("whitespaces", 1)], 121 | ); 122 | } -------------------------------------------------------------------------------- /4 - Solution/220410 - Make Interpreter Rust, Week 2/prob2/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "prob2" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /4 - Solution/220410 - Make Interpreter Rust, Week 2/prob2/README.md: -------------------------------------------------------------------------------- 1 | # Instructions 2 | 3 | A circular buffer, cyclic buffer or ring buffer is a data structure that 4 | uses a single, fixed-size buffer as if it were connected end-to-end. 5 | 6 | A circular buffer first starts empty and of some predefined length. For 7 | example, this is a 7-element buffer: 8 | 9 | [ ][ ][ ][ ][ ][ ][ ] 10 | 11 | Assume that a 1 is written into the middle of the buffer (exact starting 12 | location does not matter in a circular buffer): 13 | 14 | [ ][ ][ ][1][ ][ ][ ] 15 | 16 | Then assume that two more elements are added — 2 & 3 — which get 17 | appended after the 1: 18 | 19 | [ ][ ][ ][1][2][3][ ] 20 | 21 | If two elements are then removed from the buffer, the oldest values 22 | inside the buffer are removed. The two elements removed, in this case, 23 | are 1 & 2, leaving the buffer with just a 3: 24 | 25 | [ ][ ][ ][ ][ ][3][ ] 26 | 27 | If the buffer has 7 elements then it is completely full: 28 | 29 | [5][6][7][8][9][3][4] 30 | 31 | When the buffer is full an error will be raised, alerting the client 32 | that further writes are blocked until a slot becomes free. 33 | 34 | When the buffer is full, the client can opt to overwrite the oldest 35 | data with a forced write. In this case, two more elements — A & B — 36 | are added and they overwrite the 3 & 4: 37 | 38 | [5][6][7][8][9][A][B] 39 | 40 | 3 & 4 have been replaced by A & B making 5 now the oldest data in the 41 | buffer. Finally, if two elements are removed then what would be 42 | returned is 5 & 6 yielding the buffer: 43 | 44 | [ ][ ][7][8][9][A][B] 45 | 46 | Because there is space available, if the client again uses overwrite 47 | to store C & D then the space where 5 & 6 were stored previously will 48 | be used not the location of 7 & 8. 7 is still the oldest element and 49 | the buffer is once again full. 50 | 51 | [C][D][7][8][9][A][B] 52 | -------------------------------------------------------------------------------- /4 - Solution/220410 - Make Interpreter Rust, Week 2/prob2/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | use std::rc::Rc; 3 | 4 | pub struct CircularBuffer { 5 | data: Vec>, 6 | read_index: usize, 7 | write_index: usize, 8 | } 9 | 10 | #[derive(Debug, PartialEq)] 11 | pub enum Error { 12 | EmptyBuffer, 13 | FullBuffer, 14 | } 15 | 16 | impl CircularBuffer { 17 | pub fn new(capacity: usize) -> Self { 18 | Self { 19 | data: (0..capacity).map(|_| None).collect(), 20 | read_index: 0, 21 | write_index: 0, 22 | } 23 | } 24 | 25 | pub fn write(&mut self, element: T) -> Result<(), Error> { 26 | if self.is_full() { 27 | return Err(Error::FullBuffer); 28 | } 29 | 30 | self.write_without_check(element); 31 | Ok(()) 32 | } 33 | 34 | pub fn read(&mut self) -> Result { 35 | self.data[self.read_index] 36 | .take() 37 | .ok_or(Error::EmptyBuffer) 38 | .map(|value| { 39 | self.read_index = self.increase_index(self.read_index); 40 | value 41 | }) 42 | } 43 | 44 | pub fn clear(&mut self) { 45 | self.data = (0..self.data.len()).map(|_| None).collect(); 46 | self.read_index = 0; 47 | self.write_index = 0; 48 | } 49 | 50 | pub fn overwrite(&mut self, element: T) { 51 | let is_overwriting = self.is_full(); 52 | self.write_without_check(element); 53 | if is_overwriting { 54 | self.read_index = self.increase_index(self.read_index); 55 | } 56 | } 57 | 58 | fn is_full(&self) -> bool { 59 | self.data[self.write_index].is_some() 60 | } 61 | 62 | fn increase_index(&self, index: usize) -> usize { 63 | (index + 1) % self.data.len() 64 | } 65 | 66 | fn write_without_check(&mut self, element: T) { 67 | self.data[self.write_index] = Some(element); 68 | self.write_index = self.increase_index(self.write_index); 69 | } 70 | } 71 | 72 | #[test] 73 | fn error_on_read_empty_buffer() { 74 | let mut buffer = CircularBuffer::::new(1); 75 | assert_eq!(Err(Error::EmptyBuffer), buffer.read()); 76 | } 77 | 78 | #[test] 79 | fn can_read_item_just_written() { 80 | let mut buffer = CircularBuffer::new(1); 81 | assert!(buffer.write('1').is_ok()); 82 | assert_eq!(Ok('1'), buffer.read()); 83 | } 84 | 85 | #[test] 86 | fn each_item_may_only_be_read_once() { 87 | let mut buffer = CircularBuffer::new(1); 88 | assert!(buffer.write('1').is_ok()); 89 | assert_eq!(Ok('1'), buffer.read()); 90 | assert_eq!(Err(Error::EmptyBuffer), buffer.read()); 91 | } 92 | 93 | #[test] 94 | fn items_are_read_in_the_order_they_are_written() { 95 | let mut buffer = CircularBuffer::new(2); 96 | assert!(buffer.write('1').is_ok()); 97 | assert!(buffer.write('2').is_ok()); 98 | assert_eq!(Ok('1'), buffer.read()); 99 | assert_eq!(Ok('2'), buffer.read()); 100 | assert_eq!(Err(Error::EmptyBuffer), buffer.read()); 101 | } 102 | 103 | #[test] 104 | fn full_buffer_cant_be_written_to() { 105 | let mut buffer = CircularBuffer::new(1); 106 | assert!(buffer.write('1').is_ok()); 107 | assert_eq!(Err(Error::FullBuffer), buffer.write('2')); 108 | } 109 | 110 | #[test] 111 | fn read_frees_up_capacity_for_another_write() { 112 | let mut buffer = CircularBuffer::new(1); 113 | assert!(buffer.write('1').is_ok()); 114 | assert_eq!(Ok('1'), buffer.read()); 115 | assert!(buffer.write('2').is_ok()); 116 | assert_eq!(Ok('2'), buffer.read()); 117 | } 118 | 119 | #[test] 120 | fn read_position_is_maintained_even_across_multiple_writes() { 121 | let mut buffer = CircularBuffer::new(3); 122 | assert!(buffer.write('1').is_ok()); 123 | assert!(buffer.write('2').is_ok()); 124 | assert_eq!(Ok('1'), buffer.read()); 125 | assert!(buffer.write('3').is_ok()); 126 | assert_eq!(Ok('2'), buffer.read()); 127 | assert_eq!(Ok('3'), buffer.read()); 128 | } 129 | 130 | #[test] 131 | fn items_cleared_out_of_buffer_cant_be_read() { 132 | let mut buffer = CircularBuffer::new(1); 133 | assert!(buffer.write('1').is_ok()); 134 | buffer.clear(); 135 | assert_eq!(Err(Error::EmptyBuffer), buffer.read()); 136 | } 137 | 138 | #[test] 139 | fn clear_frees_up_capacity_for_another_write() { 140 | let mut buffer = CircularBuffer::new(1); 141 | assert!(buffer.write('1').is_ok()); 142 | buffer.clear(); 143 | assert!(buffer.write('2').is_ok()); 144 | assert_eq!(Ok('2'), buffer.read()); 145 | } 146 | 147 | #[test] 148 | fn clear_does_nothing_on_empty_buffer() { 149 | let mut buffer = CircularBuffer::new(1); 150 | buffer.clear(); 151 | assert!(buffer.write('1').is_ok()); 152 | assert_eq!(Ok('1'), buffer.read()); 153 | } 154 | 155 | #[test] 156 | fn clear_actually_frees_up_its_elements() { 157 | let mut buffer = CircularBuffer::new(1); 158 | let element = Rc::new(()); 159 | assert!(buffer.write(Rc::clone(&element)).is_ok()); 160 | assert_eq!(Rc::strong_count(&element), 2); 161 | buffer.clear(); 162 | assert_eq!(Rc::strong_count(&element), 1); 163 | } 164 | 165 | #[test] 166 | fn overwrite_acts_like_write_on_non_full_buffer() { 167 | let mut buffer = CircularBuffer::new(2); 168 | assert!(buffer.write('1').is_ok()); 169 | buffer.overwrite('2'); 170 | assert_eq!(Ok('1'), buffer.read()); 171 | assert_eq!(Ok('2'), buffer.read()); 172 | assert_eq!(Err(Error::EmptyBuffer), buffer.read()); 173 | } 174 | 175 | #[test] 176 | fn overwrite_replaces_the_oldest_item_on_full_buffer() { 177 | let mut buffer = CircularBuffer::new(2); 178 | assert!(buffer.write('1').is_ok()); 179 | assert!(buffer.write('2').is_ok()); 180 | buffer.overwrite('A'); 181 | assert_eq!(Ok('2'), buffer.read()); 182 | assert_eq!(Ok('A'), buffer.read()); 183 | } 184 | 185 | #[test] 186 | fn overwrite_replaces_the_oldest_item_remaining_in_buffer_following_a_read() { 187 | let mut buffer = CircularBuffer::new(3); 188 | assert!(buffer.write('1').is_ok()); 189 | assert!(buffer.write('2').is_ok()); 190 | assert!(buffer.write('3').is_ok()); 191 | assert_eq!(Ok('1'), buffer.read()); 192 | assert!(buffer.write('4').is_ok()); 193 | buffer.overwrite('5'); 194 | assert_eq!(Ok('3'), buffer.read()); 195 | assert_eq!(Ok('4'), buffer.read()); 196 | assert_eq!(Ok('5'), buffer.read()); 197 | } 198 | 199 | #[test] 200 | fn integer_buffer() { 201 | let mut buffer = CircularBuffer::new(2); 202 | assert!(buffer.write(1).is_ok()); 203 | assert!(buffer.write(2).is_ok()); 204 | assert_eq!(Ok(1), buffer.read()); 205 | assert!(buffer.write(-1).is_ok()); 206 | assert_eq!(Ok(2), buffer.read()); 207 | assert_eq!(Ok(-1), buffer.read()); 208 | assert_eq!(Err(Error::EmptyBuffer), buffer.read()); 209 | } 210 | 211 | #[test] 212 | fn string_buffer() { 213 | let mut buffer = CircularBuffer::new(2); 214 | buffer.write("".to_string()).unwrap(); 215 | buffer.write("Testing".to_string()).unwrap(); 216 | assert_eq!(0, buffer.read().unwrap().len()); 217 | assert_eq!(Ok("Testing".to_string()), buffer.read()); 218 | } 219 | -------------------------------------------------------------------------------- /4 - Solution/220410 - Make Interpreter Rust, Week 2/prob3/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "prob3" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /4 - Solution/220410 - Make Interpreter Rust, Week 2/prob3/README.md: -------------------------------------------------------------------------------- 1 | # Instructions 2 | 3 | Write a simple linked list implementation that uses Elements and a List. 4 | 5 | The linked list is a fundamental data structure in computer science, 6 | often used in the implementation of other data structures. They're 7 | pervasive in functional programming languages, such as Clojure, Erlang, 8 | or Haskell, but far less common in imperative languages such as Ruby or 9 | Python. 10 | 11 | The simplest kind of linked list is a singly linked list. Each element in the 12 | list contains data and a "next" field pointing to the next element in the list 13 | of elements. 14 | 15 | This variant of linked lists is often used to represent sequences or 16 | push-down stacks (also called a LIFO stack; Last In, First Out). 17 | 18 | As a first take, lets create a singly linked list to contain the range (1..10), 19 | and provide functions to reverse a linked list and convert to and from arrays. 20 | 21 | When implementing this in a language with built-in linked lists, 22 | implement your own abstract data type. 23 | 24 |
25 | Implementation Hints 26 |
27 | Do not implement the struct `SimpleLinkedList` as a wrapper around a `Vec`. Instead, allocate nodes on the heap. 28 | This might be implemented as: 29 | ``` 30 | pub struct SimpleLinkedList { 31 | head: Option>>, 32 | } 33 | ``` 34 | The `head` field points to the first element (Node) of this linked list. 35 | This implementation also requires a struct `Node` with the following fields: 36 | ``` 37 | struct Node { 38 | data: T, 39 | next: Option>>, 40 | } 41 | ``` 42 | `data` contains the stored data, and `next` points to the following node (if available) or None. 43 | 44 | ## Why `Option>>` and not just `Option>`? 45 | Try it on your own. You will get the following error. 46 | 47 | ``` 48 | | struct Node 49 | | ^^^^^^^^^^^^^^ recursive type has infinite size 50 | ... 51 | | next: Option>, 52 | | --------------------- recursive without indirection 53 | ``` 54 | 55 | The problem is that at compile time the size of next must be known. 56 | Since `next` is recursive ("a node has a node has a node..."), the compiler does not know how much memory is to be allocated. 57 | In contrast, [Box](https://doc.rust-lang.org/std/boxed/) is a heap pointer with a defined 58 |
-------------------------------------------------------------------------------- /4 - Solution/220410 - Make Interpreter Rust, Week 2/prob3/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::iter::FromIterator; 2 | 3 | struct Node { 4 | data: T, 5 | next: Option>>, 6 | } 7 | 8 | impl Node { 9 | fn new(data: T, next: Option>>) -> Self { 10 | Self { data, next } 11 | } 12 | } 13 | 14 | pub struct SimpleLinkedList { 15 | head: Option>>, 16 | } 17 | 18 | impl SimpleLinkedList { 19 | pub fn new() -> Self { 20 | Self { head: None } 21 | } 22 | 23 | pub fn is_empty(&self) -> bool { 24 | self.head.is_none() 25 | } 26 | 27 | pub fn len(&self) -> usize { 28 | let mut cur_node = &self.head; 29 | let mut ret = 0; 30 | 31 | while let Some(elem) = cur_node { 32 | ret += 1; 33 | cur_node = &elem.next; 34 | } 35 | 36 | ret 37 | } 38 | 39 | pub fn push(&mut self, element: T) { 40 | let node = Box::new(Node::new(element, self.head.take())); 41 | self.head = Some(node); 42 | } 43 | 44 | pub fn pop(&mut self) -> Option { 45 | if self.head.is_some() { 46 | let head_node = self.head.take().unwrap(); 47 | self.head = head_node.next; 48 | Some(head_node.data) 49 | } else { 50 | None 51 | } 52 | } 53 | 54 | pub fn peek(&self) -> Option<&T> { 55 | self.head.as_ref().map(|head| &(head.data)) 56 | } 57 | 58 | pub fn rev(mut self) -> SimpleLinkedList { 59 | let mut ret = SimpleLinkedList::new(); 60 | 61 | while let Some(x) = self.pop() { 62 | ret.push(x); 63 | } 64 | 65 | ret 66 | } 67 | } 68 | 69 | impl FromIterator for SimpleLinkedList { 70 | fn from_iter>(iter: I) -> Self { 71 | let mut list = SimpleLinkedList::new(); 72 | 73 | for item in iter { 74 | list.push(item); 75 | } 76 | 77 | list 78 | } 79 | } 80 | 81 | impl From> for Vec { 82 | fn from(mut linked_list: SimpleLinkedList) -> Vec { 83 | let mut ret = Vec::new(); 84 | 85 | while let Some(x) = linked_list.pop() { 86 | ret.insert(0, x); 87 | } 88 | 89 | ret 90 | } 91 | } 92 | 93 | #[test] 94 | fn test_new_list_is_empty() { 95 | let list: SimpleLinkedList = SimpleLinkedList::new(); 96 | assert_eq!(list.len(), 0, "list's length must be 0"); 97 | } 98 | 99 | #[test] 100 | fn test_push_increments_length() { 101 | let mut list: SimpleLinkedList = SimpleLinkedList::new(); 102 | list.push(1); 103 | assert_eq!(list.len(), 1, "list's length must be 1"); 104 | list.push(2); 105 | assert_eq!(list.len(), 2, "list's length must be 2"); 106 | } 107 | 108 | #[test] 109 | fn test_pop_decrements_length() { 110 | let mut list: SimpleLinkedList = SimpleLinkedList::new(); 111 | list.push(1); 112 | list.push(2); 113 | list.pop(); 114 | assert_eq!(list.len(), 1, "list's length must be 1"); 115 | list.pop(); 116 | assert_eq!(list.len(), 0, "list's length must be 0"); 117 | } 118 | 119 | #[test] 120 | fn test_is_empty() { 121 | let mut list: SimpleLinkedList = SimpleLinkedList::new(); 122 | assert!(list.is_empty(), "List wasn't empty on creation"); 123 | for inserts in 0..100 { 124 | for i in 0..inserts { 125 | list.push(i); 126 | assert!( 127 | !list.is_empty(), 128 | "List was empty after having inserted {}/{} elements", 129 | i, 130 | inserts 131 | ); 132 | } 133 | for i in 0..inserts { 134 | assert!( 135 | !list.is_empty(), 136 | "List was empty before removing {}/{} elements", 137 | i, 138 | inserts 139 | ); 140 | list.pop(); 141 | } 142 | assert!( 143 | list.is_empty(), 144 | "List wasn't empty after having removed {} elements", 145 | inserts 146 | ); 147 | } 148 | } 149 | 150 | #[test] 151 | fn test_pop_returns_head_element_and_removes_it() { 152 | let mut list: SimpleLinkedList = SimpleLinkedList::new(); 153 | list.push(1); 154 | list.push(2); 155 | assert_eq!(list.pop(), Some(2), "Element must be 2"); 156 | assert_eq!(list.pop(), Some(1), "Element must be 1"); 157 | assert_eq!(list.pop(), None, "No element should be contained in list"); 158 | } 159 | 160 | #[test] 161 | fn test_peek_returns_reference_to_head_element_but_does_not_remove_it() { 162 | let mut list: SimpleLinkedList = SimpleLinkedList::new(); 163 | assert_eq!(list.peek(), None, "No element should be contained in list"); 164 | list.push(2); 165 | assert_eq!(list.peek(), Some(&2), "Element must be 2"); 166 | assert_eq!(list.peek(), Some(&2), "Element must be still 2"); 167 | list.push(3); 168 | assert_eq!(list.peek(), Some(&3), "Head element is now 3"); 169 | assert_eq!(list.pop(), Some(3), "Element must be 3"); 170 | assert_eq!(list.peek(), Some(&2), "Head element is now 2"); 171 | assert_eq!(list.pop(), Some(2), "Element must be 2"); 172 | assert_eq!(list.peek(), None, "No element should be contained in list"); 173 | } 174 | 175 | #[test] 176 | fn test_from_slice() { 177 | let mut array = vec!["1", "2", "3", "4"]; 178 | let mut list: SimpleLinkedList<_> = array.drain(..).collect(); 179 | assert_eq!(list.pop(), Some("4")); 180 | assert_eq!(list.pop(), Some("3")); 181 | assert_eq!(list.pop(), Some("2")); 182 | assert_eq!(list.pop(), Some("1")); 183 | } 184 | 185 | #[test] 186 | fn test_reverse() { 187 | let mut list: SimpleLinkedList = SimpleLinkedList::new(); 188 | list.push(1); 189 | list.push(2); 190 | list.push(3); 191 | let mut rev_list = list.rev(); 192 | assert_eq!(rev_list.pop(), Some(1)); 193 | assert_eq!(rev_list.pop(), Some(2)); 194 | assert_eq!(rev_list.pop(), Some(3)); 195 | assert_eq!(rev_list.pop(), None); 196 | } 197 | 198 | #[test] 199 | fn test_into_vector() { 200 | let mut v = Vec::new(); 201 | let mut s = SimpleLinkedList::new(); 202 | for i in 1..4 { 203 | v.push(i); 204 | s.push(i); 205 | } 206 | let s_as_vec: Vec = s.into(); 207 | assert_eq!(v, s_as_vec); 208 | } 209 | -------------------------------------------------------------------------------- /4 - Solution/220410 - Make Interpreter Rust, Week 2/prob4/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "prob4" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | 10 | # [features] 11 | # advanced = [] 12 | # default = [ "advanced" ] 13 | -------------------------------------------------------------------------------- /4 - Solution/220410 - Make Interpreter Rust, Week 2/prob4/README.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Write a doubly linked list using unsafe Rust, including an iterator over the list 4 | and a cursor for efficient mutation. 5 | 6 | The doubly linked list is a fundamental data structure in computer science, 7 | often used in the implementation of other data structures. They're 8 | pervasive in functional programming languages, such as Clojure, Erlang, 9 | or Haskell, but far less common in imperative languages such as Ruby or 10 | Python. 11 | 12 | Each node in a doubly linked list contains data and pointers to the next 13 | and previous node, if they exist. 14 | 15 | New nodes can be efficiently added at any point in the list, if one already has 16 | a reference to the position. Likewise, all elements 17 | from another list can be inserted at any point in constant time. 18 | 19 | In Rust, linked lists are very rarely used, but occasionally they trip up 20 | newcomers, when they try implementing one. Often, they find it unexpectedly 21 | difficult to work with the yet unfamiliar borrow checker. 22 | 23 | ## A Note on `unsafe` 24 | Remember, the goal of unsafe Rust is to write safe code in cases where the compiler can't help us 25 | guarantee correctness. It must not be possible for a user to cause memory unsafety of any kind using 26 | only the safe interfaces we expose. 27 | 28 | Document the safety-critical invariants you need to uphold and comment each unsafe block explaining why it 29 | is safe. 30 | 31 | Any function where the caller has to maintain safety-critical invariants should be marked unsafe. This includes 32 | private functions. 33 | 34 | ## Step 1 35 | 36 | Implement the functionality for adding and removing elements (pushing and popping) 37 | at the front and back. This is enough to use the list as a double-ended queue. 38 | Also implement the `len` and `is_empty` functions. 39 | 40 | In the finished implementation, all modifications of the list should be done through the cursor struct 41 | to minimize duplication. The `push_*` and `pop_*` methods on `LinkedList` 42 | are defined in terms of required cursor methods in the module `linked_list`. If you wish, you 43 | can skip the `Cursor` struct for now and override the methods, but please revert them at the end. 44 | 45 | ## Step 2 46 | 47 | Implement iteration over the list from front to back with the `Iter` struct. 48 | 49 | ## Step 3 50 | 51 | Complete the functionality of the cursor. It should be able to move to any position and insert or remove elements there. 52 | 53 | ## Step 4 54 | 55 | Implement the `Drop` trait for your `LinkedList` to clean up resources. 56 | 57 | ## Step 5 (advanced and optional) 58 | 59 | The tests for these last two things are conditionally compiled via the feature flag `advanced`. 60 | Add the key `default = ["advanced"]` to the `Cargo.toml` file under `[features]` to activate them. 61 | 62 | To allow users of your structure maximum flexibility, make sure that your `LinkedList` is covariant over `T`. 63 | This means, for example, that a `LinkedList<&'static T>` can also be used as a `LinkedList<&'a T>`. 64 | See the [Rustonomicon](https://doc.rust-lang.org/nomicon/subtyping.html#subtyping-and-variance) for an explanation of variance in Rust. 65 | 66 | Make sure that your list is safe to send and share across thread boundaries 67 | and signal this to the type system by implementing `Send` and `Sync` manually. 68 | These traits are usually auto-derived, but aren't implemented here automatically, because of the use of 69 | raw pointers. See the docs for [`Send`](https://doc.rust-lang.org/std/marker/trait.Send.html) and [`Sync`](https://doc.rust-lang.org/std/marker/trait.Sync.html) and the [rustonomicon chapter](https://doc.rust-lang.org/nomicon/send-and-sync.html) on them for details on their significance. 70 | 71 |
72 | Hints 73 |
74 | * A doubly linked does not have a clear ownership hierarchy, which is why it requires either the use 75 | of unsafe or abstractions for shared ownership like `Rc`. The latter has some overhead that is unnecessary 76 | for this case. 77 | 78 | * Refer to the [Rustonomicon](https://doc.rust-lang.org/nomicon/) for details on how to use `unsafe {}` correctly. 79 | 80 | * Several functions require similar behaviour in different directions (towards front or back). Try not to duplicate 81 | shared code paths. 82 |
-------------------------------------------------------------------------------- /4 - Solution/220410 - Make Interpreter Rust, Week 2/prob4/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::{marker::PhantomData, ptr}; 2 | 3 | mod linked_list; 4 | 5 | pub struct Node { 6 | data: T, 7 | next: *mut Node, 8 | prev: *mut Node, 9 | } 10 | 11 | pub struct LinkedList { 12 | head: *mut Node, 13 | tail: *mut Node, 14 | len: usize, 15 | } 16 | 17 | pub struct Cursor<'a, T> { 18 | list: &'a mut LinkedList, 19 | curr: *mut Node, 20 | } 21 | 22 | pub struct Iter<'a, T> { 23 | curr: *mut Node, 24 | _marker: PhantomData<&'a Node>, 25 | } 26 | 27 | impl LinkedList { 28 | pub fn new() -> Self { 29 | Self { 30 | head: ptr::null_mut(), 31 | tail: ptr::null_mut(), 32 | len: 0, 33 | } 34 | } 35 | 36 | pub fn is_empty(&self) -> bool { 37 | self.len == 0 38 | } 39 | 40 | pub fn len(&self) -> usize { 41 | self.len 42 | } 43 | 44 | /// Return a cursor positioned on the front element 45 | pub fn cursor_front(&mut self) -> Cursor<'_, T> { 46 | let head_ptr: *mut _ = self.head; 47 | Cursor { 48 | list: self, 49 | curr: head_ptr, 50 | } 51 | } 52 | 53 | /// Return a cursor positioned on the back element 54 | pub fn cursor_back(&mut self) -> Cursor<'_, T> { 55 | let tail_ptr: *mut _ = self.tail; 56 | Cursor { 57 | list: self, 58 | curr: tail_ptr, 59 | } 60 | } 61 | 62 | /// Return an iterator that moves from front to back 63 | pub fn iter(&self) -> Iter<'_, T> { 64 | Iter { 65 | curr: self.head, 66 | _marker: PhantomData, 67 | } 68 | } 69 | } 70 | 71 | // the cursor is expected to act as if it is at the position of an element 72 | // and it also has to work with and be able to insert into an empty list. 73 | impl Cursor<'_, T> { 74 | /// Take a mutable reference to the current element 75 | pub fn peek_mut(&mut self) -> Option<&mut T> { 76 | if self.curr.is_null() { 77 | return None; 78 | } 79 | 80 | unsafe { Some(&mut (*self.curr).data) } 81 | } 82 | 83 | /// Move one position forward (towards the back) and 84 | /// return a reference to the new position 85 | #[allow(clippy::should_implement_trait)] 86 | pub fn next(&mut self) -> Option<&mut T> { 87 | unsafe { 88 | if self.curr.is_null() || (*self.curr).next.is_null() { 89 | return None; 90 | } 91 | 92 | self.curr = (*self.curr).next; 93 | Some(&mut (*self.curr).data) 94 | } 95 | } 96 | 97 | /// Move one position backward (towards the front) and 98 | /// return a reference to the new position 99 | pub fn prev(&mut self) -> Option<&mut T> { 100 | unsafe { 101 | if self.curr.is_null() || (*self.curr).prev.is_null() { 102 | return None; 103 | } 104 | 105 | self.curr = (*self.curr).prev; 106 | Some(&mut (*self.curr).data) 107 | } 108 | } 109 | 110 | /// Remove and return the element at the current position and move the cursor 111 | /// to the neighboring element that's closest to the back. This can be 112 | /// either the next or previous position. 113 | pub fn take(&mut self) -> Option { 114 | let curr_ptr = self.curr; 115 | 116 | if curr_ptr.is_null() { 117 | return None; 118 | } 119 | 120 | unsafe { 121 | let next_node_ptr = (*curr_ptr).next; 122 | let prev_node_ptr = (*curr_ptr).prev; 123 | 124 | if !next_node_ptr.is_null() && prev_node_ptr.is_null() { 125 | // Head 126 | (*next_node_ptr).prev = ptr::null_mut(); 127 | self.list.head = next_node_ptr; 128 | self.curr = next_node_ptr; 129 | } else if next_node_ptr.is_null() && !prev_node_ptr.is_null() { 130 | // Tail 131 | (*prev_node_ptr).next = ptr::null_mut(); 132 | self.list.tail = prev_node_ptr; 133 | self.curr = prev_node_ptr; 134 | } else if !next_node_ptr.is_null() && !prev_node_ptr.is_null() { 135 | // Middle 136 | (*prev_node_ptr).next = next_node_ptr; 137 | (*next_node_ptr).prev = prev_node_ptr; 138 | self.curr = next_node_ptr; 139 | } else { 140 | // Only one 141 | self.curr = ptr::null_mut(); 142 | self.list.head = ptr::null_mut(); 143 | self.list.tail = ptr::null_mut(); 144 | } 145 | 146 | self.list.len -= 1; 147 | 148 | let data = std::ptr::read(&(*curr_ptr).data); 149 | drop(Box::from_raw(curr_ptr)); 150 | 151 | Some(data) 152 | } 153 | } 154 | 155 | pub fn insert_after(&mut self, element: T) { 156 | let new_node = Box::new(Node { 157 | data: element, 158 | next: ptr::null_mut(), 159 | prev: ptr::null_mut(), 160 | }); 161 | 162 | let new_node_ptr: *mut _ = Box::into_raw(new_node); 163 | 164 | if !self.curr.is_null() { 165 | unsafe { 166 | (*new_node_ptr).prev = self.curr; 167 | let next_node_ptr = (*self.curr).next; 168 | 169 | if !next_node_ptr.is_null() { 170 | (*new_node_ptr).next = next_node_ptr; 171 | (*next_node_ptr).prev = new_node_ptr; 172 | } else { 173 | self.list.tail = new_node_ptr; 174 | } 175 | 176 | (*self.curr).next = new_node_ptr; 177 | } 178 | } else { 179 | self.list.head = new_node_ptr; 180 | self.list.tail = new_node_ptr; 181 | self.curr = new_node_ptr; 182 | } 183 | 184 | self.list.len += 1; 185 | } 186 | 187 | pub fn insert_before(&mut self, element: T) { 188 | let new_node = Box::new(Node { 189 | data: element, 190 | next: ptr::null_mut(), 191 | prev: ptr::null_mut(), 192 | }); 193 | 194 | let new_node_ptr: *mut _ = Box::into_raw(new_node); 195 | 196 | if !self.curr.is_null() { 197 | unsafe { 198 | (*new_node_ptr).next = self.curr; 199 | let prev_node_ptr = (*self.curr).prev; 200 | 201 | if !prev_node_ptr.is_null() { 202 | (*new_node_ptr).prev = prev_node_ptr; 203 | (*prev_node_ptr).next = new_node_ptr; 204 | } else { 205 | self.list.head = new_node_ptr; 206 | } 207 | 208 | (*self.curr).prev = new_node_ptr; 209 | } 210 | } else { 211 | self.list.head = new_node_ptr; 212 | self.list.tail = new_node_ptr; 213 | self.curr = new_node_ptr; 214 | } 215 | 216 | self.list.len += 1; 217 | } 218 | } 219 | 220 | impl<'a, T> Iterator for Iter<'a, T> { 221 | type Item = &'a T; 222 | 223 | fn next(&mut self) -> Option<&'a T> { 224 | unsafe { 225 | if self.curr.is_null() { 226 | return None; 227 | } 228 | 229 | let data = &(*self.curr).data; 230 | self.curr = (*self.curr).next; 231 | Some(data) 232 | } 233 | } 234 | } 235 | 236 | impl Drop for LinkedList { 237 | fn drop(&mut self) { 238 | let mut curr_ptr = self.head; 239 | 240 | while !curr_ptr.is_null() { 241 | unsafe { 242 | let next_ptr = (*curr_ptr).next; 243 | drop(Box::from_raw(curr_ptr)); 244 | curr_ptr = next_ptr; 245 | } 246 | } 247 | } 248 | } 249 | 250 | #[test] 251 | fn is_generic() { 252 | struct Foo; 253 | LinkedList::::new(); 254 | } 255 | 256 | // ——————————————————————————————————————————————————————————— 257 | // Tests for Step 1: push / pop at front and back 258 | // ——————————————————————————————————————————————————————————— 259 | 260 | #[test] 261 | fn basics_empty_list() { 262 | let list: LinkedList = LinkedList::new(); 263 | assert_eq!(list.len(), 0); 264 | assert!(list.is_empty()); 265 | } 266 | 267 | // push / pop at back ———————————————————————————————————————— 268 | #[test] 269 | fn basics_single_element_back() { 270 | let mut list: LinkedList = LinkedList::new(); 271 | list.push_back(5); 272 | 273 | assert_eq!(list.len(), 1); 274 | assert!(!list.is_empty()); 275 | 276 | assert_eq!(list.pop_back(), Some(5)); 277 | 278 | assert_eq!(list.len(), 0); 279 | assert!(list.is_empty()); 280 | } 281 | 282 | #[test] 283 | fn basics_push_pop_at_back() { 284 | let mut list: LinkedList = LinkedList::new(); 285 | for i in 0..10 { 286 | list.push_back(i); 287 | assert_eq!(list.len(), i as usize + 1); 288 | assert!(!list.is_empty()); 289 | } 290 | for i in (0..10).rev() { 291 | assert_eq!(list.len(), i as usize + 1); 292 | assert!(!list.is_empty()); 293 | assert_eq!(i, list.pop_back().unwrap()); 294 | } 295 | assert_eq!(list.len(), 0); 296 | assert!(list.is_empty()); 297 | } 298 | 299 | // push / pop at front ——————————————————————————————————————— 300 | #[test] 301 | fn basics_single_element_front() { 302 | let mut list: LinkedList = LinkedList::new(); 303 | list.push_front(5); 304 | 305 | assert_eq!(list.len(), 1); 306 | assert!(!list.is_empty()); 307 | 308 | assert_eq!(list.pop_front(), Some(5)); 309 | 310 | assert_eq!(list.len(), 0); 311 | assert!(list.is_empty()); 312 | } 313 | 314 | #[test] 315 | fn basics_push_pop_at_front() { 316 | let mut list: LinkedList = LinkedList::new(); 317 | for i in 0..10 { 318 | list.push_front(i); 319 | assert_eq!(list.len(), i as usize + 1); 320 | assert!(!list.is_empty()); 321 | } 322 | for i in (0..10).rev() { 323 | assert_eq!(list.len(), i as usize + 1); 324 | assert!(!list.is_empty()); 325 | assert_eq!(i, list.pop_front().unwrap()); 326 | } 327 | assert_eq!(list.len(), 0); 328 | assert!(list.is_empty()); 329 | } 330 | 331 | // push / pop at mixed sides ————————————————————————————————— 332 | #[test] 333 | fn basics_push_front_pop_back() { 334 | let mut list: LinkedList = LinkedList::new(); 335 | for i in 0..10 { 336 | list.push_front(i); 337 | assert_eq!(list.len(), i as usize + 1); 338 | assert!(!list.is_empty()); 339 | } 340 | for i in 0..10 { 341 | assert_eq!(list.len(), 10 - i as usize); 342 | assert!(!list.is_empty()); 343 | assert_eq!(i, list.pop_back().unwrap()); 344 | } 345 | assert_eq!(list.len(), 0); 346 | assert!(list.is_empty()); 347 | } 348 | 349 | #[test] 350 | fn basics_push_back_pop_front() { 351 | let mut list: LinkedList = LinkedList::new(); 352 | for i in 0..10 { 353 | list.push_back(i); 354 | assert_eq!(list.len(), i as usize + 1); 355 | assert!(!list.is_empty()); 356 | } 357 | for i in 0..10 { 358 | assert_eq!(list.len(), 10 - i as usize); 359 | assert!(!list.is_empty()); 360 | assert_eq!(i, list.pop_front().unwrap()); 361 | } 362 | assert_eq!(list.len(), 0); 363 | assert!(list.is_empty()); 364 | } 365 | 366 | // ——————————————————————————————————————————————————————————— 367 | // Tests for Step 2: iteration 368 | // ——————————————————————————————————————————————————————————— 369 | 370 | #[test] 371 | fn iter() { 372 | let mut list: LinkedList = LinkedList::new(); 373 | for num in 0..10 { 374 | list.push_back(num); 375 | } 376 | 377 | for (num, &entered_num) in (0..10).zip(list.iter()) { 378 | assert_eq!(num, entered_num); 379 | } 380 | } 381 | 382 | // ——————————————————————————————————————————————————————————— 383 | // Tests for Step 3: full cursor functionality 384 | // ——————————————————————————————————————————————————————————— 385 | 386 | #[test] 387 | fn cursor_insert_before_on_empty_list() { 388 | // insert_after on empty list is already tested via push_back() 389 | let mut list = LinkedList::new(); 390 | list.cursor_front().insert_before(0); 391 | assert_eq!(Some(0), list.pop_front()); 392 | } 393 | 394 | #[test] 395 | fn cursor_insert_after_in_middle() { 396 | let mut list = (0..10).collect::>(); 397 | 398 | { 399 | let mut cursor = list.cursor_front(); 400 | let didnt_run_into_end = cursor.seek_forward(4); 401 | assert!(didnt_run_into_end); 402 | 403 | for n in (0..10).rev() { 404 | cursor.insert_after(n); 405 | } 406 | } 407 | 408 | assert_eq!(list.len(), 20); 409 | 410 | let expected = (0..5).chain(0..10).chain(5..10); 411 | 412 | assert!(expected.eq(list.iter().cloned())); 413 | } 414 | 415 | #[test] 416 | fn cursor_insert_before_in_middle() { 417 | let mut list = (0..10).collect::>(); 418 | 419 | { 420 | let mut cursor = list.cursor_back(); 421 | let didnt_run_into_end = cursor.seek_backward(4); 422 | assert!(didnt_run_into_end); 423 | 424 | for n in 0..10 { 425 | cursor.insert_before(n); 426 | } 427 | } 428 | 429 | assert_eq!(list.len(), 20); 430 | 431 | let expected = (0..5).chain(0..10).chain(5..10); 432 | 433 | assert!(expected.eq(list.iter().cloned())); 434 | } 435 | 436 | // "iterates" via next() and checks that it visits the right elements 437 | #[test] 438 | fn cursor_next_and_peek() { 439 | let mut list = (0..10).collect::>(); 440 | let mut cursor = list.cursor_front(); 441 | 442 | assert_eq!(cursor.peek_mut(), Some(&mut 0)); 443 | 444 | for n in 1..10 { 445 | let next = cursor.next().cloned(); 446 | assert_eq!(next, Some(n)); 447 | assert_eq!(next, cursor.peek_mut().cloned()); 448 | } 449 | } 450 | 451 | // "iterates" via prev() and checks that it visits the right elements 452 | #[test] 453 | fn cursor_prev_and_peek() { 454 | let mut list = (0..10).collect::>(); 455 | let mut cursor = list.cursor_back(); 456 | 457 | assert_eq!(cursor.peek_mut(), Some(&mut 9)); 458 | 459 | for n in (0..9).rev() { 460 | let prev = cursor.prev().cloned(); 461 | assert_eq!(prev, Some(n)); 462 | assert_eq!(prev, cursor.peek_mut().cloned()); 463 | } 464 | } 465 | 466 | #[test] 467 | fn cursor_take_with_empty_list() { 468 | let mut list: LinkedList = LinkedList::new(); 469 | 470 | let mut cursor = list.cursor_front(); 471 | assert_eq!(cursor.take(), None); 472 | } 473 | 474 | // removes all elements starting from the middle 475 | #[test] 476 | fn cursor_take() { 477 | let mut list = (0..10).collect::>(); 478 | let mut cursor = list.cursor_front(); 479 | cursor.seek_forward(5); 480 | 481 | for expected in (5..10).chain((0..5).rev()) { 482 | assert_eq!(cursor.take(), Some(expected)); 483 | } 484 | } 485 | 486 | // ——————————————————————————————————————————————————————————— 487 | // Tests for Step 4: clean-up via `Drop` 488 | // ——————————————————————————————————————————————————————————— 489 | 490 | // The leak tests that are also for this step are separated into 491 | // their own files so that nothing else interferes with the allocator 492 | // whilst they run 493 | 494 | // checks number of drops 495 | // may pass for incorrect programs if double frees happen 496 | // exactly as often as destructor leaks 497 | #[test] 498 | fn drop_no_double_frees() { 499 | use std::cell::Cell; 500 | struct DropCounter<'a>(&'a Cell); 501 | 502 | impl<'a> Drop for DropCounter<'a> { 503 | fn drop(&mut self) { 504 | let num = self.0.get(); 505 | self.0.set(num + 1); 506 | } 507 | } 508 | 509 | const N: usize = 15; 510 | 511 | let counter = Cell::new(0); 512 | let list = std::iter::repeat_with(|| DropCounter(&counter)) 513 | .take(N) 514 | .collect::>(); 515 | 516 | assert_eq!(list.len(), N); 517 | drop(list); 518 | assert_eq!(counter.get(), N); 519 | } 520 | 521 | #[test] 522 | fn drop_large_list() { 523 | drop((0..2_000_000).collect::>()); 524 | } 525 | 526 | // ——————————————————————————————————————————————————————————— 527 | // Tests for Step 5 (advanced): covariance and Send/Sync 528 | // ——————————————————————————————————————————————————————————— 529 | 530 | // These are compile time tests. They won't compile unless your 531 | // code passes. 532 | 533 | #[cfg(feature = "advanced")] 534 | #[test] 535 | fn advanced_linked_list_is_send_sync() { 536 | trait AssertSend: Send {} 537 | trait AssertSync: Sync {} 538 | 539 | impl AssertSend for LinkedList {} 540 | impl AssertSync for LinkedList {} 541 | } 542 | 543 | #[cfg(feature = "advanced")] 544 | #[allow(dead_code)] 545 | #[test] 546 | fn advanced_is_covariant() { 547 | fn a<'a>(x: LinkedList<&'static str>) -> LinkedList<&'a str> { 548 | x 549 | } 550 | 551 | fn a_iter<'a>(i: Iter<'static, &'static str>) -> Iter<'a, &'a str> { 552 | i 553 | } 554 | } 555 | -------------------------------------------------------------------------------- /4 - Solution/220410 - Make Interpreter Rust, Week 2/prob4/src/linked_list.rs: -------------------------------------------------------------------------------- 1 | use crate::{Cursor, LinkedList}; 2 | 3 | impl LinkedList { 4 | pub fn push_back(&mut self, element: T) { 5 | self.cursor_back().insert_after(element); 6 | } 7 | 8 | pub fn push_front(&mut self, element: T) { 9 | self.cursor_front().insert_before(element); 10 | } 11 | 12 | pub fn pop_back(&mut self) -> Option { 13 | self.cursor_back().take() 14 | } 15 | 16 | pub fn pop_front(&mut self) -> Option { 17 | self.cursor_front().take() 18 | } 19 | 20 | pub fn front(&self) -> Option<&T> { 21 | self.iter().next() 22 | } 23 | 24 | pub fn back(&self) -> Option<&T> { 25 | self.iter().last() 26 | } 27 | } 28 | 29 | impl std::iter::FromIterator for LinkedList { 30 | fn from_iter(iter: I) -> Self 31 | where 32 | I: IntoIterator, 33 | { 34 | let mut list = Self::new(); 35 | for elem in iter { 36 | list.push_back(elem); 37 | } 38 | list 39 | } 40 | } 41 | 42 | // seek methods, return false if end of list is reached prematurely 43 | impl Cursor<'_, T> { 44 | pub fn seek_forward(&mut self, n: usize) -> bool { 45 | (0..n).all(|_| self.next().is_some()) 46 | } 47 | 48 | pub fn seek_backward(&mut self, n: usize) -> bool { 49 | (0..n).all(|_| self.prev().is_some()) 50 | } 51 | } 52 | 53 | #[allow(unused)] 54 | #[cfg(feature = "advanced")] 55 | /// ```compile_fail 56 | /// use doubly_linked_list::LinkedList; 57 | /// trait AssertSend: Send {} 58 | /// impl AssertSend for LinkedList {} 59 | /// ``` 60 | pub struct IllegalSend; 61 | 62 | #[allow(unused)] 63 | #[cfg(feature = "advanced")] 64 | /// ```compile_fail 65 | /// use doubly_linked_list::LinkedList; 66 | /// trait AssertSync: Sync {} 67 | /// impl AssertSync for LinkedList {} 68 | /// ``` 69 | pub struct IllegalSync; 70 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Chris Ohk 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 2022-Make-Interpreter-Rust 2 | 3 | 2022-Make-Interpreter-Rust is the material(lecture notes, examples and assignments) repository for making an interpreter course. I'll teach online using Discord, Zoom or Google Meet. Note that examples and assignments in this repository uses [Rust language](https://www.rust-lang.org/). 4 | 5 | ## Book 6 | 7 | - English: Writing An Interpreter In Go (Thorsten Ball, 2018) 8 | 9 | ![](https://interpreterbook.com/img/sm_card-03b4fb28.png) 10 | 11 | - Korean: 밑바닥부터 만드는 인터프리터 (인사이트, 2021) 12 | 13 | ![](https://insightbookblog.files.wordpress.com/2021/08/ec9db8ed84b0ed9484eba6aced8bb0-ed919ceca780ec9e85ecb2b4.jpg?w=408) 14 | 15 | ## Optional Readings 16 | 17 | - Crafting Interpreters (Genever Benning, 2021) 18 | - Build Your Own Programming Language (Packt Publishing, 2021) 19 | - Engineering: A Compiler (Morgan Kaufmann, 2011) 20 | - Compilers: Principles, Techniques, and Tools (Addison Wesley, 2006) 21 | 22 | ## Contents 23 | 24 | - Week 0 (3/27) [[Lecture]](./1%20-%20Lecture/220327%20-%20Make%20Interpreter%20Rust%2C%20Week%200.pdf) 25 | - Introduction 26 | - Week 1 (3/27) [[Lecture]](./1%20-%20Lecture/220327%20-%20Make%20Interpreter%20Rust%2C%20Week%201.pdf) [[Assignment]](./3%20-%20Assignment/220327%20-%20Make%20Interpreter%20Rust%2C%20Week%201) [[Solution]](./4%20-%20Solution/220327%20-%20Make%20Interpreter%20Rust%2C%20Week%201) 27 | - A Tour of Rust, Part 1 28 | - Assignment #1 29 | - Week 2 (4/10) [[Lecture]](./1%20-%20Lecture/220410%20-%20Make%20Interpreter%20Rust%2C%20Week%202.pdf) [[Assignment]](./3%20-%20Assignment/220410%20-%20Make%20Interpreter%20Rust%2C%20Week%202) [[Solution]](./4%20-%20Solution/220410%20-%20Make%20Interpreter%20Rust%2C%20Week%202) 30 | - A Tour of Rust, Part 2 31 | - Assignment #2 32 | - Week 3 (5/22) [[Code]](./2%20-%20Code/220522%20-%20Make%20Interpreter%20Rust%2C%20Week%203) 33 | - Lexing, Part 1 34 | - Lexical Analysis 35 | - Defining Tokens 36 | - The Lexer, Part 1 37 | - First REPL 38 | - Week 4 (6/5) [[Code]](./2%20-%20Code/220605%20-%20Make%20Interpreter%20Rust%2C%20Week%204) [[Assignment]](./3%20-%20Assignment/220605%20-%20Make%20Interpreter%20Rust%2C%20Week%204) 39 | - Lexing, Part 2 40 | - The Lexer, Part 2 41 | - Extensions 42 | - Assignment #3 43 | - Week 5 (6/19) 44 | - Parsing 45 | - Parsers 46 | - Statements 47 | - Assignment #4 48 | - Week 6 (TBA) 49 | - Parsing 50 | - Expressions 51 | - Read-Parse-Print-Loop 52 | - Assignment #5 53 | - Week 7 (TBA) 54 | - Evaluation 55 | - Giving Meaning to Symbols 56 | - Strategies of Evaluation 57 | - A Tree-Walking Interpreter 58 | - Representing Objects 59 | - Evaluating Expressions 60 | - Assignment #6 61 | - Week 8 (TBA) 62 | - Evaluation 63 | - Conditionals 64 | - Return Statements 65 | - Error Handling 66 | - Bindings & The Environment 67 | - Functions & Function Calls 68 | - Assignment #7 69 | - Week 9 (TBA) 70 | - Extensions 71 | - Data Types & Functions 72 | - Strings 73 | - Built-in Functions 74 | - Assignment #8 75 | - Week 10 (TBA) 76 | - Extensions 77 | - Array 78 | - Hashes 79 | - Assignment #9 80 | 81 | ## How To Contribute 82 | 83 | Contributions are always welcome, either reporting issues/bugs or forking the repository and then issuing pull requests when you have completed some additional coding that you feel will be beneficial to the main project. If you are interested in contributing in a more dedicated capacity, then please contact me. 84 | 85 | ## Contact 86 | 87 | You can contact me via e-mail (utilForever at gmail.com). I am always happy to answer questions or help with any issues you might have, and please be sure to share any additional work or your creations with me, I love seeing what other people are making. 88 | 89 | ## License 90 | 91 | 92 | 93 | The class is licensed under the [MIT License](http://opensource.org/licenses/MIT): 94 | 95 | Copyright © 2022 [Chris Ohk](http://www.github.com/utilForever). 96 | 97 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 98 | 99 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 100 | 101 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 102 | --------------------------------------------------------------------------------