aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/language/src/main/java/tools/refinery/language/resource/state/ImplicitVariableScope.java
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/language/src/main/java/tools/refinery/language/resource/state/ImplicitVariableScope.java')
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/resource/state/ImplicitVariableScope.java143
1 files changed, 143 insertions, 0 deletions
diff --git a/subprojects/language/src/main/java/tools/refinery/language/resource/state/ImplicitVariableScope.java b/subprojects/language/src/main/java/tools/refinery/language/resource/state/ImplicitVariableScope.java
new file mode 100644
index 00000000..e25887ad
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/resource/state/ImplicitVariableScope.java
@@ -0,0 +1,143 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.language.resource.state;
7
8import org.eclipse.emf.ecore.EObject;
9import org.eclipse.xtext.linking.impl.LinkingHelper;
10import org.eclipse.xtext.naming.IQualifiedNameConverter;
11import org.eclipse.xtext.naming.QualifiedName;
12import org.eclipse.xtext.nodemodel.INode;
13import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
14import org.eclipse.xtext.scoping.IScope;
15import org.eclipse.xtext.scoping.IScopeProvider;
16import tools.refinery.language.model.problem.*;
17import tools.refinery.language.naming.NamingUtil;
18
19import java.util.Deque;
20import java.util.HashSet;
21import java.util.List;
22import java.util.Set;
23
24public class ImplicitVariableScope {
25 private final EObject root;
26
27 private final ExistentialQuantifier quantifier;
28
29 private final ImplicitVariableScope parent;
30
31 private Set<String> knownVariables;
32
33 private ImplicitVariableScope(ExistentialQuantifier quantifier, ImplicitVariableScope parent) {
34 this.root = quantifier;
35 this.quantifier = quantifier;
36 this.parent = parent;
37 this.knownVariables = null;
38 }
39
40 public ImplicitVariableScope(EObject root, ExistentialQuantifier quantifier, Set<String> knownVariables) {
41 this.root = root;
42 this.quantifier = quantifier;
43 this.parent = null;
44 this.knownVariables = new HashSet<>(knownVariables);
45 }
46
47 public ImplicitVariableScope(ExistentialQuantifier root, Set<String> knownVariables) {
48 this(root, root, knownVariables);
49 }
50
51 public void createVariables(IScopeProvider scopeProvider, LinkingHelper linkingHelper,
52 IQualifiedNameConverter qualifiedNameConverter,
53 Deque<ImplicitVariableScope> scopeQueue) {
54 initializeKnownVariables();
55 processEObject(root, scopeProvider, linkingHelper, qualifiedNameConverter);
56 var treeIterator = root.eAllContents();
57 while (treeIterator.hasNext()) {
58 var child = treeIterator.next();
59 if (child instanceof ExistentialQuantifier nestedQuantifier) {
60 scopeQueue.addLast(new ImplicitVariableScope(nestedQuantifier, this));
61 treeIterator.prune();
62 } else {
63 processEObject(child, scopeProvider, linkingHelper, qualifiedNameConverter);
64 }
65 }
66 }
67
68 private void initializeKnownVariables() {
69 boolean hasKnownVariables = knownVariables != null;
70 boolean hasParent = parent != null;
71 if ((hasKnownVariables && hasParent) || (!hasKnownVariables && !hasParent)) {
72 throw new IllegalStateException("Either known variables or parent must be provided, but not both");
73 }
74 if (hasKnownVariables) {
75 return;
76 }
77 if (parent.knownVariables == null) {
78 throw new IllegalStateException("Parent scope must be processed before current scope");
79 }
80 knownVariables = new HashSet<>(parent.knownVariables);
81 }
82
83 private void processEObject(EObject eObject, IScopeProvider scopeProvider, LinkingHelper linkingHelper,
84 IQualifiedNameConverter qualifiedNameConverter) {
85 if (!(eObject instanceof VariableOrNodeExpr variableOrNodeExpr)) {
86 return;
87 }
88 IScope scope = scopeProvider.getScope(variableOrNodeExpr,
89 ProblemPackage.Literals.VARIABLE_OR_NODE_EXPR__VARIABLE_OR_NODE);
90 List<INode> nodes = NodeModelUtils.findNodesForFeature(variableOrNodeExpr,
91 ProblemPackage.Literals.VARIABLE_OR_NODE_EXPR__VARIABLE_OR_NODE);
92 for (INode node : nodes) {
93 var variableName = linkingHelper.getCrossRefNodeAsString(node, true);
94 var created = tryCreateVariableForArgument(variableOrNodeExpr, variableName, qualifiedNameConverter,
95 scope);
96 if (created) {
97 break;
98 }
99 }
100 }
101
102 protected boolean tryCreateVariableForArgument(VariableOrNodeExpr variableOrNodeExpr, String variableName,
103 IQualifiedNameConverter qualifiedNameConverter, IScope scope) {
104 if (!NamingUtil.isValidId(variableName)) {
105 return false;
106 }
107 QualifiedName qualifiedName;
108 try {
109 qualifiedName = qualifiedNameConverter.toQualifiedName(variableName);
110 } catch (IllegalArgumentException e) {
111 return false;
112 }
113 if (scope.getSingleElement(qualifiedName) != null) {
114 return false;
115 }
116 if (NamingUtil.isSingletonVariableName(variableName)) {
117 createSingletonVariable(variableOrNodeExpr, variableName);
118 return true;
119 }
120 if (!knownVariables.contains(variableName)) {
121 createVariable(variableName);
122 return true;
123 }
124 return false;
125 }
126
127 protected void createVariable(String variableName) {
128 knownVariables.add(variableName);
129 ImplicitVariable variable = createNamedVariable(variableName);
130 quantifier.getImplicitVariables().add(variable);
131 }
132
133 protected void createSingletonVariable(VariableOrNodeExpr variableOrNodeExpr, String variableName) {
134 ImplicitVariable variable = createNamedVariable(variableName);
135 variableOrNodeExpr.setSingletonVariable(variable);
136 }
137
138 protected ImplicitVariable createNamedVariable(String variableName) {
139 var variable = ProblemFactory.eINSTANCE.createImplicitVariable();
140 variable.setName(variableName);
141 return variable;
142 }
143}