use logos::Logos;
#[derive(Logos, Debug, Clone, PartialEq)]
#[logos(skip r"[ \t\r\f]+")] pub enum Token {
#[regex(r"[0-9]+(\.[0-9]+)?", |lex| lex.slice().parse::<f64>().ok())]
Number(f64),
#[regex(r#""([^"\\]|\\.)*""#, |lex| {
let s = lex.slice();
Some(s[1..s.len()-1].replace("\\\"", "\"").replace("\\\\", "\\").replace("\\n", "\n"))
})]
Str(String),
#[regex(r"\$[A-Za-z_][A-Za-z0-9_]*", |lex| lex.slice().to_owned())]
Var(String),
#[regex(r"[A-Za-z_][A-Za-z0-9_]*", |lex| lex.slice().to_owned())]
Ident(String),
#[token("(")]
LParen,
#[token(")")]
RParen,
#[token(",")]
Comma,
#[token("<<")]
CmdOpen,
#[token(">>")]
CmdClose,
#[token("{")]
BraceOpen,
#[token("}")]
BraceClose,
#[token("+")]
Plus,
#[token("-")]
Minus,
#[token("*")]
Star,
#[token("/")]
Slash,
#[token("%")]
Percent,
#[token(">=")]
Gte,
#[token("<=")]
Lte,
#[token(">")]
Gt,
#[token("<")]
Lt,
#[token("==")]
EqEq,
#[token("!=")]
Neq,
#[token("&&")]
AndAnd,
#[token("||")]
OrOr,
#[token("!")]
Bang,
#[token("=")]
Eq,
#[token(":")]
Colon,
#[token("->")]
Arrow,
#[token("=>")]
FatArrow,
#[token("---")]
BodyStart,
#[token("===")]
NodeEnd,
#[token("#")]
Hash,
#[token("\n")]
Newline,
}
pub type Spanned = (Token, std::ops::Range<usize>);
pub fn tokenise(input: &str, file: &str, line: usize) -> crate::error::Result<Vec<Spanned>> {
let mut tokens = Vec::new();
for (result, span) in Token::lexer(input).spanned() {
if let Ok(tok) = result {
tokens.push((tok, span));
} else {
let ch = input[span].chars().next().unwrap_or('?');
return Err(crate::error::DialogueError::Parse {
file: file.to_owned(),
line,
message: format!(
"unexpected character `{ch}` in expression; \
did you mean `$` for a variable?"
),
});
}
}
Ok(tokens)
}
#[cfg(test)]
#[path = "lexer_tests.rs"]
mod tests;