use logos::Logos;
#[derive(Logos, Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[logos(skip r"[ \t\r\n]+")]
pub enum Kind {
#[regex(r"\|[^|]*\|")]
Term,
#[regex(r#""([^"\\]|\\"|\\\\)*""#)]
String,
#[regex(r"[0-9]+")]
Integer,
#[token("===")]
EquivalentTo,
#[token("<<<")]
SubtypeOf,
#[token("=")]
Equal,
#[token("+")]
Plus,
#[token(":")]
Colon,
#[token(",")]
Comma,
#[token("{")]
LeftBrace,
#[token("}")]
RightBrace,
#[token("(")]
LeftParen,
#[token(")")]
RightParen,
#[token("#")]
Hash,
#[token("-")]
Dash,
#[token(".")]
Period,
}
impl crate::front::Lexeme for Kind {
fn describe(self) -> &'static str {
match self {
Self::Term => "a term between pipes",
Self::String => "a quoted string",
Self::Integer => "a number",
Self::EquivalentTo => "'==='",
Self::SubtypeOf => "'<<<'",
Self::Equal => "'='",
Self::Plus => "'+'",
Self::Colon => "':'",
Self::Comma => "','",
Self::LeftBrace => "'{'",
Self::RightBrace => "'}'",
Self::LeftParen => "'('",
Self::RightParen => "')'",
Self::Hash => "'#'",
Self::Dash => "'-'",
Self::Period => "'.'",
}
}
fn nesting(self) -> crate::front::Nesting {
match self {
Self::LeftParen | Self::LeftBrace => crate::front::Nesting::Opens,
Self::RightParen | Self::RightBrace => crate::front::Nesting::Closes,
_ => crate::front::Nesting::Neither,
}
}
}
#[cfg(test)]
mod tests {
use super::Kind;
use crate::front::lex;
fn kinds(input: &str) -> Vec<Kind> {
lex::<Kind>(input)
.expect("lexes")
.into_iter()
.map(|t| t.kind)
.collect()
}
#[test]
fn the_definition_status_prefixes_take_the_longest_match() {
assert_eq!(kinds("=== 123456"), [Kind::EquivalentTo, Kind::Integer]);
assert_eq!(kinds("<<< 123456"), [Kind::SubtypeOf, Kind::Integer]);
}
#[test]
fn a_refinement_lexes_into_its_literals() {
assert_eq!(
kinds("1 |a b| : { 2 = (3 : 4 = #-5.5) , 6 = \"t\" }"),
[
Kind::Integer,
Kind::Term,
Kind::Colon,
Kind::LeftBrace,
Kind::Integer,
Kind::Equal,
Kind::LeftParen,
Kind::Integer,
Kind::Colon,
Kind::Integer,
Kind::Equal,
Kind::Hash,
Kind::Dash,
Kind::Integer,
Kind::Period,
Kind::Integer,
Kind::RightParen,
Kind::Comma,
Kind::Integer,
Kind::Equal,
Kind::String,
Kind::RightBrace,
]
);
}
#[test]
fn an_escape_the_grammar_does_not_define_does_not_continue_a_string() {
let error = lex::<Kind>(r#""a\nb" "#).expect_err("refused");
assert_eq!(error.offset, 0);
assert_eq!(error.found, '"');
}
#[test]
fn an_unterminated_term_or_string_is_refused_at_its_opening() {
assert_eq!(
lex::<Kind>("123456 |unterminated")
.expect_err("refused")
.offset,
7
);
assert_eq!(
lex::<Kind>("123456 = \"open").expect_err("refused").offset,
9
);
}
}