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); } }