75 lines
No EOL
2.1 KiB
Rust
75 lines
No EOL
2.1 KiB
Rust
use crate::token::Token;
|
|
|
|
pub struct Lexer {
|
|
source: Vec<u8>,
|
|
pos: usize
|
|
}
|
|
|
|
impl Lexer {
|
|
pub fn new(source: &str) -> Self {
|
|
Self {
|
|
source: source.as_bytes().to_vec(),
|
|
pos: 0
|
|
}
|
|
}
|
|
|
|
fn skip_whitespace(&mut self) {
|
|
while matches!(self.peek(), Some(c) if c.is_ascii_whitespace()) {
|
|
self.advance();
|
|
}
|
|
}
|
|
|
|
fn advance(&mut self) {
|
|
self.pos += 1;
|
|
}
|
|
|
|
fn peek(&self) -> Option<u8> {
|
|
self.source.get(self.pos).copied()
|
|
}
|
|
|
|
fn number(&mut self) -> Option<Token> {
|
|
let mut num = String::new();
|
|
|
|
while matches!(self.peek(), Some(b'0' ..= b'9')) {
|
|
num.push(self.peek().unwrap() as char);
|
|
self.advance();
|
|
}
|
|
|
|
Some(Token::Number(num.parse().unwrap()))
|
|
}
|
|
|
|
fn ident(&mut self) -> Option<Token> {
|
|
let mut ident = String::new();
|
|
|
|
while matches!(self.peek(), Some(b'a' ..= b'z' | b'A' ..= b'Z')) {
|
|
ident.push(self.peek().unwrap() as char);
|
|
}
|
|
|
|
Some(Token::Ident(ident))
|
|
}
|
|
|
|
pub fn next(&mut self) -> Option<Token> {
|
|
self.skip_whitespace();
|
|
match self.peek() {
|
|
Some(b'0' ..= b'9') => self.number(),
|
|
Some(b'a' ..= b'z' | b'A' ..= b'Z') => self.ident(),
|
|
|
|
Some(b'+') => {self.advance(); Some(Token::Add)},
|
|
Some(b'-') => {self.advance(); Some(Token::Sub)},
|
|
Some(b'*') => {self.advance(); Some(Token::Mul)},
|
|
Some(b'/') => {self.advance(); Some(Token::Div)},
|
|
Some(b'%') => {self.advance(); Some(Token::Mod)},
|
|
|
|
Some(b'(') => {self.advance(); Some(Token::LParen)},
|
|
Some(b')') => {self.advance(); Some(Token::RParen)},
|
|
Some(b'[') => {self.advance(); Some(Token::LBracket)},
|
|
Some(b']') => {self.advance(); Some(Token::RBracket)},
|
|
Some(b'{') => {self.advance(); Some(Token::LBrace)},
|
|
Some(b'}') => {self.advance(); Some(Token::RBrace)},
|
|
|
|
Some(b';') => {self.advance(); Some(Token::Semicolon)}
|
|
|
|
_ => None
|
|
}
|
|
}
|
|
} |