mirror of
https://github.com/AleoHQ/leo.git
synced 2024-11-29 11:43:28 +03:00
support tuple parsing and assignment
This commit is contained in:
parent
a84875ea68
commit
e85d6499d0
@ -5,7 +5,7 @@ use crate::{
|
||||
value::ConstrainedValue,
|
||||
GroupType,
|
||||
};
|
||||
use leo_typed::Variable;
|
||||
use leo_typed::Identifier;
|
||||
|
||||
use snarkos_models::curves::{Field, PrimeField};
|
||||
|
||||
@ -13,15 +13,16 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
pub fn store_definition(
|
||||
&mut self,
|
||||
function_scope: String,
|
||||
variable: Variable,
|
||||
mutable: bool,
|
||||
identifier: Identifier,
|
||||
mut value: ConstrainedValue<F, G>,
|
||||
) -> () {
|
||||
// Store with given mutability
|
||||
if variable.mutable {
|
||||
if mutable {
|
||||
value = ConstrainedValue::Mutable(Box::new(value));
|
||||
}
|
||||
|
||||
let variable_program_identifier = new_scope(function_scope, variable.identifier.name);
|
||||
let variable_program_identifier = new_scope(function_scope, identifier.name);
|
||||
|
||||
self.store(variable_program_identifier, value);
|
||||
}
|
||||
|
@ -82,6 +82,12 @@ impl ExpressionError {
|
||||
Self::new_from_span(message, span)
|
||||
}
|
||||
|
||||
pub fn index_out_of_bounds(index: usize, span: Span) -> Self {
|
||||
let message = format!("cannot access index {} of tuple out of bounds", index);
|
||||
|
||||
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);
|
||||
|
||||
|
@ -122,6 +122,12 @@ impl StatementError {
|
||||
Self::new_from_span(message, span)
|
||||
}
|
||||
|
||||
pub fn tuple_assign_index(span: Span) -> Self {
|
||||
let message = format!("Cannot assign single index to tuple of values");
|
||||
|
||||
Self::new_from_span(message, span)
|
||||
}
|
||||
|
||||
pub fn unassigned(name: String, span: Span) -> Self {
|
||||
let message = format!("Expected assignment of return values for expression `{}`", name);
|
||||
|
||||
|
@ -234,6 +234,11 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
self.enforce_array_access(cs, file_scope, function_scope, expected_types, array, *index, span)
|
||||
}
|
||||
|
||||
// Tuples
|
||||
Expression::TupleAccess(tuple, index, span) => {
|
||||
self.enforce_tuple_access(cs, file_scope, function_scope, expected_types, tuple, index, span)
|
||||
}
|
||||
|
||||
// Circuits
|
||||
Expression::Circuit(circuit_name, members, span) => {
|
||||
self.enforce_circuit(cs, file_scope, function_scope, circuit_name, members, span)
|
||||
|
@ -29,3 +29,6 @@ pub use self::logical::*;
|
||||
|
||||
pub mod relational;
|
||||
pub use self::relational::*;
|
||||
|
||||
pub mod tuple;
|
||||
pub use self::tuple::*;
|
||||
|
40
compiler/src/expression/tuple/access.rs
Normal file
40
compiler/src/expression/tuple/access.rs
Normal file
@ -0,0 +1,40 @@
|
||||
//! Enforces array access in a compiled Leo program.
|
||||
|
||||
use crate::{errors::ExpressionError, program::ConstrainedProgram, value::ConstrainedValue, GroupType};
|
||||
use leo_typed::{Expression, Span, Type};
|
||||
|
||||
use snarkos_models::{
|
||||
curves::{Field, PrimeField},
|
||||
gadgets::r1cs::ConstraintSystem,
|
||||
};
|
||||
|
||||
impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
pub fn enforce_tuple_access<CS: ConstraintSystem<F>>(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
file_scope: String,
|
||||
function_scope: String,
|
||||
expected_types: &Vec<Type>,
|
||||
tuple: Box<Expression>,
|
||||
index: usize,
|
||||
span: Span,
|
||||
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
|
||||
let tuple = match self.enforce_operand(
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
expected_types,
|
||||
*tuple,
|
||||
span.clone(),
|
||||
)? {
|
||||
ConstrainedValue::Tuple(tuple) => tuple,
|
||||
value => return Err(ExpressionError::undefined_array(value.to_string(), span.clone())),
|
||||
};
|
||||
|
||||
if index > tuple.len() - 1 {
|
||||
return Err(ExpressionError::index_out_of_bounds(index, span));
|
||||
}
|
||||
|
||||
Ok(tuple[index].to_owned())
|
||||
}
|
||||
}
|
4
compiler/src/expression/tuple/mod.rs
Normal file
4
compiler/src/expression/tuple/mod.rs
Normal file
@ -0,0 +1,4 @@
|
||||
//! Methods to enforce tuple expressions in a compiled Leo program.
|
||||
|
||||
pub mod access;
|
||||
pub use self::access::*;
|
@ -62,6 +62,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
new_value,
|
||||
span,
|
||||
),
|
||||
Assignee::Tuple(_tuple, index) => self.assign_tuple(cs, indicator, variable_name, index, new_value, span),
|
||||
Assignee::CircuitField(_assignee, object_name) => {
|
||||
self.mutute_circuit_field(cs, indicator, variable_name, object_name, new_value, span)
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ pub fn resolve_assignee(scope: String, assignee: Assignee) -> String {
|
||||
match assignee {
|
||||
Assignee::Identifier(name) => new_scope(scope, name.to_string()),
|
||||
Assignee::Array(array, _index) => resolve_assignee(scope, *array),
|
||||
Assignee::Tuple(tuple, _index) => resolve_assignee(scope, *tuple),
|
||||
Assignee::CircuitField(circuit_name, _member) => resolve_assignee(scope, *circuit_name),
|
||||
}
|
||||
}
|
||||
|
@ -11,3 +11,6 @@ pub use self::assignee::*;
|
||||
|
||||
pub mod circuit_field;
|
||||
pub use self::circuit_field::*;
|
||||
|
||||
pub mod tuple;
|
||||
pub use self::tuple::*;
|
||||
|
45
compiler/src/statement/assign/tuple.rs
Normal file
45
compiler/src/statement/assign/tuple.rs
Normal file
@ -0,0 +1,45 @@
|
||||
//! Enforces a tuple assignment statement in a compiled Leo program.
|
||||
|
||||
use crate::{errors::StatementError, program::ConstrainedProgram, value::ConstrainedValue, GroupType};
|
||||
use leo_typed::Span;
|
||||
|
||||
use snarkos_models::{
|
||||
curves::{Field, PrimeField},
|
||||
gadgets::{
|
||||
r1cs::ConstraintSystem,
|
||||
utilities::{boolean::Boolean, select::CondSelectGadget},
|
||||
},
|
||||
};
|
||||
|
||||
impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
pub fn assign_tuple<CS: ConstraintSystem<F>>(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
indicator: Option<Boolean>,
|
||||
name: String,
|
||||
index: usize,
|
||||
mut new_value: ConstrainedValue<F, G>,
|
||||
span: Span,
|
||||
) -> Result<(), StatementError> {
|
||||
let condition = indicator.unwrap_or(Boolean::Constant(true));
|
||||
|
||||
// Modify the single value of the tuple in place
|
||||
match self.get_mutable_assignee(name, span.clone())? {
|
||||
ConstrainedValue::Tuple(old) => {
|
||||
new_value.resolve_type(&vec![old[index].to_type(span.clone())?], span.clone())?;
|
||||
|
||||
let name_unique = format!("select {} {}:{}", new_value, span.line, span.start);
|
||||
let selected_value =
|
||||
ConstrainedValue::conditionally_select(cs.ns(|| name_unique), &condition, &new_value, &old[index])
|
||||
.map_err(|_| {
|
||||
StatementError::select_fail(new_value.to_string(), old[index].to_string(), span)
|
||||
})?;
|
||||
|
||||
old[index] = selected_value;
|
||||
}
|
||||
_ => return Err(StatementError::tuple_assign_index(span)),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
//! Enforces a definition statement in a compiled Leo program.
|
||||
|
||||
use crate::{errors::StatementError, program::ConstrainedProgram, GroupType};
|
||||
use leo_typed::{Declare, Expression, Span, Variable};
|
||||
use crate::{errors::StatementError, program::ConstrainedProgram, ConstrainedValue, GroupType};
|
||||
use leo_typed::{Declare, Expression, Span, VariableName, Variables};
|
||||
|
||||
use snarkos_models::{
|
||||
curves::{Field, PrimeField},
|
||||
@ -9,39 +9,90 @@ use snarkos_models::{
|
||||
};
|
||||
|
||||
impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
fn enforce_single_definition<CS: ConstraintSystem<F>>(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
function_scope: String,
|
||||
is_constant: bool,
|
||||
variable_name: VariableName,
|
||||
mut value: ConstrainedValue<F, G>,
|
||||
span: Span,
|
||||
) -> Result<(), StatementError> {
|
||||
if is_constant && variable_name.mutable {
|
||||
return Err(StatementError::immutable_assign(variable_name.to_string(), span));
|
||||
} else {
|
||||
value.allocate_value(cs, span)?
|
||||
}
|
||||
|
||||
self.store_definition(function_scope, variable_name.mutable, variable_name.identifier, value);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn enforce_definition_statement<CS: ConstraintSystem<F>>(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
file_scope: String,
|
||||
function_scope: String,
|
||||
declare: Declare,
|
||||
variable: Variable,
|
||||
expression: Expression,
|
||||
variables: Variables,
|
||||
expressions: Vec<Expression>,
|
||||
span: Span,
|
||||
) -> Result<(), StatementError> {
|
||||
let mut expected_types = vec![];
|
||||
if let Some(ref _type) = variable._type {
|
||||
expected_types.push(_type.clone());
|
||||
}
|
||||
let mut value = self.enforce_expression(
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
&expected_types,
|
||||
expression,
|
||||
)?;
|
||||
let is_constant = match declare {
|
||||
Declare::Let => false,
|
||||
Declare::Const => true,
|
||||
};
|
||||
|
||||
match declare {
|
||||
Declare::Let => value.allocate_value(cs, span)?,
|
||||
Declare::Const => {
|
||||
if variable.mutable {
|
||||
return Err(StatementError::immutable_assign(variable.to_string(), span));
|
||||
}
|
||||
if variables.names.len() == 1 && expressions.len() == 1 {
|
||||
// Define a single variable with a single value
|
||||
|
||||
let variable = variables.names[0].clone();
|
||||
let expression = self.enforce_expression(
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
&variables.types,
|
||||
expressions[0].clone(),
|
||||
)?;
|
||||
|
||||
self.enforce_single_definition(cs, function_scope, is_constant, variable, expression, span)
|
||||
} else if variables.names.len() == 1 && expressions.len() > 1 {
|
||||
// Define a tuple (single variable with multiple values)
|
||||
|
||||
let implicit_types = variables.types.is_empty();
|
||||
let mut expected_types = vec![];
|
||||
|
||||
for i in 0..expressions.len() {
|
||||
let expected_type = if implicit_types {
|
||||
vec![]
|
||||
} else {
|
||||
vec![variables.types[i].clone()]
|
||||
};
|
||||
|
||||
expected_types.push(expected_type);
|
||||
}
|
||||
|
||||
let mut values = vec![];
|
||||
|
||||
for (expression, expected_type) in expressions.into_iter().zip(expected_types.into_iter()) {
|
||||
let value = self.enforce_expression(
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
&expected_type,
|
||||
expression,
|
||||
)?;
|
||||
|
||||
values.push(value);
|
||||
}
|
||||
|
||||
let tuple = ConstrainedValue::Tuple(values);
|
||||
let variable = variables.names[0].clone();
|
||||
|
||||
self.enforce_single_definition(cs, function_scope, is_constant, variable, tuple, span)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
self.store_definition(function_scope, variable, value);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,3 @@
|
||||
|
||||
pub mod definition;
|
||||
pub use self::definition::*;
|
||||
|
||||
pub mod multiple;
|
||||
pub use self::multiple::*;
|
||||
|
@ -1,54 +0,0 @@
|
||||
//! Enforces a multiple definition statement in a compiled Leo program.
|
||||
|
||||
use crate::{errors::StatementError, program::ConstrainedProgram, value::ConstrainedValue, GroupType};
|
||||
use leo_typed::{Expression, Span, Variable};
|
||||
|
||||
use snarkos_models::{
|
||||
curves::{Field, PrimeField},
|
||||
gadgets::r1cs::ConstraintSystem,
|
||||
};
|
||||
|
||||
impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
pub fn enforce_multiple_definition_statement<CS: ConstraintSystem<F>>(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
file_scope: String,
|
||||
function_scope: String,
|
||||
variables: Vec<Variable>,
|
||||
function: Expression,
|
||||
span: Span,
|
||||
) -> Result<(), StatementError> {
|
||||
let mut expected_types = vec![];
|
||||
for variable in variables.iter() {
|
||||
if let Some(ref _type) = variable._type {
|
||||
expected_types.push(_type.clone());
|
||||
}
|
||||
}
|
||||
|
||||
// Expect return values from function
|
||||
let return_values = match self.enforce_expression(
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
&expected_types,
|
||||
function,
|
||||
)? {
|
||||
ConstrainedValue::Return(values) => values,
|
||||
value => unimplemented!("multiple assignment only implemented for functions, got {}", value),
|
||||
};
|
||||
|
||||
if variables.len() != return_values.len() {
|
||||
return Err(StatementError::invalid_number_of_definitions(
|
||||
variables.len(),
|
||||
return_values.len(),
|
||||
span,
|
||||
));
|
||||
}
|
||||
|
||||
for (variable, value) in variables.into_iter().zip(return_values.into_iter()) {
|
||||
self.store_definition(function_scope.clone(), variable, value);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -34,11 +34,16 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
|
||||
results.push(return_value);
|
||||
}
|
||||
Statement::Definition(declare, variable, expression, span) => {
|
||||
self.enforce_definition_statement(cs, file_scope, function_scope, declare, variable, expression, span)?;
|
||||
}
|
||||
Statement::MultipleDefinition(variables, function, span) => {
|
||||
self.enforce_multiple_definition_statement(cs, file_scope, function_scope, variables, function, span)?;
|
||||
Statement::Definition(declare, variables, expressions, span) => {
|
||||
self.enforce_definition_statement(
|
||||
cs,
|
||||
file_scope,
|
||||
function_scope,
|
||||
declare,
|
||||
variables,
|
||||
expressions,
|
||||
span,
|
||||
)?;
|
||||
}
|
||||
Statement::Assign(variable, expression, span) => {
|
||||
self.enforce_assign_statement(cs, file_scope, function_scope, indicator, variable, expression, span)?;
|
||||
|
@ -37,6 +37,9 @@ pub enum ConstrainedValue<F: Field + PrimeField, G: GroupType<F>> {
|
||||
// Arrays
|
||||
Array(Vec<ConstrainedValue<F, G>>),
|
||||
|
||||
// Tuples
|
||||
Tuple(Vec<ConstrainedValue<F, G>>),
|
||||
|
||||
// Circuits
|
||||
CircuitDefinition(Circuit),
|
||||
CircuitExpression(Identifier, Vec<ConstrainedCircuitMember<F, G>>),
|
||||
@ -208,6 +211,17 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedValue<F, G> {
|
||||
})
|
||||
.collect::<Result<(), ValueError>>()?;
|
||||
}
|
||||
ConstrainedValue::Tuple(tuple) => {
|
||||
tuple
|
||||
.iter_mut()
|
||||
.enumerate()
|
||||
.map(|(i, value)| {
|
||||
let unique_name = format!("allocate tuple member {} {}:{}", i, span.line, span.start);
|
||||
|
||||
value.allocate_value(cs.ns(|| unique_name), span.clone())
|
||||
})
|
||||
.collect::<Result<(), ValueError>>()?;
|
||||
}
|
||||
ConstrainedValue::CircuitExpression(_id, members) => {
|
||||
members
|
||||
.iter_mut()
|
||||
@ -280,6 +294,11 @@ impl<F: Field + PrimeField, G: GroupType<F>> fmt::Display for ConstrainedValue<F
|
||||
}
|
||||
write!(f, "]")
|
||||
}
|
||||
ConstrainedValue::Tuple(ref tuple) => {
|
||||
let values = tuple.iter().map(|x| format!("{}", x)).collect::<Vec<_>>().join(",");
|
||||
|
||||
write!(f, "({})", values)
|
||||
}
|
||||
ConstrainedValue::CircuitExpression(ref identifier, ref members) => {
|
||||
write!(f, "{} {{", identifier)?;
|
||||
for (i, member) in members.iter().enumerate() {
|
||||
|
@ -69,7 +69,6 @@ pub enum Expression {
|
||||
ArrayAccess(Box<Expression>, Box<RangeOrExpression>, Span), // (array name, range)
|
||||
|
||||
// Tuples
|
||||
Tuple(Vec<Expression>, Span),
|
||||
TupleAccess(Box<Expression>, usize, Span),
|
||||
|
||||
// Circuits
|
||||
@ -183,11 +182,6 @@ impl<'ast> fmt::Display for Expression {
|
||||
Expression::ArrayAccess(ref array, ref index, ref _span) => write!(f, "{}[{}]", array, index),
|
||||
|
||||
// Tuples
|
||||
Expression::Tuple(ref tuple, ref _span) => {
|
||||
let values = tuple.iter().map(|x| format!("{}", x)).collect::<Vec<_>>().join(",");
|
||||
|
||||
write!(f, "({})", values)
|
||||
}
|
||||
Expression::TupleAccess(ref tuple, ref index, ref _span) => write!(f, "{}.{}", tuple, index),
|
||||
|
||||
// Circuits
|
||||
|
Loading…
Reference in New Issue
Block a user