use std::borrow::Cow;
use crate::{
arg::{Argument, ComparisonOp},
directive::{
ConditionalDirective, Directive, EmptyDirective, RepeatDirective, ReplaceDirective,
},
lexer::Token,
};
pub trait Parser {
fn parse(tokens: &[Token]) -> Option<Box<dyn Directive + Send + Sync>>;
}
pub struct DefaultParser;
fn token_to_argument(token: &Token) -> Argument {
match token {
Token::Ident(s) => Argument::variable(Cow::Owned(s.to_string())),
Token::Literal(s) => Argument::literal(Cow::Owned(s.to_string())),
Token::Int(s) => Argument::literal(Cow::Owned(s.to_string())),
Token::Float(s) => Argument::literal(Cow::Owned(s.to_string())),
_ => Argument::literal(Cow::Borrowed("")),
}
}
impl Parser for DefaultParser {
fn parse(tokens: &[Token]) -> Option<Box<dyn Directive + Send + Sync>> {
match tokens {
[Token::Ident(ident)] => Some(Box::new(ReplaceDirective(Argument::variable(
Cow::Owned(ident.to_string()),
)))),
[
p @ (Token::Ident(_) | Token::Literal(_)),
Token::Colon,
c @ (Token::Ident(_) | Token::Int(_)),
] => {
let pattern = match p {
Token::Ident(s) => Argument::variable(Cow::Owned(s.to_string())),
Token::Literal(cow) => Argument::literal(Cow::Owned(cow.to_string())),
_ => unreachable!(),
};
let count = match c {
Token::Ident(s) => Argument::variable(Cow::Owned(s.to_string())),
Token::Int(s) => Argument::literal(Cow::Owned(s.to_string())),
_ => unreachable!(),
};
Some(Box::new(RepeatDirective(pattern, count)))
}
[Token::Literal(lit)] => Some(Box::new(ReplaceDirective(Argument::literal(
Cow::Owned(lit.to_string()),
)))),
[
cond @ (Token::Ident(_) | Token::Literal(_) | Token::Int(_)),
Token::Question,
if_true @ (Token::Ident(_) | Token::Literal(_) | Token::Int(_)),
Token::Colon,
if_false @ (Token::Ident(_) | Token::Literal(_) | Token::Int(_)),
] => {
let cond_arg = token_to_argument(cond);
let true_arg = token_to_argument(if_true);
let false_arg = token_to_argument(if_false);
Some(Box::new(ConditionalDirective {
cond: cond_arg,
if_true: true_arg,
if_false: false_arg,
}))
}
[
left @ (Token::Ident(_) | Token::Literal(_) | Token::Int(_) | Token::Float(_)),
op @ (Token::Equals
| Token::NotEquals
| Token::GreaterThan
| Token::LessThan
| Token::GreaterThanEquals
| Token::LessThanEquals),
right @ (Token::Ident(_) | Token::Literal(_) | Token::Int(_) | Token::Float(_)),
Token::Question,
if_true @ (Token::Ident(_) | Token::Literal(_) | Token::Int(_)),
Token::Colon,
if_false @ (Token::Ident(_) | Token::Literal(_) | Token::Int(_)),
] => {
let left_arg = token_to_argument(left);
let right_arg = token_to_argument(right);
let comp_op = match op {
Token::Equals => ComparisonOp::Equals,
Token::NotEquals => ComparisonOp::NotEquals,
Token::GreaterThan => ComparisonOp::GreaterThan,
Token::LessThan => ComparisonOp::LessThan,
Token::GreaterThanEquals => ComparisonOp::GreaterThanEquals,
Token::LessThanEquals => ComparisonOp::LessThanEquals,
_ => unreachable!(),
};
let cond_arg = Argument::comparison(left_arg, comp_op, right_arg);
let true_arg = token_to_argument(if_true);
let false_arg = token_to_argument(if_false);
Some(Box::new(ConditionalDirective {
cond: cond_arg,
if_true: true_arg,
if_false: false_arg,
}))
}
[
Token::Not,
cond @ (Token::Ident(_) | Token::Literal(_) | Token::Int(_)),
Token::Question,
if_true @ (Token::Ident(_) | Token::Literal(_) | Token::Int(_)),
Token::Colon,
if_false @ (Token::Ident(_) | Token::Literal(_) | Token::Int(_)),
] => {
let inner_arg = token_to_argument(cond);
let cond_arg = Argument::not(inner_arg);
let true_arg = token_to_argument(if_true);
let false_arg = token_to_argument(if_false);
Some(Box::new(ConditionalDirective {
cond: cond_arg,
if_true: true_arg,
if_false: false_arg,
}))
}
_ => Some(Box::new(EmptyDirective)),
}
}
}