From deddb08214a3ca13b6c8051f9ef30fb6716bd10d Mon Sep 17 00:00:00 2001 From: John Lapeyre <1969884+jlapeyre@users.noreply.github.com> Date: Mon, 1 Dec 2025 22:26:42 -0500 Subject: [PATCH] Implement creg and qreg in the parser They were partially implemented before. Declarations were parsed correctly. But they were not recognized as parameters in subroutine signatures. This commit does not implment any semantic analysis. --- crates/oq3_parser/src/grammar.rs | 4 ++ crates/oq3_parser/src/grammar/expressions.rs | 16 ++++-- crates/oq3_parser/src/grammar/params.rs | 11 +++- .../src/syntax_kind/syntax_kind_enum.rs | 1 + .../oq3_semantics/src/syntax_to_semantics.rs | 8 ++- crates/oq3_syntax/build.rs | 1 + crates/oq3_syntax/openqasm3.ungram | 7 ++- crates/oq3_syntax/src/ast/generated/nodes.rs | 50 +++++++++++++++---- ...s__invalid__statements__io.qasm-parse.snap | 4 +- ...valid__statements__measure.qasm-parse.snap | 4 +- ...ference__assignment__alias.qasm-parse.snap | 4 +- ...ce__assignment__assignment.qasm-parse.snap | 2 +- ...ce__subroutine__subroutine.qasm-parse.snap | 45 +++++++---------- .../reference/subroutine/subroutine.qasm | 2 +- 14 files changed, 106 insertions(+), 53 deletions(-) diff --git a/crates/oq3_parser/src/grammar.rs b/crates/oq3_parser/src/grammar.rs index 51ef05c..dd34979 100644 --- a/crates/oq3_parser/src/grammar.rs +++ b/crates/oq3_parser/src/grammar.rs @@ -201,4 +201,8 @@ impl SyntaxKind { pub fn is_type(&self) -> bool { self.is_classical_type() || self.is_quantum_type() } + + pub fn is_creg_or_qreg(&self) -> bool { + matches!(self, T![qreg] | T![creg]) + } } diff --git a/crates/oq3_parser/src/grammar/expressions.rs b/crates/oq3_parser/src/grammar/expressions.rs index 3075ee7..54c0a75 100644 --- a/crates/oq3_parser/src/grammar/expressions.rs +++ b/crates/oq3_parser/src/grammar/expressions.rs @@ -148,24 +148,30 @@ pub(crate) fn stmt(p: &mut Parser<'_>) { } } -fn q_or_c_reg_declaration(p: &mut Parser<'_>, m: Marker) { - p.bump_any(); +/// The designator is on the identifier, rather than the type +/// in OQ2. So we don't parse the identifier and type separately +/// as we do for OQ3 constructions. +pub fn q_or_c_reg_param(p: &mut Parser<'_>) { + let m = p.start(); + p.bump_any(); // creg or qreg if !p.at(IDENT) { p.error("Expected qubit register name"); m.abandon(p); return; } - let m1 = p.start(); p.bump_any(); if p.at(T!['[']) && !p.at(EOF) { index_operator(p); } else { p.error("Expected index operator"); - m1.abandon(p); m.abandon(p); return; } - m1.complete(p, INDEXED_IDENTIFIER); + m.complete(p, OLD_TYPED_PARAM); +} + +fn q_or_c_reg_declaration(p: &mut Parser<'_>, m: Marker) { + q_or_c_reg_param(p); p.expect(T![;]); m.complete(p, OLD_STYLE_DECLARATION_STATEMENT); } diff --git a/crates/oq3_parser/src/grammar/params.rs b/crates/oq3_parser/src/grammar/params.rs index d6c9115..cd125be 100644 --- a/crates/oq3_parser/src/grammar/params.rs +++ b/crates/oq3_parser/src/grammar/params.rs @@ -154,7 +154,11 @@ fn _param_list_openqasm(p: &mut Parser<'_>, flavor: DefFlavor) { // Allowed starts for an item: either a type or a first-token of a param/expression, // or first token of array literal. if matches!(flavor, DefParams) && (p.at(T![mutable]) || p.at(T![readonly])) { - } else if !(p.current().is_type() || p.at_ts(PARAM_FIRST) || inner_array_literal) { + } else if !(p.current().is_type() + || p.at_ts(PARAM_FIRST) + || inner_array_literal + || p.current().is_creg_or_qreg()) + { p.error("expected value parameter"); m.abandon(p); break; @@ -294,6 +298,11 @@ fn param_untyped_or_hardware_qubit(p: &mut Parser<'_>, m: Marker) -> bool { /// Parse one parameter in the list of parameters in the signature /// of a subroutine defintion (that is, a `def` statement) fn param_typed(p: &mut Parser<'_>, m: Marker) -> bool { + if p.at(T![creg]) || p.at(T![qreg]) { + m.abandon(p); + expressions::q_or_c_reg_param(p); + return true; + } expressions::param_type_spec(p); expressions::var_name(p); m.complete(p, TYPED_PARAM); diff --git a/crates/oq3_parser/src/syntax_kind/syntax_kind_enum.rs b/crates/oq3_parser/src/syntax_kind/syntax_kind_enum.rs index 6696685..f750dda 100644 --- a/crates/oq3_parser/src/syntax_kind/syntax_kind_enum.rs +++ b/crates/oq3_parser/src/syntax_kind/syntax_kind_enum.rs @@ -191,6 +191,7 @@ pub enum SyntaxKind { RANGE_EXPR, TYPE, TYPED_PARAM, + OLD_TYPED_PARAM, TYPED_PARAM_LIST, TYPE_LIST, VERSION, diff --git a/crates/oq3_semantics/src/syntax_to_semantics.rs b/crates/oq3_semantics/src/syntax_to_semantics.rs index e4d0370..dac2615 100644 --- a/crates/oq3_semantics/src/syntax_to_semantics.rs +++ b/crates/oq3_semantics/src/syntax_to_semantics.rs @@ -1471,7 +1471,13 @@ fn bind_typed_parameter_list( param_list .typed_params() .map(|param| { - let typ = param_type_to_type(¶m.param_type().unwrap(), false, context); + let typ = if let Some(pt) = param.param_type() { + param_type_to_type(&pt, false, context) + } else if param.old_typed_param().is_some() { + Type::ToDo + } else { + panic!("You have found a bug in oq3_parser") + }; let namestr = param.name().unwrap().string(); context.new_binding(namestr.as_ref(), &typ, ¶m) }) diff --git a/crates/oq3_syntax/build.rs b/crates/oq3_syntax/build.rs index 7642382..976d275 100644 --- a/crates/oq3_syntax/build.rs +++ b/crates/oq3_syntax/build.rs @@ -446,6 +446,7 @@ mod sourcegen { "RANGE_EXPR", "TYPE", "TYPED_PARAM", + "OLD_TYPED_PARAM", "TYPED_PARAM_LIST", "TYPE_LIST", "VERSION", diff --git a/crates/oq3_syntax/openqasm3.ungram b/crates/oq3_syntax/openqasm3.ungram index 99ceded..3591cfa 100644 --- a/crates/oq3_syntax/openqasm3.ungram +++ b/crates/oq3_syntax/openqasm3.ungram @@ -195,7 +195,10 @@ ParamType = (ScalarType | ArrayRefType) TypedParam = - ParamType Name + (ParamType Name) | OldTypedParam + +OldTypedParam = + ('creg' | 'qreg') Name Designator? TypedParamList = '(' (TypedParam (',' TypedParam)* ','?)? ')' @@ -446,7 +449,7 @@ IODeclarationStatement = ('input' | 'output') (ScalarType | ArrayType) Name ';' OldStyleDeclarationStatement = - ('creg' | 'qreg') Name Designator? ';' + OldTypedParam ';' QuantumDeclarationStatement = QubitType (Name | HardwareQubit) ';' diff --git a/crates/oq3_syntax/src/ast/generated/nodes.rs b/crates/oq3_syntax/src/ast/generated/nodes.rs index 63727c0..8b321d0 100644 --- a/crates/oq3_syntax/src/ast/generated/nodes.rs +++ b/crates/oq3_syntax/src/ast/generated/nodes.rs @@ -405,15 +405,8 @@ impl Measure { pub struct OldStyleDeclarationStatement { pub(crate) syntax: SyntaxNode, } -impl ast::HasName for OldStyleDeclarationStatement {} impl OldStyleDeclarationStatement { - pub fn creg_token(&self) -> Option { - support::token(&self.syntax, T![creg]) - } - pub fn qreg_token(&self) -> Option { - support::token(&self.syntax, T![qreg]) - } - pub fn designator(&self) -> Option { + pub fn old_typed_param(&self) -> Option { support::child(&self.syntax) } pub fn semicolon_token(&self) -> Option { @@ -761,6 +754,25 @@ impl TypedParam { pub fn param_type(&self) -> Option { support::child(&self.syntax) } + pub fn old_typed_param(&self) -> Option { + support::child(&self.syntax) + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct OldTypedParam { + pub(crate) syntax: SyntaxNode, +} +impl ast::HasName for OldTypedParam {} +impl OldTypedParam { + pub fn creg_token(&self) -> Option { + support::token(&self.syntax, T![creg]) + } + pub fn qreg_token(&self) -> Option { + support::token(&self.syntax, T![qreg]) + } + pub fn designator(&self) -> Option { + support::child(&self.syntax) + } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct ArrayExpr { @@ -1955,6 +1967,21 @@ impl AstNode for TypedParam { &self.syntax } } +impl AstNode for OldTypedParam { + fn can_cast(kind: SyntaxKind) -> bool { + kind == OLD_TYPED_PARAM + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} impl AstNode for ArrayExpr { fn can_cast(kind: SyntaxKind) -> bool { kind == ARRAY_EXPR @@ -3037,10 +3064,10 @@ impl AstNode for AnyHasName { | GATE | I_O_DECLARATION_STATEMENT | LET_STMT - | OLD_STYLE_DECLARATION_STATEMENT | QUANTUM_DECLARATION_STATEMENT | PARAM | TYPED_PARAM + | OLD_TYPED_PARAM | GATE_CALL_EXPR | HARDWARE_QUBIT | INDEXED_IDENTIFIER @@ -3318,6 +3345,11 @@ impl std::fmt::Display for TypedParam { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for OldTypedParam { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for ArrayExpr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) diff --git a/crates/pipeline-tests/tests/snapshots/runner__tests__snippets__invalid__statements__io.qasm-parse.snap b/crates/pipeline-tests/tests/snapshots/runner__tests__snippets__invalid__statements__io.qasm-parse.snap index 80799b4..93fdd64 100644 --- a/crates/pipeline-tests/tests/snapshots/runner__tests__snippets__invalid__statements__io.qasm-parse.snap +++ b/crates/pipeline-tests/tests/snapshots/runner__tests__snippets__invalid__statements__io.qasm-parse.snap @@ -33,7 +33,7 @@ I_O_DECLARATION_STATEMENT@30..36: input SCALAR_TYPE@36..36: NAME@36..36: OLD_STYLE_DECLARATION_STATEMENT@36..50: qreg myvar[4]; -INDEXED_IDENTIFIER@41..49: myvar[4] +OLD_TYPED_PARAM@36..49: qreg myvar[4] INDEX_OPERATOR@46..49: [4] EXPRESSION_LIST@47..48: 4 LITERAL@47..48: 4 @@ -41,7 +41,7 @@ I_O_DECLARATION_STATEMENT@51..58: output SCALAR_TYPE@58..58: NAME@58..58: OLD_STYLE_DECLARATION_STATEMENT@58..72: qreg myvar[4]; -INDEXED_IDENTIFIER@63..71: myvar[4] +OLD_TYPED_PARAM@58..71: qreg myvar[4] INDEX_OPERATOR@68..71: [4] EXPRESSION_LIST@69..70: 4 LITERAL@69..70: 4 diff --git a/crates/pipeline-tests/tests/snapshots/runner__tests__snippets__invalid__statements__measure.qasm-parse.snap b/crates/pipeline-tests/tests/snapshots/runner__tests__snippets__invalid__statements__measure.qasm-parse.snap index 265aa69..69283ff 100644 --- a/crates/pipeline-tests/tests/snapshots/runner__tests__snippets__invalid__statements__measure.qasm-parse.snap +++ b/crates/pipeline-tests/tests/snapshots/runner__tests__snippets__invalid__statements__measure.qasm-parse.snap @@ -49,7 +49,7 @@ ERROR@58..59: > EXPR_STMT@60..62: b; IDENTIFIER@60..61: b OLD_STYLE_DECLARATION_STATEMENT@63..72: creg a[1] -INDEXED_IDENTIFIER@68..72: a[1] +OLD_TYPED_PARAM@63..72: creg a[1] INDEX_OPERATOR@69..72: [1] EXPRESSION_LIST@70..71: 1 LITERAL@70..71: 1 @@ -63,7 +63,7 @@ MEASURE_EXPRESSION@87..97: measure $0 HARDWARE_QUBIT@95..97: $0 ERROR@99..100: > OLD_STYLE_DECLARATION_STATEMENT@101..111: creg a[1]; -INDEXED_IDENTIFIER@106..110: a[1] +OLD_TYPED_PARAM@101..110: creg a[1] INDEX_OPERATOR@107..110: [1] EXPRESSION_LIST@108..109: 1 LITERAL@108..109: 1 diff --git a/crates/pipeline-tests/tests/snapshots/runner__tests__snippets__reference__assignment__alias.qasm-parse.snap b/crates/pipeline-tests/tests/snapshots/runner__tests__snippets__reference__assignment__alias.qasm-parse.snap index af8dece..22289f7 100644 --- a/crates/pipeline-tests/tests/snapshots/runner__tests__snippets__reference__assignment__alias.qasm-parse.snap +++ b/crates/pipeline-tests/tests/snapshots/runner__tests__snippets__reference__assignment__alias.qasm-parse.snap @@ -27,7 +27,7 @@ DESIGNATOR@4..7: [2] LITERAL@5..6: 2 NAME@8..9: a OLD_STYLE_DECLARATION_STATEMENT@11..21: creg b[2]; -INDEXED_IDENTIFIER@16..20: b[2] +OLD_TYPED_PARAM@11..20: creg b[2] INDEX_OPERATOR@17..20: [2] EXPRESSION_LIST@18..19: 2 LITERAL@18..19: 2 @@ -37,7 +37,7 @@ DESIGNATOR@27..30: [5] LITERAL@28..29: 5 NAME@31..33: q1 OLD_STYLE_DECLARATION_STATEMENT@35..46: qreg q2[7]; -INDEXED_IDENTIFIER@40..45: q2[7] +OLD_TYPED_PARAM@35..45: qreg q2[7] INDEX_OPERATOR@42..45: [7] EXPRESSION_LIST@43..44: 7 LITERAL@43..44: 7 diff --git a/crates/pipeline-tests/tests/snapshots/runner__tests__snippets__reference__assignment__assignment.qasm-parse.snap b/crates/pipeline-tests/tests/snapshots/runner__tests__snippets__reference__assignment__assignment.qasm-parse.snap index 44c624d..66ff10f 100644 --- a/crates/pipeline-tests/tests/snapshots/runner__tests__snippets__reference__assignment__assignment.qasm-parse.snap +++ b/crates/pipeline-tests/tests/snapshots/runner__tests__snippets__reference__assignment__assignment.qasm-parse.snap @@ -33,7 +33,7 @@ DESIGNATOR@14..17: [2] LITERAL@15..16: 2 NAME@18..19: b OLD_STYLE_DECLARATION_STATEMENT@21..31: creg b[2]; -INDEXED_IDENTIFIER@26..30: b[2] +OLD_TYPED_PARAM@21..30: creg b[2] INDEX_OPERATOR@27..30: [2] EXPRESSION_LIST@28..29: 2 LITERAL@28..29: 2 diff --git a/crates/pipeline-tests/tests/snapshots/runner__tests__snippets__reference__subroutine__subroutine.qasm-parse.snap b/crates/pipeline-tests/tests/snapshots/runner__tests__snippets__reference__subroutine__subroutine.qasm-parse.snap index 4c521e3..4f1ad24 100644 --- a/crates/pipeline-tests/tests/snapshots/runner__tests__snippets__reference__subroutine__subroutine.qasm-parse.snap +++ b/crates/pipeline-tests/tests/snapshots/runner__tests__snippets__reference__subroutine__subroutine.qasm-parse.snap @@ -3,11 +3,11 @@ source: crates/pipeline-tests/tests/runner.rs expression: parse_snap --- id: tests/snippets/reference/subroutine/subroutine.qasm -expect-parse: Todo +expect-parse: Ok --- parser --- -ok: false +ok: true panicked: false -errors: 14 +errors: 0 --- ast --- SOURCE_FILE@0..293: def test_sub1(int[5] i, qubit[2] q1, qreg q2[5]) -> int[10] { @@ -24,9 +24,13 @@ def returns_a_measure(qubit q) { return measure q; } -DEF@1..37: def test_sub1(int[5] i, qubit[2] q1, +DEF@1..137: def test_sub1(int[5] i, qubit[2] q1, qreg q2[5]) -> int[10] { + int[10] result; + if (result == 2) return 1 + result; + return result; +} NAME@5..14: test_sub1 -TYPED_PARAM_LIST@14..37: (int[5] i, qubit[2] q1, +TYPED_PARAM_LIST@14..49: (int[5] i, qubit[2] q1, qreg q2[5]) TYPED_PARAM@15..23: int[5] i SCALAR_TYPE@15..21: int[5] DESIGNATOR@18..21: [5] @@ -37,24 +41,14 @@ SCALAR_TYPE@25..33: qubit[2] DESIGNATOR@30..33: [2] LITERAL@31..32: 2 NAME@34..36: q1 -OLD_STYLE_DECLARATION_STATEMENT@38..48: qreg q2[5] -INDEXED_IDENTIFIER@43..48: q2[5] +OLD_TYPED_PARAM@38..48: qreg q2[5] INDEX_OPERATOR@45..48: [5] EXPRESSION_LIST@46..47: 5 LITERAL@46..47: 5 -ERROR@48..49: ) -EXPR_STMT@50..52: -> -PREFIX_EXPR@50..52: -> -ERROR@51..52: > +RETURN_SIGNATURE@50..60: -> int[10] SCALAR_TYPE@53..60: int[10] DESIGNATOR@56..60: [10] LITERAL@57..59: 10 -NAME@61..61: -EXPR_STMT@61..137: { - int[10] result; - if (result == 2) return 1 + result; - return result; -} BLOCK_EXPR@61..137: { int[10] result; if (result == 2) return 1 + result; @@ -77,9 +71,13 @@ IDENTIFIER@111..117: result EXPR_STMT@121..135: return result; RETURN_EXPR@121..134: return result IDENTIFIER@128..134: result -DEF@138..171: def test_sub2(int[5] i, bit[2] b, +DEF@138..237: def test_sub2(int[5] i, bit[2] b, creg c[3]) { + for int[5] j in {2, 3} + i += j; + return i+1; +} NAME@142..151: test_sub2 -TYPED_PARAM_LIST@151..171: (int[5] i, bit[2] b, +TYPED_PARAM_LIST@151..182: (int[5] i, bit[2] b, creg c[3]) TYPED_PARAM@152..160: int[5] i SCALAR_TYPE@152..158: int[5] DESIGNATOR@155..158: [5] @@ -90,17 +88,10 @@ SCALAR_TYPE@162..168: bit[2] DESIGNATOR@165..168: [2] LITERAL@166..167: 2 NAME@169..170: b -OLD_STYLE_DECLARATION_STATEMENT@172..181: creg c[3] -INDEXED_IDENTIFIER@177..181: c[3] +OLD_TYPED_PARAM@172..181: creg c[3] INDEX_OPERATOR@178..181: [3] EXPRESSION_LIST@179..180: 3 LITERAL@179..180: 3 -ERROR@181..182: ) -EXPR_STMT@183..237: { - for int[5] j in {2, 3} - i += j; - return i+1; -} BLOCK_EXPR@183..237: { for int[5] j in {2, 3} i += j; diff --git a/crates/pipeline-tests/tests/snippets/reference/subroutine/subroutine.qasm b/crates/pipeline-tests/tests/snippets/reference/subroutine/subroutine.qasm index 1486c55..3d63a3c 100644 --- a/crates/pipeline-tests/tests/snippets/reference/subroutine/subroutine.qasm +++ b/crates/pipeline-tests/tests/snippets/reference/subroutine/subroutine.qasm @@ -1,5 +1,5 @@ // lex: ok -// parse: todo +// parse: ok // sema: skip def test_sub1(int[5] i, qubit[2] q1, qreg q2[5]) -> int[10] {