/*
* SPDX-FileCopyrightText: 2021-2023 The Refinery Authors
*
* SPDX-License-Identifier: EPL-2.0
*/
package tools.refinery.language.resource.state;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.inject.name.Named;
import org.eclipse.xtext.linking.impl.LinkingHelper;
import org.eclipse.xtext.naming.IQualifiedNameConverter;
import org.eclipse.xtext.scoping.IScopeProvider;
import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider;
import tools.refinery.language.model.problem.*;
import java.util.*;
@Singleton
public class DerivedVariableComputer {
@Inject
private LinkingHelper linkingHelper;
@Inject
private IQualifiedNameConverter qualifiedNameConverter;
@Inject
@Named(AbstractDeclarativeScopeProvider.NAMED_DELEGATE)
private IScopeProvider scopeProvider;
public void installDerivedVariables(Problem problem, Set nodeNames) {
for (Statement statement : problem.getStatements()) {
if (statement instanceof ParametricDefinition definition) {
installDerivedParametricDefinitionState(definition, nodeNames);
}
}
}
protected void installDerivedParametricDefinitionState(ParametricDefinition definition, Set nodeNames) {
Set knownVariables = new HashSet<>(nodeNames);
for (Parameter parameter : definition.getParameters()) {
String name = parameter.getName();
if (name != null) {
knownVariables.add(name);
}
}
if (definition instanceof PredicateDefinition predicateDefinition) {
installDerivedPredicateDefinitionState(predicateDefinition, knownVariables);
} else if (definition instanceof FunctionDefinition functionDefinition) {
installDerivedFunctionDefinitionState(functionDefinition, knownVariables);
} else if (definition instanceof RuleDefinition ruleDefinition) {
installDerivedRuleDefinitionState(ruleDefinition, knownVariables);
} else {
throw new IllegalArgumentException("Unknown ParametricDefinition: " + definition);
}
}
protected void installDerivedPredicateDefinitionState(PredicateDefinition definition, Set knownVariables) {
for (Conjunction body : definition.getBodies()) {
createVariablesForScope(new ImplicitVariableScope(body, knownVariables));
}
}
protected void installDerivedFunctionDefinitionState(FunctionDefinition definition, Set knownVariables) {
for (Case body : definition.getCases()) {
if (body instanceof Conjunction conjunction) {
createVariablesForScope(new ImplicitVariableScope(conjunction, knownVariables));
} else if (body instanceof Match match) {
var condition = match.getCondition();
if (condition != null) {
createVariablesForScope(new ImplicitVariableScope(match, match.getCondition(), knownVariables));
}
} else {
throw new IllegalArgumentException("Unknown Case: " + body);
}
}
}
protected void installDerivedRuleDefinitionState(RuleDefinition definition, Set knownVariables) {
for (Conjunction precondition : definition.getPreconditions()) {
createVariablesForScope(new ImplicitVariableScope(precondition, knownVariables));
}
}
protected void createVariablesForScope(ImplicitVariableScope scope) {
var queue = new ArrayDeque();
queue.addLast(scope);
while (!queue.isEmpty()) {
var nextScope = queue.removeFirst();
nextScope.createVariables(scopeProvider, linkingHelper, qualifiedNameConverter, queue);
}
}
public void discardDerivedVariables(Problem problem) {
for (Statement statement : problem.getStatements()) {
if (statement instanceof ParametricDefinition parametricDefinition) {
discardParametricDefinitionState(parametricDefinition);
}
}
}
protected void discardParametricDefinitionState(ParametricDefinition definition) {
List existentialQuantifiers = new ArrayList<>();
List variableOrNodeExprs = new ArrayList<>();
var treeIterator = definition.eAllContents();
// We must collect the nodes where we are discarding derived state and only discard them after the iteration,
// because modifying the containment hierarchy during iteration causes the TreeIterator to fail with
// IndexOutOfBoundsException.
while (treeIterator.hasNext()) {
var child = treeIterator.next();
var containingFeature = child.eContainingFeature();
if (containingFeature == ProblemPackage.Literals.EXISTENTIAL_QUANTIFIER__IMPLICIT_VARIABLES ||
containingFeature == ProblemPackage.Literals.VARIABLE_OR_NODE_EXPR__SINGLETON_VARIABLE) {
treeIterator.prune();
} else if (child instanceof ExistentialQuantifier existentialQuantifier &&
!existentialQuantifier.getImplicitVariables().isEmpty()) {
existentialQuantifiers.add(existentialQuantifier);
} else if (child instanceof VariableOrNodeExpr variableOrNodeExpr &&
variableOrNodeExpr.getSingletonVariable() != null) {
variableOrNodeExprs.add(variableOrNodeExpr);
}
}
for (var existentialQuantifier : existentialQuantifiers) {
existentialQuantifier.getImplicitVariables().clear();
}
for (var variableOrNodeExpr : variableOrNodeExprs) {
variableOrNodeExpr.setSingletonVariable(null);
}
}
}