Réorganisation des dépendances avant la mise en place des tests

This commit is contained in:
2024-04-04 08:23:10 +02:00
parent 35c9ce4b5d
commit b218a4b427
7 changed files with 322 additions and 307 deletions

200
src/scanner.rs Normal file
View 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) }
}
}
}