aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/language/src/main/java
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2022-09-19 21:33:55 +0200
committerLibravatar Kristóf Marussy <kristof@marussy.com>2022-09-19 21:42:05 +0200
commit7fb99f0225911a8962aaf3493b89f41e791df359 (patch)
treef289ac1433f91e2b1c9e1a5eab92ae6b52aa9d83 /subprojects/language/src/main/java
parentrefactor(language): clarify containment hierarchy (diff)
downloadrefinery-7fb99f0225911a8962aaf3493b89f41e791df359.tar.gz
refinery-7fb99f0225911a8962aaf3493b89f41e791df359.tar.zst
refinery-7fb99f0225911a8962aaf3493b89f41e791df359.zip
feat(language): problem desugaring
Diffstat (limited to 'subprojects/language/src/main/java')
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/ProblemUtil.java151
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/resource/ProblemDerivedStateComputer.java41
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/resource/ProblemLocationInFileProvider.java2
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionStrategy.java2
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/resource/ReferenceCounter.java5
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemGlobalScopeProvider.java2
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemLocalScopeProvider.java2
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemScopeProvider.java10
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/utils/BuiltinSymbols.java14
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/utils/CollectedSymbols.java10
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/utils/NodeInfo.java12
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/utils/ProblemDesugarer.java142
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java82
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/utils/RelationInfo.java27
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/utils/SymbolCollector.java284
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java2
16 files changed, 622 insertions, 166 deletions
diff --git a/subprojects/language/src/main/java/tools/refinery/language/ProblemUtil.java b/subprojects/language/src/main/java/tools/refinery/language/ProblemUtil.java
deleted file mode 100644
index 0be296fd..00000000
--- a/subprojects/language/src/main/java/tools/refinery/language/ProblemUtil.java
+++ /dev/null
@@ -1,151 +0,0 @@
1package tools.refinery.language;
2
3import java.util.ArrayDeque;
4import java.util.Collection;
5import java.util.Deque;
6import java.util.HashSet;
7import java.util.Optional;
8import java.util.Set;
9
10import org.eclipse.emf.common.util.URI;
11import org.eclipse.emf.ecore.EObject;
12import org.eclipse.emf.ecore.resource.Resource;
13
14import tools.refinery.language.model.problem.ClassDeclaration;
15import tools.refinery.language.model.problem.ImplicitVariable;
16import tools.refinery.language.model.problem.Node;
17import tools.refinery.language.model.problem.Problem;
18import tools.refinery.language.model.problem.ProblemPackage;
19import tools.refinery.language.model.problem.ReferenceDeclaration;
20import tools.refinery.language.model.problem.Relation;
21import tools.refinery.language.model.problem.Variable;
22
23public final class ProblemUtil {
24 public static final String BUILTIN_LIBRARY_NAME = "builtin";
25
26 public static final URI BUILTIN_LIBRARY_URI = getLibraryUri(BUILTIN_LIBRARY_NAME);
27
28 public static final String NODE_CLASS_NAME = "node";
29
30 public static final String DOMAIN_CLASS_NAME = "domain";
31
32 public static final String DATA_CLASS_NAME = "data";
33
34 private ProblemUtil() {
35 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
36 }
37
38 public static boolean isSingletonVariable(Variable variable) {
39 return variable.eContainingFeature() == ProblemPackage.Literals.VARIABLE_OR_NODE_ARGUMENT__SINGLETON_VARIABLE;
40 }
41
42 public static boolean isImplicitVariable(Variable variable) {
43 return variable instanceof ImplicitVariable;
44 }
45
46 public static boolean isImplicitNode(Node node) {
47 return node.eContainingFeature() == ProblemPackage.Literals.PROBLEM__NODES;
48 }
49
50 public static boolean isImplicit(EObject eObject) {
51 if (eObject instanceof Node node) {
52 return isImplicitNode(node);
53 } else if (eObject instanceof Variable variable) {
54 return isImplicitVariable(variable);
55 } else {
56 return false;
57 }
58 }
59
60 public static boolean isIndividualNode(Node node) {
61 var containingFeature = node.eContainingFeature();
62 return containingFeature == ProblemPackage.Literals.INDIVIDUAL_DECLARATION__NODES
63 || containingFeature == ProblemPackage.Literals.ENUM_DECLARATION__LITERALS;
64 }
65
66 public static boolean isNewNode(Node node) {
67 return node.eContainingFeature() == ProblemPackage.Literals.CLASS_DECLARATION__NEW_NODE;
68 }
69
70 public static Optional<Problem> getBuiltInLibrary(EObject context) {
71 return Optional.ofNullable(context.eResource()).map(Resource::getResourceSet)
72 .map(resourceSet -> resourceSet.getResource(BUILTIN_LIBRARY_URI, true)).map(Resource::getContents)
73 .filter(contents -> !contents.isEmpty()).map(contents -> contents.get(0))
74 .filter(Problem.class::isInstance).map(Problem.class::cast);
75 }
76
77 public static boolean isBuiltIn(EObject eObject) {
78 if (eObject != null) {
79 var eResource = eObject.eResource();
80 if (eResource != null) {
81 return BUILTIN_LIBRARY_URI.equals(eResource.getURI());
82 }
83 }
84 return false;
85 }
86
87 public static Optional<ClassDeclaration> getBuiltinClassDeclaration(EObject context, String name) {
88 return getBuiltInLibrary(context).flatMap(problem -> problem.getStatements().stream()
89 .filter(ClassDeclaration.class::isInstance).map(ClassDeclaration.class::cast)
90 .filter(declaration -> name.equals(declaration.getName())).findFirst());
91 }
92
93 public static Collection<ClassDeclaration> getSuperclassesAndSelf(ClassDeclaration classDeclaration) {
94 Set<ClassDeclaration> found = new HashSet<>();
95 getBuiltinClassDeclaration(classDeclaration, NODE_CLASS_NAME).ifPresent(found::add);
96 Deque<ClassDeclaration> queue = new ArrayDeque<>();
97 queue.addLast(classDeclaration);
98 while (!queue.isEmpty()) {
99 ClassDeclaration current = queue.removeFirst();
100 if (!found.contains(current)) {
101 found.add(current);
102 for (Relation superType : current.getSuperTypes()) {
103 if (superType instanceof ClassDeclaration superDeclaration) {
104 queue.addLast(superDeclaration);
105 }
106 }
107 }
108 }
109 getBuiltinClassDeclaration(classDeclaration, DATA_CLASS_NAME).ifPresent((dataClassDelcaration) -> {
110 if (!found.contains(dataClassDelcaration)) {
111 getBuiltinClassDeclaration(classDeclaration, DOMAIN_CLASS_NAME).ifPresent(found::add);
112 }
113 });
114 return found;
115 }
116
117 public static Collection<ReferenceDeclaration> getAllReferenceDeclarations(ClassDeclaration classDeclaration) {
118 Set<ReferenceDeclaration> referenceDeclarations = new HashSet<>();
119 for (ClassDeclaration superclass : getSuperclassesAndSelf(classDeclaration)) {
120 referenceDeclarations.addAll(superclass.getReferenceDeclarations());
121 }
122 return referenceDeclarations;
123 }
124
125 public static boolean isDataClass(Relation relation) {
126 if (relation instanceof ClassDeclaration classDeclaration) {
127 var supertypes = getSuperclassesAndSelf(classDeclaration);
128 return getBuiltinClassDeclaration(classDeclaration, DATA_CLASS_NAME).map(supertypes::contains)
129 .orElse(false);
130 }
131 return false;
132 }
133
134 public static boolean isContainmentReference(ReferenceDeclaration referenceDeclaration) {
135 switch (referenceDeclaration.getKind()) {
136 case REFERENCE, CONTAINER:
137 return false;
138 case CONTAINMENT:
139 return true;
140 case DEFAULT:
141 return isDataClass(referenceDeclaration.getReferenceType());
142 default:
143 throw new IllegalArgumentException("Unknown reference kind " + referenceDeclaration.getKind());
144 }
145 }
146
147 private static URI getLibraryUri(String libraryName) {
148 return URI.createURI(ProblemUtil.class.getClassLoader()
149 .getResource("tools/refinery/language/%s.problem".formatted(libraryName)).toString());
150 }
151}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemDerivedStateComputer.java b/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemDerivedStateComputer.java
index 275feca3..f28e1791 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemDerivedStateComputer.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemDerivedStateComputer.java
@@ -24,7 +24,9 @@ import com.google.inject.Provider;
24import com.google.inject.Singleton; 24import com.google.inject.Singleton;
25import com.google.inject.name.Named; 25import com.google.inject.name.Named;
26 26
27import tools.refinery.language.model.problem.Assertion;
27import tools.refinery.language.model.problem.ClassDeclaration; 28import tools.refinery.language.model.problem.ClassDeclaration;
29import tools.refinery.language.model.problem.ConstantAssertionArgument;
28import tools.refinery.language.model.problem.Node; 30import tools.refinery.language.model.problem.Node;
29import tools.refinery.language.model.problem.Problem; 31import tools.refinery.language.model.problem.Problem;
30import tools.refinery.language.model.problem.ProblemFactory; 32import tools.refinery.language.model.problem.ProblemFactory;
@@ -34,6 +36,8 @@ import tools.refinery.language.model.problem.Statement;
34public class ProblemDerivedStateComputer implements IDerivedStateComputer { 36public class ProblemDerivedStateComputer implements IDerivedStateComputer {
35 public static final String NEW_NODE = "new"; 37 public static final String NEW_NODE = "new";
36 38
39 public static final String CONSTANT_NODE = "constant";
40
37 @Inject 41 @Inject
38 @Named(Constants.LANGUAGE_NAME) 42 @Named(Constants.LANGUAGE_NAME)
39 private String languageName; 43 private String languageName;
@@ -82,8 +86,17 @@ public class ProblemDerivedStateComputer implements IDerivedStateComputer {
82 for (Statement statement : problem.getStatements()) { 86 for (Statement statement : problem.getStatements()) {
83 if (statement instanceof ClassDeclaration declaration && !declaration.isAbstract() 87 if (statement instanceof ClassDeclaration declaration && !declaration.isAbstract()
84 && declaration.getNewNode() == null) { 88 && declaration.getNewNode() == null) {
85 var newNode = adapter.createNodeIfAbsent(declaration, key -> createNode(NEW_NODE)); 89 var newNode = adapter.createNewNodeIfAbsent(declaration, key -> createNode(NEW_NODE));
86 declaration.setNewNode(newNode); 90 declaration.setNewNode(newNode);
91 } else if (statement instanceof Assertion assertion) {
92 for (var argument : assertion.getArguments()) {
93 if (argument instanceof ConstantAssertionArgument constantAssertionArgument
94 && constantAssertionArgument.getNode() == null) {
95 var constantNode = adapter.createConstantNodeIfAbsent(constantAssertionArgument,
96 key -> createNode(CONSTANT_NODE));
97 constantAssertionArgument.setNode(constantNode);
98 }
99 }
87 } 100 }
88 } 101 }
89 } 102 }
@@ -117,14 +130,22 @@ public class ProblemDerivedStateComputer implements IDerivedStateComputer {
117 130
118 protected void discardDerivedProblemState(Problem problem, Adapter adapter) { 131 protected void discardDerivedProblemState(Problem problem, Adapter adapter) {
119 Set<ClassDeclaration> classDeclarations = new HashSet<>(); 132 Set<ClassDeclaration> classDeclarations = new HashSet<>();
133 Set<ConstantAssertionArgument> constantAssertionArguments = new HashSet<>();
120 problem.getNodes().clear(); 134 problem.getNodes().clear();
121 for (Statement statement : problem.getStatements()) { 135 for (var statement : problem.getStatements()) {
122 if (statement instanceof ClassDeclaration classDeclaration) { 136 if (statement instanceof ClassDeclaration classDeclaration) {
123 classDeclaration.setNewNode(null); 137 classDeclaration.setNewNode(null);
124 classDeclarations.add(classDeclaration); 138 classDeclarations.add(classDeclaration);
139 } else if (statement instanceof Assertion assertion) {
140 for (var argument : assertion.getArguments()) {
141 if (argument instanceof ConstantAssertionArgument constantAssertionArgument) {
142 constantAssertionArgument.setNode(null);
143 constantAssertionArguments.add(constantAssertionArgument);
144 }
145 }
125 } 146 }
126 } 147 }
127 adapter.retainAll(classDeclarations); 148 adapter.retainAll(classDeclarations, constantAssertionArguments);
128 derivedVariableComputer.discardDerivedVariables(problem); 149 derivedVariableComputer.discardDerivedVariables(problem);
129 } 150 }
130 151
@@ -147,12 +168,22 @@ public class ProblemDerivedStateComputer implements IDerivedStateComputer {
147 protected static class Adapter extends AdapterImpl { 168 protected static class Adapter extends AdapterImpl {
148 private Map<ClassDeclaration, Node> newNodes = new HashMap<>(); 169 private Map<ClassDeclaration, Node> newNodes = new HashMap<>();
149 170
150 public Node createNodeIfAbsent(ClassDeclaration classDeclaration, Function<ClassDeclaration, Node> createNode) { 171 private Map<ConstantAssertionArgument, Node> constantNodes = new HashMap<>();
172
173 public Node createNewNodeIfAbsent(ClassDeclaration classDeclaration,
174 Function<ClassDeclaration, Node> createNode) {
151 return newNodes.computeIfAbsent(classDeclaration, createNode); 175 return newNodes.computeIfAbsent(classDeclaration, createNode);
152 } 176 }
153 177
154 public void retainAll(Collection<ClassDeclaration> classDeclarations) { 178 public Node createConstantNodeIfAbsent(ConstantAssertionArgument constantAssertionArgument,
179 Function<ConstantAssertionArgument, Node> createNode) {
180 return constantNodes.computeIfAbsent(constantAssertionArgument, createNode);
181 }
182
183 public void retainAll(Collection<ClassDeclaration> classDeclarations,
184 Collection<ConstantAssertionArgument> constantAssertionArguments) {
155 newNodes.keySet().retainAll(classDeclarations); 185 newNodes.keySet().retainAll(classDeclarations);
186 constantNodes.keySet().retainAll(constantAssertionArguments);
156 } 187 }
157 188
158 @Override 189 @Override
diff --git a/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemLocationInFileProvider.java b/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemLocationInFileProvider.java
index f6ae4f1e..df822987 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemLocationInFileProvider.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemLocationInFileProvider.java
@@ -4,9 +4,9 @@ import org.eclipse.emf.ecore.EObject;
4import org.eclipse.xtext.resource.DefaultLocationInFileProvider; 4import org.eclipse.xtext.resource.DefaultLocationInFileProvider;
5import org.eclipse.xtext.util.ITextRegion; 5import org.eclipse.xtext.util.ITextRegion;
6 6
7import tools.refinery.language.ProblemUtil;
8import tools.refinery.language.model.problem.ImplicitVariable; 7import tools.refinery.language.model.problem.ImplicitVariable;
9import tools.refinery.language.model.problem.Node; 8import tools.refinery.language.model.problem.Node;
9import tools.refinery.language.utils.ProblemUtil;
10 10
11public class ProblemLocationInFileProvider extends DefaultLocationInFileProvider { 11public class ProblemLocationInFileProvider extends DefaultLocationInFileProvider {
12 @Override 12 @Override
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 24d00f07..1a0b73a8 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
@@ -12,12 +12,12 @@ import org.eclipse.xtext.util.IAcceptor;
12import com.google.inject.Inject; 12import com.google.inject.Inject;
13import com.google.inject.Singleton; 13import com.google.inject.Singleton;
14 14
15import tools.refinery.language.ProblemUtil;
16import tools.refinery.language.model.problem.NamedElement; 15import tools.refinery.language.model.problem.NamedElement;
17import tools.refinery.language.model.problem.Node; 16import tools.refinery.language.model.problem.Node;
18import tools.refinery.language.model.problem.Problem; 17import tools.refinery.language.model.problem.Problem;
19import tools.refinery.language.model.problem.Variable; 18import tools.refinery.language.model.problem.Variable;
20import tools.refinery.language.naming.NamingUtil; 19import tools.refinery.language.naming.NamingUtil;
20import tools.refinery.language.utils.ProblemUtil;
21 21
22@Singleton 22@Singleton
23public class ProblemResourceDescriptionStrategy extends DefaultResourceDescriptionStrategy { 23public class ProblemResourceDescriptionStrategy extends DefaultResourceDescriptionStrategy {
diff --git a/subprojects/language/src/main/java/tools/refinery/language/resource/ReferenceCounter.java b/subprojects/language/src/main/java/tools/refinery/language/resource/ReferenceCounter.java
index 7525dfc6..ca20325e 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/resource/ReferenceCounter.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/resource/ReferenceCounter.java
@@ -5,6 +5,7 @@ import java.util.Map;
5 5
6import org.eclipse.emf.ecore.EObject; 6import org.eclipse.emf.ecore.EObject;
7import org.eclipse.xtext.util.IResourceScopeCache; 7import org.eclipse.xtext.util.IResourceScopeCache;
8import org.eclipse.xtext.util.Tuples;
8 9
9import com.google.inject.Inject; 10import com.google.inject.Inject;
10import com.google.inject.Singleton; 11import com.google.inject.Singleton;
@@ -14,7 +15,7 @@ import tools.refinery.language.model.problem.Problem;
14@Singleton 15@Singleton
15public class ReferenceCounter { 16public class ReferenceCounter {
16 @Inject 17 @Inject
17 private IResourceScopeCache cache; 18 private IResourceScopeCache cache = IResourceScopeCache.NullImpl.INSTANCE;
18 19
19 public int countReferences(Problem problem, EObject eObject) { 20 public int countReferences(Problem problem, EObject eObject) {
20 var count = getReferenceCounts(problem).get(eObject); 21 var count = getReferenceCounts(problem).get(eObject);
@@ -29,7 +30,7 @@ public class ReferenceCounter {
29 if (resource == null) { 30 if (resource == null) {
30 return doGetReferenceCounts(problem); 31 return doGetReferenceCounts(problem);
31 } 32 }
32 return cache.get(problem, resource, () -> doGetReferenceCounts(problem)); 33 return cache.get(Tuples.create(problem, "referenceCounts"), resource, () -> doGetReferenceCounts(problem));
33 } 34 }
34 35
35 protected Map<EObject, Integer> doGetReferenceCounts(Problem problem) { 36 protected Map<EObject, Integer> doGetReferenceCounts(Problem problem) {
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 ea4682f9..b749154c 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
@@ -6,7 +6,7 @@ import org.eclipse.emf.common.util.URI;
6import org.eclipse.emf.ecore.resource.Resource; 6import org.eclipse.emf.ecore.resource.Resource;
7import org.eclipse.xtext.scoping.impl.ImportUriGlobalScopeProvider; 7import org.eclipse.xtext.scoping.impl.ImportUriGlobalScopeProvider;
8 8
9import tools.refinery.language.ProblemUtil; 9import tools.refinery.language.utils.ProblemUtil;
10 10
11public class ProblemGlobalScopeProvider extends ImportUriGlobalScopeProvider { 11public class ProblemGlobalScopeProvider extends ImportUriGlobalScopeProvider {
12 @Override 12 @Override
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 53815e37..61883f0e 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
@@ -13,7 +13,7 @@ import org.eclipse.xtext.scoping.impl.ImportedNamespaceAwareLocalScopeProvider;
13 13
14import com.google.inject.Inject; 14import com.google.inject.Inject;
15 15
16import tools.refinery.language.ProblemUtil; 16import tools.refinery.language.utils.ProblemUtil;
17 17
18public class ProblemLocalScopeProvider extends ImportedNamespaceAwareLocalScopeProvider { 18public class ProblemLocalScopeProvider extends ImportedNamespaceAwareLocalScopeProvider {
19 private static final QualifiedName BUILTIN_LIBRARY_QUALIFIED_NAME = QualifiedName 19 private static final QualifiedName BUILTIN_LIBRARY_QUALIFIED_NAME = QualifiedName
diff --git a/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemScopeProvider.java b/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemScopeProvider.java
index 925ac3a5..567c3c26 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemScopeProvider.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemScopeProvider.java
@@ -12,7 +12,8 @@ import org.eclipse.xtext.EcoreUtil2;
12import org.eclipse.xtext.scoping.IScope; 12import org.eclipse.xtext.scoping.IScope;
13import org.eclipse.xtext.scoping.Scopes; 13import org.eclipse.xtext.scoping.Scopes;
14 14
15import tools.refinery.language.ProblemUtil; 15import com.google.inject.Inject;
16
16import tools.refinery.language.model.problem.ClassDeclaration; 17import tools.refinery.language.model.problem.ClassDeclaration;
17import tools.refinery.language.model.problem.Consequent; 18import tools.refinery.language.model.problem.Consequent;
18import tools.refinery.language.model.problem.ExistentialQuantifier; 19import tools.refinery.language.model.problem.ExistentialQuantifier;
@@ -23,15 +24,18 @@ import tools.refinery.language.model.problem.ProblemPackage;
23import tools.refinery.language.model.problem.ReferenceDeclaration; 24import tools.refinery.language.model.problem.ReferenceDeclaration;
24import tools.refinery.language.model.problem.Variable; 25import tools.refinery.language.model.problem.Variable;
25import tools.refinery.language.model.problem.VariableOrNodeArgument; 26import tools.refinery.language.model.problem.VariableOrNodeArgument;
27import tools.refinery.language.utils.ProblemDesugarer;
26 28
27/** 29/**
28 * This class contains custom scoping description. 30 * This class contains custom scoping description.
29 * 31 *
30 * See 32 * See
31 * https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#scoping 33 * https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#scoping
32 * on how and when to use it. 34 * on how and when to use it.
33 */ 35 */
34public class ProblemScopeProvider extends AbstractProblemScopeProvider { 36public class ProblemScopeProvider extends AbstractProblemScopeProvider {
37 @Inject
38 private ProblemDesugarer desugarer;
35 39
36 @Override 40 @Override
37 public IScope getScope(EObject context, EReference reference) { 41 public IScope getScope(EObject context, EReference reference) {
@@ -106,7 +110,7 @@ public class ProblemScopeProvider extends AbstractProblemScopeProvider {
106 return delegateScope; 110 return delegateScope;
107 } 111 }
108 var classDeclaration = (ClassDeclaration) relation; 112 var classDeclaration = (ClassDeclaration) relation;
109 var referenceDeclarations = ProblemUtil.getAllReferenceDeclarations(classDeclaration); 113 var referenceDeclarations = desugarer.getAllReferenceDeclarations(classDeclaration);
110 return Scopes.scopeFor(referenceDeclarations, delegateScope); 114 return Scopes.scopeFor(referenceDeclarations, delegateScope);
111 } 115 }
112} 116}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/utils/BuiltinSymbols.java b/subprojects/language/src/main/java/tools/refinery/language/utils/BuiltinSymbols.java
new file mode 100644
index 00000000..7e43cecf
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/utils/BuiltinSymbols.java
@@ -0,0 +1,14 @@
1package tools.refinery.language.utils;
2
3import tools.refinery.language.model.problem.ClassDeclaration;
4import tools.refinery.language.model.problem.EnumDeclaration;
5import tools.refinery.language.model.problem.Node;
6import tools.refinery.language.model.problem.PredicateDefinition;
7import tools.refinery.language.model.problem.Problem;
8import tools.refinery.language.model.problem.ReferenceDeclaration;
9
10public record BuiltinSymbols(Problem problem, ClassDeclaration node, ReferenceDeclaration equals,
11 PredicateDefinition exists, ClassDeclaration domain, ClassDeclaration data, EnumDeclaration bool, Node boolTrue,
12 Node boolFalse, ClassDeclaration intClass, ClassDeclaration real, ClassDeclaration string,
13 PredicateDefinition contained, PredicateDefinition contains, PredicateDefinition root) {
14}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/utils/CollectedSymbols.java b/subprojects/language/src/main/java/tools/refinery/language/utils/CollectedSymbols.java
new file mode 100644
index 00000000..b5682f32
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/utils/CollectedSymbols.java
@@ -0,0 +1,10 @@
1package tools.refinery.language.utils;
2
3import java.util.Map;
4
5import tools.refinery.language.model.problem.Node;
6import tools.refinery.language.model.problem.Relation;
7
8public record CollectedSymbols(Map<Node, NodeInfo> nodes, Map<Relation, RelationInfo> relations) {
9
10}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/utils/NodeInfo.java b/subprojects/language/src/main/java/tools/refinery/language/utils/NodeInfo.java
new file mode 100644
index 00000000..38db0e29
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/utils/NodeInfo.java
@@ -0,0 +1,12 @@
1package tools.refinery.language.utils;
2
3import java.util.ArrayList;
4import java.util.Collection;
5
6import tools.refinery.language.model.problem.NodeValueAssertion;
7
8public record NodeInfo(String name, boolean individual, Collection<NodeValueAssertion> valueAssertions) {
9 public NodeInfo(String name, boolean individual) {
10 this(name, individual, new ArrayList<>());
11 }
12}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemDesugarer.java b/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemDesugarer.java
new file mode 100644
index 00000000..23fd8982
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemDesugarer.java
@@ -0,0 +1,142 @@
1package tools.refinery.language.utils;
2
3import com.google.inject.Inject;
4import com.google.inject.Provider;
5import com.google.inject.Singleton;
6import org.eclipse.emf.ecore.EObject;
7import org.eclipse.emf.ecore.resource.Resource;
8import org.eclipse.xtext.util.IResourceScopeCache;
9import org.eclipse.xtext.util.Tuples;
10import tools.refinery.language.model.problem.*;
11
12import java.util.*;
13
14@Singleton
15public class ProblemDesugarer {
16 @Inject
17 private IResourceScopeCache cache = IResourceScopeCache.NullImpl.INSTANCE;
18
19 @Inject
20 private Provider<SymbolCollector> symbolCollectorProvider;
21
22 public Optional<Problem> getBuiltinProblem(EObject context) {
23 return Optional.ofNullable(context).map(EObject::eResource).flatMap(resource ->
24 cache.get("builtinProblem", resource, () -> doGetBuiltinProblem(resource)));
25 }
26
27 private Optional<Problem> doGetBuiltinProblem(Resource resource) {
28 return Optional.ofNullable(resource).map(Resource::getResourceSet)
29 .map(resourceSet -> resourceSet.getResource(ProblemUtil.BUILTIN_LIBRARY_URI, true))
30 .map(Resource::getContents).filter(contents -> !contents.isEmpty()).map(contents -> contents.get(0))
31 .filter(Problem.class::isInstance).map(Problem.class::cast);
32 }
33
34 public Optional<BuiltinSymbols> getBuiltinSymbols(EObject context) {
35 return getBuiltinProblem(context).map(builtin ->
36 cache.get("builtinSymbols", builtin.eResource(), () -> doGetBuiltinSymbols(builtin)));
37 }
38
39 private BuiltinSymbols doGetBuiltinSymbols(Problem builtin) {
40 var node = doGetDeclaration(builtin, ClassDeclaration.class, "node");
41 var equals = doGetEqualsReference(node);
42 var exists = doGetDeclaration(builtin, PredicateDefinition.class, "exists");
43 var domain = doGetDeclaration(builtin, ClassDeclaration.class, "domain");
44 var data = doGetDeclaration(builtin, ClassDeclaration.class, "data");
45 var bool = doGetDeclaration(builtin, EnumDeclaration.class, "bool");
46 var boolTrue = doGetLiteral(bool, "true");
47 var boolFalse = doGetLiteral(bool, "false");
48 var intClass = doGetDeclaration(builtin, ClassDeclaration.class, "int");
49 var real = doGetDeclaration(builtin, ClassDeclaration.class, "real");
50 var string = doGetDeclaration(builtin, ClassDeclaration.class, "string");
51 var contained = doGetDeclaration(builtin, PredicateDefinition.class, "contained");
52 var contains = doGetDeclaration(builtin, PredicateDefinition.class, "contains");
53 var root = doGetDeclaration(builtin, PredicateDefinition.class, "root");
54 return new BuiltinSymbols(builtin, node, equals, exists, domain, data, bool, boolTrue, boolFalse, intClass,
55 real, string, contained, contains, root);
56 }
57
58 private <T extends Statement & NamedElement> T doGetDeclaration(Problem builtin, Class<T> type, String name) {
59 return builtin.getStatements().stream().filter(type::isInstance).map(type::cast)
60 .filter(declaration -> name.equals(declaration.getName())).findFirst()
61 .orElseThrow(() -> new IllegalArgumentException("Built-in declaration " + name + " was not found"));
62 }
63
64 private ReferenceDeclaration doGetEqualsReference(ClassDeclaration nodeClassDeclaration) {
65 return nodeClassDeclaration.getReferenceDeclarations().stream()
66 .filter(reference -> "equals".equals(reference.getName())).findFirst()
67 .orElseThrow(() -> new IllegalArgumentException("Reference " + "equals" + " not found"));
68 }
69
70 private Node doGetLiteral(EnumDeclaration enumDeclaration, String name) {
71 return enumDeclaration.getLiterals().stream().filter(literal -> name.equals(literal.getName())).findFirst()
72 .orElseThrow(() -> new IllegalArgumentException("Enum literal " + name + " not found"));
73 }
74
75 public Collection<ClassDeclaration> getSuperclassesAndSelf(ClassDeclaration classDeclaration) {
76 return cache.get(Tuples.create(classDeclaration, "superclassesAndSelf"), classDeclaration.eResource(),
77 () -> doGetSuperclassesAndSelf(classDeclaration));
78 }
79
80 private Collection<ClassDeclaration> doGetSuperclassesAndSelf(ClassDeclaration classDeclaration) {
81 var builtinSymbols = getBuiltinSymbols(classDeclaration);
82 Set<ClassDeclaration> found = new HashSet<>();
83 builtinSymbols.ifPresent(symbols -> found.add(symbols.node()));
84 Deque<ClassDeclaration> queue = new ArrayDeque<>();
85 queue.addLast(classDeclaration);
86 while (!queue.isEmpty()) {
87 ClassDeclaration current = queue.removeFirst();
88 if (!found.contains(current)) {
89 found.add(current);
90 for (Relation superType : current.getSuperTypes()) {
91 if (superType instanceof ClassDeclaration superDeclaration) {
92 queue.addLast(superDeclaration);
93 }
94 }
95 }
96 }
97 if (builtinSymbols.isPresent() && !found.contains(builtinSymbols.get().data())) {
98 found.add(builtinSymbols.get().domain());
99 }
100 return found;
101 }
102
103 public Collection<ReferenceDeclaration> getAllReferenceDeclarations(ClassDeclaration classDeclaration) {
104 return cache.get(Tuples.create(classDeclaration, "allReferenceDeclarations"), classDeclaration.eResource(),
105 () -> doGetAllReferenceDeclarations(classDeclaration));
106 }
107
108 private Collection<ReferenceDeclaration> doGetAllReferenceDeclarations(ClassDeclaration classDeclaration) {
109 Set<ReferenceDeclaration> referenceDeclarations = new HashSet<>();
110 for (ClassDeclaration superclass : getSuperclassesAndSelf(classDeclaration)) {
111 referenceDeclarations.addAll(superclass.getReferenceDeclarations());
112 }
113 return referenceDeclarations;
114 }
115
116 public boolean isContainmentReference(ReferenceDeclaration referenceDeclaration) {
117 switch (referenceDeclaration.getKind()) {
118 case REFERENCE, CONTAINER:
119 return false;
120 case CONTAINMENT:
121 return true;
122 case DEFAULT:
123 return isDataClass(referenceDeclaration.getReferenceType());
124 default:
125 throw new IllegalArgumentException("Unknown reference kind " + referenceDeclaration.getKind());
126 }
127 }
128
129 public boolean isDataClass(Relation relation) {
130 if (relation instanceof ClassDeclaration classDeclaration) {
131 var supertypes = getSuperclassesAndSelf(classDeclaration);
132 var builtinSymbols = getBuiltinSymbols(relation);
133 return builtinSymbols.isPresent() && supertypes.contains(builtinSymbols.get().data());
134 }
135 return false;
136 }
137
138 public CollectedSymbols collectSymbols(Problem problem) {
139 return cache.get(Tuples.create(problem, "collectedSymbols"), problem.eResource(),
140 () -> symbolCollectorProvider.get().collectSymbols(problem));
141 }
142}
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
new file mode 100644
index 00000000..a7acd747
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java
@@ -0,0 +1,82 @@
1package tools.refinery.language.utils;
2
3import org.eclipse.emf.common.util.URI;
4import org.eclipse.emf.ecore.EObject;
5
6import tools.refinery.language.model.problem.ImplicitVariable;
7import tools.refinery.language.model.problem.Node;
8import tools.refinery.language.model.problem.ProblemPackage;
9import tools.refinery.language.model.problem.Variable;
10
11public final class ProblemUtil {
12 public static final String BUILTIN_LIBRARY_NAME = "builtin";
13
14 public static final URI BUILTIN_LIBRARY_URI = getLibraryUri(BUILTIN_LIBRARY_NAME);
15
16 public static final String NODE_CLASS_NAME = "node";
17
18 public static final String DOMAIN_CLASS_NAME = "domain";
19
20 public static final String DATA_CLASS_NAME = "data";
21
22 public static final String INT_CLASS_NAME = "int";
23
24 public static final String REAL_CLASS_NAME = "real";
25
26 public static final String STRING_CLASS_NAME = "string";
27
28 public static final String EQUALS_RELATION_NAME = "equals";
29
30 public static final String EXISTS_PREDICATE_NAME = "exists";
31
32 private ProblemUtil() {
33 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
34 }
35
36 public static boolean isBuiltIn(EObject eObject) {
37 if (eObject != null) {
38 var eResource = eObject.eResource();
39 if (eResource != null) {
40 return ProblemUtil.BUILTIN_LIBRARY_URI.equals(eResource.getURI());
41 }
42 }
43 return false;
44 }
45
46 public static boolean isSingletonVariable(Variable variable) {
47 return variable.eContainingFeature() == ProblemPackage.Literals.VARIABLE_OR_NODE_ARGUMENT__SINGLETON_VARIABLE;
48 }
49
50 public static boolean isImplicitVariable(Variable variable) {
51 return variable instanceof ImplicitVariable;
52 }
53
54 public static boolean isImplicitNode(Node node) {
55 return node.eContainingFeature() == ProblemPackage.Literals.PROBLEM__NODES;
56 }
57
58 public static boolean isImplicit(EObject eObject) {
59 if (eObject instanceof Node node) {
60 return isImplicitNode(node);
61 } else if (eObject instanceof Variable variable) {
62 return isImplicitVariable(variable);
63 } else {
64 return false;
65 }
66 }
67
68 public static boolean isIndividualNode(Node node) {
69 var containingFeature = node.eContainingFeature();
70 return containingFeature == ProblemPackage.Literals.INDIVIDUAL_DECLARATION__NODES
71 || containingFeature == ProblemPackage.Literals.ENUM_DECLARATION__LITERALS;
72 }
73
74 public static boolean isNewNode(Node node) {
75 return node.eContainingFeature() == ProblemPackage.Literals.CLASS_DECLARATION__NEW_NODE;
76 }
77
78 private static URI getLibraryUri(String libraryName) {
79 return URI.createURI(ProblemUtil.class.getClassLoader()
80 .getResource("tools/refinery/language/%s.problem".formatted(libraryName)).toString());
81 }
82}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/utils/RelationInfo.java b/subprojects/language/src/main/java/tools/refinery/language/utils/RelationInfo.java
new file mode 100644
index 00000000..ae56e3a5
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/utils/RelationInfo.java
@@ -0,0 +1,27 @@
1package tools.refinery.language.utils;
2
3import java.util.ArrayList;
4import java.util.Collection;
5import java.util.List;
6
7import tools.refinery.language.model.problem.Assertion;
8import tools.refinery.language.model.problem.Conjunction;
9import tools.refinery.language.model.problem.Multiplicity;
10import tools.refinery.language.model.problem.Parameter;
11import tools.refinery.language.model.problem.PredicateKind;
12import tools.refinery.language.model.problem.Relation;
13import tools.refinery.language.model.problem.TypeScope;
14
15public record RelationInfo(String name, PredicateKind kind, List<Parameter> parameters, Multiplicity multiplicity,
16 Relation opposite, Collection<Conjunction> bodies, Collection<Assertion> defaultAssertions,
17 Collection<Assertion> assertions, Collection<TypeScope> typeScopes) {
18 public RelationInfo(String name, PredicateKind kind, List<Parameter> parameters, Multiplicity multiplicity,
19 Relation opposite, Collection<Conjunction> bodies) {
20 this(name, kind, parameters, multiplicity, opposite, bodies, new ArrayList<>(), new ArrayList<>(),
21 new ArrayList<>());
22 }
23
24 public boolean hasDefinition() {
25 return bodies != null && !bodies.isEmpty();
26 }
27}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/utils/SymbolCollector.java b/subprojects/language/src/main/java/tools/refinery/language/utils/SymbolCollector.java
new file mode 100644
index 00000000..87cce1f3
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/utils/SymbolCollector.java
@@ -0,0 +1,284 @@
1package tools.refinery.language.utils;
2
3import java.util.LinkedHashMap;
4import java.util.List;
5import java.util.Map;
6
7import org.eclipse.emf.ecore.EObject;
8import org.eclipse.emf.ecore.util.EcoreUtil;
9import org.eclipse.xtext.naming.IQualifiedNameConverter;
10import org.eclipse.xtext.naming.IQualifiedNameProvider;
11
12import com.google.inject.Inject;
13
14import tools.refinery.language.model.problem.Assertion;
15import tools.refinery.language.model.problem.ClassDeclaration;
16import tools.refinery.language.model.problem.ConstantAssertionArgument;
17import tools.refinery.language.model.problem.EnumDeclaration;
18import tools.refinery.language.model.problem.IndividualDeclaration;
19import tools.refinery.language.model.problem.IntConstant;
20import tools.refinery.language.model.problem.LogicValue;
21import tools.refinery.language.model.problem.Node;
22import tools.refinery.language.model.problem.NodeValueAssertion;
23import tools.refinery.language.model.problem.PredicateDefinition;
24import tools.refinery.language.model.problem.PredicateKind;
25import tools.refinery.language.model.problem.Problem;
26import tools.refinery.language.model.problem.ProblemFactory;
27import tools.refinery.language.model.problem.RealConstant;
28import tools.refinery.language.model.problem.Relation;
29import tools.refinery.language.model.problem.StringConstant;
30
31class SymbolCollector {
32 @Inject
33 private IQualifiedNameProvider qualifiedNameProvider;
34
35 @Inject
36 private IQualifiedNameConverter qualifiedNameConverter;
37
38 @Inject
39 ProblemDesugarer desugarer;
40
41 private BuiltinSymbols builtinSymbols;
42 private final Map<Node, NodeInfo> nodes = new LinkedHashMap<>();
43 private final Map<Relation, RelationInfo> relations = new LinkedHashMap<>();
44
45 public CollectedSymbols collectSymbols(Problem problem) {
46 builtinSymbols = desugarer.getBuiltinSymbols(problem).orElseThrow(() -> new IllegalArgumentException(
47 "Problem has no associated built-in library"));
48 collectOwnSymbols(builtinSymbols.problem());
49 collectOwnSymbols(problem);
50 return new CollectedSymbols(nodes, relations);
51 }
52
53 public void collectOwnSymbols(Problem problem) {
54 collectOwnRelations(problem);
55 collectOwnNodes(problem);
56 collectOwnAssertions(problem);
57 }
58
59 private void collectOwnRelations(Problem problem) {
60 for (var statement : problem.getStatements()) {
61 if (statement instanceof PredicateDefinition predicateDefinition) {
62 collectPredicate(predicateDefinition);
63 } else if (statement instanceof ClassDeclaration classDeclaration) {
64 collectClass(classDeclaration);
65 } else if (statement instanceof EnumDeclaration enumDeclaration) {
66 collectEnum(enumDeclaration);
67 }
68 }
69 }
70
71 private void collectPredicate(PredicateDefinition predicateDefinition) {
72 var info = new RelationInfo(getQualifiedNameString(predicateDefinition),
73 predicateDefinition.getKind(),
74 predicateDefinition.getParameters(), null, null, predicateDefinition.getBodies());
75 relations.put(predicateDefinition, info);
76 }
77
78 private void collectClass(ClassDeclaration classDeclaration) {
79 // node and domain classes are not contained by default, but every other type is
80 // contained, including data types.
81 var contained =
82 classDeclaration != builtinSymbols.node() && classDeclaration != builtinSymbols.domain();
83 var classKind = contained ? PredicateKind.CONTAINED : PredicateKind.DEFAULT;
84 var instanceParameter = ProblemFactory.eINSTANCE.createParameter();
85 instanceParameter.setName("instance");
86 var classInfo = new RelationInfo(getQualifiedNameString(classDeclaration), classKind,
87 List.of(instanceParameter), null, null, List.of());
88 relations.put(classDeclaration, classInfo);
89 collectReferences(classDeclaration);
90 }
91
92 private void collectReferences(ClassDeclaration classDeclaration) {
93 for (var referenceDeclaration : classDeclaration.getReferenceDeclarations()) {
94 var referenceKind = desugarer.isContainmentReference(referenceDeclaration) ?
95 PredicateKind.CONTAINMENT :
96 PredicateKind.DEFAULT;
97 var sourceParameter = ProblemFactory.eINSTANCE.createParameter();
98 sourceParameter.setName("source");
99 sourceParameter.setParameterType(classDeclaration);
100 var targetParameter = ProblemFactory.eINSTANCE.createParameter();
101 targetParameter.setName("target");
102 var multiplicity = referenceDeclaration.getMultiplicity();
103 if (multiplicity == null) {
104 var exactMultiplicity = ProblemFactory.eINSTANCE.createExactMultiplicity();
105 exactMultiplicity.setExactValue(1);
106 multiplicity = exactMultiplicity;
107 }
108 targetParameter.setParameterType(referenceDeclaration.getReferenceType());
109 var referenceInfo = new RelationInfo(getQualifiedNameString(referenceDeclaration), referenceKind,
110 List.of(sourceParameter, targetParameter), multiplicity, referenceDeclaration.getOpposite(),
111 List.of());
112 this.relations.put(referenceDeclaration, referenceInfo);
113 }
114 }
115
116 private void collectEnum(EnumDeclaration enumDeclaration) {
117 var instanceParameter = ProblemFactory.eINSTANCE.createParameter();
118 instanceParameter.setName("instance");
119 var info = new RelationInfo(getQualifiedNameString(enumDeclaration), PredicateKind.DEFAULT,
120 List.of(instanceParameter), null, null, List.of());
121 this.relations.put(enumDeclaration, info);
122 }
123
124 private void collectOwnNodes(Problem problem) {
125 for (var statement : problem.getStatements()) {
126 if (statement instanceof IndividualDeclaration individualDeclaration) {
127 collectIndividuals(individualDeclaration);
128 } else if (statement instanceof ClassDeclaration classDeclaration) {
129 collectNewNode(classDeclaration);
130 } else if (statement instanceof EnumDeclaration enumDeclaration) {
131 collectEnumLiterals(enumDeclaration);
132 } else if (statement instanceof Assertion assertion) {
133 collectConstantNodes(assertion);
134 }
135 }
136 for (var node : problem.getNodes()) {
137 addNode(node, false);
138 }
139 }
140
141 private void collectIndividuals(IndividualDeclaration individualDeclaration) {
142 for (var individual : individualDeclaration.getNodes()) {
143 addNode(individual, true);
144 }
145 }
146
147 private void collectNewNode(ClassDeclaration classDeclaration) {
148 var newNode = classDeclaration.getNewNode();
149 if (newNode != null) {
150 addNode(newNode, false);
151 }
152 }
153
154 private void collectEnumLiterals(EnumDeclaration enumDeclaration) {
155 for (var literal : enumDeclaration.getLiterals()) {
156 addNode(literal, true);
157 }
158 }
159
160 private void collectConstantNodes(Assertion assertion) {
161 for (var argument : assertion.getArguments()) {
162 if (argument instanceof ConstantAssertionArgument constantAssertionArgument) {
163 var constantNode = constantAssertionArgument.getNode();
164 if (constantNode != null) {
165 addNode(constantNode, false);
166 }
167 }
168 }
169 }
170
171 private void addNode(Node node, boolean individual) {
172 var info = new NodeInfo(getQualifiedNameString(node), individual);
173 this.nodes.put(node, info);
174 }
175
176 private String getQualifiedNameString(EObject eObject) {
177 var qualifiedName = qualifiedNameProvider.getFullyQualifiedName(eObject);
178 if (qualifiedName == null) {
179 return null;
180 }
181 return qualifiedNameConverter.toString(qualifiedName);
182 }
183
184 private void collectOwnAssertions(Problem problem) {
185 for (var statement : problem.getStatements()) {
186 if (statement instanceof Assertion assertion) {
187 collectAssertion(assertion);
188 } else if (statement instanceof NodeValueAssertion nodeValueAssertion) {
189 collectNodeValueAssertion(nodeValueAssertion);
190 } else if (statement instanceof ClassDeclaration classDeclaration) {
191 collectClassAssertion(classDeclaration);
192 } else if (statement instanceof EnumDeclaration enumDeclaration) {
193 collectEnumAssertions(enumDeclaration);
194 }
195 }
196 }
197
198 private void collectAssertion(Assertion assertion) {
199 var relationInfo = this.relations.get(assertion.getRelation());
200 if (relationInfo == null) {
201 throw new IllegalStateException("Assertion refers to unknown relation");
202 }
203 if (assertion.getArguments().size() != relationInfo.parameters().size()) {
204 // Silently ignoring assertions of invalid arity helps when SymbolCollector is called on an invalid
205 // Problem during editing. The errors can still be detected by the Problem validator.
206 return;
207 }
208 if (assertion.isDefault()) {
209 relationInfo.defaultAssertions().add(assertion);
210 } else {
211 relationInfo.assertions().add(assertion);
212 }
213 for (var argument : assertion.getArguments()) {
214 if (argument instanceof ConstantAssertionArgument constantAssertionArgument) {
215 var constantNode = constantAssertionArgument.getNode();
216 if (constantNode != null) {
217 var valueAssertion = ProblemFactory.eINSTANCE.createNodeValueAssertion();
218 valueAssertion.setNode(constantNode);
219 valueAssertion.setValue(EcoreUtil.copy(constantAssertionArgument.getConstant()));
220 collectNodeValueAssertion(valueAssertion);
221 var logicValue = assertion.getValue();
222 if (logicValue != LogicValue.TRUE) {
223 addAssertion(builtinSymbols.exists(), logicValue, constantNode);
224 }
225 }
226 }
227 }
228 }
229
230 private void collectNodeValueAssertion(NodeValueAssertion nodeValueAssertion) {
231 var node = nodeValueAssertion.getNode();
232 if (node == null) {
233 return;
234 }
235 var nodeInfo = this.nodes.get(node);
236 if (nodeInfo == null) {
237 throw new IllegalStateException("Node value assertion refers to unknown node");
238 }
239 nodeInfo.valueAssertions().add(nodeValueAssertion);
240 var constant = nodeValueAssertion.getValue();
241 if (constant == null) {
242 return;
243 }
244 Relation dataType;
245 if (constant instanceof IntConstant) {
246 dataType = builtinSymbols.intClass();
247 } else if (constant instanceof RealConstant) {
248 dataType = builtinSymbols.real();
249 } else if (constant instanceof StringConstant) {
250 dataType = builtinSymbols.string();
251 } else {
252 throw new IllegalArgumentException("Unknown constant type");
253 }
254 addAssertion(dataType, LogicValue.TRUE, node);
255 }
256
257 private void collectClassAssertion(ClassDeclaration classDeclaration) {
258 var node = classDeclaration.getNewNode();
259 if (node == null) {
260 return;
261 }
262 addAssertion(classDeclaration, LogicValue.TRUE, node);
263 addAssertion(builtinSymbols.exists(), LogicValue.UNKNOWN, node);
264 addAssertion(builtinSymbols.equals(), LogicValue.UNKNOWN, node, node);
265 }
266
267 private void collectEnumAssertions(EnumDeclaration enumDeclaration) {
268 for (var literal : enumDeclaration.getLiterals()) {
269 addAssertion(enumDeclaration, LogicValue.TRUE, literal);
270 }
271 }
272
273 private void addAssertion(Relation relation, LogicValue logicValue, Node... nodes) {
274 var assertion = ProblemFactory.eINSTANCE.createAssertion();
275 assertion.setRelation(relation);
276 for (var node : nodes) {
277 var argument = ProblemFactory.eINSTANCE.createNodeAssertionArgument();
278 argument.setNode(node);
279 assertion.getArguments().add(argument);
280 }
281 assertion.setValue(logicValue);
282 collectAssertion(assertion);
283 }
284}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java b/subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java
index 6b0982eb..a0e78e1d 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java
@@ -8,13 +8,13 @@ import org.eclipse.xtext.validation.Check;
8 8
9import com.google.inject.Inject; 9import com.google.inject.Inject;
10 10
11import tools.refinery.language.ProblemUtil;
12import tools.refinery.language.model.problem.Node; 11import tools.refinery.language.model.problem.Node;
13import tools.refinery.language.model.problem.Problem; 12import tools.refinery.language.model.problem.Problem;
14import tools.refinery.language.model.problem.ProblemPackage; 13import tools.refinery.language.model.problem.ProblemPackage;
15import tools.refinery.language.model.problem.Variable; 14import tools.refinery.language.model.problem.Variable;
16import tools.refinery.language.model.problem.VariableOrNodeArgument; 15import tools.refinery.language.model.problem.VariableOrNodeArgument;
17import tools.refinery.language.resource.ReferenceCounter; 16import tools.refinery.language.resource.ReferenceCounter;
17import tools.refinery.language.utils.ProblemUtil;
18 18
19/** 19/**
20 * This class contains custom validation rules. 20 * This class contains custom validation rules.