CommonMacro.java

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

import java.util.ArrayList;
import java.util.LinkedList;

import org.antlr.runtime.Token;
import org.antlr.runtime.tree.CommonTree;
import org.antlr.runtime.tree.Tree;

import edu.udel.cis.vsl.abc.front.c.preproc.PreprocessorUtils;
import edu.udel.cis.vsl.abc.token.IF.Macro;
import edu.udel.cis.vsl.abc.token.IF.SourceFile;

/**
 * Root class for representing preprocessor {@link Macro}s. This is an abstract
 * class. It has concrete subclasses {@link CommonObjectMacro} (for object-like
 * macros) and {@link CommonFunctionMacro} (for function-like macros).
 * 
 * @author siegel
 * 
 */
public abstract class CommonMacro implements Macro {

	protected Tree definitionNode;

	protected SourceFile file;

	/**
	 * The replacement "units". Each includes one non-whitespace preprocessing
	 * token, and some possible whitespace after that token.
	 */
	protected ReplacementUnit[] replacements;

	protected CommonMacro(Tree definitionNode, SourceFile file) {
		this.definitionNode = definitionNode;
		this.file = file;

		Tree bodyNode = getBodyNode();
		int numChildren = bodyNode.getChildCount(); // includes ws
		ArrayList<ReplacementUnit> replacementVec = new ArrayList<>();
		int index = 0;

		// ignore any whitespace at beginning of body (should not exist):
		while (index < numChildren && PreprocessorUtils.isWhiteSpace(
				((CommonTree) bodyNode.getChild(index)).getToken()))
			index++;
		while (index < numChildren) {
			Token token = ((CommonTree) bodyNode.getChild(index)).getToken();
			LinkedList<Token> ws = new LinkedList<>();
			Token t;

			assert !PreprocessorUtils.isWhiteSpace(token);
			index++;
			while (index < numChildren) {
				t = ((CommonTree) bodyNode.getChild(index)).getToken();
				if (PreprocessorUtils.isWhiteSpace(t))
					ws.add(t);
				else
					break;
				index++;
			}

			ReplacementUnit unit = makeReplacement(replacementVec.size(), token,
					ws.toArray(new Token[ws.size()]));

			replacementVec.add(unit);
		}
		replacements = replacementVec
				.toArray(new ReplacementUnit[replacementVec.size()]);
	}

	protected abstract ReplacementUnit makeReplacement(int index, Token token,
			Token[] whitespace);

	@Override
	public SourceFile getFile() {
		return file;
	}

	@Override
	public Tree getDefinitionNode() {
		return definitionNode;
	}

	@Override
	public ReplacementUnit getReplacementUnit(int i) {
		return replacements[i];
	}

	@Override
	public String getName() {
		return definitionNode.getChild(0).getText();
	}

	@Override
	public int getNumReplacements() {
		return replacements.length;
	}

	protected boolean equals(ReplacementUnit unit1, ReplacementUnit unit2) {
		Token t1 = unit1.token, t2 = unit2.token;

		if (t1.getType() != t2.getType() || !t1.getText().equals(t2.getText()))
			return false;
		return (unit1.whitespace.length == 0) == (unit2.whitespace.length == 0);
	}

	/**
	 * 
	 * C11 6.10.3.1: "Two replacement lists are identical if and only if the
	 * preprocessing tokens in both have the same number, ordering, spelling,
	 * and white-space separation, where all white-space separations are
	 * considered identical."
	 * 
	 * @param object
	 * @return
	 */
	@Override
	public boolean equals(Object object) {
		if (object instanceof Macro) {
			Macro that = (Macro) object;

			if (!getName().equals(that.getName()))
				return false;

			int numReplacements = getNumReplacements();

			if (numReplacements != that.getNumReplacements())
				return false;
			for (int i = 0; i < numReplacements; i++) {
				if (!equals(this.getReplacementUnit(i),
						that.getReplacementUnit(i)))
					return false;
			}
			return true;
		}
		return false;
	}
}