Add boolean and/or operators
This commit is contained in:
41
src/ast.rs
41
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<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())
|
||||
|
||||
10
src/lib.rs
10
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())),
|
||||
},
|
||||
|
||||
@ -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)))
|
||||
|
||||
@ -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)?;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user