1use crate::{AstSnapshots, CompilerOptions};
22
23pub use leo_ast::Ast;
24use leo_ast::Stub;
25use leo_errors::{CompilerError, Handler, Result};
26use leo_passes::*;
27use leo_span::{Symbol, source_map::FileName, with_session_globals};
28
29use snarkvm::prelude::Network;
30
31use indexmap::{IndexMap, IndexSet};
32use std::{
33 fs,
34 path::{Path, PathBuf},
35};
36
37pub struct Compiler<N: Network> {
39 output_directory: PathBuf,
41 pub program_name: Option<String>,
43 compiler_options: CompilerOptions,
45 state: CompilerState,
47 import_stubs: IndexMap<Symbol, Stub>,
49 pub statements_before_dce: u32,
51 pub statements_after_dce: u32,
53 phantom: std::marker::PhantomData<N>,
55}
56
57impl<N: Network> Compiler<N> {
58 pub fn parse(&mut self, source: &str, filename: FileName) -> Result<()> {
59 let source_file = with_session_globals(|s| s.source_map.new_source(source, filename));
61
62 self.state.ast = leo_parser::parse_ast::<N>(
64 self.state.handler.clone(),
65 &self.state.node_builder,
66 &source_file.src,
67 source_file.absolute_start,
68 )?;
69
70 let program_scope = self.state.ast.ast.program_scopes.values().next().unwrap();
73 if self.program_name.is_none() {
74 self.program_name = Some(program_scope.program_id.name.to_string());
75 } else if self.program_name != Some(program_scope.program_id.name.to_string()) {
76 return Err(CompilerError::program_name_should_match_file_name(
77 program_scope.program_id.name,
78 self.program_name.as_ref().unwrap(),
79 program_scope.program_id.name.span,
80 )
81 .into());
82 }
83
84 if self.compiler_options.initial_ast {
85 self.write_ast_to_json("initial.json")?;
86 self.write_ast("initial.ast")?;
87 }
88
89 Ok(())
90 }
91
92 pub fn parse_from_file(&mut self, source_file_path: impl AsRef<Path>) -> Result<()> {
93 let source = fs::read_to_string(&source_file_path)
95 .map_err(|e| CompilerError::file_read_error(source_file_path.as_ref().display().to_string(), e))?;
96 self.parse(&source, FileName::Real(source_file_path.as_ref().into()))
97 }
98
99 pub fn new(
101 expected_program_name: Option<String>,
102 is_test: bool,
103 handler: Handler,
104 output_directory: PathBuf,
105 compiler_options: Option<CompilerOptions>,
106 import_stubs: IndexMap<Symbol, Stub>,
107 ) -> Self {
108 Self {
109 state: CompilerState { handler, is_test, ..Default::default() },
110 output_directory,
111 program_name: expected_program_name,
112 compiler_options: compiler_options.unwrap_or_default(),
113 import_stubs,
114 statements_before_dce: 0,
115 statements_after_dce: 0,
116 phantom: Default::default(),
117 }
118 }
119
120 fn do_pass<P: Pass>(&mut self, input: P::Input) -> Result<P::Output> {
121 let output = P::do_pass(input, &mut self.state)?;
122
123 let write = match &self.compiler_options.ast_snapshots {
124 AstSnapshots::All => true,
125 AstSnapshots::Some(passes) => passes.contains(P::NAME),
126 };
127
128 if write {
129 self.write_ast_to_json(&format!("{}.json", P::NAME))?;
130 self.write_ast(&format!("{}.ast", P::NAME))?;
131 }
132
133 Ok(output)
134 }
135
136 pub fn intermediate_passes(&mut self) -> Result<()> {
138 let type_checking_config = TypeCheckingInput {
139 max_array_elements: N::MAX_ARRAY_ELEMENTS,
140 max_mappings: N::MAX_MAPPINGS,
141 max_functions: N::MAX_FUNCTIONS,
142 };
143
144 self.do_pass::<SymbolTableCreation>(())?;
145
146 self.do_pass::<TypeChecking>(type_checking_config.clone())?;
147
148 self.do_pass::<StaticAnalyzing>(())?;
149
150 self.do_pass::<ConstPropagationAndUnrolling>(type_checking_config)?;
151
152 self.do_pass::<ProcessingScript>(())?;
153
154 self.do_pass::<SsaForming>(SsaFormingInput { rename_defs: true })?;
155
156 self.do_pass::<Destructuring>(())?;
157
158 self.do_pass::<SsaForming>(SsaFormingInput { rename_defs: false })?;
159
160 self.do_pass::<WriteTransforming>(())?;
161
162 self.do_pass::<SsaForming>(SsaFormingInput { rename_defs: false })?;
163
164 self.do_pass::<Flattening>(())?;
165
166 self.do_pass::<FunctionInlining>(())?;
167
168 let output = self.do_pass::<DeadCodeEliminating>(())?;
169 self.statements_before_dce = output.statements_before;
170 self.statements_after_dce = output.statements_after;
171
172 Ok(())
173 }
174
175 pub fn compile(&mut self, source: &str, filename: FileName) -> Result<String> {
177 self.parse(source, filename)?;
179 self.add_import_stubs()?;
181 self.intermediate_passes()?;
183 let bytecode = CodeGenerating::do_pass((), &mut self.state)?;
185 Ok(bytecode)
186 }
187
188 pub fn compile_from_file(&mut self, source_file_path: impl AsRef<Path>) -> Result<String> {
189 let source = fs::read_to_string(&source_file_path)
190 .map_err(|e| CompilerError::file_read_error(source_file_path.as_ref().display().to_string(), e))?;
191 self.compile(&source, FileName::Real(source_file_path.as_ref().into()))
192 }
193
194 fn write_ast_to_json(&self, file_suffix: &str) -> Result<()> {
196 if self.compiler_options.ast_spans_enabled {
198 self.state.ast.to_json_file(
199 self.output_directory.clone(),
200 &format!("{}.{file_suffix}", self.program_name.as_ref().unwrap()),
201 )?;
202 } else {
203 self.state.ast.to_json_file_without_keys(
204 self.output_directory.clone(),
205 &format!("{}.{file_suffix}", self.program_name.as_ref().unwrap()),
206 &["_span", "span"],
207 )?;
208 }
209 Ok(())
210 }
211
212 fn write_ast(&self, file_suffix: &str) -> Result<()> {
214 let filename = format!("{}.{file_suffix}", self.program_name.as_ref().unwrap());
215 let full_filename = self.output_directory.join(&filename);
216 let contents = self.state.ast.ast.to_string();
217 fs::write(&full_filename, contents).map_err(|e| CompilerError::failed_ast_file(full_filename.display(), e))?;
218 Ok(())
219 }
220
221 pub fn add_import_stubs(&mut self) -> Result<()> {
224 let mut explored = IndexSet::<Symbol>::new();
225 let mut to_explore: Vec<Symbol> = self.state.ast.ast.imports.keys().cloned().collect();
226
227 while let Some(import) = to_explore.pop() {
228 explored.insert(import);
229 if let Some(stub) = self.import_stubs.get(&import) {
230 for new_import_id in stub.imports.iter() {
231 if !explored.contains(&new_import_id.name.name) {
232 to_explore.push(new_import_id.name.name);
233 }
234 }
235 } else {
236 return Err(CompilerError::imported_program_not_found(
237 self.program_name.as_ref().unwrap(),
238 import,
239 self.state.ast.ast.imports[&import].1,
240 )
241 .into());
242 }
243 }
244
245 self.state.ast.ast.stubs = self
248 .import_stubs
249 .iter()
250 .filter(|(symbol, _stub)| explored.contains(*symbol))
251 .map(|(symbol, stub)| (*symbol, stub.clone()))
252 .collect();
253 Ok(())
254 }
255}