add support for refactored array initializer in compiler

This commit is contained in:
collin 2020-11-06 19:42:07 -08:00
parent d7ff808660
commit 1ae66d9908
4 changed files with 108 additions and 4 deletions

View File

@ -19,11 +19,14 @@ use leo_grammar::types::ArrayDimensions as GrammarArrayDimensions;
use leo_input::types::ArrayDimensions as InputArrayDimensions;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::{
fmt,
hash::{Hash, Hasher},
};
/// A vector of positive numbers that represent array dimensions.
/// Can be used in an array [`Type`] or an array initializer [`Expression`].
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct ArrayDimensions(pub Vec<PositiveNumber>);
impl ArrayDimensions {
@ -127,3 +130,27 @@ impl fmt::Display for ArrayDimensions {
}
}
}
/// Compares two array dimensions and ignores `Span`s.
impl PartialEq for ArrayDimensions {
fn eq(&self, other: &Self) -> bool {
// If the number of dimensions differs return false
if self.0.len() != other.0.len() {
return false;
}
// Iteratively compare each dimension.
self.0
.iter()
.zip(&other.0)
.all(|(first, second)| first.value.eq(&second.value))
}
}
impl Eq for ArrayDimensions {}
impl Hash for ArrayDimensions {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.hash(state)
}
}

View File

@ -15,7 +15,7 @@
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::errors::{AddressError, BooleanError, FieldError, FunctionError, GroupError, IntegerError, ValueError};
use leo_ast::{Error as FormattedError, Identifier, Span};
use leo_ast::{ArrayDimensions, Error as FormattedError, Identifier, Span};
use leo_core::LeoCorePackageError;
use snarkos_errors::gadgets::SynthesisError;
@ -109,6 +109,15 @@ impl ExpressionError {
Self::new_from_span(message, span)
}
pub fn invalid_dimensions(expected: &ArrayDimensions, actual: &ArrayDimensions, span: Span) -> Self {
let message = format!(
"expected array dimensions {}, found array dimensions {}",
expected, actual
);
Self::new_from_span(message, span)
}
pub fn invalid_index(actual: String, span: &Span) -> Self {
let message = format!("index must resolve to an integer, found `{}`", actual);

View File

@ -40,9 +40,9 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
array: Vec<SpreadOrExpression>,
span: Span,
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
// Check explicit array type dimension if given
let mut expected_dimension = None;
// Check explicit array type dimension if given
if let Some(type_) = expected_type {
match type_ {
Type::Array(type_, mut dimensions) => {
@ -106,6 +106,65 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
Ok(ConstrainedValue::Array(result))
}
/// Enforce array expressions
pub fn enforce_array_initializer<CS: ConstraintSystem<F>>(
&mut self,
cs: &mut CS,
file_scope: &str,
function_scope: &str,
mut expected_type: Option<Type>,
element_expression: Expression,
mut actual_dimensions: ArrayDimensions,
span: Span,
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
let mut expected_dimensions = None;
// Check explicit array type dimension if given
if let Some(type_) = expected_type {
match type_ {
Type::Array(element_type, dimensions) => {
// Update the expected type to the element type.
expected_type = Some(*element_type);
// Update the expected dimensions to the first dimension.
expected_dimensions = Some(dimensions);
}
ref type_ => {
// Return an error if the expected type is not an array.
return Err(ExpressionError::unexpected_array(type_.to_string(), span));
}
}
}
// Evaluate array element expression.
let mut value = self.enforce_expression(cs, file_scope, function_scope, expected_type, element_expression)?;
// Check array dimensions.
if let Some(dimensions) = expected_dimensions {
if dimensions.ne(&actual_dimensions) {
return Err(ExpressionError::invalid_dimensions(
&dimensions,
&actual_dimensions,
span,
));
}
}
// Allocate the array.
while let Some(dimension) = actual_dimensions.remove_first() {
// Parse the dimension into a `usize`.
let dimension_usize = parse_index(&dimension, &span)?;
// Allocate the array dimension.
let array = vec![value; dimension_usize];
// Set the array value.
value = ConstrainedValue::Array(array);
}
Ok(value)
}
}
///

View File

@ -246,6 +246,15 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
Expression::ArrayInline(array, span) => {
self.enforce_array(cs, file_scope, function_scope, expected_type, array, span)
}
Expression::ArrayInitializer(array_w_dimensions, span) => self.enforce_array_initializer(
cs,
file_scope,
function_scope,
expected_type,
array_w_dimensions.0,
array_w_dimensions.1,
span,
),
Expression::ArrayAccess(array_w_index, span) => self.enforce_array_access(
cs,
file_scope,