Skip to content

Commit

Permalink
Make improvements in recording syntax errors when parsing types (#173)
Browse files Browse the repository at this point in the history
Also record some syntax errors when parsing index expressions.
  • Loading branch information
jlapeyre committed Mar 1, 2024
1 parent 52aa014 commit e589172
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 28 deletions.
11 changes: 7 additions & 4 deletions crates/oq3_parser/src/grammar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ fn opt_ret_type(p: &mut Parser<'_>) -> bool {
if p.at(T![->]) {
let m = p.start();
p.bump(T![->]);
if p.current().is_type_name() {
if p.current().is_type() {
scalar_type(p);
} else {
p.error("Expected return type after ->");
Expand Down Expand Up @@ -181,15 +181,18 @@ fn delimited(
}

impl SyntaxKind {
/// Return `true` if the next token begins a classical type, including array, specification.
pub fn is_classical_type(&self) -> bool {
self.is_scalar_type() || matches!(self, ARRAY_KW)
}

/// Return `true` if the next token begins quantum type.
pub fn is_quantum_type(&self) -> bool {
matches!(self, QUBIT_TYPE | HARDWARE_QUBIT)
matches!(self, T![qubit] | HARDWARE_QUBIT)
}

pub fn is_type_name(&self) -> bool {
self.is_classical_type() || matches!(self, QUBIT_KW | HARDWARE_QUBIT)
/// Return `true` if the next token begins a type.
pub fn is_type(&self) -> bool {
self.is_classical_type() || self.is_quantum_type()
}
}
65 changes: 44 additions & 21 deletions crates/oq3_parser/src/grammar/expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,11 @@ fn postfix_expr(
T!['['] if allow_calls => match lhs.kind() {
IDENTIFIER => indexed_identifier(p, lhs),
// The previous token was not `IDENTIFIER`, so we are indexing an expression.
LITERAL | TIMING_LITERAL | HARDWARE_QUBIT => {
// record error, but parse the expression anyway.
p.error("Indexing into literal is not allowed.");
index_expr(p, lhs)
}
_ => index_expr(p, lhs),
},
_ => break,
Expand All @@ -354,31 +359,52 @@ fn call_expr(p: &mut Parser<'_>, lhs: CompletedMarker) -> CompletedMarker {
m.complete(p, CALL_EXPR)
}

// A classical
fn type_name(p: &mut Parser<'_>) {
if !p.current().is_type_name() {
p.error("Expected name of type");
if !p.current().is_type() {
p.error("Expected type name.");
}
p.bump(p.current());
}

pub(crate) fn type_spec(p: &mut Parser<'_>) -> bool {
let m = p.start();
if p.at(T![array]) {
p.bump_any();
p.expect(T!['[']);
type_spec(p); // the scalar base type
p.expect(COMMA);
loop {
expr(p);
if p.at(T![']']) {
p.bump_any();
break;
}
p.expect(COMMA);
return array_type_spec(p);
}
non_array_type_spec(p)
}

// Parse an array type spec
pub(crate) fn array_type_spec(p: &mut Parser<'_>) -> bool {
assert!(p.at(T![array]));
let m = p.start();
p.bump_any();
p.expect(T!['[']);
if !matches!(
p.current(),
T![int] | T![uint] | T![float] | T![complex] | T![angle] | T![bool] | T![duration]
) {
p.error("Illegal base type for array.");
}
type_spec(p);
p.expect(COMMA);
// Parse the dimensions.
loop {
expr(p);
if p.at(T![']']) {
p.bump_any();
break;
}
m.complete(p, ARRAY_TYPE);
return true;
p.expect(COMMA);
}
m.complete(p, ARRAY_TYPE);
true
}

// Parse a scalar or quantum type.
// Don't record error if array is found. Do not parse array.
fn non_array_type_spec(p: &mut Parser<'_>) -> bool {
let m = p.start();
type_name(p);
if p.at(T!['[']) {
designator(p);
Expand All @@ -387,7 +413,8 @@ pub(crate) fn type_spec(p: &mut Parser<'_>) -> bool {
true
}

pub(crate) fn quantum_type_spec(p: &mut Parser<'_>) -> bool {
pub(crate) fn qubit_type_spec(p: &mut Parser<'_>) -> bool {
assert!(p.at(T![qubit]));
let m = p.start();
type_name(p);
if p.at(T!['[']) {
Expand Down Expand Up @@ -430,10 +457,6 @@ pub(crate) fn var_name(p: &mut Parser<'_>) {

// This includes a previously parsed expression as the first argument of `INDEX_EXPR`.
// ig `arg1[arg2]` is the expression and only `arg2` is parsed here.
// test index_expr
// fn foo() {
// x[1][2];
// }
pub(crate) fn index_expr(p: &mut Parser<'_>, lhs: CompletedMarker) -> CompletedMarker {
assert!(p.at(T!['[']));
let m = lhs.precede(p);
Expand Down
2 changes: 1 addition & 1 deletion crates/oq3_parser/src/grammar/items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ fn for_stmt(p: &mut Parser<'_>, m: Marker) {

fn qubit_declaration_stmt(p: &mut Parser<'_>, m: Marker) {
assert!(p.at(T![qubit]));
expressions::quantum_type_spec(p);
expressions::qubit_type_spec(p);
expressions::var_name(p);
p.expect(SEMICOLON);
m.complete(p, QUANTUM_DECLARATION_STATEMENT);
Expand Down
4 changes: 2 additions & 2 deletions crates/oq3_parser/src/grammar/params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ fn _param_list_openqasm(p: &mut Parser<'_>, flavor: DefFlavor, m: Option<Marker>
// while !p.at(EOF) && !p.at_ts(list_end_tokens) {
while !p.at(EOF) && !list_end_tokens.iter().any(|x| p.at(*x)) {
let m = param_marker.take().unwrap_or_else(|| p.start());
if !(p.current().is_type_name() || p.at_ts(PARAM_FIRST)) {
if !(p.current().is_type() || p.at_ts(PARAM_FIRST)) {
p.error("expected value parameter");
m.abandon(p);
break;
Expand Down Expand Up @@ -182,7 +182,7 @@ fn param_untyped(p: &mut Parser<'_>, m: Marker) -> bool {

fn param_typed(p: &mut Parser<'_>, m: Marker) -> bool {
let mut success = true;
if p.current().is_type_name() {
if p.current().is_type() {
p.bump_any();
} else {
p.error("expected type annotation");
Expand Down

0 comments on commit e589172

Please sign in to comment.