110 lines
2.4 KiB
Rust
110 lines
2.4 KiB
Rust
|
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()));
|
||
|
}
|
||
|
}
|
||
|
}
|