Skip to content

Commit 4d891c1

Browse files
authored
Add the abilty to run compilations against the AST (#1368)
1 parent 6393180 commit 4d891c1

6 files changed

Lines changed: 467 additions & 7 deletions

File tree

compiler/qsc/src/compile.rs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,27 @@ pub enum ErrorKind {
3333
Lint(#[from] qsc_linter::Lint),
3434
}
3535

36+
#[must_use]
37+
#[allow(clippy::module_name_repetitions)]
38+
pub fn compile_ast(
39+
store: &PackageStore,
40+
dependencies: &[PackageId],
41+
ast_package: qsc_ast::ast::Package,
42+
sources: SourceMap,
43+
package_type: PackageType,
44+
capabilities: TargetCapabilityFlags,
45+
) -> (CompileUnit, Vec<Error>) {
46+
let unit = qsc_frontend::compile::compile_ast(
47+
store,
48+
dependencies,
49+
ast_package,
50+
sources,
51+
capabilities,
52+
vec![],
53+
);
54+
process_compile_unit(store, package_type, capabilities, unit)
55+
}
56+
3657
#[must_use]
3758
pub fn compile(
3859
store: &PackageStore,
@@ -42,13 +63,24 @@ pub fn compile(
4263
capabilities: TargetCapabilityFlags,
4364
language_features: LanguageFeatures,
4465
) -> (CompileUnit, Vec<Error>) {
45-
let mut unit = qsc_frontend::compile::compile(
66+
let unit = qsc_frontend::compile::compile(
4667
store,
4768
dependencies,
4869
sources,
4970
capabilities,
5071
language_features,
5172
);
73+
process_compile_unit(store, package_type, capabilities, unit)
74+
}
75+
76+
#[must_use]
77+
#[allow(clippy::module_name_repetitions)]
78+
fn process_compile_unit(
79+
store: &PackageStore,
80+
package_type: PackageType,
81+
capabilities: TargetCapabilityFlags,
82+
mut unit: CompileUnit,
83+
) -> (CompileUnit, Vec<Error>) {
5284
let mut errors = Vec::new();
5385
for error in unit.errors.drain(..) {
5486
errors.push(WithSource::from_map(&unit.sources, error.into()));

compiler/qsc/src/incremental.rs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
use crate::compile::{self, compile, core, std};
55
use miette::Diagnostic;
6+
use qsc_ast::ast;
67
use qsc_data_structures::language_features::LanguageFeatures;
78
use qsc_frontend::{
89
compile::{OpenPackageStore, PackageStore, SourceMap, TargetCapabilityFlags},
@@ -80,6 +81,24 @@ impl Compiler {
8081
})
8182
}
8283

84+
pub fn from(
85+
store: PackageStore,
86+
source_package_id: PackageId,
87+
capabilities: TargetCapabilityFlags,
88+
language_features: LanguageFeatures,
89+
) -> Result<Self, Errors> {
90+
let frontend =
91+
qsc_frontend::incremental::Compiler::new(&store, [], capabilities, language_features);
92+
let store = store.open();
93+
94+
Ok(Self {
95+
store,
96+
source_package_id,
97+
frontend,
98+
passes: PassContext::new(capabilities),
99+
})
100+
}
101+
83102
/// Compiles Q# fragments. Fragments are Q# code that can contain
84103
/// top-level statements as well as namespaces. A notebook cell
85104
/// or an interpreter entry is an example of fragments.
@@ -99,6 +118,26 @@ impl Compiler {
99118
self.compile_fragments(source_name, source_contents, fail_on_error)
100119
}
101120

121+
/// Compiles Q# ast fragments. Fragments are Q# code that can contain
122+
/// top-level statements as well as namespaces. A notebook cell
123+
/// or an interpreter entry is an example of fragments.
124+
///
125+
/// This method returns the AST and HIR packages that were created as a result of
126+
/// the compilation, however it does *not* update the current compilation.
127+
///
128+
/// The caller can use the returned packages to perform passes,
129+
/// get information about the newly added items, or do other modifications.
130+
/// It is then the caller's responsibility to merge
131+
/// these packages into the current `CompileUnit` using the `update()` method.
132+
pub fn compile_ast_fragments_fail_fast(
133+
&mut self,
134+
source_name: &str,
135+
source_contents: &str,
136+
package: ast::Package,
137+
) -> Result<Increment, Errors> {
138+
self.compile_ast_fragments(source_name, source_contents, package, fail_on_error)
139+
}
140+
102141
/// Compiles Q# fragments. See [`compile_fragments_fail_fast`] for more details.
103142
///
104143
/// This method calls an accumulator function with any errors returned
@@ -140,6 +179,52 @@ impl Compiler {
140179
Ok(increment)
141180
}
142181

182+
/// Compiles Q# ast fragments. See [`compile_ast_fragments_fail_fast`] for more details.
183+
///
184+
/// This method calls an accumulator function with any errors returned
185+
/// from each of the stages (parsing, lowering).
186+
/// If the accumulator succeeds, compilation continues.
187+
/// If the accumulator returns an error, compilation stops and the
188+
/// error is returned to the caller.
189+
pub fn compile_ast_fragments<F>(
190+
&mut self,
191+
source_name: &str,
192+
source_contents: &str,
193+
package: ast::Package,
194+
mut accumulate_errors: F,
195+
) -> Result<Increment, Errors>
196+
where
197+
F: FnMut(Errors) -> Result<(), Errors>,
198+
{
199+
let (core, unit) = self.store.get_open_mut();
200+
201+
let mut errors = false;
202+
let mut increment = self.frontend.compile_ast_fragments(
203+
unit,
204+
source_name,
205+
source_contents,
206+
package,
207+
|e| {
208+
errors = errors || !e.is_empty();
209+
accumulate_errors(into_errors(e))
210+
},
211+
)?;
212+
213+
// Even if we don't fail fast, skip passes if there were compilation errors.
214+
if !errors {
215+
let pass_errors = self.passes.run_default_passes(
216+
&mut increment.hir,
217+
&mut unit.assigner,
218+
core,
219+
PackageType::Lib,
220+
);
221+
222+
accumulate_errors(into_errors_with_source(pass_errors, &unit.sources))?;
223+
}
224+
225+
Ok(increment)
226+
}
227+
143228
/// Compiles an entry expression.
144229
///
145230
/// This method returns the AST and HIR packages that were created as a result of

compiler/qsc/src/interpret.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ use qsc_fir::{
6060
use qsc_frontend::{
6161
compile::{CompileUnit, PackageStore, Source, SourceMap, TargetCapabilityFlags},
6262
error::WithSource,
63+
incremental::Increment,
6364
};
6465
use qsc_passes::{PackageType, PassContext};
6566
use rustc_hash::FxHashSet;
@@ -218,6 +219,42 @@ impl Interpreter {
218219
})
219220
}
220221

222+
pub fn from(
223+
store: PackageStore,
224+
source_package_id: qsc_hir::hir::PackageId,
225+
capabilities: TargetCapabilityFlags,
226+
language_features: LanguageFeatures,
227+
) -> std::result::Result<Self, Vec<Error>> {
228+
let compiler = Compiler::from(store, source_package_id, capabilities, language_features)
229+
.map_err(into_errors)?;
230+
231+
let mut fir_store = fir::PackageStore::new();
232+
for (id, unit) in compiler.package_store() {
233+
let mut lowerer = qsc_lowerer::Lowerer::new();
234+
fir_store.insert(
235+
map_hir_package_to_fir(id),
236+
lowerer.lower_package(&unit.package),
237+
);
238+
}
239+
240+
let source_package_id = compiler.source_package_id();
241+
let package_id = compiler.package_id();
242+
243+
Ok(Self {
244+
compiler,
245+
lines: 0,
246+
capabilities,
247+
fir_store,
248+
lowerer: qsc_lowerer::Lowerer::new(),
249+
env: Env::default(),
250+
sim: sim_circuit_backend(),
251+
quantum_seed: None,
252+
classical_seed: None,
253+
package: map_hir_package_to_fir(package_id),
254+
source_package: map_hir_package_to_fir(source_package_id),
255+
})
256+
}
257+
221258
pub fn set_quantum_seed(&mut self, seed: Option<u64>) {
222259
self.quantum_seed = seed;
223260
self.sim.set_seed(seed);
@@ -293,6 +330,36 @@ impl Interpreter {
293330
.compile_fragments_fail_fast(&label, fragments)
294331
.map_err(into_errors)?;
295332

333+
self.eval_increment(receiver, increment)
334+
}
335+
336+
/// It is assumed that if there were any parse errors on the fragments, the caller would have
337+
/// already handled them. This function is intended to be used in cases where the caller wants
338+
/// to handle the parse errors themselves.
339+
/// # Errors
340+
/// If the compilation of the fragments fails, an error is returned.
341+
/// If there is a runtime error when interpreting the fragments, an error is returned.
342+
pub fn eval_ast_fragments(
343+
&mut self,
344+
receiver: &mut impl Receiver,
345+
fragments: &str,
346+
package: qsc_ast::ast::Package,
347+
) -> InterpretResult {
348+
let label = self.next_line_label();
349+
350+
let increment = self
351+
.compiler
352+
.compile_ast_fragments_fail_fast(&label, fragments, package)
353+
.map_err(into_errors)?;
354+
355+
self.eval_increment(receiver, increment)
356+
}
357+
358+
fn eval_increment(
359+
&mut self,
360+
receiver: &mut impl Receiver,
361+
increment: Increment,
362+
) -> InterpretResult {
296363
let (graph, _) = self.lower(&increment)?;
297364

298365
// Updating the compiler state with the new AST/HIR nodes

0 commit comments

Comments
 (0)