From 319857233b75121f357801cbdd9b5028e3057dbd Mon Sep 17 00:00:00 2001 From: Mark Slee Date: Wed, 24 May 2006 21:45:31 +0000 Subject: [PATCH] Baseline commit for thrift, which is pillar v2 Reviewed By: aditya git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@664711 13f79535-47bb-0310-9956-ffa450edef68 --- compiler/Makefile | 136 ++++++ compiler/src/generate/t_cpp_generator.cc | 510 +++++++++++++++++++++++ compiler/src/generate/t_cpp_generator.h | 56 +++ compiler/src/generate/t_generator.cc | 45 ++ compiler/src/generate/t_generator.h | 63 +++ compiler/src/globals.h | 15 + compiler/src/main.cc | 159 +++++++ compiler/src/main.h | 19 + compiler/src/parse/t_base_type.h | 34 ++ compiler/src/parse/t_constant.h | 26 ++ compiler/src/parse/t_enum.h | 22 + compiler/src/parse/t_field.h | 29 ++ compiler/src/parse/t_function.h | 51 +++ compiler/src/parse/t_list.h | 27 ++ compiler/src/parse/t_program.h | 90 ++++ compiler/src/parse/t_service.h | 23 + compiler/src/parse/t_struct.h | 23 + compiler/src/parse/t_type.h | 31 ++ compiler/src/parse/t_typedef.h | 31 ++ compiler/src/thrift.l | 59 +++ compiler/src/thrift.y | 318 ++++++++++++++ compiler/test/test.thrift | 27 ++ doc/README | 20 + 23 files changed, 1814 insertions(+) create mode 100644 compiler/Makefile create mode 100644 compiler/src/generate/t_cpp_generator.cc create mode 100644 compiler/src/generate/t_cpp_generator.h create mode 100644 compiler/src/generate/t_generator.cc create mode 100644 compiler/src/generate/t_generator.h create mode 100644 compiler/src/globals.h create mode 100644 compiler/src/main.cc create mode 100644 compiler/src/main.h create mode 100644 compiler/src/parse/t_base_type.h create mode 100644 compiler/src/parse/t_constant.h create mode 100644 compiler/src/parse/t_enum.h create mode 100644 compiler/src/parse/t_field.h create mode 100644 compiler/src/parse/t_function.h create mode 100644 compiler/src/parse/t_list.h create mode 100644 compiler/src/parse/t_program.h create mode 100644 compiler/src/parse/t_service.h create mode 100644 compiler/src/parse/t_struct.h create mode 100644 compiler/src/parse/t_type.h create mode 100644 compiler/src/parse/t_typedef.h create mode 100644 compiler/src/thrift.l create mode 100644 compiler/src/thrift.y create mode 100644 compiler/test/test.thrift create mode 100644 doc/README diff --git a/compiler/Makefile b/compiler/Makefile new file mode 100644 index 00000000..8fa9e60a --- /dev/null +++ b/compiler/Makefile @@ -0,0 +1,136 @@ +# Makefile for Thrift compiler. This Makefile assumes that you are running on +# some form of *NIX operating system with the following tools and packages +# installed: +# +# g++ +# flex +# bison +# libfl +# +# Author: +# Mark Slee + +# Default build rule +target: thrift + +# Tools +CC = g++ +LD = g++ +LEX = flex +YACC = bison +MKDIR = mkdir + +# Source directory +SRC_DIR = src/ + +# Object file directory +OBJ_DIR = obj/ + +# Generated source dir +GEN_DIR = $(SRC_DIR) + +# Output directory +BIN_DIR = bin/ + +# Source files +SRC_FILES = main.cc \ + generate/t_generator.cc \ + generate/t_cpp_generator.cc + +# Autogenerated files +GEN_FILES = thrift.tab.hh \ + thrift.tab.cc \ + lex.yy.cc + +# Object files +OBJ_FILES = thrift.tab.o \ + lex.yy.o \ + ${SRC_FILES:.cc=.o} + +# Apply directory prefixes +SRCS = ${addprefix $(SRC_DIR), $(SRC_FILES)} +GENS = ${addprefix $(GEN_DIR), $(GEN_FILES)} +OBJS = ${addprefix $(OBJ_DIR), $(OBJ_FILES)} + +# Compile with strict warnings +CFL = -g -Wall -I$(SRC_DIR) -I$(OBJ_DIR) + +# Flex library +LIBS = -lfl + +# Scanner generation +$(GEN_DIR)lex.yy.cc: $(SRC_DIR)thrift.l + $(LEX) -o$(GEN_DIR)lex.yy.cc $(SRC_DIR)thrift.l + +# Parser generation +$(GEN_DIR)thrift.tab.hh: $(GEN_DIR)thrift.tab.cc + +$(GEN_DIR)thrift.tab.cc: $(SRC_DIR)thrift.y + $(YACC) -d -o$(GEN_DIR)thrift.tab.cc $(SRC_DIR)thrift.y + +# C++ compilation +$(OBJS): $(GEN_DIR)thrift.tab.cc $(GEN_DIR)lex.yy.cc obj_dir + $(CC) $(CFL) -c -o $@ ${subst $(OBJ_DIR),$(SRC_DIR),$*.cc} + +# Header dependency +$(SRC_DIR)main.cc: $(SRC_DIR)*.h + +# Main build rule +thrift: $(OBJS) + $(LD) $(CFL) -o $(BIN_DIR)thrift $(OBJS) $(LIBS) + +# Build directory +obj_dir: + $(MKDIR) -p $(OBJ_DIR)parse + $(MKDIR) -p $(OBJ_DIR)generate + +# This will auto-make dependency rules +depend: + makedepend -- $(CFL) -- $(SRCS) + +# Remove auto-gen'd files and binaries +clean: + rm -f \ + $(OBJS) \ + $(GENS) \ + $(BIN_DIR)thrift.exe \ + $(BIN_DIR)thrift +# DO NOT DELETE + +src/main.o: /usr/include/stdlib.h /usr/include/features.h +src/main.o: /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h +src/main.o: /usr/include/bits/wordsize.h /usr/include/gnu/stubs-64.h +src/main.o: /usr/include/stdio.h /usr/include/bits/types.h +src/main.o: /usr/include/bits/typesizes.h /usr/include/libio.h +src/main.o: /usr/include/_G_config.h /usr/include/wchar.h +src/main.o: /usr/include/bits/wchar.h /usr/include/gconv.h +src/main.o: /usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h +src/main.o: src/globals.h src/main.h src/parse/t_program.h +src/main.o: src/parse/t_base_type.h src/parse/t_type.h src/parse/t_typedef.h +src/main.o: src/parse/t_enum.h src/parse/t_constant.h src/parse/t_struct.h +src/main.o: src/parse/t_list.h src/parse/t_field.h src/parse/t_service.h +src/main.o: src/parse/t_function.h src/generate/t_cpp_generator.h +src/main.o: src/generate/t_generator.h +src/generate/t_generator.o: src/generate/t_generator.h src/parse/t_program.h +src/generate/t_generator.o: src/parse/t_base_type.h src/parse/t_type.h +src/generate/t_generator.o: src/parse/t_typedef.h src/parse/t_enum.h +src/generate/t_generator.o: src/parse/t_constant.h src/parse/t_struct.h +src/generate/t_generator.o: src/parse/t_list.h src/parse/t_field.h +src/generate/t_generator.o: src/parse/t_service.h src/parse/t_function.h +src/generate/t_cpp_generator.o: /usr/include/sys/stat.h +src/generate/t_cpp_generator.o: /usr/include/features.h +src/generate/t_cpp_generator.o: /usr/include/sys/cdefs.h +src/generate/t_cpp_generator.o: /usr/include/gnu/stubs.h +src/generate/t_cpp_generator.o: /usr/include/bits/wordsize.h +src/generate/t_cpp_generator.o: /usr/include/gnu/stubs-64.h +src/generate/t_cpp_generator.o: /usr/include/bits/types.h +src/generate/t_cpp_generator.o: /usr/include/bits/typesizes.h +src/generate/t_cpp_generator.o: /usr/include/bits/stat.h +src/generate/t_cpp_generator.o: src/generate/t_cpp_generator.h +src/generate/t_cpp_generator.o: src/generate/t_generator.h +src/generate/t_cpp_generator.o: src/parse/t_program.h src/parse/t_base_type.h +src/generate/t_cpp_generator.o: src/parse/t_type.h src/parse/t_typedef.h +src/generate/t_cpp_generator.o: src/parse/t_enum.h src/parse/t_constant.h +src/generate/t_cpp_generator.o: src/parse/t_struct.h src/parse/t_list.h +src/generate/t_cpp_generator.o: src/parse/t_field.h src/parse/t_service.h +src/generate/t_cpp_generator.o: src/parse/t_function.h src/globals.h diff --git a/compiler/src/generate/t_cpp_generator.cc b/compiler/src/generate/t_cpp_generator.cc new file mode 100644 index 00000000..b328a1ad --- /dev/null +++ b/compiler/src/generate/t_cpp_generator.cc @@ -0,0 +1,510 @@ +#include +#include "t_cpp_generator.h" +#include "globals.h" +using namespace std; + +/** + * Prepares for file generation by opening up the necessary file output + * streams. + * + * @param tprogram The program to generate + */ +void t_cpp_generator::init_generator(t_program* tprogram) { + // Make output directory + mkdir(T_CPP_DIR, S_IREAD | S_IWRITE | S_IEXEC); + + // Make output file + string f_types_name = string(T_CPP_DIR)+"/"+tprogram->get_name()+"_types.h"; + f_types_.open(f_types_name.c_str()); + + // Print header + f_types_ << + autogen_comment(); + + // Start ifndef + f_types_ << + "#ifndef thrift_" << tprogram->get_name() << "_types_h" << endl << + "#define thrift_" << tprogram->get_name() << "_types_h" << endl << + endl; + + // Include base types + f_types_ << + "#include " << endl << + endl; +} + +/** + * Closes the output files. + */ +void t_cpp_generator::close_generator() { + // Close ifndef + f_types_ << + "#endif" << endl; + + // Close output file + f_types_.close(); +} + + +/** + * Generates a typedef. This is just a simple 1-liner in C++ + * + * @param ttypedef The type definition + */ +void t_cpp_generator::generate_typedef(t_typedef* ttypedef) { + f_types_ << + indent() << "typedef " << type_name(ttypedef->get_type()) << " " << + ttypedef->get_symbolic() << ";" << endl << + endl; +} + +/** + * Generates code for an enumerated type. In C++, this is essentially the same + * as the thrift definition itself, using the enum keyword in C++. + * + * @param tenum The enumeration + */ +void t_cpp_generator::generate_enum(t_enum* tenum) { + f_types_ << + indent() << "enum " << tenum->get_name() << " {" << endl; + + indent_up(); + + vector constants = tenum->get_constants(); + vector::iterator c_iter; + bool first = true; + for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { + if (first) { + first = false; + } else { + f_types_ << + "," << endl; + } + f_types_ << + indent() << (*c_iter)->get_name(); + if ((*c_iter)->has_value()) { + f_types_ << + " = " << (*c_iter)->get_value(); + } + } + + indent_down(); + + f_types_ << + endl << + "};" << endl << + endl; +} + +/** + * Generates a struct definition for a thrift data type. In C++, this is just + * simple C struct with basic data members. There are no constructors, + * initializers, etc. + * + * @param tstruct The struct definition + */ +void t_cpp_generator::generate_struct(t_struct* tstruct) { + f_types_ << + indent() << "struct " << tstruct->get_name() << " {" << endl; + + indent_up(); + + const t_list* memberlist = tstruct->get_members(); + vector members = memberlist->elems(); + vector::iterator m_iter; + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + f_types_ << + indent() << (*m_iter)->get_type()->get_name() << " " << + (*m_iter)->get_name() << ";" << endl; + } + + indent_down(); + + f_types_ << + indent() << "};" << endl << + endl; +} + +/** + * Generates a thrift service. In C++, this comprises an entirely separate + * header and source file. The header file defines the methods and includes + * the data types defined in the main header file, and the implementation + * file contains implementations of the basic printer and default interfaces. + * + * @param tservice The service definition + */ +void t_cpp_generator::generate_service(t_service* tservice) { + // Make output files + string f_header_name = string(T_CPP_DIR)+"/"+tservice->get_name()+".h"; + f_header_.open(f_header_name.c_str()); + string f_service_name = string(T_CPP_DIR)+"/"+tservice->get_name()+".cc"; + f_service_.open(f_service_name.c_str()); + + // Print header file includes + f_header_ << autogen_comment(); + f_header_ << + "#ifndef " << tservice->get_name() << "_h" << endl << + "#define " << tservice->get_name() << "_h" << endl << + endl << + "#include \"TInterface.h\"" << endl << + "#include \"TDispatcher.h\"" << endl << + "#include \"TProtocol.h\"" << endl << + endl; + f_service_ << autogen_comment(); + f_service_ << + "#include \"" << tservice->get_name() << "_h\"" << endl << endl; + + // Generate the three main parts of the service + generate_service_interface(tservice); + generate_service_server(tservice); + generate_service_client(tservice); + + f_header_ << + "#endif" << endl; + + // Close files + f_header_.close(); + f_service_.close(); +} + +/** + * Generates a service interface definition. + * + * @param tservice The service to generate a header definition for + */ +void t_cpp_generator::generate_service_interface(t_service* tservice) { + f_header_ << + "class " << tservice->get_name() << " : public TInterface {" << endl << + " public: " << endl; + indent_up(); + vector functions = tservice->get_functions(); + vector::iterator f_iter; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + f_header_ << + indent() << "virtual " << function_signature(*f_iter) << " = 0;" << endl; + } + f_header_ << + indent() << "virtual ~" << tservice->get_name() << "() = 0;" << endl; + indent_down(); + f_header_ << + "}; " << endl << endl; +} + +/** + * Generates a service server definition. + * + * @param tservice The service to generate a server for. + */ +void t_cpp_generator::generate_service_server(t_service* tservice) { + // Generate the header portion + f_header_ << + "class " << tservice->get_name() << "Server : " << + "public " << tservice->get_name() << ", " << + "public TDispatcher {" << endl << + " public: " << endl; + indent_up(); + f_header_ << + indent() << "std::string dispatch(const std::string& buff);" << endl << + indent() << "virtual ~" << tservice->get_name() << "Server();" << endl; + indent_down(); + f_header_ << + "};" << endl << endl; + + // Generate the dispatch methods + vector functions = tservice->get_functions(); + vector::iterator f_iter; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + generate_dispatch_function(tservice, *f_iter); + } +} + +/** + * Generates a dispatch function definition. + * + * @param tfunction The function to write a dispatcher for + */ +void t_cpp_generator::generate_dispatch_function(t_service* tservice, + t_function* tfunction) { + f_service_ << + "std::string dispatch_" << tfunction->get_name() << + "(const char *_tbuf, " << + tservice->get_name() << " *dispatcher, " << + "TProtocol *protocol) {" << endl; + indent_up(); + + // Create a field to represent this function's arguments + t_field arg_field(tfunction->get_arglist(), "_targs", 0); + + generate_deserialize_struct(&arg_field); + + // Generate the function call + indent(f_service_) << + type_name(tfunction->get_returntype()) << " _tresult = dispatcher." << + tfunction->get_name() << "("; + vector fields = tfunction->get_arglist()->get_members()->elems(); + vector::iterator f_iter; + bool first = true; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if (first) { + first = false; + } else { + f_service_ << ", "; + } + f_service_ << "_targs." << (*f_iter)->get_name(); + } + f_service_ << ");" << endl; + + // Serialize the result + indent(f_service_) << + "std::string _tout = "";" << endl; + t_field out_field(tfunction->get_returntype(), "_tout", 0); + generate_serialize_field(&out_field); + + indent_down(); + f_service_ << + "}" << endl << + endl; +} + +/** + * Generates an unserializer for a variable. This makes two key assumptions, + * first that there is a const char* variable named data that points to the + * buffer for deserialization, and that there is a variable protocol which + * is a reference to a TProtocol serialization object. + */ +void t_cpp_generator::generate_deserialize_struct(t_field* tfield) { + t_struct* tstruct = (t_struct*)(tfield->get_type()); + vector fields = tstruct->get_members()->elems(); + vector::iterator f_iter; + + indent(f_service_) << + declare_field(tfield) << endl; + indent(f_service_) << + "map fmap = protocol.readFieldMap(_tbuf);" << endl; + + // Declare the fields up front + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + indent(f_service_) << + declare_field(*f_iter) << endl; + indent(f_service_) << + "if (fmap.find(" << tfield->get_key() << ") != fmap.end()) {" << endl; + indent_up(); + indent(f_service_) << + "_tbuf = fmap[" << tfield->get_key() << "];" << endl; + generate_deserialize_field(*f_iter); + indent_down(); + indent(f_service_) << + "}" << endl; + } +} + +/** + * Deserializes a field of any type. + */ +void t_cpp_generator::generate_deserialize_field(t_field* tfield) { + t_type* type = tfield->get_type(); + while (type->is_typedef()) { + type = ((t_typedef*)type)->get_type(); + } + + if (type->is_struct()) { + generate_deserialize_struct(tfield); + } + + 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: + return; + case t_base_type::TYPE_STRING: + return; + case t_base_type::TYPE_BYTE: + return; + case t_base_type::TYPE_I32: + return; + case t_base_type::TYPE_U32: + return; + case t_base_type::TYPE_I64: + return; + case t_base_type::TYPE_U64: + return; + default: + throw "compiler error: no C++ name for base type " + tbase; + } + } else if (type->is_enum()) { + + } +} + +/** + * Generates a service client definition. + * + * @param tservice The service to generate a server for. + */ +void t_cpp_generator::generate_service_client(t_service* tservice) { + // Generate the header portion + f_header_ << + "class " << tservice->get_name() << "Client : " << + "public " << tservice->get_name() << " {" << endl << + " public:" << endl; + + indent_up(); + f_header_ << + indent() << tservice->get_name() << + "(TDispatcher& dispatcher, TProtocol& protocol);" << endl; + vector functions = tservice->get_functions(); + vector::iterator f_iter; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + f_header_ << + indent() << function_signature(*f_iter) << ";" << endl; + } + indent_down(); + + f_header_ << + " private:" << endl; + indent_up(); + f_header_ << + indent() << "TDispatcher& dispatcher;" << endl << + indent() << "TProtocol& protocol;" << endl; + indent_down(); + f_header_ << + "};" << endl << + endl; +} + +/** + * Deserializes a field of any type. + */ +void t_cpp_generator::generate_serialize_field(t_field* tfield) { + t_type* type = tfield->get_type(); + while (type->is_typedef()) { + type = ((t_typedef*)type)->get_type(); + } + + if (type->is_struct()) { + + } + + 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: + return; + case t_base_type::TYPE_STRING: + return; + case t_base_type::TYPE_BYTE: + return; + case t_base_type::TYPE_I32: + return; + case t_base_type::TYPE_U32: + return; + case t_base_type::TYPE_I64: + return; + case t_base_type::TYPE_U64: + return; + default: + throw "compiler error: no C++ name for base type " + tbase; + } + } else if (type->is_enum()) { + + } +} + + +/** + * Generates a comment about this code being autogenerated. + * + * @return C-style comment mentioning that this file is autogenerated. + */ +string t_cpp_generator::autogen_comment() { + string result = ""; + return + result + + "/**\n" + + " * Autogenerated by Thrift\n" + + " * " + g_time_str + + " *\n" + + " * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n" + + " */\n"; +} + +/** + * Returns a C++ type name + * + * @param ttype The type + */ +string t_cpp_generator::type_name(t_type* ttype) { + if (ttype->is_base_type()) { + return base_type_name(((t_base_type*)ttype)->get_base()); + } else { + return ttype->get_name(); + } +} + +/** + * Returns the C++ type that corresponds to the thrift type. + * + * @param tbase The base type + */ +string t_cpp_generator::base_type_name(t_base_type::t_base tbase) { + switch (tbase) { + case t_base_type::TYPE_VOID: + return "void"; + case t_base_type::TYPE_STRING: + return "std::string"; + case t_base_type::TYPE_BYTE: + return "uint8_t"; + case t_base_type::TYPE_I32: + return "int32_t"; + case t_base_type::TYPE_U32: + return "uint32_t"; + case t_base_type::TYPE_I64: + return "int64_t"; + case t_base_type::TYPE_U64: + return "uint64_t"; + default: + throw "compiler error: no C++ name for base type " + tbase; + } +} + +/** + * Declares a field, which may include initialization as necessary. + * + * @param ttype The type + */ +string t_cpp_generator::declare_field(t_field* tfield) { + // TODO(mcslee): do we ever need to initialize the field? + return type_name(tfield->get_type()) + " " + tfield->get_name() + ";"; +} + +/** + * Renders a function signature of the form 'type name(args)' + * + * @param tfunction Function definition + * @return String of rendered function definition + */ +string t_cpp_generator::function_signature(t_function* tfunction) { + return + type_name(tfunction->get_returntype()) + " " + tfunction->get_name() + + "(" + field_list(tfunction->get_arglist()->get_members()) + ")"; +} + +/** + * Renders a field list + */ +string t_cpp_generator::field_list(t_list* tlist) { + string result = ""; + + vector fields = tlist->elems(); + vector::iterator f_iter; + bool first = true; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if (first) { + first = false; + } else { + result += ", "; + } + result += type_name((*f_iter)->get_type()) + " " + (*f_iter)->get_name(); + } + return result; +} diff --git a/compiler/src/generate/t_cpp_generator.h b/compiler/src/generate/t_cpp_generator.h new file mode 100644 index 00000000..87eb72a7 --- /dev/null +++ b/compiler/src/generate/t_cpp_generator.h @@ -0,0 +1,56 @@ +#ifndef T_CPP_GENERATOR_H +#define T_CPP_GENERATOR_H + +#include +#include + +#include "t_generator.h" + +#define T_CPP_DIR "gen-cpp" + +/** + * C++ code generator + * + * @author Mark Slee + */ +class t_cpp_generator : public t_generator { + public: + t_cpp_generator() {} + ~t_cpp_generator() {} + + /** Init and close methods */ + void init_generator(t_program *tprogram); + void close_generator(); + + /** Program-level generation functions */ + void generate_typedef (t_typedef* ttypedef); + void generate_enum (t_enum* tenum); + void generate_struct (t_struct* tstruct); + void generate_service (t_service* tservice); + + /** Service-level generation functions */ + void generate_service_interface (t_service* tservice); + void generate_service_server (t_service* tservice); + void generate_service_client (t_service* tservice); + + /** Serialization constructs */ + void generate_dispatch_function (t_service* tservice, t_function* tfunction); + void generate_deserialize_struct(t_field* tfield); + void generate_deserialize_field(t_field* tfield); + + /** Helper rendering functions */ + std::string autogen_comment(); + std::string type_name(t_type* ttype); + std::string base_type_name(t_base_type::t_base tbase); + std::string declare_field(t_field* tfield); + std::string function_signature(t_function* tfunction); + std::string field_list(t_list* tlist); + + private: + /** File streams */ + std::ofstream f_types_; + std::ofstream f_header_; + std::ofstream f_service_; +}; + +#endif diff --git a/compiler/src/generate/t_generator.cc b/compiler/src/generate/t_generator.cc new file mode 100644 index 00000000..0ba44ac4 --- /dev/null +++ b/compiler/src/generate/t_generator.cc @@ -0,0 +1,45 @@ +#include "t_generator.h" +using namespace std; + +/** + * Top level program generation function. Calls the generator subclass methods + * for preparing file streams etc. then iterates over all the parts of the + * program to perform the correct actions. + * + * @param program The thrift program to compile into C++ source + */ +void t_generator::generate_program(t_program *tprogram) { + // Initialize the generator + init_generator(tprogram); + + // Generate typedefs + vector typedefs = tprogram->get_typedefs(); + vector::iterator td_iter; + for (td_iter = typedefs.begin(); td_iter != typedefs.end(); ++td_iter) { + generate_typedef(*td_iter); + } + + // Generate enums + vector enums = tprogram->get_enums(); + vector::iterator en_iter; + for (en_iter = enums.begin(); en_iter != enums.end(); ++en_iter) { + generate_enum(*en_iter); + } + + // Generate structs + vector structs = tprogram->get_structs(); + vector::iterator st_iter; + for (st_iter = structs.begin(); st_iter != structs.end(); ++st_iter) { + generate_struct(*st_iter); + } + + // Generate services + vector services = tprogram->get_services(); + vector::iterator sv_iter; + for (sv_iter = services.begin(); sv_iter != services.end(); ++sv_iter) { + generate_service(*sv_iter); + } + + // Close the generator + close_generator(); +} diff --git a/compiler/src/generate/t_generator.h b/compiler/src/generate/t_generator.h new file mode 100644 index 00000000..71d0318e --- /dev/null +++ b/compiler/src/generate/t_generator.h @@ -0,0 +1,63 @@ +#ifndef T_GENERATOR_H +#define T_GENERATOR_H + +#include +#include +#include "parse/t_program.h" + +/** + * Base class for a thrift code generator. This class defines the basic + * routines for code generation and contains the top level method that + * dispatches code generation across various components. + * + * @author Mark Slee + */ +class t_generator { + public: + t_generator() {} + virtual ~t_generator() {} + + /** + * Framework generator method that iterates over all the parts of a program + * and performs general actions. This is implemented by the base class and + * should not be overwritten in the subclasses. + */ + void generate_program (t_program* tprogram); + + protected: + /** Optional methods that may be imlemented by subclasses. */ + virtual void init_generator (t_program* tprogram) {} + virtual void close_generator () {} + + /** Pure virtual methods implemented by the generator subclasses. */ + virtual void generate_typedef (t_typedef* ttypedef) = 0; + virtual void generate_enum (t_enum* tenum) = 0; + virtual void generate_struct (t_struct* tstruct) = 0; + virtual void generate_service (t_service* tservice) = 0; + + /** Indentation level modifiers */ + void indent_up() { ++indent_; } + void indent_down() { --indent_; } + + /** Indentation print function */ + std::string indent() { + std::string ind = ""; + int i; + for (i = 0; i < indent_; ++i) { + ind += " "; + } + return ind; + } + + /** Indentation wrapper */ + std::ostream& indent(std::ostream &os) { + return os << indent(); + } + + private: + /** Indentation level */ + int indent_; + +}; + +#endif diff --git a/compiler/src/globals.h b/compiler/src/globals.h new file mode 100644 index 00000000..db7e3925 --- /dev/null +++ b/compiler/src/globals.h @@ -0,0 +1,15 @@ +#ifndef T_GLOBALS_H +#define T_GLOBALS_H + +class t_program; + +/** Global variable: the master program parse tree */ +extern t_program* g_program; + +/** Global debug state */ +extern int g_debug; + +/** Global time string */ +extern char* g_time_str; + +#endif diff --git a/compiler/src/main.cc b/compiler/src/main.cc new file mode 100644 index 00000000..cc126f81 --- /dev/null +++ b/compiler/src/main.cc @@ -0,0 +1,159 @@ +/** + * thrift - a lightweight cross-language rpc/serialization tool + * + * This file contains the main compiler engine for Thrift, which invokes the + * scanner/parser to build the thrift object tree. The interface generation + * code for each language lives in a file by the language name. + * + * @author Mark Slee + */ + +#include +#include +#include +#include + +// Careful: must include globals first +#include "globals.h" + +#include "main.h" +#include "parse/t_program.h" +#include "generate/t_cpp_generator.h" + +using namespace std; + +/** Global program tree */ +t_program* g_program; + +/** Global debug state */ +int g_debug = 0; + +/** Global time string */ +char* g_time_str; + + +/** + * Report an error to the user. This is called yyerror for historical + * reasons (lex and yacc expect the error reporting routine to be called + * this). Call this function to report any errors to the user. + * yyerror takes printf style arguments. + * + * @param fmt C format string followed by additional arguments + */ +void yyerror(char* fmt, ...) { + va_list args; + fprintf(stderr, + "\n!!! Error: line %d (last token was '%s')", + yylineno, + yytext); + fprintf(stderr, "\n!!! "); + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + + fprintf(stderr, "\n"); +} + +/** + * Prints a debug message from the parser. + * + * @param fmt C format string followed by additional arguments + */ +void pdebug(char* fmt, ...) { + if (g_debug == 0) { + return; + } + va_list args; + printf("[Parse] "); + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + printf("\n"); +} + +/** + * Prints a failure message and exits + * + * @param fmt C format string followed by additional arguments + */ +void failure(char* fmt, ...) { + va_list args; + fprintf(stderr, "\n!!! Failure: "); + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + printf("\n"); + exit(1); +} + +/** + * Diplays the usage message and then exits with an error code. + */ +void usage() { + fprintf(stderr, "Usage: thrift [-d] \n"); + exit(1); +} + +/** + * Parse it up.. then spit it back out, in pretty much every language + */ +int main(int argc, char** argv) { + int i; + + // Check for necessary arguments + if (argc < 2) usage(); + + for (i = 1; i < argc-1; i++) { + if (strcmp(argv[i], "-d") == 0) { + g_debug = 1; + } else { + fprintf(stderr, "!!! Unrecognized option: %s\n", argv[i]); + usage(); + } + } + + // Setup time string + time_t now = time(NULL); + g_time_str = ctime(&now); + + // Open input file + char* input_file = argv[i]; + yyin = fopen(input_file, "r"); + if (yyin == 0) { + failure("Could not open input file: \"%s\"", input_file); + } + + // Extract program name by dropping directory and .thrift from filename + string name = input_file; + string::size_type slash = name.rfind("/"); + if (slash != string::npos) { + name = name.substr(slash+1); + } + string::size_type dot = name.find("."); + if (dot != string::npos) { + name = name.substr(0, dot); + } + + // Parse it + g_program = new t_program(name); + if (yyparse() != 0) { + failure("Parser error."); + } + + // Generate code + try { + t_cpp_generator* cpp = new t_cpp_generator(); + cpp->generate_program(g_program); + delete cpp; + } catch (const char* exc) { + printf("Error: %s\n", exc); + } + + // Clean up + delete g_program; + + // Finished + printf("\nDone!\n"); + return 0; +} diff --git a/compiler/src/main.h b/compiler/src/main.h new file mode 100644 index 00000000..b26142c8 --- /dev/null +++ b/compiler/src/main.h @@ -0,0 +1,19 @@ +#ifndef T_MAIN_H +#define T_MAIN_H + +/** Defined in the flex library */ +extern int yylex(void); +extern int yyparse(void); + +/** Expected to be defined by Flex/Bison */ +extern void yyerror(char* fmt, ...); + +/** Parse debug output */ +extern void pdebug(char* fmt, ...); + +/** Flex utilities */ +extern int yylineno; +extern char yytext[]; +extern FILE* yyin; + +#endif diff --git a/compiler/src/parse/t_base_type.h b/compiler/src/parse/t_base_type.h new file mode 100644 index 00000000..bc961227 --- /dev/null +++ b/compiler/src/parse/t_base_type.h @@ -0,0 +1,34 @@ +#ifndef T_BASE_TYPE_H +#define T_BASE_TYPE_H + +#include "t_type.h" + +/** + * A thrift base type, which must be one of the defined enumerated types. + * + * @author Mark Slee + */ +class t_base_type : public t_type { + public: + /** Enumeration of thrift base types */ + enum t_base { + TYPE_VOID, + TYPE_STRING, + TYPE_BYTE, + TYPE_I32, + TYPE_U32, + TYPE_I64, + TYPE_U64 + }; + + t_base_type(std::string name, t_base base) : + t_type(name), base_(base) {} + + t_base get_base() const { return base_; } + bool is_base_type() const { return true; } + + private: + t_base base_; +}; + +#endif diff --git a/compiler/src/parse/t_constant.h b/compiler/src/parse/t_constant.h new file mode 100644 index 00000000..1f3c868e --- /dev/null +++ b/compiler/src/parse/t_constant.h @@ -0,0 +1,26 @@ +#ifndef T_CONSTANT_H +#define T_CONSTANT_H + +#include + +class t_constant { + public: + t_constant(std::string name) : + name_(name), has_value_(false), value_(0) {} + + t_constant(std::string name, int value) : + name_(name), has_value_(true), value_(value) {} + + ~t_constant() {} + + const std::string& get_name() { return name_; } + bool has_value() { return has_value_; } + int get_value() { return value_; } + + private: + std::string name_; + bool has_value_; + int value_; +}; + +#endif diff --git a/compiler/src/parse/t_enum.h b/compiler/src/parse/t_enum.h new file mode 100644 index 00000000..f0891509 --- /dev/null +++ b/compiler/src/parse/t_enum.h @@ -0,0 +1,22 @@ +#ifndef T_ENUM_H +#define T_ENUM_H + +#include "t_constant.h" +#include + +class t_enum : public t_type { + public: + t_enum() {} + ~t_enum() {} + + void set_name(std::string name) { name_ = name; } + void append(t_constant* constant) { constants_.push_back(constant); } + + const std::vector& get_constants() { return constants_; } + bool is_enum() const { return true; } + + private: + std::vector constants_; +}; + +#endif diff --git a/compiler/src/parse/t_field.h b/compiler/src/parse/t_field.h new file mode 100644 index 00000000..304bb168 --- /dev/null +++ b/compiler/src/parse/t_field.h @@ -0,0 +1,29 @@ +#ifndef T_FIELD_H +#define T_FIELD_H + +#include + +/** + * Class to represent a field in a thrift structure or in a set of arguments + * to a thrift function. + * + * @author Mark Slee + */ +class t_field { + public: + t_field(t_type* type, std::string name, uint32_t key) : + type_(type), name_(name), key_(key) {} + + ~t_field() {} + + t_type* get_type() const { return type_; } + const std::string& get_name() const { return name_; } + uint32_t get_key() const { return key_; } + + private: + t_type* type_; + std::string name_; + uint32_t key_; +}; + +#endif diff --git a/compiler/src/parse/t_function.h b/compiler/src/parse/t_function.h new file mode 100644 index 00000000..1caf54a3 --- /dev/null +++ b/compiler/src/parse/t_function.h @@ -0,0 +1,51 @@ +#ifndef T_FUNCTION_H +#define T_FUNCTION_H + +#include +#include "t_type.h" +#include "t_struct.h" + +/** + * Representation of a function. Key parst are return type, function name, + * optional modifiers, and an argument list. Each function also has a + * hash signature that is used in the network protocol. + * + * @author Mark Slee + */ +class t_function { + public: + t_function(t_type* returntype, std::string name, t_struct* arglist) : + returntype_(returntype), name_(name), arglist_(arglist) {} + + ~t_function() {} + + /** + * Implementation of the Fowler / Noll / Vo (FNV) Hash + * http://www.isthe.com/chongo/tech/comp/fnv/ + */ + static uint32_t fnv32(const char *s) { + uint32_t hash = (uint32_t)216613626; + while (*s) { + hash += + (hash << 1) + + (hash << 4) + + (hash << 7) + + (hash << 8) + + (hash << 24); + hash ^= *s++; + } + return hash; + } + + t_type* get_returntype() const { return returntype_; } + const std::string& get_name() const { return name_; } + t_struct* get_arglist() const { return arglist_; } + uint32_t get_hash() const { return fnv32(name_.c_str()); } + + private: + t_type* returntype_; + std::string name_; + t_struct* arglist_; +}; + +#endif diff --git a/compiler/src/parse/t_list.h b/compiler/src/parse/t_list.h new file mode 100644 index 00000000..736ac48a --- /dev/null +++ b/compiler/src/parse/t_list.h @@ -0,0 +1,27 @@ +#ifndef T_LIST_H +#define T_LIST_H + +#include "t_field.h" +#include + +/** + * List of elements. + * + * @author Mark Slee + */ +class t_list { + public: + t_list() {} + ~t_list() {} + + /** Add a new field to the list */ + void append(t_field* elem) { list_.push_back(elem); } + + /** Retrieve the list contents */ + const std::vector& elems() const { return list_; } + + private: + std::vector list_; +}; + +#endif diff --git a/compiler/src/parse/t_program.h b/compiler/src/parse/t_program.h new file mode 100644 index 00000000..fd357997 --- /dev/null +++ b/compiler/src/parse/t_program.h @@ -0,0 +1,90 @@ +#ifndef T_PROGRAM_H +#define T_PROGRAM_H + +#include +#include + +#include "t_base_type.h" +#include "t_typedef.h" +#include "t_enum.h" +#include "t_struct.h" +#include "t_service.h" + +/** + * Top level class representing an entire thrift program. A program consists + * fundamentally of the following: + * + * Typedefs + * Enumerations + * Structs + * Services + * + * @author Mark Slee + */ +class t_program { + public: + t_program(std::string name) : + name_(name) { + type_void = new t_base_type("void", t_base_type::TYPE_VOID); + type_string = new t_base_type("string", t_base_type::TYPE_STRING); + type_byte = new t_base_type("byte", t_base_type::TYPE_BYTE); + type_i32 = new t_base_type("i32", t_base_type::TYPE_I32); + type_u32 = new t_base_type("u32", t_base_type::TYPE_U32); + type_i64 = new t_base_type("i64", t_base_type::TYPE_I64); + type_u64 = new t_base_type("u64", t_base_type::TYPE_U64); + } + + ~t_program() { + delete type_string; + delete type_byte; + delete type_i32; + delete type_u32; + delete type_i64; + delete type_u64; + } + + // Name accessor + const std::string& get_name() const { return name_; } + + // Accessors for program elements + const std::vector& get_typedefs() const { return typedefs_; } + const std::vector& get_enums() const { return enums_; } + const std::vector& get_structs() const { return structs_; } + const std::vector& get_services() const { return services_; } + + // New program element addition + void add_typedef(t_typedef *td) { typedefs_.push_back(td); } + void add_enum (t_enum *te) { enums_.push_back(te); } + void add_struct (t_struct *ts) { structs_.push_back(ts); } + void add_service(t_service *ts) { services_.push_back(ts); } + + // Accessors for global types + t_type* get_void_type() const { return type_void; } + t_type* get_string_type() const { return type_string; } + t_type* get_byte_type() const { return type_byte; } + t_type* get_i32_type() const { return type_i32; } + t_type* get_u32_type() const { return type_u32; } + t_type* get_i64_type() const { return type_i64; } + t_type* get_u64_type() const { return type_u64; } + + private: + // Name + std::string name_; + + // Components + std::vector typedefs_; + std::vector enums_; + std::vector structs_; + std::vector services_; + + // Global base types + t_type* type_void; + t_type* type_string; + t_type* type_byte; + t_type* type_i32; + t_type* type_u32; + t_type* type_i64; + t_type* type_u64; +}; + +#endif diff --git a/compiler/src/parse/t_service.h b/compiler/src/parse/t_service.h new file mode 100644 index 00000000..92e59b2f --- /dev/null +++ b/compiler/src/parse/t_service.h @@ -0,0 +1,23 @@ +#ifndef T_SERVICE_H +#define T_SERVICE_H + +#include "t_function.h" +#include + +class t_service { + public: + t_service() {} + ~t_service() {} + + void set_name(std::string name) { name_ = name; } + void add_function(t_function* func) { functions_.push_back(func); } + + const std::string& get_name() const { return name_; } + const std::vector& get_functions() const { return functions_; } + + private: + std::string name_; + std::vector functions_; +}; + +#endif diff --git a/compiler/src/parse/t_struct.h b/compiler/src/parse/t_struct.h new file mode 100644 index 00000000..fcc00e7b --- /dev/null +++ b/compiler/src/parse/t_struct.h @@ -0,0 +1,23 @@ +#ifndef T_STRUCT_H +#define T_STRUCT_H + +#include +#include + +#include "t_type.h" +#include "t_list.h" + +class t_struct : public t_type { + public: + t_struct(std::string name, t_list* members) : + t_type(name), members_(members) {} + ~t_struct() {} + + t_list* get_members() { return members_; } + bool is_struct() { return true; } + + private: + t_list* members_; +}; + +#endif diff --git a/compiler/src/parse/t_type.h b/compiler/src/parse/t_type.h new file mode 100644 index 00000000..436c59ea --- /dev/null +++ b/compiler/src/parse/t_type.h @@ -0,0 +1,31 @@ +#ifndef T_TYPE_H +#define T_TYPE_H + +#include + +/** + * Generic representation of a thrift type. + * + * @author Mark Slee + */ +class t_type { + public: + virtual ~t_type() {} + + virtual const std::string& get_name() const { return name_; } + + virtual bool is_base_type() const { return false; } + virtual bool is_typedef() const { return false; } + virtual bool is_enum() const { return false; } + virtual bool is_struct() const { return false; } + + protected: + t_type() {} + + t_type(std::string name) : + name_(name) {} + + std::string name_; +}; + +#endif diff --git a/compiler/src/parse/t_typedef.h b/compiler/src/parse/t_typedef.h new file mode 100644 index 00000000..97284be7 --- /dev/null +++ b/compiler/src/parse/t_typedef.h @@ -0,0 +1,31 @@ +#ifndef T_TYPEDEF_H +#define T_TYPEDEF_H + +#include +#include "t_type.h" + +/** + * A typedef is a mapping from a symbolic name to another type. + * + * @author Mark Slee + */ +class t_typedef : public t_type { + public: + t_typedef(t_type* type, std::string symbolic) : + t_type(symbolic), type_(type), symbolic_(symbolic) {} + + ~t_typedef() {} + + t_type* get_type() const { return type_; } + const std::string& get_symbolic() const { return symbolic_; } + bool is_typedef() const { return true; } + + private: + /** Type that this definition inherits from */ + t_type* type_; + + /** Symbolic name for this type */ + std::string symbolic_; +}; + +#endif diff --git a/compiler/src/thrift.l b/compiler/src/thrift.l new file mode 100644 index 00000000..5164c894 --- /dev/null +++ b/compiler/src/thrift.l @@ -0,0 +1,59 @@ +/** + * Thrift scanner. + * + * Tokenizes a thrift definition file. + * @author Mark Slee + */ +%{ + +#include "main.h" +#include "parse/t_program.h" + +/** Must be included AFTER parse/t_program.h */ +#include "thrift.tab.hh" + +%} + +/** Provides yylineno global */ +%option lex-compat + +/** Helper definitions */ +intconstant ([0-9]+) +identifier ([a-zA-Z_][a-zA-Z_0-9]*) +whitespace ([ \t\r\n]*) +multicomm ("/*""/"*([^*/]|[^*]"/"|"*"[^/])*"*"*"*/") +comment ("//"[^\n]*) +symbol ([\,\{\}\(\)\=]) + +%% + +{whitespace} { /* do nothing */ } +{multicomm} { /* do nothing */ } +{comment} { /* do nothing */ } + +{symbol} { return yytext[0]; } + +"byte" { return tok_byte; } +"string" { return tok_string; } +"i32" { return tok_i32; } +"u32" { return tok_u32; } +"i64" { return tok_i64; } +"u64" { return tok_u64; } + +"map" { return tok_map; } +"list" { return tok_list; } +"set" { return tok_set; } + +"void" { return tok_void; } +"async" { return tok_async; } + +"typedef" { return tok_typedef; } +"struct" { return tok_struct; } +"function" { return tok_function; } +"service" { return tok_service; } +"enum" { return tok_enum; } + +{intconstant} { yylval.iconst = atoi(yytext) ; return tok_int_constant; } +{identifier} { yylval.id = strdup(yytext); return tok_identifier; } + +%% diff --git a/compiler/src/thrift.y b/compiler/src/thrift.y new file mode 100644 index 00000000..f0fa3c49 --- /dev/null +++ b/compiler/src/thrift.y @@ -0,0 +1,318 @@ +%{ + +/** + * Thrift parser. + * + * This parser is used on a thrift definition file. + * + * @author Mark Slee + */ + +#include +#include "main.h" +#include "globals.h" +#include "parse/t_program.h" + +int y_field_val = 0; + +%} + +%union { + char* id; + int iconst; + t_type* ttype; + t_typedef* ttypedef; + t_enum* tenum; + t_struct* tstruct; + t_service* tservice; + t_function* tfunction; + t_field* tfield; + t_list* tlist; + t_constant* tconstant; +} + +/** Strings and constants */ +%token tok_identifier +%token tok_int_constant + +/** Base datatypes */ +%token tok_byte +%token tok_string +%token tok_i32 +%token tok_u32 +%token tok_i64 +%token tok_u64 + +/** Complex Types */ +%token tok_map +%token tok_list +%token tok_set + +/** Function types */ +%token tok_void + +/** Modifiers */ +%token tok_async + +/** Thrift actions */ +%token tok_typedef +%token tok_struct +%token tok_function +%token tok_service +%token tok_enum + +/** Types */ +%type BaseType + +%type Typedef +%type DefinitionType + +%type Field +%type FieldType +%type FieldList + +%type Enum +%type EnumDefList +%type EnumDef + +%type Struct + +%type Service + +%type Function +%type FunctionModifiers +%type FunctionType +%type FunctionList + +%% + +/** Thrift Grammar */ + +Program: + DefinitionList + { + pdebug("Program -> DefinitionList"); + } + +DefinitionList: + DefinitionList Definition + { + pdebug("DefinitionList -> DefinitionList Definition"); + } +| + { + pdebug("DefinitionList -> "); + } + +Definition: + Typedef + { + pdebug("Definition -> Typedef"); + g_program->add_typedef($1); + } +| Enum + { + pdebug("Definition -> Enum"); + g_program->add_enum($1); + } +| Struct + { + pdebug("Definition -> Struct"); + g_program->add_struct($1); + } +| Service + { + pdebug("Definition -> Service"); + g_program->add_service($1); + } + +Typedef: + tok_typedef DefinitionType tok_identifier + { + pdebug("TypeDef -> tok_typedef DefinitionType tok_identifier"); + + t_typedef *td = new t_typedef($2, $3); + $$ = td; + } + +Enum: + tok_enum tok_identifier '{' EnumDefList '}' + { + pdebug("Enum -> tok_enum tok_identifier { EnumDefList }"); + $$ = $4; + $$->set_name($2); + } + +EnumDefList: + EnumDefList ',' EnumDef + { + pdebug("EnumDefList -> EnumDefList EnumDef"); + $$ = $1; + $$->append($3); + } +| EnumDef + { + pdebug("EnumDefList -> EnumDef"); + $$ = new t_enum; + $$->append($1); + } +| + { + pdebug("EnumDefList -> "); + $$ = new t_enum; + } + +EnumDef: + tok_identifier '=' tok_int_constant + { + if ($3 < 0) { + printf("WARNING (%d): Negative value supplied for enum %s.\n", yylineno, $1); + } + pdebug("EnumDef => tok_identifier = tok_int_constant"); + $$ = new t_constant($1, $3); + } +| + tok_identifier + { + pdebug("EnumDef => tok_identifier"); + $$ = new t_constant($1); + } + +Struct: + tok_struct tok_identifier '{' FieldList '}' + { + pdebug("Struct -> tok_struct tok_identifier { FieldList }"); + $$ = new t_struct($2, $4); + y_field_val = 0; + } + +Service: + tok_service tok_identifier '{' FunctionList '}' + { + pdebug("Service -> tok_service tok_identifier { FunctionList }"); + $$ = $4; + $$->set_name($2); + } + +FunctionList: + FunctionList Function + { + pdebug("FunctionList -> FunctionList Function"); + $$ = $1; + $1->add_function($2); + } +| + { + pdebug("FunctionList -> "); + $$ = new t_service; + } + +Function: + FunctionType FunctionModifiers tok_identifier '(' FieldList ')' + { + t_struct* fun_args = new t_struct("__targs", $5); + $$ = new t_function($1, $3, fun_args); + y_field_val = 0; + } + +FunctionModifiers: + { + /** TODO(mcslee): implement async modifier, etc. */ + $$ = 0; + } + +FieldList: + FieldList ',' Field + { + pdebug("FieldList -> FieldList , Field"); + $$ = $1; + $$->append($3); + } +| Field + { + pdebug("FieldList -> Field"); + $$ = new t_list; + $$->append($1); + } +| + { + pdebug("FieldList -> "); + $$ = new t_list; + } + +Field: + FieldType tok_identifier '=' tok_int_constant + { + if ($4 < 0) { + yyerror("Negative value (%d) not allowed as a field key.", $4); + } + $$ = new t_field($1, $2, (uint32_t)$4); + y_field_val = $4+1; + } +| FieldType tok_identifier + { + printf("WARNING (%d): No field key specified for %s, resulting protocol may have conflicts or not be backwards compatible!\n", yylineno, $2); + $$ = new t_field($1, $2, y_field_val++); + } + +DefinitionType: + BaseType + { + pdebug("DefinitionType -> BaseType"); + $$ = $1; + } + +FunctionType: + FieldType + { + $$ = $1; + } +| tok_void + { + $$ = g_program->get_void_type(); + } + +FieldType: + tok_identifier + { + /** TODO(mcslee): Dynamic type lookup */ + yyerror("No dynamic type lookup yet."); + } +| BaseType + { + $$ = $1; + } + +BaseType: + tok_string + { + pdebug("BaseType -> tok_string"); + $$ = g_program->get_string_type(); + } +| tok_byte + { + pdebug("BaseType -> tok_byte"); + $$ = g_program->get_byte_type(); + } +| tok_i32 + { + pdebug("BaseType -> tok_i32"); + $$ = g_program->get_i32_type(); + } +| tok_u32 + { + pdebug("BaseType -> tok_u32"); + $$ = g_program->get_u32_type(); + } +| tok_i64 + { + pdebug("BaseType -> tok_i64"); + $$ = g_program->get_i64_type(); + } +| tok_u64 + { + pdebug("BaseType -> tok_u64"); + $$ = g_program->get_u64_type(); + } + +%% diff --git a/compiler/test/test.thrift b/compiler/test/test.thrift new file mode 100644 index 00000000..b2baab35 --- /dev/null +++ b/compiler/test/test.thrift @@ -0,0 +1,27 @@ +typedef i32 gozone_t + +enum numberz +{ + ONE = 1, + TWO, + THREE, + FIVE = 5, + SIX, + EIGHT = 8 +} + +struct things +{ + u32 first_num, + u32 second_num, + u32 third_num, + u32 fourth_num +} + +service worker +{ + void funky_void() + string funky_string(string thing) + u32 funky_u32(u32 thing) + i32 funky_i32(i32 thing) +} diff --git a/doc/README b/doc/README new file mode 100644 index 00000000..1ff665f4 --- /dev/null +++ b/doc/README @@ -0,0 +1,20 @@ +THRIFT v0.01 + +Some time soon this document will tell you all about Thrift, what it does, how +it was designed, oh and some other stuff too. But in the meantime, all it does +is tell you that Thrift is the 2nd coming of Pillar, originally written by +the great Adam D'Angelo, and now manned mainly by mcslee and aditya at +facebook.com. + +Oh yeah... a few rules: + +1) This shit is open source. Don't mess. + +2) Mark Slee is anal about coding standards and commenting, especially because + this is open source. Again, don't mess. + +3) You need three things to make a few. Otherwise call it a couple. + +That's all. + +-mcslee, May 22, 2006 -- 2.17.1