StringOrCompoundInitializerTranslateWorker.java

package edu.udel.cis.vsl.abc.transform.common;

import java.util.LinkedList;
import java.util.List;
import java.util.function.BiFunction;

import edu.udel.cis.vsl.abc.ast.node.IF.NodeFactory;
import edu.udel.cis.vsl.abc.ast.node.IF.PairNode;
import edu.udel.cis.vsl.abc.ast.node.IF.compound.CompoundInitializerNode;
import edu.udel.cis.vsl.abc.ast.node.IF.compound.CompoundLiteralObject;
import edu.udel.cis.vsl.abc.ast.node.IF.compound.DesignationNode;
import edu.udel.cis.vsl.abc.ast.node.IF.compound.LiteralObject;
import edu.udel.cis.vsl.abc.ast.node.IF.compound.ScalarLiteralObject;
import edu.udel.cis.vsl.abc.ast.node.IF.declaration.InitializerNode;
import edu.udel.cis.vsl.abc.ast.node.IF.declaration.VariableDeclarationNode;
import edu.udel.cis.vsl.abc.ast.node.IF.expression.ConstantNode;
import edu.udel.cis.vsl.abc.ast.node.IF.expression.ExpressionNode;
import edu.udel.cis.vsl.abc.ast.node.IF.expression.ExpressionNode.ExpressionKind;
import edu.udel.cis.vsl.abc.ast.node.IF.expression.OperatorNode.Operator;
import edu.udel.cis.vsl.abc.ast.node.IF.expression.StringLiteralNode;
import edu.udel.cis.vsl.abc.ast.node.IF.statement.BlockItemNode;
import edu.udel.cis.vsl.abc.ast.node.IF.statement.DeclarationListNode;
import edu.udel.cis.vsl.abc.ast.node.IF.statement.StatementNode;
import edu.udel.cis.vsl.abc.ast.node.IF.type.TypeNode;
import edu.udel.cis.vsl.abc.ast.type.IF.ArrayType;
import edu.udel.cis.vsl.abc.ast.type.IF.AtomicType;
import edu.udel.cis.vsl.abc.ast.type.IF.Field;
import edu.udel.cis.vsl.abc.ast.type.IF.QualifiedObjectType;
import edu.udel.cis.vsl.abc.ast.type.IF.StandardBasicType.BasicTypeKind;
import edu.udel.cis.vsl.abc.ast.type.IF.StandardSignedIntegerType.SignedIntKind;
import edu.udel.cis.vsl.abc.ast.type.IF.StructureOrUnionType;
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.type.IF.UnqualifiedObjectType;
import edu.udel.cis.vsl.abc.ast.value.IF.StringValue;
import edu.udel.cis.vsl.abc.ast.value.IF.Value;
import edu.udel.cis.vsl.abc.ast.value.IF.ValueFactory.Answer;
import edu.udel.cis.vsl.abc.config.IF.Configurations.Language;
import edu.udel.cis.vsl.abc.err.IF.ABCRuntimeException;
import edu.udel.cis.vsl.abc.token.IF.Source;
import edu.udel.cis.vsl.abc.token.IF.StringLiteral;

/**
 * <p>
 * This class translates a {@link CompoundInitializerNode} and an expression,
 * which represents the object that is associated to the compound initializer,
 * to a sequence of assignments.
 * </p>
 * 
 * <p>
 * <b>Foe example</b> expression : <code>e : struct</code> ,initializer :
 * <code>{a, b, c}</code>, the translation result is
 * <code>e.0 = a; e.1 = b; e.2 = c;</code>
 * </p>
 * 
 * <p>
 * Notes:
 * <ul>
 * <li>This class <b>ASSUMES</b> that the aggregate object that is associated
 * with the compound initializer has been set to have its default value if it
 * has or is suppose to be treated as having static storage.</li>
 * <li>This class guarantees that the output contains no compound initializer
 * node <b>EXCEPT FOR</b> $domain initializers</li>
 * <li>TODO: this worker currently is used by the {@link SideEffectRemover} due
 * to the fact that side-effect remover has a mature structure for traversing a
 * tree and factoring statements from expressions. Semantically, this is not a
 * "side-effect removing" work.</li>
 * </ul>
 * </p>
 * 
 * @author ziqing
 *
 * 
 */
