use std::{fmt::Display, borrow::Cow}; /// Operators #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Op { // Addition Add, Sub, // Multiplications Mul, Div, Mod, // Assignment Assign, // Equality Eq, Neq, Gt, Lt, Ge, Le, // Bool And, Or, Not, Xor, Arrow, } #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Group { Paren, Bracket, Braces, } #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Literal { Boolean(bool), Int64(i64), String(String), } #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Keyword { Let, While, Loop, If, Else, Fn, Return, Void, } #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Token { Literal(Literal), Op(Op), Open(Group), Close(Group), Ident(String), Keyword(Keyword), Semicolon, Colon, Comma, Dot, } pub struct TokenStream { tokens: Vec, idx: usize, } impl Display for Token { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let op: Cow<'static, str> = match self { Token::Op(Op::Add) => "+".into(), Token::Op(Op::Sub) => "-".into(), Token::Op(Op::Mul) => "*".into(), Token::Op(Op::Div) => "/".into(), Token::Op(Op::Mod) => "%".into(), Token::Op(Op::Eq) => "==".into(), Token::Op(Op::Neq) => "!=".into(), Token::Op(Op::Gt) => ">".into(), Token::Op(Op::Lt) => "<".into(), Token::Op(Op::Ge) => ">=".into(), Token::Op(Op::Le) => "<=".into(), Token::Op(Op::Assign) => "=".into(), Token::Op(Op::Arrow) => "->".into(), Token::Op(Op::And) => "&&".into(), Token::Op(Op::Or) => "||".into(), Token::Op(Op::Xor) => "^".into(), Token::Op(Op::Not) => "!".into(), Token::Open(Group::Paren) => "(".into(), Token::Open(Group::Bracket) => "[".into(), Token::Open(Group::Braces) => "{".into(), Token::Close(Group::Paren) => ")".into(), Token::Close(Group::Bracket) => "]".into(), Token::Close(Group::Braces) => "}".into(), Token::Literal(Literal::Int64(num)) => format!("Int64({})", num).into(), Token::Literal(Literal::String(text)) => format!("String({})", text).into(), Token::Literal(Literal::Boolean(val)) => format!("Boolean({})", val).into(), Token::Ident(ident) => format!("Ident({})", ident).into(), Token::Semicolon => ";".into(), Token::Colon => ":".into(), Token::Comma => ",".into(), Token::Dot => ".".into(), Token::Keyword(Keyword::Let) => "let".into(), Token::Keyword(Keyword::If) => "if".into(), Token::Keyword(Keyword::While) => "while".into(), Token::Keyword(Keyword::Loop) => "loop".into(), Token::Keyword(Keyword::Else) => "else".into(), Token::Keyword(Keyword::Fn) => "fn".into(), Token::Keyword(Keyword::Return) => "return".into(), Token::Keyword(Keyword::Void) => "void".into(), }; write!(f, "{}", op) } } impl TokenStream { pub fn new(tokens: Vec) -> Self { Self { tokens, idx: 0 } } pub fn as_vec(&self) -> &Vec { &self.tokens } pub fn curr(&self) -> Option<&Token> { self.tokens.get(self.idx) } pub fn peek(&self) -> Option<&Token> { self.tokens.get(self.idx + 1) } pub fn advance(&mut self) { self.idx += 1 } } impl Display for TokenStream { /// Print the TokenStream with autofomatting fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let mut indent = 0_usize; let mut fresh_line = true; for tok in self.tokens.iter() { if matches!(tok, Token::Close(Group::Braces)) { indent = indent.saturating_sub(1); fresh_line = true; } if fresh_line { write!(f, "{}", " ".repeat(indent * 4))?; fresh_line = false; } write!(f, "{} ", tok)?; match tok { Token::Open(Group::Braces) => { writeln!(f)?; indent += 1; fresh_line = true; } Token::Semicolon | Token::Close(Group::Braces) => { writeln!(f)?; fresh_line = true; } _ => () } } Ok(()) } }