Skip to content

Commit 7167620

Browse files
authored
Basic parser error recovery (microsoft#380)
Add support for error recovery to the scanner, as well as some primitive parsers for ease of use. Include some basic error recovery for items and statements.
1 parent 3e068c4 commit 7167620

File tree

21 files changed

+1216
-904
lines changed

21 files changed

+1216
-904
lines changed

compiler/qsc_ast/src/ast.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -569,7 +569,6 @@ impl Display for Stmt {
569569
#[derive(Clone, Debug, Default, PartialEq)]
570570
pub enum StmtKind {
571571
/// An empty statement.
572-
#[default]
573572
Empty,
574573
/// An expression without a trailing semicolon.
575574
Expr(Box<Expr>),
@@ -581,6 +580,9 @@ pub enum StmtKind {
581580
Qubit(QubitSource, Box<Pat>, Box<QubitInit>, Option<Box<Block>>),
582581
/// An expression with a trailing semicolon.
583582
Semi(Box<Expr>),
583+
/// An invalid statement.
584+
#[default]
585+
Err,
584586
}
585587

586588
impl Display for StmtKind {
@@ -606,6 +608,7 @@ impl Display for StmtKind {
606608
}
607609
}
608610
StmtKind::Semi(e) => write!(indent, "Semi: {e}")?,
611+
StmtKind::Err => indent.write_str("Err")?,
609612
}
610613
Ok(())
611614
}

compiler/qsc_ast/src/mut_visit.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ pub fn walk_stmt(vis: &mut impl MutVisitor, stmt: &mut Stmt) {
200200
vis.visit_span(&mut stmt.span);
201201

202202
match &mut *stmt.kind {
203-
StmtKind::Empty => {}
203+
StmtKind::Empty | StmtKind::Err => {}
204204
StmtKind::Expr(expr) | StmtKind::Semi(expr) => vis.visit_expr(expr),
205205
StmtKind::Item(item) => vis.visit_item(item),
206206
StmtKind::Local(_, pat, value) => {

compiler/qsc_ast/src/visit.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ pub fn walk_block<'a>(vis: &mut impl Visitor<'a>, block: &'a Block) {
174174

175175
pub fn walk_stmt<'a>(vis: &mut impl Visitor<'a>, stmt: &'a Stmt) {
176176
match &*stmt.kind {
177-
StmtKind::Empty => {}
177+
StmtKind::Empty | StmtKind::Err => {}
178178
StmtKind::Expr(expr) | StmtKind::Semi(expr) => vis.visit_expr(expr),
179179
StmtKind::Item(item) => vis.visit_item(item),
180180
StmtKind::Local(_, pat, value) => {

compiler/qsc_frontend/src/lower.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ impl With<'_> {
337337
pub(super) fn lower_stmt(&mut self, stmt: &ast::Stmt) -> Option<hir::Stmt> {
338338
let id = self.lower_id(stmt.id);
339339
let kind = match &*stmt.kind {
340-
ast::StmtKind::Empty => return None,
340+
ast::StmtKind::Empty | ast::StmtKind::Err => return None,
341341
ast::StmtKind::Expr(expr) => hir::StmtKind::Expr(self.lower_expr(expr)),
342342
ast::StmtKind::Item(item) => {
343343
hir::StmtKind::Item(self.lower_item(ItemScope::Local, item)?)

compiler/qsc_frontend/src/resolve.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,10 @@ impl AstVisitor<'_> for With<'_> {
350350
self.visit_block(block);
351351
}
352352
}
353-
ast::StmtKind::Empty | ast::StmtKind::Expr(_) | ast::StmtKind::Semi(_) => {
353+
ast::StmtKind::Empty
354+
| ast::StmtKind::Expr(_)
355+
| ast::StmtKind::Semi(_)
356+
| ast::StmtKind::Err => {
354357
ast_visit::walk_stmt(self, stmt);
355358
}
356359
}

compiler/qsc_frontend/src/typeck/rules.rs

+1
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ impl<'a> Context<'a> {
147147
let expr = self.infer_expr(expr);
148148
self.diverge_if(expr.diverges, converge(Ty::UNIT))
149149
}
150+
StmtKind::Err => converge(Ty::Err),
150151
};
151152

152153
self.record(stmt.id, ty.ty.clone());

compiler/qsc_parse/src/expr.rs

+34-49
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ mod tests;
99

1010
use super::{
1111
keyword::Keyword,
12-
prim::{ident, keyword, opt, pat, path, seq, token},
12+
prim::{ident, opt, pat, path, seq, token},
1313
scan::Scanner,
1414
stmt, Error, Result,
1515
};
@@ -158,46 +158,46 @@ fn expr_base(s: &mut Scanner) -> Result<Box<Expr>> {
158158
)))
159159
} else if token(s, TokenKind::DotDotDot).is_ok() {
160160
expr_range_prefix(s)
161-
} else if keyword(s, Keyword::Underscore).is_ok() {
161+
} else if token(s, TokenKind::Keyword(Keyword::Underscore)).is_ok() {
162162
Ok(Box::new(ExprKind::Hole))
163-
} else if keyword(s, Keyword::Fail).is_ok() {
163+
} else if token(s, TokenKind::Keyword(Keyword::Fail)).is_ok() {
164164
Ok(Box::new(ExprKind::Fail(expr(s)?)))
165-
} else if keyword(s, Keyword::For).is_ok() {
165+
} else if token(s, TokenKind::Keyword(Keyword::For)).is_ok() {
166166
let vars = pat(s)?;
167-
keyword(s, Keyword::In)?;
167+
token(s, TokenKind::Keyword(Keyword::In))?;
168168
let iter = expr(s)?;
169-
let body = stmt::block(s)?;
169+
let body = stmt::parse_block(s)?;
170170
Ok(Box::new(ExprKind::For(vars, iter, body)))
171-
} else if keyword(s, Keyword::If).is_ok() {
171+
} else if token(s, TokenKind::Keyword(Keyword::If)).is_ok() {
172172
expr_if(s)
173173
} else if let Some(components) = opt(s, expr_interpolate)? {
174174
Ok(Box::new(ExprKind::Interpolate(
175175
components.into_boxed_slice(),
176176
)))
177-
} else if keyword(s, Keyword::Repeat).is_ok() {
178-
let body = stmt::block(s)?;
179-
keyword(s, Keyword::Until)?;
177+
} else if token(s, TokenKind::Keyword(Keyword::Repeat)).is_ok() {
178+
let body = stmt::parse_block(s)?;
179+
token(s, TokenKind::Keyword(Keyword::Until))?;
180180
let cond = expr(s)?;
181-
let fixup = if keyword(s, Keyword::Fixup).is_ok() {
182-
Some(stmt::block(s)?)
181+
let fixup = if token(s, TokenKind::Keyword(Keyword::Fixup)).is_ok() {
182+
Some(stmt::parse_block(s)?)
183183
} else {
184184
None
185185
};
186186
Ok(Box::new(ExprKind::Repeat(body, cond, fixup)))
187-
} else if keyword(s, Keyword::Return).is_ok() {
187+
} else if token(s, TokenKind::Keyword(Keyword::Return)).is_ok() {
188188
Ok(Box::new(ExprKind::Return(expr(s)?)))
189-
} else if keyword(s, Keyword::Set).is_ok() {
189+
} else if token(s, TokenKind::Keyword(Keyword::Set)).is_ok() {
190190
expr_set(s)
191-
} else if keyword(s, Keyword::While).is_ok() {
192-
Ok(Box::new(ExprKind::While(expr(s)?, stmt::block(s)?)))
193-
} else if keyword(s, Keyword::Within).is_ok() {
194-
let outer = stmt::block(s)?;
195-
keyword(s, Keyword::Apply)?;
196-
let inner = stmt::block(s)?;
191+
} else if token(s, TokenKind::Keyword(Keyword::While)).is_ok() {
192+
Ok(Box::new(ExprKind::While(expr(s)?, stmt::parse_block(s)?)))
193+
} else if token(s, TokenKind::Keyword(Keyword::Within)).is_ok() {
194+
let outer = stmt::parse_block(s)?;
195+
token(s, TokenKind::Keyword(Keyword::Apply))?;
196+
let inner = stmt::parse_block(s)?;
197197
Ok(Box::new(ExprKind::Conjugate(outer, inner)))
198198
} else if let Some(a) = opt(s, expr_array)? {
199199
Ok(a)
200-
} else if let Some(b) = opt(s, stmt::block)? {
200+
} else if let Some(b) = opt(s, stmt::parse_block)? {
201201
Ok(Box::new(ExprKind::Block(b)))
202202
} else if let Some(l) = lit(s)? {
203203
Ok(Box::new(ExprKind::Lit(Box::new(l))))
@@ -220,13 +220,13 @@ fn expr_base(s: &mut Scanner) -> Result<Box<Expr>> {
220220

221221
fn expr_if(s: &mut Scanner) -> Result<Box<ExprKind>> {
222222
let cond = expr(s)?;
223-
let body = stmt::block(s)?;
223+
let body = stmt::parse_block(s)?;
224224
let lo = s.peek().span.lo;
225225

226-
let otherwise = if keyword(s, Keyword::Elif).is_ok() {
226+
let otherwise = if token(s, TokenKind::Keyword(Keyword::Elif)).is_ok() {
227227
Some(expr_if(s)?)
228-
} else if keyword(s, Keyword::Else).is_ok() {
229-
Some(Box::new(ExprKind::Block(stmt::block(s)?)))
228+
} else if token(s, TokenKind::Keyword(Keyword::Else)).is_ok() {
229+
Some(Box::new(ExprKind::Block(stmt::parse_block(s)?)))
230230
} else {
231231
None
232232
}
@@ -357,14 +357,7 @@ fn lit(s: &mut Scanner) -> Result<Option<Lit>> {
357357
s.advance();
358358
Ok(Some(lit))
359359
}
360-
Ok(None) if token.kind != TokenKind::Ident => Ok(None),
361-
Ok(None) => match lit_keyword(lexeme) {
362-
Some(lit) => {
363-
s.advance();
364-
Ok(Some(lit))
365-
}
366-
None => Ok(None),
367-
},
360+
Ok(None) => Ok(None),
368361
Err(err) => {
369362
s.advance();
370363
Err(err)
@@ -410,6 +403,14 @@ fn lit_token(lexeme: &str, token: Token) -> Result<Option<Lit>> {
410403
})?;
411404
Ok(Some(Lit::String(string.into())))
412405
}
406+
TokenKind::Keyword(Keyword::True) => Ok(Some(Lit::Bool(true))),
407+
TokenKind::Keyword(Keyword::Zero) => Ok(Some(Lit::Result(ast::Result::Zero))),
408+
TokenKind::Keyword(Keyword::One) => Ok(Some(Lit::Result(ast::Result::One))),
409+
TokenKind::Keyword(Keyword::PauliZ) => Ok(Some(Lit::Pauli(Pauli::Z))),
410+
TokenKind::Keyword(Keyword::False) => Ok(Some(Lit::Bool(false))),
411+
TokenKind::Keyword(Keyword::PauliX) => Ok(Some(Lit::Pauli(Pauli::X))),
412+
TokenKind::Keyword(Keyword::PauliI) => Ok(Some(Lit::Pauli(Pauli::I))),
413+
TokenKind::Keyword(Keyword::PauliY) => Ok(Some(Lit::Pauli(Pauli::Y))),
413414
_ => Ok(None),
414415
}
415416
}
@@ -426,22 +427,6 @@ fn lit_int(lexeme: &str, radix: u32) -> Option<i64> {
426427
.map(|(Wrapping(value), _)| value)
427428
}
428429

429-
#[allow(clippy::inline_always)]
430-
#[inline(always)]
431-
fn lit_keyword(lexeme: &str) -> Option<Lit> {
432-
match lexeme {
433-
"true" => Some(Lit::Bool(true)),
434-
"Zero" => Some(Lit::Result(ast::Result::Zero)),
435-
"One" => Some(Lit::Result(ast::Result::One)),
436-
"PauliZ" => Some(Lit::Pauli(Pauli::Z)),
437-
"false" => Some(Lit::Bool(false)),
438-
"PauliX" => Some(Lit::Pauli(Pauli::X)),
439-
"PauliI" => Some(Lit::Pauli(Pauli::I)),
440-
"PauliY" => Some(Lit::Pauli(Pauli::Y)),
441-
_ => None,
442-
}
443-
}
444-
445430
fn prefix_op(name: OpName) -> Option<PrefixOp> {
446431
match name {
447432
OpName::Keyword(Keyword::Not) => Some(PrefixOp {

0 commit comments

Comments
 (0)