class StringOrCompoundInitializerTranslateWorker {

	private NodeFactory nodeFactory;

	private BiFunction<Source, Type, VariableDeclarationNode> tmpVarCreator;

	private BiFunction<Source, Type, TypeNode> typeNodeCreator;

	private Language language;

	// constructor
	StringOrCompoundInitializerTranslateWorker(NodeFactory nodeFactory,
			BiFunction<Source, Type, VariableDeclarationNode> tmpVarCreator,
			BiFunction<Source, Type, TypeNode> typeNodeCreator,
			Language language) {
		this.nodeFactory = nodeFactory;
		this.tmpVarCreator = tmpVarCreator;
		this.typeNodeCreator = typeNodeCreator;
		this.language = language;
	}

	/**
	 * <p>
	 * translates a {@link CompoundInitializerNode} and an expression, which
	 * represents the object that is associated to the compound initializer, to
	 * a sequence of assignments. Note that the assignments are NOT guaranteed
	 * side-effect free.
	 * </p>
	 * 
	 * @param compound
	 *            a compound initializer
	 * @param objExpr
	 *            an expression that represents the object that will be
	 *            initialized by the compound initializer
	 * @return a list of assignments which deliver the same functionality as the
	 *         compound initializer
	 */
	List<BlockItemNode> translateCompoundInitializer(
			CompoundInitializerNode compound, ExpressionNode objExpr) {
		LiteralObject lt = compound.getLiteralObject();

		return translateInitializerWorker(objExpr, lt);
	}

	/**
	 * <p>
	 * Given an char array expression and a string literal, translates the
	 * string literal to a sequence of assignments to the array. Note that the
	 * assignments are NOT guaranteed side-effect free.
	 * </p>
	 * 
	 * <p>
	 * Assuming the array has assigned default value '0's
	 * </p>
	 * 
	 * @param stringLit
	 *            a string literal node
	 * @param lhs
	 *            an expression node of array of char type
	 * @return a list of assignment expressions
	 */
	List<BlockItemNode> translateStringLiteralInitializer(
			StringLiteralNode stringLitNode, ExpressionNode lhs) {
		StringValue strVal = stringLitNode.getConstantValue();
		StringLiteral strLit = strVal.getLiteral();

		return translateStringLiteralInitializerWorker(lhs,
				stringLitNode.getType(), strLit, stringLitNode.getSource());
	}

	private List<BlockItemNode> translateInitializerWorker(ExpressionNode obj,
			LiteralObject litObj) {
		List<BlockItemNode> results = new LinkedList<>();

		if (litObj instanceof CompoundLiteralObject) {
			// compound:
			CompoundLiteralObject compoundLitObj = (CompoundLiteralObject) litObj;
			int size = compoundLitObj.size();
			Source source = obj.getSource();
			Type dequalifiedType = litObj.getType();

			if (dequalifiedType.kind() == TypeKind.QUALIFIED)
				dequalifiedType = ((QualifiedObjectType) dequalifiedType)
						.getBaseType();
			if (dequalifiedType.kind() == TypeKind.ATOMIC)
				dequalifiedType = ((AtomicType) dequalifiedType).getBaseType();
			if (dequalifiedType.kind() == TypeKind.ARRAY)
				for (int i = 0; i < size; i++) {
					ExpressionNode subObj = nodeFactory.newOperatorNode(source,
							Operator.SUBSCRIPT, obj.copy(),
							nodeFactory.newIntConstantNode(source, i));

					results.addAll(translateInitializerWorker(subObj,
							compoundLitObj.get(i)));
				}
			else if (dequalifiedType.kind() == TypeKind.STRUCTURE_OR_UNION) {
				StructureOrUnionType type = (StructureOrUnionType) litObj
						.getType();
				int i = 0;

				if (type.isUnion())
					i = size - 1;
				for (; i < size; i++) {
					ExpressionNode subObj = nodeFactory.newDotNode(source,
							obj.copy(), nodeFactory.newIdentifierNode(source,
									type.getField(i).getName()));

					results.addAll(translateInitializerWorker(subObj,
							compoundLitObj.get(i)));
				}
			}
		} else {
			// scalar (including $domain type):
			ExpressionNode init = ((ScalarLiteralObject) litObj)
					.getExpression();

			init.remove();
			// no need to assign 0s to scalar, which should have been dealt with
			// by its default value:
			if (init.expressionKind() == ExpressionKind.CONSTANT) {
				Value val = ((ConstantNode) init).getConstantValue();

				if (val != null && val.getType().kind() == TypeKind.BASIC)
					if (val.isZero() == Answer.YES)
						return results;
				if (val instanceof StringValue)
					return translateStringLiteralInitializerWorker(obj,
							litObj.getType(), ((StringValue) val).getLiteral(),
							init.getSource());
			}
			results.add(nodeFactory.newExpressionStatementNode(
					nodeFactory.newOperatorNode(init.getSource(),
							Operator.ASSIGN, obj.copy(), init)));
		}
		return results;
	}

