add array expression module

This commit is contained in:
collin 2020-07-07 23:48:39 -07:00
parent c989061ab1
commit 5d1b242eb4
6 changed files with 190 additions and 151 deletions

View File

@ -0,0 +1,89 @@
//! Enforces constraints on array expressions in a compiled Leo program.
use crate::{
errors::ExpressionError,
program::{new_scope, ConstrainedProgram},
value::ConstrainedValue,
GroupType,
};
use leo_types::{Expression, Span, SpreadOrExpression, Type};
use snarkos_models::{
curves::{Field, PrimeField},
gadgets::r1cs::ConstraintSystem,
};
impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
/// Enforce array expressions
pub fn enforce_array_expression<CS: ConstraintSystem<F>>(
&mut self,
cs: &mut CS,
file_scope: String,
function_scope: String,
expected_types: &Vec<Type>,
array: Vec<Box<SpreadOrExpression>>,
span: Span,
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
// Check explicit array type dimension if given
let mut expected_types = expected_types.clone();
let expected_dimensions = vec![];
if !expected_types.is_empty() {
match expected_types[0] {
Type::Array(ref _type, ref dimensions) => {
expected_types = vec![expected_types[0].inner_dimension(dimensions)];
}
ref _type => {
return Err(ExpressionError::unexpected_array(
expected_types[0].to_string(),
_type.to_string(),
span,
));
}
}
}
let mut result = vec![];
for element in array.into_iter() {
match *element {
SpreadOrExpression::Spread(spread) => match spread {
Expression::Identifier(identifier) => {
let array_name = new_scope(function_scope.clone(), identifier.to_string());
match self.get(&array_name) {
Some(value) => match value {
ConstrainedValue::Array(array) => result.extend(array.clone()),
value => {
return Err(ExpressionError::invalid_spread(value.to_string(), span));
}
},
None => return Err(ExpressionError::undefined_array(identifier.name, span)),
}
}
value => return Err(ExpressionError::invalid_spread(value.to_string(), span)),
},
SpreadOrExpression::Expression(expression) => {
result.push(self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
&expected_types,
expression,
)?);
}
}
}
// Check expected_dimensions if given
if !expected_dimensions.is_empty() {
if expected_dimensions[expected_dimensions.len() - 1] != result.len() {
return Err(ExpressionError::invalid_length(
expected_dimensions[expected_dimensions.len() - 1],
result.len(),
span,
));
}
}
Ok(ConstrainedValue::Array(result))
}
}

View File

@ -0,0 +1,56 @@
//! Enforces constraints on array access expressions in a compiled Leo program.
use crate::{errors::ExpressionError, program::ConstrainedProgram, value::ConstrainedValue, GroupType};
use leo_types::{Expression, RangeOrExpression, Span, Type};
use snarkos_models::{
curves::{Field, PrimeField},
gadgets::r1cs::ConstraintSystem,
};
impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
pub fn enforce_array_access_expression<CS: ConstraintSystem<F>>(
&mut self,
cs: &mut CS,
file_scope: String,
function_scope: String,
expected_types: &Vec<Type>,
array: Box<Expression>,
index: RangeOrExpression,
span: Span,
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
let array = match self.enforce_expression_value(
cs,
file_scope.clone(),
function_scope.clone(),
expected_types,
*array,
span.clone(),
)? {
ConstrainedValue::Array(array) => array,
value => return Err(ExpressionError::undefined_array(value.to_string(), span)),
};
match index {
RangeOrExpression::Range(from, to) => {
let from_resolved = match from {
Some(from_index) => {
self.enforce_index(cs, file_scope.clone(), function_scope.clone(), from_index, span.clone())?
}
None => 0usize, // Array slice starts at index 0
};
let to_resolved = match to {
Some(to_index) => {
self.enforce_index(cs, file_scope.clone(), function_scope.clone(), to_index, span.clone())?
}
None => array.len(), // Array slice ends at array length
};
Ok(ConstrainedValue::Array(array[from_resolved..to_resolved].to_owned()))
}
RangeOrExpression::Expression(index) => {
let index_resolved = self.enforce_index(cs, file_scope, function_scope, index, span)?;
Ok(array[index_resolved].to_owned())
}
}
}
}

View File

@ -0,0 +1,33 @@
//! Enforces constraints on an array index expression in a compiled Leo program.
use crate::{errors::ExpressionError, program::ConstrainedProgram, value::ConstrainedValue, GroupType};
use leo_types::{Expression, IntegerType, Span, Type};
use snarkos_models::{
curves::{Field, PrimeField},
gadgets::r1cs::ConstraintSystem,
};
impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
pub(crate) fn enforce_index<CS: ConstraintSystem<F>>(
&mut self,
cs: &mut CS,
file_scope: String,
function_scope: String,
index: Expression,
span: Span,
) -> Result<usize, ExpressionError> {
let expected_types = vec![Type::IntegerType(IntegerType::U32)];
match self.enforce_expression_value(
cs,
file_scope.clone(),
function_scope.clone(),
&expected_types,
index,
span.clone(),
)? {
ConstrainedValue::Integer(number) => Ok(number.to_usize(span.clone())?),
value => Err(ExpressionError::invalid_index(value.to_string(), span)),
}
}
}

