Skip to content

Commit

Permalink
Q# Structs: Declaration and Constructor Syntax (#1573)
Browse files Browse the repository at this point in the history
Declaration and Constructor syntax for Q# structs.

Structs can be declared as namespace items with the following syntax:
```qsharp
struct Pair {
    First : Int,
    Second : Int
}
```

Structs types that have been declared can be constructed using the
following expression syntax:
```qsharp
new Pair { First = 3, Second = 4 }
```

Additionally, structs can be constructed from other structs using the
following copy constructor syntax:
```qsharp
new Pair { ...existing_pair }
```

and specific fields can be altered while copying:
```qsharp
new Pair { ...existing_pair, Second = 7 }
```
  • Loading branch information
ScottCarda-MS authored Jun 7, 2024
1 parent 3663b28 commit eaa558a
Show file tree
Hide file tree
Showing 39 changed files with 2,099 additions and 135 deletions.
19 changes: 17 additions & 2 deletions compiler/qsc_ast/src/assigner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

use crate::{
ast::{
Attr, Block, CallableDecl, Expr, FunctorExpr, Ident, Item, Namespace, NodeId, Package, Pat,
Path, QubitInit, SpecDecl, Stmt, Ty, TyDef, Visibility,
Attr, Block, CallableDecl, Expr, FieldAssign, FunctorExpr, Ident, Item, Namespace, NodeId,
Package, Pat, Path, QubitInit, SpecDecl, Stmt, Ty, TyDef, Visibility,
},
mut_visit::{self, MutVisitor},
};
Expand Down Expand Up @@ -76,6 +76,16 @@ impl MutVisitor for Assigner {
mut_visit::walk_callable_decl(self, decl);
}

fn visit_struct_decl(&mut self, decl: &mut crate::ast::StructDecl) {
self.assign(&mut decl.id);
mut_visit::walk_struct_decl(self, decl);
}

fn visit_field_def(&mut self, def: &mut crate::ast::FieldDef) {
self.assign(&mut def.id);
mut_visit::walk_field_def(self, def);
}

fn visit_spec_decl(&mut self, decl: &mut SpecDecl) {
self.assign(&mut decl.id);
mut_visit::walk_spec_decl(self, decl);
Expand Down Expand Up @@ -106,6 +116,11 @@ impl MutVisitor for Assigner {
mut_visit::walk_expr(self, expr);
}

fn visit_field_assign(&mut self, assign: &mut FieldAssign) {
self.assign(&mut assign.id);
mut_visit::walk_field_assign(self, assign);
}

fn visit_pat(&mut self, pat: &mut Pat) {
self.assign(&mut pat.id);
mut_visit::walk_pat(self, pat);
Expand Down
120 changes: 120 additions & 0 deletions compiler/qsc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,8 @@ pub enum ItemKind {
Open(Idents, Option<Box<Ident>>),
/// A `newtype` declaration.
Ty(Box<Ident>, Box<TyDef>),
/// A `struct` declaration.
Struct(Box<StructDecl>),
}

impl Display for ItemKind {
Expand All @@ -274,6 +276,7 @@ impl Display for ItemKind {
None => write!(f, "Open ({name})")?,
},
ItemKind::Ty(name, t) => write!(f, "New Type ({name}): {t}")?,
ItemKind::Struct(s) => write!(f, "{s}")?,
}
Ok(())
}
Expand Down Expand Up @@ -390,6 +393,70 @@ impl Display for TyDefKind {
}
}

/// A struct definition.
#[derive(Clone, Debug, PartialEq, Default)]
pub struct StructDecl {
/// The node ID.
pub id: NodeId,
/// The span.
pub span: Span,
/// The name of the struct.
pub name: Box<Ident>,
/// The type definition kind.
pub fields: Box<[Box<FieldDef>]>,
}

impl Display for StructDecl {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let mut indent = set_indentation(indented(f), 0);
write!(indent, "Struct {} {} ({}):", self.id, self.span, self.name)?;
if self.fields.is_empty() {
write!(indent, " <empty>")?;
} else {
indent = set_indentation(indent, 1);
for field in &*self.fields {
write!(indent, "\n{field}")?;
}
}
Ok(())
}
}

impl WithSpan for StructDecl {
fn with_span(self, span: Span) -> Self {
Self { span, ..self }
}
}

/// A struct field definition.
#[derive(Clone, Debug, PartialEq, Default)]
pub struct FieldDef {
/// The node ID.
pub id: NodeId,
/// The span.
pub span: Span,
/// The name of the field.
pub name: Box<Ident>,
/// The type of the field.
pub ty: Box<Ty>,
}

