Scanner - Refactored to use new error handling

This commit is contained in:
Emmanuel BENOîT 2023-01-03 22:38:57 +01:00
parent e152e40678
commit 260b19030f

View file

@ -3,7 +3,7 @@ use std::collections::HashMap;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use crate::{ use crate::{
errors::ErrorType, errors::{SloxError, SloxResult},
tokens::{Token, TokenType}, tokens::{Token, TokenType},
ErrorHandler, ErrorHandler,
}; };
@ -63,7 +63,9 @@ impl Scanner {
pub fn scan_tokens(mut self, err_hdl: &mut ErrorHandler) -> Vec<Token> { pub fn scan_tokens(mut self, err_hdl: &mut ErrorHandler) -> Vec<Token> {
while !self.is_at_end() { while !self.is_at_end() {
self.start = self.current; self.start = self.current;
self.scan_token(err_hdl); if let Err(e) = self.scan_token() {
err_hdl.report(e);
}
} }
self.tokens.push(Token { self.tokens.push(Token {
token_type: TokenType::Eof, token_type: TokenType::Eof,
@ -74,7 +76,7 @@ impl Scanner {
} }
/// Read the next token from the input /// Read the next token from the input
fn scan_token(&mut self, err_hdl: &mut ErrorHandler) { fn scan_token(&mut self) -> Result<(), SloxError> {
match self.advance() { match self.advance() {
// Single-character tokens // Single-character tokens
'(' => self.add_token(TokenType::LeftParen), '(' => self.add_token(TokenType::LeftParen),
@ -94,8 +96,9 @@ impl Scanner {
while self.peek() != '\n' && !self.is_at_end() { while self.peek() != '\n' && !self.is_at_end() {
self.current += 1; self.current += 1;
} }
Ok(())
} else if self.is_match('*') { } else if self.is_match('*') {
self.block_comment(err_hdl); self.block_comment()
} else { } else {
self.add_token(TokenType::Slash) self.add_token(TokenType::Slash)
} }
@ -130,25 +133,24 @@ impl Scanner {
} }
} }
// String litterals // String litterals
'"' => self.string_litteral(err_hdl), '"' => self.string_litteral(),
// Handle whitespace // Handle whitespace
' ' | '\r' | '\t' => (), ' ' | '\r' | '\t' => Ok(()),
'\n' => self.line += 1, '\n' => {
self.line += 1;
Ok(())
}
// Numbers // Numbers
ch if ch.is_ascii_digit() => self.number(err_hdl), ch if ch.is_ascii_digit() => self.number(),
// Identifiers // Identifiers
ch if ch.is_ascii_alphabetic() => self.identifier(), ch if ch.is_ascii_alphabetic() => self.identifier(),
// Anything else is an error // Anything else is an error
ch => err_hdl.error( ch => return self.error("unexpected character".to_owned()),
ErrorType::Parse,
self.line,
&format!("unexpected character {:#?}", ch),
),
} }
} }
/// Read the rest of a string litteral /// Read the rest of a string litteral
fn string_litteral(&mut self, err_hdl: &mut ErrorHandler) { fn string_litteral(&mut self) -> SloxResult<()> {
loop { loop {
let p = self.peek(); let p = self.peek();
if p == '"' || self.is_at_end() { if p == '"' || self.is_at_end() {
@ -161,16 +163,17 @@ impl Scanner {
} }
if self.is_at_end() { if self.is_at_end() {
err_hdl.error(ErrorType::Parse, self.line, "unterminated string"); self.error("unterminated string".to_owned())
} else { } else {
self.current += 1; // Last '"' self.current += 1; // Last '"'
let value = self.get_substring(self.start + 1, self.current - 1); let value = self.get_substring(self.start + 1, self.current - 1);
self.add_token(TokenType::String(value)); self.add_token(TokenType::String(value));
Ok(())
} }
} }
/// Read the rest of a number. /// Read the rest of a number.
fn number(&mut self, err_hdl: &mut ErrorHandler) { fn number(&mut self) -> Result<(), SloxError> {
while self.peek().is_ascii_digit() { while self.peek().is_ascii_digit() {
self.current += 1; self.current += 1;
} }
@ -183,24 +186,19 @@ impl Scanner {
let tok_string = self.get_substring(self.start, self.current); let tok_string = self.get_substring(self.start, self.current);
match tok_string.parse::<f64>() { match tok_string.parse::<f64>() {
Err(e) => {
err_hdl.error(
ErrorType::Parse,
self.line,
&format!(
"Could not parse {} as a floating point number: {:?}",
tok_string, e
),
);
}
Ok(value) => { Ok(value) => {
self.add_token(TokenType::Number(value)); self.add_token(TokenType::Number(value));
Ok(())
}
Err(e) => self.error(format!(
"Could not parse {} as a floating point number: {:?}",
tok_string, e
)),
} }
};
} }
/// Read the rest of an identifier or keyword. /// Read the rest of an identifier or keyword.
fn identifier(&mut self) { fn identifier(&mut self) -> Result<(), SloxError> {
while is_word_char(self.peek()) { while is_word_char(self.peek()) {
self.current += 1; self.current += 1;
} }
@ -212,12 +210,11 @@ impl Scanner {
} }
/// Read (and ignore) a block comment. Block comments may be nested. /// Read (and ignore) a block comment. Block comments may be nested.
fn block_comment(&mut self, err_hdl: &mut ErrorHandler) { fn block_comment(&mut self) -> Result<(), SloxError> {
let mut depth = 1; let mut depth = 1;
loop { loop {
if self.is_at_end() { if self.is_at_end() {
err_hdl.error(ErrorType::Parse, self.line, "unterminated block comment"); return self.error("unterminated block comment".to_owned());
return;
} }
let cur = self.advance(); let cur = self.advance();
@ -226,7 +223,7 @@ impl Scanner {
depth -= 1; depth -= 1;
self.current += 1; self.current += 1;
if depth == 0 { if depth == 0 {
return; return Ok(());
} }
} else if cur == '/' && next == '*' { } else if cur == '/' && next == '*' {
depth += 1; depth += 1;
@ -287,14 +284,15 @@ impl Scanner {
} }
/// Add a token to the output. /// Add a token to the output.
fn add_token(&mut self, token_type: TokenType) { fn add_token(&mut self, token_type: TokenType) -> SloxResult<()> {
let lexeme = self.get_substring(self.start, self.current); let lexeme = self.get_substring(self.start, self.current);
let token = Token { let token = Token {
token_type, token_type,
lexeme, lexeme,
line: self.line, line: self.line,
}; };
self.tokens.push(token) self.tokens.push(token);
Ok(())
} }
/// Get a substring from the source. /// Get a substring from the source.
@ -306,6 +304,19 @@ impl Scanner {
.take(end - start) .take(end - start)
.collect::<String>() .collect::<String>()
} }
/// Generate an error at the current position, with the specified message.
fn error(&self, message: String) -> SloxResult<()> {
Err(SloxError::scanner_error(
self.line,
if self.is_at_end() {
None
} else {
Some(self.peek())
},
message,
))
}
} }
/// Check whether a character is either alphanumeric or an underscore. /// Check whether a character is either alphanumeric or an underscore.