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

View File

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

View File

@ -17,6 +17,7 @@ fn main() {
Opt::Eval { template, env } => { Opt::Eval { template, env } => {
let file = read_to_string(template).expect("failed to load file"); let file = read_to_string(template).expect("failed to load file");
let template = blueprint::parse_template(&file).expect("failed to parse"); let template = blueprint::parse_template(&file).expect("failed to parse");
let env = env let env = env
.into_iter() .into_iter()
.map(|key| (key, Value::Bool(true))) .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 crate::Error;
use nom::{ use nom::{
branch::alt, branch::alt,
@ -26,31 +26,31 @@ fn expr_str(i: &str) -> IResult<&str, Expr> {
})(i) })(i)
} }
fn expr1(i: &str) -> IResult<&str, Expr> { fn cmp_op(i: &str) -> IResult<&str, Op> {
alt((expr_str, expr_var))(i)
}
fn cmp_op(i: &str) -> IResult<&str, CmpOp> {
//eprintln!("cmp_op \"{}\"", i); //eprintln!("cmp_op \"{}\"", i);
alt(( alt((
map(tag("=="), |_| CmpOp::Eq), map(tag("=="), |_| Op::Eq),
map(tag("!="), |_| CmpOp::NEq), map(tag("!="), |_| Op::NEq),
map(tag(">="), |_| CmpOp::GrEq), map(tag(">="), |_| Op::GrEq),
map(tag("<="), |_| CmpOp::LeEq), map(tag("<="), |_| Op::LeEq),
map(tag("<"), |_| CmpOp::LeTh), map(tag("<"), |_| Op::LeTh),
map(tag(">"), |_| CmpOp::GrTh), map(tag(">"), |_| Op::GrTh),
))(i) ))(i)
} }
fn expr_cmp(i: &str) -> IResult<&str, Expr> { fn expr_operator<'a>(
//eprintln!("expr_cmp \"{}\"", i); i: &'a str,
let (i, e1) = expr1(i)?; 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, _) = multispace0(i)?;
let (i, op) = cmp_op(i)?; let (i, op) = operator(i)?;
let (i, _) = multispace0(i)?; let (i, _) = multispace0(i)?;
let (i, e2) = expr(i)?; let (i, e2) = right(i)?;
let expr = Expr::Cmp { let expr = Expr::Op {
op, op,
expr1: Box::new(e1), expr1: Box::new(e1),
expr2: Box::new(e2), expr2: Box::new(e2),
@ -59,11 +59,42 @@ fn expr_cmp(i: &str) -> IResult<&str, Expr> {
Ok((i, 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> { fn expr(i: &str) -> IResult<&str, Expr> {
//eprintln!("expr \"{}\"", i); //eprintln!("expr \"{}\"", i);
let (i, _) = multispace0(i)?; let (i, _) = multispace0(i)?;
let (i, expr) = alt((expr_cmp, expr1))(i)?; let (i, expr) = expr0(i)?;
let (i, _) = multispace0(i)?; let (i, _) = multispace0(i)?;