TranslationTask.java

package edu.udel.cis.vsl.abc.main;

import java.io.File;
import java.io.PrintStream;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import edu.udel.cis.vsl.abc.config.IF.Configuration.Architecture;
import edu.udel.cis.vsl.abc.config.IF.Configurations;
import edu.udel.cis.vsl.abc.config.IF.Configurations.Language;
import edu.udel.cis.vsl.abc.err.IF.ABCException;
import edu.udel.cis.vsl.abc.transform.IF.Transform;
import edu.udel.cis.vsl.abc.transform.IF.TransformRecord;

/**
 * A {@link TranslationTask} object specifies all of the options and parameters
 * needed to perform a complete translation task, from preprocessing source
 * files, to linking (i.e., merging), analyzing, and transforming.
 * 
 * @author siegel
 */
public class TranslationTask {

	public static enum TranslationStage {
		/**
		 * For each source unit, construct preprocessor output stream but do not
		 * consume the tokens from it.
		 */
		PREPROCESS,
		/**
		 * For each source unit, construct the preprocessor output stream and
		 * consume all token emanating from it, printing them to the
		 * {@link PrintStream} specified by method
		 * {@link TranslationTask#getOut()}.
		 */
		PREPROCESS_CONSUME,
		/** Preprocess and parse each source unit to form a parse tree. */
		PARSE,
		/** Preprocess, parse, and generate an AST for each translation unit. */
		GENERATE_ASTS,
		/**
		 * Preprocess, parse, generate and analyze an AST for each translation
		 * unit.
		 */
		ANALYZE_ASTS,
		/**
		 * Preprocess, parse, generate, analyze and transform the AST for each
		 * translation unit.
		 */
		TRANSFORM_ASTS,
		/**
		 * Preprocess, parse, generate, analyze, and transform an AST for each
		 * translation unit, and link those translation units to form a whole,
		 * analyzed program.
		 */
		LINK,
		/**
		 * Preprocess, parse, generate, analyze, and transform the translation
		 * units, link the translation units and and analyze the resulting
		 * program, and finally perform the specified transformations to the
		 * whole program.
		 */
		TRANSFORM_PROGRAM
	}

	/**
	 * The objects specifying how to construct each translation unit. Each
	 * translation unit is constructed by parsing and preprocessing a sequence
	 * of files. There is no default value; must be set at construction.
	 */
	private UnitTask[] unitTasks;

	/**
	 * At which stage of translation should this task stop?
	 */
	private TranslationStage stage = TranslationStage.TRANSFORM_PROGRAM;

	/**
	 * The language used to link the translation units. Default is determined by
	 * the languages of the unit tasks. If all are in one language, that is used
	 * as the link language. Otherwise, {@link Language#CIVL_C} is the link
	 * language.
	 */
	private Language linkLanguage = Language.CIVL_C;

	/**
	 * Records for the transformations to apply to the program after linking the
	 * translation units. Default is empty list.
	 */
	private List<TransformRecord> transformRecords = new LinkedList<>();

	/**
	 * Output stream: where to print human-readable descriptions of translation
	 * artifacts. Default is standard out.
	 */
	private PrintStream out = System.out;

	/**
	 * If true, show preprocessor output as individual tokens with complete
	 * token information, instead of just the text. Very detailed and long
	 * output. Default is <code>false</code>.
	 */
	private boolean preprocTokens = false;

	/**
	 * Print out intermediate artifacts? Default is <code>false</code>.
	 */
	private boolean verbose = false;

	/**
	 * Print out program in original language, as opposed to a hierarchical
	 * representation. Default is <code>false</code>.
	 */
	private boolean prettyPrint = false;

	/**
	 * Show symbol and type tables. Default is <code>false</code>.
	 */
	private boolean showTables = false;

	/**
	 * Show the timing of each phase. Default is <code>false</code>.
	 */
	private boolean showTime = false;

	/**
	 * A special task used to show the difference between exactly two ASTs.
	 * Default is <code>false</code>.
	 */
	private boolean showDiff = false;

	/**
	 * Print nothing? Default is <code>true</code>.
	 */
	private boolean silent = true;

	/**
	 * Print the list of functions that are used but do not have definitions?
	 * Default is <code>false</code>.
	 */
	private boolean showUndefinedFunctions = false;

	/**
	 * Summarize files and external entities.
	 */
	private boolean summarize = false;

	/**
	 * Is this an SV-COMP problem?
	 */
	private boolean svcomp = false;

