source: EcnlProtoTool/trunk/prototool/src/mirb.c

Last change on this file was 439, checked in by coas-nagasima, 4 years ago

mrubyを2.1.1に更新

  • Property svn:eol-style set to native
  • Property svn:mime-type set to text/x-csrc;charset=UTF-8
File size: 16.4 KB
Line 
1/*
2** mirb - Embeddable Interactive Ruby Shell
3**
4** This program takes code from the user in
5** an interactive way and executes it
6** immediately. It's a REPL...
7*/
8
9#include <mruby.h>
10
11#ifdef MRB_DISABLE_STDIO
12# error mruby-bin-mirb conflicts 'MRB_DISABLE_STDIO' configuration in your 'build_config.rb'
13#endif
14
15#include <mruby/array.h>
16#include <mruby/proc.h>
17#include <mruby/compile.h>
18#include <mruby/dump.h>
19#include <mruby/string.h>
20#include <mruby/variable.h>
21#include <mruby/throw.h>
22
23#include <stdlib.h>
24#include <string.h>
25#include <ctype.h>
26
27#include <signal.h>
28#include <setjmp.h>
29
30#ifdef ENABLE_READLINE
31#include <readline/readline.h>
32#include <readline/history.h>
33#define MIRB_ADD_HISTORY(line) add_history(line)
34#define MIRB_READLINE(ch) readline(ch)
35#if !defined(RL_READLINE_VERSION) || RL_READLINE_VERSION < 0x600
36/* libedit & older readline do not have rl_free() */
37#define MIRB_LINE_FREE(line) free(line)
38#else
39#define MIRB_LINE_FREE(line) rl_free(line)
40#endif
41#define MIRB_WRITE_HISTORY(path) write_history(path)
42#define MIRB_READ_HISTORY(path) read_history(path)
43#define MIRB_USING_HISTORY() using_history()
44#elif defined(ENABLE_LINENOISE)
45#define ENABLE_READLINE
46#include <linenoise.h>
47#define MIRB_ADD_HISTORY(line) linenoiseHistoryAdd(line)
48#define MIRB_READLINE(ch) linenoise(ch)
49#define MIRB_LINE_FREE(line) linenoiseFree(line)
50#define MIRB_WRITE_HISTORY(path) linenoiseHistorySave(path)
51#define MIRB_READ_HISTORY(path) linenoiseHistoryLoad(history_path)
52#define MIRB_USING_HISTORY()
53#endif
54
55#ifndef _WIN32
56#define MIRB_SIGSETJMP(env) sigsetjmp(env, 1)
57#define MIRB_SIGLONGJMP(env, val) siglongjmp(env, val)
58#define SIGJMP_BUF sigjmp_buf
59#else
60#define MIRB_SIGSETJMP(env) setjmp(env)
61#define MIRB_SIGLONGJMP(env, val) longjmp(env, val)
62#define SIGJMP_BUF jmp_buf
63#endif
64
65#ifdef ENABLE_READLINE
66
67static const char history_file_name[] = ".mirb_history";
68
69static char *
70get_history_path(mrb_state *mrb)
71{
72 char *path = NULL;
73 const char *home = getenv("HOME");
74
75#ifdef _WIN32
76 if (home != NULL) {
77 home = getenv("USERPROFILE");
78 }
79#endif
80
81 if (home != NULL) {
82 int len = snprintf(NULL, 0, "%s/%s", home, history_file_name);
83 if (len >= 0) {
84 size_t size = len + 1;
85 path = (char *)mrb_malloc_simple(mrb, size);
86 if (path != NULL) {
87 int n = snprintf(path, size, "%s/%s", home, history_file_name);
88 if (n != len) {
89 mrb_free(mrb, path);
90 path = NULL;
91 }
92 }
93 }
94 }
95
96 return path;
97}
98
99#endif
100
101static void
102p(mrb_state *mrb, mrb_value obj, int prompt)
103{
104 mrb_value val;
105 char* msg;
106
107 val = mrb_funcall(mrb, obj, "inspect", 0);
108 if (prompt) {
109 if (!mrb->exc) {
110 fputs(" => ", stdout);
111 }
112 else {
113 val = mrb_funcall(mrb, mrb_obj_value(mrb->exc), "inspect", 0);
114 }
115 }
116 if (!mrb_string_p(val)) {
117 val = mrb_obj_as_string(mrb, obj);
118 }
119 msg = mrb_locale_from_utf8(RSTRING_PTR(val), (int)RSTRING_LEN(val));
120 fwrite(msg, strlen(msg), 1, stdout);
121 mrb_locale_free(msg);
122 putc('\n', stdout);
123}
124
125/* Guess if the user might want to enter more
126 * or if he wants an evaluation of his code now */
127static mrb_bool
128is_code_block_open(struct mrb_parser_state *parser)
129{
130 mrb_bool code_block_open = FALSE;
131
132 /* check for heredoc */
133 if (parser->parsing_heredoc != NULL) return TRUE;
134
135 /* check for unterminated string */
136 if (parser->lex_strterm) return TRUE;
137
138 /* check if parser error are available */
139 if (0 < parser->nerr) {
140 const char unexpected_end[] = "syntax error, unexpected $end";
141 const char *message = parser->error_buffer[0].message;
142
143 /* a parser error occur, we have to check if */
144 /* we need to read one more line or if there is */
145 /* a different issue which we have to show to */
146 /* the user */
147
148 if (strncmp(message, unexpected_end, sizeof(unexpected_end) - 1) == 0) {
149 code_block_open = TRUE;
150 }
151 else if (strcmp(message, "syntax error, unexpected keyword_end") == 0) {
152 code_block_open = FALSE;
153 }
154 else if (strcmp(message, "syntax error, unexpected tREGEXP_BEG") == 0) {
155 code_block_open = FALSE;
156 }
157 return code_block_open;
158 }
159
160 switch (parser->lstate) {
161
162 /* all states which need more code */
163
164 case EXPR_BEG:
165 /* beginning of a statement, */
166 /* that means previous line ended */
167 code_block_open = FALSE;
168 break;
169 case EXPR_DOT:
170 /* a message dot was the last token, */
171 /* there has to come more */
172 code_block_open = TRUE;
173 break;
174 case EXPR_CLASS:
175 /* a class keyword is not enough! */
176 /* we need also a name of the class */
177 code_block_open = TRUE;
178 break;
179 case EXPR_FNAME:
180 /* a method name is necessary */
181 code_block_open = TRUE;
182 break;
183 case EXPR_VALUE:
184 /* if, elsif, etc. without condition */
185 code_block_open = TRUE;
186 break;
187
188 /* now all the states which are closed */
189
190 case EXPR_ARG:
191 /* an argument is the last token */
192 code_block_open = FALSE;
193 break;
194
195 /* all states which are unsure */
196
197 case EXPR_CMDARG:
198 break;
199 case EXPR_END:
200 /* an expression was ended */
201 break;
202 case EXPR_ENDARG:
203 /* closing parenthese */
204 break;
205 case EXPR_ENDFN:
206 /* definition end */
207 break;
208 case EXPR_MID:
209 /* jump keyword like break, return, ... */
210 break;
211 case EXPR_MAX_STATE:
212 /* don't know what to do with this token */
213 break;
214 default:
215 /* this state is unexpected! */
216 break;
217 }
218
219 return code_block_open;
220}
221
222struct _args {
223 FILE *rfp;
224 mrb_bool verbose : 1;
225 mrb_bool debug : 1;
226 int argc;
227 char** argv;
228 int libc;
229 char **libv;
230};
231
232static void
233usage(const char *name)
234{
235 static const char *const usage_msg[] = {
236 "switches:",
237 "-d set $DEBUG to true (same as `mruby -d`)",
238 "-r library same as `mruby -r`",
239 "-v print version number, then run in verbose mode",
240 "--verbose run in verbose mode",
241 "--version print the version",
242 "--copyright print the copyright",
243 NULL
244 };
245 const char *const *p = usage_msg;
246
247 printf("Usage: %s [switches] [programfile] [arguments]\n", name);
248 while (*p)
249 printf(" %s\n", *p++);
250}
251
252static char *
253dup_arg_item(mrb_state *mrb, const char *item)
254{
255 size_t buflen = strlen(item) + 1;
256 char *buf = (char*)mrb_malloc(mrb, buflen);
257 memcpy(buf, item, buflen);
258 return buf;
259}
260
261static int
262parse_args(mrb_state *mrb, int argc, char **argv, struct _args *args)
263{
264 char **origargv = argv;
265 static const struct _args args_zero = { 0 };
266
267 *args = args_zero;
268
269 for (argc--,argv++; argc > 0; argc--,argv++) {
270 char *item;
271 if (argv[0][0] != '-') break;
272
273 item = argv[0] + 1;
274 switch (*item++) {
275 case 'd':
276 args->debug = TRUE;
277 break;
278 case 'r':
279 if (!item[0]) {
280 if (argc <= 1) {
281 printf("%s: No library specified for -r\n", *origargv);
282 return EXIT_FAILURE;
283 }
284 argc--; argv++;
285 item = argv[0];
286 }
287 if (args->libc == 0) {
288 args->libv = (char**)mrb_malloc(mrb, sizeof(char*));
289 }
290 else {
291 args->libv = (char**)mrb_realloc(mrb, args->libv, sizeof(char*) * (args->libc + 1));
292 }
293 args->libv[args->libc++] = dup_arg_item(mrb, item);
294 break;
295 case 'v':
296 if (!args->verbose) mrb_show_version(mrb);
297 args->verbose = TRUE;
298 break;
299 case '-':
300 if (strcmp((*argv) + 2, "version") == 0) {
301 mrb_show_version(mrb);
302 exit(EXIT_SUCCESS);
303 }
304 else if (strcmp((*argv) + 2, "verbose") == 0) {
305 args->verbose = TRUE;
306 break;
307 }
308 else if (strcmp((*argv) + 2, "copyright") == 0) {
309 mrb_show_copyright(mrb);
310 exit(EXIT_SUCCESS);
311 }
312 default:
313 return EXIT_FAILURE;
314 }
315 }
316
317 if (args->rfp == NULL) {
318 if (*argv != NULL) {
319 args->rfp = fopen(argv[0], "r");
320 if (args->rfp == NULL) {
321 printf("Cannot open program file. (%s)\n", *argv);
322 return EXIT_FAILURE;
323 }
324 argc--; argv++;
325 }
326 }
327 args->argv = (char **)mrb_realloc(mrb, args->argv, sizeof(char*) * (argc + 1));
328 memcpy(args->argv, argv, (argc+1) * sizeof(char*));
329 args->argc = argc;
330
331 return EXIT_SUCCESS;
332}
333
334static void
335cleanup(mrb_state *mrb, struct _args *args)
336{
337 if (args->rfp)
338 fclose(args->rfp);
339 mrb_free(mrb, args->argv);
340 if (args->libc) {
341 while (args->libc--) {
342 mrb_free(mrb, args->libv[args->libc]);
343 }
344 mrb_free(mrb, args->libv);
345 }
346 mrb_close(mrb);
347}
348
349/* Print a short remark for the user */
350static void
351print_hint(void)
352{
353 printf("mirb - Embeddable Interactive Ruby Shell\n\n");
354}
355
356#ifndef ENABLE_READLINE
357/* Print the command line prompt of the REPL */
358static void
359print_cmdline(int code_block_open)
360{
361 if (code_block_open) {
362 printf("* ");
363 }
364 else {
365 printf("> ");
366 }
367 fflush(stdout);
368}
369#endif
370
371void mrb_codedump_all(mrb_state*, struct RProc*);
372
373static int
374check_keyword(const char *buf, const char *word)
375{
376 const char *p = buf;
377 size_t len = strlen(word);
378
379 /* skip preceding spaces */
380 while (*p && ISSPACE(*p)) {
381 p++;
382 }
383 /* check keyword */
384 if (strncmp(p, word, len) != 0) {
385 return 0;
386 }
387 p += len;
388 /* skip trailing spaces */
389 while (*p) {
390 if (!ISSPACE(*p)) return 0;
391 p++;
392 }
393 return 1;
394}
395
396
397#ifndef ENABLE_READLINE
398volatile sig_atomic_t input_canceled = 0;
399void
400ctrl_c_handler(int signo)
401{
402 input_canceled = 1;
403}
404#else
405SIGJMP_BUF ctrl_c_buf;
406void
407ctrl_c_handler(int signo)
408{
409 MIRB_SIGLONGJMP(ctrl_c_buf, 1);
410}
411#endif
412
413#ifndef DISABLE_MIRB_UNDERSCORE
414void decl_lv_underscore(mrb_state *mrb, mrbc_context *cxt)
415{
416 struct RProc *proc;
417 struct mrb_parser_state *parser;
418
419 parser = mrb_parse_string(mrb, "_=nil", cxt);
420 if (parser == NULL) {
421 fputs("create parser state error\n", stderr);
422 mrb_close(mrb);
423 exit(EXIT_FAILURE);
424 }
425
426 proc = mrb_generate_code(mrb, parser);
427 mrb_vm_run(mrb, proc, mrb_top_self(mrb), 0);
428
429 mrb_parser_free(parser);
430}
431#endif
432
433int
434mirb_main(int argc, char **argv)
435{
436 char ruby_code[4096] = { 0 };
437 char last_code_line[1024] = { 0 };
438#ifndef ENABLE_READLINE
439 int last_char;
440 size_t char_index;
441#else
442 char *history_path;
443 char* line;
444#endif
445 mrbc_context *cxt;
446 struct mrb_parser_state *parser;
447 mrb_state *mrb;
448 mrb_value result;
449 struct _args args;
450 mrb_value ARGV;
451 int n;
452 int i;
453 mrb_bool code_block_open = FALSE;
454 int ai;
455 unsigned int stack_keep = 0;
456
457 /* new interpreter instance */
458 mrb = mrb_open();
459 if (mrb == NULL) {
460 fputs("Invalid mrb interpreter, exiting mirb\n", stderr);
461 return EXIT_FAILURE;
462 }
463
464 n = parse_args(mrb, argc, argv, &args);
465 if (n == EXIT_FAILURE) {
466 cleanup(mrb, &args);
467 usage(argv[0]);
468 return n;
469 }
470
471 ARGV = mrb_ary_new_capa(mrb, args.argc);
472 for (i = 0; i < args.argc; i++) {
473 char* utf8 = mrb_utf8_from_locale(args.argv[i], -1);
474 if (utf8) {
475 mrb_ary_push(mrb, ARGV, mrb_str_new_cstr(mrb, utf8));
476 mrb_utf8_free(utf8);
477 }
478 }
479 mrb_define_global_const(mrb, "ARGV", ARGV);
480 mrb_gv_set(mrb, mrb_intern_lit(mrb, "$DEBUG"), mrb_bool_value(args.debug));
481
482#ifdef ENABLE_READLINE
483 history_path = get_history_path(mrb);
484 if (history_path == NULL) {
485 fputs("failed to get history path\n", stderr);
486 mrb_close(mrb);
487 return EXIT_FAILURE;
488 }
489
490 MIRB_USING_HISTORY();
491 MIRB_READ_HISTORY(history_path);
492#endif
493
494 print_hint();
495
496 cxt = mrbc_context_new(mrb);
497
498#ifndef DISABLE_MIRB_UNDERSCORE
499 decl_lv_underscore(mrb, cxt);
500#endif
501
502 /* Load libraries */
503 for (i = 0; i < args.libc; i++) {
504 FILE *lfp = fopen(args.libv[i], "r");
505 if (lfp == NULL) {
506 printf("Cannot open library file. (%s)\n", args.libv[i]);
507 cleanup(mrb, &args);
508 return EXIT_FAILURE;
509 }
510 mrb_load_file_cxt(mrb, lfp, cxt);
511 fclose(lfp);
512 }
513
514 cxt->capture_errors = TRUE;
515 cxt->lineno = 1;
516 mrbc_filename(mrb, cxt, "(mirb)");
517 if (args.verbose) cxt->dump_result = TRUE;
518
519 ai = mrb_gc_arena_save(mrb);
520
521 while (TRUE) {
522 char *utf8;
523 struct mrb_jmpbuf c_jmp;
524
525 MRB_TRY(&c_jmp);
526 mrb->jmp = &c_jmp;
527 if (args.rfp) {
528 if (fgets(last_code_line, sizeof(last_code_line)-1, args.rfp) != NULL)
529 goto done;
530 break;
531 }
532
533#ifndef ENABLE_READLINE
534 print_cmdline(code_block_open);
535
536 signal(SIGINT, ctrl_c_handler);
537 char_index = 0;
538 while ((last_char = getchar()) != '\n') {
539 if (last_char == EOF) break;
540 if (char_index >= sizeof(last_code_line)-2) {
541 fputs("input string too long\n", stderr);
542 continue;
543 }
544 last_code_line[char_index++] = last_char;
545 }
546 signal(SIGINT, SIG_DFL);
547 if (input_canceled) {
548 ruby_code[0] = '\0';
549 last_code_line[0] = '\0';
550 code_block_open = FALSE;
551 puts("^C");
552 input_canceled = 0;
553 continue;
554 }
555 if (last_char == EOF) {
556 fputs("\n", stdout);
557 break;
558 }
559
560 last_code_line[char_index++] = '\n';
561 last_code_line[char_index] = '\0';
562#else
563 if (MIRB_SIGSETJMP(ctrl_c_buf) == 0) {
564 ;
565 }
566 else {
567 ruby_code[0] = '\0';
568 last_code_line[0] = '\0';
569 code_block_open = FALSE;
570 puts("^C");
571 }
572 signal(SIGINT, ctrl_c_handler);
573 line = MIRB_READLINE(code_block_open ? "* " : "> ");
574 signal(SIGINT, SIG_DFL);
575
576 if (line == NULL) {
577 printf("\n");
578 break;
579 }
580 if (strlen(line) > sizeof(last_code_line)-2) {
581 fputs("input string too long\n", stderr);
582 continue;
583 }
584 strcpy(last_code_line, line);
585 strcat(last_code_line, "\n");
586 MIRB_ADD_HISTORY(line);
587 MIRB_LINE_FREE(line);
588#endif
589
590 done:
591 if (code_block_open) {
592 if (strlen(ruby_code)+strlen(last_code_line) > sizeof(ruby_code)-1) {
593 fputs("concatenated input string too long\n", stderr);
594 continue;
595 }
596 strcat(ruby_code, last_code_line);
597 }
598 else {
599 if (check_keyword(last_code_line, "quit") || check_keyword(last_code_line, "exit")) {
600 break;
601 }
602 strcpy(ruby_code, last_code_line);
603 }
604
605 utf8 = mrb_utf8_from_locale(ruby_code, -1);
606 if (!utf8) abort();
607
608 /* parse code */
609 parser = mrb_parser_new(mrb);
610 if (parser == NULL) {
611 fputs("create parser state error\n", stderr);
612 break;
613 }
614 parser->s = utf8;
615 parser->send = utf8 + strlen(utf8);
616 parser->lineno = cxt->lineno;
617 mrb_parser_parse(parser, cxt);
618 code_block_open = is_code_block_open(parser);
619 mrb_utf8_free(utf8);
620
621 if (code_block_open) {
622 /* no evaluation of code */
623 }
624 else {
625 if (0 < parser->nwarn) {
626 /* warning */
627 char* msg = mrb_locale_from_utf8(parser->warn_buffer[0].message, -1);
628 printf("line %d: %s\n", parser->warn_buffer[0].lineno, msg);
629 mrb_locale_free(msg);
630 }
631 if (0 < parser->nerr) {
632 /* syntax error */
633 char* msg = mrb_locale_from_utf8(parser->error_buffer[0].message, -1);
634 printf("line %d: %s\n", parser->error_buffer[0].lineno, msg);
635 mrb_locale_free(msg);
636 }
637 else {
638 /* generate bytecode */
639 struct RProc *proc = mrb_generate_code(mrb, parser);
640 if (proc == NULL) {
641 fputs("codegen error\n", stderr);
642 mrb_parser_free(parser);
643 break;
644 }
645
646 if (args.verbose) {
647 mrb_codedump_all(mrb, proc);
648 }
649 /* adjust stack length of toplevel environment */
650 if (mrb->c->cibase->env) {
651 struct REnv *e = mrb->c->cibase->env;
652 if (e && MRB_ENV_STACK_LEN(e) < proc->body.irep->nlocals) {
653 MRB_ENV_SET_STACK_LEN(e, proc->body.irep->nlocals);
654 }
655 }
656 /* pass a proc for evaluation */
657 /* evaluate the bytecode */
658 result = mrb_vm_run(mrb,
659 proc,
660 mrb_top_self(mrb),
661 stack_keep);
662 stack_keep = proc->body.irep->nlocals;
663 /* did an exception occur? */
664 if (mrb->exc) {
665 p(mrb, mrb_obj_value(mrb->exc), 0);
666 mrb->exc = 0;
667 }
668 else {
669 /* no */
670 if (!mrb_respond_to(mrb, result, mrb_intern_lit(mrb, "inspect"))){
671 result = mrb_any_to_s(mrb, result);
672 }
673 p(mrb, result, 1);
674#ifndef DISABLE_MIRB_UNDERSCORE
675 *(mrb->c->stack + 1) = result;
676#endif
677 }
678 }
679 ruby_code[0] = '\0';
680 last_code_line[0] = '\0';
681 mrb_gc_arena_restore(mrb, ai);
682 }
683 mrb_parser_free(parser);
684 cxt->lineno++;
685 MRB_CATCH(&c_jmp) {
686 p(mrb, mrb_obj_value(mrb->exc), 0);
687 mrb->exc = 0;
688 }
689 MRB_END_EXC(&c_jmp);
690 }
691
692#ifdef ENABLE_READLINE
693 MIRB_WRITE_HISTORY(history_path);
694 mrb_free(mrb, history_path);
695#endif
696
697 if (args.rfp) fclose(args.rfp);
698 mrb_free(mrb, args.argv);
699 if (args.libv) {
700 for (i = 0; i < args.libc; ++i) {
701 mrb_free(mrb, args.libv[i]);
702 }
703 mrb_free(mrb, args.libv);
704 }
705 mrbc_context_free(mrb, cxt);
706 mrb_close(mrb);
707
708 return 0;
709}
Note: See TracBrowser for help on using the repository browser.