| // Copyright (c) 2006- Facebook |
| // Distributed under the Thrift Software License |
| // |
| // See accompanying file LICENSE or visit the Thrift site at: |
| // http://developers.facebook.com/thrift/ |
| |
| // still missing: inheritance, containers |
| |
| #include <stdlib.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <sstream> |
| #include "t_erl_generator.h" |
| |
| using namespace std; |
| |
| /** |
| * UI for file generation by opening up the necessary file output |
| * streams. |
| * |
| * @param tprogram The program to generate |
| */ |
| void t_erl_generator::init_generator() { |
| // Make output directory |
| mkdir(T_ERL_DIR, S_IREAD | S_IWRITE | S_IEXEC); |
| |
| // setup export lines |
| export_lines_first_ = true; |
| export_types_lines_first_ = true; |
| |
| // types files |
| string f_types_name = string(T_ERL_DIR)+"/"+program_name_+"_types.erl"; |
| string f_types_hrl_name = string(T_ERL_DIR)+"/"+program_name_+"_types.hrl"; |
| |
| f_types_file_.open(f_types_name.c_str()); |
| f_types_hrl_file_.open(f_types_hrl_name.c_str()); |
| |
| hrl_header(f_types_hrl_file_, program_name_ + "_types"); |
| |
| f_types_file_ << |
| erl_autogen_comment() << endl << |
| "-module(" << program_name_ << "_types)." << endl << |
| erl_imports() << endl; |
| |
| f_types_file_ << |
| "-include(\"" << program_name_ << "_types.hrl\")." << endl << |
| endl; |
| |
| f_types_hrl_file_ << render_includes() << endl; |
| |
| // consts file |
| string f_consts_name = string(T_ERL_DIR)+"/"+program_name_+"_constants.hrl"; |
| f_consts_.open(f_consts_name.c_str()); |
| |
| f_consts_ << |
| erl_autogen_comment() << endl << |
| erl_imports() << endl << |
| "-include(\"" << program_name_ << "_types.hrl\")." << endl << |
| endl; |
| } |
| |
| /** |
| * Boilerplate at beginning and end of header files |
| */ |
| void t_erl_generator::hrl_header(ostream& out, string name) { |
| out << "-ifndef(_" << name << "_included)." << endl << |
| "-define(_" << name << "_included, yeah)." << endl; |
| } |
| |
| void t_erl_generator::hrl_footer(ostream& out, string name) { |
| out << "-endif." << endl; |
| } |
| |
| /** |
| * Renders all the imports necessary for including another Thrift program |
| */ |
| string t_erl_generator::render_includes() { |
| const vector<t_program*>& includes = program_->get_includes(); |
| string result = ""; |
| for (size_t i = 0; i < includes.size(); ++i) { |
| result += "-include(\"" + includes[i]->get_name() + "_types.hrl\").\n"; |
| } |
| if (includes.size() > 0) { |
| result += "\n"; |
| } |
| return result; |
| } |
| |
| /** |
| * Autogen'd comment |
| */ |
| string t_erl_generator::erl_autogen_comment() { |
| return |
| std::string("%%\n") + |
| "%% Autogenerated by Thrift\n" + |
| "%%\n" + |
| "%% DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n" + |
| "%%\n"; |
| } |
| |
| /** |
| * Prints standard thrift imports |
| */ |
| string t_erl_generator::erl_imports() { |
| return |
| string("-include(\"thrift.hrl\").\n") + |
| "-include(\"tApplicationException.hrl\").\n" + |
| "-include(\"protocol/tProtocol.hrl\").\n"; |
| } |
| |
| /** |
| * Closes the type files |
| */ |
| void t_erl_generator::close_generator() { |
| // Close types file |
| f_types_file_ << "-export([" << export_types_lines_.str() << "])." << endl; |
| f_types_file_ << f_types_.str(); |
| |
| hrl_footer(f_types_hrl_file_, string("BOGUS")); |
| |
| f_types_file_.close(); |
| f_types_hrl_file_.close(); |
| f_consts_.close(); |
| } |
| |
| /** |
| * Generates a typedef. no op |
| * |
| * @param ttypedef The type definition |
| */ |
| void t_erl_generator::generate_typedef(t_typedef* ttypedef) { |
| } |
| |
| /** |
| * Generates code for an enumerated type. Done using a class to scope |
| * the values. |
| * |
| * @param tenum The enumeration |
| */ |
| void t_erl_generator::generate_enum(t_enum* tenum) { |
| vector<t_enum_value*> constants = tenum->get_constants(); |
| vector<t_enum_value*>::iterator c_iter; |
| |
| int value = -1; |
| |
| for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { |
| if ((*c_iter)->has_value()) { |
| value = (*c_iter)->get_value(); |
| } else { |
| ++value; |
| } |
| |
| string name = capitalize((*c_iter)->get_name()); |
| |
| f_types_hrl_file_ << |
| indent() << "-define(" << program_name_ << "_" << name << ", " << value << ")."<< endl; |
| } |
| |
| f_types_hrl_file_ << endl; |
| } |
| |
| /** |
| * Generate a constant value |
| */ |
| void t_erl_generator::generate_const(t_const* tconst) { |
| t_type* type = tconst->get_type(); |
| string name = capitalize(tconst->get_name()); |
| t_const_value* value = tconst->get_value(); |
| |
| f_consts_ << "-define(" << program_name_ << "_" << name << ", " << render_const_value(type, value) << ")." << endl << endl; |
| } |
| |
| /** |
| * Prints the value of a constant with the given type. Note that type checking |
| * is NOT performed in this function as it is always run beforehand using the |
| * validate_types method in main.cc |
| */ |
| string t_erl_generator::render_const_value(t_type* type, t_const_value* value) { |
| std::ostringstream out; |
| |
| if (type->is_base_type()) { |
| t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); |
| switch (tbase) { |
| case t_base_type::TYPE_STRING: |
| out << "\"" << value->get_string() << "\""; |
| break; |
| case t_base_type::TYPE_BOOL: |
| out << (value->get_integer() > 0 ? "true" : "false"); |
| break; |
| case t_base_type::TYPE_BYTE: |
| case t_base_type::TYPE_I16: |
| case t_base_type::TYPE_I32: |
| case t_base_type::TYPE_I64: |
| out << value->get_integer(); |
| break; |
| case t_base_type::TYPE_DOUBLE: |
| if (value->get_type() == t_const_value::CV_INTEGER) { |
| out << value->get_integer(); |
| } else { |
| out << value->get_double(); |
| } |
| break; |
| default: |
| throw "compiler error: no const of base type " + tbase; |
| } |
| } else if (type->is_enum()) { |
| indent(out) << value->get_integer(); |
| |
| } else if (type->is_struct() || type->is_xception()) { |
| out << "#" << type->get_name() << "{"; |
| const vector<t_field*>& fields = ((t_struct*)type)->get_members(); |
| vector<t_field*>::const_iterator f_iter; |
| const map<t_const_value*, t_const_value*>& val = value->get_map(); |
| map<t_const_value*, t_const_value*>::const_iterator v_iter; |
| |
| bool first = true; |
| for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { |
| t_type* field_type = NULL; |
| for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { |
| if ((*f_iter)->get_name() == v_iter->first->get_string()) { |
| field_type = (*f_iter)->get_type(); |
| } |
| } |
| if (field_type == NULL) { |
| throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string(); |
| } |
| |
| if (first) { |
| first = false; |
| } else { |
| out << ","; |
| } |
| out << v_iter->first->get_string(); |
| out << " = "; |
| out << render_const_value(field_type, v_iter->second); |
| } |
| indent_down(); |
| indent(out) << "}"; |
| |
| } else if (type->is_map()) { |
| t_type* ktype = ((t_map*)type)->get_key_type(); |
| t_type* vtype = ((t_map*)type)->get_val_type(); |
| const map<t_const_value*, t_const_value*>& val = value->get_map(); |
| map<t_const_value*, t_const_value*>::const_iterator v_iter; |
| |
| bool first = true; |
| out << "dict:from_list(["; |
| for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { |
| if (first) { |
| first=false; |
| } else { |
| out << ","; |
| } |
| out << "(" |
| << render_const_value(ktype, v_iter->first) << "," |
| << render_const_value(vtype, v_iter->second) << ")"; |
| } |
| out << "])"; |
| |
| } else if (type->is_set()) { |
| t_type* etype; |
| etype = ((t_set*)type)->get_elem_type(); |
| |
| bool first = true; |
| const vector<t_const_value*>& val = value->get_list(); |
| vector<t_const_value*>::const_iterator v_iter; |
| out << "sets:from_list(["; |
| for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { |
| if (first) { |
| first=false; |
| } else { |
| out << ","; |
| } |
| out << "(" << render_const_value(etype, *v_iter) << ",true)"; |
| } |
| out << "])"; |
| |
| } else if (type->is_list()) { |
| t_type* etype; |
| etype = ((t_list*)type)->get_elem_type(); |
| out << "["; |
| |
| bool first = true; |
| const vector<t_const_value*>& val = value->get_list(); |
| vector<t_const_value*>::const_iterator v_iter; |
| for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { |
| if (first) { |
| first=false; |
| } else { |
| out << ","; |
| } |
| out << render_const_value(etype, *v_iter); |
| } |
| out << "]"; |
| } |
| return out.str(); |
| } |
| |
| /** |
| * Generates a struct |
| */ |
| void t_erl_generator::generate_struct(t_struct* tstruct) { |
| generate_erl_struct(tstruct, false); |
| } |
| |
| /** |
| * Generates a struct definition for a thrift exception. Basically the same |
| * as a struct but extends the Exception class. |
| * |
| * @param txception The struct definition |
| */ |
| void t_erl_generator::generate_xception(t_struct* txception) { |
| generate_erl_struct(txception, true); |
| } |
| |
| /** |
| * Generates a struct |
| */ |
| void t_erl_generator::generate_erl_struct(t_struct* tstruct, |
| bool is_exception) { |
| generate_erl_struct_definition(f_types_, f_types_hrl_file_, tstruct, is_exception); |
| } |
| |
| /** |
| * Generates a struct definition for a thrift data type. |
| * |
| * @param tstruct The struct definition |
| */ |
| void t_erl_generator::generate_erl_struct_definition(ostream& out, |
| ostream& hrl_out, |
| t_struct* tstruct, |
| bool is_exception, |
| bool is_result) |
| { |
| const vector<t_field*>& members = tstruct->get_members(); |
| vector<t_field*>::const_iterator m_iter; |
| |
| indent(out) << "%% struct " << type_name(tstruct) << endl; |
| |
| if (is_exception) { |
| } |
| |
| out << endl; |
| |
| if (members.size() > 0) { |
| indent(out) << "% -record(" << type_name(tstruct) << ", {"; |
| indent(hrl_out) << "-record(" << type_name(tstruct) << ", {"; |
| |
| bool first = true; |
| for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { |
| if (first) { |
| first = false; |
| } else { |
| out << ", "; |
| hrl_out << ", "; |
| } |
| out << (*m_iter)->get_name(); |
| hrl_out << (*m_iter)->get_name(); |
| } |
| out << "})." << endl; |
| hrl_out << "})." << endl; |
| } else { // no members; explicit comment |
| indent(out) << "% -record(" << type_name(tstruct) << ", {})." << endl; |
| indent(hrl_out) << "-record(" << type_name(tstruct) << ", {})." << endl; |
| } |
| |
| out << endl; |
| hrl_out << endl; |
| |
| generate_erl_struct_reader(out, tstruct); |
| generate_erl_struct_writer(out, tstruct); |
| } |
| |
| /** |
| * Generates the read method for a struct |
| */ |
| void t_erl_generator::generate_erl_struct_reader(ostream& out, |
| t_struct* tstruct) { |
| const vector<t_field*>& fields = tstruct->get_members(); |
| vector<t_field*>::const_iterator f_iter; |
| |
| string name = type_name(tstruct) + "_read"; |
| |
| if (out == f_types_) { // OH HAI MR. HORRIBLE |
| export_types_string(name, 1); |
| } else { |
| export_string(name, 1); |
| } |
| |
| indent(out) << name << "(Iprot) ->" << endl; |
| indent_up(); |
| |
| out << |
| indent() << "?R0(Iprot, readStructBegin)," << endl << |
| indent() << "Str = " << type_name(tstruct) << "_read_loop(Iprot, "; |
| |
| // empty struct |
| out << "#" << type_name(tstruct) << "{}"; |
| out << ")," << endl << |
| indent() << "?R0(Iprot, readStructEnd)," << endl << |
| indent() << "Str." << endl; |
| |
| indent_down(); |
| |
| indent(out) << |
| "" << type_name(tstruct) << "_read_loop(Iprot, Str) ->" << endl; |
| indent_up(); |
| |
| // Read beginning field marker |
| out << |
| indent() << "{ _Fname, Ftype, Fid } = ?R0(Iprot, readFieldBegin)," << endl << |
| indent() << "Fid, % suppress unused warnings" << endl; |
| |
| // Check for field STOP marker and break |
| indent(out) << "if" << endl; |
| indent_up(); |
| indent(out) << "Ftype == ?tType_STOP ->" << endl << |
| indent() << " Str;" << endl; |
| |
| // Generate deserialization code for known cases |
| for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { |
| out << indent() << "(Fid == " << (*f_iter)->get_key() << ") and (Ftype == " |
| << type_to_enum((*f_iter)->get_type()) << ") ->" << endl; |
| |
| indent_up(); |
| generate_deserialize_field(out, *f_iter, "Val"); |
| |
| out << indent() << "?R0(Iprot, readFieldEnd)," << endl |
| << indent() << type_name(tstruct) << "_read_loop(Iprot, " |
| << "Str#" << type_name(tstruct) |
| << "{" << (*f_iter)->get_name() |
| << "=Val});" << endl; |
| indent_down(); |
| } |
| |
| // In the default case we skip the field |
| out << |
| indent() << "true -> " << endl << |
| indent() << " ?R1(Iprot, skip, Ftype)," << endl << |
| indent() << " ?R0(Iprot, readFieldEnd)," << endl << |
| indent() << " " << type_name(tstruct) << "_read_loop(Iprot, Str)" << endl; |
| indent_down(); |
| |
| indent(out) << "end." << endl; |
| |
| indent_down(); |
| out << endl; |
| } |
| |
| void t_erl_generator::generate_erl_struct_writer(ostream& out, |
| t_struct* tstruct) { |
| string name = tstruct->get_name(); |
| const vector<t_field*>& fields = tstruct->get_members(); |
| vector<t_field*>::const_iterator f_iter; |
| |
| string fname = type_name(tstruct) + "_write"; |
| |
| if (out == f_types_) { // OH HAI MR. HORRIBLE |
| export_types_string(fname, 2); |
| } else { |
| export_string(fname, 2); |
| } |
| |
| indent(out) << fname << "(Str, Oprot) ->" << endl; |
| indent_up(); |
| |
| out << |
| indent() << "Str, % suppress unused warnings" << endl << |
| indent() << "?R1(Oprot, writeStructBegin, \"" << name << "\")," << endl; |
| |
| string prefix = string("Str#") + type_name(tstruct) + "."; |
| for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { |
| // Write field header |
| indent(out) << |
| "if " << prefix << (*f_iter)->get_name() << " /= undefined ->" << endl; |
| indent_up(); |
| indent(out) << |
| "?R3(Oprot, writeFieldBegin, " << |
| "\"" << (*f_iter)->get_name() << "\", " << |
| type_to_enum((*f_iter)->get_type()) << ", " << |
| (*f_iter)->get_key() << ")," << endl; |
| |
| // Write field contents |
| generate_serialize_field(out, *f_iter, prefix); |
| |
| // Write field closer |
| indent(out) << |
| "?R0(Oprot, writeFieldEnd);" << endl << |
| indent() << "true -> ok" << endl; |
| |
| indent_down(); |
| out << " end," << endl; |
| } |
| |
| // Write the struct map |
| out << |
| indent() << "?R0(Oprot, writeFieldStop)," << endl << |
| indent() << "?R0(Oprot, writeStructEnd)," << endl << |
| indent() << "ok." << endl; |
| |
| indent_down(); |
| |
| out << endl; |
| } |
| |
| /** |
| * Generates a thrift service. |
| * |
| * @param tservice The service definition |
| */ |
| void t_erl_generator::generate_service(t_service* tservice) { |
| // somehow this point is reached before the constructor and it's not downcased yet |
| // ...awesome |
| service_name_[0] = tolower(service_name_[0]); |
| |
| string f_service_hrl_name = string(T_ERL_DIR)+"/"+service_name_+".hrl"; |
| string f_service_name = string(T_ERL_DIR)+"/"+service_name_+".erl"; |
| f_service_file_.open(f_service_name.c_str()); |
| f_service_hrl_.open(f_service_hrl_name.c_str()); |
| |
| hrl_header(f_service_hrl_, service_name_); |
| |
| if (tservice->get_extends() != NULL) { |
| f_service_hrl_ << "-include(\"" << |
| uncapitalize(tservice->get_extends()->get_name()) << ".hrl\"). % inherit " << endl; |
| } |
| |
| f_service_hrl_ << |
| "-include(\"" << program_name_ << "_types.hrl\")." << endl << |
| endl; |
| |
| // Generate the three main parts of the service (well, two for now in PHP) |
| generate_service_helpers(tservice); // cpiro: New Erlang Order |
| |
| generate_service_interface(tservice); |
| generate_service_client(tservice); |
| generate_service_server(tservice); |
| |
| // indent_down(); |
| |
| f_service_file_ << |
| erl_autogen_comment() << endl << |
| "-module(" << service_name_ << ")." << endl << endl << |
| erl_imports() << endl; |
| |
| f_service_file_ << "-include(\"" << uncapitalize(tservice->get_name()) << ".hrl\")." << endl << endl; |
| |
| f_service_file_ << "-export([" << export_lines_.str() << "])." << endl << endl; |
| |
| f_service_file_ << f_service_.str(); |
| |
| hrl_footer(f_service_hrl_, f_service_name); |
| |
| // Close service file |
| f_service_file_.close(); |
| f_service_hrl_.close(); |
| } |
| |
| /** |
| * Generates helper functions for a service. |
| * |
| * @param tservice The service to generate a header definition for |
| */ |
| void t_erl_generator::generate_service_helpers(t_service* tservice) { |
| vector<t_function*> functions = tservice->get_functions(); |
| vector<t_function*>::iterator f_iter; |
| |
| // indent(f_service_) << |
| // "% HELPER FUNCTIONS AND STRUCTURES" << endl << endl; |
| |
| for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { |
| t_struct* ts = (*f_iter)->get_arglist(); |
| |
| generate_erl_struct_definition(f_service_, f_service_hrl_, ts, false); |
| generate_erl_function_helpers(*f_iter); |
| } |
| } |
| |
| /** |
| * Generates a struct and helpers for a function. |
| * |
| * @param tfunction The function |
| */ |
| void t_erl_generator::generate_erl_function_helpers(t_function* tfunction) { |
| t_struct result(program_, tfunction->get_name() + "_result"); |
| t_field success(tfunction->get_returntype(), "success", 0); |
| if (!tfunction->get_returntype()->is_void()) { |
| result.append(&success); |
| } |
| t_struct* xs = tfunction->get_xceptions(); |
| const vector<t_field*>& fields = xs->get_members(); |
| vector<t_field*>::const_iterator f_iter; |
| for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { |
| result.append(*f_iter); |
| } |
| generate_erl_struct_definition(f_service_, f_service_hrl_, &result, false, true); |
| } |
| |
| /** |
| * Generates a service interface definition. |
| * |
| * @param tservice The service to generate a header definition for |
| */ |
| void t_erl_generator::generate_service_interface(t_service* tservice) { |
| // f_service_ << |
| // indent() << "module Iface" << endl; |
| // indent_up(); |
| |
| // if (tservice->get_extends() != NULL) { |
| // string extends = type_name(tservice->get_extends()); |
| // indent(f_service_) << "include " << extends << "::Iface" << endl; |
| // } |
| |
| vector<t_function*> functions = tservice->get_functions(); |
| vector<t_function*>::iterator f_iter; |
| f_service_ << "%%% interface" << endl; |
| for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { |
| f_service_ << |
| indent() << "% " << function_signature(*f_iter) << endl; |
| } |
| // indent_down(); |
| indent(f_service_) << endl; |
| } |
| |
| /** |
| * Generates a service client definition. |
| * |
| * @param tservice The service to generate a server for. |
| */ |
| void t_erl_generator::generate_service_client(t_service* tservice) { |
| string extends = ""; |
| string extends_client = ""; |
| // if (tservice->get_extends() != NULL) { |
| // extends = type_name(tservice->get_extends()); |
| // extends_client = " < " + extends + "::Client "; |
| // } |
| |
| // indent(f_service_) << |
| // "class Client" << extends_client << endl; |
| // indent_up(); |
| |
| // indent(f_service_) << |
| // "include Iface" << endl << endl; |
| |
| // Constructor function |
| export_string("new", 2); |
| export_string("new", 1); |
| |
| f_service_ << |
| indent() << "new(Iprot, Oprot) ->" << endl << |
| indent() << " #"<<service_name_<<"{iprot=Iprot, oprot=Oprot, seqid=0}." << endl << |
| indent() << "new(Iprot) ->" << endl << |
| indent() << " #"<<service_name_<<"{iprot=Iprot, oprot=Iprot, seqid=0}." << endl << endl; |
| |
| // indent(f_service_) << "end" << endl << endl; |
| |
| f_service_hrl_ << |
| indent() << "-record("<< service_name_ <<", {iprot, oprot, seqid})." << endl << endl; |
| |
| // Generate client method implementations |
| vector<t_function*> functions = tservice->get_functions(); |
| vector<t_function*>::const_iterator f_iter; |
| for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { |
| t_struct* arg_struct = (*f_iter)->get_arglist(); |
| const vector<t_field*>& fields = arg_struct->get_members(); |
| vector<t_field*>::const_iterator fld_iter; |
| string funname = (*f_iter)->get_name(); |
| |
| export_function(*f_iter); |
| |
| // Open function |
| indent(f_service_) << |
| function_signature(*f_iter) << " ->" << endl; |
| |
| indent_up(); |
| |
| indent(f_service_) << |
| "send_" << funname << "(This"; |
| |
| //bool first = true; |
| for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { |
| // if (first) { |
| // first = false; |
| // } else { |
| f_service_ << ", "; |
| // } |
| f_service_ << capitalize((*fld_iter)->get_name()); |
| } |
| f_service_ << ")," << endl; |
| |
| if (!(*f_iter)->is_async()) { |
| f_service_ << indent(); |
| if (!(*f_iter)->get_returntype()->is_void()) { |
| f_service_ << ""; |
| } |
| f_service_ << |
| "recv_" << funname << "(This), " << endl; |
| } |
| |
| indent(f_service_) << "ok." << endl; |
| indent_down(); |
| f_service_ << endl; |
| |
| export_function(*f_iter, "send_"); |
| |
| indent(f_service_) << |
| "send_" << function_signature(*f_iter) << " ->" << endl; |
| indent_up(); |
| |
| std::string argsname = capitalize((*f_iter)->get_name() + "_args"); |
| |
| // Serialize the request header |
| f_service_ << |
| indent() << "Oprot = oop:get(This, oprot)," << endl << |
| indent() << "Seqid = oop:get(This, seqid)," << endl << |
| indent() << "?R3(Oprot, writeMessageBegin, \"" << (*f_iter)->get_name() << "\", ?tMessageType_CALL, Seqid)," << endl << |
| indent() << "Args = #" << (*f_iter)->get_name() << "_args{"; |
| |
| bool first = true; |
| for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { |
| f_service_ << (first ? first = false, "" : ", ") |
| << (*fld_iter)->get_name() |
| << "=" << capitalize((*fld_iter)->get_name()); |
| } |
| f_service_ << "}," << endl; |
| |
| indent(f_service_) << (*f_iter)->get_name() << "_args_write(Args, Oprot)," << endl; |
| |
| // Write to the stream |
| f_service_ << |
| indent() << "?R0(Oprot, writeMessageEnd)," << endl << |
| indent() << "Trans = ?R1(Oprot, get, trans)," << endl << |
| indent() << "?R0(Trans, effectful_flush)," << endl << |
| indent() << "ok." << endl; |
| |
| indent_down(); |
| |
| if (!(*f_iter)->is_async()) { |
| std::string resultname = uncapitalize((*f_iter)->get_name() + "_result"); |
| t_struct noargs(program_); |
| |
| t_function recv_function((*f_iter)->get_returntype(), |
| string("recv_") + (*f_iter)->get_name(), |
| &noargs); |
| |
| export_function(&recv_function, ""); |
| |
| // Open function |
| f_service_ << |
| endl << |
| indent() << function_signature(&recv_function) << " ->" << endl; |
| indent_up(); |
| |
| // TODO(mcslee): Validate message reply here, seq ids etc. |
| |
| f_service_ << |
| indent() << "Iprot = oop:get(This, iprot)," << endl << |
| indent() << "{ _Fname, Mtype, _Rseqid } = ?R0(Iprot, readMessageBegin)," << endl << |
| indent() << "if" << endl << |
| indent() << " Mtype == ?tMessageType_EXCEPTION ->" << endl << |
| indent() << " X = tApplicationException:new()," << endl << |
| indent() << " tApplicationException:read(X, Iprot)," << endl << |
| indent() << " ?R0(Iprot, readMessageEnd), " << endl << |
| indent() << " throw(X);" << endl << |
| indent() << " true ->" << endl << |
| indent() << " Result = " << resultname << "_read(Iprot)," << endl << |
| indent() << " ?R0(Iprot, readMessageEnd)," << endl << |
| indent() << " if % time to figure out retval" << endl; |
| |
| // WATCH cpiro |
| // Careful, only return _result if not a void function |
| |
| // TODO(cpiro): exit or {ok, _} and {error, _} ?? |
| |
| std::string result = "Result#"+resultname+"."; |
| if (!(*f_iter)->get_returntype()->is_void()) { |
| f_service_ << |
| indent() << " " << result << "success /= nil ->" << endl << |
| indent() << " " << result << "success;" << endl; |
| } |
| |
| t_struct* xs = (*f_iter)->get_xceptions(); // TODO(cpiro) |
| const std::vector<t_field*>& xceptions = xs->get_members(); |
| vector<t_field*>::const_iterator x_iter; |
| for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { |
| f_service_ << |
| indent() << " " << result << (*x_iter)->get_name() << " /= nil -> " << endl << |
| indent() << " throw(" << result << (*x_iter)->get_name() << ");" << endl; |
| } |
| |
| // Careful, only return _result if not a void function |
| if ((*f_iter)->get_returntype()->is_void()) { |
| f_service_ << |
| indent() << " true -> nil" << endl << |
| indent() << " end" << endl; |
| } else { |
| f_service_ << |
| indent() << " true -> " << endl << |
| indent() << " throw(tApplicationException:new(?tApplicationException_MISSING_RESULT, \"" << (*f_iter)->get_name() << " failed: unknown result\"))" << endl << |
| indent() << " end" << endl; |
| } |
| |
| // Close function |
| indent(f_service_) << "end." << endl << endl; |
| indent_down(); |
| } |
| } |
| |
| indent_down(); |
| indent(f_service_) << endl; |
| } |
| |
| /** |
| * Generates a service server definition. |
| * |
| * @param tservice The service to generate a server for. |
| */ |
| void t_erl_generator::generate_service_server(t_service* tservice) { |
| // Generate the dispatch methods |
| vector<t_function*> functions = tservice->get_functions(); |
| vector<t_function*>::iterator f_iter; |
| |
| string extends = ""; |
| string extends_processor = ""; |
| if (tservice->get_extends() != NULL) { |
| extends = type_name(tservice->get_extends()); |
| extends_processor = " INHERIT(" + extends + "::Processor) % TODO"; |
| } |
| |
| // Generate the header portion |
| indent(f_service_) << |
| "%% processor" << extends_processor << endl; |
| |
| indent_up(); |
| |
| // TODO: inheritance runtime code (prolly) goes here: |
| |
| // f_service_ << |
| // indent() << "include Iface" << endl << |
| // indent() << "include TProcessor" << endl << |
| // endl; |
| |
| /* |
| indent(f_service_) << |
| "def initialize(handler)" << endl; |
| indent_up(); |
| if (extends.empty()) { |
| f_service_ << |
| indent() << "@handler = handler" << endl << |
| indent() << "@processMap = {}" << endl; |
| } else { |
| f_service_ << |
| indent() << "super(handler)" << endl; |
| } |
| for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { |
| f_service_ << |
| indent() << "@processMap['" << (*f_iter)->get_name() << "'] = method(:process_" << (*f_iter)->get_name() << ")" << endl; |
| } |
| indent_down(); |
| indent(f_service_) << "end" << endl << endl; |
| */ |
| |
| export_string("process", 3); |
| export_string("proc", 6); |
| |
| // Generate the server implementation |
| indent(f_service_) << |
| "process(HandlerModule, Iprot, Oprot) ->" << endl; |
| indent_up(); |
| |
| f_service_ << |
| indent() << "{ Name, _Type, Seqid } = ?R0(Iprot, readMessageBegin)," << endl << |
| indent() << "proc(Name, _Type, Seqid, HandlerModule, Iprot, Oprot)." << endl; |
| |
| indent_down(); |
| indent(f_service_) << |
| "proc(Name, _Type, Seqid, HandlerModule, Iprot, Oprot) ->" << endl; |
| indent_up(); |
| |
| // TODO(mcslee): validate message |
| |
| // HOT: dictionary function lookup |
| f_service_ << |
| // try to dispatch to one of our process_* |
| indent() << "case Name of" << endl; |
| |
| for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { |
| f_service_ << |
| indent() << " \"" << (*f_iter)->get_name() << "\" -> process_" << (*f_iter)->get_name() << "(HandlerModule, Seqid, Iprot, Oprot);" << endl; |
| } |
| |
| indent(f_service_) << " _ -> % unknown function" << endl; |
| if (tservice->get_extends() != NULL) { |
| indent(f_service_) << " " << extends << ":proc(Name,_Type,Seqid,HandlerModule, Iprot, Oprot)" << endl; |
| } else { |
| f_service_ << |
| indent() << " ?R1(Iprot, skip, ?tType_STRUCT)," << endl << |
| indent() << " ?R0(Iprot, readMessageEnd)," << endl << |
| indent() << " X = tApplicationException:new(?tApplicationException_UNKNOWN_METHOD, \"Unknown function \" ++ Name)," << endl << |
| indent() << " ?R3(Oprot, writeMessageBegin, Name, ?tMessageType_EXCEPTION, Seqid)," << endl << |
| indent() << " tApplicationException:write(X, Oprot)," << endl << |
| indent() << " ?R0(Oprot, writeMessageEnd)," << endl << |
| indent() << " Trans = ?R1(Oprot, get, trans)," << endl << |
| indent() << " ?R0(Trans, effectful_flush)," << endl << |
| indent() << " {error, X} % what's the retval in this case?" << endl; |
| } |
| f_service_ << indent() << "end." << endl; |
| |
| indent_down(); |
| |
| // Generate the process subfunctions |
| for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { |
| generate_process_function(tservice, *f_iter); |
| } |
| |
| indent_down(); |
| indent(f_service_) << endl << endl; |
| } |
| |
| /** |
| * Generates a process function definition. |
| * |
| * @param tfunction The function to write a dispatcher for |
| */ |
| void t_erl_generator::generate_process_function(t_service* tservice, |
| t_function* tfunction) { |
| |
| string name = "process_" + tfunction->get_name(); |
| |
| export_string(name, 4); |
| |
| // Open function |
| indent(f_service_) << |
| name << |
| "(HandlerModule, Seqid, Iprot, Oprot) ->" << endl; |
| indent_up(); |
| |
| f_service_ << |
| indent() << "Seqid, Oprot, % suppress unused warnings" << endl; |
| |
| string argsname = tfunction->get_name() + "_args"; |
| string resultname = tfunction->get_name() + "_result"; |
| |
| f_service_ << |
| indent() << "_Args = " << argsname << "_read(Iprot)," << endl << |
| // indent() << "Args, Seqid, Oprot, % suppress unused warnings" << endl << |
| // indent() << "Args % suppress unused warnings" << endl << |
| indent() << "?R0(Iprot, readMessageEnd)," << endl; |
| |
| t_struct* xs = tfunction->get_xceptions(); |
| const std::vector<t_field*>& xceptions = xs->get_members(); |
| vector<t_field*>::const_iterator x_iter; |
| |
| // Declare result for non async function |
| if (!tfunction->is_async()) { |
| } |
| |
| // Generate the function call |
| t_struct* arg_struct = tfunction->get_arglist(); |
| const std::vector<t_field*>& fields = arg_struct->get_members(); |
| vector<t_field*>::const_iterator f_iter; |
| |
| indent(f_service_) << "Result = "; |
| if (xceptions.size() > 0) { |
| f_service_ << "try" << endl; |
| } else { |
| f_service_ << "begin" << endl; |
| } |
| indent_up(); |
| |
| f_service_ << indent(); |
| if (!tfunction->is_async() && !tfunction->get_returntype()->is_void()) { |
| f_service_<< "Res = "; |
| } |
| f_service_ << "HandlerModule:" << tfunction->get_name() << "("; |
| |
| bool first = true; |
| for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { |
| if (first) { |
| first = false; |
| } else { |
| f_service_ << ", "; |
| } |
| f_service_ << "_Args#" << tfunction->get_name() << "_args." << (*f_iter)->get_name(); |
| } |
| f_service_ << ")," << endl; |
| if (!tfunction->is_async() && !tfunction->get_returntype()->is_void()) { |
| indent(f_service_) << "#" << resultname << "{success=Res}" << endl; |
| } else{ |
| indent(f_service_) << "#" << resultname << "{}" << endl; |
| } |
| indent_down(); |
| if (!tfunction->is_async() && xceptions.size() > 0) { |
| indent(f_service_) << "catch" << endl; |
| indent_up(); |
| for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { |
| indent(f_service_) << "E when is_record(E," << uncapitalize((*x_iter)->get_type()->get_name()) << ") ->" << endl; |
| indent_up(); |
| indent(f_service_) << "#" << resultname << "{" << (*x_iter)->get_name() << " = E};" << endl; |
| indent_down(); |
| } |
| indent(f_service_) << "dummy -> dummy % TODO: only for the semicolon's sake" << endl; |
| indent_down(); |
| } |
| indent(f_service_) << "end," << endl; |
| |
| if (tfunction->is_async()) { |
| indent(f_service_) << "% async, write nothing" << endl; |
| } else { |
| f_service_ << |
| indent() << "?R3(Oprot, writeMessageBegin, \"" << tfunction->get_name() << "\", ?tMessageType_REPLY, Seqid)," << endl << |
| indent() << tfunction->get_name() << "_result_write(Result, Oprot)," << endl << |
| indent() << "?R0(Oprot, writeMessageEnd)," << endl << |
| indent() << "Trans = ?R1(Oprot, get, trans)," << endl << |
| indent() << "?R0(Trans, effectful_flush)," << endl; |
| } |
| |
| indent(f_service_) << "Result." << endl << endl; |
| |
| indent_down(); |
| } |
| |
| /** |
| * Deserializes a field of any type. |
| */ |
| void t_erl_generator::generate_deserialize_field(ostream &out, |
| t_field* tfield, |
| string prefix, |
| bool inclass) { |
| t_type* type = tfield->get_type(); |
| while (type->is_typedef()) { |
| type = ((t_typedef*)type)->get_type(); |
| } |
| |
| if (type->is_void()) { |
| throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + |
| prefix + tfield->get_name(); |
| } |
| |
| string name = prefix; //+ tfield->get_name(); |
| |
| if (type->is_struct() || type->is_xception()) { |
| generate_deserialize_struct(out, |
| (t_struct*)type, |
| name); |
| } else if (type->is_container()) { |
| generate_deserialize_container(out, type, name); |
| } else if (type->is_base_type() || type->is_enum()) { |
| indent(out) << |
| name << " = ?R0(Iprot, "; |
| |
| if (type->is_base_type()) { |
| t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); |
| switch (tbase) { |
| case t_base_type::TYPE_VOID: |
| throw "compiler error: cannot serialize void field in a struct: " + |
| name; |
| break; |
| case t_base_type::TYPE_STRING: |
| out << "readString"; |
| break; |
| case t_base_type::TYPE_BOOL: |
| out << "readBool"; |
| break; |
| case t_base_type::TYPE_BYTE: |
| out << "readByte"; |
| break; |
| case t_base_type::TYPE_I16: |
| out << "readI16"; |
| break; |
| case t_base_type::TYPE_I32: |
| out << "readI32"; |
| break; |
| case t_base_type::TYPE_I64: |
| out << "readI64"; |
| break; |
| case t_base_type::TYPE_DOUBLE: |
| out << "readDouble"; |
| break; |
| default: |
| throw "compiler error: no PHP name for base type " + tbase; |
| } |
| } else if (type->is_enum()) { |
| out << "readI32"; |
| } |
| out << ")," << endl; |
| |
| } else { |
| printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n", |
| tfield->get_name().c_str(), type->get_name().c_str()); |
| } |
| } |
| |
| /** |
| * Generates an unserializer for a struct, calling read() |
| */ |
| void t_erl_generator::generate_deserialize_struct(ostream &out, |
| t_struct* tstruct, |
| string prefix) { |
| out << |
| indent() << prefix << " = " << (tstruct->get_program())->get_name() << "_types:" << type_name(tstruct) << "_read(Iprot)," << endl; |
| } |
| |
| /** |
| * Serialize a container by writing out the header followed by |
| * data and then a footer. |
| */ |
| void t_erl_generator::generate_deserialize_container(ostream &out, |
| t_type* ttype, |
| string prefix) { |
| string size = tmp("_size"); |
| string ktype = tmp("_ktype"); |
| string vtype = tmp("_vtype"); |
| string etype = tmp("_etype"); |
| |
| t_field fsize(g_type_i32, size); |
| t_field fktype(g_type_byte, ktype); |
| t_field fvtype(g_type_byte, vtype); |
| t_field fetype(g_type_byte, etype); |
| |
| // Declare variables, read header |
| if (ttype->is_map()) { |
| t_map* tmap = (t_map*)ttype; |
| string key = tmp("_key"); |
| string val = tmp("_val"); |
| t_field fkey(tmap->get_key_type(), key); |
| t_field fval(tmap->get_val_type(), val); |
| |
| out << |
| indent() << "{" << ktype << ", " << vtype << ", " << size << " } = ?R0(Iprot,readMapBegin)," << endl; |
| out << |
| indent() << prefix << " = dict:from_list(thrift_utils:tabulate(" << size << "," << endl; |
| indent_up(); |
| out << indent() << "fun (_) ->" << endl; |
| indent_up(); |
| generate_deserialize_field(out, &fkey,key); |
| generate_deserialize_field(out, &fval,val); |
| out << indent() << "{" << key << "," << val << "}" << endl; |
| indent_down(); |
| out << indent() << "end))," << endl; |
| indent_down(); |
| out << indent() << "?R0(Iprot,readMapEnd)," << endl; |
| |
| } else if (ttype->is_set()) { |
| t_set* tset = (t_set*)ttype; |
| string elem = tmp("_elem"); |
| t_field felem(tset->get_elem_type(), elem); |
| out << |
| indent() << "{" << etype << ", " << size << "} = ?R0(Iprot,readSetBegin)," << endl; |
| out << |
| indent() << prefix << " = sets:from_list(thrift_utils:tabulate(" << size << "," << endl; |
| indent_up(); |
| out << indent() << "fun (_) ->" << endl; |
| indent_up(); |
| generate_deserialize_field(out,&felem,elem); |
| out << indent() << elem << endl; |
| indent_down(); |
| out << indent() << "end)),"; |
| indent_down(); |
| out << indent() << "?R0(Iprot,readSetEnd)," << endl; |
| |
| } else if (ttype->is_list()) { |
| t_list* tlist = (t_list*)ttype; |
| string elem = tmp("_elem"); |
| t_field felem(tlist->get_elem_type(), elem); |
| out << indent() << "{" << etype << ", " << size << "} = ?R0(Iprot,readListBegin)," << endl; |
| out << indent() << prefix << " = thrift_utils:tabulate(" << size << "," << endl; |
| indent_up(); |
| out << indent() << "fun (_) ->" << endl; |
| indent_up(); |
| generate_deserialize_field(out,&felem,elem); |
| out << indent() << elem << endl; |
| indent_down(); |
| out << indent() << "end)," << endl; |
| indent_down(); |
| out << indent() << "?R0(Iprot,readListEnd)," << endl; |
| } |
| } |
| |
| |
| /** |
| * Generates code to deserialize a map UNUSED |
| */ |
| void t_erl_generator::generate_deserialize_map_element(ostream &out, // TODO |
| t_map* tmap, |
| string prefix) { |
| string key = tmp("_key"); |
| string val = tmp("_val"); |
| t_field fkey(tmap->get_key_type(), key); |
| t_field fval(tmap->get_val_type(), val); |
| |
| generate_deserialize_field(out, &fkey); |
| generate_deserialize_field(out, &fval); |
| |
| indent(out) << |
| prefix << "[" << key << "] = " << val << endl; |
| } |
| |
| /** |
| * Read a set element UNUSED |
| */ |
| void t_erl_generator::generate_deserialize_set_element(ostream &out, // TODO |
| t_set* tset, |
| string prefix) { |
| string elem = tmp("_elem"); |
| t_field felem(tset->get_elem_type(), elem); |
| |
| generate_deserialize_field(out, &felem); |
| |
| indent(out) << |
| prefix << "[" << elem << "] = true" << endl; |
| } |
| |
| /** |
| * Read a list element UNUSED |
| */ |
| void t_erl_generator::generate_deserialize_list_element(ostream &out, // TODO |
| t_list* tlist, |
| string prefix) { |
| string elem = tmp("_elem"); |
| t_field felem(tlist->get_elem_type(), elem); |
| |
| generate_deserialize_field(out, &felem); |
| |
| indent(out) << |
| prefix << ".push(" << elem << ")" << endl; |
| } |
| |
| |
| /** |
| * Serializes a field of any type. |
| * |
| * @param tfield The field to serialize |
| * @param prefix Name to prepend to field name |
| */ |
| void t_erl_generator::generate_serialize_field(ostream &out, |
| t_field* tfield, |
| string prefix) { |
| t_type* type = tfield->get_type(); |
| while (type->is_typedef()) { |
| type = ((t_typedef*)type)->get_type(); |
| } |
| |
| // Do nothing for void types |
| if (type->is_void()) { |
| throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + |
| prefix + tfield->get_name(); |
| } |
| |
| if (type->is_struct() || type->is_xception()) { |
| generate_serialize_struct(out, |
| (t_struct*)type, |
| prefix + tfield->get_name()); |
| } else if (type->is_container()) { |
| generate_serialize_container(out, |
| type, |
| prefix + tfield->get_name()); |
| } else if (type->is_base_type() || type->is_enum()) { |
| |
| string name = prefix + tfield->get_name(); |
| |
| indent(out) << |
| "?R1(Oprot, "; |
| |
| if (type->is_base_type()) { |
| t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); |
| switch (tbase) { |
| case t_base_type::TYPE_VOID: |
| throw |
| "compiler error: cannot serialize void field in a struct: " + name; |
| break; |
| case t_base_type::TYPE_STRING: |
| out << "writeString, " << name << "),"; |
| break; |
| case t_base_type::TYPE_BOOL: |
| out << "writeBool, " << name << "),"; |
| break; |
| case t_base_type::TYPE_BYTE: |
| out << "writeByte, " << name << "),"; |
| break; |
| case t_base_type::TYPE_I16: |
| out << "writeI16, " << name << "),"; |
| break; |
| case t_base_type::TYPE_I32: |
| out << "writeI32, " << name << "),"; |
| break; |
| case t_base_type::TYPE_I64: |
| out << "writeI64, " << name << "),"; |
| break; |
| case t_base_type::TYPE_DOUBLE: |
| out << "writeDouble, " << name << "),"; |
| break; |
| default: |
| throw "compiler error: no PHP name for base type " + tbase; |
| } |
| } else if (type->is_enum()) { |
| out << "writeI32, " << name << "),"; |
| } |
| out << "" << endl; |
| } else { |
| printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s%s' TYPE '%s'\n", |
| prefix.c_str(), |
| tfield->get_name().c_str(), |
| type->get_name().c_str()); |
| } |
| } |
| |
| /** |
| * Serializes all the members of a struct. |
| * |
| * @param tstruct The struct to serialize |
| * @param prefix String prefix to attach to all fields |
| */ |
| void t_erl_generator::generate_serialize_struct(ostream &out, |
| t_struct* tstruct, |
| string prefix) { |
| indent(out) << tstruct->get_program()->get_name() << "_types:" << uncapitalize(tstruct->get_name()) << "_write(" << prefix << ", Oprot)," << endl; |
| } |
| |
| void t_erl_generator::generate_serialize_container(ostream &out, // TODO |
| t_type* ttype, |
| string prefix) { |
| if (ttype->is_map()) { |
| indent(out) << |
| "?R3(Oprot, writeMapBegin, " << |
| type_to_enum(((t_map*)ttype)->get_key_type()) << ", " << |
| type_to_enum(((t_map*)ttype)->get_val_type()) << ", thrift_utils:dict_size(" << |
| prefix << "))," << endl; |
| } else if (ttype->is_set()) { |
| indent(out) << |
| "?R2(Oprot, writeSetBegin, " << |
| type_to_enum(((t_set*)ttype)->get_elem_type()) << ", sets:size(" << |
| prefix << "))," << endl; |
| } else if (ttype->is_list()) { |
| indent(out) << |
| "?R2(Oprot, writeListBegin, " << |
| type_to_enum(((t_list*)ttype)->get_elem_type()) << ", length(" << |
| prefix << "))," << endl; |
| } |
| |
| if (ttype->is_map()) { |
| string kiter = tmp("_kiter"); |
| string viter = tmp("_viter"); |
| indent(out) << |
| "dict:fold(fun ("<< kiter << ", " << viter << ",_)->" << endl; |
| indent_up(); |
| generate_serialize_map_element(out, (t_map*)ttype, kiter, viter); |
| indent(out) << "nil" << endl; |
| indent_down(); |
| indent(out) << "end, nil," << prefix << ")," << endl; |
| } else if (ttype->is_set()) { |
| string iter = tmp("_iter"); |
| indent(out) << |
| "sets:fold(fun ("<< iter << ",_)->" << endl; |
| indent_up(); |
| generate_serialize_set_element(out, (t_set*)ttype, iter); |
| indent(out) << "nil" << endl; |
| indent_down(); |
| indent(out) << "end, nil," << prefix << ")," << endl; |
| } else if (ttype->is_list()) { |
| string iter = tmp("_iter"); |
| indent(out) << |
| "lists:foldl(fun (" << iter << ",_)->" << endl; |
| indent_up(); |
| generate_serialize_list_element(out, (t_list*)ttype, iter); |
| indent(out) << "nil" << endl; |
| indent_down(); |
| indent(out) << "end,nil," << prefix << ")," << endl; |
| } |
| |
| if (ttype->is_map()) { |
| indent(out) << |
| "?R0(Oprot, writeMapEnd)," << endl; |
| } else if (ttype->is_set()) { |
| indent(out) << |
| "?R0(Oprot, writeSetEnd)," << endl; |
| } else if (ttype->is_list()) { |
| indent(out) << |
| "?R0(Oprot, writeListEnd)," << endl; |
| } |
| } |
| |
| /** |
| * Serializes the members of a map. |
| * |
| */ |
| void t_erl_generator::generate_serialize_map_element(ostream &out, |
| t_map* tmap, |
| string kiter, |
| string viter) { |
| t_field kfield(tmap->get_key_type(), kiter); |
| generate_serialize_field(out, &kfield, ""); |
| |
| t_field vfield(tmap->get_val_type(), viter); |
| generate_serialize_field(out, &vfield, ""); |
| } |
| |
| /** |
| * Serializes the members of a set. |
| */ |
| void t_erl_generator::generate_serialize_set_element(ostream &out, |
| t_set* tset, |
| string iter) { |
| t_field efield(tset->get_elem_type(), iter); |
| generate_serialize_field(out, &efield, ""); |
| } |
| |
| /** |
| * Serializes the members of a list. |
| */ |
| void t_erl_generator::generate_serialize_list_element(ostream &out, |
| t_list* tlist, |
| string iter) { |
| t_field efield(tlist->get_elem_type(), iter); |
| generate_serialize_field(out, &efield, ""); |
| } |
| |
| /** |
| * Declares a field, which may include initialization as necessary. |
| * |
| * @param ttype The type |
| */ |
| string t_erl_generator::declare_field(t_field* tfield) { // TODO |
| string result = "@" + tfield->get_name(); |
| t_type* type = tfield->get_type(); |
| while (type->is_typedef()) { |
| type = ((t_typedef*)type)->get_type(); |
| } |
| if (tfield->get_value() != NULL) { |
| result += " = " + render_const_value(type, tfield->get_value()); |
| } else { |
| result += " = nil"; |
| } |
| return result; |
| } |
| |
| /** |
| * Renders a function signature of the form 'type name(args)' |
| * |
| * @param tfunction Function definition |
| * @return String of rendered function definition |
| */ |
| string t_erl_generator::function_signature(t_function* tfunction, |
| string prefix) { |
| return |
| prefix + tfunction->get_name() + |
| "(This" + capitalize(argument_list(tfunction->get_arglist())) + ")"; |
| } |
| |
| /** |
| * Add a function to the exports list |
| */ |
| void t_erl_generator::export_string(string name, int num) { |
| if (export_lines_first_) { |
| export_lines_first_ = false; |
| } else { |
| export_lines_ << ", "; |
| } |
| export_lines_ << name << "/" << num; |
| } |
| |
| void t_erl_generator::export_types_function(t_function* tfunction, |
| string prefix) { |
| |
| export_types_string(prefix + tfunction->get_name(), |
| 1 // This |
| + ((tfunction->get_arglist())->get_members()).size() |
| ); |
| } |
| |
| void t_erl_generator::export_types_string(string name, int num) { |
| if (export_types_lines_first_) { |
| export_types_lines_first_ = false; |
| } else { |
| export_types_lines_ << ", "; |
| } |
| export_types_lines_ << name << "/" << num; |
| } |
| |
| void t_erl_generator::export_function(t_function* tfunction, |
| string prefix) { |
| |
| export_string(prefix + tfunction->get_name(), |
| 1 // This |
| + ((tfunction->get_arglist())->get_members()).size() |
| ); |
| } |
| |
| |
| /** |
| * Renders a field list |
| */ |
| string t_erl_generator::argument_list(t_struct* tstruct) { |
| string result = ""; |
| |
| const vector<t_field*>& fields = tstruct->get_members(); |
| vector<t_field*>::const_iterator f_iter; |
| bool first = true; |
| for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { |
| if (first) { |
| first = false; |
| result += ", "; // initial comma to compensate for initial This |
| } else { |
| result += ", "; |
| } |
| result += capitalize((*f_iter)->get_name()); |
| } |
| return result; |
| } |
| |
| string t_erl_generator::type_name(t_type* ttype) { |
| string prefix = ""; |
| t_program* program = ttype->get_program(); |
| if (program != NULL && program != program_) { |
| if (!ttype->is_service()) { |
| prefix = program->get_name() + "_types."; // TODO |
| } |
| } |
| |
| string name = ttype->get_name(); |
| |
| if (ttype->is_struct() || ttype->is_xception() || ttype->is_service()) { |
| name = uncapitalize(ttype->get_name()); |
| } |
| |
| return prefix + name; |
| } |
| |
| /** |
| * Converts the parse type to a Erlang "type" (macro for int constants) |
| */ |
| string t_erl_generator::type_to_enum(t_type* type) { |
| while (type->is_typedef()) { |
| type = ((t_typedef*)type)->get_type(); |
| } |
| |
| if (type->is_base_type()) { |
| t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); |
| switch (tbase) { |
| case t_base_type::TYPE_VOID: |
| throw "NO T_VOID CONSTRUCT"; |
| case t_base_type::TYPE_STRING: |
| return "?tType_STRING"; |
| case t_base_type::TYPE_BOOL: |
| return "?tType_BOOL"; |
| case t_base_type::TYPE_BYTE: |
| return "?tType_BYTE"; |
| case t_base_type::TYPE_I16: |
| return "?tType_I16"; |
| case t_base_type::TYPE_I32: |
| return "?tType_I32"; |
| case t_base_type::TYPE_I64: |
| return "?tType_I64"; |
| case t_base_type::TYPE_DOUBLE: |
| return "?tType_DOUBLE"; |
| } |
| } else if (type->is_enum()) { |
| return "?tType_I32"; |
| } else if (type->is_struct() || type->is_xception()) { |
| return "?tType_STRUCT"; |
| } else if (type->is_map()) { |
| return "?tType_MAP"; |
| } else if (type->is_set()) { |
| return "?tType_SET"; |
| } else if (type->is_list()) { |
| return "?tType_LIST"; |
| } |
| |
| throw "INVALID TYPE IN type_to_enum: " + type->get_name(); |
| } |