Skip to content

Commit

Permalink
Support rule recursion and fix cardinality errors (#880)
Browse files Browse the repository at this point in the history
* WIP

* Fixed rule recursion

* Fixed tests

* Fixed tests

* Fixed tests

* Improved messages

* Improved messages

* Improved messages

* Fixed tests

* Fixed merge issues
  • Loading branch information
SimonCockx authored Dec 18, 2024
1 parent 0ce292e commit 625a589
Show file tree
Hide file tree
Showing 106 changed files with 2,369 additions and 3,303 deletions.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ rosetta-profiling/reports/
**/xtend-gen/
**/emf-gen/
**/src/generated/
**/xsemantics-gen/

.antlr-generator-3.2.0-patch.jar
com.regnosys.rosetta.web/
Expand Down
8 changes: 0 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,6 @@ Install the latest version of the "Eclipse IDE for Java and DSL Developers" usin
#### Install the Checkstyle plugin
We use [Checkstyle](https://checkstyle.sourceforge.io/) for enforcing good coding practices. The Eclipse plugin for Checkstyle can be found here: [https://checkstyle.org/eclipse-cs/#!/](https://checkstyle.org/eclipse-cs/#!/).

#### Install the Xsemantics plugin
We use the [Xsemantics DSL](https://github.com/eclipse/xsemantics) to define the type system of Rune. To enable language support for it in Eclipse, follow these steps:
1. Find out which version of Xsemantics you need by looking in the `pom.xml` file of the parent project. There should be a property called `xsemantics.version`.
2. Go to Help > Install New Software...
3. In 'Work with' fill in [https://download.eclipse.org/xsemantics/milestones/](https://download.eclipse.org/xsemantics/milestones/).
4. Install the appropriate version of XSemantics.

#### Setup the project
1. **Open the project in Eclipse**: File > Open Projects from File System..., select the right folder, click Finish.
2. **Update Maven dependencies**: right click on the `com.regnosys.rosetta.parent` project > Maven > Update project... and finish.
Expand All @@ -106,7 +99,6 @@ If you're seeing 1000+ errors in the "Problems" window of Eclipse, try the follo
Support for developing Xtext projects in Intellij is limited. It has no support for
- editing `Xtend` files
- editing the `Xtext` file
- editing the `Xsemantics` file
- running `GenerateRosetta.mwe2`.

You can however let Maven take care of that, and still edit regular Java files, run tests, etc.
Expand Down
18 changes: 0 additions & 18 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@
<xtext.version>2.27.0</xtext.version>
<commons-lang3.version>3.14.0</commons-lang3.version>
<commons-text.version>1.12.0</commons-text.version>
<xsemantics.version>1.22.0</xsemantics.version>
<snakeyaml.version>2.2</snakeyaml.version>
<org.eclipse.emf.ecore.xcore.lib.version>1.6.0</org.eclipse.emf.ecore.xcore.lib.version>
<maven-plugin-api.version>3.9.8</maven-plugin-api.version>
Expand Down Expand Up @@ -275,22 +274,6 @@
<artifactId>org.eclipse.emf.ecore.xcore.lib</artifactId>
<version>${org.eclipse.emf.ecore.xcore.lib.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.xsemantics</groupId>
<artifactId>org.eclipse.xsemantics.runtime</artifactId>
<version>${xsemantics.version}</version>
<exclusions>
<exclusion>
<groupId>ch.qos.reload4j</groupId>
<artifactId>reload4j</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.eclipse.xsemantics</groupId>
<artifactId>org.eclipse.xsemantics.dsl</artifactId>
<version>${xsemantics.version}</version>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
Expand Down Expand Up @@ -451,7 +434,6 @@
<source>${project.basedir}/src-gen/main/java</source>
<source>${project.basedir}/emf-gen/main/java</source>
<source>${project.basedir}/xtend-gen/main/java</source>
<source>${project.basedir}/xsemantics-gen/main/java</source>
</sources>
</configuration>
</execution>
Expand Down
2 changes: 1 addition & 1 deletion rosetta-ide/rosetta.tmLanguage.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ variables:
unambiguousRootEnd: (?={{unambiguousRootStart}})
sectionStart: '{{wordStart}}((post-)?condition|set|add|inputs|output|alias){{wordEnd}}'
sectionEnd: (?={{sectionStart}}|{{rootStart}})(?!\bcondition\b)|(?=\bcondition\b\s*({{identifier}})?:)
functionalOperation: '{{wordStart}}(reduce|filter|map|extract|sort|min|max){{wordEnd}}'
functionalOperation: '{{wordStart}}(reduce|filter|extract|sort|min|max){{wordEnd}}'
listOperationWord: '{{functionalOperation}}|{{wordStart}}(single|multiple|exists|is|absent|only-element|count|flatten|distinct|reverse|first|last|sum){{wordEnd}}'
listOperation: ->>|->|{{listOperationWord}}
synonymAnnotationSimpleSection: '{{wordStart}}(value|meta|definition|pattern|removeHtml|dateFormat|mapper|hint|merge|condition-func|condition-path){{wordEnd}}'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,4 @@ public void fixMandatoryThen(DiagnosticResolutionAcceptor acceptor) {
return List.of(edit);
});
}

@QuickFix(RosettaIssueCodes.DEPRECATED_MAP)
public void fixDeprecatedMap(DiagnosticResolutionAcceptor acceptor) {
acceptor.accept("Replace with `extract`.", (Diagnostic diagnostic, EObject object, Document document) -> {
RosettaUnaryOperation op = (RosettaUnaryOperation)object;
Range range = rangeUtils.getRange(op, ROSETTA_OPERATION__OPERATOR);
String edited = "extract";
TextEdit edit = new TextEdit(range, edited);
return List.of(edit);
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ class ContentAssistTest extends AbstractRosettaLanguageServerTest {
is -> is [[9, 14] .. [9, 14]]
join -> join [[9, 14] .. [9, 14]]
last -> last [[9, 14] .. [9, 14]]
map -> map [[9, 14] .. [9, 14]]
max -> max [[9, 14] .. [9, 14]]
min -> min [[9, 14] .. [9, 14]]
multiple -> multiple [[9, 14] .. [9, 14]]
Expand Down Expand Up @@ -134,7 +133,6 @@ class ContentAssistTest extends AbstractRosettaLanguageServerTest {
join -> join [[7, 27] .. [7, 27]]
last -> last [[7, 27] .. [7, 27]]
library -> library [[7, 27] .. [7, 27]]
map -> map [[7, 27] .. [7, 27]]
max -> max [[7, 27] .. [7, 27]]
metaType -> metaType [[7, 27] .. [7, 27]]
min -> min [[7, 27] .. [7, 27]]
Expand Down Expand Up @@ -219,7 +217,6 @@ class ContentAssistTest extends AbstractRosettaLanguageServerTest {
is -> is [[6, 25] .. [6, 25]]
join -> join [[6, 25] .. [6, 25]]
last -> last [[6, 25] .. [6, 25]]
map -> map [[6, 25] .. [6, 25]]
max -> max [[6, 25] .. [6, 25]]
min -> min [[6, 25] .. [6, 25]]
multiple -> multiple [[6, 25] .. [6, 25]]
Expand Down Expand Up @@ -414,7 +411,6 @@ class ContentAssistTest extends AbstractRosettaLanguageServerTest {
item -> item [[19, 2] .. [19, 2]]
join -> join [[19, 2] .. [19, 2]]
last -> last [[19, 2] .. [19, 2]]
map -> map [[19, 2] .. [19, 2]]
max -> max [[19, 2] .. [19, 2]]
min -> min [[19, 2] .. [19, 2]]
multiple -> multiple [[19, 2] .. [19, 2]]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class QuickFixTest extends AbstractRosettaLanguageServerTest {
'''
testCodeAction[
it.model = model
assertCodeActions = [
it.assertCodeActions = [
assertEquals(2, size)

val sorted = it.sortWith[a,b| ru.comparePositions(a.getRight.diagnostics.head.range.start, b.getRight.diagnostics.head.range.start)]
Expand All @@ -54,37 +54,4 @@ class QuickFixTest extends AbstractRosettaLanguageServerTest {
]
]
}

@Test
def testQuickFixDeprecatedMap() {
val model = '''
namespace foo.bar
type Foo:
a int (1..1)
func Bar:
inputs: foo Foo (1..1)
output: result int (1..1)
set result: foo map a
'''
testCodeAction[
it.model = model
assertCodeActions = [
assertEquals(1, size)

val sorted = it.sortWith[a,b| ru.comparePositions(a.getRight.diagnostics.head.range.start, b.getRight.diagnostics.head.range.start)]

sorted.get(0).getRight => [
assertEquals("Replace with `extract`.", title)
edit.changes.values.head.head => [
assertEquals("extract", newText)
assertEquals(new Position(9, 17), range.start)
assertEquals(new Position(9, 20), range.end)
]
]
]
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class ChangeDetectionTest extends AbstractRosettaLanguageServerValidationTest {
// There should be a type error in func `Foo`
val issues = diagnostics.get(funcsURI)
assertEquals(1, issues.size)
assertEquals("Expected type 'int' but was 'string'", issues.head.message)
assertEquals("Expected type `int`, but got `string` instead. Cannot assign `string` to output `result`", issues.head.message)
}

@Test
Expand Down Expand Up @@ -62,7 +62,7 @@ class ChangeDetectionTest extends AbstractRosettaLanguageServerValidationTest {
// There should be a cardinality error in func `Foo`
val issues = diagnostics.get(funcsURI)
assertEquals(1, issues.size)
assertEquals("Cardinality mismatch - cannot assign list to a single value.", issues.head.message)
assertEquals("Expecting single cardinality. Cannot assign a list to a single value", issues.head.message)
}

@Test
Expand Down Expand Up @@ -107,7 +107,7 @@ class ChangeDetectionTest extends AbstractRosettaLanguageServerValidationTest {
// There should be a type error in func `Foo`
val issues = diagnostics.get(funcsURI)
assertEquals(1, issues.size)
assertEquals("Expected type 'MyType' but was 'MyType'", issues.head.message)
assertEquals("Expected type `foo.MyType`, but got `bar.MyType` instead. Cannot assign `bar.MyType` to output `result`", issues.head.message)
}

@Test
Expand All @@ -133,8 +133,8 @@ class ChangeDetectionTest extends AbstractRosettaLanguageServerValidationTest {

// There should be a type error in rule B
val issues = diagnostics.get(ruleBURI)
assertEquals(2, issues.size)
assertEquals("Expected type 'int' but was 'string'", issues.head.message)
assertEquals(1, issues.size)
assertEquals("Expected type `int`, but got `string` instead. Rule `A` cannot be called with type `string`", issues.head.message)
}

@Test
Expand Down Expand Up @@ -164,6 +164,6 @@ class ChangeDetectionTest extends AbstractRosettaLanguageServerValidationTest {
// There should be a type error in func Foo
val issues = diagnostics.get(funcURI)
assertEquals(1, issues.size)
assertEquals("Expected type 'int' but was 'string'", issues.head.message)
assertEquals("Expected type `int`, but got `string` instead. Cannot assign `string` to output `result`", issues.head.message)
}
}
2 changes: 1 addition & 1 deletion rosetta-lang/model/Rosetta.xcore
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ class RosettaExternalFunction extends RosettaRootElement, RosettaTyped, RosettaC
}
}

class RosettaParameter extends RosettaTyped, RosettaNamed {
class RosettaParameter extends RosettaTyped, RosettaSymbol {
boolean isArray
}

Expand Down
24 changes: 2 additions & 22 deletions rosetta-lang/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,6 @@
<groupId>org.eclipse.xtext</groupId>
<artifactId>org.eclipse.xtext</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.xsemantics</groupId>
<artifactId>org.eclipse.xsemantics.runtime</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.emf</groupId>
<artifactId>org.eclipse.emf.ecore.xcore.lib</artifactId>
Expand Down Expand Up @@ -124,12 +120,6 @@
<include>**/*</include>
</includes>
</fileset>
<fileset>
<directory>${basedir}/../rosetta-lang/xsemantics-gen</directory>
<includes>
<include>**/*</include>
</includes>
</fileset>
<fileset>
<directory>${basedir}/../rosetta-lang/xtend-gen</directory>
<includes>
Expand Down Expand Up @@ -184,7 +174,7 @@
<artifactId>xtext-maven-plugin</artifactId>
<executions>
<execution>
<id>xcore-and-xsemantics-generator</id>
<id>xcore-generator</id>
<phase>generate-sources</phase>
<goals>
<goal>generate</goal>
Expand All @@ -194,7 +184,7 @@
<configuration>
<compilerSourceLevel>${maven.compiler.release}</compilerSourceLevel>
<compilerTargetLevel>${maven.compiler.release}</compilerTargetLevel>
<!-- TODO: uncomment this line for faster builds once we upgrade Xtext. Also remove the `emf` and `xsemantics` folder from the clean plugin. -->
<!-- TODO: uncomment this line for faster builds once we upgrade Xtext. Also remove the `emf` folder from the clean plugin. -->
<!-- <incrementalXtextBuild>true</incrementalXtextBuild> -->
<!--<classPathLookupFilter>^.*\.(emf|xtext).*$</classPathLookupFilter>-->
<addOutputDirectoriesToCompileSourceRoots>true</addOutputDirectoriesToCompileSourceRoots>
Expand Down Expand Up @@ -224,16 +214,6 @@
</outputConfiguration>
</outputConfigurations>
</language>
<language>
<setup>
org.eclipse.xsemantics.dsl.XsemanticsStandaloneSetup</setup>
<outputConfigurations>
<outputConfiguration>
<outputDirectory>
${project.basedir}/xsemantics-gen/main/java</outputDirectory>
</outputConfiguration>
</outputConfigurations>
</language>
</languages>
</configuration>
<dependencies>
Expand Down
6 changes: 3 additions & 3 deletions rosetta-lang/src/main/java/com/regnosys/rosetta/Rosetta.xtext
Original file line number Diff line number Diff line change
Expand Up @@ -683,7 +683,7 @@ UnaryOperation returns RosettaExpression:
({ReduceOperation.argument=current} operator='reduce')
|({FilterOperation.argument=current} operator='filter')
// @Compat: remove `map`
|({MapOperation.argument=current} operator=('map' | 'extract'))
|({MapOperation.argument=current} operator='extract')
) (function=InlineFunction|=>function=ImplicitInlineFunction)?
)*
| // Without left parameter:
Expand Down Expand Up @@ -720,7 +720,7 @@ UnaryOperation returns RosettaExpression:
({ReduceOperation} operator='reduce')
|({FilterOperation} operator='filter')
// @Compat: remove `map`
|({MapOperation} operator=('map' | 'extract'))
|({MapOperation} operator='extract')
) (function=InlineFunction|=>function=ImplicitInlineFunction)?
)
(
Expand Down Expand Up @@ -758,7 +758,7 @@ UnaryOperation returns RosettaExpression:
({ReduceOperation.argument=current} operator='reduce')
|({FilterOperation.argument=current} operator='filter')
// @Compat: remove `map`
|({MapOperation.argument=current} operator=('map' | 'extract'))
|({MapOperation.argument=current} operator='extract')
) (function=InlineFunction|=>function=ImplicitInlineFunction)?
)*
;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,14 @@ import org.eclipse.xtext.resource.impl.DefaultResourceDescriptionStrategy
import org.eclipse.xtext.parser.IEncodingProvider
import com.google.inject.Binder
import org.eclipse.xtext.service.DispatchingProvider
import com.regnosys.rosetta.utils.ImplicitVariableUtil
import org.eclipse.xsemantics.runtime.validation.XsemanticsValidatorFilter
import com.regnosys.rosetta.validation.RetainXsemanticsIssuesOnGeneratedInputsFilter
import org.eclipse.xtext.conversion.IValueConverterService
import com.regnosys.rosetta.parsing.RosettaValueConverterService
import com.regnosys.rosetta.parsing.BigDecimalConverter
import com.regnosys.rosetta.transgest.ModelLoader
import com.regnosys.rosetta.transgest.ModelLoaderImpl
import com.regnosys.rosetta.formatting2.RosettaExpressionFormatter
import com.regnosys.rosetta.formatting2.FormattingUtil
import javax.inject.Provider
import com.regnosys.rosetta.generator.java.util.RecordJavaUtil
import com.regnosys.rosetta.serialization.RosettaTransientValueService
import org.eclipse.xtext.parsetree.reconstr.ITransientValueService
import com.regnosys.rosetta.resource.RosettaResource
import com.regnosys.rosetta.typing.RosettaTyping
import com.regnosys.rosetta.typing.RosettaTypingAuxiliary
import com.regnosys.rosetta.typing.RosettaTypingChecking
import org.eclipse.xtext.validation.IResourceValidator
import com.regnosys.rosetta.validation.CachingResourceValidator
import com.regnosys.rosetta.config.RosettaConfiguration
Expand All @@ -53,16 +43,6 @@ import com.regnosys.rosetta.formatting2.ResourceFormatterService
/* Use this class to register components to be used at runtime / without the Equinox extension registry.*/
class RosettaRuntimeModule extends AbstractRosettaRuntimeModule {

def void configureXsemanticsTypeSystem(Binder binder) {
// During a language server build, the following three classes are injected over and over again
// for each Rosetta resource. This means that code generation is spending up to 54% of its time
// just injecting these classes. By binding them as singletons, this time virtually disappears
// since they will only be instantiated once.
binder.bind(RosettaTyping).asEagerSingleton
binder.bind(RosettaTypingAuxiliary).asEagerSingleton
binder.bind(RosettaTypingChecking).asEagerSingleton
}

override Class<? extends IFragmentProvider> bindIFragmentProvider() {
RosettaFragmentProvider
}
Expand Down Expand Up @@ -101,19 +81,9 @@ class RosettaRuntimeModule extends AbstractRosettaRuntimeModule {
.to(UTF8EncodingProvider);
}

def Class<? extends ImplicitVariableUtil> bindImplicitVariableUtil() {
ImplicitVariableUtil
}
def Class<? extends XsemanticsValidatorFilter> bindXsemanticsValidatorFilter() {
RetainXsemanticsIssuesOnGeneratedInputsFilter
}

override Class<? extends IValueConverterService> bindIValueConverterService() {
RosettaValueConverterService
}
def Class<? extends BigDecimalConverter> bindBigDecimalConverter() {
BigDecimalConverter
}

override Class<? extends XtextResource> bindXtextResource() {
RosettaResource
Expand All @@ -126,19 +96,6 @@ class RosettaRuntimeModule extends AbstractRosettaRuntimeModule {
ModelLoaderImpl
}


def Class<? extends RosettaExpressionFormatter> bindRosettaExpressionFormatter() {
RosettaExpressionFormatter
}

def Class<? extends FormattingUtil> bindFormattingUtil() {
FormattingUtil
}

def Class<? extends RecordJavaUtil> bindRecordFeatureMap() {
RecordJavaUtil
}

def Class<? extends IResourceValidator> bindIResourceValidator() {
CachingResourceValidator
}
Expand Down
Loading

0 comments on commit 625a589

Please sign in to comment.