Merge branch 'develop' of github.com:Zokrates/ZoKrates into output-structured-marlin-artifacts
This commit is contained in:
commit
7846fecdb1
47 changed files with 878 additions and 494 deletions
1
changelogs/unreleased/1010-dark64
Normal file
1
changelogs/unreleased/1010-dark64
Normal file
|
@ -0,0 +1 @@
|
|||
Introduce ternary operator
|
1
changelogs/unreleased/1012-dark64
Normal file
1
changelogs/unreleased/1012-dark64
Normal file
|
@ -0,0 +1 @@
|
|||
Add optional message to assert statement
|
1
changelogs/unreleased/1032-schaeff
Normal file
1
changelogs/unreleased/1032-schaeff
Normal file
|
@ -0,0 +1 @@
|
|||
Fail at compile time when complex types are known not to be equal
|
1
changelogs/unreleased/1034-schaeff
Normal file
1
changelogs/unreleased/1034-schaeff
Normal file
|
@ -0,0 +1 @@
|
|||
Add Marlin proving scheme to the backend table in the book
|
|
@ -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.
|
||||
|
|
|
@ -16,10 +16,12 @@ The following table lists the precedence and associativity of all operators. Ope
|
|||
| `!=`<br>`==`<br> | Not Equal<br>Equal<br> | ✓ | ✓ | ✓ | Left | |
|
||||
| `&&` | Boolean AND | | | ✓ | Left | |
|
||||
| <code>||</code> | Boolean OR | | | ✓ | Left | |
|
||||
| `if c then x else y fi` | Conditional expression | ✓ | ✓ | ✓ | Right | |
|
||||
| `if c then x else y fi` | Conditional expression | ✓ | ✓ | ✓ | Right | [^4] |
|
||||
|
||||
[^1]: The exponent must be a compile-time constant of type `u32`
|
||||
|
||||
[^2]: The right operand must be a compile time constant of type `u32`
|
||||
|
||||
[^3]: Both operands are asserted to be strictly lower than the biggest power of 2 lower than `p/2`, unless one of them can be determined to be a compile-time constant
|
||||
[^3]: Both operands are asserted to be strictly lower than the biggest power of 2 lower than `p/2`, unless one of them can be determined to be a compile-time constant
|
||||
|
||||
[^4]: Conditional expression can also be written using a ternary operator: `c ? x : y`
|
|
@ -49,7 +49,7 @@ ZoKrates supports multiple backends. The options are the following:
|
|||
| ---- | -------- | --------------- | ------ |
|
||||
| Bellman | `--backend bellman` | G16 | ALTBN_128, BLS12_381 |
|
||||
| Libsnark | `--backend libsnark` | GM17, PGHR13 | ALTBN_128 |
|
||||
| Ark | `--backend ark` | GM17 | ALTBN_128, BLS12_377, BW6_761 |
|
||||
| Ark | `--backend ark` | GM17, MARLIN | ALTBN_128, BLS12_377, BW6_761 |
|
||||
|
||||
Default: `bellman`
|
||||
|
||||
|
|
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
|
|
@ -0,0 +1,3 @@
|
|||
def main():
|
||||
assert([1f] == [2f])
|
||||
return
|
|
@ -0,0 +1,5 @@
|
|||
def main(bool a) -> field:
|
||||
// ternary expression should be wrapped in parentheses 1 + (a ? 2 : 3)
|
||||
// otherwise the whole expression is parsed as (1 + a) ? 2 : 3
|
||||
field x = 1 + a ? 2 : 3
|
||||
return x
|
|
@ -346,8 +346,11 @@ impl<'ast> From<pest::AssertionStatement<'ast>> for absy::StatementNode<'ast> {
|
|||
fn from(statement: pest::AssertionStatement<'ast>) -> absy::StatementNode<'ast> {
|
||||
use crate::absy::NodeValue;
|
||||
|
||||
absy::Statement::Assertion(absy::ExpressionNode::from(statement.expression))
|
||||
.span(statement.span)
|
||||
absy::Statement::Assertion(
|
||||
absy::ExpressionNode::from(statement.expression),
|
||||
statement.message.map(|m| m.value),
|
||||
)
|
||||
.span(statement.span)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -375,6 +378,7 @@ impl<'ast> From<pest::Expression<'ast>> for absy::ExpressionNode<'ast> {
|
|||
match expression {
|
||||
pest::Expression::Binary(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::Identifier(e) => absy::ExpressionNode::from(e),
|
||||
pest::Expression::Postfix(e) => absy::ExpressionNode::from(e),
|
||||
|
@ -475,13 +479,27 @@ 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> {
|
||||
fn from(expression: pest::TernaryExpression<'ast>) -> absy::ExpressionNode<'ast> {
|
||||
use crate::absy::NodeValue;
|
||||
absy::Expression::IfElse(
|
||||
box absy::ExpressionNode::from(*expression.first),
|
||||
box absy::ExpressionNode::from(*expression.second),
|
||||
box absy::ExpressionNode::from(*expression.third),
|
||||
absy::Expression::Conditional(
|
||||
box absy::ExpressionNode::from(*expression.condition),
|
||||
box absy::ExpressionNode::from(*expression.consequence),
|
||||
box absy::ExpressionNode::from(*expression.alternative),
|
||||
absy::ConditionalKind::Ternary,
|
||||
)
|
||||
.span(expression.span)
|
||||
}
|
||||
|
|
|
@ -337,7 +337,7 @@ pub enum Statement<'ast> {
|
|||
Return(ExpressionListNode<'ast>),
|
||||
Declaration(VariableNode<'ast>),
|
||||
Definition(AssigneeNode<'ast>, ExpressionNode<'ast>),
|
||||
Assertion(ExpressionNode<'ast>),
|
||||
Assertion(ExpressionNode<'ast>, Option<String>),
|
||||
For(
|
||||
VariableNode<'ast>,
|
||||
ExpressionNode<'ast>,
|
||||
|
@ -355,7 +355,13 @@ impl<'ast> fmt::Display for Statement<'ast> {
|
|||
Statement::Return(ref expr) => write!(f, "return {}", expr),
|
||||
Statement::Declaration(ref var) => write!(f, "{}", var),
|
||||
Statement::Definition(ref lhs, ref rhs) => write!(f, "{} = {}", lhs, rhs),
|
||||
Statement::Assertion(ref e) => write!(f, "assert({})", e),
|
||||
Statement::Assertion(ref e, ref message) => {
|
||||
write!(f, "assert({}", e)?;
|
||||
match message {
|
||||
Some(m) => write!(f, ", \"{}\")", m),
|
||||
None => write!(f, ")"),
|
||||
}
|
||||
}
|
||||
Statement::For(ref var, ref start, ref stop, ref list) => {
|
||||
writeln!(f, "for {} in {}..{} do", var, start, stop)?;
|
||||
for l in list {
|
||||
|
@ -454,6 +460,12 @@ impl<'ast> fmt::Display for Range<'ast> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum ConditionalKind {
|
||||
IfElse,
|
||||
Ternary,
|
||||
}
|
||||
|
||||
/// An expression
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Expression<'ast> {
|
||||
|
@ -473,10 +485,11 @@ pub enum Expression<'ast> {
|
|||
Pow(Box<ExpressionNode<'ast>>, Box<ExpressionNode<'ast>>),
|
||||
Neg(Box<ExpressionNode<'ast>>),
|
||||
Pos(Box<ExpressionNode<'ast>>),
|
||||
IfElse(
|
||||
Conditional(
|
||||
Box<ExpressionNode<'ast>>,
|
||||
Box<ExpressionNode<'ast>>,
|
||||
Box<ExpressionNode<'ast>>,
|
||||
ConditionalKind,
|
||||
),
|
||||
FunctionCall(
|
||||
Box<ExpressionNode<'ast>>,
|
||||
|
@ -524,11 +537,18 @@ impl<'ast> fmt::Display for Expression<'ast> {
|
|||
Expression::Neg(ref e) => write!(f, "(-{})", e),
|
||||
Expression::Pos(ref e) => write!(f, "(+{})", e),
|
||||
Expression::BooleanConstant(b) => write!(f, "{}", b),
|
||||
Expression::IfElse(ref condition, ref consequent, ref alternative) => write!(
|
||||
f,
|
||||
"if {} then {} else {} fi",
|
||||
condition, consequent, alternative
|
||||
),
|
||||
Expression::Conditional(ref condition, ref consequent, ref alternative, ref kind) => {
|
||||
match kind {
|
||||
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) => {
|
||||
if let Some(g) = g {
|
||||
write!(
|
||||
|
|
|
@ -43,18 +43,18 @@ pub enum RuntimeError {
|
|||
Euclidean,
|
||||
ShaXor,
|
||||
Division,
|
||||
Source,
|
||||
SourceAssertion(String),
|
||||
ArgumentBitness,
|
||||
SelectRangeCheck,
|
||||
}
|
||||
|
||||
impl RuntimeError {
|
||||
fn is_malicious(&self) -> bool {
|
||||
pub(crate) fn is_malicious(&self) -> bool {
|
||||
use RuntimeError::*;
|
||||
|
||||
!matches!(
|
||||
self,
|
||||
Source | Inverse | LtSum | SelectRangeCheck | ArgumentBitness
|
||||
SourceAssertion(_) | Inverse | LtSum | SelectRangeCheck | ArgumentBitness
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -87,19 +87,12 @@ impl fmt::Display for RuntimeError {
|
|||
Euclidean => "Euclidean check failed",
|
||||
ShaXor => "Internal Sha check failed",
|
||||
Division => "Division check failed",
|
||||
Source => "User assertion failed",
|
||||
SourceAssertion(m) => m.as_str(),
|
||||
ArgumentBitness => "Argument bitness check failed",
|
||||
SelectRangeCheck => "Out of bounds array access",
|
||||
};
|
||||
|
||||
write!(f, "{}", msg)?;
|
||||
|
||||
if self.is_malicious() {
|
||||
writeln!(f)?;
|
||||
write!(f, "The default ZoKrates interpreter should not yield this error. Please open an issue")?;
|
||||
}
|
||||
|
||||
write!(f, "")
|
||||
write!(f, "{}", msg)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ use crate::ir::Interpreter;
|
|||
|
||||
use crate::compile::CompileConfig;
|
||||
use crate::embed::FlatEmbed;
|
||||
use crate::flat_absy::*;
|
||||
use crate::flat_absy::{RuntimeError, *};
|
||||
use crate::solvers::Solver;
|
||||
use crate::zir::types::{Type, UBitwidth};
|
||||
use crate::zir::*;
|
||||
|
@ -146,6 +146,15 @@ impl<T: Field> FlatUExpression<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<crate::zir::RuntimeError> for RuntimeError {
|
||||
fn from(error: crate::zir::RuntimeError) -> Self {
|
||||
match error {
|
||||
crate::zir::RuntimeError::SourceAssertion(s) => RuntimeError::SourceAssertion(s),
|
||||
crate::zir::RuntimeError::SelectRangeCheck => RuntimeError::SelectRangeCheck,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast, T: Field> Flattener<'ast, T> {
|
||||
pub fn flatten(p: ZirProgram<'ast, T>, config: &CompileConfig) -> FlatProg<T> {
|
||||
Flattener::new(config).flatten_program(p)
|
||||
|
@ -2370,13 +2379,13 @@ impl<'ast, T: Field> Flattener<'ast, T> {
|
|||
.insert(FlatExpression::Identifier(var), bits);
|
||||
}
|
||||
}
|
||||
ZirStatement::Assertion(e) => {
|
||||
ZirStatement::Assertion(e, error) => {
|
||||
match e {
|
||||
BooleanExpression::And(..) => {
|
||||
for boolean in e.into_conjunction_iterator() {
|
||||
self.flatten_statement(
|
||||
statements_flattened,
|
||||
ZirStatement::Assertion(boolean),
|
||||
ZirStatement::Assertion(boolean, error.clone()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -2384,7 +2393,12 @@ impl<'ast, T: Field> Flattener<'ast, T> {
|
|||
let lhs = self.flatten_field_expression(statements_flattened, lhs);
|
||||
let rhs = self.flatten_field_expression(statements_flattened, rhs);
|
||||
|
||||
self.flatten_equality_assertion(statements_flattened, lhs, rhs)
|
||||
self.flatten_equality_assertion(
|
||||
statements_flattened,
|
||||
lhs,
|
||||
rhs,
|
||||
error.into(),
|
||||
)
|
||||
}
|
||||
BooleanExpression::UintEq(box lhs, box rhs) => {
|
||||
let lhs = self
|
||||
|
@ -2394,13 +2408,23 @@ impl<'ast, T: Field> Flattener<'ast, T> {
|
|||
.flatten_uint_expression(statements_flattened, rhs)
|
||||
.get_field_unchecked();
|
||||
|
||||
self.flatten_equality_assertion(statements_flattened, lhs, rhs)
|
||||
self.flatten_equality_assertion(
|
||||
statements_flattened,
|
||||
lhs,
|
||||
rhs,
|
||||
error.into(),
|
||||
)
|
||||
}
|
||||
BooleanExpression::BoolEq(box lhs, box rhs) => {
|
||||
let lhs = self.flatten_boolean_expression(statements_flattened, lhs);
|
||||
let rhs = self.flatten_boolean_expression(statements_flattened, rhs);
|
||||
|
||||
self.flatten_equality_assertion(statements_flattened, lhs, rhs)
|
||||
self.flatten_equality_assertion(
|
||||
statements_flattened,
|
||||
lhs,
|
||||
rhs,
|
||||
error.into(),
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
// naive approach: flatten the boolean to a single field element and constrain it to 1
|
||||
|
@ -2410,14 +2434,14 @@ impl<'ast, T: Field> Flattener<'ast, T> {
|
|||
statements_flattened.push(FlatStatement::Condition(
|
||||
e,
|
||||
FlatExpression::Number(T::from(1)),
|
||||
RuntimeError::Source,
|
||||
error.into(),
|
||||
));
|
||||
} else {
|
||||
// swap so that left side is linear
|
||||
statements_flattened.push(FlatStatement::Condition(
|
||||
FlatExpression::Number(T::from(1)),
|
||||
e,
|
||||
RuntimeError::Source,
|
||||
error.into(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -2529,6 +2553,7 @@ impl<'ast, T: Field> Flattener<'ast, T> {
|
|||
statements_flattened: &mut FlatStatements<T>,
|
||||
lhs: FlatExpression<T>,
|
||||
rhs: FlatExpression<T>,
|
||||
error: RuntimeError,
|
||||
) {
|
||||
let (lhs, rhs) = match (lhs, rhs) {
|
||||
(FlatExpression::Mult(box x, box y), z) | (z, FlatExpression::Mult(box x, box y)) => (
|
||||
|
@ -2546,7 +2571,7 @@ impl<'ast, T: Field> Flattener<'ast, T> {
|
|||
),
|
||||
),
|
||||
};
|
||||
statements_flattened.push(FlatStatement::Condition(lhs, rhs, RuntimeError::Source));
|
||||
statements_flattened.push(FlatStatement::Condition(lhs, rhs, error));
|
||||
}
|
||||
|
||||
/// Identifies a non-linear expression by assigning it to a new identifier.
|
||||
|
@ -2653,6 +2678,7 @@ impl<'ast, T: Field> Flattener<'ast, T> {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::zir;
|
||||
use crate::zir::types::Signature;
|
||||
use crate::zir::types::Type;
|
||||
use zokrates_field::Bn128Field;
|
||||
|
@ -2679,10 +2705,13 @@ mod tests {
|
|||
Variable::boolean("y".into()),
|
||||
BooleanExpression::Value(true).into(),
|
||||
),
|
||||
ZirStatement::Assertion(BooleanExpression::BoolEq(
|
||||
box BooleanExpression::Identifier("x".into()),
|
||||
box BooleanExpression::Identifier("y".into()),
|
||||
)),
|
||||
ZirStatement::Assertion(
|
||||
BooleanExpression::BoolEq(
|
||||
box BooleanExpression::Identifier("x".into()),
|
||||
box BooleanExpression::Identifier("y".into()),
|
||||
),
|
||||
zir::RuntimeError::mock(),
|
||||
),
|
||||
],
|
||||
signature: Signature {
|
||||
inputs: vec![],
|
||||
|
@ -2711,7 +2740,7 @@ mod tests {
|
|||
box FlatExpression::Identifier(FlatVariable::new(0)),
|
||||
box FlatExpression::Number(Bn128Field::from(1)),
|
||||
),
|
||||
RuntimeError::Source,
|
||||
zir::RuntimeError::mock().into(),
|
||||
),
|
||||
],
|
||||
};
|
||||
|
@ -2741,13 +2770,16 @@ mod tests {
|
|||
Variable::field_element("y"),
|
||||
FieldElementExpression::Number(Bn128Field::from(2)).into(),
|
||||
),
|
||||
ZirStatement::Assertion(BooleanExpression::FieldEq(
|
||||
box FieldElementExpression::Add(
|
||||
box FieldElementExpression::Identifier("x".into()),
|
||||
box FieldElementExpression::Number(Bn128Field::from(1)),
|
||||
ZirStatement::Assertion(
|
||||
BooleanExpression::FieldEq(
|
||||
box FieldElementExpression::Add(
|
||||
box FieldElementExpression::Identifier("x".into()),
|
||||
box FieldElementExpression::Number(Bn128Field::from(1)),
|
||||
),
|
||||
box FieldElementExpression::Identifier("y".into()),
|
||||
),
|
||||
box FieldElementExpression::Identifier("y".into()),
|
||||
)),
|
||||
zir::RuntimeError::mock(),
|
||||
),
|
||||
],
|
||||
signature: Signature {
|
||||
inputs: vec![],
|
||||
|
@ -2779,7 +2811,7 @@ mod tests {
|
|||
),
|
||||
box FlatExpression::Number(Bn128Field::from(1)),
|
||||
),
|
||||
RuntimeError::Source,
|
||||
zir::RuntimeError::mock().into(),
|
||||
),
|
||||
],
|
||||
};
|
||||
|
@ -2811,12 +2843,15 @@ mod tests {
|
|||
.metadata(metadata.clone()),
|
||||
),
|
||||
),
|
||||
ZirStatement::Assertion(BooleanExpression::UintEq(
|
||||
box UExpressionInner::Identifier("x".into())
|
||||
.annotate(32)
|
||||
.metadata(metadata.clone()),
|
||||
box UExpressionInner::Value(42).annotate(32).metadata(metadata),
|
||||
)),
|
||||
ZirStatement::Assertion(
|
||||
BooleanExpression::UintEq(
|
||||
box UExpressionInner::Identifier("x".into())
|
||||
.annotate(32)
|
||||
.metadata(metadata.clone()),
|
||||
box UExpressionInner::Value(42).annotate(32).metadata(metadata),
|
||||
),
|
||||
zir::RuntimeError::mock(),
|
||||
),
|
||||
],
|
||||
signature: Signature {
|
||||
inputs: vec![],
|
||||
|
@ -2841,7 +2876,7 @@ mod tests {
|
|||
box FlatExpression::Identifier(FlatVariable::new(0)),
|
||||
box FlatExpression::Number(Bn128Field::from(1)),
|
||||
),
|
||||
RuntimeError::Source,
|
||||
zir::RuntimeError::mock().into(),
|
||||
),
|
||||
],
|
||||
};
|
||||
|
@ -2871,10 +2906,13 @@ mod tests {
|
|||
Variable::field_element("y"),
|
||||
FieldElementExpression::Number(Bn128Field::from(2)).into(),
|
||||
),
|
||||
ZirStatement::Assertion(BooleanExpression::FieldEq(
|
||||
box FieldElementExpression::Identifier("x".into()),
|
||||
box FieldElementExpression::Identifier("y".into()),
|
||||
)),
|
||||
ZirStatement::Assertion(
|
||||
BooleanExpression::FieldEq(
|
||||
box FieldElementExpression::Identifier("x".into()),
|
||||
box FieldElementExpression::Identifier("y".into()),
|
||||
),
|
||||
zir::RuntimeError::mock(),
|
||||
),
|
||||
],
|
||||
signature: Signature {
|
||||
inputs: vec![],
|
||||
|
@ -2903,7 +2941,7 @@ mod tests {
|
|||
box FlatExpression::Identifier(FlatVariable::new(0)),
|
||||
box FlatExpression::Number(Bn128Field::from(1)),
|
||||
),
|
||||
RuntimeError::Source,
|
||||
zir::RuntimeError::mock().into(),
|
||||
),
|
||||
],
|
||||
};
|
||||
|
@ -2939,13 +2977,16 @@ mod tests {
|
|||
Variable::field_element("z"),
|
||||
FieldElementExpression::Number(Bn128Field::from(4)).into(),
|
||||
),
|
||||
ZirStatement::Assertion(BooleanExpression::FieldEq(
|
||||
box FieldElementExpression::Mult(
|
||||
box FieldElementExpression::Identifier("x".into()),
|
||||
box FieldElementExpression::Identifier("y".into()),
|
||||
ZirStatement::Assertion(
|
||||
BooleanExpression::FieldEq(
|
||||
box FieldElementExpression::Mult(
|
||||
box FieldElementExpression::Identifier("x".into()),
|
||||
box FieldElementExpression::Identifier("y".into()),
|
||||
),
|
||||
box FieldElementExpression::Identifier("z".into()),
|
||||
),
|
||||
box FieldElementExpression::Identifier("z".into()),
|
||||
)),
|
||||
zir::RuntimeError::mock(),
|
||||
),
|
||||
],
|
||||
signature: Signature {
|
||||
inputs: vec![],
|
||||
|
@ -2978,7 +3019,7 @@ mod tests {
|
|||
box FlatExpression::Identifier(FlatVariable::new(0)),
|
||||
box FlatExpression::Identifier(FlatVariable::new(1)),
|
||||
),
|
||||
RuntimeError::Source,
|
||||
zir::RuntimeError::mock().into(),
|
||||
),
|
||||
],
|
||||
};
|
||||
|
@ -3014,13 +3055,16 @@ mod tests {
|
|||
Variable::field_element("z"),
|
||||
FieldElementExpression::Number(Bn128Field::from(4)).into(),
|
||||
),
|
||||
ZirStatement::Assertion(BooleanExpression::FieldEq(
|
||||
box FieldElementExpression::Identifier("z".into()),
|
||||
box FieldElementExpression::Mult(
|
||||
box FieldElementExpression::Identifier("x".into()),
|
||||
box FieldElementExpression::Identifier("y".into()),
|
||||
ZirStatement::Assertion(
|
||||
BooleanExpression::FieldEq(
|
||||
box FieldElementExpression::Identifier("z".into()),
|
||||
box FieldElementExpression::Mult(
|
||||
box FieldElementExpression::Identifier("x".into()),
|
||||
box FieldElementExpression::Identifier("y".into()),
|
||||
),
|
||||
),
|
||||
)),
|
||||
zir::RuntimeError::mock(),
|
||||
),
|
||||
],
|
||||
signature: Signature {
|
||||
inputs: vec![],
|
||||
|
@ -3053,7 +3097,7 @@ mod tests {
|
|||
box FlatExpression::Identifier(FlatVariable::new(0)),
|
||||
box FlatExpression::Identifier(FlatVariable::new(1)),
|
||||
),
|
||||
RuntimeError::Source,
|
||||
zir::RuntimeError::mock().into(),
|
||||
),
|
||||
],
|
||||
};
|
||||
|
@ -3096,16 +3140,19 @@ mod tests {
|
|||
Variable::field_element("t"),
|
||||
FieldElementExpression::Number(Bn128Field::from(2)).into(),
|
||||
),
|
||||
ZirStatement::Assertion(BooleanExpression::FieldEq(
|
||||
box FieldElementExpression::Mult(
|
||||
box FieldElementExpression::Identifier("x".into()),
|
||||
box FieldElementExpression::Identifier("y".into()),
|
||||
ZirStatement::Assertion(
|
||||
BooleanExpression::FieldEq(
|
||||
box FieldElementExpression::Mult(
|
||||
box FieldElementExpression::Identifier("x".into()),
|
||||
box FieldElementExpression::Identifier("y".into()),
|
||||
),
|
||||
box FieldElementExpression::Mult(
|
||||
box FieldElementExpression::Identifier("z".into()),
|
||||
box FieldElementExpression::Identifier("t".into()),
|
||||
),
|
||||
),
|
||||
box FieldElementExpression::Mult(
|
||||
box FieldElementExpression::Identifier("z".into()),
|
||||
box FieldElementExpression::Identifier("t".into()),
|
||||
),
|
||||
)),
|
||||
zir::RuntimeError::mock(),
|
||||
),
|
||||
],
|
||||
signature: Signature {
|
||||
inputs: vec![],
|
||||
|
@ -3149,7 +3196,7 @@ mod tests {
|
|||
box FlatExpression::Identifier(FlatVariable::new(0)),
|
||||
box FlatExpression::Identifier(FlatVariable::new(1)),
|
||||
),
|
||||
RuntimeError::Source,
|
||||
zir::RuntimeError::mock().into(),
|
||||
),
|
||||
],
|
||||
};
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use crate::flat_absy::flat_variable::FlatVariable;
|
||||
use crate::flat_absy::RuntimeError;
|
||||
use crate::ir::{LinComb, Prog, QuadComb, Statement, Witness};
|
||||
use crate::solvers::Solver;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -37,7 +38,7 @@ impl Interpreter {
|
|||
|
||||
for statement in program.statements.iter() {
|
||||
match statement {
|
||||
Statement::Constraint(quad, lin, message) => match lin.is_assignee(&witness) {
|
||||
Statement::Constraint(quad, lin, error) => match lin.is_assignee(&witness) {
|
||||
true => {
|
||||
let val = quad.evaluate(&witness).unwrap();
|
||||
witness.insert(lin.0.get(0).unwrap().0, val);
|
||||
|
@ -47,12 +48,7 @@ impl Interpreter {
|
|||
let rhs_value = lin.evaluate(&witness).unwrap();
|
||||
if lhs_value != rhs_value {
|
||||
return Err(Error::UnsatisfiedConstraint {
|
||||
left: lhs_value.to_dec_string(),
|
||||
right: rhs_value.to_dec_string(),
|
||||
message: message
|
||||
.as_ref()
|
||||
.map(|m| m.to_string())
|
||||
.unwrap_or_else(|| "Unknown".to_string()),
|
||||
error: error.to_owned(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -275,26 +271,32 @@ impl<T: Field> QuadComb<T> {
|
|||
|
||||
#[derive(PartialEq, Serialize, Deserialize, Clone)]
|
||||
pub enum Error {
|
||||
UnsatisfiedConstraint {
|
||||
left: String,
|
||||
right: String,
|
||||
message: String,
|
||||
},
|
||||
UnsatisfiedConstraint { error: Option<RuntimeError> },
|
||||
Solver,
|
||||
WrongInputCount {
|
||||
expected: usize,
|
||||
received: usize,
|
||||
},
|
||||
WrongInputCount { expected: usize, received: usize },
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Error::UnsatisfiedConstraint {
|
||||
ref left,
|
||||
ref right,
|
||||
ref message,
|
||||
} => write!(f, "{}: expected {} to equal {}", message, left, right),
|
||||
Error::UnsatisfiedConstraint { ref error } => {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
error
|
||||
.as_ref()
|
||||
.map(|m| m.to_string())
|
||||
.expect("Found an unsatisfied constraint without an attached error.")
|
||||
)?;
|
||||
|
||||
match error {
|
||||
Some(e) if e.is_malicious() => {
|
||||
writeln!(f)?;
|
||||
write!(f, "The default ZoKrates interpreter should not yield this error. Please open an issue.")
|
||||
}
|
||||
_ => write!(f, ""),
|
||||
}
|
||||
}
|
||||
Error::Solver => write!(f, ""),
|
||||
Error::WrongInputCount { expected, received } => write!(
|
||||
f,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::fmt;
|
||||
|
||||
#[derive(Clone, PartialEq, Copy)]
|
||||
#[derive(Clone, PartialEq, Eq, Copy, Hash, Default, PartialOrd, Ord)]
|
||||
pub struct Position {
|
||||
pub line: usize,
|
||||
pub col: usize,
|
||||
|
|
|
@ -1733,13 +1733,20 @@ impl<'ast, T: Field> Checker<'ast, T> {
|
|||
.map(|rhs| TypedStatement::Definition(var, rhs))
|
||||
.map_err(|e| vec![e])
|
||||
}
|
||||
Statement::Assertion(e) => {
|
||||
Statement::Assertion(e, message) => {
|
||||
let e = self
|
||||
.check_expression(e, module_id, types)
|
||||
.map_err(|e| vec![e])?;
|
||||
|
||||
match e {
|
||||
TypedExpression::Boolean(e) => Ok(TypedStatement::Assertion(e)),
|
||||
TypedExpression::Boolean(e) => Ok(TypedStatement::Assertion(
|
||||
e,
|
||||
RuntimeError::SourceAssertion(AssertionMetadata {
|
||||
file: module_id.display().to_string(),
|
||||
position: pos.0,
|
||||
message,
|
||||
}),
|
||||
)),
|
||||
e => Err(ErrorInner {
|
||||
pos: Some(pos),
|
||||
message: format!(
|
||||
|
@ -2263,7 +2270,7 @@ impl<'ast, T: Field> Checker<'ast, T> {
|
|||
}),
|
||||
}
|
||||
}
|
||||
Expression::IfElse(box condition, box consequence, box alternative) => {
|
||||
Expression::Conditional(box condition, box consequence, box alternative, kind) => {
|
||||
let condition_checked = self.check_expression(condition, module_id, types)?;
|
||||
let consequence_checked = self.check_expression(consequence, module_id, types)?;
|
||||
let alternative_checked = self.check_expression(alternative, module_id, types)?;
|
||||
|
@ -2275,40 +2282,49 @@ 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()),
|
||||
})?;
|
||||
|
||||
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 {
|
||||
TypedExpression::Boolean(condition) => {
|
||||
match (consequence_checked, alternative_checked) {
|
||||
(TypedExpression::FieldElement(consequence), TypedExpression::FieldElement(alternative)) => {
|
||||
Ok(FieldElementExpression::if_else(condition, consequence, alternative).into())
|
||||
Ok(FieldElementExpression::conditional(condition, consequence, alternative, kind).into())
|
||||
},
|
||||
(TypedExpression::Boolean(consequence), TypedExpression::Boolean(alternative)) => {
|
||||
Ok(BooleanExpression::if_else(condition, consequence, alternative).into())
|
||||
Ok(BooleanExpression::conditional(condition, consequence, alternative, kind).into())
|
||||
},
|
||||
(TypedExpression::Array(consequence), TypedExpression::Array(alternative)) => {
|
||||
Ok(ArrayExpression::if_else(condition, consequence, alternative).into())
|
||||
Ok(ArrayExpression::conditional(condition, consequence, alternative, kind).into())
|
||||
},
|
||||
(TypedExpression::Struct(consequence), TypedExpression::Struct(alternative)) => {
|
||||
Ok(StructExpression::if_else(condition, consequence, alternative).into())
|
||||
Ok(StructExpression::conditional(condition, consequence, alternative, kind).into())
|
||||
},
|
||||
(TypedExpression::Uint(consequence), TypedExpression::Uint(alternative)) => {
|
||||
Ok(UExpression::if_else(condition, consequence, alternative).into())
|
||||
Ok(UExpression::conditional(condition, consequence, alternative, kind).into())
|
||||
},
|
||||
(TypedExpression::Int(consequence), TypedExpression::Int(alternative)) => {
|
||||
Ok(IntExpression::if_else(condition, consequence, alternative).into())
|
||||
Ok(IntExpression::conditional(condition, consequence, alternative, kind).into())
|
||||
},
|
||||
(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()
|
||||
),
|
||||
}),
|
||||
|
@ -4602,6 +4618,7 @@ mod tests {
|
|||
.mock(),
|
||||
)
|
||||
.mock(),
|
||||
None,
|
||||
)
|
||||
.mock(),
|
||||
Statement::Return(
|
||||
|
@ -5014,6 +5031,7 @@ mod tests {
|
|||
.mock(),
|
||||
)
|
||||
.mock(),
|
||||
None,
|
||||
)
|
||||
.mock(),
|
||||
Statement::Return(
|
||||
|
|
|
@ -17,17 +17,18 @@ impl Isolator {
|
|||
}
|
||||
|
||||
impl<'ast, T: Field> Folder<'ast, T> for Isolator {
|
||||
fn fold_if_else_expression<
|
||||
E: Expr<'ast, T> + Block<'ast, T> + Fold<'ast, T> + IfElse<'ast, T>,
|
||||
fn fold_conditional_expression<
|
||||
E: Expr<'ast, T> + Block<'ast, T> + Fold<'ast, T> + Conditional<'ast, T>,
|
||||
>(
|
||||
&mut self,
|
||||
_: &E::Ty,
|
||||
e: IfElseExpression<'ast, T, E>,
|
||||
) -> IfElseOrExpression<'ast, T, E> {
|
||||
IfElseOrExpression::IfElse(IfElseExpression::new(
|
||||
e: ConditionalExpression<'ast, T, E>,
|
||||
) -> ConditionalOrExpression<'ast, T, E> {
|
||||
ConditionalOrExpression::Conditional(ConditionalExpression::new(
|
||||
self.fold_boolean_expression(*e.condition),
|
||||
E::block(vec![], e.consequence.fold(self)),
|
||||
E::block(vec![], e.alternative.fold(self)),
|
||||
e.kind,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -281,12 +281,12 @@ impl<'ast, T: Field> Flattener<T> {
|
|||
}
|
||||
}
|
||||
|
||||
fn fold_if_else_expression<E: Flatten<'ast, T>>(
|
||||
fn fold_conditional_expression<E: Flatten<'ast, T>>(
|
||||
&mut self,
|
||||
statements_buffer: &mut Vec<zir::ZirStatement<'ast, T>>,
|
||||
c: typed_absy::IfElseExpression<'ast, T, E>,
|
||||
c: typed_absy::ConditionalExpression<'ast, T, E>,
|
||||
) -> Vec<zir::ZirExpression<'ast, T>> {
|
||||
fold_if_else_expression(self, statements_buffer, c)
|
||||
fold_conditional_expression(self, statements_buffer, c)
|
||||
}
|
||||
|
||||
fn fold_member_expression<E>(
|
||||
|
@ -379,9 +379,15 @@ fn fold_statement<'ast, T: Field>(
|
|||
typed_absy::TypedStatement::Declaration(..) => {
|
||||
unreachable!()
|
||||
}
|
||||
typed_absy::TypedStatement::Assertion(e) => {
|
||||
typed_absy::TypedStatement::Assertion(e, error) => {
|
||||
let e = f.fold_boolean_expression(statements_buffer, e);
|
||||
vec![zir::ZirStatement::Assertion(e)]
|
||||
let error = match error {
|
||||
typed_absy::RuntimeError::SourceAssertion(metadata) => {
|
||||
zir::RuntimeError::SourceAssertion(metadata.to_string())
|
||||
}
|
||||
typed_absy::RuntimeError::SelectRangeCheck => zir::RuntimeError::SelectRangeCheck,
|
||||
};
|
||||
vec![zir::ZirStatement::Assertion(e, error)]
|
||||
}
|
||||
typed_absy::TypedStatement::For(..) => unreachable!(),
|
||||
typed_absy::TypedStatement::MultipleDefinition(variables, elist) => {
|
||||
|
@ -442,8 +448,8 @@ fn fold_array_expression_inner<'ast, T: Field>(
|
|||
exprs
|
||||
}
|
||||
typed_absy::ArrayExpressionInner::FunctionCall(..) => unreachable!(),
|
||||
typed_absy::ArrayExpressionInner::IfElse(c) => {
|
||||
f.fold_if_else_expression(statements_buffer, c)
|
||||
typed_absy::ArrayExpressionInner::Conditional(c) => {
|
||||
f.fold_conditional_expression(statements_buffer, c)
|
||||
}
|
||||
typed_absy::ArrayExpressionInner::Member(m) => {
|
||||
f.fold_member_expression(statements_buffer, m)
|
||||
|
@ -517,8 +523,8 @@ fn fold_struct_expression_inner<'ast, T: Field>(
|
|||
.flat_map(|e| f.fold_expression(statements_buffer, e))
|
||||
.collect(),
|
||||
typed_absy::StructExpressionInner::FunctionCall(..) => unreachable!(),
|
||||
typed_absy::StructExpressionInner::IfElse(c) => {
|
||||
f.fold_if_else_expression(statements_buffer, c)
|
||||
typed_absy::StructExpressionInner::Conditional(c) => {
|
||||
f.fold_conditional_expression(statements_buffer, c)
|
||||
}
|
||||
typed_absy::StructExpressionInner::Member(m) => {
|
||||
f.fold_member_expression(statements_buffer, m)
|
||||
|
@ -640,10 +646,10 @@ fn fold_select_expression<'ast, T: Field, E>(
|
|||
}
|
||||
}
|
||||
|
||||
fn fold_if_else_expression<'ast, T: Field, E: Flatten<'ast, T>>(
|
||||
fn fold_conditional_expression<'ast, T: Field, E: Flatten<'ast, T>>(
|
||||
f: &mut Flattener<T>,
|
||||
statements_buffer: &mut Vec<zir::ZirStatement<'ast, T>>,
|
||||
c: typed_absy::IfElseExpression<'ast, T, E>,
|
||||
c: typed_absy::ConditionalExpression<'ast, T, E>,
|
||||
) -> Vec<zir::ZirExpression<'ast, T>> {
|
||||
let mut consequence_statements = vec![];
|
||||
let mut alternative_statements = vec![];
|
||||
|
@ -736,8 +742,8 @@ fn fold_field_expression<'ast, T: Field>(
|
|||
typed_absy::FieldElementExpression::Pos(box e) => {
|
||||
f.fold_field_expression(statements_buffer, e)
|
||||
}
|
||||
typed_absy::FieldElementExpression::IfElse(c) => f
|
||||
.fold_if_else_expression(statements_buffer, c)
|
||||
typed_absy::FieldElementExpression::Conditional(c) => f
|
||||
.fold_conditional_expression(statements_buffer, c)
|
||||
.pop()
|
||||
.unwrap()
|
||||
.try_into()
|
||||
|
@ -902,8 +908,8 @@ fn fold_boolean_expression<'ast, T: Field>(
|
|||
let e = f.fold_boolean_expression(statements_buffer, e);
|
||||
zir::BooleanExpression::Not(box e)
|
||||
}
|
||||
typed_absy::BooleanExpression::IfElse(c) => f
|
||||
.fold_if_else_expression(statements_buffer, c)
|
||||
typed_absy::BooleanExpression::Conditional(c) => f
|
||||
.fold_conditional_expression(statements_buffer, c)
|
||||
.pop()
|
||||
.unwrap()
|
||||
.try_into()
|
||||
|
@ -1064,8 +1070,8 @@ fn fold_uint_expression_inner<'ast, T: Field>(
|
|||
)
|
||||
.unwrap()
|
||||
.into_inner(),
|
||||
typed_absy::UExpressionInner::IfElse(c) => zir::UExpression::try_from(
|
||||
f.fold_if_else_expression(statements_buffer, c)
|
||||
typed_absy::UExpressionInner::Conditional(c) => zir::UExpression::try_from(
|
||||
f.fold_conditional_expression(statements_buffer, c)
|
||||
.pop()
|
||||
.unwrap(),
|
||||
)
|
||||
|
|
|
@ -172,13 +172,13 @@ impl<'ast, 'a, T: Field> ResultFolder<'ast, T> for Propagator<'ast, 'a, T> {
|
|||
fold_function(self, f)
|
||||
}
|
||||
|
||||
fn fold_if_else_expression<
|
||||
E: Expr<'ast, T> + IfElse<'ast, T> + PartialEq + ResultFold<'ast, T>,
|
||||
fn fold_conditional_expression<
|
||||
E: Expr<'ast, T> + Conditional<'ast, T> + PartialEq + ResultFold<'ast, T>,
|
||||
>(
|
||||
&mut self,
|
||||
_: &E::Ty,
|
||||
e: IfElseExpression<'ast, T, E>,
|
||||
) -> Result<IfElseOrExpression<'ast, T, E>, Self::Error> {
|
||||
e: ConditionalExpression<'ast, T, E>,
|
||||
) -> Result<ConditionalOrExpression<'ast, T, E>, Self::Error> {
|
||||
Ok(
|
||||
match (
|
||||
self.fold_boolean_expression(*e.condition)?,
|
||||
|
@ -186,16 +186,16 @@ impl<'ast, 'a, T: Field> ResultFolder<'ast, T> for Propagator<'ast, 'a, T> {
|
|||
e.alternative.fold(self)?,
|
||||
) {
|
||||
(BooleanExpression::Value(true), consequence, _) => {
|
||||
IfElseOrExpression::Expression(consequence.into_inner())
|
||||
ConditionalOrExpression::Expression(consequence.into_inner())
|
||||
}
|
||||
(BooleanExpression::Value(false), _, alternative) => {
|
||||
IfElseOrExpression::Expression(alternative.into_inner())
|
||||
ConditionalOrExpression::Expression(alternative.into_inner())
|
||||
}
|
||||
(_, consequence, alternative) if consequence == alternative => {
|
||||
IfElseOrExpression::Expression(consequence.into_inner())
|
||||
ConditionalOrExpression::Expression(consequence.into_inner())
|
||||
}
|
||||
(condition, consequence, alternative) => IfElseOrExpression::IfElse(
|
||||
IfElseExpression::new(condition, consequence, alternative),
|
||||
(condition, consequence, alternative) => ConditionalOrExpression::Conditional(
|
||||
ConditionalExpression::new(condition, consequence, alternative, e.kind),
|
||||
),
|
||||
},
|
||||
)
|
||||
|
@ -620,15 +620,14 @@ impl<'ast, 'a, T: Field> ResultFolder<'ast, T> for Propagator<'ast, 'a, T> {
|
|||
|
||||
Ok(statements)
|
||||
}
|
||||
TypedStatement::Assertion(e) => {
|
||||
TypedStatement::Assertion(e, ty) => {
|
||||
let e_str = e.to_string();
|
||||
let expr = self.fold_boolean_expression(e)?;
|
||||
match expr {
|
||||
BooleanExpression::Value(v) if !v => Err(Error::AssertionFailed(format!(
|
||||
"Assertion failed on expression `{}`",
|
||||
e_str
|
||||
))),
|
||||
_ => Ok(vec![TypedStatement::Assertion(expr)]),
|
||||
BooleanExpression::Value(v) if !v => {
|
||||
Err(Error::AssertionFailed(format!("{}: ({})", ty, e_str)))
|
||||
}
|
||||
_ => Ok(vec![TypedStatement::Assertion(expr, ty)]),
|
||||
}
|
||||
}
|
||||
s @ TypedStatement::PushCallLog(..) => Ok(vec![s]),
|
||||
|
@ -1432,10 +1431,11 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn if_else_true() {
|
||||
let e = FieldElementExpression::if_else(
|
||||
let e = FieldElementExpression::conditional(
|
||||
BooleanExpression::Value(true),
|
||||
FieldElementExpression::Number(Bn128Field::from(2)),
|
||||
FieldElementExpression::Number(Bn128Field::from(3)),
|
||||
ConditionalKind::IfElse,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
|
@ -1446,10 +1446,11 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn if_else_false() {
|
||||
let e = FieldElementExpression::if_else(
|
||||
let e = FieldElementExpression::conditional(
|
||||
BooleanExpression::Value(false),
|
||||
FieldElementExpression::Number(Bn128Field::from(2)),
|
||||
FieldElementExpression::Number(Bn128Field::from(3)),
|
||||
ConditionalKind::IfElse,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
|
|
|
@ -531,7 +531,7 @@ impl<'ast, T: Field> Folder<'ast, T> for UintOptimizer<'ast, T> {
|
|||
}
|
||||
}
|
||||
}
|
||||
ZirStatement::Assertion(BooleanExpression::UintEq(box left, box right)) => {
|
||||
ZirStatement::Assertion(BooleanExpression::UintEq(box left, box right), metadata) => {
|
||||
let left = self.fold_uint_expression(left);
|
||||
let right = self.fold_uint_expression(right);
|
||||
|
||||
|
@ -539,9 +539,10 @@ impl<'ast, T: Field> Folder<'ast, T> for UintOptimizer<'ast, T> {
|
|||
let left = force_reduce(left);
|
||||
let right = force_reduce(right);
|
||||
|
||||
vec![ZirStatement::Assertion(BooleanExpression::UintEq(
|
||||
box left, box right,
|
||||
))]
|
||||
vec![ZirStatement::Assertion(
|
||||
BooleanExpression::UintEq(box left, box right),
|
||||
metadata,
|
||||
)]
|
||||
}
|
||||
s => fold_statement(self, s),
|
||||
}
|
||||
|
|
|
@ -49,13 +49,14 @@ impl<'ast> VariableWriteRemover {
|
|||
Access::Select(head) => {
|
||||
statements.insert(TypedStatement::Assertion(
|
||||
BooleanExpression::UintLt(box head.clone(), box size.into()),
|
||||
RuntimeError::SelectRangeCheck,
|
||||
));
|
||||
|
||||
ArrayExpressionInner::Value(
|
||||
(0..size)
|
||||
.map(|i| match inner_ty {
|
||||
Type::Int => unreachable!(),
|
||||
Type::Array(..) => ArrayExpression::if_else(
|
||||
Type::Array(..) => ArrayExpression::conditional(
|
||||
BooleanExpression::UintEq(
|
||||
box i.into(),
|
||||
box head.clone(),
|
||||
|
@ -73,9 +74,10 @@ impl<'ast> VariableWriteRemover {
|
|||
),
|
||||
},
|
||||
ArrayExpression::select(base.clone(), i),
|
||||
ConditionalKind::IfElse,
|
||||
)
|
||||
.into(),
|
||||
Type::Struct(..) => StructExpression::if_else(
|
||||
Type::Struct(..) => StructExpression::conditional(
|
||||
BooleanExpression::UintEq(
|
||||
box i.into(),
|
||||
box head.clone(),
|
||||
|
@ -93,9 +95,10 @@ impl<'ast> VariableWriteRemover {
|
|||
),
|
||||
},
|
||||
StructExpression::select(base.clone(), i),
|
||||
ConditionalKind::IfElse,
|
||||
)
|
||||
.into(),
|
||||
Type::FieldElement => FieldElementExpression::if_else(
|
||||
Type::FieldElement => FieldElementExpression::conditional(
|
||||
BooleanExpression::UintEq(
|
||||
box i.into(),
|
||||
box head.clone(),
|
||||
|
@ -114,9 +117,10 @@ impl<'ast> VariableWriteRemover {
|
|||
),
|
||||
},
|
||||
FieldElementExpression::select(base.clone(), i),
|
||||
ConditionalKind::IfElse,
|
||||
)
|
||||
.into(),
|
||||
Type::Boolean => BooleanExpression::if_else(
|
||||
Type::Boolean => BooleanExpression::conditional(
|
||||
BooleanExpression::UintEq(
|
||||
box i.into(),
|
||||
box head.clone(),
|
||||
|
@ -134,9 +138,10 @@ impl<'ast> VariableWriteRemover {
|
|||
),
|
||||
},
|
||||
BooleanExpression::select(base.clone(), i),
|
||||
ConditionalKind::IfElse,
|
||||
)
|
||||
.into(),
|
||||
Type::Uint(..) => UExpression::if_else(
|
||||
Type::Uint(..) => UExpression::conditional(
|
||||
BooleanExpression::UintEq(
|
||||
box i.into(),
|
||||
box head.clone(),
|
||||
|
@ -154,6 +159,7 @@ impl<'ast> VariableWriteRemover {
|
|||
),
|
||||
},
|
||||
UExpression::select(base.clone(), i),
|
||||
ConditionalKind::IfElse,
|
||||
)
|
||||
.into(),
|
||||
})
|
||||
|
|
|
@ -2,8 +2,8 @@ use crate::zir::result_folder::fold_statement;
|
|||
use crate::zir::result_folder::ResultFolder;
|
||||
use crate::zir::types::UBitwidth;
|
||||
use crate::zir::{
|
||||
BooleanExpression, FieldElementExpression, Identifier, UExpression, UExpressionInner,
|
||||
ZirExpression, ZirProgram, ZirStatement,
|
||||
BooleanExpression, FieldElementExpression, Identifier, RuntimeError, UExpression,
|
||||
UExpressionInner, ZirExpression, ZirProgram, ZirStatement,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
|
@ -15,6 +15,7 @@ type Constants<'ast, T> = HashMap<Identifier<'ast>, ZirExpression<'ast, T>>;
|
|||
pub enum Error {
|
||||
OutOfBounds(u128, u128),
|
||||
DivisionByZero,
|
||||
AssertionFailed(RuntimeError),
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
|
@ -28,6 +29,7 @@ impl fmt::Display for Error {
|
|||
Error::DivisionByZero => {
|
||||
write!(f, "Division by zero detected in zir during static analysis",)
|
||||
}
|
||||
Error::AssertionFailed(err) => write!(f, "{}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -51,9 +53,10 @@ impl<'ast, T: Field> ResultFolder<'ast, T> for ZirPropagator<'ast, T> {
|
|||
s: ZirStatement<'ast, T>,
|
||||
) -> Result<Vec<ZirStatement<'ast, T>>, Self::Error> {
|
||||
match s {
|
||||
ZirStatement::Assertion(e) => match self.fold_boolean_expression(e)? {
|
||||
ZirStatement::Assertion(e, error) => match self.fold_boolean_expression(e)? {
|
||||
BooleanExpression::Value(true) => Ok(vec![]),
|
||||
e => Ok(vec![ZirStatement::Assertion(e)]),
|
||||
BooleanExpression::Value(false) => Err(Error::AssertionFailed(error)),
|
||||
e => Ok(vec![ZirStatement::Assertion(e, error)]),
|
||||
},
|
||||
ZirStatement::Definition(a, e) => {
|
||||
let e = self.fold_expression(e)?;
|
||||
|
@ -654,21 +657,31 @@ impl<'ast, T: Field> ResultFolder<'ast, T> for ZirPropagator<'ast, T> {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::zir::RuntimeError;
|
||||
use zokrates_field::Bn128Field;
|
||||
|
||||
impl RuntimeError {
|
||||
pub fn mock() -> Self {
|
||||
RuntimeError::SourceAssertion(String::default())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn propagation() {
|
||||
// assert([x, 1] == [y, 1])
|
||||
let statements = vec![ZirStatement::Assertion(BooleanExpression::And(
|
||||
box BooleanExpression::FieldEq(
|
||||
box FieldElementExpression::Identifier("x".into()),
|
||||
box FieldElementExpression::Identifier("y".into()),
|
||||
let statements = vec![ZirStatement::Assertion(
|
||||
BooleanExpression::And(
|
||||
box BooleanExpression::FieldEq(
|
||||
box FieldElementExpression::Identifier("x".into()),
|
||||
box FieldElementExpression::Identifier("y".into()),
|
||||
),
|
||||
box BooleanExpression::FieldEq(
|
||||
box FieldElementExpression::Number(Bn128Field::from(1)),
|
||||
box FieldElementExpression::Number(Bn128Field::from(1)),
|
||||
),
|
||||
),
|
||||
box BooleanExpression::FieldEq(
|
||||
box FieldElementExpression::Number(Bn128Field::from(1)),
|
||||
box FieldElementExpression::Number(Bn128Field::from(1)),
|
||||
),
|
||||
))];
|
||||
RuntimeError::mock(),
|
||||
)];
|
||||
|
||||
let mut propagator = ZirPropagator::default();
|
||||
let statements: Vec<ZirStatement<_>> = statements
|
||||
|
@ -682,10 +695,13 @@ mod tests {
|
|||
|
||||
assert_eq!(
|
||||
statements,
|
||||
vec![ZirStatement::Assertion(BooleanExpression::FieldEq(
|
||||
box FieldElementExpression::Identifier("x".into()),
|
||||
box FieldElementExpression::Identifier("y".into()),
|
||||
))]
|
||||
vec![ZirStatement::Assertion(
|
||||
BooleanExpression::FieldEq(
|
||||
box FieldElementExpression::Identifier("x".into()),
|
||||
box FieldElementExpression::Identifier("y".into()),
|
||||
),
|
||||
RuntimeError::mock()
|
||||
)]
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -284,18 +284,18 @@ pub trait Folder<'ast, T: Field>: Sized {
|
|||
fold_block_expression(self, block)
|
||||
}
|
||||
|
||||
fn fold_if_else_expression<
|
||||
fn fold_conditional_expression<
|
||||
E: Expr<'ast, T>
|
||||
+ Fold<'ast, T>
|
||||
+ Block<'ast, T>
|
||||
+ IfElse<'ast, T>
|
||||
+ Conditional<'ast, T>
|
||||
+ From<TypedExpression<'ast, T>>,
|
||||
>(
|
||||
&mut self,
|
||||
ty: &E::Ty,
|
||||
e: IfElseExpression<'ast, T, E>,
|
||||
) -> IfElseOrExpression<'ast, T, E> {
|
||||
fold_if_else_expression(self, ty, e)
|
||||
e: ConditionalExpression<'ast, T, E>,
|
||||
) -> ConditionalOrExpression<'ast, T, E> {
|
||||
fold_conditional_expression(self, ty, e)
|
||||
}
|
||||
|
||||
fn fold_member_expression<
|
||||
|
@ -319,7 +319,7 @@ pub trait Folder<'ast, T: Field>: Sized {
|
|||
}
|
||||
|
||||
fn fold_select_expression<
|
||||
E: Expr<'ast, T> + Select<'ast, T> + IfElse<'ast, T> + From<TypedExpression<'ast, T>>,
|
||||
E: Expr<'ast, T> + Select<'ast, T> + Conditional<'ast, T> + From<TypedExpression<'ast, T>>,
|
||||
>(
|
||||
&mut self,
|
||||
ty: &E::Ty,
|
||||
|
@ -461,7 +461,9 @@ pub fn fold_statement<'ast, T: Field, F: Folder<'ast, T>>(
|
|||
TypedStatement::Definition(f.fold_assignee(a), f.fold_expression(e))
|
||||
}
|
||||
TypedStatement::Declaration(v) => TypedStatement::Declaration(f.fold_variable(v)),
|
||||
TypedStatement::Assertion(e) => TypedStatement::Assertion(f.fold_boolean_expression(e)),
|
||||
TypedStatement::Assertion(e, error) => {
|
||||
TypedStatement::Assertion(f.fold_boolean_expression(e), error)
|
||||
}
|
||||
TypedStatement::For(v, from, to, statements) => TypedStatement::For(
|
||||
f.fold_variable(v),
|
||||
f.fold_uint_expression(from),
|
||||
|
@ -504,9 +506,9 @@ pub fn fold_array_expression_inner<'ast, T: Field, F: Folder<'ast, T>>(
|
|||
FunctionCallOrExpression::Expression(u) => u,
|
||||
}
|
||||
}
|
||||
ArrayExpressionInner::IfElse(c) => match f.fold_if_else_expression(ty, c) {
|
||||
IfElseOrExpression::IfElse(s) => ArrayExpressionInner::IfElse(s),
|
||||
IfElseOrExpression::Expression(u) => u,
|
||||
ArrayExpressionInner::Conditional(c) => match f.fold_conditional_expression(ty, c) {
|
||||
ConditionalOrExpression::Conditional(s) => ArrayExpressionInner::Conditional(s),
|
||||
ConditionalOrExpression::Expression(u) => u,
|
||||
},
|
||||
ArrayExpressionInner::Select(select) => match f.fold_select_expression(ty, select) {
|
||||
SelectOrExpression::Select(s) => ArrayExpressionInner::Select(s),
|
||||
|
@ -551,9 +553,9 @@ pub fn fold_struct_expression_inner<'ast, T: Field, F: Folder<'ast, T>>(
|
|||
FunctionCallOrExpression::Expression(u) => u,
|
||||
}
|
||||
}
|
||||
StructExpressionInner::IfElse(c) => match f.fold_if_else_expression(ty, c) {
|
||||
IfElseOrExpression::IfElse(s) => StructExpressionInner::IfElse(s),
|
||||
IfElseOrExpression::Expression(u) => u,
|
||||
StructExpressionInner::Conditional(c) => match f.fold_conditional_expression(ty, c) {
|
||||
ConditionalOrExpression::Conditional(s) => StructExpressionInner::Conditional(s),
|
||||
ConditionalOrExpression::Expression(u) => u,
|
||||
},
|
||||
StructExpressionInner::Select(select) => match f.fold_select_expression(ty, select) {
|
||||
SelectOrExpression::Select(s) => StructExpressionInner::Select(s),
|
||||
|
@ -613,10 +615,10 @@ pub fn fold_field_expression<'ast, T: Field, F: Folder<'ast, T>>(
|
|||
|
||||
FieldElementExpression::Pos(box e)
|
||||
}
|
||||
FieldElementExpression::IfElse(c) => {
|
||||
match f.fold_if_else_expression(&Type::FieldElement, c) {
|
||||
IfElseOrExpression::IfElse(s) => FieldElementExpression::IfElse(s),
|
||||
IfElseOrExpression::Expression(u) => u,
|
||||
FieldElementExpression::Conditional(c) => {
|
||||
match f.fold_conditional_expression(&Type::FieldElement, c) {
|
||||
ConditionalOrExpression::Conditional(s) => FieldElementExpression::Conditional(s),
|
||||
ConditionalOrExpression::Expression(u) => u,
|
||||
}
|
||||
}
|
||||
FieldElementExpression::FunctionCall(function_call) => {
|
||||
|
@ -641,20 +643,21 @@ pub fn fold_field_expression<'ast, T: Field, F: Folder<'ast, T>>(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn fold_if_else_expression<
|
||||
pub fn fold_conditional_expression<
|
||||
'ast,
|
||||
T: Field,
|
||||
E: Expr<'ast, T> + Fold<'ast, T> + IfElse<'ast, T> + From<TypedExpression<'ast, T>>,
|
||||
E: Expr<'ast, T> + Fold<'ast, T> + Conditional<'ast, T> + From<TypedExpression<'ast, T>>,
|
||||
F: Folder<'ast, T>,
|
||||
>(
|
||||
f: &mut F,
|
||||
_: &E::Ty,
|
||||
e: IfElseExpression<'ast, T, E>,
|
||||
) -> IfElseOrExpression<'ast, T, E> {
|
||||
IfElseOrExpression::IfElse(IfElseExpression::new(
|
||||
e: ConditionalExpression<'ast, T, E>,
|
||||
) -> ConditionalOrExpression<'ast, T, E> {
|
||||
ConditionalOrExpression::Conditional(ConditionalExpression::new(
|
||||
f.fold_boolean_expression(*e.condition),
|
||||
e.consequence.fold(f),
|
||||
e.alternative.fold(f),
|
||||
e.kind,
|
||||
))
|
||||
}
|
||||
|
||||
|
@ -677,7 +680,7 @@ pub fn fold_member_expression<
|
|||
pub fn fold_select_expression<
|
||||
'ast,
|
||||
T: Field,
|
||||
E: Expr<'ast, T> + Select<'ast, T> + IfElse<'ast, T> + From<TypedExpression<'ast, T>>,
|
||||
E: Expr<'ast, T> + Select<'ast, T> + Conditional<'ast, T> + From<TypedExpression<'ast, T>>,
|
||||
F: Folder<'ast, T>,
|
||||
>(
|
||||
f: &mut F,
|
||||
|
@ -792,9 +795,10 @@ pub fn fold_boolean_expression<'ast, T: Field, F: Folder<'ast, T>>(
|
|||
FunctionCallOrExpression::Expression(u) => u,
|
||||
}
|
||||
}
|
||||
BooleanExpression::IfElse(c) => match f.fold_if_else_expression(&Type::Boolean, c) {
|
||||
IfElseOrExpression::IfElse(s) => BooleanExpression::IfElse(s),
|
||||
IfElseOrExpression::Expression(u) => u,
|
||||
BooleanExpression::Conditional(c) => match f.fold_conditional_expression(&Type::Boolean, c)
|
||||
{
|
||||
ConditionalOrExpression::Conditional(s) => BooleanExpression::Conditional(s),
|
||||
ConditionalOrExpression::Expression(u) => u,
|
||||
},
|
||||
BooleanExpression::Select(select) => match f.fold_select_expression(&Type::Boolean, select)
|
||||
{
|
||||
|
@ -920,9 +924,9 @@ pub fn fold_uint_expression_inner<'ast, T: Field, F: Folder<'ast, T>>(
|
|||
SelectOrExpression::Select(s) => UExpressionInner::Select(s),
|
||||
SelectOrExpression::Expression(u) => u,
|
||||
},
|
||||
UExpressionInner::IfElse(c) => match f.fold_if_else_expression(&ty, c) {
|
||||
IfElseOrExpression::IfElse(s) => UExpressionInner::IfElse(s),
|
||||
IfElseOrExpression::Expression(u) => u,
|
||||
UExpressionInner::Conditional(c) => match f.fold_conditional_expression(&ty, c) {
|
||||
ConditionalOrExpression::Conditional(s) => UExpressionInner::Conditional(s),
|
||||
ConditionalOrExpression::Expression(u) => u,
|
||||
},
|
||||
UExpressionInner::Member(m) => match f.fold_member_expression(&ty, m) {
|
||||
MemberOrExpression::Member(m) => UExpressionInner::Member(m),
|
||||
|
|
|
@ -5,9 +5,10 @@ use crate::typed_absy::types::{
|
|||
};
|
||||
use crate::typed_absy::UBitwidth;
|
||||
use crate::typed_absy::{
|
||||
ArrayExpression, ArrayExpressionInner, BooleanExpression, Expr, FieldElementExpression, IfElse,
|
||||
IfElseExpression, Select, SelectExpression, StructExpression, StructExpressionInner, Typed,
|
||||
TypedExpression, TypedExpressionOrSpread, TypedSpread, UExpression, UExpressionInner,
|
||||
ArrayExpression, ArrayExpressionInner, BooleanExpression, Conditional, ConditionalExpression,
|
||||
Expr, FieldElementExpression, Select, SelectExpression, StructExpression,
|
||||
StructExpressionInner, Typed, TypedExpression, TypedExpressionOrSpread, TypedSpread,
|
||||
UExpression, UExpressionInner,
|
||||
};
|
||||
use num_bigint::BigUint;
|
||||
use std::convert::TryFrom;
|
||||
|
@ -239,7 +240,7 @@ pub enum IntExpression<'ast, T> {
|
|||
Div(Box<IntExpression<'ast, T>>, Box<IntExpression<'ast, T>>),
|
||||
Rem(Box<IntExpression<'ast, T>>, Box<IntExpression<'ast, T>>),
|
||||
Pow(Box<IntExpression<'ast, T>>, Box<IntExpression<'ast, T>>),
|
||||
IfElse(IfElseExpression<'ast, T, IntExpression<'ast, T>>),
|
||||
Conditional(ConditionalExpression<'ast, T, IntExpression<'ast, T>>),
|
||||
Select(SelectExpression<'ast, T, IntExpression<'ast, T>>),
|
||||
Xor(Box<IntExpression<'ast, T>>, Box<IntExpression<'ast, T>>),
|
||||
And(Box<IntExpression<'ast, T>>, Box<IntExpression<'ast, T>>),
|
||||
|
@ -354,7 +355,7 @@ impl<'ast, T: fmt::Display> fmt::Display for IntExpression<'ast, T> {
|
|||
IntExpression::RightShift(ref e, ref by) => write!(f, "({} >> {})", e, by),
|
||||
IntExpression::LeftShift(ref e, ref by) => write!(f, "({} << {})", e, by),
|
||||
IntExpression::Not(ref e) => write!(f, "!{}", e),
|
||||
IntExpression::IfElse(ref c) => write!(f, "{}", c),
|
||||
IntExpression::Conditional(ref c) => write!(f, "{}", c),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -404,10 +405,11 @@ impl<'ast, T: Field> FieldElementExpression<'ast, T> {
|
|||
)),
|
||||
IntExpression::Pos(box e) => Ok(Self::Pos(box Self::try_from_int(e)?)),
|
||||
IntExpression::Neg(box e) => Ok(Self::Neg(box Self::try_from_int(e)?)),
|
||||
IntExpression::IfElse(c) => Ok(Self::IfElse(IfElseExpression::new(
|
||||
IntExpression::Conditional(c) => Ok(Self::Conditional(ConditionalExpression::new(
|
||||
*c.condition,
|
||||
Self::try_from_int(*c.consequence)?,
|
||||
Self::try_from_int(*c.alternative)?,
|
||||
c.kind,
|
||||
))),
|
||||
IntExpression::Select(select) => {
|
||||
let array = *select.array;
|
||||
|
@ -523,10 +525,11 @@ impl<'ast, T: Field> UExpression<'ast, T> {
|
|||
Self::try_from_int(e1, bitwidth)?,
|
||||
e2,
|
||||
)),
|
||||
IfElse(c) => Ok(UExpression::if_else(
|
||||
Conditional(c) => Ok(UExpression::conditional(
|
||||
*c.condition,
|
||||
Self::try_from_int(*c.consequence, bitwidth)?,
|
||||
Self::try_from_int(*c.alternative, bitwidth)?,
|
||||
c.kind,
|
||||
)),
|
||||
Select(select) => {
|
||||
let array = *select.array;
|
||||
|
@ -693,6 +696,7 @@ impl<'ast, T> From<BigUint> for IntExpression<'ast, T> {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::typed_absy::ConditionalKind;
|
||||
use zokrates_field::Bn128Field;
|
||||
|
||||
#[test]
|
||||
|
@ -714,7 +718,7 @@ mod tests {
|
|||
n.clone() * n.clone(),
|
||||
IntExpression::pow(n.clone(), n.clone()),
|
||||
n.clone() / n.clone(),
|
||||
IntExpression::if_else(c.clone(), n.clone(), n.clone()),
|
||||
IntExpression::conditional(c.clone(), n.clone(), n.clone(), ConditionalKind::IfElse),
|
||||
IntExpression::select(n_a.clone(), i.clone()),
|
||||
];
|
||||
|
||||
|
@ -725,7 +729,12 @@ mod tests {
|
|||
t.clone() * t.clone(),
|
||||
FieldElementExpression::pow(t.clone(), i.clone()),
|
||||
t.clone() / t.clone(),
|
||||
FieldElementExpression::if_else(c.clone(), t.clone(), t.clone()),
|
||||
FieldElementExpression::conditional(
|
||||
c.clone(),
|
||||
t.clone(),
|
||||
t.clone(),
|
||||
ConditionalKind::IfElse,
|
||||
),
|
||||
FieldElementExpression::select(t_a.clone(), i.clone()),
|
||||
];
|
||||
|
||||
|
@ -780,7 +789,7 @@ mod tests {
|
|||
IntExpression::left_shift(n.clone(), i.clone()),
|
||||
IntExpression::right_shift(n.clone(), i.clone()),
|
||||
!n.clone(),
|
||||
IntExpression::if_else(c.clone(), n.clone(), n.clone()),
|
||||
IntExpression::conditional(c.clone(), n.clone(), n.clone(), ConditionalKind::IfElse),
|
||||
IntExpression::select(n_a.clone(), i.clone()),
|
||||
];
|
||||
|
||||
|
@ -797,7 +806,7 @@ mod tests {
|
|||
UExpression::left_shift(t.clone(), i.clone()),
|
||||
UExpression::right_shift(t.clone(), i.clone()),
|
||||
!t.clone(),
|
||||
UExpression::if_else(c.clone(), t.clone(), t.clone()),
|
||||
UExpression::conditional(c.clone(), t.clone(), t.clone(), ConditionalKind::IfElse),
|
||||
UExpression::select(t_a.clone(), i.clone()),
|
||||
];
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ pub use self::types::{
|
|||
DeclarationSignature, DeclarationStructType, DeclarationType, GArrayType, GStructType, GType,
|
||||
GenericIdentifier, IntoTypes, Signature, StructType, Type, Types, UBitwidth,
|
||||
};
|
||||
use crate::parser::Position;
|
||||
use crate::typed_absy::types::ConcreteGenericsAssignment;
|
||||
|
||||
pub use self::variable::{ConcreteVariable, DeclarationVariable, GVariable, Variable};
|
||||
|
@ -575,6 +576,38 @@ impl<'ast, T: fmt::Display> fmt::Display for TypedAssignee<'ast, T> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Hash, Eq, Default, PartialOrd, Ord)]
|
||||
pub struct AssertionMetadata {
|
||||
pub file: String,
|
||||
pub position: Position,
|
||||
pub message: Option<String>,
|
||||
}
|
||||
|
||||
impl fmt::Display for AssertionMetadata {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Assertion failed at {}:{}", self.file, self.position)?;
|
||||
match &self.message {
|
||||
Some(m) => write!(f, ": \"{}\"", m),
|
||||
None => write!(f, ""),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Hash, Eq, PartialOrd, Ord)]
|
||||
pub enum RuntimeError {
|
||||
SourceAssertion(AssertionMetadata),
|
||||
SelectRangeCheck,
|
||||
}
|
||||
|
||||
impl fmt::Display for RuntimeError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
RuntimeError::SourceAssertion(metadata) => write!(f, "{}", metadata),
|
||||
RuntimeError::SelectRangeCheck => write!(f, "Range check on array access"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A statement in a `TypedFunction`
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Clone, PartialEq, Debug, Hash, Eq, PartialOrd, Ord)]
|
||||
|
@ -582,7 +615,7 @@ pub enum TypedStatement<'ast, T> {
|
|||
Return(Vec<TypedExpression<'ast, T>>),
|
||||
Definition(TypedAssignee<'ast, T>, TypedExpression<'ast, T>),
|
||||
Declaration(Variable<'ast, T>),
|
||||
Assertion(BooleanExpression<'ast, T>),
|
||||
Assertion(BooleanExpression<'ast, T>, RuntimeError),
|
||||
For(
|
||||
Variable<'ast, T>,
|
||||
UExpression<'ast, T>,
|
||||
|
@ -630,7 +663,16 @@ impl<'ast, T: fmt::Display> fmt::Display for TypedStatement<'ast, T> {
|
|||
}
|
||||
TypedStatement::Declaration(ref var) => write!(f, "{}", var),
|
||||
TypedStatement::Definition(ref lhs, ref rhs) => write!(f, "{} = {}", lhs, rhs),
|
||||
TypedStatement::Assertion(ref e) => write!(f, "assert({})", e),
|
||||
TypedStatement::Assertion(ref e, ref error) => {
|
||||
write!(f, "assert({}", e)?;
|
||||
match error {
|
||||
RuntimeError::SourceAssertion(metadata) => match &metadata.message {
|
||||
Some(m) => write!(f, ", \"{}\")", m),
|
||||
None => write!(f, ")"),
|
||||
},
|
||||
error => write!(f, ") // {}", error),
|
||||
}
|
||||
}
|
||||
TypedStatement::For(ref var, ref start, ref stop, ref list) => {
|
||||
writeln!(f, "for {} in {}..{} do", var, start, stop)?;
|
||||
for l in list {
|
||||
|
@ -750,7 +792,7 @@ impl<'ast, T: fmt::Display> fmt::Display for StructExpression<'ast, T> {
|
|||
StructExpressionInner::FunctionCall(ref function_call) => {
|
||||
write!(f, "{}", function_call)
|
||||
}
|
||||
StructExpressionInner::IfElse(ref c) => write!(f, "{}", c),
|
||||
StructExpressionInner::Conditional(ref c) => write!(f, "{}", c),
|
||||
StructExpressionInner::Member(ref m) => write!(f, "{}", m),
|
||||
StructExpressionInner::Select(ref select) => write!(f, "{}", select),
|
||||
}
|
||||
|
@ -895,30 +937,50 @@ impl<'ast, T: fmt::Display, E> fmt::Display for SelectExpression<'ast, T, E> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Debug, Hash, Eq, PartialOrd, Ord)]
|
||||
pub struct IfElseExpression<'ast, T, E> {
|
||||
#[derive(Debug, Clone, PartialEq, Hash, Eq, PartialOrd, Ord)]
|
||||
pub enum ConditionalKind {
|
||||
IfElse,
|
||||
Ternary,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Hash, Eq, PartialOrd, Ord)]
|
||||
pub struct ConditionalExpression<'ast, T, E> {
|
||||
pub condition: Box<BooleanExpression<'ast, T>>,
|
||||
pub consequence: Box<E>,
|
||||
pub alternative: Box<E>,
|
||||
pub kind: ConditionalKind,
|
||||
}
|
||||
|
||||
impl<'ast, T, E> IfElseExpression<'ast, T, E> {
|
||||
pub fn new(condition: BooleanExpression<'ast, T>, consequence: E, alternative: E) -> Self {
|
||||
IfElseExpression {
|
||||
impl<'ast, T, E> ConditionalExpression<'ast, T, E> {
|
||||
pub fn new(
|
||||
condition: BooleanExpression<'ast, T>,
|
||||
consequence: E,
|
||||
alternative: E,
|
||||
kind: ConditionalKind,
|
||||
) -> Self {
|
||||
ConditionalExpression {
|
||||
condition: box condition,
|
||||
consequence: box consequence,
|
||||
alternative: box alternative,
|
||||
kind,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast, T: fmt::Display, E: fmt::Display> fmt::Display for IfElseExpression<'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 {
|
||||
write!(
|
||||
f,
|
||||
"if {} then {} else {} fi",
|
||||
self.condition, self.consequence, self.alternative
|
||||
)
|
||||
match self.kind {
|
||||
ConditionalKind::IfElse => write!(
|
||||
f,
|
||||
"if {} then {} else {} fi",
|
||||
self.condition, self.consequence, self.alternative
|
||||
),
|
||||
ConditionalKind::Ternary => write!(
|
||||
f,
|
||||
"{} ? {} : {}",
|
||||
self.condition, self.consequence, self.alternative
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1000,7 +1062,7 @@ pub enum FieldElementExpression<'ast, T> {
|
|||
Box<FieldElementExpression<'ast, T>>,
|
||||
Box<UExpression<'ast, T>>,
|
||||
),
|
||||
IfElse(IfElseExpression<'ast, T, Self>),
|
||||
Conditional(ConditionalExpression<'ast, T, Self>),
|
||||
Neg(Box<FieldElementExpression<'ast, T>>),
|
||||
Pos(Box<FieldElementExpression<'ast, T>>),
|
||||
FunctionCall(FunctionCallExpression<'ast, T, Self>),
|
||||
|
@ -1100,7 +1162,7 @@ pub enum BooleanExpression<'ast, T> {
|
|||
Box<BooleanExpression<'ast, T>>,
|
||||
),
|
||||
Not(Box<BooleanExpression<'ast, T>>),
|
||||
IfElse(IfElseExpression<'ast, T, Self>),
|
||||
Conditional(ConditionalExpression<'ast, T, Self>),
|
||||
Member(MemberExpression<'ast, T, Self>),
|
||||
FunctionCall(FunctionCallExpression<'ast, T, Self>),
|
||||
Select(SelectExpression<'ast, T, Self>),
|
||||
|
@ -1207,7 +1269,7 @@ pub enum ArrayExpressionInner<'ast, T> {
|
|||
Identifier(Identifier<'ast>),
|
||||
Value(ArrayValue<'ast, T>),
|
||||
FunctionCall(FunctionCallExpression<'ast, T, ArrayExpression<'ast, T>>),
|
||||
IfElse(IfElseExpression<'ast, T, ArrayExpression<'ast, T>>),
|
||||
Conditional(ConditionalExpression<'ast, T, ArrayExpression<'ast, T>>),
|
||||
Member(MemberExpression<'ast, T, ArrayExpression<'ast, T>>),
|
||||
Select(SelectExpression<'ast, T, ArrayExpression<'ast, T>>),
|
||||
Slice(
|
||||
|
@ -1271,7 +1333,7 @@ pub enum StructExpressionInner<'ast, T> {
|
|||
Identifier(Identifier<'ast>),
|
||||
Value(Vec<TypedExpression<'ast, T>>),
|
||||
FunctionCall(FunctionCallExpression<'ast, T, StructExpression<'ast, T>>),
|
||||
IfElse(IfElseExpression<'ast, T, StructExpression<'ast, T>>),
|
||||
Conditional(ConditionalExpression<'ast, T, StructExpression<'ast, T>>),
|
||||
Member(MemberExpression<'ast, T, StructExpression<'ast, T>>),
|
||||
Select(SelectExpression<'ast, T, StructExpression<'ast, T>>),
|
||||
}
|
||||
|
@ -1413,7 +1475,7 @@ impl<'ast, T: fmt::Display> fmt::Display for FieldElementExpression<'ast, T> {
|
|||
FieldElementExpression::Pow(ref lhs, ref rhs) => write!(f, "{}**{}", lhs, rhs),
|
||||
FieldElementExpression::Neg(ref e) => write!(f, "(-{})", e),
|
||||
FieldElementExpression::Pos(ref e) => write!(f, "(+{})", e),
|
||||
FieldElementExpression::IfElse(ref c) => write!(f, "{}", c),
|
||||
FieldElementExpression::Conditional(ref c) => write!(f, "{}", c),
|
||||
FieldElementExpression::FunctionCall(ref function_call) => {
|
||||
write!(f, "{}", function_call)
|
||||
}
|
||||
|
@ -1447,7 +1509,7 @@ impl<'ast, T: fmt::Display> fmt::Display for UExpression<'ast, T> {
|
|||
UExpressionInner::Pos(ref e) => write!(f, "(+{})", e),
|
||||
UExpressionInner::Select(ref select) => write!(f, "{}", select),
|
||||
UExpressionInner::FunctionCall(ref function_call) => write!(f, "{}", function_call),
|
||||
UExpressionInner::IfElse(ref c) => write!(f, "{}", c),
|
||||
UExpressionInner::Conditional(ref c) => write!(f, "{}", c),
|
||||
UExpressionInner::Member(ref m) => write!(f, "{}", m),
|
||||
}
|
||||
}
|
||||
|
@ -1476,7 +1538,7 @@ impl<'ast, T: fmt::Display> fmt::Display for BooleanExpression<'ast, T> {
|
|||
BooleanExpression::Not(ref exp) => write!(f, "!{}", exp),
|
||||
BooleanExpression::Value(b) => write!(f, "{}", b),
|
||||
BooleanExpression::FunctionCall(ref function_call) => write!(f, "{}", function_call),
|
||||
BooleanExpression::IfElse(ref c) => write!(f, "{}", c),
|
||||
BooleanExpression::Conditional(ref c) => write!(f, "{}", c),
|
||||
BooleanExpression::Member(ref m) => write!(f, "{}", m),
|
||||
BooleanExpression::Select(ref select) => write!(f, "{}", select),
|
||||
}
|
||||
|
@ -1498,7 +1560,7 @@ impl<'ast, T: fmt::Display> fmt::Display for ArrayExpressionInner<'ast, T> {
|
|||
.join(", ")
|
||||
),
|
||||
ArrayExpressionInner::FunctionCall(ref function_call) => write!(f, "{}", function_call),
|
||||
ArrayExpressionInner::IfElse(ref c) => write!(f, "{}", c),
|
||||
ArrayExpressionInner::Conditional(ref c) => write!(f, "{}", c),
|
||||
ArrayExpressionInner::Member(ref m) => write!(f, "{}", m),
|
||||
ArrayExpressionInner::Select(ref select) => write!(f, "{}", select),
|
||||
ArrayExpressionInner::Slice(ref a, ref from, ref to) => {
|
||||
|
@ -1738,81 +1800,121 @@ pub enum MemberOrExpression<'ast, T, E: Expr<'ast, T>> {
|
|||
Expression(E::Inner),
|
||||
}
|
||||
|
||||
pub enum IfElseOrExpression<'ast, T, E: Expr<'ast, T>> {
|
||||
IfElse(IfElseExpression<'ast, T, E>),
|
||||
pub enum ConditionalOrExpression<'ast, T, E: Expr<'ast, T>> {
|
||||
Conditional(ConditionalExpression<'ast, T, E>),
|
||||
Expression(E::Inner),
|
||||
}
|
||||
|
||||
pub trait IfElse<'ast, T> {
|
||||
fn if_else(condition: BooleanExpression<'ast, T>, consequence: Self, alternative: Self)
|
||||
-> Self;
|
||||
}
|
||||
|
||||
impl<'ast, T> IfElse<'ast, T> for FieldElementExpression<'ast, T> {
|
||||
fn if_else(
|
||||
pub trait Conditional<'ast, T> {
|
||||
fn conditional(
|
||||
condition: BooleanExpression<'ast, T>,
|
||||
consequence: Self,
|
||||
alternative: Self,
|
||||
kind: ConditionalKind,
|
||||
) -> Self;
|
||||
}
|
||||
|
||||
impl<'ast, T> Conditional<'ast, T> for FieldElementExpression<'ast, T> {
|
||||
fn conditional(
|
||||
condition: BooleanExpression<'ast, T>,
|
||||
consequence: Self,
|
||||
alternative: Self,
|
||||
kind: ConditionalKind,
|
||||
) -> Self {
|
||||
FieldElementExpression::IfElse(IfElseExpression::new(condition, consequence, alternative))
|
||||
FieldElementExpression::Conditional(ConditionalExpression::new(
|
||||
condition,
|
||||
consequence,
|
||||
alternative,
|
||||
kind,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast, T> IfElse<'ast, T> for IntExpression<'ast, T> {
|
||||
fn if_else(
|
||||
impl<'ast, T> Conditional<'ast, T> for IntExpression<'ast, T> {
|
||||
fn conditional(
|
||||
condition: BooleanExpression<'ast, T>,
|
||||
consequence: Self,
|
||||
alternative: Self,
|
||||
kind: ConditionalKind,
|
||||
) -> Self {
|
||||
IntExpression::IfElse(IfElseExpression::new(condition, consequence, alternative))
|
||||
IntExpression::Conditional(ConditionalExpression::new(
|
||||
condition,
|
||||
consequence,
|
||||
alternative,
|
||||
kind,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast, T> IfElse<'ast, T> for BooleanExpression<'ast, T> {
|
||||
fn if_else(
|
||||
impl<'ast, T> Conditional<'ast, T> for BooleanExpression<'ast, T> {
|
||||
fn conditional(
|
||||
condition: BooleanExpression<'ast, T>,
|
||||
consequence: Self,
|
||||
alternative: Self,
|
||||
kind: ConditionalKind,
|
||||
) -> Self {
|
||||
BooleanExpression::IfElse(IfElseExpression::new(condition, consequence, alternative))
|
||||
BooleanExpression::Conditional(ConditionalExpression::new(
|
||||
condition,
|
||||
consequence,
|
||||
alternative,
|
||||
kind,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast, T> IfElse<'ast, T> for UExpression<'ast, T> {
|
||||
fn if_else(
|
||||
impl<'ast, T> Conditional<'ast, T> for UExpression<'ast, T> {
|
||||
fn conditional(
|
||||
condition: BooleanExpression<'ast, T>,
|
||||
consequence: Self,
|
||||
alternative: Self,
|
||||
kind: ConditionalKind,
|
||||
) -> Self {
|
||||
let bitwidth = consequence.bitwidth;
|
||||
|
||||
UExpressionInner::IfElse(IfElseExpression::new(condition, consequence, alternative))
|
||||
.annotate(bitwidth)
|
||||
UExpressionInner::Conditional(ConditionalExpression::new(
|
||||
condition,
|
||||
consequence,
|
||||
alternative,
|
||||
kind,
|
||||
))
|
||||
.annotate(bitwidth)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast, T: Clone> IfElse<'ast, T> for ArrayExpression<'ast, T> {
|
||||
fn if_else(
|
||||
impl<'ast, T: Clone> Conditional<'ast, T> for ArrayExpression<'ast, T> {
|
||||
fn conditional(
|
||||
condition: BooleanExpression<'ast, T>,
|
||||
consequence: Self,
|
||||
alternative: Self,
|
||||
kind: ConditionalKind,
|
||||
) -> Self {
|
||||
let ty = consequence.inner_type().clone();
|
||||
let size = consequence.size();
|
||||
ArrayExpressionInner::IfElse(IfElseExpression::new(condition, consequence, alternative))
|
||||
.annotate(ty, size)
|
||||
ArrayExpressionInner::Conditional(ConditionalExpression::new(
|
||||
condition,
|
||||
consequence,
|
||||
alternative,
|
||||
kind,
|
||||
))
|
||||
.annotate(ty, size)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast, T: Clone> IfElse<'ast, T> for StructExpression<'ast, T> {
|
||||
fn if_else(
|
||||
impl<'ast, T: Clone> Conditional<'ast, T> for StructExpression<'ast, T> {
|
||||
fn conditional(
|
||||
condition: BooleanExpression<'ast, T>,
|
||||
consequence: Self,
|
||||
alternative: Self,
|
||||
kind: ConditionalKind,
|
||||
) -> Self {
|
||||
let ty = consequence.ty().clone();
|
||||
StructExpressionInner::IfElse(IfElseExpression::new(condition, consequence, alternative))
|
||||
.annotate(ty)
|
||||
StructExpressionInner::Conditional(ConditionalExpression::new(
|
||||
condition,
|
||||
consequence,
|
||||
alternative,
|
||||
kind,
|
||||
))
|
||||
.annotate(ty)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -184,14 +184,14 @@ pub trait ResultFolder<'ast, T: Field>: Sized {
|
|||
fold_types(self, tys)
|
||||
}
|
||||
|
||||
fn fold_if_else_expression<
|
||||
E: Expr<'ast, T> + PartialEq + IfElse<'ast, T> + ResultFold<'ast, T>,
|
||||
fn fold_conditional_expression<
|
||||
E: Expr<'ast, T> + PartialEq + Conditional<'ast, T> + ResultFold<'ast, T>,
|
||||
>(
|
||||
&mut self,
|
||||
ty: &E::Ty,
|
||||
e: IfElseExpression<'ast, T, E>,
|
||||
) -> Result<IfElseOrExpression<'ast, T, E>, Self::Error> {
|
||||
fold_if_else_expression(self, ty, e)
|
||||
e: ConditionalExpression<'ast, T, E>,
|
||||
) -> Result<ConditionalOrExpression<'ast, T, E>, Self::Error> {
|
||||
fold_conditional_expression(self, ty, e)
|
||||
}
|
||||
|
||||
fn fold_block_expression<E: ResultFold<'ast, T>>(
|
||||
|
@ -458,7 +458,9 @@ pub fn fold_statement<'ast, T: Field, F: ResultFolder<'ast, T>>(
|
|||
TypedStatement::Definition(f.fold_assignee(a)?, f.fold_expression(e)?)
|
||||
}
|
||||
TypedStatement::Declaration(v) => TypedStatement::Declaration(f.fold_variable(v)?),
|
||||
TypedStatement::Assertion(e) => TypedStatement::Assertion(f.fold_boolean_expression(e)?),
|
||||
TypedStatement::Assertion(e, error) => {
|
||||
TypedStatement::Assertion(f.fold_boolean_expression(e)?, error)
|
||||
}
|
||||
TypedStatement::For(v, from, to, statements) => TypedStatement::For(
|
||||
f.fold_variable(v)?,
|
||||
f.fold_uint_expression(from)?,
|
||||
|
@ -505,9 +507,9 @@ pub fn fold_array_expression_inner<'ast, T: Field, F: ResultFolder<'ast, T>>(
|
|||
FunctionCallOrExpression::Expression(u) => u,
|
||||
}
|
||||
}
|
||||
ArrayExpressionInner::IfElse(c) => match f.fold_if_else_expression(ty, c)? {
|
||||
IfElseOrExpression::IfElse(c) => ArrayExpressionInner::IfElse(c),
|
||||
IfElseOrExpression::Expression(u) => u,
|
||||
ArrayExpressionInner::Conditional(c) => match f.fold_conditional_expression(ty, c)? {
|
||||
ConditionalOrExpression::Conditional(c) => ArrayExpressionInner::Conditional(c),
|
||||
ConditionalOrExpression::Expression(u) => u,
|
||||
},
|
||||
ArrayExpressionInner::Member(m) => match f.fold_member_expression(ty, m)? {
|
||||
MemberOrExpression::Member(m) => ArrayExpressionInner::Member(m),
|
||||
|
@ -570,9 +572,9 @@ pub fn fold_struct_expression_inner<'ast, T: Field, F: ResultFolder<'ast, T>>(
|
|||
FunctionCallOrExpression::Expression(u) => u,
|
||||
}
|
||||
}
|
||||
StructExpressionInner::IfElse(c) => match f.fold_if_else_expression(ty, c)? {
|
||||
IfElseOrExpression::IfElse(c) => StructExpressionInner::IfElse(c),
|
||||
IfElseOrExpression::Expression(u) => u,
|
||||
StructExpressionInner::Conditional(c) => match f.fold_conditional_expression(ty, c)? {
|
||||
ConditionalOrExpression::Conditional(c) => StructExpressionInner::Conditional(c),
|
||||
ConditionalOrExpression::Expression(u) => u,
|
||||
},
|
||||
StructExpressionInner::Member(m) => match f.fold_member_expression(ty, m)? {
|
||||
MemberOrExpression::Member(m) => StructExpressionInner::Member(m),
|
||||
|
@ -633,10 +635,10 @@ pub fn fold_field_expression<'ast, T: Field, F: ResultFolder<'ast, T>>(
|
|||
|
||||
FieldElementExpression::Pos(box e)
|
||||
}
|
||||
FieldElementExpression::IfElse(c) => {
|
||||
match f.fold_if_else_expression(&Type::FieldElement, c)? {
|
||||
IfElseOrExpression::IfElse(c) => FieldElementExpression::IfElse(c),
|
||||
IfElseOrExpression::Expression(u) => u,
|
||||
FieldElementExpression::Conditional(c) => {
|
||||
match f.fold_conditional_expression(&Type::FieldElement, c)? {
|
||||
ConditionalOrExpression::Conditional(c) => FieldElementExpression::Conditional(c),
|
||||
ConditionalOrExpression::Expression(u) => u,
|
||||
}
|
||||
}
|
||||
FieldElementExpression::FunctionCall(function_call) => {
|
||||
|
@ -687,11 +689,11 @@ pub fn fold_block_expression<'ast, T: Field, E: ResultFold<'ast, T>, F: ResultFo
|
|||
})
|
||||
}
|
||||
|
||||
pub fn fold_if_else_expression<
|
||||
pub fn fold_conditional_expression<
|
||||
'ast,
|
||||
T: Field,
|
||||
E: Expr<'ast, T>
|
||||
+ IfElse<'ast, T>
|
||||
+ Conditional<'ast, T>
|
||||
+ PartialEq
|
||||
+ ResultFold<'ast, T>
|
||||
+ From<TypedExpression<'ast, T>>,
|
||||
|
@ -699,13 +701,16 @@ pub fn fold_if_else_expression<
|
|||
>(
|
||||
f: &mut F,
|
||||
_: &E::Ty,
|
||||
e: IfElseExpression<'ast, T, E>,
|
||||
) -> Result<IfElseOrExpression<'ast, T, E>, F::Error> {
|
||||
Ok(IfElseOrExpression::IfElse(IfElseExpression::new(
|
||||
f.fold_boolean_expression(*e.condition)?,
|
||||
e.consequence.fold(f)?,
|
||||
e.alternative.fold(f)?,
|
||||
)))
|
||||
e: ConditionalExpression<'ast, T, E>,
|
||||
) -> Result<ConditionalOrExpression<'ast, T, E>, F::Error> {
|
||||
Ok(ConditionalOrExpression::Conditional(
|
||||
ConditionalExpression::new(
|
||||
f.fold_boolean_expression(*e.condition)?,
|
||||
e.consequence.fold(f)?,
|
||||
e.alternative.fold(f)?,
|
||||
e.kind,
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn fold_member_expression<
|
||||
|
@ -861,10 +866,12 @@ pub fn fold_boolean_expression<'ast, T: Field, F: ResultFolder<'ast, T>>(
|
|||
FunctionCallOrExpression::Expression(u) => u,
|
||||
}
|
||||
}
|
||||
BooleanExpression::IfElse(c) => match f.fold_if_else_expression(&Type::Boolean, c)? {
|
||||
IfElseOrExpression::IfElse(c) => BooleanExpression::IfElse(c),
|
||||
IfElseOrExpression::Expression(u) => u,
|
||||
},
|
||||
BooleanExpression::Conditional(c) => {
|
||||
match f.fold_conditional_expression(&Type::Boolean, c)? {
|
||||
ConditionalOrExpression::Conditional(c) => BooleanExpression::Conditional(c),
|
||||
ConditionalOrExpression::Expression(u) => u,
|
||||
}
|
||||
}
|
||||
BooleanExpression::Select(select) => {
|
||||
match f.fold_select_expression(&Type::Boolean, select)? {
|
||||
SelectOrExpression::Select(s) => BooleanExpression::Select(s),
|
||||
|
@ -989,9 +996,9 @@ pub fn fold_uint_expression_inner<'ast, T: Field, F: ResultFolder<'ast, T>>(
|
|||
SelectOrExpression::Select(s) => UExpressionInner::Select(s),
|
||||
SelectOrExpression::Expression(u) => u,
|
||||
},
|
||||
UExpressionInner::IfElse(c) => match f.fold_if_else_expression(&ty, c)? {
|
||||
IfElseOrExpression::IfElse(c) => UExpressionInner::IfElse(c),
|
||||
IfElseOrExpression::Expression(u) => u,
|
||||
UExpressionInner::Conditional(c) => match f.fold_conditional_expression(&ty, c)? {
|
||||
ConditionalOrExpression::Conditional(c) => UExpressionInner::Conditional(c),
|
||||
ConditionalOrExpression::Expression(u) => u,
|
||||
},
|
||||
UExpressionInner::Member(m) => match f.fold_member_expression(&ty, m)? {
|
||||
MemberOrExpression::Member(m) => UExpressionInner::Member(m),
|
||||
|
|
|
@ -193,7 +193,7 @@ pub enum UExpressionInner<'ast, T> {
|
|||
FunctionCall(FunctionCallExpression<'ast, T, UExpression<'ast, T>>),
|
||||
LeftShift(Box<UExpression<'ast, T>>, Box<UExpression<'ast, T>>),
|
||||
RightShift(Box<UExpression<'ast, T>>, Box<UExpression<'ast, T>>),
|
||||
IfElse(IfElseExpression<'ast, T, UExpression<'ast, T>>),
|
||||
Conditional(ConditionalExpression<'ast, T, UExpression<'ast, T>>),
|
||||
Member(MemberExpression<'ast, T, UExpression<'ast, T>>),
|
||||
Select(SelectExpression<'ast, T, UExpression<'ast, T>>),
|
||||
}
|
||||
|
|
|
@ -115,7 +115,9 @@ pub fn fold_statement<'ast, T: Field, F: Folder<'ast, T>>(
|
|||
.flat_map(|e| f.fold_statement(e))
|
||||
.collect(),
|
||||
),
|
||||
ZirStatement::Assertion(e) => ZirStatement::Assertion(f.fold_boolean_expression(e)),
|
||||
ZirStatement::Assertion(e, error) => {
|
||||
ZirStatement::Assertion(f.fold_boolean_expression(e), error)
|
||||
}
|
||||
ZirStatement::MultipleDefinition(variables, elist) => ZirStatement::MultipleDefinition(
|
||||
variables.into_iter().map(|v| f.fold_variable(v)).collect(),
|
||||
f.fold_expression_list(elist),
|
||||
|
|
|
@ -19,7 +19,6 @@ use std::fmt;
|
|||
use zokrates_field::Field;
|
||||
|
||||
pub use self::folder::Folder;
|
||||
|
||||
pub use self::identifier::{Identifier, SourceIdentifier};
|
||||
|
||||
/// A typed program as a collection of modules, one of them being the main
|
||||
|
@ -86,8 +85,23 @@ impl<'ast, T: fmt::Debug> fmt::Debug for ZirFunction<'ast, T> {
|
|||
|
||||
pub type ZirAssignee<'ast> = Variable<'ast>;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Hash, Eq)]
|
||||
pub enum RuntimeError {
|
||||
SourceAssertion(String),
|
||||
SelectRangeCheck,
|
||||
}
|
||||
|
||||
impl fmt::Display for RuntimeError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
RuntimeError::SourceAssertion(message) => write!(f, "{}", message),
|
||||
RuntimeError::SelectRangeCheck => write!(f, "Range check on array access"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A statement in a `ZirFunction`
|
||||
#[derive(Clone, PartialEq, Hash, Eq)]
|
||||
#[derive(Clone, PartialEq, Hash, Eq, Debug)]
|
||||
pub enum ZirStatement<'ast, T> {
|
||||
Return(Vec<ZirExpression<'ast, T>>),
|
||||
Definition(ZirAssignee<'ast>, ZirExpression<'ast, T>),
|
||||
|
@ -96,37 +110,10 @@ pub enum ZirStatement<'ast, T> {
|
|||
Vec<ZirStatement<'ast, T>>,
|
||||
Vec<ZirStatement<'ast, T>>,
|
||||
),
|
||||
Assertion(BooleanExpression<'ast, T>),
|
||||
Assertion(BooleanExpression<'ast, T>, RuntimeError),
|
||||
MultipleDefinition(Vec<ZirAssignee<'ast>>, ZirExpressionList<'ast, T>),
|
||||
}
|
||||
|
||||
impl<'ast, T: fmt::Debug> fmt::Debug for ZirStatement<'ast, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
ZirStatement::Return(ref exprs) => {
|
||||
write!(f, "Return(")?;
|
||||
for (i, expr) in exprs.iter().enumerate() {
|
||||
write!(f, "{:?}", expr)?;
|
||||
if i < exprs.len() - 1 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
}
|
||||
write!(f, ")")
|
||||
}
|
||||
ZirStatement::Definition(ref consequence, ref alternative) => {
|
||||
write!(f, "Definition({:?}, {:?})", consequence, alternative)
|
||||
}
|
||||
ZirStatement::IfElse(ref condition, ref lhs, ref rhs) => {
|
||||
write!(f, "IfElse({:?}, {:?}, {:?})", condition, lhs, rhs)
|
||||
}
|
||||
ZirStatement::Assertion(ref e) => write!(f, "Assertion({:?})", e),
|
||||
ZirStatement::MultipleDefinition(ref lhs, ref rhs) => {
|
||||
write!(f, "MultipleDefinition({:?}, {:?})", lhs, rhs)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast, T: fmt::Display> fmt::Display for ZirStatement<'ast, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
|
@ -158,7 +145,13 @@ impl<'ast, T: fmt::Display> fmt::Display for ZirStatement<'ast, T> {
|
|||
.join("\n")
|
||||
)
|
||||
}
|
||||
ZirStatement::Assertion(ref e) => write!(f, "assert({})", e),
|
||||
ZirStatement::Assertion(ref e, ref error) => {
|
||||
write!(f, "assert({}", e)?;
|
||||
match error {
|
||||
RuntimeError::SourceAssertion(message) => write!(f, ", \"{}\")", message),
|
||||
error => write!(f, ") // {}", error),
|
||||
}
|
||||
}
|
||||
ZirStatement::MultipleDefinition(ref ids, ref rhs) => {
|
||||
for (i, id) in ids.iter().enumerate() {
|
||||
write!(f, "{}", id)?;
|
||||
|
|
|
@ -137,7 +137,9 @@ pub fn fold_statement<'ast, T: Field, F: ResultFolder<'ast, T>>(
|
|||
.flatten()
|
||||
.collect(),
|
||||
),
|
||||
ZirStatement::Assertion(e) => ZirStatement::Assertion(f.fold_boolean_expression(e)?),
|
||||
ZirStatement::Assertion(e, error) => {
|
||||
ZirStatement::Assertion(f.fold_boolean_expression(e)?, error)
|
||||
}
|
||||
ZirStatement::MultipleDefinition(variables, elist) => ZirStatement::MultipleDefinition(
|
||||
variables
|
||||
.into_iter()
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
"UnsatisfiedConstraint": {
|
||||
"left": "4",
|
||||
"right": "2",
|
||||
"message": "Argument bitness check failed"
|
||||
"error": "ArgumentBitness"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
"UnsatisfiedConstraint": {
|
||||
"left": "1",
|
||||
"right": "0",
|
||||
"message": "Out of bounds array access"
|
||||
"error": "SelectRangeCheck"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
32
zokrates_core_test/tests/tests/assert_array_equality.json
Normal file
32
zokrates_core_test/tests/tests/assert_array_equality.json
Normal file
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"entry_point": "./tests/tests/assert_array_equality.zok",
|
||||
"curves": ["Bn128", "Bls12_381", "Bls12_377", "Bw6_761"],
|
||||
"tests": [
|
||||
{
|
||||
"input": {
|
||||
"values": ["1", "2"]
|
||||
},
|
||||
"output": {
|
||||
"Ok": {
|
||||
"values": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"input": {
|
||||
"values": ["1", "1"]
|
||||
},
|
||||
"output": {
|
||||
"Err": {
|
||||
"UnsatisfiedConstraint": {
|
||||
"left": "0",
|
||||
"right": "1",
|
||||
"error": {
|
||||
"SourceAssertion": "Assertion failed at ./tests/tests/assert_array_equality.zok:2:2"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
3
zokrates_core_test/tests/tests/assert_array_equality.zok
Normal file
3
zokrates_core_test/tests/tests/assert_array_equality.zok
Normal file
|
@ -0,0 +1,3 @@
|
|||
def main(field[2] a):
|
||||
assert(a == [1, 2])
|
||||
return
|
|
@ -1,20 +1,22 @@
|
|||
{
|
||||
"entry_point": "./tests/tests/assert_one.zok",
|
||||
"curves": ["Bn128", "Bls12_381", "Bls12_377", "Bw6_761"],
|
||||
"tests": [
|
||||
{
|
||||
"input": {
|
||||
"values": ["0"]
|
||||
},
|
||||
"output": {
|
||||
"Err": {
|
||||
"UnsatisfiedConstraint": {
|
||||
"left": "0",
|
||||
"right": "1",
|
||||
"message": "User assertion failed"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
"entry_point": "./tests/tests/assert_one.zok",
|
||||
"curves": ["Bn128", "Bls12_381", "Bls12_377", "Bw6_761"],
|
||||
"tests": [
|
||||
{
|
||||
"input": {
|
||||
"values": ["0"]
|
||||
},
|
||||
"output": {
|
||||
"Err": {
|
||||
"UnsatisfiedConstraint": {
|
||||
"left": "0",
|
||||
"right": "1",
|
||||
"error": {
|
||||
"SourceAssertion": "Assertion failed at ./tests/tests/assert_one.zok:2:2"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"entry_point": "./tests/tests/panics/conditional_bound_throw.zok",
|
||||
"curves": ["Bn128"],
|
||||
"tests": [
|
||||
"tests": [
|
||||
{
|
||||
"input": {
|
||||
"values": [
|
||||
|
@ -9,13 +9,15 @@
|
|||
]
|
||||
},
|
||||
"output": {
|
||||
"Err": {
|
||||
"UnsatisfiedConstraint": {
|
||||
"left": "0",
|
||||
"right": "1",
|
||||
"message": "User assertion failed"
|
||||
}
|
||||
}
|
||||
"Err": {
|
||||
"UnsatisfiedConstraint": {
|
||||
"left": "0",
|
||||
"right": "1",
|
||||
"error": {
|
||||
"SourceAssertion": "Assertion failed at ./tests/tests/panics/conditional_bound_throw.zok:2:5"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -25,13 +27,15 @@
|
|||
]
|
||||
},
|
||||
"output": {
|
||||
"Err": {
|
||||
"UnsatisfiedConstraint": {
|
||||
"left": "1",
|
||||
"right": "0",
|
||||
"message": "User assertion failed"
|
||||
}
|
||||
}
|
||||
"Err": {
|
||||
"UnsatisfiedConstraint": {
|
||||
"left": "1",
|
||||
"right": "0",
|
||||
"error": {
|
||||
"SourceAssertion": "Assertion failed at ./tests/tests/panics/conditional_bound_throw.zok:2:5"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -41,13 +45,15 @@
|
|||
]
|
||||
},
|
||||
"output": {
|
||||
"Err": {
|
||||
"UnsatisfiedConstraint": {
|
||||
"left": "2",
|
||||
"right": "0",
|
||||
"message": "User assertion failed"
|
||||
}
|
||||
}
|
||||
"Err": {
|
||||
"UnsatisfiedConstraint": {
|
||||
"left": "2",
|
||||
"right": "0",
|
||||
"error": {
|
||||
"SourceAssertion": "Assertion failed at ./tests/tests/panics/conditional_bound_throw.zok:2:5"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"entry_point": "./tests/tests/panics/deep_branch.zok",
|
||||
"curves": ["Bn128"],
|
||||
"tests": [
|
||||
"tests": [
|
||||
{
|
||||
"input": {
|
||||
"values": [
|
||||
|
@ -9,13 +9,15 @@
|
|||
]
|
||||
},
|
||||
"output": {
|
||||
"Err": {
|
||||
"UnsatisfiedConstraint": {
|
||||
"left": "0",
|
||||
"right": "1",
|
||||
"message": "User assertion failed"
|
||||
}
|
||||
}
|
||||
"Err": {
|
||||
"UnsatisfiedConstraint": {
|
||||
"left": "0",
|
||||
"right": "1",
|
||||
"error": {
|
||||
"SourceAssertion": "Assertion failed at ./tests/tests/panics/deep_branch.zok:2:5"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"entry_point": "./tests/tests/panics/internal_panic.zok",
|
||||
"curves": ["Bn128"],
|
||||
"tests": [
|
||||
"tests": [
|
||||
{
|
||||
"input": {
|
||||
"values": [
|
||||
|
@ -9,9 +9,9 @@
|
|||
]
|
||||
},
|
||||
"output": {
|
||||
"Ok": {
|
||||
"values": ["1"]
|
||||
}
|
||||
"Ok": {
|
||||
"values": ["1"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -21,13 +21,13 @@
|
|||
]
|
||||
},
|
||||
"output": {
|
||||
"Err": {
|
||||
"UnsatisfiedConstraint": {
|
||||
"left": "0",
|
||||
"right": "1",
|
||||
"message": "Division by zero"
|
||||
}
|
||||
}
|
||||
"Err": {
|
||||
"UnsatisfiedConstraint": {
|
||||
"left": "0",
|
||||
"right": "1",
|
||||
"error": "Inverse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"entry_point": "./tests/tests/panics/loop_bound.zok",
|
||||
"curves": ["Bn128", "Bls12_381", "Bls12_377", "Bw6_761"],
|
||||
"tests": [
|
||||
"tests": [
|
||||
{
|
||||
"input": {
|
||||
"values": [
|
||||
|
@ -9,13 +9,15 @@
|
|||
]
|
||||
},
|
||||
"output": {
|
||||
"Err": {
|
||||
"UnsatisfiedConstraint": {
|
||||
"left": "0",
|
||||
"right": "1",
|
||||
"message": "User assertion failed"
|
||||
}
|
||||
}
|
||||
"Err": {
|
||||
"UnsatisfiedConstraint": {
|
||||
"left": "0",
|
||||
"right": "1",
|
||||
"error": {
|
||||
"SourceAssertion": "Assertion failed at ./tests/tests/panics/loop_bound.zok:2:3"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -25,9 +27,9 @@
|
|||
]
|
||||
},
|
||||
"output": {
|
||||
"Ok": {
|
||||
"values": []
|
||||
}
|
||||
"Ok": {
|
||||
"values": []
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
"isolate_branches": true
|
||||
},
|
||||
"curves": ["Bn128"],
|
||||
"tests": [
|
||||
"tests": [
|
||||
{
|
||||
"input": {
|
||||
"values": [
|
||||
|
@ -16,13 +16,15 @@
|
|||
]
|
||||
},
|
||||
"output": {
|
||||
"Err": {
|
||||
"UnsatisfiedConstraint": {
|
||||
"left": "1",
|
||||
"right": "21888242871839275222246405745257275088548364400416034343698204186575808495577",
|
||||
"message": "User assertion failed"
|
||||
}
|
||||
}
|
||||
"Err": {
|
||||
"UnsatisfiedConstraint": {
|
||||
"left": "1",
|
||||
"right": "21888242871839275222246405745257275088548364400416034343698204186575808495577",
|
||||
"error": {
|
||||
"SourceAssertion": "Assertion failed at ./tests/tests/panics/panic_isolation.zok:18:5"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -35,14 +37,14 @@
|
|||
]
|
||||
},
|
||||
"output": {
|
||||
"Ok": {
|
||||
"values": [
|
||||
"Ok": {
|
||||
"values": [
|
||||
"1",
|
||||
"1",
|
||||
"1",
|
||||
"1"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -55,14 +57,14 @@
|
|||
]
|
||||
},
|
||||
"output": {
|
||||
"Ok": {
|
||||
"values": [
|
||||
"Ok": {
|
||||
"values": [
|
||||
"0",
|
||||
"2",
|
||||
"2",
|
||||
"0"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
"isolate_branches": false
|
||||
},
|
||||
"curves": ["Bn128"],
|
||||
"tests": [
|
||||
"tests": [
|
||||
{
|
||||
"input": {
|
||||
"values": [
|
||||
|
@ -16,13 +16,15 @@
|
|||
]
|
||||
},
|
||||
"output": {
|
||||
"Err": {
|
||||
"UnsatisfiedConstraint": {
|
||||
"left": "1",
|
||||
"right": "0",
|
||||
"message": "User assertion failed"
|
||||
}
|
||||
}
|
||||
"Err": {
|
||||
"UnsatisfiedConstraint": {
|
||||
"left": "1",
|
||||
"right": "0",
|
||||
"error": {
|
||||
"SourceAssertion": "Assertion failed at ./tests/tests/panics/panic_isolation.zok:14:5"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
"UnsatisfiedConstraint": {
|
||||
"left": "9",
|
||||
"right": "3",
|
||||
"message": "Argument bitness check failed"
|
||||
"error": "ArgumentBitness"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
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": 6,
|
||||
"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"]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
5
zokrates_core_test/tests/tests/ternary.zok
Normal file
5
zokrates_core_test/tests/tests/ternary.zok
Normal file
|
@ -0,0 +1,5 @@
|
|||
def main(bool a, bool b) -> field:
|
||||
field x = a ? 1 : b ? 2 : 3 // (a ? 1 : (b ? 2 : 3))
|
||||
field y = if a then 1 else if b then 2 else 3 fi fi
|
||||
assert(x == y)
|
||||
return x
|
|
@ -3,13 +3,14 @@ file = { SOI ~ NEWLINE* ~ pragma? ~ NEWLINE* ~ symbol_declaration* ~ EOI }
|
|||
|
||||
pragma = { "#pragma" ~ "curve" ~ curve }
|
||||
curve = @{ (ASCII_ALPHANUMERIC | "_") * }
|
||||
string = @{(!"\"" ~ ANY)*}
|
||||
quoted_string = _{ "\"" ~ string ~ "\"" }
|
||||
|
||||
symbol_declaration = { (import_directive | ty_struct_definition | const_definition | function_definition) ~ NEWLINE* }
|
||||
|
||||
import_directive = { main_import_directive | from_import_directive }
|
||||
from_import_directive = { "from" ~ "\"" ~ import_source ~ "\"" ~ "import" ~ import_symbol_list ~ NEWLINE* }
|
||||
main_import_directive = { "import" ~ "\"" ~ import_source ~ "\"" ~ ("as" ~ identifier)? ~ NEWLINE+ }
|
||||
import_source = @{(!"\"" ~ ANY)*}
|
||||
from_import_directive = { "from" ~ quoted_string ~ "import" ~ import_symbol_list ~ NEWLINE* }
|
||||
main_import_directive = { "import" ~ quoted_string ~ ("as" ~ identifier)? ~ NEWLINE+ }
|
||||
import_symbol = { identifier ~ ("as" ~ identifier)? }
|
||||
import_symbol_list = _{ import_symbol ~ ("," ~ import_symbol)* }
|
||||
function_definition = {"def" ~ identifier ~ constant_generics_declaration? ~ "(" ~ parameter_list ~ ")" ~ return_types ~ ":" ~ NEWLINE* ~ statement* }
|
||||
|
@ -55,7 +56,7 @@ statement = { (return_statement // does not require subsequent newline
|
|||
iteration_statement = { "for" ~ ty ~ identifier ~ "in" ~ expression ~ ".." ~ expression ~ "do" ~ NEWLINE* ~ statement* ~ "endfor"}
|
||||
return_statement = { "return" ~ expression_list}
|
||||
definition_statement = { typed_identifier_or_assignee_list ~ "=" ~ expression } // declare and assign, so only identifiers are allowed, unlike `assignment_statement`
|
||||
expression_statement = {"assert" ~ "(" ~ expression ~ ")"}
|
||||
expression_statement = {"assert" ~ "(" ~ expression ~ ("," ~ quoted_string)? ~ ")"}
|
||||
|
||||
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`
|
||||
|
@ -67,13 +68,13 @@ expression = { unaried_term ~ (op_binary ~ unaried_term)* }
|
|||
unaried_term = { op_unary? ~ powered_term }
|
||||
powered_term = { postfixed_term ~ (op_pow ~ exponent_expression)? }
|
||||
postfixed_term = { term ~ access* }
|
||||
term = { ("(" ~ expression ~ ")") | inline_struct_expression | conditional_expression | primary_expression | inline_array_expression | array_initializer_expression }
|
||||
term = { ("(" ~ expression ~ ")") | inline_struct_expression | if_else_expression | primary_expression | inline_array_expression | array_initializer_expression }
|
||||
spread = { "..." ~ expression }
|
||||
range = { from_expression? ~ ".." ~ to_expression? }
|
||||
from_expression = { expression }
|
||||
to_expression = { expression }
|
||||
|
||||
conditional_expression = { "if" ~ expression ~ "then" ~ expression ~ "else" ~ expression ~ "fi"}
|
||||
if_else_expression = { "if" ~ expression ~ "then" ~ expression ~ "else" ~ expression ~ "fi"}
|
||||
|
||||
access = { array_access | call_access | member_access }
|
||||
array_access = { "[" ~ range_or_expression ~ "]" }
|
||||
|
@ -154,8 +155,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}
|
||||
|
|
|
@ -13,7 +13,7 @@ pub use ast::{
|
|||
CallAccess, ConstantDefinition, ConstantGenericValue, DecimalLiteralExpression, DecimalNumber,
|
||||
DecimalSuffix, DefinitionStatement, ExplicitGenerics, Expression, FieldType, File,
|
||||
FromExpression, FunctionDefinition, HexLiteralExpression, HexNumberExpression,
|
||||
IdentifierExpression, ImportDirective, ImportSource, ImportSymbol, InlineArrayExpression,
|
||||
IdentifierExpression, IfElseExpression, ImportDirective, ImportSymbol, InlineArrayExpression,
|
||||
InlineStructExpression, InlineStructMember, IterationStatement, LiteralExpression, Parameter,
|
||||
PostfixExpression, Range, RangeOrExpression, ReturnStatement, Span, Spread, SpreadOrExpression,
|
||||
Statement, StructDefinition, StructField, SymbolDeclaration, TernaryExpression, ToExpression,
|
||||
|
@ -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 => Expression::ternary(
|
||||
lhs,
|
||||
Box::new(Expression::from_pest(&mut pair.into_inner()).unwrap()),
|
||||
rhs,
|
||||
span,
|
||||
),
|
||||
_ => unreachable!(),
|
||||
})
|
||||
}
|
||||
|
@ -194,7 +201,7 @@ mod ast {
|
|||
#[derive(Debug, FromPest, PartialEq, Clone)]
|
||||
#[pest_ast(rule(Rule::main_import_directive))]
|
||||
pub struct MainImportDirective<'ast> {
|
||||
pub source: ImportSource<'ast>,
|
||||
pub source: AnyString<'ast>,
|
||||
pub alias: Option<IdentifierExpression<'ast>>,
|
||||
#[pest_ast(outer())]
|
||||
pub span: Span<'ast>,
|
||||
|
@ -212,21 +219,12 @@ mod ast {
|
|||
#[derive(Debug, FromPest, PartialEq, Clone)]
|
||||
#[pest_ast(rule(Rule::from_import_directive))]
|
||||
pub struct FromImportDirective<'ast> {
|
||||
pub source: ImportSource<'ast>,
|
||||
pub source: AnyString<'ast>,
|
||||
pub symbols: Vec<ImportSymbol<'ast>>,
|
||||
#[pest_ast(outer())]
|
||||
pub span: Span<'ast>,
|
||||
}
|
||||
|
||||
#[derive(Debug, FromPest, PartialEq, Clone)]
|
||||
#[pest_ast(rule(Rule::import_source))]
|
||||
pub struct ImportSource<'ast> {
|
||||
#[pest_ast(outer(with(span_into_str)))]
|
||||
pub value: String,
|
||||
#[pest_ast(outer())]
|
||||
pub span: Span<'ast>,
|
||||
}
|
||||
|
||||
#[derive(Debug, FromPest, PartialEq, Clone)]
|
||||
#[pest_ast(rule(Rule::ty))]
|
||||
pub enum Type<'ast> {
|
||||
|
@ -357,10 +355,20 @@ mod ast {
|
|||
pub span: Span<'ast>,
|
||||
}
|
||||
|
||||
#[derive(Debug, FromPest, PartialEq, Clone)]
|
||||
#[pest_ast(rule(Rule::string))]
|
||||
pub struct AnyString<'ast> {
|
||||
#[pest_ast(outer(with(span_into_str)))]
|
||||
pub value: String,
|
||||
#[pest_ast(outer())]
|
||||
pub span: Span<'ast>,
|
||||
}
|
||||
|
||||
#[derive(Debug, FromPest, PartialEq, Clone)]
|
||||
#[pest_ast(rule(Rule::expression_statement))]
|
||||
pub struct AssertionStatement<'ast> {
|
||||
pub expression: Expression<'ast>,
|
||||
pub message: Option<AnyString<'ast>>,
|
||||
#[pest_ast(outer())]
|
||||
pub span: Span<'ast>,
|
||||
}
|
||||
|
@ -411,6 +419,7 @@ mod ast {
|
|||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum Expression<'ast> {
|
||||
Ternary(TernaryExpression<'ast>),
|
||||
IfElse(IfElseExpression<'ast>),
|
||||
Binary(BinaryExpression<'ast>),
|
||||
Unary(UnaryExpression<'ast>),
|
||||
Postfix(PostfixExpression<'ast>),
|
||||
|
@ -426,7 +435,7 @@ mod ast {
|
|||
pub enum Term<'ast> {
|
||||
Expression(Expression<'ast>),
|
||||
InlineStruct(InlineStructExpression<'ast>),
|
||||
Ternary(TernaryExpression<'ast>),
|
||||
IfElse(IfElseExpression<'ast>),
|
||||
Primary(PrimaryExpression<'ast>),
|
||||
InlineArray(InlineArrayExpression<'ast>),
|
||||
ArrayInitializer(ArrayInitializerExpression<'ast>),
|
||||
|
@ -542,7 +551,7 @@ mod ast {
|
|||
fn from(t: Term<'ast>) -> Self {
|
||||
match t {
|
||||
Term::Expression(e) => e,
|
||||
Term::Ternary(e) => Expression::Ternary(e),
|
||||
Term::IfElse(e) => Expression::IfElse(e),
|
||||
Term::Primary(e) => e.into(),
|
||||
Term::InlineArray(e) => Expression::InlineArray(e),
|
||||
Term::InlineStruct(e) => Expression::InlineStruct(e),
|
||||
|
@ -760,27 +769,49 @@ mod ast {
|
|||
pub span: Span<'ast>,
|
||||
}
|
||||
|
||||
#[derive(Debug, FromPest, PartialEq, Clone)]
|
||||
#[pest_ast(rule(Rule::conditional_expression))]
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct TernaryExpression<'ast> {
|
||||
pub first: Box<Expression<'ast>>,
|
||||
pub second: Box<Expression<'ast>>,
|
||||
pub third: Box<Expression<'ast>>,
|
||||
pub condition: Box<Expression<'ast>>,
|
||||
pub consequence: Box<Expression<'ast>>,
|
||||
pub alternative: Box<Expression<'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> {
|
||||
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(
|
||||
first: Box<Expression<'ast>>,
|
||||
second: Box<Expression<'ast>>,
|
||||
third: Box<Expression<'ast>>,
|
||||
condition: Box<Expression<'ast>>,
|
||||
consequence: Box<Expression<'ast>>,
|
||||
alternative: Box<Expression<'ast>>,
|
||||
span: Span<'ast>,
|
||||
) -> Self {
|
||||
Expression::Ternary(TernaryExpression {
|
||||
first,
|
||||
second,
|
||||
third,
|
||||
condition,
|
||||
consequence,
|
||||
alternative,
|
||||
span,
|
||||
})
|
||||
}
|
||||
|
@ -805,6 +836,7 @@ mod ast {
|
|||
Expression::Identifier(i) => &i.span,
|
||||
Expression::Literal(c) => c.span(),
|
||||
Expression::Ternary(t) => &t.span,
|
||||
Expression::IfElse(ie) => &ie.span,
|
||||
Expression::Postfix(p) => &p.span,
|
||||
Expression::InlineArray(a) => &a.span,
|
||||
Expression::InlineStruct(s) => &s.span,
|
||||
|
@ -1070,20 +1102,6 @@ mod tests {
|
|||
pub fn pow(left: Expression<'ast>, right: Expression<'ast>, span: Span<'ast>) -> Self {
|
||||
Self::binary(BinaryOperator::Pow, Box::new(left), Box::new(right), span)
|
||||
}
|
||||
|
||||
pub fn if_else(
|
||||
condition: Expression<'ast>,
|
||||
consequence: Expression<'ast>,
|
||||
alternative: Expression<'ast>,
|
||||
span: Span<'ast>,
|
||||
) -> Self {
|
||||
Self::ternary(
|
||||
Box::new(condition),
|
||||
Box::new(consequence),
|
||||
Box::new(alternative),
|
||||
span,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1097,7 +1115,7 @@ mod tests {
|
|||
pragma: None,
|
||||
declarations: vec![
|
||||
SymbolDeclaration::Import(ImportDirective::Main(MainImportDirective {
|
||||
source: ImportSource {
|
||||
source: AnyString {
|
||||
value: String::from("foo"),
|
||||
span: Span::new(source, 8, 11).unwrap()
|
||||
},
|
||||
|
@ -1158,7 +1176,7 @@ mod tests {
|
|||
pragma: None,
|
||||
declarations: vec![
|
||||
SymbolDeclaration::Import(ImportDirective::Main(MainImportDirective {
|
||||
source: ImportSource {
|
||||
source: AnyString {
|
||||
value: String::from("foo"),
|
||||
span: Span::new(source, 8, 11).unwrap()
|
||||
},
|
||||
|
@ -1243,7 +1261,7 @@ mod tests {
|
|||
pragma: None,
|
||||
declarations: vec![
|
||||
SymbolDeclaration::Import(ImportDirective::Main(MainImportDirective {
|
||||
source: ImportSource {
|
||||
source: AnyString {
|
||||
value: String::from("foo"),
|
||||
span: Span::new(source, 8, 11).unwrap()
|
||||
},
|
||||
|
@ -1262,7 +1280,7 @@ mod tests {
|
|||
}))],
|
||||
statements: vec![Statement::Return(ReturnStatement {
|
||||
expressions: vec![Expression::if_else(
|
||||
Expression::Literal(LiteralExpression::DecimalLiteral(
|
||||
Box::new(Expression::Literal(LiteralExpression::DecimalLiteral(
|
||||
DecimalLiteralExpression {
|
||||
suffix: None,
|
||||
value: DecimalNumber {
|
||||
|
@ -1270,8 +1288,8 @@ mod tests {
|
|||
},
|
||||
span: Span::new(source, 62, 63).unwrap()
|
||||
}
|
||||
)),
|
||||
Expression::Literal(LiteralExpression::DecimalLiteral(
|
||||
))),
|
||||
Box::new(Expression::Literal(LiteralExpression::DecimalLiteral(
|
||||
DecimalLiteralExpression {
|
||||
suffix: None,
|
||||
value: DecimalNumber {
|
||||
|
@ -1279,8 +1297,8 @@ mod tests {
|
|||
},
|
||||
span: Span::new(source, 69, 70).unwrap()
|
||||
}
|
||||
)),
|
||||
Expression::Literal(LiteralExpression::DecimalLiteral(
|
||||
))),
|
||||
Box::new(Expression::Literal(LiteralExpression::DecimalLiteral(
|
||||
DecimalLiteralExpression {
|
||||
suffix: None,
|
||||
value: DecimalNumber {
|
||||
|
@ -1288,8 +1306,8 @@ mod tests {
|
|||
},
|
||||
span: Span::new(source, 76, 77).unwrap()
|
||||
}
|
||||
)),
|
||||
Span::new(source, 59, 80).unwrap()
|
||||
))),
|
||||
Span::new(&source, 59, 80).unwrap()
|
||||
)],
|
||||
span: Span::new(source, 52, 80).unwrap(),
|
||||
})],
|
||||
|
|
Loading…
Reference in a new issue