diff options
author | Kristóf Marussy <kristof@marussy.com> | 2024-02-05 18:54:46 +0100 |
---|---|---|
committer | Kristóf Marussy <kristof@marussy.com> | 2024-02-06 01:50:23 +0100 |
commit | 1ae461bd5319370e12878785d9c7ec9f89429406 (patch) | |
tree | a18797b520369a7f40ee13988308de67c66e046d /subprojects | |
parent | Revert "refactor: simplify module name inference" (diff) | |
download | refinery-1ae461bd5319370e12878785d9c7ec9f89429406.tar.gz refinery-1ae461bd5319370e12878785d9c7ec9f89429406.tar.zst refinery-1ae461bd5319370e12878785d9c7ec9f89429406.zip |
feat(language): automatic problem kind inference
Diffstat (limited to 'subprojects')
13 files changed, 164 insertions, 41 deletions
diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/SolutionSerializer.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/SolutionSerializer.java index dad867b8..2fb0a49d 100644 --- a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/SolutionSerializer.java +++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/SolutionSerializer.java | |||
@@ -13,7 +13,6 @@ import org.eclipse.emf.common.util.URI; | |||
13 | import org.eclipse.emf.ecore.util.EcoreUtil; | 13 | import org.eclipse.emf.ecore.util.EcoreUtil; |
14 | import org.eclipse.xtext.naming.IQualifiedNameProvider; | 14 | import org.eclipse.xtext.naming.IQualifiedNameProvider; |
15 | import org.eclipse.xtext.naming.QualifiedName; | 15 | import org.eclipse.xtext.naming.QualifiedName; |
16 | import org.eclipse.xtext.resource.FileExtensionProvider; | ||
17 | import org.eclipse.xtext.resource.IResourceFactory; | 16 | import org.eclipse.xtext.resource.IResourceFactory; |
18 | import org.eclipse.xtext.resource.XtextResource; | 17 | import org.eclipse.xtext.resource.XtextResource; |
19 | import org.eclipse.xtext.resource.XtextResourceSet; | 18 | import org.eclipse.xtext.resource.XtextResourceSet; |
@@ -42,8 +41,6 @@ import java.util.function.Function; | |||
42 | import java.util.stream.Collectors; | 41 | import java.util.stream.Collectors; |
43 | 42 | ||
44 | public class SolutionSerializer { | 43 | public class SolutionSerializer { |
45 | private String fileExtension; | ||
46 | |||
47 | @Inject | 44 | @Inject |
48 | private Provider<XtextResourceSet> resourceSetProvider; | 45 | private Provider<XtextResourceSet> resourceSetProvider; |
49 | 46 | ||
@@ -74,13 +71,8 @@ public class SolutionSerializer { | |||
74 | private NodeDeclaration nodeDeclaration; | 71 | private NodeDeclaration nodeDeclaration; |
75 | private final MutableIntObjectMap<Node> nodes = IntObjectMaps.mutable.empty(); | 72 | private final MutableIntObjectMap<Node> nodes = IntObjectMaps.mutable.empty(); |
76 | 73 | ||
77 | @Inject | ||
78 | public void setFileExtensionProvider(FileExtensionProvider fileExtensionProvider) { | ||
79 | this.fileExtension = fileExtensionProvider.getPrimaryFileExtension(); | ||
80 | } | ||
81 | |||
82 | public Problem serializeSolution(ProblemTrace trace, Model model) { | 74 | public Problem serializeSolution(ProblemTrace trace, Model model) { |
83 | var uri = URI.createURI("__synthetic." + fileExtension); | 75 | var uri = URI.createURI("__synthetic." + ProblemUtil.MODULE_EXTENSION); |
84 | return serializeSolution(trace, model, uri); | 76 | return serializeSolution(trace, model, uri); |
85 | } | 77 | } |
86 | 78 | ||
@@ -135,8 +127,8 @@ public class SolutionSerializer { | |||
135 | private Problem copyProblem(Problem originalProblem, URI uri) { | 127 | private Problem copyProblem(Problem originalProblem, URI uri) { |
136 | var newResourceSet = resourceSetProvider.get(); | 128 | var newResourceSet = resourceSetProvider.get(); |
137 | ImportAdapter.copySettings(originalProblem, newResourceSet); | 129 | ImportAdapter.copySettings(originalProblem, newResourceSet); |
138 | if (!fileExtension.equals(uri.fileExtension())) { | 130 | if (!ProblemUtil.MODULE_EXTENSION.equals(uri.fileExtension())) { |
139 | uri = uri.appendFileExtension(fileExtension); | 131 | uri = uri.appendFileExtension(ProblemUtil.MODULE_EXTENSION); |
140 | } | 132 | } |
141 | var newResource = resourceFactory.createResource(uri); | 133 | var newResource = resourceFactory.createResource(uri); |
142 | newResourceSet.getResources().add(newResource); | 134 | newResourceSet.getResources().add(newResource); |
diff --git a/subprojects/language-semantics/src/test/java/tools/refinery/language/semantics/SolutionSerializerTest.java b/subprojects/language-semantics/src/test/java/tools/refinery/language/semantics/SolutionSerializerTest.java index 949b7047..be675a80 100644 --- a/subprojects/language-semantics/src/test/java/tools/refinery/language/semantics/SolutionSerializerTest.java +++ b/subprojects/language-semantics/src/test/java/tools/refinery/language/semantics/SolutionSerializerTest.java | |||
@@ -72,7 +72,7 @@ class SolutionSerializerTest { | |||
72 | solution.eResource().save(outputStream, Map.of()); | 72 | solution.eResource().save(outputStream, Map.of()); |
73 | actualOutput = outputStream.toString(); | 73 | actualOutput = outputStream.toString(); |
74 | } | 74 | } |
75 | assertThat(actualOutput, is("module.\n\n" + prefix + "\n" + expectedOutput)); | 75 | assertThat(actualOutput, is(prefix + "\n" + expectedOutput)); |
76 | } | 76 | } |
77 | 77 | ||
78 | static Stream<Arguments> solutionSerializerTest() { | 78 | static Stream<Arguments> solutionSerializerTest() { |
diff --git a/subprojects/language/src/main/java/tools/refinery/language/GenerateProblem.mwe2 b/subprojects/language/src/main/java/tools/refinery/language/GenerateProblem.mwe2 index 863e55d4..46ac18fe 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/GenerateProblem.mwe2 +++ b/subprojects/language/src/main/java/tools/refinery/language/GenerateProblem.mwe2 | |||
@@ -51,7 +51,7 @@ Workflow { | |||
51 | 51 | ||
52 | language = StandardLanguage { | 52 | language = StandardLanguage { |
53 | name = 'tools.refinery.language.Problem' | 53 | name = 'tools.refinery.language.Problem' |
54 | fileExtensions = 'refinery,problem' | 54 | fileExtensions = 'problem,refinery' |
55 | referencedResource = 'platform:/resource/tools.refinery.refinery-language-model/model/problem.genmodel' | 55 | referencedResource = 'platform:/resource/tools.refinery.refinery-language-model/model/problem.genmodel' |
56 | serializer = { | 56 | serializer = { |
57 | generateStub = false | 57 | generateStub = false |
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 a846e265..f9a564b0 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/ProblemRuntimeModule.java +++ b/subprojects/language/src/main/java/tools/refinery/language/ProblemRuntimeModule.java | |||
@@ -15,7 +15,9 @@ import org.eclipse.xtext.conversion.IValueConverterService; | |||
15 | import org.eclipse.xtext.linking.ILinkingService; | 15 | import org.eclipse.xtext.linking.ILinkingService; |
16 | import org.eclipse.xtext.naming.IQualifiedNameConverter; | 16 | import org.eclipse.xtext.naming.IQualifiedNameConverter; |
17 | import org.eclipse.xtext.naming.IQualifiedNameProvider; | 17 | import org.eclipse.xtext.naming.IQualifiedNameProvider; |
18 | import org.eclipse.xtext.parser.IAstFactory; | ||
18 | import org.eclipse.xtext.parser.IParser; | 19 | import org.eclipse.xtext.parser.IParser; |
20 | import org.eclipse.xtext.parsetree.reconstr.ITransientValueService; | ||
19 | import org.eclipse.xtext.resource.*; | 21 | import org.eclipse.xtext.resource.*; |
20 | import org.eclipse.xtext.scoping.IGlobalScopeProvider; | 22 | import org.eclipse.xtext.scoping.IGlobalScopeProvider; |
21 | import org.eclipse.xtext.scoping.IScopeProvider; | 23 | import org.eclipse.xtext.scoping.IScopeProvider; |
@@ -30,13 +32,18 @@ import tools.refinery.language.linking.ProblemLinkingService; | |||
30 | import tools.refinery.language.naming.ProblemDelegateQualifiedNameProvider; | 32 | import tools.refinery.language.naming.ProblemDelegateQualifiedNameProvider; |
31 | import tools.refinery.language.naming.ProblemQualifiedNameConverter; | 33 | import tools.refinery.language.naming.ProblemQualifiedNameConverter; |
32 | import tools.refinery.language.naming.ProblemQualifiedNameProvider; | 34 | import tools.refinery.language.naming.ProblemQualifiedNameProvider; |
35 | import tools.refinery.language.parser.ProblemEcoreElementFactory; | ||
33 | import tools.refinery.language.parser.antlr.TokenSourceInjectingProblemParser; | 36 | import tools.refinery.language.parser.antlr.TokenSourceInjectingProblemParser; |
34 | import tools.refinery.language.resource.*; | 37 | import tools.refinery.language.resource.ProblemLocationInFileProvider; |
38 | import tools.refinery.language.resource.ProblemResource; | ||
39 | import tools.refinery.language.resource.ProblemResourceDescriptionManager; | ||
40 | import tools.refinery.language.resource.ProblemResourceDescriptionStrategy; | ||
35 | import tools.refinery.language.resource.state.ProblemDerivedStateComputer; | 41 | import tools.refinery.language.resource.state.ProblemDerivedStateComputer; |
36 | import tools.refinery.language.scoping.ProblemGlobalScopeProvider; | 42 | import tools.refinery.language.scoping.ProblemGlobalScopeProvider; |
37 | import tools.refinery.language.scoping.ProblemLocalScopeProvider; | 43 | import tools.refinery.language.scoping.ProblemLocalScopeProvider; |
38 | import tools.refinery.language.serializer.PreferShortAssertionsProblemSemanticSequencer; | 44 | import tools.refinery.language.serializer.PreferShortAssertionsProblemSemanticSequencer; |
39 | import tools.refinery.language.serializer.ProblemCrossReferenceSerializer; | 45 | import tools.refinery.language.serializer.ProblemCrossReferenceSerializer; |
46 | import tools.refinery.language.serializer.ProblemTransientValueService; | ||
40 | import tools.refinery.language.validation.ProblemDiagnosticConverter; | 47 | import tools.refinery.language.validation.ProblemDiagnosticConverter; |
41 | 48 | ||
42 | /** | 49 | /** |
@@ -51,6 +58,11 @@ public class ProblemRuntimeModule extends AbstractProblemRuntimeModule { | |||
51 | return TokenSourceInjectingProblemParser.class; | 58 | return TokenSourceInjectingProblemParser.class; |
52 | } | 59 | } |
53 | 60 | ||
61 | @Override | ||
62 | public Class<? extends IAstFactory> bindIAstFactory() { | ||
63 | return ProblemEcoreElementFactory.class; | ||
64 | } | ||
65 | |||
54 | public Class<? extends IQualifiedNameConverter> bindIQualifiedNameConverter() { | 66 | public Class<? extends IQualifiedNameConverter> bindIQualifiedNameConverter() { |
55 | return ProblemQualifiedNameConverter.class; | 67 | return ProblemQualifiedNameConverter.class; |
56 | } | 68 | } |
@@ -116,6 +128,11 @@ public class ProblemRuntimeModule extends AbstractProblemRuntimeModule { | |||
116 | } | 128 | } |
117 | 129 | ||
118 | @Override | 130 | @Override |
131 | public Class<? extends ITransientValueService> bindITransientValueService() { | ||
132 | return ProblemTransientValueService.class; | ||
133 | } | ||
134 | |||
135 | @Override | ||
119 | public Class<? extends ISemanticSequencer> bindISemanticSequencer() { | 136 | public Class<? extends ISemanticSequencer> bindISemanticSequencer() { |
120 | return PreferShortAssertionsProblemSemanticSequencer.class; | 137 | return PreferShortAssertionsProblemSemanticSequencer.class; |
121 | } | 138 | } |
diff --git a/subprojects/language/src/main/java/tools/refinery/language/library/ClasspathBasedLibrary.java b/subprojects/language/src/main/java/tools/refinery/language/library/ClasspathBasedLibrary.java index 4b748c64..3a9bb920 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/library/ClasspathBasedLibrary.java +++ b/subprojects/language/src/main/java/tools/refinery/language/library/ClasspathBasedLibrary.java | |||
@@ -59,7 +59,7 @@ public abstract class ClasspathBasedLibrary implements RefineryLibrary { | |||
59 | } | 59 | } |
60 | int rootSegmentCount = rootUri.segmentCount(); | 60 | int rootSegmentCount = rootUri.segmentCount(); |
61 | int uriSegmentCount = uri.segmentCount(); | 61 | int uriSegmentCount = uri.segmentCount(); |
62 | if (!uri.segment(uriSegmentCount - 1).endsWith(RefineryLibrary.EXTENSION)) { | 62 | if (!uri.segment(uriSegmentCount - 1).endsWith(RefineryLibrary.FILE_NAME_SUFFIX)) { |
63 | return Optional.empty(); | 63 | return Optional.empty(); |
64 | } | 64 | } |
65 | var segments = new ArrayList<String>(); | 65 | var segments = new ArrayList<String>(); |
@@ -73,7 +73,7 @@ public abstract class ClasspathBasedLibrary implements RefineryLibrary { | |||
73 | while (i < uriSegmentCount) { | 73 | while (i < uriSegmentCount) { |
74 | var segment = uri.segment(i); | 74 | var segment = uri.segment(i); |
75 | if (i == uriSegmentCount - 1) { | 75 | if (i == uriSegmentCount - 1) { |
76 | segment = segment.substring(0, segment.length() - RefineryLibrary.EXTENSION.length()); | 76 | segment = segment.substring(0, segment.length() - RefineryLibrary.FILE_NAME_SUFFIX.length()); |
77 | } | 77 | } |
78 | segments.add(segment); | 78 | segments.add(segment); |
79 | i++; | 79 | i++; |
@@ -88,7 +88,7 @@ public abstract class ClasspathBasedLibrary implements RefineryLibrary { | |||
88 | public static Optional<URI> getLibraryUri(Class<?> context, QualifiedName qualifiedName) { | 88 | public static Optional<URI> getLibraryUri(Class<?> context, QualifiedName qualifiedName) { |
89 | var packagePath = context.getPackageName().replace('.', '/'); | 89 | var packagePath = context.getPackageName().replace('.', '/'); |
90 | var libraryPath = String.join("/", qualifiedName.getSegments()); | 90 | var libraryPath = String.join("/", qualifiedName.getSegments()); |
91 | var resourceName = "%s/%s%s".formatted(packagePath, libraryPath, RefineryLibrary.EXTENSION); | 91 | var resourceName = "%s/%s%s".formatted(packagePath, libraryPath, RefineryLibrary.FILE_NAME_SUFFIX); |
92 | var resource = context.getClassLoader().getResource(resourceName); | 92 | var resource = context.getClassLoader().getResource(resourceName); |
93 | if (resource == null) { | 93 | if (resource == null) { |
94 | return Optional.empty(); | 94 | return Optional.empty(); |
diff --git a/subprojects/language/src/main/java/tools/refinery/language/library/PathLibrary.java b/subprojects/language/src/main/java/tools/refinery/language/library/PathLibrary.java index c6f994df..8eaf8458 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/library/PathLibrary.java +++ b/subprojects/language/src/main/java/tools/refinery/language/library/PathLibrary.java | |||
@@ -41,7 +41,7 @@ public final class PathLibrary implements RefineryLibrary { | |||
41 | for (var i = 0; i < segmentCount; i++) { | 41 | for (var i = 0; i < segmentCount; i++) { |
42 | var segment = qualifiedName.getSegment(i); | 42 | var segment = qualifiedName.getSegment(i); |
43 | if (i == segmentCount - 1) { | 43 | if (i == segmentCount - 1) { |
44 | segment = segment + RefineryLibrary.EXTENSION; | 44 | segment = segment + RefineryLibrary.FILE_NAME_SUFFIX; |
45 | } | 45 | } |
46 | if (i == 0) { | 46 | if (i == 0) { |
47 | first = segment; | 47 | first = segment; |
@@ -80,10 +80,10 @@ public final class PathLibrary implements RefineryLibrary { | |||
80 | } | 80 | } |
81 | int lastIndex = segments.size() - 1; | 81 | int lastIndex = segments.size() - 1; |
82 | var lastSegment = segments.get(lastIndex); | 82 | var lastSegment = segments.get(lastIndex); |
83 | if (!lastSegment.endsWith(EXTENSION)) { | 83 | if (!lastSegment.endsWith(FILE_NAME_SUFFIX)) { |
84 | return Optional.empty(); | 84 | return Optional.empty(); |
85 | } | 85 | } |
86 | lastSegment = lastSegment.substring(0, lastSegment.length() - RefineryLibrary.EXTENSION.length()); | 86 | lastSegment = lastSegment.substring(0, lastSegment.length() - RefineryLibrary.FILE_NAME_SUFFIX.length()); |
87 | segments.set(lastIndex, lastSegment); | 87 | segments.set(lastIndex, lastSegment); |
88 | return Optional.of(QualifiedName.create(segments)); | 88 | return Optional.of(QualifiedName.create(segments)); |
89 | } | 89 | } |
diff --git a/subprojects/language/src/main/java/tools/refinery/language/library/RefineryLibrary.java b/subprojects/language/src/main/java/tools/refinery/language/library/RefineryLibrary.java index e1f8d7bc..2559749a 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/library/RefineryLibrary.java +++ b/subprojects/language/src/main/java/tools/refinery/language/library/RefineryLibrary.java | |||
@@ -7,13 +7,14 @@ package tools.refinery.language.library; | |||
7 | 7 | ||
8 | import org.eclipse.emf.common.util.URI; | 8 | import org.eclipse.emf.common.util.URI; |
9 | import org.eclipse.xtext.naming.QualifiedName; | 9 | import org.eclipse.xtext.naming.QualifiedName; |
10 | import tools.refinery.language.utils.ProblemUtil; | ||
10 | 11 | ||
11 | import java.nio.file.Path; | 12 | import java.nio.file.Path; |
12 | import java.util.List; | 13 | import java.util.List; |
13 | import java.util.Optional; | 14 | import java.util.Optional; |
14 | 15 | ||
15 | public interface RefineryLibrary { | 16 | public interface RefineryLibrary { |
16 | String EXTENSION = ".refinery"; | 17 | String FILE_NAME_SUFFIX = "." + ProblemUtil.MODULE_EXTENSION; |
17 | 18 | ||
18 | default List<QualifiedName> getAutomaticImports() { | 19 | default List<QualifiedName> getAutomaticImports() { |
19 | return List.of(); | 20 | return List.of(); |
diff --git a/subprojects/language/src/main/java/tools/refinery/language/parser/ProblemEcoreElementFactory.java b/subprojects/language/src/main/java/tools/refinery/language/parser/ProblemEcoreElementFactory.java new file mode 100644 index 00000000..b54c6970 --- /dev/null +++ b/subprojects/language/src/main/java/tools/refinery/language/parser/ProblemEcoreElementFactory.java | |||
@@ -0,0 +1,48 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.language.parser; | ||
7 | |||
8 | import org.eclipse.emf.common.notify.impl.AdapterImpl; | ||
9 | import org.eclipse.emf.ecore.EObject; | ||
10 | import org.eclipse.emf.ecore.util.EcoreUtil; | ||
11 | import org.eclipse.xtext.conversion.ValueConverterException; | ||
12 | import org.eclipse.xtext.nodemodel.INode; | ||
13 | import org.eclipse.xtext.parser.DefaultEcoreElementFactory; | ||
14 | import tools.refinery.language.model.problem.Problem; | ||
15 | import tools.refinery.language.model.problem.ProblemPackage; | ||
16 | |||
17 | public class ProblemEcoreElementFactory extends DefaultEcoreElementFactory { | ||
18 | @Override | ||
19 | public void set( | ||
20 | EObject object, String feature, Object value, String ruleName, INode node) throws ValueConverterException { | ||
21 | super.set(object, feature, value, ruleName, node); | ||
22 | if (object instanceof Problem problem && ProblemPackage.Literals.PROBLEM__KIND.getName().equals(feature)) { | ||
23 | ExplicitAssignmentTracker.install(problem); | ||
24 | } | ||
25 | } | ||
26 | |||
27 | public static boolean hasExplicitlySetProblemKind(Problem problem) { | ||
28 | return ExplicitAssignmentTracker.hasAdapter(problem); | ||
29 | } | ||
30 | |||
31 | private static class ExplicitAssignmentTracker extends AdapterImpl { | ||
32 | @Override | ||
33 | public boolean isAdapterForType(Object type) { | ||
34 | return type == ExplicitAssignmentTracker.class; | ||
35 | } | ||
36 | |||
37 | public static boolean hasAdapter(Problem problem) { | ||
38 | return EcoreUtil.getAdapter(problem.eAdapters(), ExplicitAssignmentTracker.class) != null; | ||
39 | } | ||
40 | |||
41 | public static void install(Problem problem) { | ||
42 | if (hasAdapter(problem)) { | ||
43 | throw new IllegalStateException("Duplicate explicit assignment of module kind"); | ||
44 | } | ||
45 | problem.eAdapters().add(new ExplicitAssignmentTracker()); | ||
46 | } | ||
47 | } | ||
48 | } | ||
diff --git a/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResource.java b/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResource.java index 43239ffe..57b7bb45 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResource.java +++ b/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResource.java | |||
@@ -20,9 +20,13 @@ import org.eclipse.xtext.linking.impl.IllegalNodeException; | |||
20 | import org.eclipse.xtext.linking.impl.XtextLinkingDiagnostic; | 20 | import org.eclipse.xtext.linking.impl.XtextLinkingDiagnostic; |
21 | import org.eclipse.xtext.linking.lazy.LazyLinkingResource; | 21 | import org.eclipse.xtext.linking.lazy.LazyLinkingResource; |
22 | import org.eclipse.xtext.nodemodel.INode; | 22 | import org.eclipse.xtext.nodemodel.INode; |
23 | import org.eclipse.xtext.parser.IParseResult; | ||
23 | import org.eclipse.xtext.resource.DerivedStateAwareResource; | 24 | import org.eclipse.xtext.resource.DerivedStateAwareResource; |
24 | import org.eclipse.xtext.util.Triple; | 25 | import org.eclipse.xtext.util.Triple; |
25 | import org.jetbrains.annotations.Nullable; | 26 | import org.jetbrains.annotations.Nullable; |
27 | import tools.refinery.language.model.problem.Problem; | ||
28 | import tools.refinery.language.parser.ProblemEcoreElementFactory; | ||
29 | import tools.refinery.language.utils.ProblemUtil; | ||
26 | 30 | ||
27 | import java.util.Arrays; | 31 | import java.util.Arrays; |
28 | import java.util.List; | 32 | import java.util.List; |
@@ -40,6 +44,26 @@ public class ProblemResource extends DerivedStateAwareResource { | |||
40 | */ | 44 | */ |
41 | private int cyclicLinkingDetectionCounter = 0; | 45 | private int cyclicLinkingDetectionCounter = 0; |
42 | 46 | ||
47 | @Override | ||
48 | protected void updateInternalState(IParseResult oldParseResult, IParseResult newParseResult) { | ||
49 | if (isNewRootElement(oldParseResult, newParseResult) && | ||
50 | newParseResult.getRootASTElement() instanceof Problem newRootProblem && | ||
51 | !ProblemEcoreElementFactory.hasExplicitlySetProblemKind(newRootProblem)) { | ||
52 | var defaultModuleKind = ProblemUtil.getDefaultModuleKind(getURI()); | ||
53 | newRootProblem.setKind(defaultModuleKind); | ||
54 | } | ||
55 | super.updateInternalState(oldParseResult, newParseResult); | ||
56 | } | ||
57 | |||
58 | private boolean isNewRootElement(IParseResult oldParseResult, IParseResult newParseResult) { | ||
59 | if (oldParseResult == null) { | ||
60 | return true; | ||
61 | } | ||
62 | var oldRootAstElement = oldParseResult.getRootASTElement(); | ||
63 | var newRootAstElement = newParseResult.getRootASTElement(); | ||
64 | return oldRootAstElement != newRootAstElement; | ||
65 | } | ||
66 | |||
43 | /** | 67 | /** |
44 | * Tries to resolve a reference and emits a diagnostic if the reference is unresolvable or ambiguous. | 68 | * Tries to resolve a reference and emits a diagnostic if the reference is unresolvable or ambiguous. |
45 | * <p> | 69 | * <p> |
diff --git a/subprojects/language/src/main/java/tools/refinery/language/scoping/imports/ImportCollector.java b/subprojects/language/src/main/java/tools/refinery/language/scoping/imports/ImportCollector.java index 6cdfa63e..fc4ca43c 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/scoping/imports/ImportCollector.java +++ b/subprojects/language/src/main/java/tools/refinery/language/scoping/imports/ImportCollector.java | |||
@@ -11,6 +11,7 @@ import com.google.inject.Inject; | |||
11 | import com.google.inject.Provider; | 11 | import com.google.inject.Provider; |
12 | import com.google.inject.Singleton; | 12 | import com.google.inject.Singleton; |
13 | import org.eclipse.emf.common.util.URI; | 13 | import org.eclipse.emf.common.util.URI; |
14 | import org.eclipse.emf.ecore.EObject; | ||
14 | import org.eclipse.emf.ecore.resource.Resource; | 15 | import org.eclipse.emf.ecore.resource.Resource; |
15 | import org.eclipse.xtext.linking.impl.LinkingHelper; | 16 | import org.eclipse.xtext.linking.impl.LinkingHelper; |
16 | import org.eclipse.xtext.naming.IQualifiedNameConverter; | 17 | import org.eclipse.xtext.naming.IQualifiedNameConverter; |
@@ -57,16 +58,10 @@ public class ImportCollector { | |||
57 | if (resourceSet == null) { | 58 | if (resourceSet == null) { |
58 | return ImportCollection.EMPTY; | 59 | return ImportCollection.EMPTY; |
59 | } | 60 | } |
60 | Map<QualifiedName, Set<QualifiedName>> aliasesMap = new LinkedHashMap<>(); | ||
61 | for (var statement : problem.getStatements()) { | ||
62 | if (statement instanceof ImportStatement importStatement) { | ||
63 | collectImportStatement(importStatement, aliasesMap); | ||
64 | } | ||
65 | } | ||
66 | var adapter = ImportAdapter.getOrInstall(resourceSet); | 61 | var adapter = ImportAdapter.getOrInstall(resourceSet); |
67 | var collection = new ImportCollection(); | 62 | var collection = new ImportCollection(); |
68 | collectAutomaticImports(collection, adapter); | 63 | collectAutomaticImports(collection, adapter); |
69 | collectExplicitImports(aliasesMap, collection, adapter); | 64 | collectExplicitImports(problem, collection, adapter); |
70 | collection.remove(resource.getURI()); | 65 | collection.remove(resource.getURI()); |
71 | return collection; | 66 | return collection; |
72 | } | 67 | } |
@@ -82,25 +77,30 @@ public class ImportCollector { | |||
82 | } | 77 | } |
83 | } | 78 | } |
84 | 79 | ||
85 | private void collectExplicitImports(Map<QualifiedName, Set<QualifiedName>> aliasesMap, | 80 | private void collectExplicitImports(Problem problem, ImportCollection collection, ImportAdapter adapter) { |
86 | ImportCollection collection, ImportAdapter adapter) { | 81 | for (var statement : problem.getStatements()) { |
87 | for (var entry : aliasesMap.entrySet()) { | 82 | if (statement instanceof ImportStatement importStatement) { |
88 | var qualifiedName = entry.getKey(); | 83 | collectImportStatement(importStatement, collection, adapter); |
89 | var uri = adapter.resolveQualifiedName(qualifiedName); | ||
90 | if (uri != null) { | ||
91 | var aliases = entry.getValue(); | ||
92 | collection.add(NamedImport.explicit(uri, qualifiedName, List.copyOf(aliases))); | ||
93 | } | 84 | } |
94 | } | 85 | } |
95 | } | 86 | } |
96 | 87 | ||
97 | private void collectImportStatement(ImportStatement importStatement, | 88 | private void collectImportStatement(ImportStatement importStatement, ImportCollection collection, |
98 | Map<QualifiedName, Set<QualifiedName>> aliasesMap) { | 89 | ImportAdapter adapter) { |
99 | var nodes = NodeModelUtils.findNodesForFeature(importStatement, | 90 | var nodes = NodeModelUtils.findNodesForFeature(importStatement, |
100 | ProblemPackage.Literals.IMPORT_STATEMENT__IMPORTED_MODULE); | 91 | ProblemPackage.Literals.IMPORT_STATEMENT__IMPORTED_MODULE); |
101 | var aliasString = importStatement.getAlias(); | 92 | var aliasString = importStatement.getAlias(); |
102 | var alias = Strings.isNullOrEmpty(aliasString) ? QualifiedName.EMPTY : | 93 | var alias = Strings.isNullOrEmpty(aliasString) ? QualifiedName.EMPTY : |
103 | NamingUtil.stripRootPrefix(qualifiedNameConverter.toQualifiedName(aliasString)); | 94 | NamingUtil.stripRootPrefix(qualifiedNameConverter.toQualifiedName(aliasString)); |
95 | var referredProblem = (EObject) importStatement.eGet(ProblemPackage.Literals.IMPORT_STATEMENT__IMPORTED_MODULE, | ||
96 | false); | ||
97 | URI referencedUri = null; | ||
98 | if (referredProblem != null && !referredProblem.eIsProxy()) { | ||
99 | var resource = referredProblem.eResource(); | ||
100 | if (resource != null) { | ||
101 | referencedUri = resource.getURI(); | ||
102 | } | ||
103 | } | ||
104 | for (var node : nodes) { | 104 | for (var node : nodes) { |
105 | var qualifiedNameString = linkingHelper.getCrossRefNodeAsString(node, true); | 105 | var qualifiedNameString = linkingHelper.getCrossRefNodeAsString(node, true); |
106 | if (Strings.isNullOrEmpty(qualifiedNameString)) { | 106 | if (Strings.isNullOrEmpty(qualifiedNameString)) { |
@@ -108,8 +108,10 @@ public class ImportCollector { | |||
108 | } | 108 | } |
109 | var qualifiedName = NamingUtil.stripRootPrefix( | 109 | var qualifiedName = NamingUtil.stripRootPrefix( |
110 | qualifiedNameConverter.toQualifiedName(qualifiedNameString)); | 110 | qualifiedNameConverter.toQualifiedName(qualifiedNameString)); |
111 | var aliases = aliasesMap.computeIfAbsent(qualifiedName, ignored -> new LinkedHashSet<>()); | 111 | var uri = referencedUri == null ? adapter.resolveQualifiedName(qualifiedName) : referencedUri; |
112 | aliases.add(alias); | 112 | if (uri != null) { |
113 | collection.add(NamedImport.explicit(uri, qualifiedName, List.of(alias))); | ||
114 | } | ||
113 | } | 115 | } |
114 | } | 116 | } |
115 | 117 | ||
diff --git a/subprojects/language/src/main/java/tools/refinery/language/serializer/ProblemTransientValueService.java b/subprojects/language/src/main/java/tools/refinery/language/serializer/ProblemTransientValueService.java new file mode 100644 index 00000000..c364b30b --- /dev/null +++ b/subprojects/language/src/main/java/tools/refinery/language/serializer/ProblemTransientValueService.java | |||
@@ -0,0 +1,25 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.language.serializer; | ||
7 | |||
8 | import org.eclipse.emf.ecore.EObject; | ||
9 | import org.eclipse.emf.ecore.EStructuralFeature; | ||
10 | import org.eclipse.xtext.parsetree.reconstr.impl.DefaultTransientValueService; | ||
11 | import tools.refinery.language.model.problem.Problem; | ||
12 | import tools.refinery.language.model.problem.ProblemPackage; | ||
13 | import tools.refinery.language.parser.ProblemEcoreElementFactory; | ||
14 | import tools.refinery.language.utils.ProblemUtil; | ||
15 | |||
16 | public class ProblemTransientValueService extends DefaultTransientValueService { | ||
17 | @Override | ||
18 | public boolean isTransient(EObject owner, EStructuralFeature feature, int index) { | ||
19 | if (owner instanceof Problem problem && feature == ProblemPackage.Literals.PROBLEM__KIND) { | ||
20 | return problem.getName() == null && problem.getKind() == ProblemUtil.getDefaultModuleKind(problem) && | ||
21 | !ProblemEcoreElementFactory.hasExplicitlySetProblemKind(problem); | ||
22 | } | ||
23 | return super.isTransient(owner, feature, index); | ||
24 | } | ||
25 | } | ||
diff --git a/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java b/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java index 6b48cb5a..f70893e0 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java +++ b/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java | |||
@@ -5,6 +5,7 @@ | |||
5 | */ | 5 | */ |
6 | package tools.refinery.language.utils; | 6 | package tools.refinery.language.utils; |
7 | 7 | ||
8 | import org.eclipse.emf.common.util.URI; | ||
8 | import org.eclipse.emf.ecore.EObject; | 9 | import org.eclipse.emf.ecore.EObject; |
9 | import org.eclipse.emf.ecore.util.EcoreUtil; | 10 | import org.eclipse.emf.ecore.util.EcoreUtil; |
10 | import org.eclipse.xtext.EcoreUtil2; | 11 | import org.eclipse.xtext.EcoreUtil2; |
@@ -12,6 +13,8 @@ import tools.refinery.language.library.BuiltinLibrary; | |||
12 | import tools.refinery.language.model.problem.*; | 13 | import tools.refinery.language.model.problem.*; |
13 | 14 | ||
14 | public final class ProblemUtil { | 15 | public final class ProblemUtil { |
16 | public static final String MODULE_EXTENSION = "refinery"; | ||
17 | |||
15 | private ProblemUtil() { | 18 | private ProblemUtil() { |
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 | } |
@@ -124,6 +127,18 @@ public final class ProblemUtil { | |||
124 | }; | 127 | }; |
125 | } | 128 | } |
126 | 129 | ||
130 | public static ModuleKind getDefaultModuleKind(Problem problem) { | ||
131 | var resource = problem.eResource(); | ||
132 | if (resource == null) { | ||
133 | return ModuleKind.PROBLEM; | ||
134 | } | ||
135 | return getDefaultModuleKind(resource.getURI()); | ||
136 | } | ||
137 | |||
138 | public static ModuleKind getDefaultModuleKind(URI uri) { | ||
139 | return MODULE_EXTENSION.equals(uri.fileExtension()) ? ModuleKind.MODULE : ModuleKind.PROBLEM; | ||
140 | } | ||
141 | |||
127 | public static boolean isModule(Problem problem) { | 142 | public static boolean isModule(Problem problem) { |
128 | return problem.getKind() == ModuleKind.MODULE; | 143 | return problem.getKind() == ModuleKind.MODULE; |
129 | } | 144 | } |
diff --git a/subprojects/language/src/main/resources/tools/refinery/language/library/builtin.refinery b/subprojects/language/src/main/resources/tools/refinery/language/library/builtin.refinery index f9ef959d..4e74ca03 100644 --- a/subprojects/language/src/main/resources/tools/refinery/language/library/builtin.refinery +++ b/subprojects/language/src/main/resources/tools/refinery/language/library/builtin.refinery | |||
@@ -1,7 +1,6 @@ | |||
1 | % SPDX-FileCopyrightText: 2021-2024 The Refinery Authors <https://refinery.tools/> | 1 | % SPDX-FileCopyrightText: 2021-2024 The Refinery Authors <https://refinery.tools/> |
2 | % | 2 | % |
3 | % SPDX-License-Identifier: EPL-2.0 | 3 | % SPDX-License-Identifier: EPL-2.0 |
4 | module builtin. | ||
5 | 4 | ||
6 | abstract class node. | 5 | abstract class node. |
7 | 6 | ||