feat(autogen): basis for lua-driven datatypes
This commit is contained in:
parent
fc1c4a06f1
commit
879d92f145
18 changed files with 412 additions and 69 deletions
|
@ -6,6 +6,8 @@ add_executable(autogen
|
|||
src/util.cpp
|
||||
src/object/analysis.cpp
|
||||
src/object/codegen.cpp
|
||||
src/data/analysis.cpp
|
||||
src/data/codegen.cpp
|
||||
)
|
||||
|
||||
set_target_properties(autogen PROPERTIES OUTPUT_NAME "autogen")
|
||||
|
|
126
autogen/src/data/analysis.cpp
Normal file
126
autogen/src/data/analysis.cpp
Normal file
|
@ -0,0 +1,126 @@
|
|||
#include "analysis.h"
|
||||
#include "../util.h"
|
||||
#include <clang-c/CXFile.h>
|
||||
#include <clang-c/CXSourceLocation.h>
|
||||
#include <clang-c/Index.h>
|
||||
#include <cstdio>
|
||||
#include <optional>
|
||||
|
||||
using namespace data;
|
||||
|
||||
static void processMethod(CXCursor cur, ClassAnalysis* state) {
|
||||
std::optional<std::string> propertyDef = findAnnotation(cur, "OB::def_data_method");
|
||||
if (!propertyDef) return;
|
||||
|
||||
MethodAnalysis anly;
|
||||
|
||||
auto result = parseAnnotationString(propertyDef.value());
|
||||
std::string symbolName = x_clang_toString(clang_getCursorDisplayName(cur));
|
||||
CXType retType = clang_getCursorResultType(cur);
|
||||
|
||||
anly.name = result["name"];
|
||||
anly.functionName = symbolName;
|
||||
anly.returnType = x_clang_toString(clang_getTypeSpelling(retType));
|
||||
|
||||
// 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]);
|
||||
}
|
||||
|
||||
// Populate parameter list
|
||||
// https://stackoverflow.com/a/45867090/16255372
|
||||
|
||||
for (int i = 0; i < clang_Cursor_getNumArguments(cur); i++) {
|
||||
CXCursor paramCur = clang_Cursor_getArgument(cur, i);
|
||||
|
||||
std::string paramName = x_clang_toString(clang_getCursorDisplayName(paramCur));
|
||||
std::string paramType = x_clang_toString(clang_getTypeSpelling(clang_getCursorType(paramCur)));
|
||||
|
||||
MethodParameter param;
|
||||
param.name = paramName;
|
||||
param.type = paramType;
|
||||
|
||||
anly.parameters.push_back(param);
|
||||
}
|
||||
|
||||
// If it's a static method, push it into the library instead
|
||||
if (clang_CXXMethod_isStatic(cur))
|
||||
state->staticMethods.push_back(anly);
|
||||
else
|
||||
state->methods.push_back(anly);
|
||||
}
|
||||
|
||||
// This processes both methods and fields
|
||||
static void processProperty(CXCursor cur, ClassAnalysis* state) {
|
||||
std::optional<std::string> propertyDef = findAnnotation(cur, "OB::def_data_prop");
|
||||
if (!propertyDef) return;
|
||||
|
||||
PropertyAnalysis anly;
|
||||
|
||||
auto result = parseAnnotationString(propertyDef.value());
|
||||
std::string symbolName = x_clang_toString(clang_getCursorDisplayName(cur));
|
||||
CXType retType = clang_getCursorResultType(cur);
|
||||
|
||||
anly.name = result["name"];
|
||||
anly.backingSymbol = symbolName;
|
||||
anly.valueType = x_clang_toString(clang_getTypeSpelling(retType));
|
||||
anly.backingType = clang_getCursorKind(cur) == CXCursor_CXXMethod ? PropertyBackingType::Method : PropertyBackingType::Field;
|
||||
|
||||
// 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 it's a static method, push it into the library instead
|
||||
if (clang_CXXMethod_isStatic(cur))
|
||||
state->staticProperties.push_back(anly);
|
||||
else
|
||||
state->properties.push_back(anly);
|
||||
}
|
||||
|
||||
static void processClass(CXCursor cur, AnalysisState* state, std::string className, std::string srcRoot) {
|
||||
ClassAnalysis anly;
|
||||
|
||||
anly.name = className;
|
||||
|
||||
x_clang_visitChildren(cur, [&](CXCursor cur, CXCursor parent) {
|
||||
CXCursorKind kind = clang_getCursorKind(cur);
|
||||
|
||||
if (kind == CXCursor_CXXMethod || kind == CXCursor_FieldDecl) {
|
||||
processProperty(cur, &anly);
|
||||
}
|
||||
|
||||
if (kind == CXCursor_CXXMethod) {
|
||||
processMethod(cur, &anly);
|
||||
}
|
||||
|
||||
return CXChildVisit_Continue;
|
||||
});
|
||||
|
||||
state->classes[className] = anly;
|
||||
}
|
||||
|
||||
bool data::analyzeClasses(CXCursor cursor, std::string srcRoot, AnalysisState* state) {
|
||||
// Search for classes
|
||||
x_clang_visitChildren(cursor, [&](CXCursor cur, CXCursor parent) {
|
||||
CXCursorKind kind = clang_getCursorKind(cur);
|
||||
if (kind == CXCursor_Namespace) return CXChildVisit_Recurse;
|
||||
if (kind != CXCursor_ClassDecl) return CXChildVisit_Continue;
|
||||
|
||||
CXSourceLocation loc = clang_getCursorLocation(cur);
|
||||
if (!clang_Location_isFromMainFile(loc)) return CXChildVisit_Continue; // This class is not from this header. Skip
|
||||
|
||||
std::string className = x_clang_toString(clang_getCursorDisplayName(cur));
|
||||
// Forward-decls can slip through the cracks, this prevents that, but also allows us to filter non-instance classes in the src/objects directory
|
||||
if (!findAnnotation(cur, "OB::def_data")) return CXChildVisit_Continue; // Class is not "primary" declaration/is not instance, skip
|
||||
if (state->classes.count(className) > 0) return CXChildVisit_Continue; // Class has already been analyzed, skip...
|
||||
|
||||
processClass(cur, state, className, srcRoot);
|
||||
|
||||
return CXChildVisit_Continue;
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
49
autogen/src/data/analysis.h
Normal file
49
autogen/src/data/analysis.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
#pragma once
|
||||
|
||||
#include <clang-c/Index.h>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace data {
|
||||
|
||||
enum PropertyBackingType {
|
||||
Method,
|
||||
Field
|
||||
};
|
||||
|
||||
struct PropertyAnalysis {
|
||||
std::string name;
|
||||
std::string backingSymbol;
|
||||
PropertyBackingType backingType;
|
||||
std::string valueType;
|
||||
};
|
||||
|
||||
struct MethodParameter {
|
||||
std::string name;
|
||||
std::string type;
|
||||
};
|
||||
|
||||
struct MethodAnalysis {
|
||||
std::string name;
|
||||
std::string functionName;
|
||||
std::string returnType;
|
||||
std::vector<MethodParameter> parameters;
|
||||
};
|
||||
|
||||
struct ClassAnalysis {
|
||||
std::string name;
|
||||
std::string headerPath;
|
||||
std::vector<PropertyAnalysis> properties;
|
||||
std::vector<MethodAnalysis> methods;
|
||||
std::vector<PropertyAnalysis> staticProperties;
|
||||
std::vector<MethodAnalysis> staticMethods;
|
||||
};
|
||||
|
||||
struct AnalysisState {
|
||||
std::map<std::string, ClassAnalysis> classes;
|
||||
};
|
||||
|
||||
bool analyzeClasses(CXCursor cursor, std::string srcRoot, AnalysisState* state);
|
||||
|
||||
}
|
54
autogen/src/data/codegen.cpp
Normal file
54
autogen/src/data/codegen.cpp
Normal file
|
@ -0,0 +1,54 @@
|
|||
#include "codegen.h"
|
||||
#include "analysis.h"
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
|
||||
using namespace data;
|
||||
|
||||
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";
|
||||
|
||||
|
||||
}
|
||||
|
||||
void data::writeCodeForClass(std::ofstream& out, std::string headerPath, ClassAnalysis& state) {
|
||||
// std::string fqn = "Data::" + state.name;
|
||||
|
||||
// out << "#define __AUTOGEN_EXTRA_INCLUDES__\n";
|
||||
// out << "#include \"lua.h\"";
|
||||
// out << "#include \"" << headerPath << "\"\n\n";
|
||||
// out << "const Data::TypeInfo " << fqn << "::TYPE = {\n"
|
||||
// << " .name = \"" << fqn << "\",\n"
|
||||
// << " .deserializer = &" << fqn << "::Deserialize,\n"
|
||||
// << " .fromLuaValue = %" << fqn << "::FromLuaValue,\n"
|
||||
// << "};\n\n";
|
||||
|
||||
// out << "const Data::TypeInfo& " << fqn << "::GetType() {\n"
|
||||
// << " return &TYPE;\n"
|
||||
// << "};\n\n";
|
||||
|
||||
// writeLuaLibraryGenerator(out, state);
|
||||
}
|
10
autogen/src/data/codegen.h
Normal file
10
autogen/src/data/codegen.h
Normal file
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include "analysis.h"
|
||||
#include <fstream>
|
||||
|
||||
namespace data {
|
||||
|
||||
void writeCodeForClass(std::ofstream& out, std::string headerPath, ClassAnalysis& state);
|
||||
|
||||
}
|
|
@ -6,9 +6,10 @@
|
|||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
|
||||
#include "object/analysis.h"
|
||||
#include "object/codegen.h"
|
||||
#include "data/analysis.h"
|
||||
#include "data/codegen.h"
|
||||
|
||||
// namespace data {
|
||||
// #include "data/analysis.h"
|
||||
|
@ -17,20 +18,61 @@
|
|||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
int processObject(fs::path srcRoot, fs::path srcPath, fs::path outPath) {
|
||||
object::AnalysisState state;
|
||||
// https://clang.llvm.org/docs/LibClang.html
|
||||
int processHeader(fs::path srcRoot, fs::path srcPath, fs::path outPath) {
|
||||
const char* cargs[] = { "-x", "c++", "-I", srcRoot.c_str(), "-D__AUTOGEN__", 0 };
|
||||
// THANK YOU SO MUCH THIS STACKOVERFLOW ANSWER IS SO HELPFUL
|
||||
// https://stackoverflow.com/a/59206378/16255372
|
||||
CXIndex index = clang_createIndex(0, 0);
|
||||
CXTranslationUnit unit = clang_parseTranslationUnit(
|
||||
index,
|
||||
srcPath.c_str(), cargs, 5,
|
||||
nullptr, 0,
|
||||
CXTranslationUnit_None);
|
||||
|
||||
if (!unit) {
|
||||
fprintf(stderr, "Failed to parse file\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Print errors
|
||||
int ndiags = clang_getNumDiagnostics(unit);
|
||||
for (int i = 0; i < ndiags; i++) {
|
||||
CXDiagnostic diag = clang_getDiagnostic(unit, i);
|
||||
CXString str = clang_formatDiagnostic(diag, 0);
|
||||
fprintf(stderr, "diag: %s\n", clang_getCString(str));
|
||||
|
||||
clang_disposeString(str);
|
||||
clang_disposeDiagnostic(diag);
|
||||
}
|
||||
|
||||
CXCursor cursor = clang_getTranslationUnitCursor(unit);
|
||||
|
||||
object::AnalysisState objectAnlyState;
|
||||
data::AnalysisState dataAnlyState;
|
||||
|
||||
fs::path relpath = fs::relative(srcPath, srcRoot);
|
||||
printf("[AUTOGEN] Processing file %s...\n", relpath.c_str());
|
||||
object::analyzeClasses(srcPath, srcRoot, &state);
|
||||
object::analyzeClasses(cursor, srcRoot, &objectAnlyState);
|
||||
data::analyzeClasses(cursor, srcRoot, &dataAnlyState);
|
||||
|
||||
fs::create_directories(outPath.parent_path()); // Make sure generated dir exists before we try writing to it
|
||||
|
||||
printf("[AUTOGEN] Generating file %s...\n", relpath.c_str());
|
||||
std::ofstream outStream(outPath);
|
||||
|
||||
for (auto& [_, clazz] : state.classes) {
|
||||
object::writeCodeForClass(outStream, clazz);
|
||||
if (!objectAnlyState.classes.empty() || !dataAnlyState.classes.empty()) {
|
||||
outStream << "/////////////////////////////////////////////////////////////////////////////////////////\n";
|
||||
outStream << "// This file was automatically generated by autogen, and should not be edited manually //\n";
|
||||
outStream << "/////////////////////////////////////////////////////////////////////////////////////////\n\n";
|
||||
}
|
||||
|
||||
for (auto& [_, clazz] : objectAnlyState.classes) {
|
||||
object::writeCodeForClass(outStream, relpath, clazz);
|
||||
}
|
||||
|
||||
for (auto& [_, clazz] : dataAnlyState.classes) {
|
||||
data::writeCodeForClass(outStream, relpath, clazz);
|
||||
}
|
||||
|
||||
outStream.close();
|
||||
|
@ -40,22 +82,13 @@ int processObject(fs::path srcRoot, fs::path srcPath, fs::path outPath) {
|
|||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc < 4) {
|
||||
fprintf(stderr, "Usage: autogen <object|data> <src-root> <src-file> <out-dir>\n");
|
||||
fprintf(stderr, "Usage: autogen <src-root> <src-file> <out-dir>\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string codeType = argv[1];
|
||||
fs::path srcRoot = argv[1];
|
||||
fs::path srcPath = argv[2];
|
||||
fs::path outPath = argv[3];
|
||||
|
||||
fs::path srcRoot = argv[2];
|
||||
fs::path srcPath = argv[3];
|
||||
fs::path outPath = argv[4];
|
||||
|
||||
if (codeType == "object") {
|
||||
return processObject(srcRoot, srcPath, outPath);
|
||||
} else if (codeType == "data") {
|
||||
return processObject(srcRoot, srcPath, outPath);
|
||||
}
|
||||
|
||||
fprintf(stderr, "Unknown class type '%s'\n", codeType.c_str());
|
||||
return -1;
|
||||
return processHeader(srcRoot, srcPath, outPath);
|
||||
}
|
|
@ -167,36 +167,7 @@ static void processClass(CXCursor cur, AnalysisState* state, std::string classNa
|
|||
}
|
||||
|
||||
// https://clang.llvm.org/docs/LibClang.html
|
||||
bool object::analyzeClasses(std::string path, std::string srcRoot, AnalysisState* state) {
|
||||
const char* cargs[] = { "-x", "c++", "-I", srcRoot.c_str(), "-D__AUTOGEN__", 0 };
|
||||
// THANK YOU SO MUCH THIS STACKOVERFLOW ANSWER IS SO HELPFUL
|
||||
// https://stackoverflow.com/a/59206378/16255372
|
||||
CXIndex index = clang_createIndex(0, 0);
|
||||
CXTranslationUnit unit = clang_parseTranslationUnit(
|
||||
index,
|
||||
path.c_str(), cargs, 5,
|
||||
nullptr, 0,
|
||||
CXTranslationUnit_None);
|
||||
|
||||
if (!unit) {
|
||||
fprintf(stderr, "Failed to parse file\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Print errors
|
||||
int ndiags = clang_getNumDiagnostics(unit);
|
||||
for (int i = 0; i < ndiags; i++) {
|
||||
CXDiagnostic diag = clang_getDiagnostic(unit, i);
|
||||
CXString str = clang_formatDiagnostic(diag, 0);
|
||||
fprintf(stderr, "diag: %s\n", clang_getCString(str));
|
||||
|
||||
clang_disposeString(str);
|
||||
clang_disposeDiagnostic(diag);
|
||||
}
|
||||
|
||||
CXCursor cursor = clang_getTranslationUnitCursor(unit);
|
||||
|
||||
bool flag = false;
|
||||
bool object::analyzeClasses(CXCursor cursor, std::string srcRoot, AnalysisState* state) {
|
||||
// Search for classes
|
||||
x_clang_visitChildren(cursor, [&](CXCursor cur, CXCursor parent) {
|
||||
CXCursorKind kind = clang_getCursorKind(cur);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <clang-c/Index.h>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
@ -58,6 +59,6 @@ struct AnalysisState {
|
|||
std::map<std::string, ClassAnalysis> classes;
|
||||
};
|
||||
|
||||
bool analyzeClasses(std::string path, std::string srcRoot, AnalysisState* state);
|
||||
bool analyzeClasses(CXCursor cursor, std::string srcRoot, AnalysisState* state);
|
||||
|
||||
}
|
|
@ -206,7 +206,7 @@ static void writePropertyMetaHandler(std::ofstream& out, ClassAnalysis state) {
|
|||
out << "\n};\n\n";
|
||||
}
|
||||
|
||||
void object::writeCodeForClass(std::ofstream& out, ClassAnalysis& state) {
|
||||
void object::writeCodeForClass(std::ofstream& out, std::string headerPath, ClassAnalysis& state) {
|
||||
std::string strFlags;
|
||||
if (state.flags & ClassFlag_NotCreatable)
|
||||
strFlags += " | INSTANCE_NOTCREATABLE";
|
||||
|
@ -217,10 +217,6 @@ void object::writeCodeForClass(std::ofstream& out, ClassAnalysis& state) {
|
|||
if (!strFlags.empty()) strFlags = strFlags.substr(3); // Remove leading pipe
|
||||
else strFlags = "0"; // 0 == No option
|
||||
|
||||
out << "/////////////////////////////////////////////////////////////////////////////////////////\n";
|
||||
out << "// This file was automatically generated by autogen, and should not be edited manually //\n";
|
||||
out << "/////////////////////////////////////////////////////////////////////////////////////////\n\n";
|
||||
|
||||
std::string constructorStr;
|
||||
if (state.abstract) constructorStr = "nullptr";
|
||||
else constructorStr = "&" + state.name + "::Create";
|
||||
|
|
|
@ -5,6 +5,6 @@
|
|||
|
||||
namespace object {
|
||||
|
||||
void writeCodeForClass(std::ofstream& out, ClassAnalysis& state);
|
||||
void writeCodeForClass(std::ofstream& out, std::string headerPath, ClassAnalysis& state);
|
||||
|
||||
}
|
|
@ -27,7 +27,7 @@ foreach (SRC ${AUTOGEN_SOURCES})
|
|||
add_custom_command(
|
||||
OUTPUT "${OUT_PATH}"
|
||||
DEPENDS "${SRC_PATH}"
|
||||
COMMAND "${CMAKE_BINARY_DIR}/autogen/autogen" "object" "${CMAKE_CURRENT_SOURCE_DIR}/src" "${SRC_PATH}" "${OUT_PATH}"
|
||||
COMMAND "${CMAKE_BINARY_DIR}/autogen/autogen" "${CMAKE_CURRENT_SOURCE_DIR}/src" "${SRC_PATH}" "${OUT_PATH}"
|
||||
)
|
||||
|
||||
list(APPEND AUTOGEN_OUTS "${OUT_PATH}")
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
extern "C" { typedef struct lua_State lua_State; }
|
||||
|
||||
#define DEF_WRAPPER_CLASS(CLASS_NAME, WRAPPED_TYPE) class CLASS_NAME : public Data::Base { \
|
||||
const WRAPPED_TYPE value; \
|
||||
WRAPPED_TYPE value; \
|
||||
public: \
|
||||
CLASS_NAME(WRAPPED_TYPE); \
|
||||
~CLASS_NAME(); \
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
namespace rp = reactphysics3d;
|
||||
|
||||
namespace Data {
|
||||
class DEF_DATA CFrame : Base {
|
||||
class DEF_DATA CFrame : public Base {
|
||||
glm::vec3 translation;
|
||||
glm::mat3 rotation;
|
||||
|
||||
|
@ -38,9 +38,11 @@ namespace Data {
|
|||
|
||||
virtual const Data::String ToString() const override;
|
||||
virtual void Serialize(pugi::xml_node parent) const override;
|
||||
virtual void PushLuaValue(lua_State*) const override;
|
||||
static Data::Variant Deserialize(pugi::xml_node node);
|
||||
|
||||
virtual void PushLuaValue(lua_State*) const override;
|
||||
static void PushLuaLibrary(lua_State*);
|
||||
|
||||
operator glm::mat4() const;
|
||||
operator rp::Transform() const;
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
#include <reactphysics3d/reactphysics3d.h>
|
||||
|
||||
namespace Data {
|
||||
class DEF_DATA Color3 : Base {
|
||||
class DEF_DATA Color3 : public Base {
|
||||
float r;
|
||||
float g;
|
||||
float b;
|
||||
|
@ -25,9 +25,11 @@ namespace Data {
|
|||
virtual const Data::String ToString() const override;
|
||||
DEF_DATA_METHOD std::string ToHex() const;
|
||||
virtual void Serialize(pugi::xml_node node) const override;
|
||||
virtual void PushLuaValue(lua_State*) const override;
|
||||
static Data::Variant Deserialize(pugi::xml_node node);
|
||||
|
||||
virtual void PushLuaValue(lua_State*) const override;
|
||||
static void PushLuaLibrary(lua_State*);
|
||||
|
||||
operator glm::vec3() const;
|
||||
|
||||
DEF_DATA_PROP inline float R() const { return r; }
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
class Instance;
|
||||
|
||||
namespace Data {
|
||||
class InstanceRef : Base {
|
||||
class InstanceRef : public Base {
|
||||
std::weak_ptr<Instance> ref;
|
||||
public:
|
||||
InstanceRef();
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
#include "vector.h"
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <glm/ext/quaternion_geometric.hpp>
|
||||
#include <string>
|
||||
#include "datatypes/base.h"
|
||||
#include "meta.h" // IWYU pragma: keep
|
||||
#include "panic.h"
|
||||
|
||||
Data::Vector3::Vector3() : vector(glm::vec3(0, 0, 0)) {};
|
||||
Data::Vector3::Vector3(const glm::vec3& src) : vector(src) {};
|
||||
|
@ -111,7 +113,98 @@ std::optional<Data::Variant> Data::Vector3::FromString(std::string string) {
|
|||
return Data::Vector3(components[0], components[1], components[2]);
|
||||
}
|
||||
|
||||
// Lua (TEMPORARY)
|
||||
#include "lua.h"
|
||||
|
||||
static int lib_index(lua_State*);
|
||||
static const struct luaL_Reg lib_metatable [] = {
|
||||
{"__index", lib_index},
|
||||
{NULL, NULL} /* end of array */
|
||||
};
|
||||
|
||||
static int lib_index(lua_State* L) {
|
||||
std::string key(lua_tostring(L, 2));
|
||||
lua_pop(L, 2);
|
||||
|
||||
if (key == "test") {
|
||||
Data::String("mabaref").PushLuaValue(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return luaL_error(L, "%s is not a valid member of %s\n", key.c_str(), "Vector3");
|
||||
}
|
||||
|
||||
static int data_index(lua_State*);
|
||||
static const struct luaL_Reg metatable [] = {
|
||||
{"__index", data_index},
|
||||
{NULL, NULL} /* end of array */
|
||||
};
|
||||
|
||||
static int data_index(lua_State* L) {
|
||||
auto this__ = (Data::Base*)lua_touserdata(L, 1);
|
||||
this__->GetType();
|
||||
if (&this__->GetType() != &Vector3::TYPE) return luaL_typerror(L, 0, "Vector3");
|
||||
Vector3* this_ = (Vector3*)this__;
|
||||
|
||||
std::string key(lua_tostring(L, 2));
|
||||
lua_pop(L, 2);
|
||||
|
||||
if (key == "X") {
|
||||
Data::Float(this_->X()).PushLuaValue(L);
|
||||
return 1;
|
||||
} else if (key == "Magnitude") {
|
||||
lua_pushcfunction(L, [](lua_State* L) {
|
||||
auto this__ = (Data::Base*)lua_touserdata(L, 1);
|
||||
if (&this__->GetType() != &Vector3::TYPE) return luaL_typerror(L, 0, "Vector3");
|
||||
Vector3* this_ = (Vector3*)this__;
|
||||
|
||||
Data::Float(this_->Magnitude()).PushLuaValue(L);
|
||||
return 1;
|
||||
});
|
||||
return 1;
|
||||
} else if (key == "Dot") {
|
||||
lua_pushcfunction(L, [](lua_State* L) {
|
||||
auto this__ = (Data::Base*)lua_touserdata(L, 1);
|
||||
if (&this__->GetType() != &Vector3::TYPE) return luaL_typerror(L, 0, "Vector3");
|
||||
Vector3* this_ = (Vector3*)this__;
|
||||
|
||||
auto arg0_ = (Data::Base*)lua_touserdata(L, 2);
|
||||
if (&arg0_->GetType() != &Vector3::TYPE) return luaL_typerror(L, 1, "Vector3");
|
||||
Vector3* arg0 = (Vector3*)arg0_;
|
||||
|
||||
Data::Float(this_->Dot(*arg0)).PushLuaValue(L);
|
||||
return 1;
|
||||
});
|
||||
return 1;
|
||||
}
|
||||
|
||||
return luaL_error(L, "%s is not a valid member of %s\n", key.c_str(), "Vector3");
|
||||
}
|
||||
|
||||
void Data::Vector3::PushLuaLibrary(lua_State* L) {
|
||||
int n = lua_gettop(L);
|
||||
|
||||
lua_newuserdata(L, 0);
|
||||
|
||||
// Create the library's metatable
|
||||
luaL_newmetatable(L, "__mt_lib_Vector3");
|
||||
luaL_register(L, NULL, lib_metatable);
|
||||
|
||||
lua_setmetatable(L, n+1);
|
||||
}
|
||||
|
||||
void Data::Vector3::PushLuaValue(lua_State* L) const {
|
||||
// TODO:
|
||||
panic();
|
||||
int n = lua_gettop(L);
|
||||
|
||||
// I'm torn... should this be Data::Variant, or Data::Base?
|
||||
// If I ever decouple typing from Data::Base, I'll switch it to variant,
|
||||
// otherwise, it doesn't make much sense to represent it as one
|
||||
Vector3* userdata = (Vector3*)lua_newuserdata(L, sizeof(Vector3));
|
||||
new(userdata) Vector3(*this);
|
||||
|
||||
// Create the library's metatable
|
||||
luaL_newmetatable(L, "__mt_Vector3");
|
||||
luaL_register(L, NULL, metatable);
|
||||
|
||||
lua_setmetatable(L, n+1);
|
||||
}
|
|
@ -9,7 +9,7 @@
|
|||
namespace rp = reactphysics3d;
|
||||
|
||||
namespace Data {
|
||||
class DEF_DATA Vector3 : Base {
|
||||
class DEF_DATA Vector3 : public Base {
|
||||
glm::vec3 vector;
|
||||
|
||||
public:
|
||||
|
@ -27,11 +27,13 @@ namespace Data {
|
|||
|
||||
virtual const Data::String ToString() const override;
|
||||
virtual void Serialize(pugi::xml_node node) const override;
|
||||
virtual void PushLuaValue(lua_State*) const override;
|
||||
|
||||
static Data::Variant Deserialize(pugi::xml_node node);
|
||||
static std::optional<Data::Variant> FromString(std::string);
|
||||
|
||||
virtual void PushLuaValue(lua_State*) const override;
|
||||
static void PushLuaLibrary(lua_State*);
|
||||
|
||||
operator glm::vec3() const;
|
||||
operator rp::Vector3() const;
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#include "scriptcontext.h"
|
||||
#include "datatypes/meta.h"
|
||||
#include "logger.h"
|
||||
#include <cstdint>
|
||||
#include <luajit-2.1/lauxlib.h>
|
||||
#include <luajit-2.1/lua.h>
|
||||
#include <luajit-2.1/lualib.h>
|
||||
|
|
Loading…
Add table
Reference in a new issue