StatementAnalyzer.java

package edu.udel.cis.vsl.abc.analysis.entity;

import java.util.Iterator;
import java.util.Stack;

import edu.udel.cis.vsl.abc.ast.conversion.IF.ConversionFactory;
import edu.udel.cis.vsl.abc.ast.entity.IF.Function;
import edu.udel.cis.vsl.abc.ast.entity.IF.Label;
import edu.udel.cis.vsl.abc.ast.node.IF.ASTNode;
import edu.udel.cis.vsl.abc.ast.node.IF.ASTNode.NodeKind;
import edu.udel.cis.vsl.abc.ast.node.IF.NodeFactory;
import edu.udel.cis.vsl.abc.ast.node.IF.PragmaNode;
import edu.udel.cis.vsl.abc.ast.node.IF.SequenceNode;
import edu.udel.cis.vsl.abc.ast.node.IF.StaticAssertionNode;
import edu.udel.cis.vsl.abc.ast.node.IF.acsl.ContractNode;
import edu.udel.cis.vsl.abc.ast.node.IF.declaration.FunctionDeclarationNode;
import edu.udel.cis.vsl.abc.ast.node.IF.declaration.TypedefDeclarationNode;
import edu.udel.cis.vsl.abc.ast.node.IF.declaration.VariableDeclarationNode;
import edu.udel.cis.vsl.abc.ast.node.IF.expression.ExpressionNode;
import edu.udel.cis.vsl.abc.ast.node.IF.expression.FunctionCallNode;
import edu.udel.cis.vsl.abc.ast.node.IF.expression.IdentifierExpressionNode;
import edu.udel.cis.vsl.abc.ast.node.IF.label.LabelNode;
import edu.udel.cis.vsl.abc.ast.node.IF.label.OrdinaryLabelNode;
import edu.udel.cis.vsl.abc.ast.node.IF.label.SwitchLabelNode;
import edu.udel.cis.vsl.abc.ast.node.IF.omp.OmpExecutableNode;
import edu.udel.cis.vsl.abc.ast.node.IF.omp.OmpExecutableNode.OmpExecutableKind;
import edu.udel.cis.vsl.abc.ast.node.IF.omp.OmpForNode;
import edu.udel.cis.vsl.abc.ast.node.IF.omp.OmpFunctionReductionNode;
import edu.udel.cis.vsl.abc.ast.node.IF.omp.OmpNode;
import edu.udel.cis.vsl.abc.ast.node.IF.omp.OmpNode.OmpNodeKind;
import edu.udel.cis.vsl.abc.ast.node.IF.omp.OmpParallelNode;
import edu.udel.cis.vsl.abc.ast.node.IF.omp.OmpReductionNode;
import edu.udel.cis.vsl.abc.ast.node.IF.omp.OmpReductionNode.OmpReductionNodeKind;
import edu.udel.cis.vsl.abc.ast.node.IF.omp.OmpWorksharingNode;
import edu.udel.cis.vsl.abc.ast.node.IF.statement.AtomicNode;
import edu.udel.cis.vsl.abc.ast.node.IF.statement.BlockItemNode;
import edu.udel.cis.vsl.abc.ast.node.IF.statement.ChooseStatementNode;
import edu.udel.cis.vsl.abc.ast.node.IF.statement.CivlForNode;
import edu.udel.cis.vsl.abc.ast.node.IF.statement.CompoundStatementNode;
import edu.udel.cis.vsl.abc.ast.node.IF.statement.DeclarationListNode;
import edu.udel.cis.vsl.abc.ast.node.IF.statement.ExpressionStatementNode;
import edu.udel.cis.vsl.abc.ast.node.IF.statement.ForLoopInitializerNode;
import edu.udel.cis.vsl.abc.ast.node.IF.statement.ForLoopNode;
import edu.udel.cis.vsl.abc.ast.node.IF.statement.IfNode;
import edu.udel.cis.vsl.abc.ast.node.IF.statement.JumpNode;
import edu.udel.cis.vsl.abc.ast.node.IF.statement.JumpNode.JumpKind;
import edu.udel.cis.vsl.abc.ast.node.IF.statement.LabeledStatementNode;
import edu.udel.cis.vsl.abc.ast.node.IF.statement.LoopNode;
import edu.udel.cis.vsl.abc.ast.node.IF.statement.ReturnNode;
import edu.udel.cis.vsl.abc.ast.node.IF.statement.RunNode;
import edu.udel.cis.vsl.abc.ast.node.IF.statement.StatementNode;
import edu.udel.cis.vsl.abc.ast.node.IF.statement.StatementNode.StatementKind;
import edu.udel.cis.vsl.abc.ast.node.IF.statement.SwitchNode;
import edu.udel.cis.vsl.abc.ast.node.IF.statement.UpdateNode;
import edu.udel.cis.vsl.abc.ast.node.IF.statement.WhenNode;
import edu.udel.cis.vsl.abc.ast.node.IF.statement.WithNode;
import edu.udel.cis.vsl.abc.ast.node.IF.type.EnumerationTypeNode;
import edu.udel.cis.vsl.abc.ast.node.IF.type.StructureOrUnionTypeNode;
import edu.udel.cis.vsl.abc.ast.type.IF.DomainType;
import edu.udel.cis.vsl.abc.ast.type.IF.IntegerType;
import edu.udel.cis.vsl.abc.ast.type.IF.ObjectType;
import edu.udel.cis.vsl.abc.ast.type.IF.Type;
import edu.udel.cis.vsl.abc.ast.type.IF.Type.TypeKind;
import edu.udel.cis.vsl.abc.ast.type.IF.TypeFactory;
import edu.udel.cis.vsl.abc.ast.value.IF.Value;
import edu.udel.cis.vsl.abc.config.IF.Configuration;
import edu.udel.cis.vsl.abc.err.IF.ABCRuntimeException;
import edu.udel.cis.vsl.abc.token.IF.SyntaxException;
import edu.udel.cis.vsl.abc.token.IF.UnsourcedException;

