Scanner - Support for identifiers and keywords

This commit is contained in:
Emmanuel BENOîT 2022-12-30 20:11:08 +01:00
parent 3ccbcbc1c2
commit 21778a745e
3 changed files with 63 additions and 7 deletions

9
Cargo.lock generated
View file

@ -2,6 +2,15 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "slox"
version = "0.1.0"
dependencies = [
"lazy_static",
]

View file

@ -5,3 +5,4 @@ version = "0.1.0"
edition = "2021"
[dependencies]
lazy_static = "1.4.0"

View file

@ -1,6 +1,35 @@
use crate::{tokens::TokenType, ErrorHandler};
use std::collections::HashMap;
use super::tokens::Token;
use lazy_static::lazy_static;
use crate::{
tokens::{Token, TokenType},
ErrorHandler,
};
lazy_static! {
/// A map of keywords to token types.
static ref KEYWORDS: HashMap<&'static str, TokenType> = {
let mut keywords = HashMap::new();
keywords.insert("and", TokenType::And);
keywords.insert("class", TokenType::Class);
keywords.insert("else", TokenType::Else);
keywords.insert("false", TokenType::False);
keywords.insert("for", TokenType::For);
keywords.insert("fun", TokenType::Fun);
keywords.insert("if", TokenType::If);
keywords.insert("nil", TokenType::Nil);
keywords.insert("or", TokenType::Or);
keywords.insert("print", TokenType::Print);
keywords.insert("return", TokenType::Return);
keywords.insert("super", TokenType::Super);
keywords.insert("this", TokenType::This);
keywords.insert("true", TokenType::True);
keywords.insert("var", TokenType::Var);
keywords.insert("while", TokenType::While);
keywords
};
}
/// The scanner's state, including the source it is scanning.
pub struct Scanner {
@ -91,15 +120,15 @@ impl Scanner {
}
// String litterals
'"' => self.string_litteral(err_hdl),
// Numbers
'0'..='9' => self.number(err_hdl),
// Handle whitespace
' ' | '\r' | '\t' => (),
'\n' => self.line += 1,
// Numbers
ch if ch.is_digit(10) => self.number(err_hdl),
// Identifiers
ch if ch.is_ascii_alphabetic() => self.identifier(),
// Anything else is an error
ch => {
err_hdl.error(self.line, &format!("unexpected character {:#?}", ch));
}
ch => err_hdl.error(self.line, &format!("unexpected character {:#?}", ch)),
}
}
@ -154,6 +183,18 @@ impl Scanner {
};
}
/// Read the rest of an identifier or keyword.
fn identifier(&mut self) {
while is_word_char(self.peek()) {
self.current += 1;
}
let word = self.get_substring(self.start, self.current);
match KEYWORDS.get(&word as &str) {
Some(tt) => self.add_token(tt.clone()),
None => self.add_token(TokenType::Identifier(word)),
}
}
/// Check whether the end of the input has been reached.
fn is_at_end(&self) -> bool {
self.current >= self.len
@ -224,3 +265,8 @@ impl Scanner {
.collect::<String>()
}
}
/// Check whether a character is either alphanumeric or an underscore.
fn is_word_char(c: char) -> bool {
c.is_ascii_alphanumeric() || c == '_'
}