]>
Commit | Line | Data |
---|---|---|
f67539c2 TL |
1 | /* |
2 | * Licensed to the Apache Software Foundation (ASF) under one | |
3 | * or more contributor license agreements. See the NOTICE file | |
4 | * distributed with this work for additional information | |
5 | * regarding copyright ownership. The ASF licenses this file | |
6 | * to you under the Apache License, Version 2.0 (the | |
7 | * "License"); you may not use this file except in compliance | |
8 | * with the License. You may obtain a copy of the License at | |
9 | * | |
10 | * http://www.apache.org/licenses/LICENSE-2.0 | |
11 | * | |
12 | * Unless required by applicable law or agreed to in writing, | |
13 | * software distributed under the License is distributed on an | |
14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
15 | * KIND, either express or implied. See the License for the | |
16 | * specific language governing permissions and limitations | |
17 | * under the License. | |
18 | */ | |
19 | ||
20 | #include <string> | |
21 | #include <fstream> | |
22 | #include <iostream> | |
23 | #include <vector> | |
24 | ||
25 | #include <stdlib.h> | |
26 | #include <sys/stat.h> | |
27 | #include <sys/types.h> | |
28 | #include <sstream> | |
29 | #include "thrift/platform.h" | |
30 | #include "thrift/version.h" | |
31 | #include "thrift/generate/t_oop_generator.h" | |
32 | ||
33 | using std::ios; | |
34 | using std::map; | |
35 | using std::ostream; | |
36 | using std::ostringstream; | |
37 | using std::string; | |
38 | using std::stringstream; | |
39 | using std::vector; | |
40 | ||
41 | static const string endl = "\n"; // avoid ostream << std::endl flushes | |
42 | ||
43 | /** | |
44 | * OCaml code generator. | |
45 | * | |
46 | */ | |
47 | class t_ocaml_generator : public t_oop_generator { | |
48 | public: | |
49 | t_ocaml_generator(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 | (void)option_string; | |
54 | std::map<std::string, std::string>::const_iterator iter; | |
55 | ||
56 | /* no options yet */ | |
57 | for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) { | |
58 | throw "unknown option ocaml:" + iter->first; | |
59 | } | |
60 | ||
61 | out_dir_base_ = "gen-ocaml"; | |
62 | } | |
63 | ||
64 | /** | |
65 | * Init and close methods | |
66 | */ | |
67 | ||
68 | void init_generator() override; | |
69 | void close_generator() override; | |
70 | ||
71 | /** | |
72 | * Program-level generation functions | |
73 | */ | |
74 | void generate_program() override; | |
75 | void generate_typedef(t_typedef* ttypedef) override; | |
76 | void generate_enum(t_enum* tenum) override; | |
77 | void generate_const(t_const* tconst) override; | |
78 | void generate_struct(t_struct* tstruct) override; | |
79 | void generate_xception(t_struct* txception) override; | |
80 | void generate_service(t_service* tservice) override; | |
81 | ||
82 | std::string render_const_value(t_type* type, t_const_value* value); | |
83 | bool struct_member_persistent(t_field* tmember); | |
84 | bool struct_member_omitable(t_field* tmember); | |
85 | bool struct_member_default_cheaply_comparable(t_field* tmember); | |
86 | std::string struct_member_copy_of(t_type* type, string what); | |
87 | ||
88 | /** | |
89 | * Struct generation code | |
90 | */ | |
91 | ||
92 | void generate_ocaml_struct(t_struct* tstruct, bool is_exception); | |
93 | void generate_ocaml_struct_definition(std::ostream& out, | |
94 | t_struct* tstruct, | |
95 | bool is_xception = false); | |
96 | void generate_ocaml_struct_member(std::ostream& out, string tname, t_field* tmember); | |
97 | void generate_ocaml_struct_sig(std::ostream& out, t_struct* tstruct, bool is_exception); | |
98 | void generate_ocaml_struct_reader(std::ostream& out, t_struct* tstruct); | |
99 | void generate_ocaml_struct_writer(std::ostream& out, t_struct* tstruct); | |
100 | void generate_ocaml_function_helpers(t_function* tfunction); | |
101 | void generate_ocaml_method_copy(std::ostream& out, const vector<t_field*>& members); | |
102 | void generate_ocaml_member_copy(std::ostream& out, t_field* member); | |
103 | ||
104 | /** | |
105 | * Service-level generation functions | |
106 | */ | |
107 | ||
108 | void generate_service_helpers(t_service* tservice); | |
109 | void generate_service_interface(t_service* tservice); | |
110 | void generate_service_client(t_service* tservice); | |
111 | void generate_service_server(t_service* tservice); | |
112 | void generate_process_function(t_service* tservice, t_function* tfunction); | |
113 | ||
114 | /** | |
115 | * Serialization constructs | |
116 | */ | |
117 | ||
118 | void generate_deserialize_field(std::ostream& out, t_field* tfield, std::string prefix); | |
119 | ||
120 | void generate_deserialize_struct(std::ostream& out, t_struct* tstruct); | |
121 | ||
122 | void generate_deserialize_container(std::ostream& out, t_type* ttype); | |
123 | ||
124 | void generate_deserialize_set_element(std::ostream& out, t_set* tset); | |
125 | ||
126 | void generate_deserialize_list_element(std::ostream& out, | |
127 | t_list* tlist, | |
128 | std::string prefix = ""); | |
129 | void generate_deserialize_type(std::ostream& out, t_type* type); | |
130 | ||
131 | void generate_serialize_field(std::ostream& out, t_field* tfield, std::string name = ""); | |
132 | ||
133 | void generate_serialize_struct(std::ostream& out, t_struct* tstruct, std::string prefix = ""); | |
134 | ||
135 | void generate_serialize_container(std::ostream& out, t_type* ttype, std::string prefix = ""); | |
136 | ||
137 | void generate_serialize_map_element(std::ostream& out, | |
138 | t_map* tmap, | |
139 | std::string kiter, | |
140 | std::string viter); | |
141 | ||
142 | void generate_serialize_set_element(std::ostream& out, t_set* tmap, std::string iter); | |
143 | ||
144 | void generate_serialize_list_element(std::ostream& out, t_list* tlist, std::string iter); | |
145 | ||
146 | /** | |
147 | * Helper rendering functions | |
148 | */ | |
149 | ||
150 | std::string ocaml_autogen_comment(); | |
151 | std::string ocaml_imports(); | |
152 | std::string type_name(t_type* ttype); | |
153 | std::string function_signature(t_function* tfunction, std::string prefix = ""); | |
154 | std::string function_type(t_function* tfunc, bool method = false, bool options = false); | |
155 | std::string argument_list(t_struct* tstruct); | |
156 | std::string type_to_enum(t_type* ttype); | |
157 | std::string render_ocaml_type(t_type* type); | |
158 | ||
159 | private: | |
160 | /** | |
161 | * File streams | |
162 | */ | |
163 | ||
164 | ofstream_with_content_based_conditional_update f_types_; | |
165 | ofstream_with_content_based_conditional_update f_consts_; | |
166 | ofstream_with_content_based_conditional_update f_service_; | |
167 | ||
168 | ofstream_with_content_based_conditional_update f_types_i_; | |
169 | ofstream_with_content_based_conditional_update f_service_i_; | |
170 | }; | |
171 | ||
172 | /* | |
173 | * This is necessary because we want typedefs to appear later, | |
174 | * after all the types have been declared. | |
175 | */ | |
176 | void t_ocaml_generator::generate_program() { | |
177 | // Initialize the generator | |
178 | init_generator(); | |
179 | ||
180 | // Generate enums | |
181 | vector<t_enum*> enums = program_->get_enums(); | |
182 | vector<t_enum*>::iterator en_iter; | |
183 | for (en_iter = enums.begin(); en_iter != enums.end(); ++en_iter) { | |
184 | generate_enum(*en_iter); | |
185 | } | |
186 | ||
187 | // Generate structs | |
188 | vector<t_struct*> structs = program_->get_structs(); | |
189 | vector<t_struct*>::iterator st_iter; | |
190 | for (st_iter = structs.begin(); st_iter != structs.end(); ++st_iter) { | |
191 | generate_struct(*st_iter); | |
192 | } | |
193 | ||
194 | // Generate xceptions | |
195 | vector<t_struct*> xceptions = program_->get_xceptions(); | |
196 | vector<t_struct*>::iterator x_iter; | |
197 | for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { | |
198 | generate_xception(*x_iter); | |
199 | } | |
200 | ||
201 | // Generate typedefs | |
202 | vector<t_typedef*> typedefs = program_->get_typedefs(); | |
203 | vector<t_typedef*>::iterator td_iter; | |
204 | for (td_iter = typedefs.begin(); td_iter != typedefs.end(); ++td_iter) { | |
205 | generate_typedef(*td_iter); | |
206 | } | |
207 | ||
208 | // Generate services | |
209 | vector<t_service*> services = program_->get_services(); | |
210 | vector<t_service*>::iterator sv_iter; | |
211 | for (sv_iter = services.begin(); sv_iter != services.end(); ++sv_iter) { | |
212 | service_name_ = get_service_name(*sv_iter); | |
213 | generate_service(*sv_iter); | |
214 | } | |
215 | ||
216 | // Generate constants | |
217 | vector<t_const*> consts = program_->get_consts(); | |
218 | generate_consts(consts); | |
219 | ||
220 | // Close the generator | |
221 | close_generator(); | |
222 | } | |
223 | ||
224 | /** | |
225 | * Prepares for file generation by opening up the necessary file output | |
226 | * streams. | |
227 | * | |
228 | * @param tprogram The program to generate | |
229 | */ | |
230 | void t_ocaml_generator::init_generator() { | |
231 | // Make output directory | |
232 | MKDIR(get_out_dir().c_str()); | |
233 | ||
234 | // Make output file | |
235 | string f_types_name = get_out_dir() + program_name_ + "_types.ml"; | |
236 | f_types_.open(f_types_name.c_str()); | |
237 | string f_types_i_name = get_out_dir() + program_name_ + "_types.mli"; | |
238 | f_types_i_.open(f_types_i_name.c_str()); | |
239 | ||
240 | string f_consts_name = get_out_dir() + program_name_ + "_consts.ml"; | |
241 | f_consts_.open(f_consts_name.c_str()); | |
242 | ||
243 | // Print header | |
244 | f_types_ << ocaml_autogen_comment() << endl << ocaml_imports() << endl; | |
245 | f_types_i_ << ocaml_autogen_comment() << endl << ocaml_imports() << endl; | |
246 | f_consts_ << ocaml_autogen_comment() << endl << ocaml_imports() << endl << "open " | |
247 | << capitalize(program_name_) << "_types" << endl; | |
248 | } | |
249 | ||
250 | /** | |
251 | * Autogen'd comment | |
252 | */ | |
253 | string t_ocaml_generator::ocaml_autogen_comment() { | |
254 | return std::string("(*\n") + " Autogenerated by Thrift Compiler (" + THRIFT_VERSION + ")\n" + "\n" | |
255 | + " DO NOT EDIT UNLESS YOU ARE SURE YOU KNOW WHAT YOU ARE DOING\n" + "*)\n"; | |
256 | } | |
257 | ||
258 | /** | |
259 | * Prints standard thrift imports | |
260 | */ | |
261 | string t_ocaml_generator::ocaml_imports() { | |
262 | return "open Thrift"; | |
263 | } | |
264 | ||
265 | /** | |
266 | * Closes the type files | |
267 | */ | |
268 | void t_ocaml_generator::close_generator() { | |
269 | // Close types file | |
270 | f_types_.close(); | |
271 | } | |
272 | ||
273 | /** | |
274 | * Generates a typedef. Ez. | |
275 | * | |
276 | * @param ttypedef The type definition | |
277 | */ | |
278 | void t_ocaml_generator::generate_typedef(t_typedef* ttypedef) { | |
279 | f_types_ << indent() << "type " << decapitalize(ttypedef->get_symbolic()) << " = " | |
280 | << render_ocaml_type(ttypedef->get_type()) << endl << endl; | |
281 | f_types_i_ << indent() << "type " << decapitalize(ttypedef->get_symbolic()) << " = " | |
282 | << render_ocaml_type(ttypedef->get_type()) << endl << endl; | |
283 | } | |
284 | ||
285 | /** | |
286 | * Generates code for an enumerated type. | |
287 | * the values. | |
288 | * | |
289 | * @param tenum The enumeration | |
290 | */ | |
291 | void t_ocaml_generator::generate_enum(t_enum* tenum) { | |
292 | indent(f_types_) << "module " << capitalize(tenum->get_name()) << " = " << endl << "struct" | |
293 | << endl; | |
294 | indent(f_types_i_) << "module " << capitalize(tenum->get_name()) << " : " << endl << "sig" | |
295 | << endl; | |
296 | indent_up(); | |
297 | indent(f_types_) << "type t = " << endl; | |
298 | indent(f_types_i_) << "type t = " << endl; | |
299 | indent_up(); | |
300 | vector<t_enum_value*> constants = tenum->get_constants(); | |
301 | vector<t_enum_value*>::iterator c_iter; | |
302 | for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { | |
303 | string name = capitalize((*c_iter)->get_name()); | |
304 | indent(f_types_) << "| " << name << endl; | |
305 | indent(f_types_i_) << "| " << name << endl; | |
306 | } | |
307 | indent_down(); | |
308 | ||
309 | indent(f_types_) << "let to_i = function" << endl; | |
310 | indent(f_types_i_) << "val to_i : t -> Int32.t" << endl; | |
311 | indent_up(); | |
312 | for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { | |
313 | int value = (*c_iter)->get_value(); | |
314 | string name = capitalize((*c_iter)->get_name()); | |
315 | indent(f_types_) << "| " << name << " -> " << value << "l" << endl; | |
316 | } | |
317 | indent_down(); | |
318 | ||
319 | indent(f_types_) << "let of_i = function" << endl; | |
320 | indent(f_types_i_) << "val of_i : Int32.t -> t" << endl; | |
321 | indent_up(); | |
322 | for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { | |
323 | int value = (*c_iter)->get_value(); | |
324 | string name = capitalize((*c_iter)->get_name()); | |
325 | indent(f_types_) << "| " << value << "l -> " << name << endl; | |
326 | } | |
327 | indent(f_types_) << "| _ -> raise Thrift_error" << endl; | |
328 | indent_down(); | |
329 | indent_down(); | |
330 | indent(f_types_) << "end" << endl; | |
331 | indent(f_types_i_) << "end" << endl; | |
332 | } | |
333 | ||
334 | /** | |
335 | * Generate a constant value | |
336 | */ | |
337 | void t_ocaml_generator::generate_const(t_const* tconst) { | |
338 | t_type* type = tconst->get_type(); | |
339 | string name = decapitalize(tconst->get_name()); | |
340 | t_const_value* value = tconst->get_value(); | |
341 | ||
342 | indent(f_consts_) << "let " << name << " = " << render_const_value(type, value) << endl << endl; | |
343 | } | |
344 | ||
345 | /** | |
346 | * Prints the value of a constant with the given type. Note that type checking | |
347 | * is NOT performed in this function as it is always run beforehand using the | |
348 | * validate_types method in main.cc | |
349 | */ | |
350 | string t_ocaml_generator::render_const_value(t_type* type, t_const_value* value) { | |
351 | type = get_true_type(type); | |
352 | std::ostringstream out; | |
353 | // OCaml requires all floating point numbers contain a decimal point | |
354 | out.setf(ios::showpoint); | |
355 | if (type->is_base_type()) { | |
356 | t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); | |
357 | switch (tbase) { | |
358 | case t_base_type::TYPE_STRING: | |
359 | out << '"' << get_escaped_string(value) << '"'; | |
360 | break; | |
361 | case t_base_type::TYPE_BOOL: | |
362 | out << (value->get_integer() > 0 ? "true" : "false"); | |
363 | break; | |
364 | case t_base_type::TYPE_I8: | |
365 | case t_base_type::TYPE_I16: | |
366 | out << value->get_integer(); | |
367 | break; | |
368 | case t_base_type::TYPE_I32: | |
369 | out << value->get_integer() << "l"; | |
370 | break; | |
371 | case t_base_type::TYPE_I64: | |
372 | out << value->get_integer() << "L"; | |
373 | break; | |
374 | case t_base_type::TYPE_DOUBLE: | |
375 | if (value->get_type() == t_const_value::CV_INTEGER) { | |
376 | out << value->get_integer() << ".0"; | |
377 | } else { | |
378 | out << value->get_double(); | |
379 | } | |
380 | break; | |
381 | default: | |
382 | throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase); | |
383 | } | |
384 | } else if (type->is_enum()) { | |
385 | t_enum* tenum = (t_enum*)type; | |
386 | vector<t_enum_value*> constants = tenum->get_constants(); | |
387 | vector<t_enum_value*>::iterator c_iter; | |
388 | for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { | |
389 | int val = (*c_iter)->get_value(); | |
390 | if (val == value->get_integer()) { | |
391 | indent(out) << capitalize(tenum->get_name()) << "." << capitalize((*c_iter)->get_name()); | |
392 | break; | |
393 | } | |
394 | } | |
395 | } else if (type->is_struct() || type->is_xception()) { | |
396 | string cname = type_name(type); | |
397 | string ct = tmp("_c"); | |
398 | out << endl; | |
399 | indent_up(); | |
400 | indent(out) << "(let " << ct << " = new " << cname << " in" << endl; | |
401 | indent_up(); | |
402 | const vector<t_field*>& fields = ((t_struct*)type)->get_members(); | |
403 | vector<t_field*>::const_iterator f_iter; | |
404 | const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map(); | |
405 | map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter; | |
406 | for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { | |
407 | t_type* field_type = NULL; | |
408 | for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { | |
409 | if ((*f_iter)->get_name() == v_iter->first->get_string()) { | |
410 | field_type = (*f_iter)->get_type(); | |
411 | } | |
412 | } | |
413 | if (field_type == NULL) { | |
414 | throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string(); | |
415 | } | |
416 | string fname = v_iter->first->get_string(); | |
417 | out << indent(); | |
418 | out << ct << "#set_" << fname << " "; | |
419 | out << render_const_value(field_type, v_iter->second); | |
420 | out << ";" << endl; | |
421 | } | |
422 | indent(out) << ct << ")"; | |
423 | indent_down(); | |
424 | indent_down(); | |
425 | } else if (type->is_map()) { | |
426 | t_type* ktype = ((t_map*)type)->get_key_type(); | |
427 | t_type* vtype = ((t_map*)type)->get_val_type(); | |
428 | const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map(); | |
429 | map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter; | |
430 | string hm = tmp("_hm"); | |
431 | out << endl; | |
432 | indent_up(); | |
433 | indent(out) << "(let " << hm << " = Hashtbl.create " << val.size() << " in" << endl; | |
434 | indent_up(); | |
435 | for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { | |
436 | string key = render_const_value(ktype, v_iter->first); | |
437 | string val = render_const_value(vtype, v_iter->second); | |
438 | indent(out) << "Hashtbl.add " << hm << " " << key << " " << val << ";" << endl; | |
439 | } | |
440 | indent(out) << hm << ")"; | |
441 | indent_down(); | |
442 | indent_down(); | |
443 | } else if (type->is_list()) { | |
444 | t_type* etype; | |
445 | etype = ((t_list*)type)->get_elem_type(); | |
446 | out << "[" << endl; | |
447 | indent_up(); | |
448 | const vector<t_const_value*>& val = value->get_list(); | |
449 | vector<t_const_value*>::const_iterator v_iter; | |
450 | for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { | |
451 | out << indent(); | |
452 | out << render_const_value(etype, *v_iter); | |
453 | out << ";" << endl; | |
454 | } | |
455 | indent_down(); | |
456 | indent(out) << "]"; | |
457 | } else if (type->is_set()) { | |
458 | t_type* etype = ((t_set*)type)->get_elem_type(); | |
459 | const vector<t_const_value*>& val = value->get_list(); | |
460 | vector<t_const_value*>::const_iterator v_iter; | |
461 | string hm = tmp("_hm"); | |
462 | indent(out) << "(let " << hm << " = Hashtbl.create " << val.size() << " in" << endl; | |
463 | indent_up(); | |
464 | for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { | |
465 | string val = render_const_value(etype, *v_iter); | |
466 | indent(out) << "Hashtbl.add " << hm << " " << val << " true;" << endl; | |
467 | } | |
468 | indent(out) << hm << ")" << endl; | |
469 | indent_down(); | |
470 | out << endl; | |
471 | } else { | |
472 | throw "CANNOT GENERATE CONSTANT FOR TYPE: " + type->get_name(); | |
473 | } | |
474 | return out.str(); | |
475 | } | |
476 | ||
477 | /** | |
478 | * Generates a "struct" | |
479 | */ | |
480 | void t_ocaml_generator::generate_struct(t_struct* tstruct) { | |
481 | generate_ocaml_struct(tstruct, false); | |
482 | } | |
483 | ||
484 | /** | |
485 | * Generates a struct definition for a thrift exception. Basically the same | |
486 | * as a struct, but also has an exception declaration. | |
487 | * | |
488 | * @param txception The struct definition | |
489 | */ | |
490 | void t_ocaml_generator::generate_xception(t_struct* txception) { | |
491 | generate_ocaml_struct(txception, true); | |
492 | } | |
493 | ||
494 | /** | |
495 | * Generates an OCaml struct | |
496 | */ | |
497 | void t_ocaml_generator::generate_ocaml_struct(t_struct* tstruct, bool is_exception) { | |
498 | generate_ocaml_struct_definition(f_types_, tstruct, is_exception); | |
499 | generate_ocaml_struct_sig(f_types_i_, tstruct, is_exception); | |
500 | } | |
501 | ||
502 | void t_ocaml_generator::generate_ocaml_method_copy(ostream& out, const vector<t_field*>& members) { | |
503 | vector<t_field*>::const_iterator m_iter; | |
504 | ||
505 | /* Create a copy of the current object */ | |
506 | indent(out) << "method copy =" << endl; | |
507 | indent_up(); | |
508 | indent_up(); | |
509 | indent(out) << "let _new = Oo.copy self in" << endl; | |
510 | for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) | |
511 | generate_ocaml_member_copy(out, *m_iter); | |
512 | ||
513 | indent_down(); | |
514 | indent(out) << "_new" << endl; | |
515 | indent_down(); | |
516 | } | |
517 | ||
518 | string t_ocaml_generator::struct_member_copy_of(t_type* type, string what) { | |
519 | if (type->is_struct() || type->is_xception()) { | |
520 | return what + string("#copy"); | |
521 | } | |
522 | if (type->is_map()) { | |
523 | string copy_of_k = struct_member_copy_of(((t_map*)type)->get_key_type(), "k"); | |
524 | string copy_of_v = struct_member_copy_of(((t_map*)type)->get_val_type(), "v"); | |
525 | ||
526 | if (copy_of_k == "k" && copy_of_v == "v") { | |
527 | return string("(Hashtbl.copy ") + what + string(")"); | |
528 | } else { | |
529 | return string( | |
530 | "((fun oh -> let nh = Hashtbl.create (Hashtbl.length oh) in Hashtbl.iter (fun k v " | |
531 | "-> Hashtbl.add nh ") + copy_of_k + string(" ") + copy_of_v + string(") oh; nh) ") | |
532 | + what + ")"; | |
533 | } | |
534 | } | |
535 | if (type->is_set()) { | |
536 | string copy_of = struct_member_copy_of(((t_set*)type)->get_elem_type(), "k"); | |
537 | ||
538 | if (copy_of == "k") { | |
539 | return string("(Hashtbl.copy ") + what + string(")"); | |
540 | } else { | |
541 | return string( | |
542 | "((fun oh -> let nh = Hashtbl.create (Hashtbl.length oh) in Hashtbl.iter (fun k v " | |
543 | "-> Hashtbl.add nh ") + copy_of + string(" true") + string(") oh; nh) ") + what | |
544 | + ")"; | |
545 | } | |
546 | } | |
547 | if (type->is_list()) { | |
548 | string copy_of = struct_member_copy_of(((t_list*)type)->get_elem_type(), "x"); | |
549 | if (copy_of != "x") { | |
550 | return string("(List.map (fun x -> ") + copy_of + string(") ") + what + string(")"); | |
551 | } else { | |
552 | return what; | |
553 | } | |
554 | } | |
555 | return what; | |
556 | } | |
557 | ||
558 | void t_ocaml_generator::generate_ocaml_member_copy(ostream& out, t_field* tmember) { | |
559 | string mname = decapitalize(tmember->get_name()); | |
560 | t_type* type = get_true_type(tmember->get_type()); | |
561 | ||
562 | string grab_field = string("self#grab_") + mname; | |
563 | string copy_of = struct_member_copy_of(type, grab_field); | |
564 | if (copy_of != grab_field) { | |
565 | indent(out); | |
566 | if (!struct_member_persistent(tmember)) { | |
567 | out << "if _" << mname << " <> None then" << endl; | |
568 | indent(out) << " "; | |
569 | } | |
570 | out << "_new#set_" << mname << " " << copy_of << ";" << endl; | |
571 | } | |
572 | } | |
573 | ||
574 | /** | |
575 | * Generates a struct definition for a thrift data type. | |
576 | * | |
577 | * @param tstruct The struct definition | |
578 | */ | |
579 | void t_ocaml_generator::generate_ocaml_struct_definition(ostream& out, | |
580 | t_struct* tstruct, | |
581 | bool is_exception) { | |
582 | const vector<t_field*>& members = tstruct->get_members(); | |
583 | vector<t_field*>::const_iterator m_iter; | |
584 | string tname = type_name(tstruct); | |
585 | indent(out) << "class " << tname << " =" << endl; | |
586 | indent(out) << "object (self)" << endl; | |
587 | ||
588 | indent_up(); | |
589 | ||
590 | if (members.size() > 0) { | |
591 | for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { | |
592 | generate_ocaml_struct_member(out, tname, (*m_iter)); | |
593 | out << endl; | |
594 | } | |
595 | } | |
596 | generate_ocaml_method_copy(out, members); | |
597 | generate_ocaml_struct_writer(out, tstruct); | |
598 | indent_down(); | |
599 | indent(out) << "end" << endl; | |
600 | ||
601 | if (is_exception) { | |
602 | indent(out) << "exception " << capitalize(tname) << " of " << tname << endl; | |
603 | } | |
604 | ||
605 | generate_ocaml_struct_reader(out, tstruct); | |
606 | } | |
607 | ||
608 | /** | |
609 | * Generates a structure member for a thrift data type. | |
610 | * | |
611 | * @param tname Name of the parent structure for the member | |
612 | * @param tmember Member definition | |
613 | */ | |
614 | void t_ocaml_generator::generate_ocaml_struct_member(ostream& out, | |
615 | string tname, | |
616 | t_field* tmember) { | |
617 | string x = tmp("_x"); | |
618 | string mname = decapitalize(tmember->get_name()); | |
619 | ||
620 | indent(out) << "val mutable _" << mname << " : " << render_ocaml_type(tmember->get_type()); | |
621 | t_const_value* val = tmember->get_value(); | |
622 | if (val) { | |
623 | if (struct_member_persistent(tmember)) | |
624 | out << " = " << render_const_value(tmember->get_type(), tmember->get_value()) << endl; | |
625 | else | |
626 | out << " option = Some " << render_const_value(tmember->get_type(), tmember->get_value()) | |
627 | << endl; | |
628 | } else { | |
629 | // assert(!struct_member_persistent(tmember)) | |
630 | out << " option = None" << endl; | |
631 | } | |
632 | ||
633 | if (struct_member_persistent(tmember)) { | |
634 | indent(out) << "method get_" << mname << " = Some _" << mname << endl; | |
635 | indent(out) << "method grab_" << mname << " = _" << mname << endl; | |
636 | indent(out) << "method set_" << mname << " " << x << " = _" << mname << " <- " << x << endl; | |
637 | } else { | |
638 | indent(out) << "method get_" << mname << " = _" << mname << endl; | |
639 | indent(out) << "method grab_" << mname << " = match _" << mname | |
640 | << " with None->raise (Field_empty \"" << tname << "." << mname << "\") | Some " | |
641 | << x << " -> " << x << endl; | |
642 | indent(out) << "method set_" << mname << " " << x << " = _" << mname << " <- Some " << x | |
643 | << endl; | |
644 | indent(out) << "method unset_" << mname << " = _" << mname << " <- None" << endl; | |
645 | } | |
646 | ||
647 | indent(out) << "method reset_" << mname << " = _" << mname << " <- "; | |
648 | if (val) { | |
649 | if (struct_member_persistent(tmember)) | |
650 | out << render_const_value(tmember->get_type(), tmember->get_value()) << endl; | |
651 | else | |
652 | out << "Some " << render_const_value(tmember->get_type(), tmember->get_value()) << endl; | |
653 | } else { | |
654 | out << "None" << endl; | |
655 | } | |
656 | } | |
657 | ||
658 | /** | |
659 | * Check whether a member of the structure can not have undefined value | |
660 | * | |
661 | * @param tmember Member definition | |
662 | */ | |
663 | bool t_ocaml_generator::struct_member_persistent(t_field* tmember) { | |
664 | t_const_value* val = tmember->get_value(); | |
665 | return (val ? true : false); | |
666 | } | |
667 | ||
668 | /** | |
669 | * Check whether a member of the structure can be skipped during encoding | |
670 | * | |
671 | * @param tmember Member definition | |
672 | */ | |
673 | bool t_ocaml_generator::struct_member_omitable(t_field* tmember) { | |
674 | return (tmember->get_req() != t_field::T_REQUIRED); | |
675 | } | |
676 | ||
677 | /** | |
678 | * Figure out whether a member of the structure has | |
679 | * a cheaply comparable default value. | |
680 | * | |
681 | * @param tmember Member definition | |
682 | */ | |
683 | bool t_ocaml_generator::struct_member_default_cheaply_comparable(t_field* tmember) { | |
684 | t_type* type = get_true_type(tmember->get_type()); | |
685 | t_const_value* val = tmember->get_value(); | |
686 | if (!val) { | |
687 | return false; | |
688 | } else if (type->is_base_type()) { | |
689 | // Base types are generally cheaply compared for structural equivalence. | |
690 | switch (((t_base_type*)type)->get_base()) { | |
691 | case t_base_type::TYPE_DOUBLE: | |
692 | if (val->get_double() == 0.0) | |
693 | return true; | |
694 | else | |
695 | return false; | |
696 | default: | |
697 | return true; | |
698 | } | |
699 | } else if (type->is_list()) { | |
700 | // Empty lists are cheaply compared for structural equivalence. | |
701 | // Is empty list? | |
702 | if (val->get_list().size() == 0) | |
703 | return true; | |
704 | else | |
705 | return false; | |
706 | } else { | |
707 | return false; | |
708 | } | |
709 | } | |
710 | ||
711 | /** | |
712 | * Generates a struct definition for a thrift data type. | |
713 | * | |
714 | * @param tstruct The struct definition | |
715 | */ | |
716 | void t_ocaml_generator::generate_ocaml_struct_sig(ostream& out, | |
717 | t_struct* tstruct, | |
718 | bool is_exception) { | |
719 | const vector<t_field*>& members = tstruct->get_members(); | |
720 | vector<t_field*>::const_iterator m_iter; | |
721 | string tname = type_name(tstruct); | |
722 | indent(out) << "class " << tname << " :" << endl; | |
723 | indent(out) << "object ('a)" << endl; | |
724 | ||
725 | indent_up(); | |
726 | ||
727 | string x = tmp("_x"); | |
728 | if (members.size() > 0) { | |
729 | for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { | |
730 | string mname = decapitalize((*m_iter)->get_name()); | |
731 | string type = render_ocaml_type((*m_iter)->get_type()); | |
732 | indent(out) << "method get_" << mname << " : " << type << " option" << endl; | |
733 | indent(out) << "method grab_" << mname << " : " << type << endl; | |
734 | indent(out) << "method set_" << mname << " : " << type << " -> unit" << endl; | |
735 | if (!struct_member_persistent(*m_iter)) | |
736 | indent(out) << "method unset_" << mname << " : unit" << endl; | |
737 | indent(out) << "method reset_" << mname << " : unit" << endl; | |
738 | } | |
739 | } | |
740 | indent(out) << "method copy : 'a" << endl; | |
741 | indent(out) << "method write : Protocol.t -> unit" << endl; | |
742 | indent_down(); | |
743 | indent(out) << "end" << endl; | |
744 | ||
745 | if (is_exception) { | |
746 | indent(out) << "exception " << capitalize(tname) << " of " << tname << endl; | |
747 | } | |
748 | ||
749 | indent(out) << "val read_" << tname << " : Protocol.t -> " << tname << endl; | |
750 | } | |
751 | ||
752 | /** | |
753 | * Generates the read method for a struct | |
754 | */ | |
755 | void t_ocaml_generator::generate_ocaml_struct_reader(ostream& out, t_struct* tstruct) { | |
756 | const vector<t_field*>& fields = tstruct->get_members(); | |
757 | vector<t_field*>::const_iterator f_iter; | |
758 | string sname = type_name(tstruct); | |
759 | string str = tmp("_str"); | |
760 | string t = tmp("_t"); | |
761 | string id = tmp("_id"); | |
762 | indent(out) << "let rec read_" << sname << " (iprot : Protocol.t) =" << endl; | |
763 | indent_up(); | |
764 | indent(out) << "let " << str << " = new " << sname << " in" << endl; | |
765 | indent_up(); | |
766 | indent(out) << "ignore(iprot#readStructBegin);" << endl; | |
767 | ||
768 | // Loop over reading in fields | |
769 | indent(out) << "(try while true do" << endl; | |
770 | indent_up(); | |
771 | indent_up(); | |
772 | ||
773 | // Read beginning field marker | |
774 | indent(out) << "let (_," << t << "," << id << ") = iprot#readFieldBegin in" << endl; | |
775 | ||
776 | // Check for field STOP marker and break | |
777 | indent(out) << "if " << t << " = Protocol.T_STOP then" << endl; | |
778 | indent_up(); | |
779 | indent(out) << "raise Break" << endl; | |
780 | indent_down(); | |
781 | indent(out) << "else ();" << endl; | |
782 | ||
783 | indent(out) << "(match " << id << " with " << endl; | |
784 | indent_up(); | |
785 | // Generate deserialization code for known cases | |
786 | for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { | |
787 | indent(out) << "| " << (*f_iter)->get_key() << " -> ("; | |
788 | out << "if " << t << " = " << type_to_enum((*f_iter)->get_type()) << " then" << endl; | |
789 | indent_up(); | |
790 | indent_up(); | |
791 | generate_deserialize_field(out, *f_iter, str); | |
792 | indent_down(); | |
793 | out << indent() << "else" << endl << indent() << " iprot#skip " << t << ")" << endl; | |
794 | indent_down(); | |
795 | } | |
796 | ||
797 | // In the default case we skip the field | |
798 | out << indent() << "| _ -> " | |
799 | << "iprot#skip " << t << ");" << endl; | |
800 | indent_down(); | |
801 | // Read field end marker | |
802 | indent(out) << "iprot#readFieldEnd;" << endl; | |
803 | indent_down(); | |
804 | indent(out) << "done; ()" << endl; | |
805 | indent_down(); | |
806 | indent(out) << "with Break -> ());" << endl; | |
807 | ||
808 | indent(out) << "iprot#readStructEnd;" << endl; | |
809 | ||
810 | indent(out) << str << endl << endl; | |
811 | indent_down(); | |
812 | indent_down(); | |
813 | } | |
814 | ||
815 | void t_ocaml_generator::generate_ocaml_struct_writer(ostream& out, t_struct* tstruct) { | |
816 | string name = tstruct->get_name(); | |
817 | const vector<t_field*>& fields = tstruct->get_sorted_members(); | |
818 | vector<t_field*>::const_iterator f_iter; | |
819 | string str = tmp("_str"); | |
820 | string f = tmp("_f"); | |
821 | ||
822 | indent(out) << "method write (oprot : Protocol.t) =" << endl; | |
823 | indent_up(); | |
824 | indent(out) << "oprot#writeStructBegin \"" << name << "\";" << endl; | |
825 | ||
826 | for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { | |
827 | t_field* tmember = (*f_iter); | |
828 | string mname = "_" + decapitalize(tmember->get_name()); | |
829 | string _v; | |
830 | ||
831 | if (struct_member_persistent(tmember)) { | |
832 | ||
833 | if (struct_member_omitable(tmember) && struct_member_default_cheaply_comparable(tmember)) { | |
834 | _v = "_v"; | |
835 | // Avoid redundant encoding of members having default values. | |
836 | indent(out) << "(match " << mname << " with " | |
837 | << render_const_value(tmember->get_type(), tmember->get_value()) << " -> () | " | |
838 | << _v << " -> " << endl; | |
839 | } else { | |
840 | _v = mname; | |
841 | indent(out) << "(" << endl; | |
842 | } | |
843 | ||
844 | } else { | |
845 | ||
846 | indent(out) << "(match " << mname << " with "; | |
847 | ||
848 | if (struct_member_omitable(tmember)) { | |
849 | out << "None -> ()"; | |
850 | ||
851 | if (struct_member_default_cheaply_comparable(tmember)) { | |
852 | // Avoid redundant encoding of members having default values. | |
853 | out << " | Some " << render_const_value(tmember->get_type(), tmember->get_value()) | |
854 | << " -> ()"; | |
855 | } | |
856 | out << " | Some _v -> " << endl; | |
857 | } else { | |
858 | out << endl; | |
859 | indent(out) << "| None -> raise (Field_empty \"" << type_name(tstruct) << "." << mname | |
860 | << "\")" << endl; | |
861 | indent(out) << "| Some _v -> " << endl; | |
862 | } | |
863 | ||
864 | _v = "_v"; | |
865 | } | |
866 | indent_up(); | |
867 | // Write field header | |
868 | indent(out) << "oprot#writeFieldBegin(\"" << tmember->get_name() << "\"," | |
869 | << type_to_enum(tmember->get_type()) << "," << tmember->get_key() << ");" << endl; | |
870 | ||
871 | // Write field contents | |
872 | generate_serialize_field(out, tmember, _v); | |
873 | ||
874 | // Write field closer | |
875 | indent(out) << "oprot#writeFieldEnd" << endl; | |
876 | ||
877 | indent_down(); | |
878 | indent(out) << ");" << endl; | |
879 | } | |
880 | ||
881 | // Write the struct map | |
882 | out << indent() << "oprot#writeFieldStop;" << endl << indent() << "oprot#writeStructEnd" << endl; | |
883 | ||
884 | indent_down(); | |
885 | } | |
886 | ||
887 | /** | |
888 | * Generates a thrift service. | |
889 | * | |
890 | * @param tservice The service definition | |
891 | */ | |
892 | void t_ocaml_generator::generate_service(t_service* tservice) { | |
893 | string f_service_name = get_out_dir() + capitalize(service_name_) + ".ml"; | |
894 | f_service_.open(f_service_name.c_str()); | |
895 | string f_service_i_name = get_out_dir() + capitalize(service_name_) + ".mli"; | |
896 | f_service_i_.open(f_service_i_name.c_str()); | |
897 | ||
898 | f_service_ << ocaml_autogen_comment() << endl << ocaml_imports() << endl; | |
899 | f_service_i_ << ocaml_autogen_comment() << endl << ocaml_imports() << endl; | |
900 | ||
901 | /* if (tservice->get_extends() != NULL) { | |
902 | f_service_ << | |
903 | "open " << capitalize(tservice->get_extends()->get_name()) << endl; | |
904 | f_service_i_ << | |
905 | "open " << capitalize(tservice->get_extends()->get_name()) << endl; | |
906 | } | |
907 | */ | |
908 | f_service_ << "open " << capitalize(program_name_) << "_types" << endl << endl; | |
909 | ||
910 | f_service_i_ << "open " << capitalize(program_name_) << "_types" << endl << endl; | |
911 | ||
912 | // Generate the three main parts of the service | |
913 | generate_service_helpers(tservice); | |
914 | generate_service_interface(tservice); | |
915 | generate_service_client(tservice); | |
916 | generate_service_server(tservice); | |
917 | ||
918 | // Close service file | |
919 | f_service_.close(); | |
920 | f_service_i_.close(); | |
921 | } | |
922 | ||
923 | /** | |
924 | * Generates helper functions for a service. | |
925 | * | |
926 | * @param tservice The service to generate a header definition for | |
927 | */ | |
928 | void t_ocaml_generator::generate_service_helpers(t_service* tservice) { | |
929 | vector<t_function*> functions = tservice->get_functions(); | |
930 | vector<t_function*>::iterator f_iter; | |
931 | ||
932 | indent(f_service_) << "(* HELPER FUNCTIONS AND STRUCTURES *)" << endl << endl; | |
933 | ||
934 | for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { | |
935 | t_struct* ts = (*f_iter)->get_arglist(); | |
936 | generate_ocaml_struct_definition(f_service_, ts, false); | |
937 | generate_ocaml_function_helpers(*f_iter); | |
938 | } | |
939 | } | |
940 | ||
941 | /** | |
942 | * Generates a struct and helpers for a function. | |
943 | * | |
944 | * @param tfunction The function | |
945 | */ | |
946 | void t_ocaml_generator::generate_ocaml_function_helpers(t_function* tfunction) { | |
947 | t_struct result(program_, decapitalize(tfunction->get_name()) + "_result"); | |
948 | t_field success(tfunction->get_returntype(), "success", 0); | |
949 | if (!tfunction->get_returntype()->is_void()) { | |
950 | result.append(&success); | |
951 | } | |
952 | ||
953 | t_struct* xs = tfunction->get_xceptions(); | |
954 | const vector<t_field*>& fields = xs->get_members(); | |
955 | vector<t_field*>::const_iterator f_iter; | |
956 | for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { | |
957 | result.append(*f_iter); | |
958 | } | |
959 | generate_ocaml_struct_definition(f_service_, &result, false); | |
960 | } | |
961 | ||
962 | /** | |
963 | * Generates a service interface definition. | |
964 | * | |
965 | * @param tservice The service to generate a header definition for | |
966 | */ | |
967 | void t_ocaml_generator::generate_service_interface(t_service* tservice) { | |
968 | f_service_ << indent() << "class virtual iface =" << endl << "object (self)" << endl; | |
969 | f_service_i_ << indent() << "class virtual iface :" << endl << "object" << endl; | |
970 | ||
971 | indent_up(); | |
972 | ||
973 | if (tservice->get_extends() != NULL) { | |
974 | string extends = type_name(tservice->get_extends()); | |
975 | indent(f_service_) << "inherit " << extends << ".iface" << endl; | |
976 | indent(f_service_i_) << "inherit " << extends << ".iface" << endl; | |
977 | } | |
978 | ||
979 | vector<t_function*> functions = tservice->get_functions(); | |
980 | vector<t_function*>::iterator f_iter; | |
981 | for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { | |
982 | string ft = function_type(*f_iter, true, true); | |
983 | f_service_ << indent() << "method virtual " << decapitalize((*f_iter)->get_name()) << " : " | |
984 | << ft << endl; | |
985 | f_service_i_ << indent() << "method virtual " << decapitalize((*f_iter)->get_name()) << " : " | |
986 | << ft << endl; | |
987 | } | |
988 | indent_down(); | |
989 | indent(f_service_) << "end" << endl << endl; | |
990 | indent(f_service_i_) << "end" << endl << endl; | |
991 | } | |
992 | ||
993 | /** | |
994 | * Generates a service client definition. Note that in OCaml, the client doesn't implement iface. | |
995 | *This is because | |
996 | * The client does not (and should not have to) deal with arguments being None. | |
997 | * | |
998 | * @param tservice The service to generate a server for. | |
999 | */ | |
1000 | void t_ocaml_generator::generate_service_client(t_service* tservice) { | |
1001 | string extends = ""; | |
1002 | indent(f_service_) << "class client (iprot : Protocol.t) (oprot : Protocol.t) =" << endl | |
1003 | << "object (self)" << endl; | |
1004 | indent(f_service_i_) << "class client : Protocol.t -> Protocol.t -> " << endl << "object" << endl; | |
1005 | indent_up(); | |
1006 | ||
1007 | if (tservice->get_extends() != NULL) { | |
1008 | extends = type_name(tservice->get_extends()); | |
1009 | indent(f_service_) << "inherit " << extends << ".client iprot oprot as super" << endl; | |
1010 | indent(f_service_i_) << "inherit " << extends << ".client" << endl; | |
1011 | } | |
1012 | indent(f_service_) << "val mutable seqid = 0" << endl; | |
1013 | ||
1014 | // Generate client method implementations | |
1015 | vector<t_function*> functions = tservice->get_functions(); | |
1016 | vector<t_function*>::const_iterator f_iter; | |
1017 | for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { | |
1018 | t_struct* arg_struct = (*f_iter)->get_arglist(); | |
1019 | const vector<t_field*>& fields = arg_struct->get_members(); | |
1020 | vector<t_field*>::const_iterator fld_iter; | |
1021 | string funname = (*f_iter)->get_name(); | |
1022 | ||
1023 | // Open function | |
1024 | indent(f_service_) << "method " << function_signature(*f_iter) << " = " << endl; | |
1025 | indent(f_service_i_) << "method " << decapitalize((*f_iter)->get_name()) << " : " | |
1026 | << function_type(*f_iter, true, false) << endl; | |
1027 | indent_up(); | |
1028 | indent(f_service_) << "self#send_" << funname; | |
1029 | ||
1030 | for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { | |
1031 | f_service_ << " " << decapitalize((*fld_iter)->get_name()); | |
1032 | } | |
1033 | f_service_ << ";" << endl; | |
1034 | ||
1035 | if (!(*f_iter)->is_oneway()) { | |
1036 | f_service_ << indent(); | |
1037 | f_service_ << "self#recv_" << funname << endl; | |
1038 | } | |
1039 | indent_down(); | |
1040 | ||
1041 | indent(f_service_) << "method private send_" << function_signature(*f_iter) << " = " << endl; | |
1042 | indent_up(); | |
1043 | ||
1044 | std::string argsname = decapitalize((*f_iter)->get_name() + "_args"); | |
1045 | ||
1046 | // Serialize the request header | |
1047 | f_service_ << indent() << "oprot#writeMessageBegin (\"" << (*f_iter)->get_name() << "\", " | |
1048 | << ((*f_iter)->is_oneway() ? "Protocol.ONEWAY" : "Protocol.CALL") << ", seqid);" | |
1049 | << endl; | |
1050 | ||
1051 | f_service_ << indent() << "let args = new " << argsname << " in" << endl; | |
1052 | indent_up(); | |
1053 | ||
1054 | for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { | |
1055 | f_service_ << indent() << "args#set_" << (*fld_iter)->get_name() << " " | |
1056 | << (*fld_iter)->get_name() << ";" << endl; | |
1057 | } | |
1058 | ||
1059 | // Write to the stream | |
1060 | f_service_ << indent() << "args#write oprot;" << endl << indent() << "oprot#writeMessageEnd;" | |
1061 | << endl << indent() << "oprot#getTransport#flush" << endl; | |
1062 | ||
1063 | indent_down(); | |
1064 | indent_down(); | |
1065 | ||
1066 | if (!(*f_iter)->is_oneway()) { | |
1067 | std::string resultname = decapitalize((*f_iter)->get_name() + "_result"); | |
1068 | t_struct noargs(program_); | |
1069 | ||
1070 | t_function recv_function((*f_iter)->get_returntype(), | |
1071 | string("recv_") + (*f_iter)->get_name(), | |
1072 | &noargs); | |
1073 | // Open function | |
1074 | f_service_ << indent() << "method private " << function_signature(&recv_function) << " =" | |
1075 | << endl; | |
1076 | indent_up(); | |
1077 | ||
1078 | // TODO(mcslee): Validate message reply here, seq ids etc. | |
1079 | ||
1080 | f_service_ << indent() << "let (fname, mtype, rseqid) = iprot#readMessageBegin in" << endl; | |
1081 | indent_up(); | |
1082 | f_service_ << indent() << "(if mtype = Protocol.EXCEPTION then" << endl << indent() | |
1083 | << " let x = Application_Exn.read iprot in" << endl; | |
1084 | indent_up(); | |
1085 | f_service_ << indent() << " (iprot#readMessageEnd;" << indent() | |
1086 | << " raise (Application_Exn.E x))" << endl; | |
1087 | indent_down(); | |
1088 | f_service_ << indent() << "else ());" << endl; | |
1089 | string res = "_"; | |
1090 | ||
1091 | t_struct* xs = (*f_iter)->get_xceptions(); | |
1092 | const std::vector<t_field*>& xceptions = xs->get_members(); | |
1093 | ||
1094 | if (!(*f_iter)->get_returntype()->is_void() || xceptions.size() > 0) { | |
1095 | res = "result"; | |
1096 | } | |
1097 | f_service_ << indent() << "let " << res << " = read_" << resultname << " iprot in" << endl; | |
1098 | indent_up(); | |
1099 | f_service_ << indent() << "iprot#readMessageEnd;" << endl; | |
1100 | ||
1101 | // Careful, only return _result if not a void function | |
1102 | if (!(*f_iter)->get_returntype()->is_void()) { | |
1103 | f_service_ << indent() << "match result#get_success with Some v -> v | None -> (" << endl; | |
1104 | indent_up(); | |
1105 | } | |
1106 | ||
1107 | vector<t_field*>::const_iterator x_iter; | |
1108 | for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { | |
1109 | f_service_ << indent() << "(match result#get_" << (*x_iter)->get_name() | |
1110 | << " with None -> () | Some _v ->" << endl; | |
1111 | indent(f_service_) << " raise (" << capitalize(type_name((*x_iter)->get_type())) | |
1112 | << " _v));" << endl; | |
1113 | } | |
1114 | ||
1115 | // Careful, only return _result if not a void function | |
1116 | if ((*f_iter)->get_returntype()->is_void()) { | |
1117 | indent(f_service_) << "()" << endl; | |
1118 | } else { | |
1119 | f_service_ | |
1120 | << indent() | |
1121 | << "raise (Application_Exn.E (Application_Exn.create Application_Exn.MISSING_RESULT \"" | |
1122 | << (*f_iter)->get_name() << " failed: unknown result\")))" << endl; | |
1123 | indent_down(); | |
1124 | } | |
1125 | ||
1126 | // Close function | |
1127 | indent_down(); | |
1128 | indent_down(); | |
1129 | indent_down(); | |
1130 | } | |
1131 | } | |
1132 | ||
1133 | indent_down(); | |
1134 | indent(f_service_) << "end" << endl << endl; | |
1135 | indent(f_service_i_) << "end" << endl << endl; | |
1136 | } | |
1137 | ||
1138 | /** | |
1139 | * Generates a service server definition. | |
1140 | * | |
1141 | * @param tservice The service to generate a server for. | |
1142 | */ | |
1143 | void t_ocaml_generator::generate_service_server(t_service* tservice) { | |
1144 | // Generate the dispatch methods | |
1145 | vector<t_function*> functions = tservice->get_functions(); | |
1146 | vector<t_function*>::iterator f_iter; | |
1147 | ||
1148 | // Generate the header portion | |
1149 | indent(f_service_) << "class processor (handler : iface) =" << endl << indent() << "object (self)" | |
1150 | << endl; | |
1151 | indent(f_service_i_) << "class processor : iface ->" << endl << indent() << "object" << endl; | |
1152 | indent_up(); | |
1153 | ||
1154 | f_service_ << indent() << "inherit Processor.t" << endl << endl; | |
1155 | f_service_i_ << indent() << "inherit Processor.t" << endl << endl; | |
1156 | string extends = ""; | |
1157 | ||
1158 | if (tservice->get_extends() != NULL) { | |
1159 | extends = type_name(tservice->get_extends()); | |
1160 | indent(f_service_) << "inherit " + extends + ".processor (handler :> " + extends + ".iface)" | |
1161 | << endl; | |
1162 | indent(f_service_i_) << "inherit " + extends + ".processor" << endl; | |
1163 | } | |
1164 | ||
1165 | if (extends.empty()) { | |
1166 | indent(f_service_) << "val processMap = Hashtbl.create " << functions.size() << endl; | |
1167 | } | |
1168 | indent(f_service_i_) | |
1169 | << "val processMap : (string, int * Protocol.t * Protocol.t -> unit) Hashtbl.t" << endl; | |
1170 | ||
1171 | // Generate the server implementation | |
1172 | indent(f_service_) << "method process iprot oprot =" << endl; | |
1173 | indent(f_service_i_) << "method process : Protocol.t -> Protocol.t -> bool" << endl; | |
1174 | indent_up(); | |
1175 | ||
1176 | f_service_ << indent() << "let (name, typ, seqid) = iprot#readMessageBegin in" << endl; | |
1177 | indent_up(); | |
1178 | // TODO(mcslee): validate message | |
1179 | ||
1180 | // HOT: dictionary function lookup | |
1181 | f_service_ << indent() << "if Hashtbl.mem processMap name then" << endl << indent() | |
1182 | << " (Hashtbl.find processMap name) (seqid, iprot, oprot)" << endl << indent() | |
1183 | << "else (" << endl << indent() << " iprot#skip(Protocol.T_STRUCT);" << endl | |
1184 | << indent() << " iprot#readMessageEnd;" << endl << indent() | |
1185 | << " let x = Application_Exn.create Application_Exn.UNKNOWN_METHOD (\"Unknown " | |
1186 | "function \"^name) in" << endl << indent() | |
1187 | << " oprot#writeMessageBegin(name, Protocol.EXCEPTION, seqid);" << endl << indent() | |
1188 | << " x#write oprot;" << endl << indent() << " oprot#writeMessageEnd;" << endl | |
1189 | << indent() << " oprot#getTransport#flush" << endl << indent() << ");" << endl; | |
1190 | ||
1191 | // Read end of args field, the T_STOP, and the struct close | |
1192 | f_service_ << indent() << "true" << endl; | |
1193 | indent_down(); | |
1194 | indent_down(); | |
1195 | // Generate the process subfunctions | |
1196 | for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { | |
1197 | generate_process_function(tservice, *f_iter); | |
1198 | } | |
1199 | ||
1200 | indent(f_service_) << "initializer" << endl; | |
1201 | indent_up(); | |
1202 | for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { | |
1203 | f_service_ << indent() << "Hashtbl.add processMap \"" << (*f_iter)->get_name() | |
1204 | << "\" self#process_" << (*f_iter)->get_name() << ";" << endl; | |
1205 | } | |
1206 | indent_down(); | |
1207 | ||
1208 | indent_down(); | |
1209 | indent(f_service_) << "end" << endl << endl; | |
1210 | indent(f_service_i_) << "end" << endl << endl; | |
1211 | } | |
1212 | ||
1213 | /** | |
1214 | * Generates a process function definition. | |
1215 | * | |
1216 | * @param tfunction The function to write a dispatcher for | |
1217 | */ | |
1218 | void t_ocaml_generator::generate_process_function(t_service* tservice, t_function* tfunction) { | |
1219 | (void)tservice; | |
1220 | // Open function | |
1221 | indent(f_service_) << "method private process_" << tfunction->get_name() | |
1222 | << " (seqid, iprot, oprot) =" << endl; | |
1223 | indent_up(); | |
1224 | ||
1225 | string argsname = decapitalize(tfunction->get_name()) + "_args"; | |
1226 | string resultname = decapitalize(tfunction->get_name()) + "_result"; | |
1227 | ||
1228 | // Generate the function call | |
1229 | t_struct* arg_struct = tfunction->get_arglist(); | |
1230 | const std::vector<t_field*>& fields = arg_struct->get_members(); | |
1231 | vector<t_field*>::const_iterator f_iter; | |
1232 | ||
1233 | string args = "args"; | |
1234 | if (fields.size() == 0) { | |
1235 | args = "_"; | |
1236 | } | |
1237 | ||
1238 | f_service_ << indent() << "let " << args << " = read_" << argsname << " iprot in" << endl; | |
1239 | indent_up(); | |
1240 | f_service_ << indent() << "iprot#readMessageEnd;" << endl; | |
1241 | ||
1242 | t_struct* xs = tfunction->get_xceptions(); | |
1243 | const std::vector<t_field*>& xceptions = xs->get_members(); | |
1244 | vector<t_field*>::const_iterator x_iter; | |
1245 | ||
1246 | // Declare result for non oneway function | |
1247 | if (!tfunction->is_oneway()) { | |
1248 | f_service_ << indent() << "let result = new " << resultname << " in" << endl; | |
1249 | indent_up(); | |
1250 | } | |
1251 | ||
1252 | // Try block for a function with exceptions | |
1253 | if (xceptions.size() > 0) { | |
1254 | f_service_ << indent() << "(try" << endl; | |
1255 | indent_up(); | |
1256 | } | |
1257 | ||
1258 | f_service_ << indent(); | |
1259 | if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) { | |
1260 | f_service_ << "result#set_success "; | |
1261 | } | |
1262 | f_service_ << "(handler#" << tfunction->get_name(); | |
1263 | for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { | |
1264 | f_service_ << " args#get_" << (*f_iter)->get_name(); | |
1265 | } | |
1266 | f_service_ << ");" << endl; | |
1267 | ||
1268 | if (xceptions.size() > 0) { | |
1269 | indent_down(); | |
1270 | indent(f_service_) << "with" << endl; | |
1271 | indent_up(); | |
1272 | for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { | |
1273 | f_service_ << indent() << "| " << capitalize(type_name((*x_iter)->get_type())) << " " | |
1274 | << (*x_iter)->get_name() << " -> " << endl; | |
1275 | indent_up(); | |
1276 | indent_up(); | |
1277 | if (!tfunction->is_oneway()) { | |
1278 | f_service_ << indent() << "result#set_" << (*x_iter)->get_name() << " " | |
1279 | << (*x_iter)->get_name() << endl; | |
1280 | } else { | |
1281 | indent(f_service_) << "()"; | |
1282 | } | |
1283 | indent_down(); | |
1284 | indent_down(); | |
1285 | } | |
1286 | indent_down(); | |
1287 | f_service_ << indent() << ");" << endl; | |
1288 | } | |
1289 | ||
1290 | // Shortcut out here for oneway functions | |
1291 | if (tfunction->is_oneway()) { | |
1292 | f_service_ << indent() << "()" << endl; | |
1293 | indent_down(); | |
1294 | indent_down(); | |
1295 | return; | |
1296 | } | |
1297 | ||
1298 | f_service_ << indent() << "oprot#writeMessageBegin (\"" << tfunction->get_name() | |
1299 | << "\", Protocol.REPLY, seqid);" << endl << indent() << "result#write oprot;" << endl | |
1300 | << indent() << "oprot#writeMessageEnd;" << endl << indent() | |
1301 | << "oprot#getTransport#flush" << endl; | |
1302 | ||
1303 | // Close function | |
1304 | indent_down(); | |
1305 | indent_down(); | |
1306 | indent_down(); | |
1307 | } | |
1308 | ||
1309 | /** | |
1310 | * Deserializes a field of any type. | |
1311 | */ | |
1312 | void t_ocaml_generator::generate_deserialize_field(ostream& out, t_field* tfield, string prefix) { | |
1313 | t_type* type = tfield->get_type(); | |
1314 | ||
1315 | string name = decapitalize(tfield->get_name()); | |
1316 | indent(out) << prefix << "#set_" << name << " "; | |
1317 | generate_deserialize_type(out, type); | |
1318 | out << endl; | |
1319 | } | |
1320 | ||
1321 | /** | |
1322 | * Deserializes a field of any type. | |
1323 | */ | |
1324 | void t_ocaml_generator::generate_deserialize_type(ostream& out, t_type* type) { | |
1325 | type = get_true_type(type); | |
1326 | ||
1327 | if (type->is_void()) { | |
1328 | throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE"; | |
1329 | } | |
1330 | ||
1331 | if (type->is_struct() || type->is_xception()) { | |
1332 | generate_deserialize_struct(out, (t_struct*)type); | |
1333 | } else if (type->is_container()) { | |
1334 | generate_deserialize_container(out, type); | |
1335 | } else if (type->is_base_type()) { | |
1336 | out << "iprot#"; | |
1337 | t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); | |
1338 | switch (tbase) { | |
1339 | case t_base_type::TYPE_VOID: | |
1340 | throw "compiler error: cannot serialize void field in a struct"; | |
1341 | break; | |
1342 | case t_base_type::TYPE_STRING: | |
1343 | out << "readString"; | |
1344 | break; | |
1345 | case t_base_type::TYPE_BOOL: | |
1346 | out << "readBool"; | |
1347 | break; | |
1348 | case t_base_type::TYPE_I8: | |
1349 | out << "readByte"; | |
1350 | break; | |
1351 | case t_base_type::TYPE_I16: | |
1352 | out << "readI16"; | |
1353 | break; | |
1354 | case t_base_type::TYPE_I32: | |
1355 | out << "readI32"; | |
1356 | break; | |
1357 | case t_base_type::TYPE_I64: | |
1358 | out << "readI64"; | |
1359 | break; | |
1360 | case t_base_type::TYPE_DOUBLE: | |
1361 | out << "readDouble"; | |
1362 | break; | |
1363 | default: | |
1364 | throw "compiler error: no ocaml name for base type " + t_base_type::t_base_name(tbase); | |
1365 | } | |
1366 | } else if (type->is_enum()) { | |
1367 | string ename = capitalize(type->get_name()); | |
1368 | out << "(" << ename << ".of_i iprot#readI32)"; | |
1369 | } else { | |
1370 | printf("DO NOT KNOW HOW TO DESERIALIZE TYPE '%s'\n", type->get_name().c_str()); | |
1371 | } | |
1372 | } | |
1373 | ||
1374 | /** | |
1375 | * Generates an unserializer for a struct, calling read() | |
1376 | */ | |
1377 | void t_ocaml_generator::generate_deserialize_struct(ostream& out, t_struct* tstruct) { | |
1378 | string prefix = ""; | |
1379 | t_program* program = tstruct->get_program(); | |
1380 | if (program != NULL && program != program_) { | |
1381 | prefix = capitalize(program->get_name()) + "_types."; | |
1382 | } | |
1383 | string name = decapitalize(tstruct->get_name()); | |
1384 | out << "(" << prefix << "read_" << name << " iprot)"; | |
1385 | } | |
1386 | ||
1387 | /** | |
1388 | * Serialize a container by writing out the header followed by | |
1389 | * data and then a footer. | |
1390 | */ | |
1391 | void t_ocaml_generator::generate_deserialize_container(ostream& out, t_type* ttype) { | |
1392 | string size = tmp("_size"); | |
1393 | string ktype = tmp("_ktype"); | |
1394 | string vtype = tmp("_vtype"); | |
1395 | string etype = tmp("_etype"); | |
1396 | string con = tmp("_con"); | |
1397 | ||
1398 | t_field fsize(g_type_i32, size); | |
1399 | t_field fktype(g_type_i8, ktype); | |
1400 | t_field fvtype(g_type_i8, vtype); | |
1401 | t_field fetype(g_type_i8, etype); | |
1402 | ||
1403 | out << endl; | |
1404 | indent_up(); | |
1405 | // Declare variables, read header | |
1406 | if (ttype->is_map()) { | |
1407 | indent(out) << "(let (" << ktype << "," << vtype << "," << size << ") = iprot#readMapBegin in" | |
1408 | << endl; | |
1409 | indent(out) << "let " << con << " = Hashtbl.create " << size << " in" << endl; | |
1410 | indent_up(); | |
1411 | indent(out) << "for i = 1 to " << size << " do" << endl; | |
1412 | indent_up(); | |
1413 | indent(out) << "let _k = "; | |
1414 | generate_deserialize_type(out, ((t_map*)ttype)->get_key_type()); | |
1415 | out << " in" << endl; | |
1416 | indent(out) << "let _v = "; | |
1417 | generate_deserialize_type(out, ((t_map*)ttype)->get_val_type()); | |
1418 | out << " in" << endl; | |
1419 | indent_up(); | |
1420 | indent(out) << "Hashtbl.add " << con << " _k _v" << endl; | |
1421 | indent_down(); | |
1422 | indent_down(); | |
1423 | indent(out) << "done; iprot#readMapEnd; " << con << ")"; | |
1424 | indent_down(); | |
1425 | } else if (ttype->is_set()) { | |
1426 | indent(out) << "(let (" << etype << "," << size << ") = iprot#readSetBegin in" << endl; | |
1427 | indent(out) << "let " << con << " = Hashtbl.create " << size << " in" << endl; | |
1428 | indent_up(); | |
1429 | indent(out) << "for i = 1 to " << size << " do" << endl; | |
1430 | indent_up(); | |
1431 | indent(out) << "Hashtbl.add " << con << " "; | |
1432 | generate_deserialize_type(out, ((t_set*)ttype)->get_elem_type()); | |
1433 | out << " true" << endl; | |
1434 | indent_down(); | |
1435 | indent(out) << "done; iprot#readSetEnd; " << con << ")"; | |
1436 | indent_down(); | |
1437 | } else if (ttype->is_list()) { | |
1438 | indent(out) << "(let (" << etype << "," << size << ") = iprot#readListBegin in" << endl; | |
1439 | indent_up(); | |
1440 | indent(out) << "let " << con << " = (Array.to_list (Array.init " << size << " (fun _ -> "; | |
1441 | generate_deserialize_type(out, ((t_list*)ttype)->get_elem_type()); | |
1442 | out << "))) in" << endl; | |
1443 | indent_up(); | |
1444 | indent(out) << "iprot#readListEnd; " << con << ")"; | |
1445 | indent_down(); | |
1446 | indent_down(); | |
1447 | } | |
1448 | indent_down(); | |
1449 | } | |
1450 | ||
1451 | /** | |
1452 | * Serializes a field of any type. | |
1453 | * | |
1454 | * @param tfield The field to serialize | |
1455 | * @param prefix Name to prepend to field name | |
1456 | */ | |
1457 | void t_ocaml_generator::generate_serialize_field(ostream& out, t_field* tfield, string name) { | |
1458 | t_type* type = get_true_type(tfield->get_type()); | |
1459 | ||
1460 | // Do nothing for void types | |
1461 | if (type->is_void()) { | |
1462 | throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + tfield->get_name(); | |
1463 | } | |
1464 | ||
1465 | if (name.length() == 0) { | |
1466 | name = decapitalize(tfield->get_name()); | |
1467 | } | |
1468 | ||
1469 | if (type->is_struct() || type->is_xception()) { | |
1470 | generate_serialize_struct(out, (t_struct*)type, name); | |
1471 | } else if (type->is_container()) { | |
1472 | generate_serialize_container(out, type, name); | |
1473 | } else if (type->is_base_type() || type->is_enum()) { | |
1474 | ||
1475 | indent(out) << "oprot#"; | |
1476 | ||
1477 | if (type->is_base_type()) { | |
1478 | t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); | |
1479 | switch (tbase) { | |
1480 | case t_base_type::TYPE_VOID: | |
1481 | throw "compiler error: cannot serialize void field in a struct: " + name; | |
1482 | break; | |
1483 | case t_base_type::TYPE_STRING: | |
1484 | out << "writeString(" << name << ")"; | |
1485 | break; | |
1486 | case t_base_type::TYPE_BOOL: | |
1487 | out << "writeBool(" << name << ")"; | |
1488 | break; | |
1489 | case t_base_type::TYPE_I8: | |
1490 | out << "writeByte(" << name << ")"; | |
1491 | break; | |
1492 | case t_base_type::TYPE_I16: | |
1493 | out << "writeI16(" << name << ")"; | |
1494 | break; | |
1495 | case t_base_type::TYPE_I32: | |
1496 | out << "writeI32(" << name << ")"; | |
1497 | break; | |
1498 | case t_base_type::TYPE_I64: | |
1499 | out << "writeI64(" << name << ")"; | |
1500 | break; | |
1501 | case t_base_type::TYPE_DOUBLE: | |
1502 | out << "writeDouble(" << name << ")"; | |
1503 | break; | |
1504 | default: | |
1505 | throw "compiler error: no ocaml name for base type " + t_base_type::t_base_name(tbase); | |
1506 | } | |
1507 | } else if (type->is_enum()) { | |
1508 | string ename = capitalize(type->get_name()); | |
1509 | out << "writeI32(" << ename << ".to_i " << name << ")"; | |
1510 | } | |
1511 | ||
1512 | } else { | |
1513 | printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s' TYPE '%s'\n", | |
1514 | tfield->get_name().c_str(), | |
1515 | type->get_name().c_str()); | |
1516 | } | |
1517 | out << ";" << endl; | |
1518 | } | |
1519 | ||
1520 | /** | |
1521 | * Serializes all the members of a struct. | |
1522 | * | |
1523 | * @param tstruct The struct to serialize | |
1524 | * @param prefix String prefix to attach to all fields | |
1525 | */ | |
1526 | void t_ocaml_generator::generate_serialize_struct(ostream& out, t_struct* tstruct, string prefix) { | |
1527 | (void)tstruct; | |
1528 | indent(out) << prefix << "#write(oprot)"; | |
1529 | } | |
1530 | ||
1531 | void t_ocaml_generator::generate_serialize_container(ostream& out, t_type* ttype, string prefix) { | |
1532 | if (ttype->is_map()) { | |
1533 | indent(out) << "oprot#writeMapBegin(" << type_to_enum(((t_map*)ttype)->get_key_type()) << ","; | |
1534 | out << type_to_enum(((t_map*)ttype)->get_val_type()) << ","; | |
1535 | out << "Hashtbl.length " << prefix << ");" << endl; | |
1536 | } else if (ttype->is_set()) { | |
1537 | indent(out) << "oprot#writeSetBegin(" << type_to_enum(((t_set*)ttype)->get_elem_type()) << ","; | |
1538 | out << "Hashtbl.length " << prefix << ");" << endl; | |
1539 | } else if (ttype->is_list()) { | |
1540 | indent(out) << "oprot#writeListBegin(" << type_to_enum(((t_list*)ttype)->get_elem_type()) | |
1541 | << ","; | |
1542 | out << "List.length " << prefix << ");" << endl; | |
1543 | } | |
1544 | ||
1545 | if (ttype->is_map()) { | |
1546 | string kiter = tmp("_kiter"); | |
1547 | string viter = tmp("_viter"); | |
1548 | indent(out) << "Hashtbl.iter (fun " << kiter << " -> fun " << viter << " -> " << endl; | |
1549 | indent_up(); | |
1550 | generate_serialize_map_element(out, (t_map*)ttype, kiter, viter); | |
1551 | indent_down(); | |
1552 | indent(out) << ") " << prefix << ";" << endl; | |
1553 | } else if (ttype->is_set()) { | |
1554 | string iter = tmp("_iter"); | |
1555 | indent(out) << "Hashtbl.iter (fun " << iter << " -> fun _ -> "; | |
1556 | indent_up(); | |
1557 | generate_serialize_set_element(out, (t_set*)ttype, iter); | |
1558 | indent_down(); | |
1559 | indent(out) << ") " << prefix << ";" << endl; | |
1560 | } else if (ttype->is_list()) { | |
1561 | string iter = tmp("_iter"); | |
1562 | indent(out) << "List.iter (fun " << iter << " -> "; | |
1563 | indent_up(); | |
1564 | generate_serialize_list_element(out, (t_list*)ttype, iter); | |
1565 | indent_down(); | |
1566 | indent(out) << ") " << prefix << ";" << endl; | |
1567 | } | |
1568 | ||
1569 | if (ttype->is_map()) { | |
1570 | indent(out) << "oprot#writeMapEnd"; | |
1571 | } else if (ttype->is_set()) { | |
1572 | indent(out) << "oprot#writeSetEnd"; | |
1573 | } else if (ttype->is_list()) { | |
1574 | indent(out) << "oprot#writeListEnd"; | |
1575 | } | |
1576 | } | |
1577 | ||
1578 | /** | |
1579 | * Serializes the members of a map. | |
1580 | * | |
1581 | */ | |
1582 | void t_ocaml_generator::generate_serialize_map_element(ostream& out, | |
1583 | t_map* tmap, | |
1584 | string kiter, | |
1585 | string viter) { | |
1586 | t_field kfield(tmap->get_key_type(), kiter); | |
1587 | generate_serialize_field(out, &kfield); | |
1588 | ||
1589 | t_field vfield(tmap->get_val_type(), viter); | |
1590 | generate_serialize_field(out, &vfield); | |
1591 | } | |
1592 | ||
1593 | /** | |
1594 | * Serializes the members of a set. | |
1595 | */ | |
1596 | void t_ocaml_generator::generate_serialize_set_element(ostream& out, t_set* tset, string iter) { | |
1597 | t_field efield(tset->get_elem_type(), iter); | |
1598 | generate_serialize_field(out, &efield); | |
1599 | } | |
1600 | ||
1601 | /** | |
1602 | * Serializes the members of a list. | |
1603 | */ | |
1604 | void t_ocaml_generator::generate_serialize_list_element(ostream& out, t_list* tlist, string iter) { | |
1605 | t_field efield(tlist->get_elem_type(), iter); | |
1606 | generate_serialize_field(out, &efield); | |
1607 | } | |
1608 | ||
1609 | /** | |
1610 | * Renders a function signature of the form 'name args' | |
1611 | * | |
1612 | * @param tfunction Function definition | |
1613 | * @return String of rendered function definition | |
1614 | */ | |
1615 | string t_ocaml_generator::function_signature(t_function* tfunction, string prefix) { | |
1616 | return prefix + decapitalize(tfunction->get_name()) + " " | |
1617 | + argument_list(tfunction->get_arglist()); | |
1618 | } | |
1619 | ||
1620 | string t_ocaml_generator::function_type(t_function* tfunc, bool method, bool options) { | |
1621 | string result = ""; | |
1622 | ||
1623 | const vector<t_field*>& fields = tfunc->get_arglist()->get_members(); | |
1624 | vector<t_field*>::const_iterator f_iter; | |
1625 | for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { | |
1626 | result += render_ocaml_type((*f_iter)->get_type()); | |
1627 | if (options) | |
1628 | result += " option"; | |
1629 | result += " -> "; | |
1630 | } | |
1631 | if (fields.empty() && !method) { | |
1632 | result += "unit -> "; | |
1633 | } | |
1634 | result += render_ocaml_type(tfunc->get_returntype()); | |
1635 | return result; | |
1636 | } | |
1637 | ||
1638 | /** | |
1639 | * Renders a field list | |
1640 | */ | |
1641 | string t_ocaml_generator::argument_list(t_struct* tstruct) { | |
1642 | string result = ""; | |
1643 | ||
1644 | const vector<t_field*>& fields = tstruct->get_members(); | |
1645 | vector<t_field*>::const_iterator f_iter; | |
1646 | bool first = true; | |
1647 | for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { | |
1648 | if (first) { | |
1649 | first = false; | |
1650 | } else { | |
1651 | result += " "; | |
1652 | } | |
1653 | result += (*f_iter)->get_name(); | |
1654 | } | |
1655 | return result; | |
1656 | } | |
1657 | ||
1658 | string t_ocaml_generator::type_name(t_type* ttype) { | |
1659 | string prefix = ""; | |
1660 | t_program* program = ttype->get_program(); | |
1661 | if (program != NULL && program != program_) { | |
1662 | if (!ttype->is_service()) { | |
1663 | prefix = capitalize(program->get_name()) + "_types."; | |
1664 | } | |
1665 | } | |
1666 | ||
1667 | string name = ttype->get_name(); | |
1668 | if (ttype->is_service()) { | |
1669 | name = capitalize(name); | |
1670 | } else { | |
1671 | name = decapitalize(name); | |
1672 | } | |
1673 | return prefix + name; | |
1674 | } | |
1675 | ||
1676 | /** | |
1677 | * Converts the parse type to a Protocol.t_type enum | |
1678 | */ | |
1679 | string t_ocaml_generator::type_to_enum(t_type* type) { | |
1680 | type = get_true_type(type); | |
1681 | ||
1682 | if (type->is_base_type()) { | |
1683 | t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); | |
1684 | switch (tbase) { | |
1685 | case t_base_type::TYPE_VOID: | |
1686 | return "Protocol.T_VOID"; | |
1687 | case t_base_type::TYPE_STRING: | |
1688 | return "Protocol.T_STRING"; | |
1689 | case t_base_type::TYPE_BOOL: | |
1690 | return "Protocol.T_BOOL"; | |
1691 | case t_base_type::TYPE_I8: | |
1692 | return "Protocol.T_BYTE"; | |
1693 | case t_base_type::TYPE_I16: | |
1694 | return "Protocol.T_I16"; | |
1695 | case t_base_type::TYPE_I32: | |
1696 | return "Protocol.T_I32"; | |
1697 | case t_base_type::TYPE_I64: | |
1698 | return "Protocol.T_I64"; | |
1699 | case t_base_type::TYPE_DOUBLE: | |
1700 | return "Protocol.T_DOUBLE"; | |
1701 | } | |
1702 | } else if (type->is_enum()) { | |
1703 | return "Protocol.T_I32"; | |
1704 | } else if (type->is_struct() || type->is_xception()) { | |
1705 | return "Protocol.T_STRUCT"; | |
1706 | } else if (type->is_map()) { | |
1707 | return "Protocol.T_MAP"; | |
1708 | } else if (type->is_set()) { | |
1709 | return "Protocol.T_SET"; | |
1710 | } else if (type->is_list()) { | |
1711 | return "Protocol.T_LIST"; | |
1712 | } | |
1713 | ||
1714 | throw "INVALID TYPE IN type_to_enum: " + type->get_name(); | |
1715 | } | |
1716 | ||
1717 | /** | |
1718 | * Converts the parse type to an ocaml type | |
1719 | */ | |
1720 | string t_ocaml_generator::render_ocaml_type(t_type* type) { | |
1721 | type = get_true_type(type); | |
1722 | ||
1723 | if (type->is_base_type()) { | |
1724 | t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); | |
1725 | switch (tbase) { | |
1726 | case t_base_type::TYPE_VOID: | |
1727 | return "unit"; | |
1728 | case t_base_type::TYPE_STRING: | |
1729 | return "string"; | |
1730 | case t_base_type::TYPE_BOOL: | |
1731 | return "bool"; | |
1732 | case t_base_type::TYPE_I8: | |
1733 | return "int"; | |
1734 | case t_base_type::TYPE_I16: | |
1735 | return "int"; | |
1736 | case t_base_type::TYPE_I32: | |
1737 | return "Int32.t"; | |
1738 | case t_base_type::TYPE_I64: | |
1739 | return "Int64.t"; | |
1740 | case t_base_type::TYPE_DOUBLE: | |
1741 | return "float"; | |
1742 | } | |
1743 | } else if (type->is_enum()) { | |
1744 | return capitalize(((t_enum*)type)->get_name()) + ".t"; | |
1745 | } else if (type->is_struct() || type->is_xception()) { | |
1746 | return type_name((t_struct*)type); | |
1747 | } else if (type->is_map()) { | |
1748 | t_type* ktype = ((t_map*)type)->get_key_type(); | |
1749 | t_type* vtype = ((t_map*)type)->get_val_type(); | |
1750 | return "(" + render_ocaml_type(ktype) + "," + render_ocaml_type(vtype) + ") Hashtbl.t"; | |
1751 | } else if (type->is_set()) { | |
1752 | t_type* etype = ((t_set*)type)->get_elem_type(); | |
1753 | return "(" + render_ocaml_type(etype) + ",bool) Hashtbl.t"; | |
1754 | } else if (type->is_list()) { | |
1755 | t_type* etype = ((t_list*)type)->get_elem_type(); | |
1756 | return render_ocaml_type(etype) + " list"; | |
1757 | } | |
1758 | ||
1759 | throw "INVALID TYPE IN type_to_enum: " + type->get_name(); | |
1760 | } | |
1761 | ||
1762 | THRIFT_REGISTER_GENERATOR(ocaml, "OCaml", "") |