View File

@ -0,0 +1,8 @@
pub mod array;
pub use self::array::*;
pub mod array_access;
pub use self::array_access::*;
pub mod index;
pub use self::index::*;

View File

@ -12,17 +12,7 @@ use crate::{
GroupType,
Integer,
};
use leo_types::{
CircuitFieldDefinition,
CircuitMember,
Expression,
Identifier,
IntegerType,
RangeOrExpression,
Span,
SpreadOrExpression,
Type,
};
use leo_types::{CircuitFieldDefinition, CircuitMember, Expression, Identifier, Span, Type};
use snarkos_models::{
curves::{Field, PrimeField},
@ -67,146 +57,6 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
Ok(result_value)
}
/// Enforce array expressions
fn enforce_array_expression<CS: ConstraintSystem<F>>(
&mut self,
cs: &mut CS,
file_scope: String,
function_scope: String,
expected_types: &Vec<Type>,
array: Vec<Box<SpreadOrExpression>>,
span: Span,
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
// Check explicit array type dimension if given
let mut expected_types = expected_types.clone();
let expected_dimensions = vec![];
if !expected_types.is_empty() {
match expected_types[0] {
Type::Array(ref _type, ref dimensions) => {
expected_types = vec![expected_types[0].inner_dimension(dimensions)];
}
ref _type => {
return Err(ExpressionError::unexpected_array(
expected_types[0].to_string(),
_type.to_string(),
span,
));
}
}
}
let mut result = vec![];
for element in array.into_iter() {
match *element {
SpreadOrExpression::Spread(spread) => match spread {
Expression::Identifier(identifier) => {
let array_name = new_scope(function_scope.clone(), identifier.to_string());
match self.get(&array_name) {
Some(value) => match value {
ConstrainedValue::Array(array) => result.extend(array.clone()),
value => {
return Err(ExpressionError::invalid_spread(value.to_string(), span));
}
},
None => return Err(ExpressionError::undefined_array(identifier.name, span)),
}
}
value => return Err(ExpressionError::invalid_spread(value.to_string(), span)),
},
SpreadOrExpression::Expression(expression) => {
result.push(self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
&expected_types,
expression,
)?);
}
}
}
// Check expected_dimensions if given
if !expected_dimensions.is_empty() {
if expected_dimensions[expected_dimensions.len() - 1] != result.len() {
return Err(ExpressionError::invalid_length(
expected_dimensions[expected_dimensions.len() - 1],
result.len(),
span,
));
}
}
Ok(ConstrainedValue::Array(result))
}
pub(crate) fn enforce_index<CS: ConstraintSystem<F>>(
&mut self,
cs: &mut CS,
file_scope: String,
function_scope: String,
index: Expression,
span: Span,
) -> Result<usize, ExpressionError> {
let expected_types = vec![Type::IntegerType(IntegerType::U32)];
match self.enforce_expression_value(
cs,
file_scope.clone(),
function_scope.clone(),
&expected_types,
index,
span.clone(),
)? {
ConstrainedValue::Integer(number) => Ok(number.to_usize(span.clone())?),
value => Err(ExpressionError::invalid_index(value.to_string(), span)),
}
}
fn enforce_array_access_expression<CS: ConstraintSystem<F>>(
&mut self,
cs: &mut CS,
file_scope: String,
function_scope: String,
expected_types: &Vec<Type>,
array: Box<Expression>,
index: RangeOrExpression,
span: Span,
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
let array = match self.enforce_expression_value(
cs,
file_scope.clone(),
function_scope.clone(),
expected_types,
*array,
span.clone(),
)? {
ConstrainedValue::Array(array) => array,
value => return Err(ExpressionError::undefined_array(value.to_string(), span)),
};
match index {
RangeOrExpression::Range(from, to) => {
let from_resolved = match from {
Some(from_index) => {
self.enforce_index(cs, file_scope.clone(), function_scope.clone(), from_index, span.clone())?
}
None => 0usize, // Array slice starts at index 0
};
let to_resolved = match to {
Some(to_index) => {
self.enforce_index(cs, file_scope.clone(), function_scope.clone(), to_index, span.clone())?
}
None => array.len(), // Array slice ends at array length
};
Ok(ConstrainedValue::Array(array[from_resolved..to_resolved].to_owned()))
}
RangeOrExpression::Expression(index) => {
let index_resolved = self.enforce_index(cs, file_scope, function_scope, index, span)?;
Ok(array[index_resolved].to_owned())
}
}
}
fn enforce_circuit_expression<CS: ConstraintSystem<F>>(
&mut self,
cs: &mut CS,

View File

@ -3,6 +3,9 @@
pub mod arithmetic;
pub use self::arithmetic::*;
pub mod array;
pub use self::array::*;
pub mod conditional;
pub use self::conditional::*;