use crate::{Compiled, Compiler};
use leo_ast::{Library, NetworkName, NodeBuilder, Program, Stub};
use leo_errors::{Handler, LeoError};
use leo_span::{Symbol, source_map::FileName};
use std::{path::PathBuf, rc::Rc};
use indexmap::IndexMap;
pub const PROGRAM_DELIMITER: &str = "// --- Next Program --- //";
pub const MODULE_DELIMITER: &str = "// --- Next Module:";
#[allow(clippy::type_complexity)]
pub fn whole_compile(
source: &str,
handler: &Handler,
node_builder: &Rc<NodeBuilder>,
import_stubs: IndexMap<Symbol, Stub>,
) -> Result<(Compiled, String), LeoError> {
let (main_source, modules) = split_modules(source);
let mut compiler = Compiler::new(
None,
false,
handler.clone(),
node_builder.clone(),
"/fakedirectory-wont-use".into(),
None,
import_stubs,
NetworkName::TestnetV0,
);
let module_refs: Vec<(&str, FileName)> =
modules.iter().map(|(src, path)| (src.as_str(), FileName::Custom(path.to_string_lossy().into()))).collect();
let filename = FileName::Custom("compiler-test".into());
let bytecodes = compiler.compile(&main_source, filename, &module_refs)?;
Ok((bytecodes, compiler.program_name.unwrap()))
}
pub fn parse_program(
source: &str,
handler: &Handler,
node_builder: &Rc<NodeBuilder>,
import_stubs: IndexMap<Symbol, Stub>,
) -> Result<(Program, String), LeoError> {
let (main_source, modules) = split_modules(source);
let mut compiler = Compiler::new(
None,
false,
handler.clone(),
node_builder.clone(),
"/fakedirectory-wont-use".into(),
None,
import_stubs,
NetworkName::TestnetV0,
);
let module_refs: Vec<(&str, FileName)> =
modules.iter().map(|(src, path)| (src.as_str(), FileName::Custom(path.to_string_lossy().into()))).collect();
let filename = FileName::Custom("compiler-test".into());
let program = compiler.parse_and_return_program(&main_source, filename, &module_refs)?;
Ok((program, compiler.program_name.unwrap()))
}
pub fn parse_library(
library_name: &str,
source: &str,
handler: &Handler,
node_builder: &Rc<NodeBuilder>,
) -> Result<Library, LeoError> {
let (main_source, modules) = split_modules(source);
let mut compiler = Compiler::new(
None,
false,
handler.clone(),
node_builder.clone(),
"/fakedirectory-wont-use".into(),
None,
IndexMap::new(),
NetworkName::TestnetV0,
);
let filename = FileName::Custom("compiler-test".into());
let module_refs: Vec<(&str, FileName)> =
modules.iter().map(|(src, path)| (src.as_str(), FileName::Custom(path.to_string_lossy().into()))).collect();
compiler.parse_and_return_library(library_name, &main_source, filename, &module_refs)
}
fn split_modules(source: &str) -> (String, Vec<(String, PathBuf)>) {
if !source.contains(MODULE_DELIMITER) {
return (source.to_string(), Vec::new());
}
let mut main_source = String::new();
let mut modules: Vec<(String, PathBuf)> = Vec::new();
let mut current_module_path: Option<PathBuf> = None;
let mut current_module_source = String::new();
for line in source.lines() {
if let Some(rest) = line.strip_prefix(MODULE_DELIMITER) {
if let Some(path) = current_module_path.take() {
modules.push((current_module_source.clone(), path));
current_module_source.clear();
} else {
main_source = current_module_source.clone();
current_module_source.clear();
}
let trimmed_path = rest.trim().trim_end_matches(" --- //");
current_module_path = Some(PathBuf::from(trimmed_path));
} else {
current_module_source.push_str(line);
current_module_source.push('\n');
}
}
if let Some(path) = current_module_path {
modules.push((current_module_source.clone(), path));
} else {
main_source = current_module_source;
}
(main_source, modules)
}
pub fn extract_program_name(source: &str, handler: &Handler) -> Result<String, LeoError> {
let (_program, program_name) =
parse_program(source, handler, &Rc::new(NodeBuilder::default()), indexmap::IndexMap::new())?;
Ok(program_name)
}
pub fn extract_aleo_stub_header(source: &str) -> Option<&str> {
let mut offset = 0;
for line in source.lines() {
let trimmed = line.trim();
if trimmed == "// --- aleo stub --- //" {
let rest = &source[offset + line.len()..].trim_start_matches('\n');
return Some(rest);
}
if !trimmed.is_empty() && !trimmed.starts_with("//") {
return None;
}
offset += line.len() + 1;
}
None
}
pub fn extract_library_header(source: &str) -> Option<(String, &str)> {
let mut offset = 0;
for line in source.lines() {
let trimmed = line.trim();
if trimmed.starts_with("// --- library:") && trimmed.ends_with("--- //") {
let name = trimmed.trim_start_matches("// --- library:").trim_end_matches("--- //").trim().to_string();
let rest = &source[offset + line.len()..].trim_start_matches('\n');
return Some((name, rest));
}
offset += line.len() + 1; }
None
}