]>
Commit | Line | Data |
---|---|---|
f67539c2 TL |
1 | /* |
2 | * Licensed to the Apache Software Foundation (ASF) under one | |
3 | * or more contributor license agreements. See the NOTICE file | |
4 | * distributed with this work for additional information | |
5 | * regarding copyright ownership. The ASF licenses this file | |
6 | * to you under the Apache License, Version 2.0 (the | |
7 | * "License"); you may not use this file except in compliance | |
8 | * with the License. You may obtain a copy of the License at | |
9 | * | |
10 | * http://www.apache.org/licenses/LICENSE-2.0 | |
11 | * | |
12 | * Unless required by applicable law or agreed to in writing, | |
13 | * software distributed under the License is distributed on an | |
14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
15 | * KIND, either express or implied. See the License for the | |
16 | * specific language governing permissions and limitations | |
17 | * under the License. | |
18 | * | |
19 | * Contains some contributions under the Thrift Software License. | |
20 | * Please see doc/old-thrift-license.txt in the Thrift distribution for | |
21 | * details. | |
22 | */ | |
23 | ||
24 | #include <cassert> | |
25 | ||
26 | #include <fstream> | |
27 | #include <iostream> | |
28 | #include <set> | |
29 | #include <sstream> | |
30 | #include <string> | |
31 | #include <vector> | |
32 | ||
33 | #include <sys/stat.h> | |
34 | ||
35 | #include "thrift/platform.h" | |
36 | #include "thrift/generate/t_oop_generator.h" | |
37 | ||
38 | using std::map; | |
39 | using std::ofstream; | |
40 | using std::ostream; | |
41 | using std::ostringstream; | |
42 | using std::set; | |
43 | using std::string; | |
44 | using std::vector; | |
45 | ||
46 | static const string endl = "\n"; // avoid ostream << std::endl flushes | |
47 | ||
48 | /** | |
49 | * D code generator. | |
50 | * | |
51 | * generate_*() functions are called by the base class to emit code for the | |
52 | * given entity, print_*() functions write a piece of code to the passed | |
53 | * stream, and render_*() return a string containing the D representation of | |
54 | * the passed entity. | |
55 | */ | |
56 | class t_d_generator : public t_oop_generator { | |
57 | public: | |
58 | t_d_generator(t_program* program, | |
59 | const std::map<string, string>& parsed_options, | |
60 | const string& option_string) | |
61 | : t_oop_generator(program) { | |
62 | (void)option_string; | |
63 | std::map<std::string, std::string>::const_iterator iter; | |
64 | ||
65 | /* no options yet */ | |
66 | for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) { | |
67 | throw "unknown option d:" + iter->first; | |
68 | } | |
69 | ||
70 | out_dir_base_ = "gen-d"; | |
71 | } | |
72 | ||
73 | protected: | |
74 | ||
75 | // D reserved words are suffixed with an underscore | |
76 | static string suffix_if_reserved(const string& name) { | |
77 | const bool isIn = std::binary_search(std::begin(d_reserved_words), std::end(d_reserved_words), name); | |
78 | string ret = isIn ? name + "_" : name; | |
79 | return ret; | |
80 | } | |
81 | ||
82 | void init_generator() override { | |
83 | // Make output directory | |
84 | MKDIR(get_out_dir().c_str()); | |
85 | ||
86 | string dir = program_->get_namespace("d"); | |
87 | string subdir = get_out_dir(); | |
88 | string::size_type loc; | |
89 | while ((loc = dir.find(".")) != string::npos) { | |
90 | subdir = subdir + "/" + dir.substr(0, loc); | |
91 | MKDIR(subdir.c_str()); | |
92 | dir = dir.substr(loc + 1); | |
93 | } | |
94 | if (!dir.empty()) { | |
95 | subdir = subdir + "/" + dir; | |
96 | MKDIR(subdir.c_str()); | |
97 | } | |
98 | ||
99 | package_dir_ = subdir + "/"; | |
100 | ||
101 | // Make output file | |
102 | string f_types_name = package_dir_ + program_name_ + "_types.d"; | |
103 | f_types_.open(f_types_name.c_str()); | |
104 | ||
105 | // Print header | |
106 | f_types_ << autogen_comment() << "module " << render_package(*program_) << program_name_ | |
107 | << "_types;" << endl << endl; | |
108 | ||
109 | print_default_imports(f_types_); | |
110 | ||
111 | // Include type modules from other imported programs. | |
112 | const vector<t_program*>& includes = program_->get_includes(); | |
113 | for (auto include : includes) { | |
114 | f_types_ << "public import " << render_package(*include) << include->get_name() | |
115 | << "_types;" << endl; | |
116 | } | |
117 | if (!includes.empty()) | |
118 | f_types_ << endl; | |
119 | } | |
120 | ||
121 | void close_generator() override { | |
122 | // Close output file | |
123 | f_types_.close(); | |
124 | } | |
125 | ||
126 | void generate_consts(std::vector<t_const*> consts) override { | |
127 | if (!consts.empty()) { | |
128 | string f_consts_name = package_dir_ + program_name_ + "_constants.d"; | |
129 | ofstream_with_content_based_conditional_update f_consts; | |
130 | f_consts.open(f_consts_name.c_str()); | |
131 | ||
132 | f_consts << autogen_comment() << "module " << render_package(*program_) << program_name_ | |
133 | << "_constants;" << endl << endl; | |
134 | ||
135 | print_default_imports(f_consts); | |
136 | ||
137 | f_consts << "import " << render_package(*get_program()) << program_name_ << "_types;" << endl | |
138 | << endl; | |
139 | ||
140 | vector<t_const*>::iterator c_iter; | |
141 | for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { | |
142 | this->emit_doc(*c_iter, f_consts); | |
143 | string name = suffix_if_reserved((*c_iter)->get_name()); | |
144 | t_type* type = (*c_iter)->get_type(); | |
145 | indent(f_consts) << "immutable(" << render_type_name(type) << ") " << name << ";" << endl; | |
146 | } | |
147 | ||
148 | f_consts << endl << "shared static this() {" << endl; | |
149 | indent_up(); | |
150 | ||
151 | bool first = true; | |
152 | for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { | |
153 | if (first) { | |
154 | first = false; | |
155 | } else { | |
156 | f_consts << endl; | |
157 | } | |
158 | t_type* type = (*c_iter)->get_type(); | |
159 | indent(f_consts) << suffix_if_reserved((*c_iter)->get_name()) << " = "; | |
160 | if (!is_immutable_type(type)) { | |
161 | f_consts << "cast(immutable(" << render_type_name(type) << ")) "; | |
162 | } | |
163 | f_consts << render_const_value(type, (*c_iter)->get_value()) << ";" << endl; | |
164 | } | |
165 | indent_down(); | |
166 | indent(f_consts) << "}" << endl; | |
167 | } | |
168 | } | |
169 | ||
170 | void generate_typedef(t_typedef* ttypedef) override { | |
171 | this->emit_doc(ttypedef, f_types_); | |
172 | f_types_ << indent() << "alias " << render_type_name(ttypedef->get_type()) << " " | |
173 | << ttypedef->get_symbolic() << ";" << endl << endl; | |
174 | } | |
175 | ||
176 | void generate_enum(t_enum* tenum) override { | |
177 | vector<t_enum_value*> constants = tenum->get_constants(); | |
178 | ||
179 | this->emit_doc(tenum, f_types_); | |
180 | string enum_name = suffix_if_reserved(tenum->get_name()); | |
181 | f_types_ << indent() << "enum " << enum_name << " {" << endl; | |
182 | ||
183 | indent_up(); | |
184 | ||
185 | vector<t_enum_value*>::const_iterator c_iter; | |
186 | for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { | |
187 | this->emit_doc(*c_iter, f_types_); | |
188 | indent(f_types_) << suffix_if_reserved((*c_iter)->get_name()); | |
189 | f_types_ << " = " << (*c_iter)->get_value() << ","; | |
190 | } | |
191 | ||
192 | f_types_ << endl; | |
193 | indent_down(); | |
194 | indent(f_types_) << "}" << endl; | |
195 | ||
196 | f_types_ << endl; | |
197 | } | |
198 | ||
199 | void generate_struct(t_struct* tstruct) override { | |
200 | print_struct_definition(f_types_, tstruct, false); | |
201 | } | |
202 | ||
203 | void generate_xception(t_struct* txception) override { | |
204 | print_struct_definition(f_types_, txception, true); | |
205 | } | |
206 | ||
207 | void generate_service(t_service* tservice) override { | |
208 | string svc_name = suffix_if_reserved(tservice->get_name()); | |
209 | ||
210 | // Service implementation file includes | |
211 | string f_servicename = package_dir_ + svc_name + ".d"; | |
212 | ofstream_with_content_based_conditional_update f_service; | |
213 | f_service.open(f_servicename.c_str()); | |
214 | f_service << autogen_comment() << "module " << suffix_if_reserved(render_package(*program_)) << svc_name << ";" | |
215 | << endl << endl; | |
216 | ||
217 | print_default_imports(f_service); | |
218 | ||
219 | f_service << "import " << suffix_if_reserved(render_package(*get_program())) << program_name_ << "_types;" << endl; | |
220 | ||
221 | t_service* extends_service = tservice->get_extends(); | |
222 | if (extends_service != NULL) { | |
223 | f_service << "import " << suffix_if_reserved(render_package(*(extends_service->get_program()))) | |
224 | << suffix_if_reserved(extends_service->get_name()) << ";" << endl; | |
225 | } | |
226 | ||
227 | f_service << endl; | |
228 | ||
229 | string extends = ""; | |
230 | if (tservice->get_extends() != NULL) { | |
231 | extends = " : " + suffix_if_reserved(render_type_name(tservice->get_extends())); | |
232 | } | |
233 | ||
234 | this->emit_doc(tservice, f_service); | |
235 | f_service << indent() << "interface " << svc_name << extends << " {" << endl; | |
236 | indent_up(); | |
237 | ||
238 | // Collect all the exception types service methods can throw so we can | |
239 | // emit the necessary aliases later. | |
240 | set<t_type*> exception_types; | |
241 | ||
242 | // Print the method signatures. | |
243 | vector<t_function*> functions = tservice->get_functions(); | |
244 | vector<t_function*>::iterator fn_iter; | |
245 | for (fn_iter = functions.begin(); fn_iter != functions.end(); ++fn_iter) { | |
246 | this->emit_doc(*fn_iter, f_service); | |
247 | f_service << indent(); | |
248 | print_function_signature(f_service, *fn_iter); | |
249 | f_service << ";" << endl; | |
250 | ||
251 | const vector<t_field*>& exceptions = (*fn_iter)->get_xceptions()->get_members(); | |
252 | vector<t_field*>::const_iterator ex_iter; | |
253 | for (ex_iter = exceptions.begin(); ex_iter != exceptions.end(); ++ex_iter) { | |
254 | exception_types.insert((*ex_iter)->get_type()); | |
255 | } | |
256 | } | |
257 | ||
258 | // Alias the exception types into the current scope. | |
259 | if (!exception_types.empty()) | |
260 | f_service << endl; | |
261 | set<t_type*>::const_iterator et_iter; | |
262 | for (et_iter = exception_types.begin(); et_iter != exception_types.end(); ++et_iter) { | |
263 | indent(f_service) << "alias " << render_package(*(*et_iter)->get_program()) | |
264 | << (*et_iter)->get_program()->get_name() << "_types" | |
265 | << "." << (*et_iter)->get_name() << " " << (*et_iter)->get_name() << ";" | |
266 | << endl; | |
267 | } | |
268 | ||
269 | // Write the method metadata. | |
270 | ostringstream meta; | |
271 | indent_up(); | |
272 | bool first = true; | |
273 | for (fn_iter = functions.begin(); fn_iter != functions.end(); ++fn_iter) { | |
274 | if ((*fn_iter)->get_arglist()->get_members().empty() | |
275 | && (*fn_iter)->get_xceptions()->get_members().empty() && !(*fn_iter)->is_oneway()) { | |
276 | continue; | |
277 | } | |
278 | ||
279 | if (first) { | |
280 | first = false; | |
281 | } else { | |
282 | meta << ","; | |
283 | } | |
284 | ||
285 | meta << endl << indent() << "TMethodMeta(`" << suffix_if_reserved((*fn_iter)->get_name()) << "`, " << endl; | |
286 | indent_up(); | |
287 | indent(meta) << "["; | |
288 | ||
289 | bool first = true; | |
290 | const vector<t_field*>& params = (*fn_iter)->get_arglist()->get_members(); | |
291 | vector<t_field*>::const_iterator p_iter; | |
292 | for (p_iter = params.begin(); p_iter != params.end(); ++p_iter) { | |
293 | if (first) { | |
294 | first = false; | |
295 | } else { | |
296 | meta << ", "; | |
297 | } | |
298 | ||
299 | meta << "TParamMeta(`" << suffix_if_reserved((*p_iter)->get_name()) << "`, " << (*p_iter)->get_key(); | |
300 | ||
301 | t_const_value* cv = (*p_iter)->get_value(); | |
302 | if (cv != NULL) { | |
303 | meta << ", q{" << render_const_value((*p_iter)->get_type(), cv) << "}"; | |
304 | } | |
305 | meta << ")"; | |
306 | } | |
307 | ||
308 | meta << "]"; | |
309 | ||
310 | if (!(*fn_iter)->get_xceptions()->get_members().empty() || (*fn_iter)->is_oneway()) { | |
311 | meta << "," << endl << indent() << "["; | |
312 | ||
313 | bool first = true; | |
314 | const vector<t_field*>& exceptions = (*fn_iter)->get_xceptions()->get_members(); | |
315 | vector<t_field*>::const_iterator ex_iter; | |
316 | for (ex_iter = exceptions.begin(); ex_iter != exceptions.end(); ++ex_iter) { | |
317 | if (first) { | |
318 | first = false; | |
319 | } else { | |
320 | meta << ", "; | |
321 | } | |
322 | ||
323 | meta << "TExceptionMeta(`" << suffix_if_reserved((*ex_iter)->get_name()) << "`, " | |
324 | << (*ex_iter)->get_key() << ", `" << (*ex_iter)->get_type()->get_name() << "`)"; | |
325 | } | |
326 | ||
327 | meta << "]"; | |
328 | } | |
329 | ||
330 | if ((*fn_iter)->is_oneway()) { | |
331 | meta << "," << endl << indent() << "TMethodType.ONEWAY"; | |
332 | } | |
333 | ||
334 | indent_down(); | |
335 | meta << endl << indent() << ")"; | |
336 | } | |
337 | indent_down(); | |
338 | ||
339 | string meta_str(meta.str()); | |
340 | if (!meta_str.empty()) { | |
341 | f_service << endl << indent() << "enum methodMeta = [" << meta_str << endl << indent() << "];" | |
342 | << endl; | |
343 | } | |
344 | ||
345 | indent_down(); | |
346 | indent(f_service) << "}" << endl; | |
347 | ||
348 | // Server skeleton generation. | |
349 | string f_skeletonname = package_dir_ + svc_name + "_server.skeleton.d"; | |
350 | ofstream_with_content_based_conditional_update f_skeleton; | |
351 | f_skeleton.open(f_skeletonname.c_str()); | |
352 | print_server_skeleton(f_skeleton, tservice); | |
353 | f_skeleton.close(); | |
354 | } | |
355 | ||
356 | void emit_doc(t_doc *doc, std::ostream& out) { | |
357 | if (!doc->has_doc()) { | |
358 | return; | |
359 | } | |
360 | indent(out) << "/**" << std::endl; | |
361 | indent_up(); | |
362 | // No endl -- comments reliably have a newline at the end. | |
363 | // This is true even for stuff like: | |
364 | // /** method infos */ void foo(/** huh?*/ 1: i64 stuff) | |
365 | indent(out) << doc->get_doc(); | |
366 | indent_down(); | |
367 | indent(out) << "*/" << std::endl; | |
368 | } | |
369 | ||
370 | private: | |
371 | /** | |
372 | * Writes a server skeleton for the passed service to out. | |
373 | */ | |
374 | void print_server_skeleton(ostream& out, t_service* tservice) { | |
375 | string svc_name = suffix_if_reserved(tservice->get_name()); | |
376 | ||
377 | out << "/*" << endl | |
378 | << " * This auto-generated skeleton file illustrates how to build a server. If you" << endl | |
379 | << " * intend to customize it, you should edit a copy with another file name to " << endl | |
380 | << " * avoid overwriting it when running the generator again." << endl << " */" << endl | |
381 | << "module " << render_package(*tservice->get_program()) << svc_name << "_server;" << endl | |
382 | << endl << "import std.stdio;" << endl << "import thrift.codegen.processor;" << endl | |
383 | << "import thrift.protocol.binary;" << endl << "import thrift.server.simple;" << endl | |
384 | << "import thrift.server.transport.socket;" << endl << "import thrift.transport.buffered;" | |
385 | << endl << "import thrift.util.hashset;" << endl << endl << "import " | |
386 | << render_package(*tservice->get_program()) << svc_name << ";" << endl << "import " | |
387 | << render_package(*get_program()) << program_name_ << "_types;" << endl << endl << endl | |
388 | << "class " << svc_name << "Handler : " << svc_name << " {" << endl; | |
389 | ||
390 | indent_up(); | |
391 | out << indent() << "this() {" << endl << indent() << " // Your initialization goes here." | |
392 | << endl << indent() << "}" << endl << endl; | |
393 | ||
394 | vector<t_function*> functions = tservice->get_functions(); | |
395 | vector<t_function*>::iterator f_iter; | |
396 | for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { | |
397 | out << indent(); | |
398 | print_function_signature(out, *f_iter); | |
399 | out << " {" << endl; | |
400 | ||
401 | indent_up(); | |
402 | ||
403 | out << indent() << "// Your implementation goes here." << endl << indent() << "writeln(\"" | |
404 | << suffix_if_reserved((*f_iter)->get_name()) << " called\");" << endl; | |
405 | ||
406 | t_type* rt = (*f_iter)->get_returntype(); | |
407 | if (!rt->is_void()) { | |
408 | indent(out) << "return typeof(return).init;" << endl; | |
409 | } | |
410 | ||
411 | indent_down(); | |
412 | ||
413 | out << indent() << "}" << endl << endl; | |
414 | } | |
415 | ||
416 | indent_down(); | |
417 | out << "}" << endl << endl; | |
418 | ||
419 | out << indent() << "void main() {" << endl; | |
420 | indent_up(); | |
421 | out << indent() << "auto protocolFactory = new TBinaryProtocolFactory!();" << endl << indent() | |
422 | << "auto processor = new TServiceProcessor!" << svc_name << "(new " << svc_name | |
423 | << "Handler);" << endl << indent() << "auto serverTransport = new TServerSocket(9090);" | |
424 | << endl << indent() << "auto transportFactory = new TBufferedTransportFactory;" << endl | |
425 | << indent() << "auto server = new TSimpleServer(" << endl << indent() | |
426 | << " processor, serverTransport, transportFactory, protocolFactory);" << endl << indent() | |
427 | << "server.serve();" << endl; | |
428 | indent_down(); | |
429 | out << "}" << endl; | |
430 | } | |
431 | ||
432 | /** | |
433 | * Writes the definition of a struct or an exception type to out. | |
434 | */ | |
435 | void print_struct_definition(ostream& out, t_struct* tstruct, bool is_exception) { | |
436 | const vector<t_field*>& members = tstruct->get_members(); | |
437 | ||
438 | if (is_exception) { | |
439 | indent(out) << "class " << suffix_if_reserved(tstruct->get_name()) << " : TException {" << endl; | |
440 | } else { | |
441 | indent(out) << "struct " << suffix_if_reserved(tstruct->get_name()) << " {" << endl; | |
442 | } | |
443 | indent_up(); | |
444 | ||
445 | // Declare all fields. | |
446 | vector<t_field*>::const_iterator m_iter; | |
447 | for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { | |
448 | indent(out) << render_type_name((*m_iter)->get_type()) << " " << suffix_if_reserved((*m_iter)->get_name()) << ";" | |
449 | << endl; | |
450 | } | |
451 | ||
452 | if (!members.empty()) | |
453 | indent(out) << endl; | |
454 | indent(out) << "mixin TStructHelpers!("; | |
455 | ||
456 | if (!members.empty()) { | |
457 | // If there are any fields, construct the TFieldMeta array to pass to | |
458 | // TStructHelpers. We can't just pass an empty array if not because [] | |
459 | // doesn't pass the TFieldMeta[] constraint. | |
460 | out << "["; | |
461 | indent_up(); | |
462 | ||
463 | bool first = true; | |
464 | vector<t_field*>::const_iterator m_iter; | |
465 | for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { | |
466 | if (first) { | |
467 | first = false; | |
468 | } else { | |
469 | out << ","; | |
470 | } | |
471 | out << endl; | |
472 | ||
473 | indent(out) << "TFieldMeta(`" << suffix_if_reserved((*m_iter)->get_name()) << "`, " << (*m_iter)->get_key(); | |
474 | ||
475 | t_const_value* cv = (*m_iter)->get_value(); | |
476 | t_field::e_req req = (*m_iter)->get_req(); | |
477 | out << ", " << render_req(req); | |
478 | if (cv != NULL) { | |
479 | out << ", q{" << render_const_value((*m_iter)->get_type(), cv) << "}"; | |
480 | } | |
481 | out << ")"; | |
482 | } | |
483 | ||
484 | indent_down(); | |
485 | out << endl << indent() << "]"; | |
486 | } | |
487 | ||
488 | out << ");" << endl; | |
489 | ||
490 | indent_down(); | |
491 | indent(out) << "}" << endl << endl; | |
492 | } | |
493 | ||
494 | /** | |
495 | * Prints the D function signature (including return type) for the given | |
496 | * method. | |
497 | */ | |
498 | void print_function_signature(ostream& out, t_function* fn) { | |
499 | out << render_type_name(fn->get_returntype()) << " " << suffix_if_reserved(fn->get_name()) << "("; | |
500 | ||
501 | const vector<t_field*>& fields = fn->get_arglist()->get_members(); | |
502 | vector<t_field*>::const_iterator f_iter; | |
503 | bool first = true; | |
504 | for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { | |
505 | if (first) { | |
506 | first = false; | |
507 | } else { | |
508 | out << ", "; | |
509 | } | |
510 | out << render_type_name((*f_iter)->get_type(), true) << " " << suffix_if_reserved((*f_iter)->get_name()); | |
511 | } | |
512 | ||
513 | out << ")"; | |
514 | } | |
515 | ||
516 | /** | |
517 | * Returns the D representation of value. The result is guaranteed to be a | |
518 | * single expression; for complex types, immediately called delegate | |
519 | * literals are used to achieve this. | |
520 | */ | |
521 | string render_const_value(t_type* type, t_const_value* value) { | |
522 | // Resolve any typedefs. | |
523 | type = get_true_type(type); | |
524 | ||
525 | ostringstream out; | |
526 | if (type->is_base_type()) { | |
527 | t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); | |
528 | switch (tbase) { | |
529 | case t_base_type::TYPE_STRING: | |
530 | out << '"' << get_escaped_string(value) << '"'; | |
531 | break; | |
532 | case t_base_type::TYPE_BOOL: | |
533 | out << ((value->get_integer() > 0) ? "true" : "false"); | |
534 | break; | |
535 | case t_base_type::TYPE_I8: | |
536 | case t_base_type::TYPE_I16: | |
537 | out << "cast(" << render_type_name(type) << ")" << value->get_integer(); | |
538 | break; | |
539 | case t_base_type::TYPE_I32: | |
540 | out << value->get_integer(); | |
541 | break; | |
542 | case t_base_type::TYPE_I64: | |
543 | out << value->get_integer() << "L"; | |
544 | break; | |
545 | case t_base_type::TYPE_DOUBLE: | |
546 | if (value->get_type() == t_const_value::CV_INTEGER) { | |
547 | out << value->get_integer(); | |
548 | } else { | |
549 | out << value->get_double(); | |
550 | } | |
551 | break; | |
552 | default: | |
553 | throw "Compiler error: No const of base type " + t_base_type::t_base_name(tbase); | |
554 | } | |
555 | } else if (type->is_enum()) { | |
556 | out << "cast(" << render_type_name(type) << ")" << value->get_integer(); | |
557 | } else { | |
558 | out << "{" << endl; | |
559 | indent_up(); | |
560 | ||
561 | indent(out) << render_type_name(type) << " v;" << endl; | |
562 | if (type->is_struct() || type->is_xception()) { | |
563 | indent(out) << "v = " << (type->is_xception() ? "new " : "") << render_type_name(type) | |
564 | << "();" << endl; | |
565 | ||
566 | const vector<t_field*>& fields = ((t_struct*)type)->get_members(); | |
567 | vector<t_field*>::const_iterator f_iter; | |
568 | const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map(); | |
569 | map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter; | |
570 | for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { | |
571 | t_type* field_type = NULL; | |
572 | for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { | |
573 | if ((*f_iter)->get_name() == v_iter->first->get_string()) { | |
574 | field_type = (*f_iter)->get_type(); | |
575 | } | |
576 | } | |
577 | if (field_type == NULL) { | |
578 | throw "Type error: " + type->get_name() + " has no field " | |
579 | + v_iter->first->get_string(); | |
580 | } | |
581 | string val = render_const_value(field_type, v_iter->second); | |
582 | indent(out) << "v.set!`" << v_iter->first->get_string() << "`(" << val << ");" << endl; | |
583 | } | |
584 | } else if (type->is_map()) { | |
585 | t_type* ktype = ((t_map*)type)->get_key_type(); | |
586 | t_type* vtype = ((t_map*)type)->get_val_type(); | |
587 | const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map(); | |
588 | map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter; | |
589 | for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { | |
590 | string key = render_const_value(ktype, v_iter->first); | |
591 | string val = render_const_value(vtype, v_iter->second); | |
592 | indent(out) << "v["; | |
593 | if (!is_immutable_type(ktype)) { | |
594 | out << "cast(immutable(" << render_type_name(ktype) << "))"; | |
595 | } | |
596 | out << key << "] = " << val << ";" << endl; | |
597 | } | |
598 | } else if (type->is_list()) { | |
599 | t_type* etype = ((t_list*)type)->get_elem_type(); | |
600 | const vector<t_const_value*>& val = value->get_list(); | |
601 | vector<t_const_value*>::const_iterator v_iter; | |
602 | for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { | |
603 | string val = render_const_value(etype, *v_iter); | |
604 | indent(out) << "v ~= " << val << ";" << endl; | |
605 | } | |
606 | } else if (type->is_set()) { | |
607 | t_type* etype = ((t_set*)type)->get_elem_type(); | |
608 | const vector<t_const_value*>& val = value->get_list(); | |
609 | vector<t_const_value*>::const_iterator v_iter; | |
610 | for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { | |
611 | string val = render_const_value(etype, *v_iter); | |
612 | indent(out) << "v ~= " << val << ";" << endl; | |
613 | } | |
614 | } else { | |
615 | throw "Compiler error: Invalid type in render_const_value: " + type->get_name(); | |
616 | } | |
617 | indent(out) << "return v;" << endl; | |
618 | ||
619 | indent_down(); | |
620 | indent(out) << "}()"; | |
621 | } | |
622 | ||
623 | return out.str(); | |
624 | } | |
625 | ||
626 | /** | |
627 | * Returns the D package to which modules for program are written (with a | |
628 | * trailing dot, if not empty). | |
629 | */ | |
630 | string render_package(const t_program& program) const { | |
631 | string package = program.get_namespace("d"); | |
632 | if (package.size() == 0) | |
633 | return ""; | |
634 | return package + "."; | |
635 | } | |
636 | ||
637 | /** | |
638 | * Returns the name of the D repesentation of ttype. | |
639 | * | |
640 | * If isArg is true, a const reference to the type will be returned for | |
641 | * structs. | |
642 | */ | |
643 | string render_type_name(const t_type* ttype, bool isArg = false) const { | |
644 | if (ttype->is_base_type()) { | |
645 | t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base(); | |
646 | switch (tbase) { | |
647 | case t_base_type::TYPE_VOID: | |
648 | return "void"; | |
649 | case t_base_type::TYPE_STRING: | |
650 | return "string"; | |
651 | case t_base_type::TYPE_BOOL: | |
652 | return "bool"; | |
653 | case t_base_type::TYPE_I8: | |
654 | return "byte"; | |
655 | case t_base_type::TYPE_I16: | |
656 | return "short"; | |
657 | case t_base_type::TYPE_I32: | |
658 | return "int"; | |
659 | case t_base_type::TYPE_I64: | |
660 | return "long"; | |
661 | case t_base_type::TYPE_DOUBLE: | |
662 | return "double"; | |
663 | default: | |
664 | throw "Compiler error: No D type name for base type " + t_base_type::t_base_name(tbase); | |
665 | } | |
666 | } | |
667 | ||
668 | if (ttype->is_container()) { | |
669 | t_container* tcontainer = (t_container*)ttype; | |
670 | if (tcontainer->has_cpp_name()) { | |
671 | return tcontainer->get_cpp_name(); | |
672 | } else if (ttype->is_map()) { | |
673 | t_map* tmap = (t_map*)ttype; | |
674 | t_type* ktype = tmap->get_key_type(); | |
675 | ||
676 | string name = render_type_name(tmap->get_val_type()) + "["; | |
677 | if (!is_immutable_type(ktype)) { | |
678 | name += "immutable("; | |
679 | } | |
680 | name += render_type_name(ktype); | |
681 | if (!is_immutable_type(ktype)) { | |
682 | name += ")"; | |
683 | } | |
684 | name += "]"; | |
685 | return name; | |
686 | } else if (ttype->is_set()) { | |
687 | t_set* tset = (t_set*)ttype; | |
688 | return "HashSet!(" + render_type_name(tset->get_elem_type()) + ")"; | |
689 | } else if (ttype->is_list()) { | |
690 | t_list* tlist = (t_list*)ttype; | |
691 | return render_type_name(tlist->get_elem_type()) + "[]"; | |
692 | } | |
693 | } | |
694 | ||
695 | if (ttype->is_struct() && isArg) { | |
696 | return "ref const(" + ttype->get_name() + ")"; | |
697 | } else { | |
698 | return ttype->get_name(); | |
699 | } | |
700 | } | |
701 | ||
702 | /** | |
703 | * Returns the D TReq enum member corresponding to req. | |
704 | */ | |
705 | string render_req(t_field::e_req req) const { | |
706 | switch (req) { | |
707 | case t_field::T_OPT_IN_REQ_OUT: | |
708 | return "TReq.OPT_IN_REQ_OUT"; | |
709 | case t_field::T_OPTIONAL: | |
710 | return "TReq.OPTIONAL"; | |
711 | case t_field::T_REQUIRED: | |
712 | return "TReq.REQUIRED"; | |
713 | default: { | |
714 | std::stringstream ss; | |
715 | ss << "Compiler error: Invalid requirement level " << req; | |
716 | throw ss.str(); | |
717 | } | |
718 | } | |
719 | } | |
720 | ||
721 | /** | |
722 | * Writes the default list of imports (which are written to every generated | |
723 | * module) to f. | |
724 | */ | |
725 | void print_default_imports(ostream& out) { | |
726 | indent(out) << "import thrift.base;" << endl << "import thrift.codegen.base;" << endl | |
727 | << "import thrift.util.hashset;" << endl << endl; | |
728 | } | |
729 | ||
730 | /** | |
731 | * Returns whether type is »intrinsically immutable«, in the sense that | |
732 | * a value of that type is implicitly castable to immutable(type), and it is | |
733 | * allowed for AA keys without an immutable() qualifier. | |
734 | */ | |
735 | bool is_immutable_type(t_type* type) const { | |
736 | t_type* ttype = get_true_type(type); | |
737 | return ttype->is_base_type() || ttype->is_enum(); | |
738 | } | |
739 | ||
740 | /* | |
741 | * File streams, stored here to avoid passing them as parameters to every | |
742 | * function. | |
743 | */ | |
744 | ofstream_with_content_based_conditional_update f_types_; | |
745 | ofstream_with_content_based_conditional_update f_header_; | |
746 | ||
747 | string package_dir_; | |
748 | ||
749 | protected: | |
750 | static vector<string> d_reserved_words; | |
751 | ||
752 | }; | |
753 | ||
754 | vector<string> t_d_generator::d_reserved_words = { | |
755 | // The keywords are extracted from https://dlang.org/spec/lex.html | |
756 | // and sorted for use with std::binary_search | |
757 | "__FILE_FULL_PATH__", "__FILE__", "__FUNCTION__", "__LINE__", "__MODULE__", | |
758 | "__PRETTY_FUNCTION__", "__gshared", "__parameters", "__traits", "__vector", | |
759 | "abstract", "alias", "align", "asm", "assert", "auto", "body", "bool", | |
760 | "break", "byte", "case", "cast", "catch", "cdouble", "cent", "cfloat", | |
761 | "char", "class", "const", "continue", "creal", "dchar", "debug", "default", | |
762 | "delegate", "delete", "deprecated", "do", "double", "else", "enum", | |
763 | "export", "extern", "false", "final", "finally", "float", "for", "foreach", | |
764 | "foreach_reverse", "function", "goto", "idouble", "if", "ifloat", "immutable", | |
765 | "import", "in", "inout", "int", "interface", "invariant", "ireal", "is", | |
766 | "lazy", "long", "macro ", "mixin", "module", "new", "nothrow", "null", "out", | |
767 | "override", "package", "pragma", "private", "protected", "public", "pure", | |
768 | "real", "ref", "return", "scope", "shared", "short", "static", "struct", | |
769 | "super", "switch", "synchronized", "template", "this", "throw", "true", "try", | |
770 | "typeid", "typeof", "ubyte", "ucent", "uint", "ulong", "union", "unittest", | |
771 | "ushort", "version", "void", "wchar", "while", "with" | |
772 | }; | |
773 | ||
774 | THRIFT_REGISTER_GENERATOR(d, "D", "") |