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::{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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
15
src/tests.rs
15
src/tests.rs
|
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
Loading…
Add table
Reference in a new issue