diff --git a/com.avaloq.tools.ddk.xtext.export/META-INF/MANIFEST.MF b/com.avaloq.tools.ddk.xtext.export/META-INF/MANIFEST.MF
index 0b34131a92..4fcb354f03 100644
--- a/com.avaloq.tools.ddk.xtext.export/META-INF/MANIFEST.MF
+++ b/com.avaloq.tools.ddk.xtext.export/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: com.avaloq.tools.ddk.xtext.export
Bundle-SymbolicName: com.avaloq.tools.ddk.xtext.export;singleton:=true
-Bundle-Version: 17.3.2.qualifier
+Bundle-Version: 17.3.3.qualifier
Bundle-Vendor: Avaloq Group AG
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
diff --git a/com.avaloq.tools.ddk.xtext.export/pom.xml b/com.avaloq.tools.ddk.xtext.export/pom.xml
index 3e6e99b719..65b107c1ae 100644
--- a/com.avaloq.tools.ddk.xtext.export/pom.xml
+++ b/com.avaloq.tools.ddk.xtext.export/pom.xml
@@ -6,8 +6,8 @@
18.0.1-SNAPSHOT
../ddk-parent
- 17.3.2-SNAPSHOT
+ 17.3.3-SNAPSHOT
com.avaloq.tools.ddk
com.avaloq.tools.ddk.xtext.export
eclipse-plugin
-
\ No newline at end of file
+
diff --git a/com.avaloq.tools.ddk.xtext.export/src/com/avaloq/tools/ddk/xtext/export/jvmmodel/ExportExpressionCompiler.xtend b/com.avaloq.tools.ddk.xtext.export/src/com/avaloq/tools/ddk/xtext/export/jvmmodel/ExportExpressionCompiler.xtend
index c831834a01..0ce3b5664a 100644
--- a/com.avaloq.tools.ddk.xtext.export/src/com/avaloq/tools/ddk/xtext/export/jvmmodel/ExportExpressionCompiler.xtend
+++ b/com.avaloq.tools.ddk.xtext.export/src/com/avaloq/tools/ddk/xtext/export/jvmmodel/ExportExpressionCompiler.xtend
@@ -385,6 +385,10 @@ class ExportExpressionCompiler {
}
def private boolean isNumber(Expression it, ExportTranslationContext ctx) {
+ if (isArithmeticOperatorCall(ctx)) {
+ // Arithmetic calls have no resolvable JVM method but still produce numeric values.
+ return true
+ }
val type = translator.resolveType(it, ctx)
type !== null && type.isNumeric
}
diff --git a/com.avaloq.tools.ddk.xtext.export/src/com/avaloq/tools/ddk/xtext/export/jvmmodel/ExportExpressionTranslator.xtend b/com.avaloq.tools.ddk.xtext.export/src/com/avaloq/tools/ddk/xtext/export/jvmmodel/ExportExpressionTranslator.xtend
index b47720fb5f..896376b3be 100644
--- a/com.avaloq.tools.ddk.xtext.export/src/com/avaloq/tools/ddk/xtext/export/jvmmodel/ExportExpressionTranslator.xtend
+++ b/com.avaloq.tools.ddk.xtext.export/src/com/avaloq/tools/ddk/xtext/export/jvmmodel/ExportExpressionTranslator.xtend
@@ -43,7 +43,6 @@ import org.eclipse.xtext.common.types.util.TypeReferences
import org.eclipse.xtext.xbase.XExpression
import org.eclipse.xtext.xbase.XbaseFactory
import org.eclipse.xtext.xbase.lib.BooleanExtensions
-import org.eclipse.xtext.xbase.lib.ObjectExtensions
/**
* Translates the custom {@link Expression} AST of the export expression DSL into equivalent Xbase
@@ -436,8 +435,9 @@ class ExportExpressionTranslator {
switch operator {
case '||': toBinaryOperation(xLeft, xRight, BooleanExtensions, 'operator_or', sourceElement)
case '&&': toBinaryOperation(xLeft, xRight, BooleanExtensions, 'operator_and', sourceElement)
- case '==': toBinaryOperation(xLeft, xRight, ObjectExtensions, 'operator_equals', sourceElement)
- case '!=': toBinaryOperation(xLeft, xRight, ObjectExtensions, 'operator_notEquals', sourceElement)
+ // Xbase equality is value-based, unlike the identity equality emitted by the legacy Java generator.
+ case '==': null
+ case '!=': null
default: null
}
}
diff --git a/com.avaloq.tools.ddk.xtext.generator.test/META-INF/MANIFEST.MF b/com.avaloq.tools.ddk.xtext.generator.test/META-INF/MANIFEST.MF
index ea494375fd..6a1bd1d8f8 100644
--- a/com.avaloq.tools.ddk.xtext.generator.test/META-INF/MANIFEST.MF
+++ b/com.avaloq.tools.ddk.xtext.generator.test/META-INF/MANIFEST.MF
@@ -8,7 +8,9 @@ Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Fragment-Host: com.avaloq.tools.ddk.xtext.generator
Require-Bundle: com.avaloq.tools.ddk.test.core,
+ com.avaloq.tools.ddk.xtext.export,
com.avaloq.tools.ddk.xtext.expression,
+ com.avaloq.tools.ddk.xtext.scope,
com.avaloq.tools.ddk.xtext.test.core,
org.eclipse.xtext,
org.mockito.mockito-core,
diff --git a/com.avaloq.tools.ddk.xtext.generator.test/src/com/avaloq/tools/ddk/xtext/generator/expression/AbstractExpressionCodeGenerationTest.java b/com.avaloq.tools.ddk.xtext.generator.test/src/com/avaloq/tools/ddk/xtext/generator/expression/AbstractExpressionCodeGenerationTest.java
new file mode 100644
index 0000000000..bd455094cd
--- /dev/null
+++ b/com.avaloq.tools.ddk.xtext.generator.test/src/com/avaloq/tools/ddk/xtext/generator/expression/AbstractExpressionCodeGenerationTest.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * Copyright (c) 2026 Avaloq Group AG and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Avaloq Group AG - initial API and implementation
+ *******************************************************************************/
+package com.avaloq.tools.ddk.xtext.generator.expression;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import java.io.IOException;
+
+import org.eclipse.xtext.xbase.XExpression;
+import org.junit.jupiter.api.Test;
+
+import com.avaloq.tools.ddk.xtext.expression.expression.Expression;
+import com.avaloq.tools.ddk.xtext.generator.test.util.GeneratorTestUtil;
+import com.avaloq.tools.ddk.xtext.test.jupiter.AbstractXtextTest;
+
+
+/**
+ * Shared expression code-generation contract for DSL-specific compiler implementations.
+ */
+@SuppressWarnings("nls")
+abstract class AbstractExpressionCodeGenerationTest extends AbstractXtextTest {
+
+ @Override
+ protected GeneratorTestUtil getXtextTestUtil() {
+ return GeneratorTestUtil.getInstance();
+ }
+
+ /**
+ * This test class does not have a test source file. {@inheritDoc}
+ */
+ @Override
+ protected String getTestSourceFileName() {
+ return null;
+ }
+
+ @Test
+ @SuppressWarnings("PMD.SignatureDeclareThrowsException")
+ void testNestedArithmetic() throws IOException {
+ assertCompilesUnchanged("(4 + 2) * 3");
+ assertCompilesUnchanged("(4 + 2) * 3 * 4");
+ }
+
+ @Test
+ @SuppressWarnings("PMD.SignatureDeclareThrowsException")
+ void testArithmeticControls() throws IOException {
+ assertCompilesUnchanged("4 + 2");
+ assertEquals("\"x\" + 2 + 3 + 4", compile("('x' + 2) + 3 + 4"));
+ }
+
+ @Test
+ @SuppressWarnings("PMD.SignatureDeclareThrowsException")
+ void testEqualityIsNotTranslated() throws IOException {
+ assertNull(translate("1 == 2"));
+ assertNull(translate("1 != 2"));
+ }
+
+ protected abstract String compile(Expression expression);
+
+ protected abstract XExpression translate(Expression expression);
+
+ private String compile(final String source) throws IOException {
+ return compile(parse(source));
+ }
+
+ private void assertCompilesUnchanged(final String source) throws IOException {
+ assertEquals(source, compile(source));
+ }
+
+ private XExpression translate(final String source) throws IOException {
+ return translate(parse(source));
+ }
+
+ private Expression parse(final String source) throws IOException {
+ return (Expression) getXtextTestUtil().getModel("test.expression", source);
+ }
+}
diff --git a/com.avaloq.tools.ddk.xtext.generator.test/src/com/avaloq/tools/ddk/xtext/generator/expression/ExportExpressionCodeGenerationTest.java b/com.avaloq.tools.ddk.xtext.generator.test/src/com/avaloq/tools/ddk/xtext/generator/expression/ExportExpressionCodeGenerationTest.java
new file mode 100644
index 0000000000..29de222cc6
--- /dev/null
+++ b/com.avaloq.tools.ddk.xtext.generator.test/src/com/avaloq/tools/ddk/xtext/generator/expression/ExportExpressionCodeGenerationTest.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2026 Avaloq Group AG and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Avaloq Group AG - initial API and implementation
+ *******************************************************************************/
+package com.avaloq.tools.ddk.xtext.generator.expression;
+
+import org.eclipse.xtext.xbase.XExpression;
+
+import com.avaloq.tools.ddk.xtext.export.ExportStandaloneSetup;
+import com.avaloq.tools.ddk.xtext.export.jvmmodel.ExportExpressionCompiler;
+import com.avaloq.tools.ddk.xtext.export.jvmmodel.ExportExpressionTranslator;
+import com.avaloq.tools.ddk.xtext.export.jvmmodel.ExportTranslationContext;
+import com.avaloq.tools.ddk.xtext.expression.expression.Expression;
+import com.google.inject.Injector;
+
+
+/**
+ * Tests Java source generation for expressions used by the Export DSL.
+ */
+public class ExportExpressionCodeGenerationTest extends AbstractExpressionCodeGenerationTest {
+
+ @Override
+ protected void beforeAllTests() {
+ super.beforeAllTests();
+ final Injector injector = new ExportStandaloneSetup().createInjector();
+ getTestInformation().putTestObject(ExportExpressionCompiler.class, injector.getInstance(ExportExpressionCompiler.class));
+ getTestInformation().putTestObject(ExportExpressionTranslator.class, injector.getInstance(ExportExpressionTranslator.class));
+ }
+
+ @Override
+ protected String compile(final Expression expression) {
+ final ExportTranslationContext context = new ExportTranslationContext();
+ context.setSourceElement(expression);
+ final ExportExpressionCompiler compiler = (ExportExpressionCompiler) getTestInformation().getTestObject(ExportExpressionCompiler.class);
+ return compiler.javaExpression(expression, context);
+ }
+
+ @Override
+ protected XExpression translate(final Expression expression) {
+ final ExportTranslationContext context = new ExportTranslationContext();
+ context.setSourceElement(expression);
+ final ExportExpressionTranslator translator = (ExportExpressionTranslator) getTestInformation().getTestObject(ExportExpressionTranslator.class);
+ return translator.translate(expression, context);
+ }
+}
diff --git a/com.avaloq.tools.ddk.xtext.generator.test/src/com/avaloq/tools/ddk/xtext/generator/expression/ScopeExpressionCodeGenerationTest.java b/com.avaloq.tools.ddk.xtext.generator.test/src/com/avaloq/tools/ddk/xtext/generator/expression/ScopeExpressionCodeGenerationTest.java
new file mode 100644
index 0000000000..b28ded2039
--- /dev/null
+++ b/com.avaloq.tools.ddk.xtext.generator.test/src/com/avaloq/tools/ddk/xtext/generator/expression/ScopeExpressionCodeGenerationTest.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2026 Avaloq Group AG and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Avaloq Group AG - initial API and implementation
+ *******************************************************************************/
+package com.avaloq.tools.ddk.xtext.generator.expression;
+
+import org.eclipse.xtext.xbase.XExpression;
+
+import com.avaloq.tools.ddk.xtext.expression.expression.Expression;
+import com.avaloq.tools.ddk.xtext.scope.ScopeStandaloneSetup;
+import com.avaloq.tools.ddk.xtext.scope.jvmmodel.ScopeExpressionCompiler;
+import com.avaloq.tools.ddk.xtext.scope.jvmmodel.ScopeExpressionTranslator;
+import com.avaloq.tools.ddk.xtext.scope.jvmmodel.ScopeTranslationContext;
+import com.google.inject.Injector;
+
+
+/**
+ * Tests Java source generation for expressions used by the Scope DSL.
+ */
+public class ScopeExpressionCodeGenerationTest extends AbstractExpressionCodeGenerationTest {
+
+ @Override
+ protected void beforeAllTests() {
+ super.beforeAllTests();
+ final Injector injector = new ScopeStandaloneSetup().createInjector();
+ getTestInformation().putTestObject(ScopeExpressionCompiler.class, injector.getInstance(ScopeExpressionCompiler.class));
+ getTestInformation().putTestObject(ScopeExpressionTranslator.class, injector.getInstance(ScopeExpressionTranslator.class));
+ }
+
+ @Override
+ protected String compile(final Expression expression) {
+ final ScopeTranslationContext context = new ScopeTranslationContext();
+ context.setSourceElement(expression);
+ final ScopeExpressionCompiler compiler = (ScopeExpressionCompiler) getTestInformation().getTestObject(ScopeExpressionCompiler.class);
+ return compiler.javaExpression(expression, context);
+ }
+
+ @Override
+ protected XExpression translate(final Expression expression) {
+ final ScopeTranslationContext context = new ScopeTranslationContext();
+ context.setSourceElement(expression);
+ final ScopeExpressionTranslator translator = (ScopeExpressionTranslator) getTestInformation().getTestObject(ScopeExpressionTranslator.class);
+ return translator.translate(expression, context);
+ }
+}
diff --git a/com.avaloq.tools.ddk.xtext.generator.test/src/com/avaloq/tools/ddk/xtext/generator/test/generator/GeneratorTestSuite.java b/com.avaloq.tools.ddk.xtext.generator.test/src/com/avaloq/tools/ddk/xtext/generator/test/generator/GeneratorTestSuite.java
index 9e2644a0a8..faf395b6ec 100644
--- a/com.avaloq.tools.ddk.xtext.generator.test/src/com/avaloq/tools/ddk/xtext/generator/test/generator/GeneratorTestSuite.java
+++ b/com.avaloq.tools.ddk.xtext.generator.test/src/com/avaloq/tools/ddk/xtext/generator/test/generator/GeneratorTestSuite.java
@@ -13,7 +13,9 @@
import org.junit.platform.suite.api.SelectClasses;
import org.junit.platform.suite.api.Suite;
+import com.avaloq.tools.ddk.xtext.generator.expression.ExportExpressionCodeGenerationTest;
import com.avaloq.tools.ddk.xtext.generator.expression.ExpressionsExtentionsTest;
+import com.avaloq.tools.ddk.xtext.generator.expression.ScopeExpressionCodeGenerationTest;
import com.avaloq.tools.ddk.xtext.generator.test.util.EClassComparatorTest;
import com.avaloq.tools.ddk.xtext.generator.test.util.GraphTest;
import com.avaloq.tools.ddk.xtext.generator.xbase.test.XbaseGeneratorFragmentTest;
@@ -25,7 +27,9 @@
@Suite
@SelectClasses({
// @Format-Off
+ ExportExpressionCodeGenerationTest.class,
ExpressionsExtentionsTest.class,
+ ScopeExpressionCodeGenerationTest.class,
EClassComparatorTest.class,
GraphTest.class,
XbaseGeneratorFragmentTest.class
diff --git a/com.avaloq.tools.ddk.xtext.scope/META-INF/MANIFEST.MF b/com.avaloq.tools.ddk.xtext.scope/META-INF/MANIFEST.MF
index 7c3c6b168e..eb79e677ad 100644
--- a/com.avaloq.tools.ddk.xtext.scope/META-INF/MANIFEST.MF
+++ b/com.avaloq.tools.ddk.xtext.scope/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: com.avaloq.tools.ddk.xtext.scope
Bundle-SymbolicName: com.avaloq.tools.ddk.xtext.scope;singleton:=true
-Bundle-Version: 17.3.2.qualifier
+Bundle-Version: 17.3.3.qualifier
Bundle-Vendor: Avaloq Group AG
Bundle-RequiredExecutionEnvironment: JavaSE-21
Require-Bundle: org.eclipse.xtext,
diff --git a/com.avaloq.tools.ddk.xtext.scope/pom.xml b/com.avaloq.tools.ddk.xtext.scope/pom.xml
index 542bfb0ea7..c5745c9e60 100644
--- a/com.avaloq.tools.ddk.xtext.scope/pom.xml
+++ b/com.avaloq.tools.ddk.xtext.scope/pom.xml
@@ -6,8 +6,8 @@
18.0.1-SNAPSHOT
../ddk-parent
- 17.3.2-SNAPSHOT
+ 17.3.3-SNAPSHOT
com.avaloq.tools.ddk
com.avaloq.tools.ddk.xtext.scope
eclipse-plugin
-
\ No newline at end of file
+
diff --git a/com.avaloq.tools.ddk.xtext.scope/src/com/avaloq/tools/ddk/xtext/scope/jvmmodel/ScopeExpressionCompiler.xtend b/com.avaloq.tools.ddk.xtext.scope/src/com/avaloq/tools/ddk/xtext/scope/jvmmodel/ScopeExpressionCompiler.xtend
index 9343d6c107..044da8dfc7 100644
--- a/com.avaloq.tools.ddk.xtext.scope/src/com/avaloq/tools/ddk/xtext/scope/jvmmodel/ScopeExpressionCompiler.xtend
+++ b/com.avaloq.tools.ddk.xtext.scope/src/com/avaloq/tools/ddk/xtext/scope/jvmmodel/ScopeExpressionCompiler.xtend
@@ -387,6 +387,10 @@ class ScopeExpressionCompiler {
}
def private boolean isNumber(Expression it, ScopeTranslationContext ctx) {
+ if (isArithmeticOperatorCall(ctx)) {
+ // Arithmetic calls have no resolvable JVM method but still produce numeric values.
+ return true
+ }
val type = translator.resolveType(it, ctx)
type !== null && type.isNumeric
}
diff --git a/com.avaloq.tools.ddk.xtext.scope/src/com/avaloq/tools/ddk/xtext/scope/jvmmodel/ScopeExpressionTranslator.xtend b/com.avaloq.tools.ddk.xtext.scope/src/com/avaloq/tools/ddk/xtext/scope/jvmmodel/ScopeExpressionTranslator.xtend
index 6ebcaed4f4..b911e52c32 100644
--- a/com.avaloq.tools.ddk.xtext.scope/src/com/avaloq/tools/ddk/xtext/scope/jvmmodel/ScopeExpressionTranslator.xtend
+++ b/com.avaloq.tools.ddk.xtext.scope/src/com/avaloq/tools/ddk/xtext/scope/jvmmodel/ScopeExpressionTranslator.xtend
@@ -44,7 +44,6 @@ import org.eclipse.xtext.common.types.util.TypeReferences
import org.eclipse.xtext.xbase.XExpression
import org.eclipse.xtext.xbase.XbaseFactory
import org.eclipse.xtext.xbase.lib.BooleanExtensions
-import org.eclipse.xtext.xbase.lib.ObjectExtensions
/**
* Translates the custom {@link Expression} AST of the scope/export expression DSL into equivalent Xbase
@@ -531,8 +530,9 @@ class ScopeExpressionTranslator {
switch operator {
case '||': toBinaryOperation(xLeft, xRight, BooleanExtensions, 'operator_or', sourceElement)
case '&&': toBinaryOperation(xLeft, xRight, BooleanExtensions, 'operator_and', sourceElement)
- case '==': toBinaryOperation(xLeft, xRight, ObjectExtensions, 'operator_equals', sourceElement)
- case '!=': toBinaryOperation(xLeft, xRight, ObjectExtensions, 'operator_notEquals', sourceElement)
+ // Xbase equality is value-based, unlike the identity equality emitted by the legacy Java generator.
+ case '==': null
+ case '!=': null
default: null
}
}