Skip to content

Commit d93f2f8

Browse files
committed
Parsing now bundles IO source loading errors into parse errors. (#2252)
1 parent 2d67664 commit d93f2f8

File tree

23 files changed

+235
-153
lines changed

23 files changed

+235
-153
lines changed

Diff for: compiler/qsc/src/lib.rs

-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,6 @@ pub mod partial_eval {
7676
}
7777

7878
pub mod qasm3 {
79-
pub use qsc_qasm3::io::*;
8079
pub use qsc_qasm3::parse::*;
8180
pub use qsc_qasm3::*;
8281
}

Diff for: compiler/qsc_qasm3/src/compile/tests.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ fn programs_with_includes_with_includes_can_be_compiled() -> miette::Result<(),
1818
("source2.qasm".into(), source2.into()),
1919
];
2020

21-
let unit = compile_all_fragments("source0.qasm", all_sources).map_err(|e| vec![e])?;
21+
let unit = compile_all_fragments("source0.qasm", all_sources)?;
2222
print_compilation_errors(&unit);
2323
assert!(!unit.has_errors());
2424
Ok(())
@@ -39,7 +39,7 @@ fn including_stdgates_multiple_times_causes_symbol_redifintion_errors(
3939
("source2.qasm".into(), source2.into()),
4040
];
4141

