All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.tla2bAst.BAstCreator Maven / Gradle / Ivy

The newest version!
package de.tla2bAst;

import de.be4.classicalb.core.parser.Definitions;
import de.be4.classicalb.core.parser.analysis.prolog.NodeFileNumbers;
import de.be4.classicalb.core.parser.node.*;
import de.hhu.stups.sablecc.patch.PositionedNode;
import de.hhu.stups.sablecc.patch.SourcePosition;
import de.tla2b.analysis.*;
import de.tla2b.analysis.PredicateVsExpression.DefinitionType;
import de.tla2b.analysis.UsedExternalFunctions.EXTERNAL_FUNCTIONS;
import de.tla2b.config.ConfigfileEvaluator;
import de.tla2b.config.ValueObj;
import de.tla2b.exceptions.NotImplementedException;
import de.tla2b.global.*;
import de.tla2b.translation.BMacroHandler;
import de.tla2b.translation.RecursiveFunctionHandler;
import de.tla2b.types.*;
import de.tla2b.util.DebugUtils;
import tla2sany.semantic.*;
import tla2sany.st.Location;
import tlc2.tool.BuiltInOPs;
import tlc2.value.ValueConstants;
import tlc2.value.impl.*;

import java.util.*;
import java.util.Map.Entry;
import java.util.stream.Collectors;

