BaseWorker.java
package edu.udel.cis.vsl.civl.transform.common;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import edu.udel.cis.vsl.abc.FrontEnd;
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.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.SequenceNode;
import edu.udel.cis.vsl.abc.ast.node.IF.declaration.FunctionDeclarationNode;
import edu.udel.cis.vsl.abc.ast.node.IF.declaration.FunctionDefinitionNode;
import edu.udel.cis.vsl.abc.ast.node.IF.declaration.VariableDeclarationNode;
import edu.udel.cis.vsl.abc.ast.node.IF.expression.ExpressionNode;
import edu.udel.cis.vsl.abc.ast.node.IF.expression.FunctionCallNode;
import edu.udel.cis.vsl.abc.ast.node.IF.expression.IdentifierExpressionNode;
import edu.udel.cis.vsl.abc.ast.node.IF.statement.BlockItemNode;
import edu.udel.cis.vsl.abc.ast.node.IF.statement.CompoundStatementNode;
import edu.udel.cis.vsl.abc.ast.node.IF.type.FunctionTypeNode;
import edu.udel.cis.vsl.abc.ast.node.IF.type.TypeNode;
import edu.udel.cis.vsl.abc.ast.type.IF.ArrayType;
import edu.udel.cis.vsl.abc.ast.type.IF.EnumerationType;
import edu.udel.cis.vsl.abc.ast.type.IF.PointerType;
import edu.udel.cis.vsl.abc.ast.type.IF.StandardBasicType;
import edu.udel.cis.vsl.abc.ast.type.IF.StandardBasicType.BasicTypeKind;
import edu.udel.cis.vsl.abc.ast.type.IF.Type;
import edu.udel.cis.vsl.abc.parse.IF.CParser;
import edu.udel.cis.vsl.abc.parse.IF.OmpCParser;
import edu.udel.cis.vsl.abc.parse.IF.ParseException;
import edu.udel.cis.vsl.abc.parse.IF.ParseTree;
import edu.udel.cis.vsl.abc.preproc.IF.Preprocessor;
import edu.udel.cis.vsl.abc.preproc.IF.PreprocessorException;
import edu.udel.cis.vsl.abc.token.IF.CToken;
import edu.udel.cis.vsl.abc.token.IF.CTokenSource;
import edu.udel.cis.vsl.abc.token.IF.Formation;
import edu.udel.cis.vsl.abc.token.IF.Macro;
import edu.udel.cis.vsl.abc.token.IF.Source;
import edu.udel.cis.vsl.abc.token.IF.SourceFile;
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.TransformFormation;
import edu.udel.cis.vsl.abc.transform.IF.Transformer;
import edu.udel.cis.vsl.civl.config.IF.CIVLConstants;
/**
* Object used to perform one transformation task. It is instantiated to carry
* out one invocation of {@link CIVLBaseTransformer#transform(AST)}.
*
* @author siegel
*/
public abstract class BaseWorker {
protected final static String _MAIN = "_main";
protected final static String MAIN = "main";
protected final static String ASSUME = "$assume";
protected final static String ASSERT = "$assert";
protected String identifierPrefix;
/**
* The number of new identifiers created by this transformer worker.
*/
protected int newIdentifierCounter = 0;
/**
* The name of this transformer, e.g., "OMPtoCIVLTransformer". To be used in
* output such as error messages.
*/
protected String transformerName;
/**
* The AST factory used by this transformer for all its AST needs.
*/
protected ASTFactory astFactory;
/**
* The node factory used by this transformer; same as the node factory
* associated to the {@link #astFactory}.
*/
protected NodeFactory nodeFactory;
/**
* The token factory used by this transformer; same as the token factory
* used by the {@link #astFactory}.
*/
protected TokenFactory tokenFactory;
/* ****************************** Constructor ************************** */
protected BaseWorker(String transformerName, ASTFactory astFactory) {
this.transformerName = transformerName;
this.astFactory = astFactory;
this.nodeFactory = astFactory.getNodeFactory();
this.tokenFactory = astFactory.getTokenFactory();
}
/* ************************** Protected Methods ************************ */
/**
* Transforms the AST. This is the method that will be invoked to implement
* {@link Transformer#transform(AST)}.
*
* @param ast
* the given AST to transform
* @return the transformed AST, which may or may not == the given one
* @throws SyntaxException
* if some statically-detectable error is discovered in the
* process of transformation
*/
protected abstract AST transform(AST ast) throws SyntaxException;
/**
* Does the root node contains a _main function definition in its children?
*
* @return
*/
protected boolean has_mainFunction(SequenceNode<BlockItemNode> root) {
for (BlockItemNode child : root) {
if (child == null)
continue;
if (child instanceof FunctionDefinitionNode) {
if (((FunctionDefinitionNode) child).getName().equals(_MAIN))
return true;
}
}
return false;
}
/**
* rename all main function declaration to _main, and all function call to
* main to _main.
*
* @param root
*/
protected void transformMainFunction(SequenceNode<BlockItemNode> root) {
for (BlockItemNode child : root) {
if (child == null)
continue;
if (child instanceof FunctionDeclarationNode) {
FunctionDeclarationNode funcDecl = (FunctionDeclarationNode) child;
if (funcDecl.getName().equals(MAIN)) {
funcDecl.getIdentifier().setName(_MAIN);
// FunctionTypeNode funcType = funcDecl.getTypeNode();
//
// VariableDeclarationNode secondPara = funcType
// .getParameters().getSequenceChild(1);
// secondPara.getTypeNode().setConstQualified(true);
}
}
transformMainCall(child);
}
}
protected void createNewMainFunction(SequenceNode<BlockItemNode> root) {
FunctionCallNode callMain;
List<BlockItemNode> blockItems = new LinkedList<>();
FunctionTypeNode mainFuncType;
FunctionDefinitionNode newMainFunction;
callMain = nodeFactory.newFunctionCallNode(
this.newSource("new main function", CParser.CALL),
this.identifierExpression(_MAIN),
new LinkedList<ExpressionNode>(), null);
blockItems.add(nodeFactory.newExpressionStatementNode(callMain));
mainFuncType = nodeFactory.newFunctionTypeNode(this.newSource(
"new main function", CParser.TYPE), nodeFactory
.newBasicTypeNode(
this.newSource("new main function", CParser.TYPE),
BasicTypeKind.INT), nodeFactory.newSequenceNode(this
.newSource("new main function", CParser.PARAMETER_TYPE_LIST),
"formal parameter types",
new LinkedList<VariableDeclarationNode>()), false);
newMainFunction = nodeFactory.newFunctionDefinitionNode(this.newSource(
"new main function", CParser.FUNCTION_DEFINITION), this
.identifier(MAIN), mainFuncType, null, nodeFactory
.newCompoundStatementNode(
this.newSource("new main function", CParser.BODY),
blockItems));
root.addSequenceChild(newMainFunction);
}
/**
* rename all calls to main to _main.
*
* @param node
*/
private void transformMainCall(ASTNode node) {
if (node instanceof FunctionCallNode) {
FunctionCallNode call = (FunctionCallNode) node;
ExpressionNode function = call.getFunction();
if (function instanceof IdentifierExpressionNode) {
IdentifierNode functionID = ((IdentifierExpressionNode) function)
.getIdentifier();
if (functionID.name().equals(MAIN))
functionID.setName(_MAIN);
}
}
for (ASTNode child : node.children()) {
if (child == null)
continue;
this.transformMainCall(child);
}
}
protected FunctionDeclarationNode assumeFunctionDeclaration(Source source) {
IdentifierNode name = nodeFactory.newIdentifierNode(source, "$assume");
FunctionTypeNode funcType = nodeFactory.newFunctionTypeNode(source,
nodeFactory.newVoidTypeNode(source), nodeFactory
.newSequenceNode(source, "Formals", Arrays
.asList(nodeFactory.newVariableDeclarationNode(
source, nodeFactory.newIdentifierNode(
source, "expression"),
nodeFactory.newBasicTypeNode(source,
BasicTypeKind.BOOL))))
, false);
return nodeFactory.newFunctionDeclarationNode(source, name, funcType,
null);
}
protected FunctionCallNode functionCall(Source source, String name,
List<ExpressionNode> arguments) {
return nodeFactory.newFunctionCallNode(source,
this.identifierExpression(source, name), arguments, null);
}
/**
* Produces a unique identifier ending with the given name.
*
* @param name
* The ending of the new unique identifier.
* @return a unique identifier ending with the given name.
*/
protected String newUniqueIdentifier(String name) {
return identifierPrefix + this.newIdentifierCounter++ + "_" + name;
}
/**
* Produces a unique identifier.
*
* @return a unique identifier.
*/
protected String newUniqueIdentifierPrefix() {
return identifierPrefix + this.newIdentifierCounter++;
}
/**
* parses a certain CIVL library (which resides in the folder text/include)
* into an AST.
*
* @param filename
* the file name of the library, e.g., civlc.cvh, civlc-omp.cvh,
* etc.
* @return the AST of the given library.
* @throws SyntaxException
*/
protected AST parseSystemLibrary(String filename) throws SyntaxException {
FrontEnd frontEnd = new FrontEnd();
Preprocessor preprocessor = frontEnd.getPreprocessor();
CTokenSource tokenSource;
ParseTree tree;
try {
tokenSource = preprocessor.outputTokenSource(
new File[] { CIVLConstants.CIVL_INCLUDE_PATH },
new File[0], new HashMap<String, Macro>(), filename);
tree = frontEnd.getParser().parse(tokenSource);
} catch (PreprocessorException | IOException | ParseException e) {
return null;
}
return frontEnd.getASTBuilder().getTranslationUnit(tree);
}
/**
* Creates a new {@link Source} object to associate to AST nodes that are
* invented by this transformer worker.
*
* @param method
* any string which identifies the specific part of this
* transformer responsible for creating the new content;
* typically, the name of the method that created the new
* context. This will appear in error message to help isolate the
* source of the new content.
* @param text
* the text to be shown for the source which should be some
* informative message about the source
* @param tokenType
* the integer code for the type of the token used to represent
* the source; use one of the constants in {@link CParser} or
* {@link OmpCParser}, for example, such as
* {@link CParser#IDENTIFIER}.
* @return the new source object
*/
protected Source newSource(String method, int tokenType) {
Formation formation = tokenFactory.newTransformFormation(
transformerName, method);
CToken token = tokenFactory.newCToken(tokenType, "inserted text",
formation);
Source source = tokenFactory.newSource(token);
return source;
}
/**
* This method should be called after the transformer has completed its
* transformation; it finds all source objects (in node and the descendants
* of node) that were created by this transformer and adds more information
* to them. The new information includes the pretty-print textual
* representation of the content of that node (and its descendants), and the
* precise point in original actual source code where the new content was
* inserted.
*
* @param node
* a node in the AST being transformed; typically, the root node
*/
protected void completeSources(ASTNode node) {
ASTNode postNode = nextRealNode(node);
ASTNode preNode = null;
for (; node != null; node = node.nextDFS()) {
Source source = node.getSource();
if (source != null) {
CToken firstToken = source.getFirstToken();
if (firstToken != null) {
Formation formation = firstToken.getFormation();
if (formation instanceof TransformFormation) {
TransformFormation tf = (TransformFormation) formation;
if (transformerName.equals(tf.getLastFile().getName())) {
CToken preToken = preNode == null ? null : preNode
.getSource().getLastToken();
CToken postToken = postNode == null ? null
: postNode.getSource().getFirstToken();
String text = node.prettyRepresentation()
.toString();
if (text.length() > 20)
text = text.substring(0, 18) + "...";
tf.setPreToken(preToken);
tf.setPostToken(postToken);
firstToken.setText(text);
} else {
if (node == postNode) {
preNode = postNode;
postNode = nextRealNode(preNode);
}
}
}
}
}
}
}
/**
* Creates an identifier node with a given name. The source information of
* the new node is automatically constructed using the method
* {@link #newSource(String, int)}.
*
* @param name
* The name of the identifier.
* @return the new identifier node.
*/
protected IdentifierNode identifier(String name) {
return nodeFactory.newIdentifierNode(
this.newSource("identifier " + name, CParser.IDENTIFIER), name);
}
/**
* Creates an identifier expression node with a given name. The source
* information of the new node is automatically constructed using the method
* {@link #newSource(String, int)}.
*
* @param name
* The name of the identifier.
* @return the new identifier expression node.
*/
protected ExpressionNode identifierExpression(String name) {
Source source = this
.newSource("identifier " + name, CParser.IDENTIFIER);
return nodeFactory.newIdentifierExpressionNode(source,
nodeFactory.newIdentifierNode(source, name));
}
/**
* Creates an identifier expression node with a given name.
*
* @param source
* The source information of the identifier.
* @param name
* The name of the identifier.
* @return the new identifier expression node.
*/
protected ExpressionNode identifierExpression(Source source, String name) {
return nodeFactory.newIdentifierExpressionNode(source,
nodeFactory.newIdentifierNode(source, name));
}
/**
* Creates a variable declaration node with a given name of the specified
* type. The sources are created automatically through the method
* {@link #newSource(String, int)}.
*
* @param name
* The name of the variable
* @param type
* The type of the variable
* @return the new variable declaration node
*/
protected VariableDeclarationNode variableDeclaration(String name,
TypeNode type) {
return nodeFactory.newVariableDeclarationNode(this.newSource(
"variable declaration of " + name, CParser.DECLARATION), this
.identifier(name), type);
}
/**
* Creates a variable declaration node with a given name of the specified
* type and initializer. The sources are created automatically through the
* method {@link #newSource(String, int)}.
*
* @param name
* The name of the variable
* @param type
* The type of the variable
* @param init
* The initializer of the variable
* @return the new variable declaration node.
*/
protected VariableDeclarationNode variableDeclaration(String name,
TypeNode type, ExpressionNode init) {
// String text = type.prettyRepresentation() + " " + name;
// if (init != null)
// text = text + " = " + init.prettyRepresentation();
return nodeFactory.newVariableDeclarationNode(this.newSource(
"variable declaration of " + name, CParser.DECLARATION), this
.identifier(name), type, init);
}
/**
* Creates a constant node of <code>$here</code>, the source of which is
* generated automatically using {@link #newSource(String, int)}.
*
* @return the new here node.
*/
protected ExpressionNode hereNode() {
return nodeFactory.newHereNode(this.newSource("constant $here",
CParser.HERE));
}
/**
* Creates a type node of void type, the source of which is generated
* automatically using {@link #newSource(String, int)}.
*
* @return the new void type node.
*/
protected TypeNode voidType() {
return nodeFactory.newVoidTypeNode(this.newSource("type void",
CParser.VOID));
}
/**
* Creates a type node of a certain basic type kind, the source of which is
* generated automatically using {@link #newSource(String, int)}.
*
* @param kind
* the specified basic type kind
* @return the new basic type node.
*/
protected TypeNode basicType(BasicTypeKind kind) {
String name = "";
switch (kind) {
case BOOL:
name = "_Bool";
break;
case CHAR:
name = "char";
break;
case DOUBLE:
case DOUBLE_COMPLEX:
name = "double";
break;
case FLOAT:
case FLOAT_COMPLEX:
name = "float";
break;
case INT:
name = "int";
break;
case LONG:
name = "long";
break;
case LONG_DOUBLE:
name = "long double";
break;
case LONG_DOUBLE_COMPLEX:
name = "long double";
break;
case LONG_LONG:
name = "long long";
break;
case REAL:
name = "real";
break;
case SHORT:
name = "short";
break;
case SIGNED_CHAR:
name = "signed char";
break;
case UNSIGNED:
name = "unsigned";
break;
case UNSIGNED_CHAR:
name = "unsigned char";
break;
case UNSIGNED_LONG:
name = "unsigned long";
break;
case UNSIGNED_LONG_LONG:
name = "unsigned long long";
break;
case UNSIGNED_SHORT:
name = "unsigned short";
default:
}
return this.nodeFactory.newBasicTypeNode(
this.newSource("type " + name, CParser.TYPE), kind);
}
/**
* Creates a type node of a given type, the source of which is generated
* automatically using {@link #newSource(String, int)}.
*
* @param type
* the specified type
* @return the new type node.
*/
protected TypeNode typeNode(Type type) {
Source source = this.newSource("type " + type, CParser.TYPE);
switch (type.kind()) {
case VOID:
return nodeFactory.newVoidTypeNode(source);
case BASIC:
return nodeFactory.newBasicTypeNode(source,
((StandardBasicType) type).getBasicTypeKind());
case OTHER_INTEGER:
return nodeFactory.newBasicTypeNode(source, BasicTypeKind.INT);
case ARRAY:
return nodeFactory.newArrayTypeNode(source,
this.typeNode(((ArrayType) type).getElementType()),
((ArrayType) type).getVariableSize().copy());
case POINTER:
return nodeFactory.newPointerTypeNode(source,
this.typeNode(((PointerType) type).referencedType()));
case ENUMERATION:
return nodeFactory.newEnumerationTypeNode(source,
this.identifier(((EnumerationType) type).getTag()), null);
default:
}
return null;
}
/**
* Creates a type node of a given type, with the given source.
*
* @param source
* The source of the type node
* @param type
* the specified type
* @return the new type node
*/
protected TypeNode typeNode(Source source, Type type) {
switch (type.kind()) {
case VOID:
return nodeFactory.newVoidTypeNode(source);
case BASIC:
return nodeFactory.newBasicTypeNode(source,
((StandardBasicType) type).getBasicTypeKind());
case OTHER_INTEGER:
return nodeFactory.newBasicTypeNode(source, BasicTypeKind.INT);
case ARRAY:
return nodeFactory.newArrayTypeNode(source,
this.typeNode(source, ((ArrayType) type).getElementType()),
((ArrayType) type).getVariableSize().copy());
case POINTER:
return nodeFactory.newPointerTypeNode(source, this.typeNode(source,
((PointerType) type).referencedType()));
default:
}
return null;
}
/**
* Creates a boolean constant node (either <code>$true</code> or
* <code>$false</code>), the source of which is generated automatically
* using {@link #newSource(String, int)}.
*
* @param value
* The value of the boolean constant
* @return the new boolean constant node
*/
protected ExpressionNode booleanConstant(boolean value) {
String method = value ? "constant $true" : "constant $false";
int tokenType = value ? CParser.TRUE : CParser.FALSE;
return nodeFactory.newBooleanConstantNode(
this.newSource(method, tokenType), value);
}
/**
* Creates an integer constant node of the specified value, the source of
* which is generated automatically using {@link #newSource(String, int)}.
*
* @param value
* The value of the integer constant
* @return the new integer constant node
*/
protected ExpressionNode integerConstant(int value) throws SyntaxException {
return nodeFactory.newIntegerConstantNode(
this.newSource("constant " + value, CParser.INTEGER_CONSTANT),
Integer.toString(value));
}
/**
* Combines two ASTs into one, assuming that there are no name conflicts.
*
* @param first
* the first AST
* @param second
* the second AST
* @return
* @throws SyntaxException
*/
protected AST combineASTs(AST first, AST second) throws SyntaxException {
SequenceNode<BlockItemNode> rootNode;
List<BlockItemNode> firstNodes = new ArrayList<>(), secondNodes = new ArrayList<>(), allNodes = new ArrayList<>();
List<SourceFile> sourceFiles = new ArrayList<>();
for (BlockItemNode child : first.getRootNode()) {
if (child != null)
firstNodes.add(child.copy());
}
for (BlockItemNode child : second.getRootNode()) {
// avoid identical nodes introduced by same "includes"
if (child != null && !this.existNode(firstNodes, child))
secondNodes.add(child.copy());
}
allNodes.addAll(firstNodes);
allNodes.addAll(secondNodes);
sourceFiles.addAll(first.getSourceFiles());
sourceFiles.addAll(second.getSourceFiles());
rootNode = this.nodeFactory.newSequenceNode(first.getRootNode()
.getSource(), "Translation Unit", allNodes);
return this.astFactory.newAST(rootNode, sourceFiles);
}
/**
* insert a block item node to a compound statement node at the given index.
*
* @param compoundNode
* @param node
* @return
*/
protected CompoundStatementNode insertToCompoundStatement(
CompoundStatementNode compoundNode, BlockItemNode node, int index) {
int numChildren = compoundNode.numChildren();
List<BlockItemNode> nodeList = new ArrayList<>(numChildren + 1);
for (int i = 0; i < numChildren; i++) {
BlockItemNode child = compoundNode.getSequenceChild(i);
if (i == index)
nodeList.add(node);
nodeList.add(child);
compoundNode.removeChild(i);
}
if (index >= numChildren)
nodeList.add(node);
return nodeFactory.newCompoundStatementNode(compoundNode.getSource(),
nodeList);
}
/* *************************** Private Methods ************************* */
/**
* Checks if a list of nodes contains an equivalent node of a given node.
*
* @param nodes
* the list of nodes
* @param theNode
* the specified node
* @return true iff the list of nodes contains an identical node of the
* specified node.
*/
private boolean existNode(List<? extends ASTNode> nodes, ASTNode theNode) {
for (ASTNode node : nodes) {
if (node.diff(theNode) == null)
return true;
}
return false;
}
/**
* Determines whether the given node is a leaf node, i.e., a node with no
* non-null children.
*
* @param node
* a non-null AST node
* @return true iff node is a leaf node
*/
private boolean isLeaf(ASTNode node) {
for (ASTNode child : node.children()) {
if (child != null)
return false;
}
return true;
}
/**
* Finds the next node u after the given node, in DFS order, which satisfies
* (1) u is a leaf node, and (2) u contains "actual" source (i.e., not
* source generated by a transformer).
*
* @param node
* any AST node
* @return next leaf node whose first token is actual source, or null if
* there is none
*/
private ASTNode nextRealNode(ASTNode node) {
while (true) {
node = node.nextDFS();
if (node == null)
break;
if (isLeaf(node)) {
Source source = node.getSource();
if (source != null) {
CToken token = source.getFirstToken();
if (token != null) {
Formation formation = token.getFormation();
if (!(formation instanceof TransformFormation))
break;
}
}
}
}
return node;
}
}