//import edu.udel.cis.vsl.abc.ast.node.IF.statement.AssertNode;

public class StatementAnalyzer {

	// ***************************** Fields *******************************

	private EntityAnalyzer entityAnalyzer;

	private NodeFactory nodeFactory;

	private ExpressionAnalyzer expressionAnalyzer;

	private TypeFactory typeFactory;

	private ConversionFactory conversionFactory;

	private Configuration configuration;

	private AcslContractAnalyzer acslAnalyzer;

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

	StatementAnalyzer(EntityAnalyzer entityAnalyzer,
			ExpressionAnalyzer expressionAnalyzer,
			ConversionFactory conversionFactory, TypeFactory typeFactory,
			Configuration config) {
		this.entityAnalyzer = entityAnalyzer;
		this.nodeFactory = entityAnalyzer.nodeFactory;
		this.expressionAnalyzer = expressionAnalyzer;
		this.conversionFactory = conversionFactory;
		this.typeFactory = typeFactory;
		this.configuration = config;
		this.acslAnalyzer = new AcslContractAnalyzer(entityAnalyzer,
				conversionFactory);
	}

	// ************************* Private Methods **************************

	private SyntaxException error(String message, ASTNode node) {
		return entityAnalyzer.error(message, node);
	}

	private SyntaxException error(UnsourcedException e, ASTNode node) {
		return entityAnalyzer.error(e, node);
	}

	private void processExpression(ExpressionNode expression)
			throws SyntaxException {
		if (expression != null)
			expressionAnalyzer.processExpression(expression);
	}

	private void processIf(IfNode node) throws SyntaxException {
		processExpression(node.getCondition());
		processStatement(node.getTrueBranch());
		if (node.getFalseBranch() != null)
			processStatement(node.getFalseBranch());
	}

	private SwitchNode enclosingSwitch(SwitchLabelNode labelNode) {
		for (ASTNode node = labelNode.parent(); node != null; node = node
				.parent()) {
			if (node instanceof SwitchNode)
				return (SwitchNode) node;
		}
		return null;
	}

	private ASTNode enclosingSwitchOrChoose(SwitchLabelNode labelNode) {
		for (ASTNode node = labelNode.parent(); node != null; node = node
				.parent()) {
			if (node instanceof SwitchNode
					|| node instanceof ChooseStatementNode)
				return node;
		}
		return null;
	}

