ABC.java

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

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import edu.udel.cis.vsl.abc.config.IF.Configuration.Architecture;
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.front.IF.ParseException;
import edu.udel.cis.vsl.abc.front.IF.Preprocessor;
import edu.udel.cis.vsl.abc.front.IF.PreprocessorException;
import edu.udel.cis.vsl.abc.front.IF.PreprocessorRuntimeException;
import edu.udel.cis.vsl.abc.main.TranslationTask.TranslationStage;
import edu.udel.cis.vsl.abc.token.IF.SyntaxException;
import edu.udel.cis.vsl.abc.transform.IF.Transform;

/**
 * <p>
 * This is the main class for ABC. It provides a simple command line interface
 * for using ABC. Most applications will not use this class, since they will use
 * ABC more as a library and access the functionality through the API. However,
 * the command line is useful for debugging, "seeing what is going on", and
 * general exploration.
 * </p>
 * 
 * <p>
 * By default, this command line interface will open and scan a file, preprocess
 * it, parse it to produce an ANTLR tree, translate that tree into an ABC
 * Abstract Syntax Tree (AST), and analyze the AST to determine scopes, types,
 * and entities associated to every identifier. It prints out the final result
 * in a plain text human-readable form. Various options can control how much
 * information is printed, and can also cause various transformations to be
 * applied to the AST. Executing with no command line arguments will print a
 * help message describing the options.
 * </p>
 * 
 * <p>
 * Include paths are searched as follows: for a file included with angle
 * brackets, first the paths occurring in the array <code>systemIncludes</code>
 * are searched, in order; if the file is not found there, then the built-in
 * system include paths inside of ABC are searched. For a file included with
 * double quotes, first the paths occurring in the array
 * <code>userIncludes</code> are searched (in order) and if the file is not
 * found there, the protocol for angle brackets is followed.
 * </p>
 * 
 * 
 * @author siegel
 * 
 */
public class ABC {

	/**
	 * The version number of this release of ABC: {@value} .
	 */
	public final static String version = "0.2";

	/**
	 * The date of this release of ABC: {@value} .
	 */
	public final static String date = "31-mar-2014";

	/**
	 * The default list of system include paths.
	 */
	public final static File[] DEFAULT_SYSTEM_INCLUDE_PATHS = Preprocessor.defaultSystemIncludes;

	/**
	 * The default list of user include paths.
	 */
	public final static File[] DEFAULT_USER_INCLUDE_PATHS = Preprocessor.defaultUserIncludes;

	/**
	 * The default list of pre-defined macros, specifies as a mapping from
	 * Strings (macro name) to macros.
	 */
	public final static Map<String, String> DEFAULT_IMPLICIT_MACROS = new TreeMap<>();

	/**
	 * Determines language from filename. If it ends in ".cvl" or ".cvh", it's
	 * CIVL_C, else it's C.
	 * 
	 * @param name
	 *            filename
	 * @return CIVL_C or C
	 */
	private static Language getLanguageFromName(String name) {
		int dotIndex = name.lastIndexOf('.');

		if (dotIndex >= 0) {
			String suffix = name.substring(dotIndex + 1, name.length());

			if ("cvl".equals(suffix) || "cvh".equals(suffix))
				return Language.CIVL_C;
			else if ("f".equals(suffix) || "F".equals(suffix))
				return Language.FORTRAN;
		}
		return Language.C;
	}

	/**
	 * Prints a message to the output stream specifying the command line syntax.
	 * 
	 * @param out
	 *            the stream to which the output should be printed
	 */
	private static void help(PrintStream out) {
		out.println("Usage: abc (option* filename)+ option*");
		out.println("Options:");
		out.println("-I <path>");
		out.println("  add path to system include list");
		out.println("-iquote <path>");
		out.println("  add path to user include list");
		out.println("-include <filename>");
		out.println("  include filename before preprocessing next source file");
		out.println("-D<macro> or -D<macro>=<object>");
		out.println("  define a macro for preprocessor");
		out.println("-o <filename>");
		out.println("  send output to filename");
		out.println("-E");
		out.println("  preprocess only");
		out.println("-ppt");
		out.println("  show preprocessing output as individual tokens");
		out.println("-v");
		out.println("  verbose mode, show all processing steps");
		out.println("-p");
		out.println("  pretty print programs in original language (default)");
		out.println("-a");
		out.println("  print the AST(s) using a hierarchical representation");
		out.println("-t");
		out.println("  show symbol and type tables");
		out.println("-time");
		out.println("  show time for each phase");
		out.println("-diff");
		out.println("  show the difference of two ASTs");
		out.println("-svcomp");
		out.println("  turn on special setting for svcomp benchmarks");
		out.println("-gnuc");
		out.println("  support GNU C features");
		out.println("-unknownFunc");
		out.println(
				"  print functions that are used in the program but no definition is given");
		out.println("-summarize");
		out.println("  print summary of source files and entities");
		out.println("-lang=[c|civlc|f77]");
		out.println("  set language (default determined by file suffix)");
		out.println("-acsl");
		out.println(
				"  interpret comments beginning with @ as ACSL annotations");
		out.println("-arch=[i386|amd64|unknown]");
		out.println("  set the architecture, unknown by default");
		for (String code : Transform.getCodes()) {
			String description = Transform.getShortDescription(code);

			out.println("-" + code);
			out.println("  " + description);
		}
		out.println();
		out.flush();
	}

