From 7d498bafe6e50296f2dafb3eb8b253242650da74 Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Sun, 28 Aug 2022 17:14:12 +0200 Subject: [PATCH] Add boolean and/or operators --- src/ast.rs | 45 +++++++++++++++++++-------------- src/lib.rs | 10 ++++---- src/main.rs | 1 + src/parser.rs | 69 +++++++++++++++++++++++++++++++++++++-------------- 4 files changed, 83 insertions(+), 42 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 50d8124..71bbd32 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -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>, expr2: Box>, }, @@ -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!( - "invalid {:?}, {:?} and {:?} can't be compared", - op, val1, val2 - ), + _ => { + 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()) diff --git a/src/lib.rs b/src/lib.rs index 999f2fc..8a10897 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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())), }, diff --git a/src/main.rs b/src/main.rs index f86690f..094f6ed 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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))) diff --git a/src/parser.rs b/src/parser.rs index 7f46d9d..c09755e 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -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)?;