	private void processLabeledStatement(LabeledStatementNode node)
			throws SyntaxException {
		LabelNode labelNode = node.getLabel();
		StatementNode statementNode = node.getStatement();
		Function function = entityAnalyzer.enclosingFunction(node);

		if (function == null)
			throw error("Label occurs outside of function", node);
		labelNode.setStatement(statementNode);
		if (labelNode instanceof OrdinaryLabelNode)
			processOrdinaryLabel((OrdinaryLabelNode) labelNode, function);
		else if (labelNode instanceof SwitchLabelNode)
			processSwitchLabel(node, (SwitchLabelNode) labelNode, function);
		else
			throw new RuntimeException("unreachable");
		processStatement(statementNode);

	}

	private void processOrdinaryLabel(OrdinaryLabelNode node, Function function)
			throws SyntaxException {
		Label label = entityAnalyzer.entityFactory.newLabel(node);

		node.setFunction(function);
		node.setEntity(label);
		node.getIdentifier().setEntity(label);
		try {
			function.getScope().add(label);
		} catch (UnsourcedException e) {
			throw error(e, node);
		}
	}

	private void processSwitchLabel(LabeledStatementNode labeledStatement,
			SwitchLabelNode switchLabel, Function function)
			throws SyntaxException {

		if (switchLabel.isDefault()) {
			ASTNode enclosing = enclosingSwitchOrChoose(switchLabel);

			if (enclosing instanceof ChooseStatementNode) {
				ChooseStatementNode choose = (ChooseStatementNode) enclosing;
				LabeledStatementNode oldDefault = choose.getDefaultCase();

				if (oldDefault != null)
					throw error(
							"Two default cases in choose statement.  First was at "
									+ oldDefault.getSource(),
							switchLabel);
				choose.setDefaultCase(labeledStatement);
				return;
			}
		}

		SwitchNode switchNode = enclosingSwitch(switchLabel);

		if (switchNode == null)
			throw error("Switch label occurs outside of any switch statement",
					switchLabel);
		if (switchLabel.isDefault()) {
			LabeledStatementNode oldDefault = switchNode.getDefaultCase();

			if (oldDefault != null)
				throw error(
						"Two default cases in switch statement.  First was at "
								+ oldDefault.getSource(),
						switchLabel);
			switchNode.setDefaultCase(labeledStatement);
		} else {
			ExpressionNode caseExpression = switchLabel.getExpression();
			Iterator<LabeledStatementNode> cases = switchNode.getCases();
			Value constant;

			expressionAnalyzer.processExpression(caseExpression);
			if (!caseExpression.isConstantExpression()) {
				throw error("Case expression not constant", caseExpression);
			}
			constant = nodeFactory.getConstantValue(caseExpression);
			while (cases.hasNext()) {
				SwitchLabelNode labelNode = (SwitchLabelNode) cases.next()
						.getLabel();
				ExpressionNode oldExpression = labelNode.getExpression();
				Value oldConstant = nodeFactory.getConstantValue(oldExpression);

				if (constant.equals(oldConstant))
					throw error(
							"Case constant appears twice: first time was at "
									+ oldExpression,
							caseExpression);
			}
			switchNode.addCase(labeledStatement);
		}
	}

	private void processJump(JumpNode statement) throws SyntaxException {
		switch (statement.getKind()) {
			case RETURN : {
				ExpressionNode expression = ((ReturnNode) statement)
						.getExpression();
				Function function = entityAnalyzer.enclosingFunction(statement);
				ObjectType returnType = function.getType().getReturnType();
				boolean returnTypeIsVoid = returnType.kind() == TypeKind.VOID;

				if (expression == null) {
					if (!this.configuration.getSVCOMP() && !returnTypeIsVoid)
						throw error("Missing expression in return statement",
								statement);
				} else {
					if (returnTypeIsVoid)
						throw error(
								"Argument for return in function returning void",
								statement);
					if (expression != null)
						processExpression(expression);
					try {
						expressionAnalyzer.processAssignment(returnType,
								expression);
					} catch (UnsourcedException e) {
						throw error(e, expression);
					}
				}
			}
			case GOTO : // taken care of later in processGotos
			case BREAK : // nothing to do
			case CONTINUE : // nothing to do
				break;
			default :
				throw new RuntimeException("Unreachable");
		}
	}

