/*##############################################################################
Copyright (C) 2011 HPCC Systems.
All rights reserved. This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
############################################################################## */
#include "jliball.hpp"
#include "hql.hpp"
#include "platform.h"
#include "jlib.hpp"
#include "jexcept.hpp"
#include "jmisc.hpp"
#include "hqlexpr.hpp"
#include "hqlattr.hpp"
#include "hqlstmt.hpp"
#include "hqlfunc.hpp"
#include "hqlcerrors.hpp"
#include "hqlcpp.ipp"
#include "hqlwcpp.hpp"
#include "hqlwcpp.ipp"
#define INDENT_SOURCE
#define FILE_CHUNK_SIZE 65000
#define PREFERRED_LINE_LIMIT 160
#define REASONABLE_LINE_LIMIT 512
static const char * vcIntTypes[] = { "char","short","int","int","__int64","__int64","__int64","__int64" };
static const char * vcUIntTypes[] = { "unsigned char","unsigned short","unsigned","unsigned","unsigned __int64","unsigned __int64","unsigned __int64","unsigned __int64" };
static const char * gccIntTypes[] = { "char","short","int","int","long long","long long","long long","long long" };
static const char * gccUIntTypes[] = { "unsigned char","unsigned short","unsigned","unsigned","unsigned long long","unsigned long long","unsigned long long","unsigned long long" };
// ITypeInfo and IValue implementations
inline const char * intTypeName(unsigned len, CompilerType compiler, bool isSigned)
{
switch (compiler)
{
case GccCppCompiler:
return isSigned ? gccIntTypes[len] : gccUIntTypes[len];
case Vs6CppCompiler:
return isSigned ? vcIntTypes[len] : vcUIntTypes[len];
default:
throwUnexpected();
}
}
bool isTypePassedByAddress(ITypeInfo * type)
{
switch (type->getTypeCode())
{
case type_decimal:
case type_string:
case type_data:
case type_qstring:
case type_varstring:
case type_unicode:
case type_varunicode:
case type_utf8:
case type_set:
case type_row:
return true;
case type_dictionary:
case type_table:
case type_groupedtable:
return !isArrayRowset(type);
case type_record:
throwUnexpected();
}
return false;
}
static StringBuffer & appendCapital(StringBuffer & s, StringBuffer & _name)
{
const char * name = _name.str();
if (name && name[0])
{
s.append((char)toupper(*name));
s.append(name+1);
}
return s;
}
//---------------------------------------------------------------------------
CppWriterTemplate::CppWriterTemplate()
{
text = NULL;
len = 0;
}
CppWriterTemplate::~CppWriterTemplate()
{
free(text);
}
void CppWriterTemplate::generate(ISectionWriter & writer, unsigned pass, IProperties * properties)
{
writer.setOutput(out, outStream);
const char * finger = text;
bool output = true;
BoolArray outputStack;
StringBuffer temp;
ForEachItemIn(idx, sections)
{
CppTemplateSection & cur = (CppTemplateSection &)sections.item(idx);
if (output && (cur.position > finger))
outputQuoted(writer, cur.position-finger, finger);
switch (cur.type)
{
case TplEmbed:
if (output && cur.id)
writer.generateSection(cur.indent, cur.id, pass);
break;
case TplExpand:
if (output && properties)
{
temp.clear();
properties->getProp(cur.id->str(), temp);
outputQuoted(writer, temp.length(), temp.str());
}
break;
case TplCondition:
outputStack.append(output);
if (output)
output = (properties && properties->hasProp(cur.id->str()));
break;
case TplEndCondition:
output = outputStack.pop();
break;
}
finger = cur.position + cur.len;
}
char * end = text+len;
if (output && (end > finger))
outputQuoted(writer, end-finger, finger);
writer.setOutput(NULL, NULL);
}
bool CppWriterTemplate::loadTemplate(const char * filename, const char *dir)
{
StringBuffer tpl(dir);
if(tpl.length())
tpl.append(PATHSEPCHAR);
tpl.append(filename);
Owned file = createIFile(tpl);
Owned io = file->openShared(IFOread, IFSHread);
if (!io)
return false;
offset_t size = (size32_t)io->size();
if (size != (size32_t)size)
return false;
text = (char *)malloc((size_t)size);
len=io->read(0, (size32_t)size, text);
unsigned index=0;
unsigned startLine = 0;
while (index != len)
{
char c = text[index];
switch (c)
{
case '$':
case '@':
{
unsigned start = index+1;
TplSectionType type = TplEmbed;
if (c == '$')
{
type = TplExpand;
if ((start < len) && (text[start] == '?'))
{
start++;
type = TplCondition;
if ((start < len) && (text[start] == c))
type = TplEndCondition;
}
}
unsigned end = start;
loop
{
if (end >= len)
throwError(HQLERR_MissingTemplateTerminator);
if (text[end] == c)
break;
++end;
}
unsigned indent = 0;
while (indent < index - startLine && isspace((byte)text[index-indent-1]))
indent++;
CppTemplateSection * next = new CppTemplateSection;
next->type = type;
next->position = text+index - indent;
next->len = end+1 - index + indent;
next->indent = indent;
next->id = createAtom(text+start, end-start);
if (end == index+1)
next->len--; // quoted character => include the next @/$
sections.append(*next);
index = end+1;
break;
}
case '\r':
case '\n':
startLine = index+1;
index++;
break;
default:
index++;
break;
}
}
return true;
}
//---------------------------------------------------------------------------
const char * getOpText(node_operator op)
{
switch (op)
{
case no_mul: return "*";
case no_div: return "/";
case no_modulus: return "%";
case no_negate: return "-";
case no_add: return "+";
case no_sub: return "-";
case no_eq: return "==";
case no_ne: return "!=";
case no_lt: return "<";
case no_le: return "<=";
case no_gt: return ">";
case no_ge: return ">=";
case no_not: return "!";
case no_and: return "&&";
case no_or: return "||";
case no_xor: return "xor"; //doesn't actually exist, should be transformed
case no_comma: return ",";
case no_compound: return ",";
case no_select: return ".";
case no_bnot: return "~";
case no_band: return "&";
case no_bor: return "|";
case no_bxor: return "^";
case no_postinc: return "++";
case no_postdec: return "--";
case no_preinc: return "++";
case no_predec: return "--";
case no_pselect: return "->";
case no_address: return "&";
case no_deref: return "*";
case no_lshift: return "<<";
case no_rshift: return ">>";
}
throwUnexpectedOp(op);
}
unsigned getPrecedence(IHqlExpression * expr)
{
node_operator op = expr->getOperator();
if (op == no_typetransfer)
return getPrecedence(expr->queryChild(0));
switch (op)
{
case no_order: //pseudo operator always generated in brackets
return 20;
// :: = 18
case no_pselect: case no_select: case no_index: case no_externalcall: case no_call:
case no_reference:
return 17;
case no_postinc: case no_postdec:
return 16;
case no_not: case no_negate:
case no_preinc: case no_predec:
case no_bnot: case no_address: case no_deref:
return 15;
case no_cast:
case no_implicitcast:
return 14;
case no_mul: case no_div: case no_modulus:
return 13;
case no_add: case no_sub:
return 12;
case no_lshift: case no_rshift:
return 11;
case no_lt: case no_gt: case no_le: case no_ge:
return 10;
case no_eq: case no_ne:
return 9;
case no_band:
return 8;
case no_bxor:
return 7;
case no_bor:
return 6;
case no_and:
return 5;
case no_or:
return 4;
case no_if:
return 3;
case no_assign: //op no_plus_assign etc.
return 2;
case no_compound:
case no_comma:
return 1;
}
return 50;
}
//---------------------------------------------------------------------------
class TypeNameBuilder
{
public:
TypeNameBuilder(const char * name) { typeOnLeft = false; str.append(name); }
void addPrefix(const char * text)
{
if (str.length())
str.insert(0, " ");
str.insert(0, text);
typeOnLeft = true;
}
StringBuffer & addSuffix()
{
if (typeOnLeft)
{
str.insert(0, "(").append(")");
typeOnLeft = false;
}
return str;
}
StringBuffer & addArray(unsigned length)
{
return addSuffix().append("[").append(length ? length : 1).append("]");
}
void get(StringBuffer & out) { out.append(str); }
protected:
StringBuffer str;
bool typeOnLeft;
};
void HqlCppWriter::generateType(StringBuffer & result, ITypeInfo * type, const char * name)
{
::generateTypeCpp(result, type, name, compiler);
}
void HqlCppWriter::generateType(ITypeInfo * type, const char * name)
{
TypeNameBuilder result(name);
loop
{
bool isPointer = false;
bool outOfLine= false;
ITypeInfo * fullType = type;
loop
{
typemod_t tmod = type->queryModifier();
if (tmod == typemod_none)
break;
switch (tmod)
{
case typemod_const:
// result.addPrefix("const");
break;
case typemod_outofline:
outOfLine = false;
break;
case typemod_ref:
isPointer = true;
break;
}
type = type->queryTypeBase();
}
ITypeInfo * next = type->queryChildType();
type_t tc = type->getTypeCode();
size32_t size = type->getSize();
const char * prefix = NULL;
switch (tc)
{
case type_pointer:
{
prefix = "*";
break;
}
case type_bitfield:
result.addSuffix().append(":").append(type->getBitSize());
break;
case type_array:
{
unsigned dim = type->getCardinality();
result.addSuffix().append("[");
if (dim && (dim != UNKNOWN_LENGTH))
result.addSuffix().append(dim);
result.addSuffix().append("]");
break;
}
case type_decimal:
//MORE: decimal different as parameters???
case type_varstring:
case type_string:
case type_qstring:
case type_data:
case type_utf8:
{
if ((size != UNKNOWN_LENGTH) && !isPointer)
{
result.addArray(size);
}
else
isPointer = true;
if (tc == type_data)
{
if (isPointer)
prefix = "void";
else
prefix = "byte";
}
else if (tc == type_decimal)
{
//make this to byte
if (isPointer)
prefix = "void";
else
prefix = "char";
}
else
prefix = "char";
break;
}
case type_varunicode:
case type_unicode:
{
if ((type->getSize() != UNKNOWN_LENGTH) && !isPointer)
result.addArray(size/2);
else
isPointer = true;
prefix = "UChar";
break;
}
case type_char:
prefix = "char";
break;
case type_row:
case type_sortlist:
if (hasLinkCountedModifier(fullType))
isPointer = true;
prefix = "byte";
next = NULL;
break;
case type_groupedtable:
//next = next->queryChildType();
isPointer = false;
break;
case type_dictionary:// MORE - is this right?
case type_table:
if (isArrayRowset(fullType))
{
prefix = "*";
}
else
{
isPointer = false;
prefix = "*";
}
break;
case type_class:
prefix = type->queryTypeName();
break;
case type_boolean:
prefix = "bool";
break;
case type_int:
case type_swapint:
prefix = intTypeName(size-1, compiler, type->isSigned());
break;
case type_real:
prefix = (size == 4) ? "float" : "double";
break;
case type_packedint:
case type_enumerated:
//recurse to child type
break;
case type_void:
case no_any:
prefix = "void";
break;
case type_set:
if (isPointer)
{
prefix = "byte";
next = NULL;
}
else if (!next)
{
result.addPrefix("char * *");
// result.addPrefix("byte");
isPointer = false;
}
break;
case type_function:
{
StringBuffer parameterText;
IFunctionTypeExtra * extra = queryFunctionTypeExtra(type);
IHqlExpression * args = static_cast(extra->queryParameters());
ForEachChild(i, args)
{
if (i)
parameterText.append(", ");
generateExprCpp(parameterText, args->queryChild(i));
}
//Walk args and add the types
result.addSuffix().append("(").append(parameterText).append(")");
}
break;
case type_transform:
default:
throwUnexpected();
}
if (isPointer)
{
result.addPrefix("*");
isPointer = false;
}
if (prefix)
result.addPrefix(prefix);
if (!next)
{
result.get(out);
return;
}
type = next;
}
}
bool HqlCppWriter::generateFunctionPrototype(IHqlExpression * funcdef)
{
IHqlExpression *body = funcdef->queryChild(0);
StringBuffer name;
getProperty(body, entrypointAtom, name);
if (!name.length())
name.append(funcdef->queryName());
return generateFunctionPrototype(funcdef, name);
}
bool HqlCppWriter::generateFunctionPrototype(IHqlExpression * funcdef, const char * name)
{
IHqlExpression *body = funcdef->queryChild(0);
IHqlExpression *formals = funcdef->queryChild(1);
if (body->hasProperty(includeAtom) || body->hasProperty(ctxmethodAtom) || body->hasProperty(gctxmethodAtom) || body->hasProperty(methodAtom) || body->hasProperty(sysAtom) || body->hasProperty(omethodAtom))
return false;
enum { ServiceApi, RtlApi, BcdApi, CApi, LocalApi } api = ServiceApi;
bool isVirtual = funcdef->hasProperty(virtualAtom);
bool isLocal = body->hasProperty(localAtom);
if (body->hasProperty(eclrtlAtom))
api = RtlApi;
else if (body->hasProperty(bcdAtom))
api = BcdApi;
else if (body->hasProperty(cAtom))
api = CApi;
else if (isLocal || isVirtual)
api = LocalApi;
if (isVirtual)
out.append("virtual");
else
out.append("extern ");
if ((api == ServiceApi) || api == CApi)
out.append("\"C\" ");
switch (api)
{
case ServiceApi: out.append(" SERVICE_API"); break;
case RtlApi: out.append(" RTL_API"); break;
case BcdApi: out.append(" BCD_API"); break;
}
out.append(" ");
StringBuffer returnParameters;
ITypeInfo * retType = funcdef->queryType()->queryChildType();
generateFunctionReturnType(returnParameters, retType, body);
switch (api)
{
case CApi:
switch (compiler)
{
case Vs6CppCompiler:
out.append(" _cdecl");
break;
}
break;
case BcdApi:
switch (compiler)
{
case Vs6CppCompiler:
out.append(" __fastcall");
break;
}
break;
}
out.append(" ").append(name);
bool firstParam = true;
out.append('(');
if (body->hasProperty(contextAtom))
{
out.append("ICodeContext * ctx");
firstParam = false;
}
else if (body->hasProperty(globalContextAtom) )
{
out.append("IGlobalCodeContext * gctx");
firstParam = false;
}
else if (body->hasProperty(userMatchFunctionAtom))
{
out.append("IMatchWalker * results");
firstParam = false;
}
if (returnParameters.length())
{
if (!firstParam)
out.append(',');
out.append(returnParameters);
firstParam = false;
}
ForEachChild(i, formals)
{
if (!firstParam)
out.append(',');
else
firstParam = false;
IHqlExpression * param = formals->queryChild(i);
generateParamCpp(param);
}
out.append(")");
return true;
}
void HqlCppWriter::generateInitializer(IHqlExpression * expr)
{
IValue * value = expr->queryValue();
assertex(value);
ITypeInfo * type = value->queryType();
type_t tc = type->getTypeCode();
size32_t size = value->getSize();
const byte * raw = (const byte *)value->queryValue();
out.append("{ ");
switch (tc)
{
case type_data:
case type_qstring:
case type_decimal:
{
for (unsigned i=0; i < size; i++)
{
if (i)
out.append(",");
queryBreakLine();
out.append((unsigned)raw[i]);
}
break;
}
case type_string:
case type_varstring:
case type_utf8:
{
for (unsigned i = 0; i < size; i++)
{
if (i)
out.append(",");
queryBreakLine();
byte next = raw[i];
switch (next)
{
case '\\': case '\'':
out.append("'\\").append((char)next).append('\'');
break;
default:
if (isprint(next))
out.append('\'').append((char)next).append('\'');
else
out.append((int)next);
break;
}
}
break;
}
case type_unicode:
case type_varunicode:
{
unsigned max = size/2;
for (unsigned i = 0; i < max; i++)
{
if (i)
out.append(",");
queryBreakLine();
UChar next = ((UChar *)raw)[i];
switch (next)
{
case '\\': case '\'':
out.append("'\\").append((char)next).append('\'');
break;
default:
if ((next >= 32) && (next <= 126))
out.append('\'').append((char)next).append('\'');
else
out.append((unsigned)next);
break;
}
}
break;
}
default:
throwUnexpected();
}
out.append(" }");
}
void HqlCppWriter::generateParamCpp(IHqlExpression * param)
{
ITypeInfo *paramType = param->queryType();
//Case is significant if these parameters are use for BEGINC++ sections
_ATOM paramName = param->queryName();
StringBuffer paramNameText;
paramNameText.append(paramName).toLowerCase();
bool isOut = false;
bool isConst = false;
unsigned maxAttr = param->numChildren();
unsigned attrIdx;
for (attrIdx = 0; attrIdx < maxAttr; attrIdx++)
{
IHqlExpression * attr = param->queryChild(attrIdx);
if (attr->isAttribute())
{
if (attr->queryName() == constAtom)
isConst = true;
else if (attr->queryName() == outAtom)
isOut = true;
}
}
switch (paramType->getTypeCode())
{
case type_string:
case type_qstring:
case type_data:
case type_unicode:
case type_utf8:
case type_dictionary:
case type_table:
case type_groupedtable:
if (paramType->getSize() == UNKNOWN_LENGTH)
{
out.append("size32_t");
if (isOut)
out.append(" &");
if (paramName)
appendCapital(out.append(" len"), paramNameText);
out.append(",");
}
break;
case type_set:
if (!queryProperty(paramType, oldSetFormatAtom))
{
out.append("bool");
if (paramName)
appendCapital(out.append(" isAll"), paramNameText);
out.append(",size32_t ");
if(paramName)
appendCapital(out.append(" len"), paramNameText);
}
else
{
out.append("unsigned");
if(paramName)
appendCapital(out.append(" num"), paramNameText);
}
out.append(",");
break;
case type_row:
isConst = true;
break;
}
bool nameappended = false;
switch (paramType->getTypeCode())
{
case type_record:
out.append("IOutputMetaData * ");
break;
case type_dictionary:
case type_table:
case type_groupedtable:
if (isConst)
out.append("const ");
out.append("void *");
if (isOut)
out.append(" &");
break;
case type_set:
if (!queryProperty(paramType, oldSetFormatAtom))
{
if (isConst)
out.append("const ");
out.append("void *");
if (isOut)
out.append(" &");
break;
}
else
{
ITypeInfo* childType = paramType->queryChildType();
if(!childType)
break;
if(isStringType(childType)) {
// process stringn and varstringn specially.
if(childType->getSize() > 0) {
out.append("char ");
if(paramName) {
out.append(paramNameText);
nameappended = true;
}
out.append("[][");
unsigned setlen = childType->getSize();
out.append(setlen);
out.append("]");
}
// Process string and varstring specially
else {
out.append("char *");
if(paramName) {
out.append(paramNameText);
nameappended = true;
}
out.append("[]");
}
}
else
{
OwnedITypeInfo pointerType = makePointerType(LINK(childType));
generateType(pointerType, NULL);
}
break;
}
// Other set types just fall through and will be treated like other types.
case type_qstring: case type_string: case type_varstring: case type_data:
default:
{
if (isConst)
out.append("const ");
Owned argType = LINK(paramType);
if (argType->getTypeCode() == type_function)
argType.setown(makePointerType(LINK(argType)));
if (isTypePassedByAddress(argType))
argType.setown(makeReferenceModifier(LINK(argType)));
generateType(argType, paramName->str());
nameappended = true;
if (isOut)
out.append(" &");
break;
}
}
if (paramName && !nameappended)
out.append(" ").append(paramNameText);
}
void HqlCppWriter::generateFunctionReturnType(StringBuffer & params, ITypeInfo * retType, IHqlExpression * attrs)
{
type_t tc = retType->getTypeCode();
switch (tc)
{
case type_varstring:
case type_varunicode:
if (retType->getSize() == UNKNOWN_LENGTH)
{
generateType(retType, NULL);
break;
}
//fall through
case type_qstring:
case type_string:
case type_data:
case type_unicode:
case type_utf8:
{
OwnedITypeInfo ptrType = makeReferenceModifier(LINK(retType));
out.append("void");
if (retType->getSize() == UNKNOWN_LENGTH)
{
if (retType->getTypeCode() != type_varstring)
params.append("size32_t & __lenResult,");
// if (hasConstModifier(retType))
// params.append("const ");
generateType(params, retType, NULL);
params.append(" & __result");
}
else
{
generateType(params, ptrType, "__result");
}
break;
}
case type_transform:
out.append("size32_t");
params.append("ARowBuilder & __self");
break;
case type_dictionary:
case type_table:
case type_groupedtable:
if (hasStreamedModifier(retType))
{
out.append("IRowStream *");
params.append("IEngineRowAllocator * _resultAllocator");
}
else if (hasLinkCountedModifier(retType))
{
out.append("void");
params.append("size32_t & __countResult,");
// if (hasConstModifier(retType))
// params.append("const ");
params.append("byte * * & __result");
if (hasNonNullRecord(retType) && getBoolProperty(attrs, allocatorAtom, true))
params.append(", IEngineRowAllocator * _resultAllocator");
}
else
{
out.append("void");
params.append("size32_t & __lenResult,");
// if (hasConstModifier(retType))
// params.append("const ");
params.append("void * & __result");
}
break;
case type_set:
{
out.append("void");
if (!getBoolProperty(attrs, oldSetFormatAtom))
{
params.append("bool & __isAllResult,");
params.append("size32_t & __lenResult,");
}
else
params.append("unsigned & __numResult,");
// if (hasConstModifier(retType))
// params.append("const ");
params.append("void * & __result");
break;
}
case type_row:
out.append("void");
params.append("byte * __result");
break;
default:
generateType(retType, NULL);
break;
}
}
StringBuffer & HqlCppWriter::generateExprCpp(StringBuffer & result, IHqlExpression * expr)
{
return ::generateExprCpp(result, expr, compiler);
}
StringBuffer & HqlCppWriter::generateExprCpp(IHqlExpression * expr)
{
node_operator op = expr->getOperator();
switch (op)
{
case no_constant:
{
unsigned prevLen = out.length();
expr->queryValue()->generateCPP(out, compiler);
outputLineNum += memcount(out.length()-prevLen,out.str()+prevLen,'\n');
break;
}
case no_eq:
case no_ne:
case no_lt:
case no_le:
case no_gt:
case no_ge:
case no_mul:
case no_div:
case no_modulus:
case no_add:
case no_sub:
case no_and:
case no_or:
case no_xor:
case no_comma:
case no_compound:
case no_band:
case no_bor:
case no_bxor:
case no_lshift:
case no_rshift:
{
unsigned numArgs = expr->numChildren();
for (unsigned index = 0; index < numArgs; index++)
{
if (index != 0)
out.append(' ').append(getOpText(op)).append(' ');
//would be nicer if it broke at PREFERRED_LINE_LIMIT if not inside a ()
queryBreakLine();
generateChildExpr(expr, index);
}
break;
}
case no_pselect:
case no_select:
generateExprCpp(expr->queryChild(0));
out.append(getOpText(op));
generateExprCpp(expr->queryChild(1));
break;
case no_if:
{
generateChildExpr(expr, 0);
out.append(" ? ");
queryBreakLine();
generateChildExpr(expr, 1);
out.append(" : ");
queryBreakLine();
generateChildExpr(expr, 2);
break;
}
case no_list:
{
out.append('{');
generateCommaChildren(expr);
out.append('}');
break;
}
case no_externalcall:
{
IHqlExpression *funcdef = expr->queryExternalDefinition();
IHqlExpression *props = funcdef->queryChild(0);
unsigned firstArg = 0;
unsigned numArgs = expr->numChildren();
if (props->hasProperty(ctxmethodAtom))
{
out.append("ctx->");
}
else if (props->hasProperty(gctxmethodAtom))
{
out.append("gctx->");
}
else if (props->hasProperty(methodAtom))
{
generateExprCpp(expr->queryChild(firstArg)).append("->");
++firstArg;
}
else if (props->hasProperty(omethodAtom))
{
generateExprCpp(expr->queryChild(firstArg)).append(".");
++firstArg;
}
getProperty(props, entrypointAtom, out);
out.append('(');
if (props->hasProperty(contextAtom))
{
out.append("ctx");
if (numArgs)
out.append(',');
}
else if (props->hasProperty(globalContextAtom))
{
out.append("gctx");
if (numArgs)
out.append(',');
}
for (unsigned index = firstArg; index < numArgs; index++)
{
if (index != firstArg) out.append(',');
generateExprCpp(expr->queryChild(index));
}
out.append(')');
break;
}
case no_cast:
case no_implicitcast:
{
ITypeInfo * type = expr->queryType();
IHqlExpression * child = expr->queryChild(0);
if (hasWrapperModifier(child->queryType()))
{
generateChildExpr(expr, 0).append(".");
switch (type->getTypeCode())
{
case type_string:
case type_varstring:
case type_qstring:
case type_utf8:
out.append("getstr()");
break;
case type_data:
out.append("getdata()");
break;
case type_row:
out.append("getbytes()");
break;
case type_set:
case type_dictionary:
case type_table:
case type_groupedtable:
if (hasLinkCountedModifier(type))
out.append("queryrows()");
else
out.append("getbytes()");
break;
case type_unicode:
case type_varunicode:
out.append("getustr()");
break;
default:
UNIMPLEMENTED;
}
break;
}
out.append("(");
generateType(type, NULL);
out.append(")");
generateChildExpr(expr, 0);
}
break;
case no_typetransfer:
generateExprCpp(expr->queryChild(0));
break;
case no_translated:
#ifdef _DEBUG
out.append("$translated$");//Cause a compile error.
#endif
generateExprCpp(expr->queryChild(0));
break;
case no_order:
generateOrderExpr(expr->queryChild(0), expr->queryChild(1));
break;
case no_index:
generateChildExpr(expr, 0);
out.append('[');
generateChildExpr(expr, 1);
out.append(']');
break;
case no_selectmap:
generateChildExpr(expr, 0);
out.append("[<");
generateChildExpr(expr, 1);
out.append(">]");
break;
case no_postinc:
case no_postdec:
generateChildExpr(expr, 0);
out.append(getOpText(op));
break;
case no_reference:
{
IHqlExpression * child = expr->queryChild(0);
generateChildExpr(expr, 0);
if (hasWrapperModifier(child->queryType()))
{
out.append(".");
ITypeInfo * type = expr->queryType();
switch (type->getTypeCode())
{
case type_string:
case type_varstring:
case type_qstring:
case type_utf8:
out.append("refstr()");
break;
case type_data:
out.append("refdata()");
break;
case type_row:
out.append("getbytes()");
break;
case type_set:
case type_dictionary:
case type_table:
case type_groupedtable:
if (hasLinkCountedModifier(type))
out.append("refrows()");
else
out.append("refdata()");
break;
case type_unicode:
case type_varunicode:
out.append("refustr()");
break;
}
}
break;
}
case no_address:
{
IHqlExpression * child = expr->queryChild(0);
ITypeInfo * childType = child->queryType();
if (hasWrapperModifier(childType))
{
generateChildExpr(expr, 0).append(".");
switch (childType->getTypeCode())
{
case type_string:
case type_varstring:
case type_qstring:
case type_utf8:
out.append("addrstr()");
break;
case type_data:
out.append("addrdata()");
break;
case type_row:
throwUnexpected();
out.append("getbytes()"); //????
break;
case type_set:
case type_dictionary:
case type_table:
case type_groupedtable:
if (hasLinkCountedModifier(childType))
out.append("addrrows()");
else
out.append("addrdata()");
break;
case type_unicode:
case type_varunicode:
out.append("addrustr()");
break;
default:
UNIMPLEMENTED;
}
}
else
{
out.append(getOpText(op));
generateChildExpr(expr, 0);
}
break;
}
case no_negate:
case no_not:
case no_bnot:
case no_deref:
case no_preinc:
case no_predec:
out.append(getOpText(op));
generateChildExpr(expr, 0);
break;
case no_quoted:
case no_variable:
return expr->toString(out);
case no_field:
return expr->toString(out); //MORE!!!
case no_create_initializer:
generateInitializer(expr->queryChild(0));
break;
case no_pure:
case no_impure:
generateExprCpp(expr->queryChild(0));
break;
case no_param:
generateParamCpp(expr);
break;
case no_callback:
{
IHqlDelayedCodeGenerator * generator = (IHqlDelayedCodeGenerator *)expr->queryUnknownExtra();
generator->generateCpp(out);
break;
}
case no_funcdef:
{
IHqlExpression * body = expr->queryChild(0);
assertex(body->getOperator() == no_external);
IHqlExpression * entrypoint = queryPropertyChild(body, entrypointAtom, 0);
getStringValue(out, entrypoint);
break;
}
case no_nullptr:
out.append("NULL");
break;
// case no_self: return out.append("self");
default:
return expr->toString(out.append("")).append("?>");
}
return out;
}
StringBuffer & HqlCppWriter::generateChildExpr(IHqlExpression * expr, unsigned childIndex)
{
IHqlExpression * child = expr->queryChild(childIndex);
unsigned p = getPrecedence(expr);
unsigned cp = getPrecedence(child);
bool needBra = true;
if (p < cp)
needBra = false;
else if (p == cp)
{
if (isCast(expr) && isCast(child))
needBra = false;
node_operator op = expr->getOperator();
if (op == child->getOperator())
{
switch (op)
{
case no_and:
case no_or:
case no_add:
case no_band:
case no_bor:
needBra = false;
break;
}
}
}
if (!needBra)
return generateExprCpp(child);
out.append('(');
return generateExprCpp(child).append(')');
}
StringBuffer & HqlCppWriter::generateCommaChildren(IHqlExpression * expr)
{
unsigned numArgs = expr->numChildren();
unsigned startLength = out.length();
for (unsigned index = 0; index < numArgs; index++)
{
if (index != 0)
out.append(',');
unsigned newLength = out.length();
if (newLength - startLength > PREFERRED_LINE_LIMIT)
{
newline().append("\t");
startLength = out.length();
}
generateExprCpp(expr->queryChild(index));
}
return out;
}
StringBuffer & HqlCppWriter::generateExprAsChar(IHqlExpression * expr)
{
switch (expr->getOperator())
{
case no_constant:
{
StringBuffer temp;
IValue * value = expr->queryValue();
value->getStringValue(temp);
out.append((int)temp.charAt(0));
}
break;
default:
//create an indexed node and generate that - much better
generateExprCpp(expr).append("[0]");
break;
}
return out;
}
void HqlCppWriter::generateOrderExpr(IHqlExpression * left, IHqlExpression * right)
{
ITypeInfo * lType = left->queryType();
ITypeInfo * rType = right->queryType();
ITypeInfo * lBaseType = lType;
ITypeInfo * rBaseType = rType;
if (lType->getTypeCode() == type_pointer)
lBaseType = lType->queryChildType();
if (rType->getTypeCode() == type_pointer)
rBaseType = rType->queryChildType();
assertex(isSameBasicType(lBaseType, rBaseType));
switch (lBaseType->getTypeCode())
{
case type_string:
case type_data:
case type_qstring:
case type_varstring:
case type_unicode:
case type_varunicode:
case type_utf8:
{
throwUnexpectedType(lBaseType);
break;
}
default:
out.append("(");
generateExprCpp(left).append(" < ");
generateExprCpp(right).append(" ? -1 : ");
generateExprCpp(left).append(" > ");
generateExprCpp(right).append(" ? +1 : 0)");
break;
}
}
//---------------------------------------------------------------------------
HqlCppWriter::HqlCppWriter(StringBuffer & _out, CompilerType _compiler) : out(_out)
{
curIndent = 0;
startOffset = 0;
compiler = _compiler;
outputLineNum = 1;
}
HqlCppWriter::HqlCppWriter(CompilerType _compiler) : out(defaultOut)
{
curIndent = 0;
startOffset = 0;
compiler = _compiler;
outputLineNum = 1;
}
StringBuffer & HqlCppWriter::indent()
{
#ifdef INDENT_SOURCE
unsigned i = curIndent;
for (;i > 10;i-=10)
out.append("\t\t\t\t\t\t\t\t\t\t");
out.append(i, "\t\t\t\t\t\t\t\t\t\t");
#endif
return out;
}
void HqlCppWriter::flush()
{
if (target)
{
target->write(out.length(), out.toCharArray());
out.clear();
startOffset = 0;
}
}
void HqlCppWriter::generateStmtForPass(IHqlStmt * stmt, unsigned pass)
{
switch (stmt->getStmt())
{
case pass_stmt:
if (stmt->queryExpr(0)->queryValue()->getIntValue() == pass)
generateStmt(stmt);
break;
case indirect_stmt:
{
ForEachChild(i, stmt)
generateStmtForPass(stmt->queryChild(i), pass);
break;
}
default:
generateStmt(stmt);
break;
}
}
void HqlCppWriter::generateStatementsForPass(HqlStmts & stmts, unsigned delta, unsigned pass)
{
indent((int)delta);
ForEachItemIn(i, stmts)
generateStmtForPass(&stmts.item(i), pass);
indent(-(int)delta);
flush();
}
void HqlCppWriter::generate(HqlStmtArray & stmts)
{
ForEachItemIn(idx, stmts)
{
IHqlStmt & cur = stmts.item(idx);
generateStmt(&cur);
switch (cur.getStmt())
{
case break_stmt:
case return_stmt:
case goto_stmt:
case continue_stmt:
//After these, any other expressions are irrelevant..
return;
}
}
}
void HqlCppWriter::generateChildren(IHqlStmt * stmt, bool addBraces)
{
if (addBraces)
{
queryIndent().append("{");
newline();
indent(1);
}
unsigned count = stmt->numChildren();
for (unsigned index = 0; index < count; index++)
generateStmt(stmt->queryChild(index));
if (addBraces)
{
indent(-1);
indent().append("}");
newline();
}
}
void HqlCppWriter::generateStmt(IHqlStmt * stmt)
{
if (!stmt->isIncluded())
return;
unsigned kind = stmt->getStmt();
switch (kind)
{
case assign_stmt:
generateStmtAssign(stmt);
break;
case block_stmt:
generateChildren(stmt, true);
break;
case break_stmt:
indent().append("break;");
newline();
break;
case continue_stmt:
indent().append("continue;");
newline();
break;
case case_stmt:
generateStmtCase(stmt);
break;
case declare_stmt:
case external_stmt:
generateStmtDeclare(stmt);
break;
case default_stmt:
indent().append("default:");
generateChildren(stmt, true);
break;
case expr_stmt:
indent();
generateExprCpp(stmt->queryExpr(0)).append(';');
newline();
break;
case filter_stmt:
generateStmtFilter(stmt);
break;
case goto_stmt:
indent().append("goto ");
generateExprCpp(stmt->queryExpr(0)).append(';');
newline();
break;
case function_stmt:
generateStmtFunction(stmt);
break;
case alias_stmt:
case group_stmt:
case pass_stmt:
case indirect_stmt:
generateChildren(stmt, false);
break;
case label_stmt:
generateExprCpp(stmt->queryExpr(0)).append(": ;");
newline();
break;
case loop_stmt:
generateStmtLoop(stmt);
break;
case line_stmt:
generateStmtLine(stmt);
break;
case quote_compoundopt_stmt:
case quote_stmt:
case quote_compound_stmt:
{
indent();
unsigned prevLen = out.length();
stmt->getTextExtra(out);
outputLineNum += memcount(out.length()-prevLen,out.str()+prevLen,'\n');
if (kind != quote_stmt)
{
out.append(" {");
newline();
indent(1);
generateChildren(stmt, false);
indent(-1);
indent().append("}");
IHqlExpression * extra = stmt->queryExpr(0);
if (extra)
generateExprCpp(extra);
newline();
}
else
newline();
break;
}
case return_stmt:
{
IHqlExpression * value = stmt->queryExpr(0);
if (value)
{
indent().append("return ");
generateExprCpp(stmt->queryExpr(0)).append(';');
}
else
indent().append("return;");
newline();
break;
}
case switch_stmt:
generateStmtSwitch(stmt);
break;
case assigninc_stmt:
case assigndec_stmt:
generateStmtAssignModify(stmt);
break;
}
}
void HqlCppWriter::generateSimpleAssign(IHqlExpression * target, IHqlExpression * source)
{
indent();
generateExprCpp(target).append(" = ");
generateExprCpp(source).append(";");
}
void HqlCppWriter::generateStmtAssign(IHqlStmt * assign)
{
IHqlExpression * target = assign->queryExpr(0);
IHqlExpression * source = assign->queryExpr(1);
ITypeInfo * type = target->queryType();
switch (type->getTypeCode())
{
case type_char:
case type_int:
case type_swapint:
case type_packedint:
case type_real:
case type_boolean:
case type_bitfield:
case type_pointer:
case type_enumerated:
case type_record:
generateSimpleAssign(target, source);
break;
case type_varstring:
case type_varunicode:
if (hasModifier(type, typemod_ref))
generateSimpleAssign(target, source);
else if (type->getSize() == UNKNOWN_LENGTH)
{
indent();
generateExprCpp(target).append(".setown(");
generateExprCpp(source).append(");");
}
else
throwUnexpected();
break;
case type_dictionary:
case type_table:
case type_groupedtable:
if (hasWrapperModifier(type))
{
if (hasLinkCountedModifier(type))
{
assertex(source->getOperator() == no_complex);
indent();
generateExprCpp(target).append(".setown(");
generateExprCpp(source->queryChild(0)).append(",");
generateExprCpp(source->queryChild(1)).append(");");
}
else
{
indent();
generateExprCpp(target).append(".setown(");
generateExprCpp(source).append(");");
}
}
else
generateSimpleAssign(target, source);
break;
case type_set:
case type_row:
if (hasWrapperModifier(type))
{
indent();
generateExprCpp(target).append(".setown(");
generateExprCpp(source).append(");");
}
else
generateSimpleAssign(target, source);
break;
case type_qstring:
case type_string:
case type_data:
case type_unicode:
case type_utf8:
if (hasModifier(type, typemod_ref))
generateSimpleAssign(target, source);
else
throwUnexpected();
break;
default:
if (hasModifier(type, typemod_ref))
generateSimpleAssign(target, source);
else
{
type->getTypeCode();
assertex(!"Unexpected type assignment!");
generateSimpleAssign(target, source);
out.append("$$BadType$$");
}
break;
}
newline();
}
void HqlCppWriter::generateStmtAssignModify(IHqlStmt * assign)
{
IHqlExpression * target = assign->queryExpr(0);
IHqlExpression * source = assign->queryExpr(1);
ITypeInfo * type = target->queryType();
switch (type->getTypeCode())
{
case type_row:
case type_dictionary:
case type_table:
case type_groupedtable:
//check it is a pointer increment
assertex(hasReferenceModifier(type));
case type_int:
case type_real:
case type_boolean:
case type_pointer:
indent();
generateExprCpp(target);
if (assign->getStmt() == assigninc_stmt)
out.append(" += ");
else
out.append(" -= ");
generateExprCpp(source).append(";");
break;
default:
throwUnexpected();
break;
}
newline();
}
void HqlCppWriter::generateStmtCase(IHqlStmt * stmt)
{
queryIndent().append("case ");
generateExprCpp(stmt->queryExpr(0)).append(":");
unsigned childCount = stmt->numChildren();
switch (childCount)
{
case 0:
//if case label contains nothing then it is commoned up with the next case
break;
case 1:
newline();
indent(1);
generateChildren(stmt, false);
if (stmt->queryChild(childCount-1)->getStmt() != return_stmt)
{
indent().append("break;");
newline();
}
indent(-1);
break;
default:
generateChildren(stmt, true);
if (stmt->queryChild(childCount-1)->getStmt() != return_stmt)
{
indent().append("break;");
newline();
}
}
}
void HqlCppWriter::generateStmtDeclare(IHqlStmt * declare)
{
IHqlExpression * name = declare->queryExpr(0);
IHqlExpression * value = declare->queryExpr(1);
ITypeInfo * type = name->queryType();
StringBuffer targetName;
assertex(name->getOperator() == no_variable);
name->toString(targetName);
if (declare->getStmt() == external_stmt)
out.append("extern ");
size32_t typeSize = type->getSize();
if (hasWrapperModifier(type))
{
if (hasModifier(type, typemod_builder))
indent().append("mutable rtlRowBuilder ").append(targetName);
else if (hasStreamedModifier(type))
{
indent().append("Owned ").append(targetName);
}
else if (hasLinkCountedModifier(type))
{
if (type->getTypeCode() == type_row)
indent().append("rtlRowAttr ").append(targetName);
else
indent().append("rtlRowsAttr ").append(targetName);
}
else if (typeSize != UNKNOWN_LENGTH)
indent().append("rtlFixedSizeDataAttr<").append(typeSize).append("> ").append(targetName);
else
indent().append("rtlDataAttr ").append(targetName);
}
else
{
indent();
generateType(type, targetName.str());
}
if (value)
{
out.append(" = ");
generateExprCpp(value);
}
out.append(";");
newline();
}
void HqlCppWriter::generateStmtFilter(IHqlStmt * stmt)
{
IHqlExpression * condition = stmt->queryExpr(0);
IValue * value = condition->queryValue();
//optimize generation of if (false) which can't be optimized earlier.
if (value)
{
IHqlStmt * folded = (value->getBoolValue()) ? stmt->queryChild(0) : stmt->queryChild(1);
if (folded)
{
if (folded->getStmt() == block_stmt)
generateChildren(folded, false);
else
generateStmt(folded);
}
}
else
{
indent().append("if (");
generateExprCpp(condition).append(")");
generateStmt(stmt->queryChild(0));
IHqlStmt * elseCode = stmt->queryChild(1);
if (elseCode)
{
indent().append("else");
generateStmt(elseCode);
}
}
}
void HqlCppWriter::generateStmtFunction(IHqlStmt * stmt)
{
IHqlExpression * funcdef = stmt->queryExpr(0);
indent();
generateFunctionPrototype(funcdef);
generateChildren(stmt, true);
}
void HqlCppWriter::generateStmtLoop(IHqlStmt * stmt)
{
IHqlExpression * cond = stmt->queryExpr(0);
IHqlExpression * inc = stmt->queryExpr(1);
bool atEnd = false;
if (inc)
{
if (inc->isAttribute())
{
atEnd = true;
inc = NULL;
}
else
{
if (stmt->queryExpr(2) != NULL)
atEnd = true;
}
}
if (atEnd)
{
indent().append("do {").newline();
indent(1);
generateChildren(stmt, false);
if (inc)
{
indent();
generateExprCpp(inc).append(";");
newline();
}
indent(-1);
indent().append("} while (");
generateExprCpp(cond);
out.append(");");
newline();
}
else
{
indent().append("for (;");
if (cond)
generateExprCpp(cond);
out.append(";");
if (inc)
generateExprCpp(inc);
out.append(")");
generateChildren(stmt, true);
}
}
void HqlCppWriter::generateStmtLine(IHqlStmt * stmt)
{
IHqlExpression * filename = stmt->queryExpr(0);
IHqlExpression * line = stmt->queryExpr(1);
if (filename && line)
{
out.append("#line ");
generateExprCpp(line).append(" ");
generateExprCpp(filename);
newline();
}
else
{
//NB: Sets the line number of the next line...
const char * targetFilename = targetFile->queryFilename();
out.append("#line ").append(outputLineNum+1).append(" \"");
appendStringAsCPP(out, strlen(targetFilename), targetFilename, false).append("\"");
newline();
}
}
void HqlCppWriter::generateStmtSwitch(IHqlStmt * stmt)
{
indent().append("switch (");
generateExprCpp(stmt->queryExpr(0)).append(")");
generateChildren(stmt, true);
}
StringBuffer & HqlCppWriter::newline()
{
outputLineNum++;
out.newline();
if (target && (out.length() > FILE_CHUNK_SIZE))
flush();
startOffset = out.length();
return out;
}
StringBuffer & HqlCppWriter::queryIndent()
{
if (out.length() - startOffset > REASONABLE_LINE_LIMIT)
newline();
if (out.length() == startOffset)
return indent();
return out.append(" ");
}
StringBuffer & HqlCppWriter::queryBreakLine()
{
if (out.length() - startOffset > REASONABLE_LINE_LIMIT)
{
newline();
indent().append("\t");
}
return out;
}
void HqlCppWriter::queryNewline()
{
if (out.length() != startOffset)
newline();
}
void HqlCppWriter::setOutput(IFile * _targetFile, IIOStream * _target)
{
flush();
targetFile.set(_targetFile);
target.set(_target);
out.ensureCapacity(FILE_CHUNK_SIZE + 2 * REASONABLE_LINE_LIMIT);
}
void HqlCppSectionWriter::generateSection(unsigned delta, _ATOM section, unsigned pass)
{
HqlStmts * match = instance.querySection(section);
if (match)
writer.generateStatementsForPass(*match, delta, pass);
}
//---------------------------------------------------------------------------
ITemplateExpander * createTemplateExpander(IFile * output, const char * filename, const char *dir)
{
Owned expander = new CppWriterTemplate;
if (expander->loadTemplate(filename, dir) || expander->loadTemplate(filename, ""))
{
expander->setOutput(output);
return expander.getClear();
}
return NULL;
}
ISectionWriter * createCppWriter(IHqlCppInstance & _instance, CompilerType compiler)
{
return new HqlCppSectionWriter(_instance, compiler);
}
extern HQLCPP_API StringBuffer & generateExprCpp(StringBuffer & out, IHqlExpression * expr, CompilerType compiler)
{
HqlCppWriter writer(out, compiler);
writer.generateExprCpp(expr);
return out;
}
extern HQLCPP_API StringBuffer & generateTypeCpp(StringBuffer & out, ITypeInfo * type, const char * name, CompilerType compiler)
{
HqlCppWriter writer(out, compiler);
writer.generateType(type, name);
return out;
}
void generateFunctionReturnType(StringBuffer & prefix, StringBuffer & params, ITypeInfo * retType, IHqlExpression * attrs, CompilerType compiler)
{
HqlCppWriter writer(prefix, compiler);
writer.generateFunctionReturnType(params, retType, attrs);
}
bool generateFunctionPrototype(StringBuffer & out, IHqlExpression * funcdef, CompilerType compiler)
{
HqlCppWriter writer(out, compiler);
return writer.generateFunctionPrototype(funcdef);
}