use super::{color::Color, lexer::GenericToken, percentage::Percentage};
use fraction::Fraction;
use regex::Regex;
use std::fmt::Debug;
use token::{element_separator, ignore_comments, tuple_close, tuple_open, value_or_identifier};
use try_into_value::TryIntoValue;
use winnow::{
combinator::{alt, separated, terminated},
error::ParserError,
stream::{Stream, StreamIsPartial},
PResult, Parser,
};
pub mod try_into_value;
#[derive(Debug, Clone)]
pub enum Value {
String(String),
Regex(Regex),
Number(Fraction),
Percentage(Percentage),
Boolean(bool),
Color(Color),
Identifier(String),
Tuple(Vec<Value>),
}
#[cfg(test)]
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Value::String(a), Value::String(b)) => a == b,
(Value::Regex(a), Value::Regex(b)) => a.as_str() == b.as_str(),
(Value::Number(a), Value::Number(b)) => a == b,
(Value::Boolean(a), Value::Boolean(b)) => a == b,
(Value::Color(a), Value::Color(b)) => a == b,
(Value::Identifier(a), Value::Identifier(b)) => a == b,
_ => false,
}
}
}
pub fn tuple_custom<I: Stream + StreamIsPartial, E: ParserError<I>, TO, ES, TC, IG>(
tuple_open: impl Parser<I, TO, E>,
element_separator: impl Parser<I, ES, E>,
tuple_close: impl Parser<I, TC, E>,
value: impl Parser<I, Value, E>,
ignore: impl Parser<I, IG, E> + Copy,
) -> impl Parser<I, Value, E> {
(
terminated(tuple_open, ignore),
terminated(separated(0.., value, element_separator), ignore),
tuple_close,
)
.map(|(_, t, _)| Value::Tuple(t))
}
pub fn tuple<Tok: Into<Option<GenericToken<S>>> + Clone + Debug, S: TryIntoValue>(
input: &mut &[Tok],
) -> PResult<Value> {
tuple_custom(
tuple_open,
element_separator,
tuple_close,
value_or_identifier_or_tuple,
ignore_comments,
)
.parse_next(input)
}
pub fn value_or_identifier_or_tuple<
Tok: Into<Option<GenericToken<S>>> + Clone + Debug,
S: TryIntoValue,
>(
input: &mut &[Tok],
) -> PResult<Value> {
alt((value_or_identifier, tuple)).parse_next(input)
}
pub mod token {
use super::*;
use crate::input::lexer::GenericToken;
use std::fmt::Debug;
use try_into_value::TryIntoValue;
use winnow::{
combinator::{alt, repeat},
token::one_of,
PResult,
};
pub fn identifier<Tok: Into<Option<GenericToken<S>>> + Clone + Debug, S: TryIntoValue>(
input: &mut &[Tok],
) -> PResult<String> {
one_of(|t: Tok| matches!(t.into(), Some(GenericToken::Identifier(_))))
.output_into()
.map(|t| match t {
Some(GenericToken::Identifier(i)) => i,
_ => unreachable!(),
})
.try_map(TryIntoValue::identifier)
.parse_next(input)
}
pub fn value<Tok: Into<Option<GenericToken<S>>> + Clone + Debug, S: TryIntoValue>(
input: &mut &[Tok],
) -> PResult<Value> {
one_of(|t: Tok| matches!(t.into(), Some(GenericToken::Value(_))))
.output_into()
.try_map(|t| match t {
Some(GenericToken::Value(v)) => v.try_into(),
_ => unreachable!(), })
.parse_next(input)
}
pub fn value_or_identifier<
Tok: Into<Option<GenericToken<S>>> + Clone + Debug,
S: TryIntoValue,
>(
input: &mut &[Tok],
) -> PResult<Value> {
alt((value, identifier.map(Value::Identifier))).parse_next(input)
}
pub fn comment<Tok: Into<Option<GenericToken<S>>> + Clone + Debug, S: TryIntoValue>(
input: &mut &[Tok],
) -> PResult<S> {
one_of(|t: Tok| matches!(t.into(), Some(GenericToken::Comment(_))))
.output_into()
.map(|t| match t {
Some(GenericToken::Comment(c)) => c,
_ => unreachable!(), })
.parse_next(input)
}
pub fn ignore_comments<Tok: Into<Option<GenericToken<S>>> + Clone + Debug, S: TryIntoValue>(
input: &mut &[Tok],
) -> PResult<()> {
repeat::<_, _, (), _, _>(0.., comment)
.void()
.parse_next(input)
}
pub fn tuple_open<Tok: Into<Option<GenericToken<S>>> + Clone + Debug, S: TryIntoValue>(
input: &mut &[Tok],
) -> PResult<()> {
one_of(|t: Tok| matches!(t.into(), Some(GenericToken::TupleOpen)))
.void()
.parse_next(input)
}
pub fn tuple_close<Tok: Into<Option<GenericToken<S>>> + Clone + Debug, S: TryIntoValue>(
input: &mut &[Tok],
) -> PResult<()> {
one_of(|t: Tok| matches!(t.into(), Some(GenericToken::TupleClose)))
.void()
.parse_next(input)
}
pub fn element_separator<
Tok: Into<Option<GenericToken<S>>> + Clone + Debug,
S: TryIntoValue,
>(
input: &mut &[Tok],
) -> PResult<()> {
one_of(|t: Tok| matches!(t.into(), Some(GenericToken::ElementSeparator)))
.void()
.parse_next(input)
}
}