]> git.proxmox.com Git - ceph.git/blob - ceph/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_cl_generator.cc
buildsys: switch source download to quincy
[ceph.git] / ceph / src / jaegertracing / thrift / compiler / cpp / src / thrift / generate / t_cl_generator.cc
1 /*
2 * Copyright (c) 2008- Patrick Collison <patrick@collison.ie>
3 * Copyright (c) 2006- Facebook
4 *
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
20 * under the License.
21 */
22
23 #include <string>
24 #include <fstream>
25 #include <iostream>
26 #include <vector>
27
28 #include <stdlib.h>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31 #include <sstream>
32 #include <string>
33 #include <algorithm>
34
35 #include "thrift/platform.h"
36 #include "t_oop_generator.h"
37
38 using namespace std;
39
40
41 /**
42 * Common Lisp code generator.
43 *
44 * @author Patrick Collison <patrick@collison.ie>
45 */
46 class t_cl_generator : public t_oop_generator {
47 public:
48 t_cl_generator(
49 t_program* program,
50 const std::map<std::string, std::string>& parsed_options,
51 const std::string& option_string)
52 : t_oop_generator(program)
53 {
54 no_asd = false;
55 system_prefix = "thrift-gen-";
56
57 std::map<std::string, std::string>::const_iterator iter;
58
59 for(iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
60 if(iter->first.compare("no_asd") == 0) {
61 no_asd = true;
62 } else if (iter->first.compare("sys_pref") == 0) {
63 system_prefix = iter->second;
64 } else {
65 throw "unknown option cl:" + iter->first;
66 }
67 }
68
69 out_dir_base_ = "gen-cl";
70 copy_options_ = option_string;
71 }
72
73 void init_generator() override;
74 void close_generator() override;
75
76 void generate_typedef (t_typedef* ttypedef) override;
77 void generate_enum (t_enum* tenum) override;
78 void generate_const (t_const* tconst) override;
79 void generate_struct (t_struct* tstruct) override;
80 void generate_xception (t_struct* txception) override;
81 void generate_service (t_service* tservice) override;
82 void generate_cl_struct (std::ostream& out, t_struct* tstruct, bool is_exception);
83 void generate_cl_struct_internal (std::ostream& out, t_struct* tstruct, bool is_exception);
84 void generate_exception_sig(std::ostream& out, t_function* f);
85 std::string render_const_value(t_type* type, t_const_value* value);
86
87 std::string cl_autogen_comment();
88 void asdf_def(std::ostream &out);
89 void package_def(std::ostream &out);
90 void package_in(std::ostream &out);
91 std::string generated_package();
92 std::string prefix(std::string name);
93 std::string package_of(t_program* program);
94 std::string package();
95 std::string render_includes();
96
97 std::string type_name(t_type* ttype);
98 std::string typespec (t_type *t);
99 std::string function_signature(t_function* tfunction);
100 std::string argument_list(t_struct* tstruct);
101
102 std::string cl_docstring(std::string raw);
103
104 private:
105
106 int temporary_var;
107 /**
108 * Isolate the variable definitions, as they can require structure definitions
109 */
110 ofstream_with_content_based_conditional_update f_asd_;
111 ofstream_with_content_based_conditional_update f_types_;
112 ofstream_with_content_based_conditional_update f_vars_;
113
114 std::string copy_options_;
115
116 bool no_asd;
117 std::string system_prefix;
118 };
119
120
121 void t_cl_generator::init_generator() {
122 MKDIR(get_out_dir().c_str());
123 string program_dir = get_out_dir() + "/" + program_name_;
124 MKDIR(program_dir.c_str());
125
126 temporary_var = 0;
127
128 string f_types_name = program_dir + "/" + program_name_ + "-types.lisp";
129 string f_vars_name = program_dir + "/" + program_name_ + "-vars.lisp";
130
131 f_types_.open(f_types_name);
132 f_types_ << cl_autogen_comment() << endl;
133 f_vars_.open(f_vars_name);
134 f_vars_ << cl_autogen_comment() << endl;
135
136 package_def(f_types_);
137 package_in(f_types_);
138 package_in(f_vars_);
139
140 if (!no_asd) {
141 string f_asd_name = program_dir + "/" + system_prefix + program_name_ + ".asd";
142 f_asd_.open(f_asd_name);
143 f_asd_ << cl_autogen_comment() << endl;
144 asdf_def(f_asd_);
145 }
146 }
147
148 /**
149 * Renders all the imports necessary for including another Thrift program
150 */
151 string t_cl_generator::render_includes() {
152 const vector<t_program*>& includes = program_->get_includes();
153 string result = "";
154 result += ":depends-on (:thrift";
155 for (auto include : includes) {
156 result += " :" + system_prefix + underscore(include->get_name());
157 }
158 result += ")\n";
159 return result;
160 }
161
162 string t_cl_generator::package_of(t_program* program) {
163 string prefix = program->get_namespace("cl");
164 return prefix.empty() ? "thrift-generated" : prefix;
165 }
166
167 string t_cl_generator::package() {
168 return package_of(program_);
169 }
170
171 string t_cl_generator::prefix(string symbol) {
172 return "\"" + symbol + "\"";
173 }
174
175 string t_cl_generator::cl_autogen_comment() {
176 return
177 std::string(";;; ") + "Autogenerated by Thrift\n" +
178 ";;; DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n" +
179 ";;; options string: " + copy_options_ + "\n";
180 }
181
182 string t_cl_generator::cl_docstring(string raw) {
183 replace(raw.begin(), raw.end(), '"', '\'');
184 return raw;
185 }
186
187
188 void t_cl_generator::close_generator() {
189 f_asd_.close();
190 f_types_.close();
191 f_vars_.close();
192 }
193
194 string t_cl_generator::generated_package() {
195 return program_->get_namespace("cpp");
196 }
197
198 void t_cl_generator::asdf_def(std::ostream &out) {
199 out << "(asdf:defsystem #:" << system_prefix << program_name_ << endl;
200 indent_up();
201 out << indent() << render_includes()
202 << indent() << ":serial t" << endl
203 << indent() << ":components ("
204 << "(:file \"" << program_name_ << "-types\") "
205 << "(:file \"" << program_name_ << "-vars\")))" << endl;
206 indent_down();
207 }
208
209 /***
210 * Generate a package definition. Add use references equivalent to the idl file's include statements.
211 */
212 void t_cl_generator::package_def(std::ostream &out) {
213 const vector<t_program*>& includes = program_->get_includes();
214
215 out << "(thrift:def-package :" << package();
216 if ( includes.size() > 0 ) {
217 out << " :use (";
218 for (auto include : includes) {
219 out << " :" << include->get_name();
220 }
221 out << ")";
222 }
223 out << ")" << endl << endl;
224 }
225
226 void t_cl_generator::package_in(std::ostream &out) {
227 out << "(cl:in-package :" << package() << ")" << endl << endl;
228 }
229
230 /**
231 * Generates a typedef. This is not done in Common Lisp, types are all implicit.
232 *
233 * @param ttypedef The type definition
234 */
235 void t_cl_generator::generate_typedef(t_typedef* ttypedef) {
236 (void)ttypedef;
237 }
238
239 void t_cl_generator::generate_enum(t_enum* tenum) {
240 f_types_ << "(thrift:def-enum " << prefix(tenum->get_name()) << endl;
241
242 vector<t_enum_value*> constants = tenum->get_constants();
243 vector<t_enum_value*>::iterator c_iter;
244 int value = -1;
245
246 indent_up();
247 f_types_ << indent() << "(";
248 for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
249 value = (*c_iter)->get_value();
250
251 if(c_iter != constants.begin()) f_types_ << endl << indent() << " ";
252
253 f_types_ << "(\"" << (*c_iter)->get_name() << "\" . " << value << ")";
254 }
255 indent_down();
256 f_types_ << "))" << endl << endl;
257 }
258
259 /**
260 * Generate a constant value
261 */
262 void t_cl_generator::generate_const(t_const* tconst) {
263 t_type* type = tconst->get_type();
264 string name = tconst->get_name();
265 t_const_value* value = tconst->get_value();
266
267 f_vars_ << "(thrift:def-constant " << prefix(name) << " " << render_const_value(type, value) << ")"
268 << endl << endl;
269 }
270
271 /**
272 * Prints the value of a constant with the given type. Note that type checking
273 * is NOT performed in this function as it is always run beforehand using the
274 * validate_types method in main.cc
275 */
276 string t_cl_generator::render_const_value(t_type* type, t_const_value* value) {
277 type = get_true_type(type);
278 std::ostringstream out;
279 if (type->is_base_type()) {
280 t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
281 switch (tbase) {
282 case t_base_type::TYPE_STRING:
283 out << "\"" << value->get_string() << "\"";
284 break;
285 case t_base_type::TYPE_BOOL:
286 out << (value->get_integer() > 0 ? "t" : "nil");
287 break;
288 case t_base_type::TYPE_I8:
289 case t_base_type::TYPE_I16:
290 case t_base_type::TYPE_I32:
291 case t_base_type::TYPE_I64:
292 out << value->get_integer();
293 break;
294 case t_base_type::TYPE_DOUBLE:
295 if (value->get_type() == t_const_value::CV_INTEGER) {
296 out << value->get_integer();
297 } else {
298 out << value->get_double();
299 }
300 break;
301 default:
302 throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
303 }
304 } else if (type->is_enum()) {
305 indent(out) << value->get_integer();
306 } else if (type->is_struct() || type->is_xception()) {
307 out << (type->is_struct() ? "(make-instance '" : "(make-exception '") <<
308 lowercase(type->get_name()) << " " << endl;
309 indent_up();
310
311 const vector<t_field*>& fields = ((t_struct*)type)->get_members();
312 vector<t_field*>::const_iterator f_iter;
313 const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
314 map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
315
316 for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
317 t_type* field_type = NULL;
318 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
319 if ((*f_iter)->get_name() == v_iter->first->get_string()) {
320 field_type = (*f_iter)->get_type();
321 }
322 }
323 if (field_type == NULL) {
324 throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string();
325 }
326
327 out << indent() << ":" << v_iter->first->get_string() << " " <<
328 render_const_value(field_type, v_iter->second) << endl;
329 }
330 out << indent() << ")";
331
332 indent_down();
333 } else if (type->is_map()) {
334 // emit an hash form with both keys and values to be evaluated
335 t_type* ktype = ((t_map*)type)->get_key_type();
336 t_type* vtype = ((t_map*)type)->get_val_type();
337 out << "(thrift:map ";
338 indent_up();
339 const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
340 map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
341 for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
342 out << endl << indent()
343 << "(cl:cons " << render_const_value(ktype, v_iter->first) << " "
344 << render_const_value(vtype, v_iter->second) << ")";
345 }
346 indent_down();
347 out << indent() << ")";
348 } else if (type->is_list() || type->is_set()) {
349 t_type* etype;
350 if (type->is_list()) {
351 etype = ((t_list*)type)->get_elem_type();
352 } else {
353 etype = ((t_set*)type)->get_elem_type();
354 }
355 if (type->is_set()) {
356 out << "(thrift:set" << endl;
357 } else {
358 out << "(thrift:list" << endl;
359 }
360 indent_up();
361 indent_up();
362 const vector<t_const_value*>& val = value->get_list();
363 vector<t_const_value*>::const_iterator v_iter;
364 for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
365 out << indent() << render_const_value(etype, *v_iter) << endl;
366 }
367 out << indent() << ")";
368 indent_down();
369 indent_down();
370 } else {
371 throw "CANNOT GENERATE CONSTANT FOR TYPE: " + type->get_name();
372 }
373 return out.str();
374 }
375
376 void t_cl_generator::generate_struct(t_struct* tstruct) {
377 generate_cl_struct(f_types_, tstruct, false);
378 }
379
380 void t_cl_generator::generate_xception(t_struct* txception) {
381 generate_cl_struct(f_types_, txception, true);
382 }
383
384 void t_cl_generator::generate_cl_struct_internal(std::ostream& out, t_struct* tstruct, bool is_exception) {
385 (void)is_exception;
386 const vector<t_field*>& members = tstruct->get_members();
387 vector<t_field*>::const_iterator m_iter;
388
389 out << "(";
390
391 for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
392 t_const_value* value = (*m_iter)->get_value();
393 t_type* type = (*m_iter)->get_type();
394
395 if (m_iter != members.begin()) {
396 out << endl << indent() << " ";
397 }
398 out << "(" << prefix((*m_iter)->get_name()) << " " <<
399 ( (NULL != value) ? render_const_value(type, value) : "nil" ) <<
400 " :id " << (*m_iter)->get_key();
401 if ( type->is_base_type() && "string" == typespec(type) )
402 if ( ((t_base_type*)type)->is_binary() )
403 out << " :type binary";
404 else
405 out << " :type string";
406 else
407 out << " :type " << typespec(type);
408 if ( (*m_iter)->get_req() == t_field::T_OPTIONAL ) {
409 out << " :optional t";
410 }
411 if ( (*m_iter)->has_doc()) {
412 out << " :documentation \"" << cl_docstring((*m_iter)->get_doc()) << "\"";
413 }
414 out <<")";
415 }
416
417 out << ")";
418 }
419
420 void t_cl_generator::generate_cl_struct(std::ostream& out, t_struct* tstruct, bool is_exception = false) {
421 std::string name = type_name(tstruct);
422 out << (is_exception ? "(thrift:def-exception " : "(thrift:def-struct ") <<
423 prefix(name) << endl;
424 indent_up();
425 if ( tstruct->has_doc() ) {
426 out << indent() ;
427 out << "\"" << cl_docstring(tstruct->get_doc()) << "\"" << endl;
428 }
429 out << indent() ;
430 generate_cl_struct_internal(out, tstruct, is_exception);
431 indent_down();
432 out << ")" << endl << endl;
433 }
434
435 void t_cl_generator::generate_exception_sig(std::ostream& out, t_function* f) {
436 generate_cl_struct_internal(out, f->get_xceptions(), true);
437 }
438
439 void t_cl_generator::generate_service(t_service* tservice) {
440 string extends_client;
441 vector<t_function*> functions = tservice->get_functions();
442 vector<t_function*>::iterator f_iter;
443
444 if (tservice->get_extends() != NULL) {
445 extends_client = type_name(tservice->get_extends());
446 }
447
448 extends_client = extends_client.empty() ? "nil" : prefix(extends_client);
449
450 f_types_ << "(thrift:def-service " << prefix(service_name_) << " "
451 << extends_client;
452
453 indent_up();
454
455 if ( tservice->has_doc()) {
456 f_types_ << endl << indent()
457 << "(:documentation \"" << cl_docstring(tservice->get_doc()) << "\")";
458 }
459
460 for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
461 t_function* function = *f_iter;
462 string fname = function->get_name();
463 string signature = function_signature(function);
464 t_struct* exceptions = function->get_xceptions();
465 const vector<t_field*>& xmembers = exceptions->get_members();
466
467 f_types_ << endl << indent() << "(:method " << prefix(fname);
468 f_types_ << " (" << signature << " " << typespec((*f_iter)->get_returntype()) << ")";
469 if (xmembers.size() > 0) {
470 f_types_ << endl << indent() << " :exceptions " ;
471 generate_exception_sig(f_types_, function);
472 }
473 if ( (*f_iter)->is_oneway() ) {
474 f_types_ << endl << indent() << " :oneway t";
475 }
476 if ( (*f_iter)->has_doc() ) {
477 f_types_ << endl << indent() << " :documentation \""
478 << cl_docstring((*f_iter)->get_doc()) << "\"";
479 }
480 f_types_ << ")";
481 }
482
483 f_types_ << ")" << endl << endl;
484
485 indent_down();
486 }
487
488 string t_cl_generator::typespec(t_type *t) {
489 t = get_true_type(t);
490
491 if (t -> is_binary()){
492 return "binary";
493 } else if (t->is_base_type()) {
494 return type_name(t);
495 } else if (t->is_map()) {
496 t_map *m = (t_map*) t;
497 return "(thrift:map " + typespec(m->get_key_type()) + " " +
498 typespec(m->get_val_type()) + ")";
499 } else if (t->is_struct() || t->is_xception()) {
500 return "(struct " + prefix(type_name(t)) + ")";
501 } else if (t->is_list()) {
502 return "(thrift:list " + typespec(((t_list*) t)->get_elem_type()) + ")";
503 } else if (t->is_set()) {
504 return "(thrift:set " + typespec(((t_set*) t)->get_elem_type()) + ")";
505 } else if (t->is_enum()) {
506 return "(enum \"" + ((t_enum*) t)->get_name() + "\")";
507 } else {
508 throw "Sorry, I don't know how to generate this: " + type_name(t);
509 }
510 }
511
512 string t_cl_generator::function_signature(t_function* tfunction) {
513 return argument_list(tfunction->get_arglist());
514 }
515
516 string t_cl_generator::argument_list(t_struct* tstruct) {
517 stringstream res;
518 res << "(";
519
520 const vector<t_field*>& fields = tstruct->get_members();
521 vector<t_field*>::const_iterator f_iter;
522 bool first = true;
523 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
524 if (first) {
525 first = false;
526 } else {
527 res << " ";
528 }
529 res << "(" + prefix((*f_iter)->get_name()) << " " <<
530 typespec((*f_iter)->get_type()) << " " <<
531 (*f_iter)->get_key() << ")";
532
533
534 }
535 res << ")";
536 return res.str();
537 }
538
539 string t_cl_generator::type_name(t_type* ttype) {
540 string prefix = "";
541 t_program* program = ttype->get_program();
542
543 if (program != NULL && program != program_)
544 prefix = package_of(program) == package() ? "" : package_of(program) + ":";
545
546 string name = ttype->get_name();
547
548 if (ttype->is_struct() || ttype->is_xception())
549 name = lowercase(ttype->get_name());
550
551 return prefix + name;
552 }
553
554 THRIFT_REGISTER_GENERATOR(
555 cl,
556 "Common Lisp",
557 " no_asd: Do not define ASDF systems for each generated Thrift program.\n"
558 " sys_pref= The prefix to give ASDF system names. Default: thrift-gen-\n")