diff options
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.java | 143 |
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 | */ | ||
6 | package tools.refinery.language.resource.state; | ||
7 | |||
8 | import org.eclipse.emf.ecore.EObject; | ||
9 | import org.eclipse.xtext.linking.impl.LinkingHelper; | ||
10 | import org.eclipse.xtext.naming.IQualifiedNameConverter; | ||
11 | import org.eclipse.xtext.naming.QualifiedName; | ||
12 | import org.eclipse.xtext.nodemodel.INode; | ||
13 | import org.eclipse.xtext.nodemodel.util.NodeModelUtils; | ||
14 | import org.eclipse.xtext.scoping.IScope; | ||
15 | import org.eclipse.xtext.scoping.IScopeProvider; | ||
16 | import tools.refinery.language.model.problem.*; | ||
17 | import tools.refinery.language.naming.NamingUtil; | ||
18 | |||
19 | import java.util.Deque; | ||
20 | import java.util.HashSet; | ||
21 | import java.util.List; | ||
22 | import java.util.Set; | ||
23 | |||
24 | public 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 | } | ||