Merge pull request #762 from Zokrates/neg-pos
Add negative and positive unary operators
This commit is contained in:
commit
0579a8f981
19 changed files with 396 additions and 123 deletions
1
changelogs/unreleased/762-schaeff
Normal file
1
changelogs/unreleased/762-schaeff
Normal file
|
@ -0,0 +1 @@
|
|||
Add negative `-` and positive `+` unary operators, restricting accepted expressions in some places (exponent) to allow for better parsing
|
|
@ -1,22 +1,22 @@
|
|||
## Operators
|
||||
|
||||
The following table lists the precedence and associativity of all available operators. Operators are listed top to bottom, in ascending precedence.
|
||||
|
||||
| Operator | Description | Associativity | Remarks |
|
||||
|------------------------------|--------------------------------------------------------------|------------------------------------|---------|
|
||||
| ** <br> | Power | Left | [^1] |
|
||||
| *<br> /<br> %<br> | Multiplication <br> Division <br> Remainder | Left <br> Left <br>Left | |
|
||||
| + <br> - <br> | Addition <br> Subtraction <br> | Left <br> Left | |
|
||||
| << <br> >> <br> | Left shift <br> Right shift <br> | Left <br> Left | [^2] |
|
||||
| & | Bitwise AND | Left <br> Left | |
|
||||
| \| | Bitwise OR | Left <br> Left | |
|
||||
| ^ | Bitwise XOR | Left <br> Left | |
|
||||
| >= <br><br> > <br> <= <br> < | Greater or equal <br> Greater <br> Lower or equal <br> Lower | Left <br> Left <br> Left <br> Left | [^3] |
|
||||
| != <br> == <br> | Not Equal <br> Equal <br> | Left <br> Left | |
|
||||
| && | Boolean AND | Left | |
|
||||
| \|\| | Boolean OR | Left | |
|
||||
|
||||
The following table lists the precedence and associativity of all operators. Operators are listed top to bottom, in ascending precedence. Operators in the same box group left to right. Operators are binary, unless the syntax is provided.
|
||||
|
||||
| Operator | Description | Remarks |
|
||||
|---------------------------------|-------------------------------------------------------------------|---------|
|
||||
| `**` <br> | Power | [^1] |
|
||||
| `+x` <br> `-x` <br> `!x` <br> | Positive <br> Negative <br> Negation <br> | |
|
||||
| `*` <br> `/` <br> `%` <br> | Multiplication <br> Division <br> Remainder <br> | |
|
||||
| `+` <br> `-` <br> | Addition <br> Subtraction <br> | |
|
||||
| `<<` <br> `>>` <br> | Left shift <br> Right shift <br> | [^2] |
|
||||
| `&` | Bitwise AND | |
|
||||
| <code>|</code> | Bitwise OR | |
|
||||
| `^` | Bitwise XOR | |
|
||||
| `>=` <br> `>` <br> `<=` <br> `<`| Greater or equal <br> Greater <br> Lower or equal <br> Lower <br> | [^3] |
|
||||
| `!=` <br> `==` <br> | Not Equal <br> Equal <br> | |
|
||||
| `&&` | Boolean AND | |
|
||||
| <code>||</code> | Boolean OR | |
|
||||
| `if c then x else y fi` | Conditional expression | |
|
||||
|
||||
[^1]: The exponent must be a compile-time constant of type `u32`
|
||||
|
||||
|
|
3
zokrates_cli/examples/compile_errors/double_negation.zok
Normal file
3
zokrates_cli/examples/compile_errors/double_negation.zok
Normal file
|
@ -0,0 +1,3 @@
|
|||
def main():
|
||||
field a = - - 1
|
||||
return
|
3
zokrates_cli/examples/compile_errors/double_power.zok
Normal file
3
zokrates_cli/examples/compile_errors/double_power.zok
Normal file
|
@ -0,0 +1,3 @@
|
|||
def main():
|
||||
field a = 1**2**3 // parentheses are required here
|
||||
return
|
|
@ -513,10 +513,12 @@ impl<'ast> From<pest::UnaryExpression<'ast>> for absy::ExpressionNode<'ast> {
|
|||
fn from(unary: pest::UnaryExpression<'ast>) -> absy::ExpressionNode<'ast> {
|
||||
use crate::absy::NodeValue;
|
||||
|
||||
let expression = Box::new(absy::ExpressionNode::from(*unary.expression));
|
||||
|
||||
match unary.op {
|
||||
pest::UnaryOperator::Not(_) => {
|
||||
absy::Expression::Not(Box::new(absy::ExpressionNode::from(*unary.expression)))
|
||||
}
|
||||
pest::UnaryOperator::Not(..) => absy::Expression::Not(expression),
|
||||
pest::UnaryOperator::Neg(..) => absy::Expression::Neg(expression),
|
||||
pest::UnaryOperator::Pos(..) => absy::Expression::Pos(expression),
|
||||
}
|
||||
.span(unary.span)
|
||||
}
|
||||
|
|
|
@ -502,6 +502,8 @@ pub enum Expression<'ast> {
|
|||
Div(Box<ExpressionNode<'ast>>, Box<ExpressionNode<'ast>>),
|
||||
Rem(Box<ExpressionNode<'ast>>, Box<ExpressionNode<'ast>>),
|
||||
Pow(Box<ExpressionNode<'ast>>, Box<ExpressionNode<'ast>>),
|
||||
Neg(Box<ExpressionNode<'ast>>),
|
||||
Pos(Box<ExpressionNode<'ast>>),
|
||||
IfElse(
|
||||
Box<ExpressionNode<'ast>>,
|
||||
Box<ExpressionNode<'ast>>,
|
||||
|
@ -549,6 +551,8 @@ impl<'ast> fmt::Display for Expression<'ast> {
|
|||
Expression::Div(ref lhs, ref rhs) => write!(f, "({} / {})", lhs, rhs),
|
||||
Expression::Rem(ref lhs, ref rhs) => write!(f, "({} % {})", lhs, rhs),
|
||||
Expression::Pow(ref lhs, ref rhs) => write!(f, "({}**{})", lhs, rhs),
|
||||
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,
|
||||
|
@ -633,6 +637,8 @@ impl<'ast> fmt::Debug for Expression<'ast> {
|
|||
Expression::Div(ref lhs, ref rhs) => write!(f, "Div({:?}, {:?})", lhs, rhs),
|
||||
Expression::Rem(ref lhs, ref rhs) => write!(f, "Rem({:?}, {:?})", lhs, rhs),
|
||||
Expression::Pow(ref lhs, ref rhs) => write!(f, "Pow({:?}, {:?})", lhs, rhs),
|
||||
Expression::Neg(ref e) => write!(f, "Neg({:?})", e),
|
||||
Expression::Pos(ref e) => write!(f, "Pos({:?})", e),
|
||||
Expression::BooleanConstant(b) => write!(f, "{}", b),
|
||||
Expression::IfElse(ref condition, ref consequent, ref alternative) => write!(
|
||||
f,
|
||||
|
|
|
@ -1733,6 +1733,44 @@ impl<'ast, T: Field> Checker<'ast, T> {
|
|||
}),
|
||||
}
|
||||
}
|
||||
Expression::Neg(box e) => {
|
||||
let e = self.check_expression(e, module_id, &types)?;
|
||||
|
||||
match e {
|
||||
TypedExpression::Int(e) => Ok(IntExpression::Neg(box e).into()),
|
||||
TypedExpression::FieldElement(e) => {
|
||||
Ok(FieldElementExpression::Neg(box e).into())
|
||||
}
|
||||
TypedExpression::Uint(e) => Ok((-e).into()),
|
||||
e => Err(ErrorInner {
|
||||
pos: Some(pos),
|
||||
message: format!(
|
||||
"Unary operator `-` cannot be applied to {} of type {}",
|
||||
e,
|
||||
e.get_type()
|
||||
),
|
||||
}),
|
||||
}
|
||||
}
|
||||
Expression::Pos(box e) => {
|
||||
let e = self.check_expression(e, module_id, &types)?;
|
||||
|
||||
match e {
|
||||
TypedExpression::Int(e) => Ok(IntExpression::Pos(box e).into()),
|
||||
TypedExpression::FieldElement(e) => {
|
||||
Ok(FieldElementExpression::Pos(box e).into())
|
||||
}
|
||||
TypedExpression::Uint(e) => Ok(UExpression::pos(e).into()),
|
||||
e => Err(ErrorInner {
|
||||
pos: Some(pos),
|
||||
message: format!(
|
||||
"Unary operator `+` cannot be applied to {} of type {}",
|
||||
e,
|
||||
e.get_type()
|
||||
),
|
||||
}),
|
||||
}
|
||||
}
|
||||
Expression::IfElse(box condition, box consequence, box alternative) => {
|
||||
let condition_checked = self.check_expression(condition, module_id, &types)?;
|
||||
let consequence_checked = self.check_expression(consequence, module_id, &types)?;
|
||||
|
|
|
@ -562,6 +562,15 @@ pub fn fold_field_expression<'ast, T: Field>(
|
|||
let e2 = f.fold_uint_expression(e2);
|
||||
zir::FieldElementExpression::Pow(box e1, box e2)
|
||||
}
|
||||
typed_absy::FieldElementExpression::Neg(box e) => {
|
||||
let e = f.fold_field_expression(e);
|
||||
|
||||
zir::FieldElementExpression::Sub(
|
||||
box zir::FieldElementExpression::Number(T::zero()),
|
||||
box e,
|
||||
)
|
||||
}
|
||||
typed_absy::FieldElementExpression::Pos(box e) => f.fold_field_expression(e),
|
||||
typed_absy::FieldElementExpression::IfElse(box cond, box cons, box alt) => {
|
||||
let cond = f.fold_boolean_expression(cond);
|
||||
let cons = f.fold_field_expression(cons);
|
||||
|
@ -861,6 +870,18 @@ pub fn fold_uint_expression_inner<'ast, T: Field>(
|
|||
|
||||
zir::UExpressionInner::Not(box e)
|
||||
}
|
||||
typed_absy::UExpressionInner::Neg(box e) => {
|
||||
let bitwidth = e.bitwidth();
|
||||
|
||||
f.fold_uint_expression(typed_absy::UExpressionInner::Value(0).annotate(bitwidth) - e)
|
||||
.into_inner()
|
||||
}
|
||||
|
||||
typed_absy::UExpressionInner::Pos(box e) => {
|
||||
let e = f.fold_uint_expression(e);
|
||||
|
||||
e.into_inner()
|
||||
}
|
||||
typed_absy::UExpressionInner::FunctionCall(..) => {
|
||||
unreachable!("function calls should have been removed")
|
||||
}
|
||||
|
|
|
@ -862,6 +862,23 @@ impl<'ast, 'a, T: Field> ResultFolder<'ast, T> for Propagator<'ast, 'a, T> {
|
|||
e => UExpressionInner::Not(box e.annotate(bitwidth)),
|
||||
}
|
||||
}
|
||||
UExpressionInner::Neg(box e) => {
|
||||
let e = self.fold_uint_expression(e)?.into_inner();
|
||||
match e {
|
||||
UExpressionInner::Value(v) => UExpressionInner::Value(
|
||||
(0u128.wrapping_sub(v))
|
||||
% 2_u128.pow(bitwidth.to_usize().try_into().unwrap()),
|
||||
),
|
||||
e => UExpressionInner::Neg(box e.annotate(bitwidth)),
|
||||
}
|
||||
}
|
||||
UExpressionInner::Pos(box e) => {
|
||||
let e = self.fold_uint_expression(e)?.into_inner();
|
||||
match e {
|
||||
UExpressionInner::Value(v) => UExpressionInner::Value(v),
|
||||
e => UExpressionInner::Pos(box e.annotate(bitwidth)),
|
||||
}
|
||||
}
|
||||
UExpressionInner::Select(box array, box index) => {
|
||||
let array = self.fold_array_expression(array)?;
|
||||
let index = self.fold_uint_expression(index)?;
|
||||
|
@ -980,6 +997,14 @@ impl<'ast, 'a, T: Field> ResultFolder<'ast, T> for Propagator<'ast, 'a, T> {
|
|||
}
|
||||
(e1, e2) => FieldElementExpression::Div(box e1, box e2),
|
||||
},
|
||||
FieldElementExpression::Neg(box e) => match self.fold_field_expression(e)? {
|
||||
FieldElementExpression::Number(n) => FieldElementExpression::Number(T::zero() - n),
|
||||
e => FieldElementExpression::Neg(box e),
|
||||
},
|
||||
FieldElementExpression::Pos(box e) => match self.fold_field_expression(e)? {
|
||||
FieldElementExpression::Number(n) => FieldElementExpression::Number(n),
|
||||
e => FieldElementExpression::Pos(box e),
|
||||
},
|
||||
FieldElementExpression::Pow(box e1, box e2) => {
|
||||
let e1 = self.fold_field_expression(e1)?;
|
||||
let e2 = self.fold_uint_expression(e2)?;
|
||||
|
@ -1361,8 +1386,8 @@ impl<'ast, 'a, T: Field> ResultFolder<'ast, T> for Propagator<'ast, 'a, T> {
|
|||
let e2 = self.fold_uint_expression(e2)?;
|
||||
|
||||
match (e1.as_inner(), e2.as_inner()) {
|
||||
(UExpressionInner::Value(v1), UExpressionInner::Value(v2)) => {
|
||||
BooleanExpression::Value(v1 == v2)
|
||||
(UExpressionInner::Value(n1), UExpressionInner::Value(n2)) => {
|
||||
BooleanExpression::Value(n1 == n2)
|
||||
}
|
||||
_ => BooleanExpression::UintEq(box e1, box e2),
|
||||
}
|
||||
|
|
|
@ -357,6 +357,16 @@ pub fn fold_field_expression<'ast, T: Field, F: Folder<'ast, T>>(
|
|||
let e2 = f.fold_uint_expression(e2);
|
||||
FieldElementExpression::Pow(box e1, box e2)
|
||||
}
|
||||
FieldElementExpression::Neg(box e) => {
|
||||
let e = f.fold_field_expression(e);
|
||||
|
||||
FieldElementExpression::Neg(box e)
|
||||
}
|
||||
FieldElementExpression::Pos(box e) => {
|
||||
let e = f.fold_field_expression(e);
|
||||
|
||||
FieldElementExpression::Pos(box e)
|
||||
}
|
||||
FieldElementExpression::IfElse(box cond, box cons, box alt) => {
|
||||
let cond = f.fold_boolean_expression(cond);
|
||||
let cons = f.fold_field_expression(cons);
|
||||
|
@ -591,6 +601,16 @@ pub fn fold_uint_expression_inner<'ast, T: Field, F: Folder<'ast, T>>(
|
|||
|
||||
UExpressionInner::Not(box e)
|
||||
}
|
||||
UExpressionInner::Neg(box e) => {
|
||||
let e = f.fold_uint_expression(e);
|
||||
|
||||
UExpressionInner::Neg(box e)
|
||||
}
|
||||
UExpressionInner::Pos(box e) => {
|
||||
let e = f.fold_uint_expression(e);
|
||||
|
||||
UExpressionInner::Pos(box e)
|
||||
}
|
||||
UExpressionInner::FunctionCall(key, generics, exps) => {
|
||||
let generics = generics
|
||||
.into_iter()
|
||||
|
|
|
@ -8,7 +8,7 @@ use crate::typed_absy::{
|
|||
use num_bigint::BigUint;
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
use std::ops::{Add, Div, Mul, Not, Rem, Sub};
|
||||
use std::ops::{Add, Div, Mul, Neg, Not, Rem, Sub};
|
||||
use zokrates_field::Field;
|
||||
|
||||
type TypedExpressionPair<'ast, T> = (TypedExpression<'ast, T>, TypedExpression<'ast, T>);
|
||||
|
@ -134,6 +134,8 @@ impl<'ast, T: Field> TypedExpression<'ast, T> {
|
|||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum IntExpression<'ast, T> {
|
||||
Value(BigUint),
|
||||
Pos(Box<IntExpression<'ast, T>>),
|
||||
Neg(Box<IntExpression<'ast, T>>),
|
||||
Add(Box<IntExpression<'ast, T>>, Box<IntExpression<'ast, T>>),
|
||||
Sub(Box<IntExpression<'ast, T>>, Box<IntExpression<'ast, T>>),
|
||||
Mult(Box<IntExpression<'ast, T>>, Box<IntExpression<'ast, T>>),
|
||||
|
@ -202,6 +204,14 @@ impl<'ast, T> Not for IntExpression<'ast, T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'ast, T> Neg for IntExpression<'ast, T> {
|
||||
type Output = Self;
|
||||
|
||||
fn neg(self) -> Self {
|
||||
IntExpression::Neg(box self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast, T> IntExpression<'ast, T> {
|
||||
pub fn pow(self, other: Self) -> Self {
|
||||
IntExpression::Pow(box self, box other)
|
||||
|
@ -226,12 +236,18 @@ impl<'ast, T> IntExpression<'ast, T> {
|
|||
pub fn right_shift(self, by: UExpression<'ast, T>) -> Self {
|
||||
IntExpression::RightShift(box self, box by)
|
||||
}
|
||||
|
||||
pub fn pos(self) -> Self {
|
||||
IntExpression::Pos(box self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast, T: fmt::Display> fmt::Display for IntExpression<'ast, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
IntExpression::Value(ref v) => write!(f, "{}", v),
|
||||
IntExpression::Pos(ref e) => write!(f, "(+{})", e),
|
||||
IntExpression::Neg(ref e) => write!(f, "(-{})", e),
|
||||
IntExpression::Div(ref lhs, ref rhs) => write!(f, "({} / {})", lhs, rhs),
|
||||
IntExpression::Rem(ref lhs, ref rhs) => write!(f, "({} % {})", lhs, rhs),
|
||||
IntExpression::Pow(ref lhs, ref rhs) => write!(f, "({} ** {})", lhs, rhs),
|
||||
|
@ -297,6 +313,8 @@ impl<'ast, T: Field> FieldElementExpression<'ast, T> {
|
|||
box Self::try_from_int(e1)?,
|
||||
box Self::try_from_int(e2)?,
|
||||
)),
|
||||
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(box condition, box consequence, box alternative) => {
|
||||
Ok(Self::IfElse(
|
||||
box condition,
|
||||
|
@ -374,6 +392,8 @@ impl<'ast, T: Field> UExpression<'ast, T> {
|
|||
Add(box e1, box e2) => {
|
||||
Ok(Self::try_from_int(e1, bitwidth)? + Self::try_from_int(e2, bitwidth)?)
|
||||
}
|
||||
Pos(box e) => Ok(Self::pos(Self::try_from_int(e, bitwidth)?)),
|
||||
Neg(box e) => Ok(Self::neg(Self::try_from_int(e, bitwidth)?)),
|
||||
Sub(box e1, box e2) => {
|
||||
Ok(Self::try_from_int(e1, bitwidth)? - Self::try_from_int(e2, bitwidth)?)
|
||||
}
|
||||
|
|
|
@ -809,6 +809,8 @@ pub enum FieldElementExpression<'ast, T> {
|
|||
Box<FieldElementExpression<'ast, T>>,
|
||||
Box<FieldElementExpression<'ast, T>>,
|
||||
),
|
||||
Neg(Box<FieldElementExpression<'ast, T>>),
|
||||
Pos(Box<FieldElementExpression<'ast, T>>),
|
||||
FunctionCall(
|
||||
DeclarationFunctionKey<'ast>,
|
||||
Vec<Option<UExpression<'ast, T>>>,
|
||||
|
@ -1232,6 +1234,8 @@ impl<'ast, T: fmt::Display> fmt::Display for FieldElementExpression<'ast, T> {
|
|||
FieldElementExpression::Mult(ref lhs, ref rhs) => write!(f, "({} * {})", lhs, rhs),
|
||||
FieldElementExpression::Div(ref lhs, ref rhs) => write!(f, "({} / {})", lhs, rhs),
|
||||
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 condition, ref consequent, ref alternative) => {
|
||||
write!(
|
||||
f,
|
||||
|
@ -1289,6 +1293,8 @@ impl<'ast, T: fmt::Display> fmt::Display for UExpression<'ast, T> {
|
|||
UExpressionInner::RightShift(ref e, ref by) => write!(f, "({} >> {})", e, by),
|
||||
UExpressionInner::LeftShift(ref e, ref by) => write!(f, "({} << {})", e, by),
|
||||
UExpressionInner::Not(ref e) => write!(f, "!{}", e),
|
||||
UExpressionInner::Neg(ref e) => write!(f, "(-{})", e),
|
||||
UExpressionInner::Pos(ref e) => write!(f, "(+{})", e),
|
||||
UExpressionInner::Select(ref id, ref index) => write!(f, "{}[{}]", id, index),
|
||||
UExpressionInner::FunctionCall(ref k, ref generics, ref p) => {
|
||||
write!(f, "{}", k.id,)?;
|
||||
|
@ -1516,6 +1522,8 @@ impl<'ast, T: fmt::Debug> fmt::Debug for FieldElementExpression<'ast, T> {
|
|||
}
|
||||
FieldElementExpression::Div(ref lhs, ref rhs) => write!(f, "Div({:?}, {:?})", lhs, rhs),
|
||||
FieldElementExpression::Pow(ref lhs, ref rhs) => write!(f, "Pow({:?}, {:?})", lhs, rhs),
|
||||
FieldElementExpression::Neg(ref e) => write!(f, "Neg({:?})", e),
|
||||
FieldElementExpression::Pos(ref e) => write!(f, "Pos({:?})", e),
|
||||
FieldElementExpression::IfElse(ref condition, ref consequent, ref alternative) => {
|
||||
write!(
|
||||
f,
|
||||
|
|
|
@ -417,6 +417,16 @@ pub fn fold_field_expression<'ast, T: Field, F: ResultFolder<'ast, T>>(
|
|||
let e2 = f.fold_uint_expression(e2)?;
|
||||
FieldElementExpression::Pow(box e1, box e2)
|
||||
}
|
||||
FieldElementExpression::Neg(box e) => {
|
||||
let e = f.fold_field_expression(e)?;
|
||||
|
||||
FieldElementExpression::Neg(box e)
|
||||
}
|
||||
FieldElementExpression::Pos(box e) => {
|
||||
let e = f.fold_field_expression(e)?;
|
||||
|
||||
FieldElementExpression::Pos(box e)
|
||||
}
|
||||
FieldElementExpression::IfElse(box cond, box cons, box alt) => {
|
||||
let cond = f.fold_boolean_expression(cond)?;
|
||||
let cons = f.fold_field_expression(cons)?;
|
||||
|
@ -659,6 +669,16 @@ pub fn fold_uint_expression_inner<'ast, T: Field, F: ResultFolder<'ast, T>>(
|
|||
|
||||
UExpressionInner::Not(box e)
|
||||
}
|
||||
UExpressionInner::Neg(box e) => {
|
||||
let e = f.fold_uint_expression(e)?;
|
||||
|
||||
UExpressionInner::Neg(box e)
|
||||
}
|
||||
UExpressionInner::Pos(box e) => {
|
||||
let e = f.fold_uint_expression(e)?;
|
||||
|
||||
UExpressionInner::Pos(box e)
|
||||
}
|
||||
UExpressionInner::FunctionCall(key, generics, exps) => {
|
||||
let generics = generics
|
||||
.into_iter()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::typed_absy::types::UBitwidth;
|
||||
use crate::typed_absy::*;
|
||||
use std::ops::{Add, Div, Mul, Not, Rem, Sub};
|
||||
use std::ops::{Add, Div, Mul, Neg, Not, Rem, Sub};
|
||||
use zokrates_field::Field;
|
||||
|
||||
type Bitwidth = usize;
|
||||
|
@ -69,6 +69,15 @@ impl<'ast, T> Not for UExpression<'ast, T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'ast, T> Neg for UExpression<'ast, T> {
|
||||
type Output = Self;
|
||||
|
||||
fn neg(self) -> UExpression<'ast, T> {
|
||||
let bitwidth = self.bitwidth;
|
||||
UExpressionInner::Neg(box self).annotate(bitwidth)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast, T: Field> UExpression<'ast, T> {
|
||||
pub fn xor(self, other: Self) -> UExpression<'ast, T> {
|
||||
let bitwidth = self.bitwidth;
|
||||
|
@ -88,6 +97,11 @@ impl<'ast, T: Field> UExpression<'ast, T> {
|
|||
UExpressionInner::And(box self, box other).annotate(bitwidth)
|
||||
}
|
||||
|
||||
pub fn pos(self) -> UExpression<'ast, T> {
|
||||
let bitwidth = self.bitwidth;
|
||||
UExpressionInner::Pos(box self).annotate(bitwidth)
|
||||
}
|
||||
|
||||
pub fn left_shift(self, by: UExpression<'ast, T>) -> UExpression<'ast, T> {
|
||||
let bitwidth = self.bitwidth;
|
||||
assert_eq!(by.bitwidth, UBitwidth::B32);
|
||||
|
@ -173,6 +187,8 @@ pub enum UExpressionInner<'ast, T> {
|
|||
And(Box<UExpression<'ast, T>>, Box<UExpression<'ast, T>>),
|
||||
Or(Box<UExpression<'ast, T>>, Box<UExpression<'ast, T>>),
|
||||
Not(Box<UExpression<'ast, T>>),
|
||||
Neg(Box<UExpression<'ast, T>>),
|
||||
Pos(Box<UExpression<'ast, T>>),
|
||||
FunctionCall(
|
||||
DeclarationFunctionKey<'ast>,
|
||||
Vec<Option<UExpression<'ast, T>>>,
|
||||
|
|
15
zokrates_core_test/tests/tests/neg_pos.json
Normal file
15
zokrates_core_test/tests/tests/neg_pos.json
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"entry_point": "./tests/tests/neg_pos.zok",
|
||||
"tests": [
|
||||
{
|
||||
"input": {
|
||||
"values": ["1", "2", "1", "2"]
|
||||
},
|
||||
"output": {
|
||||
"Ok": {
|
||||
"values": ["21888242871839275222246405745257275088548364400416034343698204186575808495615", "21888242871839275222246405745257275088548364400416034343698204186575808495616", "21888242871839275222246405745257275088548364400416034343698204186575808495616", "21888242871839275222246405745257275088548364400416034343698204186575808495616", "254", "255", "255", "255"]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
15
zokrates_core_test/tests/tests/neg_pos.zok
Normal file
15
zokrates_core_test/tests/tests/neg_pos.zok
Normal file
|
@ -0,0 +1,15 @@
|
|||
def main(field x, field y, u8 z, u8 t) -> (field[4], u8[4]):
|
||||
field a = -y // should parse to neg
|
||||
field b = x - y // should parse to sub
|
||||
field c = x + - y // should parse to add(neg)
|
||||
field d = x - + y // should parse to sub(pos)
|
||||
|
||||
u8 e = -t // should parse to neg
|
||||
u8 f = z - t // should parse to sub
|
||||
u8 g = z + - t // should parse to add(neg)
|
||||
u8 h = z - + t // should parse to sub(pos)
|
||||
|
||||
assert(-0x00 == 0x00)
|
||||
assert(-0f == 0)
|
||||
|
||||
return [a, b, c, d], [e, f, g, h]
|
|
@ -13,6 +13,9 @@ def main():
|
|||
|
||||
assert(0x00 ^ 0x00 == 0x00)
|
||||
|
||||
assert(0 - 2 ** 2 == -4)
|
||||
assert(-2**2 == -4)
|
||||
|
||||
//check if all statements have evalutated to true
|
||||
assert(a * b * c * d * e * f == 1)
|
||||
return
|
||||
|
|
|
@ -57,8 +57,10 @@ optionally_typed_assignee = { (ty ~ assignee) | (assignee) } // we don't use { t
|
|||
// Expressions
|
||||
expression_list = _{(expression ~ ("," ~ expression)*)?}
|
||||
|
||||
expression = { term ~ (op_binary ~ term)* }
|
||||
term = { ("(" ~ expression ~ ")") | inline_struct_expression | conditional_expression | postfix_expression | primary_expression | inline_array_expression | array_initializer_expression | unary_expression }
|
||||
expression = { unaried_term ~ (op_binary ~ unaried_term)* }
|
||||
unaried_term = { op_unary? ~ powered_term }
|
||||
powered_term = { term ~ (op_pow ~ exponent_expression)? }
|
||||
term = { ("(" ~ expression ~ ")") | inline_struct_expression | conditional_expression | postfix_expression | primary_expression | inline_array_expression | array_initializer_expression }
|
||||
spread = { "..." ~ expression }
|
||||
range = { from_expression? ~ ".." ~ to_expression? }
|
||||
from_expression = { expression }
|
||||
|
@ -89,9 +91,9 @@ inline_array_expression = { "[" ~ NEWLINE* ~ inline_array_inner ~ NEWLINE* ~ "]"
|
|||
inline_array_inner = _{(spread_or_expression ~ ("," ~ NEWLINE* ~ spread_or_expression)*)?}
|
||||
spread_or_expression = { spread | expression }
|
||||
range_or_expression = { range | expression }
|
||||
array_initializer_expression = { "[" ~ expression ~ ";" ~ expression ~ "]" }
|
||||
|
||||
unary_expression = { op_unary ~ term }
|
||||
exponent_expression = { "(" ~ expression ~ ")" | primary_expression }
|
||||
array_initializer_expression = { "[" ~ expression ~ ";" ~ expression ~ "]" }
|
||||
|
||||
// End Expressions
|
||||
|
||||
|
@ -137,13 +139,15 @@ op_sub = {"-"}
|
|||
op_mul = {"*"}
|
||||
op_div = {"/"}
|
||||
op_rem = {"%"}
|
||||
op_pow = @{"**"}
|
||||
op_pow = @{"**"}
|
||||
op_not = {"!"}
|
||||
op_neg = {"-"}
|
||||
op_pos = {"+"}
|
||||
op_left_shift = @{"<<"}
|
||||
op_right_shift = @{">>"}
|
||||
op_binary = _ { op_pow | 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_unary = { op_not }
|
||||
|
||||
// `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_unary = { op_pos | op_neg | op_not }
|
||||
|
||||
WHITESPACE = _{ " " | "\t" | "\\" ~ NEWLINE}
|
||||
COMMENT = _{ ("/*" ~ (!"*/" ~ ANY)* ~ "*/") | ("//" ~ (!NEWLINE ~ ANY)*) }
|
||||
|
|
|
@ -54,7 +54,6 @@ mod ast {
|
|||
Operator::new(Rule::op_mul, Assoc::Left)
|
||||
| Operator::new(Rule::op_div, Assoc::Left)
|
||||
| Operator::new(Rule::op_rem, Assoc::Left),
|
||||
Operator::new(Rule::op_pow, Assoc::Left),
|
||||
])
|
||||
}
|
||||
|
||||
|
@ -76,7 +75,6 @@ mod ast {
|
|||
Rule::op_mul => Expression::binary(BinaryOperator::Mul, lhs, rhs, span),
|
||||
Rule::op_div => Expression::binary(BinaryOperator::Div, lhs, rhs, span),
|
||||
Rule::op_rem => Expression::binary(BinaryOperator::Rem, lhs, rhs, span),
|
||||
Rule::op_pow => Expression::binary(BinaryOperator::Pow, lhs, rhs, span),
|
||||
Rule::op_equal => Expression::binary(BinaryOperator::Eq, lhs, rhs, span),
|
||||
Rule::op_not_equal => Expression::binary(BinaryOperator::NotEq, lhs, rhs, span),
|
||||
Rule::op_lte => Expression::binary(BinaryOperator::Lte, lhs, rhs, span),
|
||||
|
@ -99,74 +97,12 @@ mod ast {
|
|||
PREC_CLIMBER.climb(pair.into_inner(), build_factor, infix_rule)
|
||||
}
|
||||
|
||||
// Create an Expression from a `term`.
|
||||
// Precondition: `pair` MUST be a term
|
||||
// Create an Expression from a `unaried_term`.
|
||||
// Precondition: `pair` MUST be a `unaried_term`
|
||||
fn build_factor(pair: Pair<Rule>) -> Box<Expression> {
|
||||
Box::new(match pair.as_rule() {
|
||||
Rule::term => {
|
||||
// clone the pair to peek into what we should create
|
||||
let clone = pair.clone();
|
||||
// define the child pair
|
||||
let next = clone.into_inner().next().unwrap();
|
||||
match next.as_rule() {
|
||||
// this happens when we have an expression in parentheses: it needs to be processed as another sequence of terms and operators
|
||||
Rule::expression => Expression::from_pest(&mut pair.into_inner()).unwrap(),
|
||||
Rule::conditional_expression => Expression::Ternary(
|
||||
TernaryExpression::from_pest(&mut pair.into_inner()).unwrap(),
|
||||
),
|
||||
Rule::primary_expression => {
|
||||
// maybe this could be simplified
|
||||
let next = next.into_inner().next().unwrap();
|
||||
match next.as_rule() {
|
||||
Rule::literal => Expression::Literal(
|
||||
LiteralExpression::from_pest(
|
||||
&mut pair.into_inner().next().unwrap().into_inner(),
|
||||
)
|
||||
.unwrap(),
|
||||
),
|
||||
Rule::identifier => Expression::Identifier(
|
||||
IdentifierExpression::from_pest(
|
||||
&mut pair.into_inner().next().unwrap().into_inner(),
|
||||
)
|
||||
.unwrap(),
|
||||
),
|
||||
r => unreachable!("`primary_expression` should contain one of [`literal`, `identifier`], found {:#?}", r),
|
||||
}
|
||||
}
|
||||
Rule::postfix_expression => Expression::Postfix(
|
||||
PostfixExpression::from_pest(&mut pair.into_inner()).unwrap(),
|
||||
),
|
||||
Rule::inline_struct_expression => Expression::InlineStruct(
|
||||
InlineStructExpression::from_pest(&mut pair.into_inner()).unwrap(),
|
||||
),
|
||||
Rule::inline_array_expression => Expression::InlineArray(
|
||||
InlineArrayExpression::from_pest(&mut pair.into_inner()).unwrap(),
|
||||
),
|
||||
Rule::array_initializer_expression => Expression::ArrayInitializer(
|
||||
ArrayInitializerExpression::from_pest(&mut pair.into_inner()).unwrap()
|
||||
),
|
||||
Rule::unary_expression => {
|
||||
let span = next.as_span();
|
||||
let mut inner = next.into_inner();
|
||||
let op = match inner.next().unwrap().as_rule() {
|
||||
Rule::op_unary => UnaryOperator::from_pest(&mut pair.into_inner().next().unwrap().into_inner()).unwrap(),
|
||||
r => unreachable!("`unary_expression` should yield `op_unary`, found {:#?}", r)
|
||||
};
|
||||
let expression = build_factor(inner.next().unwrap());
|
||||
Expression::Unary(UnaryExpression {
|
||||
op,
|
||||
expression,
|
||||
span
|
||||
})
|
||||
},
|
||||
r => unreachable!("`term` should contain one of [`expression`, `conditional_expression`, `primary_expression`, `postfix_expression`, `inline_array_expression`, `unary_expression`, `array_initializer_expression`], found {:#?}", r)
|
||||
}
|
||||
}
|
||||
r => unreachable!(
|
||||
"`build_factor` can only be called on `term`, found {:#?}",
|
||||
r
|
||||
),
|
||||
})
|
||||
Box::new(Expression::from(
|
||||
UnariedTerm::from_pest(&mut Pairs::single(pair)).unwrap(),
|
||||
))
|
||||
}
|
||||
|
||||
#[derive(Debug, FromPest, PartialEq, Clone)]
|
||||
|
@ -435,30 +371,149 @@ mod ast {
|
|||
Pow,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, FromPest, Clone)]
|
||||
#[pest_ast(rule(Rule::op_unary))]
|
||||
pub enum UnaryOperator<'ast> {
|
||||
Not(Not<'ast>),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, FromPest, Clone)]
|
||||
#[pest_ast(rule(Rule::op_not))]
|
||||
pub struct Not<'ast> {
|
||||
#[pest_ast(outer())]
|
||||
pub span: Span<'ast>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum Expression<'ast> {
|
||||
Ternary(TernaryExpression<'ast>),
|
||||
Binary(BinaryExpression<'ast>),
|
||||
Unary(UnaryExpression<'ast>),
|
||||
Postfix(PostfixExpression<'ast>),
|
||||
Identifier(IdentifierExpression<'ast>),
|
||||
Literal(LiteralExpression<'ast>),
|
||||
InlineArray(InlineArrayExpression<'ast>),
|
||||
InlineStruct(InlineStructExpression<'ast>),
|
||||
ArrayInitializer(ArrayInitializerExpression<'ast>),
|
||||
Unary(UnaryExpression<'ast>),
|
||||
}
|
||||
|
||||
#[derive(Debug, FromPest, PartialEq, Clone)]
|
||||
#[pest_ast(rule(Rule::term))]
|
||||
pub enum Term<'ast> {
|
||||
Expression(Expression<'ast>),
|
||||
InlineStruct(InlineStructExpression<'ast>),
|
||||
Ternary(TernaryExpression<'ast>),
|
||||
Postfix(PostfixExpression<'ast>),
|
||||
Primary(PrimaryExpression<'ast>),
|
||||
InlineArray(InlineArrayExpression<'ast>),
|
||||
ArrayInitializer(ArrayInitializerExpression<'ast>),
|
||||
}
|
||||
|
||||
#[derive(Debug, FromPest, PartialEq, Clone)]
|
||||
#[pest_ast(rule(Rule::powered_term))]
|
||||
struct PoweredTerm<'ast> {
|
||||
base: Term<'ast>,
|
||||
op: Option<PowOperator>,
|
||||
exponent: Option<ExponentExpression<'ast>>,
|
||||
#[pest_ast(outer())]
|
||||
span: Span<'ast>,
|
||||
}
|
||||
|
||||
#[derive(Debug, FromPest, PartialEq, Clone)]
|
||||
#[pest_ast(rule(Rule::op_pow))]
|
||||
struct PowOperator;
|
||||
|
||||
impl<'ast> From<PoweredTerm<'ast>> for Expression<'ast> {
|
||||
fn from(t: PoweredTerm<'ast>) -> Self {
|
||||
let base = Expression::from(t.base);
|
||||
|
||||
match t.exponent {
|
||||
Some(exponent) => Expression::Binary(BinaryExpression {
|
||||
op: BinaryOperator::Pow,
|
||||
left: Box::new(base),
|
||||
right: Box::new(exponent.into()),
|
||||
span: t.span,
|
||||
}),
|
||||
None => base,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, FromPest, PartialEq, Clone)]
|
||||
#[pest_ast(rule(Rule::unaried_term))]
|
||||
struct UnariedTerm<'ast> {
|
||||
op: Option<UnaryOperator>,
|
||||
expression: PoweredTerm<'ast>,
|
||||
#[pest_ast(outer())]
|
||||
span: Span<'ast>,
|
||||
}
|
||||
|
||||
impl<'ast> From<UnariedTerm<'ast>> for Expression<'ast> {
|
||||
fn from(t: UnariedTerm<'ast>) -> Self {
|
||||
let expression = Expression::from(t.expression);
|
||||
|
||||
match t.op {
|
||||
Some(sign) => Expression::Unary(UnaryExpression {
|
||||
op: sign,
|
||||
expression: Box::new(expression),
|
||||
span: t.span,
|
||||
}),
|
||||
None => expression,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, FromPest, PartialEq, Clone)]
|
||||
#[pest_ast(rule(Rule::op_unary))]
|
||||
pub enum UnaryOperator {
|
||||
Pos(PosOperator),
|
||||
Neg(NegOperator),
|
||||
Not(NotOperator),
|
||||
}
|
||||
|
||||
#[derive(Debug, FromPest, PartialEq, Clone)]
|
||||
#[pest_ast(rule(Rule::op_pos))]
|
||||
pub struct PosOperator;
|
||||
|
||||
#[derive(Debug, FromPest, PartialEq, Clone)]
|
||||
#[pest_ast(rule(Rule::op_neg))]
|
||||
pub struct NegOperator;
|
||||
|
||||
#[derive(Debug, FromPest, PartialEq, Clone)]
|
||||
#[pest_ast(rule(Rule::op_not))]
|
||||
pub struct NotOperator;
|
||||
|
||||
impl<'ast> From<Term<'ast>> for Expression<'ast> {
|
||||
fn from(t: Term<'ast>) -> Self {
|
||||
match t {
|
||||
Term::Expression(e) => e,
|
||||
Term::Ternary(e) => Expression::Ternary(e),
|
||||
Term::Postfix(e) => Expression::Postfix(e),
|
||||
Term::Primary(e) => e.into(),
|
||||
Term::InlineArray(e) => Expression::InlineArray(e),
|
||||
Term::InlineStruct(e) => Expression::InlineStruct(e),
|
||||
Term::ArrayInitializer(e) => Expression::ArrayInitializer(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, FromPest, PartialEq, Clone)]
|
||||
#[pest_ast(rule(Rule::primary_expression))]
|
||||
pub enum PrimaryExpression<'ast> {
|
||||
Identifier(IdentifierExpression<'ast>),
|
||||
Literal(LiteralExpression<'ast>),
|
||||
}
|
||||
|
||||
impl<'ast> From<PrimaryExpression<'ast>> for Expression<'ast> {
|
||||
fn from(e: PrimaryExpression<'ast>) -> Self {
|
||||
match e {
|
||||
PrimaryExpression::Literal(c) => Expression::Literal(c),
|
||||
PrimaryExpression::Identifier(i) => Expression::Identifier(i),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, FromPest, PartialEq, Clone)]
|
||||
#[pest_ast(rule(Rule::exponent_expression))]
|
||||
pub enum ExponentExpression<'ast> {
|
||||
Expression(Expression<'ast>),
|
||||
Primary(PrimaryExpression<'ast>),
|
||||
}
|
||||
|
||||
impl<'ast> From<ExponentExpression<'ast>> for Expression<'ast> {
|
||||
fn from(e: ExponentExpression<'ast>) -> Self {
|
||||
match e {
|
||||
ExponentExpression::Expression(e) => e,
|
||||
ExponentExpression::Primary(e) => e.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, FromPest, PartialEq, Clone)]
|
||||
|
@ -509,15 +564,6 @@ mod ast {
|
|||
pub span: Span<'ast>,
|
||||
}
|
||||
|
||||
#[derive(Debug, FromPest, PartialEq, Clone)]
|
||||
#[pest_ast(rule(Rule::unary_expression))]
|
||||
pub struct UnaryExpression<'ast> {
|
||||
pub op: UnaryOperator<'ast>,
|
||||
pub expression: Box<Expression<'ast>>,
|
||||
#[pest_ast(outer())]
|
||||
pub span: Span<'ast>,
|
||||
}
|
||||
|
||||
#[derive(Debug, FromPest, PartialEq, Clone)]
|
||||
#[pest_ast(rule(Rule::inline_array_expression))]
|
||||
pub struct InlineArrayExpression<'ast> {
|
||||
|
@ -642,6 +688,13 @@ mod ast {
|
|||
pub span: Span<'ast>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct UnaryExpression<'ast> {
|
||||
pub op: UnaryOperator,
|
||||
pub expression: Box<Expression<'ast>>,
|
||||
pub span: Span<'ast>,
|
||||
}
|
||||
|
||||
#[derive(Debug, FromPest, PartialEq, Clone)]
|
||||
#[pest_ast(rule(Rule::conditional_expression))]
|
||||
pub struct TernaryExpression<'ast> {
|
||||
|
|
Loading…
Reference in a new issue