impl Display for FieldDef {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
"FieldDef {} {} ({}): {}",
self.id, self.span, self.name, self.ty
)
}
}

impl WithSpan for FieldDef {
fn with_span(self, span: Span) -> Self {
Self { span, ..self }
}
}

/// A callable declaration header.
#[derive(Clone, Debug, PartialEq)]
pub struct CallableDecl {
Expand Down Expand Up @@ -807,6 +874,8 @@ pub enum ExprKind {
Repeat(Box<Block>, Box<Expr>, Option<Box<Block>>),
/// A return: `return a`.
Return(Box<Expr>),
/// A struct constructor.
Struct(Box<Path>, Option<Box<Expr>>, Box<[Box<FieldAssign>]>),
/// A ternary operator.
TernOp(TernOp, Box<Expr>, Box<Expr>, Box<Expr>),
/// A tuple: `(a, b, c)`.
Expand Down Expand Up @@ -847,6 +916,7 @@ impl Display for ExprKind {
ExprKind::Range(start, step, end) => display_range(indent, start, step, end)?,
ExprKind::Repeat(repeat, until, fixup) => display_repeat(indent, repeat, until, fixup)?,
ExprKind::Return(e) => write!(indent, "Return: {e}")?,
ExprKind::Struct(name, copy, fields) => display_struct(indent, name, copy, fields)?,
ExprKind::TernOp(op, expr1, expr2, expr3) => {
display_tern_op(indent, *op, expr1, expr2, expr3)?;
}
Expand Down Expand Up @@ -1058,6 +1128,27 @@ fn display_repeat(
Ok(())
}

fn display_struct(
mut indent: Indented<Formatter>,
name: &Path,
copy: &Option<Box<Expr>>,
fields: &[Box<FieldAssign>],
) -> fmt::Result {
write!(indent, "Struct ({name}):")?;
if copy.is_none() && fields.is_empty() {
write!(indent, " <empty>")?;
return Ok(());
}
indent = set_indentation(indent, 1);
if let Some(copy) = copy {
write!(indent, "\nCopy: {copy}")?;
}
for field in fields {
write!(indent, "\n{field}")?;
}
Ok(())
}

fn display_tern_op(
mut indent: Indented<Formatter>,
op: TernOp,
Expand Down Expand Up @@ -1101,6 +1192,35 @@ fn display_while(mut indent: Indented<Formatter>, cond: &Expr, block: &Block) ->
Ok(())
}

/// A field assignment in a struct constructor expression.
#[derive(Clone, Debug, Default, PartialEq)]
pub struct FieldAssign {
/// The node ID.
pub id: NodeId,
/// The span.
pub span: Span,
/// The field to assign.
pub field: Box<Ident>,
/// The value to assign to the field.
pub value: Box<Expr>,
}

impl WithSpan for FieldAssign {
fn with_span(self, span: Span) -> Self {
Self { span, ..self }
}
}

impl Display for FieldAssign {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
"FieldsAssign {} {}: ({}) {}",
self.id, self.span, self.field, self.value
)
}
}

/// An interpolated string component.
#[derive(Clone, Debug, PartialEq)]
pub enum StringComponent {
Expand Down
44 changes: 40 additions & 4 deletions compiler/qsc_ast/src/mut_visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
// Licensed under the MIT License.

use crate::ast::{
Attr, Block, CallableBody, CallableDecl, Expr, ExprKind, FunctorExpr, FunctorExprKind, Ident,
Item, ItemKind, Namespace, Package, Pat, PatKind, Path, QubitInit, QubitInitKind, SpecBody,
SpecDecl, Stmt, StmtKind, StringComponent, TopLevelNode, Ty, TyDef, TyDefKind, TyKind,
Visibility,
Attr, Block, CallableBody, CallableDecl, Expr, ExprKind, FieldAssign, FieldDef, FunctorExpr,
FunctorExprKind, Ident, Item, ItemKind, Namespace, Package, Pat, PatKind, Path, QubitInit,
QubitInitKind, SpecBody, SpecDecl, Stmt, StmtKind, StringComponent, StructDecl, TopLevelNode,
Ty, TyDef, TyDefKind, TyKind, Visibility,
};
use qsc_data_structures::span::Span;

Expand Down Expand Up @@ -36,6 +36,14 @@ pub trait MutVisitor: Sized {
walk_callable_decl(self, decl);
}

fn visit_struct_decl(&mut self, decl: &mut StructDecl) {
walk_struct_decl(self, decl);
}

fn visit_field_def(&mut self, def: &mut FieldDef) {
walk_field_def(self, def);
}

fn visit_spec_decl(&mut self, decl: &mut SpecDecl) {
walk_spec_decl(self, decl);
}
Expand All @@ -60,6 +68,10 @@ pub trait MutVisitor: Sized {
walk_expr(self, expr);
}

fn visit_field_assign(&mut self, assign: &mut FieldAssign) {
walk_field_assign(self, assign);
}

fn visit_pat(&mut self, pat: &mut Pat) {
walk_pat(self, pat);
}
Expand Down Expand Up @@ -116,6 +128,7 @@ pub fn walk_item(vis: &mut impl MutVisitor, item: &mut Item) {
vis.visit_ident(ident);
vis.visit_ty_def(def);
}
ItemKind::Struct(decl) => vis.visit_struct_decl(decl),
}
}

