use console::Style; use logos::Logos; #[rustfmt::skip] #[derive(Logos, Debug, PartialEq)] pub enum Token { // keywords #[token("struct")] Struct, #[token("fn")] Function, #[token("for")] For, #[token("if")] If, #[token("else")] Else, #[token("in")] In, #[token("let")] Let, #[token("var")] Var, // separators #[token("{")] BraceOpen, #[token("}")] BraceClose, #[token("(")] ParanOpen, #[token(")")] ParanClose, #[token(";")] Semicolon, #[token(",")] Comma, #[token(".")] Dot, // arithmetic operators #[token("+")] OpAdd, #[token("-")] OpSub, #[token("*")] OpMul, #[token("/")] OpDiv, #[token("=")] OpAssign, // comparison operators #[token("<")] OpLess, #[token("<=")] OpLessEq, #[token(">")] OpGreater, #[token(">=")] OpGreaterEq, #[token("==")] OpEq, #[token("!=")] OpNeq, #[regex(r"[a-zA-Z_][a-zA-Z0-9_]*")] Identifier, #[regex(r"//[^\n]*")] SingleLineComment, #[regex(r"0b_*[01][_01]*")] BinaryInteger, #[regex(r"0o_*[0-7][_0-7]*")] OctalInteger, #[regex(r"-?[0-9][_'0-9]*")] DecimalInteger, #[regex(r"[ \t\n\f ]+")] // , logos::skip)] Whitespace, #[error] Error, } pub struct ColorTheme { normal: Style, keyword: Style, literal: Style, comment: Style, error: Style, } impl ColorTheme { pub fn new() -> Self { Self { normal: Style::default(), keyword: Style::new().blue(), literal: Style::new().cyan(), comment: Style::new().white(), error: Style::new().black().on_red().bold(), } } pub fn token_style(&self, token: &Token) -> &Style { use Token::*; match token { Struct | Function | For | If | Else | In | Let | Var => &self.keyword, BinaryInteger | OctalInteger | DecimalInteger => &self.literal, SingleLineComment => &self.comment, Error => &self.error, _ => &self.normal, } } } #[cfg(test)] mod tests { use super::*; #[test] fn color_file() { let source = include_str!("../example.ktn"); let theme = ColorTheme::new(); let mut lex = Token::lexer(source); while let Some(token) = lex.next() { let style = theme.token_style(&token); print!("{}", style.apply_to(lex.slice())); } } }