use std::str;
#[allow(unused_imports)]
use nom::{IResult, alpha, alphanumeric, eof, space, multispace};
use ::template::{
FullExpression, FilterItem,
DisjOp, ConjOp, CmpOp, SumOp, MulOp,
DisjExpr, ConjExpr, CmpExpr, Expr, Term, Factor,
DisjItem, ConjItem, CmpItem, ExprItem, TermItem,
};
use super::literals::literal;
pub fn full_expression(input: &[u8]) -> IResult<&[u8], FullExpression> {
let (i, (expr, filters)) = try_parse!(input, tuple!( expression, filter_agg ) );
IResult::Done(i, FullExpression::new(expr, filters))
}
named!(pub filter_agg<&[u8], Vec<FilterItem> >,
many0!(chain!(
many0!(multispace) ~
f: filter,
|| f
))
);
fn filter(input: &[u8]) -> IResult<&[u8], FilterItem> {
let (i, (_, _, id)) = try_parse!(input,
tuple!(
tag!("|"),
opt!(multispace),
identifier
)
);
IResult::Done(i, FilterItem::Simple(id))
}
#[cfg_attr(feature = "clippy", allow(cyclomatic_complexity))]
fn identifier(input: &[u8]) -> IResult<&[u8], String> {
let (i, id) = try_parse!(input,
chain!(
start: map_res!(alpha, str::from_utf8)~
rest: many0!(map_res!(alt!(tag!("_") | alphanumeric), str::from_utf8)),
|| {
rest.into_iter().fold(start.to_string(), |mut acc, slice| {
acc.push_str(slice);
acc
})
}
)
);
IResult::Done(i, id)
}
pub fn expression(input: &[u8]) -> IResult<&[u8], DisjExpr> {
let (i, lst) = try_parse!(input, chain!(
a: tuple!(value!(DisjOp::Or), conj) ~
mut b: many0!(tuple!(op_disj_bin, conj)) ,
|| { b.insert(0, a); b }
));
IResult::Done(i, DisjExpr { list: lst.into_iter().map(|(op, t)| DisjItem(op, t) ).collect() })
}
pub fn conj(input: &[u8]) -> IResult<&[u8], ConjExpr> {
let (i, lst) = try_parse!(input, chain!(
a: tuple!(value!(ConjOp::And), cmp) ~
mut b: many0!(tuple!(op_conj_bin, cmp)) ,
|| { b.insert(0, a); b }
));
IResult::Done(i, ConjExpr { list: lst.into_iter().map(|(op, t)| ConjItem(op, t) ).collect() })
}
pub fn cmp(input: &[u8]) -> IResult<&[u8], CmpExpr> {
let (i, lst) = try_parse!(input, chain!(
a: tuple!(value!(CmpOp::Eq), sum) ~
mut b: many0!(tuple!(op_cmp_bin, sum)) ,
|| { b.insert(0, a); b }
));
IResult::Done(i, CmpExpr { list: lst.into_iter().map(|(op, t)| CmpItem(op, t) ).collect() })
}
pub fn sum(input: &[u8]) -> IResult<&[u8], Expr> {
let (i, sum) = try_parse!(input, chain!(
a: tuple!(value!(SumOp::Add), mul) ~
mut b: many0!(tuple!(op_sum_bin, mul)) ,
|| { b.insert(0, a); b }
));
IResult::Done(i, Expr { sum: sum.into_iter().map(|(op, t)| ExprItem(op, t) ).collect() })
}
fn mul(input: &[u8]) -> IResult<&[u8], Term> {
let (i, mul) = try_parse!(input, chain!(
a: tuple!(value!(MulOp::Mul), factor) ~
mut b: many0!(tuple!(op_mul_bin, factor)) ,
|| { b.insert(0, a); b }
));
IResult::Done(i, Term{mul: mul.into_iter().map(|(op, f)| TermItem(op, f) ).collect()} )
}
fn factor(input: &[u8]) -> IResult<&[u8], Factor> {
let (i, f) = try_parse!(input, alt!(literal | variable | subexpression) );
IResult::Done(i, f.into())
}
fn subexpression(input: &[u8]) -> IResult<&[u8], Factor> {
let (i, (_, _, e, _, _)) = try_parse!(input, tuple!(
char!('('), many0!(multispace), expression, many0!(multispace), char!(')')
));
IResult::Done(i, Factor::Subexpression(e))
}
fn variable(input: &[u8]) -> IResult<&[u8], Factor> {
let (i, id) = try_parse!(input, identifier);
IResult::Done(i, Factor::Variable(id))
}
fn op_disj_bin(input: &[u8]) -> IResult<&[u8], DisjOp> {
let (i, (_, o, _)) = try_parse!(input, tuple!(many0!(multispace), alt!(tag!("or")), many0!(multispace)) );
IResult::Done(i, match o {
b"or" => DisjOp::Or,
_ => unreachable!()
})
}
fn op_conj_bin(input: &[u8]) -> IResult<&[u8], ConjOp> {
let (i, (_, o, _)) = try_parse!(input, tuple!(many0!(multispace), alt!(tag!("and")), many0!(multispace)) );
IResult::Done(i, match o {
b"and" => ConjOp::And,
_ => unreachable!()
})
}
fn op_cmp_bin(input: &[u8]) -> IResult<&[u8], CmpOp> {
let (i, (_, o, _)) = try_parse!(input, tuple!(
many0!(multispace),
alt!(tag!("<") | tag!("<=") | tag!("==") | tag!("!=") | tag!("in") | tag!("not in") | tag!(">=") | tag!(">")),
many0!(multispace)
));
IResult::Done(i, match o {
b"<" => CmpOp::Lt,
b"<=" => CmpOp::Lte,
b"==" => CmpOp::Eq,
b"!=" => CmpOp::Neq,
b"in" => CmpOp::In,
b"not in" => CmpOp::Nin,
b">=" => CmpOp::Gte,
b">" => CmpOp::Gt,
_ => unreachable!()
})
}
fn op_sum_bin(input: &[u8]) -> IResult<&[u8], SumOp> {
let (i, (_, o, _)) = try_parse!(input, tuple!(many0!(multispace), alt!(tag!("+") | tag!("-")), many0!(multispace)) );
IResult::Done(i, match o {
b"+" => SumOp::Add,
b"-" => SumOp::Sub,
_ => unreachable!()
})
}
fn op_mul_bin(input: &[u8]) -> IResult<&[u8], MulOp> {
let (i, (_, o, _)) = try_parse!(input, tuple!(many0!(multispace), alt!(tag!("*") | tag!("/")), many0!(multispace)) );
IResult::Done(i, match o {
b"*" => MulOp::Mul,
b"/" => MulOp::Div,
_ => unreachable!()
})
}
#[cfg(test)]
mod tests {
#![cfg_attr(feature = "clippy", allow(used_underscore_binding))]
use nom::IResult::Done;
#[test]
fn identifier() {
assert_eq!(Done(&b""[..], "var_name".to_owned()), super::identifier(b"var_name"));
assert_eq!(Done(&b""[..], "var_1".to_owned()), super::identifier(b"var_1"));
assert!(super::identifier(b"_wrong").is_err());
assert!(super::identifier(b"1wrong").is_err());
}
#[test]
fn format() {
use ::template::FilterItem::Simple;
assert_eq!(Done(&b""[..], Simple("e".into())), super::filter(b"|e"));
assert_eq!(Done(&b""[..], Simple("e".into())), super::filter(b"| e"));
assert_eq!(Done(&b""[..], Simple("e".into())), super::filter(b"| e"));
assert!(super::filter(b"| 1wrong").is_err());
assert!(super::filter(b"| _wrong").is_err());
}
}