CompoundLiteralAnalyzer.java

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

import edu.udel.cis.vsl.abc.ast.node.IF.ASTNode;
import edu.udel.cis.vsl.abc.ast.node.IF.IdentifierNode;
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.ArrayDesignatorNode;
import edu.udel.cis.vsl.abc.ast.node.IF.compound.CompoundInitializerNode;
import edu.udel.cis.vsl.abc.ast.node.IF.compound.DesignationNode;
import edu.udel.cis.vsl.abc.ast.node.IF.compound.DesignatorNode;
import edu.udel.cis.vsl.abc.ast.node.IF.compound.FieldDesignatorNode;
import edu.udel.cis.vsl.abc.ast.node.IF.compound.LiteralObject;
import edu.udel.cis.vsl.abc.ast.node.IF.declaration.InitializerNode;
import edu.udel.cis.vsl.abc.ast.node.IF.expression.ExpressionNode;
import edu.udel.cis.vsl.abc.ast.type.IF.ArrayType;
import edu.udel.cis.vsl.abc.ast.type.IF.Field;
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.QualifiedObjectType;
import edu.udel.cis.vsl.abc.ast.type.IF.StandardBasicType.BasicTypeKind;
import edu.udel.cis.vsl.abc.ast.type.IF.StructureOrUnionType;
import edu.udel.cis.vsl.abc.ast.type.IF.TypeFactory;
import edu.udel.cis.vsl.abc.ast.value.IF.IntegerValue;
import edu.udel.cis.vsl.abc.ast.value.IF.ValueFactory;
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.SyntaxException;
import edu.udel.cis.vsl.abc.token.IF.UnsourcedException;

/**
 * An instance of this class is used to analyzer compound literals.
 * 
 * Initialization, including of compound objects, is specified in C11 6.7.9.
 * Note in particular the following:
 * 
 * <pre>
 * 2. No initializer shall attempt to provide a value for an object
 *    not contained within the entity being initialized.
 * 3. The type of the entity to be initialized shall be an array
 *    of unknown size or a complete object type that is not a
 *    variable length array type.
 * 4. All the expressions in an initializer for an object that has
 *    static or thread storage duration shall be constant expressions
 *    or string literals.
 * 5. If the declaration of an identifier has block scope, and
 *    the identifier has external or internal linkage, the
 *    declaration shall have no initializer for the identifier.
 * 6. If a designator has the form
 *          [ constant-expression ]
 * 	  then the current object (defined below) shall have array
 *    type and the expression shall be an integer constant expression.
 *    If the array is of unknown size, any nonnegative value is valid.
 * 7. If a designator has the form
 *          . identifier
 *    then the current object (defined below) shall have structure
 *    or union type and the identifier shall be the name of a
 *    member of that type.
 * </pre>
 * 
 * The array extents must be either constants (e.g., [3]) or empty ([]). After
 * the first non-array type is reached from the root, they must all be
 * constants. Hence there is a prefix of array types which may or may not be
 * complete, followed by types which must be complete.
 */
public class CompoundLiteralAnalyzer {

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

	/** The entity analyzer controlling this declaration analyzer. */
	private EntityAnalyzer entityAnalyzer;

	private NodeFactory nodeFactory;

	private TypeFactory typeFactory;

	private ValueFactory valueFactory;

	private IntegerType intType;

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

	public CompoundLiteralAnalyzer(EntityAnalyzer entityAnalyzer) {
		this.entityAnalyzer = entityAnalyzer;
		this.nodeFactory = entityAnalyzer.nodeFactory;
		this.typeFactory = entityAnalyzer.typeFactory;
		this.valueFactory = entityAnalyzer.valueFactory;
		this.intType = (IntegerType) typeFactory.basicType(BasicTypeKind.INT);
	}

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

	void processCompoundInitializer(CompoundInitializerNode compoundInitNode,
			ObjectType type) throws SyntaxException {
		LiteralTypeNode ltNode = makeTypeTree(type);
		CommonCompoundLiteralObject literalObject = interpret(compoundInitNode,
				ltNode);
		ObjectType completeType = extractType(ltNode);

		fill(literalObject);
		compoundInitNode.setLiteralObject(literalObject);
		compoundInitNode.setType(completeType);
	}

