[thrift] maps, lists, sets, and service inheritance for Erlang

Reviewed by: cpiro

Test Plan: tested wit tutorial/tutorial.thrift

Revert Plan: ok


git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@665175 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/compiler/cpp/src/generate/t_erl_generator.cc b/compiler/cpp/src/generate/t_erl_generator.cc
index ba66247..5779078 100644
--- a/compiler/cpp/src/generate/t_erl_generator.cc
+++ b/compiler/cpp/src/generate/t_erl_generator.cc
@@ -205,13 +205,15 @@
     }
   } 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();
+
+  } 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) {
@@ -222,59 +224,74 @@
       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 << " => ";
+
+      if (first) {
+	first = false;
+      } else {
+	out << ",";
+      }
+      out << v_iter->first->get_string();
+      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
+
+  } 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;
-    if (type->is_list()) {
-      etype = ((t_list*)type)->get_elem_type();
-    } else {
-      etype = ((t_set*)type)->get_elem_type();
+    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)";
     }
-    if (type->is_set()) {
-      out << "{";
-    } else {
-      out << "[" << endl;
-    }
-    indent_up();
+    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) {
-      out << indent();
-      out << render_const_value(etype, *v_iter);
-      if (type->is_set()) {
-        out << " => true";
+      if (first) {
+	first=false;
+      } else {
+	out << ",";
       }
-      out << "," << endl;
+      out << render_const_value(etype, *v_iter);
     }
-    indent_down();
-    if (type->is_set()) {
-      indent(out) << "}";
-    } else {
-      indent(out) << "]";
-    }
+    out << "]";
   }
   return out.str();
 }
@@ -364,7 +381,7 @@
 
   string name = type_name(tstruct) + "_read";
 
-  if(out == f_types_) { // OH HAI MR. HORRIBLE
+  if (out == f_types_) { // OH HAI MR. HORRIBLE
     export_types_string(name, 1);
   } else {
     export_string(name, 1);
@@ -379,11 +396,10 @@
 
   // empty struct
   out << "#" << type_name(tstruct) << "{}";
-
   out << ")," << endl <<
     indent() << "?R0(Iprot, readStructEnd)," << endl <<
     indent() << "Str." << endl;
-  
+
   indent_down();
 
   indent(out) <<
@@ -439,7 +455,7 @@
 
   string fname = type_name(tstruct) + "_write";
 
-  if(out == f_types_) { // OH HAI MR. HORRIBLE
+  if (out == f_types_) { // OH HAI MR. HORRIBLE
     export_types_string(fname, 2);
   } else {
     export_string(fname, 2);
@@ -453,7 +469,6 @@
     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) <<
@@ -746,7 +761,7 @@
 	indent() << "    X = tApplicationException:new()," << endl <<
 	indent() << "    tApplicationException:read(X, Iprot)," << endl <<
 	indent() << "    ?R0(Iprot, readMessageEnd), " << endl <<
-	indent() << "    {error, X};" << endl <<
+	indent() << "    throw(X);" << endl <<
 	indent() << "  true ->" << endl <<
 	indent() << "    Result = " << resultname << "_read(Iprot)," << endl <<
 	indent() << "    ?R0(Iprot, readMessageEnd)," << endl <<
@@ -761,7 +776,7 @@
       if (!(*f_iter)->get_returntype()->is_void()) {
 	f_service_ <<
 	  indent() << "      " << result << "success /= nil ->" << endl << 
-	  indent() << "        {ok, " << result << "success};" << endl;
+	  indent() << "        " << result << "success;" << endl;
       }
 
       t_struct* xs = (*f_iter)->get_xceptions(); // TODO(cpiro)
@@ -770,19 +785,18 @@
       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;
+          indent() << "        throw(" << 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," << endl <<
-	  indent() << "      true -> {ok, nil}" << endl <<
+        f_service_ <<
+	  indent() << "      true -> 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() << "        throw(tApplicationException:new(?tApplicationException_MISSING_RESULT, \"" << (*f_iter)->get_name() << " failed: unknown result\"))" << endl << 
 	  indent() << "    end" << endl;
       }     
       
@@ -847,6 +861,7 @@
   */
 
   export_string("process", 3);
+  export_string("proc", 6);
 
   // Generate the server implementation
   indent(f_service_) <<
@@ -854,7 +869,13 @@
   indent_up();
 
   f_service_ <<
-    indent() << "{ Name, _Type, Seqid } = ?R0(Iprot, readMessageBegin)," << endl;
+    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
 
@@ -866,35 +887,24 @@
   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_) << "  _UnknownFunction ->" << endl <<
-    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 <<
-    indent() << "end." << 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;
-  */
+  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();
 
@@ -945,21 +955,24 @@
   if (!tfunction->is_async()) {
   }
 
-  // Try block for a function with exceptions
-  //  if (xceptions.size() > 0) {
-  //    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;
 
+  indent(f_service_) << "Result = ";
+  if (xceptions.size() > 0) {
+    f_service_ << "try" << endl;
+  } else {
+    f_service_ << "begin" << endl;
+  }
+  indent_up();
+
   f_service_ << indent();
-  f_service_ <<
-    "Retval = HandlerModule:" << tfunction->get_name() << "(";
+  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) {
@@ -971,33 +984,24 @@
     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;
+    indent(f_service_) << "#" << resultname << "{success=Res}" << endl;
   } else{
-    indent(f_service_) << "#" << resultname << "{};" << endl;
+    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_) << "{error, E} when is_record(E, " << uncapitalize((*x_iter)->get_type()->get_name()) << ") ->" << endl;
+      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_) << "dummy -> dummy % TODO: only for the semicolon's sake" << endl;
-  indent_down();
   indent(f_service_) << "end," << endl;
 
   if (tfunction->is_async()) { 
@@ -1101,7 +1105,7 @@
  * 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
+void t_erl_generator::generate_deserialize_container(ostream &out,
                                                     t_type* ttype,
                                                     string prefix) {
   string size = tmp("_size");
@@ -1116,50 +1120,66 @@
 
   // 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;
-  }
+    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);
 
-  // For loop iterates over elements
-  string i = tmp("_i");
-  indent(out) <<
-    "for " << i << " in (1.." << size << ")" << endl;
-  
+    out <<
+      indent() << "{" << ktype << ", " << vtype << ", " << size << " } = ?R0(Iprot,readMapBegin)," << endl;
+    out <<
+      indent() << prefix << " = dict:from_list(thrift_utils:tabulate(" << 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);
-    }
-    
+    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();
-  indent(out) << "end" << endl;
+    out << indent() << "end))," << endl;
+    indent_down();
+    out << indent() << "?R0(Iprot,readMapEnd)," << endl;
 
