FunctionInfo.java

package dev.civl.mc.model.common;

import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.Stack;

import dev.civl.abc.ast.node.IF.label.LabelNode;
import dev.civl.mc.model.IF.CIVLFunction;
import dev.civl.mc.model.IF.CIVLInternalException;
import dev.civl.mc.model.IF.Fragment;
import dev.civl.mc.model.IF.Identifier;
import dev.civl.mc.model.IF.location.Location;
import dev.civl.mc.model.IF.statement.Statement;
import dev.civl.mc.model.IF.type.CIVLType;
import dev.civl.mc.model.IF.variable.Variable;
import dev.civl.mc.util.IF.Pair;

/**
 * Maintains the information, e.g. labeled location, goto statements,
 * continue/break statement stack, that is required in the translation of the
 * definition of a function from ABC AST to CIVL model.
 * 
 * @author zheng
 */
public class FunctionInfo {

	/* ************************** Instance Fields ************************** */

	/**
	 * Used to keep track of break statements in nested loops/switches. Each
	 * entry on the stack corresponds to a particular loop or switch. The
	 * statements in the set for that entry are noops which need their target
	 * set to the appropriate location at the end of the loop or switch
	 * processing.
	 */
	private Stack<Set<Statement>> breakStatements;

	/**
	 * Used to keep track of continue statements in nested loops. Each entry on
	 * the stack corresponds to a particular loop. The statements in the set for
	 * that entry are noops which need their target set to the appropriate
	 * location at the end of the loop processing.
	 */
	private Stack<Set<Statement>> continueStatements;

	/**
	 * Used to keep track of bound variables. Each entry on the stack is the
	 * identifier for a bound variable corresponding to a particular quantifier.
	 * 
	 */
	private Stack<Pair<Identifier, CIVLType>> boundVariables;
	private Stack<Set<Variable>> boundVariableStack;

	/**
	 * The current function that is being processed
	 */
	private CIVLFunction function;

	/**
	 * Maps from CIVL "goto" statements to the corresponding label nodes.
	 */
	private Map<Statement, LabelNode> gotoStatements;

	/**
	 * This fields maps ABC label nodes to the corresponding CIVL locations.
	 */
	private Map<LabelNode, Location> labeledLocations;

	/* **************************** Constructors *************************** */

	/**
	 * Create a new instance of FunctionInfo
	 * 
	 * @param function
	 *            the CIVL function object that is being processed
	 */
	public FunctionInfo(CIVLFunction function) {
		this.function = function;
		labeledLocations = new LinkedHashMap<LabelNode, Location>();
		gotoStatements = new LinkedHashMap<Statement, LabelNode>();
		continueStatements = new Stack<Set<Statement>>();
		breakStatements = new Stack<Set<Statement>>();
		boundVariables = new Stack<Pair<Identifier, CIVLType>>();
		boundVariableStack = new Stack<>();
	}

	/* *************************** Public Methods ************************** */

	/**
	 * Add a set of statements to the break statement stack <dt>
	 * <b>Preconditions:</b>
	 * <dd>
	 * A new loop or switch is just encountered
	 * 
	 * @param statementSet
	 *            an empty set of statements
	 */
	public void addBreakSet(Set<Statement> statementSet) {
		this.breakStatements.add(statementSet);
	}

	/**
	 * Add a set of statements to the continue statement stack <dt>
	 * <b>Preconditions:</b>
	 * <dd>
	 * A new loop is just encountered
	 * 
	 * @param statementSet
	 *            an empty set of statement
	 */
	public void addContinueSet(Set<Statement> statementSet) {
		this.continueStatements.add(statementSet);
	}

	/**
	 * Add an empty set to the stack of bound variables.
	 */
	public void addBoundVariableSet() {
		this.boundVariableStack.push(new HashSet<Variable>());
	}

	/**
	 * Add a bound variable to the stack of bound variables.
	 */
	public void addBoundVariable(Variable variable) {
		this.boundVariableStack.peek().add(variable);
	}

	/**
	 * Add a bound variable to the stack of bound variables.
	 */
	public void addBoundVariable(Identifier name, CIVLType type) {
		this.boundVariables.add(new Pair<Identifier, CIVLType>(name, type));
	}

	/**
	 * 
	 * @param name
	 *            The name of a bound variable.
	 * @return The CIVL type of the bound variable with the given name.
	 */
	public CIVLType boundVariableType(Identifier name) {
		for (Pair<Identifier, CIVLType> p : boundVariables) {
			if (p.left.equals(name)) {
				return p.right;
			}
		}
		throw new CIVLInternalException("Unknown bound variable",
				name.getSource());
	}

