From ed5941c5c356f3d43d72aa9cf6a6ee7803a75087 Mon Sep 17 00:00:00 2001 From: Thomas Gardner Date: Thu, 30 May 2024 02:15:44 +1000 Subject: [PATCH] Reduce prefix expression with unary minus for imaginary numbers 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##"