diff --git a/autogen/CMakeLists.txt b/autogen/CMakeLists.txt index 4d1c2d2..6f4ed9f 100644 --- a/autogen/CMakeLists.txt +++ b/autogen/CMakeLists.txt @@ -4,8 +4,8 @@ find_package(Clang REQUIRED) add_executable(autogen src/main.cpp src/util.cpp - src/analysis.cpp - src/codegen.cpp + src/object/analysis.cpp + src/object/codegen.cpp ) set_target_properties(autogen PROPERTIES OUTPUT_NAME "autogen") diff --git a/autogen/src/main.cpp b/autogen/src/main.cpp index a157312..fad9c2a 100644 --- a/autogen/src/main.cpp +++ b/autogen/src/main.cpp @@ -6,26 +6,23 @@ #include #include #include -#include "analysis.h" -#include "codegen.h" + +#include "object/analysis.h" +#include "object/codegen.h" + +// namespace data { +// #include "data/analysis.h" +// #include "data/codegen.h" +// } namespace fs = std::filesystem; -int main(int argc, char** argv) { - if (argc < 4) { - fprintf(stderr, "Usage: autogen \n"); - return 1; - } - - AnalysisState state; - - fs::path srcRoot = argv[1]; - fs::path srcPath = argv[2]; - fs::path outPath = argv[3]; +int processObject(fs::path srcRoot, fs::path srcPath, fs::path outPath) { + object::AnalysisState state; fs::path relpath = fs::relative(srcPath, srcRoot); printf("[AUTOGEN] Processing file %s...\n", relpath.c_str()); - analyzeClasses(srcPath, srcRoot, &state); + object::analyzeClasses(srcPath, srcRoot, &state); fs::create_directories(outPath.parent_path()); // Make sure generated dir exists before we try writing to it @@ -33,10 +30,32 @@ int main(int argc, char** argv) { std::ofstream outStream(outPath); for (auto& [_, clazz] : state.classes) { - writeCodeForClass(outStream, clazz); + object::writeCodeForClass(outStream, clazz); } outStream.close(); return 0; +} + +int main(int argc, char** argv) { + if (argc < 4) { + fprintf(stderr, "Usage: autogen \n"); + return 1; + } + + std::string codeType = argv[1]; + + 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; } \ No newline at end of file diff --git a/autogen/src/analysis.cpp b/autogen/src/object/analysis.cpp similarity index 65% rename from autogen/src/analysis.cpp rename to autogen/src/object/analysis.cpp index ed24692..5415e5a 100644 --- a/autogen/src/analysis.cpp +++ b/autogen/src/object/analysis.cpp @@ -1,5 +1,5 @@ #include "analysis.h" -#include "util.h" +#include "../util.h" #include #include #include @@ -7,77 +7,11 @@ #include #include +using namespace object; + namespace fs = std::filesystem; -// Very simple parser -// Example format: -// name="Hello!", world=Test, read_only -// Result: -// "name": "Hello!", "world": "Test", "read_only": "" -std::map parseAnnotationString(std::string src) { - std::map result; - - std::string currentIdent = ""; - std::string currentValue = ""; - int stage = 0; - bool quoted = false; - - int i = 0; - for (; i < src.length(); i++) { - if (src[i] == ' ' && (stage != 2 || !quoted)) continue; // Ignore spaces if not in stage 2 and quoted - if (src[i] == ',' && stage == 0) continue; // Let empty commas slip by - if (stage < 2 && (src[i] >= 'a' && src[i] <= 'z' || src[i] >= 'A' && src[i] <= 'Z' || src[i] >= '0' && src[i] <= '9' || src[i] == '_')) { - currentIdent += src[i]; - stage = 1; - continue; - } - if (stage == 1 && src[i] == '=') { // What follows is a value - stage = 2; - continue; - } - if (stage == 1 && src[i] == ',') { // Value-less key - stage = 0; - result[currentIdent] = ""; - currentIdent = ""; - continue; - } - if (stage == 2 && quoted && src[i] == '"') { // Close a quoted string - quoted = false; - continue; - } - if (stage == 2 && !quoted && src[i] == '"') { // Start a quoted string - quoted = true; - continue; - } - if (stage == 2 && !quoted && (src[i] == ' ' || src[i] == ',')) { // Terminate the string - stage = 0; - result[currentIdent] = currentValue; - currentIdent = ""; - currentValue = ""; - continue; - } - if (stage == 2) { // Otherwise if in stage 2, simply add the character - currentValue += src[i]; - continue; - } - fprintf(stderr, "Unexpected symbol: %c at index %d\n", src[i], i); - fprintf(stderr, "\t%s\n", src.c_str()); - fprintf(stderr, "\t%s^\n", i > 0 ? std::string(i, '~').c_str() : ""); - abort(); - } - - // Add the remaining value - if (stage == 1) { - result[currentIdent] = ""; - } else if (stage == 2) { - result[currentIdent] = currentValue; - } - - return result; -} - - -bool findInstanceAnnotation(CXCursor cur) { +static bool findInstanceAnnotation(CXCursor cur) { bool found = false; // Look for annotations in the class itself x_clang_visitChildren(cur, [&](CXCursor cur, CXCursor parent) { @@ -93,38 +27,7 @@ bool findInstanceAnnotation(CXCursor cur) { return found; } -std::optional findAnnotation(CXCursor cur, std::string annotationName) { - std::optional ret = std::nullopt; - - x_clang_visitChildren(cur, [&](CXCursor cur, CXCursor parent) { - CXCursorKind kind = clang_getCursorKind(cur); - if (kind != CXCursor_AnnotateAttr) return CXChildVisit_Continue; - - std::string annString = x_clang_toString(clang_getCursorDisplayName(cur)); - if (annString != annotationName) return CXChildVisit_Continue; - - // Look inside for a StringLiteral - - x_clang_visitChildren(cur, [&](CXCursor cur, CXCursor parent) { - CXCursorKind kind = clang_getCursorKind(cur); - // if (kind != CXCursor_StringLiteral) return CXChildVisit_Recurse; - // String literals cannot be parsed as CXCursor_StringLiteral. I don't know why. - if (kind != CXCursor_UnexposedExpr) return CXChildVisit_Recurse; - - // https://stackoverflow.com/a/63859988/16255372 - auto res = clang_Cursor_Evaluate(cur); - ret = clang_EvalResult_getAsStr(res); - clang_EvalResult_dispose(res); - - return CXChildVisit_Break; - }); - - return CXChildVisit_Break; - }); - return ret; -} - -std::string findBaseClass(CXCursor cur) { +static std::string findBaseClass(CXCursor cur) { std::string baseClass = ""; x_clang_visitChildren(cur, [&](CXCursor cur, CXCursor parent) { CXCursorKind kind = clang_getCursorKind(cur); @@ -137,8 +40,8 @@ std::string findBaseClass(CXCursor cur) { return baseClass; } -std::string currentCategory = ""; -void processField(CXCursor cur, ClassAnalysis* state) { +static std::string currentCategory = ""; +static void processField(CXCursor cur, ClassAnalysis* state) { std::optional propertyDef = findAnnotation(cur, "OB::def_prop"); if (!propertyDef) return; @@ -216,7 +119,7 @@ void processField(CXCursor cur, ClassAnalysis* state) { }; } -void processClass(CXCursor cur, AnalysisState* state, std::string className, std::string srcRoot) { +static void processClass(CXCursor cur, AnalysisState* state, std::string className, std::string srcRoot) { ClassAnalysis anly; // Find base class @@ -264,7 +167,7 @@ void processClass(CXCursor cur, AnalysisState* state, std::string className, std } // https://clang.llvm.org/docs/LibClang.html -bool analyzeClasses(std::string path, std::string srcRoot, AnalysisState* state) { +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 diff --git a/autogen/src/analysis.h b/autogen/src/object/analysis.h similarity index 97% rename from autogen/src/analysis.h rename to autogen/src/object/analysis.h index 2bc778c..aa40aaa 100644 --- a/autogen/src/analysis.h +++ b/autogen/src/object/analysis.h @@ -4,6 +4,8 @@ #include #include +namespace object { + enum ClassFlags { ClassFlag_NotCreatable = 1<<0, ClassFlag_Service = 1<<1, @@ -56,4 +58,6 @@ struct AnalysisState { std::map classes; }; -bool analyzeClasses(std::string path, std::string srcRoot, AnalysisState* state); \ No newline at end of file +bool analyzeClasses(std::string path, std::string srcRoot, AnalysisState* state); + +} \ No newline at end of file diff --git a/autogen/src/codegen.cpp b/autogen/src/object/codegen.cpp similarity index 91% rename from autogen/src/codegen.cpp rename to autogen/src/object/codegen.cpp index 30cb388..0d5e36b 100644 --- a/autogen/src/codegen.cpp +++ b/autogen/src/object/codegen.cpp @@ -4,7 +4,9 @@ #include #include -std::map CATEGORY_STR = { +using namespace object; + +static std::map CATEGORY_STR = { { "APPEARANCE", "PROP_CATEGORY_APPEARENCE" }, { "DATA", "PROP_CATEGORY_DATA" }, { "BEHAVIOR", "PROP_CATEGORY_BEHAVIOR" }, @@ -13,7 +15,7 @@ std::map CATEGORY_STR = { { "SURFACE_INPUT", "PROP_CATEGORY_SURFACE_INPUT" }, }; -std::map MAPPED_TYPE = { +static std::map MAPPED_TYPE = { { "bool", "Data::Bool" }, { "int", "Data::Int" }, { "float", "Data::Float" }, @@ -21,11 +23,11 @@ std::map MAPPED_TYPE = { { "glm::vec3", "Vector3" }, }; -std::map ENUM_TYPES = { +static std::map ENUM_TYPES = { { "SurfaceType", std::monostate() } }; -std::string parseWeakPtr(std::string weakPtrType) { +static std::string parseWeakPtr(std::string weakPtrType) { if (!weakPtrType.starts_with("std::weak_ptr")) return ""; int pos0 = weakPtrType.find("<"); @@ -35,7 +37,7 @@ std::string parseWeakPtr(std::string weakPtrType) { return subtype; } -std::string castFromVariant(std::string valueStr, std::string fieldType) { +static std::string castFromVariant(std::string valueStr, std::string fieldType) { // Manual exception for now, enums will get their own system eventually if (fieldType == "SurfaceType") { return "(SurfaceType)(int)" + valueStr + ".get()"; @@ -45,7 +47,7 @@ std::string castFromVariant(std::string valueStr, std::string fieldType) { return valueStr + ".get<" + (!mappedType.empty() ? mappedType : fieldType) + ">()"; } -std::string castToVariant(std::string valueStr, std::string fieldType) { +static std::string castToVariant(std::string valueStr, std::string fieldType) { // Manual exception for now, enums will get their own system eventually if (fieldType == "SurfaceType") { return "Data::Int((int)" + valueStr + ")"; @@ -64,7 +66,7 @@ std::string castToVariant(std::string valueStr, std::string fieldType) { return valueStr; } -void writePropertySetHandler(std::ofstream& out, ClassAnalysis state) { +static void writePropertySetHandler(std::ofstream& out, ClassAnalysis state) { out << "fallible " << state.name << "::InternalSetPropertyValue(std::string name, Data::Variant value) {"; out << "\n "; @@ -105,7 +107,7 @@ void writePropertySetHandler(std::ofstream& out, ClassAnalysis state) { out << "\n};\n\n"; } -void writePropertyUpdateHandler(std::ofstream& out, ClassAnalysis state) { +static void writePropertyUpdateHandler(std::ofstream& out, ClassAnalysis state) { out << "void " << state.name << "::InternalUpdateProperty(std::string name) {"; out << "\n "; @@ -125,7 +127,7 @@ void writePropertyUpdateHandler(std::ofstream& out, ClassAnalysis state) { out << "\n};\n\n"; } -void writePropertyGetHandler(std::ofstream& out, ClassAnalysis state) { +static void writePropertyGetHandler(std::ofstream& out, ClassAnalysis state) { out << "result " << state.name << "::InternalGetPropertyValue(std::string name) {"; out << "\n "; @@ -151,7 +153,7 @@ void writePropertyGetHandler(std::ofstream& out, ClassAnalysis state) { out << "\n};\n\n"; } -void writePropertiesList(std::ofstream& out, ClassAnalysis state) { +static void writePropertiesList(std::ofstream& out, ClassAnalysis state) { out << "std::vector " << state.name << "::InternalGetProperties() {\n"; out << " std::vector properties = " << state.baseClass << "::InternalGetProperties();\n"; @@ -164,7 +166,7 @@ void writePropertiesList(std::ofstream& out, ClassAnalysis state) { out << "};\n\n"; } -void writePropertyMetaHandler(std::ofstream& out, ClassAnalysis state) { +static void writePropertyMetaHandler(std::ofstream& out, ClassAnalysis state) { out << "result " << state.name << "::InternalGetPropertyMeta(std::string name) {"; out << "\n "; @@ -204,7 +206,7 @@ void writePropertyMetaHandler(std::ofstream& out, ClassAnalysis state) { out << "\n};\n\n"; } -void writeCodeForClass(std::ofstream& out, ClassAnalysis& state) { +void object::writeCodeForClass(std::ofstream& out, ClassAnalysis& state) { std::string strFlags; if (state.flags & ClassFlag_NotCreatable) strFlags += " | INSTANCE_NOTCREATABLE"; diff --git a/autogen/src/codegen.h b/autogen/src/object/codegen.h similarity index 83% rename from autogen/src/codegen.h rename to autogen/src/object/codegen.h index c3a155b..49c20d0 100644 --- a/autogen/src/codegen.h +++ b/autogen/src/object/codegen.h @@ -3,4 +3,8 @@ #include "analysis.h" #include -void writeCodeForClass(std::ofstream& out, ClassAnalysis& state); \ No newline at end of file +namespace object { + +void writeCodeForClass(std::ofstream& out, ClassAnalysis& state); + +} \ No newline at end of file diff --git a/autogen/src/util.cpp b/autogen/src/util.cpp index 300a1d6..41cc3ac 100644 --- a/autogen/src/util.cpp +++ b/autogen/src/util.cpp @@ -1,5 +1,6 @@ #include "util.h" #include +#include static CXChildVisitResult _visitorFunc(CXCursor cursor, CXCursor parent, CXClientData client_data) { X_CXCursorVisitor* func = (X_CXCursorVisitor*)client_data; @@ -14,4 +15,97 @@ std::string x_clang_toString(CXString string) { std::string str(clang_getCString(string)); clang_disposeString(string); return str; +} + +std::map parseAnnotationString(std::string src) { + std::map result; + + std::string currentIdent = ""; + std::string currentValue = ""; + int stage = 0; + bool quoted = false; + + int i = 0; + for (; i < src.length(); i++) { + if (src[i] == ' ' && (stage != 2 || !quoted)) continue; // Ignore spaces if not in stage 2 and quoted + if (src[i] == ',' && stage == 0) continue; // Let empty commas slip by + if (stage < 2 && (src[i] >= 'a' && src[i] <= 'z' || src[i] >= 'A' && src[i] <= 'Z' || src[i] >= '0' && src[i] <= '9' || src[i] == '_')) { + currentIdent += src[i]; + stage = 1; + continue; + } + if (stage == 1 && src[i] == '=') { // What follows is a value + stage = 2; + continue; + } + if (stage == 1 && src[i] == ',') { // Value-less key + stage = 0; + result[currentIdent] = ""; + currentIdent = ""; + continue; + } + if (stage == 2 && quoted && src[i] == '"') { // Close a quoted string + quoted = false; + continue; + } + if (stage == 2 && !quoted && src[i] == '"') { // Start a quoted string + quoted = true; + continue; + } + if (stage == 2 && !quoted && (src[i] == ' ' || src[i] == ',')) { // Terminate the string + stage = 0; + result[currentIdent] = currentValue; + currentIdent = ""; + currentValue = ""; + continue; + } + if (stage == 2) { // Otherwise if in stage 2, simply add the character + currentValue += src[i]; + continue; + } + fprintf(stderr, "Unexpected symbol: %c at index %d\n", src[i], i); + fprintf(stderr, "\t%s\n", src.c_str()); + fprintf(stderr, "\t%s^\n", i > 0 ? std::string(i, '~').c_str() : ""); + abort(); + } + + // Add the remaining value + if (stage == 1) { + result[currentIdent] = ""; + } else if (stage == 2) { + result[currentIdent] = currentValue; + } + + return result; +} + +std::optional findAnnotation(CXCursor cur, std::string annotationName) { + std::optional ret = std::nullopt; + + x_clang_visitChildren(cur, [&](CXCursor cur, CXCursor parent) { + CXCursorKind kind = clang_getCursorKind(cur); + if (kind != CXCursor_AnnotateAttr) return CXChildVisit_Continue; + + std::string annString = x_clang_toString(clang_getCursorDisplayName(cur)); + if (annString != annotationName) return CXChildVisit_Continue; + + // Look inside for a StringLiteral + + x_clang_visitChildren(cur, [&](CXCursor cur, CXCursor parent) { + CXCursorKind kind = clang_getCursorKind(cur); + // if (kind != CXCursor_StringLiteral) return CXChildVisit_Recurse; + // String literals cannot be parsed as CXCursor_StringLiteral. I don't know why. + if (kind != CXCursor_UnexposedExpr) return CXChildVisit_Recurse; + + // https://stackoverflow.com/a/63859988/16255372 + auto res = clang_Cursor_Evaluate(cur); + ret = clang_EvalResult_getAsStr(res); + clang_EvalResult_dispose(res); + + return CXChildVisit_Break; + }); + + return CXChildVisit_Break; + }); + return ret; } \ No newline at end of file diff --git a/autogen/src/util.h b/autogen/src/util.h index a868880..a95d181 100644 --- a/autogen/src/util.h +++ b/autogen/src/util.h @@ -2,10 +2,21 @@ #include #include +#include +#include #include typedef std::function X_CXCursorVisitor; unsigned x_clang_visitChildren(CXCursor parent, X_CXCursorVisitor visitor); -std::string x_clang_toString(CXString string); \ No newline at end of file +std::string x_clang_toString(CXString string); + +// Very simple parser +// Example format: +// name="Hello!", world=Test, read_only +// Result: +// "name": "Hello!", "world": "Test", "read_only": "" +std::map parseAnnotationString(std::string src); + +std::optional findAnnotation(CXCursor cur, std::string annotationName); \ No newline at end of file diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 503a85a..2fc4aa9 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -16,18 +16,18 @@ find_package(PkgConfig REQUIRED) pkg_check_modules(LUAJIT REQUIRED luajit) # Run autogen -file(GLOB_RECURSE AUTOGEN_SOURCES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/src/objects" "src/objects/*.h") +file(GLOB_RECURSE AUTOGEN_SOURCES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/src" "src/objects/*.h" "src/datatypes/*.h") # https://cmake.org/cmake/help/book/mastering-cmake/chapter/Custom%20Commands.html foreach (SRC ${AUTOGEN_SOURCES}) string(REGEX REPLACE "[.]h$" ".cpp" OUT_SRC_NAME ${SRC}) - set(SRC_PATH "${CMAKE_CURRENT_SOURCE_DIR}/src/objects/${SRC}") + set(SRC_PATH "${CMAKE_CURRENT_SOURCE_DIR}/src/${SRC}") set(OUT_PATH "${CMAKE_BINARY_DIR}/generated/${OUT_SRC_NAME}") add_custom_command( OUTPUT "${OUT_PATH}" DEPENDS "${SRC_PATH}" - COMMAND "${CMAKE_BINARY_DIR}/autogen/autogen" "${CMAKE_CURRENT_SOURCE_DIR}/src" "${SRC_PATH}" "${OUT_PATH}" + COMMAND "${CMAKE_BINARY_DIR}/autogen/autogen" "object" "${CMAKE_CURRENT_SOURCE_DIR}/src" "${SRC_PATH}" "${OUT_PATH}" ) list(APPEND AUTOGEN_OUTS "${OUT_PATH}") diff --git a/core/src/datatypes/annotation.h b/core/src/datatypes/annotation.h new file mode 100644 index 0000000..c89a1e8 --- /dev/null +++ b/core/src/datatypes/annotation.h @@ -0,0 +1,26 @@ +#pragma once + +// Markers for the autogen engine to generate getters, setters, lua, etc. + +// Base macros +#ifdef __AUTOGEN__ +#define def_data(...) clang::annotate("OB::def_data", #__VA_ARGS__) +#define def_data_prop(...) clang::annotate("OB::def_data_prop", #__VA_ARGS__) +#define def_data_method(...) clang::annotate("OB::def_data_method", #__VA_ARGS__) +#else +#define def_data(...) +#define def_data_prop(...) +#define def_data_method(...) +#endif + +// Helper macros +#define DEF_DATA [[ def_data() ]] +#define DEF_DATA_PROP [[ def_data_prop() ]] +#define DEF_DATA_METHOD [[ def_data_method() ]] + +#define AUTOGEN_PREAMBLE_DATA \ +public: \ +virtual const TypeInfo& GetType() const override; \ +static const TypeInfo TYPE; \ +virtual void PushLuaValue(lua_State*) const override; \ +private: