use super::tokenizer::{Token, TokenSpan, Tokenizer};
use crate::query::{
ParseMode, Query,
parse::tokenizer::TokenKind,
queryable::{QueryKeyValue, Queryable},
};
pub mod relaxed;
pub mod strict;
#[derive(Debug, thiserror::Error)]
pub enum Error<E: Queryable>
where
<E::KeyValue as QueryKeyValue>::Err: std::fmt::Debug + std::fmt::Display,
{
#[error(transparent)]
Strict(#[from] strict::Error<E>),
#[error(transparent)]
Relaxed(#[from] relaxed::Error<E>),
}
#[allow(clippy::trivially_copy_pass_by_ref)]
fn format_unexpected_token(
expected: &TokenKind,
found: &Token,
f: &mut std::fmt::Formatter<'_>,
) -> Result<(), std::fmt::Error> {
write!(
f,
"Expected token '{expected}' but got token '{found}' instead."
)
}
fn format_unknown_key<E: Queryable>(
err: &<E::KeyValue as QueryKeyValue>::Err,
key: &str,
_at: &TokenSpan,
f: &mut std::fmt::Formatter<'_>,
) -> Result<(), std::fmt::Error>
where
<E::KeyValue as QueryKeyValue>::Err: std::fmt::Debug + std::fmt::Display,
{
write!(f, "The input stream countained a unknown key {key}: {err}")
}
#[allow(missing_docs)]
pub mod parsing {
use crate::query::{
parse::{
parser::format_unexpected_token,
tokenizer::{Token, TokenKind, TokenSpan},
},
queryable::{QueryKeyValue, Queryable},
};
#[derive(Debug, thiserror::Error)]
pub enum Error<E: Queryable>
where
<E::KeyValue as QueryKeyValue>::Err: std::fmt::Debug + std::fmt::Display,
{
#[error(fmt = format_unexpected_token)]
UnexpectedToken { expected: TokenKind, found: Token },
#[error("The input stream countained a unknown key {key}: {err}")]
UnknownKey {
err: <E::KeyValue as QueryKeyValue>::Err,
key: String,
at: TokenSpan,
},
}
}
pub(crate) struct Parser<'a, E: Queryable> {
tokenizer: Tokenizer<'a>,
user_state: &'a <E::KeyValue as QueryKeyValue>::UserState,
}
impl<'a, E: Queryable> Parser<'a, E>
where
<E::KeyValue as QueryKeyValue>::Err: std::fmt::Debug + std::fmt::Display,
{
pub(crate) fn new(
user_state: &'a <E::KeyValue as QueryKeyValue>::UserState,
tokenizer: Tokenizer<'a>,
) -> Self {
Self {
tokenizer,
user_state,
}
}
pub(crate) fn parse(&'a mut self, mode: ParseMode) -> Result<Query<E>, Error<E>> {
let matcher = match mode {
ParseMode::Strict => strict::Parser::parse(self)?,
ParseMode::Relaxed => relaxed::Parser::parse(self)?,
};
Ok(Query {
root: Some(matcher),
})
}
fn parse_key_from(
&self,
key_tokens: &[Token],
value: String,
value_tokens_end: usize,
) -> Result<E::KeyValue, parsing::Error<E>> {
let key = key_tokens.iter().fold(String::new(), |mut acc, t| {
if let TokenKind::Char(ch) = t.kind {
acc.push(ch);
acc
} else {
unreachable!("We filtered by chars in the `take_while`");
}
});
E::KeyValue::from_key_value(self.user_state, &key, value).map_err(|err| {
parsing::Error::UnknownKey {
err,
key,
at: TokenSpan {
start: key_tokens.first().expect("Exists").span.start,
stop: value_tokens_end,
},
}
})
}
fn parse_value_from(tokens: &[Token]) -> String {
tokens.iter().fold(String::new(), |mut acc, t| {
if let TokenKind::Char(ch) = t.kind {
acc.push(ch);
acc
} else {
unreachable!("We filtered by Chars()s in the `take_while`");
}
})
}
fn expect(&mut self, expected: TokenKind) -> Result<Token, parsing::Error<E>> {
let next = self.tokenizer.next_token();
if next.kind == expected {
Ok(next)
} else {
Err(parsing::Error::UnexpectedToken {
expected,
found: next,
})
}
}
fn take_while<F>(&mut self, pred: F) -> Result<Vec<Token>, parsing::Error<E>>
where
F: Fn(TokenKind) -> bool,
{
let mut output = vec![];
loop {
let token = self.tokenizer.peek();
if !pred(token.kind) {
return Ok(output);
}
output.push(self.tokenizer.next_token());
}
}
}
#[cfg(test)]
pub(crate) mod test {
macro_rules! query {
($name:ident {
lhs: $lhs:tt,
rhs: $rhs:tt $(,)?
}) => {
Query {
root: Some($name {
lhs: Box::new(query!(@run $lhs)),
rhs: Box::new(query!(@run $rhs)),
})
}
};
(@run {$name:ident {
lhs: $lhs:tt,
rhs: $rhs:tt $(,)?
} $(,)?}) => {
$name {
lhs: Box::new(query!(@run $lhs)),
rhs: Box::new(query!(@run $rhs)),
}
};
(@run { Match(Key1 , $value:tt) }) => {
Match {
key_value: Key1($value),
}
};
(@run { Match(Key2 , $value:expr) }) => {
Match {
key_value: Key2($value.to_owned()),
}
};
(@run { Match(Key3 , $number:expr) }) => {
Match {
key_value: Key3 {
_value: $number,
original: stringify!($number).to_owned(),
},
}
};
}
pub(crate) use query;
use crate::query::{
Matcher, Query,
queryable::{QueryKeyValue, Queryable},
};
impl PartialEq for Query<QueryTestObj> {
fn eq(&self, other: &Self) -> bool {
if let Some(me) = &self.root {
if let Some(other) = &other.root {
me == other
} else {
false
}
} else {
self.root.is_none() && other.root.is_none()
}
}
}
impl PartialEq for Matcher<QueryTestObj> {
fn eq(&self, other: &Self) -> bool {
match self {
Matcher::Or {
lhs: me_lhs,
rhs: me_rhs,
} => match other {
Matcher::Or { lhs, rhs } => me_lhs == lhs && me_rhs == rhs,
Matcher::And { .. } | Matcher::Match { .. } => false,
},
Matcher::And {
lhs: me_lhs,
rhs: me_rhs,
} => match other {
Matcher::And { lhs, rhs } => me_lhs == lhs && me_rhs == rhs,
Matcher::Or { .. } | Matcher::Match { .. } => false,
},
Matcher::Match {
key_value: me_key_value,
} => match other {
Matcher::Or { .. } | Matcher::And { .. } => false,
Matcher::Match { key_value } => key_value == me_key_value,
},
}
}
}
#[derive(Debug)]
pub(crate) struct QueryTestObj;
impl Queryable for QueryTestObj {
type KeyValue = QueryTestKeyValue;
fn matches(&self, _key: &Self::KeyValue) -> bool {
true
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub(crate) enum QueryTestKeyValue {
Key1(QueryTestKey1),
Key2(String),
Key3 { _value: u32, original: String },
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub(crate) enum QueryTestKey1 {
Value1,
Value2,
}
impl QueryKeyValue for QueryTestKeyValue {
type Err = QueryTestError;
type UserState = ();
fn from_key_value(
_user_state: &Self::UserState,
key: &str,
value: String,
) -> Result<Self, Self::Err>
where
Self: Sized,
{
match key {
"key1" => Ok(Self::Key1(match value.as_str() {
"value1" => QueryTestKey1::Value1,
"value2" => QueryTestKey1::Value2,
other => todo!("{other}"),
})),
"key2" => Ok(Self::Key2(value)),
"key3" => Ok(Self::Key3 {
_value: value.parse().unwrap(),
original: value,
}),
other => todo!("{other}"),
}
}
fn from_value(_user_state: &Self::UserState, value: String) -> Result<Self, Self::Err>
where
Self: Sized,
{
Ok(Self::Key2(value))
}
fn to_key_and_value(&self) -> (&str, &str)
where
Self: Sized,
{
match self {
QueryTestKeyValue::Key1(query_test_key1) => {
let query_test_key1_str = match query_test_key1 {
QueryTestKey1::Value1 => "value1",
QueryTestKey1::Value2 => "value2",
};
("key1", query_test_key1_str)
}
QueryTestKeyValue::Key2(val) => ("key2", val.as_str()),
QueryTestKeyValue::Key3 { original, .. } => ("key3", original.as_str()),
}
}
}
#[derive(Debug, thiserror::Error)]
pub(crate) enum QueryTestError {}
}