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