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)]
|
#[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())
|
||||||
|
|||||||
10
src/lib.rs
10
src/lib.rs
@ -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())),
|
||||||
},
|
},
|
||||||
|
|||||||
@ -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)))
|
||||||
|
|||||||
@ -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)?;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user