	private static void err(String msg) {
		System.out.println("Error: " + msg);
		System.out.println();
		help(System.out);
		System.out.flush();
		System.exit(1);
	}

	private static TranslationTask parseCommandLine(String[] args)
			throws FileNotFoundException, ABCException {
		ArrayList<UnitTask> unitTasks = new ArrayList<>();
		String outfileName = null;
		// the following are updated by -I
		ArrayList<File> systemIncludeList = new ArrayList<>();
		// the following are updated by -iquote
		ArrayList<File> userIncludeList = new ArrayList<>();
		// the following are updated by -D
		Map<String, String> macros = new HashMap<String, String>();
		boolean preprocOnly = false;
		boolean ppt = false;
		boolean verbose = false;
		boolean pretty = true;
		boolean tables = false; // show symbol and type tables
		boolean showTime = false;
		boolean showDiff = false;
		boolean gnuc = false;
		boolean silent = false;
		boolean showUndefinedFunctions = false;
		boolean svcomp = false;
		boolean acsl = false;
		boolean summarize = false;
		Architecture architecture = Architecture.UNKNOWN;
		List<String> transformCodes = new LinkedList<>();
		Language language = null, linkLang = null;
		ArrayList<File> includeFiles = new ArrayList<>();

		for (int i = 0; i < args.length; i++) {
			String arg = args[i];

			if (arg.startsWith("-o")) {
				String name;

				if (arg.length() == 2) {
					i++;
					if (i >= args.length)
						err("Filename must follow -o");
					name = args[i];
				} else {
					name = arg.substring(2);
				}
				if (outfileName == null)
					outfileName = name;
				else
					err("More than one use of -o");
			} else if (arg.startsWith("-D")) {
				String name;
				String object = "";

				if (arg.contains("=")) {
					// -D<macro>=<object>
					int indexOfEqual = arg.indexOf('=');

					name = arg.substring(2, indexOfEqual);
					object = arg.substring(indexOfEqual + 1);
				} else {
					// -D<macro>
					name = arg.substring(2);
				}
				if (macros.containsKey(name))
					err("Duplicated macro definition of " + name);
				else
					macros.put(name, object);
			} else if (arg.startsWith("-I")) {
				String name;

				if (arg.length() == 2) {
					i++;
					if (i >= args.length)
						err("Filename must follow -I");
					name = args[i];
				} else {
					name = arg.substring(2);
				}
				systemIncludeList.add(new File(name));
			} else if (arg.startsWith("-iquote")) {
				String name;

				if (arg.length() == "-iquote".length()) {
					i++;
					if (i >= args.length)
						err("Filename must follow -iquote");
					name = args[i];
				} else {
					name = arg.substring("-iquote".length());
				}
				userIncludeList.add(new File(name));
			} else if (arg.equals("-E")) {
				preprocOnly = true;
			} else if (arg.equals("-ppt")) {
				ppt = true;
			} else if (arg.equals("-v")) {
				verbose = true;
			} else if (arg.equals("-p")) {
				pretty = true;
			} else if (arg.equals("-a")) {
				pretty = false;
			} else if (arg.equals("-t")) {
				tables = true;
			} else if (arg.equals("-time")) {
				showTime = true;
			} else if (arg.equals("-diff")) {
				showDiff = true;
			} else if (arg.equals("-gnuc")) {
				gnuc = true;
			} else if (arg.equals("-unknownFunc")) {
				showUndefinedFunctions = true;
			} else if (arg.equals("-svcomp")) {
				svcomp = true;
			} else if (arg.equals("-acsl")) {
				acsl = true;
			} else if (arg.equals("-summarize")) {
				summarize = true;
			} else if (arg.startsWith("-lang")) {
				if (arg.equals("-lang=c"))
					language = Language.C;
				else if (arg.equals("-lang=civlc"))
					language = Language.CIVL_C;
				else if (arg.equals("-lang=f77"))
					language = Language.FORTRAN;
				else
					err("Unknown command line option: " + arg);
			} else if (arg.startsWith("-arch")) {
				if (arg.equals("-arch=i386"))
					architecture = Architecture._32_BIT;
				else if (arg.equals("-arch=amd64"))
					architecture = Architecture._64_BIT;
				else if (arg.equals("-arch=unknown"))
					architecture = Architecture.UNKNOWN;
				else
					err("Unknown command line option: " + arg);
			} else if (arg.equals("-silent")) {
				silent = true;
			} else if (arg.startsWith("-linkLang=")) {
				if (arg.equals("-linkLang=c"))
					linkLang = Language.C;
				else if (arg.equals("-linkLang=civlc"))
					linkLang = Language.CIVL_C;
				else if (arg.equals("-linkLang=f77"))
					linkLang = Language.FORTRAN;
				else
					err("Unknown command line option: " + arg);
			} else if (arg.startsWith("-")) {
				// try transform code...
				String code = arg.substring(1);

				if (Transform.getCodes().contains(code))
					transformCodes.add(code);
				else
					err("Unknown command line option: " + arg);
			} else {
				File file = new File(arg);
				int numIncludes = includeFiles.size();
				File[] files = new File[numIncludes + 1];

				for (int j = 0; j < numIncludes; j++)
					files[j] = includeFiles.get(j);
				files[numIncludes] = file;

				UnitTask unitTask = new UnitTask(files);

				if (language == null)
					language = getLanguageFromName(arg);
				unitTask.setLanguage(language);
				unitTask.setGNUC(gnuc);
				unitTask.setACSL(acsl);
				unitTask.setMacros(new HashMap<String, String>(macros));
				unitTask.setSystemIncludes(
						systemIncludeList.toArray(new File[0]));
				unitTask.setUserIncludes(userIncludeList.toArray(new File[0]));
				unitTasks.add(unitTask);
				language = null;
				gnuc = false;
			}
		}
		if (unitTasks.isEmpty())
			err("No input file specified");

		TranslationTask task = new TranslationTask(
				unitTasks.toArray(new UnitTask[unitTasks.size()]));

		if (outfileName == null)
			task.setOut(System.out);
		else
			task.setOut(new PrintStream(new File(outfileName)));
		if (linkLang != null)
			task.setLinkLanguage(linkLang);
		task.setVerbose(verbose);
		task.setPrettyPrint(pretty);
		task.setShowTables(tables);
		task.setShowTime(showTime);
		if (preprocOnly)
			task.setStage(TranslationStage.PREPROCESS_CONSUME);
		task.setPreprocTokens(ppt);
		task.setShowDiff(showDiff);
		task.setSummarize(summarize);
		task.setSilent(silent);
		task.setShowUndefinedFunctions(showUndefinedFunctions);
		task.setSVCOMP(svcomp);
		task.setArchitecture(architecture);
		task.addAllTransformCodes(transformCodes);
		return task;
	}