	/**
	 * <p>
	 * Translates a string literal initialization. There are two cases:
	 * <ul>
	 * <li>If the initialization has the form:
	 * <code>char * obj = string-literal</code>, the translation will be as
	 * follows: <code>
	 * char tmp[size(string-literal)];
	 * 
	 * // replacing "obj" with tmp then recursively call this method ...
	 * obj = tmp;
	 * </code></li>
	 * <li>If the initialization has the form:
	 * <code>char obj[c] = string-literal</code>, where <code>c</code> is either
	 * a fixed length or absent, the translation will be as follows: <code>
	 * obj[0] = string-literal[0];
	 * obj[1] = string-literal[1];
	 * ...
	 * </code>, where <code>string-literal[i]</code> means the i-th character in
	 * the given string literal.</li>
	 * </ul>
	 * 
	 * </p>
	 * 
	 * @param obj
	 *            the object that will be initialized, suppose to either have
	 *            array-of-char type or pointer-to-char type but this node may
	 *            not have been assigned type
	 * @param stringLiteralType
	 *            the type of the string literal expression node. The type of
	 *            this expression after applying all conversions reflects the
	 *            type of the "obj"
	 * @param strlit
	 *            the {@link StringLiteral} value of the string literal
	 *            expression
	 * @param strLitSource
	 *            the {@link Source} of the string literal expression
	 * @return a list of translated statements
	 */
	private List<BlockItemNode> translateStringLiteralInitializerWorker(
			ExpressionNode obj, Type stringLiteralType, StringLiteral strlit,
			Source strLitSource) {
		List<BlockItemNode> results = new LinkedList<>();
		ExpressionNode newInit;

		// at this point, "obj" may not have type but the type of the literal
		// object reflects whether the object has array-of-char type or
		// pointer-to-char type:
		if (stringLiteralType.isScalar()) {
			TypeFactory tf = nodeFactory.typeFactory();
			ArrayType charArrayType = tf.arrayType(
					tf.basicType(BasicTypeKind.CHAR),
					nodeFactory.getValueFactory().integerValue(
							tf.signedIntegerType(SignedIntKind.INT),
							strlit.getNumCharacters()));
			VariableDeclarationNode tmpVarDecl = tmpVarCreator
					.apply(strLitSource, charArrayType);

			results.add(tmpVarDecl);
			newInit = nodeFactory.newIdentifierExpressionNode(strLitSource,
					tmpVarDecl.getIdentifier().copy());
			results.addAll(translateStringLiteralInitializerWorker(newInit,
					charArrayType, strlit, strLitSource));
			results.add(nodeFactory.newExpressionStatementNode(
					nodeFactory.newOperatorNode(newInit.getSource(),
							Operator.ASSIGN, obj.copy(), newInit.copy())));
		} else {
			assert stringLiteralType.kind() == TypeKind.ARRAY;
			StringLiteral strLit = strlit;
			int size = strLit.getNumCharacters();

			for (int i = 0; i < size; i++) {
				ExpressionNode subObj = nodeFactory.newOperatorNode(
						strLitSource, Operator.SUBSCRIPT, obj.copy(),
						nodeFactory.newIntConstantNode(strLitSource, i));

				newInit = nodeFactory.newOperatorNode(strLitSource,
						Operator.ASSIGN, subObj,
						nodeFactory.newCharacterConstantNode(strLitSource,
								strLit.getCharacter(i).rawString(),
								strLit.getCharacter(i)));
				results.add(nodeFactory.newExpressionStatementNode(newInit));
			}
		}
		return results;
	}

