Skip to content

Tidy up Display functions in QASM3 parser #2209

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 8 commits into from
Mar 5, 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
981 changes: 311 additions & 670 deletions compiler/qsc_qasm3/src/ast.rs

Large diffs are not rendered by default.

206 changes: 206 additions & 0 deletions compiler/qsc_qasm3/src/ast/display_utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

use std::fmt::{self, Display, Write};

/// Takes a unicode buffer or stream and wraps it with
/// `indenter::Idented`. Which applies an indentation of 1
/// each time you insert a new line.
fn with_indentation<T>(f: &mut T) -> indenter::Indented<'_, T>
where
T: fmt::Write,
{
let indent = indenter::indented(f);
set_indentation(indent, 1)
}

/// Takes an `indenter::Idented` and changes its indentation level.
fn set_indentation<T>(indent: indenter::Indented<'_, T>, level: usize) -> indenter::Indented<'_, T>
where
T: fmt::Write,
{
match level {
0 => indent.with_str(""),
1 => indent.with_str(" "),
2 => indent.with_str(" "),
3 => indent.with_str(" "),
_ => unimplemented!("indentation level not supported"),
}
}

/// Writes a list of elements to the given buffer or stream.
fn write_list<'write, 'itemref, 'item, T, I>(f: &'write mut impl Write, vals: I) -> fmt::Result
where
'item: 'itemref,
T: Display + 'item,
I: IntoIterator<Item = &'itemref T>,
{
let mut iter = vals.into_iter().peekable();
if iter.peek().is_none() {
write!(f, " <empty>")
} else {
for elt in iter {
write!(f, "\n{elt}")?;
}
Ok(())
}
}

/// Writes a list of elements to the given buffer or stream
/// with an additional indentation level.
pub(super) fn write_indented_list<'write, 'itemref, 'item, T, I>(
f: &'write mut impl Write,
vals: I,
) -> fmt::Result
where
'item: 'itemref,
T: Display + 'item,
I: IntoIterator<Item = &'itemref T>,
{
let mut iter = vals.into_iter().peekable();
if iter.peek().is_none() {
write!(f, " <empty>")
} else {
let mut indent = with_indentation(f);
for elt in iter {
write!(indent, "\n{elt}")?;
}
Ok(())
}
}

/// Writes the name and span of a structure to the given buffer or stream.
pub(super) fn write_header(f: &mut impl Write, name: &str, span: super::Span) -> fmt::Result {
write!(f, "{name} {span}:")
}

/// Writes the name and span of a structure to the given buffer or stream.
/// Inserts a newline afterwards.
pub(super) fn writeln_header(f: &mut impl Write, name: &str, span: super::Span) -> fmt::Result {
writeln!(f, "{name} {span}:")
}

/// Writes a field of a structure to the given buffer
/// or stream with an additional indententation level.
pub(super) fn write_field<T: Display>(
f: &mut impl Write,
field_name: &str,
val: &T,
) -> fmt::Result {
let mut indent = with_indentation(f);
write!(indent, "{field_name}: {val}")
}

/// Writes a field of a structure to the given buffer
/// or stream with an additional indententation level.
/// Inserts a newline afterwards.
pub(super) fn writeln_field<T: Display>(
f: &mut impl Write,
field_name: &str,
val: &T,
) -> fmt::Result {
write_field(f, field_name, val)?;
writeln!(f)
}

/// Writes an optional field of a structure to the given buffer
/// or stream with an additional indententation level.
pub(super) fn write_opt_field<T: Display>(
f: &mut impl Write,
field_name: &str,
opt_val: Option<&T>,
) -> fmt::Result {
if let Some(val) = opt_val {
write_field(f, field_name, val)
} else {
write_field(f, field_name, &"<none>")
}
}

/// Writes an optional field of a structure to the given buffer
/// or stream with an additional indententation level.
/// Inserts a newline afterwards.
pub(super) fn writeln_opt_field<T: Display>(
f: &mut impl Write,
field_name: &str,
opt_val: Option<&T>,
) -> fmt::Result {
write_opt_field(f, field_name, opt_val)?;
writeln!(f)
}

