use crate::CodeGenerator;
use leo_ast::{Composite, Function, Location, Mapping, Member, Mode, Program, ProgramScope, Type, Variant};
use leo_span::{Symbol, sym};
use indexmap::IndexMap;
use std::fmt::Write as _;
const EXPECT_STR: &str = "Failed to write code";
impl<'a> CodeGenerator<'a> {
pub(crate) fn visit_program(&mut self, input: &'a Program) -> String {
let mut program_string = String::new();
input.stubs.iter().for_each(|(program_name, _)| {
writeln!(program_string, "import {}.aleo;", program_name).expect(EXPECT_STR);
});
let program_scope: &ProgramScope = input.program_scopes.values().next().unwrap();
self.program_id = Some(program_scope.program_id);
writeln!(program_string, "program {};", program_scope.program_id).expect(EXPECT_STR);
let order = self.struct_graph.post_order().unwrap();
let this_program = self.program_id.unwrap().name.name;
let lookup = |name: Symbol| {
self.symbol_table
.lookup_struct(name)
.or_else(|| self.symbol_table.lookup_record(Location::new(this_program, name)))
};
for name in order.into_iter() {
if let Some(struct_) = lookup(name) {
program_string.push_str(&self.visit_struct_or_record(struct_));
}
}
for (_symbol, mapping) in program_scope.mappings.iter() {
program_string.push_str(&self.visit_mapping(mapping));
}
for (_symbol, function) in program_scope.functions.iter() {
if function.variant != Variant::AsyncFunction {
let mut function_string = self.visit_function(function);
if function.variant == Variant::AsyncTransition {
self.finalize_caller = Some(function.identifier.name);
let finalize = &self
.symbol_table
.lookup_function(Location::new(self.program_id.unwrap().name.name, function.identifier.name))
.unwrap()
.clone()
.finalizer
.unwrap();
function_string.push_str(&self.visit_function_with(
&program_scope.functions.iter().find(|(name, _f)| name == &finalize.location.name).unwrap().1,
&finalize.future_inputs,
));
}
program_string.push_str(&function_string);
}
}
program_string
}
fn visit_struct_or_record(&mut self, struct_: &'a Composite) -> String {
if struct_.is_record { self.visit_record(struct_) } else { self.visit_struct(struct_) }
}
fn visit_struct(&mut self, struct_: &'a Composite) -> String {
self.composite_mapping.insert(&struct_.identifier.name, (false, String::from("private")));
let mut output_string = format!("\nstruct {}:\n", struct_.identifier);
for var in struct_.members.iter() {
writeln!(output_string, " {} as {};", var.identifier, Self::visit_type(&var.type_),).expect(EXPECT_STR);
}
output_string
}
fn visit_record(&mut self, record: &'a Composite) -> String {
self.composite_mapping.insert(&record.identifier.name, (true, "record".into()));
let mut output_string = format!("\nrecord {}:\n", record.identifier);
let mut members = Vec::with_capacity(record.members.len());
let mut member_map: IndexMap<Symbol, Member> =
record.members.clone().into_iter().map(|member| (member.identifier.name, member)).collect();
members.push(member_map.shift_remove(&sym::owner).unwrap());
members.extend(member_map.into_iter().map(|(_, member)| member));
for var in members.iter() {
let mode = match var.mode {
Mode::Constant => "constant",
Mode::Public => "public",
Mode::None | Mode::Private => "private",
};
writeln!(
output_string,
" {} as {}.{mode};", var.identifier,
Self::visit_type(&var.type_)
)
.expect(EXPECT_STR);
}
output_string
}
fn visit_function_with(&mut self, function: &'a Function, futures: &[Location]) -> String {
self.next_register = 0;
self.variable_mapping = IndexMap::new();
self.variant = Some(function.variant);
self.variable_mapping.insert(&sym::SelfLower, "self".to_string());
self.variable_mapping.insert(&sym::block, "block".to_string());
self.variable_mapping.insert(&sym::network, "network".to_string());
self.current_function = Some(function);
let mut function_string = match function.variant {
Variant::Transition | Variant::AsyncTransition => format!("\nfunction {}:\n", function.identifier),
Variant::Function => format!("\nclosure {}:\n", function.identifier),
Variant::AsyncFunction => format!("\nfinalize {}:\n", self.finalize_caller.unwrap()),
Variant::Inline => return String::new(),
};
let mut futures = futures.iter();
for input in function.input.iter() {
let register_string = format!("r{}", self.next_register);
self.next_register += 1;
let type_string = {
self.variable_mapping.insert(&input.identifier.name, register_string.clone());
let visibility = match (self.variant.unwrap(), input.mode) {
(Variant::AsyncTransition, Mode::None) | (Variant::Transition, Mode::None) => Mode::Private,
(Variant::AsyncFunction, Mode::None) => Mode::Public,
_ => input.mode,
};
if matches!(input.type_, Type::Future(_)) {
let location = *futures
.next()
.expect("Type checking guarantees we have future locations for each future input");
format!("{}.aleo/{}.future", location.program, location.name)
} else {
self.visit_type_with_visibility(&input.type_, visibility)
}
};
writeln!(function_string, " input {register_string} as {type_string};",).expect(EXPECT_STR);
}
let block_string = self.visit_block(&function.block);
if matches!(self.variant.unwrap(), Variant::Function | Variant::AsyncFunction)
&& block_string.lines().all(|line| line.starts_with(" output "))
{
function_string.push_str(" assert.eq true true;\n");
}
function_string.push_str(&block_string);
function_string
}
fn visit_function(&mut self, function: &'a Function) -> String {
self.visit_function_with(function, &[])
}
fn visit_mapping(&mut self, mapping: &'a Mapping) -> String {
let mut mapping_string = format!("\nmapping {}:\n", mapping.identifier);
let create_type = |type_: &Type| {
match type_ {
Type::Mapping(_) | Type::Tuple(_) => unreachable!("Mappings cannot contain mappings or tuples."),
Type::Identifier(identifier) => {
let (is_record, _) = self.composite_mapping.get(&identifier.name).unwrap();
match is_record {
false => self.visit_type_with_visibility(type_, Mode::Public),
true => unreachable!("Type checking guarantees that mappings cannot contain records."),
}
}
type_ => self.visit_type_with_visibility(type_, Mode::Public),
}
};
writeln!(mapping_string, " key as {};", create_type(&mapping.key_type)).expect(EXPECT_STR);
writeln!(mapping_string, " value as {};", create_type(&mapping.value_type)).expect(EXPECT_STR);
self.global_mapping.insert(&mapping.identifier.name, mapping.identifier.to_string());
mapping_string
}
}