	private void processLoop(LoopNode loopNode) throws SyntaxException {
		SequenceNode<ContractNode> loopContracts = loopNode.loopContracts();

		switch (loopNode.getKind()) {
			case WHILE :
				processExpression(loopNode.getCondition());
				processStatement(loopNode.getBody());
				// processExpression(loopNode.getInvariant());
				break;
			case DO_WHILE :
				processStatement(loopNode.getBody());
				processExpression(loopNode.getCondition());
				// processExpression(loopNode.getInvariant());
				break;
			case FOR : {
				ForLoopNode forNode = (ForLoopNode) loopNode;
				ForLoopInitializerNode initializer = forNode.getInitializer();

				if (initializer == null) {
				} else if (initializer instanceof ExpressionNode) {
					processExpression((ExpressionNode) initializer);
				} else if (initializer instanceof DeclarationListNode) {
					DeclarationListNode declarationList = (DeclarationListNode) initializer;

					for (VariableDeclarationNode child : declarationList) {
						if (child == null)
							continue;
						entityAnalyzer.declarationAnalyzer
								.processVariableDeclaration(child);
					}
				} else
					throw error(
							"Unknown kind of initializer clause in for loop",
							initializer);
				processExpression(loopNode.getCondition());
				processExpression(forNode.getIncrementer());
				processStatement(loopNode.getBody());
				// processExpression(loopNode.getInvariant());
				break;
			}
			default :
				throw new RuntimeException("Unreachable");
		}
		if (loopContracts != null) {
			acslAnalyzer.processLoopContractNodes(loopContracts);
		}
	}

	private void processCivlFor(CivlForNode node) throws SyntaxException {
		ExpressionNode domainNode = node.getDomain();
		int numVars = 0;
		Type domainNodeType;
		DomainType domainType;
		int domainDimension;

		for (VariableDeclarationNode child : node.getVariables()) {
			Type type;

			entityAnalyzer.declarationAnalyzer
					.processVariableDeclaration(child);
			if (child.getInitializer() != null)
				throw error(
						"Loop variable " + numVars
								+ " in $for/$parfor statement has initializer",
						child);
			type = child.getTypeNode().getType();
			if (!(type instanceof IntegerType))
				throw error("Loop variable " + numVars
						+ " in $for/$parfor has non-integer type: " + type,
						child.getTypeNode());
			numVars++;
		}
		expressionAnalyzer.processExpression(domainNode);
		domainNodeType = domainNode.getConvertedType();
		if (domainNodeType.equals(typeFactory.rangeType())) {
			domainNode.addConversion(conversionFactory
					.regularRangeToDomainConversion((ObjectType) domainNodeType,
							typeFactory.domainType(1)));
			domainNodeType = domainNode.getConvertedType();
		} else if (!(domainNodeType instanceof DomainType))
			throw error(
					"Domain expression in $for/$parfor does not have $domain type",
					domainNode);
		domainType = (DomainType) domainNodeType;
		if (!domainType.hasDimension())
			throw error("Use of incomplete domain type in $for/$parfor",
					domainNode);
		domainDimension = domainType.getDimension();
		if (domainDimension != numVars)
			throw error("Dimension of domain (" + domainDimension + ") "
					+ "does not equal number of loop variables (" + numVars
					+ ")", domainNode);
		processStatement(node.getBody());
		if (node.loopContracts() != null)
			acslAnalyzer.processLoopContractNodes(node.loopContracts());
	}

	// ************************* Exported Methods **************************

