use crate::CompilerState;
use leo_ast::*;
use leo_span::{Span, Symbol, sym};
use indexmap::IndexMap;
pub struct StorageLoweringVisitor<'a> {
pub state: &'a mut CompilerState,
pub program: Symbol,
pub new_mappings: IndexMap<Location, Mapping>,
}
impl StorageLoweringVisitor<'_> {
pub fn generate_vector_mapping_exprs(&mut self, path: &Path) -> (Expression, Expression) {
let base = path.identifier().name;
let val = Symbol::intern(&format!("{base}__"));
let len = Symbol::intern(&format!("{base}__len__"));
let make_expr = |sym| {
let ident = Identifier::new(sym, self.state.node_builder.next_id());
let mut p = Path::from(ident);
if let Some(program) = path.user_program().filter(|p| p.name != self.program) {
p = p.with_user_program(*program);
}
p.to_global(Location::new(self.program, vec![sym])).into()
};
(make_expr(val), make_expr(len))
}
pub fn literal_false(&mut self) -> Expression {
Literal::boolean(false, Span::default(), self.state.node_builder.next_id()).into()
}
pub fn literal_zero_u32(&mut self) -> Expression {
Literal::integer(IntegerType::U32, "0".to_string(), Span::default(), self.state.node_builder.next_id()).into()
}
pub fn literal_one_u32(&mut self) -> Expression {
Literal::integer(IntegerType::U32, "1".to_string(), Span::default(), self.state.node_builder.next_id()).into()
}
pub fn get_vector_len_expr(&mut self, len_path_expr: Expression, span: Span) -> Expression {
IntrinsicExpression {
name: sym::_mapping_get_or_use,
type_parameters: vec![],
arguments: vec![len_path_expr, self.literal_false(), self.literal_zero_u32()],
span,
id: self.state.node_builder.next_id(),
}
.into()
}
pub fn set_mapping_expr(
&mut self,
path_expr: Expression,
key_expr: Expression,
value_expr: Expression,
span: Span,
) -> Expression {
IntrinsicExpression {
name: sym::_mapping_set,
type_parameters: vec![],
arguments: vec![path_expr, key_expr, value_expr],
span,
id: self.state.node_builder.next_id(),
}
.into()
}
pub fn get_mapping_expr(&mut self, path_expr: Expression, key_expr: Expression, span: Span) -> Expression {
IntrinsicExpression {
name: sym::_mapping_get,
type_parameters: vec![],
arguments: vec![path_expr, key_expr],
span,
id: self.state.node_builder.next_id(),
}
.into()
}
pub fn get_or_use_mapping_expr(
&mut self,
path_expr: Expression,
key_expr: Expression,
default_expr: Expression,
span: Span,
) -> Expression {
IntrinsicExpression {
name: sym::_mapping_get_or_use,
type_parameters: vec![],
arguments: vec![path_expr, key_expr, default_expr],
span,
id: self.state.node_builder.next_id(),
}
.into()
}
pub fn ternary_expr(
&mut self,
condition: Expression,
if_true: Expression,
if_false: Expression,
span: Span,
) -> Expression {
TernaryExpression { condition, if_true, if_false, span, id: self.state.node_builder.next_id() }.into()
}
pub fn binary_expr(&mut self, left: Expression, op: BinaryOperation, right: Expression) -> Expression {
BinaryExpression { op, left, right, span: Span::default(), id: self.state.node_builder.next_id() }.into()
}
pub fn zero(&self, ty: &Type) -> Expression {
let symbol_table = &self.state.symbol_table;
let struct_lookup = |loc: &Location| {
symbol_table
.lookup_struct(self.program, loc)
.unwrap()
.members
.iter()
.map(|mem| (mem.identifier.name, mem.type_.clone()))
.collect()
};
Expression::zero(ty, Span::default(), &self.state.node_builder, &struct_lookup)
.expect("zero value generation failed")
}
pub fn reconstruct_path_or_locator(&self, input: Expression) -> Expression {
let location = match input {
Expression::Path(ref path) if path.is_local() => {
return input;
}
Expression::Path(ref path) => {
path.expect_global_location().clone()
}
_ => panic!("unexpected expression type"),
};
let Some(var) = self.state.symbol_table.lookup_global(self.program, &location) else {
return input;
};
match &var.type_ {
Some(Type::Mapping(_)) => {
input
}
Some(Type::Optional(OptionalType { inner })) => {
let id = || self.state.node_builder.next_id();
let var_name = location.path.last().unwrap();
let mapping_symbol = Symbol::intern(&format!("{var_name}__"));
let mapping_ident = Identifier::new(mapping_symbol, id());
let mapping_expr: Expression = {
let path = if let Expression::Path(path) = input {
path
} else {
panic!("unexpected expression type");
};
let mut base_path = Path::from(mapping_ident);
if let Some(user_program) = path.user_program()
&& user_program.name != self.program
{
base_path = base_path.with_user_program(*user_program);
}
base_path.to_global(Location::new(self.program, vec![mapping_ident.name])).into()
};
let false_literal: Expression = Literal::boolean(false, Span::default(), id()).into();
let contains_expr: Expression = IntrinsicExpression {
name: sym::_mapping_contains,
type_parameters: vec![],
arguments: vec![mapping_expr.clone(), false_literal.clone()],
span: Span::default(),
id: id(),
}
.into();
let zero = self.zero(inner);
let get_or_use_expr: Expression = IntrinsicExpression {
name: sym::_mapping_get_or_use,
type_parameters: vec![],
arguments: vec![mapping_expr.clone(), false_literal, zero],
span: Span::default(),
id: id(),
}
.into();
let none_expr =
Expression::Literal(Literal { variant: LiteralVariant::None, span: Span::default(), id: id() });
let ternary_expr: Expression = TernaryExpression {
condition: contains_expr,
if_true: get_or_use_expr,
if_false: none_expr,
span: Span::default(),
id: id(),
}
.into();
ternary_expr
}
_ => {
panic!("Expected an optional or a mapping, found {:?}", var.type_);
}
}
}
}