use std::ops::Range;
use ariadne::{Color, Label, Report, ReportKind, Source};
use logos::{Logos, Span};
use crate::dsl::{File, error::DslError};
#[derive(Logos, Debug, PartialEq, Clone)]
#[logos(skip r"[ \t\n\f]+")]
pub enum Token {
#[token("folder")]
KwFolder,
#[token("rule")]
KwRule,
#[token("name")]
KwName,
#[token("matcher")]
KwMatcher,
#[token("action")]
KwAction,
#[token("and")]
KwAnd,
#[token("or")]
KwOr,
#[token("not")]
KwNot,
#[token("subject")]
KwSubject,
#[token("to")]
KwTo,
#[token("from")]
KwFrom,
#[token("body")]
KwBody,
#[token("startswith")]
KwStartsWith,
#[token("contains")]
KwContains,
#[token("equals")]
KwEquals,
#[token("regex")]
KwRegex,
#[token("delete")]
KwDelete,
#[token("moveto")]
KwMoveTo,
#[regex(r"[a-z][a-z0-9_]*", |lex| lex.slice().to_owned())]
Ident(String),
#[regex(r#""([^"\\\x00-\x1f]|\\(["\\/bfnrt]|u[0-9a-fA-F]{4}))*""#, |lex| lex.slice()[1..lex.slice().len()-1].to_owned())]
Str(String),
#[token("{")]
LBrace,
#[token("}")]
RBrace,
#[token("[")]
LBracket,
#[token("]")]
RBracket,
#[token("(")]
LParen,
#[token(")")]
RParen,
#[token(":")]
Colon,
}
impl Token {
pub(super) fn to_err_string(&self) -> String {
match self {
Token::KwFolder => "keyword 'folder'".to_string(),
Token::KwRule => "keyword 'rule'".to_string(),
Token::KwName => "keyword 'name'".to_string(),
Token::KwMatcher => "keyword 'matcher'".to_string(),
Token::KwAction => "keyword 'action'".to_string(),
Token::KwAnd => "keyword 'and'".to_string(),
Token::KwOr => "keyword 'or'".to_string(),
Token::KwNot => "keyword 'not'".to_string(),
Token::KwSubject => "keyword 'subject'".to_string(),
Token::KwTo => "keyword 'to'".to_string(),
Token::KwFrom => "keyword 'from'".to_string(),
Token::KwBody => "keyword 'body'".to_string(),
Token::KwStartsWith => "keyword 'startswith'".to_string(),
Token::KwContains => "keyword 'contains'".to_string(),
Token::KwEquals => "keyword 'equals'".to_string(),
Token::KwRegex => "keyword 'regex'".to_string(),
Token::KwDelete => "keyword 'delete'".to_string(),
Token::KwMoveTo => "keyword 'moveto'".to_string(),
Token::Ident(_s) => "an identifier".to_string(),
Token::Str(_s) => "a string".to_string(),
Token::LBrace => "'{'".to_string(),
Token::RBrace => "'}'".to_string(),
Token::LBracket => "'['".to_string(),
Token::RBracket => "']'".to_string(),
Token::LParen => "'('".to_string(),
Token::RParen => "')'".to_string(),
Token::Colon => "':'".to_string(),
}
}
}
pub struct SyntaxError {
span: Range<usize>,
}
impl DslError for SyntaxError {
fn print_error(&self, file: &File) {
Report::build(ReportKind::Error, (&file.file_name, self.span.clone()))
.with_message("Syntax error.".to_string())
.with_label(
Label::new((&file.file_name, self.span.clone()))
.with_message("Error detected here")
.with_color(Color::Red),
)
.finish()
.print((&file.file_name, Source::from(&file.contents)))
.unwrap();
}
}
pub fn process_tokens(file: &File) -> Result<Vec<(Token, Span)>, Vec<impl DslError>> {
let tokens = Token::lexer(&file.contents).spanned();
let mut syntax_errors = tokens.clone().filter(|(res, _)| res.is_err()).peekable();
if syntax_errors.peek().is_some() {
return Err(syntax_errors
.map(|(_, span)| SyntaxError { span })
.collect());
}
Ok(tokens
.filter_map(|(token_result, span)| token_result.ok().map(|token| (token, span)))
.collect())
}