scanner now recognizes string literals (text between double-quotes)
This commit is contained in:
parent
92e86032ca
commit
650a31889b
3 changed files with 58 additions and 3 deletions
|
|
@ -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<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
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
15
src/tests.rs
15
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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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::<String>().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::<String>() {
|
||||
|
|
@ -56,5 +62,8 @@ pub enum TokenType {
|
|||
LESS, // <
|
||||
LESSEQUAL, // <=
|
||||
|
||||
// Literals.
|
||||
STRING,
|
||||
|
||||
EOF // end of file
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue