mod lexing;
mod stream;
use lexing::Lexeme;
pub use stream::TokenStream;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RawLexeme<'a>(&'a str);
impl<'a> RawLexeme<'a> {
pub fn new(text: &'a str) -> Self {
RawLexeme(text)
}
pub fn parse_string(self) -> &'a str {
let text = self.0;
let first_char = text.chars().next();
if let Some(quote @ '\'' | quote @ '"') = first_char {
if text.ends_with(quote) {
&text[1..text.len() - 1]
} else {
&text[1..]
}
} else {
text
}
}
}
#[cfg(feature = "std")]
pub mod std_impl {
extern crate std;
use super::RawLexeme;
use core::fmt::Write;
use std::fmt;
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl<'a> fmt::Display for RawLexeme<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let first_char = self.0.chars().next();
if let Some(quote @ '\'' | quote @ '"') = first_char {
f.write_str(self.0)?;
if !self.0.ends_with(quote) {
f.write_char(quote)?;
}
Ok(())
} else {
f.write_fmt(format_args!("\"{}\"", self.0))
}
}
}
}
#[cfg(feature = "std")]
#[allow(unused_imports)]
pub use std_impl::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Token<'a> {
Text(RawLexeme<'a>),
Attribute(RawLexeme<'a>),
}
impl<'a> Token<'a> {
pub fn into_raw_lexeme(self) -> RawLexeme<'a> {
match self {
Token::Text(inner) => inner,
Token::Attribute(inner) => inner,
}
}
pub fn is_attribute(&self) -> bool {
matches!(self, Token::Attribute(_))
}
pub fn is_text(&self) -> bool {
matches!(self, Token::Text(_))
}
fn from_lexeme(lexeme: Lexeme<'a>) -> Result<Self, UnbalancedParenthesis> {
match lexeme {
Lexeme::OpeningParen | Lexeme::ClosingParen => Err(UnbalancedParenthesis),
Lexeme::Text(text) => Ok(Token::Text(RawLexeme::new(text))),
Lexeme::Attribute(attr) => Ok(Token::Attribute(RawLexeme::new(attr))),
}
}
}
#[derive(Debug)]
pub struct UnbalancedParenthesis;
#[cfg(test)]
mod tests {
use super::RawLexeme;
extern crate std;
use std::string::ToString;
mod format_raw_lexeme {
use super::*;
#[test]
fn format_simple() {
assert_eq!(RawLexeme::new("simple").to_string(), "\"simple\"");
}
#[test]
fn format_quoted() {
assert_eq!(RawLexeme::new("'quoted'").to_string(), "'quoted'");
assert_eq!(RawLexeme::new("\"quoted\"").to_string(), "\"quoted\"");
}
#[test]
fn format_quoted_partial() {
assert_eq!(RawLexeme::new("'quoted").to_string(), "'quoted'");
assert_eq!(RawLexeme::new("\"quoted").to_string(), "\"quoted\"");
}
}
mod parse_raw_lexeme {
use super::*;
macro_rules! test_parse {
($name:ident, $text:literal => $result:expr) => {
#[test]
fn $name() {
let result = RawLexeme::new($text).parse_string();
assert_eq!(result, $result);
}
};
}
test_parse!(empty, "" => "");
test_parse!(non_empty, "abc" => "abc");
test_parse!(quoted_empty_single, "''" => "");
test_parse!(quoted_empty_double, "\"\"" => "");
test_parse!(quoted_non_empty_single, "'abc \\\' def \\\" fgh'" => "abc \\\' def \\\" fgh");
test_parse!(quoted_non_empty_double, "\"abc \\\' def \\\" fgh\"" => "abc \\\' def \\\" fgh");
test_parse!(quoted_not_terminated_single, "'abc" => "abc");
test_parse!(quoted_not_terminated_double, "\"abc" => "abc");
}
}