use crate::{AleoConstructor, AleoExpr, AleoReg, CompilerState};
use crate::{Bytecode, CompiledPrograms};
use leo_ast::{Function, Location, Program, ProgramId, Variant};
use leo_span::Symbol;
use snarkvm::prelude::Network;
use indexmap::{IndexMap, IndexSet};
use itertools::Itertools;
use std::str::FromStr;
pub struct CodeGeneratingVisitor<'a> {
pub state: &'a CompilerState,
pub next_register: u64,
pub current_function: Option<&'a Function>,
pub variable_mapping: IndexMap<Symbol, AleoExpr>,
pub composite_mapping: IndexMap<Location, bool>,
pub global_mapping: IndexMap<Location, String>,
pub variant: Option<Variant>,
pub program: &'a Program,
pub program_id: Option<ProgramId>,
pub finalize_caller: Option<Symbol>,
pub next_label: u64,
pub conditional_depth: u64,
pub internal_record_inputs: IndexSet<AleoExpr>,
}
pub(crate) fn check_snarkvm_constructor<N: Network>(actual: &AleoConstructor) -> snarkvm::prelude::Result<()> {
use snarkvm::synthesizer::program::Constructor as SVMConstructor;
SVMConstructor::<N>::from_str(actual.to_string().trim())?;
Ok(())
}
impl CodeGeneratingVisitor<'_> {
pub(crate) fn visit_package(&mut self) -> CompiledPrograms {
let import_bytecodes = self
.state
.ast
.as_repr()
.stubs
.values()
.filter_map(|stub| {
match stub {
leo_ast::Stub::FromLeo { program, .. } => {
let program_name = program
.program_scopes
.first()
.expect("programs must have a single program scope at this time.")
.0;
let transitive_imports = self
.state
.symbol_table
.get_transitive_imports(program_name)
.into_iter()
.map(|sym| sym.to_string())
.collect::<Vec<_>>();
let mut bytecode = self.visit_program(program);
bytecode.imports.extend(transitive_imports);
Some(Bytecode { program_name: program_name.to_string(), bytecode: bytecode.to_string() })
}
leo_ast::Stub::FromAleo { program, .. } => {
for (name, _) in &program.mappings {
self.global_mapping
.insert(Location::new(program.stub_id.name.name, vec![*name]), name.to_string());
}
for (name, leo_ast::Composite { is_record, .. }) in &program.composites {
self.composite_mapping
.insert(Location::new(program.stub_id.name.name, vec![*name]), *is_record);
}
None
}
}
})
.collect();
let primary_bytecode = self.visit_program(self.state.ast.as_repr()).to_string();
CompiledPrograms { primary_bytecode, import_bytecodes }
}
pub(crate) fn next_register(&mut self) -> AleoReg {
self.next_register += 1;
AleoReg::R(self.next_register - 1)
}
pub(crate) fn legalize_path(path: &[Symbol]) -> Option<String> {
fn is_legal_identifier(s: &str) -> bool {
let mut chars = s.chars();
matches!(chars.next(), Some(c) if c.is_ascii_alphabetic())
&& chars.all(|c| c.is_ascii_alphanumeric() || c == '_')
&& s.len() <= 31
}
fn generate_hashed_name(path: &[Symbol], base: &str) -> String {
use base62::encode;
use sha2::{Digest, Sha256};
use std::fmt::Write;
let full_path = path.iter().format("::").to_string();
let mut hasher = Sha256::new();
hasher.update(full_path.as_bytes());
let hash = hasher.finalize();
let hash_number = u64::from_be_bytes(hash[..8].try_into().unwrap());
let hash_base62 = encode(hash_number);
let fixed_suffix_len = 2 + hash_base62.len(); let max_ident_len = 31 - fixed_suffix_len;
let mut result = String::new();
write!(&mut result, "{base:.max_ident_len$}__{hash_base62}").unwrap();
result
}
let last = path.last()?.to_string();
if path.len() > 1 && !path[..path.len() - 1].iter().all(|sym| is_legal_identifier(&sym.to_string())) {
return None;
}
if path.len() == 1 && is_legal_identifier(&last) {
return Some(last);
}
if let Some(prefix) = last.strip_suffix("__len__") {
let truncated_prefix = &prefix[..13.min(prefix.len())];
return Some(generate_hashed_name(path, &(truncated_prefix.to_owned() + "__len")));
}
if let Some(prefix) = last.strip_suffix("__") {
let truncated_prefix = &prefix[..18.min(prefix.len())];
return Some(generate_hashed_name(path, &(truncated_prefix.to_owned() + "__")));
}
let re = regex::Regex::new(r#"^([a-zA-Z_][\w]*)(?:::\[.*?\])?$"#).unwrap();
if let Some(captures) = re.captures(&last) {
let ident = captures.get(1)?.as_str();
return Some(generate_hashed_name(path, ident));
}
if last.ends_with("?\"") {
return Some(generate_hashed_name(path, "Optional"));
}
None
}
}