blob: e38f7b18fb32087d7f0f0aa730b1c766ace51448 [file] [log] [blame]
iproctor9a41a0c2007-07-16 21:59:24 +00001// Copyright (c) 2006- Facebook
2// Distributed under the Thrift Software License
3//
4// See accompanying file LICENSE or visit the Thrift site at:
5// http://developers.facebook.com/thrift/
6
David Reiss7bb71df2008-03-27 21:40:08 +00007#include <string>
8#include <fstream>
9#include <iostream>
10#include <vector>
11
iproctor9a41a0c2007-07-16 21:59:24 +000012#include <stdlib.h>
13#include <sys/stat.h>
14#include <sys/types.h>
15#include <sstream>
David Reiss7bb71df2008-03-27 21:40:08 +000016#include "t_oop_generator.h"
David Reiss204420f2008-01-11 20:59:03 +000017#include "platform.h"
iproctor9a41a0c2007-07-16 21:59:24 +000018using namespace std;
19
David Reiss7bb71df2008-03-27 21:40:08 +000020
21/**
22 * OCaml code generator.
23 *
24 * @author Iain Proctor <iproctor@facebook.com>
25 */
26class t_ocaml_generator : public t_oop_generator {
27 public:
28 t_ocaml_generator(
29 t_program* program,
30 const std::map<std::string, std::string>& parsed_options,
31 const std::string& option_string)
32 : t_oop_generator(program)
33 {
34 out_dir_base_ = "gen-ocaml";
35 }
36
37 /**
38 * Init and close methods
39 */
40
41 void init_generator();
42 void close_generator();
43
44 /**
45 * Program-level generation functions
46 */
47 void generate_program ();
48 void generate_typedef (t_typedef* ttypedef);
49 void generate_enum (t_enum* tenum);
50 void generate_const (t_const* tconst);
51 void generate_struct (t_struct* tstruct);
52 void generate_xception (t_struct* txception);
53 void generate_service (t_service* tservice);
54
55 std::string render_const_value(t_type* type, t_const_value* value);
56
57 /**
58 * Struct generation code
59 */
60
61 void generate_ocaml_struct(t_struct* tstruct, bool is_exception);
62 void generate_ocaml_struct_definition(std::ofstream& out, t_struct* tstruct, bool is_xception=false);
63 void generate_ocaml_struct_sig(std::ofstream& out, t_struct* tstruct, bool is_exception);
64 void generate_ocaml_struct_reader(std::ofstream& out, t_struct* tstruct);
65 void generate_ocaml_struct_writer(std::ofstream& out, t_struct* tstruct);
66 void generate_ocaml_function_helpers(t_function* tfunction);
67
68 /**
69 * Service-level generation functions
70 */
71
72 void generate_service_helpers (t_service* tservice);
73 void generate_service_interface (t_service* tservice);
74 void generate_service_client (t_service* tservice);
75 void generate_service_server (t_service* tservice);
76 void generate_process_function (t_service* tservice, t_function* tfunction);
77
78 /**
79 * Serialization constructs
80 */
81
82 void generate_deserialize_field (std::ofstream &out,
83 t_field* tfield,
84 std::string prefix);
85
86 void generate_deserialize_struct (std::ofstream &out,
87 t_struct* tstruct);
88
89 void generate_deserialize_container (std::ofstream &out,
90 t_type* ttype);
91
92 void generate_deserialize_set_element (std::ofstream &out,
93 t_set* tset);
94
95
96 void generate_deserialize_list_element (std::ofstream &out,
97 t_list* tlist,
98 std::string prefix="");
99 void generate_deserialize_type (std::ofstream &out,
100 t_type* type);
101
102 void generate_serialize_field (std::ofstream &out,
103 t_field* tfield,
104 std::string name= "");
105
106 void generate_serialize_struct (std::ofstream &out,
107 t_struct* tstruct,
108 std::string prefix="");
109
110 void generate_serialize_container (std::ofstream &out,
111 t_type* ttype,
112 std::string prefix="");
113
114 void generate_serialize_map_element (std::ofstream &out,
115 t_map* tmap,
116 std::string kiter,
117 std::string viter);
118
119 void generate_serialize_set_element (std::ofstream &out,
120 t_set* tmap,
121 std::string iter);
122
123 void generate_serialize_list_element (std::ofstream &out,
124 t_list* tlist,
125 std::string iter);
126
127 /**
128 * Helper rendering functions
129 */
130
131 std::string ocaml_autogen_comment();
132 std::string ocaml_imports();
133 std::string type_name(t_type* ttype);
134 std::string function_signature(t_function* tfunction, std::string prefix="");
135 std::string function_type(t_function* tfunc, bool method=false, bool options = false);
136 std::string argument_list(t_struct* tstruct);
137 std::string type_to_enum(t_type* ttype);
138 std::string render_ocaml_type(t_type* type);
139
140
141 private:
142
143 /**
144 * File streams
145 */
146
147 std::ofstream f_types_;
148 std::ofstream f_consts_;
149 std::ofstream f_service_;
150
151 std::ofstream f_types_i_;
152 std::ofstream f_service_i_;
153
154};
155
156
iproctor9a41a0c2007-07-16 21:59:24 +0000157/*
158 * This is necessary because we want typedefs to appear later,
159 * after all the types have been declared.
160 */
161void t_ocaml_generator::generate_program() {
162 // Initialize the generator
163 init_generator();
164
165 // Generate enums
166 vector<t_enum*> enums = program_->get_enums();
167 vector<t_enum*>::iterator en_iter;
168 for (en_iter = enums.begin(); en_iter != enums.end(); ++en_iter) {
169 generate_enum(*en_iter);
170 }
171
172 // Generate structs
173 vector<t_struct*> structs = program_->get_structs();
174 vector<t_struct*>::iterator st_iter;
175 for (st_iter = structs.begin(); st_iter != structs.end(); ++st_iter) {
176 generate_struct(*st_iter);
177 }
178
179 // Generate xceptions
180 vector<t_struct*> xceptions = program_->get_xceptions();
181 vector<t_struct*>::iterator x_iter;
182 for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
183 generate_xception(*x_iter);
184 }
185
186 // Generate typedefs
187 vector<t_typedef*> typedefs = program_->get_typedefs();
188 vector<t_typedef*>::iterator td_iter;
189 for (td_iter = typedefs.begin(); td_iter != typedefs.end(); ++td_iter) {
190 generate_typedef(*td_iter);
191 }
192
193 // Generate services
194 vector<t_service*> services = program_->get_services();
195 vector<t_service*>::iterator sv_iter;
196 for (sv_iter = services.begin(); sv_iter != services.end(); ++sv_iter) {
197 service_name_ = get_service_name(*sv_iter);
198 generate_service(*sv_iter);
199 }
200
201 // Generate constants
202 vector<t_const*> consts = program_->get_consts();
203 generate_consts(consts);
204
205 // Close the generator
206 close_generator();
207}
208
209
210/**
211 * Prepares for file generation by opening up the necessary file output
212 * streams.
213 *
214 * @param tprogram The program to generate
215 */
216void t_ocaml_generator::init_generator() {
217 // Make output directory
David Reiss204420f2008-01-11 20:59:03 +0000218 MKDIR(get_out_dir().c_str());
iproctor9a41a0c2007-07-16 21:59:24 +0000219
220 // Make output file
dweatherford65b70752007-10-31 02:18:14 +0000221 string f_types_name = get_out_dir()+program_name_+"_types.ml";
iproctor9a41a0c2007-07-16 21:59:24 +0000222 f_types_.open(f_types_name.c_str());
dweatherford65b70752007-10-31 02:18:14 +0000223 string f_types_i_name = get_out_dir()+program_name_+"_types.mli";
iproctor9a41a0c2007-07-16 21:59:24 +0000224 f_types_i_.open(f_types_i_name.c_str());
225
dweatherford65b70752007-10-31 02:18:14 +0000226 string f_consts_name = get_out_dir()+program_name_+"_consts.ml";
iproctor9a41a0c2007-07-16 21:59:24 +0000227 f_consts_.open(f_consts_name.c_str());
228
229 // Print header
230 f_types_ <<
231 ocaml_autogen_comment() << endl <<
232 ocaml_imports() << endl;
233 f_types_i_ <<
234 ocaml_autogen_comment() << endl <<
235 ocaml_imports() << endl;
236 f_consts_ <<
237 ocaml_autogen_comment() << endl <<
238 ocaml_imports() << endl <<
239 "open " << capitalize(program_name_)<<"_types"<< endl;
240}
241
242
243/**
244 * Autogen'd comment
245 */
246string t_ocaml_generator::ocaml_autogen_comment() {
247 return
248 std::string("(*\n") +
249 " Autogenerated by Thrift\n" +
250 "\n" +
251 " DO NOT EDIT UNLESS YOU ARE SURE YOU KNOW WHAT YOU ARE DOING\n" +
252 "*)\n";
253}
254
255/**
256 * Prints standard thrift imports
257 */
258string t_ocaml_generator::ocaml_imports() {
259 return "open Thrift";
260}
261
262/**
263 * Closes the type files
264 */
265void t_ocaml_generator::close_generator() {
266 // Close types file
267 f_types_.close();
268}
David Reiss0c90f6f2008-02-06 22:18:40 +0000269
iproctor9a41a0c2007-07-16 21:59:24 +0000270/**
271 * Generates a typedef. Ez.
272 *
273 * @param ttypedef The type definition
274 */
275void t_ocaml_generator::generate_typedef(t_typedef* ttypedef) {
276 f_types_ <<
277 indent() << "type "<< decapitalize(ttypedef->get_symbolic()) << " = " << render_ocaml_type(ttypedef->get_type()) << endl << endl;
278 f_types_i_ <<
279 indent() << "type "<< decapitalize(ttypedef->get_symbolic()) << " = " << render_ocaml_type(ttypedef->get_type()) << endl << endl;
280}
281
282/**
David Reiss0c90f6f2008-02-06 22:18:40 +0000283 * Generates code for an enumerated type.
iproctor9a41a0c2007-07-16 21:59:24 +0000284 * the values.
285 *
286 * @param tenum The enumeration
287 */
288void t_ocaml_generator::generate_enum(t_enum* tenum) {
289 indent(f_types_) << "module " << capitalize(tenum->get_name()) << " = " << endl << "struct" << endl;
290 indent(f_types_i_) << "module " << capitalize(tenum->get_name()) << " : " << endl << "sig" << endl;
291 indent_up();
292 indent(f_types_) << "type t = " << endl;
293 indent(f_types_i_) << "type t = " << endl;
294 indent_up();
295 vector<t_enum_value*> constants = tenum->get_constants();
296 vector<t_enum_value*>::iterator c_iter;
297 int value = -1;
298 for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
299 string name = capitalize((*c_iter)->get_name());
300 indent(f_types_) << "| " << name << endl;
301 indent(f_types_i_) << "| " << name << endl;
302 }
303 indent_down();
304
305 indent(f_types_) << "let to_i = function" << endl;
306 indent(f_types_i_) << "val to_i : t -> int" << endl;
307 indent_up();
308 for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
309 if ((*c_iter)->has_value()) {
310 value = (*c_iter)->get_value();
311 } else {
312 ++value;
313 }
314 string name = capitalize((*c_iter)->get_name());
David Reiss0c90f6f2008-02-06 22:18:40 +0000315
iproctor9a41a0c2007-07-16 21:59:24 +0000316 f_types_ <<
317 indent() << "| " << name << " -> " << value << endl;
318 }
319 indent_down();
320
321 indent(f_types_) << "let of_i = function" << endl;
322 indent(f_types_i_) << "val of_i : int -> t" << endl;
323 indent_up();
324 for(c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
325 if ((*c_iter)->has_value()) {
326 value = (*c_iter)->get_value();
327 } else {
328 ++value;
329 }
330 string name = capitalize((*c_iter)->get_name());
David Reiss0c90f6f2008-02-06 22:18:40 +0000331
iproctor9a41a0c2007-07-16 21:59:24 +0000332 f_types_ <<
333 indent() << "| " << value << " -> " << name << endl;
334 }
335 indent(f_types_) << "| _ -> raise Thrift_error" << endl;
336 indent_down();
337 indent_down();
338 indent(f_types_) << "end" << endl;
339 indent(f_types_i_) << "end" << endl;
340}
341
342/**
343 * Generate a constant value
344 */
345void t_ocaml_generator::generate_const(t_const* tconst) {
346 t_type* type = tconst->get_type();
347 string name = decapitalize(tconst->get_name());
348 t_const_value* value = tconst->get_value();
349
350 indent(f_consts_) << "let " << name << " = " << render_const_value(type, value) << endl << endl;
351}
352
353/**
354 * Prints the value of a constant with the given type. Note that type checking
355 * is NOT performed in this function as it is always run beforehand using the
356 * validate_types method in main.cc
357 */
358string t_ocaml_generator::render_const_value(t_type* type, t_const_value* value) {
David Reiss9a4edfa2008-05-01 05:52:50 +0000359 type = get_true_type(type);
iproctor9a41a0c2007-07-16 21:59:24 +0000360 std::ostringstream out;
361 if (type->is_base_type()) {
362 t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
363 switch (tbase) {
364 case t_base_type::TYPE_STRING:
365 out << "\"" << value->get_string() << "\"";
366 break;
367 case t_base_type::TYPE_BOOL:
368 out << (value->get_integer() > 0 ? "true" : "false");
369 break;
370 case t_base_type::TYPE_BYTE:
371 case t_base_type::TYPE_I16:
372 case t_base_type::TYPE_I32:
373 out << value->get_integer();
374 break;
375 case t_base_type::TYPE_I64:
376 out << value->get_integer() << "L";
377 break;
378 case t_base_type::TYPE_DOUBLE:
379 if (value->get_type() == t_const_value::CV_INTEGER) {
380 out << value->get_integer();
381 } else {
382 out << value->get_double();
383 }
384 break;
385 default:
David Reissdd7796f2007-08-28 21:09:06 +0000386 throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
iproctor9a41a0c2007-07-16 21:59:24 +0000387 }
388 } else if (type->is_enum()) {
389 t_enum* tenum = (t_enum*)type;
390 vector<t_enum_value*> constants = tenum->get_constants();
391 vector<t_enum_value*>::iterator c_iter;
392 int val = -1;
393 for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
394 if ((*c_iter)->has_value()) {
395 val = (*c_iter)->get_value();
396 } else {
397 ++val;
398 }
399 if(val == value->get_integer()){
400 indent(out) << capitalize(tenum->get_name()) << "." << capitalize((*c_iter)->get_name());
401 break;
402 }
403 }
404 } else if (type->is_struct() || type->is_xception()) {
405 string cname = type_name(type);
406 string ct = tmp("_c");
407 out << endl;
408 indent_up();
409 indent(out) << "(let " << ct << " = new " << cname << " in" << endl;
410 indent_up();
411 const vector<t_field*>& fields = ((t_struct*)type)->get_members();
412 vector<t_field*>::const_iterator f_iter;
413 const map<t_const_value*, t_const_value*>& val = value->get_map();
414 map<t_const_value*, t_const_value*>::const_iterator v_iter;
415 for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
416 t_type* field_type = NULL;
417 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
418 if ((*f_iter)->get_name() == v_iter->first->get_string()) {
419 field_type = (*f_iter)->get_type();
420 }
421 }
422 if (field_type == NULL) {
423 throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string();
424 }
425 string fname = v_iter->first->get_string();
426 out << indent();
427 out << ct <<"#set_" << fname << " ";
428 out << render_const_value(field_type, v_iter->second);
429 out << ";" << endl;
430 }
431 indent(out) << ct << ")";
432 indent_down();
433 indent_down();
434 } else if (type->is_map()) {
435 t_type* ktype = ((t_map*)type)->get_key_type();
436 t_type* vtype = ((t_map*)type)->get_val_type();
437 const map<t_const_value*, t_const_value*>& val = value->get_map();
438 map<t_const_value*, t_const_value*>::const_iterator v_iter;
439 string hm = tmp("_hm");
440 out << endl;
441 indent_up();
442 indent(out) << "(let " << hm << " = Hashtbl.create " << val.size() << " in" << endl;
443 indent_up();
444 for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
445 string key = render_const_value(ktype, v_iter->first);
446 string val = render_const_value(vtype, v_iter->second);
447 indent(out) << "Hashtbl.add " << hm << " " << key << " " << val << ";" << endl;
448 }
449 indent(out) << hm << ")";
450 indent_down();
451 indent_down();
452 } else if (type->is_list()) {
453 t_type* etype;
454 etype = ((t_list*)type)->get_elem_type();
455 out << "[" << endl;
456 indent_up();
457 const vector<t_const_value*>& val = value->get_list();
458 vector<t_const_value*>::const_iterator v_iter;
459 for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
460 out << indent();
461 out << render_const_value(etype, *v_iter);
462 out << ";" << endl;
463 }
464 indent_down();
465 indent(out) << "]";
466 } else if (type->is_set()) {
467 t_type* etype = ((t_set*)type)->get_elem_type();
468 const vector<t_const_value*>& val = value->get_list();
469 vector<t_const_value*>::const_iterator v_iter;
470 string hm = tmp("_hm");
471 indent(out) << "(let " << hm << " = Hashtbl.create " << val.size() << " in" << endl;
472 indent_up();
473 for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
474 string val = render_const_value(etype, *v_iter);
475 indent(out) << "Hashtbl.add " << hm << " " << val << " true;" << endl;
476 }
477 indent(out) << hm << ")" << endl;
478 indent_down();
479 out << endl;
David Reiss9a4edfa2008-05-01 05:52:50 +0000480 } else {
481 throw "CANNOT GENERATE CONSTANT FOR TYPE: " + type->get_name();
iproctor9a41a0c2007-07-16 21:59:24 +0000482 }
483 return out.str();
484}
485
486/**
487 * Generates a "struct"
488 */
489void t_ocaml_generator::generate_struct(t_struct* tstruct) {
490 generate_ocaml_struct(tstruct, false);
491}
492
493/**
494 * Generates a struct definition for a thrift exception. Basically the same
495 * as a struct, but also has an exception declaration.
496 *
497 * @param txception The struct definition
498 */
499void t_ocaml_generator::generate_xception(t_struct* txception) {
David Reiss0c90f6f2008-02-06 22:18:40 +0000500 generate_ocaml_struct(txception, true);
iproctor9a41a0c2007-07-16 21:59:24 +0000501}
502
503/**
504 * Generates an OCaml struct
505 */
506void t_ocaml_generator::generate_ocaml_struct(t_struct* tstruct,
507 bool is_exception) {
508 generate_ocaml_struct_definition(f_types_, tstruct, is_exception);
509 generate_ocaml_struct_sig(f_types_i_,tstruct,is_exception);
510}
511
512/**
David Reiss0c90f6f2008-02-06 22:18:40 +0000513 * Generates a struct definition for a thrift data type.
iproctor9a41a0c2007-07-16 21:59:24 +0000514 *
515 * @param tstruct The struct definition
516 */
517void t_ocaml_generator::generate_ocaml_struct_definition(ofstream& out,
518 t_struct* tstruct,
519 bool is_exception) {
520 const vector<t_field*>& members = tstruct->get_members();
David Reiss0c90f6f2008-02-06 22:18:40 +0000521 vector<t_field*>::const_iterator m_iter;
iproctor9a41a0c2007-07-16 21:59:24 +0000522 string tname = type_name(tstruct);
523 indent(out) << "class " << tname << " =" << endl;
524 indent(out) << "object (self)" << endl;
525
526 indent_up();
527
528 string x = tmp("_x");
529 if (members.size() > 0) {
530 for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
531 string mname = decapitalize((*m_iter)->get_name());
532 indent(out) << "val mutable _" << mname << " : " << render_ocaml_type((*m_iter)->get_type()) << " option = None" << endl;
533 indent(out) << "method get_" << mname << " = _" << mname << endl;
534 indent(out) << "method grab_" << mname << " = match _"<<mname<<" with None->raise (Field_empty \""<<tname<<"."<<mname<<"\") | Some " << x <<" -> " << x << endl;
535 indent(out) << "method set_" << mname << " " << x << " = _" << mname << " <- Some " << x << endl;
536 }
537 }
538 generate_ocaml_struct_writer(out, tstruct);
539 indent_down();
540 indent(out) << "end" << endl;
541
542 if(is_exception){
543 indent(out) << "exception " << capitalize(tname) <<" of " << tname << endl;
544 }
545
546 generate_ocaml_struct_reader(out, tstruct);
547}
548
549/**
David Reiss0c90f6f2008-02-06 22:18:40 +0000550 * Generates a struct definition for a thrift data type.
iproctor9a41a0c2007-07-16 21:59:24 +0000551 *
552 * @param tstruct The struct definition
553 */
554void t_ocaml_generator::generate_ocaml_struct_sig(ofstream& out,
555 t_struct* tstruct,
556 bool is_exception) {
557 const vector<t_field*>& members = tstruct->get_members();
David Reiss0c90f6f2008-02-06 22:18:40 +0000558 vector<t_field*>::const_iterator m_iter;
iproctor9a41a0c2007-07-16 21:59:24 +0000559 string tname = type_name(tstruct);
560 indent(out) << "class " << tname << " :" << endl;
561 indent(out) << "object" << endl;
562
563 indent_up();
564
565 string x = tmp("_x");
566 if (members.size() > 0) {
567 for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
568 string mname = decapitalize((*m_iter)->get_name());
569 string type = render_ocaml_type((*m_iter)->get_type());
570 indent(out) << "method get_" << mname << " : " << type << " option" << endl;
571 indent(out) << "method grab_" << mname << " : " << type << endl;
572 indent(out) << "method set_" << mname << " : " << type << " -> unit" << endl;
573 }
574 }
575 indent(out) << "method write : Protocol.t -> unit" << endl;
576 indent_down();
577 indent(out) << "end" << endl;
578
579 if(is_exception){
580 indent(out) << "exception " << capitalize(tname) <<" of " << tname << endl;
581 }
582
583 indent(out) << "val read_" << tname << " : Protocol.t -> " << tname << endl;
584}
585
586/**
587 * Generates the read method for a struct
588 */
589void t_ocaml_generator::generate_ocaml_struct_reader(ofstream& out, t_struct* tstruct) {
590 const vector<t_field*>& fields = tstruct->get_members();
591 vector<t_field*>::const_iterator f_iter;
592 string sname = type_name(tstruct);
593 string str = tmp("_str");
594 string t = tmp("_t");
595 string id = tmp("_id");
596 indent(out) <<
597 "let rec read_" << sname << " (iprot : Protocol.t) =" << endl;
598 indent_up();
599 indent(out) << "let " << str << " = new " << sname << " in" << endl;
600 indent_up();
601 indent(out) <<
David Reiss0c90f6f2008-02-06 22:18:40 +0000602 "ignore(iprot#readStructBegin);" << endl;
iproctor9a41a0c2007-07-16 21:59:24 +0000603
604 // Loop over reading in fields
605 indent(out) <<
606 "(try while true do" << endl;
607 indent_up();
608 indent_up();
David Reiss0c90f6f2008-02-06 22:18:40 +0000609
iproctor9a41a0c2007-07-16 21:59:24 +0000610 // Read beginning field marker
611 indent(out) <<
612 "let (_," << t <<","<<id<<") = iprot#readFieldBegin in" << endl;
David Reiss0c90f6f2008-02-06 22:18:40 +0000613
iproctor9a41a0c2007-07-16 21:59:24 +0000614 // Check for field STOP marker and break
615 indent(out) <<
616 "if " << t <<" = Protocol.T_STOP then" << endl;
617 indent_up();
618 indent(out) <<
619 "raise Break" << endl;
620 indent_down();
621 indent(out) << "else ();" << endl;
David Reiss0c90f6f2008-02-06 22:18:40 +0000622
iproctor9a41a0c2007-07-16 21:59:24 +0000623 indent(out) << "(match " << id<<" with " << endl;
624 indent_up();
625 // Generate deserialization code for known cases
626 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
627 indent(out) << "| " << (*f_iter)->get_key() << " -> (";
628 out << "if " << t <<" = " << type_to_enum((*f_iter)->get_type()) << " then" << endl;
629 indent_up();
630 indent_up();
631 generate_deserialize_field(out, *f_iter,str);
632 indent_down();
633 out <<
634 indent() << "else" << endl <<
635 indent() << " iprot#skip "<< t << ")" << endl;
636 indent_down();
637 }
638
639 // In the default case we skip the field
640 out <<
641 indent() << "| _ -> " << "iprot#skip "<<t<<");" << endl;
David Reiss0c90f6f2008-02-06 22:18:40 +0000642 indent_down();
iproctor9a41a0c2007-07-16 21:59:24 +0000643 // Read field end marker
644 indent(out) << "iprot#readFieldEnd;" << endl;
645 indent_down();
646 indent(out) << "done; ()" << endl;
647 indent_down();
648 indent(out) << "with Break -> ());" << endl;
649
650 indent(out) <<
651 "iprot#readStructEnd;" << endl;
652
653 indent(out) << str << endl << endl;
654 indent_down();
655 indent_down();
656}
657
658void t_ocaml_generator::generate_ocaml_struct_writer(ofstream& out,
659 t_struct* tstruct) {
660 string name = tstruct->get_name();
661 const vector<t_field*>& fields = tstruct->get_members();
662 vector<t_field*>::const_iterator f_iter;
663 string str = tmp("_str");
664 string f = tmp("_f");
665
666 indent(out) <<
667 "method write (oprot : Protocol.t) =" << endl;
668 indent_up();
669 indent(out) <<
670 "oprot#writeStructBegin \""<<name<<"\";" << endl;
671
672 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
673 // Write field header
674 string mname = "_"+decapitalize((*f_iter)->get_name());
675 indent(out) <<
676 "(match " << mname << " with None -> () | Some _v -> " << endl;
677 indent_up();
678 indent(out) << "oprot#writeFieldBegin(\""<< (*f_iter)->get_name()<<"\","
679 <<type_to_enum((*f_iter)->get_type())<<","
680 <<(*f_iter)->get_key()<<");" << endl;
681
682 // Write field contents
683 generate_serialize_field(out, *f_iter, "_v");
684
685 // Write field closer
686 indent(out) << "oprot#writeFieldEnd" << endl;
687
688 indent_down();
689 indent(out) << ");" << endl;
690 }
691
692 // Write the struct map
693 out <<
694 indent() << "oprot#writeFieldStop;" << endl <<
695 indent() << "oprot#writeStructEnd" << endl;
696
697 indent_down();
698}
699
700/**
701 * Generates a thrift service.
702 *
703 * @param tservice The service definition
704 */
705void t_ocaml_generator::generate_service(t_service* tservice) {
dweatherford65b70752007-10-31 02:18:14 +0000706 string f_service_name = get_out_dir()+capitalize(service_name_)+".ml";
iproctor9a41a0c2007-07-16 21:59:24 +0000707 f_service_.open(f_service_name.c_str());
dweatherford65b70752007-10-31 02:18:14 +0000708 string f_service_i_name = get_out_dir()+capitalize(service_name_)+".mli";
iproctor9a41a0c2007-07-16 21:59:24 +0000709 f_service_i_.open(f_service_i_name.c_str());
710
711 f_service_ <<
712 ocaml_autogen_comment() << endl <<
713 ocaml_imports() << endl;
714 f_service_i_ <<
715 ocaml_autogen_comment() << endl <<
716 ocaml_imports() << endl;
717
718 /* if (tservice->get_extends() != NULL) {
719 f_service_ <<
720 "open " << capitalize(tservice->get_extends()->get_name()) << endl;
721 f_service_i_ <<
722 "open " << capitalize(tservice->get_extends()->get_name()) << endl;
723 }
724 */
725 f_service_ <<
David Reiss0c90f6f2008-02-06 22:18:40 +0000726 "open " << capitalize(program_name_) << "_types" << endl <<
iproctor9a41a0c2007-07-16 21:59:24 +0000727 endl;
728
729 f_service_i_ <<
David Reiss0c90f6f2008-02-06 22:18:40 +0000730 "open " << capitalize(program_name_) << "_types" << endl <<
iproctor9a41a0c2007-07-16 21:59:24 +0000731 endl;
732
David Reiss0c90f6f2008-02-06 22:18:40 +0000733 // Generate the three main parts of the service
iproctor9a41a0c2007-07-16 21:59:24 +0000734 generate_service_helpers(tservice);
735 generate_service_interface(tservice);
736 generate_service_client(tservice);
737 generate_service_server(tservice);
738
739
740 // Close service file
741 f_service_.close();
742 f_service_i_.close();
743}
744
745/**
746 * Generates helper functions for a service.
747 *
748 * @param tservice The service to generate a header definition for
749 */
750void t_ocaml_generator::generate_service_helpers(t_service* tservice) {
751 vector<t_function*> functions = tservice->get_functions();
752 vector<t_function*>::iterator f_iter;
753
754 indent(f_service_) <<
755 "(* HELPER FUNCTIONS AND STRUCTURES *)" << endl << endl;
756
757 for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
758 t_struct* ts = (*f_iter)->get_arglist();
759 generate_ocaml_struct_definition(f_service_, ts, false);
760 generate_ocaml_function_helpers(*f_iter);
761 }
762}
763
764/**
765 * Generates a struct and helpers for a function.
766 *
767 * @param tfunction The function
768 */
769void t_ocaml_generator::generate_ocaml_function_helpers(t_function* tfunction) {
770 t_struct result(program_, decapitalize(tfunction->get_name()) + "_result");
771 t_field success(tfunction->get_returntype(), "success", 0);
772 if (!tfunction->get_returntype()->is_void()) {
773 result.append(&success);
774 }
775
776 t_struct* xs = tfunction->get_xceptions();
777 const vector<t_field*>& fields = xs->get_members();
778 vector<t_field*>::const_iterator f_iter;
779 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
780 result.append(*f_iter);
781 }
782 generate_ocaml_struct_definition(f_service_, &result, false);
783}
784
785/**
786 * Generates a service interface definition.
787 *
788 * @param tservice The service to generate a header definition for
789 */
790void t_ocaml_generator::generate_service_interface(t_service* tservice) {
791 f_service_ <<
792 indent() << "class virtual iface =" << endl << "object (self)" << endl;
793 f_service_i_ <<
794 indent() << "class virtual iface :" << endl << "object" << endl;
795
796 indent_up();
797
798 if (tservice->get_extends() != NULL) {
799 string extends = type_name(tservice->get_extends());
800 indent(f_service_) << "inherit " << extends << ".iface" << endl;
801 indent(f_service_i_) << "inherit " << extends << ".iface" << endl;
802 }
803
804 vector<t_function*> functions = tservice->get_functions();
David Reiss0c90f6f2008-02-06 22:18:40 +0000805 vector<t_function*>::iterator f_iter;
iproctor9a41a0c2007-07-16 21:59:24 +0000806 for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
807 string ft = function_type(*f_iter,true,true);
808 f_service_ <<
809 indent() << "method virtual " << decapitalize((*f_iter)->get_name()) << " : " << ft << endl;
810 f_service_i_ <<
811 indent() << "method virtual " << decapitalize((*f_iter)->get_name()) << " : " << ft << endl;
812 }
813 indent_down();
814 indent(f_service_) << "end" << endl << endl;
815 indent(f_service_i_) << "end" << endl << endl;
816}
817
818/**
819 * Generates a service client definition. Note that in OCaml, the client doesn't implement iface. This is because
820 * The client does not (and should not have to) deal with arguments being None.
821 *
822 * @param tservice The service to generate a server for.
823 */
824void t_ocaml_generator::generate_service_client(t_service* tservice) {
825 string extends = "";
826 indent(f_service_) <<
827 "class client (iprot : Protocol.t) (oprot : Protocol.t) =" << endl << "object (self)" << endl;
828 indent(f_service_i_) <<
829 "class client : Protocol.t -> Protocol.t -> " << endl << "object" << endl;
830 indent_up();
831
832
833 if (tservice->get_extends() != NULL) {
834 extends = type_name(tservice->get_extends());
835 indent(f_service_) << "inherit " << extends << ".client iprot oprot as super" << endl;
836 indent(f_service_i_) << "inherit " << extends << ".client" << endl;
837 }
838 indent(f_service_) << "val mutable seqid = 0" << endl;
David Reiss0c90f6f2008-02-06 22:18:40 +0000839
iproctor9a41a0c2007-07-16 21:59:24 +0000840
841 // Generate client method implementations
842 vector<t_function*> functions = tservice->get_functions();
David Reiss0c90f6f2008-02-06 22:18:40 +0000843 vector<t_function*>::const_iterator f_iter;
iproctor9a41a0c2007-07-16 21:59:24 +0000844 for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
845 t_struct* arg_struct = (*f_iter)->get_arglist();
846 const vector<t_field*>& fields = arg_struct->get_members();
847 vector<t_field*>::const_iterator fld_iter;
848 string funname = (*f_iter)->get_name();
849
850 // Open function
851 indent(f_service_) <<
852 "method " << function_signature(*f_iter) << " = " << endl;
853 indent(f_service_i_) <<
854 "method " << decapitalize((*f_iter)->get_name()) << " : " << function_type(*f_iter,true,false) << endl;
855 indent_up();
856 indent(f_service_) <<
857 "self#send_" << funname;
858
David Reiss0c90f6f2008-02-06 22:18:40 +0000859
iproctor9a41a0c2007-07-16 21:59:24 +0000860 for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
861 f_service_ << " " << decapitalize((*fld_iter)->get_name());
862 }
863 f_service_ << ";" << endl;
David Reiss0c90f6f2008-02-06 22:18:40 +0000864
iproctor9a41a0c2007-07-16 21:59:24 +0000865 if (!(*f_iter)->is_async()) {
866 f_service_ << indent();
867 f_service_ <<
868 "self#recv_" << funname << endl;
869 }
870 indent_down();
David Reiss0c90f6f2008-02-06 22:18:40 +0000871
iproctor9a41a0c2007-07-16 21:59:24 +0000872 indent(f_service_) <<
873 "method private send_" << function_signature(*f_iter) << " = " << endl;
874 indent_up();
David Reiss0c90f6f2008-02-06 22:18:40 +0000875
iproctor9a41a0c2007-07-16 21:59:24 +0000876 std::string argsname = decapitalize((*f_iter)->get_name() + "_args");
David Reiss0c90f6f2008-02-06 22:18:40 +0000877
iproctor9a41a0c2007-07-16 21:59:24 +0000878 // Serialize the request header
879 f_service_ <<
880 indent() << "oprot#writeMessageBegin (\"" << (*f_iter)->get_name() << "\", Protocol.CALL, seqid);" << endl;
David Reiss0c90f6f2008-02-06 22:18:40 +0000881
iproctor9a41a0c2007-07-16 21:59:24 +0000882 f_service_ <<
883 indent() << "let args = new " << argsname << " in" << endl;
884 indent_up();
David Reiss0c90f6f2008-02-06 22:18:40 +0000885
iproctor9a41a0c2007-07-16 21:59:24 +0000886 for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
887 f_service_ <<
888 indent() << "args#set_" << (*fld_iter)->get_name() << " " << (*fld_iter)->get_name() << ";" << endl;
889 }
David Reiss0c90f6f2008-02-06 22:18:40 +0000890
iproctor9a41a0c2007-07-16 21:59:24 +0000891 // Write to the stream
892 f_service_ <<
893 indent() << "args#write oprot;" << endl <<
894 indent() << "oprot#writeMessageEnd;" << endl <<
David Reiss0c90f6f2008-02-06 22:18:40 +0000895 indent() << "oprot#getTransport#flush" << endl;
896
iproctor9a41a0c2007-07-16 21:59:24 +0000897 indent_down();
898 indent_down();
899
900 if (!(*f_iter)->is_async()) {
901 std::string resultname = decapitalize((*f_iter)->get_name() + "_result");
902 t_struct noargs(program_);
David Reiss0c90f6f2008-02-06 22:18:40 +0000903
iproctor9a41a0c2007-07-16 21:59:24 +0000904 t_function recv_function((*f_iter)->get_returntype(),
905 string("recv_") + (*f_iter)->get_name(),
906 &noargs);
907 // Open function
908 f_service_ <<
909 indent() << "method private " << function_signature(&recv_function) << " =" << endl;
910 indent_up();
911
912 // TODO(mcslee): Validate message reply here, seq ids etc.
913
914 f_service_ <<
915 indent() << "let (fname, mtype, rseqid) = iprot#readMessageBegin in" << endl;
916 indent_up();
917 f_service_ <<
918 indent() << "(if mtype = Protocol.EXCEPTION then" << endl <<
919 indent() << " let x = Application_Exn.read iprot in" << endl;
920 indent_up();
921 f_service_ <<
iproctor8361bf02008-04-10 00:31:55 +0000922 indent() << " (iprot#readMessageEnd;" <<
923 indent() << " raise (Application_Exn.E x))" << endl;
iproctor9a41a0c2007-07-16 21:59:24 +0000924 indent_down();
925 f_service_ <<
926 indent() << "else ());" << endl;
927 string res = "_";
928
929 t_struct* xs = (*f_iter)->get_xceptions();
930 const std::vector<t_field*>& xceptions = xs->get_members();
931
932 if (!(*f_iter)->get_returntype()->is_void() || xceptions.size() > 0) {
933 res = "result";
934 }
935 f_service_ <<
936 indent() << "let "<<res<<" = read_" << resultname << " iprot in" << endl;
937 indent_up();
938 f_service_ <<
939 indent() << "iprot#readMessageEnd;" << endl;
940
941 // Careful, only return _result if not a void function
942 if (!(*f_iter)->get_returntype()->is_void()) {
943 f_service_ <<
944 indent() << "match result#get_success with Some v -> v | None -> (" << endl;
945 indent_up();
946 }
David Reiss0c90f6f2008-02-06 22:18:40 +0000947
948
iproctor9a41a0c2007-07-16 21:59:24 +0000949 vector<t_field*>::const_iterator x_iter;
950 for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
951 f_service_ <<
952 indent() << "(match result#get_" << (*x_iter)->get_name() << " with None -> () | Some _v ->" << endl;
953 indent(f_service_) << " raise (" << capitalize(type_name((*x_iter)->get_type())) << " _v));" << endl;
954 }
David Reiss0c90f6f2008-02-06 22:18:40 +0000955
iproctor9a41a0c2007-07-16 21:59:24 +0000956 // Careful, only return _result if not a void function
957 if ((*f_iter)->get_returntype()->is_void()) {
958 indent(f_service_) <<
959 "()" << endl;
960 } else {
961 f_service_ <<
962 indent() << "raise (Application_Exn.E (Application_Exn.create Application_Exn.MISSING_RESULT \"" << (*f_iter)->get_name() << " failed: unknown result\")))" << endl;
963 indent_down();
David Reiss0c90f6f2008-02-06 22:18:40 +0000964 }
iproctor9a41a0c2007-07-16 21:59:24 +0000965
966 // Close function
967 indent_down();
968 indent_down();
969 indent_down();
970 }
971 }
972
973 indent_down();
974 indent(f_service_) << "end" << endl << endl;
975 indent(f_service_i_) << "end" << endl << endl;
976}
977
978/**
979 * Generates a service server definition.
980 *
981 * @param tservice The service to generate a server for.
982 */
983void t_ocaml_generator::generate_service_server(t_service* tservice) {
984 // Generate the dispatch methods
985 vector<t_function*> functions = tservice->get_functions();
David Reiss0c90f6f2008-02-06 22:18:40 +0000986 vector<t_function*>::iterator f_iter;
iproctor9a41a0c2007-07-16 21:59:24 +0000987
988
989 // Generate the header portion
990 indent(f_service_) <<
991 "class processor (handler : iface) =" << endl << indent() << "object (self)" << endl;
992 indent(f_service_i_) <<
993 "class processor : iface ->" << endl << indent() << "object" << endl;
994 indent_up();
995
996 f_service_ <<
997 indent() << "inherit Processor.t" << endl <<
998 endl;
999 f_service_i_ <<
1000 indent() << "inherit Processor.t" << endl <<
1001 endl;
1002 string extends = "";
David Reiss0c90f6f2008-02-06 22:18:40 +00001003
iproctor9a41a0c2007-07-16 21:59:24 +00001004 if (tservice->get_extends() != NULL) {
1005 extends = type_name(tservice->get_extends());
1006 indent(f_service_) << "inherit " + extends + ".processor (handler :> " + extends + ".iface)" << endl;
1007 indent(f_service_i_) << "inherit " + extends + ".processor" << endl;
1008 }
1009
1010 if (extends.empty()) {
1011 indent(f_service_) << "val processMap = Hashtbl.create " << functions.size() << endl;
1012 }
1013 indent(f_service_i_) << "val processMap : (string, int * Protocol.t * Protocol.t -> unit) Hashtbl.t" << endl;
David Reiss0c90f6f2008-02-06 22:18:40 +00001014
iproctor9a41a0c2007-07-16 21:59:24 +00001015 // Generate the server implementation
1016 indent(f_service_) <<
1017 "method process iprot oprot =" << endl;
1018 indent(f_service_i_) <<
1019 "method process : Protocol.t -> Protocol.t -> bool" << endl;
1020 indent_up();
1021
1022 f_service_ <<
1023 indent() << "let (name, typ, seqid) = iprot#readMessageBegin in" << endl;
1024 indent_up();
1025 // TODO(mcslee): validate message
1026
1027 // HOT: dictionary function lookup
1028 f_service_ <<
1029 indent() << "if Hashtbl.mem processMap name then" << endl <<
1030 indent() << " (Hashtbl.find processMap name) (seqid, iprot, oprot)" << endl <<
1031 indent() << "else (" << endl <<
1032 indent() << " iprot#skip(Protocol.T_STRUCT);" << endl <<
1033 indent() << " iprot#readMessageEnd;" << endl <<
1034 indent() << " let x = Application_Exn.create Application_Exn.UNKNOWN_METHOD (\"Unknown function \"^name) in" << endl <<
1035 indent() << " oprot#writeMessageBegin(name, Protocol.EXCEPTION, seqid);" << endl <<
1036 indent() << " x#write oprot;" << endl <<
1037 indent() << " oprot#writeMessageEnd;" << endl <<
1038 indent() << " oprot#getTransport#flush" << endl <<
1039 indent() << ");" << endl;
1040
1041 // Read end of args field, the T_STOP, and the struct close
1042 f_service_ <<
1043 indent() << "true" << endl;
1044 indent_down();
1045 indent_down();
1046 // Generate the process subfunctions
1047 for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
1048 generate_process_function(tservice, *f_iter);
1049 }
1050
1051 indent(f_service_) << "initializer" << endl;
1052 indent_up();
1053 for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
1054 f_service_ <<
1055 indent() << "Hashtbl.add processMap \"" << (*f_iter)->get_name() << "\" self#process_" << (*f_iter)->get_name() << ";" << endl;
David Reiss0c90f6f2008-02-06 22:18:40 +00001056 }
iproctor9a41a0c2007-07-16 21:59:24 +00001057 indent_down();
1058
1059 indent_down();
1060 indent(f_service_) << "end" << endl << endl;
1061 indent(f_service_i_) << "end" << endl << endl;
1062}
1063
1064/**
1065 * Generates a process function definition.
1066 *
1067 * @param tfunction The function to write a dispatcher for
1068 */
1069void t_ocaml_generator::generate_process_function(t_service* tservice,
1070 t_function* tfunction) {
1071 // Open function
1072 indent(f_service_) <<
1073 "method private process_" << tfunction->get_name() <<
1074 " (seqid, iprot, oprot) =" << endl;
1075 indent_up();
1076
1077 string argsname = decapitalize(tfunction->get_name()) + "_args";
1078 string resultname = decapitalize(tfunction->get_name()) + "_result";
1079
1080 // Generate the function call
1081 t_struct* arg_struct = tfunction->get_arglist();
1082 const std::vector<t_field*>& fields = arg_struct->get_members();
1083 vector<t_field*>::const_iterator f_iter;
1084
1085 string args = "args";
1086 if(fields.size() == 0){
1087 args="_";
1088 }
1089
1090 f_service_ <<
1091 indent() << "let "<<args<<" = read_" << argsname << " iprot in" << endl;
1092 indent_up();
1093 f_service_ <<
1094 indent() << "iprot#readMessageEnd;" << endl;
1095
1096 t_struct* xs = tfunction->get_xceptions();
1097 const std::vector<t_field*>& xceptions = xs->get_members();
1098 vector<t_field*>::const_iterator x_iter;
1099
1100 // Declare result for non async function
1101 if (!tfunction->is_async()) {
1102 f_service_ <<
1103 indent() << "let result = new " << resultname << " in" << endl;
1104 indent_up();
1105 }
1106
1107 // Try block for a function with exceptions
1108 if (xceptions.size() > 0) {
1109 f_service_ <<
1110 indent() << "(try" << endl;
1111 indent_up();
1112 }
iproctor9a41a0c2007-07-16 21:59:24 +00001113
1114
David Reiss0c90f6f2008-02-06 22:18:40 +00001115
1116
iproctor9a41a0c2007-07-16 21:59:24 +00001117 f_service_ << indent();
1118 if (!tfunction->is_async() && !tfunction->get_returntype()->is_void()) {
1119 f_service_ << "result#set_success ";
1120 }
1121 f_service_ <<
1122 "(handler#" << tfunction->get_name();
1123 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
1124 f_service_ << " args#get_" << (*f_iter)->get_name();
1125 }
1126 f_service_ << ");" << endl;
1127
David Reiss0c90f6f2008-02-06 22:18:40 +00001128
iproctor9a41a0c2007-07-16 21:59:24 +00001129 if (xceptions.size() > 0) {
1130 indent_down();
1131 indent(f_service_) << "with" <<endl;
1132 indent_up();
1133 for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
1134 f_service_ <<
1135 indent() << "| " << capitalize(type_name((*x_iter)->get_type())) << " " << (*x_iter)->get_name() << " -> " << endl;
1136 indent_up();
1137 indent_up();
1138 if(!tfunction->is_async()){
1139 f_service_ <<
1140 indent() << "result#set_" << (*x_iter)->get_name() << " " << (*x_iter)->get_name() << endl;
1141 } else {
1142 indent(f_service_) << "()";
1143 }
1144 indent_down();
1145 indent_down();
1146 }
1147 indent_down();
1148 f_service_ << indent() << ");" << endl;
1149 }
1150
1151
1152
1153 // Shortcut out here for async functions
1154 if (tfunction->is_async()) {
1155 f_service_ <<
1156 indent() << "()" << endl;
1157 indent_down();
1158 indent_down();
1159 return;
1160 }
1161
1162 f_service_ <<
1163 indent() << "oprot#writeMessageBegin (\"" << tfunction->get_name() << "\", Protocol.REPLY, seqid);" << endl <<
1164 indent() << "result#write oprot;" << endl <<
1165 indent() << "oprot#writeMessageEnd;" << endl <<
1166 indent() << "oprot#getTransport#flush" << endl;
1167
1168 // Close function
1169 indent_down();
1170 indent_down();
1171 indent_down();
1172}
1173
1174/**
1175 * Deserializes a field of any type.
1176 */
1177void t_ocaml_generator::generate_deserialize_field(ofstream &out,
1178 t_field* tfield,
1179 string prefix){
1180 t_type* type = tfield->get_type();
1181
1182
1183 string name = decapitalize(tfield->get_name());
1184 indent(out) << prefix << "#set_"<<name << " ";
1185 generate_deserialize_type(out,type);
1186 out << endl;
1187}
1188
1189
1190/**
1191 * Deserializes a field of any type.
1192 */
1193void t_ocaml_generator::generate_deserialize_type(ofstream &out,
1194 t_type* type){
David Reisse087a302007-08-23 21:43:25 +00001195 type = get_true_type(type);
iproctor9a41a0c2007-07-16 21:59:24 +00001196
1197 if (type->is_void()) {
1198 throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE";
1199 }
1200
1201
1202 if (type->is_struct() || type->is_xception()) {
1203 generate_deserialize_struct(out,
1204 (t_struct*)type);
1205 } else if (type->is_container()) {
1206 generate_deserialize_container(out, type);
1207 } else if (type->is_base_type()) {
1208 out << "iprot#";
1209 t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
1210 switch (tbase) {
1211 case t_base_type::TYPE_VOID:
1212 throw "compiler error: cannot serialize void field in a struct";
1213 break;
David Reiss0c90f6f2008-02-06 22:18:40 +00001214 case t_base_type::TYPE_STRING:
iproctor9a41a0c2007-07-16 21:59:24 +00001215 out << "readString";
1216 break;
1217 case t_base_type::TYPE_BOOL:
1218 out << "readBool";
1219 break;
1220 case t_base_type::TYPE_BYTE:
1221 out << "readByte";
1222 break;
1223 case t_base_type::TYPE_I16:
1224 out << "readI16";
1225 break;
1226 case t_base_type::TYPE_I32:
1227 out << "readI32";
1228 break;
1229 case t_base_type::TYPE_I64:
1230 out << "readI64";
1231 break;
1232 case t_base_type::TYPE_DOUBLE:
1233 out << "readDouble";
1234 break;
1235 default:
David Reissdd7796f2007-08-28 21:09:06 +00001236 throw "compiler error: no PHP name for base type " + t_base_type::t_base_name(tbase);
iproctor9a41a0c2007-07-16 21:59:24 +00001237 }
1238 } else if (type->is_enum()) {
1239 string ename = capitalize(type->get_name());
1240 out << "(" <<ename << ".of_i iprot#readI32)";
1241 } else {
1242 printf("DO NOT KNOW HOW TO DESERIALIZE TYPE '%s'\n",
1243 type->get_name().c_str());
1244 }
1245}
David Reiss0c90f6f2008-02-06 22:18:40 +00001246
iproctor9a41a0c2007-07-16 21:59:24 +00001247
1248/**
1249 * Generates an unserializer for a struct, calling read()
1250 */
1251void t_ocaml_generator::generate_deserialize_struct(ofstream &out,
1252 t_struct* tstruct) {
1253 string name = decapitalize(tstruct->get_name());
1254 out << "(read_" << name << " iprot)";
1255
1256}
1257
1258/**
1259 * Serialize a container by writing out the header followed by
1260 * data and then a footer.
1261 */
1262void t_ocaml_generator::generate_deserialize_container(ofstream &out,
1263 t_type* ttype) {
1264 string size = tmp("_size");
1265 string ktype = tmp("_ktype");
1266 string vtype = tmp("_vtype");
1267 string etype = tmp("_etype");
1268 string con = tmp("_con");
David Reiss0c90f6f2008-02-06 22:18:40 +00001269
iproctor9a41a0c2007-07-16 21:59:24 +00001270 t_field fsize(g_type_i32, size);
1271 t_field fktype(g_type_byte, ktype);
1272 t_field fvtype(g_type_byte, vtype);
1273 t_field fetype(g_type_byte, etype);
1274
1275 out << endl;
1276 indent_up();
1277 // Declare variables, read header
1278 if (ttype->is_map()) {
1279 indent(out) << "(let ("<<ktype<<","<<vtype<<","<<size<<") = iprot#readMapBegin in" << endl;
1280 indent(out) << "let "<<con<<" = Hashtbl.create "<<size<<" in" << endl;
David Reiss0c90f6f2008-02-06 22:18:40 +00001281 indent_up();
iproctor9a41a0c2007-07-16 21:59:24 +00001282 indent(out) << "for i = 1 to "<<size<<" do" <<endl;
1283 indent_up();
1284 indent(out) << "let _k = ";
1285 generate_deserialize_type(out,((t_map*)ttype)->get_key_type());
1286 out << " in" << endl;
1287 indent(out) << "let _v = ";
1288 generate_deserialize_type(out,((t_map*)ttype)->get_val_type());
1289 out << " in" << endl;
1290 indent_up();
1291 indent(out) << "Hashtbl.add "<<con<< " _k _v" << endl;
1292 indent_down();
1293 indent_down();
1294 indent(out) << "done; iprot#readMapEnd; "<<con<<")";
1295 indent_down();
1296 } else if (ttype->is_set()) {
1297 indent(out) << "(let ("<<etype<<","<<size<<") = iprot#readSetBegin in" << endl;
1298 indent(out) << "let "<<con<<" = Hashtbl.create "<<size<<" in" << endl;
David Reiss0c90f6f2008-02-06 22:18:40 +00001299 indent_up();
iproctor9a41a0c2007-07-16 21:59:24 +00001300 indent(out) << "for i = 1 to "<<size<<" do" <<endl;
1301 indent_up();
1302 indent(out) << "Hashtbl.add "<<con<<" ";
1303 generate_deserialize_type(out,((t_set*)ttype)->get_elem_type());
1304 out << " true" << endl;
1305 indent_down();
1306 indent(out) << "done; iprot#readSetEnd; "<<con<<")";
1307 indent_down();
1308 } else if (ttype->is_list()) {
1309 indent(out) << "(let ("<<etype<<","<<size<<") = iprot#readListBegin in" << endl;
1310 indent_up();
David Reiss0c90f6f2008-02-06 22:18:40 +00001311 indent(out) << "let "<<con<<" = (Array.to_list (Array.init "<<size<<" (fun _ -> ";
iproctor9a41a0c2007-07-16 21:59:24 +00001312 generate_deserialize_type(out,((t_list*)ttype)->get_elem_type());
1313 out << "))) in" << endl;
1314 indent_up();
1315 indent(out) << "iprot#readListEnd; "<<con<<")";
1316 indent_down();
1317 indent_down();
1318 }
1319 indent_down();
1320}
1321
1322
1323
1324/**
1325 * Serializes a field of any type.
1326 *
1327 * @param tfield The field to serialize
1328 * @param prefix Name to prepend to field name
1329 */
1330void t_ocaml_generator::generate_serialize_field(ofstream &out,
1331 t_field* tfield,
1332 string name) {
David Reisse087a302007-08-23 21:43:25 +00001333 t_type* type = get_true_type(tfield->get_type());
iproctor9a41a0c2007-07-16 21:59:24 +00001334
1335 // Do nothing for void types
1336 if (type->is_void()) {
1337 throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " +
1338 tfield->get_name();
1339 }
1340
1341 if(name.length() == 0){
1342 name = decapitalize(tfield->get_name());
David Reiss0c90f6f2008-02-06 22:18:40 +00001343 }
iproctor9a41a0c2007-07-16 21:59:24 +00001344
1345 if (type->is_struct() || type->is_xception()) {
1346 generate_serialize_struct(out,
1347 (t_struct*)type,
1348 name);
1349 } else if (type->is_container()) {
1350 generate_serialize_container(out,
1351 type,
1352 name);
1353 } else if (type->is_base_type() || type->is_enum()) {
1354
1355
1356 indent(out) <<
1357 "oprot#";
David Reiss0c90f6f2008-02-06 22:18:40 +00001358
iproctor9a41a0c2007-07-16 21:59:24 +00001359 if (type->is_base_type()) {
1360 t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
1361 switch (tbase) {
1362 case t_base_type::TYPE_VOID:
1363 throw
1364 "compiler error: cannot serialize void field in a struct: " + name;
1365 break;
1366 case t_base_type::TYPE_STRING:
1367 out << "writeString(" << name << ")";
1368 break;
1369 case t_base_type::TYPE_BOOL:
1370 out << "writeBool(" << name << ")";
1371 break;
1372 case t_base_type::TYPE_BYTE:
1373 out << "writeByte(" << name << ")";
1374 break;
1375 case t_base_type::TYPE_I16:
1376 out << "writeI16(" << name << ")";
1377 break;
1378 case t_base_type::TYPE_I32:
1379 out << "writeI32(" << name << ")";
1380 break;
1381 case t_base_type::TYPE_I64:
1382 out << "writeI64(" << name << ")";
1383 break;
1384 case t_base_type::TYPE_DOUBLE:
1385 out << "writeDouble(" << name << ")";
1386 break;
1387 default:
David Reissdd7796f2007-08-28 21:09:06 +00001388 throw "compiler error: no ocaml name for base type " + t_base_type::t_base_name(tbase);
iproctor9a41a0c2007-07-16 21:59:24 +00001389 }
1390 } else if (type->is_enum()) {
1391 string ename = capitalize(type->get_name());
1392 out << "writeI32("<<ename<<".to_i " << name << ")";
1393 }
David Reiss0c90f6f2008-02-06 22:18:40 +00001394
iproctor9a41a0c2007-07-16 21:59:24 +00001395 } else {
1396 printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s' TYPE '%s'\n",
1397 tfield->get_name().c_str(),
1398 type->get_name().c_str());
1399 }
1400 out << ";" << endl;
1401}
1402
1403/**
1404 * Serializes all the members of a struct.
1405 *
1406 * @param tstruct The struct to serialize
1407 * @param prefix String prefix to attach to all fields
1408 */
1409void t_ocaml_generator::generate_serialize_struct(ofstream &out,
1410 t_struct* tstruct,
1411 string prefix) {
1412 indent(out) << prefix << "#write(oprot)";
1413}
1414
1415void t_ocaml_generator::generate_serialize_container(ofstream &out,
1416 t_type* ttype,
1417 string prefix) {
1418 if (ttype->is_map()) {
1419 indent(out) << "oprot#writeMapBegin("<< type_to_enum(((t_map*)ttype)->get_key_type()) << ",";
1420 out << type_to_enum(((t_map*)ttype)->get_val_type()) << ",";
1421 out << "Hashtbl.length " << prefix << ");" << endl;
1422 } else if (ttype->is_set()) {
1423 indent(out) <<
1424 "oprot#writeSetBegin(" << type_to_enum(((t_set*)ttype)->get_elem_type()) << ",";
1425 out << "Hashtbl.length " << prefix << ");" << endl;
1426 } else if (ttype->is_list()) {
1427 indent(out) <<
1428 "oprot#writeListBegin(" << type_to_enum(((t_list*)ttype)->get_elem_type()) << ",";
1429 out << "List.length " << prefix << ");" << endl;
1430 }
1431
1432 if (ttype->is_map()) {
1433 string kiter = tmp("_kiter");
1434 string viter = tmp("_viter");
1435 indent(out) << "Hashtbl.iter (fun "<<kiter<<" -> fun " << viter << " -> " << endl;
1436 indent_up();
1437 generate_serialize_map_element(out, (t_map*)ttype, kiter, viter);
1438 indent_down();
1439 indent(out) << ") " << prefix << ";" << endl;
1440 } else if (ttype->is_set()) {
1441 string iter = tmp("_iter");
1442 indent(out) << "Hashtbl.iter (fun "<<iter<<" -> fun _ -> ";
1443 indent_up();
1444 generate_serialize_set_element(out, (t_set*)ttype, iter);
1445 indent_down();
1446 indent(out) << ") " << prefix << ";" << endl;
1447 } else if (ttype->is_list()) {
1448 string iter = tmp("_iter");
1449 indent(out) << "List.iter (fun "<<iter<<" -> ";
1450 indent_up();
1451 generate_serialize_list_element(out, (t_list*)ttype, iter);
1452 indent_down();
1453 indent(out) << ") " << prefix << ";" << endl;
1454 }
David Reiss0c90f6f2008-02-06 22:18:40 +00001455
iproctor9a41a0c2007-07-16 21:59:24 +00001456 if (ttype->is_map()) {
1457 indent(out) <<
1458 "oprot#writeMapEnd";
1459 } else if (ttype->is_set()) {
1460 indent(out) <<
1461 "oprot#writeSetEnd";
1462 } else if (ttype->is_list()) {
1463 indent(out) <<
1464 "oprot#writeListEnd";
1465 }
1466}
1467
1468/**
1469 * Serializes the members of a map.
1470 *
1471 */
1472void t_ocaml_generator::generate_serialize_map_element(ofstream &out,
1473 t_map* tmap,
1474 string kiter,
1475 string viter) {
1476 t_field kfield(tmap->get_key_type(), kiter);
1477 generate_serialize_field(out, &kfield);
1478
1479 t_field vfield(tmap->get_val_type(), viter);
1480 generate_serialize_field(out, &vfield);
1481}
1482
1483/**
1484 * Serializes the members of a set.
1485 */
1486void t_ocaml_generator::generate_serialize_set_element(ofstream &out,
1487 t_set* tset,
1488 string iter) {
1489 t_field efield(tset->get_elem_type(), iter);
1490 generate_serialize_field(out, &efield);
1491}
1492
1493/**
1494 * Serializes the members of a list.
1495 */
1496void t_ocaml_generator::generate_serialize_list_element(ofstream &out,
1497 t_list* tlist,
1498 string iter) {
1499 t_field efield(tlist->get_elem_type(), iter);
1500 generate_serialize_field(out, &efield);
1501}
1502
1503
1504
1505/**
1506 * Renders a function signature of the form 'name args'
1507 *
1508 * @param tfunction Function definition
1509 * @return String of rendered function definition
1510 */
1511string t_ocaml_generator::function_signature(t_function* tfunction,
1512 string prefix) {
1513 return
1514 prefix + decapitalize(tfunction->get_name()) +
1515 " " + argument_list(tfunction->get_arglist());
1516}
1517
1518string t_ocaml_generator::function_type(t_function* tfunc, bool method, bool options){
1519 string result="";
David Reiss0c90f6f2008-02-06 22:18:40 +00001520
iproctor9a41a0c2007-07-16 21:59:24 +00001521 const vector<t_field*>& fields = tfunc->get_arglist()->get_members();
1522 vector<t_field*>::const_iterator f_iter;
1523 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
1524 result += render_ocaml_type((*f_iter)->get_type());
1525 if(options)
1526 result += " option";
1527 result += " -> ";
1528 }
1529 if(fields.empty() && !method){
1530 result += "unit -> ";
1531 }
1532 result += render_ocaml_type(tfunc->get_returntype());
1533 return result;
1534}
1535
1536/**
1537 * Renders a field list
1538 */
1539string t_ocaml_generator::argument_list(t_struct* tstruct) {
1540 string result = "";
1541
1542 const vector<t_field*>& fields = tstruct->get_members();
1543 vector<t_field*>::const_iterator f_iter;
1544 bool first = true;
1545 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
1546 if (first) {
1547 first = false;
1548 } else {
1549 result += " ";
1550 }
1551 result += (*f_iter)->get_name();
1552 }
1553 return result;
1554}
1555
1556string t_ocaml_generator::type_name(t_type* ttype) {
1557 string prefix = "";
1558 t_program* program = ttype->get_program();
1559 if (program != NULL && program != program_) {
1560 if (!ttype->is_service()) {
1561 prefix = capitalize(program->get_name()) + "_types.";
1562 }
1563 }
1564
1565 string name = ttype->get_name();
1566 if(ttype->is_service()){
1567 name = capitalize(name);
1568 } else {
1569 name = decapitalize(name);
1570 }
1571 return prefix + name;
1572}
1573
1574/**
1575 * Converts the parse type to a Protocol.t_type enum
1576 */
1577string t_ocaml_generator::type_to_enum(t_type* type) {
David Reisse087a302007-08-23 21:43:25 +00001578 type = get_true_type(type);
David Reiss0c90f6f2008-02-06 22:18:40 +00001579
iproctor9a41a0c2007-07-16 21:59:24 +00001580 if (type->is_base_type()) {
1581 t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
1582 switch (tbase) {
1583 case t_base_type::TYPE_VOID:
1584 return "Protocol.T_VOID";
1585 case t_base_type::TYPE_STRING:
1586 return "Protocol.T_STRING";
1587 case t_base_type::TYPE_BOOL:
1588 return "Protocol.T_BOOL";
1589 case t_base_type::TYPE_BYTE:
1590 return "Protocol.T_BYTE";
1591 case t_base_type::TYPE_I16:
1592 return "Protocol.T_I16";
1593 case t_base_type::TYPE_I32:
1594 return "Protocol.T_I32";
1595 case t_base_type::TYPE_I64:
1596 return "Protocol.T_I64";
1597 case t_base_type::TYPE_DOUBLE:
1598 return "Protocol.T_DOUBLE";
1599 }
1600 } else if (type->is_enum()) {
1601 return "Protocol.T_I32";
1602 } else if (type->is_struct() || type->is_xception()) {
1603 return "Protocol.T_STRUCT";
1604 } else if (type->is_map()) {
1605 return "Protocol.T_MAP";
1606 } else if (type->is_set()) {
1607 return "Protocol.T_SET";
1608 } else if (type->is_list()) {
1609 return "Protocol.T_LIST";
1610 }
1611
1612 throw "INVALID TYPE IN type_to_enum: " + type->get_name();
1613}
1614
1615/**
1616 * Converts the parse type to an ocaml type
1617 */
1618string t_ocaml_generator::render_ocaml_type(t_type* type) {
David Reisse087a302007-08-23 21:43:25 +00001619 type = get_true_type(type);
David Reiss0c90f6f2008-02-06 22:18:40 +00001620
iproctor9a41a0c2007-07-16 21:59:24 +00001621 if (type->is_base_type()) {
1622 t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
1623 switch (tbase) {
1624 case t_base_type::TYPE_VOID:
1625 return "unit";
1626 case t_base_type::TYPE_STRING:
1627 return "string";
1628 case t_base_type::TYPE_BOOL:
1629 return "bool";
1630 case t_base_type::TYPE_BYTE:
1631 return "int";
1632 case t_base_type::TYPE_I16:
1633 return "int";
1634 case t_base_type::TYPE_I32:
1635 return "int";
1636 case t_base_type::TYPE_I64:
1637 return "Int64.t";
1638 case t_base_type::TYPE_DOUBLE:
1639 return "float";
1640 }
1641 } else if (type->is_enum()) {
1642 return capitalize(((t_enum*)type)->get_name())+".t";
1643 } else if (type->is_struct() || type->is_xception()) {
1644 return type_name((t_struct*)type);
1645 } else if (type->is_map()) {
1646 t_type* ktype = ((t_map*)type)->get_key_type();
1647 t_type* vtype = ((t_map*)type)->get_val_type();
1648 return "("+render_ocaml_type(ktype)+","+render_ocaml_type(vtype)+") Hashtbl.t";
1649 } else if (type->is_set()) {
1650 t_type* etype = ((t_set*)type)->get_elem_type();
1651 return "("+render_ocaml_type(etype)+",bool) Hashtbl.t";
1652 } else if (type->is_list()) {
1653 t_type* etype = ((t_list*)type)->get_elem_type();
1654 return render_ocaml_type(etype)+" list";
1655 }
1656
1657 throw "INVALID TYPE IN type_to_enum: " + type->get_name();
1658}
David Reissf0521b12008-03-27 21:40:05 +00001659
1660
1661THRIFT_REGISTER_GENERATOR(ocaml, "OCaml", "");