	/**
	 * Executes the commands specified by the command line.
	 * 
	 * @param args
	 *            the command line arguments
	 * @throws ABCException
	 *             if something goes wrong when processing the file, such as a
	 *             syntax exception
	 * @throws IOException
	 *             if the file cannot be opened
	 */
	public static void main(String[] args) {
		TranslationTask task = null;
		PrintStream err = System.err, out = System.out;
		boolean silent = false;

		for (String arg : args) {
			if (arg.equals("-silent"))
				silent = true;
		}
		if (!silent)
			out.println("ABC v" + version + " of " + date
					+ " -- http://vsl.cis.udel.edu/abc\n");
		out.flush();
		try {
			task = parseCommandLine(args);
		} catch (FileNotFoundException e) {
			err.println(e.toString());
			err.flush();
			System.exit(1);
		} catch (ABCException e) {
			// illegal transform code
			err.println(e.toString());
			err.flush();
			System.exit(2);
		}

		ABCExecutor executor = new ABCExecutor(task);

		try {
			executor.execute();
		} catch (PreprocessorException e) {
			err.println(e.toString());
			err.flush();
			System.exit(3);
		} catch (PreprocessorRuntimeException e) {
			err.println(e.toString());
			err.flush();
			System.exit(4);
		} catch (ParseException e) {
			err.println(e.toString());
			err.flush();
			System.exit(5);
		} catch (SyntaxException e) {
			err.println(e.toString());
			err.flush();
			System.exit(6);
		} catch (ABCException e) {
			err.println(e.toString());
			err.flush();
			System.exit(7);
		}
		task.getOut().close();
	}

}