|
|
@@ -34,9 +34,8 @@ namespace ASBindingGenerator
|
|
|
|
|
|
// https://www.angelcode.com/angelscript/sdk/docs/manual/doc_datatypes_primitives.html
|
|
|
// https://en.cppreference.com/w/cpp/language/types
|
|
|
-string CppFundamentalTypeToAS(const string& cppType)
|
|
|
+string CppPrimitiveTypeToAS(const string& cppType)
|
|
|
{
|
|
|
- // AngelScript itself detect width of bool type (look AS_SIZEOF_BOOL define)
|
|
|
if (cppType == "bool")
|
|
|
return "bool";
|
|
|
|
|
|
@@ -70,7 +69,8 @@ string CppFundamentalTypeToAS(const string& cppType)
|
|
|
if (cppType == "double")
|
|
|
return "double";
|
|
|
|
|
|
- // Types below have different width on different systems and are registered in Manual.cpp
|
|
|
+ // Types below are registered in Manual.cpp
|
|
|
+
|
|
|
if (cppType == "long")
|
|
|
return "long";
|
|
|
|
|
|
@@ -80,7 +80,10 @@ string CppFundamentalTypeToAS(const string& cppType)
|
|
|
if (cppType == "size_t")
|
|
|
return "size_t";
|
|
|
|
|
|
- throw Exception(cppType + " not a fundamental type");
|
|
|
+ if (cppType == "SDL_JoystickID")
|
|
|
+ return "SDL_JoystickID";
|
|
|
+
|
|
|
+ throw Exception(cppType + " not a primitive type");
|
|
|
}
|
|
|
|
|
|
shared_ptr<EnumAnalyzer> FindEnum(const string& name)
|
|
|
@@ -179,407 +182,411 @@ shared_ptr<ClassAnalyzer> FindClassByID(const string& id)
|
|
|
return shared_ptr<ClassAnalyzer>();
|
|
|
}
|
|
|
|
|
|
-string CppTypeToAS(const TypeAnalyzer& type, bool returnType)
|
|
|
+// Variable name can be empty for function return type
|
|
|
+ConvertedVariable CppVariableToAS(const TypeAnalyzer& type, const string& name, VariableUsage usage, string defaultValue)
|
|
|
{
|
|
|
+ ConvertedVariable result;
|
|
|
+
|
|
|
if (type.IsRvalueReference() || type.IsDoublePointer() || type.IsRefToPointer())
|
|
|
throw Exception("Error: type \"" + type.ToString() + "\" can not automatically bind");
|
|
|
|
|
|
string cppTypeName = type.GetNameWithTemplateParams();
|
|
|
|
|
|
- if (cppTypeName == "Context" && returnType)
|
|
|
- throw Exception("Error: type \"" + type.ToString() + "\" can not be returned");
|
|
|
-
|
|
|
- if (!IsKnownCppType(type.GetNameWithTemplateParams()))
|
|
|
- throw Exception("Error: type \"" + type.ToString() + "\" can not automatically bind");
|
|
|
-
|
|
|
- shared_ptr<ClassAnalyzer> analyzer = FindClassByName(type.GetNameWithTemplateParams());
|
|
|
- if (analyzer && analyzer->IsInternal())
|
|
|
- throw Exception("Error: type \"" + type.ToString() + "\" can not automatically bind bacause internal");
|
|
|
-
|
|
|
- if (analyzer && Contains(analyzer->GetComment(), "NO_BIND"))
|
|
|
- throw Exception("Error: type \"" + cppTypeName + "\" can not automatically bind bacause have @nobind mark");
|
|
|
-
|
|
|
- // analyzer can be null for simple types (int, float) or if type "using VariantVector = Vector<Variant>"
|
|
|
- // TODO add to type info "IsUsing"
|
|
|
- // TODO add description to TypeAnalyzer::GetClass()
|
|
|
-
|
|
|
- if (IsUsing(cppTypeName) && cppTypeName != "VariantMap")
|
|
|
- throw Exception("Using \"" + cppTypeName + "\" can not automatically bind");
|
|
|
-
|
|
|
- string asTypeName;
|
|
|
-
|
|
|
- try
|
|
|
+ if (cppTypeName == "void" && !type.IsPointer() && usage == VariableUsage::FunctionReturn)
|
|
|
{
|
|
|
- asTypeName = CppFundamentalTypeToAS(cppTypeName);
|
|
|
+ result.asDeclaration_ = "void";
|
|
|
+ return result;
|
|
|
}
|
|
|
- catch (...)
|
|
|
+
|
|
|
+ // Works with both Vector<String> and Vector<String>&
|
|
|
+ if ((cppTypeName == "Vector<String>" || cppTypeName == "StringVector") && !type.IsPointer() && usage == VariableUsage::FunctionReturn)
|
|
|
{
|
|
|
- asTypeName = cppTypeName;
|
|
|
+ result.asDeclaration_ = "Array<String>@";
|
|
|
+ result.newCppDeclaration_ = "CScriptArray*";
|
|
|
+ result.glue_ = "return VectorToArray<String>(result, \"Array<String>\");\n";
|
|
|
+ return result;
|
|
|
}
|
|
|
|
|
|
- if (asTypeName == "void" && type.IsPointer())
|
|
|
- throw Exception("Error: type \"void*\" can not automatically bind");
|
|
|
-
|
|
|
- if (asTypeName.find('<') != string::npos)
|
|
|
- throw Exception("Error: type \"" + type.ToString() + "\" can not automatically bind");
|
|
|
+ smatch match;
|
|
|
+ regex_match(cppTypeName, match, regex("SharedPtr<(\\w+)>"));
|
|
|
+ if (match.size() == 2 && usage == VariableUsage::FunctionReturn)
|
|
|
+ {
|
|
|
+ string cppSubtypeName = match[1].str();
|
|
|
|
|
|
- if (Contains(type.ToString(), "::"))
|
|
|
- throw Exception("Error: type \"" + type.ToString() + "\" can not automatically bind bacause internal");
|
|
|
+ string asSubtypeName;
|
|
|
|
|
|
- if (type.IsConst() && type.IsReference() && !returnType)
|
|
|
- return "const " + asTypeName + "&in";
|
|
|
+ try
|
|
|
+ {
|
|
|
+ asSubtypeName = CppPrimitiveTypeToAS(cppSubtypeName);
|
|
|
+ }
|
|
|
+ catch (...)
|
|
|
+ {
|
|
|
+ asSubtypeName = cppSubtypeName;
|
|
|
+ }
|
|
|
|
|
|
- string result = asTypeName;
|
|
|
+ if (cppSubtypeName == "WorkItem") // TODO autodetect
|
|
|
+ throw Exception("Error: type \"" + type.ToString() + "\" can not automatically bind");
|
|
|
|
|
|
- if (type.IsReference())
|
|
|
- {
|
|
|
- result += "&";
|
|
|
+ result.asDeclaration_ = asSubtypeName + "@+";
|
|
|
+ result.newCppDeclaration_ = cppSubtypeName + "*";
|
|
|
+ result.glue_ = "return result.Detach();\n";
|
|
|
+ return result;
|
|
|
}
|
|
|
- else if (type.IsPointer())
|
|
|
+
|
|
|
+ regex_match(cppTypeName, match, regex("Vector<SharedPtr<(\\w+)>>"));
|
|
|
+ if (match.size() == 2 && usage == VariableUsage::FunctionReturn)
|
|
|
{
|
|
|
- shared_ptr<ClassAnalyzer> analyzer = FindClassByName(type.GetNameWithTemplateParams());
|
|
|
+ string cppSubtypeName = match[1].str();
|
|
|
|
|
|
- if (analyzer && (analyzer->IsRefCounted() || Contains(analyzer->GetComment(), "FAKE_REF")))
|
|
|
- result += "@+";
|
|
|
- else
|
|
|
- throw Exception("Error: type \"" + type.ToString() + "\" can not automatically bind");
|
|
|
- }
|
|
|
+ string asSubtypeName;
|
|
|
|
|
|
- if (returnType && type.IsConst() && !type.IsPointer())
|
|
|
- result = "const " + result;
|
|
|
+ try
|
|
|
+ {
|
|
|
+ asSubtypeName = CppPrimitiveTypeToAS(cppSubtypeName);
|
|
|
+ }
|
|
|
+ catch (...)
|
|
|
+ {
|
|
|
+ asSubtypeName = cppSubtypeName;
|
|
|
+ }
|
|
|
|
|
|
- return result;
|
|
|
-}
|
|
|
+ result.asDeclaration_ = "Array<" + asSubtypeName + "@>@";
|
|
|
+ result.newCppDeclaration_ = "CScriptArray*";
|
|
|
|
|
|
-string CppValueToAS(const string& cppValue)
|
|
|
-{
|
|
|
- if (cppValue == "nullptr")
|
|
|
- return "null";
|
|
|
+ // Which variant is correct/better?
|
|
|
+#if 0
|
|
|
+ result->glueResult_ = "return VectorToArray<SharedPtr<" + cppTypeName + "> >(result, \"Array<" + asTypeName + "@>@\");\n";
|
|
|
+#else
|
|
|
+ result.glue_ = "return VectorToHandleArray(result, \"Array<" + asSubtypeName + "@>\");\n";
|
|
|
+#endif
|
|
|
+ return result;
|
|
|
+ }
|
|
|
|
|
|
- if (cppValue == "Variant::emptyVariantMap")
|
|
|
- return "VariantMap()";
|
|
|
+ regex_match(cppTypeName, match, regex("PODVector<(\\w+)\\*>"));
|
|
|
+ if (match.size() == 2 && usage == VariableUsage::FunctionReturn)
|
|
|
+ {
|
|
|
+ string cppSubtypeName = match[1].str();
|
|
|
|
|
|
- if (cppValue == "NPOS")
|
|
|
- return "String::NPOS";
|
|
|
+ string asSubtypeName;
|
|
|
|
|
|
- return cppValue;
|
|
|
-}
|
|
|
+ try
|
|
|
+ {
|
|
|
+ asSubtypeName = CppPrimitiveTypeToAS(cppSubtypeName);
|
|
|
+ }
|
|
|
+ catch (...)
|
|
|
+ {
|
|
|
+ asSubtypeName = cppSubtypeName;
|
|
|
+ }
|
|
|
|
|
|
-// =================================================================================
|
|
|
+ result.asDeclaration_ = "Array<" + asSubtypeName + "@>@";
|
|
|
+ result.newCppDeclaration_ = "CScriptArray*";
|
|
|
+ result.glue_ = "return VectorToHandleArray(result, \"Array<" + asSubtypeName + "@>\");\n";
|
|
|
+ return result;
|
|
|
+ }
|
|
|
|
|
|
-shared_ptr<FuncParamConv> CppFunctionParamToAS(const ParamAnalyzer& paramAnalyzer)
|
|
|
-{
|
|
|
- shared_ptr<FuncParamConv> result = make_shared<FuncParamConv>();
|
|
|
+ regex_match(cppTypeName, match, regex("PODVector<(\\w+)>"));
|
|
|
+ if (match.size() == 2 && type.IsConst() == type.IsReference() && usage == VariableUsage::FunctionReturn)
|
|
|
+ {
|
|
|
+ string cppSubtypeName = match[1].str();
|
|
|
|
|
|
- TypeAnalyzer typeAnalyzer = paramAnalyzer.GetType();
|
|
|
- string cppTypeName = typeAnalyzer.GetNameWithTemplateParams();
|
|
|
+ string asSubtypeName;
|
|
|
|
|
|
- if (cppTypeName == "Context")
|
|
|
- {
|
|
|
- result->errorMessage_ = "Context can be used as firs parameter of constructors only";
|
|
|
+ try
|
|
|
+ {
|
|
|
+ asSubtypeName = CppPrimitiveTypeToAS(cppSubtypeName);
|
|
|
+ }
|
|
|
+ catch (...)
|
|
|
+ {
|
|
|
+ asSubtypeName = cppSubtypeName;
|
|
|
+ }
|
|
|
+
|
|
|
+ result.asDeclaration_ = "Array<" + asSubtypeName + ">@";
|
|
|
+ result.newCppDeclaration_ = "CScriptArray*";
|
|
|
+ result.glue_ = "return VectorToArray(result, \"Array<" + asSubtypeName + ">\");\n";
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
- if (cppTypeName == "Vector<String>" && typeAnalyzer.IsConst() && typeAnalyzer.IsReference())
|
|
|
+ // =============================================================================
|
|
|
+
|
|
|
+ if (cppTypeName == "Context" && usage == VariableUsage::FunctionParameter)
|
|
|
+ throw Exception("Context can be used as firs parameter of constructors only");
|
|
|
+
|
|
|
+ if (cppTypeName == "Vector<String>" && type.IsConst() && type.IsReference() && usage == VariableUsage::FunctionParameter)
|
|
|
{
|
|
|
- result->success_ = true;
|
|
|
- result->inputVarName_ = paramAnalyzer.GetDeclname();
|
|
|
- result->convertedVarName_ = result->inputVarName_ + "_conv";
|
|
|
- result->glue_ = " Vector<String> " + result->convertedVarName_ + " = ArrayToVector<String>(" + result->inputVarName_ + ");\n";
|
|
|
- result->cppType_ = "CScriptArray*";
|
|
|
+ string newCppVarName = name + "_conv";
|
|
|
//result->asDecl_ = "String[]&";
|
|
|
- result->asDecl_ = "Array<String>@+";
|
|
|
+ result.asDeclaration_ = "Array<String>@+";
|
|
|
+ result.newCppDeclaration_ = "CScriptArray* " + newCppVarName;
|
|
|
+ result.glue_ = " " + cppTypeName + " " + name + " = ArrayToVector<String>(" + newCppVarName + ");\n";
|
|
|
|
|
|
- string defval = paramAnalyzer.GetDefval();
|
|
|
- if (!defval.empty())
|
|
|
+ if (!defaultValue.empty())
|
|
|
{
|
|
|
- assert(defval == "Vector< String >()");
|
|
|
+ assert(defaultValue == "Vector< String >()");
|
|
|
//result->asDecl_ += " = Array<String>()";
|
|
|
- result->asDecl_ += " = null";
|
|
|
+ result.asDeclaration_ += " = null";
|
|
|
}
|
|
|
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
- smatch match;
|
|
|
regex_match(cppTypeName, match, regex("PODVector<(\\w+)>"));
|
|
|
- if (match.size() == 2 && typeAnalyzer.IsConst() && typeAnalyzer.IsReference())
|
|
|
+ if (match.size() == 2 && type.IsConst() && type.IsReference() && usage == VariableUsage::FunctionParameter)
|
|
|
{
|
|
|
- string cppTypeName = match[1].str();
|
|
|
-
|
|
|
- string asTypeName;
|
|
|
-
|
|
|
+ string cppSubtypeName = match[1].str();
|
|
|
+
|
|
|
+ string asSubtypeName;
|
|
|
+
|
|
|
try
|
|
|
{
|
|
|
- asTypeName = CppFundamentalTypeToAS(cppTypeName);
|
|
|
+ asSubtypeName = CppPrimitiveTypeToAS(cppSubtypeName);
|
|
|
}
|
|
|
catch (...)
|
|
|
{
|
|
|
- asTypeName = cppTypeName;
|
|
|
+ asSubtypeName = cppSubtypeName;
|
|
|
}
|
|
|
|
|
|
- result->success_ = true;
|
|
|
- result->inputVarName_ = paramAnalyzer.GetDeclname();
|
|
|
- result->convertedVarName_ = result->inputVarName_ + "_conv";
|
|
|
- result->glue_ = " PODVector<" + cppTypeName + "> " + result->convertedVarName_ + " = ArrayToPODVector<" + cppTypeName + ">(" + result->inputVarName_ + ");\n";
|
|
|
- result->cppType_ = "CScriptArray*";
|
|
|
- result->asDecl_ = "Array<" + asTypeName + ">@+";
|
|
|
+ string newCppVarName = name + "_conv";
|
|
|
+ result.asDeclaration_ = "Array<" + asSubtypeName + ">@+";
|
|
|
+ result.newCppDeclaration_ = "CScriptArray* " + newCppVarName;
|
|
|
+ result.glue_ = " " + cppTypeName + " " + name + " = ArrayToPODVector<" + cppSubtypeName + ">(" + newCppVarName + ");\n";
|
|
|
|
|
|
- string defval = paramAnalyzer.GetDefval();
|
|
|
- assert(defval.empty()); // TODO: make
|
|
|
+ assert(defaultValue.empty()); // TODO: make
|
|
|
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
regex_match(cppTypeName, match, regex("PODVector<(\\w+)\\*>"));
|
|
|
// TODO check \\w is refcounted
|
|
|
- if (match.size() == 2 && typeAnalyzer.IsConst() && typeAnalyzer.IsReference())
|
|
|
+ if (match.size() == 2 && type.IsConst() && type.IsReference() && usage == VariableUsage::FunctionParameter)
|
|
|
{
|
|
|
- string cppTypeName = match[1].str();
|
|
|
-
|
|
|
- string asTypeName;
|
|
|
+ string cppSubtypeName = match[1].str();
|
|
|
+
|
|
|
+ string asSubtypeName;
|
|
|
|
|
|
try
|
|
|
{
|
|
|
- asTypeName = CppFundamentalTypeToAS(cppTypeName);
|
|
|
+ asSubtypeName = CppPrimitiveTypeToAS(cppSubtypeName);
|
|
|
}
|
|
|
catch (...)
|
|
|
{
|
|
|
- asTypeName = cppTypeName;
|
|
|
+ asSubtypeName = cppSubtypeName;
|
|
|
}
|
|
|
|
|
|
- result->success_ = true;
|
|
|
- result->inputVarName_ = paramAnalyzer.GetDeclname();
|
|
|
- result->convertedVarName_ = result->inputVarName_ + "_conv";
|
|
|
- result->glue_ = " PODVector<" + cppTypeName + "*> " + result->convertedVarName_ + " = ArrayToPODVector<" + cppTypeName + "*>(" + result->inputVarName_ + ");\n";
|
|
|
- result->cppType_ = "CScriptArray*";
|
|
|
- result->asDecl_ = "Array<" + asTypeName + "@>@";
|
|
|
+ string newCppVarName = name + "_conv";
|
|
|
+ result.asDeclaration_ = "Array<" + asSubtypeName + "@>@";
|
|
|
+ result.newCppDeclaration_ = "CScriptArray* " + newCppVarName;
|
|
|
+ result.glue_ = " " + cppTypeName + " " + name + " = ArrayToPODVector<" + cppSubtypeName + "*>(" + newCppVarName + ");\n";
|
|
|
|
|
|
- string defval = paramAnalyzer.GetDefval();
|
|
|
- assert(defval.empty()); // TODO: make
|
|
|
+ assert(defaultValue.empty()); // TODO: make
|
|
|
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
regex_match(cppTypeName, match, regex("Vector<SharedPtr<(\\w+)>>"));
|
|
|
- if (match.size() == 2 && typeAnalyzer.IsConst() && typeAnalyzer.IsReference())
|
|
|
+ if (match.size() == 2 && type.IsConst() && type.IsReference() && usage == VariableUsage::FunctionParameter)
|
|
|
{
|
|
|
- string cppTypeName = match[1].str();
|
|
|
-
|
|
|
- string asTypeName;
|
|
|
+ string cppSubtypeName = match[1].str();
|
|
|
+
|
|
|
+ string asSubtypeName;
|
|
|
|
|
|
try
|
|
|
{
|
|
|
- asTypeName = CppFundamentalTypeToAS(cppTypeName);
|
|
|
+ asSubtypeName = CppPrimitiveTypeToAS(cppSubtypeName);
|
|
|
}
|
|
|
catch (...)
|
|
|
{
|
|
|
- asTypeName = cppTypeName;
|
|
|
+ asSubtypeName = cppSubtypeName;
|
|
|
}
|
|
|
|
|
|
- if (cppTypeName == "WorkItem") // TODO autodetect
|
|
|
- {
|
|
|
- result->errorMessage_ = "TODO";
|
|
|
- return result;
|
|
|
- }
|
|
|
-
|
|
|
- result->success_ = true;
|
|
|
- result->inputVarName_ = paramAnalyzer.GetDeclname();
|
|
|
- result->convertedVarName_ = result->inputVarName_ + "_conv";
|
|
|
- result->glue_ = " Vector<SharedPtr<" + cppTypeName + "> > " + result->convertedVarName_ + " = HandleArrayToVector<" + cppTypeName + ">(" + result->inputVarName_ + ");\n";
|
|
|
- result->cppType_ = "CScriptArray*";
|
|
|
- result->asDecl_ = "Array<" + asTypeName + "@>@+";
|
|
|
+ if (cppSubtypeName == "WorkItem") // TODO autodetect
|
|
|
+ throw Exception("Error: type \"" + type.ToString() + "\" can not automatically bind");
|
|
|
|
|
|
- string defval = paramAnalyzer.GetDefval();
|
|
|
- assert(defval.empty()); // TODO: make
|
|
|
+ string newCppVarName = name + "_conv";
|
|
|
+ result.asDeclaration_ = "Array<" + asSubtypeName + "@>@+";
|
|
|
+ result.newCppDeclaration_ = "CScriptArray* " + newCppVarName;
|
|
|
+ result.glue_ = " " + cppTypeName + " " + name + " = HandleArrayToVector<" + cppSubtypeName + ">(" + newCppVarName + ");\n";
|
|
|
+
|
|
|
+ assert(defaultValue.empty()); // TODO: make
|
|
|
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
+ // =============================================================================
|
|
|
+
|
|
|
+ if (cppTypeName == "Context" && usage == VariableUsage::FunctionReturn)
|
|
|
+ throw Exception("Error: type \"" + type.ToString() + "\" can not be returned");
|
|
|
+
|
|
|
+ if (!IsKnownCppType(cppTypeName))
|
|
|
+ throw Exception("Error: type \"" + type.ToString() + "\" can not automatically bind");
|
|
|
+
|
|
|
+ shared_ptr<ClassAnalyzer> analyzer = FindClassByName(cppTypeName);
|
|
|
+ if (analyzer && analyzer->IsInternal())
|
|
|
+ throw Exception("Error: type \"" + type.ToString() + "\" can not automatically bind bacause internal");
|
|
|
+
|
|
|
+ if (analyzer && Contains(analyzer->GetComment(), "NO_BIND"))
|
|
|
+ throw Exception("Error: type \"" + cppTypeName + "\" can not automatically bind bacause have @nobind mark");
|
|
|
+
|
|
|
+ // analyzer can be null for simple types (int, float) or if type "using VariantVector = Vector<Variant>"
|
|
|
+ // TODO add to type info "IsUsing"
|
|
|
+ // TODO add description to TypeAnalyzer::GetClass()
|
|
|
+
|
|
|
+ if (IsUsing(cppTypeName) && cppTypeName != "VariantMap")
|
|
|
+ throw Exception("Using \"" + cppTypeName + "\" can not automatically bind");
|
|
|
+
|
|
|
+ string asTypeName;
|
|
|
+
|
|
|
try
|
|
|
{
|
|
|
- string asType = CppTypeToAS(typeAnalyzer, false);
|
|
|
- result->asDecl_ = asType;
|
|
|
+ asTypeName = CppPrimitiveTypeToAS(cppTypeName);
|
|
|
}
|
|
|
- catch (const Exception& e)
|
|
|
+ catch (...)
|
|
|
{
|
|
|
- result->errorMessage_ = e.what();
|
|
|
- return result;
|
|
|
+ asTypeName = cppTypeName;
|
|
|
}
|
|
|
|
|
|
- string defval = paramAnalyzer.GetDefval();
|
|
|
- if (!defval.empty())
|
|
|
+ if (asTypeName == "void" && type.IsPointer())
|
|
|
+ throw Exception("Error: type \"void*\" can not automatically bind");
|
|
|
+
|
|
|
+ if (asTypeName.find('<') != string::npos)
|
|
|
+ throw Exception("Error: type \"" + type.ToString() + "\" can not automatically bind");
|
|
|
+
|
|
|
+ if (Contains(type.ToString(), "::"))
|
|
|
+ throw Exception("Error: type \"" + type.ToString() + "\" can not automatically bind bacause internal");
|
|
|
+
|
|
|
+ if (type.IsConst() && type.IsReference() && usage == VariableUsage::FunctionParameter)
|
|
|
{
|
|
|
- defval = CppValueToAS(defval);
|
|
|
- defval = ReplaceAll(defval, "\"", "\\\"");
|
|
|
- result->asDecl_ += " = " + defval;
|
|
|
- }
|
|
|
+ result.asDeclaration_ = "const " + asTypeName + "&in";
|
|
|
|
|
|
- result->success_ = true;
|
|
|
- result->cppType_ = typeAnalyzer.ToString();
|
|
|
- result->inputVarName_ = paramAnalyzer.GetDeclname();
|
|
|
- result->convertedVarName_ = result->inputVarName_;
|
|
|
- return result;
|
|
|
-}
|
|
|
+ if (!defaultValue.empty())
|
|
|
+ {
|
|
|
+ defaultValue = CppValueToAS(defaultValue);
|
|
|
+ defaultValue = ReplaceAll(defaultValue, "\"", "\\\"");
|
|
|
+ result.asDeclaration_ += " = " + defaultValue;
|
|
|
+ }
|
|
|
|
|
|
-shared_ptr<FuncReturnTypeConv> CppFunctionReturnTypeToAS(const TypeAnalyzer& typeAnalyzer)
|
|
|
-{
|
|
|
- shared_ptr<FuncReturnTypeConv> result = make_shared<FuncReturnTypeConv>();
|
|
|
+ //if (!name.empty())
|
|
|
+ // result.asDeclaration_ += result.asDeclaration_ + " " + name;
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
|
|
|
- string cppTypeName = typeAnalyzer.GetNameWithTemplateParams();
|
|
|
+ result.asDeclaration_ = asTypeName;
|
|
|
|
|
|
- if (cppTypeName == "void" && !typeAnalyzer.IsPointer())
|
|
|
+ if (type.IsReference())
|
|
|
{
|
|
|
- result->success_ = true;
|
|
|
- result->asReturnType_ = "void";
|
|
|
- result->glueReturnType_ = "void";
|
|
|
- result->glueReturn_ = "";
|
|
|
- return result;
|
|
|
+ result.asDeclaration_ += "&";
|
|
|
}
|
|
|
-
|
|
|
- if (cppTypeName == "Context")
|
|
|
+ else if (type.IsPointer())
|
|
|
{
|
|
|
- result->errorMessage_ = "Error: type \"" + typeAnalyzer.ToString() + "\" can not be returned";
|
|
|
- return result;
|
|
|
+ shared_ptr<ClassAnalyzer> analyzer = FindClassByName(cppTypeName);
|
|
|
+
|
|
|
+ if (analyzer && (analyzer->IsRefCounted() || Contains(analyzer->GetComment(), "FAKE_REF")))
|
|
|
+ result.asDeclaration_ += "@+";
|
|
|
+ else
|
|
|
+ throw Exception("Error: type \"" + type.ToString() + "\" can not automatically bind");
|
|
|
}
|
|
|
|
|
|
- // Works with both Vector<String> and Vector<String>&
|
|
|
- if ((cppTypeName == "Vector<String>" || cppTypeName == "StringVector") && !typeAnalyzer.IsPointer())
|
|
|
+ if (usage == VariableUsage::FunctionReturn && type.IsConst() && !type.IsPointer())
|
|
|
+ result.asDeclaration_ = "const " + result.asDeclaration_;
|
|
|
+
|
|
|
+ //if (!name.empty())
|
|
|
+ // result.asDeclaration_ += result.asDeclaration_ + " " + name;
|
|
|
+
|
|
|
+ if (!defaultValue.empty())
|
|
|
{
|
|
|
- result->success_ = true;
|
|
|
- result->needWrapper_ = true;
|
|
|
- result->asReturnType_ = "Array<String>@";
|
|
|
- result->glueReturnType_ = "CScriptArray*";
|
|
|
- result->glueReturn_ = "return VectorToArray<String>(result, \"Array<String>\");\n";
|
|
|
- return result;
|
|
|
+ defaultValue = CppValueToAS(defaultValue);
|
|
|
+ defaultValue = ReplaceAll(defaultValue, "\"", "\\\"");
|
|
|
+ result.asDeclaration_ += " = " + defaultValue;
|
|
|
}
|
|
|
|
|
|
- smatch match;
|
|
|
- regex_match(cppTypeName, match, regex("SharedPtr<(\\w+)>"));
|
|
|
- if (match.size() == 2)
|
|
|
- {
|
|
|
- string cppTypeName = match[1].str();
|
|
|
-
|
|
|
- string asTypeName;
|
|
|
+ return result;
|
|
|
+}
|
|
|
|
|
|
- try
|
|
|
- {
|
|
|
- asTypeName = CppFundamentalTypeToAS(cppTypeName);
|
|
|
- }
|
|
|
- catch (...)
|
|
|
- {
|
|
|
- asTypeName = cppTypeName;
|
|
|
- }
|
|
|
+string CppTypeToAS(const TypeAnalyzer& type, TypeUsage typeUsage)
|
|
|
+{
|
|
|
+ if (type.IsRvalueReference() || type.IsDoublePointer() || type.IsRefToPointer())
|
|
|
+ throw Exception("Error: type \"" + type.ToString() + "\" can not automatically bind");
|
|
|
|
|
|
- if (cppTypeName == "WorkItem") // TODO autodetect
|
|
|
- {
|
|
|
- result->errorMessage_ = "TODO";
|
|
|
- return result;
|
|
|
- }
|
|
|
+ string cppTypeName = type.GetNameWithTemplateParams();
|
|
|
|
|
|
- result->success_ = true;
|
|
|
- result->needWrapper_ = true;
|
|
|
- result->asReturnType_ = asTypeName + "@+";
|
|
|
- result->glueReturnType_ = cppTypeName + "*";
|
|
|
- result->glueReturn_ = "return result.Detach();\n";
|
|
|
- return result;
|
|
|
- }
|
|
|
+ if (cppTypeName == "Context" && typeUsage == TypeUsage::FunctionReturn)
|
|
|
+ throw Exception("Error: type \"" + type.ToString() + "\" can not be returned");
|
|
|
|
|
|
- regex_match(cppTypeName, match, regex("Vector<SharedPtr<(\\w+)>>"));
|
|
|
- if (match.size() == 2)
|
|
|
- {
|
|
|
- string cppTypeName = match[1].str();
|
|
|
-
|
|
|
- string asTypeName;
|
|
|
+ if (!IsKnownCppType(type.GetNameWithTemplateParams()))
|
|
|
+ throw Exception("Error: type \"" + type.ToString() + "\" can not automatically bind");
|
|
|
|
|
|
- try
|
|
|
- {
|
|
|
- asTypeName = CppFundamentalTypeToAS(cppTypeName);
|
|
|
- }
|
|
|
- catch (...)
|
|
|
- {
|
|
|
- asTypeName = cppTypeName;
|
|
|
- }
|
|
|
+ shared_ptr<ClassAnalyzer> analyzer = FindClassByName(type.GetNameWithTemplateParams());
|
|
|
+ if (analyzer && analyzer->IsInternal())
|
|
|
+ throw Exception("Error: type \"" + type.ToString() + "\" can not automatically bind bacause internal");
|
|
|
|
|
|
- result->success_ = true;
|
|
|
- result->needWrapper_ = true;
|
|
|
- result->asReturnType_ = "Array<" + asTypeName + "@>@";
|
|
|
- result->glueReturnType_ = "CScriptArray*";
|
|
|
+ if (analyzer && Contains(analyzer->GetComment(), "NO_BIND"))
|
|
|
+ throw Exception("Error: type \"" + cppTypeName + "\" can not automatically bind bacause have @nobind mark");
|
|
|
|
|
|
- // Which variant is correct/better?
|
|
|
-#if 0
|
|
|
- result->glueResult_ = "return VectorToArray<SharedPtr<" + cppTypeName + "> >(result, \"Array<" + cppTypeName + "@>@\");\n";
|
|
|
-#else
|
|
|
- result->glueReturn_ = "return VectorToHandleArray(result, \"Array<" + cppTypeName + "@>\");\n";
|
|
|
-#endif
|
|
|
- return result;
|
|
|
- }
|
|
|
+ // analyzer can be null for simple types (int, float) or if type "using VariantVector = Vector<Variant>"
|
|
|
+ // TODO add to type info "IsUsing"
|
|
|
+ // TODO add description to TypeAnalyzer::GetClass()
|
|
|
|
|
|
- regex_match(cppTypeName, match, regex("PODVector<(\\w+)\\*>"));
|
|
|
- if (match.size() == 2)
|
|
|
+ if (IsUsing(cppTypeName) && cppTypeName != "VariantMap")
|
|
|
+ throw Exception("Using \"" + cppTypeName + "\" can not automatically bind");
|
|
|
+
|
|
|
+ string asTypeName;
|
|
|
+
|
|
|
+ try
|
|
|
{
|
|
|
- string cppTypeName = match[1].str();
|
|
|
-
|
|
|
- string asTypeName;
|
|
|
+ asTypeName = CppPrimitiveTypeToAS(cppTypeName);
|
|
|
+ }
|
|
|
+ catch (...)
|
|
|
+ {
|
|
|
+ asTypeName = cppTypeName;
|
|
|
+ }
|
|
|
|
|
|
- try
|
|
|
- {
|
|
|
- asTypeName = CppFundamentalTypeToAS(cppTypeName);
|
|
|
- }
|
|
|
- catch (...)
|
|
|
- {
|
|
|
- asTypeName = cppTypeName;
|
|
|
- }
|
|
|
+ if (asTypeName == "void" && type.IsPointer())
|
|
|
+ throw Exception("Error: type \"void*\" can not automatically bind");
|
|
|
|
|
|
- result->success_ = true;
|
|
|
- result->needWrapper_ = true;
|
|
|
- result->asReturnType_ = "Array<" + asTypeName + "@>@";
|
|
|
- result->glueReturnType_ = "CScriptArray*";
|
|
|
- result->glueReturn_ = "return VectorToHandleArray(result, \"Array<" + cppTypeName + "@>\");\n";
|
|
|
- return result;
|
|
|
- }
|
|
|
+ if (asTypeName.find('<') != string::npos)
|
|
|
+ throw Exception("Error: type \"" + type.ToString() + "\" can not automatically bind");
|
|
|
|
|
|
- regex_match(cppTypeName, match, regex("PODVector<(\\w+)>"));
|
|
|
- if (match.size() == 2 && (typeAnalyzer.IsConst() == typeAnalyzer.IsReference()))
|
|
|
- {
|
|
|
- string cppTypeName = match[1].str();
|
|
|
-
|
|
|
- string asTypeName;
|
|
|
+ if (Contains(type.ToString(), "::"))
|
|
|
+ throw Exception("Error: type \"" + type.ToString() + "\" can not automatically bind bacause internal");
|
|
|
|
|
|
- try
|
|
|
- {
|
|
|
- asTypeName = CppFundamentalTypeToAS(cppTypeName);
|
|
|
- }
|
|
|
- catch (...)
|
|
|
- {
|
|
|
- asTypeName = cppTypeName;
|
|
|
- }
|
|
|
+ if (type.IsConst() && type.IsReference() && typeUsage == TypeUsage::FunctionParameter)
|
|
|
+ return "const " + asTypeName + "&in";
|
|
|
|
|
|
- result->success_ = true;
|
|
|
- result->needWrapper_ = true;
|
|
|
- result->asReturnType_ = "Array<" + asTypeName + ">@";
|
|
|
- result->glueReturnType_ = "CScriptArray*";
|
|
|
- result->glueReturn_ = "return VectorToArray(result, \"Array<" + asTypeName + ">\");\n";
|
|
|
- return result;
|
|
|
- }
|
|
|
+ string result = asTypeName;
|
|
|
|
|
|
- try
|
|
|
+ if (type.IsReference())
|
|
|
{
|
|
|
- string asType = CppTypeToAS(typeAnalyzer, true);
|
|
|
- result->asReturnType_ = asType;
|
|
|
+ result += "&";
|
|
|
}
|
|
|
- catch (const Exception& e)
|
|
|
+ else if (type.IsPointer())
|
|
|
{
|
|
|
- result->errorMessage_ = e.what();
|
|
|
- return result;
|
|
|
+ shared_ptr<ClassAnalyzer> analyzer = FindClassByName(type.GetNameWithTemplateParams());
|
|
|
+
|
|
|
+ if (analyzer && (analyzer->IsRefCounted() || Contains(analyzer->GetComment(), "FAKE_REF")))
|
|
|
+ result += "@+";
|
|
|
+ else
|
|
|
+ throw Exception("Error: type \"" + type.ToString() + "\" can not automatically bind");
|
|
|
}
|
|
|
|
|
|
- result->success_ = true;
|
|
|
- result->glueReturn_ = "return result;\n";
|
|
|
- result->glueReturnType_ = typeAnalyzer.ToString();
|
|
|
+ if (typeUsage == TypeUsage::FunctionReturn && type.IsConst() && !type.IsPointer())
|
|
|
+ result = "const " + result;
|
|
|
+
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
+string CppValueToAS(const string& cppValue)
|
|
|
+{
|
|
|
+ if (cppValue == "nullptr")
|
|
|
+ return "null";
|
|
|
+
|
|
|
+ if (cppValue == "Variant::emptyVariantMap")
|
|
|
+ return "VariantMap()";
|
|
|
+
|
|
|
+ if (cppValue == "NPOS")
|
|
|
+ return "String::NPOS";
|
|
|
+
|
|
|
+ return cppValue;
|
|
|
+}
|
|
|
+
|
|
|
// =================================================================================
|
|
|
|
|
|
static string GenerateFunctionWrapperName(xml_node memberdef)
|
|
|
@@ -629,18 +636,34 @@ string GenerateWrapperName(const ClassFunctionAnalyzer& functionAnalyzer, bool t
|
|
|
|
|
|
// =================================================================================
|
|
|
|
|
|
-string GenerateWrapper(const GlobalFunctionAnalyzer& functionAnalyzer, vector<shared_ptr<FuncParamConv> >& convertedParams, shared_ptr<FuncReturnTypeConv> convertedReturn)
|
|
|
+string GenerateWrapper(const GlobalFunctionAnalyzer& functionAnalyzer, const vector<ConvertedVariable>& convertedParams, const ConvertedVariable& convertedReturn)
|
|
|
{
|
|
|
string result;
|
|
|
+
|
|
|
+ string glueReturnType;
|
|
|
+
|
|
|
+ if (!convertedReturn.newCppDeclaration_.empty())
|
|
|
+ glueReturnType = convertedReturn.newCppDeclaration_;
|
|
|
+ else
|
|
|
+ glueReturnType = functionAnalyzer.GetReturnType().ToString();
|
|
|
+
|
|
|
+ vector<ParamAnalyzer> params = functionAnalyzer.GetParams();
|
|
|
|
|
|
- result = "static " + convertedReturn->glueReturnType_ + " " + GenerateWrapperName(functionAnalyzer) + "(";
|
|
|
+ result = "static " + glueReturnType + " " + GenerateWrapperName(functionAnalyzer) + "(";
|
|
|
|
|
|
for (size_t i = 0; i < convertedParams.size(); i++)
|
|
|
{
|
|
|
if (i != 0)
|
|
|
result += ", ";
|
|
|
|
|
|
- result += convertedParams[i]->cppType_ + " " + convertedParams[i]->inputVarName_;
|
|
|
+ string paramDecl;
|
|
|
+
|
|
|
+ if (!convertedParams[i].newCppDeclaration_.empty())
|
|
|
+ paramDecl = convertedParams[i].newCppDeclaration_;
|
|
|
+ else
|
|
|
+ paramDecl = params[i].GetType().ToString() + " " + params[i].GetDeclname();
|
|
|
+
|
|
|
+ result += paramDecl;
|
|
|
}
|
|
|
|
|
|
result +=
|
|
|
@@ -648,9 +671,9 @@ string GenerateWrapper(const GlobalFunctionAnalyzer& functionAnalyzer, vector<sh
|
|
|
"{\n";
|
|
|
|
|
|
for (size_t i = 0; i < convertedParams.size(); i++)
|
|
|
- result += convertedParams[i]->glue_;
|
|
|
+ result += convertedParams[i].glue_;
|
|
|
|
|
|
- if (convertedReturn->glueReturnType_ != "void")
|
|
|
+ if (glueReturnType != "void")
|
|
|
result += " " + functionAnalyzer.GetReturnType().ToString() + " result = ";
|
|
|
else
|
|
|
result += " ";
|
|
|
@@ -662,22 +685,31 @@ string GenerateWrapper(const GlobalFunctionAnalyzer& functionAnalyzer, vector<sh
|
|
|
if (i != 0)
|
|
|
result += ", ";
|
|
|
|
|
|
- result += convertedParams[i]->convertedVarName_;
|
|
|
+ result += params[i].GetDeclname();
|
|
|
}
|
|
|
|
|
|
result += ");\n";
|
|
|
|
|
|
- if (convertedReturn->glueReturnType_ != "void")
|
|
|
- result += " " + convertedReturn->glueReturn_;
|
|
|
+ if (!convertedReturn.glue_.empty())
|
|
|
+ result += " " + convertedReturn.glue_;
|
|
|
+ else if (glueReturnType != "void")
|
|
|
+ result += " return result;\n";
|
|
|
|
|
|
result += "}";
|
|
|
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
-string GenerateWrapper(const ClassStaticFunctionAnalyzer& functionAnalyzer, vector<shared_ptr<FuncParamConv> >& convertedParams, shared_ptr<FuncReturnTypeConv> convertedReturn)
|
|
|
+string GenerateWrapper(const ClassStaticFunctionAnalyzer& functionAnalyzer, const vector<ConvertedVariable>& convertedParams, const ConvertedVariable& convertedReturn)
|
|
|
{
|
|
|
string result;
|
|
|
+
|
|
|
+ string glueReturnType;
|
|
|
+
|
|
|
+ if (!convertedReturn.newCppDeclaration_.empty())
|
|
|
+ glueReturnType = convertedReturn.newCppDeclaration_;
|
|
|
+ else
|
|
|
+ glueReturnType = functionAnalyzer.GetReturnType().ToString();
|
|
|
|
|
|
string insideDefine = InsideDefine(functionAnalyzer.GetHeaderFile());
|
|
|
|
|
|
@@ -686,14 +718,23 @@ string GenerateWrapper(const ClassStaticFunctionAnalyzer& functionAnalyzer, vect
|
|
|
|
|
|
result +=
|
|
|
"// " + functionAnalyzer.GetLocation() + "\n"
|
|
|
- "static " + convertedReturn->glueReturnType_ + " " + GenerateWrapperName(functionAnalyzer) + "(";
|
|
|
+ "static " + glueReturnType + " " + GenerateWrapperName(functionAnalyzer) + "(";
|
|
|
+
|
|
|
+ vector<ParamAnalyzer> params = functionAnalyzer.GetParams();
|
|
|
|
|
|
for (size_t i = 0; i < convertedParams.size(); i++)
|
|
|
{
|
|
|
if (i != 0)
|
|
|
result += ", ";
|
|
|
|
|
|
- result += convertedParams[i]->cppType_ + " " + convertedParams[i]->inputVarName_;
|
|
|
+ string paramDecl;
|
|
|
+
|
|
|
+ if (!convertedParams[i].newCppDeclaration_.empty())
|
|
|
+ paramDecl = convertedParams[i].newCppDeclaration_;
|
|
|
+ else
|
|
|
+ paramDecl = params[i].GetType().ToString() + " " + params[i].GetDeclname();
|
|
|
+
|
|
|
+ result += paramDecl;
|
|
|
}
|
|
|
|
|
|
result +=
|
|
|
@@ -701,9 +742,9 @@ string GenerateWrapper(const ClassStaticFunctionAnalyzer& functionAnalyzer, vect
|
|
|
"{\n";
|
|
|
|
|
|
for (size_t i = 0; i < convertedParams.size(); i++)
|
|
|
- result += convertedParams[i]->glue_;
|
|
|
+ result += convertedParams[i].glue_;
|
|
|
|
|
|
- if (convertedReturn->glueReturnType_ != "void")
|
|
|
+ if (glueReturnType != "void")
|
|
|
result += " " + functionAnalyzer.GetReturnType().ToString() + " result = ";
|
|
|
else
|
|
|
result += " ";
|
|
|
@@ -715,13 +756,15 @@ string GenerateWrapper(const ClassStaticFunctionAnalyzer& functionAnalyzer, vect
|
|
|
if (i != 0)
|
|
|
result += ", ";
|
|
|
|
|
|
- result += convertedParams[i]->convertedVarName_;
|
|
|
+ result += params[i].GetDeclname();
|
|
|
}
|
|
|
|
|
|
result += ");\n";
|
|
|
|
|
|
- if (convertedReturn->glueReturnType_ != "void")
|
|
|
- result += " " + convertedReturn->glueReturn_;
|
|
|
+ if (!convertedReturn.glue_.empty())
|
|
|
+ result += " " + convertedReturn.glue_;
|
|
|
+ else if (glueReturnType != "void")
|
|
|
+ result += " return result;\n";
|
|
|
|
|
|
result += "}\n";
|
|
|
|
|
|
@@ -733,7 +776,7 @@ string GenerateWrapper(const ClassStaticFunctionAnalyzer& functionAnalyzer, vect
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
-string GenerateWrapper(const ClassFunctionAnalyzer& functionAnalyzer, bool templateVersion, vector<shared_ptr<FuncParamConv> >& convertedParams, shared_ptr<FuncReturnTypeConv> convertedReturn)
|
|
|
+string GenerateWrapper(const ClassFunctionAnalyzer& functionAnalyzer, bool templateVersion, const vector<ConvertedVariable>& convertedParams, const ConvertedVariable& convertedReturn)
|
|
|
{
|
|
|
string result;
|
|
|
|
|
|
@@ -742,21 +785,41 @@ string GenerateWrapper(const ClassFunctionAnalyzer& functionAnalyzer, bool templ
|
|
|
if (!insideDefine.empty())
|
|
|
result += "#ifdef " + insideDefine + "\n";
|
|
|
|
|
|
+ string glueReturnType;
|
|
|
+
|
|
|
+ if (!convertedReturn.newCppDeclaration_.empty())
|
|
|
+ glueReturnType = convertedReturn.newCppDeclaration_;
|
|
|
+ else
|
|
|
+ glueReturnType = functionAnalyzer.GetReturnType().ToString();
|
|
|
+
|
|
|
result +=
|
|
|
"// " + functionAnalyzer.GetLocation() + "\n"
|
|
|
- "static " + convertedReturn->glueReturnType_ + " " + GenerateWrapperName(functionAnalyzer, templateVersion) + "(" + functionAnalyzer.GetClassName() + "* ptr";
|
|
|
+ "static " + glueReturnType + " " + GenerateWrapperName(functionAnalyzer, templateVersion) + "(" + functionAnalyzer.GetClassName() + "* ptr";
|
|
|
+
|
|
|
+ vector<ParamAnalyzer> params = functionAnalyzer.GetParams();
|
|
|
|
|
|
for (size_t i = 0; i < convertedParams.size(); i++)
|
|
|
- result += ", " + convertedParams[i]->cppType_ + " " + convertedParams[i]->inputVarName_;
|
|
|
+ {
|
|
|
+ result += ", ";
|
|
|
+
|
|
|
+ string paramDecl;
|
|
|
+
|
|
|
+ if (!convertedParams[i].newCppDeclaration_.empty())
|
|
|
+ paramDecl = convertedParams[i].newCppDeclaration_;
|
|
|
+ else
|
|
|
+ paramDecl = params[i].GetType().ToString() + " " + params[i].GetDeclname();
|
|
|
+
|
|
|
+ result += paramDecl;
|
|
|
+ }
|
|
|
|
|
|
result +=
|
|
|
")\n"
|
|
|
"{\n";
|
|
|
|
|
|
for (size_t i = 0; i < convertedParams.size(); i++)
|
|
|
- result += convertedParams[i]->glue_;
|
|
|
+ result += convertedParams[i].glue_;
|
|
|
|
|
|
- if (convertedReturn->glueReturnType_ != "void")
|
|
|
+ if (glueReturnType != "void")
|
|
|
result += " " + functionAnalyzer.GetReturnType().ToString() + " result = ";
|
|
|
else
|
|
|
result += " ";
|
|
|
@@ -768,13 +831,15 @@ string GenerateWrapper(const ClassFunctionAnalyzer& functionAnalyzer, bool templ
|
|
|
if (i != 0)
|
|
|
result += ", ";
|
|
|
|
|
|
- result += convertedParams[i]->convertedVarName_;
|
|
|
+ result += params[i].GetDeclname();
|
|
|
}
|
|
|
|
|
|
result += ");\n";
|
|
|
|
|
|
- if (convertedReturn->glueReturnType_ != "void")
|
|
|
- result += " " + convertedReturn->glueReturn_;
|
|
|
+ if (!convertedReturn.glue_.empty())
|
|
|
+ result += " " + convertedReturn.glue_;
|
|
|
+ else if (glueReturnType != "void")
|
|
|
+ result += " return result;\n";
|
|
|
|
|
|
result += "}\n";
|
|
|
|