	/**
	 * Are the GNU extensions to the C language allowed?
	 */
	private boolean gnuc = false;

	/**
	 * Which architecture is this translation targeting?
	 */
	private Architecture architecture = Architecture.UNKNOWN;

	private DynamicTask dynamicTask = null;

	/**
	 * Constructs a new translation task from given unit tasks. All other
	 * parameters have their default values.
	 * 
	 * @param unitTasks
	 *            the unit tasks
	 */
	public TranslationTask(UnitTask[] unitTasks) {
		this.unitTasks = unitTasks;
		initialize();
	}

	/**
	 * Constructs new translation task from a list of list of files. Each
	 * element of the list represents one translation unit. The files in that
	 * element are the source files which are concatenated to form the input to
	 * the preprocessor.
	 * 
	 * @param sourceUnits
	 *            list of preprocessor input units
	 */
	public TranslationTask(File[][] sourceUnits) {
		int numTasks = sourceUnits.length;

		unitTasks = new UnitTask[numTasks];
		for (int i = 0; i < numTasks; i++) {
			unitTasks[i] = new UnitTask(sourceUnits[i]);
		}
		initialize();
	}

	// Helpers...

	private Iterable<Language> getUnitLanguages() {
		return new Iterable<Language>() {
			@Override
			public Iterator<Language> iterator() {
				return new Iterator<Language>() {
					int i = 0;
					int n = unitTasks.length;

					@Override
					public boolean hasNext() {
						return i < n;
					}

					@Override
					public Language next() {
						Language result = unitTasks[i].getLanguage();

						i++;
						return result;
					}
				};
			}
		};
	}

	private void initialize() {
		this.linkLanguage = Configurations.commonLanguage(getUnitLanguages());
	}

	/**
	 * Constructs a new translation task in which each source file represents a
	 * distinct translation unit. Same as specifying a 2-dimensional array in
	 * which each element is an array of length 1.
	 * 
	 * @param sourceFiles
	 *            source files; each is compiled as a separate translation unit
	 *            and linked
	 */
	public TranslationTask(File[] sourceFiles) {
		int numFiles = sourceFiles.length;

		unitTasks = new UnitTask[numFiles];
		for (int i = 0; i < numFiles; i++) {
			unitTasks[i] = new UnitTask(new File[]{sourceFiles[i]});
		}
		initialize();
	}

	/**
	 * Constructs a new translation task consisting of a single source file.
	 * 
	 * @param sourceFile
	 *            the source file
	 */
	public TranslationTask(File sourceFile) {
		unitTasks = new UnitTask[1];
		unitTasks[0] = new UnitTask(new File[]{sourceFile});
		initialize();
	}

	/**
	 * Gets the unit tasks, the objects specifying how to construct each
	 * translation unit. Each translation unit is constructed by parsing and
	 * preprocessing a sequence of files. There is no default value; must be set
	 * at construction.
	 * 
	 * @return the unit tasks
	 */
	public UnitTask[] getUnitTasks() {
		return unitTasks;
	}

	/**
	 * Specifies how far into the translation process this task should go before
	 * stopping. Default is to go all the way, i.e.,
	 * {@link TranslationStage#TRANSFORM_PROGRAM}.
	 * 
	 * @return the final translation stage of this task
	 */
	public TranslationStage getStage() {
		return stage;
	}

	/**
	 * Specifies how far into the translation process this task should go before
	 * stopping. Default is to go all the way, i.e.,
	 * {@link TranslationStage#TRANSFORM_PROGRAM}.
	 * 
	 * @param statege
	 *            the final translation stage of this task
	 */
	public void setStage(TranslationStage stage) {
		this.stage = stage;
	}

	/**
	 * Gets the output stream --- where to print human-readable descriptions of
	 * translation artifacts. Default is standard out.
	 * 
	 * @return the output stream
	 */
	public PrintStream getOut() {
		return out;
	}

	/**
	 * Sets the output stream --- where to print human-readable descriptions of
	 * translation artifacts. Default is standard out.
	 * 
	 * @param out
	 *            the output stream
	 */
	public void setOut(PrintStream out) {
		this.out = out;
	}

	/**
	 * Should preprocessing output be displayed as individual tokens (very
	 * detailed information)? Default is <code>false</code>.
	 * 
	 * @return <code>true</code> iff preprocessing output should be displayed as
	 *         individual tokens
	 */
	public boolean getPreprocTokens() {
		return preprocTokens;
	}