Expand Down Expand Up @@ -155,6 +168,18 @@ pub fn walk_callable_decl(vis: &mut impl MutVisitor, decl: &mut CallableDecl) {
}
}

pub fn walk_struct_decl(vis: &mut impl MutVisitor, decl: &mut StructDecl) {
vis.visit_span(&mut decl.span);
vis.visit_ident(&mut decl.name);
decl.fields.iter_mut().for_each(|f| vis.visit_field_def(f));
}

pub fn walk_field_def(vis: &mut impl MutVisitor, def: &mut FieldDef) {
vis.visit_span(&mut def.span);
vis.visit_ident(&mut def.name);
vis.visit_ty(&mut def.ty);
}

pub fn walk_spec_decl(vis: &mut impl MutVisitor, decl: &mut SpecDecl) {
vis.visit_span(&mut decl.span);

Expand Down Expand Up @@ -296,6 +321,11 @@ pub fn walk_expr(vis: &mut impl MutVisitor, expr: &mut Expr) {
vis.visit_expr(until);
fixup.iter_mut().for_each(|f| vis.visit_block(f));
}
ExprKind::Struct(name, copy, fields) => {
vis.visit_path(name);
copy.iter_mut().for_each(|c| vis.visit_expr(c));
fields.iter_mut().for_each(|f| vis.visit_field_assign(f));
}
ExprKind::TernOp(_, e1, e2, e3) => {
vis.visit_expr(e1);
vis.visit_expr(e2);
Expand All @@ -310,6 +340,12 @@ pub fn walk_expr(vis: &mut impl MutVisitor, expr: &mut Expr) {
}
}

pub fn walk_field_assign(vis: &mut impl MutVisitor, assign: &mut FieldAssign) {
vis.visit_span(&mut assign.span);
vis.visit_ident(&mut assign.field);
vis.visit_expr(&mut assign.value);
}

pub fn walk_pat(vis: &mut impl MutVisitor, pat: &mut Pat) {
vis.visit_span(&mut pat.span);

Expand Down
19 changes: 17 additions & 2 deletions compiler/qsc_ast/src/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

use crate::{
ast::{
Attr, Block, CallableDecl, Expr, FunctorExpr, Ident, Item, Namespace, NodeId, Package, Pat,
Path, QubitInit, SpecDecl, Stmt, Ty, TyDef, Visibility,
Attr, Block, CallableDecl, Expr, FieldAssign, FunctorExpr, Ident, Item, Namespace, NodeId,
Package, Pat, Path, QubitInit, SpecDecl, Stmt, Ty, TyDef, Visibility,
},
visit::{self, Visitor},
};
Expand Down Expand Up @@ -63,6 +63,16 @@ impl Visitor<'_> for Validator {
visit::walk_callable_decl(self, decl);
}

fn visit_struct_decl(&mut self, decl: &'_ crate::ast::StructDecl) {
self.check(decl.id, decl);
visit::walk_struct_decl(self, decl);
}

fn visit_field_def(&mut self, def: &'_ crate::ast::FieldDef) {
self.check(def.id, def);
visit::walk_field_def(self, def);
}

fn visit_spec_decl(&mut self, decl: &SpecDecl) {
self.check(decl.id, decl);
visit::walk_spec_decl(self, decl);
Expand Down Expand Up @@ -93,6 +103,11 @@ impl Visitor<'_> for Validator {
visit::walk_expr(self, expr);
}

fn visit_field_assign(&mut self, assign: &FieldAssign) {
self.check(assign.id, assign);
visit::walk_field_assign(self, assign);
}

fn visit_pat(&mut self, pat: &Pat) {
self.check(pat.id, pat);
visit::walk_pat(self, pat);
Expand Down
Loading

0 comments on commit eaa558a

Please sign in to comment.