1
0
Fork 0
mirror of synced 2025-09-24 04:40:05 +00:00

implement read-only array slices

This commit is contained in:
schaeff 2019-06-16 09:45:53 +02:00
parent 22fd4f8b5c
commit 3d06ee1d5c
10 changed files with 235 additions and 27 deletions

View file

@ -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]

View file

@ -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),

View file

@ -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>>),
}

View file

@ -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 {}

View file

@ -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) => {

View 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]

View 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"]
}
}
}
]
}

View file

@ -13,4 +13,5 @@ zokrates_test! {
array_if,
fact_up_to_4,
split,
spread_slice,
}

View file

@ -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* }

View file

@ -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>,
}