[thrift] preliminary Erlang support (initial import)

Summary:
 * missing {list,map,set}s, inheritance is spotty
 * loose source code, plus everything is one process (application / gen_server behavior is forthcoming)
 * codegen is a mess, need t_fp_generator

Test Plan:
 * codegen invoked without -erl generates identical code for test/
 * calculatorHandler plus 'thrift -erl -r tutorial.thrift' more or less works

Revert Plan: ok


git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@665146 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/compiler/cpp/Makefile.am b/compiler/cpp/Makefile.am
index d4c56fd..4c7d15f 100644
--- a/compiler/cpp/Makefile.am
+++ b/compiler/cpp/Makefile.am
@@ -14,7 +14,8 @@
                  src/generate/t_py_generator.cc \
                  src/generate/t_rb_generator.cc \
                  src/generate/t_xsd_generator.cc \
-                 src/generate/t_perl_generator.cc
+                 src/generate/t_perl_generator.cc \
+                 src/generate/t_erl_generator.cc
 
 thrift_CXXFLAGS = -Wall -Isrc
 thrift_LDFLAGS = -Wall
diff --git a/compiler/cpp/src/generate/t_erl_generator.cc b/compiler/cpp/src/generate/t_erl_generator.cc
new file mode 100644
index 0000000..ff2e287
--- /dev/null
+++ b/compiler/cpp/src/generate/t_erl_generator.cc
@@ -0,0 +1,1643 @@
+// 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/
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sstream>
+#include "t_erl_generator.h"
+using namespace std;
+
+
+void hrl_header(std::ostream& out, std::string name);
+void hrl_footer(std::ostream& out, std::string name); 
+
+
+/**
+ * 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);
+
+  export_types_lines_first_ = export_lines_first_ = true;
+
+  // Make output file
+  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");
+
+  string f_consts_name = string(T_ERL_DIR)+"/"+program_name_+"_constants.hrl";
+  f_consts_.open(f_consts_name.c_str());
+
+  // Print header
+  f_types_file_ <<
+    erl_autogen_comment() << endl <<
+    "-module(" << uncapitalize(program_name_) << "_types)." << endl <<
+    erl_imports() << endl;
+
+  f_types_file_ <<
+    "-include(\"" << uncapitalize(program_name_) << "_types.hrl\")." << endl << 
+    endl;
+
+  f_types_hrl_file_ << render_includes() << endl;
+  
+  f_consts_ <<
+    erl_autogen_comment() << endl <<
+    erl_imports() << endl <<
+    "-include(\"" << program_name_ << "_types.hrl\"). % init_generator" << endl <<
+    endl;
+}
+
+void hrl_header(ostream& out, string name) {
+  out << "-ifndef(_" << name << "_included)." << endl <<
+    "-define(_" << name << "_included, yeah)." << endl;
+}
+
+void 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\"). % render_includes\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/protocol/tProtocol.hrl\").\n") + 
+	   "-include(\"thrift/thrift.hrl\").";
+}
+
+/**
+ * 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. This is not done in Ruby, types are all implicit.
+ *
+ * @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) {
+  //f_types_ << "module " << tenum->get_name() << endl;
+  //indent_up();
+  
+  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;
+    }
+    
+    // Ruby class constants have to be capitalized... omg i am so on the fence
+    // about languages strictly enforcing capitalization why can't we just all
+    // agree and play nice.
+    string name = capitalize((*c_iter)->get_name());
+    
+    f_types_hrl_file_ <<
+      indent() << "-define(" << name << ", " << value << ")."<< endl;
+  }
+
+  //  indent_down();
+  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 = tconst->get_name();
+  t_const_value* value = tconst->get_value();
+  
+  name[0] = toupper(name[0]);
+
+  f_consts_ << "-define(" << 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()) { // TODO
+    out << type->get_name() << "({" << endl;
+    indent_up();
+    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;
+    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();
+      }
+      out << indent();
+      out << render_const_value(g_type_string, v_iter->first);
+      out << " => ";
+      out << render_const_value(field_type, v_iter->second);
+      out << "," << endl;
+    }
+    indent_down();
+    indent(out) << "})";
+  } else if (type->is_map()) { // TODO
+    t_type* ktype = ((t_map*)type)->get_key_type();
+    t_type* vtype = ((t_map*)type)->get_val_type();
+    out << "{" << endl;
+    indent_up();
+    const map<t_const_value*, t_const_value*>& val = value->get_map();
+    map<t_const_value*, t_const_value*>::const_iterator v_iter;
+    for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+      out << indent();
+      out << render_const_value(ktype, v_iter->first);
+      out << " => ";
+      out << render_const_value(vtype, v_iter->second);
+      out << "," << endl;
+    }
+    indent_down();
+    indent(out) << "}";
+  } else if (type->is_list() || type->is_set()) { // TODO
+    t_type* etype;
+    if (type->is_list()) {
+      etype = ((t_list*)type)->get_elem_type();
+    } else {
+      etype = ((t_set*)type)->get_elem_type();
+    }
+    if (type->is_set()) {
+      out << "{";
+    } else {
+      out << "[" << endl;
+    }
+    indent_up();
+    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) {
+      out << indent();
+      out << render_const_value(etype, *v_iter);
+      if (type->is_set()) {
+        out << " => true";
+      }
+      out << "," << endl;
+    }
+    indent_down();
+    if (type->is_set()) {
+      indent(out) << "}";
+    } else {
+      indent(out) << "]";
+    }
+  }
+  return out.str();
+}
+
+/**
+ * Generates a ruby 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 ruby 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. This is nothing in PHP
+ * where the objects are all just associative arrays (unless of course we
+ * decide to start using objects for them...)
+ *
+ * @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_up(); // indent removeme
+
+  indent(out) << "%% struct " << type_name(tstruct) << endl;
+
+  if (is_exception) { // TODO
+    out << "% RUBY( < StandardError) % TODO";
+  }
+  out << endl;
+  //  indent_up();
+
+///   out << endl;
+  if (members.size() > 0) {
+    indent(out)     << "% -record(" << uncapitalize(type_name(tstruct)) << ", {";
+    indent(hrl_out) <<   "-record(" << uncapitalize(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(" << uncapitalize(type_name(tstruct)) << ", {}). % empty struct" << endl;
+    indent(hrl_out) <<   "-record(" << uncapitalize(type_name(tstruct)) << ", {}). % empty struct" << endl;
+  }
+  
+    // no constructors : use #myStruct{} and #myStruct{field=Val}
+    
+///     out <<
+///       indent() << "" << uncapitalize(type_name(tstruct)) << "(d=nil)" << endl;
+///   indent_up();
+/// 
+///   if (members.size() > 0) {
+///     for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+///       if ((*m_iter)->get_value() != NULL) {
+///         indent(out) << declare_field(*m_iter) << endl;
+///       }
+///     }
+///     indent(out) <<
+///       "if (d != nil)" << endl;
+///     indent_up();
+///     for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+///       out <<
+///         indent() << "if (d.has_key?('" << (*m_iter)->get_name() << "'))" << endl <<
+///         indent() << "  @" << (*m_iter)->get_name() << " = d['" << (*m_iter)->get_name() << "']" << endl <<
+///         indent() << "end" << endl;
+///     }
+///     indent_down();
+///     indent(out) << "end" << endl;
+///   }
+///   
+///   indent_down();
+///   indent(out) << "end" << endl;
+///  
+///   out << endl;
+/// 
+
+  out << endl;
+  hrl_out << endl;
+
+
+  generate_erl_struct_reader(out, tstruct);
+  generate_erl_struct_writer(out, tstruct);
+
+  //indent_down();
+  //  indent(out) << "end" << endl << endl;
+}
+
+/**
+ * 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 = uncapitalize(type_name(tstruct)) + "_read";
+
+  if(out == f_types_) { // TODO(cpiro): omfg please ftlog do this right
+    export_types_string(name, 1);
+  } else {
+    export_string(name, 1);
+  }
+
+  indent(out) << name << "(Iprot) ->" << endl;
+  indent_up();
+    
+  out <<
+    indent() << "?M0(Iprot, readStructBegin)," << endl <<   
+    indent() << "Str = " << uncapitalize(type_name(tstruct)) << "_read_loop(Iprot, ";
+
+  //  if (fields.size() > 0) { // cpiro: sensible default for non-empty structs
+    out << "#" << uncapitalize(type_name(tstruct)) << "{}";
+    //  } else {
+    //    out << "empty_struct";
+    //  }
+
+  out << ")," << endl <<
+    indent() << "?M0(Iprot, readStructEnd)," << endl <<
+    indent() << "Str." << endl;
+  
+  indent_down();
+
+  indent(out) <<
+    "" << uncapitalize(type_name(tstruct)) << "_read_loop(Iprot, Str) ->" << endl;
+  indent_up();
+
+    // Read beginning field marker
+    indent(out) <<
+      "{ Fname, Ftype, Fid } = ?M0(Iprot, readFieldBegin)," << endl <<
+      indent() << "Fid, Fname, % 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;
+
+    //      indent() << "true ->" << endl;
+
+    //    if (fields.size() > 0) {
+    //      indent(out) <<
+    //        "end" << endl;
+    //    }
+  
+    // Switch statement on the field we are reading
+    //bool first = true;
+    
+    // Generate deserialization code for known cases
+    for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+      //      if (first) {
+      //        first = false;
+      //        out <<
+      //          indent() << "if ";
+      //      } else {
+      //        out <<
+      //          indent() << "elsif ";
+      //      }
+      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() << "?M0(Iprot, readFieldEnd)," << endl
+	  << indent() << uncapitalize(type_name(tstruct)) << "_read_loop(Iprot, "
+	  << "Str#" << uncapitalize(type_name(tstruct))
+	  << "{" << (*f_iter)->get_name()
+	  << "=Val});" << endl;
+      indent_down();
+    }
+    
+    // In the default case we skip the field
+    out <<
+      indent() << "true -> " << endl <<
+      indent() << "  ?M1(Iprot, skip, Ftype)," << endl <<
+      indent() << "  ?M0(Iprot, readFieldEnd)," << endl <<
+      indent() << "  " << uncapitalize(type_name(tstruct)) << "_read_loop(Iprot, Str)" << endl;
+    indent_down();
+    indent(out) << "end." << endl; 
+      
+    // Read field end marker
+    //    indent(out) <<
+    //"iprot.readFieldEnd()" <<endl;
+    
+    //    indent_down();
+    //    indent(out) << "end" << endl;
+
+    //    indent(out) <<
+    //"iprot.readStructEnd()" << endl;
+
+    indent_down();
+    //    indent(out) << "end" << endl;
+  out << endl;
+}
+
+void t_erl_generator::generate_erl_struct_writer(ostream& out, // TODO
+                                               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 = uncapitalize(type_name(tstruct)) + "_write";
+
+  if(out == f_types_) { // TODO(cpiro): omfg please ftlog do this right
+    export_types_string(fname, 2);
+  } else {
+    export_string(fname, 2);
+  }
+  
+  indent(out) << fname << "(Str, Oprot) -> %xy" << endl;
+  indent_up();
+  
+  indent(out) <<
+    "Str, % suppress unused warnings" << endl << 
+    indent() << "?M1(Oprot, writeStructBegin, \"" << name << "\")," << endl;
+
+
+  string prefix = string("Str#") + uncapitalize(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) <<
+      "?M3(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) <<
+      "?M0(Oprot, writeFieldEnd);" << endl <<
+      indent() << "true -> ok" << endl;
+
+    indent_down();
+    out << "  end," << endl;
+  }
+
+  // Write the struct map
+  out <<
+    indent() << "?M0(Oprot, writeFieldStop)," << endl <<
+    indent() << "?M0(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) {
+  string f_service_hrl_name = string(T_ERL_DIR)+"/"+uncapitalize(service_name_)+".hrl";
+  string f_service_name = string(T_ERL_DIR)+"/"+uncapitalize(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_, uncapitalize(service_name_));
+
+  if (tservice->get_extends() != NULL) {
+    f_service_hrl_ << "-include(\"" << 
+      uncapitalize(tservice->get_extends()->get_name()) << ".hrl\"). % inherit " << endl;
+  }
+
+  f_service_hrl_ <<
+    "-include(\"" << uncapitalize(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(" << uncapitalize(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() << "]).";
+
+  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 << 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) -> % generate_service_client" << endl <<
+    indent() << "  #"<<uncapitalize(service_name_)<<"{iprot=Iprot, oprot=Oprot, seqid=0}." << endl <<
+    indent() << "new(Iprot) ->" << endl <<
+    indent() << "  #"<<uncapitalize(service_name_)<<"{iprot=Iprot, oprot=Iprot, seqid=0}." << endl << endl;
+
+  //  indent(f_service_) << "end" << endl << endl;
+
+  f_service_hrl_ <<
+    indent() << "-record("<< uncapitalize(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) << " -> % generate_service_client (call)" << 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) << " -> % generate_service_client (send)" << endl;
+    indent_up();
+
+      std::string argsname = capitalize((*f_iter)->get_name() + "_args");
+
+      // Serialize the request header
+      f_service_ <<
+	indent() << "Oprot = ?ATTR(oprot)," << endl <<
+	indent() << "?M3(Oprot, writeMessageBegin, \"" << (*f_iter)->get_name() << "\", ?tMessageType_CALL, ?ATTR(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() << "?M0(Oprot, writeMessageEnd)," << endl <<
+	indent() << "%% side-effect: not gonna happen" << endl <<
+	indent() << "%% ?M0(?M0(Oprot, trans), 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) << " -> % generate_service_client (recv)" << endl;
+      indent_up();
+
+      // TODO(mcslee): Validate message reply here, seq ids etc.
+      // TODO(cpiro): actually raise an Erlang exception?
+
+      f_service_ <<
+	indent() << "Iprot = ?ATTR(iprot)," << endl <<
+	indent() << "{ Fname, Mtype, Rseqid } = ?M0(Iprot, readMessageBegin)," << endl <<
+	indent() << "Fname, Rseqid, % suppress unused warnings" << endl <<
+	indent() << "if" << endl <<
+	indent() << "  Mtype == ?tMessageType_EXCEPTION ->" << endl <<
+	indent() << "    X = tApplicationException:new()," << endl <<
+	indent() << "    ?M1(X, read, Iprot), " << endl <<
+	indent() << "    ?M0(Iprot, readMessageEnd), " << endl <<
+	indent() << "    {error, X};" << endl <<
+	indent() << "  true ->" << endl <<
+	indent() << "    Result = " << resultname << "_read(Iprot)," << endl <<
+	indent() << "    ?M0(Iprot, readMessageEnd)," << endl <<
+	indent() << "    if % time to figure out retval" << endl;
+
+	//	indent() << "	 {ok, Success} % result.success casing? Success even for void?" << endl <<
+	//	indent() << "end" << endl;
+
+      // WATCH cpiro
+      // Careful, only return _result if not a void function
+
+      std::string result = "Result#"+resultname+".";
+      if (!(*f_iter)->get_returntype()->is_void()) {
+	f_service_ <<
+	  indent() << "      " << result << "success /= nil ->" << endl << 
+	  indent() << "        {ok, " << 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() << "        {error, " << result << (*x_iter)->get_name() << "};" << endl;
+      }
+
+      // Careful, only return _result if not a void function
+      if ((*f_iter)->get_returntype()->is_void()) {
+        indent(f_service_) <<
+	  indent() << "      Result, % suppress unused warnings" << endl <<
+	  indent() << "      true -> {ok, nil}" << endl <<
+	  indent() << "    end" << endl;
+      } else {
+        f_service_ <<
+          indent() << "      true -> " << endl <<
+	  indent() << "        {error, 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 = " RUBY(< " + extends + "::Processor) "; // TODO(cpiro)
+  }
+
+  // Generate the header portion
+  indent(f_service_) <<
+    "%% processor" << extends_processor << endl;
+
+  indent_up();
+
+  //  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;
+*/
+  indent(f_service_) <<
+    "% initialize wuz here" << endl << endl;
+ 
+  export_string("process", 3);
+
+  // Generate the server implementation
+  indent(f_service_) <<
+    "process(HandlerModule, Iprot, Oprot) ->" << endl;
+  indent_up();
+
+  f_service_ <<
+    indent() << "{ Name, Type, Seqid } = ?M0(Iprot, readMessageBegin)," << endl <<
+    indent() << "Type, % suppress unused warnings" << endl;
+
+  // 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_) << "  %% TODO(cpiro): pass to super" << endl;
+    indent(f_service_) << "  _ -> % unknown function" << endl <<
+    indent() << "    ?M1(Iprot, skip, ?tType_STRUCT)," << endl <<
+    indent() << "    ?M0(Iprot, readMessageEnd)," << endl <<
+    indent() << "    X = tApplicationException:new(?tApplicationException_UNKNOWN_METHOD, \"Unknown function \" ++ Name)," << endl <<
+    indent() << "    ?M3(Oprot, writeMessageBegin, Name, ?tMessageType_EXCEPTION, Seqid)," << endl <<
+    indent() << "    ?M1(X, write, Oprot)," << endl <<
+    indent() << "    ?M0(Oprot, writeMessageEnd)," << endl <<
+    indent() << "    %% side-effect: not gonna happen" << endl <<
+    indent() << "    %% ?M0(?M0(Oprot, trans), flush)," << endl <<
+    indent() << "    {error, X} % what's the retval in this case?" << endl <<
+    indent() << "end." << endl;
+      //    indent() << "ok. % this one?" << endl << endl;
+  /*
+    indent() << "if (@processMap.has_key?(name))" << endl <<
+    indent() << "  @processMap[name].call(seqid, iprot, oprot)" << endl <<
+    indent() << "else" << endl <<
+    indent() << "  iprot.skip(TType::STRUCT)" << endl <<
+    indent() << "  iprot.readMessageEnd()" << endl <<
+    indent() << "  x = TApplicationException.new(TApplicationException::UNKNOWN_METHOD, 'Unknown function '+name)" << endl <<
+    indent() << "  oprot.writeMessageBegin(name, TMessageType::EXCEPTION, seqid)" << endl <<
+    indent() << "  x.write(oprot)" << endl <<
+    indent() << "  oprot.writeMessageEnd()" << endl <<
+    indent() << "  oprot.trans.flush()" << endl <<
+    indent() << "  return" << endl <<
+    indent() << "end" << endl;
+  */
+
+  // Read end of args field, the T_STOP, and the struct close
+  //  f_service_ <<
+  //    indent() << "return true" << endl;
+
+  indent_down();
+  //  indent(f_service_) << "end" << endl << endl;
+
+  // 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) { // TODO(cpiro)
+
+  string name = "process_" + tfunction->get_name();
+  
+  export_string(name, 4);
+
+  // Open function
+  indent(f_service_) <<
+    name <<
+    "(HandlerModule, Seqid, Iprot, Oprot) ->" << endl;
+  indent_up();
+
+  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() << "?M0(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()) {
+  }
+
+
+  // Try block for a function with exceptions
+  //  if (xceptions.size() > 0) { // TODO(cpiro)
+  //    f_service_ <<
+  //      indent() << "try" << endl;
+  //    indent_up();
+  //  }
+ 
+  // 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;
+
+  f_service_ << indent();
+  f_service_ <<
+    "Retval = 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;
+
+  indent(f_service_) << "Result = case Retval of" << endl;
+  indent_up();
+  indent(f_service_) << "{ok, Value} ->" << endl;
+  indent_up();
+  indent(f_service_) << "Value, % suppress unused warnings" << endl;
+
+  if (!tfunction->is_async() && !tfunction->get_returntype()->is_void()) {
+    indent(f_service_) << "#" << resultname << "{success=Value};" << endl;
+  } else{
+    indent(f_service_) << "#" << resultname << "{};" << endl;
+  }
+  indent_down();
+
+  if (!tfunction->is_async() && xceptions.size() > 0) {
+    indent(f_service_) << "{error, E} ->" << endl;
+    indent_up();
+    indent(f_service_) << "if" << endl;
+
+    for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+      f_service_ << "";
+	//        indent() << "{error, ?" << (*x_iter)->get_type()->get_name() << "} -> " << "% TODO investigate this" << endl;
+
+	//"" << (*x_iter)->get_type()->get_name() << "} -> " << "% TODO investigate this" << endl;
+
+      if (!tfunction->is_async()) {
+        indent_up();
+        f_service_ <<
+          indent() << "is_record(E, " << uncapitalize((*x_iter)->get_type()->get_name()) << ") -> #" <<
+	  resultname << "{" << (*x_iter)->get_name() << " = E};" << endl;
+	// RUBY(Result.oops = 
+        indent_down();
+      }
+      indent_up();
+      indent(f_service_) << "true -> throw(cpiro_if_nomatch) % TODO(cpiro): spirit foul" << endl;
+      indent_down();
+    }
+    indent(f_service_) << "end;" << endl;
+    indent_down();
+  }
+  indent(f_service_) << "dummy -> throw(cpiro_case_nomatch) % TODO(cpiro): gross" << endl;
+  indent_down();
+  indent(f_service_) << "end," << endl;
+
+  //indent_down();
+
+  if (tfunction->is_async()) { 
+    indent(f_service_) << "% async" << endl;
+  } else {
+    f_service_ <<
+      indent() << "?M3(Oprot, writeMessageBegin, \"" << tfunction->get_name() << "\", ?tMessageType_REPLY, Seqid)," << endl <<
+      indent() << tfunction->get_name() << "_result_write(Result, Oprot)," << endl <<
+      indent() << "?M0(Oprot, writeMessageEnd)," << endl <<
+      indent() << "%% side-effect: not gonna happen" << endl <<
+      indent() << "%% ?M0(?M0(Oprot, trans), flush)," << endl;
+  }
+  
+  indent(f_service_) << "Result." << endl << endl;
+
+  // Close function
+  indent_down();
+  //  indent(f_service_);
+}
+
+/**
+ * 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 << " = iprot.";
+      name << " = ?M0(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 << "), % generate_deserialize_field" << 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, // TODO
+                                                  t_struct* tstruct,
+                                                  string prefix) {
+  out <<
+    //    indent() << prefix << " = " << type_name(tstruct) << ":new()" << endl <<
+    //    indent() << prefix << ".read(iprot)" << endl;
+    indent() << prefix << " = " << (tstruct->get_program())->get_name() << "_types:" << uncapitalize(type_name(tstruct)) << "_read(Iprot), % generate_deserialize_struct" << 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, // TODO
+                                                    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()) {
+    out <<
+      indent() << prefix << " = {}" << endl <<
+      indent() << "(" << ktype << ", " << vtype << ", " << size << " ) = iprot.readMapBegin() " << endl;
+  } else if (ttype->is_set()) {
+    out <<
+      indent() << prefix << " = {}" << endl <<
+      indent() << "(" << etype << ", " << size << ") = iprot.readSetBegin()" << endl;
+  } else if (ttype->is_list()) {
+    out <<
+      indent() << prefix << " = []" << endl <<
+      indent() << "(" << etype << ", " << size << ") = iprot.readListBegin()" << endl;
+  }
+
+  // For loop iterates over elements
+  string i = tmp("_i");
+  indent(out) <<
+    "for " << i << " in (1.." << size << ")" << endl;
+  
+    indent_up();
+    
+    if (ttype->is_map()) {
+      generate_deserialize_map_element(out, (t_map*)ttype, prefix);
+    } else if (ttype->is_set()) {
+      generate_deserialize_set_element(out, (t_set*)ttype, prefix);
+    } else if (ttype->is_list()) {
+      generate_deserialize_list_element(out, (t_list*)ttype, prefix);
+    }
+    
+    indent_down();
+  indent(out) << "end" << endl;
+
+  // Read container end
+  if (ttype->is_map()) {
+    indent(out) << "iprot.readMapEnd()" << endl;
+  } else if (ttype->is_set()) {
+    indent(out) << "iprot.readSetEnd()" << endl;
+  } else if (ttype->is_list()) {
+    indent(out) << "iprot.readListEnd()" << endl;
+  }
+}
+
+
+/**
+ * Generates code to deserialize a map
+ */
+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;
+}
+
+/**
+ * Write a set element
+ */
+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;
+}
+
+/**
+ * Write a list element
+ */
+void t_erl_generator::generate_deserialize_list_element(ostream &out,
+                                                        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) <<
+      "?M1(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 << " % generate_serialize_field" << 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), % generate_serialize_struct" << endl;
+  //  indent(out) <<
+  //    "?M1(" << prefix << ", write, Oprot), % generate_serialize_struct" << endl;
+}
+
+void t_erl_generator::generate_serialize_container(ostream &out,
+                                                  t_type* ttype,
+                                                  string prefix) {
+  if (ttype->is_map()) {
+    indent(out) <<
+      "?M3(Oprot, writeMapBegin, " <<
+      type_to_enum(((t_map*)ttype)->get_key_type()) << ", " <<
+      type_to_enum(((t_map*)ttype)->get_val_type()) << ", length(" <<
+      prefix << ")), % generate_serialize_container" << endl;
+  } else if (ttype->is_set()) {
+    indent(out) <<
+      "?M2(Oprot, writeSetBegin, " <<
+      type_to_enum(((t_set*)ttype)->get_elem_type()) << ", length(" <<
+      prefix << ")), % generate_serialize_container" << endl;
+  } else if (ttype->is_list()) {
+    indent(out) <<
+      "?M2(Oprot, writeListBegin, " <<
+      type_to_enum(((t_list*)ttype)->get_elem_type()) << ", length(" <<
+      prefix << ")), % generate_serialize_container" << endl;
+  }
+
+  if (ttype->is_map()) { // TODO
+    string kiter = tmp("kiter");
+    string viter = tmp("viter");
+    indent(out) << 
+      prefix << ".each do |" << kiter << ", " << viter << "|" << endl;
+    indent_up();
+    generate_serialize_map_element(out, (t_map*)ttype, kiter, viter);
+    indent_down();
+    indent(out) << "end" << endl;
+  } else if (ttype->is_set()) {
+    string iter = tmp("iter");
+    string t = tmp("true");
+    indent(out) << 
+      prefix << ".each do |" << iter << ", " << t << "|" << endl;
+    indent_up();
+    generate_serialize_set_element(out, (t_set*)ttype, iter);
+    indent_down();
+    indent(out) << "end" << endl;
+  } else if (ttype->is_list()) {
+    string iter = tmp("iter");
+    indent(out) << 
+      prefix << ".each do |" << iter << "|" << endl;
+    indent_up();
+    generate_serialize_list_element(out, (t_list*)ttype, iter);
+    indent_down();
+    indent(out) << "end" << endl;
+  }
+    
+  if (ttype->is_map()) {
+    indent(out) <<
+      "?M0(Oprot, writeMapEnd), % generate_serialize_container" << endl;
+  } else if (ttype->is_set()) {
+    indent(out) <<
+      "?M0(Oprot, writeSetEnd), % generate_serialize_container" << endl;
+  } else if (ttype->is_list()) {
+    indent(out) <<
+      "?M0(Oprot, writeListEnd), % generate_serialize_container" << endl;
+  }
+}
+
+/**
+ * Serializes the members of a map.
+ *
+ */
+void t_erl_generator::generate_serialize_map_element(ostream &out, // TODO
+                                                     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, // TODO
+                                                     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, // TODO
+                                                      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) {
+  // TODO(mcslee): Nitpicky, no ',' if argument_list is empty
+  // WATCH cpiro: do we really hardcode This into them all?
+  return
+    prefix + tfunction->get_name() +
+    "(This" +  capitalize(argument_list(tfunction->get_arglist())) + ")";
+}
+
+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) { // TODO?
+  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 += ", ";   // WATCH cpiro: 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) { // TODO
+  string prefix = "";
+  t_program* program = ttype->get_program();
+  if (program != NULL && program != program_) {
+    if (!ttype->is_service()) {
+      prefix = program->get_name() + "_types.";
+    }
+  }
+
+  string name = ttype->get_name();
+  if (ttype->is_struct() || ttype->is_xception()) {
+    name = capitalize(ttype->get_name());
+  }
+
+  return prefix + name;
+}
+
+/**
+ * Converts the parse type to a Ruby tyoe
+ */
+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();
+}
+
+//  LocalWords:  Iprot
diff --git a/compiler/cpp/src/generate/t_erl_generator.h b/compiler/cpp/src/generate/t_erl_generator.h
new file mode 100644
index 0000000..d54514e
--- /dev/null
+++ b/compiler/cpp/src/generate/t_erl_generator.h
@@ -0,0 +1,174 @@
+#ifndef T_ERL_GENERATOR_H
+#define T_ERL_GENERATOR_H
+
+#include <string>
+#include <fstream>
+#include <iostream>
+#include <vector>
+
+#include "t_oop_generator.h"
+
+#define T_ERL_DIR "gen-erl"
+
+/**
+ * Erlang code generator.
+ *
+ * @author
+ */
+class t_erl_generator : public t_oop_generator {
+ public:
+  t_erl_generator(t_program* program) :
+    t_oop_generator(program) {}
+
+  /**
+   * Init and close methods
+   */
+
+  void init_generator();
+  void close_generator();
+
+  /**
+   * Program-level generation functions
+   */
+
+  void generate_typedef  (t_typedef*  ttypedef);
+  void generate_enum     (t_enum*     tenum);
+  void generate_const    (t_const*    tconst);
+  void generate_struct   (t_struct*   tstruct);
+  void generate_xception (t_struct*   txception);
+  void generate_service  (t_service*  tservice);
+
+  std::string render_const_value(t_type* type, t_const_value* value);
+
+  /**
+   * Struct generation code
+   */
+
+  void generate_erl_struct(t_struct* tstruct, bool is_exception);
+  void generate_erl_struct_definition(std::ostream& out, std::ostream& hrl_out, t_struct* tstruct, bool is_xception=false, bool is_result=false);
+  void generate_erl_struct_reader(std::ostream& out, t_struct* tstruct);
+  void generate_erl_struct_writer(std::ostream& out, t_struct* tstruct);
+  void generate_erl_function_helpers(t_function* tfunction);
+
+  /**
+   * Service-level generation functions
+   */
+
+  void generate_service_helpers   (t_service*  tservice);
+  void generate_service_interface (t_service* tservice);
+  void generate_service_client    (t_service* tservice);
+  void generate_service_server    (t_service* tservice);
+  void generate_process_function  (t_service* tservice, t_function* tfunction);
+
+  /**
+   * Serialization constructs
+   */
+
+  void generate_deserialize_field        (std::ostream &out,
+                                          t_field*    tfield, 
+                                          std::string prefix="",
+                                          bool inclass=false);
+  
+  void generate_deserialize_struct       (std::ostream &out,
+                                          t_struct*   tstruct,
+                                          std::string prefix="");
+  
+  void generate_deserialize_container    (std::ostream &out,
+                                          t_type*     ttype,
+                                          std::string prefix="");
+  
+  void generate_deserialize_set_element  (std::ostream &out,
+                                          t_set*      tset,
+                                          std::string prefix="");
+
+  void generate_deserialize_map_element  (std::ostream &out,
+                                          t_map*      tmap,
+                                          std::string prefix="");
+
+  void generate_deserialize_list_element (std::ostream &out,
+                                          t_list*     tlist,
+                                          std::string prefix="");
+
+  void generate_serialize_field          (std::ostream &out,
+                                          t_field*    tfield,
+                                          std::string prefix="");
+
+  void generate_serialize_struct         (std::ostream &out,
+                                          t_struct*   tstruct,
+                                          std::string prefix="");
+
+  void generate_serialize_container      (std::ostream &out,
+                                          t_type*     ttype,
+                                          std::string prefix="");
+
+  void generate_serialize_map_element    (std::ostream &out,
+                                          t_map*      tmap,
+                                          std::string kiter,
+                                          std::string viter);
+
+  void generate_serialize_set_element    (std::ostream &out,
+                                          t_set*      tmap,
+                                          std::string iter);
+
+  void generate_serialize_list_element   (std::ostream &out,
+                                          t_list*     tlist,
+                                          std::string iter);
+
+  /**
+   * Helper rendering functions
+   */
+
+  std::string erl_autogen_comment();
+  std::string erl_imports();
+  std::string render_includes();
+  std::string declare_field(t_field* tfield);
+  std::string type_name(t_type* ttype);
+  std::string function_signature(t_function* tfunction, std::string prefix="");
+
+  std::string argument_list(t_struct* tstruct);
+  std::string type_to_enum(t_type* ttype);
+
+  std::string capitalize(std::string in) {
+    in[0] = toupper(in[0]);
+    return in;
+  }
+
+  std::string uncapitalize(std::string in) {
+    in[0] = tolower(in[0]);
+    return in;
+  }
+
+ private:
+
+  bool export_lines_first_;
+  std::ostringstream export_lines_;
+
+  bool export_types_lines_first_;
+  std::ostringstream export_types_lines_;
+
+  // f_types_
+  // f_consts_
+  // f_service_
+
+  /**
+   * File streams
+   */
+
+  void export_function(t_function* tfunction, std::string prefix="");
+  void export_string(std::string name, int num);
+
+  void export_types_function(t_function* tfunction, std::string prefix="");
+  void export_types_string(std::string name, int num);
+
+  std::ostringstream f_types_;
+  std::ofstream f_types_file_;
+  std::ofstream f_types_hrl_file_;
+
+  std::ofstream f_consts_; 
+  std::ostringstream f_service_;
+  std::ofstream f_service_file_;
+  std::ofstream f_service_hrl_;
+
+};
+
+#endif
diff --git a/compiler/cpp/src/main.cc b/compiler/cpp/src/main.cc
index c62c925..41305d5 100644
--- a/compiler/cpp/src/main.cc
+++ b/compiler/cpp/src/main.cc
@@ -35,6 +35,7 @@
 #include "generate/t_rb_generator.h"
 #include "generate/t_xsd_generator.h"
 #include "generate/t_perl_generator.h"
