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