mirror of
https://github.com/AleoHQ/leo.git
synced 2024-12-01 18:56:38 +03:00
add support for refactored array initializer in compiler
This commit is contained in:
parent
d7ff808660
commit
1ae66d9908
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user