FrontEnd.java
package edu.udel.cis.vsl.abc.main;
import java.io.File;
import java.io.PrintStream;
import java.util.HashMap;
import java.util.Map;
import edu.udel.cis.vsl.abc.analysis.IF.Analysis;
import edu.udel.cis.vsl.abc.analysis.IF.Analyzer;
import edu.udel.cis.vsl.abc.ast.IF.AST;
import edu.udel.cis.vsl.abc.ast.IF.ASTFactory;
import edu.udel.cis.vsl.abc.ast.IF.ASTs;
import edu.udel.cis.vsl.abc.ast.conversion.IF.ConversionFactory;
import edu.udel.cis.vsl.abc.ast.conversion.IF.Conversions;
import edu.udel.cis.vsl.abc.ast.entity.IF.Entities;
import edu.udel.cis.vsl.abc.ast.entity.IF.Entity;
import edu.udel.cis.vsl.abc.ast.entity.IF.EntityFactory;
import edu.udel.cis.vsl.abc.ast.entity.IF.Scope;
import edu.udel.cis.vsl.abc.ast.node.IF.ASTNode;
import edu.udel.cis.vsl.abc.ast.node.IF.NodeFactory;
import edu.udel.cis.vsl.abc.ast.node.IF.Nodes;
import edu.udel.cis.vsl.abc.ast.type.IF.Type;
import edu.udel.cis.vsl.abc.ast.type.IF.TypeFactory;
import edu.udel.cis.vsl.abc.ast.type.IF.Types;
import edu.udel.cis.vsl.abc.ast.value.IF.Value;
import edu.udel.cis.vsl.abc.ast.value.IF.ValueFactory;
import edu.udel.cis.vsl.abc.ast.value.IF.Values;
import edu.udel.cis.vsl.abc.config.IF.Configuration;
import edu.udel.cis.vsl.abc.config.IF.Configurations.Language;
import edu.udel.cis.vsl.abc.front.IF.ASTBuilder;
import edu.udel.cis.vsl.abc.front.IF.Front;
import edu.udel.cis.vsl.abc.front.IF.ParseException;
import edu.udel.cis.vsl.abc.front.IF.ParseTree;
import edu.udel.cis.vsl.abc.front.IF.Parser;
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.c.ptree.CParseTree;
import edu.udel.cis.vsl.abc.program.IF.Program;
import edu.udel.cis.vsl.abc.program.IF.ProgramFactory;
import edu.udel.cis.vsl.abc.program.IF.Programs;
import edu.udel.cis.vsl.abc.token.IF.CivlcToken;
import edu.udel.cis.vsl.abc.token.IF.CivlcTokenSource;
import edu.udel.cis.vsl.abc.token.IF.FileIndexer;
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.TokenFactory;
import edu.udel.cis.vsl.abc.token.IF.Tokens;
import edu.udel.cis.vsl.abc.transform.IF.Transform;
import edu.udel.cis.vsl.abc.transform.IF.Transformer;
/**
* <p>
* A FrontEnd provides a simple, high-level interface for accessing all of the
* main functionality of ABC. It provides two different families of methods: (1)
* methods to get or create individual components of the ABC tool chain, such as
* factories, {@link Preprocessor}s, {@link Parser}s, etc., and (2) higher-level
* methods which marshal together these different components in order to carry
* out a complete translation task, such as compiling a translation unit, or
* linking several translation units to form a complete {@link Program}.
* </p>
*
* @author siegel
*/
public class FrontEnd {
/**
* The configuration object specifying configuration options for this ABC
* session
*/
private Configuration configuration;
/**
* The {@link TokenFactory} used by this {@link FrontEnd} to create
* {@link CivlcToken}s, {@link Source}s and related objects.
*/
private TokenFactory tokenFactory;
/**
* The {@link TypeFactory} used by this {@link FrontEnd} to create
* {@link Type}s.
*/
private TypeFactory typeFactory;
/**
* The {@link ValueFactory} used by this {@link FrontEnd} to create
* {@link Value}s. The {@link Value}s are used to represent the result of
* evaluating a constant expression.
*/
private ValueFactory valueFactory;
/**
* The {@link NodeFactory} used by this {@link FrontEnd} to create new
* {@link ASTNode}s.
*/
private NodeFactory nodeFactory;
/**
* The {@link ASTFactory} used by this {@link FrontEnd} to produce all
* objects related to ASTs. It wraps a {@link NodeFactory},
* {@link TypeFactory}, and other factories.
*/
private ASTFactory astFactory;
/**
* The {@link EntityFactory} used by this {@link FrontEnd}.
*/
private EntityFactory entityFactory;
/**
* The {@link ConversionFactory} used by this {@link FrontEnd}.
*/
private ConversionFactory conversionFactory;
/**
* The {@link FileIndexer} used by this {@link FrontEnd} to keep track of
* all files opened.
*/
private FileIndexer fileIndexer;
/**
* For each programming {@link Language}, an {@link Analyzer} for that
* language that performs basic analysis on the AST and leaves behind
* important information in the AST nodes. These are created as needed and
* entered into this table.
*/
private Map<Language, Analyzer> analyzers = new HashMap<>();
/**
* For each programming {@link Language}, a {@link Parser} for that language
* used to parse the source code and create a {@link ParseTree}. These are
* created as needed and entered into this table.
*/
private Map<Language, Parser> parsers = new HashMap<>();
/**
* For each programming {@link Language}, a {@link Preprocessor}, used to
* preprocess a source file (and files included by that source file), to
* create a stream of {@link CivlcToken}s. These are created as needed and
* entered into this table.
*/
private Map<Language, Preprocessor> preprocessors = new HashMap<>();
/**
* For each programming {@link Language}, an {@link ASTBuilder} used to
* construct {@link AST}s from {@link ParseTree}s. These are created as
* needed and entered into this table.
*/
private Map<Language, ASTBuilder> astBuilders = new HashMap<>();
/**
* For each programming {@link Language}, a {@link ProgramFactory} used to
* construct a whole {@link Program} from a set of {@link AST}s representing
* individual translation units. This is commonly known as "linking" or "AST
* merging." These are created as needed and entered into this table.
*/
private Map<Language, ProgramFactory> programFactories = new HashMap<>();
/**
* Constructs a new front end. The front end can be used repeatedly to
* perform multiple translation tasks. The factories used by this front end
* will persist throughout its lifetime, i.e., new factories are not created
* for each task.
*
* @param configuration
* the configuration object specifying configuration options for
* this ABC session
*/
public FrontEnd(Configuration configuration) {
this.configuration = configuration;
}
/**
* Constructs a new front end, using an existing {@link FileIndexer}. The
* front end can be used repeatedly to perform multiple translation tasks.
* The factories used by this front end will persist throughout its
* lifetime, i.e., new factories are not created for each task.
*
* @param configuration
* the configuration object specifying configuration options for
* this ABC session
* @param fileIndexer
* the object used to keep track of all files opened in the
* course of this ABC session
*/
public FrontEnd(Configuration configuration, FileIndexer fileIndexer) {
this(configuration);
this.fileIndexer = fileIndexer;
}
/**
* Gets the {@link Configuration} object for this ABC Front-end.
*
* @return the {@link Configuration} object
*/
public Configuration getConfiguration() {
return this.configuration;
}
/**
* Returns the {@link TokenFactory} used by this {@link FrontEnd} to create
* {@link CivlcToken}s, {@link Source}s and related objects.
*
* @return the {@link TokenFactory} used by this {@link FrontEnd}
*/
public TokenFactory getTokenFactory() {
if (tokenFactory == null)
tokenFactory = Tokens.newTokenFactory();
return tokenFactory;
}
/**
* Returns the {@link TypeFactory} used by this {@link FrontEnd} to create
* {@link Type}s.
*
* @return the {@link TypeFactory} used by this {@link FrontEnd}
*/
public TypeFactory getTypeFactory() {
if (typeFactory == null)
typeFactory = Types.newTypeFactory();
return typeFactory;
}
/**
* Returns the {@link ValueFactory} used by this {@link FrontEnd} to create
* {@link Value}s. The {@link Value}s are used to represent the result of
* evaluating a constant expression.
*
* @return the {@link ValueFactory} used by this {@link FrontEnd}
*/
public ValueFactory getValueFactory() {
if (valueFactory == null)
valueFactory = Values.newValueFactory(configuration,
getTypeFactory());
return valueFactory;
}
/**
* Returns the {@link FileIndexer} used by this {@link FrontEnd} to keep
* track of all files opened.
*
* @return the {@link FileIndexer} used by this {@link FrontEnd}
*/
public FileIndexer getFileIndexer() {
if (fileIndexer == null)
fileIndexer = getTokenFactory().newFileIndexer();
return fileIndexer;
}
/**
* Gets the node factory used by this front end. The node factory is part of
* the {@link ASTFactory}.
*
* @return the node factory
*/
public NodeFactory getNodeFactory() {
if (nodeFactory == null)
nodeFactory = Nodes.newNodeFactory(configuration, getTypeFactory(),
getValueFactory());
return nodeFactory;
}
/**
* Returns the {@link ASTFactory} used by this front end. This factory (or
* its sub-factories) are used to create all components of an AST, including
* new {@link ASTNode}s.
*
* @return the AST factory
*/
public ASTFactory getASTFactory() {
if (astFactory == null)
astFactory = ASTs.newASTFactory(getNodeFactory(), getTokenFactory(),
getTypeFactory());
return astFactory;
}
/**
* Returns the {@link EntityFactory} used by this {@link FrontEnd}.
*
* @return the {@link EntityFactory} used by this {@link FrontEnd}
*/
public EntityFactory getEntityFactory() {
if (entityFactory == null)
entityFactory = Entities.newEntityFactory();
return entityFactory;
}
/**
* Returns the {@link ConversionFactory} used by this {@link FrontEnd}.
*
* @return the {@link ConversionFactory} used by this {@link FrontEnd}
*/
public ConversionFactory getConversionFactory() {
if (conversionFactory == null)
conversionFactory = Conversions
.newConversionFactory(getTypeFactory());
return conversionFactory;
}
/**
* Creates a {@link Preprocessor} based on the specified system and include
* path lists. The new {@link Preprocessor} can be used to preprocess source
* files repeatedly. The method {@link Preprocessor#outputTokenSource} is
* used to obtain the stream of tokens emanating from the preprocessor.
*
* @param language
* the language of requested preprocessor
* @return the new Preprocessor
*/
public Preprocessor getPreprocessor(Language language) {
Preprocessor result = preprocessors.get(language);
if (result == null) {
result = Front.newPreprocessor(language, configuration,
getFileIndexer(), getTokenFactory());
preprocessors.put(language, result);
}
return result;
}
/**
* Returns the parser used by this front end. The parser is used to parse a
* token stream and produce a {@link ParseTree}. The parser can be used
* repeatedly.
*
* @param language
* the language of the requested parser
* @return the parser
*/
public Parser getParser(Language language) {
Parser result = parsers.get(language);
if (result == null) {
result = Front.newParser(language);
parsers.put(language, result);
}
return result;
}
/**
* Returns the {@link ASTBuilder} used by this front end. The builder is
* used convert a {@link CParseTree} to an {@link AST}. The builder can be
* used repeatedly.
*
* @param language
* the language of the requested AST builder
* @return the builder used to translate parse trees to ASTs
*/
public ASTBuilder getASTBuilder(Language language) {
ASTBuilder result = astBuilders.get(language);
if (result == null) {
result = Front.newASTBuilder(language, configuration,
getASTFactory());
astBuilders.put(language, result);
}
return result;
}
/**
* Returns a standard {@link Analyzer}, which is used to analyze an AST,
* leaving behind information such as (1) the {@link Scope} of every node,
* (2) the {@link Type} of every expression, (3) the {@link Entity}
* associated to every identifier.
*
* @param language
* language of the requested analyzer
* @return a standard {@link Analyzer} for that language
*/
public Analyzer getStandardAnalyzer(Language language) {
Analyzer result = analyzers.get(language);
if (result == null) {
result = Analysis.newStandardAnalyzer(language, configuration,
getASTFactory(), getEntityFactory(),
getConversionFactory());
analyzers.put(language, result);
}
return result;
}
/**
* Returns a program factory based on the given analyzer. The factory will
* apply that analyzer every time it instantiates a new {@link Program}.
*
* @param analyzer
* an analyzer that will be applied to any program created by the
* factory
* @return the new program factory based on the analyzer
*/
public ProgramFactory getProgramFactory(Language language) {
ProgramFactory result = programFactories.get(language);
if (result == null) {
result = Programs.newProgramFactory(getASTFactory(),
getStandardAnalyzer(language));
programFactories.put(language, result);
}
return result;
}
/**
* Creates a new {@link Transformer} specified by the given transformer
* code.
*
* @param code
* a string code which specifies a transformer
* @return the new transformer
*/
public Transformer getTransformer(String code) {
return Transform.newTransformer(code, getASTFactory());
}
// Actions...
/**
* Preprocesses and parses the specified files, returning an AST
* representation. The AST will not be analyzed, and so will not have any
* information on types, identifiers, entities, and so on. This result is
* known as a "raw" translation unit.
*
* @param language
* the language of the translation unit
* @param sourceUnit
* the file sequence to parse as a single translation unit
* @param systemIncludePaths
* the system include paths to search for included system
* headers; may use {@link ABC#DEFAULT_SYSTEM_INCLUDE_PATHS}
* @param userIncludePaths
* the user include paths to search for included user headers;
* may use {@link ABC#DEFAULT_USER_INCLUDE_PATHS}
* @param predefinedMacros
* map from macro names to macros bodies to incorporate before
* preprocessing; such macros might be defined on the command
* line via -DMACRO=VALUE, for example; may use
* {@link ABC#DEFAULT_IMPLICIT_MACROS}
* @return the raw translation unit obtained by parsing the file
* @throws PreprocessorException
* if the file contains a preprocessor error
* @throws ParseException
* if the token stream emanating from the preprocessor does not
* satisfy the grammar of the language
* @throws SyntaxException
* if the file violates some aspect of the syntax of the
* language
*/
public AST parse(Language language, File[] sourceUnit,
File[] systemIncludePaths, File[] userIncludePaths,
Map<String, String> predefinedMacros)
throws PreprocessorException, SyntaxException, ParseException {
Preprocessor preprocessor = getPreprocessor(language);
CivlcTokenSource tokens = preprocessor.preprocess(systemIncludePaths,
userIncludePaths, predefinedMacros, sourceUnit);
Parser parser = this.getParser(language);
ParseTree parseTree = parser.parse(tokens);
ASTBuilder builder = this.getASTBuilder(language);
AST ast = builder.getTranslationUnit(parseTree);
return ast;
}
/**
* Compiles the given files as a single translation unit, producing an AST
* representation with full analysis results. The AST will contain type
* information, symbol table information mapping every identifier to an
* {@link Entity}, scope information, and so on. It is an
* "analyzed translation unit".
*
* @param sourceUnit
* the file to compile
* @param language
* the language in which the file is written
* @param systemIncludePaths
* the system include paths to search for included system
* headers; may use {@link ABC#DEFAULT_SYSTEM_INCLUDE_PATHS}
* @param userIncludePaths
* the user include paths to search for included user headers;
* may use {@link ABC#DEFAULT_USER_INCLUDE_PATHS}
* @param implicitMacros
* map from macro names to bodies that are to be incorporated
* before preprocessing; such macros might be defined on the
* command line via -DMACRO=VALUE, for example; may use
* {@link ABC#DEFAULT_IMPLICIT_MACROS}
* @return the analyzed AST representing the translation unit
* @throws PreprocessorException
* if the file contains a preprocessor error
* @throws ParseException
* if the token stream emanating from the preprocessor does not
* satisfy the grammar of the language
* @throws SyntaxException
* if the file violates some aspect of the syntax of the
* language
*/
public AST compile(File[] sourceUnit, Language language,
File[] systemIncludePaths, File[] userIncludePaths,
Map<String, String> implicitMacros)
throws PreprocessorException, SyntaxException, ParseException {
AST result = parse(language, sourceUnit, systemIncludePaths,
userIncludePaths, implicitMacros);
Analyzer analyzer = getStandardAnalyzer(language);
analyzer.analyze(result);
return result;
}
/**
* Compiles the given files as a single translation unit, producing an AST
* representation with full analysis results. Equivalent to invoking
* {@link #compile(File, Language, File[], File[], Map) with the default
* values {@link ABC#DEFAULT_SYSTEM_INCLUDE_PATHS},
* {@link ABC#DEFAULT_USER_INCLUDE_PATHS},
* {@link ABC#DEFAULT_IMPLICIT_MACROS} for the last three arguments.
*
* @param sourceUnit
* the file sequence to compile
* @param language
* the language in which the file is written
* @return the analyzed AST representing the translation unit
* @throws PreprocessorException
* if the file contains a preprocessor error
* @throws ParseException
* if the token stream emanating from the preprocessor does not
* satisfy the grammar of the language
* @throws SyntaxException
* if the file violates some aspect of the syntax of the
* language
*/
public AST compile(File[] sourceUnit, Language language)
throws PreprocessorException, SyntaxException, ParseException {
return compile(sourceUnit, language, ABC.DEFAULT_SYSTEM_INCLUDE_PATHS,
ABC.DEFAULT_USER_INCLUDE_PATHS, ABC.DEFAULT_IMPLICIT_MACROS);
}
/**
* Links the given translation units to form a whole program. The
* translation units may be "raw" (containing no analysis information) or
* not---it makes no difference since any analysis information will be
* erased and replaced with a fresh analysis. The translation units will be
* merged to form a single large AST; some entities may have to be renamed
* in this process, to avoid naming conflicts.
*
* @param translationUnits
* ASTs representing individual translation units
* @param language
* the language to use when analyzing and linking
* @return the program formed by linking the translation units
* @throws SyntaxException
* if any translation unit contains some statically detectable
* error or the units cannot be linked for some reason
*/
public Program link(AST[] translationUnits, Language language)
throws SyntaxException {
ProgramFactory programFactory;
Program result;
programFactory = getProgramFactory(language);
result = programFactory.newProgram(translationUnits);
return result;
}
/**
* Prints the program, symbol table, and type information to the given
* output stream in a plain-text, human-readable format.
*
* @param out
* the output stream
* @param program
* the program
* @param pretty
* if true, print AST in the original language, else print in
* hierarchical form
* @param showTables
* if true, print the symbol and type tables in addition to the
* AST
*/
public void printProgram(PrintStream out, Program program, boolean pretty,
boolean showTables) {
if (pretty)
program.prettyPrint(out);
else
program.print(out);
if (showTables) {
out.println("\n\nSymbol Table:\n");
program.printSymbolTable(out);
out.println("\n\nTypes:\n");
typeFactory.printTypes(out);
}
out.println();
out.flush();
}
}