Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make improvements in recording syntax errors when parsing types #173

Merged
merged 1 commit into from
Mar 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading