/ compiler / compiler / src / test_utils.rs
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  }