	/* *************** methods for setting default values **************** */
	/**
	 * <p>
	 * Assigns a default value to an object as if the object has static storage
	 * </p>
	 * 
	 * @param obj
	 *            the object that will be assigned
	 * @param objType
	 *            the type of the object
	 * @return a triple that either 1) only contains "after" statements (i.e. no
	 *         expression, no before statements); 2) before statements,
	 *         translated initializer and after statements.
	 */
	ExprTriple defaultValues(ExpressionNode obj, Type objType) {
		Source source = obj.getSource();

		// de-qualifiers:
		if (objType.kind() == TypeKind.QUALIFIED)
			objType = ((QualifiedObjectType) objType).getBaseType();
		if (objType.isScalar())
			return new ExprTriple(defaultValueOfScalarType(objType, source));
		else {
			if (objType.kind() == TypeKind.ARRAY)
				return defaultValuesToArray(obj, (ArrayType) objType,
						obj.getSource());
			else if (objType.kind() == TypeKind.STRUCTURE_OR_UNION) {
				ExprTriple result = new ExprTriple(null);

				result.addAllAfter(defaultValuesToStructOrUnion(obj,
						(StructureOrUnionType) objType));
				return result;
			} else {
				TypeKind kind = objType.kind();

				if (kind == TypeKind.DOMAIN || kind == TypeKind.RANGE) {
					ConstantNode zero = nodeFactory.newIntConstantNode(source,
							0);
					ExpressionNode r = nodeFactory.newRegularRangeNode(source,
							zero, zero.copy());

					if (kind == TypeKind.DOMAIN) {
						List<PairNode<DesignationNode, InitializerNode>> initList = new LinkedList<>();
						TypeNode domainTypeNode = nodeFactory
								.newDomainTypeNode(source);

						initList.add(nodeFactory.newPairNode(source, null, r));
						r = nodeFactory.newCompoundLiteralNode(source,
								domainTypeNode,
								nodeFactory.newCompoundInitializerNode(source,
										initList));
					}
					return new ExprTriple(r);
				}
				throw new ABCRuntimeException(
						"Unexpected aggregate type kind: " + kind);
			}
		}
	}

	/**
	 * <p>
	 * This method returns two kinds of default values for arrays (as if the
	 * arrays have static storage):
	 * <ul>
	 * <li>If the current language is CIVL-C language, default values of arrays
	 * are array lambdas, which can be used to initialize array with either
	 * constant or variable length.</li>
	 * <li>Otherwise, to conform C11 standard, no initializer expression but a
	 * sequence of assignments will be given for initialization of arrays with
	 * constant length. Attemptence to initialize variable size array will be
	 * reported.</li>
	 * </ul>
	 * </p>
	 *
	 * @param array
	 *            an array object
	 * @param arrType
	 *            the array type of the object
	 * @return triple that contains an array lambda expression that is
	 *         representing the default value with a sequence of "before"
	 *         statements; no "after" statement.
	 */
	private ExprTriple defaultValuesToArray(ExpressionNode arr,
			ArrayType arrType, Source source) {
		if (language == Language.CIVL_C)
			return defaultValuesToArrayLambda(arrType, source);
		else
			return defaultValuesToArrayStrict(arr, arrType, source);
	}

