use crate::CompilerState;
use leo_ast::{Function, 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, String>,
pub composite_mapping: IndexMap<Vec<Symbol>, (bool, String)>,
pub global_mapping: IndexMap<Symbol, 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<String>,
}
pub(crate) fn check_snarkvm_constructor<N: Network>(actual: &str) -> snarkvm::prelude::Result<()> {
use snarkvm::synthesizer::program::Constructor as SVMConstructor;
SVMConstructor::<N>::from_str(actual.trim())?;
Ok(())
}
impl CodeGeneratingVisitor<'_> {
pub(crate) fn next_register(&mut self) -> String {
self.next_register += 1;
format!("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
}
}