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}), ok = (foo in {foo = NULL}),
desc = "Null valued fields are still present in tuple", 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, ok = 1 + tpl.one.two * 2 + 3 == 28,
desc = "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, Sub,
Mul, Mul,
Div, Div,
// Boolean
AND,
OR,
// Comparison // Comparison
Equal, Equal,
GT, GT,
@ -428,8 +431,11 @@ impl BinaryExprType {
// Product operators are next tightly bound // Product operators are next tightly bound
BinaryExprType::Mul => 3, BinaryExprType::Mul => 3,
BinaryExprType::Div => 3, BinaryExprType::Div => 3,
// Boolean operators bind tighter than math
BinaryExprType::AND => 4,
BinaryExprType::OR => 4,
// Dot operators are most tightly bound. // Dot operators are most tightly bound.
BinaryExprType::DOT => 4, BinaryExprType::DOT => 5,
} }
} }
} }

View File

@ -178,6 +178,13 @@ impl Val {
return false; return false;
} }
pub fn is_bool(&self) -> bool {
if let &Val::Boolean(_) = self {
return true;
}
return false;
}
pub fn is_macro(&self) -> bool { pub fn is_macro(&self) -> bool {
if let &Val::Macro(_) = self { if let &Val::Macro(_) = self {
return true; 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( fn do_element_check(
&self, &self,
left: &Expression, left: &Expression,
@ -901,6 +952,15 @@ impl<'a> FileBuilder<'a> {
// TODO Should we support this operation on strings too? // TODO Should we support this operation on strings too?
return self.do_element_check(&def.left, &def.right, scope); 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 left = self.eval_expr(&def.left, scope)?;
let mut child_scope = scope.spawn_child(); let mut child_scope = scope.spawn_child();
child_scope.set_curr_val(left.clone()); child_scope.set_curr_val(left.clone());
@ -932,7 +992,10 @@ impl<'a> FileBuilder<'a> {
&BinaryExprType::NotREMatch => { &BinaryExprType::NotREMatch => {
self.eval_re_match(left, def.left.pos(), right, def.right.pos(), true) 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> { fn parse_expression(i: SliceIter<Element>) -> Result<SliceIter<Element>, Expression> {
let mut i_ = i.clone(); let mut i_ = i.clone();
if eoi(i_.clone()).is_complete() { 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> { fn parse_dot_operator(i: SliceIter<Element>) -> Result<SliceIter<Element>, BinaryExprType> {
let mut i_ = i.clone(); let mut i_ = i.clone();
if eoi(i_.clone()).is_complete() { 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. // 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) => { Result::Fail(e) => {
if firstrun { if firstrun {
// If we don't find an operator in our first // If we don't find an operator in our first
@ -278,7 +324,8 @@ make_fn!(
parse_dot_operator, parse_dot_operator,
parse_sum_operator, parse_sum_operator,
parse_product_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, "=>") 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>, make_fn!(selecttok<OffsetStrIter, Token>,
do_text_token_tok!(TokenType::BAREWORD, "select", WS) do_text_token_tok!(TokenType::BAREWORD, "select", WS)
); );
@ -376,6 +384,8 @@ fn token<'a>(input: OffsetStrIter<'a>) -> Result<OffsetStrIter<'a>, Token> {
lparentok, lparentok,
rparentok, rparentok,
dottok, dottok,
andtok,
ortok,
plustok, plustok,
dashtok, dashtok,
startok, startok,