use crate::{AtomSet, Comparison, Diagnostic, Parse, Parser, Peek, Result as ParserResult, T};
pub trait RangedFeature<'a>: Sized {
type Value: Parse<'a>;
fn new_max(
_open: T!['('],
name: T![Ident],
_colon: T![:],
_value: Self::Value,
_close: T![')'],
) -> ParserResult<Self> {
Err(Diagnostic::new(name.into(), Diagnostic::unexpected_ident))?
}
fn new_min(
_open: T!['('],
name: T![Ident],
_colon: T![:],
_value: Self::Value,
_close: T![')'],
) -> ParserResult<Self> {
Err(Diagnostic::new(name.into(), Diagnostic::unexpected_ident))?
}
fn new_exact(
open: T!['('],
name: T![Ident],
colon: T![:],
value: Self::Value,
close: T![')'],
) -> ParserResult<Self>;
fn new_left(
open: T!['('],
name: T![Ident],
comparison: Comparison,
value: Self::Value,
close: T![')'],
) -> ParserResult<Self>;
fn new_right(
open: T!['('],
value: Self::Value,
comparison: Comparison,
name: T![Ident],
close: T![')'],
) -> ParserResult<Self>;
fn new_ranged(
open: T!['('],
left: Self::Value,
left_comparison: Comparison,
name: T![Ident],
right_comparison: Comparison,
value: Self::Value,
close: T![')'],
) -> ParserResult<Self>;
fn parse_ranged_feature<I, A: AtomSet + PartialEq>(
p: &mut Parser<'a, I>,
name: &A,
min: Option<&A>,
max: Option<&A>,
) -> ParserResult<Self>
where
I: Iterator<Item = crate::Cursor> + Clone,
{
let open = p.parse::<T!['(']>()?;
let c = p.peek_n(1);
if <T![Ident]>::peek(p, c) {
let atom = p.to_atom::<A>(c);
let ident = p.parse::<T![Ident]>()?;
if <T![:]>::peek(p, p.peek_n(1)) {
let colon = p.parse::<T![:]>()?;
let value = p.parse::<Self::Value>()?;
let close = p.parse::<T![')']>()?;
if &atom == name {
return Self::new_exact(open, ident, colon, value, close);
} else if min.is_some_and(|min| &atom == min) {
return Self::new_min(open, ident, colon, value, close);
} else if max.is_some_and(|max| &atom == max) {
return Self::new_max(open, ident, colon, value, close);
} else {
Err(Diagnostic::new(c, Diagnostic::unexpected_ident))?
}
}
if &atom != name {
Err(Diagnostic::new(c, Diagnostic::unexpected_ident))?
}
let comparison = p.parse::<Comparison>()?;
let value = p.parse::<Self::Value>()?;
let close = p.parse::<T![')']>()?;
return Self::new_left(open, ident, comparison, value, close);
}
let left = p.parse::<Self::Value>()?;
let left_comparison = p.parse::<Comparison>()?;
let c = p.peek_n(1);
let ident = p.parse::<T![Ident]>()?;
if &p.to_atom::<A>(ident.into()) != name {
Err(Diagnostic::new(c, Diagnostic::unexpected))?
}
if !<T![Delim]>::peek(p, p.peek_n(1)) {
let close = p.parse::<T![')']>()?;
return Self::new_right(open, left, left_comparison, ident, close);
}
let right_comparison = p.parse::<Comparison>()?;
let right = p.parse::<Self::Value>()?;
let close = p.parse::<T![')']>()?;
Self::new_ranged(open, left, left_comparison, ident, right_comparison, right, close)
}
}
#[macro_export]
macro_rules! ranged_feature {
(@parse_call $p:ident, $feature_name:path) => {
Self::parse_ranged_feature($p, &$feature_name, None, None)
};
(@parse_call $p:ident, $feature_name:path, $min_name:path, $max_name:path) => {
Self::parse_ranged_feature($p, &$feature_name, Some(&$min_name), Some(&$max_name))
};
($(#[$meta:meta])* $vis:vis enum $feature: ident{$feature_name: path $(| $min_name: path | $max_name: path)?, $value: ty}) => {
$(#[$meta])*
$vis enum $feature {
Left($crate::T!['('], T![Ident], $crate::Comparison, $value, $crate::T![')']),
Right($crate::T!['('], $value, $crate::Comparison, T![Ident], $crate::T![')']),
Range($crate::T!['('], $value, $crate::Comparison, T![Ident], $crate::Comparison, $value, $crate::T![')']),
$(
#[doc = stringify!($min_name)]
Min($crate::T!['('], T![Ident], $crate::T![:], $value, $crate::T![')']),
#[doc = stringify!($max_name)]
Max($crate::T!['('], T![Ident], $crate::T![:], $value, $crate::T![')']),
)?
Exact($crate::T!['('], T![Ident], $crate::T![:], $value, $crate::T![')']),
}
impl<'a> $crate::Parse<'a> for $feature {
fn parse<I>(p: &mut $crate::Parser<'a, I>) -> $crate::Result<Self>
where
I: Iterator<Item = $crate::Cursor> + Clone,
{
use $crate::RangedFeature;
$crate::ranged_feature! {@parse_call p, $feature_name $(, $min_name, $max_name)?}
}
}
impl<'a> $crate::RangedFeature<'a> for $feature {
type Value = $value;
$(
#[doc = stringify!($max_name)]
fn new_max(
open: $crate::T!['('],
ident: T![Ident],
colon: $crate::T![:],
value: Self::Value,
close: $crate::T![')'],
) -> $crate::Result<Self> {
Ok(Self::Max(open, ident, colon, value, close))
}
#[doc = stringify!($min_name)]
fn new_min(
open: $crate::T!['('],
ident: T![Ident],
colon: $crate::T![:],
value: Self::Value,
close: $crate::T![')'],
) -> $crate::Result<Self> {
Ok(Self::Min(open, ident, colon, value, close))
}
)?
fn new_exact(
open: $crate::T!['('],
ident: T![Ident],
colon: $crate::T![:],
value: Self::Value,
close: $crate::T![')'],
) -> $crate::Result<Self> {
Ok(Self::Exact(open, ident, colon, value, close))
}
fn new_left(
open: $crate::T!['('],
ident: T![Ident],
comparison: $crate::Comparison,
value: Self::Value,
close: $crate::T![')'],
) -> $crate::Result<Self> {
Ok(Self::Left(open, ident, comparison, value, close))
}
fn new_right(
open: $crate::T!['('],
value: Self::Value,
comparison: $crate::Comparison,
ident: T![Ident],
close: $crate::T![')'],
) -> $crate::Result<Self> {
Ok(Self::Right(open, value, comparison, ident, close))
}
fn new_ranged(
open: $crate::T!['('],
left: Self::Value,
left_comparison: $crate::Comparison,
ident: T![Ident],
right_comparison: $crate::Comparison,
value: Self::Value,
close: $crate::T![')'],
) -> $crate::Result<Self> {
Ok(Self::Range(open, left, left_comparison, ident, right_comparison, value, close))
}
}
};
}