Scanner - Refactored to use new error handling
This commit is contained in:
parent
e152e40678
commit
260b19030f
1 changed files with 45 additions and 34 deletions
|
@ -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.
|
||||||
|
|
Loading…
Reference in a new issue