diff --git a/src/scanner.rs b/src/scanner.rs index 11a9e65..7192830 100644 --- a/src/scanner.rs +++ b/src/scanner.rs @@ -1,3 +1,5 @@ +use std::any::Any; + use crate::tokens::{Token, TokenType}; use crate::tokens::TokenType::*; @@ -99,10 +101,32 @@ impl Scanner<'_> { ' ' => {} '\t' => {} '\r' => {} + '\"' => self.string(), _ => {} } } + /// handle string literals + /// advances until a terminating double quote is found and then adds the string token to the list + /// raises an interpreter error when the double-quote is not found and the end of the source has been reached + fn string(&mut self) { + while self.peek(0) != '\"' && !self.is_at_end() { + if self.peek(0) == '\n' { + self.line += 1; + } + self.advance(); + } + + if self.is_at_end() { + self.report_error(self.line, "unterminated string"); + } else { + self.advance(); + + let value = String::from(&self.source[self.start + 1..self.current - 1]); + self.add_token_literal(STRING, Box::new(value)); + } + } + /// advance (consume) one character and return that fn advance(&mut self) -> char { self.current += 1; @@ -116,6 +140,13 @@ impl Scanner<'_> { self.tokens.push(token); } + /// adds a token of the given type and content + fn add_token_literal(&mut self, token_type: TokenType, literal: Box) { + let text = &self.source[self.start..self.current]; + let token = Token { token_type: token_type, lexeme: text, literal: literal, line: self.line }; + self.tokens.push(token); + } + /// returns true iff the end of the source has been reached fn is_at_end(&self) -> bool { self.current >= self.source.len() @@ -145,4 +176,10 @@ impl Scanner<'_> { self.current += 1; true } + + /// prints the error and sets the flag + pub fn report_error(&mut self, line: usize, message: &str) { + self.error_occured = true; + println!("[line {} ] Error {} ", line, message); + } } diff --git a/src/tests.rs b/src/tests.rs index b8d8b47..694401f 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -33,7 +33,16 @@ fn test_scan_double_char_tokens() { let token = tokens.get(0).unwrap(); assert_eq!(token.token_type, GREATEREQUAL); assert_eq!(token.lexeme, ">="); - - let token = tokens.get(1).unwrap(); - assert_eq!(token.token_type, EOF); +} + + +#[test] +fn test_scan_string_literals() { + let tokens = scan_tokens("\"hello world\"").unwrap(); + assert_eq!(tokens.len(), 2); + + let token = tokens.get(0).unwrap(); + assert_eq!(token.token_type, STRING); + assert_eq!(token.lexeme, "\"hello world\""); + assert_eq!(token.get_literal_as_string().unwrap(), "hello world"); } diff --git a/src/tokens.rs b/src/tokens.rs index 42bdc3a..7765e4b 100644 --- a/src/tokens.rs +++ b/src/tokens.rs @@ -16,6 +16,12 @@ pub struct Token<'a> { pub token_type: TokenType, } +impl Token<'_>{ + pub fn get_literal_as_string(&self) -> Option<&str>{ + self.literal.downcast_ref::().map(|s|s.as_str()) + } +} + impl fmt::Debug for Token<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let lit = match self.literal.downcast_ref::() { @@ -56,5 +62,8 @@ pub enum TokenType { LESS, // < LESSEQUAL, // <= + // Literals. + STRING, + EOF // end of file } \ No newline at end of file