diff --git a/autogen/src/data/analysis.cpp b/autogen/src/data/analysis.cpp index 5ba0295..75d3594 100644 --- a/autogen/src/data/analysis.cpp +++ b/autogen/src/data/analysis.cpp @@ -121,6 +121,33 @@ static void processMethod(CXCursor cur, ClassAnalysis* state) { state->methods.push_back(anly); } +static void processOperator(CXCursor cur, ClassAnalysis* state) { + std::optional operatorDef = findAnnotation(cur, "OB::def_data_op"); + if (!operatorDef) return; + + OperatorAnalysis anly; + + std::string symbolName = x_clang_toString(clang_getCursorSpelling(cur)); + if (!symbolName.starts_with("operator")) + return; + + std::string opName = symbolName.substr(8); + + // Special case: Unary minus gets its own type + if (clang_Cursor_getNumArguments(cur) == 0) + opName = "-()"; + + anly.type = opName; + + if (clang_Cursor_getNumArguments(cur) != 0) { + CXCursor arg = clang_Cursor_getArgument(cur, 0); + CXType type = clang_getCursorType(arg); + anly.param_type = x_clang_toString(clang_getTypeSpelling(type)); + } + + state->operators[opName].push_back(anly); +} + // This processes both methods and fields static void processProperty(CXCursor cur, ClassAnalysis* state) { std::optional propertyDef = findAnnotation(cur, "OB::def_data_prop"); @@ -219,6 +246,7 @@ static void processClass(CXCursor cur, AnalysisState* state, std::string classNa if (kind == CXCursor_CXXMethod) { processMethod(cur, &anly); + processOperator(cur, &anly); } return CXChildVisit_Continue; diff --git a/autogen/src/data/analysis.h b/autogen/src/data/analysis.h index 40b7c01..4c8cb9e 100644 --- a/autogen/src/data/analysis.h +++ b/autogen/src/data/analysis.h @@ -19,6 +19,11 @@ struct PropertyAnalysis { std::string valueType; }; +struct OperatorAnalysis { + std::string type; + std::string param_type; +}; + struct MethodParameter { std::string name; std::string type; @@ -43,6 +48,7 @@ struct ClassAnalysis { std::vector methods; std::vector staticProperties; std::vector staticMethods; + std::map> operators; }; struct AnalysisState { diff --git a/autogen/src/data/codegen.cpp b/autogen/src/data/codegen.cpp index 57354fc..5ac150a 100644 --- a/autogen/src/data/codegen.cpp +++ b/autogen/src/data/codegen.cpp @@ -33,6 +33,17 @@ static std::map LUA_PUSH_FUNCS = { // { "std::string", "lua_pushstring" }, }; +static std::map LUA_OP_NAME = { + { "==", "__eq" }, + { "<", "__lt" }, + { "<=", "__le" }, + { "+", "__add" }, + { "-", "__sub" }, + { "-()", "__unm" }, + { "*", "__mul" }, + { "/", "__div" }, +}; + static std::string getLuaMethodFqn(std::string className, std::string methodName) { return "__lua_impl__" + className + "__" + methodName; } @@ -237,14 +248,27 @@ static void writeLuaMethodImpls(std::ofstream& out, ClassAnalysis& state) { static void writeLuaValueGenerator(std::ofstream& out, ClassAnalysis& state) { std::string fqn = state.name; + // Insert additional operators + for (auto& [key, ops] : state.operators) { + std::string opname = LUA_OP_NAME[key]; + out << "static int data_" << state.name << opname << "(lua_State*);\n"; + } + out << "static int data_" << state.name << "_gc(lua_State*);\n" "static int data_" << state.name << "_index(lua_State*);\n" "static int data_" << state.name << "_tostring(lua_State*);\n" "static const struct luaL_Reg " << state.name << "_metatable [] = {\n" " {\"__gc\", data_" << state.name << "_gc},\n" " {\"__index\", data_" << state.name << "_index},\n" - " {\"__tostring\", data_" << state.name << "_tostring},\n" - " {NULL, NULL} /* end of array */\n" + " {\"__tostring\", data_" << state.name << "_tostring},\n"; + + // Insert additional operators + for (auto& [key, ops] : state.operators) { + std::string opname = LUA_OP_NAME[key]; + out << " {\"" + opname + "\", data_" << state.name << opname << "},\n"; + } + + out << " {NULL, NULL} /* end of array */\n" "};\n\n"; out << "void " << state.name << "::PushLuaValue(lua_State* L) const {\n" @@ -337,6 +361,55 @@ static void writeLuaValueGenerator(std::ofstream& out, ClassAnalysis& state) { "}\n\n"; } +static void writeLuaOperatorImpls(std::ofstream& out, ClassAnalysis& state) { + std::string fqn = "" + state.name; + + for (auto& [name, ops] : state.operators) { + out << "static int data_" << state.name << LUA_OP_NAME[name] << "(lua_State* L) {\n" + " " << fqn << "* this_ = *(" << fqn << "**)luaL_checkudata(L, 1, \"__mt_" << state.name << "\");\n" + " int n = lua_gettop(L);\n"; + out << " "; + + // Support multiple overloads of the same function + bool first = true; + for (OperatorAnalysis op : ops) { + if (!first) out << " else "; + first = false; + + // Check to see if the arguments possibly match this implementation's parameter types + out << "if ("; + + // Check number of arguments + out << "n == " << std::to_string(name == "-()" ? 2 : op.param_type == "" ? 1 : 2); // Account for first argument as 'this' + + if (op.param_type != "") { + out << " && "; + writeLuaTestArgument(out, op.param_type, 0, true); + } + + out << ") {\n"; // End if condition, start if body + + if (op.param_type != "") { + writeLuaGetArgument(out, op.param_type, 0, true); + } + + if (name == "-()") { + out << " Variant(-*this_).PushLuaValue(L);\n"; + } else { + out << " Variant(*this_ " << name << " arg0).PushLuaValue(L);\n"; + } + + out << " return 1;\n" + " }"; + } + + // No function implementation matched + out << "\n\n return luaL_error(L, \"Cannot apply '" << name << "' to values of type " << state.name << " and %s \", x_luaL_udatatname(L, 2));\n"; + + out << "}\n\n"; // End function + } +} + static void writeLuaLibraryGenerator(std::ofstream& out, ClassAnalysis& state) { std::string fqn = state.name; @@ -451,5 +524,6 @@ void data::writeCodeForClass(std::ofstream& out, std::string headerPath, ClassAn writeLuaMethodImpls(out, state); writeLuaValueGenerator(out, state); + writeLuaOperatorImpls(out, state); writeLuaLibraryGenerator(out, state); } \ No newline at end of file diff --git a/core/src/datatypes/annotation.h b/core/src/datatypes/annotation.h index bb90a43..c9e0db0 100644 --- a/core/src/datatypes/annotation.h +++ b/core/src/datatypes/annotation.h @@ -8,6 +8,7 @@ #define def_data_prop(...) clang::annotate("OB::def_data_prop", #__VA_ARGS__) #define def_data_method(...) clang::annotate("OB::def_data_method", #__VA_ARGS__) #define def_data_ctor(...) clang::annotate("OB::def_data_ctor", #__VA_ARGS__) +#define def_data_op(...) clang::annotate("OB::def_data_op", #__VA_ARGS__) #else #define def_data(...) #define def_data_prop(...) diff --git a/core/src/datatypes/cframe.cpp b/core/src/datatypes/cframe.cpp index 74c6db9..6126a69 100644 --- a/core/src/datatypes/cframe.cpp +++ b/core/src/datatypes/cframe.cpp @@ -117,6 +117,10 @@ CFrame CFrame::operator -(Vector3 vector) const { return *this + -vector; } +bool CFrame::operator ==(CFrame other) const { + return this->Position() == other.Position() && this->rotation == other.rotation; +} + // Serialization void CFrame::Serialize(pugi::xml_node node) const { diff --git a/core/src/datatypes/cframe.h b/core/src/datatypes/cframe.h index a6d8eda..840678f 100644 --- a/core/src/datatypes/cframe.h +++ b/core/src/datatypes/cframe.h @@ -72,4 +72,6 @@ public: inline CFrame operator *=(CFrame otherFrame) { return *this = *this * otherFrame; } inline CFrame operator +=(Vector3 offset) { return *this = *this + offset; } inline CFrame operator -=(Vector3 offset) { return *this = *this - offset; } + + DEF_DATA_OP bool operator ==(CFrame) const; }; \ No newline at end of file diff --git a/core/src/datatypes/color3.cpp b/core/src/datatypes/color3.cpp index f7e4ef4..a496c9b 100644 --- a/core/src/datatypes/color3.cpp +++ b/core/src/datatypes/color3.cpp @@ -35,6 +35,10 @@ Color3 Color3::FromHex(std::string hex) { return Color3(r, g, b); } +bool Color3::operator ==(Color3 other) const { + return this->r == other.r && this->g == other.g && this->b == other.b; +} + // Serialization void Color3::Serialize(pugi::xml_node node) const { diff --git a/core/src/datatypes/color3.h b/core/src/datatypes/color3.h index 60f5d74..a61feb2 100644 --- a/core/src/datatypes/color3.h +++ b/core/src/datatypes/color3.h @@ -31,4 +31,6 @@ public: DEF_DATA_PROP inline float R() const { return r; } DEF_DATA_PROP inline float G() const { return g; } DEF_DATA_PROP inline float B() const { return b; } + + DEF_DATA_OP bool operator ==(Color3) const; }; \ No newline at end of file diff --git a/core/src/datatypes/enum.cpp b/core/src/datatypes/enum.cpp index 8b69d05..3cd10a1 100644 --- a/core/src/datatypes/enum.cpp +++ b/core/src/datatypes/enum.cpp @@ -40,16 +40,18 @@ EnumItem Enum::FromValueInternal(int value) const { return result.value(); } -EnumItem::EnumItem(_EnumData* parentData, std::string name, int value) : parentData(parentData), name(name), value(value) {} - -// - std::string Enum::ToString() const { return "Enum." + this->data->name; } +bool Enum::operator ==(Enum other) const { + return this->data == other.data; +} + // +EnumItem::EnumItem(_EnumData* parentData, std::string name, int value) : parentData(parentData), name(name), value(value) {} + std::string EnumItem::ToString() const { return "Enum." + parentData->name + "." + name; } @@ -69,4 +71,8 @@ result EnumItem::FromString(std::string string, const auto result = info.enum_->FromName(string); if (result.has_value()) return result.value(); return DataParseError(string, "EnumItem"); +} + +bool EnumItem::operator ==(EnumItem other) const { + return this->parentData == other.parentData && this->value == other.value; } \ No newline at end of file diff --git a/core/src/datatypes/enum.h b/core/src/datatypes/enum.h index 4781a56..752ef24 100644 --- a/core/src/datatypes/enum.h +++ b/core/src/datatypes/enum.h @@ -31,6 +31,8 @@ public: EnumItem FromValueInternal(int) const; + DEF_DATA_OP bool operator ==(Enum) const; + std::string ToString() const; void PushLuaValue(lua_State*) const; static result FromLuaValue(lua_State*, int); @@ -50,6 +52,8 @@ public: inline int Value() const { return this->value; } inline Enum EnumType() const { return Enum(this->parentData); } + DEF_DATA_OP bool operator ==(EnumItem) const; + static result FromString(std::string, const TypeMeta); std::string ToString() const; void Serialize(pugi::xml_node) const; diff --git a/core/src/datatypes/signal.h b/core/src/datatypes/signal.h index 81f1bcd..db50950 100644 --- a/core/src/datatypes/signal.h +++ b/core/src/datatypes/signal.h @@ -117,6 +117,8 @@ public: operator std::weak_ptr(); + DEF_DATA_OP bool operator ==(SignalRef) const; + virtual const std::string ToString() const; virtual void Serialize(pugi::xml_node node) const; virtual void PushLuaValue(lua_State*) const; @@ -134,6 +136,8 @@ public: operator std::weak_ptr(); + DEF_DATA_OP bool operator ==(SignalConnectionRef) const; + virtual const std::string ToString() const; virtual void Serialize(pugi::xml_node node) const; virtual void PushLuaValue(lua_State*) const; diff --git a/core/src/datatypes/vector.cpp b/core/src/datatypes/vector.cpp index e35dee5..9cf60d2 100644 --- a/core/src/datatypes/vector.cpp +++ b/core/src/datatypes/vector.cpp @@ -72,10 +72,18 @@ bool Vector3::operator <(Vector3 other) const { return X() < other.X() && Y() < other.Y() && Z() < other.Z(); } +bool Vector3::operator <=(Vector3 other) const { + return X() <= other.X() && Y() <= other.Y() && Z() <= other.Z(); +} + bool Vector3::operator >(Vector3 other) const { return X() > other.X() && Y() > other.Y() && Z() > other.Z(); } +bool Vector3::operator >=(Vector3 other) const { + return X() >= other.X() && Y() >= other.Y() && Z() >= other.Z(); +} + Vector3 Vector3::Cross(Vector3 other) const { return glm::cross(this->vector, other.vector); } diff --git a/core/src/datatypes/vector.h b/core/src/datatypes/vector.h index 7671868..bacfeb8 100644 --- a/core/src/datatypes/vector.h +++ b/core/src/datatypes/vector.h @@ -55,8 +55,9 @@ public: DEF_DATA_OP Vector3 operator -() const; DEF_DATA_OP bool operator <(Vector3) const; - DEF_DATA_OP bool operator >(Vector3) const; - + DEF_DATA_OP bool operator <=(Vector3) const; + bool operator >(Vector3) const; + bool operator >=(Vector3) const; DEF_DATA_OP bool operator ==(Vector3) const; // Augmented shorthands diff --git a/core/src/lua.h b/core/src/lua.h index 9f7b670..1dfa1de 100644 --- a/core/src/lua.h +++ b/core/src/lua.h @@ -4,4 +4,16 @@ extern "C" { #include #include #include +} + +inline const char* x_luaL_udatatname (lua_State *L, int ud) { + void *p = lua_touserdata(L, ud); + if (p != NULL) { + lua_getmetatable(L, ud); + lua_getfield(L, -1, "__name"); + const char* str = lua_tostring(L, -1); + lua_pop(L, 2); + return str; + } + return NULL; } \ No newline at end of file