Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions compiler/ast/src/expressions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ pub use member_access::*;
mod repeat;
pub use repeat::*;

mod slice;
pub use slice::*;

mod struct_init;
pub use struct_init::*;

Expand Down Expand Up @@ -109,6 +112,8 @@ pub enum Expression {
MemberAccess(Box<MemberAccess>),
/// An array expression constructed from one repeated element, e.g., `[1u32; 5]`.
Repeat(Box<RepeatExpression>),
/// An array expression constructed from a slice of another array, e.g., `arr[1..4]`.
Slice(Box<SliceExpression>),
/// An expression constructing a struct like `Foo { bar: 42, baz }`.
Struct(StructExpression),
/// A ternary conditional expression `cond ? if_expr : else_expr`.
Expand Down Expand Up @@ -147,6 +152,7 @@ impl Node for Expression {
Locator(n) => n.span(),
MemberAccess(n) => n.span(),
Repeat(n) => n.span(),
Slice(n) => n.span(),
Struct(n) => n.span(),
Ternary(n) => n.span(),
Tuple(n) => n.span(),
Expand All @@ -173,6 +179,7 @@ impl Node for Expression {
Locator(n) => n.set_span(span),
MemberAccess(n) => n.set_span(span),
Repeat(n) => n.set_span(span),
Slice(n) => n.set_span(span),
Struct(n) => n.set_span(span),
Ternary(n) => n.set_span(span),
Tuple(n) => n.set_span(span),
Expand All @@ -198,6 +205,7 @@ impl Node for Expression {
Locator(n) => n.id(),
MemberAccess(n) => n.id(),
Repeat(n) => n.id(),
Slice(n) => n.id(),
Err(n) => n.id(),
Struct(n) => n.id(),
Ternary(n) => n.id(),
Expand All @@ -224,6 +232,7 @@ impl Node for Expression {
Locator(n) => n.set_id(id),
MemberAccess(n) => n.set_id(id),
Repeat(n) => n.set_id(id),
Slice(n) => n.set_id(id),
Err(n) => n.set_id(id),
Struct(n) => n.set_id(id),
Ternary(n) => n.set_id(id),
Expand Down Expand Up @@ -253,6 +262,7 @@ impl fmt::Display for Expression {
Locator(n) => n.fmt(f),
MemberAccess(n) => n.fmt(f),
Repeat(n) => n.fmt(f),
Slice(n) => n.fmt(f),
Struct(n) => n.fmt(f),
Ternary(n) => n.fmt(f),
Tuple(n) => n.fmt(f),
Expand Down Expand Up @@ -289,6 +299,7 @@ impl Expression {
| Locator(_)
| MemberAccess(_)
| Repeat(_)
| Slice(_)
| Struct(_)
| Tuple(_)
| TupleAccess(_)
Expand Down
60 changes: 60 additions & 0 deletions compiler/ast/src/expressions/slice.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright (C) 2019-2025 Provable Inc.
// This file is part of the Leo library.

// The Leo library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// The Leo library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// 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 super::*;

/// An array constructed from slicing another.
///
/// E.g., `arr[2..5]`, `arr[0..=3]`, `arr[1..]`, `arr[..4]`
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SliceExpression {
/// The array being sliced.
pub array: Expression,
/// The optional starting index.
pub start: Option<Expression>,
/// The optional ending index and whether it's inclusive.
pub end: Option<(bool, Expression)>,
/// The span.
pub span: Span,
/// The ID of the node.
pub id: NodeID,
}

impl fmt::Display for SliceExpression {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// Format the start expression.
let start = match &self.start {
Some(expr) => expr.to_string(),
None => "".to_string(),
};
// Format the end expression.
let end = match &self.end {
Some((true, expr)) => format!("={expr}"),
Some((false, expr)) => expr.to_string(),
None => "".to_string(),
};

write!(f, "{}[{start}..{end}]", self.array)
}
}

impl From<SliceExpression> for Expression {
fn from(value: SliceExpression) -> Self {
Expression::Slice(Box::new(value))
}
}

crate::simple_node_impl!(SliceExpression);
586 changes: 296 additions & 290 deletions compiler/ast/src/interpreter_value/evaluate.rs

Large diffs are not rendered by default.

28 changes: 28 additions & 0 deletions compiler/ast/src/interpreter_value/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,25 @@ impl Value {
Some(array[i].clone().into())
}

pub fn array_slice(&self, start: usize, end_exclusive: Option<usize>) -> Option<Self> {
let plaintext: &SvmPlaintext = self.try_as_ref()?;
let Plaintext::Array(array, ..) = plaintext else {
return None;
};

let range = match end_exclusive {
Some(end) if end <= start || end > array.len() => return None,
Some(end) => start..end,
None if start > array.len() => return None,
None => start..array.len(),
};

Some(Value::from(ValueVariants::Svm(SvmValue::Plaintext(SvmPlaintext::Array(
array[range].to_vec(),
Default::default(),
)))))
}

pub fn array_index_set(&mut self, i: usize, value: Self) -> Option<()> {
let plaintext_rhs: SvmPlaintext = value.try_into().ok()?;

Expand All @@ -584,6 +603,15 @@ impl Value {
Some(())
}

pub fn array_len(&self) -> Option<usize> {
let plaintext: &SvmPlaintext = self.try_as_ref()?;
let Plaintext::Array(array, ..) = plaintext else {
return None;
};

Some(array.len())
}

pub fn tuple_len(&self) -> Option<usize> {
let ValueVariants::Tuple(tuple) = &self.contents else {
return None;
Expand Down
3 changes: 3 additions & 0 deletions compiler/ast/src/passes/consumer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ pub trait ExpressionConsumer {
Expression::Locator(locator) => self.consume_locator(locator),
Expression::MemberAccess(access) => self.consume_member_access(*access),
Expression::Repeat(repeat) => self.consume_repeat(*repeat),
Expression::Slice(slice) => self.consume_slice(*slice),
Expression::Ternary(ternary) => self.consume_ternary(*ternary),
Expression::Tuple(tuple) => self.consume_tuple(tuple),
Expression::TupleAccess(access) => self.consume_tuple_access(*access),
Expand Down Expand Up @@ -82,6 +83,8 @@ pub trait ExpressionConsumer {

fn consume_repeat(&mut self, _input: RepeatExpression) -> Self::Output;

fn consume_slice(&mut self, _input: SliceExpression) -> Self::Output;

fn consume_ternary(&mut self, _input: TernaryExpression) -> Self::Output;

fn consume_tuple(&mut self, _input: TupleExpression) -> Self::Output;
Expand Down
20 changes: 20 additions & 0 deletions compiler/ast/src/passes/reconstructor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ pub trait AstReconstructor {
Expression::Locator(locator) => self.reconstruct_locator(locator, additional),
Expression::MemberAccess(access) => self.reconstruct_member_access(*access, additional),
Expression::Repeat(repeat) => self.reconstruct_repeat(*repeat, additional),
Expression::Slice(slice) => self.reconstruct_slice(*slice, additional),
Expression::Ternary(ternary) => self.reconstruct_ternary(*ternary, additional),
Expression::Tuple(tuple) => self.reconstruct_tuple(tuple, additional),
Expression::TupleAccess(access) => self.reconstruct_tuple_access(*access, additional),
Expand Down Expand Up @@ -215,6 +216,25 @@ pub trait AstReconstructor {
)
}

fn reconstruct_slice(
&mut self,
input: SliceExpression,
_additional: &Self::AdditionalInput,
) -> (Expression, Self::AdditionalOutput) {
(
SliceExpression {
array: self.reconstruct_expression(input.array, &Default::default()).0,
start: input.start.map(|start| self.reconstruct_expression(start, &Default::default()).0),
end: input
.end
.map(|(inclusive, end)| (inclusive, self.reconstruct_expression(end, &Default::default()).0)),
..input
}
.into(),
Default::default(),
)
}

fn reconstruct_tuple_access(
&mut self,
input: TupleAccess,
Expand Down
12 changes: 12 additions & 0 deletions compiler/ast/src/passes/visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ pub trait AstVisitor {
Expression::Locator(locator) => self.visit_locator(locator, additional),
Expression::MemberAccess(access) => self.visit_member_access(access, additional),
Expression::Repeat(repeat) => self.visit_repeat(repeat, additional),
Expression::Slice(slice) => self.visit_slice(slice, additional),
Expression::Ternary(ternary) => self.visit_ternary(ternary, additional),
Expression::Tuple(tuple) => self.visit_tuple(tuple, additional),
Expression::TupleAccess(access) => self.visit_tuple_access(access, additional),
Expand Down Expand Up @@ -227,6 +228,17 @@ pub trait AstVisitor {
Default::default()
}

fn visit_slice(&mut self, input: &SliceExpression, _additional: &Self::AdditionalInput) -> Self::Output {
self.visit_expression(&input.array, &Default::default());
if let Some(start) = input.start.as_ref() {
self.visit_expression(start, &Default::default());
}
if let Some((_, end)) = input.end.as_ref() {
self.visit_expression(end, &Default::default());
}
Default::default()
}

fn visit_ternary(&mut self, input: &TernaryExpression, _additional: &Self::AdditionalInput) -> Self::Output {
self.visit_expression(&input.condition, &Default::default());
self.visit_expression(&input.if_true, &Default::default());
Expand Down
22 changes: 22 additions & 0 deletions compiler/parser-lossless/src/grammar.lalrpop
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,22 @@ Expr1<S>: SyntaxNode<'a> = {
<x:Expr1<S>> <l:WithTrivia<LeftSquare>> <index:Expr> <r:WithTrivia<RightSquare>> => {
SyntaxNode::new(ExpressionKind::ArrayAccess, [x, l, index, r])
},
// Array slice with `[a..b]`
<x:Expr1<S>> <l:WithTrivia<LeftSquare>> <start:Expr> <op:RangeOp> <end:Expr> <r:WithTrivia<RightSquare>> => {
SyntaxNode::new(ExpressionKind::SliceBoth, [x, l, start, op, end, r])
},
// Array slice with `[a..]`
<x:Expr1<S>> <l:WithTrivia<LeftSquare>> <start:Expr> <op:WithTrivia<DotDot>> <r:WithTrivia<RightSquare>> => {
SyntaxNode::new(ExpressionKind::SliceFirst, [x, l, start, op, r])
},
// Array slice with `[..=b]`
<x:Expr1<S>> <l:WithTrivia<LeftSquare>> <op:RangeOp> <end:Expr> <r:WithTrivia<RightSquare>> => {
SyntaxNode::new(ExpressionKind::SliceLast, [x, l, op, end, r])
},
// Array slice with `[..]`
<x:Expr1<S>> <l:WithTrivia<LeftSquare>> <op:WithTrivia<DotDot>> <r:WithTrivia<RightSquare>> => {
SyntaxNode::new(ExpressionKind::SliceNone, [x, l, op, r])
},
// Member access.
<x:Expr1<S>> <d:WithTrivia<Dot>> <i:WithTrivia<Identifier>> => {
SyntaxNode::new(ExpressionKind::MemberAccess, [x, d, i])
Expand All @@ -368,6 +384,11 @@ Expr1<S>: SyntaxNode<'a> = {
},
}

RangeOp: SyntaxNode<'a> = {
WithTrivia<DotDot>,
WithTrivia<DotDotEq>,
}

Op2: SyntaxNode<'a> = {
WithTrivia<Not>,
WithTrivia<Sub>,
Expand Down Expand Up @@ -875,6 +896,7 @@ extern {
Comma => LalrToken { token: Token::Comma, .. },
Dot => LalrToken { token: Token::Dot, .. },
DotDot => LalrToken { token: Token::DotDot, .. },
DotDotEq => LalrToken { token: Token::DotDotEq, .. },
Semicolon => LalrToken { token: Token::Semicolon, .. },
Colon => LalrToken { token: Token::Colon, .. },
DoubleColon => LalrToken{ token: Token::DoubleColon, .. },
Expand Down
4 changes: 4 additions & 0 deletions compiler/parser-lossless/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,10 @@ pub enum ExpressionKind {
MethodCall,
Parenthesized,
Repeat,
SliceBoth,
SliceFirst,
SliceLast,
SliceNone,
// program.id, block.height, etc
SpecialAccess,
Struct,
Expand Down
3 changes: 3 additions & 0 deletions compiler/parser-lossless/src/tokens.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,8 @@ pub enum Token {
Dot,
#[token("..")]
DotDot,
#[token("..=")]
DotDotEq,
#[token(";")]
Semicolon,
#[token(":")]
Expand Down Expand Up @@ -411,6 +413,7 @@ impl Token {
"Comma" => "','",
"Dot" => "'.'",
"DotDot" => "'..'",
"DotDotEq" => "'..='",
"Semicolon" => "';'",
"Colon" => "':'",
"DoubleColon" => "'::'",
Expand Down
46 changes: 46 additions & 0 deletions compiler/parser/src/conversions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -719,6 +719,52 @@ pub fn to_expression(node: &SyntaxNode<'_>, builder: &NodeBuilder, handler: &Han
let count = to_expression(count, builder, handler)?;
leo_ast::RepeatExpression { expr, count, span, id }.into()
}
ExpressionKind::SliceBoth => {
let [array, _left, start, c, end, _right] = &node.children[..] else {
panic!("Can't happen");
};
let array = to_expression(array, builder, handler)?;
let start = Some(to_expression(start, builder, handler)?);
let inclusive = match c.text {
".." => false,
"..=" => true,
_ => panic!("Can't happen"),
};
let end = Some((inclusive, to_expression(end, builder, handler)?));
leo_ast::SliceExpression { array, start, end, span, id }.into()
}
ExpressionKind::SliceFirst => {
let [array, _left, start, _c, _right] = &node.children[..] else {
panic!("Can't happen");
};
let array = to_expression(array, builder, handler)?;
let start = Some(to_expression(start, builder, handler)?);
let end = None;
leo_ast::SliceExpression { array, start, end, span, id }.into()
}
ExpressionKind::SliceLast => {
let [array, _left, _c, end, _right] = &node.children[..] else {
panic!("Can't happen");
};
let array = to_expression(array, builder, handler)?;
let start = None;
let inclusive = match _c.text {
".." => false,
"..=" => true,
_ => panic!("Can't happen"),
};
let end = Some((inclusive, to_expression(end, builder, handler)?));
leo_ast::SliceExpression { array, start, end, span, id }.into()
}
ExpressionKind::SliceNone => {
let [array, _left, _c, _right] = &node.children[..] else {
panic!("Can't happen");
};
let array = to_expression(array, builder, handler)?;
let start = None;
let end = None;
leo_ast::SliceExpression { array, start, end, span, id }.into()
}
ExpressionKind::SpecialAccess => {
let [qualifier, _dot, name] = &node.children[..] else {
panic!("Can't happen");
Expand Down
Loading