| /** |
| * 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 <mcslee@facebook.com> |
| */ |
| |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <stdarg.h> |
| #include <string> |
| |
| // Careful: must include globals first |
| #include "globals.h" |
| |
| #include "main.h" |
| #include "parse/t_program.h" |
| #include "generate/t_cpp_generator.h" |
| #include "generate/t_java_generator.h" |
| #include "generate/t_php_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 [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, " -python Generate Python output files\n"); |
| fprintf(stderr, " -d Print parse debugging to standard output\n"); |
| exit(1); |
| } |
| |
| /** |
| * Parse it up.. then spit it back out, in pretty much every language |
| */ |
| int main(int argc, char** argv) { |
| int i; |
| bool gen_cpp = false; |
| bool gen_java = false; |
| bool gen_php = false; |
| |
| // Setup time string |
| time_t now = time(NULL); |
| g_time_str = ctime(&now); |
| |
| // 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 if (strcmp(argv[i], "-cpp") == 0) { |
| gen_cpp = true; |
| } else if (strcmp(argv[i], "-java") == 0) { |
| gen_java = true; |
| } else if (strcmp(argv[i], "-php") == 0) { |
| gen_php = true; |
| } else { |
| fprintf(stderr, "!!! Unrecognized option: %s\n", argv[i]); |
| usage(); |
| } |
| } |
| |
| if (!gen_cpp && !gen_java && !gen_php) { |
| 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); |
| } |
| |
| // 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 { |
| if (gen_cpp) { |
| t_cpp_generator* cpp = new t_cpp_generator(); |
| cpp->generate_program(g_program); |
| delete cpp; |
| } |
| |
| if (gen_java) { |
| t_java_generator* java = new t_java_generator(); |
| java->generate_program(g_program); |
| delete java; |
| } |
| |
| if (gen_php) { |
| t_php_generator* php = new t_php_generator(); |
| php->generate_program(g_program); |
| delete php; |
| } |
| } catch (string s) { |
| printf("Error: %s\n", s.c_str()); |
| } catch (const char* exc) { |
| printf("Error: %s\n", exc); |
| } |
| |
| // Clean up |
| delete g_program; |
| |
| // Finished |
| return 0; |
| } |