eww/src/value/attr_value/parser.rs
2021-05-13 21:07:20 +02:00

232 lines
8.8 KiB
Rust

use super::*;
use nom::{
branch::*,
bytes::complete::{tag, take_while},
character::complete::{multispace0 as multispace, *},
combinator::{map, map_res, *},
error::{context, ParseError, VerboseError},
multi::{many0, separated_list0},
sequence::{delimited, preceded, *},
IResult, Parser,
};
use super::super::*;
fn ws<'a, P, O, E: ParseError<&'a str>>(p: P) -> impl FnMut(&'a str) -> IResult<&'a str, O, E>
where
P: Parser<&'a str, O, E>,
{
delimited(multispace, p, multispace)
}
fn parse_num(i: &str) -> IResult<&str, f64, VerboseError<&str>> {
let (i, neg) = opt(tag("-"))(i)?;
let (i, num): (_, f64) = map_res(take_while(|c: char| c.is_numeric() || c == '.'), |n: &str| n.parse::<f64>())(i)?;
Ok((i, if neg.is_some() { -num } else { num }))
}
fn parse_bool(i: &str) -> IResult<&str, &str, VerboseError<&str>> {
alt((tag("true"), tag("false")))(i)
}
fn parse_literal(i: &str) -> IResult<&str, &str, VerboseError<&str>> {
alt((parse_bool, parse_stringlit, recognize(parse_num)))(i)
}
fn parse_stringlit(i: &str) -> IResult<&str, &str, VerboseError<&str>> {
alt((delimited(tag("'"), take_while(|c| c != '\''), tag("'")), delimited(tag("\""), take_while(|c| c != '"'), tag("\""))))(i)
}
fn parse_identifier(i: &str) -> IResult<&str, &str, VerboseError<&str>> {
verify(recognize(pair(alt((alpha1, tag("_"), tag("-"))), many0(alt((alphanumeric1, tag("_"), tag("-")))))), |x| {
!["if", "then", "else"].contains(x)
})(i)
}
fn parse_unary_op(i: &str) -> IResult<&str, UnaryOp, VerboseError<&str>> {
value(UnaryOp::Not, tag("!"))(i)
}
fn parse_function_call(i: &str) -> IResult<&str, AttrValExpr, VerboseError<&str>> {
let (i, name) = take_while(|c: char| c.is_ascii_alphanumeric() || c == '_')(i)?;
let (i, args) = delimited(tag("("), separated_list0(tag(","), ws(parse_factor)), tag(")"))(i)?;
Ok((i, AttrValExpr::FunctionCall(name.to_string(), args)))
}
/////////////////
// actual tree //
/////////////////
fn parse_factor(i: &str) -> IResult<&str, AttrValExpr, VerboseError<&str>> {
let (i, unary_op) = opt(parse_unary_op)(i)?;
let (i, factor) = alt((
context("expression", ws(delimited(tag("("), parse_expr, tag(")")))),
context("if-expression", ws(parse_ifelse)),
context("function-call", ws(parse_function_call)),
context("literal", map(ws(parse_literal), |x| AttrValExpr::Literal(AttrVal::parse_string(x)))),
context("identifier", map(ws(parse_identifier), |x| AttrValExpr::VarRef(VarName(x.to_string())))),
))(i)?;
Ok((
i,
match unary_op {
Some(op) => AttrValExpr::UnaryOp(op, box factor),
None => factor,
},
))
}
fn parse_object_index(i: &str) -> IResult<&str, AttrValExpr, VerboseError<&str>> {
let (i, initial) = parse_factor(i)?;
let (i, remainder) = many0(alt((
delimited(tag("["), ws(parse_expr), tag("]")),
map(preceded(tag("."), parse_identifier), |x| AttrValExpr::Literal(AttrVal::from_primitive(x))),
)))(i)?;
let indexes = remainder.into_iter().fold(initial, |acc, index| AttrValExpr::JsonAccess(box acc, box index));
Ok((i, indexes))
}
fn parse_term3(i: &str) -> IResult<&str, AttrValExpr, VerboseError<&str>> {
let (i, initial) = parse_object_index(i)?;
let (i, remainder) = many0(alt((
map(preceded(tag("*"), parse_object_index), |x| (BinOp::Times, x)),
map(preceded(tag("/"), parse_object_index), |x| (BinOp::Div, x)),
map(preceded(tag("%"), parse_object_index), |x| (BinOp::Mod, x)),
)))(i)?;
let exprs = remainder.into_iter().fold(initial, |acc, (op, expr)| AttrValExpr::BinOp(box acc, op, box expr));
Ok((i, exprs))
}
fn parse_term2(i: &str) -> IResult<&str, AttrValExpr, VerboseError<&str>> {
let (i, initial) = parse_term3(i)?;
let (i, remainder) = many0(alt((
map(preceded(tag("+"), parse_term3), |x| (BinOp::Plus, x)),
map(preceded(tag("-"), parse_term3), |x| (BinOp::Minus, x)),
)))(i)?;
let exprs = remainder.into_iter().fold(initial, |acc, (op, expr)| AttrValExpr::BinOp(box acc, op, box expr));
Ok((i, exprs))
}
fn parse_term1(i: &str) -> IResult<&str, AttrValExpr, VerboseError<&str>> {
let (i, initial) = parse_term2(i)?;
let (i, remainder) = many0(alt((
map(preceded(tag("=="), parse_term2), |x| (BinOp::Equals, x)),
map(preceded(tag("!="), parse_term2), |x| (BinOp::NotEquals, x)),
map(preceded(tag(">"), parse_term2), |x| (BinOp::GT, x)),
map(preceded(tag("<"), parse_term2), |x| (BinOp::LT, x)),
map(preceded(tag("=~"), parse_term2), |x| (BinOp::RegexMatch, x)),
)))(i)?;
let exprs = remainder.into_iter().fold(initial, |acc, (op, expr)| AttrValExpr::BinOp(box acc, op, box expr));
Ok((i, exprs))
}
pub fn parse_expr(i: &str) -> IResult<&str, AttrValExpr, VerboseError<&str>> {
let (i, initial) = parse_term1(i)?;
let (i, remainder) = many0(alt((
map(preceded(tag("&&"), parse_term1), |x| (BinOp::And, x)),
map(preceded(tag("||"), parse_term1), |x| (BinOp::Or, x)),
map(preceded(tag("?:"), parse_term1), |x| (BinOp::Elvis, x)),
)))(i)?;
let exprs = remainder.into_iter().fold(initial, |acc, (op, expr)| AttrValExpr::BinOp(box acc, op, box expr));
Ok((i, exprs))
}
fn parse_ifelse(i: &str) -> IResult<&str, AttrValExpr, VerboseError<&str>> {
let (i, _) = tag("if")(i)?;
let (i, a) = context("condition", ws(parse_expr))(i)?;
let (i, _) = tag("then")(i)?;
let (i, b) = context("true-case", ws(parse_expr))(i)?;
let (i, _) = tag("else")(i)?;
let (i, c) = context("false-case", ws(parse_expr))(i)?;
Ok((i, AttrValExpr::IfElse(box a, box b, box c)))
}
pub fn parse(i: &str) -> IResult<&str, AttrValExpr, VerboseError<&str>> {
complete(parse_expr)(i)
}
#[cfg(test)]
mod test {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn test_parser() {
assert_eq!(("", 12.22f64), parse_num("12.22").unwrap());
assert_eq!(AttrValExpr::Literal(AttrVal::from_primitive("12")), AttrValExpr::parse("12").unwrap());
assert_eq!(
AttrValExpr::UnaryOp(UnaryOp::Not, box AttrValExpr::Literal(AttrVal::from_primitive("false"))),
AttrValExpr::parse("!false").unwrap()
);
assert_eq!(
AttrValExpr::BinOp(
box AttrValExpr::Literal(AttrVal::from_primitive("12")),
BinOp::Plus,
box AttrValExpr::Literal(AttrVal::from_primitive("2"))
),
AttrValExpr::parse("12 + 2").unwrap()
);
assert_eq!(
AttrValExpr::FunctionCall(
"test".to_string(),
vec![AttrValExpr::Literal(AttrVal::from_primitive("hi")), AttrValExpr::Literal(AttrVal::from_primitive("ho")),]
),
AttrValExpr::parse("test(\"hi\", \"ho\")").unwrap()
);
assert_eq!(
AttrValExpr::UnaryOp(
UnaryOp::Not,
box AttrValExpr::BinOp(
box AttrValExpr::Literal(AttrVal::from_primitive("1")),
BinOp::Equals,
box AttrValExpr::Literal(AttrVal::from_primitive("2"))
)
),
AttrValExpr::parse("!(1 == 2)").unwrap()
);
assert_eq!(
AttrValExpr::IfElse(
box AttrValExpr::VarRef(VarName("a".to_string())),
box AttrValExpr::VarRef(VarName("b".to_string())),
box AttrValExpr::VarRef(VarName("c".to_string())),
),
AttrValExpr::parse("if a then b else c").unwrap()
);
assert_eq!(
AttrValExpr::JsonAccess(
box AttrValExpr::VarRef(VarName("array".to_string())),
box AttrValExpr::BinOp(
box AttrValExpr::Literal(AttrVal::from_primitive("1")),
BinOp::Plus,
box AttrValExpr::Literal(AttrVal::from_primitive("2"))
)
),
AttrValExpr::parse(r#"(array)[1+2]"#).unwrap()
);
assert_eq!(
AttrValExpr::JsonAccess(
box AttrValExpr::JsonAccess(
box AttrValExpr::VarRef(VarName("object".to_string())),
box AttrValExpr::Literal(AttrVal::from_primitive("field".to_string())),
),
box AttrValExpr::Literal(AttrVal::from_primitive("field2".to_string())),
),
AttrValExpr::parse(r#"object.field.field2"#).unwrap()
);
}
#[test]
fn test_complex() {
let parsed =
AttrValExpr::parse(r#"if hi > 12 + 2 * 2 && 12 == 15 then "foo" else if !true then 'hi' else "{{bruh}}""#).unwrap();
assert_eq!(
r#"(if ((hi > ("12" + ("2" * "2"))) && ("12" == "15")) then "foo" else (if !"true" then "hi" else "{{bruh}}"))"#,
format!("{}", parsed),
)
}
}