Skip to content
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

Implement parsing measure from text through ASG #42

Merged
merged 6 commits into from
Jan 19, 2024
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
6 changes: 3 additions & 3 deletions crates/oq3_parser/src/grammar/expressions/atom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,12 @@ fn measure_expression(p: &mut Parser<'_>) -> CompletedMarker {
p.bump(T![measure]);
match p.current() {
IDENT | HARDWAREIDENT => {
items::ident_or_index_expr(p);
let m1 = p.start();
// Parses elements that can be cast to GateOperand
params::arg_gate_call_qubit(p, m1);
}
_ => {
p.error("expecting qubit(s) to measure");
// m.abandon(p);
// return;
}
}
m.complete(p, MEASURE_EXPRESSION)
Expand Down
9 changes: 6 additions & 3 deletions crates/oq3_parser/src/grammar/items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,11 +233,13 @@ pub(crate) fn ident_or_index_expr(p: &mut Parser<'_>) {
p.bump(IDENT);
match p.current() {
T!['['] => {
let newm = m.complete(p, IDENT);
let newm = m.complete(p, IDENTIFIER);
expressions::index_expr(p, newm);
}
_ => {
m.complete(p, IDENT);
// FIXME: m.complete(p, IDENT) is valid, but it should not be
// it is a source of bugs!
m.complete(p, IDENTIFIER);
}
}
}
Expand Down Expand Up @@ -270,7 +272,8 @@ pub(crate) fn measure_(p: &mut Parser<'_>, m: Marker) {
p.bump(T![measure]);
match p.current() {
IDENT | HARDWAREIDENT => {
ident_or_index_expr(p);
let m1 = p.start();
params::arg_gate_call_qubit(p, m1);
}
_ => {
p.error("expecting qubit(s) to measure");
Expand Down
3 changes: 2 additions & 1 deletion crates/oq3_parser/src/grammar/params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,8 @@ fn param_typed(p: &mut Parser<'_>, m: Marker) -> bool {
success
}

fn arg_gate_call_qubit(p: &mut Parser<'_>, m: Marker) -> bool {
// These can be cast to GateOperand
pub(crate) fn arg_gate_call_qubit(p: &mut Parser<'_>, m: Marker) -> bool {
if p.at(HARDWAREIDENT) {
p.bump(HARDWAREIDENT);
m.complete(p, HARDWARE_QUBIT);
Expand Down
29 changes: 26 additions & 3 deletions crates/oq3_semantics/src/asg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,9 @@ pub enum Expr {
// But we need to handle these in a consistent way. Is there any situation where the "type" of Range is meaningful or useful?
// For example, in syntax_to_semantics, have a routine that handles out-of-tree expressions.
Range(Range),
Call, // stub function (def) call
Set, // stub
Measure, // stub
Call, // stub function (def) call
Set, // stub
MeasureExpression(MeasureExpression),
}

/// Typed expression implemented by tagging an `Expr` with a `Type`.
Expand Down Expand Up @@ -562,6 +562,29 @@ impl GateDeclaration {
}
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct MeasureExpression {
operand: Box<TExpr>,
}

impl MeasureExpression {
pub fn new(operand: TExpr) -> MeasureExpression {
MeasureExpression {
operand: Box::new(operand),
}
}

pub fn operand(&self) -> &TExpr {
&self.operand
}

// FIXME: type may not be correct here.
// This assumes a single qubit is measured.
pub fn to_texpr(self) -> TExpr {
TExpr::new(Expr::MeasureExpression(self), Type::Bit(IsConst::False))
}
Comment on lines +581 to +585
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the type map should be fairly straightforwards, if the program is semantically correct:

let out_type = match self.operand.get_type() {
    Type::Qubit => Ok(Type::Bit(IsConst::False)),
    Type::QubitArray(dims) => Ok(Type::BitArray(dims.clone(), IsConst::False)),
    _ => Err(<type error>),
}?;

}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct GateCall {
name: SymbolIdResult,
Expand Down
46 changes: 27 additions & 19 deletions crates/oq3_semantics/src/syntax_to_semantics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,14 +228,36 @@ fn from_expr(expr: synast::Expr, context: &mut Context) -> Option<asg::TExpr> {
Some(indexed_identifier.to_texpr())
}

synast::Expr::MeasureExpression(_measure_expr) => None,
synast::Expr::MeasureExpression(ref measure_expr) => {
let gate_operand = measure_expr.gate_operand().unwrap(); // FIXME: check this
let gate_operand_asg = from_gate_operand(gate_operand, context);
Some(asg::MeasureExpression::new(gate_operand_asg).to_texpr())
}

// Everything else is not yet implemented
_ => {
println!("Expression not supported {:?}", expr);
None
}
}
}

fn from_gate_operand(gate_operand: synast::GateOperand, context: &mut Context) -> asg::TExpr {
match gate_operand {
synast::GateOperand::HardwareQubit(ref hwq) => {
asg::GateOperand::HardwareQubit(ast_hardware_qubit(hwq)).to_texpr(Type::HardwareQubit)
}
synast::GateOperand::Identifier(identifier) => {
let (astidentifier, typ) = ast_identifier(&identifier, context);
asg::GateOperand::Identifier(astidentifier).to_texpr(typ)
}
synast::GateOperand::IndexedIdentifier(indexed_identifier) => {
let (indexed_identifier, typ) = ast_indexed_identifier(&indexed_identifier, context);
asg::GateOperand::IndexedIdentifier(indexed_identifier).to_texpr(typ)
}
}
}

fn from_index_operator(
index_op: synast::IndexOperator,
context: &mut Context,
Expand Down Expand Up @@ -437,21 +459,7 @@ fn from_item(item: synast::Item, context: &mut Context) -> Option<asg::Stmt> {
.qubit_list()
.unwrap()
.gate_operands()
.map(|qubit| match qubit {
synast::GateOperand::HardwareQubit(ref hwq) => {
asg::GateOperand::HardwareQubit(ast_hardware_qubit(hwq))
.to_texpr(Type::HardwareQubit)
}
synast::GateOperand::Identifier(identifier) => {
let (astidentifier, typ) = ast_identifier(&identifier, context);
asg::GateOperand::Identifier(astidentifier).to_texpr(typ)
}
synast::GateOperand::IndexedIdentifier(indexed_identifier) => {
let (indexed_identifier, typ) =
ast_indexed_identifier(&indexed_identifier, context);
asg::GateOperand::IndexedIdentifier(indexed_identifier).to_texpr(typ)
}
})
.map(|qubit| from_gate_operand(qubit, context))
.collect();

let param_list = gate_call
Expand Down Expand Up @@ -571,19 +579,19 @@ fn from_assignment_stmt(
assignment_stmt: &synast::AssignmentStmt,
context: &mut Context,
) -> Option<asg::Stmt> {
let nameb = assignment_stmt.name();
let nameb = assignment_stmt.name(); // LHS of assignment
let name = nameb.as_ref().unwrap();
let name_str = name.string();
let expr = from_expr(assignment_stmt.expr().unwrap(), context); // rhs of `=` operator

let (symbol_id, typ) = context.lookup_symbol(name_str.as_str(), name).as_tuple();
let is_mutating_const = symbol_id.is_ok() && typ.is_const();
let lvalue = asg::LValue::Identifier(symbol_id);
let ret_stmt = Some(asg::Assignment::new(lvalue, expr.unwrap()).to_stmt());
let stmt_asg = Some(asg::Assignment::new(lvalue, expr.unwrap()).to_stmt());
if is_mutating_const {
context.insert_error(MutateConstError, assignment_stmt);
}
ret_stmt
stmt_asg
}

//
Expand Down
11 changes: 10 additions & 1 deletion crates/oq3_semantics/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,16 @@ impl Type {
pub fn is_const(&self) -> bool {
use Type::*;
match self {
Bit(c) | Int(_, c) => matches!(*c, IsConst::True),
Bit(c)
| Int(_, c)
| UInt(_, c)
| Float(_, c)
| Angle(_, c)
| Complex(_, c)
| Bool(c)
| Duration(c)
| Stretch(c)
| BitArray(_, c) => matches!(*c, IsConst::True),
_ => true,
}
}
Expand Down
109 changes: 80 additions & 29 deletions crates/oq3_semantics/tests/from_string_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use oq3_semantics::asg;
use oq3_semantics::semantic_error::SemanticErrorList;
use oq3_semantics::symbols::{SymbolTable, SymbolType};
use oq3_semantics::syntax_to_semantics::parse_source_string;
use oq3_semantics::types::{IsConst, Type};
use oq3_semantics::types::{ArrayDims, IsConst, Type};

fn parse_string(code: &str) -> (asg::Program, SemanticErrorList, SymbolTable) {
parse_source_string(code, None).take_context().as_tuple()
Expand Down Expand Up @@ -166,31 +166,82 @@ bit[4] b = "1001";
assert_eq!(program.len(), 1);
}

// #[test]
// fn test_include() {
// let code = r##"
// include "somefile.qasm";
// "##;
// let (program, errors, symbol_table) = parse_string(code);
// assert_eq!(errors.len(), 0);
// assert_eq!(program.len(), 1);
// }

// #[test]
// fn test_from_string_qubit_register_decl() {
// let code = r##"
// qubit[3] q;
// "##;
// let (program, errors, symbol_table) = parse_string(code);
// assert!(errors.is_empty());
// assert_eq!(program.len(), 1);
// let stmt = program.first();
// assert!(matches!(stmt, Some(asg::Stmt::DeclareQuantum(_))));
// let qdecl = match stmt {
// Some(asg::Stmt::DeclareQuantum(qdecl)) => qdecl,
// _ => unreachable!(),
// };
// let varname_id = qdecl.name().clone().unwrap();
// assert_eq!(varname_id, symbol_table.lookup("q").unwrap().symbol_id());
// assert_eq!(&Type::Qubit(Some(3)), (symbol_table[varname_id]).symbol_type());
// }
#[test]
fn test_from_string_qubit_register_decl() {
let code = r#"
qubit[3] q;
"#;
let (program, errors, symbol_table) = parse_string(code);
assert!(errors.is_empty());
assert_eq!(program.len(), 1);
let stmt = program.first();
assert!(matches!(stmt, Some(asg::Stmt::DeclareQuantum(_))));
let qdecl = match stmt {
Some(asg::Stmt::DeclareQuantum(qdecl)) => qdecl,
_ => unreachable!(),
};
let varname_id = qdecl.name().clone().unwrap();
assert_eq!(varname_id, symbol_table.lookup("q").unwrap().symbol_id());
assert_eq!(
&Type::QubitArray(ArrayDims::D1(3)),
symbol_table[&varname_id].symbol_type()
);
}

#[test]
fn test_from_string_measure() {
let code = r#"
qubit q;
measure q;
"#;
let (program, errors, _symbol_table) = parse_string(code);
assert!(errors.is_empty());
assert_eq!(program.len(), 2);
}

// Issue #42
#[test]
fn test_from_string_measure_assign() {
let code = r#"
qubit q;
bit c;
c = measure q;
"#;
let (program, errors, _symbol_table) = parse_string(code);
assert!(errors.is_empty());
assert_eq!(program.len(), 3);
}

#[test]
fn test_from_string_measure_indexed() {
let code = r#"
qubit[2] q;
measure q[1];
"#;
let (program, errors, _symbol_table) = parse_string(code);
assert!(errors.is_empty());
assert_eq!(program.len(), 2);
}

#[test]
fn test_from_string_measure_hardware() {
let code = r#"
measure $0;
measure $42;
"#;
let (program, errors, _symbol_table) = parse_string(code);
assert!(errors.is_empty());
assert_eq!(program.len(), 2);
}

#[test]
fn test_from_string_gate_call_indexed() {
let code = r#"
gate h q {}
qubit[2] q;
h q[1];
"#;
let (program, errors, _symbol_table) = parse_string(code);
assert!(errors.is_empty());
assert_eq!(program.len(), 3);
}