From db62d285d7b7a8004a0305f5dd30456b09be6966 Mon Sep 17 00:00:00 2001 From: ChrisRackauckas Date: Tue, 9 Sep 2025 05:56:37 -0400 Subject: [PATCH] Add support for negative variables (unary minus) (#35) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add BaseModelicaUnaryMinus AST node for unary minus operations - Update create_arithmetic_expression to handle unary minus (-x) - Add evaluation logic for BaseModelicaUnaryMinus in evaluator - Add test cases for both simple unary minus (-5) and negative variables - Fixes #35: der('x') = -'x' now parses and evaluates correctly 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- src/evaluator.jl | 1 + src/parser.jl | 6 ++++++ test/runtests.jl | 14 ++++++++++++++ test/testfiles/NegativeVariable.mo | 7 +++++++ 4 files changed, 28 insertions(+) create mode 100644 test/testfiles/NegativeVariable.mo diff --git a/src/evaluator.jl b/src/evaluator.jl index 23bbfc8..db6eb3c 100644 --- a/src/evaluator.jl +++ b/src/evaluator.jl @@ -19,6 +19,7 @@ function eval_AST(expr::BaseModelicaExpr) BaseModelicaFactor(base, exp) => (f(base))^f(exp) BaseModelicaSum(left, right) => (f(left)) + (f(right)) BaseModelicaMinus(left, right) => f(left) - f(right) + BaseModelicaUnaryMinus(operand) => -f(operand) BaseModelicaProd(left, right) => f(left) * f(right) BaseModelicaDivide(left, right) => f(left) / f(right) BaseModelicaNot(relation) => !(f(relation)) diff --git a/src/parser.jl b/src/parser.jl index 3228a2c..398afb6 100644 --- a/src/parser.jl +++ b/src/parser.jl @@ -63,6 +63,7 @@ end BaseModelicaIdentifier(name) BaseModelicaSum(left, right) BaseModelicaMinus(left, right) + BaseModelicaUnaryMinus(operand) BaseModelicaProd(left, right) BaseModelicaFactor(base, exp) BaseModelicaElementWiseFactor(base, exp) @@ -126,6 +127,11 @@ function create_term(input_list) end function create_arithmetic_expression(input_list) + # Handle unary minus case - if first element is a minus operator + if length(input_list) >= 2 && input_list[1] isa BMSubtract + return BaseModelicaUnaryMinus(input_list[2]) + end + left_el = input_list[1] for (i, element) in enumerate(input_list) left_el = @match element begin diff --git a/test/runtests.jl b/test/runtests.jl index 4c44d6d..f88bbcd 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -19,6 +19,11 @@ if GROUP == "All" || GROUP == "Core" arith_test = only(PC.parse_one("5 + 6*(45 + 9^2)^2", BM.arithmetic_expression)) @test arith_test isa BM.BaseModelicaSum @test BM.eval_AST(arith_test) == 95261.0 + + # Test unary minus parsing (issue #35) + unary_minus_test = only(PC.parse_one("-5", BM.arithmetic_expression)) + @test unary_minus_test isa BM.BaseModelicaUnaryMinus + @test BM.eval_AST(unary_minus_test) == -5.0 newton_path = joinpath( dirname(dirname(pathof(BM))), "test", "testfiles", "NewtonCoolingBase.mo") @@ -27,6 +32,15 @@ if GROUP == "All" || GROUP == "Core" newton_system = BM.baseModelica_to_ModelingToolkit(newton_cooling) @test newton_system isa ODESystem @test parse_basemodelica("testfiles/NewtonCoolingBase.mo") isa ODESystem + + # Test parsing with negative variables (issue #35) + negative_path = joinpath( + dirname(dirname(pathof(BM))), "test", "testfiles", "NegativeVariable.mo") + negative_package = BM.parse_file(negative_path) + @test negative_package isa BM.BaseModelicaPackage + negative_system = BM.baseModelica_to_ModelingToolkit(negative_package) + @test negative_system isa ODESystem + @test parse_basemodelica("testfiles/NegativeVariable.mo") isa ODESystem end end end diff --git a/test/testfiles/NegativeVariable.mo b/test/testfiles/NegativeVariable.mo new file mode 100644 index 0000000..f64e688 --- /dev/null +++ b/test/testfiles/NegativeVariable.mo @@ -0,0 +1,7 @@ +package 'Negate' + model 'Negate' + Real 'x'; + equation + der('x') = -'x'; + end 'Negate'; +end 'Negate'; \ No newline at end of file