/*
* Copyright 2010 Pablo Arrighi, Alex Concha, Miguel Lezama for version 1.
* Copyright 2013 Pablo Arrighi, Miguel Lezama, Kevin Mazet for version 2.
*
* This file is part of GOOL.
*
* GOOL is free software: you can redistribute it and/or modify it under the terms of the GNU
* General Public License as published by the Free Software Foundation, version 3.
*
* GOOL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License version 3 for more details.
*
* You should have received a copy of the GNU General Public License along with GOOL,
* in the file COPYING.txt. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Visits the abstract GOOL tree to generate concrete C++
*/
package gool.generator.cpp;
import gool.ast.core.BinaryOperation;
import gool.ast.core.CastExpression;
import gool.ast.core.Catch;
import gool.ast.core.ClassDef;
import gool.ast.core.ClassNew;
import gool.ast.core.Constant;
import gool.ast.core.CustomDependency;
import gool.ast.core.Dependency;
import gool.ast.core.EnhancedForLoop;
import gool.ast.core.EqualsCall;
import gool.ast.core.Expression;
import gool.ast.core.Field;
import gool.ast.core.Finally;
import gool.ast.core.MainMeth;
import gool.ast.core.MemberSelect;
import gool.ast.core.Meth;
import gool.ast.core.MethCall;
import gool.ast.core.Modifier;
import gool.ast.core.Operator;
import gool.ast.core.ParentCall;
import gool.ast.core.RecognizedDependency;
import gool.ast.core.ThisCall;
import gool.ast.core.Throw;
import gool.ast.core.ToStringCall;
import gool.ast.core.Try;
import gool.ast.core.TypeDependency;
import gool.ast.core.VarDeclaration;
import gool.ast.list.ListAddCall;
import gool.ast.list.ListContainsCall;
import gool.ast.list.ListGetCall;
import gool.ast.list.ListGetIteratorCall;
import gool.ast.list.ListIsEmptyCall;
import gool.ast.list.ListRemoveAtCall;
import gool.ast.list.ListRemoveCall;
import gool.ast.list.ListSizeCall;
import gool.ast.map.MapContainsKeyCall;
import gool.ast.map.MapEntryGetKeyCall;
import gool.ast.map.MapEntryGetValueCall;
import gool.ast.map.MapGetCall;
import gool.ast.map.MapGetIteratorCall;
import gool.ast.map.MapIsEmptyCall;
import gool.ast.map.MapPutCall;
import gool.ast.map.MapRemoveCall;
import gool.ast.map.MapSizeCall;
import gool.ast.system.SystemCommandDependency;
import gool.ast.system.SystemOutDependency;
import gool.ast.system.SystemOutPrintCall;
import gool.ast.type.IType;
import gool.ast.type.TypeBool;
import gool.ast.type.TypeChar;
import gool.ast.type.TypeClass;
import gool.ast.type.TypeDecimal;
import gool.ast.type.TypeEntry;
import gool.ast.type.TypeException;
import gool.ast.type.TypeInt;
import gool.ast.type.TypeList;
import gool.ast.type.TypeMap;
import gool.ast.type.TypeGoolLibraryClass;
import gool.ast.type.TypeNull;
import gool.ast.type.TypeObject;
import gool.ast.type.TypeString;
import gool.generator.GeneratorHelper;
import gool.generator.common.CodeGeneratorNoVelocity;
import gool.generator.common.CommonCodeGenerator;
import gool.generator.common.GeneratorMatcher;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
public class CppGenerator extends CommonCodeGenerator /*implements
CodeGeneratorNoVelocity*/ {
private String removePointer(IType type) {
return removePointer(type.toString());
}
private String removePointer(String type) {
return type.replaceAll("[\\s*]+$", "");
}
@Override
public String getCode(Modifier modifier) {
if (modifier == Modifier.FINAL) {
return "const";
}
return super.getCode(modifier);
}
@Override
public String getCode(Field field) {
Modifier m = field.getAccessModifier();
List<Modifier> modifiers = new ArrayList<Modifier>(field.getModifiers());
modifiers.remove(m);
String out = String.format("%s: %s %s %s", m, getCode(modifiers),
field.getType(), field.getName());
if (field.getDefaultValue() != null) {
// out = String.format("%s = %s", out, field.getDefaultValue());
// C++ does not seem to allow instance fields initialization.
if (!field.getModifiers().contains(Modifier.FINAL)
&& !field.getModifiers().contains(Modifier.STATIC)) {
throw new IllegalArgumentException(
String.format(
"The field '%s' should be initialized within one of the class constructors.",
field.getName()));
}
}
return out;
}
@Override
public String getCode(MethCall methodCall) {
String out = "";
if (methodCall.getTarget() != null) {
out = methodCall.getTarget().toString();
String goolMethod = methodCall.getGoolLibraryMethod();
if(goolMethod!=null){
//here, get matched output method name with the GeneratorMatcher
String methodName=GeneratorMatcher.matchGoolMethod(goolMethod);
if(methodName!=null)
out=out.substring(0, out.lastIndexOf("->")+2)+methodName;
}
}
// if (methodCall.getType() != null) {
// out += "< " + methodCall.getType() + " >";
// }
out += "(";
if (methodCall.getParameters() != null) {
out += StringUtils.join(methodCall.getParameters(), ", ");
}
out += ")";
return out;
}
@Override
public String getCode(TypeBool typeBool) {
return "int";
}
@Override
public String getCode(TypeClass typeClass) {
String pointer = typeClass.isEnum() ? "" : "*";
return super.getCode(typeClass).replaceAll("\\.", "::") + pointer;
}
@Override
public String getCode(TypeInt typeInt) {
return "int";
}
@Override
public String getCode(TypeString typeString) {
return "std::string*";
}
@Override
public String getCode(TypeObject typeObject) {
return "boost::any";
}
@Override
public String getCode(CastExpression cast) {
if (cast.getType().equals(cast.getExpression().getType())) {
return String.format("%s", cast.getExpression());
} else if (cast.getExpression().getType() == TypeObject.INSTANCE) {
return String.format("any_cast< %s >( %s )", cast.getType(),
cast.getExpression());
} else {
return String.format("( ( %s )( %s ) )", cast.getType(),
cast.getExpression());
}
}
@Override
public String getCode(Constant constant) {
if (constant.getType().equals(TypeString.INSTANCE)) {
return String.format("( new std::string ( %s ) )",
super.getCode(constant));
} else if (constant.getType().equals(TypeNull.INSTANCE)) {
return "NULL";
} else if (constant.getType().equals(TypeBool.INSTANCE)) {
return String.valueOf(constant.getValue().toString()
.equalsIgnoreCase("true") ? 1 : 0);
} else {
return super.getCode(constant);
}
}
@Override
public String getCode(TypeList typeList) {
if (typeList.getElementType() == null) {
/*
* TODO: Avoid elements of different types within the same list.
* Currently, the remove method is broken due to the cast made of
* every element in the list (the both operands in the comparison
* should have the same type, otherwise the C++ compiler throws an
* error).
*/
return "std::vector<" + TypeObject.INSTANCE + ">*";
}
return "std::vector<" + typeList.getElementType().toString() + ">*";
}
@Override
public String getCode(MemberSelect memberSelect) {
IType targetType = memberSelect.getTarget().getType();
String memberAccess = memberSelect.getTarget().toString();
/*
* Verify if the target type is an enumeration, so we can remove the
* generation of the target.
*
* In C++, a "enum Foo {A, B}" should be accesed without using the
* prefix "Foo".
*
* Related to "TODO identifiers".
*/
if (targetType instanceof TypeClass
&& ((TypeClass) targetType).isEnum()) {
memberAccess = "";
} else if (memberSelect.getTarget() instanceof Constant) {
memberAccess += " :: "; // A static member access
} else {
memberAccess += " -> ";
}
return String
.format("%s%s", memberAccess, memberSelect.getIdentifier());
}
@Override
public String getCode(TypeDecimal typeReal) {
return "double";
}
@Override
public String getCode(TypeChar typeChar) {
// TODO Auto-generated method stub
return "char";
}
@Override
public String getCode(SystemOutPrintCall systemOutPrintCall) {
Expression toPrint = systemOutPrintCall.getParameters().get(0);
if (toPrint.getType().equals(TypeString.INSTANCE)) {
return String.format("std::cout << (%s)->data() << std::endl",
GeneratorHelper.joinParams(systemOutPrintCall
.getParameters()));
} else {
return String.format("std::cout << (%s) << std::endl",
GeneratorHelper.joinParams(systemOutPrintCall
.getParameters()));
}
}
@Override
public String getCode(ParentCall parentCall) {
return null;
}
public String getCode(SystemOutDependency systemOutDependency) {
return "iostream";
}
@Override
public String getCode(TypeDependency typeDependency) {
if (typeDependency.getType() instanceof TypeList) {
// Use the vector type because it allows random access over
// lists.
if (((TypeList) typeDependency.getType()).getElementType() == null) {
return "vector";
}
return "vector"; // TODO what type should be used when it is not a
// generic list?
}
if (typeDependency.getType() instanceof TypeMap) {
return "map";
}
if (typeDependency.getType() instanceof TypeEntry) {
return "map";
}
if (typeDependency.getType() instanceof TypeString) {
return "string";
}
if (typeDependency.getType() instanceof TypeBool) {
return "noprint";
}
if (typeDependency.getType() instanceof TypeInt) {
return "noprint";
}
return removePointer(super.getCode(typeDependency)).concat(".h");
}
@Override
public String getCode(MapSizeCall mapSizeCall) {
return String.format("%s -> size()", mapSizeCall.getExpression());
}
@Override
public String getCode(MapRemoveCall mapRemoveCall) {
return String.format("%s -> erase(%s)", mapRemoveCall.getExpression(),
GeneratorHelper.joinParams(mapRemoveCall.getParameters()));
}
@Override
public String getCode(MapPutCall mapPutCall) {
return String.format("%s -> insert( std::make_pair( %s, %s ) )",
mapPutCall.getExpression(), mapPutCall.getParameters().get(0),
mapPutCall.getParameters().get(1));
}
@Override
public String getCode(MapIsEmptyCall mapIsEmptyCall) {
return String.format("%s -> size() == 0",
mapIsEmptyCall.getExpression());
}
@Override
public String getCode(BinaryOperation binaryOp) {
String left = binaryOp.getLeft().toString();
String right = binaryOp.getRight().toString();
if (binaryOp.getOperator() == Operator.PLUS
&& binaryOp.getType().equals(TypeString.INSTANCE)) {
if (!binaryOp.getLeft().getType().equals(TypeString.INSTANCE)) {
left = String
.format("new std::string(boost::lexical_cast<std::string>(%s))",
left);
}
if (!binaryOp.getRight().getType().equals(TypeString.INSTANCE)) {
right = String
.format("new std::string(boost::lexical_cast<std::string>(%s))",
right);
}
left = String.format("(%s)", left);
right = String.format("(%s)", right);
return String.format("new std::string(%s -> append(* (%s)))", left,
right);
}
return super.getCode(binaryOp);
}
@Override
public String getCode(MapGetIteratorCall mapGetIteratorCall) {
// TODO Auto-generated method stub
return mapGetIteratorCall.getClass().toString();
}
@Override
public String getCode(MapGetCall mapGetCall) {
return String.format("%s -> find( %s ) -> second",
mapGetCall.getExpression(),
GeneratorHelper.joinParams(mapGetCall.getParameters()));
}
@Override
public String getCode(MapContainsKeyCall mapContainsKeyCall) {
String expr = mapContainsKeyCall.getExpression().toString();
return String.format("(%s) -> find(%s) != (%s) -> end()", expr,
GeneratorHelper.joinParams(mapContainsKeyCall.getParameters()),
expr);
}
@Override
public String getCode(TypeMap typeMap) {
/*
* Maps with with keys of type Object are not allowed in C++ because
* there is not a base type.
*/
if (typeMap.getKeyType() == null
|| typeMap.getKeyType().equals(TypeObject.INSTANCE)) {
throw new IllegalStateException(
"The map's key is of type Object, this is not supported in C++.");
}
if (typeMap.getElementType() == null) {
return "std::map<" + typeMap.getKeyType() + ", "
+ TypeObject.INSTANCE + ">*";
}
return "std::map<" + StringUtils.join(typeMap.getTypeArguments(), ", ")
+ ">*";
}
@Override
public String getCode(EnhancedForLoop enhancedForLoop) {
VarDeclaration varDec = enhancedForLoop.getVarDec();
String varName = varDec.getName();
Expression expression = enhancedForLoop.getExpression();
String expressionToString = enhancedForLoop.getExpression().toString();
return String
.format("for(%s::iterator %sIterator = %s->begin(); %sIterator != %s->end(); ++%sIterator){\n"
+ "%s %s *%sIterator;" + "%s" + "\n}",
removePointer(expression.getType()),
varName,
expressionToString,
varName,
expressionToString,
varName,
varDec.getType(),
(expression.getType() instanceof TypeMap) ? (String
.format("* %s = (%s*)&", varName,
varDec.getType())) : (String.format(
"%s = ", varName)), varName, enhancedForLoop
.getStatements());
}
private static Map<String, Dependency> customDependencies = new HashMap<String, Dependency>();
public String getCode(CustomDependency customDependency) {
if (!customDependencies.containsKey(customDependency.getName())) {
throw new IllegalArgumentException(
String.format(
"There is no equivalent type in C++ for the GOOL type '%s'.",
customDependency.getName()));
}
return customDependencies.get(customDependency.getName()).toString();
}
public void addCustomDependency(String key, Dependency value) {
customDependencies.put(key, value);
}
@Override
public String getCode(TypeEntry typeEntry) {
return String.format("std::pair<%s, %s>", typeEntry.getKeyType(),
typeEntry.getElementType());
}
@Override
public String getCode(MapEntryGetKeyCall mapEntryGetKeyCall) {
return String.format("%s->first", mapEntryGetKeyCall.getExpression());
}
@Override
public String getCode(MapEntryGetValueCall mapEntryGetValueCall) {
return String
.format("%s->second", mapEntryGetValueCall.getExpression());
}
@Override
public String getCode(ThisCall thisCall) {
// TODO Auto-generated method stub
return null;
}
@Override
public String getCode(EqualsCall equalsCall) {
return String.format("%s -> equals(%s)", equalsCall.getTarget(),
StringUtils.join(equalsCall.getParameters(), ", "));
}
@Override
public String getCode(ToStringCall tsc) {
return String.format("%s -> toString()", tsc.getTarget());
}
@Override
public String getCode(ListContainsCall lcc) {
return String
.format("/* ListContainsCall not implemented in C++ at the moment */");
}
@Override
public String getCode(ListGetCall lgc) {
return String.format("%s->%s(%s)", lgc.getExpression(), "at",
GeneratorHelper.joinParams(lgc.getParameters()));
}
@Override
public String getCode(ListGetIteratorCall lgic) {
// TODO Auto-generated method stub
return null;
}
@Override
public String getCode(ListIsEmptyCall liec) {
return String.format("%s -> empty()", liec.getExpression());
}
@Override
public String getCode(ListRemoveAtCall lrc) {
return String.format("%s -> erase(%s -> begin()+%s)",
lrc.getExpression(), lrc.getExpression(),
GeneratorHelper.joinParams(lrc.getParameters()));
}
@Override
public String getCode(ListRemoveCall lrc) {
/*
* for(std::vector<int>::size_type i = 0; i != v->size(); i++) { if (
* v->at(i) == 1 ) { v->erase(v->begin()+i); break; } }
*/
String expr = lrc.getExpression().toString();
return String
.format("for(std::vector<int>::size_type i = 0; i != %s->size(); i++) {"
+ "if ( boost::any_cast<%s>(%s->at(i)) == %s ) {%s->erase(%s->begin()+i);break;}}",
expr, lrc.getParameters().get(0).getType(), expr, lrc
.getParameters().get(0), expr, expr);
}
@Override
public String getCode(ListSizeCall lsc) {
return String.format("%s -> size()", lsc.getExpression());
}
@Override
public String getCode(ListAddCall lac) {
return String.format("%s->%s(%s)", lac.getExpression(), "push_back",
GeneratorHelper.joinParams(lac.getParameters()));
}
/**
* It is meant to be only used in the CPP file (it does not print the
* modifiers).
*/
@Override
public String getCode(Meth meth) {
List<Modifier> modifiers = new ArrayList<Modifier>(meth.getModifiers());
// Remove (public, private, protected, final) invalid modifiers.
modifiers.remove(meth.getAccessModifier());
modifiers.remove(Modifier.FINAL);
return String.format("%s %s %s::%s(%s)", getCode(modifiers),
meth.getType(), meth.getClassDef().getName(), meth.getName(),
StringUtils.join(meth.getParams(), ", "));
}
@Override
public String getCode(ClassNew classNew) {
return String.format("(new %s(%s))", removePointer(classNew.getType()),
StringUtils.join(classNew.getParameters(), ", "));
}
@Override
public String getCode(MainMeth mainMeth) {
return "int main()";
}
@Override
public String getCode(SystemCommandDependency systemCommandDependency) {
// TODO Auto-generated method stub
return null;
}
//@Override
public String printClass(ClassDef classDef) {
StringBuilder sb = new StringBuilder(String.format(
"// Platform: %s\n\n", classDef.getPlatform()));
// print the package containing the class
if (classDef.getPpackage() != null)
sb = sb.append(String.format("namespace %s {",
classDef.getPackageName()));
sb = sb.append("#include <boost/any.hpp>\n");
sb = sb.append("#include <boost/lexical_cast.hpp>\n");
sb = sb.append("#include \"finally.h\"\n\n");
sb = sb.append(String.format("#include \"%s.h\"\n\n",
classDef.getName()));
Set<String> dependencies = GeneratorHelper.printDependencies(classDef);
if (!dependencies.isEmpty()) {
for (String dependency : dependencies) {
if (!dependency.equals("noprint"))
sb = sb.append(String.format("#include \"%s\"\n",
dependency));
}
sb = sb.append("\n");
}
for (Meth meth : classDef.getMethods()) {
// TODO: deal with constructors ?
if (classDef.isInterface())
sb = sb.append(formatIndented("%-1%s;\n\n", meth.getHeader()));
else
sb = sb.append(formatIndented("%-1%s {%2%-1}\n\n",
meth.getHeader(), meth.getBlock()));
}
if (classDef.getPpackage() != null)
sb = sb.append("}");
return sb.toString();
}
@Override
public String getCode(Throw throwStatement) {
return String.format("throw %s", throwStatement.getExpression());
}
@Override
public String getCode(Catch catchStatement) {
return formatIndented("catch (%s * %s)\n{%1}\n", catchStatement
.getParameter().getType(), catchStatement.getParameter()
.getName(), catchStatement.getBlock());
}
@Override
public String getCode(Try tryStatement) {
String ret = "";
if (tryStatement.getFinilyBlock().getStatements().isEmpty()) {
ret = formatIndented("try\n{%1}", tryStatement.getBlock());
} else {
ret = formatIndented("try\n{\nfinally(%1) // finally%1}",
tryStatement.getFinilyBlock(), tryStatement.getBlock());
}
for (Catch c : tryStatement.getCatches()) {
ret += "\n" + c;
}
return ret;
}
@Override
public String getCode(TypeException typeException) {
switch (typeException.getKind()) {
case GLOBAL:
return "exception";
default:
return typeException.getName();
}
}
public String getCode(RecognizedDependency recognizedDependency) {
List<String> imports = GeneratorMatcher.matchImports(recognizedDependency.getName());
if(imports.isEmpty())
return "/* import "+recognizedDependency.getName()+" not generated by GOOL, passed on. */";
String result = "";
for (String Import : imports) {
if (Import.startsWith("+"))
Import = Import.substring(1);
result += "#include \"" + Import.replace(".", "/") + ".h\"\n";
}
return result;
}
@Override
public String getCode(TypeGoolLibraryClass typeMatchedGoolClass) {
String res = GeneratorMatcher.matchGoolClass(typeMatchedGoolClass
.getGoolclassname());
if (res == null)
return typeMatchedGoolClass.getGoolclassname()
+ " /* Ungenerated by GOOL, passed on. */";
else {
return res+"*";
}
}
}