	void processStatement(StatementNode statement) throws SyntaxException {
		StatementKind kind = statement.statementKind();

		switch (kind) {
			case COMPOUND :
				processCompoundStatement((CompoundStatementNode) statement);
				break;
			case EXPRESSION :
				processExpression(
						((ExpressionStatementNode) statement).getExpression());
				break;
			case IF :
				processIf((IfNode) statement);
				break;
			case JUMP :
				processJump((JumpNode) statement);
				break;
			case LABELED :
				processLabeledStatement((LabeledStatementNode) statement);
				break;
			case LOOP :
				processLoop((LoopNode) statement);
				break;
			case SWITCH :
				processExpression(((SwitchNode) statement).getCondition());
				processStatement(((SwitchNode) statement).getBody());
				break;
			case PRAGMA :
				entityAnalyzer.processPragma((PragmaNode) statement);
				break;
			case RUN :
				processRunStatement((RunNode) statement);
				break;
			case OMP :
				processOmpNode((OmpNode) statement);
				break;
			case NULL :
				break;
			case WHEN : {
				ExpressionNode guard = ((WhenNode) statement).getGuard();
				Type guardType;

				processExpression(guard);
				if (!guard.isSideEffectFree(false))
					throw this.error(
							"the guard of a $when statement is not allowed to have side effects.",
							guard);
				guardType = guard.getConvertedType();
				// check guardType can be converted to a boolean...
				if (!guardType.isScalar())
					throw error("Guard has non-scalar type " + guardType,
							guard);
				processStatement(((WhenNode) statement).getBody());
				break;
			}
			case WITH : {
				WithNode withNode = (WithNode) statement;
				ExpressionNode stateRef = withNode.getStateReference();
				Type stateType;

				processExpression(stateRef);
				stateType = stateRef.getConvertedType();
				if (!entityAnalyzer.standardTypes.isCollateStateType(stateType))
					throw this.error(
							"The state reference expression of $with doesn't have type of collate statet",
							statement);
				processStatement(withNode.getBodyNode());
				break;
			}
			case UPDATE : {
				UpdateNode updateNode = (UpdateNode) statement;

				processExpression(updateNode.getCollator());
				processExpression(updateNode.getFunctionCall());
				break;
			}
			case CHOOSE : {
				ChooseStatementNode chooseStatement = (ChooseStatementNode) statement;

				for (StatementNode child : chooseStatement)
					processStatement(child);
				break;
			}
			case ATOMIC :
				processStatement(((AtomicNode) statement).getBody());
				break;
			case CIVL_FOR :
				processCivlFor((CivlForNode) statement);
				break;
			default :
				throw error("Unknown kind of statement", statement);
		}
	}

	private void processOmpNode(OmpNode ompNode) throws SyntaxException {
		OmpNodeKind ompKind = ompNode.ompNodeKind();

		switch (ompKind) {
			case EXECUTABLE :
				processOmpExecutableNode((OmpExecutableNode) ompNode);
				break;
			case DECLARATIVE :
			default :

		}
	}

	/**
	 * Process the body statements of a $run statement. Apply checking for no
	 * return node is allowed inside the body of the $run statement.
	 * 
	 * @param runNode
	 *            The {@link RunNode}
	 * @throws SyntaxException
	 */
	private void processRunStatement(RunNode runNode) throws SyntaxException {
		processStatement(runNode.getStatement());

		StatementNode body = runNode.getStatement();
		ASTNode next = body;
		Stack<ASTNode> children = new Stack<ASTNode>();

		children.push(next);
		while (!children.isEmpty()) {
			ASTNode item = children.pop();

			if (item != null) {
				if (item.nodeKind() == NodeKind.STATEMENT)
					if (((StatementNode) item)
							.statementKind() == StatementKind.JUMP)
						if (((JumpNode) item).getKind() == JumpKind.RETURN)
							throw error(
									"No return statement is allowed to be inside a $run statement block.",
									item);
				for (ASTNode child : item.children())
					children.push(child);
			}
		}
	}

