From ae59917bf7a44cb580e4520c9aadc91f13a90476 Mon Sep 17 00:00:00 2001 From: abhamra Date: Wed, 29 May 2024 17:38:37 -0400 Subject: [PATCH 1/5] Adding from_designator + notes for usage --- crates/oq3_semantics/src/syntax_to_semantics.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/crates/oq3_semantics/src/syntax_to_semantics.rs b/crates/oq3_semantics/src/syntax_to_semantics.rs index fb88711..ffeb446 100644 --- a/crates/oq3_semantics/src/syntax_to_semantics.rs +++ b/crates/oq3_semantics/src/syntax_to_semantics.rs @@ -286,7 +286,7 @@ fn from_stmt(stmt: synast::Stmt, context: &mut Context) -> Option { return Some(asg::DeclareHardwareQubit::new(ast_hardware_qubit(&hw_qubit)).to_stmt()); }; let qubit_type = q_decl.qubit_type().unwrap(); - let width = match qubit_type.designator().and_then(|x| x.expr()) { + let width = match from_designator(qubit_type.designator()) { Some(synast::Expr::Literal(ref literal)) => { match literal.kind() { synast::LiteralKind::IntNumber(int_num) => { @@ -418,7 +418,7 @@ fn from_stmt(stmt: synast::Stmt, context: &mut Context) -> Option { .map(|qubit| from_gate_operand(qubit, context)) .collect() }); - let duration = from_expr(delay_stmt.designator().unwrap().expr(), context).unwrap(); + let duration = from_expr(from_designator(delay_stmt.designator()), context).unwrap(); if !matches!(duration.get_type(), Type::Duration(_)) { context.insert_error(IncompatibleTypesError, &delay_stmt.designator().unwrap()); } @@ -958,7 +958,7 @@ fn from_scalar_type( // not complex scalar_type.designator() }; - let width = match designator.and_then(|desg| desg.expr()) { + let width = match from_designator(designator) { // We only support literal integer designators at the moment. Some(synast::Expr::Literal(ref literal)) => { match literal.kind() { @@ -1232,6 +1232,13 @@ fn bind_typed_parameter_list( }) } +fn from_designator(arg: Option) -> Option { + arg.and_then(|desg| desg.expr()) + // NOTE: Other uses of this pattern above have certain error checking, + // but because it was not standardized, it is not included + // in this helper function +} + // This works, but using it is pretty clumsy. // fn with_scope(context: &mut Context, scope: ScopeType, func: F) where // F: FnOnce(&mut Context) From 1a82eab58bfa82212d1d00b980bcfe52ef97fd72 Mon Sep 17 00:00:00 2001 From: atomgardner Date: Sat, 1 Jun 2024 07:32:07 +1000 Subject: [PATCH 2/5] Reduce prefix expression with unary minus for imaginary numbers (#220) fixes: #195 --- .../oq3_semantics/src/syntax_to_semantics.rs | 88 +++++++++++-------- .../oq3_semantics/tests/from_string_tests.rs | 30 ++++++- 2 files changed, 80 insertions(+), 38 deletions(-) diff --git a/crates/oq3_semantics/src/syntax_to_semantics.rs b/crates/oq3_semantics/src/syntax_to_semantics.rs index fb88711..335d0e9 100644 --- a/crates/oq3_semantics/src/syntax_to_semantics.rs +++ b/crates/oq3_semantics/src/syntax_to_semantics.rs @@ -553,54 +553,70 @@ fn from_paren_expr(paren_expr: synast::ParenExpr, context: &mut Context) -> Opti from_expr(paren_expr.expr(), context) } +fn negative_float(f: synast::FloatNumber) -> asg::FloatLiteral { + let num = f.value().unwrap(); + let float = format!("-{num}"); + asg::FloatLiteral::new(float) +} + +fn negative_int(n: synast::IntNumber) -> asg::IntLiteral { + let num = n.value_u128().unwrap(); // fn value_u128 is kind of a hack + asg::IntLiteral::new(num, false) // `false` means negative +} + fn from_expr(expr_maybe: Option, context: &mut Context) -> Option { let expr = expr_maybe?; match expr { // FIXME: Ugh. could clean up logic here // It is convenient to rewrite literals wrapped in unary minus as literals // with negative values. - synast::Expr::PrefixExpr(prefix_expr) => { - match prefix_expr.op_kind() { - Some(synast::UnaryOp::Neg) => { - match prefix_expr.expr() { - Some(synast::Expr::Literal(ref literal)) => { - match literal.kind() { - synast::LiteralKind::FloatNumber(float_num) => { - let num = float_num.value().unwrap(); - let float = format!("-{num}"); - Some(asg::FloatLiteral::new(float).to_texpr()) + synast::Expr::PrefixExpr(prefix_expr) => match prefix_expr.op_kind() { + Some(synast::UnaryOp::Neg) => match prefix_expr.expr() { + Some(synast::Expr::Literal(ref literal)) => Some(match literal.kind() { + synast::LiteralKind::FloatNumber(f) => negative_float(f).to_texpr(), + synast::LiteralKind::IntNumber(n) => negative_int(n).to_texpr(), + _ => { + panic!("Only integers and floats are supported as operands to unary minus.") + } + }), + + Some(synast::Expr::TimingLiteral(ref timing_literal)) => { + match timing_literal.time_unit().unwrap() { + synast::TimeUnit::Imaginary => { + Some(match timing_literal.literal().unwrap().kind() { + synast::LiteralKind::FloatNumber(f) => { + negative_float(f).to_imaginary_texpr() } - synast::LiteralKind::IntNumber(int_num) => { - let num = int_num.value_u128().unwrap(); // fn value_u128 is kind of a hack - Some(asg::IntLiteral::new(num, false).to_texpr()) - // `false` means negative + synast::LiteralKind::IntNumber(n) => { + negative_int(n).to_imaginary_texpr() } - _ => panic!("Only integers and floats are supported as operands to unary minus."), - } + _ => panic!("You have found a bug in oq3_syntax or oq3_parser"), + }) + } + _ => { + panic!("Only floats are supported as operands to unary minus.") } - - Some(synexpr) => Some( - asg::UnaryExpr::new( - asg::UnaryOp::Minus, - from_expr(Some(synexpr), context).unwrap(), - ) - .to_texpr(), - ), - - None => panic!( - "You have found a bug in oq3_parser. No operand to unary minus found." - ), } } - Some(op) => panic!( - "Unary operators other than minus are not supported. Found '{:?}.'", - op - ), - _ => panic!( - "You have found a bug in oq3_parser. No operand to unary operator found." + + Some(synexpr) => Some( + asg::UnaryExpr::new( + asg::UnaryOp::Minus, + from_expr(Some(synexpr), context).unwrap(), + ) + .to_texpr(), ), - } - } + + None => { + panic!("You have found a bug in oq3_parser. No operand to unary minus found.") + } + }, + Some(op) => panic!( + "Unary operators other than minus are not supported. Found '{:?}.'", + op + ), + _ => panic!("You have found a bug in oq3_parser. No operand to unary operator found."), + }, synast::Expr::ParenExpr(paren_expr) => from_paren_expr(paren_expr, context), diff --git a/crates/oq3_semantics/tests/from_string_tests.rs b/crates/oq3_semantics/tests/from_string_tests.rs index cfadbab..f97ecfa 100644 --- a/crates/oq3_semantics/tests/from_string_tests.rs +++ b/crates/oq3_semantics/tests/from_string_tests.rs @@ -398,8 +398,10 @@ fn expr_from_expr_stmt(stmt: &asg::Stmt) -> asg::Expr { fn literal_value(stmt: &asg::Stmt) -> Option { match expr_from_expr_stmt(stmt) { asg::Expr::Literal(lit) => match lit { - asg::Literal::Float(float) => Some(float.value().to_string()), - asg::Literal::Int(int) => { + asg::Literal::Float(float) | asg::Literal::ImaginaryFloat(float) => { + Some(float.value().to_string()) + } + asg::Literal::Int(int) | asg::Literal::ImaginaryInt(int) => { if *int.sign() { Some(format!("{}", int.value())) } else { @@ -471,6 +473,30 @@ fn test_from_string_neg_lit_int() { assert_eq!(expr, "-123"); } +#[test] +fn test_from_string_neg_lit_int_im() { + let code = r##" +-1 im; +"##; + let (program, errors, _symbol_table) = parse_string(code); + assert!(errors.is_empty()); + assert_eq!(program.len(), 1); + let expr = literal_value(&program[0]).unwrap(); + assert_eq!(expr, "-1"); +} + +#[test] +fn test_from_string_neg_lit_float_im() { + let code = r##" +-10.1 im; +"##; + let (program, errors, _symbol_table) = parse_string(code); + assert!(errors.is_empty()); + assert_eq!(program.len(), 1); + let expr = literal_value(&program[0]).unwrap(); + assert_eq!(expr, "-10.1"); +} + #[test] fn test_from_string_neg_spc_lit_int() { let code = r##" From 5d521bed66fd14a00200133487ffea844918a71c Mon Sep 17 00:00:00 2001 From: John Lapeyre Date: Fri, 31 May 2024 18:41:34 -0400 Subject: [PATCH 3/5] Try to compile and pass lint with both 1.70 and 1.75 (#223) In CI this often fails even if it succeeds locally. * Limit the version of triomphe to 0.1.11 because 0.1.12 uses unstable features * Remove some conditional compilation for clippy complaints. Maybe clippy was upgraded independently of rustc? --- crates/oq3_syntax/Cargo.toml | 2 +- crates/oq3_syntax/src/ptr.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/oq3_syntax/Cargo.toml b/crates/oq3_syntax/Cargo.toml index b8a11f3..2e49883 100644 --- a/crates/oq3_syntax/Cargo.toml +++ b/crates/oq3_syntax/Cargo.toml @@ -25,7 +25,7 @@ rowan = "0.15.11" rustc-hash = "1.1.0" smol_str = "0.2.0" stdx = { version = "0.0.188", package = "ra_ap_stdx"} -triomphe = { version = "0.1.8", default-features = false, features = ["std"] } +triomphe = { version = "<= 0.1.11", default-features = false, features = ["std"] } xshell = "0.2.2" rustversion = "1.0" # local crates diff --git a/crates/oq3_syntax/src/ptr.rs b/crates/oq3_syntax/src/ptr.rs index 6921bd5..4b916fc 100644 --- a/crates/oq3_syntax/src/ptr.rs +++ b/crates/oq3_syntax/src/ptr.rs @@ -35,7 +35,7 @@ impl Clone for AstPtr { #[rustversion::before(1.74)] fn clone(&self) -> AstPtr { AstPtr { - raw: self.raw.clone(), + raw: self.raw, _ty: PhantomData, } } @@ -82,7 +82,7 @@ impl AstPtr { #[rustversion::before(1.74)] pub fn syntax_node_ptr(&self) -> SyntaxNodePtr { - self.raw.clone() + self.raw } pub fn text_range(&self) -> TextRange { From 614ee04dc5cf41f96fa193023fe78d0050e2a6b6 Mon Sep 17 00:00:00 2001 From: atomgardner Date: Sat, 1 Jun 2024 08:58:51 +1000 Subject: [PATCH 4/5] Factor lowering a list of qubits into a helper function (#221) * Factor lowering a list of qubits into a helper function fixes: #163 * Update crates/oq3_semantics/src/syntax_to_semantics.rs remove comments that were moved elsewhere * Update crates/oq3_semantics/src/syntax_to_semantics.rs Move a comment from elsewhere to here, where it makes more sense. * Run cargo fmt --------- Co-authored-by: John Lapeyre --- .../oq3_semantics/src/syntax_to_semantics.rs | 41 ++++++++----------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/crates/oq3_semantics/src/syntax_to_semantics.rs b/crates/oq3_semantics/src/syntax_to_semantics.rs index 335d0e9..8a368d6 100644 --- a/crates/oq3_semantics/src/syntax_to_semantics.rs +++ b/crates/oq3_semantics/src/syntax_to_semantics.rs @@ -402,29 +402,19 @@ fn from_stmt(stmt: synast::Stmt, context: &mut Context) -> Option { } synast::Stmt::Barrier(barrier) => { - let gate_operands = barrier.qubit_list().map(|operands| { - operands - .gate_operands() - .map(|qubit| from_gate_operand(qubit, context)) - .collect() - }); - Some(asg::Stmt::Barrier(asg::Barrier::new(gate_operands))) + let gate_operands = from_qubit_list(barrier.qubit_list(), context); + Some(asg::Stmt::Barrier(asg::Barrier::new(Some(gate_operands)))) } synast::Stmt::DelayStmt(delay_stmt) => { - let gate_operands = delay_stmt.qubit_list().map(|operands| { - operands - .gate_operands() - .map(|qubit| from_gate_operand(qubit, context)) - .collect() - }); + let gate_operands = from_qubit_list(delay_stmt.qubit_list(), context); let duration = from_expr(delay_stmt.designator().unwrap().expr(), context).unwrap(); if !matches!(duration.get_type(), Type::Duration(_)) { context.insert_error(IncompatibleTypesError, &delay_stmt.designator().unwrap()); } Some(asg::Stmt::Delay(asg::DelayStmt::new( duration, - gate_operands.unwrap(), + gate_operands, ))) } @@ -767,15 +757,7 @@ fn from_gate_call_expr( modifiers: Vec, context: &mut Context, ) -> Option { - // Warning, I think map overlooks None. This can cause a bug in the present case. - // Because None means a coding error upstream. Better to blow up here. - let gate_operands: Vec<_> = gate_call_expr - .qubit_list() - .unwrap() - .gate_operands() - .map(|qubit| from_gate_operand(qubit, context)) - .collect(); - + let gate_operands: Vec<_> = from_qubit_list(gate_call_expr.qubit_list(), context); let param_list = gate_call_expr .arg_list() .map(|ex| inner_expression_list(ex.expression_list().unwrap(), context)); @@ -871,6 +853,19 @@ fn from_expression_list( asg::ExpressionList::new(inner_expression_list(expression_list, context)) } +fn from_qubit_list( + qubit_list: Option, + context: &mut Context, +) -> Vec { + // Warning, I think map overlooks None. This can cause a bug in the present case. + // Because None means a coding error upstream. Better to blow up here. + qubit_list + .unwrap() + .gate_operands() + .map(|qubit| from_gate_operand(qubit, context)) + .collect() +} + // Return a Vec of TExpr. fn inner_expression_list( expression_list: synast::ExpressionList, From 8b3550c8b7cab86c38dc49fe9f1d559761c5fff2 Mon Sep 17 00:00:00 2001 From: abhamra Date: Wed, 29 May 2024 17:38:37 -0400 Subject: [PATCH 5/5] Adding from_designator + notes for usage --- crates/oq3_semantics/src/syntax_to_semantics.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/crates/oq3_semantics/src/syntax_to_semantics.rs b/crates/oq3_semantics/src/syntax_to_semantics.rs index 8a368d6..f7774c9 100644 --- a/crates/oq3_semantics/src/syntax_to_semantics.rs +++ b/crates/oq3_semantics/src/syntax_to_semantics.rs @@ -286,7 +286,7 @@ fn from_stmt(stmt: synast::Stmt, context: &mut Context) -> Option { return Some(asg::DeclareHardwareQubit::new(ast_hardware_qubit(&hw_qubit)).to_stmt()); }; let qubit_type = q_decl.qubit_type().unwrap(); - let width = match qubit_type.designator().and_then(|x| x.expr()) { + let width = match from_designator(qubit_type.designator()) { Some(synast::Expr::Literal(ref literal)) => { match literal.kind() { synast::LiteralKind::IntNumber(int_num) => { @@ -969,7 +969,7 @@ fn from_scalar_type( // not complex scalar_type.designator() }; - let width = match designator.and_then(|desg| desg.expr()) { + let width = match from_designator(designator) { // We only support literal integer designators at the moment. Some(synast::Expr::Literal(ref literal)) => { match literal.kind() { @@ -1243,6 +1243,13 @@ fn bind_typed_parameter_list( }) } +fn from_designator(arg: Option) -> Option { + arg.and_then(|desg| desg.expr()) + // NOTE: Other uses of this pattern above have certain error checking, + // but because it was not standardized, it is not included + // in this helper function +} + // This works, but using it is pretty clumsy. // fn with_scope(context: &mut Context, scope: ScopeType, func: F) where // F: FnOnce(&mut Context)