Add boolean and/or operators

This commit is contained in:
2022-08-28 17:14:12 +02:00
parent 974a6e89b2
commit 7d498bafe6
4 changed files with 83 additions and 42 deletions

View File

@ -22,19 +22,21 @@ pub enum Part<'a> {
}
#[derive(Debug, PartialEq)]
pub enum CmpOp {
pub enum Op {
Eq,
NEq,
GrEq,
LeEq,
GrTh,
LeTh,
Or,
And,
}
#[derive(Debug, PartialEq)]
pub enum Expr<'a> {
Cmp {
op: CmpOp,
Op {
op: Op,
expr1: Box<Expr<'a>>,
expr2: Box<Expr<'a>>,
},
@ -152,29 +154,36 @@ impl Expr<'_> {
match self {
Expr::Variable(var) => Cow::Borrowed(env.get(&**var).unwrap_or(&Value::Null)),
Expr::Str(s) => Cow::Owned(Value::Str(s.to_string())),
Expr::Cmp { op, expr1, expr2 } => {
Expr::Op { op, expr1, expr2 } => {
let val1 = expr1.eval(env);
let val2 = expr2.eval(env);
let result = match (&*val1, &*val2) {
(_, Value::Null) | (Value::Null, _) => false,
(Value::Bool(b1), Value::Bool(b2)) => match op {
CmpOp::Eq => b1 == b2,
CmpOp::NEq => b1 != b2,
(&Value::Bool(b1), &Value::Bool(b2)) => match op {
Op::Eq => b1 == b2,
Op::NEq => b1 != b2,
Op::Or => b1 || b2,
Op::And => b1 && b2,
_ => panic!("bools cannot be ordered"),
},
(Value::Str(s1), Value::Str(s2)) => match op {
CmpOp::Eq => s1 == s2,
CmpOp::NEq => s1 != s2,
CmpOp::GrEq => s1 >= s2,
CmpOp::LeEq => s1 <= s2,
CmpOp::GrTh => s1 > s2,
CmpOp::LeTh => s1 < s2,
Op::Eq => s1 == s2,
Op::NEq => s1 != s2,
Op::GrEq => s1 >= s2,
Op::LeEq => s1 <= s2,
Op::GrTh => s1 > s2,
Op::LeTh => s1 < s2,
Op::Or | Op::And => {
panic!("boolean and/or can't be used on strings");
}
},
_ => panic!(
_ => {
panic!(
"invalid {:?}, {:?} and {:?} can't be compared",
op, val1, val2
),
);
}
};
Cow::Owned(Value::Bool(result))
@ -186,7 +195,7 @@ impl Expr<'_> {
match self {
Expr::Variable(var) => vec![var],
Expr::Str(_) => vec![],
Expr::Cmp { expr1, expr2, .. } => expr1
Expr::Op { expr1, expr2, .. } => expr1
.list_variables()
.into_iter()
.chain(expr2.list_variables())

View File

@ -3,7 +3,7 @@ mod env;
mod error;
mod parser;
pub use ast::{CmpOp, Expr, IfThen, Part, Template};
pub use ast::{Expr, IfThen, Op, Part, Template};
pub use env::{Env, Value};
pub use error::Error;
pub use parser::parse_template;
@ -69,8 +69,8 @@ mod test {
Part::Text("one ".into()),
Part::If {
if_then: IfThen {
cond: Expr::Cmp {
op: CmpOp::GrEq,
cond: Expr::Op {
op: Op::GrEq,
expr1: Box::new(Expr::Variable("foo".into())),
expr2: Box::new(Expr::Str("bar".into())),
},
@ -97,8 +97,8 @@ mod test {
Part::Text("one ".into()),
Part::If {
if_then: IfThen {
cond: Expr::Cmp {
op: CmpOp::Eq,
cond: Expr::Op {
op: Op::Eq,
expr1: Box::new(Expr::Variable("foo".into())),
expr2: Box::new(Expr::Variable("bar".into())),
},

View File

@ -17,6 +17,7 @@ fn main() {
Opt::Eval { template, env } => {
let file = read_to_string(template).expect("failed to load file");
let template = blueprint::parse_template(&file).expect("failed to parse");
let env = env
.into_iter()
.map(|key| (key, Value::Bool(true)))

View File

@ -1,4 +1,4 @@
use crate::ast::{CmpOp, Expr, IfThen, Part, Template};
use crate::ast::{Expr, IfThen, Op, Part, Template};
use crate::Error;
use nom::{
branch::alt,
@ -26,31 +26,31 @@ fn expr_str(i: &str) -> IResult<&str, Expr> {
})(i)
}
fn expr1(i: &str) -> IResult<&str, Expr> {
alt((expr_str, expr_var))(i)
}
fn cmp_op(i: &str) -> IResult<&str, CmpOp> {
fn cmp_op(i: &str) -> IResult<&str, Op> {
//eprintln!("cmp_op \"{}\"", i);
alt((
map(tag("=="), |_| CmpOp::Eq),
map(tag("!="), |_| CmpOp::NEq),
map(tag(">="), |_| CmpOp::GrEq),
map(tag("<="), |_| CmpOp::LeEq),
map(tag("<"), |_| CmpOp::LeTh),
map(tag(">"), |_| CmpOp::GrTh),
map(tag("=="), |_| Op::Eq),
map(tag("!="), |_| Op::NEq),
map(tag(">="), |_| Op::GrEq),
map(tag("<="), |_| Op::LeEq),
map(tag("<"), |_| Op::LeTh),
map(tag(">"), |_| Op::GrTh),
))(i)
}
fn expr_cmp(i: &str) -> IResult<&str, Expr> {
//eprintln!("expr_cmp \"{}\"", i);
let (i, e1) = expr1(i)?;
fn expr_operator<'a>(
i: &'a str,
left: impl FnOnce(&'a str) -> IResult<&'a str, Expr>,
right: impl FnOnce(&'a str) -> IResult<&'a str, Expr>,
operator: impl FnOnce(&'a str) -> IResult<&'a str, Op>,
) -> IResult<&str, Expr> {
let (i, e1) = left(i)?;
let (i, _) = multispace0(i)?;
let (i, op) = cmp_op(i)?;
let (i, op) = operator(i)?;
let (i, _) = multispace0(i)?;
let (i, e2) = expr(i)?;
let (i, e2) = right(i)?;
let expr = Expr::Cmp {
let expr = Expr::Op {
op,
expr1: Box::new(e1),
expr2: Box::new(e2),
@ -59,11 +59,42 @@ fn expr_cmp(i: &str) -> IResult<&str, Expr> {
Ok((i, expr))
}
fn expr_cmp(i: &str) -> IResult<&str, Expr> {
//eprintln!("expr_cmp \"{}\"", i);
expr_operator(i, expr3, expr2, cmp_op)
}
fn expr_and(i: &str) -> IResult<&str, Expr> {
//eprintln!("expr_and \"{}\"", i);
expr_operator(i, expr2, expr1, map(tag("&&"), |_| Op::And))
}
fn expr_or(i: &str) -> IResult<&str, Expr> {
//eprintln!("expr_or \"{}\"", i);
expr_operator(i, expr1, expr0, map(tag("||"), |_| Op::Or))
}
fn expr3(i: &str) -> IResult<&str, Expr> {
alt((expr_str, expr_var))(i)
}
fn expr2(i: &str) -> IResult<&str, Expr> {
alt((expr_cmp, expr3))(i)
}
fn expr1(i: &str) -> IResult<&str, Expr> {
alt((expr_and, expr2))(i)
}
fn expr0(i: &str) -> IResult<&str, Expr> {
alt((expr_or, expr1))(i)
}
fn expr(i: &str) -> IResult<&str, Expr> {
//eprintln!("expr \"{}\"", i);
let (i, _) = multispace0(i)?;
let (i, expr) = alt((expr_cmp, expr1))(i)?;
let (i, expr) = expr0(i)?;
let (i, _) = multispace0(i)?;