Réorganisation des dépendances avant la mise en place des tests
This commit is contained in:
200
src/scanner.rs
Normal file
200
src/scanner.rs
Normal file
@@ -0,0 +1,200 @@
|
||||
use std::collections::HashMap;
|
||||
use crate::token::Token;
|
||||
use crate::token_type::TokenType;
|
||||
|
||||
fn is_digit(c: char) -> bool {
|
||||
c >= '0' && c <= '9'
|
||||
}
|
||||
|
||||
fn is_alpha(c: char) -> bool {
|
||||
(c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'
|
||||
}
|
||||
|
||||
fn is_alpha_numeric(c: char) -> bool {
|
||||
is_digit(c) || is_alpha(c)
|
||||
}
|
||||
|
||||
pub struct Scanner {
|
||||
source: Vec<char>,
|
||||
tokens: Vec<Token>,
|
||||
|
||||
start: usize,
|
||||
current: usize,
|
||||
line: u32,
|
||||
|
||||
keywords: HashMap<String, TokenType>,
|
||||
}
|
||||
|
||||
impl Scanner {
|
||||
pub fn create_scanner( src: String ) -> Self {
|
||||
Self {
|
||||
source: src.chars().collect::<Vec<_>>(),
|
||||
tokens: vec![],
|
||||
start: 0,
|
||||
current: 0,
|
||||
line: 0,
|
||||
keywords: HashMap::new()
|
||||
}
|
||||
}
|
||||
|
||||
fn init_keywords(&mut self) {
|
||||
self.keywords = HashMap::new();
|
||||
self.keywords.insert(String::from("and"), TokenType::And);
|
||||
self.keywords.insert(String::from("class"), TokenType::Class);
|
||||
self.keywords.insert(String::from("else"), TokenType::Else);
|
||||
self.keywords.insert(String::from("false"), TokenType::False);
|
||||
self.keywords.insert(String::from("for"), TokenType::For);
|
||||
self.keywords.insert(String::from("fun"), TokenType::Fun);
|
||||
self.keywords.insert(String::from("if"), TokenType::If);
|
||||
self.keywords.insert(String::from("nil"), TokenType::Nil);
|
||||
self.keywords.insert(String::from("or"), TokenType::Or);
|
||||
self.keywords.insert(String::from("print"), TokenType::Print);
|
||||
self.keywords.insert(String::from("return"), TokenType::Return);
|
||||
self.keywords.insert(String::from("super"), TokenType::Super);
|
||||
self.keywords.insert(String::from("this"), TokenType::This);
|
||||
self.keywords.insert(String::from("true"), TokenType::True);
|
||||
self.keywords.insert(String::from("var"), TokenType::Var);
|
||||
self.keywords.insert(String::from("while"), TokenType::While);
|
||||
}
|
||||
|
||||
fn scan_tokens(&mut self) {
|
||||
while !self.is_at_end() {
|
||||
self.start = self.current;
|
||||
self.scan_token();
|
||||
}
|
||||
|
||||
// Ajout d'un token final quand il n'y a plus rien à parser
|
||||
self.tokens.push(Token { token_type: TokenType::Eof, lexeme: String::from(""), literal: String::from(""), line: self.line });
|
||||
}
|
||||
|
||||
fn is_at_end(&self) -> bool {
|
||||
self.current >= self.source.len()
|
||||
}
|
||||
|
||||
fn scan_token(&mut self) {
|
||||
let c = self.advance();
|
||||
match c {
|
||||
'(' => self.add_simple_token(TokenType::LeftParen),
|
||||
')' => self.add_simple_token(TokenType::RightParen),
|
||||
'{' => self.add_simple_token(TokenType::LeftBrace),
|
||||
'}' => self.add_simple_token(TokenType::RightBrace),
|
||||
',' => self.add_simple_token(TokenType::Comma),
|
||||
'.' => self.add_simple_token(TokenType::Dot),
|
||||
'-' => self.add_simple_token(TokenType::Minus),
|
||||
'+' => self.add_simple_token(TokenType::Plus),
|
||||
';' => self.add_simple_token(TokenType::Semicolon),
|
||||
'*' => self.add_simple_token(TokenType::Star),
|
||||
'!' => { if self.match_next('=') { self.add_simple_token(TokenType::BangEqual) } else { self.add_simple_token(TokenType::Bang) } },
|
||||
'=' => { if self.match_next('=') { self.add_simple_token(TokenType::EqualEqual) } else { self.add_simple_token(TokenType::Equal) } },
|
||||
'<' => { if self.match_next('=') { self.add_simple_token(TokenType::LessEqual) } else { self.add_simple_token(TokenType::Less) } },
|
||||
'>' => { if self.match_next('=') { self.add_simple_token(TokenType::GreaterEqual) } else { self.add_simple_token(TokenType::Greater) } },
|
||||
'/' => {
|
||||
if self.match_next('/') {
|
||||
// commentaire : avance jusqu'à la fin de la ligne sans ajouter de token
|
||||
while self.peek() != '\n' && !self.is_at_end() {
|
||||
self.advance();
|
||||
}
|
||||
} else {
|
||||
self.add_simple_token(TokenType::Slash)
|
||||
}
|
||||
},
|
||||
' ' => (),
|
||||
'\r' => (),
|
||||
'\t' => (),
|
||||
'\n' => self.line += 1,
|
||||
'"' => self.string(),
|
||||
_ => {
|
||||
if is_digit(c) {
|
||||
self.number();
|
||||
} else if is_alpha(c) {
|
||||
self.identifier();
|
||||
} else {
|
||||
// Erreur : lexeme inconnu
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn advance(&mut self) -> char {
|
||||
self.current += 1;
|
||||
self.source[self.current]
|
||||
}
|
||||
|
||||
fn match_next(&mut self, expected: char) -> bool {
|
||||
if self.is_at_end() { return false; }
|
||||
if self.source[self.current] != expected { return false; }
|
||||
|
||||
self.current += 1;
|
||||
true
|
||||
}
|
||||
|
||||
fn peek(&self) -> char {
|
||||
if self.is_at_end() {
|
||||
'\0'
|
||||
} else {
|
||||
self.source[self.current]
|
||||
}
|
||||
}
|
||||
|
||||
fn peek_next(&self) -> char {
|
||||
if self.current + 1 >= self.source.len() {
|
||||
'\0'
|
||||
} else {
|
||||
self.source[self.current + 1]
|
||||
}
|
||||
}
|
||||
|
||||
fn add_simple_token(&mut self, t: TokenType) {
|
||||
self.add_token(t, String::from(""));
|
||||
}
|
||||
|
||||
fn add_token(&mut self, t: TokenType, l: String) {
|
||||
let text = self.source[self.start..self.current].iter().collect();
|
||||
self.tokens.push(Token { token_type: t, lexeme: text, literal: l, line: self.line });
|
||||
}
|
||||
|
||||
fn string(&mut self) {
|
||||
// Consomme les caractères jusqu'à trouver le délimiteur de chaînes ou la fin du fichier
|
||||
while self.peek() != '"' && !self.is_at_end() {
|
||||
if self.peek() == '\n' {
|
||||
self.line += 1; // les chaînes peuvent être multilignes
|
||||
}
|
||||
self.advance();
|
||||
}
|
||||
|
||||
if self.is_at_end() {
|
||||
// Erreur : chaîne non terminée
|
||||
return;
|
||||
}
|
||||
|
||||
self.advance(); // Consomme le délimiteur final
|
||||
|
||||
self.add_token(TokenType::String, self.source[self.start + 1..self.current - 1].into_iter().collect());
|
||||
}
|
||||
|
||||
fn number(&mut self) {
|
||||
while is_digit(self.peek()) {
|
||||
self.advance();
|
||||
}
|
||||
|
||||
if self.peek() == '.' && is_digit(self.peek_next()) {
|
||||
while is_digit(self.peek()) {
|
||||
self.advance();
|
||||
}
|
||||
}
|
||||
|
||||
self.add_token(TokenType::Number, self.source[self.start..self.current].into_iter().collect()); // Il faudra faire un parse sur la chaîne pour connaître la valeur effective
|
||||
}
|
||||
|
||||
fn identifier(&mut self) {
|
||||
while is_alpha_numeric(self.peek()) {
|
||||
self.advance();
|
||||
}
|
||||
|
||||
let text: String = self.source[self.start..self.current].into_iter().collect();
|
||||
match self.keywords.get(&text) {
|
||||
Some(t) => { self.add_simple_token(*t) },
|
||||
None => { self.add_token(TokenType::Identifier, text) }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user