/// Writes an field of a structure to the given buffer
/// or stream with an additional indententation level.
/// The field must be an iterable.
pub(super) fn write_list_field<'write, 'itemref, 'item, T, I>(
f: &mut impl Write,
field_name: &str,
vals: I,
) -> fmt::Result
where
'item: 'itemref,
T: Display + 'item,
I: IntoIterator<Item = &'itemref T>,
{
let mut indent = with_indentation(f);
write!(indent, "{field_name}:")?;
let mut indent = set_indentation(indent, 2);
write_list(&mut indent, vals)
}

/// Writes an field of a structure to the given buffer
/// or stream with an additional indententation level.
/// The field must be an iterable.
/// Inserts a newline afterwards.
pub(super) fn writeln_list_field<'write, 'itemref, 'item, T, I>(
f: &mut impl Write,
field_name: &str,
vals: I,
) -> fmt::Result
where
'item: 'itemref,
T: Display + 'item,
I: IntoIterator<Item = &'itemref T>,
{
write_list_field(f, field_name, vals)?;
writeln!(f)
}

/// Writes an optional field of a structure to the given buffer
/// or stream with an additional indententation level.
/// The field must be an iterable.
pub(super) fn write_opt_list_field<'write, 'itemref, 'item, T, I>(
f: &mut impl Write,
field_name: &str,
opt_vals: Option<I>,
) -> fmt::Result
where
'item: 'itemref,
T: Display + 'item,
I: IntoIterator<Item = &'itemref T>,
{
if let Some(vals) = opt_vals {
write_list_field(f, field_name, vals)
} else {
let mut indent = with_indentation(f);
write!(indent, "{field_name}: <none>")
}
}

/// Writes an optional field of a structure to the given buffer
/// or stream with an additional indententation level.
/// The field must be an iterable.
/// Inserts a newline afterwards.
pub(super) fn writeln_opt_list_field<'write, 'itemref, 'item, T, I>(
f: &mut impl Write,
field_name: &str,
opt_vals: Option<I>,
) -> fmt::Result
where
'item: 'itemref,
T: Display + 'item,
I: IntoIterator<Item = &'itemref T>,
{
write_opt_list_field(f, field_name, opt_vals)?;
writeln!(f)
}
20 changes: 10 additions & 10 deletions compiler/qsc_qasm3/src/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ use crate::{
ast::{
self, list_from_iter, AssignExpr, AssignOpExpr, BinOp, BinaryOpExpr, Cast, DiscreteSet,
Expr, ExprKind, FunctionCall, GateOperand, HardwareQubit, Ident, IndexElement, IndexExpr,
IndexSetItem, IndexedIdent, List, Lit, LiteralKind, MeasureExpr, RangeDefinition, TimeUnit,
TypeDef, UnaryOp, ValueExpression, Version,
IndexSet, IndexSetItem, IndexedIdent, List, Lit, LiteralKind, MeasureExpr, RangeDefinition,
TimeUnit, TypeDef, UnaryOp, ValueExpression, Version,
},
keyword::Keyword,
lex::{
Expand Down Expand Up @@ -291,7 +291,7 @@ fn lit_token(lexeme: &str, token: Token) -> Result<Option<Lit>> {

Ok(Some(Lit {
span: token.span,
kind: LiteralKind::Bitstring(value, width),
kind: LiteralKind::Bitstring { value, width },
}))
}
Literal::Imaginary => {
Expand Down Expand Up @@ -492,13 +492,13 @@ fn index_element(s: &mut ParserContext) -> Result<IndexElement> {
Ok(Some(v)) => IndexElement::DiscreteSet(v),
Err(err) => return Err(err),
Ok(None) => {
let lo = s.peek().span.lo;
let (exprs, _) = seq(s, index_set_item)?;
let exprs = exprs
.into_iter()
.map(Box::new)
.collect::<Vec<_>>()
.into_boxed_slice();
IndexElement::IndexSet(exprs)
let exprs = list_from_iter(exprs);
IndexElement::IndexSet(IndexSet {
span: s.span(lo),
values: exprs,
})
}
};
Ok(index)
Expand Down Expand Up @@ -556,7 +556,7 @@ pub(crate) fn set_expr(s: &mut ParserContext) -> Result<DiscreteSet> {
recovering_token(s, TokenKind::Close(Delim::Brace));
Ok(DiscreteSet {
span: s.span(lo),
values: exprs.into_boxed_slice(),
values: list_from_iter(exprs),
})
}

Expand Down
Loading