	/**
	 * worker method of {@link #defaultValuesToArray(ArrayType, Source)} for
	 * creating array lambda kind default value
	 */
	private ExprTriple defaultValuesToArrayLambda(ArrayType arrType,
			Source source) {
		ExprTriple result;
		Type baseType = arrType;
		ExpressionNode elementDefaultVal;
		int dims = arrType.getDimension();
		List<VariableDeclarationNode> varDecls = new LinkedList<>();
		List<BlockItemNode> before = new LinkedList<BlockItemNode>();

		for (int i = 0; i < dims; i++) {
			baseType = ((ArrayType) baseType).getElementType();
			varDecls.add(nodeFactory.newVariableDeclarationNode(source,
					nodeFactory.newIdentifierNode(source, "i" + i),
					nodeFactory.newBasicTypeNode(source, BasicTypeKind.INT)));
		}
		if (!baseType.isScalar()) {
			VariableDeclarationNode tmpVar = tmpVarCreator.apply(source,
					baseType);
			ExprTriple subResult;

			elementDefaultVal = nodeFactory.newIdentifierExpressionNode(source,
					tmpVar.getIdentifier().copy());
			subResult = defaultValues(elementDefaultVal, baseType);
			if (subResult != null) {
				before.addAll(subResult.getBefore());
				before.add(tmpVar);
				if (subResult.getNode() != null)
					tmpVar.setInitializer(subResult.getNode());
				before.addAll(subResult.getAfter());
			} else
				before.add(tmpVar);
		} else
			elementDefaultVal = defaultValueOfScalarType(
					(UnqualifiedObjectType) baseType, source);

		ExpressionNode arrLambda = nodeFactory.newArrayLambdaNode(source,
				typeNodeCreator.apply(source, arrType), varDecls, null,
				elementDefaultVal.copy());

		result = new ExprTriple(arrLambda);
		result.addAllBefore(before);
		return result;
	}

	/**
	 * worker method of {@link #defaultValuesToArray(ArrayType, Source)} for
	 * creating default value strictly conforming C11 standard
	 */
	private ExprTriple defaultValuesToArrayStrict(ExpressionNode arr,
			ArrayType arrType, Source source) {
		assert !arrType
				.isVariableLengthArrayType() : "Initializer cannot be used to initialize variable "
						+ "length array in C language.\nNote that CIVL-C programs, "
						+ "whose source files end with suffix \".cvl\", support such feature.";
		assert arrType.isComplete();

		Type elementType = arrType.getElementType();
		ExpressionNode elementDefaultVal;
		List<BlockItemNode> result = new LinkedList<>();

		if (!elementType.isScalar()) {
			VariableDeclarationNode tmpVar = tmpVarCreator.apply(source,
					elementType);
			ExprTriple subResult;

			elementDefaultVal = nodeFactory.newIdentifierExpressionNode(source,
					tmpVar.getIdentifier().copy());
			subResult = defaultValues(elementDefaultVal, elementType);
			assert subResult.getNode() == null;
			assert subResult.getBefore().isEmpty();
			result.add(tmpVar);
			result.addAll(subResult.getAfter());
		} else {
			elementDefaultVal = nodeFactory.newIntConstantNode(source, 0);
		}

		// make a for-loop to initialize the array:
		int size = arrType.getConstantSize().getIntegerValue().intValueExact();
		ExpressionNode sizeNode = nodeFactory.newIntConstantNode(source, size);
		VariableDeclarationNode decl = tmpVarCreator.apply(source,
				nodeFactory.typeFactory().basicType(BasicTypeKind.INT));
		List<VariableDeclarationNode> loopVarDecls = new LinkedList<>();
		DeclarationListNode forLoopInit;
		ExpressionNode forLoopCond, forLoopId, forLoopInc;
		StatementNode forLoopBody;

		decl.setInitializer(nodeFactory.newIntConstantNode(source, 0));
		loopVarDecls.add(decl);
		forLoopId = nodeFactory.newIdentifierExpressionNode(source,
				decl.getIdentifier().copy());
		forLoopInit = nodeFactory.newForLoopInitializerNode(source,
				loopVarDecls);
		forLoopCond = nodeFactory.newOperatorNode(source, Operator.LT,
				forLoopId, sizeNode);
		forLoopInc = nodeFactory.newOperatorNode(source, Operator.PLUS,
				forLoopId.copy(), nodeFactory.newIntConstantNode(source, 1));
		forLoopInc = nodeFactory.newOperatorNode(source, Operator.ASSIGN,
				forLoopId.copy(), forLoopInc);
		forLoopBody = nodeFactory.newExpressionStatementNode(
				nodeFactory.newOperatorNode(source, Operator.ASSIGN,
						nodeFactory.newOperatorNode(source, Operator.SUBSCRIPT,
								arr.copy(), forLoopId.copy()),
						elementDefaultVal.copy()));
		result.add(nodeFactory.newForLoopNode(source, forLoopInit, forLoopCond,
				forLoopInc, forLoopBody, null));

		ExprTriple ret = new ExprTriple(null);

		ret.setAfter(result);
		return ret;
	}

