FEATURE: Boolean Operators.

This commit is contained in:
Jeremy Wall 2019-01-17 19:20:01 -06:00
parent ea2297a5eb
commit d40e89fea9
7 changed files with 177 additions and 4 deletions

View File

@ -184,3 +184,33 @@ assert {
ok = (foo in {foo = NULL}),
desc = "Null valued fields are still present in tuple",
};
assert {
ok = true && true == true,
desc = "&&: truth",
};
assert {
ok = true && false == false,
desc = "&&: propagates false",
};
assert {
ok = false && true == false,
desc = "&&: propagates false part 2",
};
assert {
ok = true || false == true,
desc = "||: propagates true",
};
assert {
ok = false || true == true,
desc = "||: propagates true part 2",
};
assert {
ok = true || true == true,
desc = "||: likes truth",
};

View File

@ -52,3 +52,13 @@ assert {
ok = 1 + tpl.one.two * 2 + 3 == 28,
desc = "1 + tpl.one.two * 2 + 3 == 28",
};
assert {
ok = (1 == 1) && (1 != 1) == false,
desc = "(1 == 1) && (1 != 1) == true",
};
assert {
ok = (1 == 1) || (1 != 1) == true,
desc = "(1 == 1) && (1 != 1) == true",
};

View File

@ -392,6 +392,9 @@ pub enum BinaryExprType {
Sub,
Mul,
Div,
// Boolean
AND,
OR,
// Comparison
Equal,
GT,
@ -428,8 +431,11 @@ impl BinaryExprType {
// Product operators are next tightly bound
BinaryExprType::Mul => 3,
BinaryExprType::Div => 3,
// Boolean operators bind tighter than math
BinaryExprType::AND => 4,
BinaryExprType::OR => 4,
// Dot operators are most tightly bound.
BinaryExprType::DOT => 4,
BinaryExprType::DOT => 5,
}
}
}

View File

@ -178,6 +178,13 @@ impl Val {
return false;
}
pub fn is_bool(&self) -> bool {
if let &Val::Boolean(_) = self {
return true;
}
return false;
}
pub fn is_macro(&self) -> bool {
if let &Val::Macro(_) = self {
return true;

View File

@ -815,6 +815,57 @@ impl<'a> FileBuilder<'a> {
}
}
fn do_bool_operator(
&self,
kind: &BinaryExprType,
left: &Expression,
right: &Expression,
scope: &Scope,
) -> Result<Rc<Val>, Box<dyn Error>> {
let left_pos = left.pos();
let left = self.eval_expr(left, scope)?;
if let Val::Boolean(b) = left.as_ref() {
let right_pos = right.pos();
let b = *b;
if kind == &BinaryExprType::AND {
if !b {
// short circuit
return Ok(Rc::new(Val::Boolean(b)));
}
let right = self.eval_expr(right, scope)?;
if right.is_bool() {
return Ok(right);
}
} else {
if b {
// short circuit
return Ok(Rc::new(Val::Boolean(b)));
}
let right = self.eval_expr(right, scope)?;
if right.is_bool() {
return Ok(right);
}
}
return Err(Box::new(error::BuildError::new(
format!(
"Expected boolean value for operator but got {}",
left.type_name()
),
error::ErrorType::TypeFail,
right_pos.clone(),
)));
} else {
return Err(Box::new(error::BuildError::new(
format!(
"Expected boolean value for operator but got {}",
left.type_name()
),
error::ErrorType::TypeFail,
left_pos.clone(),
)));
}
}
fn do_element_check(
&self,
left: &Expression,
@ -901,6 +952,15 @@ impl<'a> FileBuilder<'a> {
// TODO Should we support this operation on strings too?
return self.do_element_check(&def.left, &def.right, scope);
};
match kind {
// We special case the boolean operators because we want them to short circuit.
&BinaryExprType::AND | &BinaryExprType::OR => {
return self.do_bool_operator(kind, &def.left, &def.right, scope);
}
_ => {
// noop
}
}
let left = self.eval_expr(&def.left, scope)?;
let mut child_scope = scope.spawn_child();
child_scope.set_curr_val(left.clone());
@ -932,7 +992,10 @@ impl<'a> FileBuilder<'a> {
&BinaryExprType::NotREMatch => {
self.eval_re_match(left, def.left.pos(), right, def.right.pos(), true)
}
&BinaryExprType::IN | &BinaryExprType::DOT => panic!("Unreachable"),
&BinaryExprType::IN
| &BinaryExprType::DOT
| &BinaryExprType::AND
| &BinaryExprType::OR => panic!("Unreachable"),
}
}

View File

@ -52,6 +52,18 @@ make_fn!(
)
);
make_fn!(
bool_op_type<SliceIter<Token>, Element>,
either!(
do_each!(
_ => punct!("&&"),
(Element::Op(BinaryExprType::AND))),
do_each!(
_ => punct!("||"),
(Element::Op(BinaryExprType::OR)))
)
);
fn parse_expression(i: SliceIter<Element>) -> Result<SliceIter<Element>, Expression> {
let mut i_ = i.clone();
if eoi(i_.clone()).is_complete() {
@ -73,6 +85,34 @@ fn parse_expression(i: SliceIter<Element>) -> Result<SliceIter<Element>, Express
));
}
fn parse_bool_operator(i: SliceIter<Element>) -> Result<SliceIter<Element>, BinaryExprType> {
let mut i_ = i.clone();
if eoi(i_.clone()).is_complete() {
return Result::Fail(Error::new(
format!("Expected Expression found End Of Input"),
Box::new(i_),
));
}
let el = i_.next();
if let Some(&Element::Op(ref op)) = el {
match op {
BinaryExprType::AND | BinaryExprType::OR => {
return Result::Complete(i_.clone(), op.clone());
}
_other => {
// noop
}
};
}
return Result::Fail(Error::new(
format!(
"Error while parsing Binary Expression Unexpected Operator {:?}",
el
),
Box::new(i_),
));
}
fn parse_dot_operator(i: SliceIter<Element>) -> Result<SliceIter<Element>, BinaryExprType> {
let mut i_ = i.clone();
if eoi(i_.clone()).is_complete() {
@ -243,7 +283,13 @@ fn parse_operand_list<'a>(i: SliceIter<'a, Token>) -> ParseResult<'a, Vec<Elemen
}
}
// 3. Parse an operator.
match either!(_i.clone(), dot_op_type, math_op_type, compare_op_type) {
match either!(
_i.clone(),
dot_op_type,
math_op_type,
compare_op_type,
bool_op_type
) {
Result::Fail(e) => {
if firstrun {
// If we don't find an operator in our first
@ -278,7 +324,8 @@ make_fn!(
parse_dot_operator,
parse_sum_operator,
parse_product_operator,
parse_compare_operator
parse_compare_operator,
parse_bool_operator
)
);

View File

@ -258,6 +258,14 @@ make_fn!(fatcommatok<OffsetStrIter, Token>,
do_text_token_tok!(TokenType::PUNCT, "=>")
);
make_fn!(andtok<OffsetStrIter, Token>,
do_text_token_tok!(TokenType::PUNCT, "&&")
);
make_fn!(ortok<OffsetStrIter, Token>,
do_text_token_tok!(TokenType::PUNCT, "||")
);
make_fn!(selecttok<OffsetStrIter, Token>,
do_text_token_tok!(TokenType::BAREWORD, "select", WS)
);
@ -376,6 +384,8 @@ fn token<'a>(input: OffsetStrIter<'a>) -> Result<OffsetStrIter<'a>, Token> {
lparentok,
rparentok,
dottok,
andtok,
ortok,
plustok,
dashtok,
startok,