Select Git revision
prob_handbook.adoc
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
SpecAnalyser.java 9.75 KiB
package de.tla2b.analysis;
import de.tla2b.config.ConfigfileEvaluator;
import de.tla2b.exceptions.ConfigFileErrorException;
import de.tla2b.exceptions.NotImplementedException;
import de.tla2b.exceptions.SemanticErrorException;
import de.tla2b.global.BBuiltInOPs;
import de.tla2b.translation.BDefinitionsFinder;
import de.tla2b.translation.OperationsFinder;
import de.tla2b.translation.UsedDefinitionsFinder;
import de.tla2b.util.DebugUtils;
import de.tla2b.util.TlaUtils;
import tla2sany.semantic.*;
import tlc2.tool.BuiltInOPs;
import java.util.*;
import java.util.stream.Stream;
public class SpecAnalyser extends BuiltInOPs {
private OpDefNode spec;
private OpDefNode init;
private OpDefNode next;
private List<OpDefNode> invariants = new ArrayList<>();
private List<OpDeclNode> bConstants = new ArrayList<>();
// used to check if a b constant has arguments and is not overridden in the configfile
private OpDefNode expressionOpdefNode;
private final Map<String, SymbolNode> namingMap = new HashMap<>();
private final ModuleNode moduleNode;
private ExprNode nextExpr;
private List<BOperation> bOperations = new ArrayList<>();
private final List<ExprNode> inits = new ArrayList<>();
private Set<OpDefNode> bDefinitionsSet = new HashSet<>();
// set of OpDefNodes which will appear in the resulting B Machine
private Set<OpDefNode> usedDefinitions = new HashSet<>();
// definitions which are used for the type inference algorithm
private final Map<OpDefNode, FormalParamNode[]> letParams = new HashMap<>();
// additional parameters of a let Operator, these parameters are from the surrounding operator
private final List<String> definitionMacros = new ArrayList<>();
private final List<OpDefNode> recursiveFunctions = new ArrayList<>();
private final List<RecursiveDefinition> recursiveDefinitions = new ArrayList<>();
private ConfigfileEvaluator configFileEvaluator;
private SpecAnalyser(ModuleNode m) {
this.moduleNode = m;
}
public static SpecAnalyser createSpecAnalyser(ModuleNode m, ConfigfileEvaluator conEval) {
SpecAnalyser specAnalyser = new SpecAnalyser(m);
specAnalyser.spec = conEval.getSpecNode();
specAnalyser.init = conEval.getInitNode();
specAnalyser.next = conEval.getNextNode();
specAnalyser.invariants = conEval.getInvariants();
specAnalyser.bConstants = conEval.getbConstantList();
specAnalyser.configFileEvaluator = conEval;
return specAnalyser;
}
public static SpecAnalyser createSpecAnalyserForTlaExpression(ModuleNode m) {
SpecAnalyser specAnalyser = new SpecAnalyser(m);
specAnalyser.expressionOpdefNode = m.getOpDefs()[m.getOpDefs().length - 1];
specAnalyser.usedDefinitions.add(specAnalyser.expressionOpdefNode);
specAnalyser.bDefinitionsSet.add(specAnalyser.expressionOpdefNode);
return specAnalyser;
}
public static SpecAnalyser createSpecAnalyser(ModuleNode m) {
Map<String, OpDefNode> definitions = TlaUtils.getOpDefsMap(m.getOpDefs());
SpecAnalyser specAnalyser = new SpecAnalyser(m);
specAnalyser.spec = detectClause(Stream.of("Spec", "SPECIFICATION", "SPEC"),
definitions, "INITIALISATION+OPERATIONS");
specAnalyser.init = detectClause(Stream.of("Init", "INIT", "Initialisation", "INITIALISATION"),
definitions, "INITIALISATION");
specAnalyser.next = detectClause(Stream.of("Next", "NEXT"), definitions, "OPERATIONS");
OpDefNode invariant = detectClause(Stream.of("Inv", "INVARIANTS", "INVARIANT", "INV", "Invariant",
"Invariants", "TypeInv", "TypeOK", "IndInv"),
definitions, "INVARIANTS");
if (invariant != null) {
specAnalyser.invariants.add(invariant);
} else {
DebugUtils.printMsg("No default Invariant detected");
}
// TODO are constant in the right order
specAnalyser.bConstants.addAll(Arrays.asList(m.getConstantDecls()));
return specAnalyser;
}
private static OpDefNode detectClause(Stream<String> keywords, Map<String, OpDefNode> definitions, String bClause) {
return keywords.filter(definitions::containsKey)
.findFirst()
.map(keyword -> {
DebugUtils.printMsg("Detected TLA+ Default Definition " + keyword + " for Clause: " + bClause);
return definitions.get(keyword);
})
.orElse(null);
}
public void start() throws SemanticErrorException, ConfigFileErrorException, NotImplementedException {
evalSpec();
for (OpDefNode inv : new ArrayList<>(invariants)) {
try {
OpApplNode opApplNode = (OpApplNode) inv.getBody();
OpDefNode opDefNode = (OpDefNode) opApplNode.getOperator();
if (opDefNode.getKind() == UserDefinedOpKind && !BBuiltInOPs.contains(opDefNode.getName())) {
int i = invariants.indexOf(inv);
invariants.set(i, opDefNode);
DebugUtils.printDebugMsg("Adding invariant " + i);
}
} catch (ClassCastException e) {
}
}
DebugUtils.printDebugMsg("Detecting OPERATIONS from disjunctions");
bOperations = new OperationsFinder(this).getBOperations();
DebugUtils.printDebugMsg("Finding used definitions");
this.usedDefinitions = new UsedDefinitionsFinder(this).getUsedDefinitions();
this.bDefinitionsSet = new BDefinitionsFinder(this).getBDefinitionsSet();
// usedDefinitions.addAll(bDefinitionsSet);
DebugUtils.printDebugMsg("Computing variable declarations");
// test whether there is an init predicate if there is a variable
if (moduleNode.getVariableDecls().length > 0 && inits == null) {
throw new SemanticErrorException("No initial predicate is defined.");
}
// check if there is B constant with arguments.
for (OpDeclNode con : bConstants) {
if (con.getArity() > 0) {
throw new ConfigFileErrorException(
String.format("Constant '%s' must be overridden in the configuration file.", con.getName()));
}
}
findRecursiveConstructs();
for (OpDeclNode var : moduleNode.getVariableDecls()) {
namingMap.put(var.getName().toString(), var);
}
DebugUtils.printMsg("Number of variables detected: " + moduleNode.getVariableDecls().length);
namingMap.putAll(TlaUtils.getConstantsMap(moduleNode.getConstantDecls()));
DebugUtils.printMsg("Number of constants detected: " + moduleNode.getConstantDecls().length);
namingMap.putAll(TlaUtils.getOpDefsMap(usedDefinitions.toArray(new OpDefNode[0])));
}
private void evalSpec() throws SemanticErrorException {
if (spec != null) {
DebugUtils.printMsg("Using TLA+ Spec to determine B INITIALISATION and OPERATIONS");
processConfigSpec(spec.getBody());
} else {
if (init != null) {
DebugUtils.printMsg("Using TLA+ Init definition to determine B INITIALISATION");
inits.add(init.getBody());
}
if (next != null) {
DebugUtils.printMsg("Using TLA+ Next definition to determine B OPERATIONS");
nextExpr = next.getBody();
}
}
}
private void processConfigSpec(ExprNode exprNode) throws SemanticErrorException {
if (exprNode instanceof OpApplNode) {
OpApplNode opApp = (OpApplNode) exprNode;
ExprOrOpArgNode[] args = opApp.getArgs();
if (args.length == 0) {
SymbolNode opNode = opApp.getOperator();
if (opNode instanceof OpDefNode) {
OpDefNode def = (OpDefNode) opNode;
ExprNode body = def.getBody();
body.levelCheck(1);
if (body.getLevel() == 1) {
inits.add(exprNode);
} else {
processConfigSpec(body);
}
return;
}
throw new SemanticErrorException("Can not handle specification conjunction.");
}
int opcode = BuiltInOPs.getOpCode(opApp.getOperator().getName());
if (opcode == OPCODE_cl || opcode == OPCODE_land) {
for (ExprOrOpArgNode arg : args) {
this.processConfigSpec((ExprNode) arg);
}
return;
}
if (opcode == OPCODE_box) {
SemanticNode boxArg = args[0];
if ((boxArg instanceof OpApplNode)
&& BuiltInOPs.getOpCode(((OpApplNode) boxArg).getOperator().getName()) == OPCODE_sa) {
this.nextExpr = (ExprNode) ((OpApplNode) boxArg).getArgs()[0];
return;
}
}
}
if (exprNode.getLevel() <= 1) {
// init
inits.add(exprNode);
} else if (exprNode.getLevel() == 3) {
// temporal
} else {
throw new SemanticErrorException("Can not handle specification conjunction.");
}
}
private void findRecursiveConstructs() throws NotImplementedException {
Set<OpDefNode> set = new HashSet<>(usedDefinitions);
for (OpDefNode def : set) {
if (def.getInRecursive()) {
throw new NotImplementedException("Recursive definitions are currently not supported: " + def.getName()
+ "\n" + def.getLocation());
// bDefinitionsSet.remove(def);
// RecursiveDefinition rd = new RecursiveDefinition(def);
// recursiveDefinitions.add(rd);
} else if (def.getBody() instanceof OpApplNode) {
OpApplNode o = (OpApplNode) def.getBody();
if (getOpCode(o.getOperator().getName()) == OPCODE_rfs) {// recursive Function
bDefinitionsSet.remove(def);
recursiveFunctions.add(def);
}
}
}
}
public List<BOperation> getBOperations() {
return this.bOperations;
}
public List<ExprNode> getInits() {
return this.inits;
}
public ExprNode getNext() {
return this.nextExpr;
}
public Set<OpDefNode> getBDefinitions() {
return bDefinitionsSet;
}
public Map<OpDefNode, FormalParamNode[]> getLetParams() {
return new HashMap<>(letParams);
}
public List<String> getDefinitionMacros() {
return definitionMacros;
}
public Set<OpDefNode> getUsedDefinitions() {
return usedDefinitions;
}
public List<OpDefNode> getRecursiveFunctions() {
return recursiveFunctions;
}
public List<RecursiveDefinition> getRecursiveDefinitions() {
return recursiveDefinitions;
}
public ModuleNode getModuleNode() {
return this.moduleNode;
}
public ConfigfileEvaluator getConfigFileEvaluator() {
return configFileEvaluator;
}
public List<OpDefNode> getInvariants() {
return invariants;
}
public OpDefNode getInitDef() {
return init;
}
public OpDefNode getExpressionOpdefNode() {
return expressionOpdefNode;
}
public SymbolNode getSymbolNodeByName(String name) {
return namingMap.get(name);
}
}