/libmspgameview.a
/libmspgameview.so
/mspgame.pc
+/mspgame-setupgen
require "mspcore";
require "mspdatafile";
require "mspmath";
- require "mspgui";
- require "mspgl";
require "sigc++-2.0";
build_info
{
source "source/gameview";
use "mspgame";
+ require "mspgui";
+ require "mspgl";
install true;
install_map
{
map "source" "include/msp";
};
};
+
+ program "mspgame-setupgen"
+ {
+ source "tools/setupgen";
+ install true;
+ };
};
--- /dev/null
+#include "enum.h"
+#include <msp/io/print.h>
+
+using namespace std;
+using namespace Msp;
+
+Enum::Enum(const string &n):
+ name(n)
+{ }
+
+void Enum::define_type(IO::Base &out) const
+{
+ IO::print(out, "enum class %s\n{\n", name);
+ for(auto i=values.begin();; )
+ {
+ IO::print(out, "\t%s", i->name);
+ if(i->value)
+ IO::print(out, " = %d", i->value.value());
+ ++i;
+ if(i==values.end())
+ break;
+ IO::print(out, ",\n");
+ }
+ IO::print(out, "\n};\n");
+}
+
+void Enum::declare_conversions(IO::Base &out) const
+{
+ IO::print(out, "void operator>>(const Msp::LexicalConverter &, %s &);\n", name);
+ IO::print(out, "void operator<<(Msp::LexicalConverter &, %s);\n", name);
+}
+
+void Enum::define_conversions(IO::Base &out) const
+{
+ IO::print(out, "void operator>>(const Msp::LexicalConverter &conv, %s &_e)\n{\n", name);
+ IO::print(out, "\tconst std::string &str = conv.get();\n\t");
+ for(auto i=values.begin(); i!=values.end(); ++i)
+ {
+ if(i!=values.begin())
+ IO::print(out, "else ");
+ IO::print(out, "if(str==\"%s\")\n\t\t_e = %s::%s;\n\t", i->name, name, i->name);
+ }
+ IO::print(out, "else\n\t\tthrow Msp::lexical_error(Msp::format(\"conversion of '%%s' to %s\", str));\n", name);
+ IO::print(out, "}\n");
+
+ IO::print(out, "\nvoid operator<<(Msp::LexicalConverter &conv, %s _e)\n{\n", name);
+ IO::print(out, "\tswitch(_e)\n\t{\n");
+ for(const Value &v: values)
+ IO::print(out, "\tcase %s::%s: conv.result(\"%s\"); break;\n", name, v.name, v.name);
+ IO::print(out, "\tdefault: conv.result(Msp::format(\"%s(%%#x)\", static_cast<int>(_e)));\n", name);
+ IO::print(out, "\t}\n}\n");
+}
+
+
+Enum::Loader::Loader(Enum &e):
+ ObjectLoader<Enum>(e)
+{
+ static ActionMap shared_actions;
+ set_actions(shared_actions);
+}
+
+void Enum::Loader::init_actions()
+{
+ add("value", &Loader::value);
+ add("value", &Loader::value_auto);
+}
+
+void Enum::Loader::value(const DataFile::Symbol &n, int v)
+{
+ obj.values.emplace_back(n.name, v);
+}
+
+void Enum::Loader::value_auto(const DataFile::Symbol &n)
+{
+ obj.values.emplace_back(n.name);
+}
--- /dev/null
+#ifndef ENUM_H_
+#define ENUM_H_
+
+#include <optional>
+#include <string>
+#include <vector>
+#include <msp/datafile/objectloader.h>
+#include <msp/io/base.h>
+
+class Enum
+{
+public:
+ class Loader: public Msp::DataFile::ObjectLoader<Enum>
+ {
+ public:
+ Loader(Enum &);
+
+ private:
+ void init_actions() override;
+
+ void value(const Msp::DataFile::Symbol &, int);
+ void value_auto(const Msp::DataFile::Symbol &);
+ };
+
+ struct Value
+ {
+ std::string name;
+ std::optional<int> value;
+ };
+
+private:
+ std::string name;
+ std::vector<Value> values;
+
+public:
+ Enum(const std::string &);
+
+ void define_type(Msp::IO::Base &) const;
+ void declare_conversions(Msp::IO::Base &) const;
+ void define_conversions(Msp::IO::Base &) const;
+};
+
+#endif
--- /dev/null
+#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))));
+}
--- /dev/null
+#ifndef SETUPGEN_H_
+#define SETUPGEN_H_
+
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <msp/core/application.h>
+#include <msp/datafile/objectloader.h>
+#include "enum.h"
+#include "struct.h"
+#include "type.h"
+
+class SetupGen: public Msp::RegisteredApplication<SetupGen>
+{
+private:
+ class Loader: public Msp::DataFile::ObjectLoader<SetupGen>
+ {
+ public:
+ Loader(SetupGen &);
+
+ private:
+ void init_actions() override;
+
+ void enum_def(const Msp::DataFile::Symbol &);
+ void name_space(const std::string &);
+ void struct_def(Struct::Kind, const Msp::DataFile::Symbol &);
+ };
+
+ std::string in_fn;
+ std::string out_fn;
+ std::string name_space;
+ std::map<std::string, Type> types;
+ std::vector<std::unique_ptr<Enum>> enums;
+ std::vector<std::unique_ptr<Struct>> structs;
+ std::set<std::string> headers;
+
+public:
+ SetupGen(int, char **);
+
+ int main() override;
+private:
+ void create_standard_types();
+ Type &add_type(const std::string &, Type::Kind);
+public:
+ const Type &get_type(const std::string &) const;
+
+private:
+ void load();
+ void collect_headers();
+ void generate_header(Msp::IO::Base &) const;
+ void generate_code(Msp::IO::Base &) const;
+};
+
+#endif
--- /dev/null
+#include "struct.h"
+#include <algorithm>
+#include <msp/core/maputils.h>
+#include <msp/io/print.h>
+#include "setupgen.h"
+
+using namespace std;
+using namespace Msp;
+
+class Struct::Field::Loader: public Msp::DataFile::ObjectLoader<Struct::Field>
+{
+public:
+ Loader(Struct::Field &);
+
+private:
+ void init_actions() override;
+};
+
+
+Struct::Struct(const string &n, Kind k):
+ name(n),
+ kind(k)
+{ }
+
+void Struct::define_type(IO::Base &out) const
+{
+ IO::print(out, "struct %s\n{\n", name);
+ IO::print(out, "\tclass Loader;\n\n");
+
+ for(const Field &f: fields)
+ {
+ IO::print(out, "\t%s %s", f.type->get_cpp_type(), f.name);
+ if(!f.default_value.empty())
+ {
+ if(const string &c = f.type->get_conversion(); !c.empty())
+ IO::print(out, " = %s(%s)", f.type->get_conversion(), f.default_value);
+ else if(f.type->get_kind() == Type::AGGREGATE)
+ IO::print(out, " = { %s }", f.default_value);
+ else if(f.type->get_kind() == Type::ENUM)
+ IO::print(out, " = %s::%s", f.type->get_name(), f.default_value);
+ else
+ IO::print(out, " = %s", f.default_value);
+ }
+ IO::print(out, ";\n");
+ }
+
+ IO::print(out, "};\n");
+}
+
+void Struct::define_loader(IO::Base &out) const
+{
+ IO::print(out, "class %s::Loader: public Msp::DataFile::ObjectLoader<%s>\n{\n", name, name);
+ IO::print(out, "public:\n");
+ IO::print(out, "\tLoader(%s &);\n", name);
+ IO::print(out, "\nprivate:\n");
+ IO::print(out, "\tvoid init_actions() override;\n");
+ for(const Field &f: fields)
+ if(f.type->needs_loader_function())
+ IO::print(out, "\tvoid %s(%s);\n", f.name, f.type->create_loader_params(false));
+ IO::print(out, "};\n");
+}
+
+void Struct::define_functions(IO::Base &out) const
+{
+ IO::print(out, "%s::Loader::Loader(%s &o):\n", name, name);
+ IO::print(out, "\tObjectLoader<%s>(o)\n{\n", name);
+ IO::print(out, "\tstatic ActionMap shared_actions;\n");
+ IO::print(out, "\tset_actions(shared_actions);\n}\n");
+
+ IO::print(out, "\nvoid %s::Loader::init_actions()\n{\n", name);
+ for(const Field &f: fields)
+ {
+ IO::print(out, "\tadd(\"%s\", ", f.name);
+ if(f.type->needs_loader_function())
+ IO::print(out, "&Loader::%s", f.name);
+ else
+ IO::print(out, "&%s::%s", name, f.name);
+ IO::print(out, ");\n");
+ }
+ IO::print(out, "}\n");
+
+ for(const Field &f: fields)
+ if(f.type->needs_loader_function())
+ {
+ IO::print(out, "\nvoid %s::Loader::%s(%s)\n{\n", name, f.name, f.type->create_loader_params(true));
+ IO::print(out, "\tauto &_v = obj.%s;\n", f.name);
+ IO::print(out, "\t%s\n", f.type->create_loader_statement());
+ IO::print(out, "}\n");
+ }
+}
+
+
+Struct::Loader::Loader(Struct &s, const SetupGen &g):
+ ObjectLoader<Struct>(s),
+ gen(g)
+{
+ static ActionMap shared_actions;
+ set_actions(shared_actions);
+}
+
+void Struct::Loader::init_actions()
+{
+ add("field", &Loader::field);
+}
+
+void Struct::Loader::field(const DataFile::Symbol &n, const DataFile::Symbol &t)
+{
+ if(ranges::any_of(obj.fields, [&n](const Field &f){ return f.name==n.name; }))
+ throw key_error(n.name);
+ Field fld;
+ fld.name=n.name;
+ fld.type=&gen.get_type(t.name);
+ load_sub(fld);
+ obj.fields.emplace_back(move(fld));
+}
+
+
+Struct::Field::Loader::Loader(Field &f):
+ ObjectLoader<Field>(f)
+{
+ static ActionMap shared_actions;
+ set_actions(shared_actions);
+}
+
+void Struct::Field::Loader::init_actions()
+{
+ add("default", &Field::default_value);
+}
--- /dev/null
+#ifndef STRUCT_H_
+#define STRUCT_H_
+
+#include <string>
+#include <vector>
+#include <msp/datafile/objectloader.h>
+#include <msp/io/base.h>
+
+class SetupGen;
+class Type;
+
+class Struct
+{
+public:
+ enum Kind
+ {
+ DATA,
+ ENTITY,
+ COMPONENT
+ };
+
+ struct Field
+ {
+ class Loader;
+
+ std::string name;
+ const Type *type = nullptr;
+ std::string default_value;
+ };
+
+ class Loader;
+
+private:
+ std::string name;
+ Kind kind;
+ std::vector<Field> fields;
+
+public:
+ Struct(const std::string &, Kind);
+
+ const std::vector<Field> &get_fields() const { return fields; }
+
+ void define_type(Msp::IO::Base &) const;
+ void define_loader(Msp::IO::Base &) const;
+ void define_functions(Msp::IO::Base &) const;
+};
+
+class Struct::Loader: public Msp::DataFile::ObjectLoader<Struct>
+{
+private:
+ const SetupGen &gen;
+
+public:
+ Loader(Struct &, const SetupGen &);
+
+private:
+ void init_actions() override;
+
+ void field(const Msp::DataFile::Symbol &, const Msp::DataFile::Symbol &);
+};
+
+#endif
--- /dev/null
+#include "type.h"
+#include <msp/core/except.h>
+#include <msp/strings/format.h>
+#include <msp/strings/utils.h>
+
+using namespace std;
+using namespace Msp;
+
+Type::Type(const string &n, Kind k):
+ name(n),
+ kind(k)
+{
+ if(kind==VALUE || kind==ENUM)
+ cpp_type = name;
+ if(kind==STRUCT)
+ cpp_type = name+"Setup";
+}
+
+Type &Type::set_cpp_type(const string &t, const string &h)
+{
+ cpp_type = t;
+ header = h;
+ return *this;
+}
+
+Type &Type::set_elements(const Type &t, unsigned c)
+{
+ if(kind!=AGGREGATE && kind!=POINTER && kind!=DYN_ARRAY)
+ throw invalid_state("wrong kind");
+ if((kind==POINTER && c>1) || (kind==DYN_ARRAY && c>0))
+ throw invalid_argument("Type::set_elements");
+
+ element_type = &t;
+ element_count = (kind==POINTER ? 1 : c);
+ if(cpp_type.empty())
+ {
+ if(kind==POINTER)
+ cpp_type = format("%s *", t.get_cpp_type());
+ else if(kind==DYN_ARRAY)
+ cpp_type = format("std::vector<%s>", t.get_cpp_type());
+ }
+
+ return *this;
+}
+
+Type &Type::set_struct(const Struct &s)
+{
+ if(kind!=STRUCT)
+ throw invalid_state("wrong kind");
+ struct_def = &s;
+ return *this;
+}
+
+Type &Type::set_enum(const Enum &e)
+{
+ if(kind!=ENUM)
+ throw invalid_state("wrong kind");
+ enum_def = &e;
+ return *this;
+}
+
+Type &Type::set_load(const string &l, const string &c)
+{
+ if(kind!=VALUE)
+ throw invalid_state("wrong kind");
+ load_type = l;
+ conversion = c;
+ return *this;
+}
+
+const Type &Type::get_element_type() const
+{
+ if(kind!=AGGREGATE && kind!=POINTER && kind!=DYN_ARRAY)
+ throw invalid_state("wrong kind");
+ if(!element_type)
+ throw invalid_state("no element type");
+ return *element_type;
+}
+
+const Struct &Type::get_struct() const
+{
+ if(kind!=STRUCT)
+ throw invalid_state("wrong kind");
+ if(!struct_def)
+ throw invalid_state("no struct");
+ return *struct_def;
+}
+
+const Enum &Type::get_enum() const
+{
+ if(kind!=ENUM)
+ throw invalid_state("wrong kind");
+ if(!enum_def)
+ throw invalid_state("no enum");
+ return *enum_def;
+}
+
+bool Type::needs_loader_function() const
+{
+ switch(kind)
+ {
+ case VALUE: return !load_type.empty();
+ case POINTER:
+ case ENUM: return false;
+ case AGGREGATE:
+ case DYN_ARRAY:
+ case STRUCT: return true;
+ default: throw invalid_state("bad kind");
+ }
+}
+
+string Type::create_loader_params(bool with_names) const
+{
+ if(kind==AGGREGATE)
+ {
+ string result;
+ for(unsigned i=0; i<element_count; ++i)
+ {
+ if(with_names)
+ append(result, ", ", format("%s _a%d", element_type->get_cpp_type(), i));
+ else
+ append(result, ", ", element_type->get_cpp_type());
+ }
+ return result;
+ }
+ else if(kind==VALUE)
+ {
+ const string &type = (load_type.empty() ? cpp_type : load_type);
+ return (with_names ? format("%s _a", type) : type);
+ }
+ else if(kind==DYN_ARRAY)
+ {
+ Kind elem_kind = element_type->get_kind();
+ if(elem_kind==VALUE || elem_kind==ENUM)
+ {
+ string type = format("std::vector<%s>", element_type->get_cpp_type());
+ return (with_names ? format("%s _a", type) : type);
+ }
+ else
+ return string();
+ }
+ else if(kind==STRUCT)
+ return string();
+ else
+ throw invalid_state("wrong kind");
+}
+
+string Type::create_loader_statement() const
+{
+ if(kind==AGGREGATE)
+ {
+ string init;
+ for(unsigned i=0; i<element_count; ++i)
+ append(init, ", ", format("_a%d", i));
+ return format("_v = { %s };", init);
+ }
+ else if(kind==VALUE)
+ return format("_v = %s(_a);", (conversion.empty() ? cpp_type : conversion));
+ else if(kind==STRUCT)
+ return "load_sub(_v);";
+ else if(kind==DYN_ARRAY)
+ {
+ Kind elem_kind = element_type->get_kind();
+ if(elem_kind==VALUE || elem_kind==ENUM)
+ return "_v.insert(_v.end(), _a.begin(), _a.end());";
+ else
+ return format("%s _e; load_sub(_e); _v.emplace_back(move(_e));", cpp_type);
+ }
+ else
+ throw invalid_state("wrong kind");
+}
--- /dev/null
+#ifndef TYPE_H_
+#define TYPE_H_
+
+#include <string>
+
+class Enum;
+class Struct;
+
+class Type
+{
+public:
+ enum Kind
+ {
+ VALUE,
+ AGGREGATE,
+ POINTER,
+ DYN_ARRAY,
+ STRUCT,
+ ENUM
+ };
+
+private:
+ std::string name;
+ Kind kind = VALUE;
+ std::string cpp_type;
+ std::string header;
+ const Type *element_type = nullptr;
+ unsigned element_count = 0;
+ std::string load_type;
+ std::string conversion;
+ const Struct *struct_def = nullptr;
+ const Enum *enum_def = nullptr;
+
+public:
+ Type(const std::string &, Kind);
+
+ Type &set_cpp_type(const std::string &, const std::string & = std::string());
+ Type &set_elements(const Type &, unsigned = 0);
+ Type &set_struct(const Struct &);
+ Type &set_enum(const Enum &);
+ Type &set_load(const std::string &, const std::string & = std::string());
+
+ const std::string &get_name() const { return name; }
+ Kind get_kind() const { return kind; }
+ const std::string &get_cpp_type() const { return cpp_type; }
+ const std::string &get_header() const { return header; }
+ const Type &get_element_type() const;
+ const std::string &get_conversion() const { return conversion; }
+ const Struct &get_struct() const;
+ const Enum &get_enum() const;
+
+ bool needs_loader_function() const;
+ std::string create_loader_params(bool) const;
+ std::string create_loader_statement() const;
+};
+
+#endif