2 * Copyright (c) 2008- Patrick Collison <patrick@collison.ie>
3 * Copyright (c) 2006- Facebook
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
13 * http://www.apache.org/licenses/LICENSE-2.0
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
30 #include <sys/types.h>
35 #include "thrift/platform.h"
36 #include "t_oop_generator.h"
42 * Common Lisp code generator.
44 * @author Patrick Collison <patrick@collison.ie>
46 class t_cl_generator
: public t_oop_generator
{
50 const std::map
<std::string
, std::string
>& parsed_options
,
51 const std::string
& option_string
)
52 : t_oop_generator(program
)
55 system_prefix
= "thrift-gen-";
57 std::map
<std::string
, std::string
>::const_iterator iter
;
59 for(iter
= parsed_options
.begin(); iter
!= parsed_options
.end(); ++iter
) {
60 if(iter
->first
.compare("no_asd") == 0) {
62 } else if (iter
->first
.compare("sys_pref") == 0) {
63 system_prefix
= iter
->second
;
65 throw "unknown option cl:" + iter
->first
;
69 out_dir_base_
= "gen-cl";
70 copy_options_
= option_string
;
73 void init_generator() override
;
74 void close_generator() override
;
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
);
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();
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
);
102 std::string
cl_docstring(std::string raw
);
108 * Isolate the variable definitions, as they can require structure definitions
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_
;
114 std::string copy_options_
;
117 std::string system_prefix
;
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());
128 string f_types_name
= program_dir
+ "/" + program_name_
+ "-types.lisp";
129 string f_vars_name
= program_dir
+ "/" + program_name_
+ "-vars.lisp";
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
;
136 package_def(f_types_
);
137 package_in(f_types_
);
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
;
149 * Renders all the imports necessary for including another Thrift program
151 string
t_cl_generator::render_includes() {
152 const vector
<t_program
*>& includes
= program_
->get_includes();
154 result
+= ":depends-on (:thrift";
155 for (auto include
: includes
) {
156 result
+= " :" + system_prefix
+ underscore(include
->get_name());
162 string
t_cl_generator::package_of(t_program
* program
) {
163 string prefix
= program
->get_namespace("cl");
164 return prefix
.empty() ? "thrift-generated" : prefix
;
167 string
t_cl_generator::package() {
168 return package_of(program_
);
171 string
t_cl_generator::prefix(string symbol
) {
172 return "\"" + symbol
+ "\"";
175 string
t_cl_generator::cl_autogen_comment() {
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";
182 string
t_cl_generator::cl_docstring(string raw
) {
183 replace(raw
.begin(), raw
.end(), '"', '\'');
188 void t_cl_generator::close_generator() {
194 string
t_cl_generator::generated_package() {
195 return program_
->get_namespace("cpp");
198 void t_cl_generator::asdf_def(std::ostream
&out
) {
199 out
<< "(asdf:defsystem #:" << system_prefix
<< program_name_
<< endl
;
201 out
<< indent() << render_includes()
202 << indent() << ":serial t" << endl
203 << indent() << ":components ("
204 << "(:file \"" << program_name_
<< "-types\") "
205 << "(:file \"" << program_name_
<< "-vars\")))" << endl
;
210 * Generate a package definition. Add use references equivalent to the idl file's include statements.
212 void t_cl_generator::package_def(std::ostream
&out
) {
213 const vector
<t_program
*>& includes
= program_
->get_includes();
215 out
<< "(thrift:def-package :" << package();
216 if ( includes
.size() > 0 ) {
218 for (auto include
: includes
) {
219 out
<< " :" << include
->get_name();
223 out
<< ")" << endl
<< endl
;
226 void t_cl_generator::package_in(std::ostream
&out
) {
227 out
<< "(cl:in-package :" << package() << ")" << endl
<< endl
;
231 * Generates a typedef. This is not done in Common Lisp, types are all implicit.
233 * @param ttypedef The type definition
235 void t_cl_generator::generate_typedef(t_typedef
* ttypedef
) {
239 void t_cl_generator::generate_enum(t_enum
* tenum
) {
240 f_types_
<< "(thrift:def-enum " << prefix(tenum
->get_name()) << endl
;
242 vector
<t_enum_value
*> constants
= tenum
->get_constants();
243 vector
<t_enum_value
*>::iterator c_iter
;
247 f_types_
<< indent() << "(";
248 for (c_iter
= constants
.begin(); c_iter
!= constants
.end(); ++c_iter
) {
249 value
= (*c_iter
)->get_value();
251 if(c_iter
!= constants
.begin()) f_types_
<< endl
<< indent() << " ";
253 f_types_
<< "(\"" << (*c_iter
)->get_name() << "\" . " << value
<< ")";
256 f_types_
<< "))" << endl
<< endl
;
260 * Generate a constant value
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();
267 f_vars_
<< "(thrift:def-constant " << prefix(name
) << " " << render_const_value(type
, value
) << ")"
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
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();
282 case t_base_type::TYPE_STRING
:
283 out
<< "\"" << value
->get_string() << "\"";
285 case t_base_type::TYPE_BOOL
:
286 out
<< (value
->get_integer() > 0 ? "t" : "nil");
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();
294 case t_base_type::TYPE_DOUBLE
:
295 if (value
->get_type() == t_const_value::CV_INTEGER
) {
296 out
<< value
->get_integer();
298 out
<< value
->get_double();
302 throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase
);
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
;
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
;
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();
323 if (field_type
== NULL
) {
324 throw "type error: " + type
->get_name() + " has no field " + v_iter
->first
->get_string();
327 out
<< indent() << ":" << v_iter
->first
->get_string() << " " <<
328 render_const_value(field_type
, v_iter
->second
) << endl
;
330 out
<< indent() << ")";
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 ";
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
) << ")";
347 out
<< indent() << ")";
348 } else if (type
->is_list() || type
->is_set()) {
350 if (type
->is_list()) {
351 etype
= ((t_list
*)type
)->get_elem_type();
353 etype
= ((t_set
*)type
)->get_elem_type();
355 if (type
->is_set()) {
356 out
<< "(thrift:set" << endl
;
358 out
<< "(thrift:list" << endl
;
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
;
367 out
<< indent() << ")";
371 throw "CANNOT GENERATE CONSTANT FOR TYPE: " + type
->get_name();
376 void t_cl_generator::generate_struct(t_struct
* tstruct
) {
377 generate_cl_struct(f_types_
, tstruct
, false);
380 void t_cl_generator::generate_xception(t_struct
* txception
) {
381 generate_cl_struct(f_types_
, txception
, true);
384 void t_cl_generator::generate_cl_struct_internal(std::ostream
& out
, t_struct
* tstruct
, bool is_exception
) {
386 const vector
<t_field
*>& members
= tstruct
->get_members();
387 vector
<t_field
*>::const_iterator m_iter
;
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();
395 if (m_iter
!= members
.begin()) {
396 out
<< endl
<< indent() << " ";
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";
405 out
<< " :type string";
407 out
<< " :type " << typespec(type
);
408 if ( (*m_iter
)->get_req() == t_field::T_OPTIONAL
) {
409 out
<< " :optional t";
411 if ( (*m_iter
)->has_doc()) {
412 out
<< " :documentation \"" << cl_docstring((*m_iter
)->get_doc()) << "\"";
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
;
425 if ( tstruct
->has_doc() ) {
427 out
<< "\"" << cl_docstring(tstruct
->get_doc()) << "\"" << endl
;
430 generate_cl_struct_internal(out
, tstruct
, is_exception
);
432 out
<< ")" << endl
<< endl
;
435 void t_cl_generator::generate_exception_sig(std::ostream
& out
, t_function
* f
) {
436 generate_cl_struct_internal(out
, f
->get_xceptions(), true);
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
;
444 if (tservice
->get_extends() != NULL
) {
445 extends_client
= type_name(tservice
->get_extends());
448 extends_client
= extends_client
.empty() ? "nil" : prefix(extends_client
);
450 f_types_
<< "(thrift:def-service " << prefix(service_name_
) << " "
455 if ( tservice
->has_doc()) {
456 f_types_
<< endl
<< indent()
457 << "(:documentation \"" << cl_docstring(tservice
->get_doc()) << "\")";
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();
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
);
473 if ( (*f_iter
)->is_oneway() ) {
474 f_types_
<< endl
<< indent() << " :oneway t";
476 if ( (*f_iter
)->has_doc() ) {
477 f_types_
<< endl
<< indent() << " :documentation \""
478 << cl_docstring((*f_iter
)->get_doc()) << "\"";
483 f_types_
<< ")" << endl
<< endl
;
488 string
t_cl_generator::typespec(t_type
*t
) {
489 t
= get_true_type(t
);
491 if (t
-> is_binary()){
493 } else if (t
->is_base_type()) {
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() + "\")";
508 throw "Sorry, I don't know how to generate this: " + type_name(t
);
512 string
t_cl_generator::function_signature(t_function
* tfunction
) {
513 return argument_list(tfunction
->get_arglist());
516 string
t_cl_generator::argument_list(t_struct
* tstruct
) {
520 const vector
<t_field
*>& fields
= tstruct
->get_members();
521 vector
<t_field
*>::const_iterator f_iter
;
523 for (f_iter
= fields
.begin(); f_iter
!= fields
.end(); ++f_iter
) {
529 res
<< "(" + prefix((*f_iter
)->get_name()) << " " <<
530 typespec((*f_iter
)->get_type()) << " " <<
531 (*f_iter
)->get_key() << ")";
539 string
t_cl_generator::type_name(t_type
* ttype
) {
541 t_program
* program
= ttype
->get_program();
543 if (program
!= NULL
&& program
!= program_
)
544 prefix
= package_of(program
) == package() ? "" : package_of(program
) + ":";
546 string name
= ttype
->get_name();
548 if (ttype
->is_struct() || ttype
->is_xception())
549 name
= lowercase(ttype
->get_name());
551 return prefix
+ name
;
554 THRIFT_REGISTER_GENERATOR(
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")