use std::fmt::Display;
use super::{TypeDetect, LexingError};
use logos::Logos;
use wagon_macros::inherit_from_base;
use logos_display::{Debug, Display};
use wagon_utils::rem_first_and_last_char;
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub enum ImportType {
Basic,
Full,
Recursive,
Exclude
}
impl TypeDetect for ImportType {
fn detect(inp: &str, span: logos::Span) -> Result<Self, LexingError> {
match inp.chars().last() {
Some('-') => Ok(Self::Basic),
Some('=') => Ok(Self::Full),
Some('<') => Ok(Self::Recursive),
Some('/') => Ok(Self::Exclude),
Some(x) => Err(LexingError::UnexpectedCharacter(x.to_string(), span)),
None => Err(LexingError::UnexpectedEOF(span))
}
}
}
impl Default for ImportType {
fn default() -> Self {
Self::Basic
}
}
impl Display for ImportType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Basic => write!(f, "<-"),
Self::Full => write!(f, "<="),
Self::Recursive => write!(f, "<<"),
Self::Exclude => write!(f, "</"),
}
}
}
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub enum EbnfType {
Some,
Many,
Maybe
}
impl TypeDetect for EbnfType {
fn detect(inp: &str, span: logos::Span) -> Result<Self, LexingError> {
match inp.chars().next() {
Some('+') => Ok(Self::Some),
Some('*') => Ok(Self::Many),
Some('?') => Ok(Self::Maybe),
Some(x) => Err(LexingError::UnexpectedCharacter(x.to_string(), span)),
None => Err(LexingError::UnexpectedEOF(span))
}
}
}
impl Display for EbnfType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Some => write!(f, "+"),
Self::Many => write!(f, "*"),
Self::Maybe => write!(f, "?"),
}
}
}
#[derive(Eq)]
#[inherit_from_base]
pub enum Productions {
#[token("->")]
Produce,
#[token("=>")]
Generate,
#[token("|")]
Alternative,
#[token("&")]
Additional,
#[display_override("Import")]
#[regex("<(-|=|<|/)", |lex| ImportType::detect(lex.slice(), lex.span()))]
Import(ImportType),
#[display_override("Regex")]
#[regex(r#"/[^\*]([^/\\]|\\.)*/"#, |lex| rem_first_and_last_char(lex.slice()))]
LitRegex(String),
#[display_override("EBNF Operator")]
#[regex(r#"(\*|\+|\?)"#, |lex| EbnfType::detect(lex.slice(), lex.span()))]
Ebnf(EbnfType),
}
#[cfg(test)]
mod tests {
use wagon_ident::Ident;
use crate::{assert_lex, LexingError};
use std::assert_eq;
use logos::Logos;
use super::Productions::{self};
use super::ImportType::*;
use super::EbnfType;
#[test]
fn test_quoted_string_double() {
let s = r#""This is \" a 'with single quotes inside' string \\\" ""#;
let mut lex = Productions::lexer(s);
assert_eq!(lex.next(), Some(Ok(Productions::LitString("This is \\\" a 'with single quotes inside' string \\\\\\\" ".to_string()))));
let s2 = r#""This one should \\" fail" before this"#;
let mut lex = Productions::lexer(s2);
assert_eq!(lex.next(), Some(Ok(Productions::LitString("This one should \\\\".to_string()))));
assert_eq!(lex.next(), Some(Ok(Productions::Identifier(Ident::Unknown("fail".to_string())))));
assert_eq!(lex.next(), Some(Err(LexingError::UnknownError)));
}
#[test]
fn test_quoted_string_single() {
let s = r#"'This is \' a "with double quotes inside" string \\\' '"#;
let mut lex = Productions::lexer(s);
assert_eq!(lex.next(), Some(Ok(Productions::LitString("This is \\' a \"with double quotes inside\" string \\\\\\' ".to_string()))));
let s2 = r"'This one should \\' fail' before this";
let mut lex = Productions::lexer(s2);
assert_eq!(lex.next(), Some(Ok(Productions::LitString("This one should \\\\".to_string()))));
assert_eq!(lex.next(), Some(Ok(Productions::Identifier(Ident::Unknown("fail".to_string())))));
assert_eq!(lex.next(), Some(Err(LexingError::UnknownError)));
}
#[test]
fn test_identifier_matching() {
let s = "&synthesized *inherited $local unknown";
let expect = &[
Ok(Productions::Identifier(Ident::Synth("synthesized".to_string()))),
Ok(Productions::Identifier(Ident::Inherit("inherited".to_string()))),
Ok(Productions::Identifier(Ident::Local("local".to_string()))),
Ok(Productions::Identifier(Ident::Unknown("unknown".to_string())))
];
assert_lex(s, expect);
}
#[test]
fn test_regex() {
let s = r"/[a-z][A-Z][^\/]/";
let expect = &[Ok(Productions::LitRegex("[a-z][A-Z][^\\/]".to_string()))];
assert_lex(s, expect);
}
#[test]
fn test_imports() {
let s = "<- <= << </";
let expect = &[
Ok(Productions::Import(Basic)),
Ok(Productions::Import(Full)),
Ok(Productions::Import(Recursive)),
Ok(Productions::Import(Exclude))
];
assert_lex(s, expect);
}
#[test]
fn test_ebnf() {
let s = "* + ?";
let expect = &[
Ok(Productions::Ebnf(EbnfType::Many)),
Ok(Productions::Ebnf(EbnfType::Some)),
Ok(Productions::Ebnf(EbnfType::Maybe))
];
assert_lex(s, expect);
}
#[test]
fn test_simple() {
let s = "S -> 'a' S | ";
let expect = &[
Ok(Productions::Identifier(Ident::Unknown("S".to_string()))),
Ok(Productions::Produce),
Ok(Productions::LitString("a".to_string())),
Ok(Productions::Identifier(Ident::Unknown("S".to_string()))),
Ok(Productions::Alternative)
];
assert_lex(s, expect);
}
}