From 2aae16744e9cb1d04c06e4d860d0aba507941252 Mon Sep 17 00:00:00 2001 From: maelstrom Date: Mon, 5 May 2025 10:35:17 +0200 Subject: [PATCH] feat(autogen): added libraries for Vector3, CFrame and Color3 --- autogen/src/data/analysis.cpp | 49 ++++++- autogen/src/data/codegen.cpp | 156 ++++++++++++++++++---- core/src/objects/script/scriptcontext.cpp | 6 + 3 files changed, 179 insertions(+), 32 deletions(-) diff --git a/autogen/src/data/analysis.cpp b/autogen/src/data/analysis.cpp index 5a73d44..8767685 100644 --- a/autogen/src/data/analysis.cpp +++ b/autogen/src/data/analysis.cpp @@ -1,5 +1,6 @@ #include "analysis.h" #include "../util.h" +#include #include #include #include @@ -8,6 +9,34 @@ using namespace data; +static std::string toStaticName(std::string orig) { + bool isSnakeCase = orig.find('_') == -1; + + std::string newName = ""; + int wordStart = 0; + for (char c : orig) { + if (c == '_') { + wordStart = 1; + continue; + } + + if (wordStart == 1) + newName += std::toupper(c); + else if (wordStart == 0) + newName += std::tolower(c); + else if (wordStart == 2) + newName += c; + + if (c >= 'a' && c <= 'z') + wordStart = 2; + else + wordStart = 0; + } + + newName[0] = std::tolower(newName[0]); + return newName; +} + static void processMethod(CXCursor cur, ClassAnalysis* state) { std::optional propertyDef = findAnnotation(cur, "OB::def_data_method"); if (!propertyDef) return; @@ -17,6 +46,8 @@ static void processMethod(CXCursor cur, ClassAnalysis* state) { auto result = parseAnnotationString(propertyDef.value()); std::string symbolName = x_clang_toString(clang_getCursorSpelling(cur)); CXType retType = clang_getCursorResultType(cur); + + bool isStatic = clang_CXXMethod_isStatic(cur); anly.name = result["name"]; anly.functionName = symbolName; @@ -25,7 +56,10 @@ static void processMethod(CXCursor cur, ClassAnalysis* state) { // if name field is not provided, use fieldName instead, but capitalize the first character if (anly.name == "") { anly.name = symbolName; - anly.name[0] = std::toupper(anly.name[0]); + if (!isStatic) + anly.name[0] = std::toupper(anly.name[0]); + else + anly.name[0] = std::tolower(anly.name[0]); } // Populate parameter list @@ -61,6 +95,8 @@ static void processProperty(CXCursor cur, ClassAnalysis* state) { auto result = parseAnnotationString(propertyDef.value()); std::string symbolName = x_clang_toString(clang_getCursorSpelling(cur)); CXType retType = clang_getCursorResultType(cur); + + bool isStatic = clang_getCursorKind(cur) == CXCursor_VarDecl || clang_CXXMethod_isStatic(cur); anly.name = result["name"]; anly.backingSymbol = symbolName; @@ -70,11 +106,14 @@ static void processProperty(CXCursor cur, ClassAnalysis* state) { // if name field is not provided, use fieldName instead, but capitalize the first character if (anly.name == "") { anly.name = symbolName; - anly.name[0] = std::toupper(anly.name[0]); + if (!isStatic) + anly.name[0] = std::toupper(anly.name[0]); + else + anly.name = toStaticName(anly.name); } - // If it's a static method, push it into the library instead - if (clang_CXXMethod_isStatic(cur)) + // If it's a static property, push it into the library instead + if (isStatic) state->staticProperties.push_back(anly); else state->properties.push_back(anly); @@ -96,7 +135,7 @@ static void processClass(CXCursor cur, AnalysisState* state, std::string classNa x_clang_visitChildren(cur, [&](CXCursor cur, CXCursor parent) { CXCursorKind kind = clang_getCursorKind(cur); - if (kind == CXCursor_CXXMethod || kind == CXCursor_FieldDecl) { + if (kind == CXCursor_CXXMethod || kind == CXCursor_FieldDecl || kind == CXCursor_VarDecl) { processProperty(cur, &anly); } diff --git a/autogen/src/data/codegen.cpp b/autogen/src/data/codegen.cpp index c356228..fa60b87 100644 --- a/autogen/src/data/codegen.cpp +++ b/autogen/src/data/codegen.cpp @@ -27,43 +27,21 @@ static std::string getLuaMethodFqn(std::string className, std::string methodName return "__lua_impl__" + className + "__" + methodName; } -static void writeLuaLibraryGenerator(std::ofstream& out, ClassAnalysis& state) { - out << "static int lib_gc(lua_State*);" - << "static int lib_index(lua_State*);" - << "static int lib_newindex(lua_State*);" - << "static const struct luaL_Reg metatable [] = {" - << " {\"__index\", lib_index}," - << " {\"__newindex\", lib_newindex}," - << " {NULL, NULL} /* end of array */" - << "};"; - - // Create push function - out << "void Data::" << state.name << "::PushLuaLibrary(lua_State* L) {\n"; - - out << " int n = lua_gettop(L);\n" - << " lua_newuserdata(L, 0);\n" - << " luaL_newmetatable(L, \"__mt_" << state.name << "\");\n" - << " luaL_register(L, NULL, library_metatable);\n" - << " lua_setmetatable(L, n+1);\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) { +static void writeLuaGetArgument(std::ofstream& out, std::string type, int narg, bool member) { 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"; + out << " " << type << " " << varname << " = " << checkFunc << "(L, " << std::to_string(member ? narg + 1 : narg) << ");\n"; } else { std::string udataname = getMtName(type); - out << " " << type << " " << varname << " = *(" << type << "*)luaL_checkudata(L, " << std::to_string(narg) << ", \"" << udataname << "\");\n"; + out << " " << type << " " << varname << " = *(" << type << "*)luaL_checkudata(L, " << std::to_string(member ? narg + 1 : narg) << ", \"" << udataname << "\");\n"; } } @@ -72,10 +50,15 @@ static void writeLuaMethodImpls(std::ofstream& out, ClassAnalysis& state) { // Collect all method names to account for overloaded functions std::map> methods; + std::map> staticMethods; for (MethodAnalysis method : state.methods) { methods[method.name].push_back(method); } + + for (MethodAnalysis method : state.staticMethods) { + staticMethods[method.name].push_back(method); + } for (auto& [name, methodImpls] : methods) { std::string methodFqn = getLuaMethodFqn(state.name, name); @@ -87,7 +70,7 @@ static void writeLuaMethodImpls(std::ofstream& out, ClassAnalysis& state) { // Currently overloads are not supported for (int i = 0; i < methodImpls[0].parameters.size(); i++) { - writeLuaGetArgument(out, methodImpls[0].parameters[i].type, i); + writeLuaGetArgument(out, methodImpls[0].parameters[i].type, i, true); } // Store result @@ -123,6 +106,51 @@ static void writeLuaMethodImpls(std::ofstream& out, ClassAnalysis& state) { out << "}\n\n"; } + + for (auto& [name, methodImpls] : staticMethods) { + std::string methodFqn = getLuaMethodFqn(state.name, name); + out << "static int " << methodFqn << "(lua_State* L) {\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, false); + } + + // Store result + if (methodImpls[0].returnType != "void") + out << " " << methodImpls[0].returnType << " result = "; + else + out << " "; + + // Call function + out << fqn << "::" << 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) { @@ -233,6 +261,80 @@ static void writeLuaValueGenerator(std::ofstream& out, ClassAnalysis& state) { "}\n\n"; } +static void writeLuaLibraryGenerator(std::ofstream& out, ClassAnalysis& state) { + std::string fqn = "Data::" + state.name; + + out << "static int lib_index(lua_State*);\n" + "static const struct luaL_Reg lib_metatable [] = {\n" + " {\"__index\", lib_index},\n" + " {NULL, NULL} /* end of array */\n" + "};\n\n"; + + out << "void Data::" << state.name << "::PushLuaLibrary(lua_State* L) {\n" + " lua_getglobal(L, \"_G\");\n" + " lua_pushstring(L, \"" << state.name << "\");\n" + "\n" + " lua_newuserdata(L, 0);\n" + "\n" + " // Create the library's metatable\n" + " luaL_newmetatable(L, \"__mt_lib_" << state.name << "\");\n" + " luaL_register(L, NULL, lib_metatable);\n" + " lua_setmetatable(L, -2);\n" + "\n" + " lua_rawset(L, -3);\n" + " lua_pop(L, 1);\n" + "}\n\n"; + + // Indexing methods and properties + + out << "static int lib_index(lua_State* L) {\n" + " std::string key(lua_tostring(L, 2));\n" + " lua_pop(L, 2);\n" + "\n"; + + out << " "; + + bool first = true; + for (PropertyAnalysis prop : state.staticProperties) { + 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 = fqn + "::" + prop.backingSymbol; + else if (prop.backingType == PropertyBackingType::Method) + valueExpr = fqn + "::" + prop.backingSymbol + "()"; + + out << " " << type << "(" << valueExpr << ").PushLuaValue(L);\n"; + out << " return 1;\n"; + + out << " }"; + } + + std::map accountedMethods; + for (MethodAnalysis method : state.staticMethods) { + 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"; +} + void data::writeCodeForClass(std::ofstream& out, std::string headerPath, ClassAnalysis& state) { std::string fqn = "Data::" + state.name; @@ -251,7 +353,7 @@ void data::writeCodeForClass(std::ofstream& out, std::string headerPath, ClassAn << " return TYPE;\n" << "};\n\n"; - // writeLuaLibraryGenerator(out, state); writeLuaMethodImpls(out, state); writeLuaValueGenerator(out, state); + writeLuaLibraryGenerator(out, state); } \ No newline at end of file diff --git a/core/src/objects/script/scriptcontext.cpp b/core/src/objects/script/scriptcontext.cpp index f982b01..aa03220 100644 --- a/core/src/objects/script/scriptcontext.cpp +++ b/core/src/objects/script/scriptcontext.cpp @@ -1,5 +1,7 @@ #include "scriptcontext.h" +#include "datatypes/cframe.h" #include "datatypes/meta.h" +#include "datatypes/vector.h" #include "logger.h" #include #include @@ -41,6 +43,10 @@ void ScriptContext::InitService() { // luaopen_debug(state); luaopen_bit(state); + Data::Vector3::PushLuaLibrary(state); + Data::CFrame::PushLuaLibrary(state); + Data::Color3::PushLuaLibrary(state); + // TODO: custom os library // Override print