remove if-expressions
This commit is contained in:
parent
8b16c0b4bf
commit
66b273dabc
22 changed files with 50 additions and 180 deletions
|
@ -18,18 +18,12 @@ Generic paramaters, if any, must be compile-time constants. They are inferred by
|
||||||
{{#include ../../../zokrates_cli/examples/book/generic_call.zok}}
|
{{#include ../../../zokrates_cli/examples/book/generic_call.zok}}
|
||||||
```
|
```
|
||||||
|
|
||||||
### If-expressions
|
### Conditional expressions
|
||||||
|
|
||||||
An if-expression allows you to branch your code depending on a boolean condition.
|
A conditional expression allows you to branch your code depending on a boolean condition. This can be written using a ternary operator:
|
||||||
|
|
||||||
```zokrates
|
```zokrates
|
||||||
{{#include ../../../zokrates_cli/examples/book/if_else.zok}}
|
{{#include ../../../zokrates_cli/examples/book/conditional_ternary.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:
|
||||||
|
@ -39,11 +33,11 @@ There are two important caveats when it comes to conditional expressions. Before
|
||||||
Now the two caveats:
|
Now the two caveats:
|
||||||
- **Both branches are always executed**. No short-circuiting happens based on the value of the condition. Therefore, the complexity of a program in terms of the number of constraints it compiles down to is the *sum* of the cost of all branches.
|
- **Both branches are always executed**. No short-circuiting happens based on the value of the condition. Therefore, the complexity of a program in terms of the number of constraints it compiles down to is the *sum* of the cost of all branches.
|
||||||
```zokrates
|
```zokrates
|
||||||
{{#include ../../../zokrates_cli/examples/book/if_else_expensive.zok}}
|
{{#include ../../../zokrates_cli/examples/book/conditional_expensive.zok}}
|
||||||
```
|
```
|
||||||
- **An unsatisfied constraint inside any branch will make the whole execution fail, even if this branch is not logically executed**. Also, the compiler itself inserts assertions which can fail. This can lead to unexpected results:
|
- **An unsatisfied constraint inside any branch will make the whole execution fail, even if this branch is not logically executed**. Also, the compiler itself inserts assertions which can fail. This can lead to unexpected results:
|
||||||
```zokrates
|
```zokrates
|
||||||
{{#include ../../../zokrates_cli/examples/book/if_else_panic.zok}}
|
{{#include ../../../zokrates_cli/examples/book/conditional_panic.zok}}
|
||||||
```
|
```
|
||||||
The experimental flag `--branch-isolation` can be activated in the CLI in order to restrict any unsatisfied constraint to make the execution fail only if it is in a logically executed branch. This way, the execution of the program above will always succeed.
|
The experimental flag `--branch-isolation` can be activated in the CLI in order to restrict any unsatisfied constraint to make the execution fail only if it is in a logically executed branch. This way, the execution of the program above will always succeed.
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ The following table lists the precedence and associativity of all operators. Ope
|
||||||
| `!=`<br>`==`<br> | Not Equal<br>Equal<br> | ✓ | ✓ | ✓ | Left |
|
| `!=`<br>`==`<br> | Not Equal<br>Equal<br> | ✓ | ✓ | ✓ | Left |
|
||||||
| `&&` | Boolean AND | | | ✓ | Left |
|
| `&&` | Boolean AND | | | ✓ | Left |
|
||||||
| <code>||</code> | Boolean OR | | | ✓ | Left |
|
| <code>||</code> | Boolean OR | | | ✓ | Left |
|
||||||
| `c ? x : y`<br><br>`if c then x else y fi` | Conditional expression | ✓ | ✓ | ✓ | Right | |
|
| `c ? x : y` | Conditional expression | ✓ | ✓ | ✓ | Right | |
|
||||||
|
|
||||||
[^1]: The exponent must be a compile-time constant of type `u32`
|
[^1]: The exponent must be a compile-time constant of type `u32`
|
||||||
|
|
||||||
|
|
11
zokrates_cli/examples/book/conditional_expensive.zok
Normal file
11
zokrates_cli/examples/book/conditional_expensive.zok
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
def cheap(field x) -> field {
|
||||||
|
return x + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
def expensive(field x) -> field {
|
||||||
|
return x**1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
def main(field x) -> field {
|
||||||
|
return x == 1 ? cheap(x) : expensive(x); // both branches executed
|
||||||
|
}
|
3
zokrates_cli/examples/book/conditional_panic.zok
Normal file
3
zokrates_cli/examples/book/conditional_panic.zok
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
def main(field x) -> field {
|
||||||
|
return x == 0 ? 0 : 1 / x; // executed even for x := 0, which leads to the execution failing
|
||||||
|
}
|
4
zokrates_cli/examples/book/conditional_ternary.zok
Normal file
4
zokrates_cli/examples/book/conditional_ternary.zok
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
def main(field x) -> field {
|
||||||
|
field y = x + 2 == 3 ? 1 : 5;
|
||||||
|
return y;
|
||||||
|
}
|
|
@ -1,3 +0,0 @@
|
||||||
def main(field x) -> field:
|
|
||||||
field y = if x + 2 == 3 then 1 else 5 fi
|
|
||||||
return y
|
|
|
@ -1,12 +0,0 @@
|
||||||
def cheap(field x) -> field:
|
|
||||||
return x + 1
|
|
||||||
|
|
||||||
def expensive(field x) -> field:
|
|
||||||
return x**1000
|
|
||||||
|
|
||||||
def main(field x) -> field:
|
|
||||||
return if x == 1 then\
|
|
||||||
cheap(x)\// executed
|
|
||||||
else\
|
|
||||||
expensive(x)\// also executed
|
|
||||||
fi
|
|
|
@ -1,6 +0,0 @@
|
||||||
def main(field x) -> field:
|
|
||||||
return if x == 0 then\
|
|
||||||
0\
|
|
||||||
else\
|
|
||||||
1/x\// executed even for x := 0, which leads to the execution failing
|
|
||||||
fi
|
|
|
@ -1,3 +0,0 @@
|
||||||
def main(field x) -> field:
|
|
||||||
field y = x + 2 == 3 ? 1 : 5
|
|
||||||
return y
|
|
|
@ -405,7 +405,6 @@ impl<'ast> From<pest::Expression<'ast>> for absy::ExpressionNode<'ast> {
|
||||||
match expression {
|
match expression {
|
||||||
pest::Expression::Binary(e) => absy::ExpressionNode::from(e),
|
pest::Expression::Binary(e) => absy::ExpressionNode::from(e),
|
||||||
pest::Expression::Ternary(e) => absy::ExpressionNode::from(e),
|
pest::Expression::Ternary(e) => absy::ExpressionNode::from(e),
|
||||||
pest::Expression::IfElse(e) => absy::ExpressionNode::from(e),
|
|
||||||
pest::Expression::Literal(e) => absy::ExpressionNode::from(e),
|
pest::Expression::Literal(e) => absy::ExpressionNode::from(e),
|
||||||
pest::Expression::Identifier(e) => absy::ExpressionNode::from(e),
|
pest::Expression::Identifier(e) => absy::ExpressionNode::from(e),
|
||||||
pest::Expression::Postfix(e) => absy::ExpressionNode::from(e),
|
pest::Expression::Postfix(e) => absy::ExpressionNode::from(e),
|
||||||
|
@ -507,19 +506,6 @@ impl<'ast> From<pest::BinaryExpression<'ast>> for absy::ExpressionNode<'ast> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ast> From<pest::IfElseExpression<'ast>> for absy::ExpressionNode<'ast> {
|
|
||||||
fn from(expression: pest::IfElseExpression<'ast>) -> absy::ExpressionNode<'ast> {
|
|
||||||
use crate::absy::NodeValue;
|
|
||||||
absy::Expression::Conditional(
|
|
||||||
box absy::ExpressionNode::from(*expression.condition),
|
|
||||||
box absy::ExpressionNode::from(*expression.consequence),
|
|
||||||
box absy::ExpressionNode::from(*expression.alternative),
|
|
||||||
absy::ConditionalKind::IfElse,
|
|
||||||
)
|
|
||||||
.span(expression.span)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ast> From<pest::TernaryExpression<'ast>> for absy::ExpressionNode<'ast> {
|
impl<'ast> From<pest::TernaryExpression<'ast>> for absy::ExpressionNode<'ast> {
|
||||||
fn from(expression: pest::TernaryExpression<'ast>) -> absy::ExpressionNode<'ast> {
|
fn from(expression: pest::TernaryExpression<'ast>) -> absy::ExpressionNode<'ast> {
|
||||||
use crate::absy::NodeValue;
|
use crate::absy::NodeValue;
|
||||||
|
@ -527,7 +513,6 @@ impl<'ast> From<pest::TernaryExpression<'ast>> for absy::ExpressionNode<'ast> {
|
||||||
box absy::ExpressionNode::from(*expression.condition),
|
box absy::ExpressionNode::from(*expression.condition),
|
||||||
box absy::ExpressionNode::from(*expression.consequence),
|
box absy::ExpressionNode::from(*expression.consequence),
|
||||||
box absy::ExpressionNode::from(*expression.alternative),
|
box absy::ExpressionNode::from(*expression.alternative),
|
||||||
absy::ConditionalKind::Ternary,
|
|
||||||
)
|
)
|
||||||
.span(expression.span)
|
.span(expression.span)
|
||||||
}
|
}
|
||||||
|
|
|
@ -510,12 +510,6 @@ impl<'ast> fmt::Display for Range<'ast> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub enum ConditionalKind {
|
|
||||||
IfElse,
|
|
||||||
Ternary,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An expression
|
/// An expression
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum Expression<'ast> {
|
pub enum Expression<'ast> {
|
||||||
|
@ -539,7 +533,6 @@ pub enum Expression<'ast> {
|
||||||
Box<ExpressionNode<'ast>>,
|
Box<ExpressionNode<'ast>>,
|
||||||
Box<ExpressionNode<'ast>>,
|
Box<ExpressionNode<'ast>>,
|
||||||
Box<ExpressionNode<'ast>>,
|
Box<ExpressionNode<'ast>>,
|
||||||
ConditionalKind,
|
|
||||||
),
|
),
|
||||||
FunctionCall(
|
FunctionCall(
|
||||||
Box<ExpressionNode<'ast>>,
|
Box<ExpressionNode<'ast>>,
|
||||||
|
@ -589,17 +582,8 @@ impl<'ast> fmt::Display for Expression<'ast> {
|
||||||
Expression::Neg(ref e) => write!(f, "(-{})", e),
|
Expression::Neg(ref e) => write!(f, "(-{})", e),
|
||||||
Expression::Pos(ref e) => write!(f, "(+{})", e),
|
Expression::Pos(ref e) => write!(f, "(+{})", e),
|
||||||
Expression::BooleanConstant(b) => write!(f, "{}", b),
|
Expression::BooleanConstant(b) => write!(f, "{}", b),
|
||||||
Expression::Conditional(ref condition, ref consequent, ref alternative, ref kind) => {
|
Expression::Conditional(ref condition, ref consequent, ref alternative) => {
|
||||||
match kind {
|
write!(f, "{} ? {} : {}", condition, consequent, alternative)
|
||||||
ConditionalKind::IfElse => write!(
|
|
||||||
f,
|
|
||||||
"if {} then {} else {} fi",
|
|
||||||
condition, consequent, alternative
|
|
||||||
),
|
|
||||||
ConditionalKind::Ternary => {
|
|
||||||
write!(f, "{} ? {} : {}", condition, consequent, alternative)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Expression::FunctionCall(ref i, ref g, ref p) => {
|
Expression::FunctionCall(ref i, ref g, ref p) => {
|
||||||
if let Some(g) = g {
|
if let Some(g) = g {
|
||||||
|
|
|
@ -2461,7 +2461,7 @@ impl<'ast, T: Field> Checker<'ast, T> {
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expression::Conditional(box condition, box consequence, box alternative, kind) => {
|
Expression::Conditional(box condition, box consequence, box alternative) => {
|
||||||
let condition_checked = self.check_expression(condition, module_id, types)?;
|
let condition_checked = self.check_expression(condition, module_id, types)?;
|
||||||
let consequence_checked = self.check_expression(consequence, module_id, types)?;
|
let consequence_checked = self.check_expression(consequence, module_id, types)?;
|
||||||
let alternative_checked = self.check_expression(alternative, module_id, types)?;
|
let alternative_checked = self.check_expression(alternative, module_id, types)?;
|
||||||
|
@ -2476,35 +2476,26 @@ impl<'ast, T: Field> Checker<'ast, T> {
|
||||||
message: format!("{{consequence}} and {{alternative}} in conditional 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()),
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let kind = match kind {
|
|
||||||
crate::absy::ConditionalKind::IfElse => {
|
|
||||||
crate::typed_absy::ConditionalKind::IfElse
|
|
||||||
}
|
|
||||||
crate::absy::ConditionalKind::Ternary => {
|
|
||||||
crate::typed_absy::ConditionalKind::Ternary
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
match condition_checked {
|
match condition_checked {
|
||||||
TypedExpression::Boolean(condition) => {
|
TypedExpression::Boolean(condition) => {
|
||||||
match (consequence_checked, alternative_checked) {
|
match (consequence_checked, alternative_checked) {
|
||||||
(TypedExpression::FieldElement(consequence), TypedExpression::FieldElement(alternative)) => {
|
(TypedExpression::FieldElement(consequence), TypedExpression::FieldElement(alternative)) => {
|
||||||
Ok(FieldElementExpression::conditional(condition, consequence, alternative, kind).into())
|
Ok(FieldElementExpression::conditional(condition, consequence, alternative).into())
|
||||||
},
|
},
|
||||||
(TypedExpression::Boolean(consequence), TypedExpression::Boolean(alternative)) => {
|
(TypedExpression::Boolean(consequence), TypedExpression::Boolean(alternative)) => {
|
||||||
Ok(BooleanExpression::conditional(condition, consequence, alternative, kind).into())
|
Ok(BooleanExpression::conditional(condition, consequence, alternative).into())
|
||||||
},
|
},
|
||||||
(TypedExpression::Array(consequence), TypedExpression::Array(alternative)) => {
|
(TypedExpression::Array(consequence), TypedExpression::Array(alternative)) => {
|
||||||
Ok(ArrayExpression::conditional(condition, consequence, alternative, kind).into())
|
Ok(ArrayExpression::conditional(condition, consequence, alternative).into())
|
||||||
},
|
},
|
||||||
(TypedExpression::Struct(consequence), TypedExpression::Struct(alternative)) => {
|
(TypedExpression::Struct(consequence), TypedExpression::Struct(alternative)) => {
|
||||||
Ok(StructExpression::conditional(condition, consequence, alternative, kind).into())
|
Ok(StructExpression::conditional(condition, consequence, alternative).into())
|
||||||
},
|
},
|
||||||
(TypedExpression::Uint(consequence), TypedExpression::Uint(alternative)) => {
|
(TypedExpression::Uint(consequence), TypedExpression::Uint(alternative)) => {
|
||||||
Ok(UExpression::conditional(condition, consequence, alternative, kind).into())
|
Ok(UExpression::conditional(condition, consequence, alternative).into())
|
||||||
},
|
},
|
||||||
(TypedExpression::Int(consequence), TypedExpression::Int(alternative)) => {
|
(TypedExpression::Int(consequence), TypedExpression::Int(alternative)) => {
|
||||||
Ok(IntExpression::conditional(condition, consequence, alternative, kind).into())
|
Ok(IntExpression::conditional(condition, consequence, alternative).into())
|
||||||
},
|
},
|
||||||
(c, a) => Err(ErrorInner {
|
(c, a) => Err(ErrorInner {
|
||||||
pos: Some(pos),
|
pos: Some(pos),
|
||||||
|
|
|
@ -28,7 +28,6 @@ impl<'ast, T: Field> Folder<'ast, T> for Isolator {
|
||||||
self.fold_boolean_expression(*e.condition),
|
self.fold_boolean_expression(*e.condition),
|
||||||
E::block(vec![], e.consequence.fold(self)),
|
E::block(vec![], e.consequence.fold(self)),
|
||||||
E::block(vec![], e.alternative.fold(self)),
|
E::block(vec![], e.alternative.fold(self)),
|
||||||
e.kind,
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,7 +81,6 @@ impl<'ast, T: Field> Folder<'ast, T> for ConditionRedefiner<'ast, T> {
|
||||||
condition,
|
condition,
|
||||||
consequence,
|
consequence,
|
||||||
alternative,
|
alternative,
|
||||||
e.kind,
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -209,7 +209,7 @@ impl<'ast, 'a, T: Field> ResultFolder<'ast, T> for Propagator<'ast, 'a, T> {
|
||||||
ConditionalOrExpression::Expression(consequence.into_inner())
|
ConditionalOrExpression::Expression(consequence.into_inner())
|
||||||
}
|
}
|
||||||
(condition, consequence, alternative) => ConditionalOrExpression::Conditional(
|
(condition, consequence, alternative) => ConditionalOrExpression::Conditional(
|
||||||
ConditionalExpression::new(condition, consequence, alternative, e.kind),
|
ConditionalExpression::new(condition, consequence, alternative),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -73,7 +73,6 @@ impl<'ast> VariableWriteRemover {
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
ArrayExpression::select(base.clone(), i),
|
ArrayExpression::select(base.clone(), i),
|
||||||
ConditionalKind::IfElse,
|
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
Type::Struct(..) => StructExpression::conditional(
|
Type::Struct(..) => StructExpression::conditional(
|
||||||
|
@ -94,7 +93,6 @@ impl<'ast> VariableWriteRemover {
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
StructExpression::select(base.clone(), i),
|
StructExpression::select(base.clone(), i),
|
||||||
ConditionalKind::IfElse,
|
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
Type::Tuple(..) => TupleExpression::conditional(
|
Type::Tuple(..) => TupleExpression::conditional(
|
||||||
|
@ -115,7 +113,6 @@ impl<'ast> VariableWriteRemover {
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
TupleExpression::select(base.clone(), i),
|
TupleExpression::select(base.clone(), i),
|
||||||
ConditionalKind::IfElse,
|
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
Type::FieldElement => FieldElementExpression::conditional(
|
Type::FieldElement => FieldElementExpression::conditional(
|
||||||
|
@ -137,7 +134,6 @@ impl<'ast> VariableWriteRemover {
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
FieldElementExpression::select(base.clone(), i),
|
FieldElementExpression::select(base.clone(), i),
|
||||||
ConditionalKind::IfElse,
|
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
Type::Boolean => BooleanExpression::conditional(
|
Type::Boolean => BooleanExpression::conditional(
|
||||||
|
@ -158,7 +154,6 @@ impl<'ast> VariableWriteRemover {
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
BooleanExpression::select(base.clone(), i),
|
BooleanExpression::select(base.clone(), i),
|
||||||
ConditionalKind::IfElse,
|
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
Type::Uint(..) => UExpression::conditional(
|
Type::Uint(..) => UExpression::conditional(
|
||||||
|
@ -179,7 +174,6 @@ impl<'ast> VariableWriteRemover {
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
UExpression::select(base.clone(), i),
|
UExpression::select(base.clone(), i),
|
||||||
ConditionalKind::IfElse,
|
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
})
|
})
|
||||||
|
|
|
@ -749,7 +749,6 @@ pub fn fold_conditional_expression<
|
||||||
f.fold_boolean_expression(*e.condition),
|
f.fold_boolean_expression(*e.condition),
|
||||||
e.consequence.fold(f),
|
e.consequence.fold(f),
|
||||||
e.alternative.fold(f),
|
e.alternative.fold(f),
|
||||||
e.kind,
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -452,7 +452,6 @@ impl<'ast, T: Field> FieldElementExpression<'ast, T> {
|
||||||
*c.condition,
|
*c.condition,
|
||||||
Self::try_from_int(*c.consequence)?,
|
Self::try_from_int(*c.consequence)?,
|
||||||
Self::try_from_int(*c.alternative)?,
|
Self::try_from_int(*c.alternative)?,
|
||||||
c.kind,
|
|
||||||
))),
|
))),
|
||||||
IntExpression::Select(select) => {
|
IntExpression::Select(select) => {
|
||||||
let array = *select.array;
|
let array = *select.array;
|
||||||
|
@ -572,7 +571,6 @@ impl<'ast, T: Field> UExpression<'ast, T> {
|
||||||
*c.condition,
|
*c.condition,
|
||||||
Self::try_from_int(*c.consequence, bitwidth)?,
|
Self::try_from_int(*c.consequence, bitwidth)?,
|
||||||
Self::try_from_int(*c.alternative, bitwidth)?,
|
Self::try_from_int(*c.alternative, bitwidth)?,
|
||||||
c.kind,
|
|
||||||
)),
|
)),
|
||||||
Select(select) => {
|
Select(select) => {
|
||||||
let array = *select.array;
|
let array = *select.array;
|
||||||
|
|
|
@ -1016,50 +1016,30 @@ impl<'ast, T: fmt::Display, E> fmt::Display for ElementExpression<'ast, T, E> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Hash, Eq, PartialOrd, Ord)]
|
|
||||||
pub enum ConditionalKind {
|
|
||||||
IfElse,
|
|
||||||
Ternary,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Hash, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, PartialEq, Hash, Eq, PartialOrd, Ord)]
|
||||||
pub struct ConditionalExpression<'ast, T, E> {
|
pub struct ConditionalExpression<'ast, T, E> {
|
||||||
pub condition: Box<BooleanExpression<'ast, T>>,
|
pub condition: Box<BooleanExpression<'ast, T>>,
|
||||||
pub consequence: Box<E>,
|
pub consequence: Box<E>,
|
||||||
pub alternative: Box<E>,
|
pub alternative: Box<E>,
|
||||||
pub kind: ConditionalKind,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ast, T, E> ConditionalExpression<'ast, T, E> {
|
impl<'ast, T, E> ConditionalExpression<'ast, T, E> {
|
||||||
pub fn new(
|
pub fn new(condition: BooleanExpression<'ast, T>, consequence: E, alternative: E) -> Self {
|
||||||
condition: BooleanExpression<'ast, T>,
|
|
||||||
consequence: E,
|
|
||||||
alternative: E,
|
|
||||||
kind: ConditionalKind,
|
|
||||||
) -> Self {
|
|
||||||
ConditionalExpression {
|
ConditionalExpression {
|
||||||
condition: box condition,
|
condition: box condition,
|
||||||
consequence: box consequence,
|
consequence: box consequence,
|
||||||
alternative: box alternative,
|
alternative: box alternative,
|
||||||
kind,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ast, T: fmt::Display, E: fmt::Display> fmt::Display for ConditionalExpression<'ast, T, E> {
|
impl<'ast, T: fmt::Display, E: fmt::Display> fmt::Display for ConditionalExpression<'ast, T, E> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self.kind {
|
write!(
|
||||||
ConditionalKind::IfElse => write!(
|
f,
|
||||||
f,
|
"{} ? {} : {}",
|
||||||
"if {} then {} else {} fi",
|
self.condition, self.consequence, self.alternative
|
||||||
self.condition, self.consequence, self.alternative
|
)
|
||||||
),
|
|
||||||
ConditionalKind::Ternary => write!(
|
|
||||||
f,
|
|
||||||
"{} ? {} : {}",
|
|
||||||
self.condition, self.consequence, self.alternative
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2010,7 +1990,6 @@ pub trait Conditional<'ast, T> {
|
||||||
condition: BooleanExpression<'ast, T>,
|
condition: BooleanExpression<'ast, T>,
|
||||||
consequence: Self,
|
consequence: Self,
|
||||||
alternative: Self,
|
alternative: Self,
|
||||||
kind: ConditionalKind,
|
|
||||||
) -> Self;
|
) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2019,13 +1998,11 @@ impl<'ast, T> Conditional<'ast, T> for FieldElementExpression<'ast, T> {
|
||||||
condition: BooleanExpression<'ast, T>,
|
condition: BooleanExpression<'ast, T>,
|
||||||
consequence: Self,
|
consequence: Self,
|
||||||
alternative: Self,
|
alternative: Self,
|
||||||
kind: ConditionalKind,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
FieldElementExpression::Conditional(ConditionalExpression::new(
|
FieldElementExpression::Conditional(ConditionalExpression::new(
|
||||||
condition,
|
condition,
|
||||||
consequence,
|
consequence,
|
||||||
alternative,
|
alternative,
|
||||||
kind,
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2035,13 +2012,11 @@ impl<'ast, T> Conditional<'ast, T> for IntExpression<'ast, T> {
|
||||||
condition: BooleanExpression<'ast, T>,
|
condition: BooleanExpression<'ast, T>,
|
||||||
consequence: Self,
|
consequence: Self,
|
||||||
alternative: Self,
|
alternative: Self,
|
||||||
kind: ConditionalKind,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
IntExpression::Conditional(ConditionalExpression::new(
|
IntExpression::Conditional(ConditionalExpression::new(
|
||||||
condition,
|
condition,
|
||||||
consequence,
|
consequence,
|
||||||
alternative,
|
alternative,
|
||||||
kind,
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2051,13 +2026,11 @@ impl<'ast, T> Conditional<'ast, T> for BooleanExpression<'ast, T> {
|
||||||
condition: BooleanExpression<'ast, T>,
|
condition: BooleanExpression<'ast, T>,
|
||||||
consequence: Self,
|
consequence: Self,
|
||||||
alternative: Self,
|
alternative: Self,
|
||||||
kind: ConditionalKind,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
BooleanExpression::Conditional(ConditionalExpression::new(
|
BooleanExpression::Conditional(ConditionalExpression::new(
|
||||||
condition,
|
condition,
|
||||||
consequence,
|
consequence,
|
||||||
alternative,
|
alternative,
|
||||||
kind,
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2067,7 +2040,6 @@ impl<'ast, T> Conditional<'ast, T> for UExpression<'ast, T> {
|
||||||
condition: BooleanExpression<'ast, T>,
|
condition: BooleanExpression<'ast, T>,
|
||||||
consequence: Self,
|
consequence: Self,
|
||||||
alternative: Self,
|
alternative: Self,
|
||||||
kind: ConditionalKind,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let bitwidth = consequence.bitwidth;
|
let bitwidth = consequence.bitwidth;
|
||||||
|
|
||||||
|
@ -2075,7 +2047,6 @@ impl<'ast, T> Conditional<'ast, T> for UExpression<'ast, T> {
|
||||||
condition,
|
condition,
|
||||||
consequence,
|
consequence,
|
||||||
alternative,
|
alternative,
|
||||||
kind,
|
|
||||||
))
|
))
|
||||||
.annotate(bitwidth)
|
.annotate(bitwidth)
|
||||||
}
|
}
|
||||||
|
@ -2086,7 +2057,6 @@ impl<'ast, T: Clone> Conditional<'ast, T> for ArrayExpression<'ast, T> {
|
||||||
condition: BooleanExpression<'ast, T>,
|
condition: BooleanExpression<'ast, T>,
|
||||||
consequence: Self,
|
consequence: Self,
|
||||||
alternative: Self,
|
alternative: Self,
|
||||||
kind: ConditionalKind,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let ty = consequence.inner_type().clone();
|
let ty = consequence.inner_type().clone();
|
||||||
let size = consequence.size();
|
let size = consequence.size();
|
||||||
|
@ -2094,7 +2064,6 @@ impl<'ast, T: Clone> Conditional<'ast, T> for ArrayExpression<'ast, T> {
|
||||||
condition,
|
condition,
|
||||||
consequence,
|
consequence,
|
||||||
alternative,
|
alternative,
|
||||||
kind,
|
|
||||||
))
|
))
|
||||||
.annotate(ty, size)
|
.annotate(ty, size)
|
||||||
}
|
}
|
||||||
|
@ -2105,14 +2074,12 @@ impl<'ast, T: Clone> Conditional<'ast, T> for StructExpression<'ast, T> {
|
||||||
condition: BooleanExpression<'ast, T>,
|
condition: BooleanExpression<'ast, T>,
|
||||||
consequence: Self,
|
consequence: Self,
|
||||||
alternative: Self,
|
alternative: Self,
|
||||||
kind: ConditionalKind,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let ty = consequence.ty().clone();
|
let ty = consequence.ty().clone();
|
||||||
StructExpressionInner::Conditional(ConditionalExpression::new(
|
StructExpressionInner::Conditional(ConditionalExpression::new(
|
||||||
condition,
|
condition,
|
||||||
consequence,
|
consequence,
|
||||||
alternative,
|
alternative,
|
||||||
kind,
|
|
||||||
))
|
))
|
||||||
.annotate(ty)
|
.annotate(ty)
|
||||||
}
|
}
|
||||||
|
@ -2123,14 +2090,12 @@ impl<'ast, T: Clone> Conditional<'ast, T> for TupleExpression<'ast, T> {
|
||||||
condition: BooleanExpression<'ast, T>,
|
condition: BooleanExpression<'ast, T>,
|
||||||
consequence: Self,
|
consequence: Self,
|
||||||
alternative: Self,
|
alternative: Self,
|
||||||
kind: ConditionalKind,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let ty = consequence.ty().clone();
|
let ty = consequence.ty().clone();
|
||||||
TupleExpressionInner::Conditional(ConditionalExpression::new(
|
TupleExpressionInner::Conditional(ConditionalExpression::new(
|
||||||
condition,
|
condition,
|
||||||
consequence,
|
consequence,
|
||||||
alternative,
|
alternative,
|
||||||
kind,
|
|
||||||
))
|
))
|
||||||
.annotate(ty)
|
.annotate(ty)
|
||||||
}
|
}
|
||||||
|
|
|
@ -822,7 +822,6 @@ pub fn fold_conditional_expression<
|
||||||
f.fold_boolean_expression(*e.condition)?,
|
f.fold_boolean_expression(*e.condition)?,
|
||||||
e.consequence.fold(f)?,
|
e.consequence.fold(f)?,
|
||||||
e.alternative.fold(f)?,
|
e.alternative.fold(f)?,
|
||||||
e.kind,
|
|
||||||
),
|
),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,17 +54,16 @@ vis = { vis_private | vis_public }
|
||||||
|
|
||||||
// Statements
|
// Statements
|
||||||
statement = { (iteration_statement // does not require semicolon
|
statement = { (iteration_statement // does not require semicolon
|
||||||
| (return_statement
|
| ((return_statement
|
||||||
| definition_statement
|
| definition_statement
|
||||||
| expression_statement
|
| assertion_statement
|
||||||
) ~ semicolon
|
) ~ semicolon)) ~ NEWLINE* }
|
||||||
) ~ NEWLINE* }
|
|
||||||
|
|
||||||
block_statement = _{ "{" ~ NEWLINE* ~ statement* ~ NEWLINE* ~ "}" }
|
block_statement = _{ "{" ~ NEWLINE* ~ statement* ~ NEWLINE* ~ "}" }
|
||||||
iteration_statement = { "for" ~ typed_identifier ~ "in" ~ expression ~ ".." ~ expression ~ block_statement }
|
iteration_statement = { "for" ~ typed_identifier ~ "in" ~ expression ~ ".." ~ expression ~ block_statement }
|
||||||
return_statement = { "return" ~ expression_list}
|
return_statement = { "return" ~ expression_list }
|
||||||
definition_statement = { typed_identifier_or_assignee_list ~ "=" ~ expression } // declare and assign, so only identifiers are allowed, unlike `assignment_statement`
|
definition_statement = { typed_identifier_or_assignee_list ~ "=" ~ expression } // declare and assign, so only identifiers are allowed, unlike `assignment_statement`
|
||||||
expression_statement = {"assert" ~ "(" ~ expression ~ ("," ~ quoted_string)? ~ ")"}
|
assertion_statement = {"assert" ~ "(" ~ expression ~ ("," ~ quoted_string)? ~ ")"}
|
||||||
|
|
||||||
typed_identifier_or_assignee_list = _{ typed_identifier_or_assignee ~ ("," ~ typed_identifier_or_assignee)* }
|
typed_identifier_or_assignee_list = _{ typed_identifier_or_assignee ~ ("," ~ typed_identifier_or_assignee)* }
|
||||||
typed_identifier_or_assignee = { typed_identifier | assignee } // we don't use { ty? ~ identifier } as with a single token, it gets parsed as `ty` but we want `identifier`
|
typed_identifier_or_assignee = { typed_identifier | assignee } // we don't use { ty? ~ identifier } as with a single token, it gets parsed as `ty` but we want `identifier`
|
||||||
|
@ -76,7 +75,7 @@ expression = { unaried_term ~ (op_binary ~ unaried_term)* }
|
||||||
unaried_term = { op_unary? ~ powered_term }
|
unaried_term = { op_unary? ~ powered_term }
|
||||||
powered_term = { postfixed_term ~ (op_pow ~ exponent_expression)? }
|
powered_term = { postfixed_term ~ (op_pow ~ exponent_expression)? }
|
||||||
postfixed_term = { term ~ access* }
|
postfixed_term = { term ~ access* }
|
||||||
term = { ("(" ~ expression ~ ")") | inline_tuple_expression | inline_struct_expression | if_else_expression | primary_expression | inline_array_expression | array_initializer_expression }
|
term = { ("(" ~ expression ~ ")") | inline_tuple_expression | inline_struct_expression | primary_expression | inline_array_expression | array_initializer_expression }
|
||||||
spread = { "..." ~ expression }
|
spread = { "..." ~ expression }
|
||||||
range = { from_expression? ~ ".." ~ to_expression? }
|
range = { from_expression? ~ ".." ~ to_expression? }
|
||||||
from_expression = { expression }
|
from_expression = { expression }
|
||||||
|
@ -87,8 +86,6 @@ inline_tuple_empty_expression_inner = _{ "" }
|
||||||
inline_tuple_single_expression_inner = _{ expression ~ "," }
|
inline_tuple_single_expression_inner = _{ expression ~ "," }
|
||||||
inline_tuple_multiple_expression_inner = _{ expression ~ ("," ~ expression)+ ~ ","? }
|
inline_tuple_multiple_expression_inner = _{ expression ~ ("," ~ expression)+ ~ ","? }
|
||||||
|
|
||||||
if_else_expression = { "if" ~ expression ~ "then" ~ expression ~ "else" ~ expression ~ "fi"}
|
|
||||||
|
|
||||||
access = { array_access | call_access | dot_access }
|
access = { array_access | call_access | dot_access }
|
||||||
array_access = { "[" ~ range_or_expression ~ "]" }
|
array_access = { "[" ~ range_or_expression ~ "]" }
|
||||||
call_access = { ("::" ~ explicit_generics)? ~ "(" ~ arguments ~ ")" }
|
call_access = { ("::" ~ explicit_generics)? ~ "(" ~ arguments ~ ")" }
|
||||||
|
|
|
@ -13,7 +13,7 @@ pub use ast::{
|
||||||
BinaryOperator, CallAccess, ConstantDefinition, ConstantGenericValue, DecimalLiteralExpression,
|
BinaryOperator, CallAccess, ConstantDefinition, ConstantGenericValue, DecimalLiteralExpression,
|
||||||
DecimalNumber, DecimalSuffix, DefinitionStatement, ExplicitGenerics, Expression, FieldType,
|
DecimalNumber, DecimalSuffix, DefinitionStatement, ExplicitGenerics, Expression, FieldType,
|
||||||
File, FromExpression, FunctionDefinition, HexLiteralExpression, HexNumberExpression,
|
File, FromExpression, FunctionDefinition, HexLiteralExpression, HexNumberExpression,
|
||||||
IdentifierExpression, IdentifierOrDecimal, IfElseExpression, ImportDirective, ImportSymbol,
|
IdentifierExpression, IdentifierOrDecimal, ImportDirective, ImportSymbol,
|
||||||
InlineArrayExpression, InlineStructExpression, InlineStructMember, InlineTupleExpression,
|
InlineArrayExpression, InlineStructExpression, InlineStructMember, InlineTupleExpression,
|
||||||
IterationStatement, LiteralExpression, Parameter, PostfixExpression, Range, RangeOrExpression,
|
IterationStatement, LiteralExpression, Parameter, PostfixExpression, Range, RangeOrExpression,
|
||||||
ReturnStatement, Span, Spread, SpreadOrExpression, Statement, StructDefinition, StructField,
|
ReturnStatement, Span, Spread, SpreadOrExpression, Statement, StructDefinition, StructField,
|
||||||
|
@ -384,7 +384,7 @@ mod ast {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, FromPest, PartialEq, Clone)]
|
#[derive(Debug, FromPest, PartialEq, Clone)]
|
||||||
#[pest_ast(rule(Rule::expression_statement))]
|
#[pest_ast(rule(Rule::assertion_statement))]
|
||||||
pub struct AssertionStatement<'ast> {
|
pub struct AssertionStatement<'ast> {
|
||||||
pub expression: Expression<'ast>,
|
pub expression: Expression<'ast>,
|
||||||
pub message: Option<AnyString<'ast>>,
|
pub message: Option<AnyString<'ast>>,
|
||||||
|
@ -437,7 +437,6 @@ mod ast {
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub enum Expression<'ast> {
|
pub enum Expression<'ast> {
|
||||||
Ternary(TernaryExpression<'ast>),
|
Ternary(TernaryExpression<'ast>),
|
||||||
IfElse(IfElseExpression<'ast>),
|
|
||||||
Binary(BinaryExpression<'ast>),
|
Binary(BinaryExpression<'ast>),
|
||||||
Unary(UnaryExpression<'ast>),
|
Unary(UnaryExpression<'ast>),
|
||||||
Postfix(PostfixExpression<'ast>),
|
Postfix(PostfixExpression<'ast>),
|
||||||
|
@ -454,7 +453,6 @@ mod ast {
|
||||||
pub enum Term<'ast> {
|
pub enum Term<'ast> {
|
||||||
Expression(Expression<'ast>),
|
Expression(Expression<'ast>),
|
||||||
InlineStruct(InlineStructExpression<'ast>),
|
InlineStruct(InlineStructExpression<'ast>),
|
||||||
IfElse(IfElseExpression<'ast>),
|
|
||||||
Primary(PrimaryExpression<'ast>),
|
Primary(PrimaryExpression<'ast>),
|
||||||
InlineArray(InlineArrayExpression<'ast>),
|
InlineArray(InlineArrayExpression<'ast>),
|
||||||
InlineTuple(InlineTupleExpression<'ast>),
|
InlineTuple(InlineTupleExpression<'ast>),
|
||||||
|
@ -571,7 +569,6 @@ mod ast {
|
||||||
fn from(t: Term<'ast>) -> Self {
|
fn from(t: Term<'ast>) -> Self {
|
||||||
match t {
|
match t {
|
||||||
Term::Expression(e) => e,
|
Term::Expression(e) => e,
|
||||||
Term::IfElse(e) => Expression::IfElse(e),
|
|
||||||
Term::Primary(e) => e.into(),
|
Term::Primary(e) => e.into(),
|
||||||
Term::InlineArray(e) => Expression::InlineArray(e),
|
Term::InlineArray(e) => Expression::InlineArray(e),
|
||||||
Term::InlineTuple(e) => Expression::InlineTuple(e),
|
Term::InlineTuple(e) => Expression::InlineTuple(e),
|
||||||
|
@ -814,31 +811,7 @@ mod ast {
|
||||||
pub span: Span<'ast>,
|
pub span: Span<'ast>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, FromPest, PartialEq, Clone)]
|
|
||||||
#[pest_ast(rule(Rule::if_else_expression))]
|
|
||||||
pub struct IfElseExpression<'ast> {
|
|
||||||
pub condition: Box<Expression<'ast>>,
|
|
||||||
pub consequence: Box<Expression<'ast>>,
|
|
||||||
pub alternative: Box<Expression<'ast>>,
|
|
||||||
#[pest_ast(outer())]
|
|
||||||
pub span: Span<'ast>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ast> Expression<'ast> {
|
impl<'ast> Expression<'ast> {
|
||||||
pub fn if_else(
|
|
||||||
condition: Box<Expression<'ast>>,
|
|
||||||
consequence: Box<Expression<'ast>>,
|
|
||||||
alternative: Box<Expression<'ast>>,
|
|
||||||
span: Span<'ast>,
|
|
||||||
) -> Self {
|
|
||||||
Expression::IfElse(IfElseExpression {
|
|
||||||
condition,
|
|
||||||
consequence,
|
|
||||||
alternative,
|
|
||||||
span,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ternary(
|
pub fn ternary(
|
||||||
condition: Box<Expression<'ast>>,
|
condition: Box<Expression<'ast>>,
|
||||||
consequence: Box<Expression<'ast>>,
|
consequence: Box<Expression<'ast>>,
|
||||||
|
@ -873,7 +846,6 @@ mod ast {
|
||||||
Expression::Identifier(i) => &i.span,
|
Expression::Identifier(i) => &i.span,
|
||||||
Expression::Literal(c) => c.span(),
|
Expression::Literal(c) => c.span(),
|
||||||
Expression::Ternary(t) => &t.span,
|
Expression::Ternary(t) => &t.span,
|
||||||
Expression::IfElse(ie) => &ie.span,
|
|
||||||
Expression::Postfix(p) => &p.span,
|
Expression::Postfix(p) => &p.span,
|
||||||
Expression::InlineArray(a) => &a.span,
|
Expression::InlineArray(a) => &a.span,
|
||||||
Expression::InlineStruct(s) => &s.span,
|
Expression::InlineStruct(s) => &s.span,
|
||||||
|
@ -1291,7 +1263,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn ternary() {
|
fn ternary() {
|
||||||
let source = r#"import "foo"
|
let source = r#"import "foo"
|
||||||
def main() -> (field): return if 1 then 2 else 3 fi
|
def main() -> (field): return 1 ? 2 : 3
|
||||||
"#;
|
"#;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
generate_ast(source),
|
generate_ast(source),
|
||||||
|
@ -1317,7 +1289,7 @@ mod tests {
|
||||||
span: Span::new(source, 44, 49).unwrap()
|
span: Span::new(source, 44, 49).unwrap()
|
||||||
}))],
|
}))],
|
||||||
statements: vec![Statement::Return(ReturnStatement {
|
statements: vec![Statement::Return(ReturnStatement {
|
||||||
expressions: vec![Expression::if_else(
|
expressions: vec![Expression::ternary(
|
||||||
Box::new(Expression::Literal(LiteralExpression::DecimalLiteral(
|
Box::new(Expression::Literal(LiteralExpression::DecimalLiteral(
|
||||||
DecimalLiteralExpression {
|
DecimalLiteralExpression {
|
||||||
suffix: None,
|
suffix: None,
|
||||||
|
|
Loading…
Reference in a new issue