]> git.tdb.fi Git - libs/game.git/blobdiff - tools/setupgen/setupgen.cpp
Add a tool for generating setup structs and loaders for them
[libs/game.git] / tools / setupgen / setupgen.cpp
diff --git a/tools/setupgen/setupgen.cpp b/tools/setupgen/setupgen.cpp
new file mode 100644 (file)
index 0000000..9506677
--- /dev/null
@@ -0,0 +1,214 @@
+#include "setupgen.h"
+#include <msp/core/getopt.h>
+#include <msp/core/maputils.h>
+#include <msp/fs/utils.h>
+#include <msp/io/console.h>
+#include <msp/io/print.h>
+#include <msp/strings/utils.h>
+
+using namespace std;
+using namespace Msp;
+
+SetupGen::SetupGen(int argc, char **argv)
+{
+       GetOpt getopt;
+       getopt.add_option('o', "output", out_fn, GetOpt::REQUIRED_ARG);
+       getopt.add_argument("input_file", in_fn, GetOpt::REQUIRED_ARG);
+       getopt(argc, argv);
+}
+
+int SetupGen::main()
+{
+       create_standard_types();
+       load();
+
+       collect_headers();
+
+       if(out_fn.empty())
+       {
+               generate_header(IO::cout);
+               generate_code(IO::cout);
+       }
+       else
+       {
+               string out_base = FS::basename(out_fn);
+               string out_ext = FS::extpart(out_base);
+               if(out_ext==".cpp" || out_ext==".h")
+               {
+                       FS::Path out_dir = FS::dirname(out_fn);
+                       out_base = FS::basepart(out_base);
+                       IO::BufferedFile header_out((out_dir/(out_base+".h")).str(), IO::M_WRITE);
+                       generate_header(header_out);
+                       IO::BufferedFile code_out((out_dir/(out_base+".cpp")).str(), IO::M_WRITE);
+                       IO::print(code_out, "#include \"%s.h\"\n", out_base);
+                       if(!enums.empty())
+                               IO::print(code_out, "#include <msp/strings/format.h>\n");
+                       generate_code(code_out);
+               }
+               else
+               {
+                       IO::BufferedFile out(out_fn, IO::M_WRITE);
+                       generate_header(out);
+                       out.put('\n');
+                       generate_code(out);
+               }
+       }
+
+       return 0;
+}
+
+void SetupGen::create_standard_types()
+{
+       add_type("bool", Type::VALUE);
+       add_type("int", Type::VALUE);
+       add_type("uint", Type::VALUE).set_cpp_type("unsigned");
+       const Type &float_type = add_type("float", Type::VALUE);
+       add_type("string", Type::VALUE).set_cpp_type("std::string", "string");
+       add_type("angle", Type::VALUE).set_cpp_type("Msp::Geometry::Angle<float>", "msp/geometry/angle.h")
+               .set_load("float", "Msp::Geometry::Angle<float>::from_degrees");
+       add_type("vector2", Type::AGGREGATE).set_cpp_type("Msp::LinAl::Vector<float, 2>", "msp/linal/vector.h")
+               .set_elements(float_type, 2);
+       add_type("vector3", Type::AGGREGATE).set_cpp_type("Msp::LinAl::Vector<float, 3>", "msp/linal/vector.h")
+               .set_elements(float_type, 3);
+}
+
+Type &SetupGen::add_type(const string &n, Type::Kind k)
+{
+       auto result = types.try_emplace(n, n, k);
+       if(!result.second)
+               throw key_error(n);
+       return result.first->second;
+}
+
+const Type &SetupGen::get_type(const string &n) const
+{
+       return get_item(types, n);
+}
+
+void SetupGen::load()
+{
+       IO::BufferedFile in_file(in_fn);
+       DataFile::Parser parser(in_file, in_fn);
+       Loader ldr(*this);
+       ldr.load(parser);
+}
+
+void SetupGen::collect_headers()
+{
+       headers.insert("msp/datafile/objectloader.h");
+       headers.insert("msp/strings/lexicalcast.h");
+       for(const unique_ptr<Struct> &s: structs)
+               for(const Struct::Field &f: s->get_fields())
+                       if(const string &h = f.type->get_header(); !h.empty())
+                               headers.insert(h);
+}
+
+void SetupGen::generate_header(IO::Base &out) const
+{
+       string guard = toupper(FS::basepart(FS::basename(out_fn.empty() ? in_fn : out_fn)))+"_H_";
+       if(!name_space.empty())
+       {
+               vector<string> parts = split(name_space, "::");
+               parts.emplace_back(move(guard));
+               guard = join(parts.begin(), parts.end(), "_");
+       }
+       IO::print(out, "#ifndef %s\n", guard);
+       IO::print(out, "#define %s\n", guard);
+
+       if(!headers.empty())
+       {
+               out.put('\n');
+               for(const string &h: headers)
+                       IO::print(out, "#include <%s>\n", h);
+       }
+
+       if(!name_space.empty())
+               IO::print(out, "\nnamespace %s {\n", name_space);
+
+       for(const unique_ptr<Enum> &e: enums)
+       {
+               out.put('\n');
+               e->define_type(out);
+       }
+
+       for(const unique_ptr<Struct> &s: structs)
+       {
+               out.put('\n');
+               s->define_type(out);
+       }
+
+       for(const unique_ptr<Struct> &s: structs)
+       {
+               out.put('\n');
+               s->define_loader(out);
+       }
+
+       for(const unique_ptr<Enum> &e: enums)
+       {
+               out.put('\n');
+               e->declare_conversions(out);
+       }
+
+       if(!name_space.empty())
+               IO::print(out, "\n}  // namespace %s\n", name_space);
+
+       IO::print(out, "\n#endif\n");
+}
+
+void SetupGen::generate_code(IO::Base &out) const
+{
+       if(!name_space.empty())
+               IO::print(out, "\nnamespace %s {\n", name_space);
+
+       for(const unique_ptr<Struct> &s: structs)
+       {
+               out.put('\n');
+               s->define_functions(out);
+       }
+
+       for(const unique_ptr<Enum> &e: enums)
+       {
+               out.put('\n');
+               e->define_conversions(out);
+       }
+
+       if(!name_space.empty())
+               IO::print(out, "\n}  // namespace %s\n", name_space);
+}
+
+
+SetupGen::Loader::Loader(SetupGen &s):
+       ObjectLoader<SetupGen>(s)
+{
+       static ActionMap shared_actions;
+       set_actions(shared_actions);
+}
+
+void SetupGen::Loader::init_actions()
+{
+       add("component", &Loader::struct_def, Struct::COMPONENT);
+       add("entity", &Loader::struct_def, Struct::ENTITY);
+       add("enum", &Loader::enum_def);
+       add("namespace", &Loader::name_space);
+}
+
+void SetupGen::Loader::enum_def(const DataFile::Symbol &n)
+{
+       Enum en(n.name);
+       load_sub(en);
+       Type &type = obj.add_type(n.name, Type::ENUM);
+       type.set_enum(*obj.enums.emplace_back(make_unique<Enum>(move(en))));
+}
+
+void SetupGen::Loader::name_space(const string &ns)
+{
+       obj.name_space = ns;
+}
+
+void SetupGen::Loader::struct_def(Struct::Kind kind, const DataFile::Symbol &n)
+{
+       Struct sct(n.name+"Setup", kind);
+       load_sub(sct, obj);
+       Type &type = obj.add_type(n.name, Type::STRUCT);
+       type.set_struct(*obj.structs.emplace_back(make_unique<Struct>(move(sct))));
+}