+#include "generate/t_erl_generator.h"
 
 using namespace std;
 
@@ -124,6 +125,7 @@
 bool gen_php = false;
 bool gen_phpi = false;
 bool gen_perl = false;
+bool gen_erl = false;
 bool gen_recurse = false;
 
 /**
@@ -301,6 +303,7 @@
   fprintf(stderr, "  -rb         Generate Ruby output files\n");
   fprintf(stderr, "  -xsd        Generate XSD output files\n");
   fprintf(stderr, "  -perl       Generate Perl output files\n");
+  fprintf(stderr, "  -erl        Generate Erlang output files\n");
   fprintf(stderr, "  -I dir      Add a directory to the list of directories \n");
   fprintf(stderr, "                searched for include directives\n");
   fprintf(stderr, "  -nowarn     Suppress all compiler warnings (BAD!)\n");
@@ -563,6 +566,13 @@
       delete perl;
     }
 
+    if (gen_erl) {
+      pverbose("Generating Erlang\n");
+      t_erl_generator* erl = new t_erl_generator(program);
+      erl->generate_program();
+      delete erl;
+    }
+
   } catch (string s) {
     printf("Error: %s\n", s.c_str());
   } catch (const char* exc) {
@@ -625,6 +635,8 @@
         gen_xsd = true;
       } else if (strcmp(arg, "-perl") == 0) {
         gen_perl = true;
+      } else if (strcmp(arg, "-erl") == 0) {
+        gen_erl = true;
       } else if (strcmp(arg, "-I") == 0) {
         // An argument of "-I\ asdf" is invalid and has unknown results
         arg = argv[++i];
@@ -645,7 +657,7 @@
   }
 
   // You gotta generate something!
-  if (!gen_cpp && !gen_java && !gen_php && !gen_phpi && !gen_py && !gen_rb && !gen_xsd && !gen_perl) {
+  if (!gen_cpp && !gen_java && !gen_php && !gen_phpi && !gen_py && !gen_rb && !gen_xsd && !gen_perl && !gen_erl) {
     fprintf(stderr, "!!! No output language(s) specified\n\n");
     usage();
   }