diff --git a/src/compiler/scanner.rs b/src/compiler/scanner.rs index 080db33..e3ef8ce 100644 --- a/src/compiler/scanner.rs +++ b/src/compiler/scanner.rs @@ -58,6 +58,7 @@ impl<'a> Iterator for ScannerIterator<'a> { } impl Scanner { + //noinspection DuplicatedCode fn scan(&mut self) -> Option { self.skip_whitespace(); self.start = self.current; @@ -69,14 +70,14 @@ impl Scanner { self.current += 1; return Option::from(self.make_token(TokenType::Eof)); } - let c = self.advance(); if Scanner::is_alpha(c) { - return self.make_identifier(); + return Option::from(self.make_identifier()); } if Scanner::is_digit(c) { return Option::from(self.make_number()); } + match c { '(' => return Option::from(self.make_token(TokenType::LeftParen)), ')' => return Option::from(self.make_token(TokenType::RightParen)), @@ -89,54 +90,52 @@ impl Scanner { ';' => return Option::from(self.make_token(TokenType::Semicolon)), '*' => return Option::from(self.make_token(TokenType::Star)), '!' => { - if self.matches('=') { - return Option::from(self.make_token(TokenType::BangEqual)); + return if self.matches('=') { + Option::from(self.make_token(TokenType::BangEqual)) } else { - return Option::from(self.make_token(TokenType::Bang)); + Option::from(self.make_token(TokenType::Bang)) } } '=' => { - if self.matches('=') { - return Option::from(self.make_token(TokenType::EqualEqual)); + return if self.matches('=') { + Option::from(self.make_token(TokenType::EqualEqual)) } else { - return Option::from(self.make_token(TokenType::Equal)); + Option::from(self.make_token(TokenType::Equal)) } } '<' => { - if self.matches('=') { - return Option::from(self.make_token(TokenType::LessEqual)); + return if self.matches('=') { + Option::from(self.make_token(TokenType::LessEqual)) } else { - return Option::from(self.make_token(TokenType::Less)); + Option::from(self.make_token(TokenType::Less)) } } '>' => { - if self.matches('=') { - return Option::from(self.make_token(TokenType::GreaterEqual)); + return if self.matches('=') { + Option::from(self.make_token(TokenType::GreaterEqual)) } else { - return Option::from(self.make_token(TokenType::Greater)); + Option::from(self.make_token(TokenType::Greater)) } } '/' => { - if self.matches('/') { + return if self.matches('/') { while self.peek_next() != '\n' && !self.is_at_end() { self.advance(); } - return self.scan(); + self.scan() } else { - return Option::from(self.make_token(TokenType::Slash)); + Option::from(self.make_token(TokenType::Slash)) } } '"' => return self.make_string(), - _ => Option::from(self.error_token("Unexpected character.")), + _ => Option::from(self.make_error_token("Unexpected character.")), } } -} -impl Scanner { fn make_string(&mut self) -> Option { loop { if self.is_at_end() { - return Option::from(self.error_token("Unterminated string.")); + return Option::from(self.make_error_token("Unterminated string.")); } if self.peek() == '"' { break; @@ -146,20 +145,18 @@ impl Scanner { } self.advance(); } - self.advance(); - return Option::from(self.make_token(TokenType::String)); + Option::from(self.make_token(TokenType::String)) } - fn make_identifier(&mut self) -> Option { + fn make_identifier(&mut self) -> Token { loop { if !Scanner::is_alpha(self.peek()) && !Scanner::is_digit(self.peek()) { break; } self.advance(); } - - return Option::from(self.make_token(self.make_identifier_type())); + self.make_token(self.make_identifier_type()) } fn make_number(&mut self) -> Token { @@ -170,9 +167,7 @@ impl Scanner { self.advance(); } - // Look for a fractional part. if self.peek() == '.' && Scanner::is_digit(self.peek_next()) { - // Consume the ".". self.advance(); } @@ -180,52 +175,11 @@ impl Scanner { if !Scanner::is_digit(self.peek()) { break; } - self.advance(); } - self.make_token(TokenType::Number) } - fn make_identifier_type(&self) -> TokenType { - let chr = self.source[self.start]; - return match chr { - 'a' => self.check_keyword(1, 2, "nd", TokenType::And), - 'c' => self.check_keyword(1, 4, "lass", TokenType::Class), - 'e' => self.check_keyword(1, 3, "lse", TokenType::Else), - 'i' => self.check_keyword(1, 1, "f", TokenType::If), - 'n' => self.check_keyword(1, 2, "il", TokenType::Nil), - 'o' => self.check_keyword(1, 1, "r", TokenType::Or), - 'p' => self.check_keyword(1, 4, "rint", TokenType::Print), - 'r' => self.check_keyword(1, 5, "eturn", TokenType::Return), - 's' => self.check_keyword(1, 4, "uper", TokenType::Super), - 'v' => self.check_keyword(1, 2, "ar", TokenType::Var), - 'w' => self.check_keyword(1, 4, "hile", TokenType::While), - 'f' => { - if self.current - self.start > 1 { - return match self.source[self.start + 1] { - 'a' => self.check_keyword(2, 3, "lse", TokenType::False), - 'o' => self.check_keyword(2, 1, "r", TokenType::For), - 'u' => self.check_keyword(2, 1, "n", TokenType::Fun), - _ => TokenType::Identifier, - }; - } - TokenType::Identifier - } - 't' => { - if self.current - self.start > 1 { - return match self.source[self.start + 1] { - 'h' => self.check_keyword(2, 2, "is", TokenType::This), - 'r' => self.check_keyword(2, 1, "ue", TokenType::True), - _ => TokenType::Identifier, - }; - } - TokenType::Identifier - } - _ => TokenType::Identifier, - }; - } - fn check_keyword( &self, start: usize, @@ -239,29 +193,29 @@ impl Scanner { return token_type; } } - return TokenType::Identifier; + TokenType::Identifier } fn is_alpha(c: char) -> bool { - return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; + (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' } fn is_digit(c: char) -> bool { - return c >= '0' && c <= '9'; + c >= '0' && c <= '9' } - fn peek_next(&self) -> char { - if self.current + 1 >= self.source.len() { + fn peek(&self) -> char { + if self.is_at_end() { return '\0'; } - return self.source[self.current + 1]; + self.source[self.current] } - fn peek(&self) -> char { - if self.is_at_end() { + fn peek_next(&self) -> char { + if self.current + 1 >= self.source.len() { return '\0'; } - return self.source[self.current]; + self.source[self.current + 1] } fn skip_whitespace(&mut self) { @@ -282,22 +236,20 @@ impl Scanner { } } + fn advance(&mut self) -> char { + self.current += 1; + self.source[self.current - 1] + } + fn matches(&mut self, chr: char) -> bool { if self.is_at_end() { return false; } - if self.source[self.current] != chr { return false; } - self.current += 1; - return true; - } - - fn advance(&mut self) -> char { - self.current += 1; - self.source[self.current - 1] + true } fn is_at_end(&self) -> bool { @@ -308,7 +260,46 @@ impl Scanner { self.current > self.source.len() } - fn error_token(&self, message: &str) -> Token { + fn make_identifier_type(&self) -> TokenType { + let chr = self.source[self.start]; + return match chr { + 'a' => self.check_keyword(1, 2, "nd", TokenType::And), + 'c' => self.check_keyword(1, 4, "lass", TokenType::Class), + 'e' => self.check_keyword(1, 3, "lse", TokenType::Else), + 'i' => self.check_keyword(1, 1, "f", TokenType::If), + 'n' => self.check_keyword(1, 2, "il", TokenType::Nil), + 'o' => self.check_keyword(1, 1, "r", TokenType::Or), + 'p' => self.check_keyword(1, 4, "rint", TokenType::Print), + 'r' => self.check_keyword(1, 5, "eturn", TokenType::Return), + 's' => self.check_keyword(1, 4, "uper", TokenType::Super), + 'v' => self.check_keyword(1, 2, "ar", TokenType::Var), + 'w' => self.check_keyword(1, 4, "hile", TokenType::While), + 'f' => { + if self.current - self.start > 1 { + return match self.source[self.start + 1] { + 'a' => self.check_keyword(2, 3, "lse", TokenType::False), + 'o' => self.check_keyword(2, 1, "r", TokenType::For), + 'u' => self.check_keyword(2, 1, "n", TokenType::Fun), + _ => TokenType::Identifier, + }; + } + TokenType::Identifier + } + 't' => { + if self.current - self.start > 1 { + return match self.source[self.start + 1] { + 'h' => self.check_keyword(2, 2, "is", TokenType::This), + 'r' => self.check_keyword(2, 1, "ue", TokenType::True), + _ => TokenType::Identifier, + }; + } + TokenType::Identifier + } + _ => TokenType::Identifier, + }; + } + + fn make_error_token(&self, message: &str) -> Token { Token::new(TokenType::Error, self.start, message.len(), self.line) } diff --git a/src/main.rs b/src/main.rs index 1ee6373..2b13ab7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -35,14 +35,10 @@ fn run_repl() { println!("Running REPL"); let mut vm = VirtualMachine::new(); - loop { print_prompt(); - let line = read_line(); - vm.interpret(line); - println!(); } } diff --git a/src/vm/mod.rs b/src/vm/mod.rs index 47cf984..70fed0b 100644 --- a/src/vm/mod.rs +++ b/src/vm/mod.rs @@ -4,6 +4,7 @@ mod virtual_machine; pub type Value = f64; +#[derive(Debug, PartialEq)] pub enum Result { Ok, CompileError, diff --git a/src/vm/virtual_machine.rs b/src/vm/virtual_machine.rs index 4ca9efb..e28afc9 100644 --- a/src/vm/virtual_machine.rs +++ b/src/vm/virtual_machine.rs @@ -96,3 +96,45 @@ impl VirtualMachine { self.stack.pop().unwrap() } } + +#[cfg(test)] +mod tests { + use colored::Color::Black; + + #[test] + fn can_create_vm() { + let vm = super::VirtualMachine::new(); + assert_eq!(0, vm.ip); + assert_eq!(0, vm.stack.len()); + } + + #[test] + fn can_execute_simple_arithmetics() { + let mut block = super::Block::new("ZeBlock"); + + block.write_constant(1.0, 0); + block.write_constant(2.0, 0); + block.write_op_code(super::OpCode::Add, 0); + block.write_constant(3.0, 0); + block.write_op_code(super::OpCode::Multiply, 0); + block.write_constant(2.0, 0); + block.write_op_code(super::OpCode::Subtract, 0); + block.write_constant(2.0, 0); + block.write_op_code(super::OpCode::Divide, 0); + + // Pushing throw away value to the stack. + // This is needed because the Return OpCode will pop a value from the stack and print it. + block.write_constant(0.0, 0); + block.write_op_code(super::OpCode::Return, 0); + + let mut vm = super::VirtualMachine { + ip: 0, + block, + stack: Vec::new(), + }; + + let result = vm.run(); + assert_eq!(super::Result::Ok, result); + assert_eq!(3.5, vm.pop()); + } +}