]>
Commit | Line | Data |
---|---|---|
f67539c2 TL |
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") |