mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-12-24 02:31:44 +03:00
add support for refactored array type in compiler
This commit is contained in:
parent
1daf6c9831
commit
d7ff808660
@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::PositiveNumber;
|
||||
use crate::{PositiveNumber, Span};
|
||||
use leo_grammar::types::ArrayDimensions as GrammarArrayDimensions;
|
||||
use leo_input::types::ArrayDimensions as InputArrayDimensions;
|
||||
|
||||
@ -23,16 +23,71 @@ use std::fmt;
|
||||
|
||||
/// A vector of positive numbers that represent array dimensions.
|
||||
/// Can be used in an array [`Type`] or an array initializer [`Expression`].
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct ArrayDimensions(pub Vec<PositiveNumber>);
|
||||
|
||||
impl ArrayDimensions {
|
||||
///
|
||||
/// Creates a new `PositiveNumber` from the given `usize` and `Span`.
|
||||
/// Appends the new `PositiveNumber` to the array dimensions.
|
||||
///
|
||||
pub fn push_usize(&mut self, number: usize, span: Span) {
|
||||
let positive_number = PositiveNumber {
|
||||
value: number.to_string(),
|
||||
span,
|
||||
};
|
||||
|
||||
self.0.push(positive_number)
|
||||
}
|
||||
|
||||
///
|
||||
/// Appends a vector of array dimensions to the self array dimensions.
|
||||
///
|
||||
pub fn append(&mut self, other: &mut ArrayDimensions) {
|
||||
self.0.append(&mut other.0)
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns the array dimensions as strings.
|
||||
///
|
||||
pub fn to_strings(&self) -> Vec<String> {
|
||||
self.0.iter().map(|number| number.to_string()).collect()
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns `true` if the all array dimensions have been removed.
|
||||
///
|
||||
/// This method is called after repeated calls to `remove_first`.
|
||||
///
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns the first dimension of the array.
|
||||
///
|
||||
pub fn first(&self) -> Option<&PositiveNumber> {
|
||||
self.0.first()
|
||||
}
|
||||
|
||||
///
|
||||
/// Attempts to remove the first dimension from the array.
|
||||
///
|
||||
/// If the first dimension exists, then remove and return `Some(PositiveNumber)`.
|
||||
/// If the first dimension does not exist, then return `None`.
|
||||
///
|
||||
pub fn remove_first(&mut self) -> Option<PositiveNumber> {
|
||||
// If there are no dimensions in the array, then return None.
|
||||
if self.0.get(0).is_none() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Remove the first dimension.
|
||||
let removed = self.0.remove(0);
|
||||
|
||||
// Return the first dimension.
|
||||
Some(removed)
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new [`ArrayDimensions`] from a [`GrammarArrayDimensions`] in a Leo program file.
|
||||
|
@ -109,10 +109,10 @@ impl ExpressionError {
|
||||
Self::new_from_span(message, span)
|
||||
}
|
||||
|
||||
pub fn invalid_index(actual: String, span: Span) -> Self {
|
||||
pub fn invalid_index(actual: String, span: &Span) -> Self {
|
||||
let message = format!("index must resolve to an integer, found `{}`", actual);
|
||||
|
||||
Self::new_from_span(message, span)
|
||||
Self::new_from_span(message, span.to_owned())
|
||||
}
|
||||
|
||||
pub fn invalid_length(expected: usize, actual: usize, span: Span) -> Self {
|
||||
@ -157,6 +157,12 @@ impl ExpressionError {
|
||||
Self::new_from_span(message, span)
|
||||
}
|
||||
|
||||
pub fn undefined_tuple(actual: String, span: Span) -> Self {
|
||||
let message = format!("tuple `{}` must be declared before it is used in an expression", actual);
|
||||
|
||||
Self::new_from_span(message, span)
|
||||
}
|
||||
|
||||
pub fn undefined_circuit(actual: String, span: Span) -> Self {
|
||||
let message = format!(
|
||||
"circuit `{}` must be declared before it is used in an expression",
|
||||
|
@ -22,7 +22,7 @@ use crate::{
|
||||
value::ConstrainedValue,
|
||||
GroupType,
|
||||
};
|
||||
use leo_ast::{Expression, Span, SpreadOrExpression, Type};
|
||||
use leo_ast::{ArrayDimensions, Expression, PositiveNumber, Span, SpreadOrExpression, Type};
|
||||
|
||||
use snarkos_models::{
|
||||
curves::{Field, PrimeField},
|
||||
@ -41,20 +41,28 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
span: Span,
|
||||
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
|
||||
// Check explicit array type dimension if given
|
||||
let mut expected_dimensions = vec![];
|
||||
let mut expected_dimension = None;
|
||||
|
||||
if let Some(type_) = expected_type {
|
||||
match type_ {
|
||||
Type::Array(ref type_, ref dimensions) => {
|
||||
let number = match dimensions.first() {
|
||||
Some(number) => *number,
|
||||
Type::Array(type_, mut dimensions) => {
|
||||
// Remove the first dimension of the array.
|
||||
let first = match dimensions.remove_first() {
|
||||
Some(number) => {
|
||||
// Parse the array dimension into a `usize`.
|
||||
parse_index(&number, &span)?
|
||||
}
|
||||
None => return Err(ExpressionError::unexpected_array(type_.to_string(), span)),
|
||||
};
|
||||
|
||||
expected_dimensions.push(number);
|
||||
expected_type = Some(type_.outer_dimension(dimensions));
|
||||
// Update the expected dimension to the first dimension.
|
||||
expected_dimension = Some(first);
|
||||
|
||||
// Update the expected type to a new array type with the first dimension removed.
|
||||
expected_type = Some(inner_array_type(*type_, dimensions));
|
||||
}
|
||||
ref type_ => {
|
||||
// Return an error if the expected type is not an array.
|
||||
return Err(ExpressionError::unexpected_array(type_.to_string(), span));
|
||||
}
|
||||
}
|
||||
@ -88,15 +96,44 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
}
|
||||
}
|
||||
|
||||
// Check expected_dimensions if given
|
||||
if !expected_dimensions.is_empty() && expected_dimensions[expected_dimensions.len() - 1] != result.len() {
|
||||
return Err(ExpressionError::invalid_length(
|
||||
expected_dimensions[expected_dimensions.len() - 1],
|
||||
result.len(),
|
||||
span,
|
||||
));
|
||||
// Check expected_dimension if given.
|
||||
if let Some(dimension) = expected_dimension {
|
||||
// Return an error if the expected dimension != the actual dimension.
|
||||
if dimension != result.len() {
|
||||
return Err(ExpressionError::invalid_length(dimension, result.len(), span));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ConstrainedValue::Array(result))
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns the index as a usize.
|
||||
///
|
||||
pub fn parse_index(number: &PositiveNumber, span: &Span) -> Result<usize, ExpressionError> {
|
||||
number
|
||||
.value
|
||||
.parse::<usize>()
|
||||
.map_err(|_| ExpressionError::invalid_index(number.value.to_owned(), span))
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns the type of the inner array given an array element and array dimensions.
|
||||
///
|
||||
/// If the array has no dimensions, then an inner array does not exist. Simply return the given
|
||||
/// element type.
|
||||
///
|
||||
/// If the array has dimensions, then an inner array exists. Create a new type for the
|
||||
/// inner array. The element type of the new array should be the same as the old array. The
|
||||
/// dimensions of the new array should be the old array dimensions with the first dimension removed.
|
||||
///
|
||||
pub fn inner_array_type(element_type: Type, dimensions: ArrayDimensions) -> Type {
|
||||
if dimensions.is_empty() {
|
||||
// The array has one dimension.
|
||||
element_type
|
||||
} else {
|
||||
// The array has multiple dimensions.
|
||||
Type::Array(Box::new(element_type), dimensions)
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
let expected_type = Some(Type::IntegerType(IntegerType::U32));
|
||||
match self.enforce_operand(cs, file_scope, function_scope, expected_type, index, &span)? {
|
||||
ConstrainedValue::Integer(number) => Ok(number.to_usize(span)?),
|
||||
value => Err(ExpressionError::invalid_index(value.to_string(), span.to_owned())),
|
||||
value => Err(ExpressionError::invalid_index(value.to_string(), span)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -260,9 +260,15 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
Expression::Tuple(tuple, span) => {
|
||||
self.enforce_tuple(cs, file_scope, function_scope, expected_type, tuple, span)
|
||||
}
|
||||
Expression::TupleAccess(tuple, index, span) => {
|
||||
self.enforce_tuple_access(cs, file_scope, function_scope, expected_type, *tuple, index, &span)
|
||||
}
|
||||
Expression::TupleAccess(tuple_w_index, span) => self.enforce_tuple_access(
|
||||
cs,
|
||||
file_scope,
|
||||
function_scope,
|
||||
expected_type,
|
||||
tuple_w_index.0,
|
||||
tuple_w_index.1,
|
||||
&span,
|
||||
),
|
||||
|
||||
// Circuits
|
||||
Expression::Circuit(circuit_name, members, span) => {
|
||||
|
@ -16,8 +16,8 @@
|
||||
|
||||
//! Enforces array access in a compiled Leo program.
|
||||
|
||||
use crate::{errors::ExpressionError, program::ConstrainedProgram, value::ConstrainedValue, GroupType};
|
||||
use leo_ast::{Expression, Span, Type};
|
||||
use crate::{errors::ExpressionError, parse_index, program::ConstrainedProgram, value::ConstrainedValue, GroupType};
|
||||
use leo_ast::{Expression, PositiveNumber, Span, Type};
|
||||
|
||||
use snarkos_models::{
|
||||
curves::{Field, PrimeField},
|
||||
@ -33,18 +33,23 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
function_scope: &str,
|
||||
expected_type: Option<Type>,
|
||||
tuple: Expression,
|
||||
index: usize,
|
||||
index: PositiveNumber,
|
||||
span: &Span,
|
||||
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
|
||||
// Get the tuple values.
|
||||
let tuple = match self.enforce_operand(cs, file_scope, function_scope, expected_type, tuple, &span)? {
|
||||
ConstrainedValue::Tuple(tuple) => tuple,
|
||||
value => return Err(ExpressionError::undefined_array(value.to_string(), span.to_owned())),
|
||||
};
|
||||
|
||||
if index > tuple.len() - 1 {
|
||||
return Err(ExpressionError::index_out_of_bounds(index, span.to_owned()));
|
||||
// Parse the tuple index.
|
||||
let index_usize = parse_index(&index, &span)?;
|
||||
|
||||
// Check for out of bounds access.
|
||||
if index_usize > tuple.len() - 1 {
|
||||
return Err(ExpressionError::index_out_of_bounds(index_usize, span.to_owned()));
|
||||
}
|
||||
|
||||
Ok(tuple[index].to_owned())
|
||||
Ok(tuple[index_usize].to_owned())
|
||||
}
|
||||
}
|
||||
|
@ -18,13 +18,16 @@
|
||||
|
||||
use crate::{
|
||||
errors::FunctionError,
|
||||
inner_array_type,
|
||||
parse_index,
|
||||
program::{new_scope, ConstrainedProgram},
|
||||
value::ConstrainedValue,
|
||||
GroupType,
|
||||
};
|
||||
|
||||
use leo_ast::{InputValue, Span, Type};
|
||||
use leo_ast::{ArrayDimensions, InputValue, Span, Type};
|
||||
|
||||
use crate::errors::ExpressionError;
|
||||
use snarkos_models::{
|
||||
curves::{Field, PrimeField},
|
||||
gadgets::r1cs::ConstraintSystem,
|
||||
@ -36,11 +39,27 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
cs: &mut CS,
|
||||
name: &str,
|
||||
array_type: Type,
|
||||
array_dimensions: Vec<usize>,
|
||||
mut array_dimensions: ArrayDimensions,
|
||||
input_value: Option<InputValue>,
|
||||
span: &Span,
|
||||
) -> Result<ConstrainedValue<F, G>, FunctionError> {
|
||||
let expected_length = array_dimensions[0];
|
||||
let expected_length = match array_dimensions.remove_first() {
|
||||
Some(number) => {
|
||||
// Parse the array dimension into a `usize`.
|
||||
parse_index(&number, &span)?
|
||||
}
|
||||
None => {
|
||||
return Err(FunctionError::ExpressionError(ExpressionError::unexpected_array(
|
||||
array_type.to_string(),
|
||||
span.to_owned(),
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
// Get the expected type for each array element.
|
||||
let inner_array_type = inner_array_type(array_type, array_dimensions);
|
||||
|
||||
// Build the array value using the expected types.
|
||||
let mut array_value = vec![];
|
||||
|
||||
match input_value {
|
||||
@ -48,11 +67,10 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
// Allocate each value in the current row
|
||||
for (i, value) in arr.into_iter().enumerate() {
|
||||
let value_name = new_scope(&name, &i.to_string());
|
||||
let value_type = array_type.outer_dimension(&array_dimensions);
|
||||
|
||||
array_value.push(self.allocate_main_function_input(
|
||||
cs,
|
||||
value_type,
|
||||
inner_array_type.clone(),
|
||||
&value_name,
|
||||
Some(value),
|
||||
span,
|
||||
@ -63,9 +81,14 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
// Allocate all row values as none
|
||||
for i in 0..expected_length {
|
||||
let value_name = new_scope(&name, &i.to_string());
|
||||
let value_type = array_type.outer_dimension(&array_dimensions);
|
||||
|
||||
array_value.push(self.allocate_main_function_input(cs, value_type, &value_name, None, span)?);
|
||||
array_value.push(self.allocate_main_function_input(
|
||||
cs,
|
||||
inner_array_type.clone(),
|
||||
&value_name,
|
||||
None,
|
||||
span,
|
||||
)?);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
|
@ -83,8 +83,8 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
new_value,
|
||||
span,
|
||||
),
|
||||
AssigneeAccess::Tuple(index) => {
|
||||
self.assign_tuple(cs, indicator, &variable_name, index, new_value, span)
|
||||
AssigneeAccess::Tuple(index, span) => {
|
||||
self.assign_tuple(cs, indicator, &variable_name, index, new_value, &span)
|
||||
}
|
||||
AssigneeAccess::Member(identifier) => {
|
||||
// Mutate a circuit variable using the self keyword.
|
||||
|
@ -16,8 +16,8 @@
|
||||
|
||||
//! Enforces a tuple assignment statement in a compiled Leo program.
|
||||
|
||||
use crate::{errors::StatementError, program::ConstrainedProgram, value::ConstrainedValue, GroupType};
|
||||
use leo_ast::Span;
|
||||
use crate::{errors::StatementError, parse_index, program::ConstrainedProgram, value::ConstrainedValue, GroupType};
|
||||
use leo_ast::{PositiveNumber, Span};
|
||||
|
||||
use snarkos_models::{
|
||||
curves::{Field, PrimeField},
|
||||
@ -33,28 +33,32 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
cs: &mut CS,
|
||||
indicator: Option<Boolean>,
|
||||
name: &str,
|
||||
index: usize,
|
||||
index: PositiveNumber,
|
||||
mut new_value: ConstrainedValue<F, G>,
|
||||
span: &Span,
|
||||
) -> Result<(), StatementError> {
|
||||
// Get the indicator value.
|
||||
let condition = indicator.unwrap_or(Boolean::Constant(true));
|
||||
|
||||
// Parse the index.
|
||||
let index_usize = parse_index(&index, &span)?;
|
||||
|
||||
// Modify the single value of the tuple in place
|
||||
match self.get_mutable_assignee(name, &span)? {
|
||||
ConstrainedValue::Tuple(old) => {
|
||||
new_value.resolve_type(Some(old[index].to_type(&span)?), &span)?;
|
||||
new_value.resolve_type(Some(old[index_usize].to_type(&span)?), &span)?;
|
||||
|
||||
let selected_value = ConstrainedValue::conditionally_select(
|
||||
cs.ns(|| format!("select {} {}:{}", new_value, span.line, span.start)),
|
||||
&condition,
|
||||
&new_value,
|
||||
&old[index],
|
||||
&old[index_usize],
|
||||
)
|
||||
.map_err(|_| {
|
||||
StatementError::select_fail(new_value.to_string(), old[index].to_string(), span.to_owned())
|
||||
StatementError::select_fail(new_value.to_string(), old[index_usize].to_string(), span.to_owned())
|
||||
})?;
|
||||
|
||||
old[index] = selected_value;
|
||||
old[index_usize] = selected_value;
|
||||
}
|
||||
_ => return Err(StatementError::tuple_assign_index(span.to_owned())),
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ use crate::{
|
||||
GroupType,
|
||||
Integer,
|
||||
};
|
||||
use leo_ast::{Circuit, Function, GroupValue, Identifier, Span, Type};
|
||||
use leo_ast::{ArrayDimensions, Circuit, Function, GroupValue, Identifier, Span, Type};
|
||||
use leo_core::Value;
|
||||
|
||||
use snarkos_errors::gadgets::SynthesisError;
|
||||
@ -114,7 +114,8 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedValue<F, G> {
|
||||
// Data type wrappers
|
||||
ConstrainedValue::Array(array) => {
|
||||
let array_type = array[0].to_type(span)?;
|
||||
let mut dimensions = vec![array.len()];
|
||||
let mut dimensions = ArrayDimensions::default();
|
||||
dimensions.push_usize(array.len(), span.to_owned());
|
||||
|
||||
// Nested array type
|
||||
if let Type::Array(inner_type, inner_dimensions) = &array_type {
|
||||
|
Loading…
Reference in New Issue
Block a user