	// ************************* 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 LiteralTypeNode makeTypeTree(ObjectType type) {
		switch (type.kind()) {
			case ARRAY : {
				LiteralTypeNode child = makeTypeTree(
						((ArrayType) type).getElementType());
				LiteralTypeNode result = new LiteralArrayTypeNode(
						(ArrayType) type, child);

				child.setParent(result);
				return result;
			}
			case STRUCTURE_OR_UNION : {
				StructureOrUnionType sut = (StructureOrUnionType) type;
				int numFields = sut.getNumFields();
				LiteralTypeNode[] children = new LiteralTypeNode[numFields];
				LiteralTypeNode result;

				for (int i = 0; i < numFields; i++) {
					Field field = sut.getField(i);
					ObjectType fieldType = field.getType();
					LiteralTypeNode child = makeTypeTree(fieldType);

					children[i] = child;
				}
				result = new LiteralStructOrUnionTypeNode(sut, children);
				for (int i = 0; i < numFields; i++)
					children[i].setParent(result);
				return result;
			}
			case QUALIFIED : {
				return makeTypeTree(((QualifiedObjectType) type).getBaseType());
			}
			default :
				return new LiteralScalarTypeNode(type);
		}
	}

	/**
	 * Extracts the complete type from the ltNode after it has been refined
	 * through the construction of the literal object.
	 * 
	 * As soon as you hit a non-array type, you can stop, because the fields of
	 * structs or unions have to be complete, except for the last "flexible
	 * member", but that can't be initialized.
	 * 
	 * @param ltNode
	 *            the literal type node, which has been updated after processing
	 *            the compound literal
	 * @return the complete Type specified by that node
	 */
	private ObjectType extractType(LiteralTypeNode ltNode) {
		if (ltNode instanceof LiteralArrayTypeNode) {
			LiteralTypeNode child = ((LiteralArrayTypeNode) ltNode)
					.getElementNode();
			ObjectType elementType = extractType(child);
			int length = ltNode.length();

			return typeFactory.arrayType(elementType,
					valueFactory.integerValue(intType, length));
		}
		return ltNode.getType();
	}

	/**
	 * Constructs an abstract Designation from an AST designation node.
	 * 
	 * @param desNode
	 * @param ltNode
	 * @return
	 * @throws SyntaxException
	 */
	private Designation processDesignation(DesignationNode desNode,
			LiteralTypeNode ltNode) throws SyntaxException {
		Designation result = new Designation(ltNode);

		for (DesignatorNode designatorNode : desNode) {
			int index;

			if (designatorNode instanceof FieldDesignatorNode) {
				IdentifierNode fieldId = ((FieldDesignatorNode) designatorNode)
						.getField();
				String fieldName = fieldId.name();
				StructureOrUnionType suType = (StructureOrUnionType) ltNode
						.getType();
				Field field = suType.getField(fieldName);

				if (field == null)
					throw error(
							"Structure or union type " + suType.getTag()
									+ " contains no field named " + fieldName,
							fieldId);
				fieldId.setEntity(field);
				index = field.getMemberIndex();
			} else if (designatorNode instanceof ArrayDesignatorNode) {
				ExpressionNode indexExpr = ((ArrayDesignatorNode) designatorNode)
						.getIndex();
				IntegerValue indexValue;

				entityAnalyzer.expressionAnalyzer.processExpression(indexExpr);
				indexValue = (IntegerValue) nodeFactory
						.getConstantValue(indexExpr);
				index = indexValue.getIntegerValue().intValue();
			} else
				throw new ABCRuntimeException(
						"Unreachable: unknown kind of designator node: "
								+ designatorNode);
			result.add(new Navigator(index, designatorNode.getSource()));
		}
		return result;
	}

	private CommonCompoundLiteralObject interpret(
			CompoundInitializerNode compoundInitNode, LiteralTypeNode ltNode)
			throws SyntaxException {
		CommonCompoundLiteralObject result = new CommonCompoundLiteralObject(
				ltNode, compoundInitNode);
		Designation position = new Designation(ltNode);

		for (PairNode<DesignationNode, InitializerNode> pair : compoundInitNode) {
			DesignationNode desNode = pair.getLeft();
			InitializerNode initNode = pair.getRight();
			LiteralObject subLiteral;
			LiteralTypeNode subType;

			if (desNode != null) {
				position = processDesignation(desNode, ltNode);
			} else {
				if (position.length() == 0)
					position.add(new Navigator(0, initNode.getSource()));
				else
					position.increment(ltNode);
			}
			if (initNode instanceof CompoundInitializerNode) {
				subType = position.getDesignatedType();
				subLiteral = interpret((CompoundInitializerNode) initNode,
						subType);
			} else {
				ExpressionNode expr = (ExpressionNode) initNode;

				entityAnalyzer.expressionAnalyzer.processExpression(expr);
				subType = null;
				for (int i = 0; i < 2; i++) { // make at most 2 attempts:
					try {
						/*
						 * if an exception gets thrown, the current object
						 * cannot be assigned by the initializer, which means
						 * either there is an error or it is the sub-object that
						 * should be initialized. Hence, call "descendToType"
						 * and try again.
						 */
						subType = position.getDesignatedType();
						entityAnalyzer.expressionAnalyzer
								.processAssignment(subType.getType(), expr);
					} catch (UnsourcedException e) {
						if (i == 0) { // i == 0 means looking at sub-obj
							position.descendToType((ObjectType) expr.getType(),
									initNode.getSource());
							continue;
						} else
							throw error(e, expr);
					}
					break;
				}
				assert subType != null;
				subLiteral = new CommonScalarLiteralObject(subType, expr);
			}
			result.set(compoundInitNode.getSource(), position, subLiteral);
		}
		return result;
	}

	/**
	 * Fills in missing spaces with 0s. Needs to create fake expression nodes
	 * for the 0s. Creates them with source the entire surrounding compound
	 * initializer.
	 * 
	 * @param object
	 *            compound literal object that has already been processed
	 * @throws SyntaxException
	 */
	private void fill(CommonCompoundLiteralObject object)
			throws SyntaxException {
		// for proper sourcing, need a node for each compound
		// literal object, or source
		LiteralTypeNode ltNode = object.getTypeNode();
		int length = ltNode.length();
		ASTNode sourceNode = object.getSourceNode();
		Source source = sourceNode.getSource();

		for (int i = 0; i < length; i++) {
			LiteralObject member = object.get(i);

			if (member == null) {
				// what is the type of this member supposed to be?
				LiteralTypeNode child = ltNode.getChild(i);

				if (child instanceof LiteralScalarTypeNode) {
					ExpressionNode fakeNode = nodeFactory
							.newIntegerConstantNode(source, "0");

					try {
						entityAnalyzer.expressionAnalyzer
								.processAssignment(child.getType(), fakeNode);
					} catch (UnsourcedException e) {
						throw error(e, sourceNode);
					}
					member = new CommonScalarLiteralObject(child, fakeNode);
				} else {
					member = new CommonCompoundLiteralObject(child, sourceNode);
				}
				object.setElement(source, i, member);
			}
			if (member instanceof CommonCompoundLiteralObject) {
				fill((CommonCompoundLiteralObject) member);
			}
		}
	}
}