	/**
	 * <p>
	 * Assigns a default value to an struct or union object as if the object has
	 * static storage.
	 * </p>
	 * 
	 * @param obj
	 *            a struct or union object
	 * @param objType
	 *            the type of the struct or union object
	 * @return a list of assignment statements that goes "after" the first
	 *         appearance of the given object
	 */
	private List<BlockItemNode> defaultValuesToStructOrUnion(ExpressionNode obj,
			StructureOrUnionType objType) {
		int numFields = objType.getNumFields();
		Source source = obj.getSource();
		List<BlockItemNode> results = new LinkedList<>();

		if (objType.isUnion())
			numFields = 1;
		for (int i = 0; i < numFields; i++) {
			Field field = objType.getField(i);
			ExpressionNode fieldObj;
			String fieldName = field.getName();

			/*
			 * C11 sec 6.7.9 semantics 9: Except where explicitly stated
			 * otherwise, for the purposes of this subclause unnamed members of
			 * objects of structure and union type do not participate in
			 * initialization. Unnamed members of structure objects have
			 * indeterminate value even after initialization.
			 */
			if (fieldName == null)
				continue;
			fieldObj = nodeFactory.newDotNode(source, obj.copy(),
					nodeFactory.newIdentifierNode(source, fieldName));

			Type fieldType = objType.getField(i).getType();
			ExprTriple subResult = defaultValues(fieldObj, fieldType);

			if (subResult != null) {
				results.addAll(subResult.getBefore());
				results.addAll(subResult.getAfter());
				if (subResult.getNode() != null)
					results.add(nodeFactory.newExpressionStatementNode(
							nodeFactory.newOperatorNode(source, Operator.ASSIGN,
									fieldObj.copy(), subResult.getNode())));
			}
		}
		return results;
	}

	/**
	 * <p>
	 * returns an expression that represents default value of a scalar type
	 * object as if the object has static storage
	 * </p>
	 * 
	 * @param scalarType
	 *            the scalar type
	 * @param source
	 *            the {@link Source} that is associated to the returned
	 *            expression
	 * @return an expression that represents default value of a scalar type
	 *         object as if the object has static storage
	 */
	private ExpressionNode defaultValueOfScalarType(Type scalarType,
			Source source) {
		switch (scalarType.kind()) {
			case BASIC :
			case ENUMERATION :
			case OTHER_INTEGER :
				return nodeFactory.newIntConstantNode(source, 0);
			case POINTER :
			case RANGE :
			case SCOPE : {
				return nodeFactory.newCastNode(source,
						typeNodeCreator.apply(source, scalarType),
						nodeFactory.newIntConstantNode(source, 0));
			}
			case PROCESS :
				return nodeFactory.newProcnullNode(source);
			case STATE :
				return nodeFactory.newStatenullNode(source);
			default :
				throw new ABCRuntimeException(
						"unexpected scalar type kind for inferring its default value : "
								+ scalarType.kind());
		}
	}
}