ComplexWorker.java
package dev.civl.mc.transform.common;
import static dev.civl.abc.ast.node.IF.expression.OperatorNode.Operator.DIV;
import static dev.civl.abc.ast.node.IF.expression.OperatorNode.Operator.DIVEQ;
import static dev.civl.abc.ast.node.IF.expression.OperatorNode.Operator.EQUALS;
import static dev.civl.abc.ast.node.IF.expression.OperatorNode.Operator.MINUS;
import static dev.civl.abc.ast.node.IF.expression.OperatorNode.Operator.MINUSEQ;
import static dev.civl.abc.ast.node.IF.expression.OperatorNode.Operator.NEQ;
import static dev.civl.abc.ast.node.IF.expression.OperatorNode.Operator.PLUS;
import static dev.civl.abc.ast.node.IF.expression.OperatorNode.Operator.PLUSEQ;
import static dev.civl.abc.ast.node.IF.expression.OperatorNode.Operator.TIMES;
import static dev.civl.abc.ast.node.IF.expression.OperatorNode.Operator.TIMESEQ;
import static dev.civl.abc.ast.node.IF.expression.OperatorNode.Operator.UNARYMINUS;
import static dev.civl.abc.ast.type.IF.StandardBasicType.BasicTypeKind.BOOL;
import static dev.civl.abc.ast.type.IF.StandardBasicType.BasicTypeKind.DOUBLE_COMPLEX;
import static dev.civl.abc.ast.type.IF.StandardBasicType.BasicTypeKind.FLOAT_COMPLEX;
import static dev.civl.abc.ast.type.IF.StandardBasicType.BasicTypeKind.LONG_DOUBLE_COMPLEX;
import java.io.File;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import dev.civl.abc.ast.IF.AST;
import dev.civl.abc.ast.IF.ASTFactory;
import dev.civl.abc.ast.conversion.IF.Conversion;
import dev.civl.abc.ast.node.IF.ASTNode;
import dev.civl.abc.ast.node.IF.IdentifierNode;
import dev.civl.abc.ast.node.IF.PairNode;
import dev.civl.abc.ast.node.IF.SequenceNode;
import dev.civl.abc.ast.node.IF.compound.CompoundInitializerNode;
import dev.civl.abc.ast.node.IF.compound.DesignationNode;
import dev.civl.abc.ast.node.IF.declaration.InitializerNode;
import dev.civl.abc.ast.node.IF.expression.CastNode;
import dev.civl.abc.ast.node.IF.expression.CompoundLiteralNode;
import dev.civl.abc.ast.node.IF.expression.ExpressionNode;
import dev.civl.abc.ast.node.IF.expression.FloatingConstantNode;
import dev.civl.abc.ast.node.IF.expression.FunctionCallNode;
import dev.civl.abc.ast.node.IF.expression.IdentifierExpressionNode;
import dev.civl.abc.ast.node.IF.expression.OperatorNode;
import dev.civl.abc.ast.node.IF.expression.OperatorNode.Operator;
import dev.civl.abc.ast.node.IF.statement.BlockItemNode;
import dev.civl.abc.ast.node.IF.statement.IfNode;
import dev.civl.abc.ast.node.IF.statement.LoopNode;
import dev.civl.abc.ast.node.IF.type.TypeNode;
import dev.civl.abc.ast.node.IF.type.TypedefNameNode;
import dev.civl.abc.ast.type.IF.ArithmeticType;
import dev.civl.abc.ast.type.IF.AtomicType;
import dev.civl.abc.ast.type.IF.FloatingType.FloatKind;
import dev.civl.abc.ast.type.IF.QualifiedObjectType;
import dev.civl.abc.ast.type.IF.StandardBasicType;
import dev.civl.abc.ast.type.IF.StandardBasicType.BasicTypeKind;
import dev.civl.abc.ast.type.IF.Type;
import dev.civl.abc.ast.type.IF.TypeFactory;
import dev.civl.abc.ast.value.IF.ComplexFloatingValue;
import dev.civl.abc.ast.value.IF.RealFloatingValue;
import dev.civl.abc.ast.value.IF.ValueFactory.Answer;
import dev.civl.abc.token.IF.Source;
import dev.civl.abc.token.IF.SourceFile;
import dev.civl.abc.token.IF.SyntaxException;
import dev.civl.mc.config.IF.CIVLConstants;
import dev.civl.mc.model.IF.CIVLInternalException;
public class ComplexWorker extends BaseWorker {
private static String COMPLEX_H = "complex.h";
private static String COMPLEX_CVL = "complex.cvl";
private static String MATH_H = "math.h";
// private static String MATH_H_MACRO = "_MATH_";
//
// private static String COMPLEX_H_MACRO = "_COMPLEX_";
private TypeFactory typeFactory;
/**
* The last index of a child node of root belong to math.h, or -1 if no math.h
* is present. This is needed so that complex.cvl can be inserted after math.h.
*/
private int mathHIndex = -1;
public ComplexWorker(String transformerName, ASTFactory astFactory) {
super(transformerName, astFactory);
typeFactory = astFactory.getTypeFactory();
}
/**
* Is the given type one of the 3 native C complex types: double _Complex, float
* _Complex, or long double _Complex?
*
* @param type the type, which may be null
* @return {@code} true iff {@code type} is one of the 3 native C complex types
*/
private boolean isComplex(Type type) {
if (type == null)
return false;
switch (type.kind()) {
case BASIC: {
BasicTypeKind btk = ((StandardBasicType) type).getBasicTypeKind();
return btk == DOUBLE_COMPLEX || btk == FLOAT_COMPLEX || btk == LONG_DOUBLE_COMPLEX;
}
case QUALIFIED:
return isComplex(((QualifiedObjectType) type).getBaseType());
case ATOMIC:
return isComplex(((AtomicType) type).getBaseType());
default:
return false;
}
}
private boolean isBool(Type type) {
if (type == null)
return false;
switch (type.kind()) {
case BASIC: {
BasicTypeKind btk = ((StandardBasicType) type).getBasicTypeKind();
return btk == BOOL;
}
case QUALIFIED:
return isBool(((QualifiedObjectType) type).getBaseType());
case ATOMIC:
return isBool(((AtomicType) type).getBaseType());
default:
return false;
}
}
private boolean isReal(Type type) {
if (type == null)
return false;
if (type instanceof ArithmeticType)
return ((ArithmeticType) type).inRealDomain();
if (type instanceof QualifiedObjectType)
return isReal(((QualifiedObjectType) type).getBaseType());
if (type instanceof AtomicType)
return isReal(((AtomicType) type).getBaseType());
return false;
}
/**
* Returns the basic type kind of a complex type.
*
* @param complexType one of the complex types
* @return the basic type kind of the given type
*/
private BasicTypeKind kind(Type complexType) {
switch (complexType.kind()) {
case BASIC:
return ((StandardBasicType) complexType).getBasicTypeKind();
case QUALIFIED:
return kind(((QualifiedObjectType) complexType).getBaseType());
case ATOMIC:
return kind(((AtomicType) complexType).getBaseType());
default:
throw new RuntimeException("unreachable");
}
}
/**
* Given a type node for one of the complex types, returns a new type node for
* the corresponding CIVL complex type: one of the $*_complex types. Type
* qualifiers are preserved.
*
* Note: a type node for a complex type must be one of the following: a
* TypedefNameNode, BasicTypeNode, or AtomicTypeNode.
*
* @param kind a basic type kind, one of *_COMPLEX
* @param source source for the type node for the new node
* @return new typedef name node
*/
private TypeNode replacementTypeNode(TypeNode complexTypeNode) {
Source source = complexTypeNode.getSource();
IdentifierNode idn;
switch (kind(complexTypeNode.getType())) {
case DOUBLE_COMPLEX:
idn = nodeFactory.newIdentifierNode(source, "$double_complex");
break;
case FLOAT_COMPLEX:
idn = nodeFactory.newIdentifierNode(source, "$float_complex");
break;
case LONG_DOUBLE_COMPLEX:
idn = nodeFactory.newIdentifierNode(source, "$ldouble_complex");
break;
default:
throw new RuntimeException("unreachable");
}
TypedefNameNode newtn = nodeFactory.newTypedefNameNode(idn, null);
newtn.setAtomicQualified(complexTypeNode.isAtomicQualified());
newtn.setConstQualified(complexTypeNode.isConstQualified());
newtn.setRestrictQualified(complexTypeNode.isRestrictQualified());
newtn.setVolatileQualified(complexTypeNode.isVolatileQualified());
newtn.setInputQualified(complexTypeNode.isInputQualified());
newtn.setOutputQualified(complexTypeNode.isOutputQualified());
return newtn;
}
private TypeNode replacementTypeNode(Type complexType, Source source) {
IdentifierNode idn;
switch (kind(complexType)) {
case DOUBLE_COMPLEX:
idn = nodeFactory.newIdentifierNode(source, "$double_complex");
break;
case FLOAT_COMPLEX:
idn = nodeFactory.newIdentifierNode(source, "$float_complex");
break;
case LONG_DOUBLE_COMPLEX:
idn = nodeFactory.newIdentifierNode(source, "$ldouble_complex");
break;
default:
throw new RuntimeException("unreachable");
}
TypedefNameNode newtn = nodeFactory.newTypedefNameNode(idn, null);
// Note: _Atomic(type) is a type specifier, represented by an AtomicType and an
// AtomicTypeNode.
// _Atomic ... is a type qualifier, represented by an AtomicType and an
// arbitrary TypeNode with the atomic-qualified bit set.
switch (complexType.kind()) {
case BASIC:
return newtn;
case QUALIFIED: {
QualifiedObjectType qot = (QualifiedObjectType) complexType;
newtn.setAtomicQualified(false);
newtn.setConstQualified(qot.isConstQualified());
newtn.setRestrictQualified(qot.isRestrictQualified());
newtn.setVolatileQualified(qot.isVolatileQualified());
newtn.setInputQualified(qot.isInputQualified());
newtn.setOutputQualified(qot.isOutputQualified());
return newtn;
}
case ATOMIC:
// choice: AtomicTypeNode, or just qualify the typedef name node.
return nodeFactory.newAtomicTypeNode(source, newtn);
default:
throw new RuntimeException("unreachable");
}
}
/**
* Is the operator one of the assignment operators that combines an arithmetic
* operation with assignment, possibly on complex numbers: +=, -=, *=, or /=.
*
* @param op any Operator
* @return {@code true} iff {@code op} is one of the 4 operators above
*/
private boolean isAssignOp(Operator op) {
return op == PLUSEQ || op == MINUSEQ || op == TIMESEQ || op == DIVEQ;
}
/**
* Is the operator one that performs an arithmetic operation that could possibly
* consume a complex type. This includes the assignment operators +=, -=, etc.,
* as well as the pure operators +,-, etc. It includes == and !=, and the unary
* minus operator as well.
*
* @param op any Operator
* @return {@code true} iff {@code op} is an operator
*/
private boolean isArithmeticOp(Operator op) {
return isAssignOp(op) || op == PLUS || op == MINUS || op == TIMES || op == DIV || op == EQUALS || op == NEQ
|| op == UNARYMINUS;
}
private ExpressionNode realToComplex(ExpressionNode realExpr, Type complexType) {
// Result will look like: ($*_complex){ realExpr, 0 }
// The int 0 will be converted to the appropriate real type.
// Note: we already checked all the static type properties before getting to
// this Transformer, so we can assume they are all good.
Source source = realExpr.getSource();
ExpressionNode zeroNode = nodeFactory.newIntConstantNode(source, 0);
realExpr.remove();
PairNode<DesignationNode, InitializerNode> realPair = nodeFactory.newPairNode(source, null, realExpr),
imagPair = nodeFactory.newPairNode(source, null, zeroNode);
TypeNode typeNode = replacementTypeNode(complexType, source);
CompoundInitializerNode cin = nodeFactory.newCompoundInitializerNode(source, Arrays.asList(realPair, imagPair));
CompoundLiteralNode cln = nodeFactory.newCompoundLiteralNode(source, typeNode, cin);
cln.setInitialType(complexType);
return cln;
}
private ExpressionNode complexToBool(ExpressionNode node, Type complexType) {
Source source = node.getSource();
String name;
switch (kind(complexType)) {
case FLOAT_COMPLEX:
name = "$cfloat2bool";
break;
case DOUBLE_COMPLEX:
name = "$cdouble2bool";
break;
case LONG_DOUBLE_COMPLEX:
name = "$cldouble2bool";
break;
default:
throw new RuntimeException("unreachable");
}
node.remove();
FunctionCallNode fcn = nodeFactory.newFunctionCallNode(source,
nodeFactory.newIdentifierExpressionNode(source, nodeFactory.newIdentifierNode(source, name)),
Arrays.asList(node), null);
fcn.setInitialType(typeFactory.basicType(BOOL));
return fcn;
}
private ExpressionNode complexToComplex(ExpressionNode node, Type oldComplexType, Type newComplexType) {
Source source = node.getSource();
BasicTypeKind kind1 = kind(oldComplexType), kind2 = kind(newComplexType);
if (kind1 == kind2)
return node;
String name;
switch (kind(oldComplexType)) {
case FLOAT_COMPLEX:
name = kind2 == DOUBLE_COMPLEX ? "$cfloat2double" : "$cfloat2ldouble";
break;
case DOUBLE_COMPLEX:
name = kind2 == FLOAT_COMPLEX ? "$cdouble2float" : "$cdouble2ldouble";
break;
case LONG_DOUBLE_COMPLEX:
name = kind2 == FLOAT_COMPLEX ? "$cldouble2float" : "$cldouble2double";
break;
default:
throw new RuntimeException("unreachable");
}
node.remove();
FunctionCallNode fcn = nodeFactory.newFunctionCallNode(source,
nodeFactory.newIdentifierExpressionNode(source, nodeFactory.newIdentifierNode(source, name)),
Arrays.asList(node), null);
fcn.setInitialType(newComplexType);
return fcn;
}
private ExpressionNode complexToReal(ExpressionNode node, Type realType) {
Source source = node.getSource();
node.remove();
ExpressionNode result = nodeFactory.newDotNode(source, node, nodeFactory.newIdentifierNode(source, "real"));
result.setInitialType(realType);
return result;
}
private ExpressionNode convert(ExpressionNode node, Type oldType, Type newType) {
if (isComplex(oldType)) {
if (isBool(newType))
return complexToBool(node, oldType);
else if (isReal(newType))
return complexToReal(node, newType);
else if (isComplex(newType))
return complexToComplex(node, oldType, newType);
else
throw new CIVLInternalException("No conversion from " + oldType + " to " + newType, node.getSource());
} else if (isComplex(newType)) { // non-complex -> complex
return realToComplex(node, newType);
}
// conversion does not involve complex type: ignore
return node;
}
private ExpressionNode convertLiteral(FloatingConstantNode fcn) {
assert fcn.isComplex();
Source source = fcn.getSource();
ComplexFloatingValue value = (ComplexFloatingValue) fcn.getConstantValue();
RealFloatingValue realPart = value.getRealPart(), imagPart = value.getImaginaryPart();
assert realPart.isZero() == Answer.YES;
FloatingConstantNode imagNode = nodeFactory.newFloatingConstantNode(source, fcn.getStringRepresentation(),
fcn.wholePart(), fcn.fractionPart(), fcn.exponent(), imagPart);
String zeroString, typeString;
FloatKind fkind = realPart.getType().getFloatKind();
if (fkind == FloatKind.DOUBLE) {
zeroString = "0.0";
typeString = "$double_complex";
} else if (fkind == FloatKind.FLOAT) {
zeroString = "0.0f";
typeString = "$float_complex";
} else if (fkind == FloatKind.LONG_DOUBLE) {
zeroString = "0.0l";
typeString = "$ldouble_complex";
} else {
throw new RuntimeException("unreachable");
}
FloatingConstantNode zeroNode;
try {
zeroNode = nodeFactory.newFloatingConstantNode(source, zeroString);
} catch (SyntaxException e) {
throw new CIVLInternalException("Syntax error parsing zero constant: " + zeroString, source);
}
PairNode<DesignationNode, InitializerNode> realPair = nodeFactory.newPairNode(source, null, zeroNode),
imagPair = nodeFactory.newPairNode(source, null, imagNode);
IdentifierNode idNode = nodeFactory.newIdentifierNode(source, typeString);
TypedefNameNode tdnn = nodeFactory.newTypedefNameNode(idNode, null);
CompoundInitializerNode cin = nodeFactory.newCompoundInitializerNode(source, Arrays.asList(realPair, imagPair));
CompoundLiteralNode cln = nodeFactory.newCompoundLiteralNode(source, tdnn, cin);
cln.setInitialType(fcn.getInitialType());
return cln;
}
/**
* Given an operator node for one of the operators satisfying method
* {@link #isArithmeticOp(Operator)}, produces the replacement node using the
* appropriate function call. For pure operators, the replacement node will be a
* function call node. For operators that combine an assignment with the
* operation (e.g., "+="), the replacement node will be an assignment node in
* which the second argument is the function call node.
*
* @param opNode an operator node for one of the operators satisfying
* {@link #isArithmeticOp(Operator)}
* @return the replacement node
*/
private ExpressionNode arithmeticReplacement(OperatorNode opNode) {
Operator op = opNode.getOperator();
ExpressionNode arg0 = opNode.getArgument(0);
Source source = opNode.getSource();
String funName;
switch (op) {
case PLUS:
funName = "$cadd";
break;
case PLUSEQ:
funName = "$caddeq";
break;
case MINUS:
funName = "$csub";
break;
case MINUSEQ:
funName = "$csubeq";
break;
case TIMES:
funName = "$cmul";
break;
case TIMESEQ:
funName = "$cmuleq";
break;
case DIV:
funName = "$cdiv";
break;
case DIVEQ:
funName = "$cdiveq";
break;
case EQUALS:
funName = "$ceq";
break;
case NEQ:
funName = "$cneq";
break;
case UNARYMINUS:
funName = "$cneg";
break;
default:
throw new RuntimeException("unreachable");
}
BasicTypeKind kind = kind(arg0.getConvertedType());
if (kind == FLOAT_COMPLEX)
funName += "f";
else if (kind == LONG_DOUBLE_COMPLEX)
funName += "l";
IdentifierNode funNameNode = nodeFactory.newIdentifierNode(source, funName);
IdentifierExpressionNode funExprNode = nodeFactory.newIdentifierExpressionNode(source, funNameNode);
// Pattern: a+b ==> fun(a,b). a+=b ==> fun(&a,b).
// Note: have to use a pointer &a. Alternatives would require creating two
// copies of a, which would be wrong if evaluation of a has side-effects, e.g.,
// if a is array[++i].
List<ExpressionNode> argList = new LinkedList<>();
int numArgs = opNode.getNumberOfArguments();
for (int i = 0; i < numArgs; i++) {
ExpressionNode arg = opNode.getArgument(i);
arg.remove();
argList.add(arg);
}
if (isAssignOp(op)) {
assert numArgs == 2;
arg0 = argList.get(0);
argList.set(0, nodeFactory.newOperatorNode(arg0.getSource(), Operator.ADDRESSOF, arg0));
}
ExpressionNode result = nodeFactory.newFunctionCallNode(source, funExprNode, argList, null);
result.setInitialType(opNode.getInitialType());
return result;
}
/**
* Replaces C complex primitives with CIVL-C structure primitives in complex.cvh
* and complex.cvl.
*
* typeNode: just replace type node
*
* constantNode "3if" followed by conversion to double complex: first
* convertLiteral then apply conversions.
*
* constantNode "1" converted to a complex type: apply conversions
*
* operator node "a+b" followed by conversions: first arithmetic replacement,
* then apply conversions
*
* cast node to or from complex: convert cast, then apply additional implicit
* conversions
*
* Expression node: first translate to new node, then apply conversions.
*
* @param node the root of the tree in which replacement will occur
* @return {@code true} iff any change was made to the tree
*/
private boolean process(ASTNode node) {
boolean change = false;
int numChildren = node.numChildren();
for (int i = 0; i < numChildren; i++) {
ASTNode child = node.child(i);
if (child != null && process(child))
change = true;
}
ASTNode parent = node.parent();
int idx = node.childIndex();
if (node instanceof TypeNode) {
// for reasons I don't understand, a TypedefNameNode may
// contain qualifiers but those are not present in its Type.
Type type = ((TypeNode) node).getType();
if (type != null && isComplex(type)) {
node = replacementTypeNode((TypeNode) node);
assert node != null;
parent.setChild(idx, node);
change = true;
}
} else if (node instanceof ExpressionNode) {
// first, save the conversions:
int numConversions = ((ExpressionNode) node).getNumConversions();
Conversion[] conversions = new Conversion[numConversions];
for (int i = 0; i < numConversions; i++)
conversions[i] = ((ExpressionNode) node).getConversion(i);
if (node instanceof OperatorNode) {
OperatorNode opNode = (OperatorNode) node;
if (isArithmeticOp(opNode.getOperator()) && isComplex(opNode.getArgument(0).getConvertedType())) {
node = arithmeticReplacement(opNode);
assert node != null;
parent.setChild(idx, node);
change = true;
}
} else if (node instanceof FloatingConstantNode) {
FloatingConstantNode fcn = (FloatingConstantNode) node;
if (fcn.isComplex()) {
node = convertLiteral(fcn);
assert node != null;
parent.setChild(idx, node);
change = true;
}
} else if (node instanceof CastNode) {
ExpressionNode arg = ((CastNode) node).getArgument();
Type oldType = arg.getConvertedType();
Type newType = ((CastNode) node).getInitialType();
ExpressionNode tmp = convert(arg, oldType, newType);
if (tmp != arg) {
node = tmp;
parent.setChild(idx, node);
change = true;
}
}
// now, apply the conversions:
for (int i = 0; i < numConversions; i++) {
Conversion cv = conversions[i];
ExpressionNode tmp = convert((ExpressionNode) node, cv.getOldType(), cv.getNewType());
if (tmp != node) {
node = tmp;
parent.setChild(idx, node);
change = true;
}
}
} else if (node instanceof IfNode || node instanceof LoopNode) {
ExpressionNode cond = node instanceof IfNode ? ((IfNode) node).getCondition()
: ((LoopNode) node).getCondition();
int condIdx = cond.childIndex();
Type type = cond.getType();
if (isComplex(type)) {
cond = complexToBool(cond, type);
node.setChild(condIdx, cond);
change = true;
}
}
return change;
}
@Override
protected AST transformCore(AST ast) throws SyntaxException {
SequenceNode<BlockItemNode> root = ast.getRootNode();
boolean needsTransform = false;
for (ASTNode node = root; !needsTransform && node != null; node = node.nextDFS()) {
if (node instanceof ExpressionNode) {
ExpressionNode expr = (ExpressionNode) node;
if (isComplex(expr.getInitialType())) {
needsTransform = true;
break;
}
if (!needsTransform) {
int numConversions = expr.getNumConversions();
for (int i = 0; !needsTransform && i < numConversions; i++) {
if (isComplex(expr.getConversion(i).getNewType())) {
needsTransform = true;
break;
}
}
}
} else if (node instanceof TypeNode) {
if (isComplex(((TypeNode) node).getType())) {
needsTransform = true;
break;
}
}
}
if (!needsTransform)
return ast;
boolean isWhole = ast.isWholeProgram();
Collection<SourceFile> sourceFiles = ast.getSourceFiles();
boolean hasComplexCvl = false;
// remove all items from complex.h...
ast.release();
int nchildren = root.numChildren();
for (int i = 0; i < nchildren; i++) {
BlockItemNode node = root.getSequenceChild(i);
Source source = node.getSource();
String sourceName = source.getFirstToken().getSourceFile().getName();
if (COMPLEX_H.equals(sourceName)) {
root.removeChild(i);
} else if (COMPLEX_CVL.equals(sourceName)) {
hasComplexCvl = true;
} else if (MATH_H.equals(sourceName)) {
mathHIndex = i;
}
}
// TODO: this only sets the child to null. get rid of the null gaps?
process(root);
if (!hasComplexCvl) {
// remove old math.h if present:
if (mathHIndex >= 0) {
nchildren = root.numChildren();
for (int i = 0; i < nchildren; i++) {
ASTNode node = root.child(i);
if (node != null && MATH_H.equals(node.getSource().getFirstToken().getSourceFile().getName()))
root.removeChild(i);
}
}
// insert complex.cvl (which includes math.h and complex.cvh) at beginning:
File file = new File(CIVLConstants.CIVL_LIB_SRC_PATH, COMPLEX_CVL);
AST lib = this.parseSystemLibrary(file, EMPTY_MACRO_MAP);
SequenceNode<BlockItemNode> libRoot = lib.getRootNode();
lib.release();
List<BlockItemNode> libNodes = new LinkedList<BlockItemNode>();
for (BlockItemNode node : libRoot) {
node.remove();
libNodes.add(node);
}
root.insertChildren(0, libNodes);
}
ast = astFactory.newAST(root, sourceFiles, isWhole);
return ast;
}
}