]>
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 | ||
30 | #include "thrift/platform.h" | |
31 | #include "thrift/version.h" | |
32 | ||
33 | #include "thrift/generate/t_oop_generator.h" | |
34 | ||
35 | using std::map; | |
36 | using std::ostream; | |
37 | using std::ostringstream; | |
38 | using std::string; | |
39 | using std::stringstream; | |
40 | using std::vector; | |
41 | ||
42 | static const string endl = "\n"; // avoid ostream << std::endl flushes | |
43 | ||
44 | /** | |
45 | * Haskell code generator. | |
46 | * | |
47 | */ | |
48 | class t_hs_generator : public t_oop_generator { | |
49 | public: | |
50 | t_hs_generator(t_program* program, | |
51 | const map<string, string>& parsed_options, | |
52 | const string& option_string) | |
53 | : t_oop_generator(program) { | |
54 | (void)option_string; | |
55 | std::map<std::string, std::string>::const_iterator iter; | |
56 | ||
57 | /* no options yet */ | |
58 | for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) { | |
59 | throw "unknown option hs:" + iter->first; | |
60 | } | |
61 | ||
62 | out_dir_base_ = "gen-hs"; | |
63 | } | |
64 | ||
65 | /** | |
66 | * Init and close methods | |
67 | */ | |
68 | ||
69 | void init_generator() override; | |
70 | void close_generator() override; | |
71 | ||
72 | /** | |
73 | * Program-level generation functions | |
74 | */ | |
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 | string render_const_value(t_type* type, t_const_value* value); | |
83 | ||
84 | /** | |
85 | * Struct generation code | |
86 | */ | |
87 | ||
88 | void generate_hs_struct(t_struct* tstruct, bool is_exception); | |
89 | ||
90 | void generate_hs_struct_definition(ostream& out, | |
91 | t_struct* tstruct, | |
92 | bool is_xception = false, | |
93 | bool helper = false); | |
94 | ||
95 | void generate_hs_struct_reader(ostream& out, t_struct* tstruct); | |
96 | ||
97 | void generate_hs_struct_writer(ostream& out, t_struct* tstruct); | |
98 | ||
99 | void generate_hs_struct_arbitrary(ostream& out, t_struct* tstruct); | |
100 | ||
101 | void generate_hs_function_helpers(t_function* tfunction); | |
102 | ||
103 | void generate_hs_typemap(ostream& out, t_struct* tstruct); | |
104 | ||
105 | void generate_hs_default(ostream& out, t_struct* tstruct); | |
106 | ||
107 | /** | |
108 | * Service-level generation functions | |
109 | */ | |
110 | ||
111 | void generate_service_helpers(t_service* tservice); | |
112 | void generate_service_interface(t_service* tservice); | |
113 | void generate_service_client(t_service* tservice); | |
114 | void generate_service_server(t_service* tservice); | |
115 | void generate_process_function(t_service* tservice, t_function* tfunction); | |
116 | ||
117 | /** | |
118 | * Serialization constructs | |
119 | */ | |
120 | ||
121 | void generate_deserialize_field(ostream& out, t_field* tfield, string prefix); | |
122 | ||
123 | void generate_deserialize_struct(ostream& out, t_struct* tstruct, string name = ""); | |
124 | ||
125 | void generate_deserialize_container(ostream& out, t_type* ttype, string arg = ""); | |
126 | ||
127 | void generate_deserialize_set_element(ostream& out, t_set* tset); | |
128 | ||
129 | void generate_deserialize_list_element(ostream& out, t_list* tlist, string prefix = ""); | |
130 | ||
131 | void generate_deserialize_type(ostream& out, t_type* type, string arg = ""); | |
132 | ||
133 | void generate_serialize_type(ostream& out, t_type* type, string name = ""); | |
134 | ||
135 | void generate_serialize_struct(ostream& out, t_struct* tstruct, string prefix = ""); | |
136 | ||
137 | void generate_serialize_container(ostream& out, t_type* ttype, string prefix = ""); | |
138 | ||
139 | void generate_serialize_map_element(ostream& out, t_map* tmap, string kiter, string viter); | |
140 | ||
141 | void generate_serialize_set_element(ostream& out, t_set* tmap, string iter); | |
142 | ||
143 | void generate_serialize_list_element(ostream& out, t_list* tlist, string iter); | |
144 | ||
145 | /** | |
146 | * Helper rendering functions | |
147 | */ | |
148 | ||
149 | string hs_autogen_comment(); | |
150 | string hs_language_pragma(); | |
151 | string hs_imports(); | |
152 | ||
153 | string type_name(t_type* ttype, string function_prefix = ""); | |
154 | ||
155 | string field_name(string tname, string fname); | |
156 | ||
157 | string function_type(t_function* tfunc, | |
158 | bool options = false, | |
159 | bool io = false, | |
160 | bool method = false); | |
161 | ||
162 | string type_to_enum(t_type* ttype); | |
163 | ||
164 | string type_to_default(t_type* ttype); | |
165 | ||
166 | string render_hs_type(t_type* type, bool needs_parens); | |
167 | ||
168 | string type_to_constructor(t_type* ttype); | |
169 | ||
170 | string render_hs_type_for_function_name(t_type* type); | |
171 | ||
172 | private: | |
173 | ofstream_with_content_based_conditional_update f_types_; | |
174 | ofstream_with_content_based_conditional_update f_consts_; | |
175 | ofstream_with_content_based_conditional_update f_service_; | |
176 | ofstream_with_content_based_conditional_update f_iface_; | |
177 | ofstream_with_content_based_conditional_update f_client_; | |
178 | }; | |
179 | ||
180 | /** | |
181 | * Prepares for file generation by opening up the necessary file output | |
182 | * streams. | |
183 | * | |
184 | * @param tprogram The program to generate | |
185 | */ | |
186 | void t_hs_generator::init_generator() { | |
187 | // Make output directory | |
188 | MKDIR(get_out_dir().c_str()); | |
189 | ||
190 | // Make output file | |
191 | string pname = capitalize(program_name_); | |
192 | string f_types_name = get_out_dir() + pname + "_Types.hs"; | |
193 | f_types_.open(f_types_name.c_str()); | |
194 | ||
195 | string f_consts_name = get_out_dir() + pname + "_Consts.hs"; | |
196 | f_consts_.open(f_consts_name.c_str()); | |
197 | ||
198 | // Print header | |
199 | f_types_ << hs_language_pragma() << endl; | |
200 | f_types_ << hs_autogen_comment() << endl; | |
201 | f_types_ << "module " << pname << "_Types where" << endl; | |
202 | f_types_ << hs_imports() << endl; | |
203 | ||
204 | f_consts_ << hs_language_pragma() << endl; | |
205 | f_consts_ << hs_autogen_comment() << endl; | |
206 | f_consts_ << "module " << pname << "_Consts where" << endl; | |
207 | f_consts_ << hs_imports() << endl; | |
208 | f_consts_ << "import " << pname << "_Types" << endl; | |
209 | } | |
210 | ||
211 | string t_hs_generator::hs_language_pragma() { | |
212 | return string( | |
213 | "{-# LANGUAGE DeriveDataTypeable #-}\n" | |
214 | "{-# LANGUAGE DeriveGeneric #-}\n" | |
215 | "{-# LANGUAGE OverloadedStrings #-}\n" | |
216 | "{-# OPTIONS_GHC -fno-warn-missing-fields #-}\n" | |
217 | "{-# OPTIONS_GHC -fno-warn-missing-signatures #-}\n" | |
218 | "{-# OPTIONS_GHC -fno-warn-name-shadowing #-}\n" | |
219 | "{-# OPTIONS_GHC -fno-warn-unused-imports #-}\n" | |
220 | "{-# OPTIONS_GHC -fno-warn-unused-matches #-}\n"); | |
221 | } | |
222 | ||
223 | /** | |
224 | * Autogen'd comment | |
225 | */ | |
226 | string t_hs_generator::hs_autogen_comment() { | |
227 | return string("-----------------------------------------------------------------\n") | |
228 | + "-- Autogenerated by Thrift Compiler (" + THRIFT_VERSION + ") --\n" | |
229 | + "-- --\n" | |
230 | + "-- DO NOT EDIT UNLESS YOU ARE SURE YOU KNOW WHAT YOU ARE DOING --\n" | |
231 | + "-----------------------------------------------------------------\n"; | |
232 | } | |
233 | ||
234 | /** | |
235 | * Prints standard thrift imports | |
236 | */ | |
237 | string t_hs_generator::hs_imports() { | |
238 | const vector<t_program*>& includes = program_->get_includes(); | |
239 | string result = string( | |
240 | "import Prelude (($), (.), (>>=), (==), (++))\n" | |
241 | "import qualified Prelude as P\n" | |
242 | "import qualified Control.Exception as X\n" | |
243 | "import qualified Control.Monad as M ( liftM, ap, when )\n" | |
244 | "import Data.Functor ( (<$>) )\n" | |
245 | "import qualified Data.ByteString.Lazy as LBS\n" | |
246 | "import qualified Data.Hashable as H\n" | |
247 | "import qualified Data.Int as I\n" | |
248 | "import qualified Data.Maybe as M (catMaybes)\n" | |
249 | "import qualified Data.Text.Lazy.Encoding as E ( decodeUtf8, encodeUtf8 )\n" | |
250 | "import qualified Data.Text.Lazy as LT\n" | |
251 | "import qualified GHC.Generics as G (Generic)\n" | |
252 | "import qualified Data.Typeable as TY ( Typeable )\n" | |
253 | "import qualified Data.HashMap.Strict as Map\n" | |
254 | "import qualified Data.HashSet as Set\n" | |
255 | "import qualified Data.Vector as Vector\n" | |
256 | "import qualified Test.QuickCheck.Arbitrary as QC ( Arbitrary(..) )\n" | |
257 | "import qualified Test.QuickCheck as QC ( elements )\n" | |
258 | "\n" | |
259 | "import qualified Thrift as T\n" | |
260 | "import qualified Thrift.Types as T\n" | |
261 | "import qualified Thrift.Arbitraries as T\n" | |
262 | "\n"); | |
263 | ||
264 | for (auto include : includes) | |
265 | result += "import qualified " + capitalize(include->get_name()) + "_Types\n"; | |
266 | ||
267 | if (includes.size() > 0) | |
268 | result += "\n"; | |
269 | ||
270 | return result; | |
271 | } | |
272 | ||
273 | /** | |
274 | * Closes the type files | |
275 | */ | |
276 | void t_hs_generator::close_generator() { | |
277 | // Close types file | |
278 | f_types_.close(); | |
279 | f_consts_.close(); | |
280 | } | |
281 | ||
282 | /** | |
283 | * Generates a typedef. Ez. | |
284 | * | |
285 | * @param ttypedef The type definition | |
286 | */ | |
287 | void t_hs_generator::generate_typedef(t_typedef* ttypedef) { | |
288 | string tname = capitalize(ttypedef->get_symbolic()); | |
289 | string tdef = render_hs_type(ttypedef->get_type(), false); | |
290 | indent(f_types_) << "type " << tname << " = " << tdef << endl; | |
291 | f_types_ << endl; | |
292 | } | |
293 | ||
294 | /** | |
295 | * Generates code for an enumerated type. | |
296 | * the values. | |
297 | * | |
298 | * @param tenum The enumeration | |
299 | */ | |
300 | void t_hs_generator::generate_enum(t_enum* tenum) { | |
301 | indent(f_types_) << "data " << capitalize(tenum->get_name()) << " = "; | |
302 | indent_up(); | |
303 | vector<t_enum_value*> constants = tenum->get_constants(); | |
304 | vector<t_enum_value*>::iterator c_iter; | |
305 | ||
306 | bool first = true; | |
307 | for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { | |
308 | string name = capitalize((*c_iter)->get_name()); | |
309 | f_types_ << (first ? "" : "|"); | |
310 | f_types_ << name; | |
311 | first = false; | |
312 | } | |
313 | indent(f_types_) << "deriving (P.Show, P.Eq, G.Generic, TY.Typeable, P.Ord, P.Bounded)" << endl; | |
314 | indent_down(); | |
315 | ||
316 | string ename = capitalize(tenum->get_name()); | |
317 | ||
318 | indent(f_types_) << "instance P.Enum " << ename << " where" << endl; | |
319 | indent_up(); | |
320 | indent(f_types_) << "fromEnum t = case t of" << 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_) << name << " -> " << value << endl; | |
326 | } | |
327 | indent_down(); | |
328 | indent(f_types_) << "toEnum t = case t of" << endl; | |
329 | indent_up(); | |
330 | for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { | |
331 | int value = (*c_iter)->get_value(); | |
332 | string name = capitalize((*c_iter)->get_name()); | |
333 | indent(f_types_) << value << " -> " << name << endl; | |
334 | } | |
335 | indent(f_types_) << "_ -> X.throw T.ThriftException" << endl; | |
336 | indent_down(); | |
337 | indent_down(); | |
338 | ||
339 | indent(f_types_) << "instance H.Hashable " << ename << " where" << endl; | |
340 | indent_up(); | |
341 | indent(f_types_) << "hashWithSalt salt = H.hashWithSalt salt P.. P.fromEnum" << endl; | |
342 | indent_down(); | |
343 | ||
344 | indent(f_types_) << "instance QC.Arbitrary " << ename << " where" << endl; | |
345 | indent_up(); | |
346 | indent(f_types_) << "arbitrary = QC.elements (P.enumFromTo P.minBound P.maxBound)" << endl; | |
347 | indent_down(); | |
348 | } | |
349 | ||
350 | /** | |
351 | * Generate a constant value | |
352 | */ | |
353 | void t_hs_generator::generate_const(t_const* tconst) { | |
354 | t_type* type = tconst->get_type(); | |
355 | string name = decapitalize(tconst->get_name()); | |
356 | ||
357 | t_const_value* value = tconst->get_value(); | |
358 | ||
359 | indent(f_consts_) << name << " :: " << render_hs_type(type, false) << endl; | |
360 | indent(f_consts_) << name << " = " << render_const_value(type, value) << endl; | |
361 | f_consts_ << endl; | |
362 | } | |
363 | ||
364 | /** | |
365 | * Prints the value of a constant with the given type. Note that type checking | |
366 | * is NOT performed in this function as it is always run beforehand using the | |
367 | * validate_types method in main.cc | |
368 | */ | |
369 | string t_hs_generator::render_const_value(t_type* type, t_const_value* value) { | |
370 | if (value == NULL) | |
371 | return type_to_default(type); | |
372 | ||
373 | type = get_true_type(type); | |
374 | ostringstream out; | |
375 | ||
376 | if (type->is_base_type()) { | |
377 | t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); | |
378 | switch (tbase) { | |
379 | ||
380 | case t_base_type::TYPE_STRING: | |
381 | out << '"' << get_escaped_string(value) << '"'; | |
382 | break; | |
383 | ||
384 | case t_base_type::TYPE_BOOL: | |
385 | out << (value->get_integer() > 0 ? "P.True" : "P.False"); | |
386 | break; | |
387 | ||
388 | case t_base_type::TYPE_I8: | |
389 | case t_base_type::TYPE_I16: | |
390 | case t_base_type::TYPE_I32: | |
391 | case t_base_type::TYPE_I64: | |
392 | out << "(" << value->get_integer() << ")"; | |
393 | break; | |
394 | ||
395 | case t_base_type::TYPE_DOUBLE: | |
396 | if (value->get_type() == t_const_value::CV_INTEGER) { | |
397 | out << "(" << value->get_integer() << ")"; | |
398 | } else { | |
399 | out << "(" << value->get_double() << ")"; | |
400 | } | |
401 | break; | |
402 | ||
403 | default: | |
404 | throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase); | |
405 | } | |
406 | ||
407 | } else if (type->is_enum()) { | |
408 | t_enum* tenum = (t_enum*)type; | |
409 | vector<t_enum_value*> constants = tenum->get_constants(); | |
410 | for (auto & constant : constants) { | |
411 | int val = constant->get_value(); | |
412 | if (val == value->get_integer()) { | |
413 | t_program* prog = type->get_program(); | |
414 | if (prog != NULL && prog != program_) | |
415 | out << capitalize(prog->get_name()) << "_Types."; | |
416 | out << capitalize(constant->get_name()); | |
417 | break; | |
418 | } | |
419 | } | |
420 | ||
421 | } else if (type->is_struct() || type->is_xception()) { | |
422 | string cname = type_name(type); | |
423 | out << "default_" << cname << "{"; | |
424 | ||
425 | const vector<t_field*>& fields = ((t_struct*)type)->get_members(); | |
426 | const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map(); | |
427 | ||
428 | bool first = true; | |
429 | for (auto v_iter : val) { | |
430 | t_field* field = NULL; | |
431 | ||
432 | for (auto f_iter : fields) | |
433 | if (f_iter->get_name() == v_iter.first->get_string()) | |
434 | field = f_iter; | |
435 | ||
436 | if (field == NULL) | |
437 | throw "type error: " + cname + " has no field " + v_iter.first->get_string(); | |
438 | ||
439 | string fname = v_iter.first->get_string(); | |
440 | string const_value = render_const_value(field->get_type(), v_iter.second); | |
441 | ||
442 | out << (first ? "" : ", "); | |
443 | out << field_name(cname, fname) << " = "; | |
444 | if (field->get_req() == t_field::T_OPTIONAL || ((t_type*)field->get_type())->is_xception()) { | |
445 | out << "P.Just "; | |
446 | } | |
447 | out << const_value; | |
448 | first = false; | |
449 | } | |
450 | ||
451 | out << "}"; | |
452 | ||
453 | } else if (type->is_map()) { | |
454 | t_type* ktype = ((t_map*)type)->get_key_type(); | |
455 | t_type* vtype = ((t_map*)type)->get_val_type(); | |
456 | ||
457 | const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map(); | |
458 | map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter; | |
459 | ||
460 | out << "(Map.fromList ["; | |
461 | ||
462 | bool first = true; | |
463 | for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { | |
464 | string key = render_const_value(ktype, v_iter->first); | |
465 | string val = render_const_value(vtype, v_iter->second); | |
466 | out << (first ? "" : ","); | |
467 | out << "(" << key << "," << val << ")"; | |
468 | first = false; | |
469 | } | |
470 | out << "])"; | |
471 | ||
472 | } else if (type->is_list() || type->is_set()) { | |
473 | t_type* etype = type->is_list() ? ((t_list*)type)->get_elem_type() | |
474 | : ((t_set*)type)->get_elem_type(); | |
475 | ||
476 | const vector<t_const_value*>& val = value->get_list(); | |
477 | vector<t_const_value*>::const_iterator v_iter; | |
478 | ||
479 | if (type->is_set()) | |
480 | out << "(Set.fromList ["; | |
481 | else | |
482 | out << "(Vector.fromList ["; | |
483 | ||
484 | bool first = true; | |
485 | for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { | |
486 | out << (first ? "" : ","); | |
487 | out << render_const_value(etype, *v_iter); | |
488 | first = false; | |
489 | } | |
490 | ||
491 | out << "])"; | |
492 | ||
493 | } else { | |
494 | throw "CANNOT GENERATE CONSTANT FOR TYPE: " + type->get_name(); | |
495 | } | |
496 | ||
497 | return out.str(); | |
498 | } | |
499 | ||
500 | /** | |
501 | * Generates a "struct" | |
502 | */ | |
503 | void t_hs_generator::generate_struct(t_struct* tstruct) { | |
504 | generate_hs_struct(tstruct, false); | |
505 | } | |
506 | ||
507 | /** | |
508 | * Generates a struct definition for a thrift exception. Basically the same | |
509 | * as a struct, but also has an exception declaration. | |
510 | * | |
511 | * @param txception The struct definition | |
512 | */ | |
513 | void t_hs_generator::generate_xception(t_struct* txception) { | |
514 | generate_hs_struct(txception, true); | |
515 | } | |
516 | ||
517 | /** | |
518 | * Generates a Haskell struct | |
519 | */ | |
520 | void t_hs_generator::generate_hs_struct(t_struct* tstruct, bool is_exception) { | |
521 | generate_hs_struct_definition(f_types_, tstruct, is_exception, false); | |
522 | } | |
523 | ||
524 | /** | |
525 | * Generates a struct definition for a thrift data type. | |
526 | * | |
527 | * @param tstruct The struct definition | |
528 | */ | |
529 | void t_hs_generator::generate_hs_struct_definition(ostream& out, | |
530 | t_struct* tstruct, | |
531 | bool is_exception, | |
532 | bool helper) { | |
533 | (void)helper; | |
534 | string tname = type_name(tstruct); | |
535 | string name = tstruct->get_name(); | |
536 | const vector<t_field*>& members = tstruct->get_members(); | |
537 | ||
538 | indent(out) << "data " << tname << " = " << tname; | |
539 | if (members.size() > 0) { | |
540 | indent_up(); | |
541 | bool first = true; | |
542 | for (auto member : members) { | |
543 | if (first) { | |
544 | indent(out) << "{ "; | |
545 | first = false; | |
546 | } else { | |
547 | indent(out) << ", "; | |
548 | } | |
549 | string mname = member->get_name(); | |
550 | out << field_name(tname, mname) << " :: "; | |
551 | if (member->get_req() == t_field::T_OPTIONAL | |
552 | || ((t_type*)member->get_type())->is_xception()) { | |
553 | out << "P.Maybe "; | |
554 | } | |
555 | out << render_hs_type(member->get_type(), true) << endl; | |
556 | } | |
557 | indent(out) << "}"; | |
558 | indent_down(); | |
559 | } | |
560 | ||
561 | out << " deriving (P.Show,P.Eq,G.Generic,TY.Typeable)" << endl; | |
562 | ||
563 | if (is_exception) | |
564 | out << "instance X.Exception " << tname << endl; | |
565 | ||
566 | indent(out) << "instance H.Hashable " << tname << " where" << endl; | |
567 | indent_up(); | |
568 | indent(out) << "hashWithSalt salt record = salt"; | |
569 | for (auto member : members) { | |
570 | string mname = member->get_name(); | |
571 | indent(out) << " `H.hashWithSalt` " << field_name(tname, mname) << " record"; | |
572 | } | |
573 | indent(out) << endl; | |
574 | indent_down(); | |
575 | ||
576 | generate_hs_struct_arbitrary(out, tstruct); | |
577 | generate_hs_struct_writer(out, tstruct); | |
578 | generate_hs_struct_reader(out, tstruct); | |
579 | generate_hs_typemap(out, tstruct); | |
580 | generate_hs_default(out, tstruct); | |
581 | } | |
582 | ||
583 | void t_hs_generator::generate_hs_struct_arbitrary(ostream& out, t_struct* tstruct) { | |
584 | string tname = type_name(tstruct); | |
585 | string name = tstruct->get_name(); | |
586 | const vector<t_field*>& members = tstruct->get_members(); | |
587 | ||
588 | indent(out) << "instance QC.Arbitrary " << tname << " where " << endl; | |
589 | indent_up(); | |
590 | if (members.size() > 0) { | |
591 | indent(out) << "arbitrary = M.liftM " << tname; | |
592 | indent_up(); | |
593 | indent_up(); | |
594 | indent_up(); | |
595 | indent_up(); | |
596 | bool first = true; | |
597 | for (auto member : members) { | |
598 | if (first) { | |
599 | first = false; | |
600 | out << " "; | |
601 | } else { | |
602 | indent(out) << "`M.ap`"; | |
603 | } | |
604 | out << "("; | |
605 | if (member->get_req() == t_field::T_OPTIONAL | |
606 | || ((t_type*)member->get_type())->is_xception()) { | |
607 | out << "M.liftM P.Just "; | |
608 | } | |
609 | out << "QC.arbitrary)" << endl; | |
610 | } | |
611 | indent_down(); | |
612 | indent_down(); | |
613 | indent_down(); | |
614 | indent_down(); | |
615 | ||
616 | // Shrink | |
617 | indent(out) << "shrink obj | obj == default_" << tname << " = []" << endl; | |
618 | indent(out) << " | P.otherwise = M.catMaybes" << endl; | |
619 | indent_up(); | |
620 | first = true; | |
621 | for (auto member : members) { | |
622 | if (first) { | |
623 | first = false; | |
624 | indent(out) << "[ "; | |
625 | } else { | |
626 | indent(out) << ", "; | |
627 | } | |
628 | string fname = field_name(tname, member->get_name()); | |
629 | out << "if obj == default_" << tname; | |
630 | out << "{" << fname << " = " << fname << " obj} "; | |
631 | out << "then P.Nothing "; | |
632 | out << "else P.Just $ default_" << tname; | |
633 | out << "{" << fname << " = " << fname << " obj}" << endl; | |
634 | } | |
635 | indent(out) << "]" << endl; | |
636 | indent_down(); | |
637 | } else { /* 0 == members.size() */ | |
638 | indent(out) << "arbitrary = QC.elements [" << tname << "]" << endl; | |
639 | } | |
640 | indent_down(); | |
641 | } | |
642 | ||
643 | /** | |
644 | * Generates the read method for a struct | |
645 | */ | |
646 | void t_hs_generator::generate_hs_struct_reader(ostream& out, t_struct* tstruct) { | |
647 | const vector<t_field*>& fields = tstruct->get_members(); | |
648 | ||
649 | string sname = type_name(tstruct); | |
650 | string id = tmp("_id"); | |
651 | string val = tmp("_val"); | |
652 | ||
653 | indent(out) << "to_" << sname << " :: T.ThriftVal -> " << sname << endl; | |
654 | indent(out) << "to_" << sname << " (T.TStruct fields) = " << sname << "{" << endl; | |
655 | indent_up(); | |
656 | ||
657 | bool first = true; | |
658 | ||
659 | // Generate deserialization code for known cases | |
660 | for (auto field : fields) { | |
661 | int32_t key = field->get_key(); | |
662 | string etype = type_to_enum(field->get_type()); | |
663 | string fname = field->get_name(); | |
664 | ||
665 | if (first) { | |
666 | first = false; | |
667 | } else { | |
668 | out << "," << endl; | |
669 | } | |
670 | ||
671 | // Fill in Field | |
672 | indent(out) << field_name(sname, fname) << " = "; | |
673 | ||
674 | out << "P.maybe ("; | |
675 | if (field->get_req() == t_field::T_REQUIRED) { | |
676 | out << "P.error \"Missing required field: " << fname << "\""; | |
677 | } else { | |
678 | if ((field->get_req() == t_field::T_OPTIONAL | |
679 | || ((t_type*)field->get_type())->is_xception()) && field->get_value() == NULL) { | |
680 | out << "P.Nothing"; | |
681 | } else { | |
682 | out << field_name(sname, fname) << " default_" << sname; | |
683 | } | |
684 | } | |
685 | out << ") "; | |
686 | ||
687 | out << "(\\(_," << val << ") -> "; | |
688 | if (field->get_req() == t_field::T_OPTIONAL | |
689 | || ((t_type*)field->get_type())->is_xception()) | |
690 | out << "P.Just "; | |
691 | generate_deserialize_field(out, field, val); | |
692 | out << ")"; | |
693 | out << " (Map.lookup (" << key << ") fields)"; | |
694 | } | |
695 | ||
696 | out << endl; | |
697 | indent(out) << "}" << endl; | |
698 | indent_down(); | |
699 | ||
700 | // read | |
701 | string tmap = type_name(tstruct, "typemap_"); | |
702 | indent(out) << "to_" << sname << " _ = P.error \"not a struct\"" << endl; | |
703 | ||
704 | indent(out) << "read_" << sname << " :: T.Protocol p => p -> P.IO " << sname | |
705 | << endl; | |
706 | indent(out) << "read_" << sname << " iprot = to_" << sname; | |
707 | out << " <$> T.readVal iprot (T.T_STRUCT " << tmap << ")" << endl; | |
708 | ||
709 | indent(out) << "decode_" << sname | |
710 | << " :: T.StatelessProtocol p => p -> LBS.ByteString -> " << sname << endl; | |
711 | indent(out) << "decode_" << sname << " iprot bs = to_" << sname << " $ "; | |
712 | out << "T.deserializeVal iprot (T.T_STRUCT " << tmap << ") bs" << endl; | |
713 | } | |
714 | ||
715 | void t_hs_generator::generate_hs_struct_writer(ostream& out, t_struct* tstruct) { | |
716 | string name = type_name(tstruct); | |
717 | const vector<t_field*>& fields = tstruct->get_sorted_members(); | |
718 | string str = tmp("_str"); | |
719 | string f = tmp("_f"); | |
720 | string v = tmp("_v"); | |
721 | ||
722 | indent(out) << "from_" << name << " :: " << name << " -> T.ThriftVal" << endl; | |
723 | indent(out) << "from_" << name << " record = T.TStruct $ Map.fromList "; | |
724 | indent_up(); | |
725 | ||
726 | // Get Exceptions | |
727 | bool hasExn = false; | |
728 | for (auto field : fields) { | |
729 | if (((t_type*)field->get_type())->is_xception()) { | |
730 | hasExn = true; | |
731 | break; | |
732 | } | |
733 | } | |
734 | ||
735 | bool isfirst = true; | |
736 | if (hasExn) { | |
737 | out << endl; | |
738 | indent(out) << "(let exns = M.catMaybes "; | |
739 | indent_up(); | |
740 | for (auto field : fields) { | |
741 | if (((t_type*)field->get_type())->is_xception()) { | |
742 | if (isfirst) { | |
743 | out << "[ "; | |
744 | isfirst = false; | |
745 | } else { | |
746 | out << ", "; | |
747 | } | |
748 | string mname = field->get_name(); | |
749 | int32_t key = field->get_key(); | |
750 | out << "(\\" << v << " -> (" << key << ", (\"" << mname << "\","; | |
751 | generate_serialize_type(out, field->get_type(), v); | |
752 | out << "))) <$> " << field_name(name, mname) << " record"; | |
753 | } | |
754 | } | |
755 | if (!isfirst) { | |
756 | out << "]" << endl; | |
757 | } | |
758 | indent_down(); | |
759 | indent(out) << "in if P.not (P.null exns) then exns else "; | |
760 | indent_up(); | |
761 | } else { | |
762 | out << "$ "; | |
763 | } | |
764 | ||
765 | out << "M.catMaybes" << endl; | |
766 | // Get the Rest | |
767 | isfirst = true; | |
768 | for (auto field : fields) { | |
769 | // Write field header | |
770 | if (isfirst) { | |
771 | indent(out) << "[ "; | |
772 | isfirst = false; | |
773 | } else { | |
774 | indent(out) << ", "; | |
775 | } | |
776 | string mname = field->get_name(); | |
777 | int32_t key = field->get_key(); | |
778 | out << "(\\"; | |
779 | out << v << " -> "; | |
780 | if (field->get_req() != t_field::T_OPTIONAL | |
781 | && !((t_type*)field->get_type())->is_xception()) { | |
782 | out << "P.Just "; | |
783 | } | |
784 | out << "(" << key << ", (\"" << mname << "\","; | |
785 | generate_serialize_type(out, field->get_type(), v); | |
786 | out << "))) "; | |
787 | if (field->get_req() != t_field::T_OPTIONAL | |
788 | && !((t_type*)field->get_type())->is_xception()) { | |
789 | out << "$"; | |
790 | } else { | |
791 | out << "<$>"; | |
792 | } | |
793 | out << " " << field_name(name, mname) << " record" << endl; | |
794 | } | |
795 | ||
796 | // Write the struct map | |
797 | if (isfirst) { | |
798 | indent(out) << "[]" << endl; | |
799 | } else { | |
800 | indent(out) << "]" << endl; | |
801 | } | |
802 | if (hasExn) { | |
803 | indent(out) << ")" << endl; | |
804 | indent_down(); | |
805 | } | |
806 | indent_down(); | |
807 | ||
808 | // write | |
809 | indent(out) << "write_" << name << " :: T.Protocol p => p -> " << name | |
810 | << " -> P.IO ()" << endl; | |
811 | indent(out) << "write_" << name << " oprot record = T.writeVal oprot $ from_"; | |
812 | out << name << " record" << endl; | |
813 | ||
814 | // encode | |
815 | indent(out) << "encode_" << name << " :: T.StatelessProtocol p => p -> " << name | |
816 | << " -> LBS.ByteString" << endl; | |
817 | indent(out) << "encode_" << name << " oprot record = T.serializeVal oprot $ "; | |
818 | out << "from_" << name << " record" << endl; | |
819 | } | |
820 | ||
821 | /** | |
822 | * Generates a thrift service. | |
823 | * | |
824 | * @param tservice The service definition | |
825 | */ | |
826 | void t_hs_generator::generate_service(t_service* tservice) { | |
827 | string f_service_name = get_out_dir() + capitalize(service_name_) + ".hs"; | |
828 | f_service_.open(f_service_name.c_str()); | |
829 | ||
830 | f_service_ << hs_language_pragma() << endl; | |
831 | f_service_ << hs_autogen_comment() << endl; | |
832 | f_service_ << "module " << capitalize(service_name_) << " where" << endl; | |
833 | f_service_ << hs_imports() << endl; | |
834 | ||
835 | if (tservice->get_extends()) { | |
836 | f_service_ << "import qualified " << capitalize(tservice->get_extends()->get_name()) << endl; | |
837 | } | |
838 | ||
839 | f_service_ << "import " << capitalize(program_name_) << "_Types" << endl; | |
840 | f_service_ << "import qualified " << capitalize(service_name_) << "_Iface as Iface" << endl; | |
841 | ||
842 | // Generate the three main parts of the service | |
843 | generate_service_helpers(tservice); | |
844 | generate_service_interface(tservice); | |
845 | generate_service_client(tservice); | |
846 | generate_service_server(tservice); | |
847 | ||
848 | // Close service file | |
849 | f_service_.close(); | |
850 | } | |
851 | ||
852 | /** | |
853 | * Generates helper functions for a service. | |
854 | * | |
855 | * @param tservice The service to generate a header definition for | |
856 | */ | |
857 | void t_hs_generator::generate_service_helpers(t_service* tservice) { | |
858 | vector<t_function*> functions = tservice->get_functions(); | |
859 | vector<t_function*>::iterator f_iter; | |
860 | ||
861 | indent(f_service_) << "-- HELPER FUNCTIONS AND STRUCTURES --" << endl; | |
862 | indent(f_service_) << endl; | |
863 | ||
864 | for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { | |
865 | t_struct* ts = (*f_iter)->get_arglist(); | |
866 | generate_hs_struct_definition(f_service_, ts, false); | |
867 | generate_hs_function_helpers(*f_iter); | |
868 | } | |
869 | } | |
870 | ||
871 | /** | |
872 | * Generates a struct and helpers for a function. | |
873 | * | |
874 | * @param tfunction The function | |
875 | */ | |
876 | void t_hs_generator::generate_hs_function_helpers(t_function* tfunction) { | |
877 | t_struct result(program_, field_name(tfunction->get_name(), "result")); | |
878 | t_field success(tfunction->get_returntype(), "success", 0); | |
879 | ||
880 | if (!tfunction->get_returntype()->is_void()) | |
881 | result.append(&success); | |
882 | ||
883 | t_struct* xs = tfunction->get_xceptions(); | |
884 | const vector<t_field*>& fields = xs->get_members(); | |
885 | ||
886 | vector<t_field*>::const_iterator f_iter; | |
887 | for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) | |
888 | result.append(*f_iter); | |
889 | ||
890 | generate_hs_struct_definition(f_service_, &result, false); | |
891 | } | |
892 | ||
893 | /** | |
894 | * Generate the map from field names to (type, id) | |
895 | * @param tstruct the Struct | |
896 | */ | |
897 | void t_hs_generator::generate_hs_typemap(ostream& out, t_struct* tstruct) { | |
898 | string name = type_name(tstruct); | |
899 | const vector<t_field*>& fields = tstruct->get_sorted_members(); | |
900 | ||
901 | indent(out) << "typemap_" << name << " :: T.TypeMap" << endl; | |
902 | indent(out) << "typemap_" << name << " = Map.fromList ["; | |
903 | bool first = true; | |
904 | for (auto field : fields) { | |
905 | string mname = field->get_name(); | |
906 | if (!first) { | |
907 | out << ","; | |
908 | } | |
909 | ||
910 | t_type* type = get_true_type(field->get_type()); | |
911 | int32_t key = field->get_key(); | |
912 | out << "(" << key << ",(\"" << mname << "\"," << type_to_enum(type) << "))"; | |
913 | first = false; | |
914 | } | |
915 | out << "]" << endl; | |
916 | } | |
917 | ||
918 | /** | |
919 | * generate the struct with default values filled in | |
920 | * @param tstruct the Struct | |
921 | */ | |
922 | void t_hs_generator::generate_hs_default(ostream& out, t_struct* tstruct) { | |
923 | string name = type_name(tstruct); | |
924 | string fname = type_name(tstruct, "default_"); | |
925 | const vector<t_field*>& fields = tstruct->get_sorted_members(); | |
926 | ||
927 | indent(out) << fname << " :: " << name << endl; | |
928 | indent(out) << fname << " = " << name << "{" << endl; | |
929 | indent_up(); | |
930 | bool first = true; | |
931 | for (auto field : fields) { | |
932 | string mname = field->get_name(); | |
933 | if (first) { | |
934 | first = false; | |
935 | } else { | |
936 | out << "," << endl; | |
937 | } | |
938 | ||
939 | t_type* type = get_true_type(field->get_type()); | |
940 | t_const_value* value = field->get_value(); | |
941 | indent(out) << field_name(name, mname) << " = "; | |
942 | if (field->get_req() == t_field::T_OPTIONAL | |
943 | || ((t_type*)field->get_type())->is_xception()) { | |
944 | if (value == NULL) { | |
945 | out << "P.Nothing"; | |
946 | } else { | |
947 | out << "P.Just " << render_const_value(type, value); | |
948 | } | |
949 | } else { | |
950 | out << render_const_value(type, value); | |
951 | } | |
952 | } | |
953 | out << "}" << endl; | |
954 | indent_down(); | |
955 | } | |
956 | ||
957 | /** | |
958 | * Generates a service interface definition. | |
959 | * | |
960 | * @param tservice The service to generate a header definition for | |
961 | */ | |
962 | void t_hs_generator::generate_service_interface(t_service* tservice) { | |
963 | string f_iface_name = get_out_dir() + capitalize(service_name_) + "_Iface.hs"; | |
964 | f_iface_.open(f_iface_name.c_str()); | |
965 | ||
966 | f_iface_ << hs_language_pragma() << endl; | |
967 | f_iface_ << hs_autogen_comment() << endl; | |
968 | ||
969 | f_iface_ << "module " << capitalize(service_name_) << "_Iface where" << endl; | |
970 | ||
971 | f_iface_ << hs_imports() << endl; | |
972 | f_iface_ << "import " << capitalize(program_name_) << "_Types" << endl; | |
973 | f_iface_ << endl; | |
974 | ||
975 | string sname = capitalize(service_name_); | |
976 | if (tservice->get_extends() != NULL) { | |
977 | string extends = type_name(tservice->get_extends()); | |
978 | ||
979 | indent(f_iface_) << "import " << extends << "_Iface" << endl; | |
980 | indent(f_iface_) << "class " << extends << "_Iface a => " << sname << "_Iface a where" << endl; | |
981 | ||
982 | } else { | |
983 | indent(f_iface_) << "class " << sname << "_Iface a where" << endl; | |
984 | } | |
985 | ||
986 | indent_up(); | |
987 | ||
988 | vector<t_function*> functions = tservice->get_functions(); | |
989 | vector<t_function*>::iterator f_iter; | |
990 | for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { | |
991 | string ft = function_type(*f_iter, true, true, true); | |
992 | indent(f_iface_) << decapitalize((*f_iter)->get_name()) << " :: a -> " << ft << endl; | |
993 | } | |
994 | ||
995 | indent_down(); | |
996 | f_iface_.close(); | |
997 | } | |
998 | ||
999 | /** | |
1000 | * Generates a service client definition. Note that in Haskell, the client doesn't implement iface. | |
1001 | *This is because | |
1002 | * The client does not (and should not have to) deal with arguments being Nothing. | |
1003 | * | |
1004 | * @param tservice The service to generate a server for. | |
1005 | */ | |
1006 | void t_hs_generator::generate_service_client(t_service* tservice) { | |
1007 | string f_client_name = get_out_dir() + capitalize(service_name_) + "_Client.hs"; | |
1008 | f_client_.open(f_client_name.c_str()); | |
1009 | f_client_ << hs_language_pragma() << endl; | |
1010 | f_client_ << hs_autogen_comment() << endl; | |
1011 | ||
1012 | vector<t_function*> functions = tservice->get_functions(); | |
1013 | vector<t_function*>::const_iterator f_iter; | |
1014 | ||
1015 | string extends = ""; | |
1016 | string exports = ""; | |
1017 | ||
1018 | bool first = true; | |
1019 | for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { | |
1020 | exports += (first ? "" : ","); | |
1021 | string funname = (*f_iter)->get_name(); | |
1022 | exports += decapitalize(funname); | |
1023 | first = false; | |
1024 | } | |
1025 | ||
1026 | string sname = capitalize(service_name_); | |
1027 | indent(f_client_) << "module " << sname << "_Client(" << exports << ") where" << endl; | |
1028 | ||
1029 | if (tservice->get_extends() != NULL) { | |
1030 | extends = type_name(tservice->get_extends()); | |
1031 | indent(f_client_) << "import " << extends << "_Client" << endl; | |
1032 | } | |
1033 | ||
1034 | indent(f_client_) << "import qualified Data.IORef as R" << endl; | |
1035 | indent(f_client_) << hs_imports() << endl; | |
1036 | indent(f_client_) << "import " << capitalize(program_name_) << "_Types" << endl; | |
1037 | indent(f_client_) << "import " << capitalize(service_name_) << endl; | |
1038 | ||
1039 | // DATS RITE A GLOBAL VAR | |
1040 | indent(f_client_) << "seqid = R.newIORef 0" << endl; | |
1041 | ||
1042 | // Generate client method implementations | |
1043 | for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { | |
1044 | t_struct* arg_struct = (*f_iter)->get_arglist(); | |
1045 | const vector<t_field*>& fields = arg_struct->get_members(); | |
1046 | vector<t_field*>::const_iterator fld_iter; | |
1047 | string funname = (*f_iter)->get_name(); | |
1048 | ||
1049 | string fargs = ""; | |
1050 | for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) | |
1051 | fargs += " arg_" + (*fld_iter)->get_name(); | |
1052 | ||
1053 | // Open function | |
1054 | indent(f_client_) << decapitalize(funname) << " (ip,op)" << fargs << " = do" << endl; | |
1055 | indent_up(); | |
1056 | indent(f_client_) << "send_" << funname << " op" << fargs; | |
1057 | ||
1058 | f_client_ << endl; | |
1059 | ||
1060 | if (!(*f_iter)->is_oneway()) | |
1061 | indent(f_client_) << "recv_" << funname << " ip" << endl; | |
1062 | ||
1063 | indent_down(); | |
1064 | ||
1065 | indent(f_client_) << "send_" << funname << " op" << fargs << " = do" << endl; | |
1066 | indent_up(); | |
1067 | ||
1068 | indent(f_client_) << "seq <- seqid" << endl; | |
1069 | indent(f_client_) << "seqn <- R.readIORef seq" << endl; | |
1070 | string argsname = capitalize((*f_iter)->get_name() + "_args"); | |
1071 | ||
1072 | // Serialize the request header | |
1073 | string fname = (*f_iter)->get_name(); | |
1074 | string msgType = (*f_iter)->is_oneway() ? "T.M_ONEWAY" : "T.M_CALL"; | |
1075 | indent(f_client_) << "T.writeMessage op (\"" << fname << "\", " << msgType << ", seqn) $" | |
1076 | << endl; | |
1077 | indent_up(); | |
1078 | indent(f_client_) << "write_" << argsname << " op (" << argsname << "{"; | |
1079 | ||
1080 | bool first = true; | |
1081 | for (auto field : fields) { | |
1082 | string fieldname = field->get_name(); | |
1083 | f_client_ << (first ? "" : ","); | |
1084 | f_client_ << field_name(argsname, fieldname) << "="; | |
1085 | if (field->get_req() == t_field::T_OPTIONAL | |
1086 | || ((t_type*)field->get_type())->is_xception()) | |
1087 | f_client_ << "P.Just "; | |
1088 | f_client_ << "arg_" << fieldname; | |
1089 | first = false; | |
1090 | } | |
1091 | f_client_ << "})" << endl; | |
1092 | indent_down(); | |
1093 | indent_down(); | |
1094 | ||
1095 | if (!(*f_iter)->is_oneway()) { | |
1096 | string resultname = capitalize((*f_iter)->get_name() + "_result"); | |
1097 | t_struct noargs(program_); | |
1098 | ||
1099 | string funname = string("recv_") + (*f_iter)->get_name(); | |
1100 | t_function recv_function((*f_iter)->get_returntype(), funname, &noargs); | |
1101 | ||
1102 | // Open function | |
1103 | indent(f_client_) << funname << " ip = do" << endl; | |
1104 | indent_up(); | |
1105 | ||
1106 | indent(f_client_) << "T.readMessage ip $ \\(fname, mtype, rseqid) -> do" << endl; | |
1107 | indent_up(); | |
1108 | indent(f_client_) << "M.when (mtype == T.M_EXCEPTION) $ do { exn <- T.readAppExn ip ; " | |
1109 | "X.throw exn }" << endl; | |
1110 | ||
1111 | indent(f_client_) << "res <- read_" << resultname << " ip" << endl; | |
1112 | ||
1113 | t_struct* xs = (*f_iter)->get_xceptions(); | |
1114 | const vector<t_field*>& xceptions = xs->get_members(); | |
1115 | ||
1116 | for (auto xception : xceptions) { | |
1117 | indent(f_client_) << "P.maybe (P.return ()) X.throw (" | |
1118 | << field_name(resultname, xception->get_name()) << " res)" << endl; | |
1119 | } | |
1120 | ||
1121 | if (!(*f_iter)->get_returntype()->is_void()) | |
1122 | indent(f_client_) << "P.return $ " << field_name(resultname, "success") << " res" << endl; | |
1123 | else | |
1124 | indent(f_client_) << "P.return ()" << endl; | |
1125 | ||
1126 | // Close function | |
1127 | indent_down(); | |
1128 | indent_down(); | |
1129 | } | |
1130 | } | |
1131 | ||
1132 | f_client_.close(); | |
1133 | } | |
1134 | ||
1135 | /** | |
1136 | * Generates a service server definition. | |
1137 | * | |
1138 | * @param tservice The service to generate a server for. | |
1139 | */ | |
1140 | void t_hs_generator::generate_service_server(t_service* tservice) { | |
1141 | // Generate the dispatch methods | |
1142 | vector<t_function*> functions = tservice->get_functions(); | |
1143 | vector<t_function*>::iterator f_iter; | |
1144 | ||
1145 | // Generate the process subfunctions | |
1146 | for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) | |
1147 | generate_process_function(tservice, *f_iter); | |
1148 | ||
1149 | indent(f_service_) << "proc_ handler (iprot,oprot) (name,typ,seqid) = case name of" << endl; | |
1150 | indent_up(); | |
1151 | ||
1152 | for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { | |
1153 | string fname = (*f_iter)->get_name(); | |
1154 | indent(f_service_) << "\"" << fname << "\" -> process_" << decapitalize(fname) | |
1155 | << " (seqid,iprot,oprot,handler)" << endl; | |
1156 | } | |
1157 | ||
1158 | indent(f_service_) << "_ -> "; | |
1159 | if (tservice->get_extends() != NULL) { | |
1160 | f_service_ << type_name(tservice->get_extends()) | |
1161 | << ".proc_ handler (iprot,oprot) (name,typ,seqid)" << endl; | |
1162 | ||
1163 | } else { | |
1164 | f_service_ << "do" << endl; | |
1165 | indent_up(); | |
1166 | indent(f_service_) << "_ <- T.readVal iprot (T.T_STRUCT Map.empty)" << endl; | |
1167 | indent(f_service_) << "T.writeMessage oprot (name,T.M_EXCEPTION,seqid) $" << endl; | |
1168 | indent_up(); | |
1169 | indent(f_service_) << "T.writeAppExn oprot (T.AppExn T.AE_UNKNOWN_METHOD (\"Unknown function " | |
1170 | "\" ++ LT.unpack name))" << endl; | |
1171 | indent_down(); | |
1172 | indent_down(); | |
1173 | } | |
1174 | ||
1175 | indent_down(); | |
1176 | ||
1177 | // Generate the server implementation | |
1178 | indent(f_service_) << "process handler (iprot, oprot) = do" << endl; | |
1179 | indent_up(); | |
1180 | ||
1181 | indent(f_service_) << "T.readMessage iprot (" << endl; | |
1182 | indent(f_service_) << " proc_ handler (iprot,oprot))" << endl; | |
1183 | indent(f_service_) << "P.return P.True" << endl; | |
1184 | indent_down(); | |
1185 | } | |
1186 | ||
1187 | bool hasNoArguments(t_function* func) { | |
1188 | return (func->get_arglist()->get_members().empty()); | |
1189 | } | |
1190 | ||
1191 | string t_hs_generator::render_hs_type_for_function_name(t_type* type) { | |
1192 | string type_str = render_hs_type(type, false); | |
1193 | std::string::size_type found = -1; | |
1194 | ||
1195 | while (true) { | |
1196 | found = type_str.find_first_of("[]. ", found + 1); | |
1197 | if (string::npos == size_t(found)) { | |
1198 | break; | |
1199 | } | |
1200 | ||
1201 | if (type_str[found] == '.') | |
1202 | type_str[found] = '_'; | |
1203 | else | |
1204 | type_str[found] = 'Z'; | |
1205 | } | |
1206 | return type_str; | |
1207 | } | |
1208 | ||
1209 | /** | |
1210 | * Generates a process function definition. | |
1211 | * | |
1212 | * @param tfunction The function to write a dispatcher for | |
1213 | */ | |
1214 | void t_hs_generator::generate_process_function(t_service* tservice, t_function* tfunction) { | |
1215 | (void)tservice; | |
1216 | // Open function | |
1217 | string funname = decapitalize(tfunction->get_name()); | |
1218 | indent(f_service_) << "process_" << funname << " (seqid, iprot, oprot, handler) = do" << endl; | |
1219 | indent_up(); | |
1220 | ||
1221 | string argsname = capitalize(tfunction->get_name()) + "_args"; | |
1222 | string resultname = capitalize(tfunction->get_name()) + "_result"; | |
1223 | ||
1224 | // Generate the function call | |
1225 | t_struct* arg_struct = tfunction->get_arglist(); | |
1226 | const vector<t_field*>& fields = arg_struct->get_members(); | |
1227 | vector<t_field*>::const_iterator f_iter; | |
1228 | ||
1229 | indent(f_service_) << "args <- read_" << argsname << " iprot" << endl; | |
1230 | ||
1231 | t_struct* xs = tfunction->get_xceptions(); | |
1232 | const vector<t_field*>& xceptions = xs->get_members(); | |
1233 | vector<t_field*>::const_iterator x_iter; | |
1234 | ||
1235 | size_t n = xceptions.size() + 1; | |
1236 | // Try block for a function with exceptions | |
1237 | if (n > 0) { | |
1238 | for (size_t i = 0; i < n; i++) { | |
1239 | indent(f_service_) << "(X.catch" << endl; | |
1240 | indent_up(); | |
1241 | } | |
1242 | } | |
1243 | ||
1244 | if (n > 0) { | |
1245 | indent(f_service_) << "(do" << endl; | |
1246 | indent_up(); | |
1247 | } | |
1248 | indent(f_service_); | |
1249 | ||
1250 | if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) | |
1251 | f_service_ << "val <- "; | |
1252 | ||
1253 | f_service_ << "Iface." << decapitalize(tfunction->get_name()) << " handler"; | |
1254 | for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) | |
1255 | f_service_ << " (" << field_name(argsname, (*f_iter)->get_name()) << " args)"; | |
1256 | ||
1257 | if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) { | |
1258 | f_service_ << endl; | |
1259 | indent(f_service_) << "let res = default_" << resultname << "{" | |
1260 | << field_name(resultname, "success") << " = val}"; | |
1261 | ||
1262 | } else if (!tfunction->is_oneway()) { | |
1263 | f_service_ << endl; | |
1264 | indent(f_service_) << "let res = default_" << resultname; | |
1265 | } | |
1266 | f_service_ << endl; | |
1267 | ||
1268 | // Shortcut out here for oneway functions | |
1269 | if (tfunction->is_oneway()) { | |
1270 | indent(f_service_) << "P.return ()"; | |
1271 | } else { | |
1272 | indent(f_service_) << "T.writeMessage oprot (\"" << tfunction->get_name() | |
1273 | << "\", T.M_REPLY, seqid) $" << endl; | |
1274 | indent_up(); | |
1275 | indent(f_service_) << "write_" << resultname << " oprot res"; | |
1276 | indent_down(); | |
1277 | } | |
1278 | if (n > 0) { | |
1279 | f_service_ << ")"; | |
1280 | indent_down(); | |
1281 | } | |
1282 | f_service_ << endl; | |
1283 | ||
1284 | if (n > 0) { | |
1285 | for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { | |
1286 | indent(f_service_) << "(\\e -> do" << endl; | |
1287 | indent_up(); | |
1288 | ||
1289 | if (!tfunction->is_oneway()) { | |
1290 | indent(f_service_) << "let res = default_" << resultname << "{" | |
1291 | << field_name(resultname, (*x_iter)->get_name()) << " = P.Just e}" | |
1292 | << endl; | |
1293 | indent(f_service_) << "T.writeMessage oprot (\"" << tfunction->get_name() | |
1294 | << "\", T.M_REPLY, seqid) $" << endl; | |
1295 | indent_up(); | |
1296 | indent(f_service_) << "write_" << resultname << " oprot res"; | |
1297 | indent_down(); | |
1298 | } else { | |
1299 | indent(f_service_) << "P.return ()"; | |
1300 | } | |
1301 | ||
1302 | f_service_ << "))" << endl; | |
1303 | indent_down(); | |
1304 | indent_down(); | |
1305 | } | |
1306 | indent(f_service_) << "((\\_ -> do" << endl; | |
1307 | indent_up(); | |
1308 | ||
1309 | if (!tfunction->is_oneway()) { | |
1310 | indent(f_service_) << "T.writeMessage oprot (\"" << tfunction->get_name() | |
1311 | << "\", T.M_EXCEPTION, seqid) $" << endl; | |
1312 | indent_up(); | |
1313 | indent(f_service_) << "T.writeAppExn oprot (T.AppExn T.AE_UNKNOWN \"\")"; | |
1314 | indent_down(); | |
1315 | } else { | |
1316 | indent(f_service_) << "P.return ()"; | |
1317 | } | |
1318 | ||
1319 | f_service_ << ") :: X.SomeException -> P.IO ()))" << endl; | |
1320 | indent_down(); | |
1321 | indent_down(); | |
1322 | } | |
1323 | // Close function | |
1324 | indent_down(); | |
1325 | } | |
1326 | ||
1327 | /** | |
1328 | * Deserializes a field of any type. | |
1329 | */ | |
1330 | void t_hs_generator::generate_deserialize_field(ostream& out, t_field* tfield, string prefix) { | |
1331 | (void)prefix; | |
1332 | t_type* type = tfield->get_type(); | |
1333 | generate_deserialize_type(out, type, prefix); | |
1334 | } | |
1335 | ||
1336 | /** | |
1337 | * Deserializes a field of any type. | |
1338 | */ | |
1339 | void t_hs_generator::generate_deserialize_type(ostream& out, t_type* type, string arg) { | |
1340 | type = get_true_type(type); | |
1341 | string val = tmp("_val"); | |
1342 | out << "(case " << arg << " of {" << type_to_constructor(type) << " " << val << " -> "; | |
1343 | ||
1344 | if (type->is_void()) | |
1345 | throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE"; | |
1346 | ||
1347 | if (type->is_struct() || type->is_xception()) { | |
1348 | generate_deserialize_struct(out, (t_struct*)type, val); | |
1349 | ||
1350 | } else if (type->is_container()) { | |
1351 | generate_deserialize_container(out, type, val); | |
1352 | ||
1353 | } else if (type->is_base_type()) { | |
1354 | t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); | |
1355 | if (tbase == t_base_type::TYPE_STRING && !type->is_binary()) { | |
1356 | out << "E.decodeUtf8 "; | |
1357 | } | |
1358 | out << val; | |
1359 | if (type->is_binary()) { | |
1360 | // Since wire type of binary is the same as string, we actually receive T.TString not | |
1361 | // T.TBinary | |
1362 | out << "; T.TString " << val << " -> " << val; | |
1363 | } | |
1364 | } else if (type->is_enum()) { | |
1365 | out << "P.toEnum $ P.fromIntegral " << val; | |
1366 | ||
1367 | } else { | |
1368 | throw "DO NOT KNOW HOW TO DESERIALIZE TYPE " + type->get_name(); | |
1369 | } | |
1370 | out << "; _ -> P.error \"wrong type\"})"; | |
1371 | } | |
1372 | ||
1373 | /** | |
1374 | * Generates an unserializer for a struct, calling read() | |
1375 | */ | |
1376 | void t_hs_generator::generate_deserialize_struct(ostream& out, t_struct* tstruct, string name) { | |
1377 | ||
1378 | out << "(" << type_name(tstruct, "to_") << " (T.TStruct " << name << "))"; | |
1379 | } | |
1380 | ||
1381 | /** | |
1382 | * Serialize a container by writing out the header followed by | |
1383 | * data and then a footer. | |
1384 | */ | |
1385 | void t_hs_generator::generate_deserialize_container(ostream& out, t_type* ttype, string arg) { | |
1386 | ||
1387 | string val = tmp("_v"); | |
1388 | // Declare variables, read header | |
1389 | if (ttype->is_map()) { | |
1390 | string key = tmp("_k"); | |
1391 | out << "(Map.fromList $ P.map (\\(" << key << "," << val << ") -> ("; | |
1392 | generate_deserialize_type(out, ((t_map*)ttype)->get_key_type(), key); | |
1393 | ||
1394 | out << ","; | |
1395 | generate_deserialize_type(out, ((t_map*)ttype)->get_val_type(), val); | |
1396 | ||
1397 | out << ")) " << arg << ")"; | |
1398 | ||
1399 | } else if (ttype->is_set()) { | |
1400 | out << "(Set.fromList $ P.map (\\" << val << " -> "; | |
1401 | generate_deserialize_type(out, ((t_set*)ttype)->get_elem_type(), val); | |
1402 | out << ") " << arg << ")"; | |
1403 | ||
1404 | } else if (ttype->is_list()) { | |
1405 | out << "(Vector.fromList $ P.map (\\" << val << " -> "; | |
1406 | generate_deserialize_type(out, ((t_list*)ttype)->get_elem_type(), val); | |
1407 | out << ") " << arg << ")"; | |
1408 | } | |
1409 | } | |
1410 | ||
1411 | /** | |
1412 | * Serializes a field of any type. | |
1413 | * | |
1414 | * @param tfield The field to serialize | |
1415 | * @param prefix Name to prepend to field name | |
1416 | */ | |
1417 | void t_hs_generator::generate_serialize_type(ostream& out, t_type* type, string name) { | |
1418 | ||
1419 | type = get_true_type(type); | |
1420 | // Do nothing for void types | |
1421 | if (type->is_void()) | |
1422 | throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE"; | |
1423 | ||
1424 | if (type->is_struct() || type->is_xception()) { | |
1425 | generate_serialize_struct(out, (t_struct*)type, name); | |
1426 | ||
1427 | } else if (type->is_container()) { | |
1428 | generate_serialize_container(out, type, name); | |
1429 | ||
1430 | } else if (type->is_base_type() || type->is_enum()) { | |
1431 | if (type->is_base_type()) { | |
1432 | t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); | |
1433 | out << type_to_constructor(type) << " "; | |
1434 | if (tbase == t_base_type::TYPE_STRING && !type->is_binary()) { | |
1435 | out << "$ E.encodeUtf8 "; | |
1436 | } | |
1437 | out << name; | |
1438 | ||
1439 | } else if (type->is_enum()) { | |
1440 | string ename = capitalize(type->get_name()); | |
1441 | out << "T.TI32 $ P.fromIntegral $ P.fromEnum " << name; | |
1442 | } | |
1443 | ||
1444 | } else { | |
1445 | throw "DO NOT KNOW HOW TO SERIALIZE FIELD OF TYPE " + type->get_name(); | |
1446 | } | |
1447 | } | |
1448 | ||
1449 | /** | |
1450 | * Serializes all the members of a struct. | |
1451 | * | |
1452 | * @param tstruct The struct to serialize | |
1453 | * @param prefix String prefix to attach to all fields | |
1454 | */ | |
1455 | void t_hs_generator::generate_serialize_struct(ostream& out, t_struct* tstruct, string prefix) { | |
1456 | out << type_name(tstruct, "from_") << " " << prefix; | |
1457 | } | |
1458 | ||
1459 | void t_hs_generator::generate_serialize_container(ostream& out, t_type* ttype, string prefix) { | |
1460 | string k = tmp("_k"); | |
1461 | string v = tmp("_v"); | |
1462 | ||
1463 | if (ttype->is_map()) { | |
1464 | t_type* ktype = ((t_map*)ttype)->get_key_type(); | |
1465 | t_type* vtype = ((t_map*)ttype)->get_val_type(); | |
1466 | out << "T.TMap " << type_to_enum(ktype) << " " << type_to_enum(vtype); | |
1467 | out << " $ P.map (\\(" << k << "," << v << ") -> ("; | |
1468 | generate_serialize_type(out, ktype, k); | |
1469 | out << ", "; | |
1470 | generate_serialize_type(out, vtype, v); | |
1471 | out << ")) $ Map.toList " << prefix; | |
1472 | ||
1473 | } else if (ttype->is_set()) { | |
1474 | out << "T.TSet " << type_to_enum(((t_set*)ttype)->get_elem_type()); | |
1475 | out << " $ P.map (\\" << v << " -> "; | |
1476 | generate_serialize_type(out, ((t_set*)ttype)->get_elem_type(), v); | |
1477 | out << ") $ Set.toList " << prefix; | |
1478 | ||
1479 | } else if (ttype->is_list()) { | |
1480 | out << "T.TList " << type_to_enum(((t_list*)ttype)->get_elem_type()); | |
1481 | out << " $ P.map (\\" << v << " -> "; | |
1482 | generate_serialize_type(out, ((t_list*)ttype)->get_elem_type(), v); | |
1483 | out << ") $ Vector.toList " << prefix; | |
1484 | } | |
1485 | } | |
1486 | ||
1487 | string t_hs_generator::function_type(t_function* tfunc, bool options, bool io, bool method) { | |
1488 | string result = ""; | |
1489 | ||
1490 | const vector<t_field*>& fields = tfunc->get_arglist()->get_members(); | |
1491 | for (auto field : fields) { | |
1492 | if (field->get_req() == t_field::T_OPTIONAL | |
1493 | || ((t_type*)field->get_type())->is_xception()) | |
1494 | result += "P.Maybe "; | |
1495 | result += render_hs_type(field->get_type(), options); | |
1496 | result += " -> "; | |
1497 | } | |
1498 | ||
1499 | if (fields.empty() && !method) | |
1500 | result += "() -> "; | |
1501 | ||
1502 | if (io) | |
1503 | result += "P.IO "; | |
1504 | ||
1505 | result += render_hs_type(tfunc->get_returntype(), io); | |
1506 | return result; | |
1507 | } | |
1508 | ||
1509 | string t_hs_generator::type_name(t_type* ttype, string function_prefix) { | |
1510 | string prefix = ""; | |
1511 | t_program* program = ttype->get_program(); | |
1512 | ||
1513 | if (program != NULL && program != program_) | |
1514 | if (!ttype->is_service()) | |
1515 | prefix = capitalize(program->get_name()) + "_Types."; | |
1516 | ||
1517 | return prefix + function_prefix + capitalize(ttype->get_name()); | |
1518 | } | |
1519 | ||
1520 | string t_hs_generator::field_name(string tname, string fname) { | |
1521 | return decapitalize(tname) + "_" + fname; | |
1522 | } | |
1523 | ||
1524 | /** | |
1525 | * Converts the parse type to a Protocol.t_type enum | |
1526 | */ | |
1527 | string t_hs_generator::type_to_enum(t_type* type) { | |
1528 | type = get_true_type(type); | |
1529 | ||
1530 | if (type->is_base_type()) { | |
1531 | t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); | |
1532 | switch (tbase) { | |
1533 | case t_base_type::TYPE_VOID: | |
1534 | return "T.T_VOID"; | |
1535 | case t_base_type::TYPE_STRING: | |
1536 | return type->is_binary() ? "T.T_BINARY" : "T.T_STRING"; | |
1537 | case t_base_type::TYPE_BOOL: | |
1538 | return "T.T_BOOL"; | |
1539 | case t_base_type::TYPE_I8: | |
1540 | return "T.T_BYTE"; | |
1541 | case t_base_type::TYPE_I16: | |
1542 | return "T.T_I16"; | |
1543 | case t_base_type::TYPE_I32: | |
1544 | return "T.T_I32"; | |
1545 | case t_base_type::TYPE_I64: | |
1546 | return "T.T_I64"; | |
1547 | case t_base_type::TYPE_DOUBLE: | |
1548 | return "T.T_DOUBLE"; | |
1549 | } | |
1550 | ||
1551 | } else if (type->is_enum()) { | |
1552 | return "T.T_I32"; | |
1553 | ||
1554 | } else if (type->is_struct() || type->is_xception()) { | |
1555 | return "(T.T_STRUCT " + type_name((t_struct*)type, "typemap_") + ")"; | |
1556 | ||
1557 | } else if (type->is_map()) { | |
1558 | string ktype = type_to_enum(((t_map*)type)->get_key_type()); | |
1559 | string vtype = type_to_enum(((t_map*)type)->get_val_type()); | |
1560 | return "(T.T_MAP " + ktype + " " + vtype + ")"; | |
1561 | ||
1562 | } else if (type->is_set()) { | |
1563 | return "(T.T_SET " + type_to_enum(((t_set*)type)->get_elem_type()) + ")"; | |
1564 | ||
1565 | } else if (type->is_list()) { | |
1566 | return "(T.T_LIST " + type_to_enum(((t_list*)type)->get_elem_type()) + ")"; | |
1567 | } | |
1568 | ||
1569 | throw "INVALID TYPE IN type_to_enum: " + type->get_name(); | |
1570 | } | |
1571 | ||
1572 | /** | |
1573 | * Converts the parse type to a default value | |
1574 | */ | |
1575 | string t_hs_generator::type_to_default(t_type* type) { | |
1576 | type = get_true_type(type); | |
1577 | ||
1578 | if (type->is_base_type()) { | |
1579 | t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); | |
1580 | switch (tbase) { | |
1581 | case t_base_type::TYPE_VOID: | |
1582 | return "P.error \"No default value for type T_VOID\""; | |
1583 | case t_base_type::TYPE_STRING: | |
1584 | return "\"\""; | |
1585 | case t_base_type::TYPE_BOOL: | |
1586 | return "P.False"; | |
1587 | case t_base_type::TYPE_I8: | |
1588 | return "0"; | |
1589 | case t_base_type::TYPE_I16: | |
1590 | return "0"; | |
1591 | case t_base_type::TYPE_I32: | |
1592 | return "0"; | |
1593 | case t_base_type::TYPE_I64: | |
1594 | return "0"; | |
1595 | case t_base_type::TYPE_DOUBLE: | |
1596 | return "0"; | |
1597 | } | |
1598 | ||
1599 | } else if (type->is_enum()) { | |
1600 | return "(P.toEnum 0)"; | |
1601 | ||
1602 | } else if (type->is_struct() || type->is_xception()) { | |
1603 | return type_name((t_struct*)type, "default_"); | |
1604 | ||
1605 | } else if (type->is_map()) { | |
1606 | return "Map.empty"; | |
1607 | ||
1608 | } else if (type->is_set()) { | |
1609 | return "Set.empty"; | |
1610 | ||
1611 | } else if (type->is_list()) { | |
1612 | return "Vector.empty"; | |
1613 | } | |
1614 | ||
1615 | throw "INVALID TYPE IN type_to_default: " + type->get_name(); | |
1616 | } | |
1617 | ||
1618 | /** | |
1619 | * Converts the parse type to an haskell type | |
1620 | */ | |
1621 | string t_hs_generator::render_hs_type(t_type* type, bool needs_parens) { | |
1622 | type = get_true_type(type); | |
1623 | string type_repr; | |
1624 | ||
1625 | if (type->is_base_type()) { | |
1626 | t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); | |
1627 | switch (tbase) { | |
1628 | case t_base_type::TYPE_VOID: | |
1629 | return "()"; | |
1630 | case t_base_type::TYPE_STRING: | |
1631 | return (type->is_binary() ? "LBS.ByteString" : "LT.Text"); | |
1632 | case t_base_type::TYPE_BOOL: | |
1633 | return "P.Bool"; | |
1634 | case t_base_type::TYPE_I8: | |
1635 | return "I.Int8"; | |
1636 | case t_base_type::TYPE_I16: | |
1637 | return "I.Int16"; | |
1638 | case t_base_type::TYPE_I32: | |
1639 | return "I.Int32"; | |
1640 | case t_base_type::TYPE_I64: | |
1641 | return "I.Int64"; | |
1642 | case t_base_type::TYPE_DOUBLE: | |
1643 | return "P.Double"; | |
1644 | } | |
1645 | ||
1646 | } else if (type->is_enum()) { | |
1647 | return type_name((t_enum*)type); | |
1648 | ||
1649 | } else if (type->is_struct() || type->is_xception()) { | |
1650 | return type_name((t_struct*)type); | |
1651 | ||
1652 | } else if (type->is_map()) { | |
1653 | t_type* ktype = ((t_map*)type)->get_key_type(); | |
1654 | t_type* vtype = ((t_map*)type)->get_val_type(); | |
1655 | type_repr = "Map.HashMap " + render_hs_type(ktype, true) + " " + render_hs_type(vtype, true); | |
1656 | ||
1657 | } else if (type->is_set()) { | |
1658 | t_type* etype = ((t_set*)type)->get_elem_type(); | |
1659 | type_repr = "Set.HashSet " + render_hs_type(etype, true); | |
1660 | ||
1661 | } else if (type->is_list()) { | |
1662 | t_type* etype = ((t_list*)type)->get_elem_type(); | |
1663 | type_repr = "Vector.Vector " + render_hs_type(etype, true); | |
1664 | ||
1665 | } else { | |
1666 | throw "INVALID TYPE IN type_to_enum: " + type->get_name(); | |
1667 | } | |
1668 | ||
1669 | return needs_parens ? "(" + type_repr + ")" : type_repr; | |
1670 | } | |
1671 | ||
1672 | /** | |
1673 | * Converts the parse type to a haskell constructor | |
1674 | */ | |
1675 | string t_hs_generator::type_to_constructor(t_type* type) { | |
1676 | type = get_true_type(type); | |
1677 | ||
1678 | if (type->is_base_type()) { | |
1679 | t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); | |
1680 | switch (tbase) { | |
1681 | case t_base_type::TYPE_VOID: | |
1682 | throw "invalid type: T_VOID"; | |
1683 | case t_base_type::TYPE_STRING: | |
1684 | return type->is_binary() ? "T.TBinary" : "T.TString"; | |
1685 | case t_base_type::TYPE_BOOL: | |
1686 | return "T.TBool"; | |
1687 | case t_base_type::TYPE_I8: | |
1688 | return "T.TByte"; | |
1689 | case t_base_type::TYPE_I16: | |
1690 | return "T.TI16"; | |
1691 | case t_base_type::TYPE_I32: | |
1692 | return "T.TI32"; | |
1693 | case t_base_type::TYPE_I64: | |
1694 | return "T.TI64"; | |
1695 | case t_base_type::TYPE_DOUBLE: | |
1696 | return "T.TDouble"; | |
1697 | } | |
1698 | ||
1699 | } else if (type->is_enum()) { | |
1700 | return "T.TI32"; | |
1701 | ||
1702 | } else if (type->is_struct() || type->is_xception()) { | |
1703 | return "T.TStruct"; | |
1704 | ||
1705 | } else if (type->is_map()) { | |
1706 | return "T.TMap _ _"; | |
1707 | ||
1708 | } else if (type->is_set()) { | |
1709 | return "T.TSet _"; | |
1710 | ||
1711 | } else if (type->is_list()) { | |
1712 | return "T.TList _"; | |
1713 | } | |
1714 | throw "INVALID TYPE IN type_to_enum: " + type->get_name(); | |
1715 | } | |
1716 | ||
1717 | THRIFT_REGISTER_GENERATOR(hs, "Haskell", "") |