implement ternary operator
This commit is contained in:
parent
af704063ca
commit
11b595ef21
7 changed files with 60 additions and 4 deletions
|
@ -26,6 +26,12 @@ An if-expression allows you to branch your code depending on a boolean condition
|
|||
{{#include ../../../zokrates_cli/examples/book/if_else.zok}}
|
||||
```
|
||||
|
||||
The conditional expression can also be written using a ternary operator:
|
||||
|
||||
```zokrates
|
||||
{{#include ../../../zokrates_cli/examples/book/ternary.zok}}
|
||||
```
|
||||
|
||||
There are two important caveats when it comes to conditional expressions. Before we go into them, let's define two concepts:
|
||||
- for an execution of the program, *an executed branch* is a branch which has to be paid for when executing the program, generating proofs, etc.
|
||||
- for an execution of the program, *a logically executed branch* is a branch which is "chosen" by the condition of an if-expression. This is the more intuitive notion of execution, and there is only one for each if-expression.
|
||||
|
|
3
zokrates_cli/examples/book/ternary.zok
Normal file
3
zokrates_cli/examples/book/ternary.zok
Normal file
|
@ -0,0 +1,3 @@
|
|||
def main(field x) -> field:
|
||||
field y = x + 2 == 3 ? 1 : 5
|
||||
return y
|
|
@ -2225,7 +2225,7 @@ impl<'ast, T: Field> Checker<'ast, T> {
|
|||
)
|
||||
.map_err(|(e1, e2)| ErrorInner {
|
||||
pos: Some(pos),
|
||||
message: format!("{{consequence}} and {{alternative}} in `if/else` expression should have the same type, found {}, {}", e1.get_type(), e2.get_type()),
|
||||
message: format!("{{consequence}} and {{alternative}} in conditional expression should have the same type, found {}, {}", e1.get_type(), e2.get_type()),
|
||||
})?;
|
||||
|
||||
match condition_checked {
|
||||
|
@ -2251,14 +2251,14 @@ impl<'ast, T: Field> Checker<'ast, T> {
|
|||
},
|
||||
(c, a) => Err(ErrorInner {
|
||||
pos: Some(pos),
|
||||
message: format!("{{consequence}} and {{alternative}} in `if/else` expression should have the same type, found {}, {}", c.get_type(), a.get_type())
|
||||
message: format!("{{consequence}} and {{alternative}} in conditional expression should have the same type, found {}, {}", c.get_type(), a.get_type())
|
||||
})
|
||||
}
|
||||
}
|
||||
c => Err(ErrorInner {
|
||||
pos: Some(pos),
|
||||
message: format!(
|
||||
"{{condition}} after `if` should be a boolean, found {}",
|
||||
"{{condition}} should be a boolean, found {}",
|
||||
c.get_type()
|
||||
),
|
||||
}),
|
||||
|
|
36
zokrates_core_test/tests/tests/ternary.json
Normal file
36
zokrates_core_test/tests/tests/ternary.json
Normal file
|
@ -0,0 +1,36 @@
|
|||
{
|
||||
"entry_point": "./tests/tests/ternary.zok",
|
||||
"max_constraint_count": 4,
|
||||
"tests": [
|
||||
{
|
||||
"input": {
|
||||
"values": ["1", "0"]
|
||||
},
|
||||
"output": {
|
||||
"Ok": {
|
||||
"values": ["1"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"input": {
|
||||
"values": ["0", "1"]
|
||||
},
|
||||
"output": {
|
||||
"Ok": {
|
||||
"values": ["2"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"input": {
|
||||
"values": ["0", "0"]
|
||||
},
|
||||
"output": {
|
||||
"Ok": {
|
||||
"values": ["3"]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
2
zokrates_core_test/tests/tests/ternary.zok
Normal file
2
zokrates_core_test/tests/tests/ternary.zok
Normal file
|
@ -0,0 +1,2 @@
|
|||
def main(bool a, bool b) -> field:
|
||||
return a ? 1 : b ? 2 : 3 // (a ? 1 : (b ? 2 : 3))
|
|
@ -154,8 +154,10 @@ op_neg = {"-"}
|
|||
op_pos = {"+"}
|
||||
op_left_shift = @{"<<"}
|
||||
op_right_shift = @{">>"}
|
||||
op_ternary = {"?" ~ expression ~ ":"}
|
||||
|
||||
// `op_pow` is *not* in `op_binary` because its precedence is handled in this parser rather than down the line in precedence climbing
|
||||
op_binary = _ { op_or | op_and | op_bit_xor | op_bit_and | op_bit_or | op_left_shift | op_right_shift | op_equal | op_not_equal | op_lte | op_lt | op_gte | op_gt | op_add | op_sub | op_mul | op_div | op_rem }
|
||||
op_binary = _ { op_or | op_and | op_bit_xor | op_bit_and | op_bit_or | op_left_shift | op_right_shift | op_equal | op_not_equal | op_lte | op_lt | op_gte | op_gt | op_add | op_sub | op_mul | op_div | op_rem | op_ternary }
|
||||
op_unary = { op_pos | op_neg | op_not }
|
||||
|
||||
WHITESPACE = _{ " " | "\t" | "\\" ~ COMMENT? ~ NEWLINE}
|
||||
|
|
|
@ -38,6 +38,7 @@ mod ast {
|
|||
// based on https://docs.python.org/3/reference/expressions.html#operator-precedence
|
||||
fn build_precedence_climber() -> PrecClimber<Rule> {
|
||||
PrecClimber::new(vec![
|
||||
Operator::new(Rule::op_ternary, Assoc::Right),
|
||||
Operator::new(Rule::op_or, Assoc::Left),
|
||||
Operator::new(Rule::op_and, Assoc::Left),
|
||||
Operator::new(Rule::op_lt, Assoc::Left)
|
||||
|
@ -89,6 +90,12 @@ mod ast {
|
|||
Rule::op_bit_or => Expression::binary(BinaryOperator::BitOr, lhs, rhs, span),
|
||||
Rule::op_right_shift => Expression::binary(BinaryOperator::RightShift, lhs, rhs, span),
|
||||
Rule::op_left_shift => Expression::binary(BinaryOperator::LeftShift, lhs, rhs, span),
|
||||
Rule::op_ternary => dbg!(Expression::ternary(
|
||||
lhs,
|
||||
Box::new(Expression::from_pest(&mut pair.into_inner()).unwrap()),
|
||||
rhs,
|
||||
span,
|
||||
)),
|
||||
_ => unreachable!(),
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue