Skip to content

Commit

Permalink
Implement gate modifiers (#79)
Browse files Browse the repository at this point in the history
* 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<TExpr> is somewhat confused at the
moment. We need to think about this and standardize how it is done.

* Remove unused stuff
  • Loading branch information
jlapeyre authored Jan 29, 2024
1 parent 175c7bc commit e6b1777
Show file tree
Hide file tree
Showing 14 changed files with 800 additions and 131 deletions.
33 changes: 11 additions & 22 deletions crates/oq3_parser/src/grammar/expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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() {
Expand All @@ -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(
Expand All @@ -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
Expand All @@ -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),
Expand All @@ -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);
Expand All @@ -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)
}
Expand Down
94 changes: 92 additions & 2 deletions crates/oq3_parser/src/grammar/expressions/atom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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![']']]);
Expand Down Expand Up @@ -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.
Expand All @@ -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]);
Expand All @@ -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)
}

Expand Down
16 changes: 0 additions & 16 deletions crates/oq3_parser/src/grammar/items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -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]);
Expand Down
2 changes: 0 additions & 2 deletions crates/oq3_parser/src/grammar/params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,6 @@ fn _param_list_openqasm(p: &mut Parser<'_>, flavor: DefFlavor, m: Option<Marker>
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;
}
Expand All @@ -118,7 +117,6 @@ fn _param_list_openqasm(p: &mut Parser<'_>, flavor: DefFlavor, m: Option<Marker>
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),
Expand Down
2 changes: 1 addition & 1 deletion crates/oq3_parser/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ impl Marker {
}
}

#[derive(Debug)]
#[derive(Debug, Clone)]
pub(crate) struct CompletedMarker {
pos: u32,
kind: SyntaxKind,
Expand Down
Loading

0 comments on commit e6b1777

Please sign in to comment.