implement read-only array slices
This commit is contained in:
parent
22fd4f8b5c
commit
3d06ee1d5c
10 changed files with 235 additions and 27 deletions
|
@ -1,5 +1,6 @@
|
|||
def main() -> (field):
|
||||
field[3] a = [1, 2, 3]
|
||||
field[4] b = [...a, 4]
|
||||
field[2] c = a[1..3]
|
||||
a[2] = 4
|
||||
return a[0] + a[2]
|
|
@ -342,6 +342,43 @@ impl<'ast, T: Field> From<pest::Spread<'ast>> for absy::SpreadNode<'ast, T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'ast, T: Field> From<pest::Range<'ast>> for absy::RangeNode<T> {
|
||||
fn from(range: pest::Range<'ast>) -> absy::RangeNode<T> {
|
||||
use absy::NodeValue;
|
||||
|
||||
let from = range
|
||||
.from
|
||||
.map(|e| match absy::ExpressionNode::from(e.0).value {
|
||||
absy::Expression::Number(n) => n,
|
||||
e => unimplemented!("Range bounds should be constants, found {}", e),
|
||||
});
|
||||
|
||||
let to = range
|
||||
.to
|
||||
.map(|e| match absy::ExpressionNode::from(e.0).value {
|
||||
absy::Expression::Number(n) => n,
|
||||
e => unimplemented!("Range bounds should be constants, found {}", e),
|
||||
});
|
||||
|
||||
absy::Range { from, to }.span(range.span)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast, T: Field> From<pest::RangeOrExpression<'ast>> for absy::RangeOrExpression<'ast, T> {
|
||||
fn from(
|
||||
range_or_expression: pest::RangeOrExpression<'ast>,
|
||||
) -> absy::RangeOrExpression<'ast, T> {
|
||||
match range_or_expression {
|
||||
pest::RangeOrExpression::Expression(e) => {
|
||||
absy::RangeOrExpression::Expression(absy::ExpressionNode::from(e))
|
||||
}
|
||||
pest::RangeOrExpression::Range(r) => {
|
||||
absy::RangeOrExpression::Range(absy::RangeNode::from(r))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast, T: Field> From<pest::SpreadOrExpression<'ast>> for absy::SpreadOrExpression<'ast, T> {
|
||||
fn from(
|
||||
spread_or_expression: pest::SpreadOrExpression<'ast>,
|
||||
|
@ -400,7 +437,7 @@ impl<'ast, T: Field> From<pest::PostfixExpression<'ast>> for absy::ExpressionNod
|
|||
),
|
||||
pest::Access::Select(a) => absy::Expression::Select(
|
||||
box absy::ExpressionNode::from(expression.id),
|
||||
box absy::ExpressionNode::from(a.expression),
|
||||
box absy::RangeOrExpression::from(a.expression),
|
||||
),
|
||||
}
|
||||
.span(expression.span)
|
||||
|
@ -439,7 +476,7 @@ impl<'ast, T: Field> From<pest::Assignee<'ast>> for absy::AssigneeNode<'ast, T>
|
|||
0 => a,
|
||||
1 => absy::Assignee::ArrayElement(
|
||||
box a,
|
||||
box absy::ExpressionNode::from(assignee.indices[0].clone()),
|
||||
box absy::RangeOrExpression::from(assignee.indices[0].clone()),
|
||||
)
|
||||
.span(assignee.span),
|
||||
n => unimplemented!("Array should have one dimension, found {} in {}", n, a),
|
||||
|
|
|
@ -132,7 +132,7 @@ impl<'ast, T: Field> fmt::Debug for Function<'ast, T> {
|
|||
#[derive(Clone, PartialEq)]
|
||||
pub enum Assignee<'ast, T: Field> {
|
||||
Identifier(Identifier<'ast>),
|
||||
ArrayElement(Box<AssigneeNode<'ast, T>>, Box<ExpressionNode<'ast, T>>),
|
||||
ArrayElement(Box<AssigneeNode<'ast, T>>, Box<RangeOrExpression<'ast, T>>),
|
||||
}
|
||||
|
||||
pub type AssigneeNode<'ast, T> = Node<Assignee<'ast, T>>;
|
||||
|
@ -238,6 +238,30 @@ impl<'ast, T: Field> fmt::Debug for SpreadOrExpression<'ast, T> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub enum RangeOrExpression<'ast, T: Field> {
|
||||
Range(RangeNode<T>),
|
||||
Expression(ExpressionNode<'ast, T>),
|
||||
}
|
||||
|
||||
impl<'ast, T: Field> fmt::Display for RangeOrExpression<'ast, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
RangeOrExpression::Range(ref s) => write!(f, "{}", s),
|
||||
RangeOrExpression::Expression(ref e) => write!(f, "{}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast, T: Field> fmt::Debug for RangeOrExpression<'ast, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
RangeOrExpression::Range(ref s) => write!(f, "{:?}", s),
|
||||
RangeOrExpression::Expression(ref e) => write!(f, "{:?}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type SpreadNode<'ast, T> = Node<Spread<'ast, T>>;
|
||||
|
||||
impl<'ast, T: Field> fmt::Display for Spread<'ast, T> {
|
||||
|
@ -257,6 +281,37 @@ pub struct Spread<'ast, T: Field> {
|
|||
pub expression: ExpressionNode<'ast, T>,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct Range<T: Field> {
|
||||
pub from: Option<T>,
|
||||
pub to: Option<T>,
|
||||
}
|
||||
|
||||
pub type RangeNode<T> = Node<Range<T>>;
|
||||
|
||||
impl<'ast, T: Field> fmt::Display for Range<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}..{}",
|
||||
self.from
|
||||
.as_ref()
|
||||
.map(|e| e.to_string())
|
||||
.unwrap_or("".to_string()),
|
||||
self.to
|
||||
.as_ref()
|
||||
.map(|e| e.to_string())
|
||||
.unwrap_or("".to_string())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast, T: Field> fmt::Debug for Range<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Range({:?}, {:?})", self.from, self.to)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub enum Expression<'ast, T: Field> {
|
||||
Number(T),
|
||||
|
@ -280,7 +335,10 @@ pub enum Expression<'ast, T: Field> {
|
|||
And(Box<ExpressionNode<'ast, T>>, Box<ExpressionNode<'ast, T>>),
|
||||
Not(Box<ExpressionNode<'ast, T>>),
|
||||
InlineArray(Vec<SpreadOrExpression<'ast, T>>),
|
||||
Select(Box<ExpressionNode<'ast, T>>, Box<ExpressionNode<'ast, T>>),
|
||||
Select(
|
||||
Box<ExpressionNode<'ast, T>>,
|
||||
Box<RangeOrExpression<'ast, T>>,
|
||||
),
|
||||
Or(Box<ExpressionNode<'ast, T>>, Box<ExpressionNode<'ast, T>>),
|
||||
}
|
||||
|
||||
|
|
|
@ -69,6 +69,7 @@ impl<'ast, T: Field> NodeValue for Statement<'ast, T> {}
|
|||
impl<'ast, T: Field> NodeValue for Function<'ast, T> {}
|
||||
impl<'ast, T: Field> NodeValue for Prog<'ast, T> {}
|
||||
impl<'ast, T: Field> NodeValue for Spread<'ast, T> {}
|
||||
impl<T: Field> NodeValue for Range<T> {}
|
||||
impl<'ast> NodeValue for Variable<'ast> {}
|
||||
impl<'ast> NodeValue for Parameter<'ast> {}
|
||||
impl NodeValue for Import {}
|
||||
|
|
|
@ -497,7 +497,13 @@ impl<'ast> Checker<'ast> {
|
|||
},
|
||||
Assignee::ArrayElement(box assignee, box index) => {
|
||||
let checked_assignee = self.check_assignee(assignee)?;
|
||||
let checked_index = self.check_expression(index)?;
|
||||
let checked_index = match index {
|
||||
RangeOrExpression::Expression(e) => self.check_expression(e)?,
|
||||
r => unimplemented!(
|
||||
"Using slices in assignments is not supported yet, found {}",
|
||||
r
|
||||
),
|
||||
};
|
||||
|
||||
let checked_typed_index = match checked_index {
|
||||
TypedExpression::FieldElement(e) => Ok(e),
|
||||
|
@ -870,19 +876,79 @@ impl<'ast> Checker<'ast> {
|
|||
}
|
||||
Expression::Select(box array, box index) => {
|
||||
let array = self.check_expression(array)?;
|
||||
let index = self.check_expression(index)?;
|
||||
match (array, index) {
|
||||
(TypedExpression::FieldElementArray(a), TypedExpression::FieldElement(i)) => {
|
||||
Ok(FieldElementExpression::Select(box a, box i).into())
|
||||
}
|
||||
(a, e) => Err(Error {
|
||||
pos: Some(pos),
|
||||
message: format!(
|
||||
"Cannot access element {} on expression of type {}",
|
||||
e,
|
||||
a.get_type()
|
||||
),
|
||||
}),
|
||||
//let index = self.check_range_or_expression(index)?;
|
||||
|
||||
match index {
|
||||
RangeOrExpression::Range(r) => match array {
|
||||
TypedExpression::FieldElementArray(array) => {
|
||||
let array_size = array.size();
|
||||
|
||||
let from = r
|
||||
.value
|
||||
.from
|
||||
.map(|v| v.to_dec_string().parse::<usize>().unwrap())
|
||||
.unwrap_or(0);
|
||||
|
||||
let to = r
|
||||
.value
|
||||
.to
|
||||
.map(|v| v.to_dec_string().parse::<usize>().unwrap())
|
||||
.unwrap_or(array_size);
|
||||
|
||||
println!("from {} to {}", from, to);
|
||||
|
||||
match (from, to, array_size) {
|
||||
(f, _, s) if f > s => Err(Error {
|
||||
pos: Some(pos),
|
||||
message: format!(
|
||||
"Lower range bound {} is out of array bounds [0, {}]",
|
||||
f, s,
|
||||
),
|
||||
}),
|
||||
(_, t, s) if t > s => Err(Error {
|
||||
pos: Some(pos),
|
||||
message: format!(
|
||||
"Higher range bound {} is out of array bounds [0, {}]",
|
||||
t, s,
|
||||
),
|
||||
}),
|
||||
(f, t, _) if f > t => Err(Error {
|
||||
pos: Some(pos),
|
||||
message: format!(
|
||||
"Lower range bound {} is larger than higher range bound {}",
|
||||
f, t,
|
||||
),
|
||||
}),
|
||||
(f, t, _) => Ok(FieldElementArrayExpression::Value(
|
||||
t - f,
|
||||
(f..t)
|
||||
.map(|i| {
|
||||
FieldElementExpression::Select(
|
||||
box array.clone(),
|
||||
box FieldElementExpression::Number(T::from(i)),
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
.into()),
|
||||
}
|
||||
}
|
||||
_ => panic!(""),
|
||||
},
|
||||
RangeOrExpression::Expression(e) => match (array, self.check_expression(e)?) {
|
||||
(
|
||||
TypedExpression::FieldElementArray(a),
|
||||
TypedExpression::FieldElement(i),
|
||||
) => Ok(FieldElementExpression::Select(box a, box i).into()),
|
||||
(a, e) => Err(Error {
|
||||
pos: Some(pos),
|
||||
message: format!(
|
||||
"Cannot access element {} on expression of type {}",
|
||||
e,
|
||||
a.get_type()
|
||||
),
|
||||
}),
|
||||
},
|
||||
}
|
||||
}
|
||||
Expression::InlineArray(expressions) => {
|
||||
|
|
2
zokrates_core/tests/bench/spread_slice.code
Normal file
2
zokrates_core/tests/bench/spread_slice.code
Normal file
|
@ -0,0 +1,2 @@
|
|||
def main(field[3] a, field[3] b, field c) -> (field[9]):
|
||||
return [...a[..2], ...b[1..], ...a[..], ...b[1..2], c]
|
14
zokrates_core/tests/bench/spread_slice.json
Normal file
14
zokrates_core/tests/bench/spread_slice.json
Normal file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"tests": [
|
||||
{
|
||||
"input": {
|
||||
"values": ["1", "2", "3", "4", "5", "6", "7"]
|
||||
},
|
||||
"output": {
|
||||
"Ok": {
|
||||
"values": ["1", "2", "5", "6", "1", "2", "3", "5", "7"]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -13,4 +13,5 @@ zokrates_test! {
|
|||
array_if,
|
||||
fact_up_to_4,
|
||||
split,
|
||||
spread_slice,
|
||||
}
|
||||
|
|
|
@ -50,12 +50,15 @@ expression_list = _{(expression ~ ("," ~ expression)*)?}
|
|||
expression = { term ~ (op_binary ~ term)* }
|
||||
term = { ("(" ~ expression ~ ")") | conditional_expression | postfix_expression | primary_expression | inline_array_expression | unary_expression }
|
||||
spread = { "..." ~ expression }
|
||||
range = { from_expression? ~ ".." ~ to_expression? }
|
||||
from_expression = { expression }
|
||||
to_expression = { expression }
|
||||
|
||||
conditional_expression = { "if" ~ expression ~ "then" ~ expression ~ "else" ~ expression ~ "fi"}
|
||||
|
||||
postfix_expression = { identifier ~ access+ } // we force there to be at least one access, otherwise this matches single identifiers. Not sure that's what we want.
|
||||
access = { array_access | call_access }
|
||||
array_access = { "[" ~ expression ~ "]" }
|
||||
array_access = { "[" ~ range_or_expression ~ "]" }
|
||||
call_access = { "(" ~ expression_list ~ ")" }
|
||||
|
||||
primary_expression = { identifier
|
||||
|
@ -65,11 +68,12 @@ primary_expression = { identifier
|
|||
inline_array_expression = { "[" ~ inline_array_inner ~ "]" }
|
||||
inline_array_inner = _{(spread_or_expression ~ ("," ~ spread_or_expression)*)?}
|
||||
spread_or_expression = { spread | expression }
|
||||
range_or_expression = { range | expression }
|
||||
unary_expression = { op_unary ~ term }
|
||||
|
||||
// End Expressions
|
||||
|
||||
assignee = { identifier ~ ("[" ~ expression ~ "]")* }
|
||||
assignee = { identifier ~ ("[" ~ range_or_expression ~ "]")* }
|
||||
identifier = @{ ((!keyword ~ ASCII_ALPHA) | (keyword ~ (ASCII_ALPHANUMERIC | "_"))) ~ (ASCII_ALPHANUMERIC | "_")* }
|
||||
constant = @{ "0" | ASCII_NONZERO_DIGIT ~ ASCII_DIGIT* }
|
||||
|
||||
|
|
|
@ -10,10 +10,10 @@ extern crate lazy_static;
|
|||
pub use ast::{
|
||||
Access, ArrayAccess, ArrayType, AssertionStatement, Assignee, AssignmentStatement, BasicType,
|
||||
BinaryExpression, BinaryOperator, CallAccess, ConstantExpression, DefinitionStatement,
|
||||
Expression, File, Function, IdentifierExpression, ImportDirective, ImportSource,
|
||||
InlineArrayExpression, IterationStatement, MultiAssignmentStatement, Parameter,
|
||||
PostfixExpression, ReturnStatement, Span, Spread, SpreadOrExpression, Statement,
|
||||
TernaryExpression, Type, UnaryExpression, UnaryOperator, Visibility,
|
||||
Expression, File, FromExpression, Function, IdentifierExpression, ImportDirective,
|
||||
ImportSource, InlineArrayExpression, IterationStatement, MultiAssignmentStatement, Parameter,
|
||||
PostfixExpression, Range, RangeOrExpression, ReturnStatement, Span, Spread, SpreadOrExpression,
|
||||
Statement, TernaryExpression, ToExpression, Type, UnaryExpression, UnaryOperator, Visibility,
|
||||
};
|
||||
|
||||
mod ast {
|
||||
|
@ -370,6 +370,30 @@ mod ast {
|
|||
pub span: Span<'ast>,
|
||||
}
|
||||
|
||||
#[derive(Debug, FromPest, PartialEq, Clone)]
|
||||
#[pest_ast(rule(Rule::range_or_expression))]
|
||||
pub enum RangeOrExpression<'ast> {
|
||||
Range(Range<'ast>),
|
||||
Expression(Expression<'ast>),
|
||||
}
|
||||
|
||||
#[derive(Debug, FromPest, PartialEq, Clone)]
|
||||
#[pest_ast(rule(Rule::range))]
|
||||
pub struct Range<'ast> {
|
||||
pub from: Option<FromExpression<'ast>>,
|
||||
pub to: Option<ToExpression<'ast>>,
|
||||
#[pest_ast(outer())]
|
||||
pub span: Span<'ast>,
|
||||
}
|
||||
|
||||
#[derive(Debug, FromPest, PartialEq, Clone)]
|
||||
#[pest_ast(rule(Rule::from_expression))]
|
||||
pub struct FromExpression<'ast>(pub Expression<'ast>);
|
||||
|
||||
#[derive(Debug, FromPest, PartialEq, Clone)]
|
||||
#[pest_ast(rule(Rule::to_expression))]
|
||||
pub struct ToExpression<'ast>(pub Expression<'ast>);
|
||||
|
||||
#[derive(Debug, FromPest, PartialEq, Clone)]
|
||||
#[pest_ast(rule(Rule::postfix_expression))]
|
||||
pub struct PostfixExpression<'ast> {
|
||||
|
@ -423,7 +447,7 @@ mod ast {
|
|||
#[derive(Debug, FromPest, PartialEq, Clone)]
|
||||
#[pest_ast(rule(Rule::array_access))]
|
||||
pub struct ArrayAccess<'ast> {
|
||||
pub expression: Expression<'ast>,
|
||||
pub expression: RangeOrExpression<'ast>,
|
||||
#[pest_ast(outer())]
|
||||
pub span: Span<'ast>,
|
||||
}
|
||||
|
@ -532,8 +556,8 @@ mod ast {
|
|||
#[derive(Debug, FromPest, PartialEq, Clone)]
|
||||
#[pest_ast(rule(Rule::assignee))]
|
||||
pub struct Assignee<'ast> {
|
||||
pub id: IdentifierExpression<'ast>, // a
|
||||
pub indices: Vec<Expression<'ast>>, // [42 + x][31][7]
|
||||
pub id: IdentifierExpression<'ast>, // a
|
||||
pub indices: Vec<RangeOrExpression<'ast>>, // [42 + x][31][7]
|
||||
#[pest_ast(outer())]
|
||||
pub span: Span<'ast>,
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue