mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-22 18:19:54 -04:00
FEATURE: Boolean Operators.
This commit is contained in:
parent
ea2297a5eb
commit
d40e89fea9
@ -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",
|
||||
};
|
@ -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",
|
||||
};
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user