]> git.tdb.fi Git - libs/game.git/blob - tools/setupgen/setupgen.cpp
083fc8a96be3e6ec158d18e10de7ca993b2f0bb0
[libs/game.git] / tools / setupgen / setupgen.cpp
1 #include "setupgen.h"
2 #include <msp/core/getopt.h>
3 #include <msp/core/maputils.h>
4 #include <msp/fs/utils.h>
5 #include <msp/io/console.h>
6 #include <msp/io/print.h>
7 #include <msp/strings/utils.h>
8
9 using namespace std;
10 using namespace Msp;
11
12 SetupGen::SetupGen(int argc, char **argv)
13 {
14         GetOpt getopt;
15         getopt.add_option('o', "output", out_fn, GetOpt::REQUIRED_ARG);
16         getopt.add_argument("input_file", in_fn, GetOpt::REQUIRED_ARG);
17         getopt(argc, argv);
18 }
19
20 int SetupGen::main()
21 {
22         create_standard_types();
23         load(in_fn);
24
25         const Module &main_mod = modules.front();
26         collect_headers(main_mod);
27
28         if(out_fn.empty())
29         {
30                 generate_header(main_mod, IO::cout);
31                 generate_code(main_mod, IO::cout);
32         }
33         else
34         {
35                 string out_base = FS::basename(out_fn);
36                 string out_ext = FS::extpart(out_base);
37                 if(out_ext==".cpp" || out_ext==".h")
38                 {
39                         FS::Path out_dir = FS::dirname(out_fn);
40                         out_base = FS::basepart(out_base);
41                         IO::BufferedFile header_out((out_dir/(out_base+".h")).str(), IO::M_WRITE);
42                         generate_header(main_mod, header_out);
43                         IO::BufferedFile code_out((out_dir/(out_base+".cpp")).str(), IO::M_WRITE);
44                         IO::print(code_out, "#include \"%s.h\"\n", out_base);
45                         if(!main_mod.enums.empty())
46                                 IO::print(code_out, "#include <msp/strings/format.h>\n");
47                         generate_code(main_mod, code_out);
48                 }
49                 else
50                 {
51                         IO::BufferedFile out(out_fn, IO::M_WRITE);
52                         generate_header(main_mod, out);
53                         out.put('\n');
54                         generate_code(main_mod, out);
55                 }
56         }
57
58         return 0;
59 }
60
61 void SetupGen::create_standard_types()
62 {
63         add_type("bool", Type::VALUE);
64         add_type("int", Type::VALUE);
65         add_type("uint", Type::VALUE).set_cpp_type("unsigned");
66         const Type &float_type = add_type("float", Type::VALUE);
67         add_type("string", Type::VALUE).set_cpp_type("std::string", "string");
68         add_type("angle", Type::VALUE).set_cpp_type("Msp::Geometry::Angle<float>", "msp/geometry/angle.h")
69                 .set_load("float", "Msp::Geometry::Angle<float>::from_degrees");
70         add_type("vector2", Type::AGGREGATE).set_cpp_type("Msp::LinAl::Vector<float, 2>", "msp/linal/vector.h")
71                 .set_elements(float_type, 2);
72         add_type("vector3", Type::AGGREGATE).set_cpp_type("Msp::LinAl::Vector<float, 3>", "msp/linal/vector.h")
73                 .set_elements(float_type, 3);
74 }
75
76 Type &SetupGen::add_type(const string &n, Type::Kind k)
77 {
78         auto result = types.try_emplace(n, n, k);
79         if(!result.second)
80                 throw key_error(n);
81         return result.first->second;
82 }
83
84 const Type &SetupGen::get_type(const string &n) const
85 {
86         return get_item(types, n);
87 }
88
89 void SetupGen::load(const FS::Path &fn)
90 {
91         Module &mod = modules.emplace_back();
92         IO::BufferedFile in_file(fn.str());
93         DataFile::Parser parser(in_file, fn.str());
94         Loader ldr(*this, mod);
95         ldr.load(parser);
96 }
97
98 void SetupGen::collect_headers(const Module &mod)
99 {
100         headers.insert("msp/datafile/objectloader.h");
101         headers.insert("msp/strings/lexicalcast.h");
102         for(const unique_ptr<Struct> &s: mod.structs)
103                 for(const Struct::Field &f: s->get_fields())
104                         if(const string &h = f.type->get_header(); !h.empty())
105                                 headers.insert(h);
106 }
107
108 void SetupGen::generate_header(const Module &mod, IO::Base &out) const
109 {
110         string guard = toupper(FS::basepart(FS::basename(out_fn.empty() ? in_fn : out_fn)))+"_H_";
111         if(!mod.name_space.empty())
112         {
113                 vector<string> parts = split(mod.name_space, "::");
114                 parts.emplace_back(move(guard));
115                 guard = join(parts.begin(), parts.end(), "_");
116         }
117         IO::print(out, "#ifndef %s\n", guard);
118         IO::print(out, "#define %s\n", guard);
119
120         if(!headers.empty())
121         {
122                 out.put('\n');
123                 for(const string &h: headers)
124                         IO::print(out, "#include <%s>\n", h);
125         }
126
127         if(!mod.name_space.empty())
128                 IO::print(out, "\nnamespace %s {\n", mod.name_space);
129
130         for(const unique_ptr<Enum> &e: mod.enums)
131         {
132                 out.put('\n');
133                 e->define_type(out);
134         }
135
136         for(const unique_ptr<Struct> &s: mod.structs)
137         {
138                 out.put('\n');
139                 s->define_type(out);
140         }
141
142         for(const unique_ptr<Struct> &s: mod.structs)
143         {
144                 out.put('\n');
145                 s->define_loader(out);
146         }
147
148         for(const unique_ptr<Enum> &e: mod.enums)
149         {
150                 out.put('\n');
151                 e->declare_conversions(out);
152         }
153
154         if(!mod.name_space.empty())
155                 IO::print(out, "\n}  // namespace %s\n", mod.name_space);
156
157         IO::print(out, "\n#endif\n");
158 }
159
160 void SetupGen::generate_code(const Module &mod, IO::Base &out) const
161 {
162         if(!mod.name_space.empty())
163                 IO::print(out, "\nnamespace %s {\n", mod.name_space);
164
165         for(const unique_ptr<Struct> &s: mod.structs)
166         {
167                 out.put('\n');
168                 s->define_functions(out);
169         }
170
171         for(const unique_ptr<Enum> &e: mod.enums)
172         {
173                 out.put('\n');
174                 e->define_conversions(out);
175         }
176
177         if(!mod.name_space.empty())
178                 IO::print(out, "\n}  // namespace %s\n", mod.name_space);
179 }
180
181
182 SetupGen::Loader::Loader(SetupGen &s, Module &m):
183         ObjectLoader<SetupGen>(s),
184         mod(m)
185 {
186         static ActionMap shared_actions;
187         set_actions(shared_actions);
188 }
189
190 void SetupGen::Loader::init_actions()
191 {
192         add("component", &Loader::struct_def, Struct::COMPONENT);
193         add("entity", &Loader::struct_def, Struct::ENTITY);
194         add("enum", &Loader::enum_def);
195         add("namespace", &Loader::name_space);
196 }
197
198 void SetupGen::Loader::enum_def(const DataFile::Symbol &n)
199 {
200         Enum en(n.name);
201         load_sub(en);
202         Type &type = obj.add_type(n.name, Type::ENUM);
203         type.set_enum(*mod.enums.emplace_back(make_unique<Enum>(move(en))));
204 }
205
206 void SetupGen::Loader::name_space(const string &ns)
207 {
208         mod.name_space = ns;
209 }
210
211 void SetupGen::Loader::struct_def(Struct::Kind kind, const DataFile::Symbol &n)
212 {
213         Struct sct(n.name+"Setup", kind);
214         load_sub(sct, obj);
215         Type &type = obj.add_type(n.name, Type::STRUCT);
216         type.set_struct(*mod.structs.emplace_back(make_unique<Struct>(move(sct))));
217 }