Skip to content

Commit

Permalink
Scan numbers
Browse files Browse the repository at this point in the history
  • Loading branch information
JamesAC committed Dec 15, 2023
1 parent 7f1e977 commit fc7c3e9
Show file tree
Hide file tree
Showing 2 changed files with 187 additions and 75 deletions.
260 changes: 186 additions & 74 deletions src/scanner.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crust_grammar::token::{self, Token};
use crust_grammar::token::Token;
use std::str::FromStr;

use crate::util::{CrustCoreErr, CrustCoreResult};

Expand Down Expand Up @@ -45,101 +46,97 @@ impl<'a> Scanner<'a> {
fn scan_token(&mut self, errors: &mut Vec<CrustCoreErr>) {
let char = self.advance();
match char {
"(" => self.tokens.push(Token::LeftParen {
'(' => self.tokens.push(Token::LeftParen {
offset: self.current - 1,
line: self.line,
}),
")" => self.tokens.push(Token::RightParen {
')' => self.tokens.push(Token::RightParen {
offset: self.current - 1,
line: self.line,
}),
"{" => self.tokens.push(Token::LeftBrace {
'{' => self.tokens.push(Token::LeftBrace {
offset: self.current - 1,
line: self.line,
}),
"}" => self.tokens.push(Token::RightBrace {
'}' => self.tokens.push(Token::RightBrace {
offset: self.current - 1,
line: self.line,
}),
"," => self.tokens.push(Token::Comma {
',' => self.tokens.push(Token::Comma {
offset: self.current - 1,
line: self.line,
}),
"." => self.tokens.push(Token::Dot {
'.' => self.tokens.push(Token::Dot {
offset: self.current - 1,
line: self.line,
}),
"-" => self.tokens.push(Token::Minus {
'-' => self.tokens.push(Token::Minus {
offset: self.current - 1,
line: self.line,
}),
"+" => self.tokens.push(Token::Plus {
'+' => self.tokens.push(Token::Plus {
offset: self.current - 1,
line: self.line,
}),
";" => self.tokens.push(Token::Semicolon {
';' => self.tokens.push(Token::Semicolon {
offset: self.current - 1,
line: self.line,
}),
"*" => self.tokens.push(Token::Star {
'*' => self.tokens.push(Token::Star {
offset: self.current - 1,
line: self.line,
}),
"!" => {
if self.advance_if("=") {
self.tokens.push(Token::BangEqual {
offset: self.current - 2,
line: self.line,
});
} else {
self.tokens.push(Token::Bang {
offset: self.current - 1,
line: self.line,
});
}
'!' if self.advance_if('=') => {
self.tokens.push(Token::BangEqual {
offset: self.current - 2,
line: self.line,
});
}
"=" => {
if self.advance_if("=") {
self.tokens.push(Token::EqualEqual {
offset: self.current - 2,
line: self.line,
});
} else {
self.tokens.push(Token::Equal {
offset: self.current - 1,
line: self.line,
});
}
'!' => {
self.tokens.push(Token::Bang {
offset: self.current - 1,
line: self.line,
});
}
"<" => {
if self.advance_if("=") {
self.tokens.push(Token::LessEqual {
offset: self.current - 2,
line: self.line,
});
} else {
self.tokens.push(Token::Less {
offset: self.current - 1,
line: self.line,
});
}
'=' if self.advance_if('=') => {
self.tokens.push(Token::EqualEqual {
offset: self.current - 2,
line: self.line,
});
}
">" => {
if self.advance_if("=") {
self.tokens.push(Token::GreaterEqual {
offset: self.current - 2,
line: self.line,
});
} else {
self.tokens.push(Token::Greater {
offset: self.current - 1,
line: self.line,
});
}
'=' => {
self.tokens.push(Token::Equal {
offset: self.current - 1,
line: self.line,
});
}
'<' if self.advance_if('=') => {
self.tokens.push(Token::LessEqual {
offset: self.current - 2,
line: self.line,
});
}
'<' => {
self.tokens.push(Token::Less {
offset: self.current - 1,
line: self.line,
});
}
"/" => {
if self.advance_if("/") {
while self.peek() != "\n" && !self.is_at_end() {
'>' if self.advance_if('=') => {
self.tokens.push(Token::GreaterEqual {
offset: self.current - 2,
line: self.line,
});
}
'>' => {
self.tokens.push(Token::Greater {
offset: self.current - 1,
line: self.line,
});
}
'/' => {
if self.advance_if('/') {
while self.peek() != '\n' && !self.is_at_end() {
self.advance();
}
} else {
Expand All @@ -149,9 +146,14 @@ impl<'a> Scanner<'a> {
});
}
}
" " | "\t" | "\r" => {}
"\n" => self.line += 1,
"\"" => {
'0'..='9' => {
if let Err(e) = self.take_number_literal() {
errors.push(e);
}
}
' ' | '\t' | '\r' => {}
'\n' => self.line += 1,
'\"' => {
if let Err(e) = self.take_string_literal() {
errors.push(e);
}
Expand All @@ -167,12 +169,12 @@ impl<'a> Scanner<'a> {
self.current >= self.source.len()
}

fn advance(&mut self) -> &str {
fn advance(&mut self) -> char {
self.current += 1;
self.char_at(self.current - 1)
}

fn advance_if(&mut self, pattern: &str) -> bool {
fn advance_if(&mut self, pattern: char) -> bool {
if self.is_at_end() || self.char_at(self.current) != pattern {
false
} else {
Expand All @@ -181,21 +183,21 @@ impl<'a> Scanner<'a> {
}
}

fn peek(&self) -> &str {
fn peek(&self) -> char {
if self.is_at_end() {
"\0"
'\0'
} else {
self.char_at(self.current)
}
}

fn char_at(&self, index: usize) -> &str {
&self.source[index..index + 1]
fn char_at(&self, index: usize) -> char {
self.source[index..index + 1].chars().next().unwrap()
}

fn take_string_literal(&mut self) -> CrustCoreResult<()> {
while self.peek() != "\"" && !self.is_at_end() {
if self.peek() == "\n" {
fn take_string_literal(&mut self) -> CrustCoreResult {
while self.peek() != '\"' && !self.is_at_end() {
if self.peek() == '\n' {
self.line += 1;
}
self.advance();
Expand All @@ -219,6 +221,58 @@ impl<'a> Scanner<'a> {

Ok(())
}

fn take_number_literal(&mut self) -> CrustCoreResult {
while self.peek().is_ascii_digit() {
self.advance();
}

if self.peek() == '.' && self.peek_next().is_ascii_digit() {
self.advance();
while self.peek().is_ascii_digit() {
self.advance();
}
}

let literal = &self.source[self.start..self.current];
if literal.contains('.') {
if let Ok(val) = f32::from_str(literal) {
self.tokens.push(Token::Float {
offset: self.start,
length: self.current - self.start,
line: self.line,
value: val,
});
} else {
return Err(CrustCoreErr::Scan {
line: self.line,
message: "Invalid float value".to_string(),
});
}
} else if let Ok(val) = i32::from_str(literal) {
self.tokens.push(Token::Integer {
offset: self.start,
length: self.current - self.start,
line: self.line,
value: val,
});
} else {
return Err(CrustCoreErr::Scan {
line: self.line,
message: "Invalid integer value".to_string(),
});
}

Ok(())
}

fn peek_next(&self) -> char {
if self.current + 1 >= self.source.len() {
'\0'
} else {
self.char_at(self.current + 1)
}
}
}

#[cfg(test)]
Expand Down Expand Up @@ -330,6 +384,64 @@ mod tests {
.for_each(|(token, symbol)| assert_eq!(*token, symbol));
}

#[test]
fn scan_float_literal_with_access() {
let symbols = vec![
Token::LeftParen { offset: 0, line: 1 },
Token::Float {
line: 1,
offset: 1,
length: 3,
value: 1.3f32,
},
Token::Dot { line: 1, offset: 4 },
Token::RightParen { offset: 5, line: 1 },
Token::Integer {
line: 1,
offset: 6,
length: 2,
value: 25i32,
},
Token::Dot { line: 1, offset: 8 },
];
let scanner = Scanner::new("(1.3.)25.");
let tokens = scanner.scan_tokens();

tokens
.unwrap()
.iter()
.zip(symbols)
.for_each(|(token, symbol)| assert_eq!(*token, symbol));
}

#[test]
fn scan_number_literal() {
let symbols = vec![
Token::LeftParen { offset: 0, line: 1 },
Token::Float {
line: 1,
offset: 1,
length: 3,
value: 1.3f32,
},
Token::RightParen { offset: 4, line: 1 },
Token::Integer {
line: 1,
offset: 5,
length: 2,
value: 25i32,
},
];
let scanner = Scanner::new("(1.3)25");
let tokens = scanner.scan_tokens();

tokens
.unwrap()
.iter()
.zip(symbols)
.for_each(|(token, symbol)| assert_eq!(*token, symbol));
}

#[test]
fn scan_string_literal() {
let symbols = vec![
Expand Down
2 changes: 1 addition & 1 deletion src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ pub enum CrustCoreErr {
Runtime,
}

pub type CrustCoreResult<T> = Result<T, CrustCoreErr>;
pub type CrustCoreResult<T = ()> = Result<T, CrustCoreErr>;

0 comments on commit fc7c3e9

Please sign in to comment.