cxgraph/src/language/scanner.rs

119 lines
2.3 KiB
Rust

use std::{iter::Peekable, str::Chars};
use super::Position;
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Token<'s> {
Error,
Name(&'s str),
Number(f64),
Plus,
Minus,
Star,
Slash,
Caret,
Equal,
Comma,
Arrow,
LParen,
RParen,
Newline,
}
pub struct Scanner<'s> {
pub src: &'s str,
pub chars: Peekable<Chars<'s>>,
pub pos: Position,
}
impl <'s> Scanner<'s> {
pub fn new(src: &'s str) -> Self {
Self {
src,
chars: src.chars().peekable(),
pos: Position { pos: 0, line: 1, col: 1 }
}
}
fn next(&mut self) -> Option<char> {
match self.chars.next() {
Some('\n') => {
self.pos.pos += 1;
self.pos.line += 1;
self.pos.col = 1;
Some('\n')
},
Some(c) => {
self.pos.pos += 1;
self.pos.col += 1;
Some(c)
},
None => None,
}
}
fn peek(&mut self) -> Option<char> {
self.chars.peek().copied()
}
fn next_number(&mut self, pos: u32) -> Token<'s> {
while matches!(self.peek(), Some('0'..='9')) {
self.next();
}
if matches!(self.peek(), Some('.')) {
self.next();
while matches!(self.peek(), Some('0'..='9')) {
self.next();
}
}
let s = &self.src[pos as usize..self.pos.pos as usize];
match s.parse() {
Ok(x) => Token::Number(x),
Err(_) => Token::Error,
}
}
fn next_name(&mut self, pos: u32) -> Token<'s> {
while matches!(self.peek(), Some('a'..='z' | 'A'..='Z' | '0'..='9' | '_')) {
self.next();
}
let s = &self.src[pos as usize..self.pos.pos as usize];
Token::Name(s)
}
pub fn next_token(&mut self) -> Option<(Position, Token<'s>)> {
while matches!(self.peek(), Some(' ' | '\t')) {
self.next();
}
self.peek()?;
let pos = self.pos;
let tok = match self.next().unwrap() {
'\n' => Token::Newline,
'+' => Token::Plus,
'-' => match self.peek().unwrap() {
'>' => { self.next(); Token::Arrow },
_ => Token::Minus,
}
'*' => Token::Star,
'/' => Token::Slash,
'^' => Token::Caret,
'=' => Token::Equal,
',' => Token::Comma,
'(' => Token::LParen,
')' => Token::RParen,
'0'..='9' => self.next_number(pos.pos),
'a'..='z' | 'A'..='Z' => self.next_name(pos.pos),
_ => Token::Error,
};
Some((pos, tok))
}
}
impl <'s> Iterator for Scanner<'s> {
type Item = (Position, Token<'s>);
fn next(&mut self) -> Option<Self::Item> {
self.next_token()
}
}