package printer;
import java.io.BufferedWriter;
import java.util.Iterator;
import speclistener.SpecBaseListener;
import symboltable.ASTNode;
import symboltable.Constructor;
import symboltable.ContainsProperties;
import symboltable.Entity;
import symboltable.EntityProperty;
import symboltable.EnumProperty;
import symboltable.Enumeration;
import symboltable.ExistentialProperty;
import symboltable.ExprForASTElement;
import symboltable.IntProperty;
import symboltable.NodeProperty;
import symboltable.Property;
import symboltable.StringProperty;
import symboltable.SymbolTable;
import static util.PrintUtil.writeToFile;
import static util.PrintUtil.quote;
import visitor.forContainsProperties.ContainsPropertiesVisitorRv;
//TODO: handle uniformly identetion calls and code gen calls
/**
*
* @author kostasferles
*/
public class PrintClangCsvPlugin extends SpecBaseListener{
private BufferedWriter output;
private int ident = 0;
private String identStr = "";
private SymbolTable table = SymbolTable.getInstance();
/*
* REVIEW: Is there a way to parameterize these strings?
*/
private final String targetConsName = "getConstructorProperties";
private final String consListArg = "vector<string>&";
private final String consListArgName = "l";
private final String aggrListName = "aggr";
private final String relationListName = "rel";
private final String writerName = "writer";
private final String callToWriteLine = "writeLine";
private final String csvWriteMethoForEntities = "writeCsvFor";
/*
* TODO: If any of these methods are more useful for other classes, move them to PrintUtility
*/
private String identMethodDecl(String s){
return this.identStr + s;
}
private void insertIndentation(){
for(int i = 0 ; i < this.ident ; i++)
writeToFile(this.output, " ");
}
private void increaseIdent(){
this.ident++;
this.identStr += " ";
}
private void decreaseIdent(){
this.ident--;
if(this.ident > 0)
this.identStr = this.identStr.substring(0, this.identStr.length() - 2);
}
private StringBuilder identMethodDeclAndField(StringBuilder in){
return in.append(identStr);
}
private StringBuilder identMethodBody(StringBuilder in){
return in.append(identStr).append(" ");
}
private String identMethodBody(String s){
return this.identStr + " " + s;
}
private String callToString(ExprForASTElement elem){
return "toString(" + elem.getCodeForElement() + ")";
}
private String callToString(String elem){
return "toString(" + elem + ")";
}
private StringBuilder getParameters(StringBuilder in, String... params){
in.append("(");
if(params.length > 0){
in.append(params[0]);
for(int i = 1 ; i < params.length ; ++i){
in = in.append(", ").append(params[i]);
}
}
in.append(")");
return in;
}
private StringBuilder getMethodDeclHead(StringBuilder in, String rv, String methodName, String... params){
in = in.append(identMethodDecl(rv)).append(" ").append(methodName);
in = getParameters(in, params);
in.append("{\n");
return in;
}
private StringBuilder getMethodCall(StringBuilder in, String receiver, String operator, String methodName, String... params){
in = in.append(receiver).append(operator).append(methodName);
in = getParameters(in, params);
in.append(";\n");
return in;
}
private StringBuilder getMethodFooter(StringBuilder in){
return in.append(identMethodDecl("}\n"));
}
private class CallWriteCsvForEntity implements ContainsPropertiesVisitorRv<Boolean>{
@Override
public Boolean visit(ASTNode n) {
return false;
}
@Override
public Boolean visit(Entity en) {
return true;
}
}
private CallWriteCsvForEntity condVis = new CallWriteCsvForEntity();
private String generateCodeForProperty(Property p, boolean callWriteCsv4Entity){
StringBuilder rv = new StringBuilder();
Property.Type t = p.getType();
ContainsProperties elem = p.getParent();
Constructor cons = elem.getConstructor();
boolean consProperty = !p.isOptional() && cons.getParent() == elem && cons.containsProperty(p);
String nodeName = elem.getName();
String propertyName = p.getName();
String lbPropertyName = nodeName + ':' + propertyName;
switch(t){
case OneToOne:
case ManyToOne:
ExprForASTElement expr = p.getExprForValue();
if(callWriteCsv4Entity && !p.isOptional()){
identMethodBody(rv);
rv = getMethodCall(rv, "this", "->", csvWriteMethoForEntities, expr.getCodeForElement());
}
if(!consProperty){
if(!p.isOptional()){
rv = identMethodBody(rv);
rv = getMethodCall(rv, aggrListName, ".", "push_back", callToString(expr));
}
else{
identMethodBody(rv).
append("if(").append(p.getCond()).append("){\n");
//TODO: make a method for this group of calls
getMethodCall(identMethodBody(rv).append(" "), relationListName, ".", "push_back", callToString(expr));
getMethodCall(identMethodBody(rv).append(" "), writerName, "->", callToWriteLine, quote(lbPropertyName), "&"+relationListName);
getMethodCall(identMethodBody(rv).append(" "), relationListName, ".", "pop_back");
if(callWriteCsv4Entity){
getMethodCall(identMethodBody(rv).append(" "), "this", "->", csvWriteMethoForEntities, expr.getCodeForElement());
}
identMethodBody(rv).append("}\n");
}
}
break;
case OneToMany:
case ManyToMany:
assert !consProperty;
boolean isOptional = p.isOptional();
if(isOptional){
identMethodBody(rv).append("if(").
append(p.getCond()).
append("){\n");
}
if(p.hasIterators()){
String valName = p.getValueName();
String iterHead = nodeName + '_' + propertyName + '_' + valName;
String iteratorStart = iterHead + "_start";
String iteratorEnd = iterHead + "_end";
String index = iterHead + "_index";
//TODO: if target language type is missing throw exception
String targetLangType = p.getTargetLangType();
if(p.isOrdered()){
identMethodBody(rv.append(isOptional ? " " : "")).
append("int ").append(index).append(" = 0;\n");
}
rv = identMethodBody(rv.append(isOptional ? " " : "")).
append(targetLangType).
append(" ").
append(iteratorEnd).
append(" = ").
append(p.getExprEnd().getCodeForElement()).
append(";\n");
rv = identMethodBody(rv.append(isOptional ? " " : "")).
append("for(").
append(targetLangType).
append(" ").
append(iteratorStart).
append(" = ").
append(p.getExprStart().getCodeForElement()).
append(" ;\n");
rv = identMethodBody(rv.append(isOptional ? " " : "")).
append(" ").
append(iteratorStart).
append(" != ").
append(iteratorEnd).
append(" ;\n");
rv = identMethodBody(rv.append(isOptional ? " " : "")).
append(" ").
append(iteratorStart).
append("++) {\n");
String commandForIt = (p.needsDeref() ? "*" : "") + iteratorStart;
if(callWriteCsv4Entity){
identMethodBody(rv.append(isOptional ? " " : "")).append(" ");
getMethodCall(rv, "this", "->", csvWriteMethoForEntities, commandForIt);
}
rv = identMethodBody(rv.append(isOptional ? " " : "")).append(" ");
rv = getMethodCall(rv, relationListName, ".", "push_back", callToString(commandForIt));
if(p.isOrdered()){
identMethodBody(rv.append(isOptional ? " " : "")).append(" ");
getMethodCall(rv, relationListName, ".", "push_back", callToString(index));
identMethodBody(rv.append(isOptional ? " " : "")).append(" ");
rv.append(index).append("++;\n");
}
rv = identMethodBody(rv.append(isOptional ? " " : "")).append(" ");
rv = getMethodCall(rv, writerName, "->", callToWriteLine, quote(lbPropertyName), "&"+relationListName);
rv = identMethodBody(rv.append(isOptional ? " " : "")).append(" ");
rv = getMethodCall(rv, relationListName, ".", "pop_back");
if(p.isOrdered()){
rv = identMethodBody(rv.append(isOptional ? " " : "")).append(" ");
rv = getMethodCall(rv, relationListName, ".", "pop_back");
}
rv = identMethodBody(rv.append(isOptional ? " " : "")).append("}\n");
}
else{
String commandForVal = p.getExprForValue().getCodeForElement();
if(callWriteCsv4Entity){
identMethodBody(rv.append(isOptional ? " " : "")).append(" ");
getMethodCall(rv, "this", "->", csvWriteMethoForEntities, commandForVal);
}
getMethodCall(identMethodBody(rv.append(isOptional ? " " : "")), relationListName, ".", "push_back", callToString(commandForVal));
getMethodCall(identMethodBody(rv.append(isOptional ? " " : "")), writerName, "->", callToWriteLine, quote(lbPropertyName), "&" + relationListName);
getMethodCall(identMethodBody(rv.append(isOptional ? " " : "")), relationListName, ".", "pop_back");
}
if(isOptional)
identMethodBody(rv).append("}");
rv.append("\n");
break;
case Existential:
assert !consProperty;
identMethodBody(rv).append("if(").append(p.getExprForValue().getCodeForElement()).append(")\n");
identMethodBody(rv).append(" ");
getMethodCall(rv, writerName, "->", callToWriteLine, quote(lbPropertyName), "&" + relationListName);
rv.append("\n");
break;
default:
assert false;
}
return rv.toString();
}
private void generateCodeForConstructor(Constructor cons){
StringBuilder conStr = new StringBuilder();
ContainsProperties conParentNode = cons.getParent();
String consThisName = conParentNode.getThisName();
String consTargetType = conParentNode.getTargetLangType();
conStr = getMethodDeclHead(conStr, "void", targetConsName, consTargetType + " " + consThisName, consListArg + consListArgName);
Iterator<Property> it = cons.getProperties();
while(it.hasNext()){
Property pr = it.next();
ExprForASTElement expr = pr.getExprForValue();
conStr = conStr.append(identMethodBody(consListArgName)).
append(".push_back(").
append(callToString(expr)).
append(");\n");
}
conStr = getMethodFooter(conStr);
conStr.append("\n");
conStr = getMethodDeclHead(conStr, "string", "toString", consTargetType + " " + consThisName);
identMethodBody(conStr).append("const string delim = ").append(writerName).append("->getDelim();\n");
identMethodBody(conStr).append("ostringstream rv;\n");
it = cons.getProperties();
Property pr = it.next();
assert pr.getExprForValue() != null;
conStr = identMethodBody(conStr).append("rv << ").append(callToString(pr.getExprForValue()));
while(it.hasNext()){
assert pr.getExprForValue() != null;
pr = it.next();
ExprForASTElement expr = pr.getExprForValue();
conStr = identMethodBody(conStr.append("\n")).append(" ");
conStr = conStr.append(" << delim <<").append(callToString(expr));
}
conStr.append(";\n");
identMethodBody(conStr).append("return rv.str();\n");
conStr = getMethodFooter(conStr).append("\n");
writeToFile(this.output, conStr.toString());
}
public PrintClangCsvPlugin(BufferedWriter output){
this.output = output;
}
@Override
public void enter(){
String header = this.table.getHeader();
String members = this.table.getMembers();
if(header != null){
writeToFile(this.output, header);
writeToFile(this.output, "\n");
}
String clangSpecName = this.table.getClangSpecName();
writeToFile(this.output, "namespace {\n");
this.increaseIdent();
this.insertIndentation();
writeToFile(this.output, "class ");
writeToFile(this.output, clangSpecName);
writeToFile(this.output, " : public RecursiveASTVisitor<");
writeToFile(this.output, clangSpecName);
writeToFile(this.output, "> {\n");
this.insertIndentation();
writeToFile(this.output, "public:\n");
this.increaseIdent();
if(members != null){
this.insertIndentation();
members = members.replaceAll("\n", "\n" + identStr);
writeToFile(this.output, members);
writeToFile(this.output, "\n");
}
}
@Override
public void enterEnumNode(Enumeration e){
StringBuilder enumStr = new StringBuilder();
String paramName = "enumeration";
String prefix = e.getPrefix();
String enumName = e.getName();
String enumType = prefix != null ? prefix + "::" + enumName : enumName;
enumStr = getMethodDeclHead(enumStr, "string", "toString", enumType + " " + paramName);
enumStr = identMethodBody(enumStr).
append("switch(").append(paramName).append("){\n");
Iterator<String> enumerators = e.getEnumerators();
int i = 0;
while(enumerators.hasNext()){
String enumerator = enumerators.next();
enumerator = prefix != null ? prefix + "::" + enumerator : enumerator ;
enumStr = identMethodBody(enumStr).
append("case ").append(enumerator).append(":\n");
enumStr = identMethodBody(enumStr).
append(" return ").append(quote(Integer.toString(i++))).append(";\n");
}
enumStr = identMethodBody(enumStr).append("}\n");
enumStr = identMethodBody(enumStr).append("return \"\";\n");
enumStr = identMethodDeclAndField(enumStr).append("}\n");
writeToFile(this.output, enumStr.toString());
writeToFile(this.output, "\n");
}
//TODO: error message if there is no targetLangType for both ASTNodes and Entities
//TODO: group the code generation code for caching nodes and entities
@Override
public void enterAstNode(ASTNode n){
Constructor cons = n.getConstructor();
if(cons.getParent() == n){
generateCodeForConstructor(cons);
}
StringBuilder nodeHeadStr = new StringBuilder();
String nodeName = n.getName();
String paramName = n.getThisName();
String setNodeName = "alreadyVisited" + nodeName;
if(n.needsCache())
identMethodDeclAndField(nodeHeadStr).append("boost::unordered_set<string> ").append(setNodeName).append(";\n\n");
nodeHeadStr = getMethodDeclHead(nodeHeadStr, "bool", "Visit" + nodeName, n.getTargetLangType() + " " + paramName);
if(n.needsCache()){
identMethodBody(nodeHeadStr).append("string consS = ").
append(callToString(paramName)).
append(";\n");
identMethodBody(nodeHeadStr).append("if(").
append(setNodeName).
append(".find(consS) != ").
append(setNodeName).
append(".end())\n");
identMethodBody(nodeHeadStr).append(" return true;\n\n");
getMethodCall(identMethodBody(nodeHeadStr), setNodeName, ".", "insert", "consS");
}
nodeHeadStr = identMethodBody(nodeHeadStr);
nodeHeadStr = nodeHeadStr.append("vector<string> ").
append(aggrListName).
append(";\n");
nodeHeadStr = identMethodBody(nodeHeadStr);
nodeHeadStr = getMethodCall(nodeHeadStr, "this", "->", targetConsName, paramName, aggrListName);
nodeHeadStr = identMethodBody(nodeHeadStr).
append("vector<string> ").
append(relationListName).
append(" = ").
append(aggrListName).
append(";\n");
writeToFile(this.output, nodeHeadStr.toString());
}
@Override
public void enterEntity(Entity e){
Constructor cons = e.getConstructor();
if(cons.getParent() == e){
generateCodeForConstructor(cons);
}
StringBuilder entityHeadStr = new StringBuilder();
String entityName = e.getName();
String paramName = e.getThisName();
String targetLangType = e.getTargetLangType();
String setFieldName = "alreadyVisited" + entityName;
identMethodDeclAndField(entityHeadStr).append("boost::unordered_set<string> ").append(setFieldName).append(";\n\n");
entityHeadStr = getMethodDeclHead(entityHeadStr, "void", csvWriteMethoForEntities, targetLangType + " " + paramName);
identMethodBody(entityHeadStr).append("string consS = ").
append(callToString(paramName)).
append(";\n");
identMethodBody(entityHeadStr).append("if(").
append(setFieldName).
append(".find(consS) != ").
append(setFieldName).
append(".end())\n");
identMethodBody(entityHeadStr).append(" return ;\n\n");
identMethodBody(entityHeadStr).append("vector<string> ").
append(aggrListName).
append(";\n");
identMethodBody(entityHeadStr);
entityHeadStr = getMethodCall(entityHeadStr, "this", "->", targetConsName, paramName, aggrListName);
identMethodBody(entityHeadStr).append("vector<string> ").
append(relationListName).
append(" = ").
append(aggrListName).
append(";\n");
getMethodCall(identMethodBody(entityHeadStr), setFieldName, ".", "insert", "consS");
getMethodCall(identMethodBody(entityHeadStr), "this", "->", csvWriteMethoForEntities, e.getThisName());
writeToFile(output, entityHeadStr.toString());
}
@Override
public void enterStringProperty(StringProperty sp){
String propStr = generateCodeForProperty(sp, false);
writeToFile(output, propStr);
}
@Override
public void enterIntProperty(IntProperty ip){
String propStr = generateCodeForProperty(ip, false);
writeToFile(output, propStr);
}
@Override
public void enterEnumProperty(EnumProperty ep){
String propStr = generateCodeForProperty(ep, false);
writeToFile(output, propStr);
}
@Override
public void enterNodeProperty(NodeProperty np){
String propStr = generateCodeForProperty(np, false);
writeToFile(output, propStr);
}
@Override
public void enterEntityProperty(EntityProperty ep){
String propStr = generateCodeForProperty(ep, true);
writeToFile(output, propStr);
}
@Override
public void enterExistentialProperty(ExistentialProperty ep){
String propStr = generateCodeForProperty(ep, ep.getParent().accept(condVis));
writeToFile(output, propStr);
}
@Override
public void exitAstNode(ASTNode n){
StringBuilder nodeFooterStr = new StringBuilder();
nodeFooterStr = identMethodBody(nodeFooterStr);
nodeFooterStr = getMethodCall(nodeFooterStr, writerName, "->", callToWriteLine, quote(n.getName()), "&"+aggrListName);
nodeFooterStr = identMethodBody(nodeFooterStr).append("return true;\n");
nodeFooterStr = getMethodFooter(nodeFooterStr);
writeToFile(this.output, nodeFooterStr.toString());
writeToFile(this.output, "\n");
}
@Override
public void exitEntity(Entity n){
StringBuilder entityFooterStr = new StringBuilder();
identMethodBody(entityFooterStr);
getMethodCall(entityFooterStr, writerName, "->", callToWriteLine, quote(n.getName()), "&"+aggrListName);
getMethodFooter(entityFooterStr);
writeToFile(output, entityFooterStr.toString());
writeToFile(output, "\n");
}
@Override
public void exitAstNodes(){
this.decreaseIdent();
}
@Override
public void exit(){
this.insertIndentation();
writeToFile(this.output, "}; //class\n");
this.decreaseIdent();
writeToFile(this.output, "} //namespace\n");
assert this.ident == 0;
}
}