blob: 5779078b796b027fefcab32b87f7f3e961cf7328 [file] [log] [blame]
// 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();
}