blob: a6f55f98ce499dcb13ed772030835ed0d3450a58 [file] [log] [blame]
Mark Sleee9ce01c2007-05-16 02:29:53 +00001// Copyright (c) 2006- Facebook
2// Distributed under the Thrift Software License
3//
4// See accompanying file LICENSE or visit the Thrift site at:
5// http://developers.facebook.com/thrift/
6
Mark Slee31985722006-05-24 21:45:31 +00007/**
8 * thrift - a lightweight cross-language rpc/serialization tool
9 *
10 * This file contains the main compiler engine for Thrift, which invokes the
11 * scanner/parser to build the thrift object tree. The interface generation
Mark Sleef5377b32006-10-10 01:42:59 +000012 * code for each language lives in a file by the language name under the
13 * generate/ folder, and all parse structures live in parse/
Mark Slee31985722006-05-24 21:45:31 +000014 *
15 * @author Mark Slee <mcslee@facebook.com>
16 */
17
18#include <stdlib.h>
19#include <stdio.h>
20#include <stdarg.h>
21#include <string>
Mark Sleef0712dc2006-10-25 19:03:57 +000022#include <sys/types.h>
23#include <sys/stat.h>
Mark Slee31985722006-05-24 21:45:31 +000024
Mark Sleef0712dc2006-10-25 19:03:57 +000025// Careful: must include globals first for extern definitions
Mark Slee31985722006-05-24 21:45:31 +000026#include "globals.h"
27
28#include "main.h"
29#include "parse/t_program.h"
Mark Sleef0712dc2006-10-25 19:03:57 +000030#include "parse/t_scope.h"
Mark Slee31985722006-05-24 21:45:31 +000031#include "generate/t_cpp_generator.h"
Mark Sleeb15a68b2006-06-07 06:46:24 +000032#include "generate/t_java_generator.h"
Mark Slee6e536442006-06-30 18:28:50 +000033#include "generate/t_php_generator.h"
Mark Sleefc89d392006-09-04 00:04:39 +000034#include "generate/t_py_generator.h"
Mark Slee6d7d5952007-01-27 01:44:22 +000035#include "generate/t_rb_generator.h"
Mark Slee0e0ff7e2007-01-18 22:59:59 +000036#include "generate/t_xsd_generator.h"
Mark Slee2c44d202007-05-16 02:18:07 +000037#include "generate/t_perl_generator.h"
iproctor9a41a0c2007-07-16 21:59:24 +000038#include "generate/t_ocaml_generator.h"
Christopher Piro2f5afce2007-06-29 07:17:33 +000039#include "generate/t_erl_generator.h"
iproctorff8eb922007-07-25 19:06:13 +000040#include "generate/t_hs_generator.h"
Mark Slee31985722006-05-24 21:45:31 +000041
42using namespace std;
43
Mark Sleef5377b32006-10-10 01:42:59 +000044/**
45 * Global program tree
46 */
Mark Slee31985722006-05-24 21:45:31 +000047t_program* g_program;
48
Mark Sleef5377b32006-10-10 01:42:59 +000049/**
Mark Sleef0712dc2006-10-25 19:03:57 +000050 * Global types
51 */
52
53t_type* g_type_void;
54t_type* g_type_string;
Mark Slee8d725a22007-04-13 01:57:12 +000055t_type* g_type_binary;
Mark Sleeb6200d82007-01-19 19:14:36 +000056t_type* g_type_slist;
Mark Sleef0712dc2006-10-25 19:03:57 +000057t_type* g_type_bool;
58t_type* g_type_byte;
59t_type* g_type_i16;
60t_type* g_type_i32;
61t_type* g_type_i64;
62t_type* g_type_double;
63
64/**
65 * Global scope
66 */
67t_scope* g_scope;
68
69/**
70 * Parent scope to also parse types
71 */
72t_scope* g_parent_scope;
73
74/**
75 * Prefix for putting types in parent scope
76 */
77string g_parent_prefix;
78
79/**
80 * Parsing pass
81 */
82PARSE_MODE g_parse_mode;
83
84/**
85 * Current directory of file being parsed
86 */
87string g_curdir;
88
89/**
90 * Current file being parsed
91 */
92string g_curpath;
93
94/**
Martin Kraemer32c66e12006-11-09 00:06:36 +000095 * Search path for inclusions
96 */
Mark Slee2329a832006-11-09 00:23:30 +000097vector<string> g_incl_searchpath;
Martin Kraemer32c66e12006-11-09 00:06:36 +000098
99/**
Mark Sleef5377b32006-10-10 01:42:59 +0000100 * Global debug state
101 */
Mark Slee31985722006-05-24 21:45:31 +0000102int g_debug = 0;
103
Mark Sleef5377b32006-10-10 01:42:59 +0000104/**
Mark Sleef0712dc2006-10-25 19:03:57 +0000105 * Warning level
106 */
107int g_warn = 1;
108
109/**
110 * Verbose output
111 */
112int g_verbose = 0;
113
114/**
Mark Sleef5377b32006-10-10 01:42:59 +0000115 * Global time string
116 */
Mark Slee31985722006-05-24 21:45:31 +0000117char* g_time_str;
118
Mark Slee31985722006-05-24 21:45:31 +0000119/**
Mark Sleef0712dc2006-10-25 19:03:57 +0000120 * Flags to control code generation
121 */
122bool gen_cpp = false;
123bool gen_java = false;
Mark Slee6d7d5952007-01-27 01:44:22 +0000124bool gen_rb = false;
Mark Sleef0712dc2006-10-25 19:03:57 +0000125bool gen_py = false;
Mark Slee0e0ff7e2007-01-18 22:59:59 +0000126bool gen_xsd = false;
Mark Sleef0712dc2006-10-25 19:03:57 +0000127bool gen_php = false;
128bool gen_phpi = false;
Mark Slee756b1d12007-07-06 00:30:21 +0000129bool gen_rest = false;
Mark Slee2c44d202007-05-16 02:18:07 +0000130bool gen_perl = false;
iproctor9a41a0c2007-07-16 21:59:24 +0000131bool gen_ocaml = false;
Christopher Piro2f5afce2007-06-29 07:17:33 +0000132bool gen_erl = false;
iproctorff8eb922007-07-25 19:06:13 +0000133bool gen_hs = false;
Mark Sleef0712dc2006-10-25 19:03:57 +0000134bool gen_recurse = false;
135
136/**
Mark Slee31985722006-05-24 21:45:31 +0000137 * Report an error to the user. This is called yyerror for historical
138 * reasons (lex and yacc expect the error reporting routine to be called
139 * this). Call this function to report any errors to the user.
140 * yyerror takes printf style arguments.
141 *
142 * @param fmt C format string followed by additional arguments
143 */
144void yyerror(char* fmt, ...) {
145 va_list args;
146 fprintf(stderr,
Mark Sleef0712dc2006-10-25 19:03:57 +0000147 "[ERROR:%s:%d] (last token was '%s')\n",
148 g_curpath.c_str(),
Mark Slee31985722006-05-24 21:45:31 +0000149 yylineno,
150 yytext);
Mark Slee31985722006-05-24 21:45:31 +0000151
152 va_start(args, fmt);
153 vfprintf(stderr, fmt, args);
154 va_end(args);
155
156 fprintf(stderr, "\n");
157}
158
159/**
160 * Prints a debug message from the parser.
161 *
162 * @param fmt C format string followed by additional arguments
163 */
164void pdebug(char* fmt, ...) {
165 if (g_debug == 0) {
166 return;
167 }
168 va_list args;
Mark Slee30152872006-11-28 01:24:07 +0000169 printf("[PARSE:%d] ", yylineno);
Mark Sleef0712dc2006-10-25 19:03:57 +0000170 va_start(args, fmt);
171 vprintf(fmt, args);
172 va_end(args);
173 printf("\n");
174}
175
176/**
177 * Prints a verbose output mode message
178 *
179 * @param fmt C format string followed by additional arguments
180 */
181void pverbose(char* fmt, ...) {
182 if (g_verbose == 0) {
183 return;
184 }
185 va_list args;
186 va_start(args, fmt);
187 vprintf(fmt, args);
188 va_end(args);
189}
190
191/**
192 * Prints a warning message
193 *
194 * @param fmt C format string followed by additional arguments
195 */
196void pwarning(int level, char* fmt, ...) {
197 if (g_warn < level) {
198 return;
199 }
200 va_list args;
201 printf("[WARNING:%s:%d] ", g_curpath.c_str(), yylineno);
Mark Slee31985722006-05-24 21:45:31 +0000202 va_start(args, fmt);
203 vprintf(fmt, args);
204 va_end(args);
205 printf("\n");
206}
207
208/**
209 * Prints a failure message and exits
210 *
211 * @param fmt C format string followed by additional arguments
212 */
Mark Slee30152872006-11-28 01:24:07 +0000213void failure(const char* fmt, ...) {
Mark Slee2c44d202007-05-16 02:18:07 +0000214 va_list args;
Mark Sleef0712dc2006-10-25 19:03:57 +0000215 fprintf(stderr, "[FAILURE:%s:%d] ", g_curpath.c_str(), yylineno);
Mark Slee31985722006-05-24 21:45:31 +0000216 va_start(args, fmt);
217 vfprintf(stderr, fmt, args);
218 va_end(args);
219 printf("\n");
220 exit(1);
221}
222
223/**
Mark Sleef0712dc2006-10-25 19:03:57 +0000224 * Converts a string filename into a thrift program name
225 */
226string program_name(string filename) {
227 string::size_type slash = filename.rfind("/");
228 if (slash != string::npos) {
229 filename = filename.substr(slash+1);
230 }
231 string::size_type dot = filename.rfind(".");
232 if (dot != string::npos) {
233 filename = filename.substr(0, dot);
234 }
235 return filename;
236}
237
238/**
239 * Gets the directory path of a filename
240 */
241string directory_name(string filename) {
242 string::size_type slash = filename.rfind("/");
243 // No slash, just use the current directory
244 if (slash == string::npos) {
245 return ".";
246 }
247 return filename.substr(0, slash);
248}
249
250/**
251 * Finds the appropriate file path for the given filename
252 */
253string include_file(string filename) {
254 // Absolute path? Just try that
Martin Kraemer32c66e12006-11-09 00:06:36 +0000255 if (filename[0] == '/') {
256 // Realpath!
257 char rp[PATH_MAX];
258 if (realpath(filename.c_str(), rp) == NULL) {
259 pwarning(0, "Cannot open include file %s\n", filename.c_str());
260 return std::string();
261 }
Mark Slee2c44d202007-05-16 02:18:07 +0000262
263 // Stat this file
Martin Kraemer32c66e12006-11-09 00:06:36 +0000264 struct stat finfo;
265 if (stat(rp, &finfo) == 0) {
266 return rp;
267 }
268 } else { // relative path, start searching
269 // new search path with current dir global
270 vector<string> sp = g_incl_searchpath;
271 sp.insert(sp.begin(), g_curdir);
Mark Slee2c44d202007-05-16 02:18:07 +0000272
Martin Kraemer32c66e12006-11-09 00:06:36 +0000273 // iterate through paths
274 vector<string>::iterator it;
275 for (it = sp.begin(); it != sp.end(); it++) {
276 string sfilename = *(it) + "/" + filename;
Mark Slee2c44d202007-05-16 02:18:07 +0000277
Martin Kraemer32c66e12006-11-09 00:06:36 +0000278 // Realpath!
279 char rp[PATH_MAX];
280 if (realpath(sfilename.c_str(), rp) == NULL) {
281 continue;
282 }
Mark Slee2c44d202007-05-16 02:18:07 +0000283
Martin Kraemer32c66e12006-11-09 00:06:36 +0000284 // Stat this files
285 struct stat finfo;
286 if (stat(rp, &finfo) == 0) {
287 return rp;
288 }
289 }
Mark Sleef0712dc2006-10-25 19:03:57 +0000290 }
Mark Slee2c44d202007-05-16 02:18:07 +0000291
Mark Sleef0712dc2006-10-25 19:03:57 +0000292 // Uh oh
293 pwarning(0, "Could not find include file %s\n", filename.c_str());
294 return std::string();
295}
296
297/**
Mark Slee31985722006-05-24 21:45:31 +0000298 * Diplays the usage message and then exits with an error code.
299 */
300void usage() {
Mark Sleeb15a68b2006-06-07 06:46:24 +0000301 fprintf(stderr, "Usage: thrift [options] file\n");
302 fprintf(stderr, "Options:\n");
Mark Slee2329a832006-11-09 00:23:30 +0000303 fprintf(stderr, " -cpp Generate C++ output files\n");
304 fprintf(stderr, " -java Generate Java output files\n");
305 fprintf(stderr, " -php Generate PHP output files\n");
306 fprintf(stderr, " -phpi Generate PHP inlined files\n");
307 fprintf(stderr, " -py Generate Python output files\n");
ccheeverf53b5cf2007-02-05 20:33:11 +0000308 fprintf(stderr, " -rb Generate Ruby output files\n");
309 fprintf(stderr, " -xsd Generate XSD output files\n");
Mark Slee2c44d202007-05-16 02:18:07 +0000310 fprintf(stderr, " -perl Generate Perl output files\n");
iproctor9a41a0c2007-07-16 21:59:24 +0000311 fprintf(stderr, " -ocaml Generate OCaml output files\n");
Christopher Piro2f5afce2007-06-29 07:17:33 +0000312 fprintf(stderr, " -erl Generate Erlang output files\n");
iproctorff8eb922007-07-25 19:06:13 +0000313 fprintf(stderr, " -hs Generate Haskell output files\n");
Mark Slee227ac2c2007-03-07 05:46:50 +0000314 fprintf(stderr, " -I dir Add a directory to the list of directories \n");
315 fprintf(stderr, " searched for include directives\n");
Mark Slee2329a832006-11-09 00:23:30 +0000316 fprintf(stderr, " -nowarn Suppress all compiler warnings (BAD!)\n");
317 fprintf(stderr, " -strict Strict compiler warnings on\n");
318 fprintf(stderr, " -v[erbose] Verbose mode\n");
319 fprintf(stderr, " -r[ecurse] Also generate included files\n");
320 fprintf(stderr, " -debug Parse debug trace to stdout\n");
Mark Slee31985722006-05-24 21:45:31 +0000321 exit(1);
322}
323
324/**
Mark Slee30152872006-11-28 01:24:07 +0000325 * You know, when I started working on Thrift I really thought it wasn't going
326 * to become a programming language because it was just a generator and it
327 * wouldn't need runtime type information and all that jazz. But then we
328 * decided to add constants, and all of a sudden that means runtime type
329 * validation and inference, except the "runtime" is the code generator
330 * runtime. Shit. I've been had.
331 */
332void validate_const_rec(std::string name, t_type* type, t_const_value* value) {
333 if (type->is_void()) {
334 throw "type error: cannot declare a void const: " + name;
335 }
336
337 if (type->is_base_type()) {
338 t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
339 switch (tbase) {
340 case t_base_type::TYPE_STRING:
341 if (value->get_type() != t_const_value::CV_STRING) {
342 throw "type error: const \"" + name + "\" was declared as string";
343 }
344 break;
345 case t_base_type::TYPE_BOOL:
346 if (value->get_type() != t_const_value::CV_INTEGER) {
347 throw "type error: const \"" + name + "\" was declared as bool";
348 }
349 break;
350 case t_base_type::TYPE_BYTE:
351 if (value->get_type() != t_const_value::CV_INTEGER) {
352 throw "type error: const \"" + name + "\" was declared as byte";
353 }
354 break;
355 case t_base_type::TYPE_I16:
356 if (value->get_type() != t_const_value::CV_INTEGER) {
357 throw "type error: const \"" + name + "\" was declared as i16";
358 }
359 break;
360 case t_base_type::TYPE_I32:
361 if (value->get_type() != t_const_value::CV_INTEGER) {
362 throw "type error: const \"" + name + "\" was declared as i32";
363 }
364 break;
365 case t_base_type::TYPE_I64:
366 if (value->get_type() != t_const_value::CV_INTEGER) {
367 throw "type error: const \"" + name + "\" was declared as i64";
368 }
369 break;
370 case t_base_type::TYPE_DOUBLE:
371 if (value->get_type() != t_const_value::CV_INTEGER &&
372 value->get_type() != t_const_value::CV_DOUBLE) {
373 throw "type error: const \"" + name + "\" was declared as double";
374 }
375 break;
376 default:
377 throw "compiler error: no const of base type " + tbase + name;
378 }
379 } else if (type->is_enum()) {
380 if (value->get_type() != t_const_value::CV_INTEGER) {
381 throw "type error: const \"" + name + "\" was declared as enum";
382 }
383 } else if (type->is_struct() || type->is_xception()) {
384 if (value->get_type() != t_const_value::CV_MAP) {
385 throw "type error: const \"" + name + "\" was declared as struct/xception";
386 }
387 const vector<t_field*>& fields = ((t_struct*)type)->get_members();
388 vector<t_field*>::const_iterator f_iter;
389
390 const map<t_const_value*, t_const_value*>& val = value->get_map();
391 map<t_const_value*, t_const_value*>::const_iterator v_iter;
392 for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
393 if (v_iter->first->get_type() != t_const_value::CV_STRING) {
394 throw "type error: " + name + " struct key must be string";
395 }
396 t_type* field_type = NULL;
397 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
398 if ((*f_iter)->get_name() == v_iter->first->get_string()) {
399 field_type = (*f_iter)->get_type();
400 }
401 }
402 if (field_type == NULL) {
403 throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string();
404 }
405
406 validate_const_rec(name + "." + v_iter->first->get_string(), field_type, v_iter->second);
407 }
408 } else if (type->is_map()) {
409 t_type* k_type = ((t_map*)type)->get_key_type();
410 t_type* v_type = ((t_map*)type)->get_val_type();
411 const map<t_const_value*, t_const_value*>& val = value->get_map();
412 map<t_const_value*, t_const_value*>::const_iterator v_iter;
413 for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
414 validate_const_rec(name + "<key>", k_type, v_iter->first);
415 validate_const_rec(name + "<val>", v_type, v_iter->second);
Mark Slee2c44d202007-05-16 02:18:07 +0000416 }
Mark Slee30152872006-11-28 01:24:07 +0000417 } else if (type->is_list() || type->is_set()) {
418 t_type* e_type;
419 if (type->is_list()) {
420 e_type = ((t_list*)type)->get_elem_type();
421 } else {
422 e_type = ((t_set*)type)->get_elem_type();
423 }
424 const vector<t_const_value*>& val = value->get_list();
425 vector<t_const_value*>::const_iterator v_iter;
426 for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
427 validate_const_rec(name + "<elem>", e_type, *v_iter);
428 }
429 }
430}
431
432/**
433 * Check the type of the parsed const information against its declared type
434 */
435void validate_const_type(t_const* c) {
436 validate_const_rec(c->get_name(), c->get_type(), c->get_value());
437}
438
439/**
Mark Slee7ff32452007-02-01 05:26:18 +0000440 * Check the type of a default value assigned to a field.
441 */
442void validate_field_value(t_field* field, t_const_value* cv) {
443 validate_const_rec(field->get_name(), field->get_type(), cv);
444}
445
446/**
Mark Sleef0712dc2006-10-25 19:03:57 +0000447 * Parses a program
448 */
Mark Slee2c44d202007-05-16 02:18:07 +0000449void parse(t_program* program, t_program* parent_program) {
Mark Sleef0712dc2006-10-25 19:03:57 +0000450 // Get scope file path
451 string path = program->get_path();
Mark Slee2c44d202007-05-16 02:18:07 +0000452
Mark Sleef0712dc2006-10-25 19:03:57 +0000453 // Set current dir global, which is used in the include_file function
454 g_curdir = directory_name(path);
455 g_curpath = path;
456
457 // Open the file
458 yyin = fopen(path.c_str(), "r");
459 if (yyin == 0) {
460 failure("Could not open input file: \"%s\"", path.c_str());
461 }
462
463 // Create new scope and scan for includes
464 pverbose("Scanning %s for includes\n", path.c_str());
Mark Slee2c44d202007-05-16 02:18:07 +0000465 g_parse_mode = INCLUDES;
Mark Sleef0712dc2006-10-25 19:03:57 +0000466 g_program = program;
467 g_scope = program->scope();
Mark Slee30152872006-11-28 01:24:07 +0000468 try {
Mark Slee36bfa2e2007-01-19 20:09:51 +0000469 yylineno = 1;
Mark Slee30152872006-11-28 01:24:07 +0000470 if (yyparse() != 0) {
471 failure("Parser error during include pass.");
472 }
473 } catch (string x) {
474 failure(x.c_str());
Mark Sleef0712dc2006-10-25 19:03:57 +0000475 }
476 fclose(yyin);
477
478 // Recursively parse all the include programs
479 vector<t_program*>& includes = program->get_includes();
480 vector<t_program*>::iterator iter;
481 for (iter = includes.begin(); iter != includes.end(); ++iter) {
482 parse(*iter, program);
483 }
484
485 // Parse the program the file
486 g_parse_mode = PROGRAM;
487 g_program = program;
488 g_scope = program->scope();
489 g_parent_scope = (parent_program != NULL) ? parent_program->scope() : NULL;
490 g_parent_prefix = program->get_name() + ".";
491 g_curpath = path;
492 yyin = fopen(path.c_str(), "r");
493 if (yyin == 0) {
494 failure("Could not open input file: \"%s\"", path.c_str());
495 }
496 pverbose("Parsing %s for types\n", path.c_str());
Mark Slee36bfa2e2007-01-19 20:09:51 +0000497 yylineno = 1;
David Reiss877237a2007-07-27 00:40:19 +0000498 try {
499 if (yyparse() != 0) {
500 failure("Parser error during types pass.");
501 }
502 } catch (string x) {
503 failure(x.c_str());
Mark Sleef0712dc2006-10-25 19:03:57 +0000504 }
505 fclose(yyin);
506}
507
508/**
509 * Generate code
510 */
511void generate(t_program* program) {
512 // Oooohh, recursive code generation, hot!!
513 if (gen_recurse) {
514 const vector<t_program*>& includes = program->get_includes();
515 for (size_t i = 0; i < includes.size(); ++i) {
516 generate(includes[i]);
517 }
518 }
519
520 // Generate code!
521 try {
522 pverbose("Program: %s\n", program->get_path().c_str());
523
524 if (gen_cpp) {
525 pverbose("Generating C++\n");
526 t_cpp_generator* cpp = new t_cpp_generator(program);
527 cpp->generate_program();
528 delete cpp;
529 }
530
531 if (gen_java) {
532 pverbose("Generating Java\n");
533 t_java_generator* java = new t_java_generator(program);
534 java->generate_program();
535 delete java;
536 }
537
538 if (gen_php) {
539 pverbose("Generating PHP\n");
Mark Slee756b1d12007-07-06 00:30:21 +0000540 t_php_generator* php = new t_php_generator(program, false, gen_rest);
Mark Sleef0712dc2006-10-25 19:03:57 +0000541 php->generate_program();
542 delete php;
543 }
544
545 if (gen_phpi) {
546 pverbose("Generating PHP-inline\n");
Mark Slee756b1d12007-07-06 00:30:21 +0000547 t_php_generator* phpi = new t_php_generator(program, true, gen_rest);
Mark Sleef0712dc2006-10-25 19:03:57 +0000548 phpi->generate_program();
549 delete phpi;
550 }
551
552 if (gen_py) {
553 pverbose("Generating Python\n");
554 t_py_generator* py = new t_py_generator(program);
555 py->generate_program();
556 delete py;
557 }
Mark Slee0e0ff7e2007-01-18 22:59:59 +0000558
Mark Slee6d7d5952007-01-27 01:44:22 +0000559 if (gen_rb) {
560 pverbose("Generating Ruby\n");
561 t_rb_generator* rb = new t_rb_generator(program);
562 rb->generate_program();
563 delete rb;
564 }
565
Mark Slee0e0ff7e2007-01-18 22:59:59 +0000566 if (gen_xsd) {
567 pverbose("Generating XSD\n");
568 t_xsd_generator* xsd = new t_xsd_generator(program);
569 xsd->generate_program();
570 delete xsd;
571 }
572
Mark Slee2c44d202007-05-16 02:18:07 +0000573 if (gen_perl) {
574 pverbose("Generating PERL\n");
575 t_perl_generator* perl = new t_perl_generator(program);
576 perl->generate_program();
577 delete perl;
578 }
579
iproctor9a41a0c2007-07-16 21:59:24 +0000580 if (gen_ocaml) {
581 pverbose("Generating OCaml\n");
582 t_ocaml_generator* ocaml = new t_ocaml_generator(program);
583 ocaml->generate_program();
584 delete ocaml;
585 }
586
Christopher Piro2f5afce2007-06-29 07:17:33 +0000587 if (gen_erl) {
588 pverbose("Generating Erlang\n");
589 t_erl_generator* erl = new t_erl_generator(program);
590 erl->generate_program();
591 delete erl;
592 }
iproctorff8eb922007-07-25 19:06:13 +0000593 if (gen_hs) {
594 pverbose("Generating Haskell\n");
595 t_hs_generator* hs = new t_hs_generator(program);
596 hs->generate_program();
597 delete hs;
598 }
iproctor9a41a0c2007-07-16 21:59:24 +0000599
Mark Sleef0712dc2006-10-25 19:03:57 +0000600 } catch (string s) {
601 printf("Error: %s\n", s.c_str());
602 } catch (const char* exc) {
603 printf("Error: %s\n", exc);
604 }
605
606}
607
608/**
Mark Sleef5377b32006-10-10 01:42:59 +0000609 * Parse it up.. then spit it back out, in pretty much every language. Alright
610 * not that many languages, but the cool ones that we care about.
Mark Slee31985722006-05-24 21:45:31 +0000611 */
612int main(int argc, char** argv) {
613 int i;
Mark Sleef5377b32006-10-10 01:42:59 +0000614
Mark Sleeb15a68b2006-06-07 06:46:24 +0000615 // Setup time string
616 time_t now = time(NULL);
617 g_time_str = ctime(&now);
Mark Slee31985722006-05-24 21:45:31 +0000618
Mark Sleef0712dc2006-10-25 19:03:57 +0000619 // Check for necessary arguments, you gotta have at least a filename and
620 // an output language flag
Mark Sleeb15a68b2006-06-07 06:46:24 +0000621 if (argc < 2) {
622 usage();
623 }
Mark Slee31985722006-05-24 21:45:31 +0000624
Mark Sleef5377b32006-10-10 01:42:59 +0000625 // Hacky parameter handling... I didn't feel like using a library sorry!
Mark Slee31985722006-05-24 21:45:31 +0000626 for (i = 1; i < argc-1; i++) {
Mark Sleefdbee812006-09-27 18:50:48 +0000627 char* arg;
Mark Slee2329a832006-11-09 00:23:30 +0000628
Mark Sleefdbee812006-09-27 18:50:48 +0000629 arg = strtok(argv[i], " ");
630 while (arg != NULL) {
Mark Slee2329a832006-11-09 00:23:30 +0000631 // Treat double dashes as single dashes
Mark Slee52cb2232006-11-10 22:32:07 +0000632 if (arg[0] == '-' && arg[1] == '-') {
Mark Slee2329a832006-11-09 00:23:30 +0000633 ++arg;
634 }
635
636 if (strcmp(arg, "-debug") == 0) {
Mark Sleefdbee812006-09-27 18:50:48 +0000637 g_debug = 1;
Mark Slee2329a832006-11-09 00:23:30 +0000638 } else if (strcmp(arg, "-nowarn") == 0) {
Mark Sleef0712dc2006-10-25 19:03:57 +0000639 g_warn = 0;
Mark Slee2329a832006-11-09 00:23:30 +0000640 } else if (strcmp(arg, "-strict") == 0) {
Mark Sleef0712dc2006-10-25 19:03:57 +0000641 g_warn = 2;
Mark Slee2329a832006-11-09 00:23:30 +0000642 } else if (strcmp(arg, "-v") == 0 || strcmp(arg, "-verbose") == 0 ) {
Mark Sleef0712dc2006-10-25 19:03:57 +0000643 g_verbose = 1;
Mark Slee2329a832006-11-09 00:23:30 +0000644 } else if (strcmp(arg, "-r") == 0 || strcmp(arg, "-recurse") == 0 ) {
Mark Sleef0712dc2006-10-25 19:03:57 +0000645 gen_recurse = true;
Mark Slee2329a832006-11-09 00:23:30 +0000646 } else if (strcmp(arg, "-cpp") == 0) {
Mark Sleefdbee812006-09-27 18:50:48 +0000647 gen_cpp = true;
Mark Slee2329a832006-11-09 00:23:30 +0000648 } else if (strcmp(arg, "-java") == 0) {
Mark Sleefdbee812006-09-27 18:50:48 +0000649 gen_java = true;
Mark Slee2329a832006-11-09 00:23:30 +0000650 } else if (strcmp(arg, "-php") == 0) {
Mark Sleefdbee812006-09-27 18:50:48 +0000651 gen_php = true;
Mark Slee2329a832006-11-09 00:23:30 +0000652 } else if (strcmp(arg, "-phpi") == 0) {
Mark Sleef5377b32006-10-10 01:42:59 +0000653 gen_phpi = true;
Mark Slee756b1d12007-07-06 00:30:21 +0000654 } else if (strcmp(arg, "-rest") == 0) {
655 gen_rest = true;
Mark Slee2329a832006-11-09 00:23:30 +0000656 } else if (strcmp(arg, "-py") == 0) {
Mark Sleefdbee812006-09-27 18:50:48 +0000657 gen_py = true;
Mark Slee6d7d5952007-01-27 01:44:22 +0000658 } else if (strcmp(arg, "-rb") == 0) {
659 gen_rb = true;
Mark Slee0e0ff7e2007-01-18 22:59:59 +0000660 } else if (strcmp(arg, "-xsd") == 0) {
661 gen_xsd = true;
Mark Slee2c44d202007-05-16 02:18:07 +0000662 } else if (strcmp(arg, "-perl") == 0) {
663 gen_perl = true;
iproctor9a41a0c2007-07-16 21:59:24 +0000664 } else if (strcmp(arg, "-ocaml") == 0) {
665 gen_ocaml = true;
Christopher Piro2f5afce2007-06-29 07:17:33 +0000666 } else if (strcmp(arg, "-erl") == 0) {
667 gen_erl = true;
iproctorff8eb922007-07-25 19:06:13 +0000668 } else if (strcmp(arg, "-hs") == 0) {
669 gen_hs = true;
Martin Kraemer32c66e12006-11-09 00:06:36 +0000670 } else if (strcmp(arg, "-I") == 0) {
671 // An argument of "-I\ asdf" is invalid and has unknown results
672 arg = argv[++i];
673
674 if (arg == NULL) {
675 fprintf(stderr, "!!! Missing Include directory");
676 usage();
677 }
678 g_incl_searchpath.push_back(arg);
Mark Sleefdbee812006-09-27 18:50:48 +0000679 } else {
680 fprintf(stderr, "!!! Unrecognized option: %s\n", arg);
681 usage();
682 }
683
684 // Tokenize more
685 arg = strtok(NULL, " ");
Mark Slee31985722006-05-24 21:45:31 +0000686 }
687 }
Mark Slee2c44d202007-05-16 02:18:07 +0000688
Mark Sleef0712dc2006-10-25 19:03:57 +0000689 // You gotta generate something!
iproctorff8eb922007-07-25 19:06:13 +0000690 if (!gen_cpp && !gen_java && !gen_php && !gen_phpi && !gen_py && !gen_rb && !gen_xsd && !gen_perl && !gen_ocaml && !gen_erl && !gen_hs) {
Mark Sleeb15a68b2006-06-07 06:46:24 +0000691 fprintf(stderr, "!!! No output language(s) specified\n\n");
692 usage();
693 }
Mark Sleef0712dc2006-10-25 19:03:57 +0000694
695 // Real-pathify it
696 char rp[PATH_MAX];
697 if (realpath(argv[i], rp) == NULL) {
698 failure("Could not open input file: %s", argv[i]);
Mark Slee31985722006-05-24 21:45:31 +0000699 }
Mark Sleef0712dc2006-10-25 19:03:57 +0000700 string input_file(rp);
701
Mark Sleef5377b32006-10-10 01:42:59 +0000702 // Instance of the global parse tree
Mark Sleef0712dc2006-10-25 19:03:57 +0000703 t_program* program = new t_program(input_file);
704
705 // Initialize global types
706 g_type_void = new t_base_type("void", t_base_type::TYPE_VOID);
707 g_type_string = new t_base_type("string", t_base_type::TYPE_STRING);
Mark Slee8d725a22007-04-13 01:57:12 +0000708 g_type_binary = new t_base_type("string", t_base_type::TYPE_STRING);
709 ((t_base_type*)g_type_binary)->set_binary(true);
Mark Sleeb6200d82007-01-19 19:14:36 +0000710 g_type_slist = new t_base_type("string", t_base_type::TYPE_STRING);
711 ((t_base_type*)g_type_slist)->set_string_list(true);
Mark Sleef0712dc2006-10-25 19:03:57 +0000712 g_type_bool = new t_base_type("bool", t_base_type::TYPE_BOOL);
713 g_type_byte = new t_base_type("byte", t_base_type::TYPE_BYTE);
714 g_type_i16 = new t_base_type("i16", t_base_type::TYPE_I16);
715 g_type_i32 = new t_base_type("i32", t_base_type::TYPE_I32);
716 g_type_i64 = new t_base_type("i64", t_base_type::TYPE_I64);
717 g_type_double = new t_base_type("double", t_base_type::TYPE_DOUBLE);
Mark Sleee8540632006-05-30 09:24:40 +0000718
Mark Sleef5377b32006-10-10 01:42:59 +0000719 // Parse it!
Mark Sleef0712dc2006-10-25 19:03:57 +0000720 parse(program, NULL);
Mark Slee31985722006-05-24 21:45:31 +0000721
Mark Sleef0712dc2006-10-25 19:03:57 +0000722 // Generate it!
723 generate(program);
Mark Sleeb15a68b2006-06-07 06:46:24 +0000724
Mark Sleef0712dc2006-10-25 19:03:57 +0000725 // Clean up. Who am I kidding... this program probably orphans heap memory
726 // all over the place, but who cares because it is about to exit and it is
727 // all referenced and used by this wacky parse tree up until now anyways.
Mark Sleeb15a68b2006-06-07 06:46:24 +0000728
Mark Sleef0712dc2006-10-25 19:03:57 +0000729 delete program;
730 delete g_type_void;
731 delete g_type_string;
732 delete g_type_bool;
733 delete g_type_byte;
734 delete g_type_i16;
735 delete g_type_i32;
736 delete g_type_i64;
737 delete g_type_double;
Mark Slee31985722006-05-24 21:45:31 +0000738
739 // Finished
Mark Slee31985722006-05-24 21:45:31 +0000740 return 0;
741}