public class BAstCreator extends BuiltInOPs
	implements TranslationGlobals, ASTConstants, BBuildIns, ValueConstants {

	List machineClauseList;
	ConfigfileEvaluator conEval;
	final SpecAnalyser specAnalyser;
	private final PredicateVsExpression predicateVsExpression;
	private final BMacroHandler bMacroHandler;
	private final RecursiveFunctionHandler recursiveFunctionHandler;

	private List bConstants;

	private final ModuleNode moduleNode;
	private UsedExternalFunctions usedExternalFunctions;

	private final Definitions bDefinitions = new Definitions();

	private Start start;
	private final Map types = new HashMap<>();
	private final Set sourcePosition = new HashSet<>();
	private final NodeFileNumbers nodeFileNumbers = new NodeFileNumbers();
	private final List filesOrderedById = new ArrayList<>();
	private List toplevelUnchangedVariableNames = new ArrayList<>();

	public Start expressionStart;

	/**
	 * Creates a B AST node for a TLA expression
	 */
	public BAstCreator(ModuleNode moduleNode, SpecAnalyser specAnalyser) {
		this.moduleNode = moduleNode;
		this.specAnalyser = specAnalyser;
		this.bMacroHandler = new BMacroHandler();
		this.predicateVsExpression = new PredicateVsExpression(moduleNode);
		this.recursiveFunctionHandler = new RecursiveFunctionHandler(specAnalyser);

		ExprNode expr = moduleNode.getOpDefs()[moduleNode.getOpDefs().length - 1].getBody();
		if (expressionIsAPredicate(expr)) {
			APredicateParseUnit predicateParseUnit = new APredicateParseUnit();
			predicateParseUnit.setPredicate(visitExprNodePredicate(expr));
			expressionStart = new Start(predicateParseUnit, new EOF());
		} else {
			AExpressionParseUnit expressionParseUnit = new AExpressionParseUnit();
			expressionParseUnit.setExpression(visitExprNodeExpression(expr));
			expressionStart = new Start(expressionParseUnit, new EOF());
		}
	}

	private boolean expressionIsAPredicate(ExprNode expr) {
		if (expr.getKind() == OpApplKind) {
			OpApplNode opApplNode = (OpApplNode) expr;
			int kind = opApplNode.getOperator().getKind();
			if (kind == BuiltInKind) {
				int opcode = getOpCode(opApplNode.getOperator().getName());
				return OperatorTypes.tlaOperatorIsPredicate(opcode);
			} else if (kind == UserDefinedOpKind && BBuiltInOPs.contains(opApplNode.getOperator().getName())) {
				int opcode = BBuiltInOPs.getOpcode(opApplNode.getOperator().getName());
				return OperatorTypes.bbuiltInOperatorIsPredicate(opcode);
			}
		} else if (expr.getKind() == LetInKind) {
			LetInNode letInNode = (LetInNode) expr;
			return expressionIsAPredicate(letInNode.getBody());
		}
		return false;
	}

	public BAstCreator(ModuleNode moduleNode, ConfigfileEvaluator conEval, SpecAnalyser specAnalyser,
	                   UsedExternalFunctions usedExternalFunctions, PredicateVsExpression predicateVsExpression,
	                   BMacroHandler bMacroHandler, RecursiveFunctionHandler recursiveFunctionHandler) {
		this.predicateVsExpression = predicateVsExpression;
		this.bMacroHandler = bMacroHandler;
		this.recursiveFunctionHandler = recursiveFunctionHandler;

		this.conEval = conEval;
		this.moduleNode = moduleNode;
		this.specAnalyser = specAnalyser;
		this.usedExternalFunctions = usedExternalFunctions;

		if (conEval != null) {
			this.bConstants = conEval.getbConstantList();
		} else {
			this.bConstants = Arrays.asList(moduleNode.getConstantDecls());
		}

		machineClauseList = new ArrayList<>();

		AAbstractMachineParseUnit aAbstractMachineParseUnit = new AAbstractMachineParseUnit();
		aAbstractMachineParseUnit.setVariant(new AMachineMachineVariant());
		AMachineHeader machineHeader = new AMachineHeader();
		List headerName = createTIdentifierLiteral(getName(moduleNode));
		machineHeader.setName(headerName);
		aAbstractMachineParseUnit.setHeader(machineHeader);

		createSetsClause();
		createDefinitionClause();
		createAbstractConstantsClause();
		createConstantsClause();
		createPropertyClause();
		createVariableClause();
		createInvariantClause();
		createInitClause();
		createOperationsClause();

		aAbstractMachineParseUnit.setMachineClauses(machineClauseList);
		start = new Start(aAbstractMachineParseUnit, new EOF());

	}

	private void createSetsClause() {
		if (conEval == null || conEval.getEnumerationSet() == null || conEval.getEnumerationSet().isEmpty())
			return;
		ASetsMachineClause setsClause = new ASetsMachineClause();

		ArrayList printed = new ArrayList<>();
		OpDeclNode[] cons = moduleNode.getConstantDecls();
		for (OpDeclNode con : cons) {
			TLAType type = (TLAType) con.getToolObject(TYPE_ID);

			EnumType e;
			if (type instanceof SetType) {
				if (((SetType) type).getSubType() instanceof EnumType) {
					e = (EnumType) ((SetType) type).getSubType();
					if (!printed.contains(e)) {
						printed.add(e);
					}
				}
			} else if ((type instanceof EnumType)) {
				e = (EnumType) type;
				if (!printed.contains(e)) {
					printed.add(e);
				}
			}
		}

		ArrayList sets = new ArrayList<>();
		for (int i = 0; i < printed.size(); i++) {
			AEnumeratedSetSet eSet = new AEnumeratedSetSet();
			printed.get(i).id = i + 1;
			eSet.setIdentifier(createTIdentifierLiteral("ENUM" + (i + 1)));
			List list = new ArrayList<>();
			for (String s : printed.get(i).modelvalues) {
				list.add(createIdentifierNode(s));
			}
			eSet.setElements(list);
			sets.add(eSet);
		}
		setsClause.setSetDefinitions(sets);
		machineClauseList.add(setsClause);

	}

	private void createDefinitionClause() {
		ArrayList bDefs = new ArrayList<>();
		for (int i = 0; i < moduleNode.getOpDefs().length; i++) {
			OpDefNode def = moduleNode.getOpDefs()[i];
			if (specAnalyser.getBDefinitions().contains(def)) {
				if (conEval != null && conEval.getConstantOverrideTable().containsValue(def)) {
					DebugUtils.printVeryVerboseMsg("Not creating B DEFINITION (in Override Table) " + def.getName() + " " + def);
					continue;
				}
				if (def.getOriginallyDefinedInModuleNode().getName().toString().equals("MC")) {
					continue;
				}
				//debugUtils.printVeryVerboseMsg("Creating B DEFINITION " + def.getName() + " " + def);

				bDefs.add(def);
			} else {
				DebugUtils.printVeryVerboseMsg("Not creating unused B DEFINITION for " + def.getName() + " " + def);
			}

		}

		Set set = usedExternalFunctions.getUsedExternalFunctions();
		List defs = new ArrayList<>(createDefinitionsForExternalFunctions(set));

		for (OpDefNode opDefNode : bDefs) {
			List list = new ArrayList<>();
			for (int i = 0; i < opDefNode.getParams().length; i++) {
				FormalParamNode p = opDefNode.getParams()[i];
				list.add(createIdentifierNode(p));
			}
			// TLAType type = (TLAType) opDefNode.getToolObject(TYPE_ID);
			// if (opDefNode.level == 2 || type instanceof BoolType) {
			if (predicateVsExpression.getDefinitionType(opDefNode) == DefinitionType.PREDICATE) {
				APredicateDefinitionDefinition d = new APredicateDefinitionDefinition();
				d.setName(new TDefLiteralPredicate(getName(opDefNode)));
				d.setParameters(list);
				d.setRhs(visitExprNodePredicate(opDefNode.getBody()));
				defs.add(createPositionedNode(d,opDefNode));
			} else {
				AExpressionDefinitionDefinition d = new AExpressionDefinitionDefinition();
				d.setName(new TIdentifierLiteral(getName(opDefNode)));
				// System.out.println("Creating Expression DEFINITION " + getName(opDefNode));
				// TODO: these definitions have no position info in the definition_decl term nor at the top-level body

				d.setParameters(list);
				d.setRhs(visitExprNodeExpression(opDefNode.getBody()));
				defs.add(createPositionedNode(d,opDefNode));
			}

		}

		if (!defs.isEmpty()) {
			machineClauseList.add(new ADefinitionsMachineClause(defs));

			for (PDefinition def : defs) {
				if (def instanceof AExpressionDefinitionDefinition) {
					bDefinitions.addDefinition((AExpressionDefinitionDefinition) def, Definitions.Type.Expression);
				} else if (def instanceof APredicateDefinitionDefinition) {
					bDefinitions.addDefinition((APredicateDefinitionDefinition) def, Definitions.Type.Predicate);
				} else {
					bDefinitions.addDefinition((ASubstitutionDefinitionDefinition) def, Definitions.Type.Substitution);
				}
			}
		}
	}

	private List createDefinitionsForExternalFunctions(Set set) {
		List list = new ArrayList<>();
		if (set.contains(UsedExternalFunctions.EXTERNAL_FUNCTIONS.CHOOSE)) {
			list.add(new AExpressionDefinitionDefinition(
					new TIdentifierLiteral("CHOOSE"),
					createIdentifierList("X"),
					new AStringExpression(new TStringLiteral("a member of X"))
			));
			list.add(new AExpressionDefinitionDefinition(
					new TIdentifierLiteral("EXTERNAL_FUNCTION_CHOOSE"),
					createIdentifierList("T"),
					new ATotalFunctionExpression(
							new APowSubsetExpression(createIdentifierNode("T")),
							createIdentifierNode("T")
					)
			));
		}
		if (set.contains(UsedExternalFunctions.EXTERNAL_FUNCTIONS.ASSERT)) {
			list.add(new APredicateDefinitionDefinition(
					new TDefLiteralPredicate("ASSERT_TRUE"),
					Arrays.asList(createIdentifierNode("P"), createIdentifierNode("Msg")),
					new ATruthPredicate()
			));
			list.add(new AExpressionDefinitionDefinition(
					new TIdentifierLiteral("EXTERNAL_PREDICATE_ASSERT_TRUE"),
					new ArrayList<>(),
					new AMultOrCartExpression(new ABoolSetExpression(), new AStringSetExpression())
			));
		}
		return list;
	}

	private void createOperationsClause() {
		List bOperations = specAnalyser.getBOperations();
		if (bOperations == null || bOperations.isEmpty()) {
			return;
		}

		List opList = new ArrayList<>();
		for (BOperation op : bOperations) {
			opList.add(createPositionedNode(op.getBOperation(this), op.getNode()));
		}

		machineClauseList.add(new AOperationsMachineClause(opList));
	}

	private void createInitClause() {
		OpDeclNode[] vars = moduleNode.getVariableDecls();
		if (vars.length == 0)
			return;
		List varList = new ArrayList<>();
		for (OpDeclNode var : vars) {
			varList.add(createIdentifierNode(var));
		}

		List predList = new ArrayList<>();
		for (ExprNode node : specAnalyser.getInits()) {
			predList.add(visitExprNodePredicate(node));
		}
		if (predList.isEmpty()) {
			throw new IllegalStateException("Could not find a definition of Init.");
		}
		machineClauseList.add(new AInitialisationMachineClause(
				new ABecomesSuchSubstitution(varList,createConjunction(predList))
		));
	}

	private void createVariableClause() {
		List bVariables = Arrays.asList(moduleNode.getVariableDecls());
		if (!bVariables.isEmpty()) {
			List list = new ArrayList<>();
			for (OpDeclNode opDeclNode : bVariables) {
				AIdentifierExpression id = createPositionedNode(createIdentifierNode(getName(opDeclNode)), opDeclNode);
				list.add(id);
				types.put(id, (TLAType) opDeclNode.getToolObject(TYPE_ID));
			}
			machineClauseList.add(new AVariablesMachineClause(list));
		}
	}

	private void createAbstractConstantsClause() {
		List constantsList = new ArrayList<>();

		for (RecursiveDefinition recDef : specAnalyser.getRecursiveDefinitions()) {
			AIdentifierExpression id = createPositionedNode(createIdentifierNode(getName(recDef.getOpDefNode())),
				recDef.getOpDefNode());
			constantsList.add(id);
			types.put(id, (TLAType) recDef.getOpDefNode().getToolObject(TYPE_ID));
		}

		for (OpDefNode recFunc : specAnalyser.getRecursiveFunctions()) {
			AIdentifierExpression id = new AIdentifierExpression(createTIdentifierLiteral(getName(recFunc)));
			constantsList.add(id);
			types.put(id, (TLAType) recFunc.getToolObject(TYPE_ID));
		}

		if (!constantsList.isEmpty()) {
			machineClauseList.add(new AAbstractConstantsMachineClause(constantsList));
		}
	}

	private void createConstantsClause() {
		List bConstants;
		if (conEval != null) {
			bConstants = conEval.getbConstantList();
		} else {
			bConstants = Arrays.asList(moduleNode.getConstantDecls());
		}

		List constantsList = new ArrayList<>();
		for (OpDeclNode opDeclNode : bConstants) {
			AIdentifierExpression id = createPositionedNode(createIdentifierNode(getName(opDeclNode)), opDeclNode);
			constantsList.add(id);
			TLAType type = (TLAType) opDeclNode.getToolObject(TYPE_ID);
			types.put(id, type);
		}
		if (!constantsList.isEmpty()) {
			AConstantsMachineClause constantsClause = new AConstantsMachineClause(constantsList);
			machineClauseList.add(constantsClause);
		}
	}

	public AIdentifierExpression createIdentifierNode(SymbolNode symbolNode) {
		if (bMacroHandler.containsSymbolNode(symbolNode)) {
			return createPositionedNode(createIdentifierNode(bMacroHandler.getNewName(symbolNode)), symbolNode);
		} else {
			return createPositionedNode(createIdentifierNode(symbolNode.getName().toString()), symbolNode);
		}
	}

	public static AIdentifierExpression createIdentifierNode(String name) {
		return new AIdentifierExpression(createTIdentifierLiteral(name));
	}

	private void createPropertyClause() {
		List propertiesList = new ArrayList<>();
		propertiesList.addAll(evalRecursiveDefinitions());
		propertiesList.addAll(evalRecursiveFunctions());
		for (OpDeclNode con : bConstants) {
			if (conEval != null && conEval.getConstantAssignments().containsKey(con) && bConstants.contains(con)) {
				ValueObj v = conEval.getConstantAssignments().get(con);
				TLAType t = v.getType();
				boolean isEnum = false;
				if (t instanceof SetType) {
					TLAType sub = ((SetType) t).getSubType();
					if (sub instanceof EnumType) {
						EnumType en = (EnumType) sub;
						SetEnumValue set = (SetEnumValue) v.getValue();
						if (set.elems.size() == en.modelvalues.size()) {
							isEnum = true;
						}
					}
				}
				AEqualPredicate equal = new AEqualPredicate();
				if (isEnum) {
					equal.setLeft(createIdentifierNode(con));
					equal.setRight(createIdentifierNode(((SetType) t).getSubType().toString()));
				} else {
					equal.setLeft(createIdentifierNode(con));
					Value tlcValue = v.getValue();
					equal.setRight(createTLCValue(tlcValue));
				}
				propertiesList.add(equal);
			} else {
				AMemberPredicate member = new AMemberPredicate();
				member.setLeft(createIdentifierNode(con));
				TLAType t = (TLAType) con.getToolObject(TYPE_ID);
				member.setRight(t.getBNode());
				propertiesList.add(member);
			}
		}

		if (conEval != null) {
			for (Entry entry : conEval.getConstantOverrideTable().entrySet()) {
				OpDeclNode con = entry.getKey();
				OpDefNode generatedDef = entry.getValue();
				OpDefNode def;
				try {
					OpApplNode opApplNode = (OpApplNode) generatedDef.getBody();
					if (opApplNode.getKind() == UserDefinedOpKind) {
						def = (OpDefNode) opApplNode.getOperator();
					} else {
						def = generatedDef;
					}
				} catch (ClassCastException e) {
					def = generatedDef;
				}

				AEqualPredicate equal = new AEqualPredicate();
				equal.setLeft(createIdentifierNode(con));
				equal.setRight(visitExprNodeExpression(def.getBody()));
				propertiesList.add(equal);
			}
		}

		AssumeNode[] assumes = moduleNode.getAssumptions();
		List assertionList = new ArrayList<>();
		for (AssumeNode assumeNode : assumes) {
			ThmOrAssumpDefNode def = assumeNode.getDef();
			if (def != null) {
				ALabelPredicate aLabelPredicate = new ALabelPredicate(new TPragmaIdOrString(def.getName().toString()),
					createPositionedNode(visitAssumeNode(assumeNode), assumeNode));
				assertionList.add(aLabelPredicate);
			} else {
				propertiesList.add(visitAssumeNode(assumeNode));
			}
		}
		if (!propertiesList.isEmpty()) {
			APropertiesMachineClause propertiesClause = new APropertiesMachineClause();
			propertiesClause.setPredicates(createConjunction(propertiesList));
			machineClauseList.add(propertiesClause);
		}
		if (!assertionList.isEmpty()) {
			AAssertionsMachineClause assertionClause = new AAssertionsMachineClause();
			assertionClause.setPredicates(assertionList);
			machineClauseList.add(assertionClause);
		}

	}

	private List evalRecursiveFunctions() {
		List propertiesList = new ArrayList<>();
		for (OpDefNode def : specAnalyser.getRecursiveFunctions()) {
			AEqualPredicate equals = new AEqualPredicate(createIdentifierNode(def),
				visitExprNodeExpression(def.getBody()));
			propertiesList.add(equals);
		}
		return propertiesList;
	}

	private List evalRecursiveDefinitions() {
		List propertiesList = new ArrayList<>();

		for (RecursiveDefinition recDef : specAnalyser.getRecursiveDefinitions()) {
			OpDefNode def = recDef.getOpDefNode();
			// TLAType t = (TLAType) def.getToolObject(TYPE_ID);
			// OpApplNode ifThenElse = recDef.getIfThenElse();
			FormalParamNode[] params = def.getParams();
			ArrayList paramList1 = new ArrayList<>();
			ArrayList typeList = new ArrayList<>();
			// ArrayList paramList2 = new ArrayList();
			for (FormalParamNode p : params) {
				paramList1.add(createIdentifierNode(p));
				// paramList2.add(createIdentifierNode(p.getName().toString()));
				TLAType paramType = (TLAType) p.getToolObject(TYPE_ID);
				AEqualPredicate equal = new AEqualPredicate(createIdentifierNode(p), paramType.getBNode());
				typeList.add(equal);
			}
			ALambdaExpression lambda1 = new ALambdaExpression();
			lambda1.setIdentifiers(paramList1);
			lambda1.setPredicate(createConjunction(typeList));
			lambda1.setExpression(visitExprNodeExpression(def.getBody()));
			// lambda1.setPredicate(visitExprOrOpArgNodePredicate(ifThenElse.getArgs()[0]));
			// lambda1.setExpression(visitExprOrOpArgNodeExpression(ifThenElse.getArgs()[1]));

			// ALambdaExpression lambda2 = new ALambdaExpression();
			// lambda2.setIdentifiers(paramList2);
			// ANegationPredicate not = new
			// ANegationPredicate(visitExprOrOpArgNodePredicate(ifThenElse.getArgs()[0]));
			// lambda2.setPredicate(not);
			// lambda2.setExpression(visitExprOrOpArgNodeExpression(ifThenElse.getArgs()[2]));
			// AUnionExpression union = new AUnionExpression(lambda1, lambda2);

			AEqualPredicate equals = new AEqualPredicate(createIdentifierNode(def), lambda1);
			propertiesList.add(equals);
		}

		return propertiesList;
	}

	private PExpression createTLCValue(Value tlcValue) {
		switch (tlcValue.getKind()) {
			case INTVALUE:
				return new AIntegerExpression(new TIntegerLiteral(tlcValue.toString()));
			case SETENUMVALUE: {
				SetEnumValue s = (SetEnumValue) tlcValue;
				ArrayList list = new ArrayList<>();
				for (int i = 0; i < s.elems.size(); i++) {
					Value v = s.elems.elementAt(i);
					list.add(createTLCValue(v));
				}
				return new ASetExtensionExpression(list);
			}
			case MODELVALUE: {
				ModelValue m = (ModelValue) tlcValue;
				return createIdentifierNode(m.toString());
			}
			case STRINGVALUE: {
				StringValue stringValue = (StringValue) tlcValue;
				return new AStringExpression(new TStringLiteral(stringValue.getVal().toString()));
			}
			default:
				throw new NotImplementedException("TLC value in configuration file: " + tlcValue.getClass());
		}
	}

	private void createInvariantClause() {
		OpDeclNode[] vars = moduleNode.getVariableDecls();

		List predList = new ArrayList<>();
		for (OpDeclNode var : vars) {
			TLAType varType = (TLAType) var.getToolObject(TYPE_ID);

			AMemberPredicate member = new AMemberPredicate();
			member.setLeft(createIdentifierNode(var));
			member.setRight(varType.getBNode());

			predList.add(member);
		}

		for (OpDefNode def : specAnalyser.getInvariants()) {
			if (def.getOriginallyDefinedInModuleNode().getName().toString().equals("MC")) {
				predList.add(visitExprNodePredicate(def.getBody()));
			} else {
				if (predicateVsExpression.getDefinitionType(def) == DefinitionType.PREDICATE) {
					ADefinitionPredicate defPred = new ADefinitionPredicate();
					defPred.setDefLiteral(new TDefLiteralPredicate(getName(def)));
					predList.add(defPred);
				} else {
					ADefinitionExpression defExpr = new ADefinitionExpression();
					defExpr.setDefLiteral(new TIdentifierLiteral(getName(def)));
					predList.add(new AEqualPredicate(defExpr, new ABooleanTrueExpression()));
				}

			}
		}

		if (!predList.isEmpty()) {
			AInvariantMachineClause invClause = new AInvariantMachineClause(createConjunction(predList));
			machineClauseList.add(invClause);
		}

	}

	private PPredicate visitAssumeNode(AssumeNode assumeNode) {
		return visitExprNodePredicate(assumeNode.getAssume());
	}

	public PPredicate visitExprNodePredicate(ExprNode exprNode) {
		switch (exprNode.getKind()) {
			case OpApplKind:
				return visitOpApplNodePredicate((OpApplNode) exprNode);
			case LetInKind: {
				LetInNode letInNode = (LetInNode) exprNode;
				return visitExprNodePredicate(letInNode.getBody());
			}
			case NumeralKind:
			case DecimalKind: {
				throw new RuntimeException();
			}
			default:
				throw new NotImplementedException(exprNode.getClass().toString());
		}

	}

	private PExpression visitExprNodeExpression(ExprNode exprNode) {
		switch (exprNode.getKind()) {
			case OpApplKind:
				return visitOpApplNodeExpression((OpApplNode) exprNode);
			case NumeralKind: {
				String number = String.valueOf(((NumeralNode) exprNode).val());
				return createPositionedNode(new AIntegerExpression(new TIntegerLiteral(number)), exprNode);
			}
			case DecimalKind: {
				return createPositionedNode(new ARealExpression(new TRealLiteral(exprNode.toString())), exprNode);
			}
			case StringKind: {
				StringNode s = (StringNode) exprNode;
				return createPositionedNode(new AStringExpression(new TStringLiteral(s.getRep().toString())), exprNode);
			}
			case AtNodeKind: { // @
				AtNode at = (AtNode) exprNode;
				TLAType type = (TLAType) at.getExceptRef().getToolObject(TYPE_ID);
				OpApplNode seq = at.getAtModifier();
				LinkedList list = new LinkedList<>(Arrays.asList(seq.getArgs()));
				// PExpression base = visitExprNodeExpression(at.getAtBase());
				PExpression base = (PExpression) at.getExceptComponentRef().getToolObject(EXCEPT_BASE);
				return evalAtNode(list, type, base.clone());
			}
			case LetInKind: {
				LetInNode letInNode = (LetInNode) exprNode;
				return visitExprNodeExpression(letInNode.getBody());
			}
			default:
				throw new NotImplementedException(exprNode.getClass().toString());
		}

	}

	private PExpression evalAtNode(LinkedList list, TLAType type, PExpression base) {
		if (list.isEmpty()) {
			return base;
		}
		if (type instanceof FunctionType) {
			FunctionType funcType = (FunctionType) type;
			PExpression param = visitExprOrOpArgNodeExpression(list.poll());
			List params = new ArrayList<>();
			params.add(param);
			AFunctionExpression funCall = new AFunctionExpression();
			funCall.setIdentifier(base);
			funCall.setParameters(params);
			return evalAtNode(list, funcType.getRange(), funCall);
		} else {
			StructType structType = (StructType) type;
			ARecordFieldExpression select = new ARecordFieldExpression();
			select.setRecord(base);
			StringNode stringNode = (StringNode) list.poll();
			// TODO rename field name
			String fieldName = stringNode.getRep().toString();
			select.setIdentifier(new TIdentifierLiteral(fieldName));
			return evalAtNode(list, structType.getType(fieldName), select);
		}
	}

	private PPredicate visitOpApplNodePredicate(OpApplNode opApplNode) {
		switch (opApplNode.getOperator().getKind()) {
			case VariableDeclKind:
			case ConstantDeclKind:
			case FormalParamKind: {
				return createPositionedNode(
					new AEqualPredicate(createIdentifierNode(opApplNode.getOperator()), new ABooleanTrueExpression()),
					opApplNode);
			}
			case BuiltInKind:
				return visitBuiltInKindPredicate(opApplNode);

			case UserDefinedOpKind: {
				return visitUserdefinedOpPredicate(opApplNode);
			}
			default:
				throw new NotImplementedException(opApplNode.getClass().toString());
		}

	}

	private PExpression visitOpApplNodeExpression(OpApplNode n) {
		switch (n.getOperator().getKind()) {
			case ConstantDeclKind:
			case VariableDeclKind: {
				return createIdentifierNode(n.getOperator());
			}
			case FormalParamKind: {
				FormalParamNode param = (FormalParamNode) n.getOperator();
				ExprOrOpArgNode e = (ExprOrOpArgNode) param.getToolObject(SUBSTITUTE_PARAM);
				if (e != null) {
					return visitExprOrOpArgNodeExpression(e);
				}

				if (recursiveFunctionHandler.isRecursiveFunction(param)) {
					ArrayList list = recursiveFunctionHandler.getAdditionalParams(param);
					if (!list.isEmpty()) {
						AFunctionExpression call = new AFunctionExpression();
						call.setIdentifier(createIdentifierNode(param));
						ArrayList params = new ArrayList<>();
						for (SymbolNode symbolNode : list) {
							params.add(createIdentifierNode(symbolNode));
						}
						call.setParameters(params);
						return call;
					}
				}
				FormalParamNode[] tuple = (FormalParamNode[]) param.getToolObject(TUPLE);
				if (tuple != null) {
					if (tuple.length == 1) {
						AFunctionExpression functionCall = new AFunctionExpression();
						functionCall.setIdentifier(createIdentifierNode(n.getOperator()));
						List paramList = new ArrayList<>();
						paramList.add(new AIntegerExpression(new TIntegerLiteral("1")));
						functionCall.setParameters(paramList);
						return functionCall;
					} else {

						StringBuilder sb = new StringBuilder();
						List typeList = new ArrayList<>();
						int number = -1;
						for (int j = 0; j < tuple.length; j++) {
							FormalParamNode p = tuple[j];
							sb.append(p.getName().toString());
							TLAType type = (TLAType) p.getToolObject(TYPE_ID);
							typeList.add(type);
							if (p == param) {
								number = j + 1;
							}
						}
						TupleType tupleType = new TupleType(typeList);
						PExpression id = createIdentifierNode(sb.toString());
						return createProjectionFunction(id, number, tupleType);
					}
				}
				return createIdentifierNode(n.getOperator());
			}
			case BuiltInKind:
				return visitBuiltInKindExpression(n);

			case UserDefinedOpKind: {
				return visitUserdefinedOpExpression(n);
			}
			default:
				throw new NotImplementedException(n.getOperator().getName().toString());
		}
	}

	private PPredicate visitUserdefinedOpPredicate(OpApplNode n) {
		OpDefNode def = (OpDefNode) n.getOperator();
		if (BBuiltInOPs.contains(def.getName()) // Operator is a B built-in
			// operator
			&& STANDARD_MODULES.contains(def.getSource().getOriginallyDefinedInModuleNode().getName().toString())) {
			return visitBBuiltInsPredicate(n);
		}
		if (specAnalyser.getRecursiveFunctions().contains(def)) {
			return new AEqualPredicate(createIdentifierNode(def), new ABooleanTrueExpression());
		}
		if (Arrays.asList(moduleNode.getOpDefs()).contains(def)) {
			List params = new ArrayList<>();
			for (int i = 0; i < n.getArgs().length; i++) {
				params.add(visitExprOrOpArgNodeExpression(n.getArgs()[i]));
			}
			if (predicateVsExpression.getDefinitionType(def) == DefinitionType.EXPRESSION) {
				ADefinitionExpression defCall = new ADefinitionExpression();
				defCall.setDefLiteral(new TIdentifierLiteral(getName(def)));
				defCall.setParameters(params);
				return new AEqualPredicate(defCall, new ABooleanTrueExpression());
			} else {
				ADefinitionPredicate defCall = new ADefinitionPredicate();
				defCall.setDefLiteral(new TDefLiteralPredicate(getName(def)));
				defCall.setParameters(params);
				return defCall;
			}
		} else {
			FormalParamNode[] params = def.getParams();
			for (int i = 0; i < params.length; i++) {
				FormalParamNode param = params[i];
				param.setToolObject(SUBSTITUTE_PARAM, n.getArgs()[i]);
			}
			return visitExprNodePredicate(def.getBody());
		}
	}

	private String getName(SymbolNode def) {
		return def.getName().toString();
	}

	private PExpression visitUserdefinedOpExpression(OpApplNode n) {
		OpDefNode def = (OpDefNode) n.getOperator();
		// Operator is a B built-in operator
		if (BBuiltInOPs.contains(def.getName())
			&& STANDARD_MODULES.contains(def.getSource().getOriginallyDefinedInModuleNode().getName().toString())) {
			return visitBBuiltInsExpression(n);
		}

		if (specAnalyser.getRecursiveFunctions().contains(def)) {
			ArrayList list = recursiveFunctionHandler.getAdditionalParams(def);
			if (!list.isEmpty()) {
				AFunctionExpression call = new AFunctionExpression();
				call.setIdentifier(createIdentifierNode(def));
				ArrayList params = new ArrayList<>();
				for (SymbolNode symbolNode : list) {
					params.add(createIdentifierNode(symbolNode));
				}
				call.setParameters(params);
				return call;
			} else {
				return createIdentifierNode(def);
			}
		}

		if (Arrays.asList(moduleNode.getOpDefs()).contains(def)) {
			List params = new ArrayList<>();
			for (int i = 0; i < n.getArgs().length; i++) {
				params.add(visitExprOrOpArgNodeExpression(n.getArgs()[i]));
			}

			if (conEval != null && conEval.getConstantOverrideTable().containsValue(def)) {
				// used constants name instead of the definition overriding the
				// constant
				Iterator> iter = conEval.getConstantOverrideTable().entrySet().iterator();
				String name = null;
				while (iter.hasNext()) {
					Entry entry = iter.next();
					if (entry.getValue().equals(def)) {
						name = getName(entry.getKey());
					}
				}
				if (params.isEmpty()) {
					return createIdentifierNode(name);
				} else {
					AFunctionExpression funcCall = new AFunctionExpression();
					funcCall.setIdentifier(createIdentifierNode(name));
					funcCall.setParameters(params);
					return funcCall;
				}
			} else {
				if (predicateVsExpression.getDefinitionType(def) == DefinitionType.PREDICATE) {
					ADefinitionPredicate defPred = new ADefinitionPredicate();
					defPred.setDefLiteral(new TDefLiteralPredicate(getName(n.getOperator())));
					defPred.setParameters(params);
					return new AConvertBoolExpression(defPred);
				} else {
					ADefinitionExpression defExpr = new ADefinitionExpression();
					defExpr.setDefLiteral(new TIdentifierLiteral(getName(n.getOperator())));
					defExpr.setParameters(params);
					return defExpr;
				}
			}

		} else {
			FormalParamNode[] params = def.getParams();
			for (int i = 0; i < params.length; i++) {
				FormalParamNode param = params[i];
				param.setToolObject(SUBSTITUTE_PARAM, n.getArgs()[i]);
			}
			return visitExprNodeExpression(def.getBody());
		}

	}

	private PPredicate visitBBuiltInsPredicate(OpApplNode opApplNode) {
		PPredicate returnNode = null;
		switch (BBuiltInOPs.getOpcode(opApplNode.getOperator().getName())) {
			case B_OPCODE_lt: // <
				returnNode = new ALessPredicate(visitExprOrOpArgNodeExpression(opApplNode.getArgs()[0]),
					visitExprOrOpArgNodeExpression(opApplNode.getArgs()[1]));
				break;

			case B_OPCODE_gt: // >
				returnNode = new AGreaterPredicate(visitExprOrOpArgNodeExpression(opApplNode.getArgs()[0]),
					visitExprOrOpArgNodeExpression(opApplNode.getArgs()[1]));
				break;

			case B_OPCODE_leq: // <=
				returnNode = new ALessEqualPredicate(visitExprOrOpArgNodeExpression(opApplNode.getArgs()[0]),
					visitExprOrOpArgNodeExpression(opApplNode.getArgs()[1]));
				break;

			case B_OPCODE_geq: // >=
				returnNode = new AGreaterEqualPredicate(visitExprOrOpArgNodeExpression(opApplNode.getArgs()[0]),
					visitExprOrOpArgNodeExpression(opApplNode.getArgs()[1]));
				break;

			case B_OPCODE_finite: // IsFiniteSet({1,2,3})
			{
				AMemberPredicate member = new AMemberPredicate();
				member.setLeft(visitExprOrOpArgNodeExpression(opApplNode.getArgs()[0]));
				member.setRight(new AFinSubsetExpression(visitExprOrOpArgNodeExpression(opApplNode.getArgs()[0])));
				returnNode = member;
				break;
			}
			case B_OPCODE_true: // TRUE
				returnNode = new AEqualPredicate(new ABooleanTrueExpression(), new ABooleanTrueExpression());
				break;

			case B_OPCODE_false: // FALSE
				returnNode = new AEqualPredicate(new ABooleanFalseExpression(), new ABooleanTrueExpression());
				break;

			case B_OPCODE_assert: {
				ADefinitionPredicate pred = new ADefinitionPredicate();
				pred.setDefLiteral(new TDefLiteralPredicate("ASSERT_TRUE"));
				ArrayList list = new ArrayList<>();
				list.add(visitExprOrOpArgNodeExpression(opApplNode.getArgs()[0]));
				if (opApplNode.getArgs()[1] instanceof StringNode) {
					StringNode stringNode = (StringNode) opApplNode.getArgs()[1];
					list.add(new AStringExpression(new TStringLiteral(stringNode.getRep().toString())));
				} else {
					list.add(new AStringExpression(new TStringLiteral("Error")));
				}
				pred.setParameters(list);
				returnNode = pred;
				break;
			}
		}
		if (returnNode != null) {
			return createPositionedNode(returnNode, opApplNode);
		} else {
			throw new RuntimeException("Unexpected operator: " + opApplNode.getOperator().getName().toString() + "\n"
				+ opApplNode.stn.getLocation());
		}
	}

	private PExpression visitBBuiltInsExpression(OpApplNode opApplNode) {
		PExpression returnNode = null;
		switch (BBuiltInOPs.getOpcode(opApplNode.getOperator().getName())) {
			case B_OPCODE_bool: // BOOLEAN
				returnNode = new ABoolSetExpression();
				break;
			case B_OPCODE_true: // TRUE
				returnNode = new ABooleanTrueExpression();
				break;
			case B_OPCODE_false: // FALSE
				returnNode = new ABooleanFalseExpression();
				break;

			/*
			 * Standard Module Naturals
			 */
			case B_OPCODE_nat: // Nat
				returnNode = new ANaturalSetExpression();
				break;

			case B_OPCODE_plus: // +
				returnNode = new AAddExpression(visitExprOrOpArgNodeExpression(opApplNode.getArgs()[0]),
					visitExprOrOpArgNodeExpression(opApplNode.getArgs()[1]));
				break;

			case B_OPCODE_minus: // -
				returnNode = new AMinusOrSetSubtractExpression(visitExprOrOpArgNodeExpression(opApplNode.getArgs()[0]),
					visitExprOrOpArgNodeExpression(opApplNode.getArgs()[1]));
				break;

			case B_OPCODE_times: // *
				returnNode = new AMultOrCartExpression(visitExprOrOpArgNodeExpression(opApplNode.getArgs()[0]),
					visitExprOrOpArgNodeExpression(opApplNode.getArgs()[1]));
				break;

			case B_OPCODE_exp: // x^y
				returnNode = new APowerOfExpression(visitExprOrOpArgNodeExpression(opApplNode.getArgs()[0]),
					visitExprOrOpArgNodeExpression(opApplNode.getArgs()[1]));
				break;

			case B_OPCODE_lt: // <
				returnNode = new AConvertBoolExpression(
					new ALessPredicate(visitExprOrOpArgNodeExpression(opApplNode.getArgs()[0]),
						visitExprOrOpArgNodeExpression(opApplNode.getArgs()[1])));
				break;

			case B_OPCODE_gt: // >
				returnNode = new AConvertBoolExpression(
					new AGreaterPredicate(visitExprOrOpArgNodeExpression(opApplNode.getArgs()[0]),
						visitExprOrOpArgNodeExpression(opApplNode.getArgs()[1])));
				break;
			case B_OPCODE_leq: // <=
				returnNode = new AConvertBoolExpression(
					new ALessEqualPredicate(visitExprOrOpArgNodeExpression(opApplNode.getArgs()[0]),
						visitExprOrOpArgNodeExpression(opApplNode.getArgs()[1])));
				break;

			case B_OPCODE_geq: // >=
				returnNode = new AConvertBoolExpression(
					new AGreaterEqualPredicate(visitExprOrOpArgNodeExpression(opApplNode.getArgs()[0]),
						visitExprOrOpArgNodeExpression(opApplNode.getArgs()[1])));
				break;

			case B_OPCODE_mod: // modulo a % b = a - b* (a/b)
			{
				PExpression a = visitExprOrOpArgNodeExpression(opApplNode.getArgs()[0]);
				PExpression b = visitExprOrOpArgNodeExpression(opApplNode.getArgs()[1]);
				PExpression a2 = visitExprOrOpArgNodeExpression(opApplNode.getArgs()[0]);
				PExpression b2 = visitExprOrOpArgNodeExpression(opApplNode.getArgs()[1]);

				AFlooredDivExpression div = new AFlooredDivExpression(a, b);
				AMultOrCartExpression mult = new AMultOrCartExpression(b2, div);
				returnNode = new AMinusOrSetSubtractExpression(a2, mult);
				break;
			}

			case B_OPCODE_div: // \div
				AFlooredDivExpression aFlooredDivExpression = new AFlooredDivExpression(
					visitExprOrOpArgNodeExpression(opApplNode.getArgs()[0]),
					visitExprOrOpArgNodeExpression(opApplNode.getArgs()[1]));

				setPosition(aFlooredDivExpression, opApplNode);
				returnNode = aFlooredDivExpression;
				break;

			case B_OPCODE_realdiv: // /
				ADivExpression aDivExpression = new ADivExpression(
					visitExprOrOpArgNodeExpression(opApplNode.getArgs()[0]),
					visitExprOrOpArgNodeExpression(opApplNode.getArgs()[1]));

				setPosition(aDivExpression, opApplNode);
				returnNode = aDivExpression;
				break;

			case B_OPCODE_dotdot: // ..
				returnNode = new AIntervalExpression(visitExprOrOpArgNodeExpression(opApplNode.getArgs()[0]),
					visitExprOrOpArgNodeExpression(opApplNode.getArgs()[1]));
				break;

			case B_OPCODE_int: // Int
				returnNode = new AIntegerSetExpression();
				break;

			case B_OPCODE_real: // Real
				returnNode = new ARealSetExpression();
				break;

			case B_OPCODE_uminus: // -x
				returnNode = new AUnaryMinusExpression(visitExprOrOpArgNodeExpression(opApplNode.getArgs()[0]));
				break;

			case B_OPCODE_card: // Cardinality
				returnNode = new ACardExpression(visitExprOrOpArgNodeExpression(opApplNode.getArgs()[0]));
				break;

			case B_OPCODE_finite: // IsFiniteSet({1,2,3})
			{
				AMemberPredicate member = new AMemberPredicate();
				member.setLeft(visitExprOrOpArgNodeExpression(opApplNode.getArgs()[0]));
				member.setRight(new AFinSubsetExpression(visitExprOrOpArgNodeExpression(opApplNode.getArgs()[0])));
				returnNode = new AConvertBoolExpression(member);
				break;
			}

			case B_OPCODE_string: // STRING
				returnNode = new AStringSetExpression();
				break;
			/*
			 * Standard Module Sequences
			 */

			case B_OPCODE_seq: // Seq(S) - set of sequences
				returnNode = new ASeqExpression(visitExprOrOpArgNodeExpression(opApplNode.getArgs()[0]));
				break;

			case B_OPCODE_len: // length of the sequence
				returnNode = new ASizeExpression(visitExprOrOpArgNodeExpression(opApplNode.getArgs()[0]));
				break;

			case B_OPCODE_conc: // s \o s2 - concatenation of s and s2
				returnNode = new AConcatExpression(visitExprOrOpArgNodeExpression(opApplNode.getArgs()[0]),
					visitExprOrOpArgNodeExpression(opApplNode.getArgs()[1]));
				break;

			case B_OPCODE_append: // Append(s,x)
				returnNode = new AInsertTailExpression(visitExprOrOpArgNodeExpression(opApplNode.getArgs()[0]),
					visitExprOrOpArgNodeExpression(opApplNode.getArgs()[1]));
				break;

			case B_OPCODE_head: // Head(s)
				returnNode = new AFirstExpression(visitExprOrOpArgNodeExpression(opApplNode.getArgs()[0]));
				break;

			case B_OPCODE_tail: // Tail(s)
				returnNode = new ATailExpression(visitExprOrOpArgNodeExpression(opApplNode.getArgs()[0]));
				break;

			case B_OPCODE_subseq: { // SubSeq(s,a,b)
				// %p.(p : 1..(b-a+1)| s(p+a-1))
				ALambdaExpression lambda = new ALambdaExpression();
				lambda.setIdentifiers(createIdentifierList("t_"));
				AMemberPredicate member = new AMemberPredicate();
				member.setLeft(createIdentifierNode("t_"));
				AIntervalExpression interval = new AIntervalExpression();
				interval.setLeftBorder(new AIntegerExpression(new TIntegerLiteral("1")));
				AMinusOrSetSubtractExpression minus = new AMinusOrSetSubtractExpression();
				minus.setLeft(visitExprOrOpArgNodeExpression(opApplNode.getArgs()[2]));
				minus.setRight(visitExprOrOpArgNodeExpression(opApplNode.getArgs()[1]));
				AAddExpression add = new AAddExpression();
				add.setLeft(minus);
				add.setRight(new AIntegerExpression(new TIntegerLiteral("1")));
				interval.setRightBorder(add);
				member.setRight(interval);
				lambda.setPredicate(member);
				AAddExpression add2 = new AAddExpression();
				add2.setLeft(createIdentifierNode("t_"));
				add2.setRight(visitExprOrOpArgNodeExpression(opApplNode.getArgs()[1]));
				AMinusOrSetSubtractExpression minus2 = new AMinusOrSetSubtractExpression();
				minus2.setLeft(add2);
				minus2.setRight(new AIntegerExpression(new TIntegerLiteral("1")));
				ArrayList params = new ArrayList<>();
				params.add(minus2);
				AFunctionExpression func = new AFunctionExpression();
				func.setIdentifier(visitExprOrOpArgNodeExpression(opApplNode.getArgs()[0]));
				func.setParameters(params);
				lambda.setExpression(func);
				returnNode = lambda;
				break;
			}

			case B_OPCODE_assert: {
				ADefinitionPredicate pred = new ADefinitionPredicate();
				pred.setDefLiteral(new TDefLiteralPredicate("ASSERT_TRUE"));
				ArrayList list = new ArrayList<>();
				list.add(visitExprOrOpArgNodeExpression(opApplNode.getArgs()[0]));
				list.add(new AStringExpression(new TStringLiteral("Error")));
				pred.setParameters(list);
				returnNode = new AConvertBoolExpression(pred);
				break;
			}

			case B_OPCODE_setsum: {
				AGeneralSumExpression sum = new AGeneralSumExpression();
				String variableName = "t_"; // TODO unused identifier name
				sum.setIdentifiers(Collections.singletonList(createIdentifierNode(variableName)));
				AMemberPredicate memberPredicate = new AMemberPredicate();
				memberPredicate.setLeft(createIdentifierNode(variableName));
				memberPredicate.setRight(visitExprOrOpArgNodeExpression(opApplNode.getArgs()[0]));
				sum.setPredicates(memberPredicate);
				sum.setExpression(createIdentifierNode(variableName));
				returnNode = sum;
				break;
			}
		}
		if (returnNode != null) {
			return createPositionedNode(returnNode, opApplNode);
		} else {
			throw new RuntimeException("Unexpected operator: " + opApplNode.getOperator().getName().toString() + "\n"
				+ opApplNode.stn.getLocation());
		}

	}

	private  T createPositionedNode(T positionedNode, SemanticNode semanticNode) {
		Location location = semanticNode.getTreeNode().getLocation();
		positionedNode.setStartPos(new SourcePosition(location.beginLine(), location.beginColumn()));
		positionedNode.setEndPos(new SourcePosition(location.endLine(), location.endColumn()));
		sourcePosition.add(positionedNode);
		String source = semanticNode.getLocation().source();
		int id = filesOrderedById.indexOf(source);
		if (id == -1) {
			id = filesOrderedById.size();
			filesOrderedById.add(source);
		}
		nodeFileNumbers.assignIdentifiers(id+1, (Node) positionedNode);
		return positionedNode;
	}

	private void setPosition(PositionedNode positionNode, OpApplNode opApplNode) {
		createPositionedNode(positionNode, opApplNode);
	}

	private PExpression visitBuiltInKindExpression(OpApplNode n) {
		switch (getOpCode(n.getOperator().getName())) {

			case OPCODE_land: // \land
			{
				AConjunctPredicate conjunction = new AConjunctPredicate();
				conjunction.setLeft(visitExprOrOpArgNodePredicate(n.getArgs()[0]));
				conjunction.setRight(visitExprOrOpArgNodePredicate(n.getArgs()[1]));
				return new AConvertBoolExpression(conjunction);
			}

			case OPCODE_equiv: // \equiv
				AEquivalencePredicate equiv = new AEquivalencePredicate(visitExprOrOpArgNodePredicate(n.getArgs()[0]),
					visitExprOrOpArgNodePredicate(n.getArgs()[1]));
				return new AConvertBoolExpression(equiv);

			case OPCODE_implies: // =>
				AImplicationPredicate impl = new AImplicationPredicate(visitExprOrOpArgNodePredicate(n.getArgs()[0]),
					visitExprOrOpArgNodePredicate(n.getArgs()[1]));
				new AConvertBoolExpression(impl);

			case OPCODE_cl: // $ConjList
			{
				List list = new ArrayList<>();
				for (int i = 0; i < n.getArgs().length; i++) {
					list.add(visitExprOrOpArgNodePredicate(n.getArgs()[i]));
				}
				return new AConvertBoolExpression(createConjunction(list));
			}

			case OPCODE_dl: // $DisjList
			{
				List list = new ArrayList<>();
				for (int i = 0; i < n.getArgs().length; i++) {
					list.add(visitExprOrOpArgNodePredicate(n.getArgs()[i]));
				}
				return new AConvertBoolExpression(createDisjunction(list));
			}

			case OPCODE_lor: // \/
			{
				ADisjunctPredicate disjunction = new ADisjunctPredicate();
				disjunction.setLeft(visitExprOrOpArgNodePredicate(n.getArgs()[0]));
				disjunction.setRight(visitExprOrOpArgNodePredicate(n.getArgs()[1]));
				return new AConvertBoolExpression(disjunction);
			}

			case OPCODE_lnot: // \lnot
				return new AConvertBoolExpression(new ANegationPredicate(visitExprOrOpArgNodePredicate(n.getArgs()[0])));

			case OPCODE_in: // \in
			{
				AMemberPredicate memberPredicate = new AMemberPredicate();
				memberPredicate.setLeft(visitExprOrOpArgNodeExpression(n.getArgs()[0]));
				memberPredicate.setRight(visitExprOrOpArgNodeExpression(n.getArgs()[1]));
				return new AConvertBoolExpression(memberPredicate);
			}

			case OPCODE_notin: // \notin
			{
				ANotMemberPredicate notMemberPredicate = new ANotMemberPredicate();
				notMemberPredicate.setLeft(visitExprOrOpArgNodeExpression(n.getArgs()[0]));
				notMemberPredicate.setRight(visitExprOrOpArgNodeExpression(n.getArgs()[1]));
				return new AConvertBoolExpression(notMemberPredicate);
			}

			case OPCODE_eq: { // =
				AEqualPredicate equal = new AEqualPredicate();
				equal.setLeft(visitExprOrOpArgNodeExpression(n.getArgs()[0]));
				equal.setRight(visitExprOrOpArgNodeExpression(n.getArgs()[1]));
				return new AConvertBoolExpression(equal);
			}

			case OPCODE_noteq: // /=
			{
				ANotEqualPredicate notEqual = new ANotEqualPredicate();
				notEqual.setLeft(visitExprOrOpArgNodeExpression(n.getArgs()[0]));
				notEqual.setRight(visitExprOrOpArgNodeExpression(n.getArgs()[1]));
				return new AConvertBoolExpression(notEqual);
			}

			/*
			 * Set Operators
			 */

			case OPCODE_se: // SetEnumeration {..}
			{
				if (n.getArgs().length == 0) {
					return new AEmptySetExpression();
				} else {
					List list = new ArrayList<>();

					for (int i = 0; i < n.getArgs().length; i++) {
						list.add(visitExprOrOpArgNodeExpression(n.getArgs()[i]));
					}
					return new ASetExtensionExpression(list);
				}
			}

			case 0: {
				return visitBBuiltInsExpression(n);
			}

			case OPCODE_setdiff: // set difference
			{
				AMinusOrSetSubtractExpression minus = new AMinusOrSetSubtractExpression();
				minus.setLeft(visitExprOrOpArgNodeExpression(n.getArgs()[0]));
				minus.setRight(visitExprOrOpArgNodeExpression(n.getArgs()[1]));
				return minus;
			}

			case OPCODE_cup: // set union
			{
				AUnionExpression union = new AUnionExpression();
				union.setLeft(visitExprOrOpArgNodeExpression(n.getArgs()[0]));
				union.setRight(visitExprOrOpArgNodeExpression(n.getArgs()[1]));
				return union;
			}

			case OPCODE_cap: // set intersection
			{
				AIntersectionExpression inter = new AIntersectionExpression();
				inter.setLeft(visitExprOrOpArgNodeExpression(n.getArgs()[0]));
				inter.setRight(visitExprOrOpArgNodeExpression(n.getArgs()[1]));
				return inter;
			}

			case OPCODE_subset: // SUBSET
			{
				APowSubsetExpression pow = new APowSubsetExpression();
				pow.setExpression(visitExprOrOpArgNodeExpression(n.getArgs()[0]));
				return pow;
			}

			case OPCODE_union: // Union - Union{{1},{2}}
			{
				AGeneralUnionExpression union = new AGeneralUnionExpression();
				union.setExpression(visitExprOrOpArgNodeExpression(n.getArgs()[0]));
				return union;
			}

			case OPCODE_subseteq: // \subseteq {1,2} <: {1,2,3}
			{
				ASubsetPredicate subset = new ASubsetPredicate();
				subset.setLeft(visitExprOrOpArgNodeExpression(n.getArgs()[0]));
				subset.setRight(visitExprOrOpArgNodeExpression(n.getArgs()[1]));
				return new AConvertBoolExpression(subset);
			}

			case OPCODE_sso: { // $SubsetOf Represents {x \in S : P}
				FormalParamNode[][] params = n.getBdedQuantSymbolLists();
				List list = new ArrayList<>();
				for (int i = 0; i < params[0].length; i++) {
					FormalParamNode p = params[0][i];
					list.add(createIdentifierNode(p));
				}
				return new AComprehensionSetExpression(
					list,
					new AConjunctPredicate(
						visitBoundsOfFunctionsVariables(n),
						visitExprOrOpArgNodePredicate(n.getArgs()[0])
					)
				);
			}

			case OPCODE_soa: { // $SetOfAll Represents {e : p1 \in S, p2,p3 \in S2}
				FormalParamNode[][] params = n.getBdedQuantSymbolLists();
				List idList = createListOfIdentifier(params);
				List predList = new ArrayList<>();
				predList.add(visitBoundsOfLocalVariables(n));

				// currently not used:
				/* final String nameOfTempVariable = "t_";

				AEqualPredicate equals = new AEqualPredicate(
					createIdentifierNode(nameOfTempVariable),
					visitExprOrOpArgNodeExpression(n.getArgs()[0])
				);
				// predList.add(equals);
				AExistsPredicate exist = new AExistsPredicate(
					idList,
					createConjunction(predList)
				);

				AComprehensionSetExpression compre = new AComprehensionSetExpression();
				List tList = new ArrayList<>();
				tList.add(createIdentifierNode(nameOfTempVariable));
				compre.setIdentifiers(tList);
				compre.setPredicates(exist);*/

				// UNION(p1,p2,p3).(P | {e})
				return new AQuantifiedUnionExpression(
					idList,
					createConjunction(predList),
					new ASetExtensionExpression(
						Collections.singletonList(visitExprOrOpArgNodeExpression(n.getArgs()[0])))
				);
			}

			case OPCODE_nrfs:
			case OPCODE_fc: // Represents [x \in S |-> e].
			case OPCODE_rfs: {
				FormalParamNode[][] params = n.getBdedQuantSymbolLists();
				List idList = new ArrayList<>();
				for (FormalParamNode[] param : params) {
					for (FormalParamNode p : param) {
						idList.add(createIdentifierNode(p));
					}
				}
				boolean[] isTuple = n.isBdedQuantATuple();
				ALambdaExpression lambda = new ALambdaExpression();
				List idList2 = new ArrayList<>();
				for (int i = 0; i < params.length; i++) {
					if (isTuple[i] && i > 0) {
						StringBuilder sb = new StringBuilder();
						for (int j = 0; j < params[i].length; j++) {
							FormalParamNode p = params[i][j];
							sb.append(p.getName().toString());

						}
						idList2.add(createIdentifierNode(sb.toString()));
					} else {
						for (int j = 0; j < params[i].length; j++) {
							FormalParamNode p = params[i][j];
							idList2.add(createIdentifierNode(p.getName().toString()));
						}
					}
				}

				lambda.setIdentifiers(idList2);
				lambda.setPredicate(visitBoundsOfFunctionsVariables(n));
				lambda.setExpression(visitExprOrOpArgNodeExpression(n.getArgs()[0]));

				if (recursiveFunctionHandler.isRecursiveFunction(n)) {

					ArrayList externParams = recursiveFunctionHandler.getAdditionalParams(n);
					if (!externParams.isEmpty()) {
						ALambdaExpression lambda2 = new ALambdaExpression();
						ArrayList shiftedParams = new ArrayList<>();
						List predList2 = new ArrayList<>();
						for (SymbolNode p : externParams) {
							shiftedParams.add(createIdentifierNode(p));

							AMemberPredicate member = new AMemberPredicate();
							member.setLeft(createIdentifierNode(p));
							TLAType t = (TLAType) p.getToolObject(TYPE_ID);
							member.setRight(t.getBNode());
							predList2.add(member);
						}
						lambda2.setIdentifiers(shiftedParams);
						lambda2.setPredicate(createConjunction(predList2));
						lambda2.setExpression(lambda);
						return lambda2;
					}
				}
				return lambda;
			}

			case OPCODE_fa: { // f[1]
				TLAType t = (TLAType) n.getArgs()[0].getToolObject(TYPE_ID);
				if (t instanceof TupleType) {
					NumeralNode num = (NumeralNode) n.getArgs()[1];
					int field = num.val();
					PExpression pair = visitExprOrOpArgNodeExpression(n.getArgs()[0]);
					return createProjectionFunction(pair, field, t);
				} else {
					AFunctionExpression func = new AFunctionExpression();
					func.setIdentifier(visitExprOrOpArgNodeExpression(n.getArgs()[0]));
					List paramList = new ArrayList<>();

					ExprOrOpArgNode dom = n.getArgs()[1];
					if (dom instanceof OpApplNode
						&& ((OpApplNode) dom).getOperator().getName().toString().equals("$Tuple")) {
						OpApplNode domOpAppl = (OpApplNode) dom;
						if (domOpAppl.getArgs().length == 1) {
							List list = new ArrayList<>();
							list.add(visitExprOrOpArgNodeExpression(domOpAppl.getArgs()[0]));
							ASequenceExtensionExpression seq = new ASequenceExtensionExpression(list);
							paramList.add(seq);
						} else {
							for (int i = 0; i < domOpAppl.getArgs().length; i++) {
								paramList.add(visitExprOrOpArgNodeExpression(domOpAppl.getArgs()[i]));
							}
						}

					} else {
						paramList.add(visitExprOrOpArgNodeExpression(dom));
					}
					func.setParameters(paramList);
					return func;
				}

			}

			case OPCODE_domain:
				return new ADomainExpression(visitExprOrOpArgNodeExpression(n.getArgs()[0]));

			case OPCODE_sof: // [ A -> B]
				return new ATotalFunctionExpression(visitExprOrOpArgNodeExpression(n.getArgs()[0]),
					visitExprOrOpArgNodeExpression(n.getArgs()[1]));

			case OPCODE_tup: { // $Tuple
				List list = new ArrayList<>();
				for (int i = 0; i < n.getArgs().length; i++) {
					list.add(visitExprOrOpArgNodeExpression(n.getArgs()[i]));
				}
				TLAType t = (TLAType) n.getToolObject(TYPE_ID);
				if (t instanceof TupleType) {
					return new ACoupleExpression(list);
				} else {
					if (list.isEmpty()) {
						return new AEmptySequenceExpression();
					} else {
						return new ASequenceExtensionExpression(list);
					}
				}
			}

			case OPCODE_cp: // $CartesianProd A \X B \X C
			{
				PExpression first = visitExprOrOpArgNodeExpression(n.getArgs()[0]);
				for (int i = 1; i < n.getArgs().length; i++) {
					PExpression second = visitExprOrOpArgNodeExpression(n.getArgs()[i]);
					first = new AMultOrCartExpression(first, second);
				}
				return first;
			}

			case OPCODE_sor: { // $SetOfRcds [L1 : e1, L2 : e2]
				SetType pow = (SetType) n.getToolObject(TYPE_ID);
				StructType struct = (StructType) pow.getSubType();
				ExprOrOpArgNode[] args = n.getArgs();
				Hashtable pairTable = new Hashtable<>();
				for (ExprOrOpArgNode arg : args) {
					OpApplNode pair = (OpApplNode) arg;
					StringNode stringNode = (StringNode) pair.getArgs()[0];
					pairTable.put(stringNode.getRep().toString(), visitExprOrOpArgNodeExpression(pair.getArgs()[1]));
				}
				List recList = new ArrayList<>();
				if (struct.isExtensible()) {
					for (int i = 0; i < struct.getFields().size(); i++) {
						String fieldName = struct.getFields().get(i); // name
						ARecEntry rec = new ARecEntry();
						rec.setIdentifier(new TIdentifierLiteral(fieldName));
						AMultOrCartExpression cart = new AMultOrCartExpression();
						cart.setLeft(new ABoolSetExpression());
						if (pairTable.containsKey(fieldName)) {
							cart.setRight(pairTable.get(fieldName));
						} else {
							cart.setRight(struct.getType(fieldName).getBNode());
						}
						rec.setValue(new APowSubsetExpression(cart));
						recList.add(rec);
					}
				} else {
					for (int i = 0; i < struct.getFields().size(); i++) {
						String fieldName = struct.getFields().get(i);
						ARecEntry rec = new ARecEntry();
						rec.setIdentifier(new TIdentifierLiteral(fieldName));
						if (pairTable.containsKey(fieldName)) {
							rec.setValue(pairTable.get(fieldName));
						} else {
							rec.setValue(struct.getType(fieldName).getBNode());
						}
						recList.add(rec);
					}

				}
				return new AStructExpression(recList);
			}

			case OPCODE_rc: { // [h_1 |-> 1, h_2 |-> 2]
				StructType struct = (StructType) n.getToolObject(TYPE_ID);
				if (struct.isExtensible()) {
					Map pairTable = new HashMap<>();
					ExprOrOpArgNode[] args = n.getArgs();
					for (ExprOrOpArgNode arg : args) {
						OpApplNode pair = (OpApplNode) arg;
						StringNode stringNode = (StringNode) pair.getArgs()[0];
						pairTable.put(stringNode.getRep().toString(), visitExprOrOpArgNodeExpression(pair.getArgs()[1]));
					}
					List recList = new ArrayList<>();
					for (String fieldName : struct.getFields()) {
						ARecEntry rec = new ARecEntry();
						rec.setIdentifier(new TIdentifierLiteral(fieldName));
						if (pairTable.containsKey(fieldName)) {
							ACoupleExpression couple = new ACoupleExpression(Arrays.asList(
									new ABooleanTrueExpression(), pairTable.get(fieldName)));
							rec.setValue(new ASetExtensionExpression(Collections.singletonList(couple)));
						} else {
							AEmptySetExpression emptySet = new AEmptySetExpression();
							rec.setValue(emptySet);
						}
						recList.add(rec);
					}
					return new ARecExpression(recList);

				} else {
					Hashtable pairTable = new Hashtable<>();
					ExprOrOpArgNode[] args = n.getArgs();
					for (ExprOrOpArgNode arg : args) {
						OpApplNode pair = (OpApplNode) arg;
						StringNode stringNode = (StringNode) pair.getArgs()[0];
						pairTable.put(stringNode.getRep().toString(), visitExprOrOpArgNodeExpression(pair.getArgs()[1]));
					}
					List recList = new ArrayList<>();
					for (String fieldName : struct.getFields()) {
						ARecEntry rec = new ARecEntry();
						rec.setIdentifier(new TIdentifierLiteral(fieldName));
						if (pairTable.containsKey(fieldName)) {
							rec.setValue(pairTable.get(fieldName));
						} else {
							// this struct is extensible
							throw new NotImplementedException("Missing case handling extensible structs.");
						}
						recList.add(rec);
					}
					return new ARecExpression(recList);
				}

			}

			case OPCODE_rs: { // $RcdSelect r.c
				StructType struct = (StructType) n.getArgs()[0].getToolObject(TYPE_ID);
				if (struct.isExtensible()) {
					ARecordFieldExpression rcdSelect = new ARecordFieldExpression();
					rcdSelect.setRecord(visitExprOrOpArgNodeExpression(n.getArgs()[0]));
					StringNode stringNode = (StringNode) n.getArgs()[1];
					rcdSelect.setIdentifier(new TIdentifierLiteral(stringNode.getRep().toString()));
					AFunctionExpression funcCall = new AFunctionExpression();
					funcCall.setIdentifier(rcdSelect);
					funcCall.setParameters(Collections.singletonList(new ABooleanTrueExpression()));
					return funcCall;
				} else {
					ARecordFieldExpression rcdSelect = new ARecordFieldExpression();
					rcdSelect.setRecord(visitExprOrOpArgNodeExpression(n.getArgs()[0]));
					StringNode stringNode = (StringNode) n.getArgs()[1];
					rcdSelect.setIdentifier(new TIdentifierLiteral(stringNode.getRep().toString()));
					return rcdSelect;
				}
			}

			case OPCODE_prime: // prime
			{
				OpApplNode node = (OpApplNode) n.getArgs()[0];
				return createIdentifierNode(node.getOperator().getName().toString() + "_n");
			}

			case OPCODE_ite: { // IF THEN ELSE
				return new AIfThenElseExpression(
						visitExprOrOpArgNodePredicate(n.getArgs()[0]),
						visitExprOrOpArgNodeExpression(n.getArgs()[1]),
						new ArrayList<>(),
						visitExprOrOpArgNodeExpression(n.getArgs()[2]));

				// ALambdaExpression lambda1 = new ALambdaExpression();
				// lambda1.setIdentifiers(createIdentifierList("t_"));
				// AEqualPredicate eq1 = new AEqualPredicate(
				// createIdentifierNode("t_"), new AIntegerExpression(
				// new TIntegerLiteral("0")));
				// AConjunctPredicate c1 = new AConjunctPredicate();
				// c1.setLeft(eq1);
				// c1.setRight(visitExprOrOpArgNodePredicate(n.getArgs()[0]));
				// lambda1.setPredicate(c1);
				// lambda1.setExpression(visitExprOrOpArgNodeExpression(n.getArgs()[1]));
				//
				// ALambdaExpression lambda2 = new ALambdaExpression();
				// lambda2.setIdentifiers(createIdentifierList("t_"));
				// AEqualPredicate eq2 = new AEqualPredicate(
				// createIdentifierNode("t_"), new AIntegerExpression(
				// new TIntegerLiteral("0")));
				// AConjunctPredicate c2 = new AConjunctPredicate();
				// c2.setLeft(eq2);
				// ANegationPredicate not = new ANegationPredicate(
				// visitExprOrOpArgNodePredicate(n.getArgs()[0]));
				// c2.setRight(not);
				// lambda2.setPredicate(c2);
				// lambda2.setExpression(visitExprOrOpArgNodeExpression(n.getArgs()[2]));
				//
				// AUnionExpression union = new AUnionExpression(lambda1, lambda2);
				// AFunctionExpression funCall = new AFunctionExpression();
				// funCall.setIdentifier(union);
				// List list = new ArrayList();
				// list.add(new AIntegerExpression(new TIntegerLiteral("0")));
				// funCall.setParameters(list);
				// return funCall;
			}

			case OPCODE_case: {
				return createCaseNode(n);
			}

			case OPCODE_exc: // $Except
			{
				TLAType type = (TLAType) n.getToolObject(TYPE_ID);

				if (type.getKind() == IType.STRUCT) {
					StructType structType = (StructType) type;
					PExpression res = visitExprOrOpArgNodeExpression(n.getArgs()[0]);
					for (int i = 1; i < n.getArgs().length; i++) {
						OpApplNode pair = (OpApplNode) n.getArgs()[i];
						ExprOrOpArgNode first = pair.getArgs()[0];
						ExprOrOpArgNode val = pair.getArgs()[1];
						OpApplNode seq = (OpApplNode) first;

						LinkedList seqList = new LinkedList<>();
						Collections.addAll(seqList, seq.getArgs());

						pair.setToolObject(EXCEPT_BASE, res.clone());
						res = evalExceptValue(res.clone(), seqList, structType, val);
					}
					return res;

				} else {
					FunctionType func = (FunctionType) type;

					PExpression res = visitExprOrOpArgNodeExpression(n.getArgs()[0]);
					for (int i = 1; i < n.getArgs().length; i++) {
						OpApplNode pair = (OpApplNode) n.getArgs()[i];

						ExprOrOpArgNode first = pair.getArgs()[0];
						ExprOrOpArgNode val = pair.getArgs()[1];
						OpApplNode seq = (OpApplNode) first;

						LinkedList seqList = new LinkedList<>();
						Collections.addAll(seqList, seq.getArgs());

						pair.setToolObject(EXCEPT_BASE, res.clone());
						res = evalExceptValue(res.clone(), seqList, func, val);
					}
					return res;
				}
			}

			case OPCODE_seq: { // !
				return visitExprOrOpArgNodeExpression(n.getArgs()[0]);
			}

			case OPCODE_uc: { // CHOOSE x : P
				return createUnboundedChoose(n);
			}

			case OPCODE_bc: { // CHOOSE x \in S: P
				return createBoundedChoose(n);
			}

			case OPCODE_bf: { // \A x \in S : P
				FormalParamNode[][] params = n.getBdedQuantSymbolLists();
				List list = new ArrayList<>();
				for (FormalParamNode[] param : params) {
					for (FormalParamNode formalParamNode : param) {
						list.add(createIdentifierNode(formalParamNode));
					}
				}
				return new AConvertBoolExpression(
						new AForallPredicate(
								list,
								new AImplicationPredicate(
									visitBoundsOfLocalVariables(n),
									visitExprOrOpArgNodePredicate(n.getArgs()[0])
								)
						)
				);
			}

			case OPCODE_be: { // \E x \in S : P
				FormalParamNode[][] params = n.getBdedQuantSymbolLists();
				List list = new ArrayList<>();
				for (FormalParamNode[] param : params) {
					for (FormalParamNode formalParamNode : param) {
						list.add(createIdentifierNode(formalParamNode));
					}
				}
				return new AConvertBoolExpression(
						new AExistsPredicate(
								list,
								new AConjunctPredicate(
										visitBoundsOfLocalVariables(n),
										visitExprOrOpArgNodePredicate(n.getArgs()[0])
								)
						)
				);
			}

		}

		throw new NotImplementedException("Missing support for operator: " + n.getOperator().getName());
	}

	private List createListOfIdentifier(FormalParamNode[][] params) {
		List list = new ArrayList<>();
		for (FormalParamNode[] formalParamNodes : params) {
			for (FormalParamNode param : formalParamNodes) {
				list.add(createIdentifierNode(param));
			}
		}
		return list;
	}

	private PExpression evalExceptValue(PExpression prefix, LinkedList seqList, TLAType tlaType,
	                                    ExprOrOpArgNode val) {

		ExprOrOpArgNode head = seqList.poll();
		if (head == null) {
			return visitExprOrOpArgNodeExpression(val);
		}

		if (tlaType instanceof StructType) {
			StructType structType = (StructType) tlaType;
			String field = ((StringNode) head).getRep().toString();

			List list = new ArrayList<>();
			for (int i = 0; i < structType.getFields().size(); i++) {
				ARecEntry entry = new ARecEntry();
				String fieldName = structType.getFields().get(i);
				entry.setIdentifier(new TIdentifierLiteral(fieldName));

				PExpression value;
				ARecordFieldExpression select = new ARecordFieldExpression();
				select.setRecord(prefix.clone());
				select.setIdentifier(new TIdentifierLiteral(fieldName));
				if (fieldName.equals(field)) {
					value = evalExceptValue(select, seqList, structType.getType(fieldName), val);
				} else {
					value = select;
				}
				entry.setValue(value);
				list.add(entry);

			}

			return new ARecExpression(list);

		} else {
			AFunctionExpression funcCall = new AFunctionExpression(prefix,
					Collections.singletonList(visitExprOrOpArgNodeExpression(head)));
			List coupleList = Arrays.asList(visitExprOrOpArgNodeExpression(head),
					evalExceptValue(funcCall, seqList, ((FunctionType) tlaType).getRange(), val));
			return new AOverwriteExpression(
					prefix.clone(),
					new ASetExtensionExpression(Collections.singletonList(new ACoupleExpression(coupleList)))
			);
		}
	}

	private PExpression createProjectionFunction(PExpression pair, int field, TLAType t) {
		TupleType tuple = (TupleType) t;

		AFunctionExpression returnFunc = new AFunctionExpression();
		int index;
		if (field == 1) {
			index = 2;
			returnFunc.setIdentifier(new AFirstProjectionExpression(
					tuple.getTypes().get(0).getBNode(),
					tuple.getTypes().get(1).getBNode()
			));
		} else {
			index = field;
			List typeList = new ArrayList<>();
			for (int i = 0; i < field - 1; i++) {
				typeList.add(tuple.getTypes().get(i));
			}
			// we could use AEventBSecondProjectionV2Expression here (which would be much easier),
			// but this is only supported by ProB (?)
			returnFunc.setIdentifier(new ASecondProjectionExpression(
					createNestedCouple(typeList),
					tuple.getTypes().get(field - 1).getBNode()
			));
		}
		AFunctionExpression func = returnFunc;
		for (int i = index; i < tuple.getTypes().size(); i++) {
			AFunctionExpression newfunc = new AFunctionExpression();
			List typeList = new ArrayList<>();
			for (int j = 0; j < i; j++) {
				typeList.add(tuple.getTypes().get(j));
			}
			newfunc.setIdentifier(new AFirstProjectionExpression(
					createNestedCouple(typeList),
					tuple.getTypes().get(i).getBNode()
			));

			func.setParameters(Collections.singletonList(newfunc));
			func = newfunc;
		}
		func.setParameters(Collections.singletonList(pair));
		return returnFunc;
	}

	public static PExpression createNestedCouple(List typeList) {
		if (typeList.size() == 1) {
			return typeList.get(0).getBNode();
		}
		List list = new ArrayList<>();
		for (TLAType t : typeList) {
			list.add(t.getBNode());
		}
		AMultOrCartExpression card = new AMultOrCartExpression();
		card.setLeft(list.get(0));
		for (int i = 1; i < list.size(); i++) {
			if (i < list.size() - 1) {
				AMultOrCartExpression old = card;
				old.setRight(list.get(i));
				card = new AMultOrCartExpression();
				card.setLeft(old);
			} else {
				card.setRight(list.get(i));
			}
		}
		return card;
	}

	private PExpression createUnboundedChoose(OpApplNode n) {
		return new ADefinitionExpression(
				new TIdentifierLiteral("CHOOSE"),
				Collections.singletonList(new AComprehensionSetExpression(
						Arrays.stream(n.getUnbdedQuantSymbols()).map(this::createIdentifierNode).collect(Collectors.toList()),
						visitExprOrOpArgNodePredicate(n.getArgs()[0])
				))
		);
	}

	private PExpression createBoundedChoose(OpApplNode n) {
		return new ADefinitionExpression(
				new TIdentifierLiteral("CHOOSE"),
				Collections.singletonList(new AComprehensionSetExpression(
						Arrays.stream(n.getBdedQuantSymbolLists()[0]).map(this::createIdentifierNode).collect(Collectors.toList()),
						new AConjunctPredicate(
								visitBoundsOfLocalVariables(n),
								visitExprOrOpArgNodePredicate(n.getArgs()[0])
						)
				))
		);
	}

	private PExpression createCaseNode(OpApplNode n) {
		List conditions = new ArrayList<>();
		List disjunctionList = new ArrayList<>();
		for (int i = 0; i < n.getArgs().length; i++) {
			OpApplNode pair = (OpApplNode) n.getArgs()[i];

			AConjunctPredicate conj = new AConjunctPredicate();
			if (pair.getArgs()[0] == null) {
				ANegationPredicate neg = new ANegationPredicate();
				neg.setPredicate(createDisjunction(conditions));
				conj.setLeft(neg);
			} else {
				conditions.add(visitExprOrOpArgNodePredicate(pair.getArgs()[0]));
				conj.setLeft(visitExprOrOpArgNodePredicate(pair.getArgs()[0]));
			}
			AEqualPredicate equals = new AEqualPredicate();
			equals.setLeft(createIdentifierNode("t_"));
			equals.setRight(visitExprOrOpArgNodeExpression(pair.getArgs()[1]));
			conj.setRight(equals);
			disjunctionList.add(conj);
		}
		AComprehensionSetExpression comprehension = new AComprehensionSetExpression();
		comprehension.setIdentifiers(createIdentifierList("t_"));
		comprehension.setPredicates(createDisjunction(disjunctionList));
		ADefinitionExpression defCall = new ADefinitionExpression();
		defCall.setDefLiteral(new TIdentifierLiteral("CHOOSE"));
		List params = new ArrayList<>();
		params.add(comprehension);
		defCall.setParameters(params);
		return defCall;
	}

	private List createIdentifierList(String name) {
		ArrayList list = new ArrayList<>();
		list.add(createIdentifierNode(name));
		return list;
	}

	private PPredicate visitBuiltInKindPredicate(OpApplNode n) {
		PPredicate returnNode;
		switch (getOpCode(n.getOperator().getName())) {
			case OPCODE_land: // \land
			{
				AConjunctPredicate conjunction = new AConjunctPredicate();
				conjunction.setLeft(visitExprOrOpArgNodePredicate(n.getArgs()[0]));
				conjunction.setRight(visitExprOrOpArgNodePredicate(n.getArgs()[1]));
				returnNode = conjunction;
				break;
			}
			case OPCODE_cl: // $ConjList
			{
				List list = new ArrayList<>();
				for (int i = 0; i < n.getArgs().length; i++) {
					list.add(visitExprOrOpArgNodePredicate(n.getArgs()[i]));
				}
				returnNode = createConjunction(list);
				break;
			}
			case OPCODE_lor: // \/
			{
				ADisjunctPredicate disjunction = new ADisjunctPredicate();
				disjunction.setLeft(visitExprOrOpArgNodePredicate(n.getArgs()[0]));
				disjunction.setRight(visitExprOrOpArgNodePredicate(n.getArgs()[1]));
				returnNode = disjunction;
				break;
			}
			case OPCODE_dl: // $DisjList
			{
				List list = new ArrayList<>();
				for (int i = 0; i < n.getArgs().length; i++) {
					list.add(visitExprOrOpArgNodePredicate(n.getArgs()[i]));
				}
				returnNode = createDisjunction(list);
				break;
			}
			case OPCODE_lnot: // \lnot
				returnNode = new ANegationPredicate(visitExprOrOpArgNodePredicate(n.getArgs()[0]));
				break;
			case OPCODE_equiv: // \equiv
				returnNode = new AEquivalencePredicate(visitExprOrOpArgNodePredicate(n.getArgs()[0]),
					visitExprOrOpArgNodePredicate(n.getArgs()[1]));
				break;

			case OPCODE_implies: // =>
				returnNode = new AImplicationPredicate(visitExprOrOpArgNodePredicate(n.getArgs()[0]),
					visitExprOrOpArgNodePredicate(n.getArgs()[1]));
				break;

			case OPCODE_be: { // \E x \in S : P
				FormalParamNode[][] params = n.getBdedQuantSymbolLists();
				ArrayList list = new ArrayList<>();
				for (FormalParamNode[] param : params) {
					for (FormalParamNode formalParamNode : param) {
						list.add(createIdentifierNode(formalParamNode));
					}
				}
				AConjunctPredicate conjunction = new AConjunctPredicate();
				conjunction.setLeft(visitBoundsOfLocalVariables(n));
				conjunction.setRight(visitExprOrOpArgNodePredicate(n.getArgs()[0]));
				returnNode = new AExistsPredicate(list, conjunction);
				break;
			}

			case OPCODE_bf: { // \A x \in S : P
				FormalParamNode[][] params = n.getBdedQuantSymbolLists();
				ArrayList list = new ArrayList<>();
				for (FormalParamNode[] param : params) {
					for (FormalParamNode formalParamNode : param) {
						list.add(createIdentifierNode(formalParamNode));
					}
				}
				AImplicationPredicate implication = new AImplicationPredicate();
				implication.setLeft(visitBoundsOfLocalVariables(n));
				implication.setRight(visitExprOrOpArgNodePredicate(n.getArgs()[0]));
				returnNode = new AForallPredicate(list, implication);
				break;
			}

			case OPCODE_eq: { // =
				AEqualPredicate equal = new AEqualPredicate();
				equal.setLeft(visitExprOrOpArgNodeExpression(n.getArgs()[0]));
				equal.setRight(visitExprOrOpArgNodeExpression(n.getArgs()[1]));
				returnNode = equal;
				break;
			}
			case OPCODE_noteq: // /=
			{
				ANotEqualPredicate notEqual = new ANotEqualPredicate();
				notEqual.setLeft(visitExprOrOpArgNodeExpression(n.getArgs()[0]));
				notEqual.setRight(visitExprOrOpArgNodeExpression(n.getArgs()[1]));
				returnNode = notEqual;
				break;
			}
			case OPCODE_in: // \in
			{
				AMemberPredicate memberPredicate = new AMemberPredicate();
				memberPredicate.setLeft(visitExprOrOpArgNodeExpression(n.getArgs()[0]));
				memberPredicate.setRight(visitExprOrOpArgNodeExpression(n.getArgs()[1]));
				returnNode = memberPredicate;
				break;
			}

			case OPCODE_notin: // \notin
			{
				ANotMemberPredicate notMemberPredicate = new ANotMemberPredicate();
				notMemberPredicate.setLeft(visitExprOrOpArgNodeExpression(n.getArgs()[0]));
				notMemberPredicate.setRight(visitExprOrOpArgNodeExpression(n.getArgs()[1]));
				returnNode = notMemberPredicate;
				break;
			}

			case OPCODE_subseteq: // \subseteq {1,2} <: {1,2,3}
			{
				ASubsetPredicate subset = new ASubsetPredicate();
				subset.setLeft(visitExprOrOpArgNodeExpression(n.getArgs()[0]));
				subset.setRight(visitExprOrOpArgNodeExpression(n.getArgs()[1]));
				returnNode = subset;
				break;
			}

			case OPCODE_fa: { // f[1]
				AFunctionExpression func = new AFunctionExpression();
				func.setIdentifier(visitExprOrOpArgNodeExpression(n.getArgs()[0]));
				List paramList = new ArrayList<>();

				ExprOrOpArgNode dom = n.getArgs()[1];
				if (dom instanceof OpApplNode && ((OpApplNode) dom).getOperator().getName().toString().equals("$Tuple")) {
					OpApplNode domOpAppl = (OpApplNode) dom;
					for (int i = 0; i < domOpAppl.getArgs().length; i++) {
						paramList.add(visitExprOrOpArgNodeExpression(domOpAppl.getArgs()[i]));
					}
				} else {
					paramList.add(visitExprOrOpArgNodeExpression(dom));
				}
				func.setParameters(paramList);
				returnNode = new AEqualPredicate(func, new ABooleanTrueExpression());
				break;
			}

			case OPCODE_rs: { // $RcdSelect r.c
				ARecordFieldExpression rcdSelect = new ARecordFieldExpression();
				rcdSelect.setRecord(visitExprOrOpArgNodeExpression(n.getArgs()[0]));
				StringNode stringNode = (StringNode) n.getArgs()[1];
				rcdSelect.setIdentifier(new TIdentifierLiteral(stringNode.getRep().toString()));
				returnNode = new AEqualPredicate(rcdSelect, new ABooleanTrueExpression());
				break;
			}

			case OPCODE_case: {
				returnNode = new AEqualPredicate(createCaseNode(n), new ABooleanTrueExpression());
				break;
			}
			case OPCODE_prime: // prime
			{
				OpApplNode node = (OpApplNode) n.getArgs()[0];
				returnNode = new AEqualPredicate(createIdentifierNode(getName(node.getOperator()) + "_n"),
					new ABooleanTrueExpression());
				break;
			}
			case OPCODE_unchanged: {
				OpApplNode node = (OpApplNode) n.getArgs()[0];
				// System.out.println(" Translating UNCHANGED : " + node.toString());
				// System.out.println(" Top-level unchanged for this operation: " + this.toplevelUnchangedVariableNames);
				if (node.getOperator().getKind() == VariableDeclKind) {
					return CreateUnchangedPrimeEquality(node);

				} else if (node.getOperator().getKind() == UserDefinedOpKind) {
					OpDefNode operator = (OpDefNode) node.getOperator();
					ExprNode e = operator.getBody();
					node = (OpApplNode) e;
				}

				ArrayList list = new ArrayList<>();
				for (int i = 0; i < node.getArgs().length; i++) {
					OpApplNode var = (OpApplNode) node.getArgs()[i];
					list.add(CreateUnchangedPrimeEquality(var));
				}
				returnNode = createConjunction(list);
				// returnNode = new AEqualPredicate(new ABooleanTrueExpression(),
				// new ABooleanTrueExpression());
				break;
			}
			case OPCODE_uc: { // CHOOSE x : P
				returnNode = new AEqualPredicate(createUnboundedChoose(n), new ABooleanTrueExpression());
				break;
			}
			case OPCODE_bc: { // CHOOSE x \in S: P
				returnNode = new AEqualPredicate(createBoundedChoose(n), new ABooleanTrueExpression());
				break;
			}
			case OPCODE_ite: // IF THEN ELSE
			{
				AImplicationPredicate impl1 = new AImplicationPredicate();
				impl1.setLeft(visitExprOrOpArgNodePredicate(n.getArgs()[0]));
				impl1.setRight(visitExprOrOpArgNodePredicate(n.getArgs()[1]));

				AImplicationPredicate impl2 = new AImplicationPredicate();
				ANegationPredicate neg = new ANegationPredicate(visitExprOrOpArgNodePredicate(n.getArgs()[0]));
				impl2.setLeft(neg);
				impl2.setRight(visitExprOrOpArgNodePredicate(n.getArgs()[2]));
				returnNode = new AConjunctPredicate(impl1, impl2);
				break;
			}
			case 0:
				return visitBBuiltInsPredicate(n);
			default:
				throw new NotImplementedException(n.getOperator().getName().toString());
		}
		return createPositionedNode(returnNode, n);
	}

	// create an equality predicate var_n = var if required
	private AEqualPredicate CreateUnchangedPrimeEquality(OpApplNode var) {
		if (!this.toplevelUnchangedVariableNames.contains(getName(var.getOperator()))) {
			AEqualPredicate equal = new AEqualPredicate();
			equal.setLeft(createIdentifierNode(getName(var.getOperator()) + "_n"));
			equal.setRight(createIdentifierNode(var.getOperator()));
			return equal;
		} else {
			// the variable is marked UNCHANGED in a top-level UNCHANGED predicate
			// Hence it will not be added to the ANY variables and we do not need it
			return new AEqualPredicate(new ABooleanTrueExpression(), new ABooleanTrueExpression());
		}

	}

	public PPredicate visitBoundsOfLocalVariables(OpApplNode n) {
		FormalParamNode[][] params = n.getBdedQuantSymbolLists();
		ExprNode[] in = n.getBdedQuantBounds();
		boolean[] isTuple = n.isBdedQuantATuple();

		List predList = new ArrayList<>();
		for (int i = 0; i < params.length; i++) {
			if (isTuple[i]) {
				if (params[i].length == 1) {
					// one-tuple is handled is translated as a sequence
					FormalParamNode param = params[i][0];
					predList.add(new AMemberPredicate(
							new ASequenceExtensionExpression(Collections.singletonList(createIdentifierNode(param))),
							visitExprNodeExpression(in[i]))
					);

				} else {
					ArrayList list = new ArrayList<>();
					for (int j = 0; j < params[i].length; j++) {
						list.add(createIdentifierNode(params[i][j]));
					}
					predList.add(new AMemberPredicate(
							new ACoupleExpression(list),
							visitExprNodeExpression(in[i])
					));
				}
			} else {
				for (int j = 0; j < params[i].length; j++) {
					predList.add(new AMemberPredicate(
							createIdentifierNode(params[i][j]),
							visitExprNodeExpression(in[i])
					));
				}
			}
		}
		return createPositionedNode(createConjunction(predList), n);
	}

	public PPredicate visitBoundsOfFunctionsVariables(OpApplNode n) {
		FormalParamNode[][] params = n.getBdedQuantSymbolLists();
		ExprNode[] in = n.getBdedQuantBounds();
		boolean[] isTuple = n.isBdedQuantATuple();

		List predList = new ArrayList<>();
		for (int i = 0; i < params.length; i++) {
			if (isTuple[i]) {
				if (params[i].length == 1) { // one-tuple is handled as a
					// sequence
					FormalParamNode param = params[i][0];
					param.setToolObject(TUPLE, params[i]);
					predList.add(new AMemberPredicate(createIdentifierNode(param), visitExprNodeExpression(in[i])));
				} else if (i == 0) {
					List list = new ArrayList<>();
					for (int j = 0; j < params[i].length; j++) {
						list.add(createIdentifierNode(params[i][j]));
					}
					predList.add(new AMemberPredicate(new ACoupleExpression(list), visitExprNodeExpression(in[i])));
				} else {
					ArrayList list = new ArrayList<>();
					StringBuilder sb = new StringBuilder();
					for (int j = 0; j < params[i].length; j++) {
						FormalParamNode param = params[i][j];
						if (i > 0) { // do not use prj1 & prj2 if it is the
							// first tuple
							param.setToolObject(TUPLE, params[i]);
						}
						sb.append(param.getName().toString());
						list.add(createIdentifierNode(param));
					}
					predList.add(new AMemberPredicate(createIdentifierNode(sb.toString()), visitExprNodeExpression(in[i])));
				}
			} else {
				for (int j = 0; j < params[i].length; j++) {
					predList.add(new AMemberPredicate(createIdentifierNode(params[i][j]), visitExprNodeExpression(in[i])));
				}
			}
		}
		return createConjunction(predList);
	}

	public PPredicate visitExprOrOpArgNodePredicate(ExprOrOpArgNode n) {
		if (n instanceof ExprNode) {
			return visitExprNodePredicate((ExprNode) n);
		} else {
			throw new RuntimeException("OpArgNode not implemented jet");
		}
	}

	public PExpression visitExprOrOpArgNodeExpression(ExprOrOpArgNode n) {
		if (n instanceof ExprNode) {
			return visitExprNodeExpression((ExprNode) n);
		} else {
			throw new RuntimeException("OpArgNode not implemented jet");
		}
	}

	public PPredicate createConjunction(List list) {
		if (list.size() == 1)
			return list.get(0);
		AConjunctPredicate conj = new AConjunctPredicate();
		conj.setLeft(list.get(0));
		for (int i = 1; i < list.size(); i++) {
			if (i < list.size() - 1) {
				AConjunctPredicate old = conj;
				old.setRight(list.get(i));
				conj = new AConjunctPredicate();
				conj.setLeft(old);
			} else {
				conj.setRight(list.get(i));
			}
		}
		return conj;
	}

	private PPredicate createDisjunction(List list) {
		if (list.size() == 1)
			return list.get(0);
		ADisjunctPredicate disjunction = new ADisjunctPredicate();
		disjunction.setLeft(list.get(0));
		for (int i = 1; i < list.size(); i++) {
			if (i < list.size() - 1) {
				disjunction.setRight(list.get(i));
				disjunction = new ADisjunctPredicate(disjunction, null);
			} else {
				disjunction.setRight(list.get(i));
			}
		}
		return disjunction;
	}

	public static List createTIdentifierLiteral(String name) {
		return Collections.singletonList(new TIdentifierLiteral(name));
	}

	public List createPositionedTIdentifierLiteral(String name, SemanticNode node) {
		return Collections.singletonList(createPositionedNode(new TIdentifierLiteral(name), node));
	}

	public Start getStartNode() {
		return start;
	}

	public Definitions getBDefinitions() {
		// used for the recursive machine loader
		return bDefinitions;
	}

	public Map getTypes() {
		return this.types;
	}

	public Set getSourcePositions() {
		return this.sourcePosition;
	}

	public NodeFileNumbers getNodeFileNumbers() {
		return nodeFileNumbers;
	}

	public List getFilesOrderedById() {
		return filesOrderedById;
	}

	public List getUnchangedVariablesNames() {
		return toplevelUnchangedVariableNames;
	}

	public void setUnchangedVariablesNames(List unchangedVariablesNames) {
		this.toplevelUnchangedVariableNames = unchangedVariablesNames;
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy