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}}
|
{{#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:
|
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, *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.
|
- 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 {
|
.map_err(|(e1, e2)| ErrorInner {
|
||||||
pos: Some(pos),
|
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 {
|
match condition_checked {
|
||||||
|
@ -2251,14 +2251,14 @@ impl<'ast, T: Field> Checker<'ast, T> {
|
||||||
},
|
},
|
||||||
(c, a) => Err(ErrorInner {
|
(c, a) => Err(ErrorInner {
|
||||||
pos: Some(pos),
|
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 {
|
c => Err(ErrorInner {
|
||||||
pos: Some(pos),
|
pos: Some(pos),
|
||||||
message: format!(
|
message: format!(
|
||||||
"{{condition}} after `if` should be a boolean, found {}",
|
"{{condition}} should be a boolean, found {}",
|
||||||
c.get_type()
|
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_pos = {"+"}
|
||||||
op_left_shift = @{"<<"}
|
op_left_shift = @{"<<"}
|
||||||
op_right_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_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 }
|
op_unary = { op_pos | op_neg | op_not }
|
||||||
|
|
||||||
WHITESPACE = _{ " " | "\t" | "\\" ~ COMMENT? ~ NEWLINE}
|
WHITESPACE = _{ " " | "\t" | "\\" ~ COMMENT? ~ NEWLINE}
|
||||||
|
|
|
@ -38,6 +38,7 @@ mod ast {
|
||||||
// based on https://docs.python.org/3/reference/expressions.html#operator-precedence
|
// based on https://docs.python.org/3/reference/expressions.html#operator-precedence
|
||||||
fn build_precedence_climber() -> PrecClimber<Rule> {
|
fn build_precedence_climber() -> PrecClimber<Rule> {
|
||||||
PrecClimber::new(vec![
|
PrecClimber::new(vec![
|
||||||
|
Operator::new(Rule::op_ternary, Assoc::Right),
|
||||||
Operator::new(Rule::op_or, Assoc::Left),
|
Operator::new(Rule::op_or, Assoc::Left),
|
||||||
Operator::new(Rule::op_and, Assoc::Left),
|
Operator::new(Rule::op_and, Assoc::Left),
|
||||||
Operator::new(Rule::op_lt, 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_bit_or => Expression::binary(BinaryOperator::BitOr, lhs, rhs, span),
|
||||||
Rule::op_right_shift => Expression::binary(BinaryOperator::RightShift, 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_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!(),
|
_ => unreachable!(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue