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