From 5f40048013091635296096e7d792e691fd720f82 Mon Sep 17 00:00:00 2001 From: JKRT Date: Thu, 29 Oct 2026 14:19:57 +0100 Subject: [PATCH] dumpExp: parentheses where Modelica precedence needs them (1.4.1) BINARY, LBINARY, RELATION, UNARY and LUNARY operands are parenthesised by precedence: 2*(3 + 4) was printed 2*3 + 4, a + (-b) as a + -b. Unary minus binds tighter than * (-a*x, as omc) and looser than ^. Assisted-by: Claude Opus 5.5 --- Project.toml | 2 +- src/absynDump.jl | 52 +++++++++++++++++++++++++++++++++++++---- test/test_absyn_dump.jl | 18 ++++++++++++++ 3 files changed, 66 insertions(+), 6 deletions(-) diff --git a/Project.toml b/Project.toml index 9390aff..5348f5a 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Absyn" uuid = "ce2f92e2-a952-11e9-0543-8b443f216f1d" authors = ["johti "] -version = "1.4.0" +version = "1.4.1" [deps] ExportAll = "ad2082ca-a69e-11e9-38fa-e96309a31fe4" MetaModelica = "9d7f2a79-07b5-5542-8b19-c0100dda6b06" diff --git a/src/absynDump.jl b/src/absynDump.jl index 88d69df..dbc7a66 100644 --- a/src/absynDump.jl +++ b/src/absynDump.jl @@ -53,11 +53,12 @@ function dumpExp(exp::Exp)::String STRING(__) => "\"" * exp.value * "\"" BOOL(__) => exp.value ? "true" : "false" CREF(__) => dumpCref(exp.componentRef) - BINARY(__) => dumpExp(exp.exp1) * dumpOperator(exp.op) * dumpExp(exp.exp2) - UNARY(__) => dumpOperator(exp.op) * dumpExp(exp.exp) - LBINARY(__) => dumpExp(exp.exp1) * dumpOperator(exp.op) * dumpExp(exp.exp2) - LUNARY(__) => dumpOperator(exp.op) * " " * dumpExp(exp.exp) - RELATION(__) => dumpExp(exp.exp1) * dumpOperator(exp.op) * dumpExp(exp.exp2) + BINARY(__) => dumpBinary(exp.exp1, exp.op, exp.exp2, dumpPrecedence(exp)) + UNARY(__) => dumpOperator(exp.op) * dumpOperand(exp.exp, dumpPrecedence(exp), true) + LBINARY(__) => dumpBinary(exp.exp1, exp.op, exp.exp2, dumpPrecedence(exp)) + LUNARY(__) => dumpOperator(exp.op) * dumpOperand(exp.exp, dumpPrecedence(exp), true) + RELATION(__) => dumpOperand(exp.exp1, dumpPrecedence(exp), true) * dumpOperator(exp.op) * + dumpOperand(exp.exp2, dumpPrecedence(exp), true) IFEXP(__) => begin local s = "if " * dumpExp(exp.ifExp) * " then " * dumpExp(exp.trueBranch) for (c, b) in exp.elseIfBranch @@ -93,6 +94,47 @@ function dumpExp(exp::Exp)::String end end +# --- Precedence: parentheses where Modelica needs them --- + +""" +How tightly an expression binds, higher is tighter: if-expressions and ranges, `or`, `and`, `not`, relations, +`+ -`, `* /`, unary `+ -`, `^`, then primaries. Unary minus binds tighter than `*` (`-a*x`, as omc prints it) and +looser than `^` (`(-a)^2`). +""" +function dumpPrecedence(exp::Exp)::Int + if exp isa IFEXP || exp isa RANGE + return 0 + elseif exp isa LBINARY + return exp.op isa OR ? 1 : 2 + elseif exp isa LUNARY + return 3 + elseif exp isa RELATION + return 4 + elseif exp isa BINARY + return exp.op isa Union{POW, POW_EW} ? 8 : exp.op isa Union{MUL, DIV, MUL_EW, DIV_EW} ? 6 : 5 + elseif exp isa UNARY + return 7 + end + return 10 +end + +"""An operand in parentheses when it binds looser than its operator, or as tightly with `tight`.""" +function dumpOperand(exp::Exp, precedence::Int, tight::Bool)::String + local p::Int = dumpPrecedence(exp) + local s::String = dumpExp(exp) + return (p < precedence || (tight && p == precedence)) ? "(" * s * ")" : s +end + +""" +A binary or logical operation: the right operand in parentheses also when it binds as tightly (`a - (b - c)`) or is +a unary operation (`a + (-b)`: Modelica allows a unary operator only at the start); `^` takes no operator +expression unparenthesised on either side. +""" +function dumpBinary(exp1::Exp, op::Operator, exp2::Exp, precedence::Int)::String + local right::String = exp2 isa UNARY ? "(" * dumpExp(exp2) * ")" : dumpOperand(exp2, precedence, true) + return dumpOperand(exp1, precedence, precedence == 8) * dumpOperator(op) * right +end + # --- Operator --- function dumpOperator(op::Operator)::String diff --git a/test/test_absyn_dump.jl b/test/test_absyn_dump.jl index d8bbf5e..95ac2df 100644 --- a/test/test_absyn_dump.jl +++ b/test/test_absyn_dump.jl @@ -55,6 +55,24 @@ @test Absyn.dumpExp(BINARY(INTEGER(1), ADD(), INTEGER(2))) == "1 + 2" @test Absyn.dumpExp(UNARY(UMINUS(), CREF(CREF_IDENT("a", nil)))) == "-a" @test Absyn.dumpExp(BINARY(UNARY(UMINUS(), CREF(CREF_IDENT("a", nil))), MUL(), CREF(CREF_IDENT("x", nil)))) == "-a*x" + local a = CREF(CREF_IDENT("a", nil)) + local b = CREF(CREF_IDENT("b", nil)) + local c = CREF(CREF_IDENT("c", nil)) + #= parentheses where Modelica needs them, none where it does not =# + @test Absyn.dumpExp(BINARY(INTEGER(2), MUL(), BINARY(INTEGER(3), ADD(), INTEGER(4)))) == "2*(3 + 4)" + @test Absyn.dumpExp(BINARY(BINARY(INTEGER(1), ADD(), INTEGER(2)), MUL(), INTEGER(3))) == "(1 + 2)*3" + @test Absyn.dumpExp(BINARY(BINARY(a, SUB(), b), SUB(), c)) == "a - b - c" + @test Absyn.dumpExp(BINARY(a, SUB(), BINARY(b, SUB(), c))) == "a - (b - c)" + @test Absyn.dumpExp(BINARY(a, ADD(), BINARY(b, MUL(), c))) == "a + b*c" + @test Absyn.dumpExp(BINARY(a, ADD(), UNARY(UMINUS(), b))) == "a + (-b)" + @test Absyn.dumpExp(UNARY(UMINUS(), BINARY(a, ADD(), b))) == "-(a + b)" + @test Absyn.dumpExp(BINARY(UNARY(UMINUS(), a), POW(), INTEGER(2))) == "(-a)^2" + @test Absyn.dumpExp(BINARY(BINARY(a, POW(), b), POW(), c)) == "(a^b)^c" + @test Absyn.dumpExp(LUNARY(NOT(), LBINARY(a, AND(), b))) == "not (a and b)" + @test Absyn.dumpExp(LUNARY(NOT(), a)) == "not a" + @test Absyn.dumpExp(LBINARY(LBINARY(a, OR(), b), AND(), c)) == "(a or b) and c" + @test Absyn.dumpExp(LBINARY(RELATION(a, LESS(), b), AND(), c)) == "a < b and c" + @test Absyn.dumpExp(BINARY(IFEXP(a, b, c, nil), ADD(), INTEGER(1))) == "(if a then b else c) + 1" end @testset "dumpExp CALL" begin