	private void processOmpExecutableNode(OmpExecutableNode statement)
			throws SyntaxException {
		OmpExecutableKind kind = statement.ompExecutableKind();
		SequenceNode<OmpReductionNode> reductionList = (SequenceNode<OmpReductionNode>) statement
				.reductionList();

		for (int i = 0; i <= 5; i++) {
			@SuppressWarnings("unchecked")
			SequenceNode<ExpressionNode> list = (SequenceNode<ExpressionNode>) statement
					.child(i);

			if (list != null) {
				int count = list.numChildren();

				for (int j = 0; j < count; j++) {
					this.expressionAnalyzer
							.processExpression(list.getSequenceChild(j));
				}
			}
		}
		if (reductionList != null) {
			int count = reductionList.numChildren();

			for (int j = 0; j < count; j++) {
				this.processOmpReductionNode(reductionList.getSequenceChild(j));
			}
		}
		switch (kind) {
			case PARALLEL :
				OmpParallelNode parallel = (OmpParallelNode) statement;

				if (parallel.ifClause() != null)
					processExpression(parallel.ifClause());
				if (parallel.numThreads() != null)
					processExpression(parallel.numThreads());
				break;
			case WORKSHARING :
				OmpWorksharingNode workshare = (OmpWorksharingNode) statement;

				switch (workshare.ompWorkshareNodeKind()) {
					case FOR :
						OmpForNode forNode = (OmpForNode) statement;
						SequenceNode<FunctionCallNode> assertions = forNode
								.assertions();
						FunctionCallNode invariant = forNode.invariant();
						ExpressionNode chunkSize = forNode.chunkSize();

						if (assertions != null) {
							for (FunctionCallNode node : assertions)
								processExpression(node);
						}
						if (invariant != null)
							processExpression(invariant);
						if (chunkSize != null)
							processExpression(chunkSize);
						break;
					default :
				}
				break;
			case SIMD :
				// TODO: there are more of SIMD clauses under construction
			case SYNCHRONIZATION :
				break;
			default :
				throw new ABCRuntimeException(
						"unknown OpenMP executable node kind to StatementAnalyzer: "
								+ kind + "\nStatement: "
								+ statement.prettyRepresentation());
		}
		if (statement.statementNode() != null)
			processStatement(statement.statementNode());
	}

	private void processOmpReductionNode(OmpReductionNode reduction)
			throws SyntaxException {
		OmpReductionNodeKind kind = reduction.ompReductionOperatorNodeKind();
		SequenceNode<IdentifierExpressionNode> list = reduction.variables();
		int count = list.numChildren();

		if (kind == OmpReductionNodeKind.FUNCTION) {
			this.expressionAnalyzer.processExpression(
					((OmpFunctionReductionNode) reduction).function());
		}
		for (int i = 0; i < count; i++) {
			this.expressionAnalyzer.processExpression(list.getSequenceChild(i));
		}
	}

	/**
	 * <ul>
	 * <li>StatementNode</li> (includes PragmaNode)
	 * <li>StructureOrUnionTypeNode</li>
	 * <li>EnumerationTypeNode</li>
	 * <li>StaticAssertionNode</li>
	 * <li>VariableDeclarationNode</li>
	 * <li>FunctionDeclarationNode</li> (but not a FunctionDefinitionNode)
	 * <li>TypedefDeclarationNode</li>
	 * </ul>
	 * 
	 * @param node
	 * @throws SyntaxException
	 */
	void processCompoundStatement(CompoundStatementNode node)
			throws SyntaxException {
		for (BlockItemNode item : node) {
			if (item == null)
				continue;
			if (item instanceof StatementNode)
				processStatement((StatementNode) item);
			else if (item instanceof StructureOrUnionTypeNode)
				entityAnalyzer.typeAnalyzer.processStructureOrUnionType(
						(StructureOrUnionTypeNode) item);
			else if (item instanceof EnumerationTypeNode)
				entityAnalyzer.typeAnalyzer
						.processEnumerationType((EnumerationTypeNode) item);
			else if (item instanceof StaticAssertionNode)
				entityAnalyzer
						.processStaticAssertion((StaticAssertionNode) item);
			else if (item instanceof VariableDeclarationNode)
				entityAnalyzer.declarationAnalyzer.processVariableDeclaration(
						(VariableDeclarationNode) item);
			else if (item instanceof FunctionDeclarationNode)
				entityAnalyzer.declarationAnalyzer.processFunctionDeclaration(
						(FunctionDeclarationNode) item);
			else if (item instanceof TypedefDeclarationNode)
				entityAnalyzer.declarationAnalyzer.processTypedefDeclaration(
						(TypedefDeclarationNode) item);
			else
				throw error("Unknown kind of block item", item);
		}
	}
}