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