feat: finish scanning section of the book

This commit is contained in:
Seymur Bagirov 2024-11-19 06:57:56 +04:00
parent 2bac50ecba
commit cd5fae595c
2 changed files with 64 additions and 16 deletions

View File

@ -4,6 +4,7 @@ use std::{
io::{self, Write}, io::{self, Write},
}; };
use scanner::Scanner;
use token::Token; use token::Token;
mod scanner; mod scanner;
@ -13,24 +14,37 @@ mod utils;
pub fn run_file(path: &str) -> Result<(), Box<dyn Error>> { pub fn run_file(path: &str) -> Result<(), Box<dyn Error>> {
let file = fs::read_to_string(path)?; let file = fs::read_to_string(path)?;
run(&file);
Ok(()) Ok(())
} }
pub fn run(src: &str) { pub fn run(src: &str) {
let tokens: Vec<Token> = 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<dyn Error>> { pub fn run_prompt() -> Result<(), Box<dyn Error>> {
let stdin = io::stdin(); let stdin = io::stdin();
let input = &mut String::new(); let input = &mut String::new();
print!("> ");
io::stdout().flush()?;
loop { loop {
input.clear(); input.clear();
let _ = stdin.read_line(input)?;
print!("> "); print!("> ");
io::stdout().flush()?; io::stdout().flush()?;
stdin.read_line(input)?;
run(input);
} }
} }
@ -40,12 +54,6 @@ pub struct RloxError {
line: usize, line: usize,
} }
impl RloxError { pub fn report(line: usize, location: &str, message: &str) {
pub fn error(line: i32, message: &str) {
report(line, "", message);
}
}
pub fn report(line: i32, location: &str, message: &str) {
eprintln!("[line {line}] Error {location}: {message}"); eprintln!("[line {line}] Error {location}: {message}");
} }

View File

@ -16,7 +16,7 @@ pub struct Scanner {
} }
impl 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 // 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 // rust doesn't allow having both the iterator and iterable inside one
// structure(understandably so bcs of reference invalidation) // 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 // this is so awful for me to write. This function needs to be not mutable in theory and it
// could be accomplished. TODO! // could be accomplished. TODO!
fn scan_tokens(&mut self) -> Result<&Vec<Token>, Vec<RloxError>> { pub fn scan_tokens(&mut self) -> Result<&Vec<Token>, Vec<RloxError>> {
let mut errors = Vec::new(); let mut errors = Vec::new();
while self.peek().is_some() { while self.peek().is_some() {
self.start = self.current; self.start = self.current;
@ -94,10 +94,11 @@ impl Scanner {
} }
'/' => self.add_token(TokenType::Slash), '/' => self.add_token(TokenType::Slash),
'"' => error = self.string(), '"' => error = self.string(),
'0'..='9' => self.number(),
' ' | '\r' | '\t' => (), ' ' | '\r' | '\t' => (),
'\n' => self.line += 1, '\n' => self.line += 1,
'0'..='9' => self.number(),
'a'..='z' | 'A'..='Z' | '_' => self.identifier(),
_ => { _ => {
error = Err(RloxError { error = Err(RloxError {
msg: "Unexpected character".to_string(), msg: "Unexpected character".to_string(),
@ -200,6 +201,45 @@ impl Scanner {
self.add_token_literal(TokenType::Number, Some(LiteralType::Number(number))); 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<TokenType> {
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)] #[cfg(test)]
@ -312,7 +352,7 @@ mod tests {
fn correct_fractional_number_scan() { fn correct_fractional_number_scan() {
let value = r#" let value = r#"
// number test // number test
123.456"# 123.aaa"#
.to_string(); .to_string();
let mut scanner = Scanner::new(value); let mut scanner = Scanner::new(value);