-  // Read container end
-  if (ttype->is_map()) {
-    indent(out) << "iprot.readMapEnd()" << endl;
   } else if (ttype->is_set()) {
-    indent(out) << "iprot.readSetEnd()" << endl;
+    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()) {
-    indent(out) << "iprot.readListEnd()" << endl;
-  }
+    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
+ * Generates code to deserialize a map UNUSED
  */
 void t_erl_generator::generate_deserialize_map_element(ostream &out, // TODO
                                                        t_map* tmap,
@@ -1177,7 +1197,7 @@
 }
 
 /**
- * Write a set element
+ * Read a set element UNUSED
  */
 void t_erl_generator::generate_deserialize_set_element(ostream &out, // TODO
                                                        t_set* tset,
@@ -1192,7 +1212,7 @@
 }
 
 /**
- * Write a list element
+ * Read a list element UNUSED
  */
 void t_erl_generator::generate_deserialize_list_element(ostream &out, // TODO
                                                         t_list* tlist,
@@ -1304,12 +1324,12 @@
     indent(out) <<
       "?R3(Oprot, writeMapBegin, " <<
       type_to_enum(((t_map*)ttype)->get_key_type()) << ", " <<
-      type_to_enum(((t_map*)ttype)->get_val_type()) << ", length(" <<
+      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()) << ", length(" <<
+      type_to_enum(((t_set*)ttype)->get_elem_type()) << ", sets:size(" <<
       prefix << "))," << endl;
   } else if (ttype->is_list()) {
     indent(out) <<
@@ -1319,31 +1339,33 @@
   }
 
   if (ttype->is_map()) {
-    string kiter = tmp("kiter");
-    string viter = tmp("viter");
+    string kiter = tmp("_kiter");
+    string viter = tmp("_viter");
     indent(out) << 
-      prefix << ".each do |" << kiter << ", " << viter << "|" << endl;
+      "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" << endl;
+    indent(out) << "end, nil," << prefix << ")," << endl;
   } else if (ttype->is_set()) {
-    string iter = tmp("iter");
-    string t = tmp("true");
+    string iter = tmp("_iter");
     indent(out) << 
-      prefix << ".each do |" << iter << ", " << t << "|" << endl;
+      "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" << endl;
+    indent(out) << "end, nil," << prefix << ")," << endl;
   } else if (ttype->is_list()) {
-    string iter = tmp("iter");
+    string iter = tmp("_iter");
     indent(out) << 
-      prefix << ".each do |" << iter << "|" << endl;
+      "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" << endl;
+    indent(out) << "end,nil," << prefix << ")," << endl;
   }
     
   if (ttype->is_map()) {
@@ -1429,7 +1451,7 @@
  * Add a function to the exports list
  */
 void t_erl_generator::export_string(string name, int num) {
-  if(export_lines_first_) {
+  if (export_lines_first_) {
     export_lines_first_ = false;
   } else {
     export_lines_ << ", ";
@@ -1447,7 +1469,7 @@
 }
 
 void t_erl_generator::export_types_string(string name, int num) {
-  if(export_types_lines_first_) {
+  if (export_types_lines_first_) {
     export_types_lines_first_ = false;
   } else {
     export_types_lines_ << ", ";
@@ -1497,7 +1519,7 @@
 
   string name = ttype->get_name();
   
-  if (ttype->is_struct() || ttype->is_xception()) {
+  if (ttype->is_struct() || ttype->is_xception() || ttype->is_service()) {
     name = uncapitalize(ttype->get_name());
   }
 
diff --git a/lib/erl/lib/thrift/server.sh b/lib/erl/lib/thrift/server.sh
index 4247c98..0ea4a6b 100755
--- a/lib/erl/lib/thrift/server.sh
+++ b/lib/erl/lib/thrift/server.sh
@@ -2,7 +2,7 @@
 if ! [ -d tutorial/gen-erl ]; then
     echo generating gen-erl
     cd tutorial
-    thrift -erl -r tutorial.thrift
+    thrift -erl -rb -r tutorial.thrift
     cd ..
 fi
 echo "Compiling user/ and tutorial/gen-erl/..."
diff --git a/lib/erl/lib/thrift/src/oop.erl b/lib/erl/lib/thrift/src/oop.erl
index bb6f34f..3e6da05 100644
--- a/lib/erl/lib/thrift/src/oop.erl
+++ b/lib/erl/lib/thrift/src/oop.erl
@@ -88,7 +88,7 @@
 
 call_loop(Obj, Method, Args, TryModule, TriedRev, FirstObj) ->
     try
-	%% io:format("call_loop~n ~p~n ~p~n ~p~n ~p~n ~n", [Obj, Method, Args, TryModule]),
+       %% io:format("call_loop~n ~p~n ~p~n ~p~n ~p~n ~n", [inspect(Obj), Method, Args, TryModule]),
 	apply(TryModule, Method, Args)
     catch
 	error:Kind when Kind == undef; Kind == function_clause ->
diff --git a/lib/erl/lib/thrift/src/thrift_utils.erl b/lib/erl/lib/thrift/src/thrift_utils.erl
new file mode 100644
index 0000000..c1c6a25
--- /dev/null
+++ b/lib/erl/lib/thrift/src/thrift_utils.erl
@@ -0,0 +1,11 @@
+-module(thrift_utils).
+
+-export([tabulate/2,dict_size/1]).
+
+tabulateh(N,M,_) when N==M -> [];
+tabulateh(N,M,F) -> [F(N)|tabulateh(N+1,M,F)].
+tabulate(N,F) -> tabulateh(0,N,F).
+
+% makin me sad
+dict_size(Dict) ->
+  dict:fold(fun (_,_,I) -> I+1 end,0,Dict).
diff --git a/tutorial/erl/server.erl b/tutorial/erl/server.erl
index c924589..0d5c1a9 100644
--- a/tutorial/erl/server.erl
+++ b/tutorial/erl/server.erl
@@ -9,34 +9,38 @@
 
 -include("calculator.hrl").
 
--export([start/0, stop/1, ping/0, add/2, calculate/2, getStruct/1, zip/0]).
+
+-export([start/0, start/1, stop/1, ping/0, add/2, calculate/2, getStruct/1, zip/0]).
 
 ping() ->
     io:format("ping()~n",[]),
-    {ok, nil}.
+    nil.
 
 add(N1, N2) ->
     io:format("add(~p,~p)~n",[N1,N2]),
-    {ok, N1+N2}.
+    N1+N2.
 
 calculate(Logid, Work) ->
     { Op, Num1, Num2 } = { Work#work.op, Work#work.num1, Work#work.num2 },
     io:format("calculate(~p, {~p,~p,~p})~n", [Logid, Op, Num1, Num2]),
     case Op of
-        ?ADD      -> {ok, Num1 + Num2};
-	?SUBTRACT -> {ok, Num1 - Num2};
-	?MULTIPLY -> {ok, Num1 * Num2};
-	?DIVIDE ->
-	    if 	Num2 == 0 -> {error, #invalidOperation{what=Op, why="Cannot divide by 0"}};
-		true      -> {ok, Num1 / Num2}
-	    end;
-	true ->
-	    {error, #invalidOperation{what=Op, why="Invalid operation"}}
+        ?tutorial_ADD      -> Num1 + Num2;
+	?tutorial_SUBTRACT -> Num1 - Num2;
+	?tutorial_MULTIPLY -> Num1 * Num2;
+
+	?tutorial_DIVIDE when Num2 == 0 ->
+	    throw(#invalidOperation{what=Op, why="Cannot divide by 0"});
+	?tutorial_DIVIDE ->
+	    Num1 div Num2;
+
+	_Else ->
+	    throw(#invalidOperation{what=Op, why="Invalid operation"})
+
     end.
 
 getStruct(Key) ->
     io:format("getStruct(~p)~n", [Key]),
-    {ok, get(Key)}.
+    #sharedStruct{key=Key, value="RARG"}.
 
 zip() ->
     io:format("zip~n").
@@ -44,9 +48,11 @@
 %%
 
 start() ->
-    Handler   = ?MODULE, % cpiro: or generated handler?
+    start(9090).
+
+start(Port) ->
+    Handler   = ?MODULE,
     Processor = calculator,
-    Port      = 9090,
 
     TF = tBufferedTransportFactory:new(),
     PF = tBinaryProtocolFactory:new(),
@@ -63,4 +69,3 @@
 stop(Server) ->
     ?C0(Server, stop),
     ok.
-
diff --git a/tutorial/rb/RubyClient.rb b/tutorial/rb/RubyClient.rb
index bcf1300..9ee6e79 100755
--- a/tutorial/rb/RubyClient.rb
+++ b/tutorial/rb/RubyClient.rb
@@ -2,27 +2,40 @@
 
 $:.push('../gen-rb')
 
-require 'thrift/transport/tsocket'
-require 'thrift/protocol/tbinaryprotocol'
+require 'thrift/transport/tsocket.rb'
+require 'thrift/protocol/tbinaryprotocol.rb'
 
 require 'Calculator'
 
 begin
+  port = ARGV[0] || 9090
   
-  transport = TBufferedTransport.new(TSocket.new('localhost', 9090))
+  transport = TBufferedTransport.new(TSocket.new('localhost', port))
   protocol = TBinaryProtocol.new(transport)
   client = Calculator::Client.new(protocol)
-  
+
   transport.open()
-  
+
   client.ping()
   print "ping()\n"
-  
+
   sum = client.add(1,1)
   print "1+1=", sum, "\n"
-  
+
+  sum = client.add(1,4)
+  print "1+4=", sum, "\n"
+
   work = Work.new()
-  
+
+  work.op = Operation::SUBTRACT
+  work.num1 = 15
+  work.num2 = 10
+  diff = client.calculate(1, work)
+  print "15-10=", diff, "\n"
+
+  log = client.getStruct(1)
+  print "Log: ", log.value, "\n"
+
   begin
     work.op = Operation::DIVIDE
     work.num1 = 1
@@ -32,16 +45,10 @@
   rescue InvalidOperation => io
     print "InvalidOperation: ", io.why, "\n"
   end
-  
-  work.op = Operation::SUBTRACT
-  work.num1 = 15
-  work.num2 = 10
-  diff = client.calculate(1, work)
-  print "15-10=", diff, "\n"
-  
-  log = client.getStruct(1)
-  print "Log: ", log.value, "\n"
-  
+
+  client.zip()
+  print "zip\n"
+
   transport.close()
 
 rescue TException => tx