scanner now recognizes string literals (text between double-quotes)

This commit is contained in:
Sander Hautvast 2020-01-24 13:42:43 +01:00
parent 92e86032ca
commit 650a31889b
3 changed files with 58 additions and 3 deletions

View file

@ -1,3 +1,5 @@
use std::any::Any;
use crate::tokens::{Token, TokenType}; use crate::tokens::{Token, TokenType};
use crate::tokens::TokenType::*; use crate::tokens::TokenType::*;
@ -99,10 +101,32 @@ impl Scanner<'_> {
' ' => {} ' ' => {}
'\t' => {} '\t' => {}
'\r' => {} '\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 /// advance (consume) one character and return that
fn advance(&mut self) -> char { fn advance(&mut self) -> char {
self.current += 1; self.current += 1;
@ -116,6 +140,13 @@ impl Scanner<'_> {
self.tokens.push(token); self.tokens.push(token);
} }
/// adds a token of the given type and content
fn add_token_literal(&mut self, token_type: TokenType, literal: Box<dyn Any>) {
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 /// returns true iff the end of the source has been reached
fn is_at_end(&self) -> bool { fn is_at_end(&self) -> bool {
self.current >= self.source.len() self.current >= self.source.len()
@ -145,4 +176,10 @@ impl Scanner<'_> {
self.current += 1; self.current += 1;
true 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);
}
} }

View file

@ -33,7 +33,16 @@ fn test_scan_double_char_tokens() {
let token = tokens.get(0).unwrap(); let token = tokens.get(0).unwrap();
assert_eq!(token.token_type, GREATEREQUAL); assert_eq!(token.token_type, GREATEREQUAL);
assert_eq!(token.lexeme, ">="); 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");
} }

View file

@ -16,6 +16,12 @@ pub struct Token<'a> {
pub token_type: TokenType, pub token_type: TokenType,
} }
impl Token<'_>{
pub fn get_literal_as_string(&self) -> Option<&str>{
self.literal.downcast_ref::<String>().map(|s|s.as_str())
}
}
impl fmt::Debug for Token<'_> { impl fmt::Debug for Token<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let lit = match self.literal.downcast_ref::<String>() { let lit = match self.literal.downcast_ref::<String>() {
@ -56,5 +62,8 @@ pub enum TokenType {
LESS, // < LESS, // <
LESSEQUAL, // <= LESSEQUAL, // <=
// Literals.
STRING,
EOF // end of file EOF // end of file
} }