Skip to content

Make assignment a StmtKind #2216

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

Merged
merged 3 commits into from
Mar 11, 2025
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
26 changes: 14 additions & 12 deletions compiler/qsc_qasm3/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,8 @@ impl Display for AliasDeclStmt {
#[derive(Clone, Debug, Default)]
pub enum StmtKind {
Alias(AliasDeclStmt),
Assign(AssignStmt),
AssignOp(AssignOpStmt),
Barrier(BarrierStmt),
Box(BoxStmt),
Break(BreakStmt),
Expand Down Expand Up @@ -351,6 +353,8 @@ pub enum StmtKind {
impl Display for StmtKind {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
StmtKind::Assign(stmt) => write!(f, "{stmt}"),
StmtKind::AssignOp(stmt) => write!(f, "{stmt}"),
StmtKind::Alias(alias) => write!(f, "{alias}"),
StmtKind::Barrier(barrier) => write!(f, "{barrier}"),
StmtKind::Box(box_stmt) => write!(f, "{box_stmt}"),
Expand Down Expand Up @@ -1432,8 +1436,6 @@ impl Display for SwitchCase {

#[derive(Clone, Debug, Default)]
pub enum ExprKind {
Assign(AssignExpr),
AssignOp(AssignOpExpr),
/// An expression with invalid syntax that can't be parsed.
#[default]
Err,
Expand All @@ -1458,37 +1460,37 @@ impl Display for ExprKind {
ExprKind::FunctionCall(call) => write!(f, "{call}"),
ExprKind::Cast(expr) => write!(f, "{expr}"),
ExprKind::IndexExpr(expr) => write!(f, "{expr}"),
ExprKind::Assign(expr) => write!(f, "{expr}"),
ExprKind::AssignOp(expr) => write!(f, "{expr}"),
ExprKind::Paren(expr) => write!(f, "Paren {expr}"),
}
}
}

#[derive(Clone, Debug)]
pub struct AssignExpr {
pub lhs: Expr,
pub struct AssignStmt {
pub span: Span,
pub lhs: IndexedIdent,
pub rhs: Expr,
}

impl Display for AssignExpr {
impl Display for AssignStmt {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
writeln!(f, "AssignExpr:")?;
writeln_header(f, "AssignStmt", self.span)?;
writeln_field(f, "lhs", &self.lhs)?;
write_field(f, "rhs", &self.rhs)
}
}

#[derive(Clone, Debug)]
pub struct AssignOpExpr {
pub struct AssignOpStmt {
pub span: Span,
pub op: BinOp,
pub lhs: Expr,
pub lhs: IndexedIdent,
pub rhs: Expr,
}

impl Display for AssignOpExpr {
impl Display for AssignOpStmt {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
writeln!(f, "AssignOpExpr:")?;
writeln_header(f, "AssignOpStmt", self.span)?;
writeln_field(f, "op", &self.op)?;
writeln_field(f, "lhs", &self.lhs)?;
write_field(f, "rhs", &self.rhs)
Expand Down
4 changes: 4 additions & 0 deletions compiler/qsc_qasm3/src/parser/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,9 @@ pub enum ErrorKind {
#[error("invalid gate call designator")]
#[diagnostic(code("Qasm3.Parse.InvalidGateCallDesignator"))]
InvalidGateCallDesignator(#[label] Span),
#[error("multiple index operators are only allowed in assignments")]
#[diagnostic(code("Qasm3.Parse.MultipleIndexOperators"))]
MultipleIndexOperators(#[label] Span),
}

impl ErrorKind {
Expand All @@ -156,6 +159,7 @@ impl ErrorKind {
Self::ExpectedItem(token, span) => Self::ExpectedItem(token, span + offset),
Self::GPhaseInvalidArguments(span) => Self::GPhaseInvalidArguments(span + offset),
Self::InvalidGateCallDesignator(span) => Self::InvalidGateCallDesignator(span + offset),
Self::MultipleIndexOperators(span) => Self::MultipleIndexOperators(span + offset),
}
}
}
2 changes: 1 addition & 1 deletion compiler/qsc_qasm3/src/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,7 @@ fn cast_op(s: &mut ParserContext, r#type: TypeDef) -> Result<ExprKind> {
}

fn index_expr(s: &mut ParserContext, lhs: Expr) -> Result<ExprKind> {
let lo = s.span(0).hi - 1;
let lo = lhs.span.lo;
let index = index_element(s)?;
recovering_token(s, TokenKind::Close(Delim::Bracket));
Ok(ExprKind::IndexExpr(IndexExpr {
Expand Down
16 changes: 8 additions & 8 deletions compiler/qsc_qasm3/src/parser/expr/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -953,7 +953,7 @@ fn index_expr() {
check_expr(
"foo[1]",
&expect![[r#"
Expr [0-6]: IndexExpr [3-6]:
Expr [0-6]: IndexExpr [0-6]:
collection: Expr [0-3]: Ident [0-3] "foo"
index: IndexSet [4-5]:
values:
Expand All @@ -966,7 +966,7 @@ fn index_set() {
check_expr(
"foo[{1, 4, 5}]",
&expect![[r#"
Expr [0-14]: IndexExpr [3-14]:
Expr [0-14]: IndexExpr [0-14]:
collection: Expr [0-3]: Ident [0-3] "foo"
index: DiscreteSet [4-13]:
values:
Expand All @@ -981,7 +981,7 @@ fn index_multiple_ranges() {
check_expr(
"foo[1:5, 3:7, 4:8]",
&expect![[r#"
Expr [0-18]: IndexExpr [3-18]:
Expr [0-18]: IndexExpr [0-18]:
collection: Expr [0-3]: Ident [0-3] "foo"
index: IndexSet [4-17]:
values:
Expand All @@ -1005,7 +1005,7 @@ fn index_range() {
check_expr(
"foo[1:5:2]",
&expect![[r#"
Expr [0-10]: IndexExpr [3-10]:
Expr [0-10]: IndexExpr [0-10]:
collection: Expr [0-3]: Ident [0-3] "foo"
index: IndexSet [4-9]:
values:
Expand All @@ -1021,7 +1021,7 @@ fn index_full_range() {
check_expr(
"foo[:]",
&expect![[r#"
Expr [0-6]: IndexExpr [3-6]:
Expr [0-6]: IndexExpr [0-6]:
collection: Expr [0-3]: Ident [0-3] "foo"
index: IndexSet [4-5]:
values:
Expand All @@ -1037,7 +1037,7 @@ fn index_range_start() {
check_expr(
"foo[1:]",
&expect![[r#"
Expr [0-7]: IndexExpr [3-7]:
Expr [0-7]: IndexExpr [0-7]:
collection: Expr [0-3]: Ident [0-3] "foo"
index: IndexSet [4-6]:
values:
Expand All @@ -1053,7 +1053,7 @@ fn index_range_end() {
check_expr(
"foo[:5]",
&expect![[r#"
Expr [0-7]: IndexExpr [3-7]:
Expr [0-7]: IndexExpr [0-7]:
collection: Expr [0-3]: Ident [0-3] "foo"
index: IndexSet [4-6]:
values:
Expand All @@ -1069,7 +1069,7 @@ fn index_range_step() {
check_expr(
"foo[:2:]",
&expect![[r#"
Expr [0-8]: IndexExpr [3-8]:
Expr [0-8]: IndexExpr [0-8]:
collection: Expr [0-3]: Ident [0-3] "foo"
index: IndexSet [4-7]:
values:
Expand Down
146 changes: 123 additions & 23 deletions compiler/qsc_qasm3/src/parser/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ use super::{
use crate::{
ast::{
list_from_iter, AccessControl, AliasDeclStmt, AngleType, Annotation, ArrayBaseTypeKind,
ArrayReferenceType, ArrayType, ArrayTypedParameter, AssignExpr, BarrierStmt, BitType,
Block, BoxStmt, BreakStmt, CalibrationGrammarStmt, CalibrationStmt, Cast,
ArrayReferenceType, ArrayType, ArrayTypedParameter, AssignOpStmt, AssignStmt, BarrierStmt,
BitType, Block, BoxStmt, BreakStmt, CalibrationGrammarStmt, CalibrationStmt, Cast,
ClassicalDeclarationStmt, ComplexType, ConstantDeclStmt, ContinueStmt, DefCalStmt, DefStmt,
DelayStmt, EndStmt, EnumerableSet, Expr, ExprKind, ExprStmt, ExternDecl, ExternParameter,
FloatType, ForStmt, FunctionCall, GPhase, GateCall, GateModifierKind, GateOperand,
Expand Down Expand Up @@ -107,6 +107,81 @@ pub(super) fn parse(s: &mut ParserContext) -> Result<Box<Stmt>> {
Box::new(StmtKind::Break(stmt))
} else if let Some(stmt) = opt(s, parse_end_stmt)? {
Box::new(StmtKind::End(stmt))
} else if let Some(indexed_ident) = opt(s, indexed_identifier)? {
if s.peek().kind == TokenKind::Eq {
s.advance();
let expr = expr::expr(s)?;
recovering_semi(s);
Box::new(StmtKind::Assign(AssignStmt {
span: s.span(lo),
lhs: indexed_ident,
rhs: expr,
}))
} else if let TokenKind::BinOpEq(op) = s.peek().kind {
s.advance();
let op = expr::closed_bin_op(op);
let expr = expr::expr(s)?;
recovering_semi(s);
Box::new(StmtKind::AssignOp(AssignOpStmt {
span: s.span(lo),
op,
lhs: indexed_ident,
rhs: expr,
}))
} else if s.peek().kind == TokenKind::Open(Delim::Paren) {
if !indexed_ident.indices.is_empty() {
s.push_error(Error::new(ErrorKind::Convert(
"Ident",
"IndexedIdent",
indexed_ident.span,
)));
}

let ident = indexed_ident.name;

s.advance();
let (args, _) = seq(s, expr::expr)?;
token(s, TokenKind::Close(Delim::Paren))?;

let funcall = Expr {
span: s.span(lo),
kind: Box::new(ExprKind::FunctionCall(FunctionCall {
span: s.span(lo),
name: ident,
args: args.into_iter().map(Box::new).collect(),
})),
};

let expr = expr::expr_with_lhs(s, funcall)?;

Box::new(parse_gate_call_with_expr(s, expr)?)
} else {
let kind = if indexed_ident.indices.is_empty() {
ExprKind::Ident(indexed_ident.name)
} else {
if indexed_ident.indices.len() > 1 {
s.push_error(Error::new(ErrorKind::MultipleIndexOperators(
indexed_ident.span,
)));
}

ExprKind::IndexExpr(IndexExpr {
span: indexed_ident.span,
collection: Expr {
span: indexed_ident.name.span,
kind: Box::new(ExprKind::Ident(indexed_ident.name)),
},
index: *indexed_ident.indices[0].clone(),
})
};

let expr = Expr {
span: indexed_ident.span,
kind: Box::new(kind),
};

Box::new(parse_gate_call_with_expr(s, expr)?)
}
} else if let Some(stmt_kind) = opt(s, parse_gate_call_stmt)? {
Box::new(stmt_kind)
} else if let Some(stmt) = opt(s, |s| parse_expression_stmt(s, None))? {
Expand Down Expand Up @@ -1098,27 +1173,7 @@ fn parse_end_stmt(s: &mut ParserContext) -> Result<EndStmt> {
/// Grammar: `(expression | assignExpr | AssignOpExpr) SEMICOLON`.
fn parse_expression_stmt(s: &mut ParserContext, lhs: Option<Expr>) -> Result<ExprStmt> {
let expr = if let Some(lhs) = lhs {
if opt(s, |s| token(s, TokenKind::Eq))?.is_some() {
let rhs = expr::expr(s)?;
Expr {
span: s.span(lhs.span.lo),
kind: Box::new(ExprKind::Assign(AssignExpr { lhs, rhs })),
}
} else if let TokenKind::BinOpEq(op) = s.peek().kind {
s.advance();
let op = expr::closed_bin_op(op);
let rhs = expr::expr(s)?;
Expr {
span: s.span(lhs.span.lo),
kind: Box::new(ExprKind::AssignOp(crate::ast::AssignOpExpr {
op,
lhs,
rhs,
})),
}
} else {
expr::expr_with_lhs(s, lhs)?
}
expr::expr_with_lhs(s, lhs)?
} else {
expr::expr(s)?
};
Expand Down Expand Up @@ -1296,6 +1351,51 @@ fn parse_gate_call_stmt(s: &mut ParserContext) -> Result<StmtKind> {
}))
}

/// This parser is used to disambiguate statements starting with an index
/// identifier. It is a simplified version of `parse_gate_call_stmt`.
fn parse_gate_call_with_expr(s: &mut ParserContext, gate_or_expr: Expr) -> Result<StmtKind> {
let lo = gate_or_expr.span.lo;
let mut duration = opt(s, designator)?;
let qubits = gate_operand_list(s)?;

// If didn't parse modifiers, a duration, nor qubit args then this is an expr, not a gate call.
if duration.is_none() && qubits.is_empty() {
return Ok(StmtKind::ExprStmt(parse_expression_stmt(
s,
Some(gate_or_expr),
)?));
}

// We parse the recovering semi after we call parse_expr_stmt.
recovering_semi(s);

// Reinterpret the function call or ident as a gate call.
let (name, args) = match *gate_or_expr.kind {
ExprKind::FunctionCall(FunctionCall { name, args, .. }) => (name, args),
ExprKind::Ident(ident) => (ident, Default::default()),
ExprKind::IndexExpr(index_expr) => reinterpret_index_expr(index_expr, &mut duration)?,
_ => {
return Err(Error::new(ErrorKind::ExpectedItem(
TokenKind::Identifier,
gate_or_expr.span,
)))
}
};

if qubits.is_empty() {
s.push_error(Error::new(ErrorKind::MissingGateCallOperands(s.span(lo))));
}

Ok(StmtKind::GateCall(GateCall {
span: s.span(lo),
modifiers: Default::default(),
name,
args,
qubits,
duration,
}))
}

/// This helper function reinterprets an indexed expression as
/// a gate call. There are two valid cases we are interested in:
/// 1. Ident[4]
Expand Down
4 changes: 2 additions & 2 deletions compiler/qsc_qasm3/src/parser/stmt/tests/alias.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ fn concatenation_alias() {
kind: AliasDeclStmt [0-32]:
ident: Ident [4-5] "x"
exprs:
Expr [8-14]: IndexExpr [9-14]:
Expr [8-14]: IndexExpr [8-14]:
collection: Expr [8-9]: Ident [8-9] "a"
index: IndexSet [10-13]:
values:
Expand All @@ -40,7 +40,7 @@ fn concatenation_alias() {
step: <none>
end: Expr [12-13]: Lit: Int(2)
Expr [18-19]: Ident [18-19] "b"
Expr [23-31]: IndexExpr [24-31]:
Expr [23-31]: IndexExpr [23-31]:
collection: Expr [23-24]: Ident [23-24] "c"
index: IndexSet [25-30]:
values:
Expand Down
Loading
Loading