aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2024-02-02 16:28:19 +0100
committerLibravatar Kristóf Marussy <kristof@marussy.com>2024-02-02 17:36:24 +0100
commit2dfcb286216419976368ad926f8ac7f018aa2bf9 (patch)
treeb9d235ebf2049e42e58126e743c782333d64681a /subprojects
parentrefactor: serialize solutions as modules (diff)
downloadrefinery-2dfcb286216419976368ad926f8ac7f018aa2bf9.tar.gz
refinery-2dfcb286216419976368ad926f8ac7f018aa2bf9.tar.zst
refinery-2dfcb286216419976368ad926f8ac7f018aa2bf9.zip
refactor(language): name disambiguation
* Use fully qualified names starting with :: (as in C++) to unambiguously refer to an element. * Name shadowing within modules.
Diffstat (limited to 'subprojects')
-rw-r--r--subprojects/frontend/src/editor/EditorTheme.ts4
-rw-r--r--subprojects/frontend/src/language/problem.grammar11
-rw-r--r--subprojects/frontend/src/language/problemLanguageSupport.ts4
-rw-r--r--subprojects/frontend/src/language/tokens.ts46
-rw-r--r--subprojects/frontend/src/xtext/ContentAssistService.ts8
-rw-r--r--subprojects/frontend/types/grammar.d.ts6
-rw-r--r--subprojects/language-ide/src/main/java/tools/refinery/language/ide/contentassist/ProblemCrossrefProposalProvider.java22
-rw-r--r--subprojects/language-ide/src/main/java/tools/refinery/language/ide/syntaxcoloring/ProblemSemanticHighlightingCalculator.java2
-rw-r--r--subprojects/language-model/problem.aird92
-rw-r--r--subprojects/language-model/problem.aird.license2
-rw-r--r--subprojects/language-model/src/main/resources/model/problem.ecore4
-rw-r--r--subprojects/language-model/src/main/resources/model/problem.ecore.license2
-rw-r--r--subprojects/language-model/src/main/resources/model/problem.genmodel4
-rw-r--r--subprojects/language-model/src/main/resources/model/problem.genmodel.license2
-rw-r--r--subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ModelInitializer.java4
-rw-r--r--subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ProblemTraceImpl.java4
-rw-r--r--subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/SemanticsUtils.java10
-rw-r--r--subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/PartialInterpretation2Json.java4
-rw-r--r--subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/metadata/MetadataCreator.java21
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/Problem.xtext14
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/ProblemRuntimeModule.java23
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/naming/NamingUtil.java33
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/naming/ProblemDelegateQualifiedNameProvider.java18
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/naming/ProblemQualifiedNameConverter.java2
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/naming/ProblemQualifiedNameProvider.java41
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/parser/antlr/IdentifierTokenProvider.java25
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/parser/antlr/ProblemTokenSource.java38
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescription.java106
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionManager.java19
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionStrategy.java62
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/scoping/NormalizedSelectable.java122
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemGlobalScopeProvider.java23
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemLocalScopeProvider.java49
33 files changed, 697 insertions, 130 deletions
diff --git a/subprojects/frontend/src/editor/EditorTheme.ts b/subprojects/frontend/src/editor/EditorTheme.ts
index f499b0d9..4978c7f7 100644
--- a/subprojects/frontend/src/editor/EditorTheme.ts
+++ b/subprojects/frontend/src/editor/EditorTheme.ts
@@ -40,7 +40,9 @@ function createTypeHashStyles(
40export default styled('div', { 40export default styled('div', {
41 name: 'EditorTheme', 41 name: 'EditorTheme',
42 shouldForwardProp: (propName) => 42 shouldForwardProp: (propName) =>
43 propName !== 'showLineNumbers' && propName !== 'showActiveLine', 43 propName !== 'showLineNumbers' &&
44 propName !== 'showActiveLine' &&
45 propName !== 'colorIdentifiers',
44})<{ 46})<{
45 showLineNumbers: boolean; 47 showLineNumbers: boolean;
46 showActiveLine: boolean; 48 showActiveLine: boolean;
diff --git a/subprojects/frontend/src/language/problem.grammar b/subprojects/frontend/src/language/problem.grammar
index ac451e7a..e39ce102 100644
--- a/subprojects/frontend/src/language/problem.grammar
+++ b/subprojects/frontend/src/language/problem.grammar
@@ -1,5 +1,5 @@
1/* 1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> 2 * SPDX-FileCopyrightText: 2021-2024 The Refinery Authors <https://refinery.tools/>
3 * 3 *
4 * SPDX-License-Identifier: EPL-2.0 4 * SPDX-License-Identifier: EPL-2.0
5 */ 5 */
@@ -22,6 +22,9 @@
22@top Problem { statement* } 22@top Problem { statement* }
23 23
24statement { 24statement {
25 ImportStatement {
26 kw<"import"> QualifiedName (kw<"as"> identifier)? "."
27 } |
25 Assertion { 28 Assertion {
26 kw<"default">? (NotOp | UnknownOp)? RelationName 29 kw<"default">? (NotOp | UnknownOp)? RelationName
27 ParameterList<AssertionArgument> 30 ParameterList<AssertionArgument>
@@ -170,7 +173,7 @@ VariableName[@dynamicPrecedence=10] { QualifiedName ~name }
170 173
171NodeName { QualifiedName } 174NodeName { QualifiedName }
172 175
173QualifiedName[implicitCompletion=true] { identifier ("::" identifier)* } 176QualifiedName[implicitCompletion=true] { "::"? identifier (QualifiedNameSeparator "::" identifier)* }
174 177
175kw<term> { @specialize[@name={term},implicitCompletion=true]<identifier, term> } 178kw<term> { @specialize[@name={term},implicitCompletion=true]<identifier, term> }
176 179
@@ -184,6 +187,10 @@ sep1<separator, content> { content (separator content)* }
184 187
185@skip { LineComment | BlockComment | whitespace } 188@skip { LineComment | BlockComment | whitespace }
186 189
190@external tokens qualifiedNameSeparator from "./tokens" {
191 QualifiedNameSeparator
192}
193
187@tokens { 194@tokens {
188 whitespace { std.whitespace+ } 195 whitespace { std.whitespace+ }
189 196
diff --git a/subprojects/frontend/src/language/problemLanguageSupport.ts b/subprojects/frontend/src/language/problemLanguageSupport.ts
index 2483c2e3..aebccfa4 100644
--- a/subprojects/frontend/src/language/problemLanguageSupport.ts
+++ b/subprojects/frontend/src/language/problemLanguageSupport.ts
@@ -1,5 +1,5 @@
1/* 1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> 2 * SPDX-FileCopyrightText: 2021-2024 The Refinery Authors <https://refinery.tools/>
3 * 3 *
4 * SPDX-License-Identifier: EPL-2.0 4 * SPDX-License-Identifier: EPL-2.0
5 */ 5 */
@@ -28,7 +28,7 @@ const parserWithMetadata = parser.configure({
28 LineComment: t.lineComment, 28 LineComment: t.lineComment,
29 BlockComment: t.blockComment, 29 BlockComment: t.blockComment,
30 'module problem class enum pred fn scope': t.definitionKeyword, 30 'module problem class enum pred fn scope': t.definitionKeyword,
31 'declare atom multi': t.definitionKeyword, 31 'import as declare atom multi': t.definitionKeyword,
32 'abstract extends refers contains container opposite': t.modifier, 32 'abstract extends refers contains container opposite': t.modifier,
33 'default error contained containment': t.modifier, 33 'default error contained containment': t.modifier,
34 'true false unknown error': t.keyword, 34 'true false unknown error': t.keyword,
diff --git a/subprojects/frontend/src/language/tokens.ts b/subprojects/frontend/src/language/tokens.ts
new file mode 100644
index 00000000..b5d022f1
--- /dev/null
+++ b/subprojects/frontend/src/language/tokens.ts
@@ -0,0 +1,46 @@
1/*
2 * Copyright (C) 2018-2024 by Marijn Haverbeke <marijnh@gmail.com> and others
3 * Copyright (C) 2024 The Refinery Authors <https://refinery.tools/>
4 *
5 * SPDX-License-Identifier: MIT AND EPL-2.0
6 *
7 * Based on the CSS tokenizer at
8 * https://github.com/lezer-parser/css/blob/790568c968a660a94bf0fbd97a86c66da1c529e5/src/tokens.js
9 */
10
11import { ExternalTokenizer } from '@lezer/lr';
12
13/* eslint-disable-next-line import/no-unresolved --
14 Synthetic import from `@lezer/generator/rollup` cannot be found by ESLint.
15*/
16import { QualifiedNameSeparator } from './problem.grammar.terms';
17
18const colon = 58;
19const underscore = 95;
20
21function isAlpha(ch: number) {
22 return (ch >= 65 && ch <= 90) || (ch >= 97 && ch <= 122) || ch >= 161;
23}
24
25function isDigit(ch: number) {
26 return ch >= 48 && ch <= 57;
27}
28
29function isIdentifier(ch: number) {
30 return isAlpha(ch) || isDigit(ch) || ch === underscore;
31}
32
33/* eslint-disable-next-line import/prefer-default-export --
34 Lezer requires a named export.
35*/
36export const qualifiedNameSeparator = new ExternalTokenizer((input) => {
37 if (input.peek(0) === colon && input.peek(1) === colon) {
38 const previous = input.peek(-1);
39 if (isIdentifier(previous)) {
40 // Inject an extra 0-length token into the token stream to let Lezer
41 // consume the `::` on its own. Explicitly consuming 2 characters here
42 // leads to inconsistent highlighting of qualified names.
43 input.acceptToken(QualifiedNameSeparator);
44 }
45 }
46});
diff --git a/subprojects/frontend/src/xtext/ContentAssistService.ts b/subprojects/frontend/src/xtext/ContentAssistService.ts
index ac8ab36a..d42dde14 100644
--- a/subprojects/frontend/src/xtext/ContentAssistService.ts
+++ b/subprojects/frontend/src/xtext/ContentAssistService.ts
@@ -1,5 +1,5 @@
1/* 1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> 2 * SPDX-FileCopyrightText: 2021-2024 The Refinery Authors <https://refinery.tools/>
3 * 3 *
4 * SPDX-License-Identifier: EPL-2.0 4 * SPDX-License-Identifier: EPL-2.0
5 */ 5 */
@@ -108,7 +108,11 @@ function createCompletion(entry: ContentAssistEntry): Completion {
108 boost = -90; 108 boost = -90;
109 break; 109 break;
110 default: 110 default:
111 { 111 if (entry.proposal.startsWith('::')) {
112 // Move absolute names below relative names,
113 // they should only be preferred if no relative name is available.
114 boost = -60;
115 } else {
112 // Penalize qualified names (vs available unqualified names). 116 // Penalize qualified names (vs available unqualified names).
113 const extraSegments = entry.proposal.match(/::/g)?.length || 0; 117 const extraSegments = entry.proposal.match(/::/g)?.length || 0;
114 boost = Math.max(-5 * extraSegments, -50); 118 boost = Math.max(-5 * extraSegments, -50);
diff --git a/subprojects/frontend/types/grammar.d.ts b/subprojects/frontend/types/grammar.d.ts
index 92f99ec3..c7848e33 100644
--- a/subprojects/frontend/types/grammar.d.ts
+++ b/subprojects/frontend/types/grammar.d.ts
@@ -1,6 +1,6 @@
1/* 1/*
2 * Copyright (C) 2018 by Marijn Haverbeke <marijn@haverbeke.berlin> and others 2 * Copyright (C) 2018 by Marijn Haverbeke <marijn@haverbeke.berlin> and others
3 * Copyright (C) 2021-2023 The Refinery Authors <https://refinery.tools/> 3 * Copyright (C) 2021-2024 The Refinery Authors <https://refinery.tools/>
4 * 4 *
5 * SPDX-License-Identifier: MIT 5 * SPDX-License-Identifier: MIT
6 */ 6 */
@@ -10,3 +10,7 @@ declare module '*.grammar' {
10 10
11 export const parser: LRParser; 11 export const parser: LRParser;
12} 12}
13
14declare module '*.terms' {
15 export const QualifiedNameSeparator: number;
16}
diff --git a/subprojects/language-ide/src/main/java/tools/refinery/language/ide/contentassist/ProblemCrossrefProposalProvider.java b/subprojects/language-ide/src/main/java/tools/refinery/language/ide/contentassist/ProblemCrossrefProposalProvider.java
index 166b4400..fa04a012 100644
--- a/subprojects/language-ide/src/main/java/tools/refinery/language/ide/contentassist/ProblemCrossrefProposalProvider.java
+++ b/subprojects/language-ide/src/main/java/tools/refinery/language/ide/contentassist/ProblemCrossrefProposalProvider.java
@@ -1,5 +1,5 @@
1/* 1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> 2 * SPDX-FileCopyrightText: 2021-2024 The Refinery Authors <https://refinery.tools/>
3 * 3 *
4 * SPDX-License-Identifier: EPL-2.0 4 * SPDX-License-Identifier: EPL-2.0
5 */ 5 */
@@ -15,13 +15,14 @@ import org.eclipse.xtext.EcoreUtil2;
15import org.eclipse.xtext.GrammarUtil; 15import org.eclipse.xtext.GrammarUtil;
16import org.eclipse.xtext.ide.editor.contentassist.ContentAssistContext; 16import org.eclipse.xtext.ide.editor.contentassist.ContentAssistContext;
17import org.eclipse.xtext.ide.editor.contentassist.IdeCrossrefProposalProvider; 17import org.eclipse.xtext.ide.editor.contentassist.IdeCrossrefProposalProvider;
18import org.eclipse.xtext.naming.QualifiedName;
19import org.eclipse.xtext.nodemodel.util.NodeModelUtils; 18import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
20import org.eclipse.xtext.resource.IEObjectDescription; 19import org.eclipse.xtext.resource.IEObjectDescription;
21import org.eclipse.xtext.scoping.IScope; 20import org.eclipse.xtext.scoping.IScope;
22import org.eclipse.xtext.xtext.CurrentTypeFinder; 21import org.eclipse.xtext.xtext.CurrentTypeFinder;
23import org.jetbrains.annotations.Nullable; 22import org.jetbrains.annotations.Nullable;
24import tools.refinery.language.model.problem.*; 23import tools.refinery.language.model.problem.*;
24import tools.refinery.language.naming.NamingUtil;
25import tools.refinery.language.naming.ProblemQualifiedNameConverter;
25import tools.refinery.language.resource.ProblemResourceDescriptionStrategy; 26import tools.refinery.language.resource.ProblemResourceDescriptionStrategy;
26import tools.refinery.language.utils.BuiltinSymbols; 27import tools.refinery.language.utils.BuiltinSymbols;
27import tools.refinery.language.utils.ProblemDesugarer; 28import tools.refinery.language.utils.ProblemDesugarer;
@@ -46,14 +47,12 @@ public class ProblemCrossrefProposalProvider extends IdeCrossrefProposalProvider
46 @Override 47 @Override
47 protected Iterable<IEObjectDescription> queryScope(IScope scope, CrossReference crossReference, 48 protected Iterable<IEObjectDescription> queryScope(IScope scope, CrossReference crossReference,
48 ContentAssistContext context) { 49 ContentAssistContext context) {
49 var eObjectDescriptionsByName = new HashMap<QualifiedName, List<IEObjectDescription>>(); 50 var eObjectDescriptionsByName = new HashMap<ProblemResourceDescriptionStrategy.ShadowingKey,
51 List<IEObjectDescription>>();
50 for (var candidate : super.queryScope(scope, crossReference, context)) { 52 for (var candidate : super.queryScope(scope, crossReference, context)) {
51 if (isExistingObject(candidate, crossReference, context)) { 53 if (isExistingObject(candidate, crossReference, context)) {
52 // {@code getQualifiedName()} will refer to the full name for objects that are loaded from the global 54 var shadowingKey = ProblemResourceDescriptionStrategy.getShadowingKey(candidate);
53 // scope, but {@code getName()} returns the qualified name that we set in 55 var candidateList = eObjectDescriptionsByName.computeIfAbsent(shadowingKey,
54 // {@code ProblemResourceDescriptionStrategy}.
55 var qualifiedName = candidate.getName();
56 var candidateList = eObjectDescriptionsByName.computeIfAbsent(qualifiedName,
57 ignored -> new ArrayList<>()); 56 ignored -> new ArrayList<>());
58 candidateList.add(candidate); 57 candidateList.add(candidate);
59 } 58 }
@@ -61,7 +60,7 @@ public class ProblemCrossrefProposalProvider extends IdeCrossrefProposalProvider
61 var eObjectDescriptions = new ArrayList<IEObjectDescription>(); 60 var eObjectDescriptions = new ArrayList<IEObjectDescription>();
62 for (var candidates : eObjectDescriptionsByName.values()) { 61 for (var candidates : eObjectDescriptionsByName.values()) {
63 if (candidates.size() == 1) { 62 if (candidates.size() == 1) {
64 var candidate = candidates.get(0); 63 var candidate = candidates.getFirst();
65 if (shouldBeVisible(candidate, crossReference, context)) { 64 if (shouldBeVisible(candidate, crossReference, context)) {
66 eObjectDescriptions.add(candidate); 65 eObjectDescriptions.add(candidate);
67 } 66 }
@@ -96,6 +95,11 @@ public class ProblemCrossrefProposalProvider extends IdeCrossrefProposalProvider
96 95
97 protected boolean shouldBeVisible(IEObjectDescription candidate, CrossReference crossReference, 96 protected boolean shouldBeVisible(IEObjectDescription candidate, CrossReference crossReference,
98 ContentAssistContext context) { 97 ContentAssistContext context) {
98 if (NamingUtil.isFullyQualified(candidate.getName()) &&
99 !context.getPrefix().startsWith(ProblemQualifiedNameConverter.DELIMITER)) {
100 // Do not propose names with a root prefix unless explicitly asked for.
101 return false;
102 }
99 var errorPredicate = candidate.getUserData(ProblemResourceDescriptionStrategy.ERROR_PREDICATE); 103 var errorPredicate = candidate.getUserData(ProblemResourceDescriptionStrategy.ERROR_PREDICATE);
100 if (ProblemResourceDescriptionStrategy.ERROR_PREDICATE_TRUE.equals(errorPredicate)) { 104 if (ProblemResourceDescriptionStrategy.ERROR_PREDICATE_TRUE.equals(errorPredicate)) {
101 return false; 105 return false;
diff --git a/subprojects/language-ide/src/main/java/tools/refinery/language/ide/syntaxcoloring/ProblemSemanticHighlightingCalculator.java b/subprojects/language-ide/src/main/java/tools/refinery/language/ide/syntaxcoloring/ProblemSemanticHighlightingCalculator.java
index 94a61c4f..891c73c7 100644
--- a/subprojects/language-ide/src/main/java/tools/refinery/language/ide/syntaxcoloring/ProblemSemanticHighlightingCalculator.java
+++ b/subprojects/language-ide/src/main/java/tools/refinery/language/ide/syntaxcoloring/ProblemSemanticHighlightingCalculator.java
@@ -99,7 +99,7 @@ public class ProblemSemanticHighlightingCalculator extends DefaultSemanticHighli
99 99
100 protected String[] getHighlightClass(EObject eObject, EReference reference) { 100 protected String[] getHighlightClass(EObject eObject, EReference reference) {
101 boolean isError = ProblemUtil.isError(eObject); 101 boolean isError = ProblemUtil.isError(eObject);
102 if (ProblemUtil.isBuiltIn(eObject)) { 102 if (ProblemUtil.isBuiltIn(eObject) && !(eObject instanceof Problem)) {
103 var className = isError ? ERROR_CLASS : BUILTIN_CLASS; 103 var className = isError ? ERROR_CLASS : BUILTIN_CLASS;
104 return new String[]{className}; 104 return new String[]{className};
105 } 105 }
diff --git a/subprojects/language-model/problem.aird b/subprojects/language-model/problem.aird
index a3420a04..6583d364 100644
--- a/subprojects/language-model/problem.aird
+++ b/subprojects/language-model/problem.aird
@@ -7,7 +7,7 @@
7 <semanticResources>build/resources/main/model/problem.genmodel</semanticResources> 7 <semanticResources>build/resources/main/model/problem.genmodel</semanticResources>
8 <ownedViews xmi:type="viewpoint:DView" uid="_CsAAYKA4EeuqkpDnuik1sg"> 8 <ownedViews xmi:type="viewpoint:DView" uid="_CsAAYKA4EeuqkpDnuik1sg">
9 <viewpoint xmi:type="description:Viewpoint" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']"/> 9 <viewpoint xmi:type="description:Viewpoint" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']"/>
10 <ownedRepresentationDescriptors xmi:type="viewpoint:DRepresentationDescriptor" uid="_CsYa4KA4EeuqkpDnuik1sg" name="declarations" repPath="#_CsUwgKA4EeuqkpDnuik1sg" changeId="1706658830410"> 10 <ownedRepresentationDescriptors xmi:type="viewpoint:DRepresentationDescriptor" uid="_CsYa4KA4EeuqkpDnuik1sg" name="declarations" repPath="#_CsUwgKA4EeuqkpDnuik1sg" changeId="1706722245720">
11 <description xmi:type="description_1:DiagramDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']"/> 11 <description xmi:type="description_1:DiagramDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']"/>
12 <target xmi:type="ecore:EPackage" href="src/main/resources/model/problem.ecore#/"/> 12 <target xmi:type="ecore:EPackage" href="src/main/resources/model/problem.ecore#/"/>
13 </ownedRepresentationDescriptors> 13 </ownedRepresentationDescriptors>
@@ -478,6 +478,19 @@
478 <styles xmi:type="notation:ShapeStyle" xmi:id="_aq0Q0b_KEe6Mo_-4--GvQg" fontName="Noto Sans" fontHeight="8"/> 478 <styles xmi:type="notation:ShapeStyle" xmi:id="_aq0Q0b_KEe6Mo_-4--GvQg" fontName="Noto Sans" fontHeight="8"/>
479 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_aq0Q0r_KEe6Mo_-4--GvQg" x="131" y="716" width="120" height="100"/> 479 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_aq0Q0r_KEe6Mo_-4--GvQg" x="131" y="716" width="120" height="100"/>
480 </children> 480 </children>
481 <children xmi:type="notation:Node" xmi:id="_GTtOgMBeEe6Mo_-4--GvQg" type="2003" element="_GTZsgMBeEe6Mo_-4--GvQg">
482 <children xmi:type="notation:Node" xmi:id="_GTtOg8BeEe6Mo_-4--GvQg" type="5007"/>
483 <children xmi:type="notation:Node" xmi:id="_GTtOhMBeEe6Mo_-4--GvQg" type="7004">
484 <children xmi:type="notation:Node" xmi:id="_dTgyIMBeEe6Mo_-4--GvQg" type="3010" element="_dTNQIMBeEe6Mo_-4--GvQg">
485 <styles xmi:type="notation:FontStyle" xmi:id="_dTgyIcBeEe6Mo_-4--GvQg" fontColor="2697711" fontName="Noto Sans" fontHeight="8"/>
486 <layoutConstraint xmi:type="notation:Location" xmi:id="_dTgyIsBeEe6Mo_-4--GvQg"/>
487 </children>
488 <styles xmi:type="notation:SortingStyle" xmi:id="_GTtOhcBeEe6Mo_-4--GvQg"/>
489 <styles xmi:type="notation:FilteringStyle" xmi:id="_GTtOhsBeEe6Mo_-4--GvQg"/>
490 </children>
491 <styles xmi:type="notation:ShapeStyle" xmi:id="_GTtOgcBeEe6Mo_-4--GvQg" fontName="Noto Sans" fontHeight="8"/>
492 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_GTtOgsBeEe6Mo_-4--GvQg" x="-48" y="524" width="120" height="100"/>
493 </children>
481 <styles xmi:type="notation:DiagramStyle" xmi:id="_CsZB8qA4EeuqkpDnuik1sg"/> 494 <styles xmi:type="notation:DiagramStyle" xmi:id="_CsZB8qA4EeuqkpDnuik1sg"/>
482 <edges xmi:type="notation:Edge" xmi:id="_4eaYwKA8EeuqkpDnuik1sg" type="4001" element="_4eU5TqA8EeuqkpDnuik1sg" source="_D1D6MKA4EeuqkpDnuik1sg" target="_xsq_MKA8EeuqkpDnuik1sg"> 495 <edges xmi:type="notation:Edge" xmi:id="_4eaYwKA8EeuqkpDnuik1sg" type="4001" element="_4eU5TqA8EeuqkpDnuik1sg" source="_D1D6MKA4EeuqkpDnuik1sg" target="_xsq_MKA8EeuqkpDnuik1sg">
483 <children xmi:type="notation:Node" xmi:id="_4ea_06A8EeuqkpDnuik1sg" type="6001"> 496 <children xmi:type="notation:Node" xmi:id="_4ea_06A8EeuqkpDnuik1sg" type="6001">
@@ -1357,6 +1370,38 @@
1357 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_8s_6QDrXEe62Q_vL_UTCsA" id="(0.3586206896551724,0.0)"/> 1370 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_8s_6QDrXEe62Q_vL_UTCsA" id="(0.3586206896551724,0.0)"/>
1358 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_8s_6QTrXEe62Q_vL_UTCsA" id="(0.5338983050847458,1.0)"/> 1371 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_8s_6QTrXEe62Q_vL_UTCsA" id="(0.5338983050847458,1.0)"/>
1359 </edges> 1372 </edges>
1373 <edges xmi:type="notation:Edge" xmi:id="_UVB0MMBeEe6Mo_-4--GvQg" type="4001" element="_UU0Y5cBeEe6Mo_-4--GvQg" source="_GTtOgMBeEe6Mo_-4--GvQg" target="_e73WIKA9EeuqkpDnuik1sg">
1374 <children xmi:type="notation:Node" xmi:id="_UVB0NMBeEe6Mo_-4--GvQg" type="6001">
1375 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_UVB0NcBeEe6Mo_-4--GvQg" y="-10"/>
1376 </children>
1377 <children xmi:type="notation:Node" xmi:id="_UVB0NsBeEe6Mo_-4--GvQg" type="6002">
1378 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_UVB0N8BeEe6Mo_-4--GvQg" y="10"/>
1379 </children>
1380 <children xmi:type="notation:Node" xmi:id="_UVB0OMBeEe6Mo_-4--GvQg" type="6003">
1381 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_UVB0OcBeEe6Mo_-4--GvQg" y="10"/>
1382 </children>
1383 <styles xmi:type="notation:ConnectorStyle" xmi:id="_UVB0McBeEe6Mo_-4--GvQg" routing="Tree"/>
1384 <styles xmi:type="notation:FontStyle" xmi:id="_UVB0MsBeEe6Mo_-4--GvQg" fontName="Noto Sans" fontHeight="8"/>
1385 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_UVB0M8BeEe6Mo_-4--GvQg" points="[0, 0, -706, 180]$[706, -180, 0, 0]"/>
1386 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_UVCbQMBeEe6Mo_-4--GvQg" id="(0.635593220338983,0.030612244897959183)"/>
1387 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_UVCbQcBeEe6Mo_-4--GvQg" id="(0.5,0.0)"/>
1388 </edges>
1389 <edges xmi:type="notation:Edge" xmi:id="_bYblgMBeEe6Mo_-4--GvQg" type="4001" element="_bYOKL8BeEe6Mo_-4--GvQg" source="_GTtOgMBeEe6Mo_-4--GvQg" target="_D1D6MKA4EeuqkpDnuik1sg">
1390 <children xmi:type="notation:Node" xmi:id="_bYblhMBeEe6Mo_-4--GvQg" type="6001">
1391 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_bYblhcBeEe6Mo_-4--GvQg" x="-13" y="-10"/>
1392 </children>
1393 <children xmi:type="notation:Node" xmi:id="_bYblhsBeEe6Mo_-4--GvQg" type="6002">
1394 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_bYblh8BeEe6Mo_-4--GvQg" x="4" y="10"/>
1395 </children>
1396 <children xmi:type="notation:Node" xmi:id="_bYbliMBeEe6Mo_-4--GvQg" type="6003">
1397 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_bYblicBeEe6Mo_-4--GvQg" x="-4" y="10"/>
1398 </children>
1399 <styles xmi:type="notation:ConnectorStyle" xmi:id="_bYblgcBeEe6Mo_-4--GvQg" routing="Rectilinear"/>
1400 <styles xmi:type="notation:FontStyle" xmi:id="_bYblgsBeEe6Mo_-4--GvQg" fontColor="7490599" fontName="Noto Sans" fontHeight="8"/>
1401 <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_bYblg8BeEe6Mo_-4--GvQg" points="[0, 0, -778, 292]$[0, -293, -778, -1]$[776, -293, -2, -1]"/>
1402 <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_bYblisBeEe6Mo_-4--GvQg" id="(0.4067796610169492,0.0)"/>
1403 <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_bYbli8BeEe6Mo_-4--GvQg" id="(0.01694915254237288,0.5714285714285714)"/>
1404 </edges>
1360 </data> 1405 </data>
1361 </ownedAnnotationEntries> 1406 </ownedAnnotationEntries>
1362 <ownedAnnotationEntries xmi:type="description:AnnotationEntry" uid="_Csiy8KA4EeuqkpDnuik1sg" source="DANNOTATION_CUSTOMIZATION_KEY"> 1407 <ownedAnnotationEntries xmi:type="description:AnnotationEntry" uid="_Csiy8KA4EeuqkpDnuik1sg" source="DANNOTATION_CUSTOMIZATION_KEY">
@@ -1372,7 +1417,7 @@
1372 </computedStyleDescriptions> 1417 </computedStyleDescriptions>
1373 </data> 1418 </data>
1374 </ownedAnnotationEntries> 1419 </ownedAnnotationEntries>
1375 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_D05iIKA4EeuqkpDnuik1sg" name="Problem" tooltipText="" outgoingEdges="_4eU5TqA8EeuqkpDnuik1sg _hU64ZqA9EeuqkpDnuik1sg _m-6OTNXtEeuF_d0WEhR3Xw" width="12" height="10"> 1420 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_D05iIKA4EeuqkpDnuik1sg" name="Problem" tooltipText="" outgoingEdges="_4eU5TqA8EeuqkpDnuik1sg _hU64ZqA9EeuqkpDnuik1sg _m-6OTNXtEeuF_d0WEhR3Xw" incomingEdges="_bYOKL8BeEe6Mo_-4--GvQg" width="12" height="10">
1376 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Problem"/> 1421 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Problem"/>
1377 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Problem"/> 1422 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Problem"/>
1378 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints> 1423 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
@@ -1578,7 +1623,7 @@
1578 </ownedStyle> 1623 </ownedStyle>
1579 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/> 1624 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
1580 </ownedDiagramElements> 1625 </ownedDiagramElements>
1581 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_e7ydoKA9EeuqkpDnuik1sg" name="Statement" tooltipText="" incomingEdges="_hU64ZqA9EeuqkpDnuik1sg _mzziwKA9EeuqkpDnuik1sg _plK3JqA9EeuqkpDnuik1sg _vdptgqA9EeuqkpDnuik1sg _WX_5w9bGEeuymriYTNxK2g _s68oXAGyEey7cfH5K6RyCw" width="12" height="10"> 1626 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_e7ydoKA9EeuqkpDnuik1sg" name="Statement" tooltipText="" incomingEdges="_hU64ZqA9EeuqkpDnuik1sg _mzziwKA9EeuqkpDnuik1sg _plK3JqA9EeuqkpDnuik1sg _vdptgqA9EeuqkpDnuik1sg _WX_5w9bGEeuymriYTNxK2g _s68oXAGyEey7cfH5K6RyCw _UU0Y5cBeEe6Mo_-4--GvQg" width="12" height="10">
1582 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Statement"/> 1627 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Statement"/>
1583 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Statement"/> 1628 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Statement"/>
1584 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints> 1629 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
@@ -2684,6 +2729,47 @@
2684 <actualMapping xmi:type="description_1:NodeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EEnum']/@subNodeMappings[name='EC%20EEnumLiteral']"/> 2729 <actualMapping xmi:type="description_1:NodeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EEnum']/@subNodeMappings[name='EC%20EEnumLiteral']"/>
2685 </ownedElements> 2730 </ownedElements>
2686 </ownedDiagramElements> 2731 </ownedDiagramElements>
2732 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_GTZsgMBeEe6Mo_-4--GvQg" name="ImportStatement" tooltipText="" outgoingEdges="_UU0Y5cBeEe6Mo_-4--GvQg _bYOKL8BeEe6Mo_-4--GvQg" width="12" height="10">
2733 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ImportStatement"/>
2734 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ImportStatement"/>
2735 <arrangeConstraints>KEEP_LOCATION</arrangeConstraints>
2736 <arrangeConstraints>KEEP_SIZE</arrangeConstraints>
2737 <arrangeConstraints>KEEP_RATIO</arrangeConstraints>
2738 <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_d1xxt8BeEe6Mo_-4--GvQg" borderSize="1" borderSizeComputationExpression="1" backgroundStyle="Liquid" foregroundColor="255,252,216">
2739 <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@style"/>
2740 </ownedStyle>
2741 <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/>
2742 <ownedElements xmi:type="diagram:DNodeListElement" uid="_dTNQIMBeEe6Mo_-4--GvQg" name="alias : EString" tooltipText="">
2743 <target xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//ImportStatement/alias"/>
2744 <semanticElements xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//ImportStatement/alias"/>
2745 <ownedStyle xmi:type="diagram:BundledImage" uid="_d1y_0sBeEe6Mo_-4--GvQg" labelAlignment="LEFT">
2746 <description xmi:type="style:BundledImageDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']/@style"/>
2747 </ownedStyle>
2748 <actualMapping xmi:type="description_1:NodeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']"/>
2749 </ownedElements>
2750 </ownedDiagramElements>
2751 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_UU0Y5cBeEe6Mo_-4--GvQg" sourceNode="_GTZsgMBeEe6Mo_-4--GvQg" targetNode="_e7ydoKA9EeuqkpDnuik1sg">
2752 <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ImportStatement"/>
2753 <semanticElements xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//ImportStatement"/>
2754 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_d134gMBeEe6Mo_-4--GvQg" lineStyle="dash" targetArrow="InputClosedArrow" routingStyle="tree">
2755 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']/@conditionnalStyles.0/@style"/>
2756 <beginLabelStyle xmi:type="diagram:BeginLabelStyle" uid="_d134gcBeEe6Mo_-4--GvQg" showIcon="false">
2757 <labelFormat>italic</labelFormat>
2758 </beginLabelStyle>
2759 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_d134gsBeEe6Mo_-4--GvQg" showIcon="false"/>
2760 </ownedStyle>
2761 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC%20ESupertypes']"/>
2762 </ownedDiagramElements>
2763 <ownedDiagramElements xmi:type="diagram:DEdge" uid="_bYOKL8BeEe6Mo_-4--GvQg" name="[0..1] importedModule" sourceNode="_GTZsgMBeEe6Mo_-4--GvQg" targetNode="_D05iIKA4EeuqkpDnuik1sg">
2764 <target xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//ImportStatement/importedModule"/>
2765 <semanticElements xmi:type="ecore:EReference" href="src/main/resources/model/problem.ecore#//ImportStatement/importedModule"/>
2766 <ownedStyle xmi:type="diagram:EdgeStyle" uid="_bYOKMMBeEe6Mo_-4--GvQg" routingStyle="manhattan" strokeColor="0,0,0">
2767 <description xmi:type="style:EdgeStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']/@style"/>
2768 <centerLabelStyle xmi:type="diagram:CenterLabelStyle" uid="_bYOKMsBeEe6Mo_-4--GvQg" showIcon="false"/>
2769 <endLabelStyle xmi:type="diagram:EndLabelStyle" uid="_bYOKMcBeEe6Mo_-4--GvQg" labelSize="6" showIcon="false" labelColor="39,76,114"/>
2770 </ownedStyle>
2771 <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']"/>
2772 </ownedDiagramElements>
2687 <description xmi:type="description_1:DiagramDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']"/> 2773 <description xmi:type="description_1:DiagramDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']"/>
2688 <filterVariableHistory xmi:type="diagram:FilterVariableHistory" uid="_CsWlsKA4EeuqkpDnuik1sg"/> 2774 <filterVariableHistory xmi:type="diagram:FilterVariableHistory" uid="_CsWlsKA4EeuqkpDnuik1sg"/>
2689 <activatedLayers xmi:type="description_1:Layer" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer"/> 2775 <activatedLayers xmi:type="description_1:Layer" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer"/>
diff --git a/subprojects/language-model/problem.aird.license b/subprojects/language-model/problem.aird.license
index e5db6ccd..a73a3364 100644
--- a/subprojects/language-model/problem.aird.license
+++ b/subprojects/language-model/problem.aird.license
@@ -1,3 +1,3 @@
1SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> 1SPDX-FileCopyrightText: 2021-2024 The Refinery Authors <https://refinery.tools/>
2 2
3SPDX-License-Identifier: EPL-2.0 3SPDX-License-Identifier: EPL-2.0
diff --git a/subprojects/language-model/src/main/resources/model/problem.ecore b/subprojects/language-model/src/main/resources/model/problem.ecore
index aa05a2fe..f7c85290 100644
--- a/subprojects/language-model/src/main/resources/model/problem.ecore
+++ b/subprojects/language-model/src/main/resources/model/problem.ecore
@@ -270,4 +270,8 @@
270 <eLiterals name="ATOM" value="1"/> 270 <eLiterals name="ATOM" value="1"/>
271 <eLiterals name="MULTI" value="2"/> 271 <eLiterals name="MULTI" value="2"/>
272 </eClassifiers> 272 </eClassifiers>
273 <eClassifiers xsi:type="ecore:EClass" name="ImportStatement" eSuperTypes="#//Statement">
274 <eStructuralFeatures xsi:type="ecore:EReference" name="importedModule" eType="#//Problem"/>
275 <eStructuralFeatures xsi:type="ecore:EAttribute" name="alias" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
276 </eClassifiers>
273</ecore:EPackage> 277</ecore:EPackage>
diff --git a/subprojects/language-model/src/main/resources/model/problem.ecore.license b/subprojects/language-model/src/main/resources/model/problem.ecore.license
index e5db6ccd..a73a3364 100644
--- a/subprojects/language-model/src/main/resources/model/problem.ecore.license
+++ b/subprojects/language-model/src/main/resources/model/problem.ecore.license
@@ -1,3 +1,3 @@
1SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> 1SPDX-FileCopyrightText: 2021-2024 The Refinery Authors <https://refinery.tools/>
2 2
3SPDX-License-Identifier: EPL-2.0 3SPDX-License-Identifier: EPL-2.0
diff --git a/subprojects/language-model/src/main/resources/model/problem.genmodel b/subprojects/language-model/src/main/resources/model/problem.genmodel
index be8b62eb..85cd802b 100644
--- a/subprojects/language-model/src/main/resources/model/problem.genmodel
+++ b/subprojects/language-model/src/main/resources/model/problem.genmodel
@@ -238,5 +238,9 @@
238 <genClasses ecoreClass="problem.ecore#//LogicConstant"> 238 <genClasses ecoreClass="problem.ecore#//LogicConstant">
239 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute problem.ecore#//LogicConstant/logicValue"/> 239 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute problem.ecore#//LogicConstant/logicValue"/>
240 </genClasses> 240 </genClasses>
241 <genClasses ecoreClass="problem.ecore#//ImportStatement">
242 <genFeatures notify="false" createChild="false" propertySortChoices="true" ecoreFeature="ecore:EReference problem.ecore#//ImportStatement/importedModule"/>
243 <genFeatures createChild="false" ecoreFeature="ecore:EAttribute problem.ecore#//ImportStatement/alias"/>
244 </genClasses>
241 </genPackages> 245 </genPackages>
242</genmodel:GenModel> 246</genmodel:GenModel>
diff --git a/subprojects/language-model/src/main/resources/model/problem.genmodel.license b/subprojects/language-model/src/main/resources/model/problem.genmodel.license
index e5db6ccd..a73a3364 100644
--- a/subprojects/language-model/src/main/resources/model/problem.genmodel.license
+++ b/subprojects/language-model/src/main/resources/model/problem.genmodel.license
@@ -1,3 +1,3 @@
1SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> 1SPDX-FileCopyrightText: 2021-2024 The Refinery Authors <https://refinery.tools/>
2 2
3SPDX-License-Identifier: EPL-2.0 3SPDX-License-Identifier: EPL-2.0
diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ModelInitializer.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ModelInitializer.java
index b462fd70..7c649232 100644
--- a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ModelInitializer.java
+++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ModelInitializer.java
@@ -247,7 +247,7 @@ public class ModelInitializer {
247 } 247 }
248 248
249 private String getName(Relation relation) { 249 private String getName(Relation relation) {
250 return semanticsUtils.getName(relation).orElseGet(() -> "::" + relationInfoMap.size()); 250 return semanticsUtils.getNameWithoutRootPrefix(relation).orElseGet(() -> "::" + relationInfoMap.size());
251 } 251 }
252 252
253 private void collectMetamodel() { 253 private void collectMetamodel() {
@@ -659,7 +659,7 @@ public class ModelInitializer {
659 var variableOrNode = variableOrNodeExpr.getVariableOrNode(); 659 var variableOrNode = variableOrNodeExpr.getVariableOrNode();
660 if (variableOrNode instanceof Node node) { 660 if (variableOrNode instanceof Node node) {
661 int nodeId = getNodeId(node); 661 int nodeId = getNodeId(node);
662 var tempVariable = Variable.of(semanticsUtils.getName(node).orElse("_" + nodeId)); 662 var tempVariable = Variable.of(semanticsUtils.getNameWithoutRootPrefix(node).orElse("_" + nodeId));
663 literals.add(new ConstantLiteral(tempVariable, nodeId)); 663 literals.add(new ConstantLiteral(tempVariable, nodeId));
664 argumentList.add(tempVariable); 664 argumentList.add(tempVariable);
665 } else if (variableOrNode instanceof tools.refinery.language.model.problem.Variable problemVariable) { 665 } else if (variableOrNode instanceof tools.refinery.language.model.problem.Variable problemVariable) {
diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ProblemTraceImpl.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ProblemTraceImpl.java
index f686e980..aef0fd89 100644
--- a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ProblemTraceImpl.java
+++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ProblemTraceImpl.java
@@ -79,7 +79,7 @@ class ProblemTraceImpl implements ProblemTrace {
79 try { 79 try {
80 return nodeTrace.getOrThrow(node); 80 return nodeTrace.getOrThrow(node);
81 } catch (IllegalStateException e) { 81 } catch (IllegalStateException e) {
82 var qualifiedName = semanticsUtils.getName(node); 82 var qualifiedName = semanticsUtils.getNameWithoutRootPrefix(node);
83 throw new TracedException(node, "No node ID for " + qualifiedName, e); 83 throw new TracedException(node, "No node ID for " + qualifiedName, e);
84 } 84 }
85 } 85 }
@@ -144,7 +144,7 @@ class ProblemTraceImpl implements ProblemTrace {
144 public PartialRelation getPartialRelation(Relation relation) { 144 public PartialRelation getPartialRelation(Relation relation) {
145 var partialRelation = mutableRelationTrace.get(relation); 145 var partialRelation = mutableRelationTrace.get(relation);
146 if (partialRelation == null) { 146 if (partialRelation == null) {
147 var qualifiedName = semanticsUtils.getName(relation); 147 var qualifiedName = semanticsUtils.getNameWithoutRootPrefix(relation);
148 throw new TracedException(relation, "No partial relation for " + qualifiedName); 148 throw new TracedException(relation, "No partial relation for " + qualifiedName);
149 } 149 }
150 return partialRelation; 150 return partialRelation;
diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/SemanticsUtils.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/SemanticsUtils.java
index b72ba697..110295b2 100644
--- a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/SemanticsUtils.java
+++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/SemanticsUtils.java
@@ -7,6 +7,7 @@ package tools.refinery.language.semantics;
7 7
8import com.google.inject.Inject; 8import com.google.inject.Inject;
9import com.google.inject.Singleton; 9import com.google.inject.Singleton;
10import com.google.inject.name.Named;
10import org.eclipse.emf.ecore.EObject; 11import org.eclipse.emf.ecore.EObject;
11import org.eclipse.emf.ecore.util.EcoreUtil; 12import org.eclipse.emf.ecore.util.EcoreUtil;
12import org.eclipse.xtext.naming.IQualifiedNameConverter; 13import org.eclipse.xtext.naming.IQualifiedNameConverter;
@@ -16,6 +17,7 @@ import org.eclipse.xtext.scoping.IScope;
16import org.jetbrains.annotations.NotNull; 17import org.jetbrains.annotations.NotNull;
17import org.jetbrains.annotations.Nullable; 18import org.jetbrains.annotations.Nullable;
18import tools.refinery.language.model.problem.Problem; 19import tools.refinery.language.model.problem.Problem;
20import tools.refinery.language.naming.ProblemQualifiedNameProvider;
19 21
20import java.util.Optional; 22import java.util.Optional;
21 23
@@ -25,10 +27,14 @@ public class SemanticsUtils {
25 private IQualifiedNameProvider qualifiedNameProvider; 27 private IQualifiedNameProvider qualifiedNameProvider;
26 28
27 @Inject 29 @Inject
30 @Named(ProblemQualifiedNameProvider.NAMED_DELEGATE)
31 private IQualifiedNameProvider delegateQualifiedNameProvider;
32
33 @Inject
28 private IQualifiedNameConverter qualifiedNameConverter; 34 private IQualifiedNameConverter qualifiedNameConverter;
29 35
30 public Optional<String> getName(EObject eObject) { 36 public Optional<String> getNameWithoutRootPrefix(EObject eObject) {
31 var qualifiedName = qualifiedNameProvider.getFullyQualifiedName(eObject); 37 var qualifiedName = delegateQualifiedNameProvider.getFullyQualifiedName(eObject);
32 if (qualifiedName == null) { 38 if (qualifiedName == null) {
33 return Optional.empty(); 39 return Optional.empty();
34 } 40 }
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/PartialInterpretation2Json.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/PartialInterpretation2Json.java
index efe04592..ff811033 100644
--- a/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/PartialInterpretation2Json.java
+++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/PartialInterpretation2Json.java
@@ -1,5 +1,5 @@
1/* 1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> 2 * SPDX-FileCopyrightText: 2023-2024 The Refinery Authors <https://refinery.tools/>
3 * 3 *
4 * SPDX-License-Identifier: EPL-2.0 4 * SPDX-License-Identifier: EPL-2.0
5 */ 5 */
@@ -32,7 +32,7 @@ public class PartialInterpretation2Json {
32 var relation = entry.getKey(); 32 var relation = entry.getKey();
33 var partialSymbol = entry.getValue(); 33 var partialSymbol = entry.getValue();
34 var tuples = getTuplesJson(facade, partialSymbol); 34 var tuples = getTuplesJson(facade, partialSymbol);
35 var name = semanticsUtils.getName(relation).orElse(partialSymbol.name()); 35 var name = semanticsUtils.getNameWithoutRootPrefix(relation).orElse(partialSymbol.name());
36 json.add(name, tuples); 36 json.add(name, tuples);
37 cancellationToken.checkCancelled(); 37 cancellationToken.checkCancelled();
38 } 38 }
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/metadata/MetadataCreator.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/metadata/MetadataCreator.java
index 1f49b0d0..1858fc87 100644
--- a/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/metadata/MetadataCreator.java
+++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/metadata/MetadataCreator.java
@@ -7,6 +7,7 @@ package tools.refinery.language.web.semantics.metadata;
7 7
8import com.google.inject.Inject; 8import com.google.inject.Inject;
9import com.google.inject.Provider; 9import com.google.inject.Provider;
10import com.google.inject.name.Named;
10import org.eclipse.emf.ecore.EObject; 11import org.eclipse.emf.ecore.EObject;
11import org.eclipse.xtext.naming.IQualifiedNameConverter; 12import org.eclipse.xtext.naming.IQualifiedNameConverter;
12import org.eclipse.xtext.naming.IQualifiedNameProvider; 13import org.eclipse.xtext.naming.IQualifiedNameProvider;
@@ -14,6 +15,7 @@ import org.eclipse.xtext.naming.QualifiedName;
14import org.eclipse.xtext.scoping.IScope; 15import org.eclipse.xtext.scoping.IScope;
15import org.eclipse.xtext.scoping.IScopeProvider; 16import org.eclipse.xtext.scoping.IScopeProvider;
16import tools.refinery.language.model.problem.*; 17import tools.refinery.language.model.problem.*;
18import tools.refinery.language.naming.ProblemQualifiedNameProvider;
17import tools.refinery.language.semantics.ProblemTrace; 19import tools.refinery.language.semantics.ProblemTrace;
18import tools.refinery.language.semantics.TracedException; 20import tools.refinery.language.semantics.TracedException;
19import tools.refinery.language.utils.ProblemUtil; 21import tools.refinery.language.utils.ProblemUtil;
@@ -32,6 +34,7 @@ public class MetadataCreator {
32 private IScopeProvider scopeProvider; 34 private IScopeProvider scopeProvider;
33 35
34 @Inject 36 @Inject
37 @Named(ProblemQualifiedNameProvider.NAMED_DELEGATE)
35 private IQualifiedNameProvider qualifiedNameProvider; 38 private IQualifiedNameProvider qualifiedNameProvider;
36 39
37 @Inject 40 @Inject
@@ -122,17 +125,13 @@ public class MetadataCreator {
122 if (ProblemUtil.isBuiltIn(relation) && !ProblemUtil.isError(relation)) { 125 if (ProblemUtil.isBuiltIn(relation) && !ProblemUtil.isError(relation)) {
123 return getBuiltInDetail(); 126 return getBuiltInDetail();
124 } 127 }
125 if (relation instanceof ClassDeclaration classDeclaration) { 128 return switch (relation) {
126 return getClassDetail(classDeclaration); 129 case ClassDeclaration classDeclaration -> getClassDetail(classDeclaration);
127 } else if (relation instanceof ReferenceDeclaration) { 130 case ReferenceDeclaration ignored -> getReferenceDetail(partialRelation);
128 return getReferenceDetail(partialRelation); 131 case EnumDeclaration ignored -> getEnumDetail();
129 } else if (relation instanceof EnumDeclaration) { 132 case PredicateDefinition predicateDefinition -> getPredicateDetail(predicateDefinition);
130 return getEnumDetail(); 133 default -> throw new TracedException(relation, "Unknown relation");
131 } else if (relation instanceof PredicateDefinition predicateDefinition) { 134 };
132 return getPredicateDetail(predicateDefinition);
133 } else {
134 throw new TracedException(relation, "Unknown relation");
135 }
136 } 135 }
137 136
138 private RelationDetail getBuiltInDetail() { 137 private RelationDetail getBuiltInDetail() {
diff --git a/subprojects/language/src/main/java/tools/refinery/language/Problem.xtext b/subprojects/language/src/main/java/tools/refinery/language/Problem.xtext
index 4ce3fae1..f0d6c38c 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/Problem.xtext
+++ b/subprojects/language/src/main/java/tools/refinery/language/Problem.xtext
@@ -1,5 +1,5 @@
1/* 1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> 2 * SPDX-FileCopyrightText: 2021-2024 The Refinery Authors <https://refinery.tools/>
3 * 3 *
4 * SPDX-License-Identifier: EPL-2.0 4 * SPDX-License-Identifier: EPL-2.0
5 */ 5 */
@@ -16,10 +16,13 @@ enum ModuleKind:
16 PROBLEM="problem" | MODULE="module"; 16 PROBLEM="problem" | MODULE="module";
17 17
18Statement: 18Statement:
19 Assertion | ClassDeclaration | EnumDeclaration | 19 ImportStatement | Assertion | ClassDeclaration | EnumDeclaration |
20 PredicateDefinition | /* FunctionDefinition | RuleDefinition | */ 20 PredicateDefinition | /* FunctionDefinition | RuleDefinition | */
21 ScopeDeclaration | NodeDeclaration; 21 ScopeDeclaration | NodeDeclaration;
22 22
23ImportStatement:
24 "import" importedModule=[Problem|QualifiedName] ("as" alias=ID)? ".";
25
23ClassDeclaration: 26ClassDeclaration:
24 abstract?="abstract"? "class" 27 abstract?="abstract"? "class"
25 name=Identifier 28 name=Identifier
@@ -266,10 +269,10 @@ UpperBound returns ecore::EInt:
266 INT | "*"; 269 INT | "*";
267 270
268QualifiedName hidden(): 271QualifiedName hidden():
269 Identifier ("::" Identifier)*; 272 "::"? Identifier (QUALIFIED_NAME_SEPARATOR Identifier)*;
270 273
271NonContainmentQualifiedName hidden(): 274NonContainmentQualifiedName hidden():
272 NonContainmentIdentifier ("::" Identifier)*; 275 (NonContainmentIdentifier | "::" Identifier) (QUALIFIED_NAME_SEPARATOR Identifier)*;
273 276
274Identifier: 277Identifier:
275 NonContainmentIdentifier | "contains" | "container"; 278 NonContainmentIdentifier | "contains" | "container";
@@ -284,6 +287,9 @@ Real returns ecore::EDouble:
284terminal TRANSITIVE_CLOSURE: 287terminal TRANSITIVE_CLOSURE:
285 "synthetic:TRANSITIVE_CLOSURE"; 288 "synthetic:TRANSITIVE_CLOSURE";
286 289
290terminal QUALIFIED_NAME_SEPARATOR:
291 "synthetic::QUALIFIED_NAME_SEPARATOR";
292
287@Override 293@Override
288terminal ID: 294terminal ID:
289 ('a'..'z' | 'A'..'Z' | '_') ('a'..'z' | 'A'..'Z' | '_' | '0'..'9')*; 295 ('a'..'z' | 'A'..'Z' | '_') ('a'..'z' | 'A'..'Z' | '_' | '0'..'9')*;
diff --git a/subprojects/language/src/main/java/tools/refinery/language/ProblemRuntimeModule.java b/subprojects/language/src/main/java/tools/refinery/language/ProblemRuntimeModule.java
index 00dd3de3..19816da4 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/ProblemRuntimeModule.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/ProblemRuntimeModule.java
@@ -1,5 +1,5 @@
1/* 1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> 2 * SPDX-FileCopyrightText: 2021-2024 The Refinery Authors <https://refinery.tools/>
3 * 3 *
4 * SPDX-License-Identifier: EPL-2.0 4 * SPDX-License-Identifier: EPL-2.0
5 */ 5 */
@@ -14,6 +14,7 @@ import com.google.inject.name.Names;
14import org.eclipse.xtext.conversion.IValueConverterService; 14import org.eclipse.xtext.conversion.IValueConverterService;
15import org.eclipse.xtext.linking.ILinkingService; 15import org.eclipse.xtext.linking.ILinkingService;
16import org.eclipse.xtext.naming.IQualifiedNameConverter; 16import org.eclipse.xtext.naming.IQualifiedNameConverter;
17import org.eclipse.xtext.naming.IQualifiedNameProvider;
17import org.eclipse.xtext.parser.IParser; 18import org.eclipse.xtext.parser.IParser;
18import org.eclipse.xtext.resource.*; 19import org.eclipse.xtext.resource.*;
19import org.eclipse.xtext.scoping.IGlobalScopeProvider; 20import org.eclipse.xtext.scoping.IGlobalScopeProvider;
@@ -26,12 +27,11 @@ import org.eclipse.xtext.validation.IResourceValidator;
26import org.eclipse.xtext.xbase.annotations.validation.DerivedStateAwareResourceValidator; 27import org.eclipse.xtext.xbase.annotations.validation.DerivedStateAwareResourceValidator;
27import tools.refinery.language.conversion.ProblemValueConverterService; 28import tools.refinery.language.conversion.ProblemValueConverterService;
28import tools.refinery.language.linking.ProblemLinkingService; 29import tools.refinery.language.linking.ProblemLinkingService;
30import tools.refinery.language.naming.ProblemDelegateQualifiedNameProvider;
29import tools.refinery.language.naming.ProblemQualifiedNameConverter; 31import tools.refinery.language.naming.ProblemQualifiedNameConverter;
32import tools.refinery.language.naming.ProblemQualifiedNameProvider;
30import tools.refinery.language.parser.antlr.TokenSourceInjectingProblemParser; 33import tools.refinery.language.parser.antlr.TokenSourceInjectingProblemParser;
31import tools.refinery.language.resource.ProblemDerivedStateComputer; 34import tools.refinery.language.resource.*;
32import tools.refinery.language.resource.ProblemLocationInFileProvider;
33import tools.refinery.language.resource.ProblemResource;
34import tools.refinery.language.resource.ProblemResourceDescriptionStrategy;
35import tools.refinery.language.scoping.ProblemGlobalScopeProvider; 35import tools.refinery.language.scoping.ProblemGlobalScopeProvider;
36import tools.refinery.language.scoping.ProblemLocalScopeProvider; 36import tools.refinery.language.scoping.ProblemLocalScopeProvider;
37import tools.refinery.language.serializer.PreferShortAssertionsProblemSemanticSequencer; 37import tools.refinery.language.serializer.PreferShortAssertionsProblemSemanticSequencer;
@@ -54,6 +54,17 @@ public class ProblemRuntimeModule extends AbstractProblemRuntimeModule {
54 return ProblemQualifiedNameConverter.class; 54 return ProblemQualifiedNameConverter.class;
55 } 55 }
56 56
57 public void configureIQualifiedNameProviderDelegate(Binder binder) {
58 binder.bind(IQualifiedNameProvider.class)
59 .annotatedWith(Names.named(ProblemQualifiedNameProvider.NAMED_DELEGATE))
60 .to(ProblemDelegateQualifiedNameProvider.class);
61 }
62
63 @Override
64 public Class<? extends IQualifiedNameProvider> bindIQualifiedNameProvider() {
65 return ProblemQualifiedNameProvider.class;
66 }
67
57 public Class<? extends IDefaultResourceDescriptionStrategy> bindIDefaultResourceDescriptionStrategy() { 68 public Class<? extends IDefaultResourceDescriptionStrategy> bindIDefaultResourceDescriptionStrategy() {
58 return ProblemResourceDescriptionStrategy.class; 69 return ProblemResourceDescriptionStrategy.class;
59 } 70 }
@@ -87,7 +98,7 @@ public class ProblemRuntimeModule extends AbstractProblemRuntimeModule {
87 // Method name follows Xtext convention. 98 // Method name follows Xtext convention.
88 @SuppressWarnings("squid:S100") 99 @SuppressWarnings("squid:S100")
89 public Class<? extends IResourceDescription.Manager> bindIResourceDescription$Manager() { 100 public Class<? extends IResourceDescription.Manager> bindIResourceDescription$Manager() {
90 return DerivedStateAwareResourceDescriptionManager.class; 101 return ProblemResourceDescriptionManager.class;
91 } 102 }
92 103
93 public Class<? extends IResourceValidator> bindIResourceValidator() { 104 public Class<? extends IResourceValidator> bindIResourceValidator() {
diff --git a/subprojects/language/src/main/java/tools/refinery/language/naming/NamingUtil.java b/subprojects/language/src/main/java/tools/refinery/language/naming/NamingUtil.java
index 1647d4e7..feae5ebb 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/naming/NamingUtil.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/naming/NamingUtil.java
@@ -1,21 +1,24 @@
1/* 1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> 2 * SPDX-FileCopyrightText: 2021-2024 The Refinery Authors <https://refinery.tools/>
3 * 3 *
4 * SPDX-License-Identifier: EPL-2.0 4 * SPDX-License-Identifier: EPL-2.0
5 */ 5 */
6package tools.refinery.language.naming; 6package tools.refinery.language.naming;
7 7
8import org.eclipse.xtext.naming.QualifiedName;
9
8import java.util.regex.Pattern; 10import java.util.regex.Pattern;
9 11
10public final class NamingUtil { 12public final class NamingUtil {
11 private static final String SINGLETON_VARIABLE_PREFIX = "_"; 13 private static final String SINGLETON_VARIABLE_PREFIX = "_";
12 14 public static final QualifiedName ROOT_NAME = QualifiedName.create("");
13 private static final Pattern ID_REGEX = Pattern.compile("[_a-zA-Z][_0-9a-zA-Z]*"); 15
16 private static final Pattern ID_REGEX = Pattern.compile("[_a-zA-Z]\\w*");
14 17
15 private NamingUtil() { 18 private NamingUtil() {
16 throw new IllegalStateException("This is a static utility class and should not be instantiated directly"); 19 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
17 } 20 }
18 21
19 public static boolean isNullOrEmpty(String name) { 22 public static boolean isNullOrEmpty(String name) {
20 return name == null || name.isEmpty(); 23 return name == null || name.isEmpty();
21 } 24 }
@@ -23,8 +26,28 @@ public final class NamingUtil {
23 public static boolean isSingletonVariableName(String name) { 26 public static boolean isSingletonVariableName(String name) {
24 return name != null && name.startsWith(SINGLETON_VARIABLE_PREFIX); 27 return name != null && name.startsWith(SINGLETON_VARIABLE_PREFIX);
25 } 28 }
26 29
30 // This method name only makes sense if it checks for the positive case.
31 @SuppressWarnings("BooleanMethodIsAlwaysInverted")
27 public static boolean isValidId(String name) { 32 public static boolean isValidId(String name) {
28 return name != null && ID_REGEX.matcher(name).matches(); 33 return name != null && ID_REGEX.matcher(name).matches();
29 } 34 }
35
36 public static boolean isFullyQualified(QualifiedName name) {
37 return name.startsWith(ROOT_NAME);
38 }
39
40 public static QualifiedName stripRootPrefix(QualifiedName name) {
41 if (name == null) {
42 return null;
43 }
44 return isFullyQualified(name) ? name.skipFirst(ROOT_NAME.getSegmentCount()) : name;
45 }
46
47 public static QualifiedName addRootPrefix(QualifiedName name) {
48 if (name == null) {
49 return null;
50 }
51 return isFullyQualified(name) ? name : ROOT_NAME.append(name);
52 }
30} 53}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/naming/ProblemDelegateQualifiedNameProvider.java b/subprojects/language/src/main/java/tools/refinery/language/naming/ProblemDelegateQualifiedNameProvider.java
new file mode 100644
index 00000000..1e78cee1
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/naming/ProblemDelegateQualifiedNameProvider.java
@@ -0,0 +1,18 @@
1/*
2 * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.language.naming;
7
8import org.eclipse.xtext.naming.DefaultDeclarativeQualifiedNameProvider;
9import org.eclipse.xtext.naming.QualifiedName;
10import tools.refinery.language.model.problem.Problem;
11
12public class ProblemDelegateQualifiedNameProvider extends DefaultDeclarativeQualifiedNameProvider {
13 protected QualifiedName qualifiedName(Problem ele) {
14 var qualifiedName = computeFullyQualifiedNameFromNameAttribute(ele);
15 // Strip the root prefix even if explicitly provided.
16 return NamingUtil.stripRootPrefix(qualifiedName);
17 }
18}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/naming/ProblemQualifiedNameConverter.java b/subprojects/language/src/main/java/tools/refinery/language/naming/ProblemQualifiedNameConverter.java
index 74b4e208..88a0fe9a 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/naming/ProblemQualifiedNameConverter.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/naming/ProblemQualifiedNameConverter.java
@@ -12,7 +12,7 @@ import com.google.inject.Singleton;
12@Singleton 12@Singleton
13public class ProblemQualifiedNameConverter extends IQualifiedNameConverter.DefaultImpl { 13public class ProblemQualifiedNameConverter extends IQualifiedNameConverter.DefaultImpl {
14 public static final String DELIMITER = "::"; 14 public static final String DELIMITER = "::";
15 15
16 @Override 16 @Override
17 public String getDelimiter() { 17 public String getDelimiter() {
18 return DELIMITER; 18 return DELIMITER;
diff --git a/subprojects/language/src/main/java/tools/refinery/language/naming/ProblemQualifiedNameProvider.java b/subprojects/language/src/main/java/tools/refinery/language/naming/ProblemQualifiedNameProvider.java
new file mode 100644
index 00000000..5b682058
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/naming/ProblemQualifiedNameProvider.java
@@ -0,0 +1,41 @@
1/*
2 * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.language.naming;
7
8import com.google.inject.Inject;
9import com.google.inject.name.Named;
10import org.eclipse.emf.ecore.EObject;
11import org.eclipse.xtext.naming.IQualifiedNameProvider;
12import org.eclipse.xtext.naming.QualifiedName;
13import org.eclipse.xtext.util.IResourceScopeCache;
14import org.eclipse.xtext.util.Tuples;
15import tools.refinery.language.resource.ProblemResourceDescriptionStrategy;
16
17public class ProblemQualifiedNameProvider extends IQualifiedNameProvider.AbstractImpl {
18 private static final String PREFIX = "tools.refinery.language.naming.ProblemQualifiedNameProvider.";
19 public static final String NAMED_DELEGATE = PREFIX + "NAMED_DELEGATE";
20 public static final String CACHE_KEY = PREFIX + "CACHE_KEY";
21
22 @Inject
23 @Named(NAMED_DELEGATE)
24 private IQualifiedNameProvider delegate;
25
26 @Inject
27 private IResourceScopeCache cache = IResourceScopeCache.NullImpl.INSTANCE;
28
29 @Override
30 public QualifiedName getFullyQualifiedName(EObject obj) {
31 return cache.get(Tuples.pair(obj, CACHE_KEY), obj.eResource(), () -> computeFullyQualifiedName(obj));
32 }
33
34 public QualifiedName computeFullyQualifiedName(EObject obj) {
35 var qualifiedName = delegate.getFullyQualifiedName(obj);
36 if (qualifiedName != null && ProblemResourceDescriptionStrategy.shouldExport(obj)) {
37 return NamingUtil.addRootPrefix(qualifiedName);
38 }
39 return qualifiedName;
40 }
41}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/parser/antlr/IdentifierTokenProvider.java b/subprojects/language/src/main/java/tools/refinery/language/parser/antlr/IdentifierTokenProvider.java
index 306a86fc..0e19357f 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/parser/antlr/IdentifierTokenProvider.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/parser/antlr/IdentifierTokenProvider.java
@@ -1,5 +1,5 @@
1/* 1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> 2 * SPDX-FileCopyrightText: 2021-2024 The Refinery Authors <https://refinery.tools/>
3 * 3 *
4 * SPDX-License-Identifier: EPL-2.0 4 * SPDX-License-Identifier: EPL-2.0
5 */ 5 */
@@ -59,7 +59,7 @@ public class IdentifierTokenProvider {
59 59
60 private void createValueToTokenIdMap() { 60 private void createValueToTokenIdMap() {
61 var tokenIdToValueMap = tokenDefProvider.getTokenDefMap(); 61 var tokenIdToValueMap = tokenDefProvider.getTokenDefMap();
62 valueToTokenIdMap = new HashMap<>(tokenIdToValueMap.size()); 62 valueToTokenIdMap = HashMap.newHashMap(tokenIdToValueMap.size());
63 for (var entry : tokenIdToValueMap.entrySet()) { 63 for (var entry : tokenIdToValueMap.entrySet()) {
64 valueToTokenIdMap.put(entry.getValue(), entry.getKey()); 64 valueToTokenIdMap.put(entry.getValue(), entry.getKey());
65 } 65 }
@@ -74,17 +74,16 @@ public class IdentifierTokenProvider {
74 } 74 }
75 75
76 private void collectIdentifierTokensFromElement(AbstractElement element) { 76 private void collectIdentifierTokensFromElement(AbstractElement element) {
77 if (element instanceof Alternatives alternatives) { 77 switch (element) {
78 for (var alternative : alternatives.getElements()) { 78 case Alternatives alternatives -> {
79 collectIdentifierTokensFromElement(alternative); 79 for (var alternative : alternatives.getElements()) {
80 } 80 collectIdentifierTokensFromElement(alternative);
81 } else if (element instanceof RuleCall ruleCall) { 81 }
82 collectIdentifierTokensFromRule(ruleCall.getRule()); 82 }
83 } else if (element instanceof Keyword keyword) { 83 case RuleCall ruleCall -> collectIdentifierTokensFromRule(ruleCall.getRule());
84 collectToken("'" + keyword.getValue() + "'"); 84 case Keyword keyword -> collectToken("'" + keyword.getValue() + "'");
85 } else { 85 default -> throw new IllegalArgumentException("Unknown Xtext grammar element: " + element);
86 throw new IllegalArgumentException("Unknown Xtext grammar element: " + element); 86 }
87 }
88 } 87 }
89 88
90 private void collectToken(String value) { 89 private void collectToken(String value) {
diff --git a/subprojects/language/src/main/java/tools/refinery/language/parser/antlr/ProblemTokenSource.java b/subprojects/language/src/main/java/tools/refinery/language/parser/antlr/ProblemTokenSource.java
index 5b91a6cc..487e4ceb 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/parser/antlr/ProblemTokenSource.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/parser/antlr/ProblemTokenSource.java
@@ -1,5 +1,5 @@
1/* 1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> 2 * SPDX-FileCopyrightText: 2021-2024 The Refinery Authors <https://refinery.tools/>
3 * 3 *
4 * SPDX-License-Identifier: EPL-2.0 4 * SPDX-License-Identifier: EPL-2.0
5 */ 5 */
@@ -28,6 +28,8 @@ public class ProblemTokenSource implements TokenSource {
28 28
29 private boolean seenId; 29 private boolean seenId;
30 30
31 private boolean lastVisible;
32
31 public ProblemTokenSource(TokenSource delegate) { 33 public ProblemTokenSource(TokenSource delegate) {
32 this.delegate = delegate; 34 this.delegate = delegate;
33 } 35 }
@@ -47,18 +49,18 @@ public class ProblemTokenSource implements TokenSource {
47 49
48 @Override 50 @Override
49 public Token nextToken() { 51 public Token nextToken() {
50 if (!buffer.isEmpty()) { 52 boolean fromStream = buffer.isEmpty();
51 return buffer.removeFirst(); 53 var token = fromStream ? delegate.nextToken() : buffer.removeFirst();
52 } 54 if (seenId) {
53 var token = delegate.nextToken(); 55 if (fromStream && isPlusOrTransitiveClosure(token) && peekForTransitiveClosure()) {
54 if (isIdentifier(token)) {
55 seenId = true;
56 } else if (seenId && isPlusOrTransitiveClosure(token)) {
57 if (peekForTransitiveClosure()) {
58 token.setType(InternalProblemParser.RULE_TRANSITIVE_CLOSURE); 56 token.setType(InternalProblemParser.RULE_TRANSITIVE_CLOSURE);
57 } else if (lastVisible && isQualifiedNameSeparator(token)) {
58 token.setType(InternalProblemParser.RULE_QUALIFIED_NAME_SEPARATOR);
59 } 59 }
60 } else if (isVisibleToken(token)) { 60 }
61 seenId = false; 61 lastVisible = isVisibleToken(token);
62 if (lastVisible) {
63 seenId = isIdentifier(token);
62 } 64 }
63 return token; 65 return token;
64 } 66 }
@@ -76,6 +78,10 @@ public class ProblemTokenSource implements TokenSource {
76 return token.getType() == InternalProblemParser.PlusSign; 78 return token.getType() == InternalProblemParser.PlusSign;
77 } 79 }
78 80
81 protected boolean isQualifiedNameSeparator(Token token) {
82 return token.getType() == InternalProblemParser.ColonColon;
83 }
84
79 protected boolean isVisibleToken(Token token) { 85 protected boolean isVisibleToken(Token token) {
80 int tokenId = token.getType(); 86 int tokenId = token.getType();
81 return tokenId != InternalProblemParser.RULE_WS && tokenId != InternalProblemParser.RULE_SL_COMMENT && 87 return tokenId != InternalProblemParser.RULE_WS && tokenId != InternalProblemParser.RULE_SL_COMMENT &&
@@ -87,11 +93,16 @@ public class ProblemTokenSource implements TokenSource {
87 if (token.getType() != InternalProblemParser.LeftParenthesis) { 93 if (token.getType() != InternalProblemParser.LeftParenthesis) {
88 return false; 94 return false;
89 } 95 }
96 boolean allowFullyQualifiedName = true;
90 while (true) { 97 while (true) {
91 token = peekWithSkipWhitespace(); 98 token = peekWithSkipWhitespace();
99 if (allowFullyQualifiedName && token.getType() == InternalProblemParser.ColonColon) {
100 token = peekWithSkipWhitespace();
101 }
92 if (!isIdentifier(token)) { 102 if (!isIdentifier(token)) {
93 return false; 103 return false;
94 } 104 }
105 allowFullyQualifiedName = false;
95 token = peekWithSkipWhitespace(); 106 token = peekWithSkipWhitespace();
96 switch (token.getType()) { 107 switch (token.getType()) {
97 case InternalProblemParser.Comma: 108 case InternalProblemParser.Comma:
@@ -112,11 +123,6 @@ public class ProblemTokenSource implements TokenSource {
112 123
113 protected Token peekToken() { 124 protected Token peekToken() {
114 var token = delegate.nextToken(); 125 var token = delegate.nextToken();
115 if (isIdentifier(token)) {
116 seenId = true;
117 } else if (isVisibleToken(token)) {
118 seenId = false;
119 }
120 buffer.addLast(token); 126 buffer.addLast(token);
121 return token; 127 return token;
122 } 128 }
diff --git a/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescription.java b/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescription.java
new file mode 100644
index 00000000..498a7c57
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescription.java
@@ -0,0 +1,106 @@
1/*******************************************************************************
2 * Copyright (c) 2009, 2011 itemis AG (http://www.itemis.eu) and others.
3 * Copyright (c) 2024 The Refinery Authors <https://refinery.tools/>
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License 2.0 which is available at
6 * http://www.eclipse.org/legal/epl-2.0.
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.language.resource;
10
11import org.apache.log4j.Logger;
12import org.eclipse.emf.common.util.TreeIterator;
13import org.eclipse.emf.ecore.EObject;
14import org.eclipse.emf.ecore.resource.Resource;
15import org.eclipse.emf.ecore.util.EcoreUtil;
16import org.eclipse.xtext.resource.IDefaultResourceDescriptionStrategy;
17import org.eclipse.xtext.resource.IEObjectDescription;
18import org.eclipse.xtext.resource.impl.DefaultResourceDescription;
19import org.eclipse.xtext.resource.impl.EObjectDescriptionLookUp;
20import org.eclipse.xtext.util.IAcceptor;
21import tools.refinery.language.naming.NamingUtil;
22
23import java.io.IOException;
24import java.util.*;
25
26/**
27 * A resource description that takes {@link ProblemResourceDescriptionStrategy#SHADOWING_KEY} into account when
28 * describing EObjects.
29 * <p>
30 * Based on {@link DefaultResourceDescription}.
31 */
32public class ProblemResourceDescription extends DefaultResourceDescription {
33 private static final Logger log = Logger.getLogger(ProblemResourceDescription.class);
34
35 private final IDefaultResourceDescriptionStrategy strategy;
36
37 public ProblemResourceDescription(Resource resource, IDefaultResourceDescriptionStrategy strategy) {
38 super(resource, strategy);
39 this.strategy = strategy;
40 }
41
42 /**
43 * Based on {@link DefaultResourceDescription#computeExportedObjects()}.
44 *
45 * @return The computed exported objects, taking shadowing into account.
46 */
47 @Override
48 protected List<IEObjectDescription> computeExportedObjects() {
49 if (!getResource().isLoaded()) {
50 try {
51 getResource().load(null);
52 } catch (IOException e) {
53 log.error(e.getMessage(), e);
54 return Collections.emptyList();
55 }
56 }
57 final Map<ProblemResourceDescriptionStrategy.ShadowingKey, List<IEObjectDescription>> nameToDescriptionsMap =
58 new LinkedHashMap<>();
59 IAcceptor<IEObjectDescription> acceptor = eObjectDescription -> {
60 var key = ProblemResourceDescriptionStrategy.getShadowingKey(eObjectDescription);
61 var descriptions = nameToDescriptionsMap.computeIfAbsent(key, ignored -> new ArrayList<>());
62 descriptions.add(eObjectDescription);
63 };
64 TreeIterator<EObject> allProperContents = EcoreUtil.getAllProperContents(getResource(), false);
65 while (allProperContents.hasNext()) {
66 EObject content = allProperContents.next();
67 if (!strategy.createEObjectDescriptions(content, acceptor)) {
68 allProperContents.prune();
69 }
70 }
71 return omitShadowedNames(nameToDescriptionsMap);
72 }
73
74 private static List<IEObjectDescription> omitShadowedNames(
75 Map<ProblemResourceDescriptionStrategy.ShadowingKey, List<IEObjectDescription>> nameToDescriptionsMap) {
76 final List<IEObjectDescription> exportedEObjects = new ArrayList<>();
77 for (var entry : nameToDescriptionsMap.entrySet()) {
78 var descriptions = entry.getValue();
79 if (NamingUtil.isFullyQualified(entry.getKey().name())) {
80 exportedEObjects.addAll(descriptions);
81 } else {
82 boolean foundPreferred = false;
83 for (var description : descriptions) {
84 if (ProblemResourceDescriptionStrategy.PREFERRED_NAME_TRUE.equals(
85 description.getUserData(ProblemResourceDescriptionStrategy.PREFERRED_NAME))) {
86 exportedEObjects.add(description);
87 foundPreferred = true;
88 }
89 }
90 if (!foundPreferred) {
91 exportedEObjects.addAll(descriptions);
92 }
93 }
94 }
95 return exportedEObjects;
96 }
97
98 // Based on {@code DerivedStateAwareResourceDescriptionManager#createResourceDescription}.
99 @Override
100 protected EObjectDescriptionLookUp getLookUp() {
101 if (lookup == null) {
102 lookup = new EObjectDescriptionLookUp(computeExportedObjects());
103 }
104 return lookup;
105 }
106}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionManager.java b/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionManager.java
new file mode 100644
index 00000000..23ca139a
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionManager.java
@@ -0,0 +1,19 @@
1/*
2 * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.language.resource;
7
8import org.eclipse.emf.ecore.resource.Resource;
9import org.eclipse.xtext.resource.DerivedStateAwareResourceDescriptionManager;
10import org.eclipse.xtext.resource.IDefaultResourceDescriptionStrategy;
11import org.eclipse.xtext.resource.IResourceDescription;
12
13public class ProblemResourceDescriptionManager extends DerivedStateAwareResourceDescriptionManager {
14 @Override
15 protected IResourceDescription createResourceDescription(Resource resource,
16 IDefaultResourceDescriptionStrategy strategy) {
17 return new ProblemResourceDescription(resource, strategy);
18 }
19}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionStrategy.java b/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionStrategy.java
index 79dad6e7..76fd5852 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionStrategy.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionStrategy.java
@@ -25,9 +25,16 @@ import java.util.Map;
25@Singleton 25@Singleton
26public class ProblemResourceDescriptionStrategy extends DefaultResourceDescriptionStrategy { 26public class ProblemResourceDescriptionStrategy extends DefaultResourceDescriptionStrategy {
27 private static final String DATA_PREFIX = "tools.refinery.language.resource.ProblemResourceDescriptionStrategy."; 27 private static final String DATA_PREFIX = "tools.refinery.language.resource.ProblemResourceDescriptionStrategy.";
28
28 public static final String ARITY = DATA_PREFIX + "ARITY"; 29 public static final String ARITY = DATA_PREFIX + "ARITY";
29 public static final String ERROR_PREDICATE = DATA_PREFIX + "ERROR_PREDICATE"; 30 public static final String ERROR_PREDICATE = DATA_PREFIX + "ERROR_PREDICATE";
30 public static final String ERROR_PREDICATE_TRUE = "true"; 31 public static final String ERROR_PREDICATE_TRUE = "true";
32 public static final String SHADOWING_KEY = DATA_PREFIX + "SHADOWING_KEY";
33 public static final String SHADOWING_KEY_PROBLEM = "problem";
34 public static final String SHADOWING_KEY_NODE = "node";
35 public static final String SHADOWING_KEY_RELATION = "relation";
36 public static final String PREFERRED_NAME = DATA_PREFIX + "PREFERRED_NAME";
37 public static final String PREFERRED_NAME_TRUE = "true";
31 public static final String COLOR_RELATION = DATA_PREFIX + "COLOR_RELATION"; 38 public static final String COLOR_RELATION = DATA_PREFIX + "COLOR_RELATION";
32 public static final String COLOR_RELATION_TRUE = "true"; 39 public static final String COLOR_RELATION_TRUE = "true";
33 40
@@ -44,8 +51,12 @@ public class ProblemResourceDescriptionStrategy extends DefaultResourceDescripti
44 return true; 51 return true;
45 } 52 }
46 var problem = EcoreUtil2.getContainerOfType(eObject, Problem.class); 53 var problem = EcoreUtil2.getContainerOfType(eObject, Problem.class);
47 var problemQualifiedName = getNameAsQualifiedName(problem);
48 var userData = getUserData(eObject); 54 var userData = getUserData(eObject);
55 if (eObject.equals(problem)) {
56 acceptEObjectDescription(eObject, qualifiedName, QualifiedName.EMPTY, userData, true, acceptor);
57 return true;
58 }
59 var problemQualifiedName = getNameAsQualifiedName(problem);
49 QualifiedName lastQualifiedNameToExport = null; 60 QualifiedName lastQualifiedNameToExport = null;
50 if (shouldExportSimpleName(eObject)) { 61 if (shouldExportSimpleName(eObject)) {
51 lastQualifiedNameToExport = qualifiedName; 62 lastQualifiedNameToExport = qualifiedName;
@@ -82,10 +93,14 @@ public class ProblemResourceDescriptionStrategy extends DefaultResourceDescripti
82 if (NamingUtil.isNullOrEmpty(name)) { 93 if (NamingUtil.isNullOrEmpty(name)) {
83 return null; 94 return null;
84 } 95 }
85 return qualifiedNameConverter.toQualifiedName(name); 96 var qualifiedName = qualifiedNameConverter.toQualifiedName(name);
97 if (eObject instanceof Problem) {
98 return NamingUtil.stripRootPrefix(qualifiedName);
99 }
100 return qualifiedName;
86 } 101 }
87 102
88 protected boolean shouldExport(EObject eObject) { 103 public static boolean shouldExport(EObject eObject) {
89 if (eObject instanceof Variable) { 104 if (eObject instanceof Variable) {
90 // Variables are always private to the containing predicate definition. 105 // Variables are always private to the containing predicate definition.
91 return false; 106 return false;
@@ -98,7 +113,12 @@ public class ProblemResourceDescriptionStrategy extends DefaultResourceDescripti
98 113
99 protected Map<String, String> getUserData(EObject eObject) { 114 protected Map<String, String> getUserData(EObject eObject) {
100 var builder = ImmutableMap.<String, String>builder(); 115 var builder = ImmutableMap.<String, String>builder();
101 if (eObject instanceof Relation relation) { 116 if (eObject instanceof Problem) {
117 builder.put(SHADOWING_KEY, SHADOWING_KEY_PROBLEM);
118 } else if (eObject instanceof Node) {
119 builder.put(SHADOWING_KEY, SHADOWING_KEY_NODE);
120 } else if (eObject instanceof Relation relation) {
121 builder.put(SHADOWING_KEY, SHADOWING_KEY_RELATION);
102 int arity = ProblemUtil.getArity(relation); 122 int arity = ProblemUtil.getArity(relation);
103 builder.put(ARITY, Integer.toString(arity)); 123 builder.put(ARITY, Integer.toString(arity));
104 } 124 }
@@ -124,20 +144,31 @@ public class ProblemResourceDescriptionStrategy extends DefaultResourceDescripti
124 } 144 }
125 145
126 private void acceptEObjectDescription(EObject eObject, QualifiedName prefix, QualifiedName qualifiedName, 146 private void acceptEObjectDescription(EObject eObject, QualifiedName prefix, QualifiedName qualifiedName,
127 Map<String, String> userData, boolean fullyQualified, 147 Map<String, String> userData, boolean preferredName,
128 IAcceptor<IEObjectDescription> acceptor) { 148 IAcceptor<IEObjectDescription> acceptor) {
129 var qualifiedNameWithPrefix = prefix == null ? qualifiedName : prefix.append(qualifiedName); 149 var qualifiedNameWithPrefix = prefix == null ? qualifiedName : prefix.append(qualifiedName);
130 Map<String, String> userDataWithFullyQualified; 150 var userDataWithPreference = userData;
131 if (fullyQualified && shouldColorRelation(eObject)) { 151 if (preferredName) {
132 userDataWithFullyQualified = ImmutableMap.<String, String>builder() 152 userDataWithPreference = ImmutableMap.<String, String>builder()
133 .putAll(userData) 153 .putAll(userData)
134 .put(COLOR_RELATION, COLOR_RELATION_TRUE) 154 .put(PREFERRED_NAME, PREFERRED_NAME_TRUE)
135 .build(); 155 .build();
136 } else {
137 userDataWithFullyQualified = userData;
138 } 156 }
139 var description = EObjectDescription.create(qualifiedNameWithPrefix, eObject, userDataWithFullyQualified); 157 var description = EObjectDescription.create(qualifiedNameWithPrefix, eObject, userDataWithPreference);
140 acceptor.accept(description); 158 acceptor.accept(description);
159 if (!preferredName) {
160 return;
161 }
162 var userDataWithFullyQualified = userDataWithPreference;
163 if (shouldColorRelation(eObject)) {
164 userDataWithFullyQualified = ImmutableMap.<String, String>builder()
165 .putAll(userDataWithPreference)
166 .put(COLOR_RELATION, COLOR_RELATION_TRUE)
167 .build();
168 }
169 var rootQualifiedName = NamingUtil.addRootPrefix(qualifiedNameWithPrefix);
170 var rootDescription = EObjectDescription.create(rootQualifiedName, eObject, userDataWithFullyQualified);
171 acceptor.accept(rootDescription);
141 } 172 }
142 173
143 private boolean shouldColorRelation(EObject eObject) { 174 private boolean shouldColorRelation(EObject eObject) {
@@ -146,4 +177,11 @@ public class ProblemResourceDescriptionStrategy extends DefaultResourceDescripti
146 } 177 }
147 return eObject instanceof ClassDeclaration || eObject instanceof EnumDeclaration; 178 return eObject instanceof ClassDeclaration || eObject instanceof EnumDeclaration;
148 } 179 }
180
181 public static ShadowingKey getShadowingKey(IEObjectDescription description) {
182 return new ShadowingKey(description.getName(), description.getUserData(SHADOWING_KEY));
183 }
184
185 public record ShadowingKey(QualifiedName name, String shadowingKey) {
186 }
149} 187}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/scoping/NormalizedSelectable.java b/subprojects/language/src/main/java/tools/refinery/language/scoping/NormalizedSelectable.java
new file mode 100644
index 00000000..0c7828d8
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/scoping/NormalizedSelectable.java
@@ -0,0 +1,122 @@
1/*
2 * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.language.scoping;
7
8import com.google.common.collect.Iterables;
9import org.eclipse.emf.ecore.EClass;
10import org.eclipse.emf.ecore.EObject;
11import org.eclipse.xtext.naming.QualifiedName;
12import org.eclipse.xtext.resource.IEObjectDescription;
13import org.eclipse.xtext.resource.ISelectable;
14import org.eclipse.xtext.resource.impl.AliasedEObjectDescription;
15import org.jetbrains.annotations.NotNull;
16
17import java.util.Iterator;
18import java.util.NoSuchElementException;
19
20public class NormalizedSelectable implements ISelectable {
21 private final ISelectable delegateSelectable;
22 private final QualifiedName originalPrefix;
23 private final QualifiedName normalizedPrefix;
24
25 private NormalizedSelectable(ISelectable delegateSelectable, QualifiedName originalPrefix,
26 QualifiedName normalizedPrefix) {
27 this.delegateSelectable = delegateSelectable;
28 this.originalPrefix = originalPrefix;
29 this.normalizedPrefix = normalizedPrefix;
30 }
31
32 @Override
33 public boolean isEmpty() {
34 return delegateSelectable.isEmpty();
35 }
36
37 @Override
38 public Iterable<IEObjectDescription> getExportedObjects() {
39 var delegateIterable = delegateSelectable.getExportedObjects();
40 var aliasedIterable = getAliasedElements(delegateIterable);
41 return Iterables.concat(delegateIterable, aliasedIterable);
42 }
43
44 @Override
45 public Iterable<IEObjectDescription> getExportedObjects(EClass type, QualifiedName name, boolean ignoreCase) {
46 var delegateIterable = delegateSelectable.getExportedObjects(type, name, ignoreCase);
47 boolean startsWith = ignoreCase ? name.startsWithIgnoreCase(normalizedPrefix) :
48 name.startsWith(normalizedPrefix);
49 if (startsWith && name.getSegmentCount() > normalizedPrefix.getSegmentCount()) {
50 var originalName = originalPrefix.append(name.skipFirst(normalizedPrefix.getSegmentCount()));
51 var originalIterable = Iterables.transform(
52 delegateSelectable.getExportedObjects(type, originalName, ignoreCase),
53 description -> {
54 var normalizedName = normalizedPrefix.append(
55 description.getName().skipFirst(originalPrefix.getSegmentCount()));
56 return new AliasedEObjectDescription(normalizedName, description);
57 });
58 return Iterables.concat(originalIterable, delegateIterable);
59 }
60 return delegateIterable;
61 }
62
63 @Override
64 public Iterable<IEObjectDescription> getExportedObjectsByType(EClass type) {
65 var delegateIterable = delegateSelectable.getExportedObjectsByType(type);
66 var aliasedIterable = getAliasedElements(delegateIterable);
67 return Iterables.concat(delegateIterable, aliasedIterable);
68 }
69
70 @Override
71 public Iterable<IEObjectDescription> getExportedObjectsByObject(EObject object) {
72 var delegateIterable = delegateSelectable.getExportedObjectsByObject(object);
73 var aliasedIterable = getAliasedElements(delegateIterable);
74 return Iterables.concat(delegateIterable, aliasedIterable);
75 }
76
77 private Iterable<IEObjectDescription> getAliasedElements(Iterable<IEObjectDescription> delegateIterable) {
78 return () -> new Iterator<>() {
79 private final Iterator<IEObjectDescription> delegateIterator = delegateIterable.iterator();
80 private IEObjectDescription next = computeNext();
81
82 @Override
83 public boolean hasNext() {
84 return next != null;
85 }
86
87 @Override
88 public IEObjectDescription next() {
89 if (!hasNext()) {
90 throw new NoSuchElementException();
91 }
92 var current = next;
93 next = computeNext();
94 return current;
95 }
96
97 private IEObjectDescription computeNext() {
98 while (delegateIterator.hasNext()) {
99 var description = delegateIterator.next();
100 var qualifiedName = description.getName();
101 if (qualifiedName.startsWith(originalPrefix) &&
102 qualifiedName.getSegmentCount() > originalPrefix.getSegmentCount()) {
103 var alias = normalizedPrefix.append(qualifiedName.skipFirst(originalPrefix.getSegmentCount()));
104 return new AliasedEObjectDescription(alias, description);
105 }
106 }
107 return null;
108 }
109 };
110 }
111
112 public static ISelectable of(@NotNull ISelectable delegateSelectable, @NotNull QualifiedName originalPrefix,
113 @NotNull QualifiedName normalizedPrefix) {
114 if (originalPrefix.equals(normalizedPrefix)) {
115 return delegateSelectable;
116 }
117 if (originalPrefix.equals(QualifiedName.EMPTY)) {
118 throw new IllegalArgumentException("Cannot normalize empty qualified name prefix");
119 }
120 return new NormalizedSelectable(delegateSelectable, originalPrefix, normalizedPrefix);
121 }
122}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemGlobalScopeProvider.java b/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemGlobalScopeProvider.java
index 4d2dd772..37a67c0c 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemGlobalScopeProvider.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemGlobalScopeProvider.java
@@ -5,14 +5,21 @@
5 */ 5 */
6package tools.refinery.language.scoping; 6package tools.refinery.language.scoping;
7 7
8import java.util.LinkedHashSet; 8import com.google.common.base.Predicate;
9
10import org.eclipse.emf.common.util.URI; 9import org.eclipse.emf.common.util.URI;
10import org.eclipse.emf.ecore.EClass;
11import org.eclipse.emf.ecore.resource.Resource; 11import org.eclipse.emf.ecore.resource.Resource;
12import org.eclipse.xtext.naming.QualifiedName;
13import org.eclipse.xtext.resource.IEObjectDescription;
14import org.eclipse.xtext.resource.IResourceDescriptions;
15import org.eclipse.xtext.resource.ISelectable;
16import org.eclipse.xtext.scoping.IScope;
12import org.eclipse.xtext.scoping.impl.ImportUriGlobalScopeProvider; 17import org.eclipse.xtext.scoping.impl.ImportUriGlobalScopeProvider;
13 18import org.eclipse.xtext.scoping.impl.SelectableBasedScope;
14import tools.refinery.language.utils.ProblemUtil; 19import tools.refinery.language.utils.ProblemUtil;
15 20
21import java.util.LinkedHashSet;
22
16public class ProblemGlobalScopeProvider extends ImportUriGlobalScopeProvider { 23public class ProblemGlobalScopeProvider extends ImportUriGlobalScopeProvider {
17 @Override 24 @Override
18 protected LinkedHashSet<URI> getImportedUris(Resource resource) { 25 protected LinkedHashSet<URI> getImportedUris(Resource resource) {
@@ -20,4 +27,14 @@ public class ProblemGlobalScopeProvider extends ImportUriGlobalScopeProvider {
20 importedUris.add(ProblemUtil.BUILTIN_LIBRARY_URI); 27 importedUris.add(ProblemUtil.BUILTIN_LIBRARY_URI);
21 return importedUris; 28 return importedUris;
22 } 29 }
30
31 @Override
32 protected IScope createLazyResourceScope(IScope parent, URI uri, IResourceDescriptions descriptions, EClass type,
33 Predicate<IEObjectDescription> filter, boolean ignoreCase) {
34 ISelectable description = descriptions.getResourceDescription(uri);
35 if (description != null && ProblemUtil.BUILTIN_LIBRARY_URI.equals(uri)) {
36 description = NormalizedSelectable.of(description, QualifiedName.create("builtin"), QualifiedName.EMPTY);
37 }
38 return SelectableBasedScope.createScope(parent, description, filter, type, ignoreCase);
39 }
23} 40}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemLocalScopeProvider.java b/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemLocalScopeProvider.java
index 229960a0..9be32636 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemLocalScopeProvider.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemLocalScopeProvider.java
@@ -1,47 +1,42 @@
1/* 1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> 2 * SPDX-FileCopyrightText: 2021-2024 The Refinery Authors <https://refinery.tools/>
3 * 3 *
4 * SPDX-License-Identifier: EPL-2.0 4 * SPDX-License-Identifier: EPL-2.0
5 */ 5 */
6package tools.refinery.language.scoping; 6package tools.refinery.language.scoping;
7 7
8import java.util.List; 8import com.google.inject.Inject;
9
10import org.eclipse.emf.ecore.EObject;
11import org.eclipse.emf.ecore.resource.Resource; 9import org.eclipse.emf.ecore.resource.Resource;
10import org.eclipse.xtext.naming.IQualifiedNameProvider;
12import org.eclipse.xtext.naming.QualifiedName; 11import org.eclipse.xtext.naming.QualifiedName;
13import org.eclipse.xtext.resource.IResourceDescriptions;
14import org.eclipse.xtext.resource.IResourceDescriptionsProvider; 12import org.eclipse.xtext.resource.IResourceDescriptionsProvider;
15import org.eclipse.xtext.resource.ISelectable; 13import org.eclipse.xtext.resource.ISelectable;
16import org.eclipse.xtext.scoping.impl.ImportNormalizer; 14import org.eclipse.xtext.scoping.impl.SimpleLocalScopeProvider;
17import org.eclipse.xtext.scoping.impl.ImportedNamespaceAwareLocalScopeProvider; 15import tools.refinery.language.naming.NamingUtil;
18
19import com.google.inject.Inject;
20
21import tools.refinery.language.utils.ProblemUtil;
22 16
23public class ProblemLocalScopeProvider extends ImportedNamespaceAwareLocalScopeProvider { 17public class ProblemLocalScopeProvider extends SimpleLocalScopeProvider {
24 private static final QualifiedName BUILTIN_LIBRARY_QUALIFIED_NAME = QualifiedName 18 @Inject
25 .create(ProblemUtil.BUILTIN_LIBRARY_NAME); 19 private IQualifiedNameProvider qualifiedNameProvider;
26 20
27 @Inject 21 @Inject
28 private IResourceDescriptionsProvider resourceDescriptionsProvider; 22 private IResourceDescriptionsProvider resourceDescriptionsProvider;
29 23
30 @Override 24 @Override
31 protected List<ImportNormalizer> getImplicitImports(boolean ignoreCase) { 25 protected ISelectable getAllDescriptions(Resource resource) {
32 return List.of(doCreateImportNormalizer(BUILTIN_LIBRARY_QUALIFIED_NAME, true, ignoreCase));
33 }
34
35 @Override
36 protected List<ImportNormalizer> getImportedNamespaceResolvers(EObject context, boolean ignoreCase) {
37 return List.of();
38 }
39
40 @Override
41 protected ISelectable internalGetAllDescriptions(Resource resource) {
42 // Force the use of ProblemResourceDescriptionStrategy to include all QualifiedNames of objects. 26 // Force the use of ProblemResourceDescriptionStrategy to include all QualifiedNames of objects.
43 IResourceDescriptions resourceDescriptions = resourceDescriptionsProvider 27 var resourceDescriptions = resourceDescriptionsProvider
44 .getResourceDescriptions(resource.getResourceSet()); 28 .getResourceDescriptions(resource.getResourceSet());
45 return resourceDescriptions.getResourceDescription(resource.getURI()); 29 var resourceDescription = resourceDescriptions.getResourceDescription(resource.getURI());
30 if (resourceDescription != null && !resource.getContents().isEmpty()) {
31 var rootElement = resource.getContents().getFirst();
32 if (rootElement != null) {
33 var rootName = NamingUtil.stripRootPrefix(qualifiedNameProvider.getFullyQualifiedName(rootElement));
34 if (rootName == null) {
35 return resourceDescription;
36 }
37 return NormalizedSelectable.of(resourceDescription, rootName, QualifiedName.EMPTY);
38 }
39 }
40 return resourceDescription;
46 } 41 }
47} 42}