test_utils.rs
1 // Copyright (C) 2019-2025 ADnet Contributors 2 // This file is part of the ADL library. 3 4 // The ADL library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 9 // The ADL library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 14 // You should have received a copy of the GNU General Public License 15 // along with the ADL library. If not, see <https://www.gnu.org/licenses/>. 16 17 use crate::Compiler; 18 19 use adl_ast::{NetworkName, Stub}; 20 use adl_errors::{AdlError, Handler}; 21 use adl_span::{Symbol, source_map::FileName}; 22 23 use std::path::PathBuf; 24 25 use indexmap::IndexMap; 26 27 pub const PROGRAM_DELIMITER: &str = "// --- Next Program --- //"; 28 pub const MODULE_DELIMITER: &str = "// --- Next Module:"; 29 30 /// Compiles a complete program from a single source string that may contain 31 /// embedded modules marked by a delimiter. 32 /// 33 /// The source string is expected to contain sections separated by the `MODULE_DELIMITER`, 34 /// each representing either the main source or a named module. The compiler parses each 35 /// section and compiles the full program, including any modules. 36 pub fn whole_compile( 37 source: &str, 38 handler: &Handler, 39 import_stubs: IndexMap<Symbol, Stub>, 40 ) -> Result<(String, String), AdlError> { 41 let mut compiler = Compiler::new( 42 None, 43 /* is_test */ false, 44 handler.clone(), 45 "/fakedirectory-wont-use".into(), 46 None, 47 import_stubs, 48 NetworkName::AlphaTestnetV0, 49 ); 50 51 if !source.contains(MODULE_DELIMITER) { 52 // Fast path: no modules 53 let filename = FileName::Custom("compiler-test".into()); 54 let bytecode = compiler.compile(source, filename.clone(), &Vec::new())?; 55 return Ok((bytecode, compiler.program_name.unwrap())); 56 } 57 58 let mut main_source = String::new(); 59 let mut modules: Vec<(String, PathBuf)> = Vec::new(); 60 61 let mut current_module_path: Option<PathBuf> = None; 62 let mut current_module_source = String::new(); 63 64 for line in source.lines() { 65 if let Some(rest) = line.strip_prefix(MODULE_DELIMITER) { 66 // Save previous block 67 if let Some(path) = current_module_path.take() { 68 modules.push((current_module_source.clone(), path)); 69 current_module_source.clear(); 70 } else { 71 main_source = current_module_source.clone(); 72 current_module_source.clear(); 73 } 74 75 // Start new module 76 let trimmed_path = rest.trim().trim_end_matches(" --- //"); 77 current_module_path = Some(PathBuf::from(trimmed_path)); 78 } else { 79 current_module_source.push_str(line); 80 current_module_source.push('\n'); 81 } 82 } 83 84 // Push the last module or main 85 if let Some(path) = current_module_path { 86 modules.push((current_module_source.clone(), path)); 87 } else { 88 main_source = current_module_source; 89 } 90 91 // Prepare module references for compiler 92 let module_refs: Vec<(&str, FileName)> = 93 modules.iter().map(|(src, path)| (src.as_str(), FileName::Custom(path.to_string_lossy().into()))).collect(); 94 95 let filename = FileName::Custom("compiler-test".into()); 96 let bytecode = compiler.compile(&main_source, filename, &module_refs)?; 97 98 Ok((bytecode, compiler.program_name.unwrap())) 99 }