diff options
author | Kristóf Marussy <kristof@marussy.com> | 2022-09-19 21:33:55 +0200 |
---|---|---|
committer | Kristóf Marussy <kristof@marussy.com> | 2022-09-19 21:42:05 +0200 |
commit | 7fb99f0225911a8962aaf3493b89f41e791df359 (patch) | |
tree | f289ac1433f91e2b1c9e1a5eab92ae6b52aa9d83 /subprojects/language/src | |
parent | refactor(language): clarify containment hierarchy (diff) | |
download | refinery-7fb99f0225911a8962aaf3493b89f41e791df359.tar.gz refinery-7fb99f0225911a8962aaf3493b89f41e791df359.tar.zst refinery-7fb99f0225911a8962aaf3493b89f41e791df359.zip |
feat(language): problem desugaring
Diffstat (limited to 'subprojects/language/src')
21 files changed, 917 insertions, 179 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 @@ | |||
1 | package tools.refinery.language; | ||
2 | |||
3 | import java.util.ArrayDeque; | ||
4 | import java.util.Collection; | ||
5 | import java.util.Deque; | ||
6 | import java.util.HashSet; | ||
7 | import java.util.Optional; | ||
8 | import java.util.Set; | ||
9 | |||
10 | import org.eclipse.emf.common.util.URI; | ||
11 | import org.eclipse.emf.ecore.EObject; | ||
12 | import org.eclipse.emf.ecore.resource.Resource; | ||
13 | |||
14 | import tools.refinery.language.model.problem.ClassDeclaration; | ||
15 | import tools.refinery.language.model.problem.ImplicitVariable; | ||
16 | import tools.refinery.language.model.problem.Node; | ||
17 | import tools.refinery.language.model.problem.Problem; | ||
18 | import tools.refinery.language.model.problem.ProblemPackage; | ||
19 | import tools.refinery.language.model.problem.ReferenceDeclaration; | ||
20 | import tools.refinery.language.model.problem.Relation; | ||
21 | import tools.refinery.language.model.problem.Variable; | ||
22 | |||
23 | public 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; | |||
24 | import com.google.inject.Singleton; | 24 | import com.google.inject.Singleton; |
25 | import com.google.inject.name.Named; | 25 | import com.google.inject.name.Named; |
26 | 26 | ||
27 | import tools.refinery.language.model.problem.Assertion; | ||
27 | import tools.refinery.language.model.problem.ClassDeclaration; | 28 | import tools.refinery.language.model.problem.ClassDeclaration; |
29 | import tools.refinery.language.model.problem.ConstantAssertionArgument; | ||
28 | import tools.refinery.language.model.problem.Node; | 30 | import tools.refinery.language.model.problem.Node; |
29 | import tools.refinery.language.model.problem.Problem; | 31 | import tools.refinery.language.model.problem.Problem; |
30 | import tools.refinery.language.model.problem.ProblemFactory; | 32 | import tools.refinery.language.model.problem.ProblemFactory; |
@@ -34,6 +36,8 @@ import tools.refinery.language.model.problem.Statement; | |||
34 | public class ProblemDerivedStateComputer implements IDerivedStateComputer { | 36 | public 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; | |||
4 | import org.eclipse.xtext.resource.DefaultLocationInFileProvider; | 4 | import org.eclipse.xtext.resource.DefaultLocationInFileProvider; |
5 | import org.eclipse.xtext.util.ITextRegion; | 5 | import org.eclipse.xtext.util.ITextRegion; |
6 | 6 | ||
7 | import tools.refinery.language.ProblemUtil; | ||
8 | import tools.refinery.language.model.problem.ImplicitVariable; | 7 | import tools.refinery.language.model.problem.ImplicitVariable; |
9 | import tools.refinery.language.model.problem.Node; | 8 | import tools.refinery.language.model.problem.Node; |
9 | import tools.refinery.language.utils.ProblemUtil; | ||
10 | 10 | ||
11 | public class ProblemLocationInFileProvider extends DefaultLocationInFileProvider { | 11 | public 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; | |||
12 | import com.google.inject.Inject; | 12 | import com.google.inject.Inject; |
13 | import com.google.inject.Singleton; | 13 | import com.google.inject.Singleton; |
14 | 14 | ||
15 | import tools.refinery.language.ProblemUtil; | ||
16 | import tools.refinery.language.model.problem.NamedElement; | 15 | import tools.refinery.language.model.problem.NamedElement; |
17 | import tools.refinery.language.model.problem.Node; | 16 | import tools.refinery.language.model.problem.Node; |
18 | import tools.refinery.language.model.problem.Problem; | 17 | import tools.refinery.language.model.problem.Problem; |
19 | import tools.refinery.language.model.problem.Variable; | 18 | import tools.refinery.language.model.problem.Variable; |
20 | import tools.refinery.language.naming.NamingUtil; | 19 | import tools.refinery.language.naming.NamingUtil; |
20 | import tools.refinery.language.utils.ProblemUtil; | ||
21 | 21 | ||
22 | @Singleton | 22 | @Singleton |
23 | public class ProblemResourceDescriptionStrategy extends DefaultResourceDescriptionStrategy { | 23 | public 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 | ||
6 | import org.eclipse.emf.ecore.EObject; | 6 | import org.eclipse.emf.ecore.EObject; |
7 | import org.eclipse.xtext.util.IResourceScopeCache; | 7 | import org.eclipse.xtext.util.IResourceScopeCache; |
8 | import org.eclipse.xtext.util.Tuples; | ||
8 | 9 | ||
9 | import com.google.inject.Inject; | 10 | import com.google.inject.Inject; |
10 | import com.google.inject.Singleton; | 11 | import com.google.inject.Singleton; |
@@ -14,7 +15,7 @@ import tools.refinery.language.model.problem.Problem; | |||
14 | @Singleton | 15 | @Singleton |
15 | public class ReferenceCounter { | 16 | public 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; | |||
6 | import org.eclipse.emf.ecore.resource.Resource; | 6 | import org.eclipse.emf.ecore.resource.Resource; |
7 | import org.eclipse.xtext.scoping.impl.ImportUriGlobalScopeProvider; | 7 | import org.eclipse.xtext.scoping.impl.ImportUriGlobalScopeProvider; |
8 | 8 | ||
9 | import tools.refinery.language.ProblemUtil; | 9 | import tools.refinery.language.utils.ProblemUtil; |
10 | 10 | ||
11 | public class ProblemGlobalScopeProvider extends ImportUriGlobalScopeProvider { | 11 | public 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 | ||
14 | import com.google.inject.Inject; | 14 | import com.google.inject.Inject; |
15 | 15 | ||
16 | import tools.refinery.language.ProblemUtil; | 16 | import tools.refinery.language.utils.ProblemUtil; |
17 | 17 | ||
18 | public class ProblemLocalScopeProvider extends ImportedNamespaceAwareLocalScopeProvider { | 18 | public 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; | |||
12 | import org.eclipse.xtext.scoping.IScope; | 12 | import org.eclipse.xtext.scoping.IScope; |
13 | import org.eclipse.xtext.scoping.Scopes; | 13 | import org.eclipse.xtext.scoping.Scopes; |
14 | 14 | ||
15 | import tools.refinery.language.ProblemUtil; | 15 | import com.google.inject.Inject; |
16 | |||
16 | import tools.refinery.language.model.problem.ClassDeclaration; | 17 | import tools.refinery.language.model.problem.ClassDeclaration; |
17 | import tools.refinery.language.model.problem.Consequent; | 18 | import tools.refinery.language.model.problem.Consequent; |
18 | import tools.refinery.language.model.problem.ExistentialQuantifier; | 19 | import tools.refinery.language.model.problem.ExistentialQuantifier; |
@@ -23,15 +24,18 @@ import tools.refinery.language.model.problem.ProblemPackage; | |||
23 | import tools.refinery.language.model.problem.ReferenceDeclaration; | 24 | import tools.refinery.language.model.problem.ReferenceDeclaration; |
24 | import tools.refinery.language.model.problem.Variable; | 25 | import tools.refinery.language.model.problem.Variable; |
25 | import tools.refinery.language.model.problem.VariableOrNodeArgument; | 26 | import tools.refinery.language.model.problem.VariableOrNodeArgument; |
27 | import 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 | */ |
34 | public class ProblemScopeProvider extends AbstractProblemScopeProvider { | 36 | public 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 @@ | |||
1 | package tools.refinery.language.utils; | ||
2 | |||
3 | import tools.refinery.language.model.problem.ClassDeclaration; | ||
4 | import tools.refinery.language.model.problem.EnumDeclaration; | ||
5 | import tools.refinery.language.model.problem.Node; | ||
6 | import tools.refinery.language.model.problem.PredicateDefinition; | ||
7 | import tools.refinery.language.model.problem.Problem; | ||
8 | import tools.refinery.language.model.problem.ReferenceDeclaration; | ||
9 | |||
10 | public 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 @@ | |||
1 | package tools.refinery.language.utils; | ||
2 | |||
3 | import java.util.Map; | ||
4 | |||
5 | import tools.refinery.language.model.problem.Node; | ||
6 | import tools.refinery.language.model.problem.Relation; | ||
7 | |||
8 | public 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 @@ | |||
1 | package tools.refinery.language.utils; | ||
2 | |||
3 | import java.util.ArrayList; | ||
4 | import java.util.Collection; | ||
5 | |||
6 | import tools.refinery.language.model.problem.NodeValueAssertion; | ||
7 | |||
8 | public 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 @@ | |||
1 | package tools.refinery.language.utils; | ||
2 | |||
3 | import com.google.inject.Inject; | ||
4 | import com.google.inject.Provider; | ||
5 | import com.google.inject.Singleton; | ||
6 | import org.eclipse.emf.ecore.EObject; | ||
7 | import org.eclipse.emf.ecore.resource.Resource; | ||
8 | import org.eclipse.xtext.util.IResourceScopeCache; | ||
9 | import org.eclipse.xtext.util.Tuples; | ||
10 | import tools.refinery.language.model.problem.*; | ||
11 | |||
12 | import java.util.*; | ||
13 | |||
14 | @Singleton | ||
15 | public 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 @@ | |||
1 | package tools.refinery.language.utils; | ||
2 | |||
3 | import org.eclipse.emf.common.util.URI; | ||
4 | import org.eclipse.emf.ecore.EObject; | ||
5 | |||
6 | import tools.refinery.language.model.problem.ImplicitVariable; | ||
7 | import tools.refinery.language.model.problem.Node; | ||
8 | import tools.refinery.language.model.problem.ProblemPackage; | ||
9 | import tools.refinery.language.model.problem.Variable; | ||
10 | |||
11 | public 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 @@ | |||
1 | package tools.refinery.language.utils; | ||
2 | |||
3 | import java.util.ArrayList; | ||
4 | import java.util.Collection; | ||
5 | import java.util.List; | ||
6 | |||
7 | import tools.refinery.language.model.problem.Assertion; | ||
8 | import tools.refinery.language.model.problem.Conjunction; | ||
9 | import tools.refinery.language.model.problem.Multiplicity; | ||
10 | import tools.refinery.language.model.problem.Parameter; | ||
11 | import tools.refinery.language.model.problem.PredicateKind; | ||
12 | import tools.refinery.language.model.problem.Relation; | ||
13 | import tools.refinery.language.model.problem.TypeScope; | ||
14 | |||
15 | public 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 @@ | |||
1 | package tools.refinery.language.utils; | ||
2 | |||
3 | import java.util.LinkedHashMap; | ||
4 | import java.util.List; | ||
5 | import java.util.Map; | ||
6 | |||
7 | import org.eclipse.emf.ecore.EObject; | ||
8 | import org.eclipse.emf.ecore.util.EcoreUtil; | ||
9 | import org.eclipse.xtext.naming.IQualifiedNameConverter; | ||
10 | import org.eclipse.xtext.naming.IQualifiedNameProvider; | ||
11 | |||
12 | import com.google.inject.Inject; | ||
13 | |||
14 | import tools.refinery.language.model.problem.Assertion; | ||
15 | import tools.refinery.language.model.problem.ClassDeclaration; | ||
16 | import tools.refinery.language.model.problem.ConstantAssertionArgument; | ||
17 | import tools.refinery.language.model.problem.EnumDeclaration; | ||
18 | import tools.refinery.language.model.problem.IndividualDeclaration; | ||
19 | import tools.refinery.language.model.problem.IntConstant; | ||
20 | import tools.refinery.language.model.problem.LogicValue; | ||
21 | import tools.refinery.language.model.problem.Node; | ||
22 | import tools.refinery.language.model.problem.NodeValueAssertion; | ||
23 | import tools.refinery.language.model.problem.PredicateDefinition; | ||
24 | import tools.refinery.language.model.problem.PredicateKind; | ||
25 | import tools.refinery.language.model.problem.Problem; | ||
26 | import tools.refinery.language.model.problem.ProblemFactory; | ||
27 | import tools.refinery.language.model.problem.RealConstant; | ||
28 | import tools.refinery.language.model.problem.Relation; | ||
29 | import tools.refinery.language.model.problem.StringConstant; | ||
30 | |||
31 | class 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 | ||
9 | import com.google.inject.Inject; | 9 | import com.google.inject.Inject; |
10 | 10 | ||
11 | import tools.refinery.language.ProblemUtil; | ||
12 | import tools.refinery.language.model.problem.Node; | 11 | import tools.refinery.language.model.problem.Node; |
13 | import tools.refinery.language.model.problem.Problem; | 12 | import tools.refinery.language.model.problem.Problem; |
14 | import tools.refinery.language.model.problem.ProblemPackage; | 13 | import tools.refinery.language.model.problem.ProblemPackage; |
15 | import tools.refinery.language.model.problem.Variable; | 14 | import tools.refinery.language.model.problem.Variable; |
16 | import tools.refinery.language.model.problem.VariableOrNodeArgument; | 15 | import tools.refinery.language.model.problem.VariableOrNodeArgument; |
17 | import tools.refinery.language.resource.ReferenceCounter; | 16 | import tools.refinery.language.resource.ReferenceCounter; |
17 | import tools.refinery.language.utils.ProblemUtil; | ||
18 | 18 | ||
19 | /** | 19 | /** |
20 | * This class contains custom validation rules. | 20 | * This class contains custom validation rules. |
diff --git a/subprojects/language/src/main/resources/tools/refinery/language/builtin.problem b/subprojects/language/src/main/resources/tools/refinery/language/builtin.problem index e39ff1a3..2f7c667a 100644 --- a/subprojects/language/src/main/resources/tools/refinery/language/builtin.problem +++ b/subprojects/language/src/main/resources/tools/refinery/language/builtin.problem | |||
@@ -14,10 +14,10 @@ enum bool { | |||
14 | true, false | 14 | true, false |
15 | } | 15 | } |
16 | 16 | ||
17 | class real extends data. | ||
18 | |||
19 | class int extends data. | 17 | class int extends data. |
20 | 18 | ||
19 | class real extends data. | ||
20 | |||
21 | class string extends data. | 21 | class string extends data. |
22 | 22 | ||
23 | pred contained(node node). | 23 | pred contained(node node). |
diff --git a/subprojects/language/src/test/java/tools/refinery/language/tests/utils/SymbolCollectorTest.java b/subprojects/language/src/test/java/tools/refinery/language/tests/utils/SymbolCollectorTest.java new file mode 100644 index 00000000..e2e3218c --- /dev/null +++ b/subprojects/language/src/test/java/tools/refinery/language/tests/utils/SymbolCollectorTest.java | |||
@@ -0,0 +1,276 @@ | |||
1 | package tools.refinery.language.tests.utils; | ||
2 | |||
3 | import com.google.inject.Inject; | ||
4 | import org.eclipse.xtext.testing.InjectWith; | ||
5 | import org.eclipse.xtext.testing.extensions.InjectionExtension; | ||
6 | import org.hamcrest.Matcher; | ||
7 | import org.junit.jupiter.api.Test; | ||
8 | import org.junit.jupiter.api.extension.ExtendWith; | ||
9 | import org.junit.jupiter.params.ParameterizedTest; | ||
10 | import org.junit.jupiter.params.provider.Arguments; | ||
11 | import org.junit.jupiter.params.provider.MethodSource; | ||
12 | import tools.refinery.language.model.problem.*; | ||
13 | import tools.refinery.language.model.tests.utils.ProblemParseHelper; | ||
14 | import tools.refinery.language.tests.ProblemInjectorProvider; | ||
15 | import tools.refinery.language.utils.ProblemDesugarer; | ||
16 | |||
17 | import java.util.stream.Stream; | ||
18 | |||
19 | import static org.hamcrest.MatcherAssert.assertThat; | ||
20 | import static org.hamcrest.Matchers.*; | ||
21 | import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; | ||
22 | |||
23 | @ExtendWith(InjectionExtension.class) | ||
24 | @InjectWith(ProblemInjectorProvider.class) | ||
25 | class SymbolCollectorTest { | ||
26 | @Inject | ||
27 | private ProblemParseHelper parseHelper; | ||
28 | |||
29 | @Inject | ||
30 | private ProblemDesugarer desugarer; | ||
31 | |||
32 | @Test | ||
33 | void implicitNodeTest() { | ||
34 | var problem = parseHelper.parse(""" | ||
35 | exists(a). | ||
36 | """); | ||
37 | var collectedSymbols = desugarer.collectSymbols(problem.get()); | ||
38 | var node = problem.node("a"); | ||
39 | assertThat(collectedSymbols.nodes(), hasKey(node)); | ||
40 | assertThat(collectedSymbols.nodes().get(node).individual(), is(false)); | ||
41 | } | ||
42 | |||
43 | @Test | ||
44 | void individualNodeTest() { | ||
45 | var problem = parseHelper.parse(""" | ||
46 | indiv a. | ||
47 | """); | ||
48 | var collectedSymbols = desugarer.collectSymbols(problem.get()); | ||
49 | var node = problem.individualNode("a"); | ||
50 | assertThat(collectedSymbols.nodes(), hasKey(node)); | ||
51 | assertThat(collectedSymbols.nodes().get(node).individual(), is(true)); | ||
52 | } | ||
53 | |||
54 | @Test | ||
55 | void classTest() { | ||
56 | var problem = parseHelper.parse(""" | ||
57 | class Foo. | ||
58 | """); | ||
59 | var collectedSymbols = desugarer.collectSymbols(problem.get()); | ||
60 | var classDeclaration = problem.findClass("Foo").get(); | ||
61 | assertThat(collectedSymbols.relations(), hasKey(classDeclaration)); | ||
62 | var classInfo = collectedSymbols.relations().get(classDeclaration); | ||
63 | assertThat(classInfo.parameters(), hasSize(1)); | ||
64 | assertThat(classInfo.kind(), is(PredicateKind.CONTAINED)); | ||
65 | assertThat(classInfo.hasDefinition(), is(false)); | ||
66 | var newNode = classDeclaration.getNewNode(); | ||
67 | assertThat(collectedSymbols.nodes(), hasKey(newNode)); | ||
68 | assertThat(collectedSymbols.nodes().get(newNode).individual(), is(false)); | ||
69 | assertThat(classInfo.assertions(), assertsNode(newNode, LogicValue.TRUE)); | ||
70 | assertThat(collectedSymbols.relations().get(problem.builtinSymbols().exists()).assertions(), | ||
71 | assertsNode(newNode, LogicValue.UNKNOWN)); | ||
72 | assertThat(collectedSymbols.relations().get(problem.builtinSymbols().equals()).assertions(), | ||
73 | assertsNode(newNode, LogicValue.UNKNOWN)); | ||
74 | } | ||
75 | |||
76 | @Test | ||
77 | void abstractClassTest() { | ||
78 | var problem = parseHelper.parse(""" | ||
79 | abstract class Foo. | ||
80 | """); | ||
81 | var collectedSymbols = desugarer.collectSymbols(problem.get()); | ||
82 | assertThat(collectedSymbols.relations().get(problem.findClass("Foo").get()).assertions(), hasSize(0)); | ||
83 | } | ||
84 | |||
85 | @Test | ||
86 | void referenceTest() { | ||
87 | var problem = parseHelper.parse(""" | ||
88 | class Foo { | ||
89 | Foo[] bar opposite quux | ||
90 | Foo quux opposite bar | ||
91 | } | ||
92 | """); | ||
93 | var collectedSymbols = desugarer.collectSymbols(problem.get()); | ||
94 | var fooClass = problem.findClass("Foo"); | ||
95 | var barReference = fooClass.reference("bar"); | ||
96 | var barInfo = collectedSymbols.relations().get(barReference); | ||
97 | var quuxReference = fooClass.reference("quux"); | ||
98 | var quuxInfo = collectedSymbols.relations().get(quuxReference); | ||
99 | assertThat(barInfo.kind(), is(PredicateKind.DEFAULT)); | ||
100 | assertThat(barInfo.opposite(), is(quuxReference)); | ||
101 | assertThat(barInfo.multiplicity(), is(instanceOf(UnboundedMultiplicity.class))); | ||
102 | assertThat(barInfo.hasDefinition(), is(false)); | ||
103 | assertThat(quuxInfo.kind(), is(PredicateKind.DEFAULT)); | ||
104 | assertThat(quuxInfo.opposite(), is(barReference)); | ||
105 | assertThat(quuxInfo.multiplicity(), is(instanceOf(ExactMultiplicity.class))); | ||
106 | assertThat(quuxInfo.multiplicity(), hasProperty("exactValue", is(1))); | ||
107 | assertThat(quuxInfo.hasDefinition(), is(false)); | ||
108 | } | ||
109 | |||
110 | @Test | ||
111 | void containmentReferenceTest() { | ||
112 | var problem = parseHelper.parse(""" | ||
113 | class Foo { | ||
114 | contains Foo[] bar | ||
115 | } | ||
116 | """); | ||
117 | var collectedSymbols = desugarer.collectSymbols(problem.get()); | ||
118 | assertThat(collectedSymbols.relations().get(problem.findClass("Foo").reference("bar")).kind(), | ||
119 | is(PredicateKind.CONTAINMENT)); | ||
120 | } | ||
121 | |||
122 | @Test | ||
123 | void dataReferenceTest() { | ||
124 | var problem = parseHelper.parse(""" | ||
125 | class Foo { | ||
126 | int bar | ||
127 | } | ||
128 | """); | ||
129 | var collectedSymbols = desugarer.collectSymbols(problem.get()); | ||
130 | assertThat(collectedSymbols.relations().get(problem.findClass("Foo").reference("bar")).kind(), | ||
131 | is(PredicateKind.CONTAINMENT)); | ||
132 | } | ||
133 | |||
134 | @Test | ||
135 | void enumTest() { | ||
136 | var problem = parseHelper.parse(""" | ||
137 | enum Foo { | ||
138 | bar, quux | ||
139 | } | ||
140 | """); | ||
141 | var collectedSymbols = desugarer.collectSymbols(problem.get()); | ||
142 | var enumDeclaration = problem.findEnum("Foo"); | ||
143 | var enumInfo = collectedSymbols.relations().get(enumDeclaration.get()); | ||
144 | assertThat(enumInfo.kind(), is(PredicateKind.DEFAULT)); | ||
145 | assertThat(enumInfo.assertions(), assertsNode(enumDeclaration.literal("bar"), LogicValue.TRUE)); | ||
146 | assertThat(enumInfo.assertions(), assertsNode(enumDeclaration.literal("quux"), LogicValue.TRUE)); | ||
147 | } | ||
148 | |||
149 | @ParameterizedTest | ||
150 | @MethodSource | ||
151 | void predicateTest(String keyword, PredicateKind kind) { | ||
152 | var problem = parseHelper.parse(keyword + " foo(node x) <-> domain(x); data(x)."); | ||
153 | var collectedSymbols = desugarer.collectSymbols(problem.get()); | ||
154 | var predicateInfo = collectedSymbols.relations().get(problem.pred("foo").get()); | ||
155 | assertThat(predicateInfo.kind(), is(kind)); | ||
156 | assertThat(predicateInfo.parameters(), hasSize(1)); | ||
157 | assertThat(predicateInfo.bodies(), hasSize(2)); | ||
158 | assertThat(predicateInfo.hasDefinition(), is(true)); | ||
159 | } | ||
160 | |||
161 | static Stream<Arguments> predicateTest() { | ||
162 | return Stream.of(Arguments.of("pred", PredicateKind.DEFAULT), Arguments.of("error", PredicateKind.ERROR), | ||
163 | Arguments.of("contained", PredicateKind.CONTAINED), Arguments.of("containment", | ||
164 | PredicateKind.CONTAINMENT)); | ||
165 | } | ||
166 | |||
167 | @ParameterizedTest | ||
168 | @MethodSource("logicValues") | ||
169 | void assertionTest(String keyword, LogicValue value) { | ||
170 | var problem = parseHelper.parse(""" | ||
171 | pred foo(node x). | ||
172 | foo(a): %s. | ||
173 | """.formatted(keyword)); | ||
174 | var collectedSymbols = desugarer.collectSymbols(problem.get()); | ||
175 | assertThat(collectedSymbols.relations().get(problem.pred("foo").get()).assertions(), | ||
176 | assertsNode(problem.node("a"), value)); | ||
177 | } | ||
178 | |||
179 | @ParameterizedTest | ||
180 | @MethodSource("logicValues") | ||
181 | void defaultAssertionTest(String keyword, LogicValue value) { | ||
182 | var problem = parseHelper.parse(""" | ||
183 | pred foo(node x). | ||
184 | default foo(a): %s. | ||
185 | """.formatted(keyword)); | ||
186 | var collectedSymbols = desugarer.collectSymbols(problem.get()); | ||
187 | assertThat(collectedSymbols.relations().get(problem.pred("foo").get()).defaultAssertions(), | ||
188 | assertsNode(problem.node("a"), value)); | ||
189 | } | ||
190 | |||
191 | static Stream<Arguments> logicValues() { | ||
192 | return Stream.of(Arguments.of("true", LogicValue.TRUE), Arguments.of("false", LogicValue.FALSE), | ||
193 | Arguments.of("unknown", LogicValue.UNKNOWN), Arguments.of("error", LogicValue.ERROR)); | ||
194 | } | ||
195 | |||
196 | @Test | ||
197 | void invalidAssertionArityTest() { | ||
198 | var problem = parseHelper.parse(""" | ||
199 | pred foo(node x). | ||
200 | foo(a, b). | ||
201 | """); | ||
202 | var collectedSymbols = desugarer.collectSymbols(problem.get()); | ||
203 | assertThat(collectedSymbols.relations().get(problem.pred("foo").get()).assertions(), hasSize(0)); | ||
204 | } | ||
205 | |||
206 | @ParameterizedTest | ||
207 | @MethodSource("valueTypes") | ||
208 | void nodeValueAssertionTest(String value, String typeName) { | ||
209 | var problem = parseHelper.parse("a: %s.".formatted(value)); | ||
210 | var collectedSymbols = desugarer.collectSymbols(problem.get()); | ||
211 | var node = problem.node("a"); | ||
212 | var nodeInfo = collectedSymbols.nodes().get(node); | ||
213 | assertThat(nodeInfo.individual(), is(false)); | ||
214 | assertThat(nodeInfo.valueAssertions(), hasSize(1)); | ||
215 | assertThat(collectedSymbols.relations().get(problem.builtin().findClass(typeName).get()).assertions(), | ||
216 | assertsNode(node, LogicValue.TRUE)); | ||
217 | } | ||
218 | |||
219 | @ParameterizedTest | ||
220 | @MethodSource("valueTypes") | ||
221 | void constantInAssertionTest(String value, String typeName) { | ||
222 | var problem = parseHelper.parse(""" | ||
223 | containment pred foo(node x, data y). | ||
224 | foo(a, %s). | ||
225 | """.formatted(value)); | ||
226 | var collectedSymbols = desugarer.collectSymbols(problem.get()); | ||
227 | var node = problem.assertion(0).arg(1).constantNode(); | ||
228 | var nodeInfo = collectedSymbols.nodes().get(node); | ||
229 | assertThat(nodeInfo.individual(), is(false)); | ||
230 | assertThat(nodeInfo.valueAssertions(), hasSize(1)); | ||
231 | assertThat(collectedSymbols.relations().get(problem.pred("foo").get()).assertions(), assertsNode(node, | ||
232 | LogicValue.TRUE)); | ||
233 | assertThat(collectedSymbols.relations().get(problem.builtin().findClass(typeName).get()).assertions(), | ||
234 | assertsNode(node, LogicValue.TRUE)); | ||
235 | } | ||
236 | |||
237 | @ParameterizedTest | ||
238 | @MethodSource("valueTypes") | ||
239 | void constantInUnknownAssertionTest(String value, String typeName) { | ||
240 | var problem = parseHelper.parse(""" | ||
241 | containment pred foo(node x, data y). | ||
242 | foo(a, %s): unknown. | ||
243 | """.formatted(value)); | ||
244 | var collectedSymbols = desugarer.collectSymbols(problem.get()); | ||
245 | var node = problem.assertion(0).arg(1).constantNode(); | ||
246 | var nodeInfo = collectedSymbols.nodes().get(node); | ||
247 | assertThat(nodeInfo.individual(), is(false)); | ||
248 | assertThat(nodeInfo.valueAssertions(), hasSize(1)); | ||
249 | assertThat(collectedSymbols.relations().get(problem.pred("foo").get()).assertions(), assertsNode(node, | ||
250 | LogicValue.UNKNOWN)); | ||
251 | assertThat(collectedSymbols.relations().get(problem.builtin().findClass(typeName).get()).assertions(), | ||
252 | assertsNode(node, LogicValue.TRUE)); | ||
253 | assertThat(collectedSymbols.relations().get(problem.builtinSymbols().exists()).assertions(), assertsNode(node, | ||
254 | LogicValue.UNKNOWN)); | ||
255 | } | ||
256 | |||
257 | @Test | ||
258 | void invalidProblemTest() { | ||
259 | var problem = parseHelper.parse(""" | ||
260 | class Foo { | ||
261 | bar[] opposite quux | ||
262 | Foo quux opposite bar | ||
263 | } | ||
264 | """).get(); | ||
265 | assertDoesNotThrow(() -> desugarer.collectSymbols(problem)); | ||
266 | } | ||
267 | |||
268 | static Stream<Arguments> valueTypes() { | ||
269 | return Stream.of(Arguments.of("3", "int"), Arguments.of("3.14", "real"), Arguments.of("\"foo\"", "string")); | ||
270 | } | ||
271 | |||
272 | private static Matcher<Iterable<? super Assertion>> assertsNode(Node node, LogicValue value) { | ||
273 | return hasItem(allOf(hasProperty("arguments", hasItem(hasProperty("node", is(node)))), hasProperty("value", | ||
274 | is(value)))); | ||
275 | } | ||
276 | } | ||
diff --git a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedArgument.java b/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedArgument.java index 61492184..ad57a438 100644 --- a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedArgument.java +++ b/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedArgument.java | |||
@@ -1,10 +1,6 @@ | |||
1 | package tools.refinery.language.model.tests.utils; | 1 | package tools.refinery.language.model.tests.utils; |
2 | 2 | ||
3 | import tools.refinery.language.model.problem.Argument; | 3 | import tools.refinery.language.model.problem.*; |
4 | import tools.refinery.language.model.problem.Node; | ||
5 | import tools.refinery.language.model.problem.Variable; | ||
6 | import tools.refinery.language.model.problem.VariableOrNode; | ||
7 | import tools.refinery.language.model.problem.VariableOrNodeArgument; | ||
8 | 4 | ||
9 | public record WrappedArgument(Argument argument) { | 5 | public record WrappedArgument(Argument argument) { |
10 | public Argument get() { | 6 | public Argument get() { |
@@ -18,7 +14,7 @@ public record WrappedArgument(Argument argument) { | |||
18 | public Variable variable() { | 14 | public Variable variable() { |
19 | return (Variable) variableOrNode(); | 15 | return (Variable) variableOrNode(); |
20 | } | 16 | } |
21 | 17 | ||
22 | public Node node() { | 18 | public Node node() { |
23 | return (Node) variableOrNode(); | 19 | return (Node) variableOrNode(); |
24 | } | 20 | } |
diff --git a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedAssertionArgument.java b/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedAssertionArgument.java index 77ad3746..b8b3a7de 100644 --- a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedAssertionArgument.java +++ b/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedAssertionArgument.java | |||
@@ -1,6 +1,7 @@ | |||
1 | package tools.refinery.language.model.tests.utils; | 1 | package tools.refinery.language.model.tests.utils; |
2 | 2 | ||
3 | import tools.refinery.language.model.problem.AssertionArgument; | 3 | import tools.refinery.language.model.problem.AssertionArgument; |
4 | import tools.refinery.language.model.problem.ConstantAssertionArgument; | ||
4 | import tools.refinery.language.model.problem.Node; | 5 | import tools.refinery.language.model.problem.Node; |
5 | import tools.refinery.language.model.problem.NodeAssertionArgument; | 6 | import tools.refinery.language.model.problem.NodeAssertionArgument; |
6 | 7 | ||
@@ -8,8 +9,12 @@ public record WrappedAssertionArgument(AssertionArgument assertionArgument) { | |||
8 | public AssertionArgument get() { | 9 | public AssertionArgument get() { |
9 | return assertionArgument; | 10 | return assertionArgument; |
10 | } | 11 | } |
11 | 12 | ||
12 | public Node node() { | 13 | public Node node() { |
13 | return ((NodeAssertionArgument) assertionArgument).getNode(); | 14 | return ((NodeAssertionArgument) assertionArgument).getNode(); |
14 | } | 15 | } |
16 | |||
17 | public Node constantNode() { | ||
18 | return ((ConstantAssertionArgument) assertionArgument).getNode(); | ||
19 | } | ||
15 | } | 20 | } |
diff --git a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedProblem.java b/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedProblem.java index 2bedb4a6..aef96b5b 100644 --- a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedProblem.java +++ b/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedProblem.java | |||
@@ -6,7 +6,6 @@ import java.util.stream.Stream; | |||
6 | import org.eclipse.emf.ecore.resource.Resource.Diagnostic; | 6 | import org.eclipse.emf.ecore.resource.Resource.Diagnostic; |
7 | import org.eclipse.emf.ecore.util.EcoreUtil; | 7 | import org.eclipse.emf.ecore.util.EcoreUtil; |
8 | 8 | ||
9 | import tools.refinery.language.ProblemUtil; | ||
10 | import tools.refinery.language.model.problem.Assertion; | 9 | import tools.refinery.language.model.problem.Assertion; |
11 | import tools.refinery.language.model.problem.ClassDeclaration; | 10 | import tools.refinery.language.model.problem.ClassDeclaration; |
12 | import tools.refinery.language.model.problem.EnumDeclaration; | 11 | import tools.refinery.language.model.problem.EnumDeclaration; |
@@ -18,6 +17,8 @@ import tools.refinery.language.model.problem.PredicateDefinition; | |||
18 | import tools.refinery.language.model.problem.Problem; | 17 | import tools.refinery.language.model.problem.Problem; |
19 | import tools.refinery.language.model.problem.RuleDefinition; | 18 | import tools.refinery.language.model.problem.RuleDefinition; |
20 | import tools.refinery.language.model.problem.Statement; | 19 | import tools.refinery.language.model.problem.Statement; |
20 | import tools.refinery.language.utils.BuiltinSymbols; | ||
21 | import tools.refinery.language.utils.ProblemDesugarer; | ||
21 | 22 | ||
22 | public record WrappedProblem(Problem problem) { | 23 | public record WrappedProblem(Problem problem) { |
23 | public Problem get() { | 24 | public Problem get() { |
@@ -30,11 +31,15 @@ public record WrappedProblem(Problem problem) { | |||
30 | } | 31 | } |
31 | 32 | ||
32 | public WrappedProblem builtin() { | 33 | public WrappedProblem builtin() { |
33 | return new WrappedProblem(ProblemUtil.getBuiltInLibrary(problem).get()); | 34 | return new WrappedProblem(new ProblemDesugarer().getBuiltinProblem(problem).orElseThrow()); |
35 | } | ||
36 | |||
37 | public BuiltinSymbols builtinSymbols() { | ||
38 | return new ProblemDesugarer().getBuiltinSymbols(problem).orElseThrow(); | ||
34 | } | 39 | } |
35 | 40 | ||
36 | public List<String> nodeNames() { | 41 | public List<String> nodeNames() { |
37 | return problem.getNodes().stream().map(node -> node.getName()).toList(); | 42 | return problem.getNodes().stream().map(Node::getName).toList(); |
38 | } | 43 | } |
39 | 44 | ||
40 | public WrappedPredicateDefinition pred(String name) { | 45 | public WrappedPredicateDefinition pred(String name) { |
@@ -80,6 +85,6 @@ public record WrappedProblem(Problem problem) { | |||
80 | } | 85 | } |
81 | 86 | ||
82 | private <T extends Statement> T nthStatementOfType(Class<? extends T> type, int n) { | 87 | private <T extends Statement> T nthStatementOfType(Class<? extends T> type, int n) { |
83 | return statementsOfType(type).skip(n).findFirst().get(); | 88 | return statementsOfType(type).skip(n).findFirst().orElseThrow(); |
84 | } | 89 | } |
85 | } | 90 | } |