	/**
	 * Specifies whether preprocessing output should be displayed as individual
	 * tokens (very detailed information). Default is <code>false</code>.
	 * 
	 * @param flag
	 *            <code>true</code> iff preprocessing output should be displayed
	 *            as individual tokens
	 */
	public void setPreprocTokens(boolean flag) {
		this.preprocTokens = flag;
	}

	/**
	 * Should very detailed information be printed during processing? Default is
	 * <code>false</code>.
	 * 
	 * @return <code>true</code> iff very detailed information should be printed
	 *         during processing
	 */
	public boolean getVerbose() {
		return verbose;
	}

	/**
	 * Specifies whether very detailed information should be printed during
	 * processing.
	 * 
	 * @param verbose
	 *            <code>true</code> iff very detailed information should be
	 *            printed during processing
	 */
	public void setVerbose(boolean flag) {
		this.verbose = flag;
		if (verbose)
			silent = false;
	}

	/**
	 * Returns the sequence of transform records as a Java {@link Collection} .
	 * The order does matter. These are the transformations that will be applied
	 * to the program AFTER linking the translation units to form the whole
	 * program.
	 * 
	 * @return the sequence of transformers
	 */
	public Collection<TransformRecord> getTransformRecords() {
		return transformRecords;
	}

	/**
	 * Adds the given transform record to the end of the transform record
	 * sequence.
	 * 
	 * @param record
	 *            a non-<code>null</code> transform record
	 */
	public void addTransformRecord(TransformRecord record) {
		transformRecords.add(record);
	}

	/**
	 * Adds the transformation record specified by the given code to the end of
	 * the transform record sequence. These are the transformations that will be
	 * applied to the program AFTER linking the translation units to form the
	 * whole program.
	 * 
	 * @param code
	 *            an AST transformation code
	 * @throws ABCException
	 *             if no record for that code exists
	 */
	public void addTransformCode(String code) throws ABCException {
		TransformRecord record = Transform.getRecord(code);

		if (record == null)
			throw new ABCException("Unknown transformer code: " + code);
		transformRecords.add(record);
	}

	/**
	 * Add records for all of the given transformation codes (in order) to the
	 * transform record sequence of this translation task.
	 * 
	 * @param codes
	 *            a sequence of AST transformation codes
	 * @throws ABCException
	 *             if for some code in the collection, no record for that code
	 *             exists
	 */
	public void addAllTransformCodes(Collection<String> codes)
			throws ABCException {
		for (String code : codes)
			addTransformCode(code);
	}

	/**
	 * Should the program be displayed as CIVL-C code, as opposed to a
	 * hierarchical representation? Default is <code>false</code>.
	 * 
	 * @return <code>true</code> iff program should be displayed as CIVL-C code
	 */
	public boolean getPrettyPrint() {
		return prettyPrint;
	}

	/**
	 * Specifies whether program should be displayed as CIVL-C code, as opposed
	 * to a hierarchical representation. Default is <code>false</code>.
	 * 
	 * @param flag
	 *            <code>true</code> iff program should be displayed as CIVL-C
	 *            code
	 */
	public void setPrettyPrint(boolean flag) {
		this.prettyPrint = flag;
	}

	/**
	 * Should symbol and type tables be displayed? Default is <code>false</code>
	 * .
	 * 
	 * @return <code>true</code> iff symbol and type tables should be displayed
	 */
	public boolean getShowTables() {
		return showTables;
	}

	/**
	 * Specifies whether the symbol and type tables should be displayed. Default
	 * is <code>false</code>.
	 * 
	 * @param flag
	 *            <code>true</code> iff symbol and type tables should be
	 *            displayed
	 */
	public void setShowTables(boolean flag) {
		this.showTables = flag;
	}

	/**
	 * Show timing information for each phase of translation? Default is
	 * <code>false</code>.
	 * 
	 * @return <code>true</code> iff timing information should be displayed
	 */
	public boolean getShowTime() {
		return showTime;
	}

	/**
	 * Is this task to show the difference between two ASTs? Default is
	 * <code>false</code>.
	 * 
	 * @return <code>true</code> iff the task is to show the difference between
	 *         two ASTs
	 */
	public boolean getShowDiff() {
		return this.showDiff;
	}

	/**
	 * Specifies whether to show timing information for each translation phase.
	 * Default is <code>false</code>.
	 * 
	 * @param flag
	 *            <code>true</code> iff timing information should be displayed
	 */
	public void setShowTime(boolean flag) {
		this.showTime = flag;
	}

