use std::fmt;
use smallvec::SmallVec;
use super::expr::Expr;
pub(crate) type Block<'a> = Vec<Stmt<'a>>;
#[derive(Clone, Debug)]
pub(crate) struct Stmt<'a> {
pub line: usize,
pub kind: StmtKind<'a>,
}
#[derive(Clone, Debug)]
pub(crate) enum StmtKind<'a> {
Version(&'a str),
Include,
RegisterDecl {
kind: RegisterKind,
name: &'a str,
size: Option<Expr<'a>>,
},
InputDecl {
ty: &'a str,
name: &'a str,
},
OutputDecl {
ty: &'a str,
name: &'a str,
size: Option<Expr<'a>>,
},
ClassicalDecl {
constant: bool,
ty: &'a str,
width: Option<Expr<'a>>,
name: &'a str,
value: Option<Expr<'a>>,
},
Assign {
target: &'a str,
op: Option<AssignOp>,
value: Expr<'a>,
},
Alias {
name: &'a str,
sources: Vec<Operand<'a>>,
},
Call {
modifiers: Vec<Modifier<'a>>,
name: &'a str,
params: SmallVec<[Argument<'a>; 1]>,
operands: SmallVec<[Operand<'a>; 2]>,
},
Measure(Box<Measure<'a>>),
Empty,
Reset {
targets: Vec<Operand<'a>>,
},
Barrier {
targets: Vec<Operand<'a>>,
},
Delay {
duration: Expr<'a>,
targets: Vec<Operand<'a>>,
},
If(Box<Conditional<'a>>),
For {
variable: &'a str,
range: ForRange<'a>,
body: Block<'a>,
},
Switch {
operand: Operand<'a>,
arms: Vec<SwitchArm<'a>>,
},
While {
condition: Condition<'a>,
body: Block<'a>,
},
Break,
Continue,
GateDef {
name: &'a str,
params: Vec<&'a str>,
qubits: Vec<&'a str>,
body: Block<'a>,
},
DefDef {
name: &'a str,
args: Vec<DefParam<'a>>,
result: Option<BitResult<'a>>,
body: Block<'a>,
},
Return(Option<ReturnValue<'a>>),
ArrayDecl(Box<ArrayDecl<'a>>),
ElementAssign(Box<ElementAssign<'a>>),
CallAssign(Box<CallAssign<'a>>),
Box {
duration: Option<Expr<'a>>,
body: Block<'a>,
},
Pragma(&'a str),
}
#[derive(Clone, Debug)]
pub(crate) struct ArrayDecl<'a> {
pub constant: bool,
pub ty: &'a str,
pub dims: Vec<Expr<'a>>,
pub name: &'a str,
pub init: Option<ArrayInit<'a>>,
}
#[derive(Clone, Debug)]
pub(crate) enum ArrayInit<'a> {
Value(Expr<'a>),
List(Vec<ArrayInit<'a>>),
}
#[derive(Clone, Debug)]
pub(crate) struct ElementAssign<'a> {
pub array: &'a str,
pub indices: Vec<Expr<'a>>,
pub op: Option<AssignOp>,
pub value: Expr<'a>,
}
#[derive(Clone, Debug)]
pub(crate) struct CallAssign<'a> {
pub target: Operand<'a>,
pub name: &'a str,
pub args: SmallVec<[Argument<'a>; 1]>,
}
#[derive(Clone, Debug)]
pub(crate) struct BitResult<'a> {
pub width: Option<Expr<'a>>,
}
#[derive(Clone, Debug)]
pub(crate) enum ReturnValue<'a> {
Measure(Operand<'a>),
Value(Expr<'a>),
}
#[derive(Clone, Debug)]
pub(crate) struct Measure<'a> {
pub source: Operand<'a>,
pub target: Operand<'a>,
}
#[derive(Clone, Debug)]
pub(crate) struct Conditional<'a> {
pub condition: Condition<'a>,
pub then_body: Block<'a>,
pub else_body: Option<Block<'a>>,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub(crate) enum RegisterKind {
Qubit,
Classical,
}
impl RegisterKind {
pub(crate) fn name(self) -> &'static str {
match self {
RegisterKind::Qubit => "qubit",
RegisterKind::Classical => "bit",
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub(crate) enum AssignOp {
Add,
Sub,
Mul,
Div,
Rem,
}
#[derive(Clone, Debug)]
pub(crate) enum Modifier<'a> {
Inv,
Pow(Expr<'a>),
Ctrl {
negated: bool,
},
}
#[derive(Clone, Debug)]
pub(crate) enum Argument<'a> {
Operand(Operand<'a>),
Value(Expr<'a>),
}
#[derive(Clone, Debug)]
pub(crate) struct Operand<'a> {
pub name: OperandName<'a>,
pub index: Option<Index<'a>>,
pub line: usize,
}
impl<'a> Operand<'a> {
pub(crate) fn describe(&self) -> String {
let base = match self.name {
OperandName::Register(name) => name.to_string(),
OperandName::Physical(index) => format!("${index}"),
};
match &self.index {
Some(index) => format!("{base}[{index}]"),
None => base,
}
}
pub(crate) fn register(&self) -> Option<&'a str> {
match self.name {
OperandName::Register(name) => Some(name),
OperandName::Physical(_) => None,
}
}
}
#[derive(Clone, Copy, Debug)]
pub(crate) enum OperandName<'a> {
Register(&'a str),
Physical(usize),
}
#[derive(Clone, Debug)]
pub(crate) enum Index<'a> {
Single(Expr<'a>),
Range(Box<Range<'a>>),
Set(Vec<Expr<'a>>),
}
impl fmt::Display for Index<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Index::Single(expr) => write!(f, "{expr}"),
Index::Set(entries) => {
f.write_str("{")?;
for (at, entry) in entries.iter().enumerate() {
if at > 0 {
f.write_str(", ")?;
}
write!(f, "{entry}")?;
}
f.write_str("}")
}
Index::Range(range) => {
if let Some(start) = &range.start {
write!(f, "{start}")?;
}
f.write_str(":")?;
if let Some(step) = &range.step {
write!(f, "{step}:")?;
}
match &range.stop {
Some(stop) => write!(f, "{stop}"),
None => Ok(()),
}
}
}
}
}
#[derive(Clone, Debug)]
pub(crate) struct Range<'a> {
pub start: Option<Expr<'a>>,
pub step: Option<Expr<'a>>,
pub stop: Option<Expr<'a>>,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub(crate) enum CmpOp {
Equal,
NotEqual,
}
#[derive(Clone, Debug)]
pub(crate) enum Condition<'a> {
Truthy(Operand<'a>),
Negated(Operand<'a>),
Compare {
lhs: Operand<'a>,
op: CmpOp,
rhs: Expr<'a>,
},
Parity {
bits: Vec<Operand<'a>>,
compare: Option<(CmpOp, Expr<'a>)>,
},
Expr(Expr<'a>),
}
#[derive(Clone, Debug)]
pub(crate) enum ForRange<'a> {
Range {
start: Expr<'a>,
step: Option<Expr<'a>>,
stop: Expr<'a>,
},
Set(Vec<Expr<'a>>),
}
#[derive(Clone, Debug)]
pub(crate) struct SwitchArm<'a> {
pub labels: Option<Vec<Expr<'a>>>,
pub body: Block<'a>,
pub line: usize,
}
#[derive(Clone, Debug)]
pub(crate) enum DefParam<'a> {
Qubit(&'a str),
Value {
name: &'a str,
integral: bool,
},
Duration(&'a str),
Bit {
name: &'a str,
width: Option<Expr<'a>>,
},
}