use super::ast::{
BinaryOp, ClassicalRef, ComparisonOp, Condition, Declaration, Expression, ForLoop,
GateDefinition, Literal, Measurement, QasmGate, QasmProgram, QasmRegister, QasmStatement,
QubitRef, UnaryOp,
};
use std::collections::HashMap;
use std::str::FromStr;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum ParseError {
#[error("Unexpected token: {0}")]
UnexpectedToken(String),
#[error("Expected {expected}, found {found}")]
ExpectedToken { expected: String, found: String },
#[error("Invalid syntax: {0}")]
InvalidSyntax(String),
#[error("Undefined identifier: {0}")]
UndefinedIdentifier(String),
#[error("Type mismatch: {0}")]
TypeMismatch(String),
#[error("Invalid number: {0}")]
InvalidNumber(String),
#[error("Unexpected end of input")]
UnexpectedEof,
#[error("Version mismatch: expected 3.0, found {0}")]
VersionMismatch(String),
}
#[derive(Debug, Clone, PartialEq)]
enum Token {
OpenQasm,
Include,
Qubit,
Bit,
Gate,
Measure,
Reset,
Barrier,
If,
Else,
For,
While,
In,
Const,
Def,
Return,
Delay,
Ctrl,
Inv,
Pow,
Identifier(String),
Integer(i64),
Float(f64),
String(String),
Plus,
Minus,
Star,
Slash,
Percent,
Power,
Assign,
Eq,
Ne,
Lt,
Le,
Gt,
Ge,
And,
Or,
Not,
BitAnd,
BitOr,
BitXor,
BitNot,
Shl,
Shr,
Arrow,
LeftParen,
RightParen,
LeftBracket,
RightBracket,
LeftBrace,
RightBrace,
Semicolon,
Comma,
Colon,
Dot,
Eof,
}
struct Lexer<'a> {
input: &'a str,
position: usize,
current: Option<char>,
}
impl<'a> Lexer<'a> {
fn new(input: &'a str) -> Self {
let mut lexer = Lexer {
input,
position: 0,
current: None,
};
lexer.advance();
lexer
}
fn advance(&mut self) {
self.current = self.input.chars().nth(self.position);
if self.current.is_some() {
self.position += 1;
}
}
fn peek(&self) -> Option<char> {
self.input.chars().nth(self.position)
}
fn skip_whitespace(&mut self) {
while let Some(ch) = self.current {
if ch.is_whitespace() {
self.advance();
} else if ch == '/' && self.peek() == Some('/') {
while self.current.is_some() && self.current != Some('\n') {
self.advance();
}
} else if ch == '/' && self.peek() == Some('*') {
self.advance(); self.advance(); while self.current.is_some() {
if self.current == Some('*') && self.peek() == Some('/') {
self.advance(); self.advance(); break;
}
self.advance();
}
} else {
break;
}
}
}
fn read_identifier(&mut self) -> String {
let mut result = String::new();
while let Some(ch) = self.current {
if ch.is_alphanumeric() || ch == '_' {
result.push(ch);
self.advance();
} else {
break;
}
}
result
}
fn read_number(&mut self) -> Result<Token, ParseError> {
let mut result = String::new();
let mut has_dot = false;
while let Some(ch) = self.current {
if ch.is_numeric() {
result.push(ch);
self.advance();
} else if ch == '.' && !has_dot && self.peek().is_some_and(char::is_numeric) {
has_dot = true;
result.push(ch);
self.advance();
} else if ch == 'e' || ch == 'E' {
result.push(ch);
self.advance();
if let Some(sign_ch) = self.current {
if sign_ch == '+' || sign_ch == '-' {
result.push(sign_ch);
self.advance();
}
}
} else {
break;
}
}
if has_dot || result.contains('e') || result.contains('E') {
result
.parse::<f64>()
.map(Token::Float)
.map_err(|_| ParseError::InvalidNumber(result))
} else {
result
.parse::<i64>()
.map(Token::Integer)
.map_err(|_| ParseError::InvalidNumber(result))
}
}
fn read_string(&mut self) -> Result<String, ParseError> {
let mut result = String::new();
self.advance();
while let Some(ch) = self.current {
if ch == '"' {
self.advance(); return Ok(result);
} else if ch == '\\' {
self.advance();
match self.current {
Some('n') => result.push('\n'),
Some('t') => result.push('\t'),
Some('r') => result.push('\r'),
Some('\\') => result.push('\\'),
Some('"') => result.push('"'),
_ => return Err(ParseError::InvalidSyntax("Invalid escape sequence".into())),
}
self.advance();
} else {
result.push(ch);
self.advance();
}
}
Err(ParseError::UnexpectedEof)
}
fn next_token(&mut self) -> Result<Token, ParseError> {
self.skip_whitespace();
match self.current {
None => Ok(Token::Eof),
Some(ch) => match ch {
'+' => {
self.advance();
Ok(Token::Plus)
}
'*' => {
self.advance();
if self.current == Some('*') {
self.advance();
Ok(Token::Power)
} else {
Ok(Token::Star)
}
}
'/' => {
self.advance();
Ok(Token::Slash)
}
'%' => {
self.advance();
Ok(Token::Percent)
}
'(' => {
self.advance();
Ok(Token::LeftParen)
}
')' => {
self.advance();
Ok(Token::RightParen)
}
'[' => {
self.advance();
Ok(Token::LeftBracket)
}
']' => {
self.advance();
Ok(Token::RightBracket)
}
'{' => {
self.advance();
Ok(Token::LeftBrace)
}
'}' => {
self.advance();
Ok(Token::RightBrace)
}
';' => {
self.advance();
Ok(Token::Semicolon)
}
',' => {
self.advance();
Ok(Token::Comma)
}
':' => {
self.advance();
Ok(Token::Colon)
}
'.' => {
self.advance();
Ok(Token::Dot)
}
'~' => {
self.advance();
Ok(Token::BitNot)
}
'"' => self.read_string().map(Token::String),
'-' => {
self.advance();
if self.current == Some('>') {
self.advance();
Ok(Token::Arrow)
} else {
Ok(Token::Minus)
}
}
'=' => {
self.advance();
if self.current == Some('=') {
self.advance();
Ok(Token::Eq)
} else {
Ok(Token::Assign)
}
}
'!' => {
self.advance();
if self.current == Some('=') {
self.advance();
Ok(Token::Ne)
} else {
Ok(Token::Not)
}
}
'<' => {
self.advance();
match self.current {
Some('=') => {
self.advance();
Ok(Token::Le)
}
Some('<') => {
self.advance();
Ok(Token::Shl)
}
_ => Ok(Token::Lt),
}
}
'>' => {
self.advance();
match self.current {
Some('=') => {
self.advance();
Ok(Token::Ge)
}
Some('>') => {
self.advance();
Ok(Token::Shr)
}
_ => Ok(Token::Gt),
}
}
'&' => {
self.advance();
if self.current == Some('&') {
self.advance();
Ok(Token::And)
} else {
Ok(Token::BitAnd)
}
}
'|' => {
self.advance();
if self.current == Some('|') {
self.advance();
Ok(Token::Or)
} else {
Ok(Token::BitOr)
}
}
'^' => {
self.advance();
Ok(Token::BitXor)
}
_ if ch.is_alphabetic() || ch == '_' => {
let ident = self.read_identifier();
Ok(match ident.as_str() {
"OPENQASM" => Token::OpenQasm,
"include" => Token::Include,
"qubit" => Token::Qubit,
"bit" => Token::Bit,
"gate" => Token::Gate,
"measure" => Token::Measure,
"reset" => Token::Reset,
"barrier" => Token::Barrier,
"if" => Token::If,
"else" => Token::Else,
"for" => Token::For,
"while" => Token::While,
"in" => Token::In,
"const" => Token::Const,
"def" => Token::Def,
"return" => Token::Return,
"delay" => Token::Delay,
"ctrl" => Token::Ctrl,
"inv" => Token::Inv,
"pow" => Token::Pow,
"pi" => Token::Identifier("pi".into()),
"e" => Token::Identifier("e".into()),
"tau" => Token::Identifier("tau".into()),
_ => Token::Identifier(ident),
})
}
_ if ch.is_numeric() => self.read_number(),
_ => Err(ParseError::UnexpectedToken(ch.to_string())),
},
}
}
}
pub struct QasmParser<'a> {
lexer: Lexer<'a>,
current_token: Token,
symbols: HashMap<String, SymbolType>,
}
#[derive(Debug, Clone)]
enum SymbolType {
QuantumRegister(usize),
ClassicalRegister(usize),
Gate(Vec<String>, Vec<String>), Constant(f64),
Variable,
}
fn eval_const_binary(op: BinaryOp, l: f64, r: f64) -> f64 {
let bool_to_f64 = |b: bool| if b { 1.0 } else { 0.0 };
match op {
BinaryOp::Add => l + r,
BinaryOp::Sub => l - r,
BinaryOp::Mul => l * r,
BinaryOp::Div => l / r,
BinaryOp::Mod => l % r,
BinaryOp::Pow => l.powf(r),
BinaryOp::Eq => bool_to_f64((l - r).abs() < f64::EPSILON),
BinaryOp::Ne => bool_to_f64((l - r).abs() >= f64::EPSILON),
BinaryOp::Lt => bool_to_f64(l < r),
BinaryOp::Le => bool_to_f64(l <= r),
BinaryOp::Gt => bool_to_f64(l > r),
BinaryOp::Ge => bool_to_f64(l >= r),
BinaryOp::And => bool_to_f64(l != 0.0 && r != 0.0),
BinaryOp::Or => bool_to_f64(l != 0.0 || r != 0.0),
BinaryOp::Xor => bool_to_f64((l != 0.0) ^ (r != 0.0)),
BinaryOp::BitAnd => ((l as i64) & (r as i64)) as f64,
BinaryOp::BitOr => ((l as i64) | (r as i64)) as f64,
BinaryOp::BitXor => ((l as i64) ^ (r as i64)) as f64,
BinaryOp::Shl => ((l as i64) << (r as i64)) as f64,
BinaryOp::Shr => ((l as i64) >> (r as i64)) as f64,
}
}
fn eval_const_unary(op: UnaryOp, v: f64) -> f64 {
match op {
UnaryOp::Neg => -v,
UnaryOp::Not => {
if v == 0.0 {
1.0
} else {
0.0
}
}
UnaryOp::BitNot => (!(v as i64)) as f64,
UnaryOp::Sin => v.sin(),
UnaryOp::Cos => v.cos(),
UnaryOp::Tan => v.tan(),
UnaryOp::Asin => v.asin(),
UnaryOp::Acos => v.acos(),
UnaryOp::Atan => v.atan(),
UnaryOp::Exp => v.exp(),
UnaryOp::Ln => v.ln(),
UnaryOp::Sqrt => v.sqrt(),
}
}
fn expr_contains_function_or_index(expr: &Expression) -> bool {
match expr {
Expression::Literal(_) | Expression::Variable(_) => false,
Expression::Binary(_, lhs, rhs) => {
expr_contains_function_or_index(lhs) || expr_contains_function_or_index(rhs)
}
Expression::Unary(_, inner) => expr_contains_function_or_index(inner),
Expression::Function(_, _) | Expression::Index(_, _) => true,
}
}
fn eval_const_function(name: &str, args: &[f64]) -> Result<f64, ParseError> {
let arity_err = |expected: usize| {
ParseError::InvalidSyntax(format!(
"function '{name}' expects {expected} argument(s), got {}",
args.len()
))
};
match name {
"sin" | "cos" | "tan" | "asin" | "acos" | "atan" | "exp" | "ln" | "log2" | "log10"
| "sqrt" | "abs" | "floor" | "ceil" | "round" => {
if args.len() != 1 {
return Err(arity_err(1));
}
let v = args[0];
Ok(match name {
"sin" => v.sin(),
"cos" => v.cos(),
"tan" => v.tan(),
"asin" => v.asin(),
"acos" => v.acos(),
"atan" => v.atan(),
"exp" => v.exp(),
"ln" => v.ln(),
"log2" => v.log2(),
"log10" => v.log10(),
"sqrt" => v.sqrt(),
"abs" => v.abs(),
"floor" => v.floor(),
"ceil" => v.ceil(),
"round" => v.round(),
_ => unreachable!(),
})
}
"pow" | "atan2" | "min" | "max" => {
if args.len() != 2 {
return Err(arity_err(2));
}
let (a, b) = (args[0], args[1]);
Ok(match name {
"pow" => a.powf(b),
"atan2" => a.atan2(b),
"min" => a.min(b),
"max" => a.max(b),
_ => unreachable!(),
})
}
_ => Err(ParseError::InvalidSyntax(format!(
"unknown function '{name}' in constant expression"
))),
}
}
impl<'a> QasmParser<'a> {
pub fn new(input: &'a str) -> Result<Self, ParseError> {
let mut lexer = Lexer::new(input);
let current_token = lexer.next_token()?;
Ok(QasmParser {
lexer,
current_token,
symbols: HashMap::new(),
})
}
pub fn parse_program(&mut self) -> Result<QasmProgram, ParseError> {
self.expect_token(&Token::OpenQasm)?;
let version = self.parse_version()?;
self.expect_token(&Token::Semicolon)?;
let mut includes = Vec::new();
while self.current_token == Token::Include {
includes.push(self.parse_include()?);
}
let mut declarations = Vec::new();
let mut statements = Vec::new();
while self.current_token != Token::Eof {
match &self.current_token {
Token::Qubit => declarations.push(self.parse_quantum_register()?),
Token::Bit => declarations.push(self.parse_classical_register()?),
Token::Gate => declarations.push(self.parse_gate_definition()?),
Token::Const => declarations.push(self.parse_constant()?),
_ => statements.push(self.parse_statement()?),
}
}
Ok(QasmProgram {
version,
includes,
declarations,
statements,
})
}
fn advance(&mut self) -> Result<(), ParseError> {
self.current_token = self.lexer.next_token()?;
Ok(())
}
fn expect_token(&mut self, expected: &Token) -> Result<(), ParseError> {
if std::mem::discriminant(&self.current_token) == std::mem::discriminant(expected) {
self.advance()
} else {
Err(ParseError::ExpectedToken {
expected: format!("{expected:?}"),
found: format!("{:?}", self.current_token),
})
}
}
fn parse_version(&mut self) -> Result<String, ParseError> {
match &self.current_token {
Token::Float(v) => {
let version = if *v == 3.0 {
"3.0".to_string()
} else {
format!("{v}")
};
if !version.starts_with("3.") {
return Err(ParseError::VersionMismatch(version));
}
self.advance()?;
Ok(version)
}
Token::Integer(v) if *v == 3 => {
self.advance()?;
if self.current_token == Token::Dot {
self.advance()?;
if let Token::Integer(minor) = self.current_token.clone() {
let minor_val = minor;
self.advance()?;
Ok(format!("3.{minor_val}"))
} else {
Ok("3.0".to_string())
}
} else {
Ok("3.0".to_string())
}
}
_ => Err(ParseError::ExpectedToken {
expected: "version number".into(),
found: format!("{:?}", self.current_token),
}),
}
}
fn parse_include(&mut self) -> Result<String, ParseError> {
self.expect_token(&Token::Include)?;
match &self.current_token {
Token::String(s) => {
let include = s.clone();
self.advance()?;
self.expect_token(&Token::Semicolon)?;
Ok(include)
}
_ => Err(ParseError::ExpectedToken {
expected: "string".into(),
found: format!("{:?}", self.current_token),
}),
}
}
fn parse_quantum_register(&mut self) -> Result<Declaration, ParseError> {
self.expect_token(&Token::Qubit)?;
let (size, name) = if self.current_token == Token::LeftBracket {
self.advance()?;
let size = match &self.current_token {
Token::Integer(n) => {
let size = *n as usize;
self.advance()?;
size
}
Token::Identifier(const_name) => {
let const_name = const_name.clone();
self.advance()?;
self.resolve_register_size(&const_name)?
}
_ => {
return Err(ParseError::ExpectedToken {
expected: "integer or identifier".into(),
found: format!("{:?}", self.current_token),
})
}
};
self.expect_token(&Token::RightBracket)?;
let name = match &self.current_token {
Token::Identifier(s) => s.clone(),
_ => {
return Err(ParseError::ExpectedToken {
expected: "identifier".into(),
found: format!("{:?}", self.current_token),
})
}
};
self.advance()?;
(size, name)
} else {
let name = match &self.current_token {
Token::Identifier(s) => s.clone(),
_ => {
return Err(ParseError::ExpectedToken {
expected: "identifier".into(),
found: format!("{:?}", self.current_token),
})
}
};
self.advance()?;
(1, name)
};
self.expect_token(&Token::Semicolon)?;
self.symbols
.insert(name.clone(), SymbolType::QuantumRegister(size));
Ok(Declaration::QuantumRegister(QasmRegister { name, size }))
}
fn parse_classical_register(&mut self) -> Result<Declaration, ParseError> {
self.expect_token(&Token::Bit)?;
let (size, name) = if self.current_token == Token::LeftBracket {
self.advance()?;
let size = match &self.current_token {
Token::Integer(n) => {
let size = *n as usize;
self.advance()?;
size
}
Token::Identifier(const_name) => {
let const_name = const_name.clone();
self.advance()?;
self.resolve_register_size(&const_name)?
}
_ => {
return Err(ParseError::ExpectedToken {
expected: "integer or identifier".into(),
found: format!("{:?}", self.current_token),
})
}
};
self.expect_token(&Token::RightBracket)?;
let name = match &self.current_token {
Token::Identifier(s) => s.clone(),
_ => {
return Err(ParseError::ExpectedToken {
expected: "identifier".into(),
found: format!("{:?}", self.current_token),
})
}
};
self.advance()?;
(size, name)
} else {
let name = match &self.current_token {
Token::Identifier(s) => s.clone(),
_ => {
return Err(ParseError::ExpectedToken {
expected: "identifier".into(),
found: format!("{:?}", self.current_token),
})
}
};
self.advance()?;
(1, name)
};
self.expect_token(&Token::Semicolon)?;
self.symbols
.insert(name.clone(), SymbolType::ClassicalRegister(size));
Ok(Declaration::ClassicalRegister(QasmRegister { name, size }))
}
fn parse_gate_definition(&mut self) -> Result<Declaration, ParseError> {
self.expect_token(&Token::Gate)?;
let name = match &self.current_token {
Token::Identifier(s) => s.clone(),
_ => {
return Err(ParseError::ExpectedToken {
expected: "identifier".into(),
found: format!("{:?}", self.current_token),
})
}
};
self.advance()?;
let mut params = Vec::new();
if self.current_token == Token::LeftParen {
self.advance()?;
while self.current_token != Token::RightParen {
match &self.current_token {
Token::Identifier(s) => {
params.push(s.clone());
self.advance()?;
}
_ => {
return Err(ParseError::ExpectedToken {
expected: "identifier".into(),
found: format!("{:?}", self.current_token),
})
}
}
if self.current_token == Token::Comma {
self.advance()?;
}
}
self.expect_token(&Token::RightParen)?;
}
let mut qubits = Vec::new();
while self.current_token != Token::LeftBrace {
match &self.current_token {
Token::Identifier(s) => {
qubits.push(s.clone());
self.advance()?;
}
_ => {
return Err(ParseError::ExpectedToken {
expected: "identifier".into(),
found: format!("{:?}", self.current_token),
})
}
}
if self.current_token == Token::Comma {
self.advance()?;
}
}
self.expect_token(&Token::LeftBrace)?;
let mut body = Vec::new();
while self.current_token != Token::RightBrace {
body.push(self.parse_statement()?);
}
self.expect_token(&Token::RightBrace)?;
self.symbols.insert(
name.clone(),
SymbolType::Gate(params.clone(), qubits.clone()),
);
Ok(Declaration::GateDefinition(GateDefinition {
name,
params,
qubits,
body,
}))
}
fn parse_constant(&mut self) -> Result<Declaration, ParseError> {
self.expect_token(&Token::Const)?;
let name = match &self.current_token {
Token::Identifier(s) => s.clone(),
_ => {
return Err(ParseError::ExpectedToken {
expected: "identifier".into(),
found: format!("{:?}", self.current_token),
})
}
};
self.advance()?;
self.expect_token(&Token::Assign)?;
let expr = self.parse_expression()?;
self.expect_token(&Token::Semicolon)?;
match self.eval_const_expr(&expr) {
Ok(value) => {
self.symbols
.insert(name.clone(), SymbolType::Constant(value));
}
Err(err) => {
if expr_contains_function_or_index(&expr) {
self.symbols.insert(name.clone(), SymbolType::Variable);
} else {
return Err(err);
}
}
}
Ok(Declaration::Constant(name, expr))
}
fn resolve_register_size(&self, name: &str) -> Result<usize, ParseError> {
match self.symbols.get(name) {
Some(SymbolType::Constant(value)) => {
let value = *value;
if !value.is_finite() || value < 0.0 || value.fract() != 0.0 {
return Err(ParseError::TypeMismatch(format!(
"constant '{name}' = {value} is not a valid register size (expected a non-negative integer)"
)));
}
Ok(value as usize)
}
Some(_) => Err(ParseError::TypeMismatch(format!(
"'{name}' is not a constant and cannot be used as a register size"
))),
None => Err(ParseError::UndefinedIdentifier(name.to_string())),
}
}
fn eval_const_expr(&self, expr: &Expression) -> Result<f64, ParseError> {
match expr {
Expression::Literal(lit) => match lit {
Literal::Integer(n) => Ok(*n as f64),
Literal::Float(x) => Ok(*x),
Literal::Bool(b) => Ok(if *b { 1.0 } else { 0.0 }),
Literal::Pi => Ok(std::f64::consts::PI),
Literal::Tau => Ok(std::f64::consts::TAU),
Literal::Euler => Ok(std::f64::consts::E),
Literal::String(s) => Err(ParseError::TypeMismatch(format!(
"string literal \"{s}\" is not a numeric constant"
))),
},
Expression::Variable(name) => match self.symbols.get(name) {
Some(SymbolType::Constant(value)) => Ok(*value),
Some(_) => Err(ParseError::TypeMismatch(format!(
"'{name}' is not a constant and cannot appear in a constant expression"
))),
None => Err(ParseError::UndefinedIdentifier(name.clone())),
},
Expression::Binary(op, lhs, rhs) => {
let l = self.eval_const_expr(lhs)?;
let r = self.eval_const_expr(rhs)?;
Ok(eval_const_binary(*op, l, r))
}
Expression::Unary(op, inner) => {
let v = self.eval_const_expr(inner)?;
Ok(eval_const_unary(*op, v))
}
Expression::Function(name, args) => {
let values: Result<Vec<f64>, ParseError> =
args.iter().map(|a| self.eval_const_expr(a)).collect();
eval_const_function(name, &values?)
}
Expression::Index(name, _) => Err(ParseError::TypeMismatch(format!(
"array index into '{name}' is not a constant expression"
))),
}
}
fn parse_statement(&mut self) -> Result<QasmStatement, ParseError> {
match &self.current_token {
Token::Measure => self.parse_measure(),
Token::Reset => self.parse_reset(),
Token::Barrier => self.parse_barrier(),
Token::If => self.parse_if(),
Token::For => self.parse_for(),
Token::While => self.parse_while(),
Token::Delay => self.parse_delay(),
Token::Identifier(_) => {
self.parse_identifier_statement()
}
Token::Ctrl | Token::Inv | Token::Pow => self.parse_modified_gate(),
_ => Err(ParseError::UnexpectedToken(format!(
"{:?}",
self.current_token
))),
}
}
fn parse_measure(&mut self) -> Result<QasmStatement, ParseError> {
self.expect_token(&Token::Measure)?;
let mut qubits = Vec::new();
let mut targets = Vec::new();
qubits.push(self.parse_qubit_ref()?);
self.expect_token(&Token::Arrow)?;
targets.push(self.parse_classical_ref()?);
while self.current_token == Token::Comma {
self.advance()?;
qubits.push(self.parse_qubit_ref()?);
self.expect_token(&Token::Arrow)?;
targets.push(self.parse_classical_ref()?);
}
self.expect_token(&Token::Semicolon)?;
Ok(QasmStatement::Measure(Measurement { qubits, targets }))
}
fn parse_reset(&mut self) -> Result<QasmStatement, ParseError> {
self.expect_token(&Token::Reset)?;
let mut qubits = Vec::new();
qubits.push(self.parse_qubit_ref()?);
while self.current_token == Token::Comma {
self.advance()?;
qubits.push(self.parse_qubit_ref()?);
}
self.expect_token(&Token::Semicolon)?;
Ok(QasmStatement::Reset(qubits))
}
fn parse_barrier(&mut self) -> Result<QasmStatement, ParseError> {
self.expect_token(&Token::Barrier)?;
let mut qubits = Vec::new();
if self.current_token != Token::Semicolon {
qubits.push(self.parse_qubit_ref()?);
while self.current_token == Token::Comma {
self.advance()?;
qubits.push(self.parse_qubit_ref()?);
}
}
self.expect_token(&Token::Semicolon)?;
Ok(QasmStatement::Barrier(qubits))
}
fn parse_if(&mut self) -> Result<QasmStatement, ParseError> {
self.expect_token(&Token::If)?;
self.expect_token(&Token::LeftParen)?;
let condition = self.parse_condition()?;
self.expect_token(&Token::RightParen)?;
let statement = Box::new(self.parse_statement()?);
Ok(QasmStatement::If(condition, statement))
}
fn parse_for(&mut self) -> Result<QasmStatement, ParseError> {
self.expect_token(&Token::For)?;
let variable = match &self.current_token {
Token::Identifier(s) => s.clone(),
_ => {
return Err(ParseError::ExpectedToken {
expected: "identifier".into(),
found: format!("{:?}", self.current_token),
})
}
};
self.advance()?;
self.expect_token(&Token::In)?;
self.expect_token(&Token::LeftBracket)?;
let start = self.parse_expression()?;
self.expect_token(&Token::Colon)?;
let end = self.parse_expression()?;
let step = if self.current_token == Token::Colon {
self.advance()?;
Some(self.parse_expression()?)
} else {
None
};
self.expect_token(&Token::RightBracket)?;
self.expect_token(&Token::LeftBrace)?;
let mut body = Vec::new();
while self.current_token != Token::RightBrace {
body.push(self.parse_statement()?);
}
self.expect_token(&Token::RightBrace)?;
Ok(QasmStatement::For(ForLoop {
variable,
start,
end,
step,
body,
}))
}
fn parse_while(&mut self) -> Result<QasmStatement, ParseError> {
self.expect_token(&Token::While)?;
self.expect_token(&Token::LeftParen)?;
let condition = self.parse_condition()?;
self.expect_token(&Token::RightParen)?;
self.expect_token(&Token::LeftBrace)?;
let mut body = Vec::new();
while self.current_token != Token::RightBrace {
body.push(self.parse_statement()?);
}
self.expect_token(&Token::RightBrace)?;
Ok(QasmStatement::While(condition, body))
}
fn parse_delay(&mut self) -> Result<QasmStatement, ParseError> {
self.expect_token(&Token::Delay)?;
self.expect_token(&Token::LeftBracket)?;
let duration = self.parse_expression()?;
self.expect_token(&Token::RightBracket)?;
let mut qubits = Vec::new();
if self.current_token != Token::Semicolon {
qubits.push(self.parse_qubit_ref()?);
while self.current_token == Token::Comma {
self.advance()?;
qubits.push(self.parse_qubit_ref()?);
}
}
self.expect_token(&Token::Semicolon)?;
Ok(QasmStatement::Delay(duration, qubits))
}
fn parse_identifier_statement(&mut self) -> Result<QasmStatement, ParseError> {
let name = match &self.current_token {
Token::Identifier(s) => s.clone(),
_ => return Err(ParseError::InvalidSyntax("Expected identifier".into())),
};
self.advance()?;
match &self.current_token {
Token::LeftParen => {
self.parse_gate_or_call(name)
}
Token::LeftBracket | Token::Identifier(_) => {
let mut gate = QasmGate {
name,
params: Vec::new(),
qubits: Vec::new(),
control: None,
inverse: false,
power: None,
};
gate.qubits.push(self.parse_qubit_ref()?);
while self.current_token == Token::Comma {
self.advance()?;
gate.qubits.push(self.parse_qubit_ref()?);
}
self.expect_token(&Token::Semicolon)?;
Ok(QasmStatement::Gate(gate))
}
_ => Err(ParseError::InvalidSyntax("Invalid statement".into())),
}
}
fn parse_modified_gate(&mut self) -> Result<QasmStatement, ParseError> {
let mut control = None;
let mut inverse = false;
let mut power = None;
loop {
match &self.current_token {
Token::Ctrl => {
self.advance()?;
if self.current_token == Token::LeftParen {
self.advance()?;
control = Some(match &self.current_token {
Token::Integer(n) => *n as usize,
_ => {
return Err(ParseError::ExpectedToken {
expected: "integer".into(),
found: format!("{:?}", self.current_token),
})
}
});
self.advance()?;
self.expect_token(&Token::RightParen)?;
} else {
control = Some(1);
}
}
Token::Inv => {
inverse = true;
self.advance()?;
}
Token::Pow => {
self.advance()?;
self.expect_token(&Token::LeftParen)?;
power = Some(self.parse_expression()?);
self.expect_token(&Token::RightParen)?;
}
_ => break,
}
}
let name = match &self.current_token {
Token::Identifier(s) => s.clone(),
_ => {
return Err(ParseError::ExpectedToken {
expected: "gate name".into(),
found: format!("{:?}", self.current_token),
})
}
};
self.advance()?;
let mut params = Vec::new();
if self.current_token == Token::LeftParen {
self.advance()?;
while self.current_token != Token::RightParen {
params.push(self.parse_expression()?);
if self.current_token == Token::Comma {
self.advance()?;
}
}
self.expect_token(&Token::RightParen)?;
}
let mut qubits = Vec::new();
qubits.push(self.parse_qubit_ref()?);
while self.current_token == Token::Comma {
self.advance()?;
qubits.push(self.parse_qubit_ref()?);
}
self.expect_token(&Token::Semicolon)?;
Ok(QasmStatement::Gate(QasmGate {
name,
params,
qubits,
control,
inverse,
power,
}))
}
fn parse_gate_or_call(&mut self, name: String) -> Result<QasmStatement, ParseError> {
self.expect_token(&Token::LeftParen)?;
let mut args = Vec::new();
while self.current_token != Token::RightParen {
args.push(self.parse_expression()?);
if self.current_token == Token::Comma {
self.advance()?;
}
}
self.expect_token(&Token::RightParen)?;
match &self.current_token {
Token::Identifier(_) | Token::LeftBracket => {
let mut qubits = Vec::new();
qubits.push(self.parse_qubit_ref()?);
while self.current_token == Token::Comma {
self.advance()?;
qubits.push(self.parse_qubit_ref()?);
}
self.expect_token(&Token::Semicolon)?;
Ok(QasmStatement::Gate(QasmGate {
name,
params: args,
qubits,
control: None,
inverse: false,
power: None,
}))
}
Token::Semicolon => {
self.advance()?;
Ok(QasmStatement::Call(name, args))
}
_ => Err(ParseError::InvalidSyntax(
"Expected qubits or semicolon".into(),
)),
}
}
fn parse_qubit_ref(&mut self) -> Result<QubitRef, ParseError> {
let register = match &self.current_token {
Token::Identifier(s) => s.clone(),
_ => {
return Err(ParseError::ExpectedToken {
expected: "register name".into(),
found: format!("{:?}", self.current_token),
})
}
};
self.advance()?;
if self.current_token == Token::LeftBracket {
self.advance()?;
let start = match &self.current_token {
Token::Integer(n) => *n as usize,
_ => {
return Err(ParseError::ExpectedToken {
expected: "integer".into(),
found: format!("{:?}", self.current_token),
})
}
};
self.advance()?;
if self.current_token == Token::Colon {
self.advance()?;
let end = match &self.current_token {
Token::Integer(n) => *n as usize,
_ => {
return Err(ParseError::ExpectedToken {
expected: "integer".into(),
found: format!("{:?}", self.current_token),
})
}
};
self.advance()?;
self.expect_token(&Token::RightBracket)?;
Ok(QubitRef::Slice {
register,
start,
end,
})
} else {
self.expect_token(&Token::RightBracket)?;
Ok(QubitRef::Single {
register,
index: start,
})
}
} else {
Ok(QubitRef::Register(register))
}
}
fn parse_classical_ref(&mut self) -> Result<ClassicalRef, ParseError> {
let register = match &self.current_token {
Token::Identifier(s) => s.clone(),
_ => {
return Err(ParseError::ExpectedToken {
expected: "register name".into(),
found: format!("{:?}", self.current_token),
})
}
};
self.advance()?;
if self.current_token == Token::LeftBracket {
self.advance()?;
let start = match &self.current_token {
Token::Integer(n) => *n as usize,
_ => {
return Err(ParseError::ExpectedToken {
expected: "integer".into(),
found: format!("{:?}", self.current_token),
})
}
};
self.advance()?;
if self.current_token == Token::Colon {
self.advance()?;
let end = match &self.current_token {
Token::Integer(n) => *n as usize,
_ => {
return Err(ParseError::ExpectedToken {
expected: "integer".into(),
found: format!("{:?}", self.current_token),
})
}
};
self.advance()?;
self.expect_token(&Token::RightBracket)?;
Ok(ClassicalRef::Slice {
register,
start,
end,
})
} else {
self.expect_token(&Token::RightBracket)?;
Ok(ClassicalRef::Single {
register,
index: start,
})
}
} else {
Ok(ClassicalRef::Register(register))
}
}
fn parse_expression(&mut self) -> Result<Expression, ParseError> {
self.parse_or_expression()
}
fn parse_or_expression(&mut self) -> Result<Expression, ParseError> {
let mut left = self.parse_and_expression()?;
while self.current_token == Token::Or {
self.advance()?;
let right = self.parse_and_expression()?;
left = Expression::Binary(BinaryOp::Or, Box::new(left), Box::new(right));
}
Ok(left)
}
fn parse_and_expression(&mut self) -> Result<Expression, ParseError> {
let mut left = self.parse_equality_expression()?;
while self.current_token == Token::And {
self.advance()?;
let right = self.parse_equality_expression()?;
left = Expression::Binary(BinaryOp::And, Box::new(left), Box::new(right));
}
Ok(left)
}
fn parse_equality_expression(&mut self) -> Result<Expression, ParseError> {
let mut left = self.parse_relational_expression()?;
loop {
let op = match &self.current_token {
Token::Eq => BinaryOp::Eq,
Token::Ne => BinaryOp::Ne,
_ => break,
};
self.advance()?;
let right = self.parse_relational_expression()?;
left = Expression::Binary(op, Box::new(left), Box::new(right));
}
Ok(left)
}
fn parse_relational_expression(&mut self) -> Result<Expression, ParseError> {
let mut left = self.parse_additive_expression()?;
loop {
let op = match &self.current_token {
Token::Lt => BinaryOp::Lt,
Token::Le => BinaryOp::Le,
Token::Gt => BinaryOp::Gt,
Token::Ge => BinaryOp::Ge,
_ => break,
};
self.advance()?;
let right = self.parse_additive_expression()?;
left = Expression::Binary(op, Box::new(left), Box::new(right));
}
Ok(left)
}
fn parse_additive_expression(&mut self) -> Result<Expression, ParseError> {
let mut left = self.parse_multiplicative_expression()?;
loop {
let op = match &self.current_token {
Token::Plus => BinaryOp::Add,
Token::Minus => BinaryOp::Sub,
_ => break,
};
self.advance()?;
let right = self.parse_multiplicative_expression()?;
left = Expression::Binary(op, Box::new(left), Box::new(right));
}
Ok(left)
}
fn parse_multiplicative_expression(&mut self) -> Result<Expression, ParseError> {
let mut left = self.parse_unary_expression()?;
loop {
let op = match &self.current_token {
Token::Star => BinaryOp::Mul,
Token::Slash => BinaryOp::Div,
Token::Percent => BinaryOp::Mod,
_ => break,
};
self.advance()?;
let right = self.parse_unary_expression()?;
left = Expression::Binary(op, Box::new(left), Box::new(right));
}
Ok(left)
}
fn parse_unary_expression(&mut self) -> Result<Expression, ParseError> {
match &self.current_token {
Token::Minus => {
self.advance()?;
Ok(Expression::Unary(
UnaryOp::Neg,
Box::new(self.parse_unary_expression()?),
))
}
Token::Not => {
self.advance()?;
Ok(Expression::Unary(
UnaryOp::Not,
Box::new(self.parse_unary_expression()?),
))
}
Token::BitNot => {
self.advance()?;
Ok(Expression::Unary(
UnaryOp::BitNot,
Box::new(self.parse_unary_expression()?),
))
}
_ => self.parse_postfix_expression(),
}
}
fn parse_postfix_expression(&mut self) -> Result<Expression, ParseError> {
let mut expr = self.parse_primary_expression()?;
loop {
match &self.current_token {
Token::LeftBracket => {
self.advance()?;
let index = self.parse_expression()?;
self.expect_token(&Token::RightBracket)?;
match expr {
Expression::Variable(name) => {
expr = Expression::Index(name, Box::new(index));
}
_ => {
return Err(ParseError::InvalidSyntax(
"Cannot index non-variable".into(),
))
}
}
}
Token::LeftParen => {
self.advance()?;
let mut args = Vec::new();
while self.current_token != Token::RightParen {
args.push(self.parse_expression()?);
if self.current_token == Token::Comma {
self.advance()?;
}
}
self.expect_token(&Token::RightParen)?;
match expr {
Expression::Variable(name) => {
expr = Expression::Function(name, args);
}
_ => {
return Err(ParseError::InvalidSyntax(
"Cannot call non-function".into(),
))
}
}
}
_ => break,
}
}
Ok(expr)
}
fn parse_primary_expression(&mut self) -> Result<Expression, ParseError> {
match &self.current_token {
Token::Integer(n) => {
let value = *n;
self.advance()?;
Ok(Expression::Literal(Literal::Integer(value)))
}
Token::Float(f) => {
let value = *f;
self.advance()?;
Ok(Expression::Literal(Literal::Float(value)))
}
Token::String(s) => {
let value = s.clone();
self.advance()?;
Ok(Expression::Literal(Literal::String(value)))
}
Token::Identifier(s) => {
let name = s.clone();
self.advance()?;
match name.as_str() {
"pi" => Ok(Expression::Literal(Literal::Pi)),
"e" => Ok(Expression::Literal(Literal::Euler)),
"tau" => Ok(Expression::Literal(Literal::Tau)),
_ => Ok(Expression::Variable(name)),
}
}
Token::LeftParen => {
self.advance()?;
let expr = self.parse_expression()?;
self.expect_token(&Token::RightParen)?;
Ok(expr)
}
_ => Err(ParseError::UnexpectedToken(format!(
"{:?}",
self.current_token
))),
}
}
fn parse_condition(&mut self) -> Result<Condition, ParseError> {
let left = self.parse_expression()?;
let op = match &self.current_token {
Token::Eq => ComparisonOp::Eq,
Token::Ne => ComparisonOp::Ne,
Token::Lt => ComparisonOp::Lt,
Token::Le => ComparisonOp::Le,
Token::Gt => ComparisonOp::Gt,
Token::Ge => ComparisonOp::Ge,
_ => {
return Err(ParseError::ExpectedToken {
expected: "comparison operator".into(),
found: format!("{:?}", self.current_token),
})
}
};
self.advance()?;
let right = self.parse_expression()?;
Ok(Condition { left, op, right })
}
}
pub fn parse_qasm3(input: &str) -> Result<QasmProgram, ParseError> {
let mut parser = QasmParser::new(input)?;
parser.parse_program()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_simple_circuit() {
let input = r#"
OPENQASM 3.0;
include "stdgates.inc";
qubit[2] q;
bit[2] c;
h q[0];
cx q[0], q[1];
measure q -> c;
"#;
let result = parse_qasm3(input);
assert!(result.is_ok());
let program = result.expect("parse_qasm3 should succeed for valid input");
assert_eq!(program.version, "3.0");
assert_eq!(program.includes, vec!["stdgates.inc"]);
assert_eq!(program.declarations.len(), 2);
assert_eq!(program.statements.len(), 3);
}
#[test]
fn test_parse_gate_definition() {
let input = r"
OPENQASM 3.0;
gate mygate(theta) q {
rx(theta) q;
ry(theta/2) q;
}
qubit q;
mygate(pi/4) q;
";
let result = parse_qasm3(input);
assert!(result.is_ok());
}
#[test]
fn test_const_register_size() {
use super::super::ast::Declaration;
let input = r#"
OPENQASM 3.0;
const n = 5;
qubit[n] q;
bit[n] c;
"#;
let program = parse_qasm3(input).expect("parse_qasm3 should succeed");
let mut q_size = None;
let mut c_size = None;
for decl in &program.declarations {
match decl {
Declaration::QuantumRegister(reg) => q_size = Some(reg.size),
Declaration::ClassicalRegister(reg) => c_size = Some(reg.size),
_ => {}
}
}
assert_eq!(q_size, Some(5));
assert_eq!(c_size, Some(5));
}
#[test]
fn test_const_register_size_arithmetic() {
use super::super::ast::Declaration;
let input = r#"
OPENQASM 3.0;
const n = 2 + 3;
qubit[n] q;
"#;
let program = parse_qasm3(input).expect("parse_qasm3 should succeed");
let size = program.declarations.iter().find_map(|d| match d {
Declaration::QuantumRegister(reg) => Some(reg.size),
_ => None,
});
assert_eq!(size, Some(5));
}
#[test]
fn test_undefined_const_register_size_errors() {
let input = r#"
OPENQASM 3.0;
qubit[undefined_n] q;
"#;
assert!(parse_qasm3(input).is_err());
}
}