42-
let unit = compile_all_fragments("source0.qasm", all_sources).map_err(|e| vec![e])?;
42+
let unit = compile_all_fragments("source0.qasm", all_sources)?;
4343
assert!(unit.has_errors());
4444
for error in unit.errors() {
4545
assert!(error.to_string().contains("Redefined symbol: "));

Diff for: compiler/qsc_qasm3/src/compiler.rs

+4-41
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4-
use std::{path::Path, rc::Rc, sync::Arc};
4+
use std::{path::Path, rc::Rc};
55

66
use num_bigint::BigInt;
77
use qsc_data_structures::span::Span;
@@ -25,7 +25,6 @@ use crate::{
2525
build_wrapped_block_expr, managed_qubit_alloc_array, map_qsharp_type_to_ast_ty,
2626
wrap_expr_in_parens,
2727
},
28-
io::{InMemorySourceResolver, SourceResolver},
2928
parser::ast::{list_from_iter, List},
3029
runtime::{get_runtime_function_decls, RuntimeFunctions},
3130
semantic::{
@@ -45,48 +44,12 @@ use crate::{
4544
use crate::semantic::ast as semast;
4645
use qsc_ast::ast::{self as qsast, NodeId, Package};
4746

48-
pub fn compile_anon_with_config<S>(
49-
source: S,
50-
config: CompilerConfig,
51-
) -> miette::Result<QasmCompileUnit>
52-
where
53-
S: AsRef<str>,
54-
{
55-
let path = std::path::PathBuf::from("Test.qasm");
56-
let sources = [(
57-
Arc::from(path.display().to_string().as_str()),
58-
Arc::from(source.as_ref()),
59-
)];
60-
let resolver = InMemorySourceResolver::from_iter(sources);
61-
let source = resolver.resolve(&path)?.1;
62-
compile_with_config(source, &path, &resolver, config)
63-
}
64-
65-
pub fn compile_all_with_config<P>(
66-
path: P,
67-
sources: impl IntoIterator<Item = (Arc<str>, Arc<str>)>,
68-
config: CompilerConfig,
69-
) -> miette::Result<QasmCompileUnit>
70-
where
71-
P: AsRef<Path>,
72-
{
73-
let resolver = InMemorySourceResolver::from_iter(sources);
74-
let source = resolver.resolve(path.as_ref())?.1;
75-
compile_with_config(source, path, &resolver, config)
76-
}
77-
78-
pub fn compile_with_config<S, P, R>(
79-
source: S,
80-
path: P,
81-
resolver: &R,
82-
config: CompilerConfig,
83-
) -> miette::Result<QasmCompileUnit>
47+
pub fn compile_with_config<S, P>(source: S, path: P, config: CompilerConfig) -> QasmCompileUnit
8448
where
8549
S: AsRef<str>,
8650
P: AsRef<Path>,
87-
R: SourceResolver,
8851
{
89-
let res = crate::semantic::parse_source(source, path, resolver)?;
52+
let res = crate::semantic::parse(source, path);
9053
let program = res.program;
9154

9255
let compiler = crate::compiler::QasmCompiler {
@@ -98,7 +61,7 @@ where
9861
errors: res.errors,
9962
};
10063

101-
Ok(compiler.compile(&program))
64+
compiler.compile(&program)
10265
}
10366

10467
pub struct QasmCompiler {

Diff for: compiler/qsc_qasm3/src/io.rs

+11-11
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,38 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4+
mod error;
5+
pub use error::Error;
6+
pub use error::ErrorKind;
7+
48
use std::{
59
path::{Path, PathBuf},
610
sync::Arc,
711
};
812

913
use rustc_hash::FxHashMap;
1014

11-
use miette::IntoDiagnostic;
12-
1315
/// A trait for resolving include file paths to their contents.
1416
/// This is used by the parser to resolve `include` directives.
1517
/// Implementations of this trait can be provided to the parser
1618
/// to customize how include files are resolved.
1719
pub trait SourceResolver {
1820
#[cfg(feature = "fs")]
19-
fn resolve<P>(&self, path: P) -> miette::Result<(PathBuf, String)>
21+
fn resolve<P>(&self, path: P) -> miette::Result<(PathBuf, String), Error>
2022
where
2123
P: AsRef<Path>,
2224
{
2325
let path = std::fs::canonicalize(path).map_err(|e| {
24-
crate::Error(crate::ErrorKind::IO(format!(
26+
Error(ErrorKind::IO(format!(
2527
"Could not resolve include file path: {e}"
2628
)))
2729
})?;
2830
match std::fs::read_to_string(&path) {
2931
Ok(source) => Ok((path, source)),
30-
Err(_) => Err(crate::Error(crate::ErrorKind::NotFound(format!(
32+
Err(_) => Err(Error(ErrorKind::NotFound(format!(
3133
"Could not resolve include file: {}",
3234
path.display()
33-
))))
34-
.into_diagnostic(),
35+
)))),
3536
}
3637
}
3738
#[cfg(not(feature = "fs"))]
@@ -62,18 +63,17 @@ impl FromIterator<(Arc<str>, Arc<str>)> for InMemorySourceResolver {
6263
}
6364

6465
impl SourceResolver for InMemorySourceResolver {
65-
fn resolve<P>(&self, path: P) -> miette::Result<(PathBuf, String)>
66+
fn resolve<P>(&self, path: P) -> miette::Result<(PathBuf, String), Error>
6667
where
6768
P: AsRef<Path>,
6869
{
6970
let path = path.as_ref();
7071
match self.sources.get(path) {
7172
Some(source) => Ok((path.to_owned(), source.clone())),
72-
None => Err(crate::Error(crate::ErrorKind::NotFound(format!(
73+
None => Err(Error(ErrorKind::NotFound(format!(
7374
"Could not resolve include file: {}",
7475
path.display()
75-
))))
76-
.into_diagnostic(),
76+
)))),
7777
}
7878
}
7979
}

Diff for: compiler/qsc_qasm3/src/io/error.rs

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
use miette::Diagnostic;
5+
use thiserror::Error;
6+
7+
#[derive(Clone, Debug, Diagnostic, Eq, Error, PartialEq)]
8+
#[error(transparent)]
9+
#[diagnostic(transparent)]
10+
pub struct Error(pub ErrorKind);
11+
12+
#[derive(Clone, Debug, Diagnostic, Eq, Error, PartialEq)]
13+
pub enum ErrorKind {
14+
#[error("Not Found {0}")]
15+
NotFound(String),
16+
#[error("IO Error: {0}")]
17+
IO(String),
18+
}
19+
20+
impl From<Error> for crate::Error {
21+
fn from(val: Error) -> Self {
22+
crate::Error(crate::ErrorKind::IO(val))
23+
}
24+
}

Diff for: compiler/qsc_qasm3/src/lib.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ mod ast_builder;
99
mod compile;
1010
mod compiler;
1111
pub use compile::qasm_to_program;
12+
pub use compiler::compile_with_config;
1213
pub mod io;
1314
mod keyword;
1415
mod lex;
@@ -64,6 +65,9 @@ impl Error {
6465
#[derive(Clone, Debug, Diagnostic, Eq, Error, PartialEq)]
6566
#[error(transparent)]
6667
pub enum ErrorKind {
68+
#[error(transparent)]
69+
#[diagnostic(transparent)]
70+
IO(#[from] crate::io::Error),
6771
#[error("QASM3 Parse Error: {0}")]
6872
Parse(String, #[label] Span),
6973
#[error(transparent)]
@@ -75,17 +79,18 @@ pub enum ErrorKind {
7579
#[error("QASM3 Parse Error: Not Found {0}")]
7680
NotFound(String),
7781
#[error("IO Error: {0}")]
78-
IO(String),
82+
OldIO(String),
7983
}
8084

8185
impl ErrorKind {
8286
fn with_offset(self, offset: u32) -> Self {
8387
match self {
88+
ErrorKind::IO(error) => Self::IO(error),
8489
ErrorKind::Parse(error, span) => Self::Parse(error, span + offset),
8590
ErrorKind::Parser(error) => Self::Parser(error.with_offset(offset)),
8691
ErrorKind::Semantic(error) => Self::Semantic(error.with_offset(offset)),
8792
ErrorKind::NotFound(error) => Self::NotFound(error),
88-
ErrorKind::IO(error) => Self::IO(error),
93+
ErrorKind::OldIO(error) => Self::OldIO(error),
8994
}
9095
}
9196
}

Diff for: compiler/qsc_qasm3/src/parser.rs

+36-19
Original file line numberDiff line numberDiff line change
@@ -110,14 +110,14 @@ fn update_offsets(source_map: &SourceMap, source: &mut QasmSource) {
110110
/// This function will resolve includes using the provided resolver.
111111
/// If an include file cannot be resolved, an error will be returned.
112112
/// If a file is included recursively, a stack overflow occurs.
113-
pub fn parse_source<S, P, R>(source: S, path: P, resolver: &R) -> miette::Result<QasmParseResult>
113+
pub fn parse_source<S, P, R>(source: S, path: P, resolver: &R) -> QasmParseResult
114114
where
115115
S: AsRef<str>,
116116
P: AsRef<Path>,
117117
R: SourceResolver,
118118
{
119-
let res = parse_qasm_source(source, path, resolver)?;
120-
Ok(QasmParseResult::new(res))
119+
let res = parse_qasm_source(source, path, resolver);
120+
QasmParseResult::new(res)
121121
}
122122

123123
/// Creates a Q# source map from a QASM parse output. The `QasmSource`
@@ -230,38 +230,54 @@ impl QasmSource {
230230
/// This function is the start of a recursive process that will resolve all
231231
/// includes in the QASM file. Any includes are parsed as if their contents
232232
/// were defined where the include statement is.
233-
fn parse_qasm_file<P, R>(path: P, resolver: &R) -> miette::Result<QasmSource>
233+
fn parse_qasm_file<P, R>(path: P, resolver: &R) -> QasmSource
234234
where
235235
P: AsRef<Path>,
236236
R: SourceResolver,
237237
{
238-
let (path, source) = resolver.resolve(&path)?;
239-
parse_qasm_source(source, path, resolver)
238+
match resolver.resolve(&path) {
239+
Ok((path, source)) => parse_qasm_source(source, path, resolver),
240+
Err(e) => {
241+
let error = crate::parser::error::ErrorKind::IO(e);
242+
let error = crate::parser::Error(error, None);
243+
QasmSource {
244+
path: path.as_ref().to_owned(),
245+
source: Default::default(),
246+
program: Program {
247+
span: Span::default(),
248+
statements: vec![].into_boxed_slice(),
249+
version: None,
250+
},
251+
errors: vec![error],
252+
included: vec![],
253+
}
254+
}
255+
}
240256
}
241257

242-
fn parse_qasm_source<S, P, R>(source: S, path: P, resolver: &R) -> miette::Result<QasmSource>
258+
fn parse_qasm_source<S, P, R>(source: S, path: P, resolver: &R) -> QasmSource
243259
where
244260
S: AsRef<str>,
245261
P: AsRef<Path>,
246262
R: SourceResolver,
247263
{
248-
let (program, errors, includes) = parse_source_and_includes(source.as_ref(), resolver)?;
249-
Ok(QasmSource::new(source, path, program, errors, includes))
264+
let (program, errors, includes) = parse_source_and_includes(source.as_ref(), resolver);
265+
QasmSource::new(source, path, program, errors, includes)
250266
}
251267

252268
fn parse_source_and_includes<P: AsRef<str>, R>(
253269
source: P,
254270
resolver: &R,
255-
) -> miette::Result<(Program, Vec<Error>, Vec<QasmSource>)>
271+
) -> (Program, Vec<Error>, Vec<QasmSource>)
256272
where
257273
R: SourceResolver,
258274
{
259-
let (program, errors) = parse(source.as_ref())?;
260-
let included = parse_includes(&program, resolver)?;
261-
Ok((program, errors, included))
275+
let (program, errors) = parse(source.as_ref());
276+
let included = parse_includes(&program, resolver);
277+
(program, errors, included)
262278
}
263279

264-
fn parse_includes<R>(program: &Program, resolver: &R) -> miette::Result<Vec<QasmSource>>
280+
fn parse_includes<R>(program: &Program, resolver: &R) -> Vec<QasmSource>
265281
where
266282
R: SourceResolver,
267283
{
@@ -274,12 +290,12 @@ where
274290
if file_path.to_lowercase() == "stdgates.inc" {
275291
continue;
276292
}
277-
let source = parse_qasm_file(file_path, resolver)?;
293+
let source = parse_qasm_file(file_path, resolver);
278294
includes.push(source);
279295
}
280296
}
281297

282-
Ok(includes)
298+
includes
283299
}
284300

285301
pub(crate) type Result<T> = std::result::Result<T, crate::parser::error::Error>;
@@ -288,8 +304,9 @@ pub(crate) trait Parser<T>: FnMut(&mut ParserContext) -> Result<T> {}
288304

289305
impl<T, F: FnMut(&mut ParserContext) -> Result<T>> Parser<T> for F {}
290306

291-
pub fn parse(input: &str) -> Result<(Program, Vec<Error>)> {
307+
#[must_use]
308+
pub fn parse(input: &str) -> (Program, Vec<Error>) {
292309
let mut scanner = ParserContext::new(input);
293-
let program = prgm::parse(&mut scanner)?;
294-
Ok((program, scanner.into_errors()))
310+
let program = prgm::parse(&mut scanner);
311+
(program, scanner.into_errors())
295312
}

Diff for: compiler/qsc_qasm3/src/parser/error.rs

+10
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,9 @@ pub enum ErrorKind {
137137
#[error("multiple index operators are only allowed in assignments")]
138138
#[diagnostic(code("Qasm3.Parse.MultipleIndexOperators"))]
139139
MultipleIndexOperators(#[label] Span),
140+
#[error(transparent)]
141+
#[diagnostic(transparent)]
142+
IO(#[from] crate::io::Error),
140143
}
141144

142145
impl ErrorKind {
@@ -160,6 +163,13 @@ impl ErrorKind {
160163
Self::GPhaseInvalidArguments(span) => Self::GPhaseInvalidArguments(span + offset),
161164
Self::InvalidGateCallDesignator(span) => Self::InvalidGateCallDesignator(span + offset),
162165
Self::MultipleIndexOperators(span) => Self::MultipleIndexOperators(span + offset),
166+
Self::IO(error) => Self::IO(error),
163167
}
164168
}
165169
}
170+
171+
impl From<Error> for crate::Error {
172+
fn from(val: Error) -> Self {
173+
crate::Error(crate::ErrorKind::Parser(val))
174+
}
175+
}

Diff for: compiler/qsc_qasm3/src/parser/prgm.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,20 @@ use super::ast::{Program, Stmt, StmtKind, Version};
1515
use super::ParserContext;
1616

1717
/// Grammar: `version? statementOrScope* EOF`.
18-
pub(super) fn parse(s: &mut ParserContext) -> Result<Program> {
18+
pub(super) fn parse(s: &mut ParserContext) -> Program {
1919
let lo = s.peek().span.lo;
20-
let version = opt(s, parse_version)?;
21-
let stmts = parse_top_level_nodes(s)?;
20+
let version = opt(s, parse_version).unwrap_or_default();
21+
let stmts = parse_top_level_nodes(s).unwrap_or_default();
2222

23-
Ok(Program {
23+
Program {
2424
span: s.span(lo),
2525
version,
2626
statements: stmts
2727
.into_iter()
2828
.map(Box::new)
2929
.collect::<Vec<_>>()
3030
.into_boxed_slice(),
31-
})
31+
}
3232
}
3333

3434
/// Grammar: `OPENQASM VersionSpecifier SEMICOLON`.

0 commit comments

Comments
 (0)