Update thrift compiler for new syntax, generate new form of C++ code
Reviewed By: wayne, he loves less warnings
git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@664840 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/compiler/cpp/src/main.cc b/compiler/cpp/src/main.cc
index 9a8e29e..aee89ae 100644
--- a/compiler/cpp/src/main.cc
+++ b/compiler/cpp/src/main.cc
@@ -13,12 +13,15 @@
#include <stdio.h>
#include <stdarg.h>
#include <string>
+#include <sys/types.h>
+#include <sys/stat.h>
-// Careful: must include globals first here for extern/global definitions
+// Careful: must include globals first for extern definitions
#include "globals.h"
#include "main.h"
#include "parse/t_program.h"
+#include "parse/t_scope.h"
#include "generate/t_cpp_generator.h"
#include "generate/t_java_generator.h"
#include "generate/t_php_generator.h"
@@ -32,16 +35,79 @@
t_program* g_program;
/**
+ * Global types
+ */
+
+t_type* g_type_void;
+t_type* g_type_string;
+t_type* g_type_bool;
+t_type* g_type_byte;
+t_type* g_type_i16;
+t_type* g_type_i32;
+t_type* g_type_i64;
+t_type* g_type_double;
+
+/**
+ * Global scope
+ */
+t_scope* g_scope;
+
+/**
+ * Parent scope to also parse types
+ */
+t_scope* g_parent_scope;
+
+/**
+ * Prefix for putting types in parent scope
+ */
+string g_parent_prefix;
+
+/**
+ * Parsing pass
+ */
+PARSE_MODE g_parse_mode;
+
+/**
+ * Current directory of file being parsed
+ */
+string g_curdir;
+
+/**
+ * Current file being parsed
+ */
+string g_curpath;
+
+/**
* Global debug state
*/
int g_debug = 0;
/**
+ * Warning level
+ */
+int g_warn = 1;
+
+/**
+ * Verbose output
+ */
+int g_verbose = 0;
+
+/**
* Global time string
*/
char* g_time_str;
/**
+ * Flags to control code generation
+ */
+bool gen_cpp = false;
+bool gen_java = false;
+bool gen_py = false;
+bool gen_php = false;
+bool gen_phpi = false;
+bool gen_recurse = false;
+
+/**
* 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.
@@ -52,10 +118,10 @@
void yyerror(char* fmt, ...) {
va_list args;
fprintf(stderr,
- "\n!!! Error: line %d (last token was '%s')",
+ "[ERROR:%s:%d] (last token was '%s')\n",
+ g_curpath.c_str(),
yylineno,
yytext);
- fprintf(stderr, "\n!!! ");
va_start(args, fmt);
vfprintf(stderr, fmt, args);
@@ -74,7 +140,39 @@
return;
}
va_list args;
- printf("[Parse] ");
+ printf("[PARSE] ");
+ va_start(args, fmt);
+ vprintf(fmt, args);
+ va_end(args);
+ printf("\n");
+}
+
+/**
+ * Prints a verbose output mode message
+ *
+ * @param fmt C format string followed by additional arguments
+ */
+void pverbose(char* fmt, ...) {
+ if (g_verbose == 0) {
+ return;
+ }
+ va_list args;
+ va_start(args, fmt);
+ vprintf(fmt, args);
+ va_end(args);
+}
+
+/**
+ * Prints a warning message
+ *
+ * @param fmt C format string followed by additional arguments
+ */
+void pwarning(int level, char* fmt, ...) {
+ if (g_warn < level) {
+ return;
+ }
+ va_list args;
+ printf("[WARNING:%s:%d] ", g_curpath.c_str(), yylineno);
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);
@@ -88,7 +186,7 @@
*/
void failure(char* fmt, ...) {
va_list args;
- fprintf(stderr, "\n!!! Failure: ");
+ fprintf(stderr, "[FAILURE:%s:%d] ", g_curpath.c_str(), yylineno);
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
@@ -97,38 +195,201 @@
}
/**
+ * Converts a string filename into a thrift program name
+ */
+string program_name(string filename) {
+ string::size_type slash = filename.rfind("/");
+ if (slash != string::npos) {
+ filename = filename.substr(slash+1);
+ }
+ string::size_type dot = filename.rfind(".");
+ if (dot != string::npos) {
+ filename = filename.substr(0, dot);
+ }
+ return filename;
+}
+
+/**
+ * Gets the directory path of a filename
+ */
+string directory_name(string filename) {
+ string::size_type slash = filename.rfind("/");
+ // No slash, just use the current directory
+ if (slash == string::npos) {
+ return ".";
+ }
+ return filename.substr(0, slash);
+}
+
+/**
+ * Finds the appropriate file path for the given filename
+ */
+string include_file(string filename) {
+ // Absolute path? Just try that
+ if (filename[0] != '/') {
+ filename = g_curdir + "/" + filename;
+ }
+
+ // Realpath!
+ char rp[PATH_MAX];
+ if (realpath(filename.c_str(), rp) == NULL) {
+ pwarning(0, "Cannot open include file %s\n", filename.c_str());
+ return std::string();
+ }
+
+ // Stat this files
+ struct stat finfo;
+ if (stat(rp, &finfo) == 0) {
+ return rp;
+ }
+
+ // Uh oh
+ pwarning(0, "Could not find include file %s\n", filename.c_str());
+ return std::string();
+}
+
+/**
* Diplays the usage message and then exits with an error code.
*/
void usage() {
fprintf(stderr, "Usage: thrift [options] file\n");
fprintf(stderr, "Options:\n");
- fprintf(stderr, " --cpp Generate C++ output files\n");
- fprintf(stderr, " --java Generate Java output files\n");
- fprintf(stderr, " --php Generate PHP output files\n");
- fprintf(stderr, " --phpi Generate PHP inlined files\n");
- fprintf(stderr, " --py Generate Python output files\n");
- fprintf(stderr, " --debug Print parse debugging to standard output\n");
+ fprintf(stderr, " --cpp Generate C++ output files\n");
+ fprintf(stderr, " --java Generate Java output files\n");
+ fprintf(stderr, " --php Generate PHP output files\n");
+ fprintf(stderr, " --phpi Generate PHP inlined files\n");
+ fprintf(stderr, " --py Generate Python output files\n");
+ fprintf(stderr, " --nowarn Suppress all compiler warnings (BAD!)\n");
+ fprintf(stderr, " --strict Strict compiler warnings on\n");
+ fprintf(stderr, " --v[erbose] Verbose mode\n");
+ fprintf(stderr, " --r[ecurse] Also generate included files\n");
+ fprintf(stderr, " --debug Parse debug trace to stdout\n");
exit(1);
}
/**
+ * Parses a program
+ */
+void parse(t_program* program, t_program* parent_program) {
+ // Get scope file path
+ string path = program->get_path();
+
+ // Set current dir global, which is used in the include_file function
+ g_curdir = directory_name(path);
+ g_curpath = path;
+
+ // Open the file
+ yyin = fopen(path.c_str(), "r");
+ if (yyin == 0) {
+ failure("Could not open input file: \"%s\"", path.c_str());
+ }
+
+ // Create new scope and scan for includes
+ pverbose("Scanning %s for includes\n", path.c_str());
+ g_parse_mode = INCLUDES;
+ g_program = program;
+ g_scope = program->scope();
+ if (yyparse() != 0) {
+ failure("Parser error during include pass.");
+ }
+ fclose(yyin);
+
+ // Recursively parse all the include programs
+ vector<t_program*>& includes = program->get_includes();
+ vector<t_program*>::iterator iter;
+ for (iter = includes.begin(); iter != includes.end(); ++iter) {
+ parse(*iter, program);
+ }
+
+ // Parse the program the file
+ g_parse_mode = PROGRAM;
+ g_program = program;
+ g_scope = program->scope();
+ g_parent_scope = (parent_program != NULL) ? parent_program->scope() : NULL;
+ g_parent_prefix = program->get_name() + ".";
+ g_curpath = path;
+ yyin = fopen(path.c_str(), "r");
+ if (yyin == 0) {
+ failure("Could not open input file: \"%s\"", path.c_str());
+ }
+ pverbose("Parsing %s for types\n", path.c_str());
+ if (yyparse() != 0) {
+ failure("Parser error during types pass.");
+ }
+ fclose(yyin);
+}
+
+/**
+ * Generate code
+ */
+void generate(t_program* program) {
+ // Oooohh, recursive code generation, hot!!
+ if (gen_recurse) {
+ const vector<t_program*>& includes = program->get_includes();
+ for (size_t i = 0; i < includes.size(); ++i) {
+ generate(includes[i]);
+ }
+ }
+
+ // Generate code!
+ try {
+ pverbose("Program: %s\n", program->get_path().c_str());
+
+ if (gen_cpp) {
+ pverbose("Generating C++\n");
+ t_cpp_generator* cpp = new t_cpp_generator(program);
+ cpp->generate_program();
+ delete cpp;
+ }
+
+ if (gen_java) {
+ pverbose("Generating Java\n");
+ t_java_generator* java = new t_java_generator(program);
+ java->generate_program();
+ delete java;
+ }
+
+ if (gen_php) {
+ pverbose("Generating PHP\n");
+ t_php_generator* php = new t_php_generator(program, false);
+ php->generate_program();
+ delete php;
+ }
+
+ if (gen_phpi) {
+ pverbose("Generating PHP-inline\n");
+ t_php_generator* phpi = new t_php_generator(program, true);
+ phpi->generate_program();
+ delete phpi;
+ }
+
+ if (gen_py) {
+ pverbose("Generating Python\n");
+ t_py_generator* py = new t_py_generator(program);
+ py->generate_program();
+ delete py;
+ }
+ } catch (string s) {
+ printf("Error: %s\n", s.c_str());
+ } catch (const char* exc) {
+ printf("Error: %s\n", exc);
+ }
+
+}
+
+/**
* Parse it up.. then spit it back out, in pretty much every language. Alright
* not that many languages, but the cool ones that we care about.
*/
int main(int argc, char** argv) {
int i;
- bool gen_cpp = false;
- bool gen_java = false;
- bool gen_py = false;
- bool gen_php = false;
- bool gen_phpi = false;
-
// Setup time string
time_t now = time(NULL);
g_time_str = ctime(&now);
- // Check for necessary arguments
+ // Check for necessary arguments, you gotta have at least a filename and
+ // an output language flag
if (argc < 2) {
usage();
}
@@ -140,6 +401,14 @@
while (arg != NULL) {
if (strcmp(arg, "--debug") == 0) {
g_debug = 1;
+ } else if (strcmp(arg, "--nowarn") == 0) {
+ g_warn = 0;
+ } else if (strcmp(arg, "--strict") == 0) {
+ g_warn = 2;
+ } else if (strcmp(arg, "--v") == 0 || strcmp(arg, "--verbose") == 0 ) {
+ g_verbose = 1;
+ } else if (strcmp(arg, "--r") == 0 || strcmp(arg, "--recurse") == 0 ) {
+ gen_recurse = true;
} else if (strcmp(arg, "--cpp") == 0) {
gen_cpp = true;
} else if (strcmp(arg, "--java") == 0) {
@@ -160,77 +429,51 @@
}
}
+ // You gotta generate something!
if (!gen_cpp && !gen_java && !gen_php && !gen_phpi && !gen_py) {
fprintf(stderr, "!!! No output language(s) specified\n\n");
usage();
}
-
- // 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);
+
+ // Real-pathify it
+ char rp[PATH_MAX];
+ if (realpath(argv[i], rp) == NULL) {
+ failure("Could not open input file: %s", argv[i]);
}
-
- // 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);
- }
-
+ string input_file(rp);
+
// Instance of the global parse tree
- g_program = new t_program(name);
+ t_program* program = new t_program(input_file);
+
+ // Initialize global types
+ g_type_void = new t_base_type("void", t_base_type::TYPE_VOID);
+ g_type_string = new t_base_type("string", t_base_type::TYPE_STRING);
+ g_type_bool = new t_base_type("bool", t_base_type::TYPE_BOOL);
+ g_type_byte = new t_base_type("byte", t_base_type::TYPE_BYTE);
+ g_type_i16 = new t_base_type("i16", t_base_type::TYPE_I16);
+ g_type_i32 = new t_base_type("i32", t_base_type::TYPE_I32);
+ g_type_i64 = new t_base_type("i64", t_base_type::TYPE_I64);
+ g_type_double = new t_base_type("double", t_base_type::TYPE_DOUBLE);
// Parse it!
- if (yyparse() != 0) {
- failure("Parser error.");
- }
+ parse(program, NULL);
- // Generate code
- try {
- if (gen_cpp) {
- t_cpp_generator* cpp = new t_cpp_generator();
- cpp->generate_program(g_program);
- delete cpp;
- }
+ // Generate it!
+ generate(program);
- if (gen_java) {
- t_java_generator* java = new t_java_generator();
- java->generate_program(g_program);
- delete java;
- }
+ // Clean up. Who am I kidding... this program probably orphans heap memory
+ // all over the place, but who cares because it is about to exit and it is
+ // all referenced and used by this wacky parse tree up until now anyways.
- if (gen_php) {
- t_php_generator* php = new t_php_generator(false);
- php->generate_program(g_program);
- delete php;
- }
-
- if (gen_phpi) {
- t_php_generator* phpi = new t_php_generator(true);
- phpi->generate_program(g_program);
- delete phpi;
- }
-
- if (gen_py) {
- t_py_generator* py = new t_py_generator();
- py->generate_program(g_program);
- delete py;
- }
-
- } catch (string s) {
- printf("Error: %s\n", s.c_str());
- } catch (const char* exc) {
- printf("Error: %s\n", exc);
- }
-
- // Clean up
- delete g_program;
+ delete program;
+ delete g_type_void;
+ delete g_type_string;
+ delete g_type_bool;
+ delete g_type_byte;
+ delete g_type_i16;
+ delete g_type_i32;
+ delete g_type_i64;
+ delete g_type_double;
// Finished
return 0;