From e6b1777db6ab47cf92111eababb86ae3bb6b71a9 Mon Sep 17 00:00:00 2001 From: John Lapeyre Date: Sun, 28 Jan 2024 22:39:22 -0500 Subject: [PATCH] Implement gate modifiers (#79) * Start implementing gate modifiers Make gate call and fn call expression statements * Support `pow` gate modifier * Implement ctrl and negctrl gate modifiers * Implement multiple gate modifiers on a single gate call Deciding when to return Option is somewhat confused at the moment. We need to think about this and standardize how it is done. * Remove unused stuff --- crates/oq3_parser/src/grammar/expressions.rs | 33 +- .../src/grammar/expressions/atom.rs | 94 +++++- crates/oq3_parser/src/grammar/items.rs | 16 - crates/oq3_parser/src/grammar/params.rs | 2 - crates/oq3_parser/src/parser.rs | 2 +- .../src/syntax_kind/syntax_kind_enum.rs | 20 +- crates/oq3_semantics/src/asg.rs | 152 +++++++-- .../oq3_semantics/src/syntax_to_semantics.rs | 210 ++++++++---- .../oq3_semantics/tests/from_string_tests.rs | 12 + crates/oq3_syntax/openqasm3.ungram | 30 +- crates/oq3_syntax/src/ast/expr_ext.rs | 15 + crates/oq3_syntax/src/ast/generated/nodes.rs | 300 +++++++++++++++++- crates/oq3_syntax/src/ast/prec.rs | 32 +- crates/oq3_syntax/src/tests/ast_src.rs | 13 +- 14 files changed, 800 insertions(+), 131 deletions(-) diff --git a/crates/oq3_parser/src/grammar/expressions.rs b/crates/oq3_parser/src/grammar/expressions.rs index fe51dfd..52a8c73 100644 --- a/crates/oq3_parser/src/grammar/expressions.rs +++ b/crates/oq3_parser/src/grammar/expressions.rs @@ -56,7 +56,6 @@ pub(crate) fn expr_no_struct(p: &mut Parser<'_>) { // GJL made public. remove visibility pub(crate) fn stmt(p: &mut Parser<'_>, semicolon: Semicolon) { - // dbg!(p.current()); if p.eat(T![;]) { return; } @@ -168,7 +167,8 @@ enum Associativity { fn current_op(p: &Parser<'_>) -> (u8, SyntaxKind, Associativity) { use Associativity::*; // It seems that return value is never checked for `NOT_AN_OP` - const NOT_AN_OP: (u8, SyntaxKind, Associativity) = (0, T![@], Left); + // r-a had @ for not an op. But we use triple dot + const NOT_AN_OP: (u8, SyntaxKind, Associativity) = (0, T![...], Left); match p.current() { T![|] if p.at(T![||]) => (3, T![||], Left), T![|] if p.at(T![|=]) => (1, T![|=], Right), @@ -225,13 +225,8 @@ fn expr_bp( return None; } let lhs_result = lhs(p, r); - // dbg!(&lhs_result); let mut lhs = match lhs_result { - Some((lhs, blocklike, got_call)) => { - if got_call { - m.abandon(p); - return None; - } + Some((lhs, blocklike)) => { let lhs = lhs.extend_to(p, m); if r.prefer_stmt && blocklike.is_block() { return Some((lhs, BlockLike::Block)); @@ -281,7 +276,7 @@ const LHS_FIRST: TokenSet = atom::ATOM_EXPR_FIRST.union(TokenSet::new(&[T![&], T![*], T![!], T![.], T![-], T![_]])); // Handles only prefix and postfix expressions?? Not binary infix? -fn lhs(p: &mut Parser<'_>, r: Restrictions) -> Option<(CompletedMarker, BlockLike, bool)> { +fn lhs(p: &mut Parser<'_>, r: Restrictions) -> Option<(CompletedMarker, BlockLike)> { let m; // Unary operators. In OQ3 should be ~ ! -, In r-a this is * ! - let kind = match p.current() { @@ -292,15 +287,15 @@ fn lhs(p: &mut Parser<'_>, r: Restrictions) -> Option<(CompletedMarker, BlockLik } _ => { let (lhs, blocklike) = atom::atom_expr(p, r)?; - let (cm, block_like, got_call) = + let (cm, block_like) = postfix_expr(p, lhs, blocklike, !(r.prefer_stmt && blocklike.is_block())); - return Some((cm, block_like, got_call)); + return Some((cm, block_like)); } }; // parse the interior of the unary expression expr_bp(p, None, r, 255); let cm = m.complete(p, kind); - Some((cm, BlockLike::NotBlock, false)) + Some((cm, BlockLike::NotBlock)) } fn postfix_expr( @@ -311,8 +306,7 @@ fn postfix_expr( // `while true {break}; ();` mut block_like: BlockLike, mut allow_calls: bool, -) -> (CompletedMarker, BlockLike, bool) { - let mut got_call = false; +) -> (CompletedMarker, BlockLike) { loop { lhs = match p.current() { // test stmt_postfix_expr_ambiguity @@ -323,10 +317,7 @@ fn postfix_expr( // [] => {} // } // } - T!['('] if allow_calls => { - got_call = true; - call_expr(p, lhs) - } + T!['('] if allow_calls => call_expr(p, lhs), T!['['] if allow_calls => match lhs.kind() { IDENTIFIER => indexed_identifer(p, lhs), _ => index_expr(p, lhs), @@ -336,12 +327,10 @@ fn postfix_expr( allow_calls = true; block_like = BlockLike::NotBlock; } - (lhs, block_like, got_call) + (lhs, block_like) } // Consumes either a function (def) call, or a gate call. -// expressions::gate_call_stmt also consumes gate calls in a different context. -// I think the present function gets all gate calls that include params in parens. fn call_expr(p: &mut Parser<'_>, lhs: CompletedMarker) -> CompletedMarker { assert!(p.at(T!['('])); let m = lhs.precede(p); @@ -351,7 +340,7 @@ fn call_expr(p: &mut Parser<'_>, lhs: CompletedMarker) -> CompletedMarker { // If there is no identifier, it is a function call. if matches!(p.current(), IDENT | HARDWAREIDENT) { params::arg_list_gate_call_qubits(p); - return m.complete(p, GATE_CALL_STMT); + return m.complete(p, GATE_CALL_EXPR); } m.complete(p, CALL_EXPR) } diff --git a/crates/oq3_parser/src/grammar/expressions/atom.rs b/crates/oq3_parser/src/grammar/expressions/atom.rs index 2052631..92c0c98 100644 --- a/crates/oq3_parser/src/grammar/expressions/atom.rs +++ b/crates/oq3_parser/src/grammar/expressions/atom.rs @@ -44,6 +44,10 @@ pub(super) const ATOM_EXPR_FIRST: TokenSet = T![return], T![while], T![measure], + T![inv], + T![ctrl], + T![negctrl], + T![pow], ])); pub(super) const EXPR_RECOVERY_SET: TokenSet = TokenSet::new(&[T![')'], T![']']]); @@ -77,9 +81,13 @@ pub(super) fn atom_expr( T![return] => return_expr(p), T!['{'] => block_expr(p), T![for] => for_expr(p, None), + T![inv] => inv_modifier_expr(p), + T![pow] => pow_modifier_expr(p), + T![ctrl] => ctrl_modifier_expr(p), + T![negctrl] => negctrl_modifier_expr(p), // FIXME: This is the simplest gate call. Need to cover // `mygate(myparam) q1, q2;` as well. - // IDENT if la == IDENT => gate_call_expr(p), + IDENT if la == IDENT => gate_call_expr(p), IDENT if (la == T![=] && p.nth(2) != T![=]) => grammar::items::assignment_statement(p), // FIXME: An identifer bound by the user in the program. // Need to handle more than identifier. @@ -98,6 +106,88 @@ pub(super) fn atom_expr( Some((done, blocklike)) } +fn inv_modifier_expr(p: &mut Parser<'_>) -> CompletedMarker { + let m = p.start(); + p.bump(T![inv]); + if p.at(T![@]) { + p.bump(T![@]); + } else if p.at(T!['(']) { + p.error("Modifier `inv` accepts no parameter. Expecting `@`"); + } else { + p.error("Expecting `@`"); + } + maybe_modified_gate_call_expr(p); + m.complete(p, INV_GATE_CALL_EXPR) +} + +fn pow_modifier_expr(p: &mut Parser<'_>) -> CompletedMarker { + let m = p.start(); + assert!(p.at(T![pow])); + p.bump(T![pow]); + if p.at(T!['(']) { + let m1 = p.start(); + p.expect(T!['(']); + expressions::expr(p); + p.expect(T![')']); + m1.complete(p, PAREN_EXPR); + } else { + p.error("expecting argument to pow gate modifier"); + } + p.expect(T![@]); + maybe_modified_gate_call_expr(p); + m.complete(p, POW_GATE_CALL_EXPR) +} + +fn ctrl_modifier_expr(p: &mut Parser<'_>) -> CompletedMarker { + let m = p.start(); + p.bump(T![ctrl]); + if p.at(T!['(']) { + let m1 = p.start(); + p.expect(T!['(']); + expressions::expr(p); + p.expect(T![')']); + m1.complete(p, PAREN_EXPR); + } + p.expect(T![@]); + maybe_modified_gate_call_expr(p); + m.complete(p, CTRL_GATE_CALL_EXPR) +} + +fn negctrl_modifier_expr(p: &mut Parser<'_>) -> CompletedMarker { + let m = p.start(); + p.bump(T![negctrl]); + if p.at(T!['(']) { + let m1 = p.start(); + p.expect(T!['(']); + expressions::expr(p); + p.expect(T![')']); + m1.complete(p, PAREN_EXPR); + } + p.expect(T![@]); + maybe_modified_gate_call_expr(p); + m.complete(p, NEG_CTRL_GATE_CALL_EXPR) +} + +fn maybe_modified_gate_call_expr(p: &mut Parser<'_>) -> CompletedMarker { + match p.current() { + T![inv] => inv_modifier_expr(p), + T![pow] => pow_modifier_expr(p), + T![ctrl] => ctrl_modifier_expr(p), + T![negctrl] => negctrl_modifier_expr(p), + _ => gate_call_expr(p), + } +} + +fn gate_call_expr(p: &mut Parser<'_>) -> CompletedMarker { + let m = p.start(); + identifier(p); // name of gate + if p.at(T!['(']) { + expressions::call_arg_list(p); + } + params::arg_list_gate_call_qubits(p); + m.complete(p, GATE_CALL_EXPR) +} + fn measure_expression(p: &mut Parser<'_>) -> CompletedMarker { let m = p.start(); p.bump(T![measure]); @@ -118,7 +208,7 @@ fn measure_expression(p: &mut Parser<'_>) -> CompletedMarker { // FIXME: changed it back to IDENTIFIER pub(crate) fn identifier(p: &mut Parser<'_>) -> CompletedMarker { let m = p.start(); - p.bump(IDENT); + p.expect(IDENT); m.complete(p, IDENTIFIER) } diff --git a/crates/oq3_parser/src/grammar/items.rs b/crates/oq3_parser/src/grammar/items.rs index 9ab04ca..9b93f11 100644 --- a/crates/oq3_parser/src/grammar/items.rs +++ b/crates/oq3_parser/src/grammar/items.rs @@ -101,7 +101,6 @@ pub(super) fn opt_item(p: &mut Parser<'_>, m: Marker) -> Result<(), Marker> { match p.current() { T![qubit] => qubit_declaration_stmt(p, m), T![const] => expressions::classical_declaration_stmt(p, m), - IDENT if (la == IDENT || la == HARDWAREIDENT) => gate_call_stmt(p, m), IDENT if (la == T![=] && p.nth(2) != T![=]) => assignment_statement_with_marker(p, m), T![gate] => gate_definition(p, m), T![break] => break_(p, m), @@ -127,21 +126,6 @@ pub(super) fn opt_item(p: &mut Parser<'_>, m: Marker) -> Result<(), Marker> { Ok(()) } -// expressions::call_expr also handles some occurences of gate calls -// FIXME: params in parens are likely always caught in expressions::call_expr -fn gate_call_stmt(p: &mut Parser<'_>, m: Marker) { - // expressions::var_name(p); - expressions::atom::identifier(p); // name of gate - assert!(!p.at(T!['('])); - // This is never true, I hope - if p.at(T!['(']) { - expressions::call_arg_list(p); - } - params::arg_list_gate_call_qubits(p); - p.expect(SEMICOLON); - m.complete(p, GATE_CALL_STMT); -} - fn gphase_call(p: &mut Parser<'_>, m: Marker) { assert!(p.at(T![gphase])); p.bump(T![gphase]); diff --git a/crates/oq3_parser/src/grammar/params.rs b/crates/oq3_parser/src/grammar/params.rs index 7c995c0..015fcad 100644 --- a/crates/oq3_parser/src/grammar/params.rs +++ b/crates/oq3_parser/src/grammar/params.rs @@ -108,7 +108,6 @@ fn _param_list_openqasm(p: &mut Parser<'_>, flavor: DefFlavor, m: Option let m = param_marker.take().unwrap_or_else(|| p.start()); if !(p.current().is_type_name() || p.at_ts(PARAM_FIRST)) { p.error("expected value parameter"); - println!("!!!! Got {:?}", p.current()); m.abandon(p); break; } @@ -118,7 +117,6 @@ fn _param_list_openqasm(p: &mut Parser<'_>, flavor: DefFlavor, m: Option expressions::expr_or_range_expr(p); true } - // SetExpression => { m.abandon(p); expressions::expr(p); true } GateCallQubits => arg_gate_call_qubit(p, m), DefParams | DefCalParams => param_typed(p, m), _ => param_untyped(p, m), diff --git a/crates/oq3_parser/src/parser.rs b/crates/oq3_parser/src/parser.rs index b98063c..c56d19b 100644 --- a/crates/oq3_parser/src/parser.rs +++ b/crates/oq3_parser/src/parser.rs @@ -352,7 +352,7 @@ impl Marker { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub(crate) struct CompletedMarker { pos: u32, kind: SyntaxKind, 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 5c7f8aa..7bec9a9 100644 --- a/crates/oq3_parser/src/syntax_kind/syntax_kind_enum.rs +++ b/crates/oq3_parser/src/syntax_kind/syntax_kind_enum.rs @@ -99,6 +99,10 @@ pub enum SyntaxKind { QUBIT_KW, VOID_KW, ARRAY_KW, + CTRL_KW, + NEGCTRL_KW, + INV_KW, + POW_KW, FALSE_KW, TRUE_KW, #[doc = r" literals"] @@ -163,6 +167,7 @@ pub enum SyntaxKind { CALL_EXPR, CAST_EXPRESSION, GATE_CALL_STMT, + GATE_CALL_EXPR, G_PHASE_CALL_STMT, INDEX_EXPR, PREFIX_EXPR, @@ -220,6 +225,11 @@ pub enum SyntaxKind { MEASURE_EXPRESSION, OLD_STYLE_DECLARATION_STATEMENT, QUANTUM_DECLARATION_STATEMENT, + INV_GATE_CALL_EXPR, + CTRL_GATE_CALL_EXPR, + NEG_CTRL_GATE_CALL_EXPR, + POW_GATE_CALL_EXPR, + GEN_GATE_CALL_EXPR, #[doc(hidden)] __LAST, } @@ -263,6 +273,10 @@ impl SyntaxKind { | QUBIT_KW | VOID_KW | ARRAY_KW + | CTRL_KW + | NEGCTRL_KW + | INV_KW + | POW_KW | FALSE_KW | TRUE_KW ) @@ -390,6 +404,10 @@ impl SyntaxKind { "qubit" => QUBIT_KW, "void" => VOID_KW, "array" => ARRAY_KW, + "ctrl" => CTRL_KW, + "negctrl" => NEGCTRL_KW, + "inv" => INV_KW, + "pow" => POW_KW, "false" => FALSE_KW, "true" => TRUE_KW, _ => return None, @@ -447,5 +465,5 @@ impl SyntaxKind { } } #[macro_export] -macro_rules ! T { [++] => { $ crate :: SyntaxKind :: DOUBLE_PLUS } ; [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; [_] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [..] => { $ crate :: SyntaxKind :: DOT2 } ; [...] => { $ crate :: SyntaxKind :: DOT3 } ; [..=] => { $ crate :: SyntaxKind :: DOT2EQ } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLON2 } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [=>] => { $ crate :: SyntaxKind :: FAT_ARROW } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [->] => { $ crate :: SyntaxKind :: THIN_ARROW } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [+=] => { $ crate :: SyntaxKind :: PLUSEQ } ; [-=] => { $ crate :: SyntaxKind :: MINUSEQ } ; [|=] => { $ crate :: SyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: SyntaxKind :: AMPEQ } ; [^=] => { $ crate :: SyntaxKind :: CARETEQ } ; [/=] => { $ crate :: SyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: SyntaxKind :: STAREQ } ; [%=] => { $ crate :: SyntaxKind :: PERCENTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; [<<=] => { $ crate :: SyntaxKind :: SHLEQ } ; [>>=] => { $ crate :: SyntaxKind :: SHREQ } ; [OPENQASM] => { $ crate :: SyntaxKind :: O_P_E_N_Q_A_S_M_KW } ; [include] => { $ crate :: SyntaxKind :: INCLUDE_KW } ; [def] => { $ crate :: SyntaxKind :: DEF_KW } ; [defcalgrammar] => { $ crate :: SyntaxKind :: DEFCALGRAMMAR_KW } ; [cal] => { $ crate :: SyntaxKind :: CAL_KW } ; [defcal] => { $ crate :: SyntaxKind :: DEFCAL_KW } ; [gate] => { $ crate :: SyntaxKind :: GATE_KW } ; [delay] => { $ crate :: SyntaxKind :: DELAY_KW } ; [reset] => { $ crate :: SyntaxKind :: RESET_KW } ; [measure] => { $ crate :: SyntaxKind :: MEASURE_KW } ; [pragma] => { $ crate :: SyntaxKind :: PRAGMA_KW } ; [let] => { $ crate :: SyntaxKind :: LET_KW } ; [box] => { $ crate :: SyntaxKind :: BOX_KW } ; [extern] => { $ crate :: SyntaxKind :: EXTERN_KW } ; [const] => { $ crate :: SyntaxKind :: CONST_KW } ; [barrier] => { $ crate :: SyntaxKind :: BARRIER_KW } ; [gphase] => { $ crate :: SyntaxKind :: GPHASE_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [continue] => { $ crate :: SyntaxKind :: CONTINUE_KW } ; [return] => { $ crate :: SyntaxKind :: RETURN_KW } ; [break] => { $ crate :: SyntaxKind :: BREAK_KW } ; [end] => { $ crate :: SyntaxKind :: END_KW } ; [input] => { $ crate :: SyntaxKind :: INPUT_KW } ; [output] => { $ crate :: SyntaxKind :: OUTPUT_KW } ; [readonly] => { $ crate :: SyntaxKind :: READONLY_KW } ; [mutable] => { $ crate :: SyntaxKind :: MUTABLE_KW } ; [qreg] => { $ crate :: SyntaxKind :: QREG_KW } ; [creg] => { $ crate :: SyntaxKind :: CREG_KW } ; [qubit] => { $ crate :: SyntaxKind :: QUBIT_KW } ; [void] => { $ crate :: SyntaxKind :: VOID_KW } ; [array] => { $ crate :: SyntaxKind :: ARRAY_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [float] => { $ crate :: SyntaxKind :: FLOAT_TY } ; [int] => { $ crate :: SyntaxKind :: INT_TY } ; [uint] => { $ crate :: SyntaxKind :: UINT_TY } ; [complex] => { $ crate :: SyntaxKind :: COMPLEX_TY } ; [bool] => { $ crate :: SyntaxKind :: BOOL_TY } ; [bit] => { $ crate :: SyntaxKind :: BIT_TY } ; [duration] => { $ crate :: SyntaxKind :: DURATION_TY } ; [stretch] => { $ crate :: SyntaxKind :: STRETCH_TY } ; [angle] => { $ crate :: SyntaxKind :: ANGLE_TY } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; } +macro_rules ! T { [++] => { $ crate :: SyntaxKind :: DOUBLE_PLUS } ; [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; [_] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [..] => { $ crate :: SyntaxKind :: DOT2 } ; [...] => { $ crate :: SyntaxKind :: DOT3 } ; [..=] => { $ crate :: SyntaxKind :: DOT2EQ } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLON2 } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [=>] => { $ crate :: SyntaxKind :: FAT_ARROW } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [->] => { $ crate :: SyntaxKind :: THIN_ARROW } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [+=] => { $ crate :: SyntaxKind :: PLUSEQ } ; [-=] => { $ crate :: SyntaxKind :: MINUSEQ } ; [|=] => { $ crate :: SyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: SyntaxKind :: AMPEQ } ; [^=] => { $ crate :: SyntaxKind :: CARETEQ } ; [/=] => { $ crate :: SyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: SyntaxKind :: STAREQ } ; [%=] => { $ crate :: SyntaxKind :: PERCENTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; [<<=] => { $ crate :: SyntaxKind :: SHLEQ } ; [>>=] => { $ crate :: SyntaxKind :: SHREQ } ; [OPENQASM] => { $ crate :: SyntaxKind :: O_P_E_N_Q_A_S_M_KW } ; [include] => { $ crate :: SyntaxKind :: INCLUDE_KW } ; [def] => { $ crate :: SyntaxKind :: DEF_KW } ; [defcalgrammar] => { $ crate :: SyntaxKind :: DEFCALGRAMMAR_KW } ; [cal] => { $ crate :: SyntaxKind :: CAL_KW } ; [defcal] => { $ crate :: SyntaxKind :: DEFCAL_KW } ; [gate] => { $ crate :: SyntaxKind :: GATE_KW } ; [delay] => { $ crate :: SyntaxKind :: DELAY_KW } ; [reset] => { $ crate :: SyntaxKind :: RESET_KW } ; [measure] => { $ crate :: SyntaxKind :: MEASURE_KW } ; [pragma] => { $ crate :: SyntaxKind :: PRAGMA_KW } ; [let] => { $ crate :: SyntaxKind :: LET_KW } ; [box] => { $ crate :: SyntaxKind :: BOX_KW } ; [extern] => { $ crate :: SyntaxKind :: EXTERN_KW } ; [const] => { $ crate :: SyntaxKind :: CONST_KW } ; [barrier] => { $ crate :: SyntaxKind :: BARRIER_KW } ; [gphase] => { $ crate :: SyntaxKind :: GPHASE_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [continue] => { $ crate :: SyntaxKind :: CONTINUE_KW } ; [return] => { $ crate :: SyntaxKind :: RETURN_KW } ; [break] => { $ crate :: SyntaxKind :: BREAK_KW } ; [end] => { $ crate :: SyntaxKind :: END_KW } ; [input] => { $ crate :: SyntaxKind :: INPUT_KW } ; [output] => { $ crate :: SyntaxKind :: OUTPUT_KW } ; [readonly] => { $ crate :: SyntaxKind :: READONLY_KW } ; [mutable] => { $ crate :: SyntaxKind :: MUTABLE_KW } ; [qreg] => { $ crate :: SyntaxKind :: QREG_KW } ; [creg] => { $ crate :: SyntaxKind :: CREG_KW } ; [qubit] => { $ crate :: SyntaxKind :: QUBIT_KW } ; [void] => { $ crate :: SyntaxKind :: VOID_KW } ; [array] => { $ crate :: SyntaxKind :: ARRAY_KW } ; [ctrl] => { $ crate :: SyntaxKind :: CTRL_KW } ; [negctrl] => { $ crate :: SyntaxKind :: NEGCTRL_KW } ; [inv] => { $ crate :: SyntaxKind :: INV_KW } ; [pow] => { $ crate :: SyntaxKind :: POW_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [float] => { $ crate :: SyntaxKind :: FLOAT_TY } ; [int] => { $ crate :: SyntaxKind :: INT_TY } ; [uint] => { $ crate :: SyntaxKind :: UINT_TY } ; [complex] => { $ crate :: SyntaxKind :: COMPLEX_TY } ; [bool] => { $ crate :: SyntaxKind :: BOOL_TY } ; [bit] => { $ crate :: SyntaxKind :: BIT_TY } ; [duration] => { $ crate :: SyntaxKind :: DURATION_TY } ; [stretch] => { $ crate :: SyntaxKind :: STRETCH_TY } ; [angle] => { $ crate :: SyntaxKind :: ANGLE_TY } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; } pub use T; diff --git a/crates/oq3_semantics/src/asg.rs b/crates/oq3_semantics/src/asg.rs index 1550023..2b0e624 100644 --- a/crates/oq3_semantics/src/asg.rs +++ b/crates/oq3_semantics/src/asg.rs @@ -123,6 +123,11 @@ pub enum Expr { Cast(Cast), Identifier(Identifier), HardwareQubit(HardwareQubit), + GateCallExpr(Box), + InvGateCallExpr(Box), + PowGateCallExpr(Box), + CtrlGateCallExpr(Box), + NegCtrlGateCallExpr(Box), IndexExpression(IndexExpression), IndexedIdentifier(IndexedIdentifier), GateOperand(GateOperand), @@ -186,7 +191,6 @@ pub enum Stmt { Extern, // stub For, // stub GateDeclaration(GateDeclaration), - GateCall(GateCall), // A statement because a gate call does not return anything GPhaseCall(GPhaseCall), IODeclaration, // stub If(If), @@ -611,23 +615,14 @@ impl Barrier { } #[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct GateCall { +pub struct GateCallExpr { name: SymbolIdResult, params: Option>, qubits: Vec, - modifier: Option, -} - -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub enum GateModifier { - Inv, - Pow(TExpr), - Ctrl(Option), - NegCtrl(Option), } // Following naming in ref parser instead -// We ~~will~~ should try to use the distinction between "parameter", which appears in the signature, +// We ~~will~~ try to use the distinction between "parameter", which appears in the signature, // and "argument", which appears in the call expression. #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum QubitArgument { @@ -648,18 +643,16 @@ impl GateOperand { } } -impl GateCall { +impl GateCallExpr { pub fn new( name: SymbolIdResult, params: Option>, qubits: Vec, - modifier: Option, - ) -> GateCall { - GateCall { + ) -> GateCallExpr { + GateCallExpr { name, params, qubits, - modifier, } } @@ -675,8 +668,129 @@ impl GateCall { &self.params } - pub fn modifier(&self) -> &Option { - &self.modifier + pub fn to_expr(self) -> Expr { + Expr::GateCallExpr(Box::new(self)) + } + + // FIXME: use Gate(i32, i32) + pub fn to_texpr(self) -> TExpr { + TExpr::new(self.to_expr(), Type::ToDo) + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct InvGateCallExpr { + gate_call: TExpr, +} + +impl InvGateCallExpr { + pub fn new(gate_call: TExpr) -> InvGateCallExpr { + InvGateCallExpr { gate_call } + } + + pub fn gate_call(&self) -> &TExpr { + &self.gate_call + } + + pub fn to_expr(self) -> Expr { + Expr::InvGateCallExpr(Box::new(self)) + } + + pub fn to_texpr(self) -> TExpr { + TExpr::new(self.to_expr(), Type::ToDo) + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct PowGateCallExpr { + gate_call: TExpr, + exponent: TExpr, +} + +impl PowGateCallExpr { + pub fn new(gate_call: TExpr, exponent: TExpr) -> PowGateCallExpr { + PowGateCallExpr { + gate_call, + exponent, + } + } + + pub fn gate_call(&self) -> &TExpr { + &self.gate_call + } + + pub fn exponent(&self) -> &TExpr { + &self.exponent + } + + pub fn to_expr(self) -> Expr { + Expr::PowGateCallExpr(Box::new(self)) + } + + pub fn to_texpr(self) -> TExpr { + TExpr::new(self.to_expr(), Type::ToDo) + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct CtrlGateCallExpr { + gate_call: TExpr, + exponent: Option, +} + +impl CtrlGateCallExpr { + pub fn new(gate_call: TExpr, exponent: Option) -> CtrlGateCallExpr { + CtrlGateCallExpr { + gate_call, + exponent, + } + } + + pub fn gate_call(&self) -> &TExpr { + &self.gate_call + } + + pub fn exponent(&self) -> Option<&TExpr> { + self.exponent.as_ref() + } + + pub fn to_expr(self) -> Expr { + Expr::CtrlGateCallExpr(Box::new(self)) + } + + pub fn to_texpr(self) -> TExpr { + TExpr::new(self.to_expr(), Type::ToDo) + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct NegCtrlGateCallExpr { + gate_call: TExpr, + exponent: Option, +} + +impl NegCtrlGateCallExpr { + pub fn new(gate_call: TExpr, exponent: Option) -> NegCtrlGateCallExpr { + NegCtrlGateCallExpr { + gate_call, + exponent, + } + } + + pub fn gate_call(&self) -> &TExpr { + &self.gate_call + } + + pub fn exponent(&self) -> Option<&TExpr> { + self.exponent.as_ref() + } + + pub fn to_expr(self) -> Expr { + Expr::NegCtrlGateCallExpr(Box::new(self)) + } + + pub fn to_texpr(self) -> TExpr { + TExpr::new(self.to_expr(), Type::ToDo) } } diff --git a/crates/oq3_semantics/src/syntax_to_semantics.rs b/crates/oq3_semantics/src/syntax_to_semantics.rs index 99bcdb1..51cb3c9 100644 --- a/crates/oq3_semantics/src/syntax_to_semantics.rs +++ b/crates/oq3_semantics/src/syntax_to_semantics.rs @@ -197,9 +197,14 @@ fn from_expr_stmt(expr_stmt: synast::ExprStmt, context: &mut Context) -> Option< ) } +fn from_paren_expr(paren_expr: synast::ParenExpr, context: &mut Context) -> Option { + from_expr(paren_expr.expr().unwrap(), context) +} + fn from_expr(expr: synast::Expr, context: &mut Context) -> Option { match expr { - synast::Expr::ParenExpr(paren_expr) => from_expr(paren_expr.expr().unwrap(), context), + // synast::Expr::ParenExpr(paren_expr) => from_expr(paren_expr.expr().unwrap(), context), + synast::Expr::ParenExpr(paren_expr) => from_paren_expr(paren_expr, context), synast::Expr::BinExpr(bin_expr) => { let synast_op = bin_expr.op_kind().unwrap(); let left_syn = bin_expr.lhs().unwrap(); @@ -247,6 +252,24 @@ fn from_expr(expr: synast::Expr, context: &mut Context) -> Option { Some(asg::MeasureExpression::new(gate_operand_asg).to_texpr()) } + synast::Expr::GateCallExpr(gate_call) => Some(from_gate_call_expr(gate_call, context)), + + synast::Expr::InvGateCallExpr(inv_gate_call) => { + from_inv_gate_call_expr(inv_gate_call, context) + } + + synast::Expr::PowGateCallExpr(pow_gate_call) => { + from_pow_gate_call_expr(pow_gate_call, context) + } + + synast::Expr::CtrlGateCallExpr(ctrl_gate_call) => { + from_ctrl_gate_call_expr(ctrl_gate_call, context) + } + + synast::Expr::NegCtrlGateCallExpr(negctrl_gate_call) => { + from_negctrl_gate_call_expr(negctrl_gate_call, context) + } + // Everything else is not yet implemented _ => { println!("Expression not supported {:?}", expr); @@ -255,6 +278,132 @@ fn from_expr(expr: synast::Expr, context: &mut Context) -> Option { } } +fn from_inv_gate_call_expr( + inv_gate_call_expr: synast::InvGateCallExpr, + context: &mut Context, +) -> Option { + let gate_call = + from_gen_gate_call_expr(inv_gate_call_expr.gen_gate_call_expr().unwrap(), context).unwrap(); + Some(asg::InvGateCallExpr::new(gate_call).to_texpr()) +} + +fn from_pow_gate_call_expr( + pow_gate_call_expr: synast::PowGateCallExpr, + context: &mut Context, +) -> Option { + let gate_call = + from_gen_gate_call_expr(pow_gate_call_expr.gen_gate_call_expr().unwrap(), context).unwrap(); + let exponent = from_paren_expr(pow_gate_call_expr.paren_expr().unwrap(), context).unwrap(); + Some(asg::PowGateCallExpr::new(gate_call, exponent).to_texpr()) +} + +fn from_ctrl_gate_call_expr( + ctrl_gate_call_expr: synast::CtrlGateCallExpr, + context: &mut Context, +) -> Option { + let gate_call = + from_gen_gate_call_expr(ctrl_gate_call_expr.gen_gate_call_expr().unwrap(), context) + .unwrap(); + let exponent = match ctrl_gate_call_expr.paren_expr() { + Some(paren_expr) => from_paren_expr(paren_expr, context), + None => None, + }; + Some(asg::CtrlGateCallExpr::new(gate_call, exponent).to_texpr()) +} + +fn from_negctrl_gate_call_expr( + negctrl_gate_call_expr: synast::NegCtrlGateCallExpr, + context: &mut Context, +) -> Option { + let gate_call = from_gen_gate_call_expr( + negctrl_gate_call_expr.gen_gate_call_expr().unwrap(), + context, + ) + .unwrap(); + let exponent = match negctrl_gate_call_expr.paren_expr() { + Some(paren_expr) => from_paren_expr(paren_expr, context), + None => None, + }; + Some(asg::NegCtrlGateCallExpr::new(gate_call, exponent).to_texpr()) +} + +fn from_gen_gate_call_expr( + gen_gate_call_expr: synast::GenGateCallExpr, + context: &mut Context, +) -> Option { + match gen_gate_call_expr { + synast::GenGateCallExpr::GateCallExpr(gate_call_expr) => { + Some(from_gate_call_expr(gate_call_expr, context)) + } + synast::GenGateCallExpr::InvGateCallExpr(gate_call_expr) => { + from_inv_gate_call_expr(gate_call_expr, context) + } + synast::GenGateCallExpr::PowGateCallExpr(gate_call_expr) => { + from_pow_gate_call_expr(gate_call_expr, context) + } + synast::GenGateCallExpr::CtrlGateCallExpr(gate_call_expr) => { + from_ctrl_gate_call_expr(gate_call_expr, context) + } + synast::GenGateCallExpr::NegCtrlGateCallExpr(gate_call_expr) => { + from_negctrl_gate_call_expr(gate_call_expr, context) + } + } +} + +fn from_gate_call_expr(gate_call_expr: synast::GateCallExpr, context: &mut Context) -> asg::TExpr { + // Warning, I think map overlooks None. which can cause a bug, in the present case. + // Because None means a coding error upstream. Better to blow up here. + let gate_operands: Vec<_> = gate_call_expr + .qubit_list() + .unwrap() + .gate_operands() + .map(|qubit| from_gate_operand(qubit, context)) + .collect(); + + let param_list = gate_call_expr + .arg_list() + .map(|ex| inner_expression_list(ex.expression_list().unwrap(), context)); + let num_params = match param_list { + Some(ref params) => params.len(), + None => 0, + }; + let gate_id = gate_call_expr.identifier(); + // FIXME: make sure we are efficient with strings + let gate_name = gate_call_expr.identifier().unwrap().text().to_string(); + let (symbol_result, gate_type) = context + .lookup_gate_symbol(gate_name.as_ref(), gate_id.as_ref().unwrap()) + .as_tuple(); + if matches!(gate_type, Type::Gate(_, _)) { + let (def_num_params, def_num_qubits) = match gate_type { + Type::Gate(np, nq) => (np, nq), + _ => (0, 0), + }; + if def_num_params != num_params.try_into().unwrap() { + if num_params != 0 { + // If num params is mismatched, locate error at list of params supplied. + context.insert_error(NumGateParamsError, &gate_call_expr.arg_list().unwrap()); + } else { + // If no params are supplied, but some are expected, locate error at gate name. + context.insert_error(NumGateParamsError, &gate_id.unwrap()); + } + } + let num_qubits: usize = gate_operands.len(); + if def_num_qubits != num_qubits.try_into().unwrap() { + if num_qubits == 0 { + // This probably can't happen because no qubit args is not recognized syntactially + // as a gate call. + context.insert_error(NumGateQubitsError, &gate_call_expr); + } else { + context.insert_error(NumGateQubitsError, &gate_call_expr.qubit_list().unwrap()); + }; + } + } else if symbol_result.is_ok() { + // If symbol_result.is_err then we have already logged UndefGateError. + context.insert_error(IncompatibleTypesError, &gate_id.unwrap()); + } + asg::GateCallExpr::new(symbol_result, param_list, gate_operands).to_texpr() +} + fn from_gate_operand(gate_operand: synast::GateOperand, context: &mut Context) -> asg::TExpr { match gate_operand { synast::GateOperand::HardwareQubit(ref hwq) => { @@ -480,65 +629,6 @@ fn from_item(item: synast::Item, context: &mut Context) -> Option { Some(asg::GateDeclaration::new(gate_name_symbol_id, params, qubits, block).to_stmt()) } - synast::Item::GateCallStmt(gate_call) => { - // Warning, I think map overlooks None. which can cause a bug, in the present case. - // Because None means a coding error upstream. Better to blow up here. - let gate_operands: Vec<_> = gate_call - .qubit_list() - .unwrap() - .gate_operands() - .map(|qubit| from_gate_operand(qubit, context)) - .collect(); - - let param_list = gate_call - .arg_list() - .map(|ex| inner_expression_list(ex.expression_list().unwrap(), context)); - let num_params = match param_list { - Some(ref params) => params.len(), - None => 0, - }; - let gate_id = gate_call.identifier(); - // FIXME: make sure we are efficient with strings - let gate_name = gate_call.identifier().unwrap().text().to_string(); - let (symbol_result, gate_type) = context - .lookup_gate_symbol(gate_name.as_ref(), gate_id.as_ref().unwrap()) - .as_tuple(); - if matches!(gate_type, Type::Gate(_, _)) { - let (def_num_params, def_num_qubits) = match gate_type { - Type::Gate(np, nq) => (np, nq), - _ => (0, 0), - }; - if def_num_params != num_params.try_into().unwrap() { - if num_params != 0 { - // If num params is mismatched, locate error at list of params supplied. - context.insert_error(NumGateParamsError, &gate_call.arg_list().unwrap()); - } else { - // If no params are supplied, but some are expected, locate error at gate name. - context.insert_error(NumGateParamsError, &gate_id.unwrap()); - } - } - let num_qubits: usize = gate_operands.len(); - if def_num_qubits != num_qubits.try_into().unwrap() { - if num_qubits == 0 { - // This probably can't happen because no qubit args is not recognized syntactially - // as a gate call. - context.insert_error(NumGateQubitsError, &gate_call); - } else { - context.insert_error(NumGateQubitsError, &gate_call.qubit_list().unwrap()); - }; - } - } else if symbol_result.is_ok() { - // If symbol_result.is_err then we have already logged UndefGateError. - context.insert_error(IncompatibleTypesError, &gate_id.unwrap()); - } - Some(asg::Stmt::GateCall(asg::GateCall::new( - symbol_result, - param_list, - gate_operands, - None, - ))) - } - synast::Item::Barrier(barrier) => { let gate_operands = barrier.qubit_list().map(|operands| { operands diff --git a/crates/oq3_semantics/tests/from_string_tests.rs b/crates/oq3_semantics/tests/from_string_tests.rs index 3cacf69..2993e74 100644 --- a/crates/oq3_semantics/tests/from_string_tests.rs +++ b/crates/oq3_semantics/tests/from_string_tests.rs @@ -248,6 +248,18 @@ h q[1]; assert_eq!(program.len(), 3); } +#[test] +fn test_from_string_inv_gate_call() { + let code = r#" +gate h q {} +qubit q; +inv @ h q; +"#; + let (program, errors, _symbol_table) = parse_string(code); + assert!(errors.is_empty()); + assert_eq!(program.len(), 3); +} + #[test] fn test_from_string_barrier() { let code = r#" diff --git a/crates/oq3_syntax/openqasm3.ungram b/crates/oq3_syntax/openqasm3.ungram index 3be9238..30a99ea 100644 --- a/crates/oq3_syntax/openqasm3.ungram +++ b/crates/oq3_syntax/openqasm3.ungram @@ -199,6 +199,7 @@ Expr = | BlockExpr | BoxExpr | CallExpr +| GateCallExpr | CastExpression | IndexExpr | IndexedIdentifier @@ -210,6 +211,10 @@ Expr = | MeasureExpression | Identifier | HardwareQubit +| InvGateCallExpr +| PowGateCallExpr +| CtrlGateCallExpr +| NegCtrlGateCallExpr Identifier = 'ident' @@ -268,9 +273,6 @@ ArrayExpr = IndexExpr = Expr IndexOperator -CallExpr = - Expr ArgList - // Note: I don't know if the modifier '?' is used at all. Not in methods on `struct ArgList`. // FIXME: use ExpressionList ArgList = @@ -287,9 +289,31 @@ CastExpression = GateCallStmt = Name ArgList? QubitList +// FIXME: Explain why head of call expression is Expr and not Name +CallExpr = + Expr ArgList + +GateCallExpr = + Name ArgList? QubitList + GPhaseCallStmt = 'gphase' arg:Expr +InvGateCallExpr = + 'inv' '@' GenGateCallExpr + +PowGateCallExpr = + 'pow' ParenExpr '@' GenGateCallExpr + +CtrlGateCallExpr = + 'ctrl' ParenExpr? '@' GenGateCallExpr + +NegCtrlGateCallExpr = + 'negctrl' ParenExpr? '@' GenGateCallExpr + +GenGateCallExpr = + InvGateCallExpr | PowGateCallExpr | CtrlGateCallExpr | NegCtrlGateCallExpr | GateCallExpr + // label 'then_branch' is handled manually in expr_ext.rs IfStmt = 'if' condition:Expr then_branch:Expr diff --git a/crates/oq3_syntax/src/ast/expr_ext.rs b/crates/oq3_syntax/src/ast/expr_ext.rs index e3cad7d..38aec86 100644 --- a/crates/oq3_syntax/src/ast/expr_ext.rs +++ b/crates/oq3_syntax/src/ast/expr_ext.rs @@ -432,6 +432,21 @@ impl ast::GateCallStmt { } } +impl ast::GateCallExpr { + // This may be redundant + pub fn name(&self) -> Option { + support::child(&self.syntax) + } + + pub fn identifier(&self) -> Option { + let maybe_id = support::children(self.syntax()).next(); + match maybe_id { + Some(ast::Expr::Identifier(ident)) => Some(ident), + _ => None, + } + } +} + // Angles are optional, but they come before qubits. // So we need a bit of logic. // diff --git a/crates/oq3_syntax/src/ast/generated/nodes.rs b/crates/oq3_syntax/src/ast/generated/nodes.rs index 8b7be9c..f38ea52 100644 --- a/crates/oq3_syntax/src/ast/generated/nodes.rs +++ b/crates/oq3_syntax/src/ast/generated/nodes.rs @@ -532,6 +532,17 @@ impl CallExpr { } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct GateCallExpr { + pub(crate) syntax: SyntaxNode, +} +impl ast::HasName for GateCallExpr {} +impl ast::HasArgList for GateCallExpr {} +impl GateCallExpr { + pub fn qubit_list(&self) -> Option { + support::child(&self.syntax) + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CastExpression { pub(crate) syntax: SyntaxNode, } @@ -667,6 +678,75 @@ pub struct HardwareQubit { impl ast::HasName for HardwareQubit {} impl HardwareQubit {} #[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct InvGateCallExpr { + pub(crate) syntax: SyntaxNode, +} +impl InvGateCallExpr { + pub fn inv_token(&self) -> Option { + support::token(&self.syntax, T![inv]) + } + pub fn at_token(&self) -> Option { + support::token(&self.syntax, T![@]) + } + pub fn gen_gate_call_expr(&self) -> Option { + support::child(&self.syntax) + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct PowGateCallExpr { + pub(crate) syntax: SyntaxNode, +} +impl PowGateCallExpr { + pub fn pow_token(&self) -> Option { + support::token(&self.syntax, T![pow]) + } + pub fn paren_expr(&self) -> Option { + support::child(&self.syntax) + } + pub fn at_token(&self) -> Option { + support::token(&self.syntax, T![@]) + } + pub fn gen_gate_call_expr(&self) -> Option { + support::child(&self.syntax) + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct CtrlGateCallExpr { + pub(crate) syntax: SyntaxNode, +} +impl CtrlGateCallExpr { + pub fn ctrl_token(&self) -> Option { + support::token(&self.syntax, T![ctrl]) + } + pub fn paren_expr(&self) -> Option { + support::child(&self.syntax) + } + pub fn at_token(&self) -> Option { + support::token(&self.syntax, T![@]) + } + pub fn gen_gate_call_expr(&self) -> Option { + support::child(&self.syntax) + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct NegCtrlGateCallExpr { + pub(crate) syntax: SyntaxNode, +} +impl NegCtrlGateCallExpr { + pub fn negctrl_token(&self) -> Option { + support::token(&self.syntax, T![negctrl]) + } + pub fn paren_expr(&self) -> Option { + support::child(&self.syntax) + } + pub fn at_token(&self) -> Option { + support::token(&self.syntax, T![@]) + } + pub fn gen_gate_call_expr(&self) -> Option { + support::child(&self.syntax) + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct ConcatenationExpr { pub(crate) syntax: SyntaxNode, } @@ -964,6 +1044,7 @@ pub enum Expr { BlockExpr(BlockExpr), BoxExpr(BoxExpr), CallExpr(CallExpr), + GateCallExpr(GateCallExpr), CastExpression(CastExpression), IndexExpr(IndexExpr), IndexedIdentifier(IndexedIdentifier), @@ -975,6 +1056,10 @@ pub enum Expr { MeasureExpression(MeasureExpression), Identifier(Identifier), HardwareQubit(HardwareQubit), + InvGateCallExpr(InvGateCallExpr), + PowGateCallExpr(PowGateCallExpr), + CtrlGateCallExpr(CtrlGateCallExpr), + NegCtrlGateCallExpr(NegCtrlGateCallExpr), } #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum GateOperand { @@ -988,6 +1073,14 @@ pub enum Stmt { Item(Item), } #[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum GenGateCallExpr { + InvGateCallExpr(InvGateCallExpr), + PowGateCallExpr(PowGateCallExpr), + CtrlGateCallExpr(CtrlGateCallExpr), + NegCtrlGateCallExpr(NegCtrlGateCallExpr), + GateCallExpr(GateCallExpr), +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum IndexKind { SetExpression(SetExpression), ExpressionList(ExpressionList), @@ -1592,6 +1685,21 @@ impl AstNode for CallExpr { &self.syntax } } +impl AstNode for GateCallExpr { + fn can_cast(kind: SyntaxKind) -> bool { + kind == GATE_CALL_EXPR + } + 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 CastExpression { fn can_cast(kind: SyntaxKind) -> bool { kind == CAST_EXPRESSION @@ -1757,6 +1865,66 @@ impl AstNode for HardwareQubit { &self.syntax } } +impl AstNode for InvGateCallExpr { + fn can_cast(kind: SyntaxKind) -> bool { + kind == INV_GATE_CALL_EXPR + } + 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 PowGateCallExpr { + fn can_cast(kind: SyntaxKind) -> bool { + kind == POW_GATE_CALL_EXPR + } + 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 CtrlGateCallExpr { + fn can_cast(kind: SyntaxKind) -> bool { + kind == CTRL_GATE_CALL_EXPR + } + 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 NegCtrlGateCallExpr { + fn can_cast(kind: SyntaxKind) -> bool { + kind == NEG_CTRL_GATE_CALL_EXPR + } + 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 ConcatenationExpr { fn can_cast(kind: SyntaxKind) -> bool { kind == CONCATENATION_EXPR @@ -2211,6 +2379,11 @@ impl From for Expr { Expr::CallExpr(node) } } +impl From for Expr { + fn from(node: GateCallExpr) -> Expr { + Expr::GateCallExpr(node) + } +} impl From for Expr { fn from(node: CastExpression) -> Expr { Expr::CastExpression(node) @@ -2266,6 +2439,26 @@ impl From for Expr { Expr::HardwareQubit(node) } } +impl From for Expr { + fn from(node: InvGateCallExpr) -> Expr { + Expr::InvGateCallExpr(node) + } +} +impl From for Expr { + fn from(node: PowGateCallExpr) -> Expr { + Expr::PowGateCallExpr(node) + } +} +impl From for Expr { + fn from(node: CtrlGateCallExpr) -> Expr { + Expr::CtrlGateCallExpr(node) + } +} +impl From for Expr { + fn from(node: NegCtrlGateCallExpr) -> Expr { + Expr::NegCtrlGateCallExpr(node) + } +} impl AstNode for Expr { fn can_cast(kind: SyntaxKind) -> bool { matches!( @@ -2275,6 +2468,7 @@ impl AstNode for Expr { | BLOCK_EXPR | BOX_EXPR | CALL_EXPR + | GATE_CALL_EXPR | CAST_EXPRESSION | INDEX_EXPR | INDEXED_IDENTIFIER @@ -2286,6 +2480,10 @@ impl AstNode for Expr { | MEASURE_EXPRESSION | IDENTIFIER | HARDWARE_QUBIT + | INV_GATE_CALL_EXPR + | POW_GATE_CALL_EXPR + | CTRL_GATE_CALL_EXPR + | NEG_CTRL_GATE_CALL_EXPR ) } fn cast(syntax: SyntaxNode) -> Option { @@ -2295,6 +2493,7 @@ impl AstNode for Expr { BLOCK_EXPR => Expr::BlockExpr(BlockExpr { syntax }), BOX_EXPR => Expr::BoxExpr(BoxExpr { syntax }), CALL_EXPR => Expr::CallExpr(CallExpr { syntax }), + GATE_CALL_EXPR => Expr::GateCallExpr(GateCallExpr { syntax }), CAST_EXPRESSION => Expr::CastExpression(CastExpression { syntax }), INDEX_EXPR => Expr::IndexExpr(IndexExpr { syntax }), INDEXED_IDENTIFIER => Expr::IndexedIdentifier(IndexedIdentifier { syntax }), @@ -2306,6 +2505,10 @@ impl AstNode for Expr { MEASURE_EXPRESSION => Expr::MeasureExpression(MeasureExpression { syntax }), IDENTIFIER => Expr::Identifier(Identifier { syntax }), HARDWARE_QUBIT => Expr::HardwareQubit(HardwareQubit { syntax }), + INV_GATE_CALL_EXPR => Expr::InvGateCallExpr(InvGateCallExpr { syntax }), + POW_GATE_CALL_EXPR => Expr::PowGateCallExpr(PowGateCallExpr { syntax }), + CTRL_GATE_CALL_EXPR => Expr::CtrlGateCallExpr(CtrlGateCallExpr { syntax }), + NEG_CTRL_GATE_CALL_EXPR => Expr::NegCtrlGateCallExpr(NegCtrlGateCallExpr { syntax }), _ => return None, }; Some(res) @@ -2317,6 +2520,7 @@ impl AstNode for Expr { Expr::BlockExpr(it) => &it.syntax, Expr::BoxExpr(it) => &it.syntax, Expr::CallExpr(it) => &it.syntax, + Expr::GateCallExpr(it) => &it.syntax, Expr::CastExpression(it) => &it.syntax, Expr::IndexExpr(it) => &it.syntax, Expr::IndexedIdentifier(it) => &it.syntax, @@ -2328,6 +2532,10 @@ impl AstNode for Expr { Expr::MeasureExpression(it) => &it.syntax, Expr::Identifier(it) => &it.syntax, Expr::HardwareQubit(it) => &it.syntax, + Expr::InvGateCallExpr(it) => &it.syntax, + Expr::PowGateCallExpr(it) => &it.syntax, + Expr::CtrlGateCallExpr(it) => &it.syntax, + Expr::NegCtrlGateCallExpr(it) => &it.syntax, } } } @@ -2377,6 +2585,65 @@ impl From for Stmt { Stmt::Item(node) } } +impl From for GenGateCallExpr { + fn from(node: InvGateCallExpr) -> GenGateCallExpr { + GenGateCallExpr::InvGateCallExpr(node) + } +} +impl From for GenGateCallExpr { + fn from(node: PowGateCallExpr) -> GenGateCallExpr { + GenGateCallExpr::PowGateCallExpr(node) + } +} +impl From for GenGateCallExpr { + fn from(node: CtrlGateCallExpr) -> GenGateCallExpr { + GenGateCallExpr::CtrlGateCallExpr(node) + } +} +impl From for GenGateCallExpr { + fn from(node: NegCtrlGateCallExpr) -> GenGateCallExpr { + GenGateCallExpr::NegCtrlGateCallExpr(node) + } +} +impl From for GenGateCallExpr { + fn from(node: GateCallExpr) -> GenGateCallExpr { + GenGateCallExpr::GateCallExpr(node) + } +} +impl AstNode for GenGateCallExpr { + fn can_cast(kind: SyntaxKind) -> bool { + matches!( + kind, + INV_GATE_CALL_EXPR + | POW_GATE_CALL_EXPR + | CTRL_GATE_CALL_EXPR + | NEG_CTRL_GATE_CALL_EXPR + | GATE_CALL_EXPR + ) + } + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + INV_GATE_CALL_EXPR => GenGateCallExpr::InvGateCallExpr(InvGateCallExpr { syntax }), + POW_GATE_CALL_EXPR => GenGateCallExpr::PowGateCallExpr(PowGateCallExpr { syntax }), + CTRL_GATE_CALL_EXPR => GenGateCallExpr::CtrlGateCallExpr(CtrlGateCallExpr { syntax }), + NEG_CTRL_GATE_CALL_EXPR => { + GenGateCallExpr::NegCtrlGateCallExpr(NegCtrlGateCallExpr { syntax }) + } + GATE_CALL_EXPR => GenGateCallExpr::GateCallExpr(GateCallExpr { syntax }), + _ => return None, + }; + Some(res) + } + fn syntax(&self) -> &SyntaxNode { + match self { + GenGateCallExpr::InvGateCallExpr(it) => &it.syntax, + GenGateCallExpr::PowGateCallExpr(it) => &it.syntax, + GenGateCallExpr::CtrlGateCallExpr(it) => &it.syntax, + GenGateCallExpr::NegCtrlGateCallExpr(it) => &it.syntax, + GenGateCallExpr::GateCallExpr(it) => &it.syntax, + } + } +} impl From for IndexKind { fn from(node: SetExpression) -> IndexKind { IndexKind::SetExpression(node) @@ -2416,7 +2683,7 @@ impl AnyHasArgList { } impl AstNode for AnyHasArgList { fn can_cast(kind: SyntaxKind) -> bool { - matches!(kind, GATE_CALL_STMT | CALL_EXPR) + matches!(kind, GATE_CALL_STMT | CALL_EXPR | GATE_CALL_EXPR) } fn cast(syntax: SyntaxNode) -> Option { Self::can_cast(syntax.kind()).then_some(AnyHasArgList { syntax }) @@ -2467,6 +2734,7 @@ impl AstNode for AnyHasName { | TYPE_SPEC | PARAM | EXTERN_ITEM + | GATE_CALL_EXPR | INDEXED_IDENTIFIER | HARDWARE_QUBIT | ALIAS_DECLARATION_STATEMENT @@ -2502,6 +2770,11 @@ impl std::fmt::Display for Stmt { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for GenGateCallExpr { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for IndexKind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) @@ -2702,6 +2975,11 @@ impl std::fmt::Display for CallExpr { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for GateCallExpr { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for CastExpression { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) @@ -2757,6 +3035,26 @@ impl std::fmt::Display for HardwareQubit { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for InvGateCallExpr { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for PowGateCallExpr { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for CtrlGateCallExpr { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for NegCtrlGateCallExpr { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for ConcatenationExpr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) diff --git a/crates/oq3_syntax/src/ast/prec.rs b/crates/oq3_syntax/src/ast/prec.rs index 75ce18b..ae7d15f 100644 --- a/crates/oq3_syntax/src/ast/prec.rs +++ b/crates/oq3_syntax/src/ast/prec.rs @@ -138,7 +138,15 @@ impl Expr { ArrayLiteral(_) => (0, 0), // These need to be checked MeasureExpression(_) => (0, 0), BoxExpr(_) => (0, 27), - CallExpr(_) | CastExpression(_) | IndexExpr(_) | IndexedIdentifier(_) => (29, 0), + GateCallExpr(_) + | CallExpr(_) + | CastExpression(_) + | IndexExpr(_) + | CtrlGateCallExpr(_) + | NegCtrlGateCallExpr(_) + | InvGateCallExpr(_) + | PowGateCallExpr(_) + | IndexedIdentifier(_) => (29, 0), ArrayExpr(_) | Literal(_) | ParenExpr(_) | Identifier(_) | HardwareQubit(_) | BlockExpr(_) => (0, 0), } @@ -185,6 +193,12 @@ impl Expr { BinExpr(e) => e.op_token(), BoxExpr(e) => e.box_token(), CallExpr(e) => e.arg_list().and_then(|args| args.l_paren_token()), + // FIXME: next line is quick fix, likely wrong + GateCallExpr(_) => None, + InvGateCallExpr(_) => None, + PowGateCallExpr(_) => None, + CtrlGateCallExpr(_) => None, + NegCtrlGateCallExpr(_) => None, CastExpression(e) => e.l_paren_token(), // IndexExpr(e) => e.l_brack_token(), // The bracket in IndexExpr is now absorbed in IndexOperator @@ -207,8 +221,20 @@ impl Expr { use Expr::*; match self { - ArrayExpr(_) | BlockExpr(_) | CallExpr(_) | CastExpression(_) | IndexExpr(_) - | IndexedIdentifier(_) | Literal(_) | Identifier(_) | HardwareQubit(_) + ArrayExpr(_) + | BlockExpr(_) + | CallExpr(_) + | GateCallExpr(_) + | CastExpression(_) + | InvGateCallExpr(_) + | IndexExpr(_) + | IndexedIdentifier(_) + | Literal(_) + | CtrlGateCallExpr(_) + | NegCtrlGateCallExpr(_) + | PowGateCallExpr(_) + | Identifier(_) + | HardwareQubit(_) | ParenExpr(_) => false, // For BinExpr and RangeExpr this is technically wrong -- the child can be on the left... diff --git a/crates/oq3_syntax/src/tests/ast_src.rs b/crates/oq3_syntax/src/tests/ast_src.rs index e80744a..d56442e 100644 --- a/crates/oq3_syntax/src/tests/ast_src.rs +++ b/crates/oq3_syntax/src/tests/ast_src.rs @@ -106,6 +106,11 @@ pub(crate) const KINDS_SRC: KindsSrc<'_> = KindsSrc { "qubit", "void", "array", + // Gate modifiers + "ctrl", + "negctrl", + "inv", + "pow", // I suppose these are literals "false", "true", @@ -167,6 +172,7 @@ pub(crate) const KINDS_SRC: KindsSrc<'_> = KindsSrc { "CALL_EXPR", "CAST_EXPRESSION", "GATE_CALL_STMT", + "GATE_CALL_EXPR", "G_PHASE_CALL_STMT", "INDEX_EXPR", // unary @@ -217,7 +223,6 @@ pub(crate) const KINDS_SRC: KindsSrc<'_> = KindsSrc { "INDEX_KIND", "INDEXED_IDENTIFIER", "IDENTIFIER", - // "EXPR_OR_RANGE", // Remove if we dont take this route "ARRAY_LITERAL", "HARDWARE_QUBIT", "CLASSICAL_DECLARATION_STATEMENT", @@ -229,6 +234,12 @@ pub(crate) const KINDS_SRC: KindsSrc<'_> = KindsSrc { "MEASURE_EXPRESSION", "OLD_STYLE_DECLARATION_STATEMENT", "QUANTUM_DECLARATION_STATEMENT", + // modified gate calls + "INV_GATE_CALL_EXPR", + "CTRL_GATE_CALL_EXPR", + "NEG_CTRL_GATE_CALL_EXPR", + "POW_GATE_CALL_EXPR", + "GEN_GATE_CALL_EXPR", ], };