	/**
	 * Specifies whether this is a task to show the difference between two ASTs.
	 * Default is <code>false</code>.
	 * 
	 * @param flag
	 *            <code>true</code> iff the task is to show the difference
	 *            between two ASTs
	 */
	public void setShowDiff(boolean flag) {
		this.showDiff = flag;
	}

	/**
	 * Should very little output be produced? Default is <code>false</code>.
	 * 
	 * @return <code>true</code> iff very little output should be produced
	 */
	public boolean isSilent() {
		return silent;
	}

	/**
	 * Specifies whether to show very little output. Default is
	 * <code>false</code>.
	 * 
	 * @param flag
	 *            <code> true</code> iff very little output should be produced
	 */
	public void setSilent(boolean flag) {
		this.silent = flag;
		if (silent)
			verbose = false;
	}

	/**
	 * Should the output list all called functions which do not have
	 * definitions? Default is <code>false</code>.
	 * 
	 * @return <code>true</code> iff ABC should output all called functions
	 *         without definitions
	 */
	public boolean getShowUndefinedFunctions() {
		return showUndefinedFunctions;
	}

	/**
	 * Specifies whether the output should list all called functions which do
	 * not have definitions. Default is <code>false</code>.
	 * 
	 * @param flag
	 *            <code>true</code> iff ABC should output all called functions
	 *            without definitions
	 */
	public void setShowUndefinedFunctions(boolean flag) {
		this.showUndefinedFunctions = flag;
	}

	/**
	 * Gets the language that will be in effect when the translation units are
	 * linked. Default is determined by the languages of the unit tasks. If all
	 * are in one language, that is used as the link language. Otherwise,
	 * {@link Language#CIVL_C} is the link language.
	 * 
	 * @return the language that will be in effect when the translation units
	 *         are linked
	 */
	public Language getLinkLanguage() {
		return linkLanguage;
	}

	/**
	 * Sets the language that will be in effect when the translation units are
	 * linked. Default is determined by the languages of the unit tasks. If all
	 * are in one language, that is used as the link language. Otherwise,
	 * {@link Language#CIVL_C} is the link language.
	 * 
	 * @param language
	 *            the language that will be in effect when the translation units
	 *            are linked
	 */
	public void setLinkLanguage(Language language) {
		this.linkLanguage = language;
	}

	/**
	 * Is this configuration being used to solve an SV-COMP problem?
	 * 
	 * @return <code>true</code> iff this is an SV-COMP problem
	 */
	public boolean getSVCOMP() {
		return svcomp;
	}

	/**
	 * Sets the SVCOMP flag, which specifies whether this task is being used to
	 * solve an SV-COMP problem. If <code>true</code>, also automatically sets
	 * GNUC to <code>true</code>.
	 * 
	 * @param flag
	 *            <code>true</code> iff this is an SV-COMP problem
	 */
	public void setSVCOMP(boolean flag) {
		this.svcomp = flag;
		if (flag)
			this.gnuc = true;
	}

	public boolean getSummarize() {
		return summarize;
	}

	public void setSummarize(boolean value) {
		this.summarize = value;
	}

	/**
	 * Gets the architecture type for this translation task. Default is
	 * {@link Architecture#UNKNOWN}.
	 * 
	 * @return the architecture type
	 */
	public Architecture getArchitecture() {
		return architecture;
	}

	/**
	 * Sets the architecture type for this translation task. Default is
	 * {@link Architecture#UNKNOWN}.
	 * 
	 * @param architecture
	 *            the architecture type
	 */
	public void setArchitecture(Architecture arch) {
		this.architecture = arch;
	}

	/**
	 * Are the GNU extensions to the C language allowed?
	 * 
	 * @return value of the GNUC flag
	 */
	public boolean getGNUC() {
		return gnuc;
	}

	/**
	 * Specifies whether the GNU extensions to the C language are allowed.
	 * Default is false. This flag is also automatically set to true when the
	 * SVCOMP flag is set to true
	 * 
	 * @param flag
	 *            value of GNUC flag
	 */
	public void setGNUC(boolean flag) {
		this.gnuc = flag;
	}

	public DynamicTask getDynamicTask() {
		return dynamicTask;
	}

	public void setDynamicTask(DynamicTask dtask) {
		this.dynamicTask = dtask;
	}
}