mod token;
use crate::Error;
pub use token::{
Token,
TokenClass,
};
pub struct Charstream {
chars: Vec<char>,
index: usize,
}
const TOKENBREAK: [char; 2] = [' ', ';'];
const WHITESPACE: [char; 3] = [' ', '\t', '\n'];
impl Charstream {
pub fn from(input: &str) -> Self {
Self {
chars: input.chars().collect::<Vec<char>>(),
index: 0,
}
}
fn skip(&mut self) {
if let Some (c) = self.peek() {
if WHITESPACE.contains(&c) {
self.next();
}
}
}
pub fn get(&mut self) -> Option<Token> {
use TokenClass::*;
self.skip();
let peek = match self.peek() {
Some (p) => p,
None => return None,
};
let class: TokenClass = peek.into();
let mut value = String::new();
while let Some (c) = self.peek() {
if TOKENBREAK.contains(&c) {
self.next();
break;
}
if ('A'..='Z').contains(&c) && class == Identifier {
value.push(c);
} else if ('a'..='z').contains(&c) && class == Identifier {
value.push(c);
} else if c == '_' && class == Identifier {
value.push(c);
} else if ('0'..'9').contains(&c) && class == Identifier {
value.push(c);
} else if c == '=' && class == Assignment {
value.push(c);
} else if c == '+' && class == Plus {
value.push(c);
} else if c == '-' && class == Minus {
value.push(c);
} else if c == '*' && class == Times {
value.push(c);
} else if c == '/' && class == Divide {
value.push(c);
} else if c == '(' && class == OpenParen {
value.push(c);
} else if c == ')' && class == CloseParen {
value.push(c);
} else if c == '!' && class == Symbolic {
value.push(c);
} else if c == '\n' && class == Newline {
value.push(c);
} else if (('0'..='9').contains(&c) || c == '.' || c == 'e' || c == '+' || c == '-') && class == Number {
value.push(c);
} else {
break;
}
self.next();
}
if value.as_str() == "let" && class == Identifier {
return Some (Token {
class: Let,
value: "let".to_string(),
});
}
Some (Token {
class,
value,
})
}
pub fn peek(&self) -> Option<char> {
if self.index < self.chars.len() {
Some (self.chars[self.index])
} else {
None
}
}
pub fn next(&mut self) -> Option<char> {
let c = self.peek();
self.index += 1;
c
}
}
#[derive(Debug)]
pub struct Tokenstream {
tokens: Vec<Token>,
index: usize,
}
impl Tokenstream {
pub fn from(input: &str, debug: bool) -> Self {
let mut tokens = Vec::new();
let mut charstream = Charstream::from(input);
while let Some (t) = charstream.get() {
tokens.push(t);
}
if debug {
println!("Constructed token stream with {} tokens", tokens.len());
println!();
}
Self {
tokens,
index: 0,
}
}
pub fn peek(&self) -> Option<Token> {
if self.index < self.tokens.len() {
Some (self.tokens[self.index].to_owned())
} else {
None
}
}
pub fn next(&mut self) -> Option<Token> {
let t = self.peek();
self.index += 1;
t
}
pub fn next_unwrap(&mut self) -> Token {
let last = match self.tokens.iter().last() {
Some (t) => t.value.clone(),
None => String::from("EOF"),
};
let next = self.next();
match next {
Some (t) => t,
None => Error::UnexpectedEOF (&last).throw(),
}
}
pub fn get(&mut self, class: TokenClass) -> Token {
let token = self.next_unwrap();
if token.class == class {
token
} else {
Error::Expected (class, token.class).throw()
}
}
pub fn precedence(&self) -> u8 {
match self.peek() {
Some (t) => t.precedence(),
None => 0,
}
}
}
#[test]
fn test_tokenization() {
let tokens = Tokenstream::from("hello_world = 3", false);
println!("{:#?}", tokens);
}