diff --git a/src/lib.rs b/src/lib.rs index 37686ca..3e87e4c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,7 @@ use std::{ io::{self, Write}, }; +use scanner::Scanner; use token::Token; mod scanner; @@ -13,24 +14,37 @@ mod utils; pub fn run_file(path: &str) -> Result<(), Box> { let file = fs::read_to_string(path)?; + run(&file); Ok(()) } pub fn run(src: &str) { - let tokens: Vec = Vec::new(); + let mut scanner = Scanner::new(src.to_string()); + let tokens = scanner.scan_tokens(); + + match tokens { + Err(ref errors) => { + for err in errors { + report(err.line, "", &err.msg); + } + } + Ok(_) => { + for token in tokens.unwrap() { + println!("{}", token); + } + } + } } pub fn run_prompt() -> Result<(), Box> { let stdin = io::stdin(); let input = &mut String::new(); - print!("> "); - io::stdout().flush()?; loop { input.clear(); - let _ = stdin.read_line(input)?; - print!("> "); io::stdout().flush()?; + stdin.read_line(input)?; + run(input); } } @@ -40,12 +54,6 @@ pub struct RloxError { line: usize, } -impl RloxError { - pub fn error(line: i32, message: &str) { - report(line, "", message); - } -} - -pub fn report(line: i32, location: &str, message: &str) { +pub fn report(line: usize, location: &str, message: &str) { eprintln!("[line {line}] Error {location}: {message}"); } diff --git a/src/scanner.rs b/src/scanner.rs index 1804cef..bad0b2d 100644 --- a/src/scanner.rs +++ b/src/scanner.rs @@ -16,7 +16,7 @@ pub struct Scanner { } impl Scanner { - fn new(source: String) -> Self { + pub fn new(source: String) -> Self { // the reason for using unsafe here is to have the ability to use utf-8 symbols // rust doesn't allow having both the iterator and iterable inside one // structure(understandably so bcs of reference invalidation) @@ -35,7 +35,7 @@ impl Scanner { // this is so awful for me to write. This function needs to be not mutable in theory and it // could be accomplished. TODO! - fn scan_tokens(&mut self) -> Result<&Vec, Vec> { + pub fn scan_tokens(&mut self) -> Result<&Vec, Vec> { let mut errors = Vec::new(); while self.peek().is_some() { self.start = self.current; @@ -94,10 +94,11 @@ impl Scanner { } '/' => self.add_token(TokenType::Slash), '"' => error = self.string(), - '0'..='9' => self.number(), ' ' | '\r' | '\t' => (), '\n' => self.line += 1, + '0'..='9' => self.number(), + 'a'..='z' | 'A'..='Z' | '_' => self.identifier(), _ => { error = Err(RloxError { msg: "Unexpected character".to_string(), @@ -200,6 +201,45 @@ impl Scanner { self.add_token_literal(TokenType::Number, Some(LiteralType::Number(number))); } + + fn identifier(&mut self) { + while self.peek().is_some_and(is_alpha_numeric) { + self.advance(); + } + + let text_value = self.source.slice(self.start..self.current); + if let Some(identified_token) = get_identified_keyword(text_value) { + return self.add_token(identified_token); + } + + self.add_token(TokenType::Identifier); + } +} + +fn is_alpha_numeric(chr: char) -> bool { + matches!(chr ,'0'..='9'| '_' | 'a'..='z'|'A'..='Z') +} + +fn get_identified_keyword(identifier: &str) -> Option { + match identifier { + "and" => Some(TokenType::And), + "class" => Some(TokenType::Class), + "else" => Some(TokenType::Else), + "false" => Some(TokenType::False), + "for" => Some(TokenType::For), + "fun" => Some(TokenType::Fun), + "if" => Some(TokenType::If), + "nil" => Some(TokenType::Nil), + "or" => Some(TokenType::OR), + "print" => Some(TokenType::Print), + "return" => Some(TokenType::Return), + "super" => Some(TokenType::Super), + "this" => Some(TokenType::This), + "true" => Some(TokenType::True), + "var" => Some(TokenType::Var), + "while" => Some(TokenType::While), + _ => None, + } } #[cfg(test)] @@ -312,7 +352,7 @@ mod tests { fn correct_fractional_number_scan() { let value = r#" // number test - 123.456"# + 123.aaa"# .to_string(); let mut scanner = Scanner::new(value);