use crate::CompilerState;
use leo_ast::{
AstReconstructor,
BinaryExpression,
BinaryOperation,
DefinitionPlace,
DefinitionStatement,
Expression,
Identifier,
Node as _,
Path,
Statement,
TupleExpression,
Type,
};
use leo_span::Symbol;
use indexmap::IndexMap;
pub struct DestructuringVisitor<'a> {
pub state: &'a mut CompilerState,
pub tuples: IndexMap<Symbol, Vec<Identifier>>,
pub is_async: bool,
}
impl DestructuringVisitor<'_> {
pub fn reconstruct_expression_tuple(&mut self, expression: Expression) -> (Expression, Vec<Statement>) {
let Type::Tuple(tuple_type) =
self.state.type_table.get(&expression.id()).expect("Expressions should have types.")
else {
return self.reconstruct_expression(expression, &());
};
let (new_expression, mut statements) = self.reconstruct_expression(expression, &());
match new_expression {
Expression::Path(path) => {
let identifiers = self.tuples.get(&path.identifier().name).expect("Tuples should have been found");
let elements: Vec<Expression> =
identifiers.iter().map(|identifier| Path::from(*identifier).to_local().into()).collect();
let tuple: Expression =
TupleExpression { elements, span: Default::default(), id: self.state.node_builder.next_id() }
.into();
self.state.type_table.insert(tuple.id(), Type::Tuple(tuple_type.clone()));
(tuple, statements)
}
tuple @ Expression::Tuple(..) => {
(tuple, statements)
}
expr @ Expression::Call(..) => {
let definition_stmt = self.assign_tuple(expr, Symbol::intern("destructure"));
let Statement::Definition(DefinitionStatement {
place: DefinitionPlace::Multiple(identifiers), ..
}) = &definition_stmt
else {
panic!("`assign_tuple` always creates a definition with `Multiple`");
};
let elements = identifiers.iter().map(|identifier| Path::from(*identifier).to_local().into()).collect();
let expr = Expression::Tuple(TupleExpression {
elements,
span: Default::default(),
id: self.state.node_builder.next_id(),
});
self.state.type_table.insert(expr.id(), Type::Tuple(tuple_type.clone()));
statements.push(definition_stmt);
(expr, statements)
}
_ => panic!("Tuples may only be identifiers, tuple literals, or calls."),
}
}
pub fn fold_with_op<I>(&mut self, op: BinaryOperation, pieces: I) -> Expression
where
I: Iterator<Item = Expression>,
{
pieces
.reduce(|left, right| {
let expr: Expression = BinaryExpression {
op,
left,
right,
span: Default::default(),
id: self.state.node_builder.next_id(),
}
.into();
self.state.type_table.insert(expr.id(), Type::Boolean);
expr
})
.expect("fold_with_op called with empty iterator")
}
pub fn assign_tuple(&mut self, expression: Expression, name: Symbol) -> Statement {
let Type::Tuple(tuple_type) =
self.state.type_table.get(&expression.id()).expect("Expressions should have types.")
else {
panic!("assign_tuple should only be called for tuple types.");
};
let new_identifiers: Vec<Identifier> = (0..tuple_type.length())
.map(|i| {
let new_symbol = self.state.assigner.unique_symbol(name, format_args!("#{i}#"));
Identifier::new(new_symbol, self.state.node_builder.next_id())
})
.collect();
Statement::Definition(DefinitionStatement {
place: DefinitionPlace::Multiple(new_identifiers),
type_: Some(Type::Tuple(tuple_type.clone())),
value: expression,
span: Default::default(),
id: self.state.node_builder.next_id(),
})
}
}