feat(autogen): implement methods
This commit is contained in:
parent
879d92f145
commit
49dfeec18c
3 changed files with 204 additions and 18 deletions
|
@ -15,7 +15,7 @@ static void processMethod(CXCursor cur, ClassAnalysis* state) {
|
||||||
MethodAnalysis anly;
|
MethodAnalysis anly;
|
||||||
|
|
||||||
auto result = parseAnnotationString(propertyDef.value());
|
auto result = parseAnnotationString(propertyDef.value());
|
||||||
std::string symbolName = x_clang_toString(clang_getCursorDisplayName(cur));
|
std::string symbolName = x_clang_toString(clang_getCursorSpelling(cur));
|
||||||
CXType retType = clang_getCursorResultType(cur);
|
CXType retType = clang_getCursorResultType(cur);
|
||||||
|
|
||||||
anly.name = result["name"];
|
anly.name = result["name"];
|
||||||
|
@ -59,7 +59,7 @@ static void processProperty(CXCursor cur, ClassAnalysis* state) {
|
||||||
PropertyAnalysis anly;
|
PropertyAnalysis anly;
|
||||||
|
|
||||||
auto result = parseAnnotationString(propertyDef.value());
|
auto result = parseAnnotationString(propertyDef.value());
|
||||||
std::string symbolName = x_clang_toString(clang_getCursorDisplayName(cur));
|
std::string symbolName = x_clang_toString(clang_getCursorSpelling(cur));
|
||||||
CXType retType = clang_getCursorResultType(cur);
|
CXType retType = clang_getCursorResultType(cur);
|
||||||
|
|
||||||
anly.name = result["name"];
|
anly.name = result["name"];
|
||||||
|
|
|
@ -1,13 +1,30 @@
|
||||||
#include "codegen.h"
|
#include "codegen.h"
|
||||||
#include "analysis.h"
|
#include "analysis.h"
|
||||||
|
#include <fstream>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
using namespace data;
|
using namespace data;
|
||||||
|
|
||||||
|
static std::map<std::string, std::string> MAPPED_TYPE = {
|
||||||
|
{ "bool", "Data::Bool" },
|
||||||
|
{ "int", "Data::Int" },
|
||||||
|
{ "float", "Data::Float" },
|
||||||
|
{ "std::string", "Data::String" },
|
||||||
|
{ "glm::vec3", "Vector3" },
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::map<std::string, std::string> LUA_CHECK_FUNCS = {
|
||||||
|
{ "bool", "lua_toboolean" },
|
||||||
|
{ "int", "luaL_checkinteger" },
|
||||||
|
{ "float", "luaL_checknumber" },
|
||||||
|
{ "std::string", "luaL_checkstring" },
|
||||||
|
};
|
||||||
|
|
||||||
static std::string getLuaMethodFqn(std::string className, std::string methodName) {
|
static std::string getLuaMethodFqn(std::string className, std::string methodName) {
|
||||||
return "__lua_impl_" + className + "__" + methodName;
|
return "__lua_impl__" + className + "__" + methodName;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void writeLuaLibraryGenerator(std::ofstream& out, ClassAnalysis& state) {
|
static void writeLuaLibraryGenerator(std::ofstream& out, ClassAnalysis& state) {
|
||||||
|
@ -30,25 +47,193 @@ static void writeLuaLibraryGenerator(std::ofstream& out, ClassAnalysis& state) {
|
||||||
<< " lua_setmetatable(L, n+1);\n";
|
<< " lua_setmetatable(L, n+1);\n";
|
||||||
|
|
||||||
out << "}\n";
|
out << "}\n";
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::string getMtName(std::string type) {
|
||||||
|
if (type.starts_with("Data::"))
|
||||||
|
return "__mt_" + type.substr(6);
|
||||||
|
return "__mt_" + type;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void writeLuaGetArgument(std::ofstream& out, std::string type, int narg) {
|
||||||
|
std::string varname = "arg" + std::to_string(narg);
|
||||||
|
|
||||||
|
std::string checkFunc = LUA_CHECK_FUNCS[type];
|
||||||
|
if (checkFunc != "") {
|
||||||
|
out << " " << type << " " << varname << " = " << checkFunc << "(L, " << std::to_string(narg) << ");\n";
|
||||||
|
} else {
|
||||||
|
std::string udataname = getMtName(type);
|
||||||
|
out << " " << type << " " << varname << " = *(" << type << "*)luaL_checkudata(L, " << std::to_string(narg) << ", \"" << udataname << "\");\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void writeLuaMethodImpls(std::ofstream& out, ClassAnalysis& state) {
|
||||||
|
std::string fqn = "Data::" + state.name;
|
||||||
|
|
||||||
|
// Collect all method names to account for overloaded functions
|
||||||
|
std::map<std::string, std::vector<MethodAnalysis>> methods;
|
||||||
|
|
||||||
|
for (MethodAnalysis method : state.methods) {
|
||||||
|
methods[method.name].push_back(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& [name, methodImpls] : methods) {
|
||||||
|
std::string methodFqn = getLuaMethodFqn(state.name, name);
|
||||||
|
out << "static int " << methodFqn << "(lua_State* L) {\n"
|
||||||
|
" auto this__ = (Data::Base*)lua_touserdata(L, 1);\n"
|
||||||
|
" if (&this__->GetType() != &" << fqn << "::TYPE) return luaL_typerror(L, 0, \"" << state.name << "\");\n"
|
||||||
|
" " << fqn << "* this_ = (" << fqn << "*)this__;\n\n";
|
||||||
|
|
||||||
|
// Currently overloads are not supported
|
||||||
|
|
||||||
|
for (int i = 0; i < methodImpls[0].parameters.size(); i++) {
|
||||||
|
writeLuaGetArgument(out, methodImpls[0].parameters[i].type, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store result
|
||||||
|
if (methodImpls[0].returnType != "void")
|
||||||
|
out << " " << methodImpls[0].returnType << " result = ";
|
||||||
|
else
|
||||||
|
out << " ";
|
||||||
|
|
||||||
|
// Call function
|
||||||
|
out << "this_->" << methodImpls[0].functionName << "(";
|
||||||
|
|
||||||
|
for (int i = 0; i < methodImpls[0].parameters.size(); i++) {
|
||||||
|
std::string varname = "arg" + std::to_string(i);
|
||||||
|
if (i != 0) out << ", ";
|
||||||
|
out << varname;
|
||||||
|
}
|
||||||
|
|
||||||
|
out << ");\n";
|
||||||
|
|
||||||
|
// Return result
|
||||||
|
if (methodImpls[0].returnType != "void") {
|
||||||
|
std::string mappedType = MAPPED_TYPE[methodImpls[0].returnType];
|
||||||
|
if (mappedType == "")
|
||||||
|
out << " result.PushLuaValue(L);\n";
|
||||||
|
else
|
||||||
|
out << " " << mappedType << "(result).PushLuaValue(L);\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (methodImpls[0].returnType == "void")
|
||||||
|
out << " return 0;\n";
|
||||||
|
else
|
||||||
|
out << " return 1;\n";
|
||||||
|
|
||||||
|
out << "}\n\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void writeLuaValueGenerator(std::ofstream& out, ClassAnalysis& state) {
|
||||||
|
std::string fqn = "Data::" + state.name;
|
||||||
|
|
||||||
|
out << "static int data_gc(lua_State*);\n"
|
||||||
|
"static int data_index(lua_State*);\n"
|
||||||
|
"static const struct luaL_Reg metatable [] = {\n"
|
||||||
|
" {\"__gc\", data_gc},\n"
|
||||||
|
" {\"__index\", data_index},\n"
|
||||||
|
" {NULL, NULL} /* end of array */\n"
|
||||||
|
"};\n\n";
|
||||||
|
|
||||||
|
out << "void Data::" << state.name << "::PushLuaValue(lua_State* L) const {\n"
|
||||||
|
" int n = lua_gettop(L);\n"
|
||||||
|
|
||||||
|
// " // I'm torn... should this be Data::Variant, or Data::Base?\n"
|
||||||
|
// " // If I ever decouple typing from Data::Base, I'll switch it to variant,\n"
|
||||||
|
// " // otherwise, it doesn't make much sense to represent it as one\n"
|
||||||
|
" " << fqn << "* userdata = (" << fqn << "*)lua_newuserdata(L, sizeof(" << fqn << "));\n"
|
||||||
|
" new(userdata) " << fqn << "(*this);\n"
|
||||||
|
|
||||||
|
" // Create the library's metatable\n"
|
||||||
|
" luaL_newmetatable(L, \"__mt_" << state.name << "\");\n"
|
||||||
|
" luaL_register(L, NULL, metatable);\n"
|
||||||
|
|
||||||
|
" lua_setmetatable(L, n+1);\n"
|
||||||
|
"}\n\n";
|
||||||
|
|
||||||
|
// Indexing methods and properties
|
||||||
|
|
||||||
|
out << "static int data_index(lua_State* L) {\n"
|
||||||
|
" auto this__ = (Data::Base*)lua_touserdata(L, 1);\n"
|
||||||
|
" if (&this__->GetType() != &" << fqn << "::TYPE) return luaL_typerror(L, 0, \"" << state.name << "\");\n"
|
||||||
|
" " << fqn << "* this_ = (" << fqn << "*)this__;\n"
|
||||||
|
"\n"
|
||||||
|
" std::string key(lua_tostring(L, 2));\n"
|
||||||
|
" lua_pop(L, 2);\n"
|
||||||
|
"\n";
|
||||||
|
|
||||||
|
out << " ";
|
||||||
|
|
||||||
|
bool first = true;
|
||||||
|
for (PropertyAnalysis prop : state.properties) {
|
||||||
|
if (!first) out << " else ";
|
||||||
|
first = false;
|
||||||
|
|
||||||
|
out << "if (key == \"" << prop.name << "\") {\n";
|
||||||
|
|
||||||
|
std::string type = MAPPED_TYPE[prop.valueType];
|
||||||
|
if (type == "") type = prop.valueType;
|
||||||
|
|
||||||
|
std::string valueExpr;
|
||||||
|
if (prop.backingType == PropertyBackingType::Field)
|
||||||
|
valueExpr = "this_->" + prop.backingSymbol;
|
||||||
|
else if (prop.backingType == PropertyBackingType::Method)
|
||||||
|
valueExpr = "this_->" + prop.backingSymbol + "()";
|
||||||
|
|
||||||
|
// This largely depends on the type
|
||||||
|
out << " " << type << "(" << valueExpr << ").PushLuaValue(L);\n";
|
||||||
|
out << " return 1;\n";
|
||||||
|
|
||||||
|
out << " }";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<std::string, bool> accountedMethods;
|
||||||
|
for (MethodAnalysis method : state.methods) {
|
||||||
|
if (accountedMethods[method.name]) continue;
|
||||||
|
if (!first) out << " else ";
|
||||||
|
first = false;
|
||||||
|
accountedMethods[method.name] = true;
|
||||||
|
|
||||||
|
out << "if (key == \"" << method.name << "\") {\n";
|
||||||
|
out << " lua_pushcfunction(L, " << getLuaMethodFqn(state.name, method.name) << ");\n";
|
||||||
|
out << " return 1;\n";
|
||||||
|
|
||||||
|
out << " }";
|
||||||
|
}
|
||||||
|
|
||||||
|
out << "\n\n"
|
||||||
|
" return luaL_error(L, \"%s is not a valid member of %s\\n\", key.c_str(), \"" << state.name << "\");\n"
|
||||||
|
"}\n\n";
|
||||||
|
|
||||||
|
// Destructor
|
||||||
|
|
||||||
|
out << "\nint data_gc(lua_State* L) {\n"
|
||||||
|
" auto this_ = (" << fqn << "*)lua_touserdata(L, 1);\n"
|
||||||
|
" delete this_;\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"}\n\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void data::writeCodeForClass(std::ofstream& out, std::string headerPath, ClassAnalysis& state) {
|
void data::writeCodeForClass(std::ofstream& out, std::string headerPath, ClassAnalysis& state) {
|
||||||
// std::string fqn = "Data::" + state.name;
|
std::string fqn = "Data::" + state.name;
|
||||||
|
|
||||||
// out << "#define __AUTOGEN_EXTRA_INCLUDES__\n";
|
out << "#define __AUTOGEN_EXTRA_INCLUDES__\n";
|
||||||
// out << "#include \"lua.h\"";
|
out << "#include \"" << headerPath << "\"\n\n";
|
||||||
// out << "#include \"" << headerPath << "\"\n\n";
|
out << "#include \"datatypes/meta.h\"\n";
|
||||||
// out << "const Data::TypeInfo " << fqn << "::TYPE = {\n"
|
out << "#include \"lua.h\"\n\n";
|
||||||
// << " .name = \"" << fqn << "\",\n"
|
out << "const Data::TypeInfo " << fqn << "::TYPE = {\n"
|
||||||
// << " .deserializer = &" << fqn << "::Deserialize,\n"
|
<< " .name = \"" << fqn << "\",\n"
|
||||||
// << " .fromLuaValue = %" << fqn << "::FromLuaValue,\n"
|
<< " .deserializer = &" << fqn << "::Deserialize,\n"
|
||||||
// << "};\n\n";
|
<< " .fromLuaValue = &" << fqn << "::FromLuaValue,\n"
|
||||||
|
<< "};\n\n";
|
||||||
|
|
||||||
// out << "const Data::TypeInfo& " << fqn << "::GetType() {\n"
|
out << "const Data::TypeInfo& " << fqn << "::GetType() const {\n"
|
||||||
// << " return &TYPE;\n"
|
<< " return TYPE;\n"
|
||||||
// << "};\n\n";
|
<< "};\n\n";
|
||||||
|
|
||||||
// writeLuaLibraryGenerator(out, state);
|
// writeLuaLibraryGenerator(out, state);
|
||||||
|
writeLuaMethodImpls(out, state);
|
||||||
|
writeLuaValueGenerator(out, state);
|
||||||
}
|
}
|
|
@ -30,7 +30,8 @@ namespace Data {
|
||||||
|
|
||||||
static Data::Variant Deserialize(pugi::xml_node node);
|
static Data::Variant Deserialize(pugi::xml_node node);
|
||||||
static std::optional<Data::Variant> FromString(std::string);
|
static std::optional<Data::Variant> FromString(std::string);
|
||||||
|
|
||||||
|
static result<Data::Variant, LuaCastError> FromLuaValue(lua_State*, int idx);
|
||||||
virtual void PushLuaValue(lua_State*) const override;
|
virtual void PushLuaValue(lua_State*) const override;
|
||||||
static void PushLuaLibrary(lua_State*);
|
static void PushLuaLibrary(lua_State*);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue