Skip to content

Add ability to parse quantum decls and lit exprs #2160

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 4 commits into from
Feb 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
47 changes: 25 additions & 22 deletions compiler/qsc_qasm3/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#![allow(dead_code)]

use indenter::{indented, Indented};
use num_bigint::BigInt;
use qsc_data_structures::span::{Span, WithSpan};
use std::{
fmt::{self, Display, Formatter, Write},
Expand Down Expand Up @@ -1037,7 +1038,7 @@ impl Display for IncludeStmt {
#[derive(Clone, Debug)]
pub struct QubitDeclaration {
pub span: Span,
pub qubit: Identifier,
pub qubit: Ident,
pub size: Option<ExprStmt>,
}

Expand Down Expand Up @@ -1571,7 +1572,7 @@ pub enum ExprKind {
Ident(Ident),
UnaryExpr(UnaryExpr),
BinaryExpr(BinaryExpr),
Literal(Lit),
Lit(Lit),
FunctionCall(FunctionCall),
Cast(Cast),
Concatenation(Concatenation),
Expand All @@ -1583,14 +1584,14 @@ impl Display for ExprKind {
let indent = set_indentation(indented(f), 0);
match self {
ExprKind::Err => write!(f, "Err"),
ExprKind::Ident(id) => write!(f, "Ident {id}"),
ExprKind::UnaryExpr(expr) => write!(f, "UnaryExpr {expr}"),
ExprKind::Ident(id) => write!(f, "{id}"),
ExprKind::UnaryExpr(expr) => write!(f, "{expr}"),
ExprKind::BinaryExpr(expr) => display_bin_op(indent, expr),
ExprKind::Literal(lit) => write!(f, "Literal {lit}"),
ExprKind::FunctionCall(call) => write!(f, "FunctionCall {call}"),
ExprKind::Cast(cast) => write!(f, "Cast {cast}"),
ExprKind::Concatenation(concat) => write!(f, "Concatenation {concat}"),
ExprKind::IndexExpr(index) => write!(f, "IndexExpr {index}"),
ExprKind::Lit(lit) => write!(f, "{lit}"),
ExprKind::FunctionCall(call) => write!(f, "{call}"),
ExprKind::Cast(cast) => write!(f, "{cast}"),
ExprKind::Concatenation(concat) => write!(f, "{concat}"),
ExprKind::IndexExpr(index) => write!(f, "{index}"),
}
}
}
Expand Down Expand Up @@ -1740,19 +1741,20 @@ pub struct Lit {

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

#[derive(Clone, Debug)]
pub enum LiteralKind {
Array(List<ExprStmt>),
Bitstring { value: usize, width: u32 },
Boolean(bool),
Bitstring(BigInt, usize),
Bool(bool),
Duration { value: f64, unit: TimeUnit },
Float(f64),
Imaginary(f64),
Integer(i64),
Int(i64),
BigInt(BigInt),
String(Rc<str>),
}

Expand All @@ -1764,21 +1766,22 @@ impl Display for LiteralKind {
write!(indent, "Array:")?;
indent = set_indentation(indent, 1);
for expr in exprs {
write!(indent, "\n{expr}")?;
write!(indent, "\n{expr:?}")?;
}
Ok(())
}
LiteralKind::Bitstring { value, width } => {
write!(f, "Bitstring: {value} (width {width})")
LiteralKind::Bitstring(value, width) => {
write!(f, "Bitstring(\"{:0>width$}\")", value.to_str_radix(2))
}
LiteralKind::Boolean(b) => write!(f, "Boolean: {b}"),
LiteralKind::Bool(b) => write!(f, "Bool({b:?})"),
LiteralKind::Duration { value, unit } => {
write!(f, "Duration: {value} {unit}")
write!(f, "Duration({value:?}, {unit:?})")
}
LiteralKind::Float(value) => write!(f, "Float: {value}"),
LiteralKind::Imaginary(value) => write!(f, "Imaginary: {value} im"),
LiteralKind::Integer(i) => write!(f, "Integer: {i}"),
LiteralKind::String(s) => write!(f, "String: {s}"),
LiteralKind::Float(value) => write!(f, "Float({value:?})"),
LiteralKind::Imaginary(value) => write!(f, "Imaginary({value:?})"),
LiteralKind::Int(i) => write!(f, "Int({i:?})"),
LiteralKind::BigInt(i) => write!(f, "BigInt({i:?})"),
LiteralKind::String(s) => write!(f, "String({s:?})"),
}
}
}
Expand Down
68 changes: 61 additions & 7 deletions compiler/qsc_qasm3/src/lex/cooked.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ pub enum Error {
#[diagnostic(code("Qasm3.Lex.UnterminatedString"))]
UnterminatedString(#[label] Span),

#[error("string literal with an invalid escape sequence")]
#[diagnostic(code("Qasm3.Lex.InvalidEscapeSequence"))]
InvalidEscapeSequence(#[label] Span),

#[error("unrecognized character `{0}`")]
#[diagnostic(code("Qasm3.Lex.UnknownChar"))]
Unknown(char, #[label] Span),
Expand All @@ -64,6 +68,7 @@ impl Error {
Self::IncompleteEof(expected, token, span + offset)
}
Self::UnterminatedString(span) => Self::UnterminatedString(span + offset),
Self::InvalidEscapeSequence(span) => Self::InvalidEscapeSequence(span + offset),
Self::Unknown(c, span) => Self::Unknown(c, span + offset),
}
}
Expand All @@ -73,6 +78,7 @@ impl Error {
Error::Incomplete(_, _, _, s)
| Error::IncompleteEof(_, _, s)
| Error::UnterminatedString(s)
| Error::InvalidEscapeSequence(s)
| Error::Unknown(_, s) => s,
}
}
Expand All @@ -82,6 +88,7 @@ impl Error {
#[derive(Clone, Copy, Debug, Eq, PartialEq, Sequence)]
pub enum TokenKind {
Annotation,
Pragma,
Keyword(Keyword),
Type(Type),

Expand Down Expand Up @@ -118,7 +125,6 @@ pub enum TokenKind {
PlusPlus,
/// `->`
Arrow,
At,

// Operators,
ClosedBinOp(ClosedBinOp),
Expand All @@ -141,6 +147,7 @@ impl Display for TokenKind {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
TokenKind::Annotation => write!(f, "annotation"),
TokenKind::Pragma => write!(f, "pragma"),
TokenKind::Keyword(keyword) => write!(f, "keyword `{keyword}`"),
TokenKind::Type(type_) => write!(f, "keyword `{type_}`"),
TokenKind::GPhase => write!(f, "gphase"),
Expand All @@ -166,7 +173,6 @@ impl Display for TokenKind {
TokenKind::Comma => write!(f, "`,`"),
TokenKind::PlusPlus => write!(f, "`++`"),
TokenKind::Arrow => write!(f, "`->`"),
TokenKind::At => write!(f, "`@`"),
TokenKind::ClosedBinOp(op) => write!(f, "`{op}`"),
TokenKind::BinOpEq(op) => write!(f, "`{op}=`"),
TokenKind::ComparisonOp(op) => write!(f, "`{op}`"),
Expand Down Expand Up @@ -273,7 +279,6 @@ impl FromStr for Type {
#[derive(Clone, Copy, Debug, Eq, PartialEq, Sequence)]
pub enum Literal {
Bitstring,
Boolean,
Float,
Imaginary,
Integer(Radix),
Expand All @@ -285,7 +290,6 @@ impl Display for Literal {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Literal::Bitstring => "bitstring",
Literal::Boolean => "boolean",
Literal::Float => "float",
Literal::Imaginary => "imaginary",
Literal::Integer(_) => "integer",
Expand Down Expand Up @@ -459,6 +463,23 @@ impl<'a> Lexer<'a> {
tokens.next().map(|i| i.kind)
}

/// Consumes the characters while they satisfy `f`. Returns the last character eaten, if any.
fn eat_while(&mut self, mut f: impl FnMut(raw::TokenKind) -> bool) -> Option<raw::TokenKind> {
let mut last_eaten: Option<raw::Token> = None;
loop {
let t = self.tokens.next_if(|t| f(t.kind));
if t.is_none() {
return last_eaten.map(|t| t.kind);
}
last_eaten = t;
}
}

fn eat_to_end_of_line(&mut self) {
self.eat_while(|t| t != raw::TokenKind::Newline);
self.next_if_eq(raw::TokenKind::Newline);
}

/// Consumes a list of tokens zero or more times.
fn kleen_star(&mut self, tokens: &[raw::TokenKind], complete: TokenKind) -> Result<(), Error> {
let mut iter = tokens.iter();
Expand All @@ -471,6 +492,7 @@ impl<'a> Lexer<'a> {
Ok(())
}

#[allow(clippy::too_many_lines)]
fn cook(&mut self, token: &raw::Token) -> Result<Option<Token>, Error> {
let kind = match token.kind {
raw::TokenKind::Bitstring { terminated: true } => {
Expand All @@ -487,7 +509,13 @@ impl<'a> Lexer<'a> {
}
raw::TokenKind::Ident => {
let ident = &self.input[(token.offset as usize)..(self.offset() as usize)];
Ok(Some(Self::ident(ident)))
let cooked_ident = Self::ident(ident);
if matches!(cooked_ident, TokenKind::Keyword(Keyword::Pragma)) {
self.eat_to_end_of_line();
Ok(Some(TokenKind::Pragma))
} else {
Ok(Some(cooked_ident))
}
}
raw::TokenKind::HardwareQubit => Ok(Some(TokenKind::HardwareQubit)),
raw::TokenKind::LiteralFragment(_) => {
Expand Down Expand Up @@ -525,6 +553,26 @@ impl<'a> Lexer<'a> {
_ => Ok(Some(number.into())),
}
}
raw::TokenKind::Single(Single::Sharp) => {
let complete = TokenKind::Pragma;
self.expect(raw::TokenKind::Ident, complete)?;
let ident = &self.input[(token.offset as usize + 1)..(self.offset() as usize)];
if matches!(Self::ident(ident), TokenKind::Keyword(Keyword::Pragma)) {
self.eat_to_end_of_line();
Ok(Some(complete))
} else {
let span = Span {
lo: token.offset,
hi: self.offset(),
};
Err(Error::Incomplete(
raw::TokenKind::Ident,
complete,
raw::TokenKind::Ident,
span,
))
}
}
raw::TokenKind::Single(single) => self.single(single).map(Some),
raw::TokenKind::String { terminated: true } => {
Ok(Some(TokenKind::Literal(Literal::String)))
Expand Down Expand Up @@ -565,7 +613,13 @@ impl<'a> Lexer<'a> {
Ok(self.closed_bin_op(ClosedBinOp::Amp))
}
}
Single::At => Ok(TokenKind::At),
Single::At => {
// AnnotationKeyword: '@' Identifier ('.' Identifier)* -> pushMode(EAT_TO_LINE_END);
let complete = TokenKind::Annotation;
self.expect(raw::TokenKind::Ident, complete);
self.eat_to_end_of_line();
Ok(complete)
}
Single::Bang => {
if self.next_if_eq_single(Single::Eq) {
Ok(TokenKind::ComparisonOp(ComparisonOp::BangEq))
Expand Down Expand Up @@ -627,6 +681,7 @@ impl<'a> Lexer<'a> {
}
}
Single::Semi => Ok(TokenKind::Semicolon),
Single::Sharp => unreachable!(),
Single::Slash => Ok(self.closed_bin_op(ClosedBinOp::Slash)),
Single::Star => {
if self.next_if_eq_single(Single::Star) {
Expand Down Expand Up @@ -659,7 +714,6 @@ impl<'a> Lexer<'a> {
"delay" => TokenKind::Delay,
"reset" => TokenKind::Reset,
"measure" => TokenKind::Measure,
"false" | "true" => TokenKind::Literal(Literal::Boolean),
ident => {
if let Ok(keyword) = ident.parse::<Keyword>() {
TokenKind::Keyword(keyword)
Expand Down
Loading