	/**
	 * Complete the function with a fragment translated from its body node,
	 * including:
	 * <ol>
	 * <li>set the target location of each goto statement, which can't be
	 * decided when the goto node is translated;</li>
	 * <li>add all locations and statements reachable from the start location of
	 * the body fragment into the function.</li>
	 * </ol>
	 * 
	 * @param functionBody
	 *            a fragment translated from the body of the function
	 */
	public void completeFunction(Fragment functionBody) {
		Location location;
		boolean first = true;
		Stack<Location> working = new Stack<>();
		Set<Location> visited = new HashSet<>();

		for (Statement stmt : gotoStatements.keySet()) {
			stmt.setTarget(labeledLocations.get(gotoStatements.get(stmt)));
		}
		// start from the start location of the fragment
		location = functionBody.startLocation();
		location.setAsStart(true);
		working.add(location);
		while (!working.isEmpty()) {
			Location current = working.pop();
			int outCount = current.getNumOutgoing();

			if (first) {
				function.setStartLocation(current);
				first = false;
			} else
				function.addLocation(current);
			for (int i = outCount - 1; i >= 0; i--) {
				Statement stmt = current.getOutgoing(i);
				Location newLoc = stmt.target();

				function.addStatement(stmt);
				if (newLoc != null)
					if (!visited.contains(newLoc)) {
						working.push(newLoc);
						visited.add(newLoc);
					}
			}
		}
	}

	/**
	 * 
	 * @param name
	 *            The name of a variable.
	 * @return Whether or not this variable is on the stack of bound variables.
	 */
	public Variable findBoundVariable(Identifier name) {
		for (Set<Variable> variables : boundVariableStack) {
			for (Variable variable : variables)
				if (variable.name().equals(name))
					return variable;
		}
		// for (Pair<Identifier, CIVLType> p : boundVariables) {
		// if (p.left.equals(name)) {
		// return true;
		// }
		// }
		return null;
	}

	/**
	 * Get the CIVL function that is being processed
	 * 
	 * @return The current function
	 */
	public CIVLFunction function() {
		return this.function;
	}

	/**
	 * Return the map of goto statements
	 * 
	 * @return mapping from goto statement to label node
	 */
	public Map<Statement, LabelNode> gotoStatements() {
		return this.gotoStatements;
	}

	/**
	 * Return the map of labeled locations
	 * 
	 * @return mapping from labeled node to locations
	 */
	public Map<LabelNode, Location> labeledLocations() {
		return this.labeledLocations;
	}

	/**
	 * Peek the break stack, called only when processing a jump node with break
	 * kind
	 * 
	 * @return the set of break statements on the top of the stack
	 */
	public Set<Statement> peekBreakStack() {
		return this.breakStatements.peek();
	}

	/**
	 * Peek the continue stack, called only when processing a jump node with
	 * continue kind
	 * 
	 * @return the set of continue statements on the top of the stack
	 */
	public Set<Statement> peekContinueStack() {
		return this.continueStatements.peek();
	}

	/**
	 * Pop the set of break statements from the stack
	 * 
	 * @return the set of break statements from the top of the stack
	 */
	public Set<Statement> popBreakStack() {
		return this.breakStatements.pop();
	}

	/**
	 * Pop the set of continue statements from the stack
	 * 
	 * @return the set of continue statements from the top of the stack
	 */
	public Set<Statement> popContinueStack() {
		return this.continueStatements.pop();
	}

	/**
	 * Pop the top identifier from the bound variable stack.
	 * 
	 * @return The identifier from the top of the bound variable stack.
	 */
	public Identifier popBoundVariableStack() {
		return boundVariables.pop().left;
	}

	/**
	 * Pop the top set from the bound variable stack.
	 * 
	 * @return The set of variables from the top of the bound variable stack.
	 */
	public Set<Variable> popBoundVariableStackNew() {
		return boundVariableStack.pop();
	}

	/**
	 * Add a mapping of a goto statement and a label node to the map of goto
	 * statements
	 * 
	 * @param statement
	 *            The goto statement
	 * @param labelNode
	 *            The label node of the target of the goto statement
	 */
	public void putToGotoStatement(Statement statement, LabelNode labelNode) {
		this.gotoStatements.put(statement, labelNode);
	}

	/**
	 * Add a mapping from a label node to a location
	 * 
	 * @param labelNode
	 *            The label node
	 * @param location
	 *            The location corresponding to the label node
	 */
	public void putToLabeledLocations(LabelNode labelNode, Location location) {
		this.labeledLocations.put(labelNode, location);
	}

}