mirror of
https://github.com/ProvableHQ/leo.git
synced 2025-01-08 20:11:12 +03:00
constraints array access
This commit is contained in:
parent
c14ed231ea
commit
025d9ab45d
@ -1,14 +1,5 @@
|
||||
struct Point {
|
||||
field x
|
||||
field y
|
||||
}
|
||||
bool[2] a = [true, false]
|
||||
|
||||
Point p = Point {x: 1, y: 0}
|
||||
|
||||
//bool[2] a = [true, false]
|
||||
//bool[2] b = [true; 2]
|
||||
|
||||
//field[4] c = [1, 2, 3, 4]
|
||||
//field[3] d = [1; 3]
|
||||
p = a[0..2]
|
||||
|
||||
return p
|
@ -1,6 +1,7 @@
|
||||
use crate::aleo_program::{
|
||||
BooleanExpression, BooleanSpreadOrExpression, Expression, FieldExpression,
|
||||
FieldSpreadOrExpression, Function, Program, Statement, Struct, StructMember, Type, Variable,
|
||||
FieldRangeOrExpression, FieldSpreadOrExpression, Function, Program, Statement, Struct,
|
||||
StructMember, Type, Variable,
|
||||
};
|
||||
|
||||
use snarkos_models::curves::{Field, PrimeField};
|
||||
@ -463,6 +464,70 @@ impl ResolvedProgram {
|
||||
}
|
||||
}
|
||||
|
||||
fn enforce_index<F: Field + PrimeField, CS: ConstraintSystem<F>>(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
index: FieldExpression,
|
||||
) -> usize {
|
||||
match self.enforce_field_expression(cs, index) {
|
||||
ResolvedValue::FieldElement(number) => number.value.unwrap() as usize,
|
||||
value => unimplemented!("From index must resolve to a uint32, got {}", value),
|
||||
}
|
||||
}
|
||||
|
||||
fn enforce_array_access_expression<F: Field + PrimeField, CS: ConstraintSystem<F>>(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
array: Box<Expression>,
|
||||
index: FieldRangeOrExpression,
|
||||
) -> ResolvedValue {
|
||||
match self.enforce_expression(cs, *array) {
|
||||
ResolvedValue::FieldElementArray(field_array) => {
|
||||
match index {
|
||||
FieldRangeOrExpression::Range(from, to) => {
|
||||
let from_resolved = match from {
|
||||
Some(from_index) => self.enforce_index(cs, from_index),
|
||||
None => 0usize, // Array slice starts at index 0
|
||||
};
|
||||
let to_resolved = match to {
|
||||
Some(to_index) => self.enforce_index(cs, to_index),
|
||||
None => field_array.len(), // Array slice ends at array length
|
||||
};
|
||||
ResolvedValue::FieldElementArray(
|
||||
field_array[from_resolved..to_resolved].to_owned(),
|
||||
)
|
||||
}
|
||||
FieldRangeOrExpression::FieldExpression(index) => {
|
||||
let index_resolved = self.enforce_index(cs, index);
|
||||
ResolvedValue::FieldElement(field_array[index_resolved].to_owned())
|
||||
}
|
||||
}
|
||||
}
|
||||
ResolvedValue::BooleanArray(bool_array) => {
|
||||
match index {
|
||||
FieldRangeOrExpression::Range(from, to) => {
|
||||
let from_resolved = match from {
|
||||
Some(from_index) => self.enforce_index(cs, from_index),
|
||||
None => 0usize, // Array slice starts at index 0
|
||||
};
|
||||
let to_resolved = match to {
|
||||
Some(to_index) => self.enforce_index(cs, to_index),
|
||||
None => bool_array.len(), // Array slice ends at array length
|
||||
};
|
||||
ResolvedValue::BooleanArray(
|
||||
bool_array[from_resolved..to_resolved].to_owned(),
|
||||
)
|
||||
}
|
||||
FieldRangeOrExpression::FieldExpression(index) => {
|
||||
let index_resolved = self.enforce_index(cs, index);
|
||||
ResolvedValue::Boolean(bool_array[index_resolved].to_owned())
|
||||
}
|
||||
}
|
||||
}
|
||||
value => unimplemented!("Cannot access element of untyped array"),
|
||||
}
|
||||
}
|
||||
|
||||
fn enforce_expression<F: Field + PrimeField, CS: ConstraintSystem<F>>(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
@ -499,6 +564,9 @@ impl ResolvedProgram {
|
||||
Expression::Struct(struct_name, members) => {
|
||||
self.enforce_struct_expression(cs, struct_name, members)
|
||||
}
|
||||
Expression::ArrayAccess(array, index) => {
|
||||
self.enforce_array_access_expression(cs, array, index)
|
||||
} // _ => unimplemented!("expression not enforced yet")
|
||||
}
|
||||
}
|
||||
|
||||
@ -596,6 +664,7 @@ impl ResolvedProgram {
|
||||
Expression::Struct(_v, _m) => {
|
||||
unimplemented!("return struct not impl");
|
||||
}
|
||||
_ => unimplemented!("expression can't be returned yet"),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -21,6 +21,13 @@ pub enum FieldSpreadOrExpression {
|
||||
FieldExpression(FieldExpression),
|
||||
}
|
||||
|
||||
/// Range or field expression enum
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum FieldRangeOrExpression {
|
||||
Range(Option<FieldExpression>, Option<FieldExpression>),
|
||||
FieldExpression(FieldExpression),
|
||||
}
|
||||
|
||||
/// Expression that evaluates to a field value
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum FieldExpression {
|
||||
@ -85,7 +92,9 @@ pub enum Expression {
|
||||
Boolean(BooleanExpression),
|
||||
FieldElement(FieldExpression),
|
||||
Variable(Variable),
|
||||
ArrayAccess(Box<Expression>, FieldRangeOrExpression),
|
||||
Struct(Variable, Vec<StructMember>),
|
||||
// StructMemberAccess(Variable, Variable)// (struct name, struct member name)
|
||||
}
|
||||
|
||||
/// Program statement that defines some action (or expression) to be carried out.
|
||||
@ -98,8 +107,8 @@ pub enum Statement {
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Type {
|
||||
FieldElement,
|
||||
Boolean,
|
||||
FieldElement,
|
||||
Array(Box<Type>, usize),
|
||||
Struct(Variable),
|
||||
}
|
||||
|
@ -6,7 +6,8 @@
|
||||
|
||||
use crate::aleo_program::{
|
||||
BooleanExpression, BooleanSpread, BooleanSpreadOrExpression, Expression, FieldExpression,
|
||||
FieldSpread, FieldSpreadOrExpression, Statement, Struct, StructField, Type, Variable,
|
||||
FieldRangeOrExpression, FieldSpread, FieldSpreadOrExpression, Statement, Struct, StructField,
|
||||
Type, Variable,
|
||||
};
|
||||
|
||||
use std::fmt;
|
||||
@ -108,6 +109,22 @@ impl<'ast> fmt::Display for BooleanExpression {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast> fmt::Display for FieldRangeOrExpression {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
FieldRangeOrExpression::Range(ref from, ref to) => write!(
|
||||
f,
|
||||
"{}..{}",
|
||||
from.as_ref()
|
||||
.map(|e| e.to_string())
|
||||
.unwrap_or("".to_string()),
|
||||
to.as_ref().map(|e| e.to_string()).unwrap_or("".to_string())
|
||||
),
|
||||
FieldRangeOrExpression::FieldExpression(ref e) => write!(f, "{}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast> fmt::Display for Expression {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
@ -124,6 +141,8 @@ impl<'ast> fmt::Display for Expression {
|
||||
}
|
||||
write!(f, "}}")
|
||||
}
|
||||
Expression::ArrayAccess(ref array, ref index) => write!(f, "{}[{}]", array, index),
|
||||
_ => unimplemented!("can't display expression yet"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -76,11 +76,7 @@ impl<'ast> From<ast::Expression<'ast>> for types::BooleanExpression {
|
||||
match types::Expression::from(expression) {
|
||||
types::Expression::Boolean(boolean_expression) => boolean_expression,
|
||||
types::Expression::Variable(variable) => types::BooleanExpression::Variable(variable),
|
||||
types::Expression::FieldElement(field_expression) => unimplemented!(
|
||||
"cannot compare field expression {} in boolean expression",
|
||||
field_expression
|
||||
),
|
||||
types::Expression::Struct(_v, _m) => unimplemented!("no inline struct yet"),
|
||||
_ => unimplemented!("expected boolean in boolean expression"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -90,11 +86,7 @@ impl<'ast> From<ast::Expression<'ast>> for types::FieldExpression {
|
||||
match types::Expression::from(expression) {
|
||||
types::Expression::FieldElement(field_expression) => field_expression,
|
||||
types::Expression::Variable(variable) => types::FieldExpression::Variable(variable),
|
||||
types::Expression::Boolean(boolean_expression) => unimplemented!(
|
||||
"cannot compare boolean expression {} in field expression",
|
||||
boolean_expression
|
||||
),
|
||||
types::Expression::Struct(_v, _m) => unimplemented!("no inline struct yet"),
|
||||
_ => unimplemented!("expected field in field expression"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -281,6 +273,71 @@ impl<'ast> From<ast::TernaryExpression<'ast>> for types::Expression {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast> From<ast::RangeOrExpression<'ast>> for types::FieldRangeOrExpression {
|
||||
fn from(range_or_expression: ast::RangeOrExpression<'ast>) -> Self {
|
||||
match range_or_expression {
|
||||
ast::RangeOrExpression::Range(range) => {
|
||||
let from = range
|
||||
.from
|
||||
.map(|from| match types::Expression::from(from.0) {
|
||||
types::Expression::FieldElement(field) => field,
|
||||
expression => {
|
||||
unimplemented!("Range bounds should be numbers, found {}", expression)
|
||||
}
|
||||
});
|
||||
let to = range.to.map(|to| match types::Expression::from(to.0) {
|
||||
types::Expression::FieldElement(field) => field,
|
||||
expression => {
|
||||
unimplemented!("Range bounds should be numbers, found {}", expression)
|
||||
}
|
||||
});
|
||||
|
||||
types::FieldRangeOrExpression::Range(from, to)
|
||||
}
|
||||
ast::RangeOrExpression::Expression(expression) => {
|
||||
match types::Expression::from(expression) {
|
||||
types::Expression::FieldElement(field_expression) => {
|
||||
types::FieldRangeOrExpression::FieldExpression(field_expression)
|
||||
}
|
||||
// types::Expression::ArrayAccess(expression, field), // recursive array access
|
||||
expression => unimplemented!("expression must be field, found {}", expression),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast> From<ast::PostfixExpression<'ast>> for types::Expression {
|
||||
fn from(expression: ast::PostfixExpression<'ast>) -> Self {
|
||||
let variable = types::Expression::Variable(types::Variable::from(expression.variable));
|
||||
|
||||
// ast::PostFixExpression contains an array of "accesses": `a(34)[42]` is represented as `[a, [Call(34), Select(42)]]`, but Access call expressions
|
||||
// are recursive, so it is `Select(Call(a, 34), 42)`. We apply this transformation here
|
||||
|
||||
// we start with the id, and we fold the array of accesses by wrapping the current value
|
||||
expression
|
||||
.accesses
|
||||
.into_iter()
|
||||
.fold(variable, |acc, access| match access {
|
||||
ast::Access::Call(a) => match acc {
|
||||
types::Expression::Variable(_) => {
|
||||
unimplemented!("function calls not implemented")
|
||||
}
|
||||
expression => {
|
||||
unimplemented!("only function names are callable, found \"{}\"", expression)
|
||||
}
|
||||
},
|
||||
ast::Access::Member(struct_member) => {
|
||||
unimplemented!("struct calls not implemented")
|
||||
}
|
||||
ast::Access::Select(array) => types::Expression::ArrayAccess(
|
||||
Box::new(acc),
|
||||
types::FieldRangeOrExpression::from(array.expression),
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast> From<ast::Expression<'ast>> for types::Expression {
|
||||
fn from(expression: ast::Expression<'ast>) -> Self {
|
||||
match expression {
|
||||
@ -295,6 +352,7 @@ impl<'ast> From<ast::Expression<'ast>> for types::Expression {
|
||||
ast::Expression::ArrayInitializer(_expression) => {
|
||||
unimplemented!("unknown type for array initializer expression")
|
||||
}
|
||||
ast::Expression::Postfix(expression) => types::Expression::from(expression),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
16
src/ast.rs
16
src/ast.rs
@ -392,13 +392,13 @@ pub enum RangeOrExpression<'ast> {
|
||||
Expression(Expression<'ast>),
|
||||
}
|
||||
|
||||
// #[derive(Clone, Debug, FromPest, PartialEq)]
|
||||
// #[pest_ast(rule(Rule::call_access))]
|
||||
// pub struct CallAccess<'ast> {
|
||||
// pub expressions: Vec<Expression<'ast>>,
|
||||
// #[pest_ast(outer())]
|
||||
// pub span: Span<'ast>,
|
||||
// }
|
||||
#[derive(Clone, Debug, FromPest, PartialEq)]
|
||||
#[pest_ast(rule(Rule::access_call))]
|
||||
pub struct CallAccess<'ast> {
|
||||
pub expressions: Vec<Expression<'ast>>,
|
||||
#[pest_ast(outer())]
|
||||
pub span: Span<'ast>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, FromPest, PartialEq)]
|
||||
#[pest_ast(rule(Rule::access_array))]
|
||||
@ -419,7 +419,7 @@ pub struct MemberAccess<'ast> {
|
||||
#[derive(Clone, Debug, FromPest, PartialEq)]
|
||||
#[pest_ast(rule(Rule::access))]
|
||||
pub enum Access<'ast> {
|
||||
// Call(CallAccess<'ast>),
|
||||
Call(CallAccess<'ast>),
|
||||
Select(ArrayAccess<'ast>),
|
||||
Member(MemberAccess<'ast>),
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ access_array = { "[" ~ range_or_expression ~ "]" }
|
||||
access_call = { "(" ~ expression_tuple ~ ")" }
|
||||
access_member = { "." ~ variable }
|
||||
access = { access_array | access_call | access_member }
|
||||
expression_postfix = { variable ~ access+ } // add ++ and -- operators
|
||||
expression_postfix = { variable ~ access+ }
|
||||
|
||||
spread = { "..." ~ expression }
|
||||
spread_or_expression = { spread | expression }
|
||||
|
Loading…
Reference in New Issue
Block a user