1
0
Fork 0
mirror of synced 2025-09-23 12:18:44 +00:00

implement ternary operator

This commit is contained in:
dark64 2021-09-15 19:39:01 +02:00
parent af704063ca
commit 11b595ef21
7 changed files with 60 additions and 4 deletions

View file

@ -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.

View file

@ -0,0 +1,3 @@
def main(field x) -> field:
field y = x + 2 == 3 ? 1 : 5
return y

View file

@ -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()
),
}),

View 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"]
}
}
}
]
}

View file

@ -0,0 +1,2 @@
def main(bool a, bool b) -> field:
return a ? 1 : b ? 2 : 3 // (a ? 1 : (b ? 2 : 3))

View file

@ -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}

View file

@ -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!(),
})
}