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 } }