I'm relatively new to rust, so you don't need to go easy on the criticism.
right now, this lexes 1MB of text (well, trims a 1MB-long word) in 30ms. is there any way I can make this faster?
cursor.rs:
use std::str::Chars;
#[derive(Debug)]
pub struct Cursor<'a> {
peek: Option<char>,
pub chars: Chars<'a>,
pub pos: usize,
pub row: usize,
pub col: usize
}
impl<'a> Cursor<'a> {
pub fn new(text: &'a str) -> Self {
let mut chars = text.chars();
Self {
peek: chars.next(),
chars,
pos: 0,
row: 1,
col: 0,
}
}
pub fn next(&mut self) -> Option<char> {
let peek = self.peek?;
if peek == '\n' {
self.col = 1;
self.row += 1;
} else {
self.col += 1;
}
self.pos += peek.len_utf8();
self.peek = self.chars.next();
Some(peek)
}
pub fn peek_is<F: FnOnce(char) -> bool>(&mut self, fun: F) -> bool {
self.peek.map_or(false, fun)
}
#[inline]
pub fn peeking(&mut self, ch: char) -> bool {
self.peek == Some(ch)
}
pub fn next_if<F: FnOnce(char) -> bool>(&mut self, fun: F) -> Option<char> {
if self.peek_is(fun) {
self.next()
} else {
None
}
}
}
lexer.rs:
use crate::cursor::Cursor;
pub enum TokenKind<'a> {
Ident(&'a str),
Num(&'a str),
Str(String)
}
pub struct Lexer<'a> {
text: &'a str,
pub cursor: Cursor<'a>
}
impl<'a> Lexer<'a> {
pub fn new<T: Into<&'a str>>(text: T) -> Self {
let text = text.into();
Self {
text,
cursor: Cursor::new(text)
}
}
#[inline]
pub fn token<F: FnOnce(char) -> bool + Copy>(&mut self, fun: F) -> &'a str {
let pos = self.cursor.pos;
while self.cursor.peek_is(fun) {
self.cursor.next();
}
&self.text[pos..self.cursor.pos]
}
pub fn trim_ident(&mut self) -> &'a str {
self.token(|x| x.is_alphanumeric() || x == '_')
}
pub fn trim_integer(&mut self) -> &'a str {
self.token(char::is_numeric)
}
}
// impl<'a> Iterator for Lexer<'a> {
// type Item = TokenKind<'a>;
//
// fn next(&mut self) -> Option<TokenKind<'a>> {
// todo!()
// }
// }
lib.rs:
#![allow(unused)]
pub mod cursor;
pub mod lexer;
pub use lexer::*;
#[cfg(test)]
mod tests {
use super::*;
}
main.rs:
use scanner_rs::*;
macro_rules! time {
($name:literal, $($test:tt)*) => {
{
let start = std::time::Instant::now();
$($test)*
println!("{}: {:?}", $name, start.elapsed());
}
}
}
fn main() {
let code = "H".repeat(104_857_6);
let mut test_time = Lexer::new(&*code);
time! {
"init",
Lexer::new(&*code);
};
time! {
"next",
lexer.cursor.next();
};
time! {
"ident",
println!("{}", lexer.trim_ident()); // HHHHHHHHHHHH...
};
let mut hello_world = Lexer::new("Hello");
println!("{}", lexer.trim_ident()); // Hello
}
next
method onCursor
, you should do so by implementing theIterator
trait for it. \$\endgroup\$