mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-12-26 03:33:44 +03:00
Merge pull request #163 from AleoHQ/feature/unary-minus
Implement unary minus operator to negate a value, variable, or expression
This commit is contained in:
commit
9e5b986b6f
@ -6,11 +6,11 @@ use crate::{
|
||||
ArrayInlineExpression,
|
||||
CircuitInlineExpression,
|
||||
Expression,
|
||||
NotExpression,
|
||||
PostfixExpression,
|
||||
TernaryExpression,
|
||||
UnaryExpression,
|
||||
},
|
||||
operations::{BinaryOperation, NotOperation},
|
||||
operations::{BinaryOperation, UnaryOperation},
|
||||
values::Value,
|
||||
};
|
||||
|
||||
@ -76,17 +76,18 @@ fn parse_term(pair: Pair<Rule>) -> Box<Expression> {
|
||||
Rule::expression_conditional => {
|
||||
Expression::Ternary(TernaryExpression::from_pest(&mut pair.into_inner()).unwrap())
|
||||
}
|
||||
Rule::expression_not => {
|
||||
Rule::expression_unary => {
|
||||
// The following is necessary to match with the unary operator and its unary expression
|
||||
let span = next.as_span();
|
||||
let mut inner = next.into_inner();
|
||||
let operation = match inner.next().unwrap().as_rule() {
|
||||
Rule::operation_not => {
|
||||
NotOperation::from_pest(&mut pair.into_inner().next().unwrap().into_inner()).unwrap()
|
||||
Rule::operation_unary => {
|
||||
UnaryOperation::from_pest(&mut pair.into_inner().next().unwrap().into_inner()).unwrap()
|
||||
}
|
||||
rule => unreachable!("`expression_not` should yield `operation_pre_not`, found {:#?}", rule),
|
||||
rule => unreachable!("`expression_unary` should yield `operation_unary`, found {:#?}", rule),
|
||||
};
|
||||
let expression = parse_term(inner.next().unwrap());
|
||||
Expression::Not(NotExpression {
|
||||
Expression::Unary(UnaryExpression {
|
||||
operation,
|
||||
expression,
|
||||
span,
|
||||
@ -95,21 +96,8 @@ fn parse_term(pair: Pair<Rule>) -> Box<Expression> {
|
||||
Rule::expression_postfix => {
|
||||
Expression::Postfix(PostfixExpression::from_pest(&mut pair.into_inner()).unwrap())
|
||||
}
|
||||
Rule::expression_primitive => {
|
||||
let next = next.into_inner().next().unwrap();
|
||||
match next.as_rule() {
|
||||
Rule::value => Expression::Value(
|
||||
Value::from_pest(&mut pair.into_inner().next().unwrap().into_inner()).unwrap(),
|
||||
),
|
||||
Rule::identifier => Expression::Identifier(
|
||||
Identifier::from_pest(&mut pair.into_inner().next().unwrap().into_inner()).unwrap(),
|
||||
),
|
||||
rule => unreachable!(
|
||||
"`expression_primitive` should contain one of [`value`, `identifier`], found {:#?}",
|
||||
rule
|
||||
),
|
||||
}
|
||||
}
|
||||
Rule::value => Expression::Value(Value::from_pest(&mut pair.into_inner()).unwrap()),
|
||||
Rule::identifier => Expression::Identifier(Identifier::from_pest(&mut pair.into_inner()).unwrap()),
|
||||
rule => unreachable!(
|
||||
"`term` should contain one of ['value', 'identifier', 'expression', 'expression_not', 'expression_increment', 'expression_decrement'], found {:#?}",
|
||||
rule
|
||||
|
@ -8,7 +8,7 @@ use std::fmt;
|
||||
pub enum Expression<'ast> {
|
||||
Value(Value<'ast>),
|
||||
Identifier(Identifier<'ast>),
|
||||
Not(NotExpression<'ast>),
|
||||
Unary(UnaryExpression<'ast>),
|
||||
Binary(BinaryExpression<'ast>),
|
||||
Ternary(TernaryExpression<'ast>),
|
||||
ArrayInline(ArrayInlineExpression<'ast>),
|
||||
@ -50,7 +50,7 @@ impl<'ast> Expression<'ast> {
|
||||
match self {
|
||||
Expression::Value(expression) => &expression.span(),
|
||||
Expression::Identifier(expression) => &expression.span,
|
||||
Expression::Not(expression) => &expression.span,
|
||||
Expression::Unary(expression) => &expression.span,
|
||||
Expression::Binary(expression) => &expression.span,
|
||||
Expression::Ternary(expression) => &expression.span,
|
||||
Expression::ArrayInline(expression) => &expression.span,
|
||||
@ -66,7 +66,7 @@ impl<'ast> fmt::Display for Expression<'ast> {
|
||||
match *self {
|
||||
Expression::Value(ref expression) => write!(f, "{}", expression),
|
||||
Expression::Identifier(ref expression) => write!(f, "{}", expression),
|
||||
Expression::Not(ref expression) => write!(f, "!{}", expression.expression),
|
||||
Expression::Unary(ref expression) => write!(f, "{}", expression),
|
||||
Expression::Binary(ref expression) => write!(f, "{} == {}", expression.left, expression.right),
|
||||
Expression::Ternary(ref expression) => write!(
|
||||
f,
|
||||
|
@ -13,8 +13,8 @@ pub use circuit_inline_expression::*;
|
||||
pub mod expression;
|
||||
pub use expression::*;
|
||||
|
||||
pub mod not_expression;
|
||||
pub use not_expression::*;
|
||||
pub mod unary_expression;
|
||||
pub use unary_expression::*;
|
||||
|
||||
pub mod postfix_expression;
|
||||
pub use postfix_expression::*;
|
||||
|
@ -1,15 +0,0 @@
|
||||
use crate::{ast::Rule, expressions::Expression, operations::NotOperation, SpanDef};
|
||||
|
||||
use pest::Span;
|
||||
use pest_ast::FromPest;
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Clone, Debug, FromPest, PartialEq, Serialize)]
|
||||
#[pest_ast(rule(Rule::expression_not))]
|
||||
pub struct NotExpression<'ast> {
|
||||
pub operation: NotOperation<'ast>,
|
||||
pub expression: Box<Expression<'ast>>,
|
||||
#[pest_ast(outer())]
|
||||
#[serde(with = "SpanDef")]
|
||||
pub span: Span<'ast>,
|
||||
}
|
22
ast/src/expressions/unary_expression.rs
Normal file
22
ast/src/expressions/unary_expression.rs
Normal file
@ -0,0 +1,22 @@
|
||||
use crate::{ast::Rule, expressions::Expression, operations::UnaryOperation, SpanDef};
|
||||
|
||||
use pest::Span;
|
||||
use pest_ast::FromPest;
|
||||
use serde::Serialize;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Debug, FromPest, PartialEq, Serialize)]
|
||||
#[pest_ast(rule(Rule::expression_unary))]
|
||||
pub struct UnaryExpression<'ast> {
|
||||
pub operation: UnaryOperation,
|
||||
pub expression: Box<Expression<'ast>>,
|
||||
#[pest_ast(outer())]
|
||||
#[serde(with = "SpanDef")]
|
||||
pub span: Span<'ast>,
|
||||
}
|
||||
|
||||
impl<'ast> fmt::Display for UnaryExpression<'ast> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}{}", self.operation, self.expression)
|
||||
}
|
||||
}
|
@ -68,8 +68,13 @@ let_ = { "let " }
|
||||
|
||||
/// Operations
|
||||
|
||||
// Declared in operations/not_operation.rs
|
||||
// Declared in operations/unary_operation.rs
|
||||
operation_unary = {
|
||||
operation_not
|
||||
| operation_negate
|
||||
}
|
||||
operation_not = { "!" }
|
||||
operation_negate = { "-" }
|
||||
|
||||
// Declared in operations/binary_operation.rs
|
||||
operation_and = { "&&" }
|
||||
@ -263,11 +268,12 @@ expression_term = {
|
||||
| expression_array_inline
|
||||
| expression_circuit_inline
|
||||
| expression_conditional
|
||||
| expression_not
|
||||
| value
|
||||
| expression_unary // must be defined below value to avoid conflicts with negative values
|
||||
| expression_postfix
|
||||
| expression_primitive
|
||||
| identifier
|
||||
}
|
||||
expression_primitive = { value | identifier }
|
||||
|
||||
expression_tuple = _{ (expression ~ ("," ~ expression)*)? }
|
||||
|
||||
// Declared in expressions/expression.rs
|
||||
@ -284,8 +290,8 @@ inline_array_inner = _{(spread_or_expression ~ ("," ~ NEWLINE* ~ spread_or_expre
|
||||
expression_circuit_inline = { identifier ~ "{" ~ NEWLINE* ~ circuit_field_list ~ NEWLINE* ~ "}" }
|
||||
circuit_field_list = _{ (circuit_field ~ ("," ~ NEWLINE* ~ circuit_field)*)? ~ ","? }
|
||||
|
||||
// Declared in expressions/not_expression.rs
|
||||
expression_not = { operation_not ~ expression_term }
|
||||
// Declared in expressions/unary_expression.rs
|
||||
expression_unary = { operation_unary ~ expression_term }
|
||||
|
||||
// Declared in expressions/postfix_expression.rs
|
||||
expression_postfix = { identifier ~ access+ }
|
||||
|
@ -4,5 +4,5 @@ pub use assign_operation::*;
|
||||
pub mod binary_operation;
|
||||
pub use binary_operation::*;
|
||||
|
||||
pub mod not_operation;
|
||||
pub use not_operation::*;
|
||||
pub mod unary_operation;
|
||||
pub use unary_operation::*;
|
||||
|
@ -1,13 +0,0 @@
|
||||
use crate::{ast::Rule, SpanDef};
|
||||
|
||||
use pest::Span;
|
||||
use pest_ast::FromPest;
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Clone, Debug, FromPest, PartialEq, Serialize)]
|
||||
#[pest_ast(rule(Rule::operation_not))]
|
||||
pub struct NotOperation<'ast> {
|
||||
#[pest_ast(outer())]
|
||||
#[serde(with = "SpanDef")]
|
||||
pub span: Span<'ast>,
|
||||
}
|
29
ast/src/operations/unary_operation.rs
Normal file
29
ast/src/operations/unary_operation.rs
Normal file
@ -0,0 +1,29 @@
|
||||
use crate::ast::Rule;
|
||||
|
||||
use pest_ast::FromPest;
|
||||
use serde::Serialize;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Debug, FromPest, PartialEq, Serialize)]
|
||||
#[pest_ast(rule(Rule::operation_unary))]
|
||||
pub enum UnaryOperation {
|
||||
Negate(Negate),
|
||||
Not(Not),
|
||||
}
|
||||
|
||||
impl fmt::Display for UnaryOperation {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
UnaryOperation::Negate(_) => write!(f, "-"),
|
||||
UnaryOperation::Not(_) => write!(f, "!"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, FromPest, PartialEq, Serialize)]
|
||||
#[pest_ast(rule(Rule::operation_not))]
|
||||
pub struct Not {}
|
||||
|
||||
#[derive(Clone, Debug, FromPest, PartialEq, Serialize)]
|
||||
#[pest_ast(rule(Rule::operation_negate))]
|
||||
pub struct Negate {}
|
@ -20,7 +20,13 @@ impl FieldError {
|
||||
FieldError::Error(FormattedError::new_from_span(message, span))
|
||||
}
|
||||
|
||||
pub fn cannot_enforce(operation: String, error: SynthesisError, span: Span) -> Self {
|
||||
pub fn negate_operation(error: SynthesisError, span: Span) -> Self {
|
||||
let message = format!("field negation failed due to synthesis error `{}`", error,);
|
||||
|
||||
Self::new_from_span(message, span)
|
||||
}
|
||||
|
||||
pub fn binary_operation(operation: String, error: SynthesisError, span: Span) -> Self {
|
||||
let message = format!(
|
||||
"the field binary operation `{}` failed due to synthesis error `{}`",
|
||||
operation, error,
|
||||
|
@ -20,7 +20,13 @@ impl GroupError {
|
||||
GroupError::Error(FormattedError::new_from_span(message, span))
|
||||
}
|
||||
|
||||
pub fn cannot_enforce(operation: String, error: SynthesisError, span: Span) -> Self {
|
||||
pub fn negate_operation(error: SynthesisError, span: Span) -> Self {
|
||||
let message = format!("group negation failed due to the synthesis error `{}`", error,);
|
||||
|
||||
Self::new_from_span(message, span)
|
||||
}
|
||||
|
||||
pub fn binary_operation(operation: String, error: SynthesisError, span: Span) -> Self {
|
||||
let message = format!(
|
||||
"the group binary operation `{}` failed due to the synthesis error `{}`",
|
||||
operation, error,
|
||||
|
@ -51,7 +51,13 @@ impl IntegerError {
|
||||
Self::new_from_span(message, span)
|
||||
}
|
||||
|
||||
pub fn cannot_evaluate(operation: String, span: Span) -> Self {
|
||||
pub fn negate_operation(span: Span) -> Self {
|
||||
let message = format!("integer negation can only be enforced on signed integers");
|
||||
|
||||
Self::new_from_span(message, span)
|
||||
}
|
||||
|
||||
pub fn binary_operation(operation: String, span: Span) -> Self {
|
||||
let message = format!(
|
||||
"the integer binary operation `{}` can only be enforced on integers of the same type",
|
||||
operation
|
||||
|
@ -6,6 +6,9 @@ pub use self::add::*;
|
||||
pub mod sub;
|
||||
pub use self::sub::*;
|
||||
|
||||
pub mod negate;
|
||||
pub use self::negate::*;
|
||||
|
||||
pub mod mul;
|
||||
pub use self::mul::*;
|
||||
|
||||
|
22
compiler/src/expression/arithmetic/negate.rs
Normal file
22
compiler/src/expression/arithmetic/negate.rs
Normal file
@ -0,0 +1,22 @@
|
||||
//! Enforces a unary negate `-` operator in a resolved Leo program.
|
||||
|
||||
use crate::{errors::ExpressionError, value::ConstrainedValue, GroupType};
|
||||
use leo_typed::Span;
|
||||
|
||||
use snarkos_models::{
|
||||
curves::{Field, PrimeField},
|
||||
gadgets::r1cs::ConstraintSystem,
|
||||
};
|
||||
|
||||
pub fn enforce_negate<F: Field + PrimeField, G: GroupType<F>, CS: ConstraintSystem<F>>(
|
||||
cs: &mut CS,
|
||||
value: ConstrainedValue<F, G>,
|
||||
span: Span,
|
||||
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
|
||||
match value {
|
||||
ConstrainedValue::Integer(integer) => Ok(ConstrainedValue::Integer(integer.negate(cs, span)?)),
|
||||
ConstrainedValue::Field(field) => Ok(ConstrainedValue::Field(field.negate(cs, span)?)),
|
||||
ConstrainedValue::Group(group) => Ok(ConstrainedValue::Group(group.negate(cs, span)?)),
|
||||
value => Err(ExpressionError::incompatible_types(format!("-{}", value), span)),
|
||||
}
|
||||
}
|
@ -45,6 +45,12 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
}
|
||||
|
||||
// Binary operations
|
||||
Expression::Negate(expression, span) => {
|
||||
let resolved_value =
|
||||
self.enforce_expression(cs, file_scope, function_scope, expected_types, *expression)?;
|
||||
|
||||
enforce_negate(cs, resolved_value, span)
|
||||
}
|
||||
Expression::Add(left, right, span) => {
|
||||
let (resolved_left, resolved_right) = self.enforce_binary_expression(
|
||||
cs,
|
||||
|
@ -43,6 +43,17 @@ impl<F: Field + PrimeField> FieldType<F> {
|
||||
Ok(FieldType::Constant(value))
|
||||
}
|
||||
|
||||
pub fn negate<CS: ConstraintSystem<F>>(&self, cs: CS, span: Span) -> Result<Self, FieldError> {
|
||||
match self {
|
||||
FieldType::Constant(field) => Ok(FieldType::Constant(field.neg())),
|
||||
FieldType::Allocated(field) => {
|
||||
let result = field.negate(cs).map_err(|e| FieldError::negate_operation(e, span))?;
|
||||
|
||||
Ok(FieldType::Allocated(result))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add<CS: ConstraintSystem<F>>(&self, cs: CS, other: &Self, span: Span) -> Result<Self, FieldError> {
|
||||
match (self, other) {
|
||||
(FieldType::Constant(self_value), FieldType::Constant(other_value)) => {
|
||||
@ -52,7 +63,7 @@ impl<F: Field + PrimeField> FieldType<F> {
|
||||
(FieldType::Allocated(self_value), FieldType::Allocated(other_value)) => {
|
||||
let result = self_value
|
||||
.add(cs, other_value)
|
||||
.map_err(|e| FieldError::cannot_enforce(format!("+"), e, span))?;
|
||||
.map_err(|e| FieldError::binary_operation(format!("+"), e, span))?;
|
||||
|
||||
Ok(FieldType::Allocated(result))
|
||||
}
|
||||
@ -61,7 +72,7 @@ impl<F: Field + PrimeField> FieldType<F> {
|
||||
| (FieldType::Allocated(allocated_value), FieldType::Constant(constant_value)) => Ok(FieldType::Allocated(
|
||||
allocated_value
|
||||
.add_constant(cs, constant_value)
|
||||
.map_err(|e| FieldError::cannot_enforce(format!("+"), e, span))?,
|
||||
.map_err(|e| FieldError::binary_operation(format!("+"), e, span))?,
|
||||
)),
|
||||
}
|
||||
}
|
||||
@ -75,7 +86,7 @@ impl<F: Field + PrimeField> FieldType<F> {
|
||||
(FieldType::Allocated(self_value), FieldType::Allocated(other_value)) => {
|
||||
let result = self_value
|
||||
.sub(cs, other_value)
|
||||
.map_err(|e| FieldError::cannot_enforce(format!("-"), e, span))?;
|
||||
.map_err(|e| FieldError::binary_operation(format!("-"), e, span))?;
|
||||
|
||||
Ok(FieldType::Allocated(result))
|
||||
}
|
||||
@ -84,7 +95,7 @@ impl<F: Field + PrimeField> FieldType<F> {
|
||||
| (FieldType::Allocated(allocated_value), FieldType::Constant(constant_value)) => Ok(FieldType::Allocated(
|
||||
allocated_value
|
||||
.sub_constant(cs, constant_value)
|
||||
.map_err(|e| FieldError::cannot_enforce(format!("+"), e, span))?,
|
||||
.map_err(|e| FieldError::binary_operation(format!("+"), e, span))?,
|
||||
)),
|
||||
}
|
||||
}
|
||||
@ -98,7 +109,7 @@ impl<F: Field + PrimeField> FieldType<F> {
|
||||
(FieldType::Allocated(self_value), FieldType::Allocated(other_value)) => {
|
||||
let result = self_value
|
||||
.mul(cs, other_value)
|
||||
.map_err(|e| FieldError::cannot_enforce(format!("*"), e, span))?;
|
||||
.map_err(|e| FieldError::binary_operation(format!("*"), e, span))?;
|
||||
|
||||
Ok(FieldType::Allocated(result))
|
||||
}
|
||||
@ -107,7 +118,7 @@ impl<F: Field + PrimeField> FieldType<F> {
|
||||
| (FieldType::Allocated(allocated_value), FieldType::Constant(constant_value)) => Ok(FieldType::Allocated(
|
||||
allocated_value
|
||||
.mul_by_constant(cs, constant_value)
|
||||
.map_err(|e| FieldError::cannot_enforce(format!("*"), e, span))?,
|
||||
.map_err(|e| FieldError::binary_operation(format!("*"), e, span))?,
|
||||
)),
|
||||
}
|
||||
}
|
||||
@ -124,7 +135,7 @@ impl<F: Field + PrimeField> FieldType<F> {
|
||||
FieldType::Allocated(allocated) => {
|
||||
let allocated_inverse = allocated
|
||||
.inverse(&mut cs)
|
||||
.map_err(|e| FieldError::cannot_enforce(format!("+"), e, span.clone()))?;
|
||||
.map_err(|e| FieldError::binary_operation(format!("+"), e, span.clone()))?;
|
||||
|
||||
FieldType::Allocated(allocated_inverse)
|
||||
}
|
||||
|
@ -36,6 +36,8 @@ pub trait GroupType<F: Field>:
|
||||
|
||||
fn to_allocated<CS: ConstraintSystem<F>>(&self, cs: CS, span: Span) -> Result<Self, GroupError>;
|
||||
|
||||
fn negate<CS: ConstraintSystem<F>>(&self, cs: CS, span: Span) -> Result<Self, GroupError>;
|
||||
|
||||
fn add<CS: ConstraintSystem<F>>(&self, cs: CS, other: &Self, span: Span) -> Result<Self, GroupError>;
|
||||
|
||||
fn sub<CS: ConstraintSystem<F>>(&self, cs: CS, other: &Self, span: Span) -> Result<Self, GroupError>;
|
||||
|
@ -23,7 +23,11 @@ use snarkos_models::{
|
||||
},
|
||||
},
|
||||
};
|
||||
use std::{borrow::Borrow, ops::Sub, str::FromStr};
|
||||
use std::{
|
||||
borrow::Borrow,
|
||||
ops::{Neg, Sub},
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum EdwardsGroupType {
|
||||
@ -50,6 +54,18 @@ impl GroupType<Fq> for EdwardsGroupType {
|
||||
.map_err(|error| GroupError::synthesis_error(error, span))
|
||||
}
|
||||
|
||||
fn negate<CS: ConstraintSystem<Fq>>(&self, cs: CS, span: Span) -> Result<Self, GroupError> {
|
||||
match self {
|
||||
EdwardsGroupType::Constant(group) => Ok(EdwardsGroupType::Constant(group.neg())),
|
||||
EdwardsGroupType::Allocated(group) => {
|
||||
let result = <EdwardsBlsGadget as GroupGadget<GroupAffine<EdwardsParameters>, Fq>>::negate(group, cs)
|
||||
.map_err(|e| GroupError::negate_operation(e, span))?;
|
||||
|
||||
Ok(EdwardsGroupType::Allocated(result))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add<CS: ConstraintSystem<Fq>>(&self, cs: CS, other: &Self, span: Span) -> Result<Self, GroupError> {
|
||||
match (self, other) {
|
||||
(EdwardsGroupType::Constant(self_value), EdwardsGroupType::Constant(other_value)) => {
|
||||
@ -62,7 +78,7 @@ impl GroupType<Fq> for EdwardsGroupType {
|
||||
cs,
|
||||
other_value,
|
||||
)
|
||||
.map_err(|e| GroupError::cannot_enforce(format!("+"), e, span))?;
|
||||
.map_err(|e| GroupError::binary_operation(format!("+"), e, span))?;
|
||||
|
||||
Ok(EdwardsGroupType::Allocated(result))
|
||||
}
|
||||
@ -72,7 +88,7 @@ impl GroupType<Fq> for EdwardsGroupType {
|
||||
Ok(EdwardsGroupType::Allocated(
|
||||
allocated_value
|
||||
.add_constant(cs, constant_value)
|
||||
.map_err(|e| GroupError::cannot_enforce(format!("+"), e, span))?,
|
||||
.map_err(|e| GroupError::binary_operation(format!("+"), e, span))?,
|
||||
))
|
||||
}
|
||||
}
|
||||
@ -90,7 +106,7 @@ impl GroupType<Fq> for EdwardsGroupType {
|
||||
cs,
|
||||
other_value,
|
||||
)
|
||||
.map_err(|e| GroupError::cannot_enforce(format!("-"), e, span))?;
|
||||
.map_err(|e| GroupError::binary_operation(format!("-"), e, span))?;
|
||||
|
||||
Ok(EdwardsGroupType::Allocated(result))
|
||||
}
|
||||
@ -100,7 +116,7 @@ impl GroupType<Fq> for EdwardsGroupType {
|
||||
Ok(EdwardsGroupType::Allocated(
|
||||
allocated_value
|
||||
.sub_constant(cs, constant_value)
|
||||
.map_err(|e| GroupError::cannot_enforce(format!("-"), e, span))?,
|
||||
.map_err(|e| GroupError::binary_operation(format!("-"), e, span))?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -347,6 +347,21 @@ impl Integer {
|
||||
Self::allocate_type(cs, integer_type, name, option, span)
|
||||
}
|
||||
|
||||
pub fn negate<F: Field + PrimeField, CS: ConstraintSystem<F>>(
|
||||
self,
|
||||
cs: &mut CS,
|
||||
span: Span,
|
||||
) -> Result<Self, IntegerError> {
|
||||
let unique_namespace = format!("enforce -{} {}:{}", self, span.line, span.start);
|
||||
|
||||
let a = self;
|
||||
let s = span.clone();
|
||||
|
||||
let result = match_signed_integer!(a, s => a.neg(cs.ns(|| unique_namespace)));
|
||||
|
||||
result.ok_or(IntegerError::negate_operation(span))
|
||||
}
|
||||
|
||||
pub fn add<F: Field + PrimeField, CS: ConstraintSystem<F>>(
|
||||
self,
|
||||
cs: &mut CS,
|
||||
@ -361,7 +376,7 @@ impl Integer {
|
||||
|
||||
let result = match_integers_span!((a, b), s => a.add(cs.ns(|| unique_namespace), &b));
|
||||
|
||||
result.ok_or(IntegerError::cannot_evaluate(format!("+"), span))
|
||||
result.ok_or(IntegerError::binary_operation(format!("+"), span))
|
||||
}
|
||||
|
||||
pub fn sub<F: Field + PrimeField, CS: ConstraintSystem<F>>(
|
||||
@ -378,7 +393,7 @@ impl Integer {
|
||||
|
||||
let result = match_integers_span!((a, b), s => a.sub(cs.ns(|| unique_namespace), &b));
|
||||
|
||||
result.ok_or(IntegerError::cannot_evaluate(format!("-"), span))
|
||||
result.ok_or(IntegerError::binary_operation(format!("-"), span))
|
||||
}
|
||||
|
||||
pub fn mul<F: Field + PrimeField, CS: ConstraintSystem<F>>(
|
||||
@ -395,7 +410,7 @@ impl Integer {
|
||||
|
||||
let result = match_integers_span!((a, b), s => a.mul(cs.ns(|| unique_namespace), &b));
|
||||
|
||||
result.ok_or(IntegerError::cannot_evaluate(format!("*"), span))
|
||||
result.ok_or(IntegerError::binary_operation(format!("*"), span))
|
||||
}
|
||||
|
||||
pub fn div<F: Field + PrimeField, CS: ConstraintSystem<F>>(
|
||||
@ -412,7 +427,7 @@ impl Integer {
|
||||
|
||||
let result = match_integers_span!((a, b), s => a.div(cs.ns(|| unique_namespace), &b));
|
||||
|
||||
result.ok_or(IntegerError::cannot_evaluate(format!("÷"), span))
|
||||
result.ok_or(IntegerError::binary_operation(format!("÷"), span))
|
||||
}
|
||||
|
||||
pub fn pow<F: Field + PrimeField, CS: ConstraintSystem<F>>(
|
||||
@ -429,7 +444,7 @@ impl Integer {
|
||||
|
||||
let result = match_integers_span!((a, b), s => a.pow(cs.ns(|| unique_namespace), &b));
|
||||
|
||||
result.ok_or(IntegerError::cannot_evaluate(format!("**"), span))
|
||||
result.ok_or(IntegerError::binary_operation(format!("**"), span))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -49,6 +49,21 @@ macro_rules! match_integer {
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! match_signed_integer {
|
||||
($integer: ident, $span: ident => $expression: expr) => {
|
||||
match $integer {
|
||||
Integer::I8($integer) => Some(Integer::I8($expression.map_err(|e| IntegerError::signed(e, $span))?)),
|
||||
Integer::I16($integer) => Some(Integer::I16($expression.map_err(|e| IntegerError::signed(e, $span))?)),
|
||||
Integer::I32($integer) => Some(Integer::I32($expression.map_err(|e| IntegerError::signed(e, $span))?)),
|
||||
Integer::I64($integer) => Some(Integer::I64($expression.map_err(|e| IntegerError::signed(e, $span))?)),
|
||||
Integer::I128($integer) => Some(Integer::I128($expression.map_err(|e| IntegerError::signed(e, $span))?)),
|
||||
|
||||
_ => None,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! match_integers {
|
||||
(($a: ident, $b: ident) => $expression:expr) => {
|
||||
|
@ -23,6 +23,33 @@ pub fn field_to_decimal_string(f: Fq) -> String {
|
||||
f_bigint.to_str_radix(10)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_negate() {
|
||||
use std::ops::Neg;
|
||||
|
||||
let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
|
||||
|
||||
for _ in 0..10 {
|
||||
let a: Fq = rng.gen();
|
||||
let b = a.neg();
|
||||
|
||||
let a_string = field_to_decimal_string(a);
|
||||
let b_string = field_to_decimal_string(b);
|
||||
|
||||
let bytes = include_bytes!("negate.leo");
|
||||
let mut program = parse_program(bytes).unwrap();
|
||||
|
||||
let main_input = generate_main_input(vec![
|
||||
("a", Some(InputValue::Field(a_string))),
|
||||
("b", Some(InputValue::Field(b_string))),
|
||||
]);
|
||||
|
||||
program.set_main_input(main_input);
|
||||
|
||||
assert_satisfied(program)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add() {
|
||||
use std::ops::Add;
|
||||
|
3
compiler/tests/field/negate.leo
Normal file
3
compiler/tests/field/negate.leo
Normal file
@ -0,0 +1,3 @@
|
||||
function main(a: field, b: field) {
|
||||
assert_eq!(-a, b);
|
||||
}
|
@ -59,6 +59,32 @@ fn test_input() {
|
||||
expect_synthesis_error(program);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_negate() {
|
||||
use std::ops::Neg;
|
||||
|
||||
let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
|
||||
|
||||
for _ in 0..10 {
|
||||
let a: EdwardsAffine = rng.gen();
|
||||
let b = a.neg();
|
||||
|
||||
let a_string = group_to_decimal_string(a);
|
||||
let b_string = group_to_decimal_string(b);
|
||||
|
||||
let bytes = include_bytes!("negate.leo");
|
||||
let mut program = parse_program(bytes).unwrap();
|
||||
|
||||
let main_input = generate_main_input(vec![
|
||||
("a", Some(InputValue::Group(a_string))),
|
||||
("b", Some(InputValue::Group(b_string))),
|
||||
]);
|
||||
program.set_main_input(main_input);
|
||||
|
||||
assert_satisfied(program)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add() {
|
||||
use std::ops::Add;
|
||||
|
3
compiler/tests/group/negate.leo
Normal file
3
compiler/tests/group/negate.leo
Normal file
@ -0,0 +1,3 @@
|
||||
function main(a: group, b: group) {
|
||||
assert_eq!(-a, b);
|
||||
}
|
@ -30,6 +30,21 @@ fn test_i128_max_fail() {
|
||||
Testi128::test_max_fail();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_i128_neg() {
|
||||
Testi128::test_negate();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_i128_neg_max_fail() {
|
||||
Testi128::test_negate_min_fail();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_i128_neg_zero() {
|
||||
Testi128::test_negate_zero();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_i128_add() {
|
||||
Testi128::test_add();
|
||||
|
3
compiler/tests/integers/i128/negate.leo
Normal file
3
compiler/tests/integers/i128/negate.leo
Normal file
@ -0,0 +1,3 @@
|
||||
function main(a: i128, b: i128) {
|
||||
assert_eq!(-a, b);
|
||||
}
|
4
compiler/tests/integers/i128/negate_min.leo
Normal file
4
compiler/tests/integers/i128/negate_min.leo
Normal file
@ -0,0 +1,4 @@
|
||||
function main() {
|
||||
let a: i128 = -170141183460469231731687303715884105728;
|
||||
let b = -a;
|
||||
}
|
5
compiler/tests/integers/i128/negate_zero.leo
Normal file
5
compiler/tests/integers/i128/negate_zero.leo
Normal file
@ -0,0 +1,5 @@
|
||||
function main() {
|
||||
let a = 0i128;
|
||||
|
||||
assert_eq!(-a, 0i128);
|
||||
}
|
@ -30,6 +30,21 @@ fn test_i16_max_fail() {
|
||||
Testi16::test_max_fail();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_i16_neg() {
|
||||
Testi16::test_negate();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_i16_neg_max_fail() {
|
||||
Testi16::test_negate_min_fail();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_i16_neg_zero() {
|
||||
Testi16::test_negate_zero();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_i16_add() {
|
||||
Testi16::test_add();
|
||||
|
3
compiler/tests/integers/i16/negate.leo
Normal file
3
compiler/tests/integers/i16/negate.leo
Normal file
@ -0,0 +1,3 @@
|
||||
function main(a: i16, b: i16) {
|
||||
assert_eq!(-a, b);
|
||||
}
|
4
compiler/tests/integers/i16/negate_min.leo
Normal file
4
compiler/tests/integers/i16/negate_min.leo
Normal file
@ -0,0 +1,4 @@
|
||||
function main() {
|
||||
let a = -32768i16;
|
||||
let b = -a;
|
||||
}
|
5
compiler/tests/integers/i16/negate_zero.leo
Normal file
5
compiler/tests/integers/i16/negate_zero.leo
Normal file
@ -0,0 +1,5 @@
|
||||
function main() {
|
||||
let a = 0i16;
|
||||
|
||||
assert_eq!(-a, 0i16);
|
||||
}
|
@ -30,6 +30,21 @@ fn test_i32_max_fail() {
|
||||
Testi32::test_max_fail();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_i32_neg() {
|
||||
Testi32::test_negate();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_i32_neg_max_fail() {
|
||||
Testi32::test_negate_min_fail();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_i32_neg_zero() {
|
||||
Testi32::test_negate_zero();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_i32_add() {
|
||||
Testi32::test_add();
|
||||
|
3
compiler/tests/integers/i32/negate.leo
Normal file
3
compiler/tests/integers/i32/negate.leo
Normal file
@ -0,0 +1,3 @@
|
||||
function main(a: i32, b: i32) {
|
||||
assert_eq!(-a, b);
|
||||
}
|
4
compiler/tests/integers/i32/negate_min.leo
Normal file
4
compiler/tests/integers/i32/negate_min.leo
Normal file
@ -0,0 +1,4 @@
|
||||
function main() {
|
||||
let a = -2147483648i32;
|
||||
let b = -a;
|
||||
}
|
5
compiler/tests/integers/i32/negate_zero.leo
Normal file
5
compiler/tests/integers/i32/negate_zero.leo
Normal file
@ -0,0 +1,5 @@
|
||||
function main() {
|
||||
let a = 0i32;
|
||||
|
||||
assert_eq!(-a, 0i32);
|
||||
}
|
@ -30,6 +30,21 @@ fn test_i64_max_fail() {
|
||||
Testi64::test_max_fail();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_i64_neg() {
|
||||
Testi64::test_negate();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_i64_neg_max_fail() {
|
||||
Testi64::test_negate_min_fail();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_i64_neg_zero() {
|
||||
Testi64::test_negate_zero();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_i64_add() {
|
||||
Testi64::test_add();
|
||||
|
3
compiler/tests/integers/i64/negate.leo
Normal file
3
compiler/tests/integers/i64/negate.leo
Normal file
@ -0,0 +1,3 @@
|
||||
function main(a: i64, b: i64) {
|
||||
assert_eq!(-a, b);
|
||||
}
|
4
compiler/tests/integers/i64/negate_min.leo
Normal file
4
compiler/tests/integers/i64/negate_min.leo
Normal file
@ -0,0 +1,4 @@
|
||||
function main() {
|
||||
let a: i64 = -9223372036854775808;
|
||||
let b = -a;
|
||||
}
|
5
compiler/tests/integers/i64/negate_zero.leo
Normal file
5
compiler/tests/integers/i64/negate_zero.leo
Normal file
@ -0,0 +1,5 @@
|
||||
function main() {
|
||||
let a = 0i64;
|
||||
|
||||
assert_eq!(-a, 0i64);
|
||||
}
|
@ -30,6 +30,21 @@ fn test_i8_max_fail() {
|
||||
Testi8::test_max_fail();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_i8_neg() {
|
||||
Testi8::test_negate();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_i8_neg_max_fail() {
|
||||
Testi8::test_negate_min_fail();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_i8_neg_zero() {
|
||||
Testi8::test_negate_zero();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_i8_add() {
|
||||
Testi8::test_add();
|
||||
|
3
compiler/tests/integers/i8/negate.leo
Normal file
3
compiler/tests/integers/i8/negate.leo
Normal file
@ -0,0 +1,3 @@
|
||||
function main(a: i8, b: i8) {
|
||||
assert_eq!(-a, b);
|
||||
}
|
4
compiler/tests/integers/i8/negate_min.leo
Normal file
4
compiler/tests/integers/i8/negate_min.leo
Normal file
@ -0,0 +1,4 @@
|
||||
function main() {
|
||||
let a = -128i8;
|
||||
let b = -a;
|
||||
}
|
5
compiler/tests/integers/i8/negate_zero.leo
Normal file
5
compiler/tests/integers/i8/negate_zero.leo
Normal file
@ -0,0 +1,5 @@
|
||||
function main() {
|
||||
let a = 0i8;
|
||||
|
||||
assert_eq!(-a, 0i8);
|
||||
}
|
@ -2,6 +2,44 @@ macro_rules! test_int {
|
||||
($name: ident, $type_: ty, $integer_type: expr, $gadget: ty) => {
|
||||
pub struct $name {}
|
||||
|
||||
impl $name {
|
||||
fn test_negate() {
|
||||
for _ in 0..10 {
|
||||
let a: $type_ = rand::random();
|
||||
|
||||
let b = match a.checked_neg() {
|
||||
Some(valid) => valid,
|
||||
None => continue,
|
||||
};
|
||||
|
||||
let bytes = include_bytes!("negate.leo");
|
||||
let mut program = parse_program(bytes).unwrap();
|
||||
let main_input = generate_main_input(vec![
|
||||
("a", Some(InputValue::Integer($integer_type, a.to_string()))),
|
||||
("b", Some(InputValue::Integer($integer_type, b.to_string()))),
|
||||
]);
|
||||
|
||||
program.set_main_input(main_input);
|
||||
|
||||
assert_satisfied(program);
|
||||
}
|
||||
}
|
||||
|
||||
fn test_negate_min_fail() {
|
||||
let bytes = include_bytes!("negate_min.leo");
|
||||
let program = parse_program(bytes).unwrap();
|
||||
|
||||
expect_computation_error(program);
|
||||
}
|
||||
|
||||
fn test_negate_zero() {
|
||||
let bytes = include_bytes!("negate_zero.leo");
|
||||
let program = parse_program(bytes).unwrap();
|
||||
|
||||
assert_satisfied(program);
|
||||
}
|
||||
}
|
||||
|
||||
impl IntegerTester for $name {
|
||||
fn test_min() {
|
||||
let bytes = include_bytes!("min.leo");
|
||||
|
@ -8,11 +8,11 @@ use leo_ast::{
|
||||
BinaryExpression,
|
||||
CircuitInlineExpression,
|
||||
Expression as AstExpression,
|
||||
NotExpression,
|
||||
PostfixExpression,
|
||||
TernaryExpression,
|
||||
UnaryExpression,
|
||||
},
|
||||
operations::BinaryOperation,
|
||||
operations::{BinaryOperation, UnaryOperation},
|
||||
values::{
|
||||
AddressValue,
|
||||
BooleanValue,
|
||||
@ -52,6 +52,7 @@ pub enum Expression {
|
||||
|
||||
// Boolean operations
|
||||
Not(Box<Expression>, Span),
|
||||
Negate(Box<Expression>, Span),
|
||||
Or(Box<Expression>, Box<Expression>, Span),
|
||||
And(Box<Expression>, Box<Expression>, Span),
|
||||
Eq(Box<Expression>, Box<Expression>, Span),
|
||||
@ -139,6 +140,7 @@ impl<'ast> fmt::Display for Expression {
|
||||
Expression::Integer(ref type_, ref integer, ref _span) => write!(f, "{}{}", integer, type_),
|
||||
|
||||
// Number operations
|
||||
Expression::Negate(ref expression, ref _span) => write!(f, "-{}", expression),
|
||||
Expression::Add(ref left, ref right, ref _span) => write!(f, "{} + {}", left, right),
|
||||
Expression::Sub(ref left, ref right, ref _span) => write!(f, "{} - {}", left, right),
|
||||
Expression::Mul(ref left, ref right, ref _span) => write!(f, "{} * {}", left, right),
|
||||
@ -272,7 +274,7 @@ impl<'ast> From<AstExpression<'ast>> for Expression {
|
||||
match expression {
|
||||
AstExpression::Value(value) => Expression::from(value),
|
||||
AstExpression::Identifier(variable) => Expression::from(variable),
|
||||
AstExpression::Not(expression) => Expression::from(expression),
|
||||
AstExpression::Unary(expression) => Expression::from(expression),
|
||||
AstExpression::Binary(expression) => Expression::from(expression),
|
||||
AstExpression::Ternary(expression) => Expression::from(expression),
|
||||
AstExpression::ArrayInline(expression) => Expression::from(expression),
|
||||
@ -426,12 +428,18 @@ impl<'ast> From<Value<'ast>> for Expression {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast> From<NotExpression<'ast>> for Expression {
|
||||
fn from(expression: NotExpression<'ast>) -> Self {
|
||||
Expression::Not(
|
||||
Box::new(Expression::from(*expression.expression)),
|
||||
Span::from(expression.span),
|
||||
)
|
||||
impl<'ast> From<UnaryExpression<'ast>> for Expression {
|
||||
fn from(expression: UnaryExpression<'ast>) -> Self {
|
||||
match expression.operation {
|
||||
UnaryOperation::Not(_) => Expression::Not(
|
||||
Box::new(Expression::from(*expression.expression)),
|
||||
Span::from(expression.span),
|
||||
),
|
||||
UnaryOperation::Negate(_) => Expression::Negate(
|
||||
Box::new(Expression::from(*expression.expression)),
|
||||
Span::from(expression.span),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user