1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <mruby.h>
5 #include <mruby/compile.h>
6 #include <mruby/dump.h>
7 #include <mruby/proc.h>
8
9 #define RITEBIN_EXT ".mrb"
10 #define C_EXT ".c"
11
12 struct mrbc_args {
13 int argc;
14 char **argv;
15 int idx;
16 const char *prog;
17 const char *outfile;
18 const char *initname;
19 mrb_bool check_syntax : 1;
20 mrb_bool verbose : 1;
21 mrb_bool remove_lv : 1;
22 unsigned int flags : 4;
23 };
24
25 static void
usage(const char * name)26 usage(const char *name)
27 {
28 static const char *const usage_msg[] = {
29 "switches:",
30 "-c check syntax only",
31 "-o<outfile> place the output into <outfile>",
32 "-v print version number, then turn on verbose mode",
33 "-g produce debugging information",
34 "-B<symbol> binary <symbol> output in C language format",
35 "-e generate little endian iseq data",
36 "-E generate big endian iseq data",
37 "--remove-lv remove local variables",
38 "--verbose run at verbose mode",
39 "--version print the version",
40 "--copyright print the copyright",
41 NULL
42 };
43 const char *const *p = usage_msg;
44
45 printf("Usage: %s [switches] programfile\n", name);
46 while (*p)
47 printf(" %s\n", *p++);
48 }
49
50 static char *
get_outfilename(mrb_state * mrb,char * infile,const char * ext)51 get_outfilename(mrb_state *mrb, char *infile, const char *ext)
52 {
53 size_t infilelen;
54 size_t extlen;
55 char *outfile;
56 char *p;
57
58 infilelen = strlen(infile);
59 extlen = strlen(ext);
60 outfile = (char*)mrb_malloc(mrb, infilelen + extlen + 1);
61 memcpy(outfile, infile, infilelen + 1);
62 if (*ext) {
63 if ((p = strrchr(outfile, '.')) == NULL)
64 p = outfile + infilelen;
65 memcpy(p, ext, extlen + 1);
66 }
67
68 return outfile;
69 }
70
71 static int
parse_args(mrb_state * mrb,int argc,char ** argv,struct mrbc_args * args)72 parse_args(mrb_state *mrb, int argc, char **argv, struct mrbc_args *args)
73 {
74 char *outfile = NULL;
75 static const struct mrbc_args args_zero = { 0 };
76 int i;
77
78 *args = args_zero;
79 args->argc = argc;
80 args->argv = argv;
81 args->prog = argv[0];
82
83 for (i=1; i<argc; i++) {
84 if (argv[i][0] == '-') {
85 switch ((argv[i])[1]) {
86 case 'o':
87 if (args->outfile) {
88 fprintf(stderr, "%s: an output file is already specified. (%s)\n",
89 args->prog, outfile);
90 return -1;
91 }
92 if (argv[i][2] == '\0' && argv[i+1]) {
93 i++;
94 args->outfile = get_outfilename(mrb, argv[i], "");
95 }
96 else {
97 args->outfile = get_outfilename(mrb, argv[i] + 2, "");
98 }
99 break;
100 case 'B':
101 if (argv[i][2] == '\0' && argv[i+1]) {
102 i++;
103 args->initname = argv[i];
104 }
105 else {
106 args->initname = argv[i]+2;
107 }
108 if (*args->initname == '\0') {
109 fprintf(stderr, "%s: function name is not specified.\n", args->prog);
110 return -1;
111 }
112 break;
113 case 'c':
114 args->check_syntax = TRUE;
115 break;
116 case 'v':
117 if (!args->verbose) mrb_show_version(mrb);
118 args->verbose = TRUE;
119 break;
120 case 'g':
121 args->flags |= DUMP_DEBUG_INFO;
122 break;
123 case 'E':
124 args->flags = DUMP_ENDIAN_BIG | (args->flags & ~DUMP_ENDIAN_MASK);
125 break;
126 case 'e':
127 args->flags = DUMP_ENDIAN_LIL | (args->flags & ~DUMP_ENDIAN_MASK);
128 break;
129 case 'h':
130 return -1;
131 case '-':
132 if (argv[i][1] == '\n') {
133 return i;
134 }
135 if (strcmp(argv[i] + 2, "version") == 0) {
136 mrb_show_version(mrb);
137 exit(EXIT_SUCCESS);
138 }
139 else if (strcmp(argv[i] + 2, "verbose") == 0) {
140 args->verbose = TRUE;
141 break;
142 }
143 else if (strcmp(argv[i] + 2, "copyright") == 0) {
144 mrb_show_copyright(mrb);
145 exit(EXIT_SUCCESS);
146 }
147 else if (strcmp(argv[i] + 2, "remove-lv") == 0) {
148 args->remove_lv = TRUE;
149 break;
150 }
151 return -1;
152 default:
153 return i;
154 }
155 }
156 else {
157 break;
158 }
159 }
160 if (args->verbose && args->initname && (args->flags & DUMP_ENDIAN_MASK) == 0) {
161 fprintf(stderr, "%s: generating %s endian C file. specify -e/-E for cross compiling.\n",
162 args->prog, bigendian_p() ? "big" : "little");
163 }
164 return i;
165 }
166
167 static void
cleanup(mrb_state * mrb,struct mrbc_args * args)168 cleanup(mrb_state *mrb, struct mrbc_args *args)
169 {
170 mrb_free(mrb, (void*)args->outfile);
171 mrb_close(mrb);
172 }
173
174 static int
partial_hook(struct mrb_parser_state * p)175 partial_hook(struct mrb_parser_state *p)
176 {
177 mrbc_context *c = p->cxt;
178 struct mrbc_args *args = (struct mrbc_args *)c->partial_data;
179 const char *fn;
180
181 if (p->f) fclose(p->f);
182 if (args->idx >= args->argc) {
183 p->f = NULL;
184 return -1;
185 }
186 fn = args->argv[args->idx++];
187 p->f = fopen(fn, "r");
188 if (p->f == NULL) {
189 fprintf(stderr, "%s: cannot open program file. (%s)\n", args->prog, fn);
190 return -1;
191 }
192 mrb_parser_set_filename(p, fn);
193 return 0;
194 }
195
196 static mrb_value
load_file(mrb_state * mrb,struct mrbc_args * args)197 load_file(mrb_state *mrb, struct mrbc_args *args)
198 {
199 mrbc_context *c;
200 mrb_value result;
201 char *input = args->argv[args->idx];
202 FILE *infile;
203 mrb_bool need_close = FALSE;
204
205 c = mrbc_context_new(mrb);
206 if (args->verbose)
207 c->dump_result = TRUE;
208 c->no_exec = TRUE;
209 if (input[0] == '-' && input[1] == '\0') {
210 infile = stdin;
211 }
212 else {
213 need_close = TRUE;
214 if ((infile = fopen(input, "r")) == NULL) {
215 fprintf(stderr, "%s: cannot open program file. (%s)\n", args->prog, input);
216 return mrb_nil_value();
217 }
218 }
219 mrbc_filename(mrb, c, input);
220 args->idx++;
221 if (args->idx < args->argc) {
222 need_close = FALSE;
223 mrbc_partial_hook(mrb, c, partial_hook, (void*)args);
224 }
225
226 result = mrb_load_file_cxt(mrb, infile, c);
227 if (need_close) fclose(infile);
228 mrbc_context_free(mrb, c);
229 if (mrb_undef_p(result)) {
230 return mrb_nil_value();
231 }
232 return result;
233 }
234
235 static int
dump_file(mrb_state * mrb,FILE * wfp,const char * outfile,struct RProc * proc,struct mrbc_args * args)236 dump_file(mrb_state *mrb, FILE *wfp, const char *outfile, struct RProc *proc, struct mrbc_args *args)
237 {
238 int n = MRB_DUMP_OK;
239 mrb_irep *irep = proc->body.irep;
240
241 if (args->remove_lv) {
242 mrb_irep_remove_lv(mrb, irep);
243 }
244 if (args->initname) {
245 n = mrb_dump_irep_cfunc(mrb, irep, args->flags, wfp, args->initname);
246 if (n == MRB_DUMP_INVALID_ARGUMENT) {
247 fprintf(stderr, "%s: invalid C language symbol name\n", args->initname);
248 }
249 }
250 else {
251 n = mrb_dump_irep_binary(mrb, irep, args->flags, wfp);
252 }
253 if (n != MRB_DUMP_OK) {
254 fprintf(stderr, "%s: error in mrb dump (%s) %d\n", args->prog, outfile, n);
255 }
256 return n;
257 }
258
259 int
main(int argc,char ** argv)260 main(int argc, char **argv)
261 {
262 mrb_state *mrb = mrb_open();
263 int n, result;
264 struct mrbc_args args;
265 FILE *wfp;
266 mrb_value load;
267
268 if (mrb == NULL) {
269 fputs("Invalid mrb_state, exiting mrbc\n", stderr);
270 return EXIT_FAILURE;
271 }
272
273 n = parse_args(mrb, argc, argv, &args);
274 if (n < 0) {
275 cleanup(mrb, &args);
276 usage(argv[0]);
277 return EXIT_FAILURE;
278 }
279 if (n == argc) {
280 fprintf(stderr, "%s: no program file given\n", args.prog);
281 return EXIT_FAILURE;
282 }
283 if (args.outfile == NULL && !args.check_syntax) {
284 if (n + 1 == argc) {
285 args.outfile = get_outfilename(mrb, argv[n], args.initname ? C_EXT : RITEBIN_EXT);
286 }
287 else {
288 fprintf(stderr, "%s: output file should be specified to compile multiple files\n", args.prog);
289 return EXIT_FAILURE;
290 }
291 }
292
293 args.idx = n;
294 load = load_file(mrb, &args);
295 if (mrb_nil_p(load)) {
296 cleanup(mrb, &args);
297 return EXIT_FAILURE;
298 }
299 if (args.check_syntax) {
300 printf("%s:%s:Syntax OK\n", args.prog, argv[n]);
301 }
302
303 if (args.check_syntax) {
304 cleanup(mrb, &args);
305 return EXIT_SUCCESS;
306 }
307
308 if (args.outfile) {
309 if (strcmp("-", args.outfile) == 0) {
310 wfp = stdout;
311 }
312 else if ((wfp = fopen(args.outfile, "wb")) == NULL) {
313 fprintf(stderr, "%s: cannot open output file:(%s)\n", args.prog, args.outfile);
314 return EXIT_FAILURE;
315 }
316 }
317 else {
318 fprintf(stderr, "Output file is required\n");
319 return EXIT_FAILURE;
320 }
321 result = dump_file(mrb, wfp, args.outfile, mrb_proc_ptr(load), &args);
322 fclose(wfp);
323 cleanup(mrb, &args);
324 if (result != MRB_DUMP_OK) {
325 return EXIT_FAILURE;
326 }
327 return EXIT_SUCCESS;
328 }
329
330 void
mrb_init_mrblib(mrb_state * mrb)331 mrb_init_mrblib(mrb_state *mrb)
332 {
333 }
334
335 #ifndef DISABLE_GEMS
336 void
mrb_init_mrbgems(mrb_state * mrb)337 mrb_init_mrbgems(mrb_state *mrb)
338 {
339 }
340
341 void
mrb_final_mrbgems(mrb_state * mrb)342 mrb_final_mrbgems(mrb_state *mrb)
343 {
344 }
345 #endif
346