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

Last change on this file since 279 was 279, checked in by coas-nagasima, 7 years ago

ファイルを追加、更新。

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
  • Property svn:mime-type set to text/x-csrc
File size: 10.5 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 <stdlib.h>
10#include <string.h>
11#include <stdio.h>
12#include <ctype.h>
13
14#ifdef ENABLE_READLINE
15#include <readline/readline.h>
16#include <readline/history.h>
17#define MIRB_ADD_HISTORY(line) add_history(line)
18#define MIRB_READLINE(ch) readline(ch)
19#define MIRB_WRITE_HISTORY(path) write_history(path)
20#define MIRB_READ_HISTORY(path) read_history(path)
21#define MIRB_USING_HISTORY() using_history()
22#elif defined(ENABLE_LINENOISE)
23#define ENABLE_READLINE
24#include <linenoise.h>
25#define MIRB_ADD_HISTORY(line) linenoiseHistoryAdd(line)
26#define MIRB_READLINE(ch) linenoise(ch)
27#define MIRB_WRITE_HISTORY(path) linenoiseHistorySave(path)
28#define MIRB_READ_HISTORY(path) linenoiseHistoryLoad(history_path)
29#define MIRB_USING_HISTORY()
30#endif
31
32#include "mruby.h"
33#include "mruby/array.h"
34#include "mruby/proc.h"
35#include "mruby/compile.h"
36#include "mruby/string.h"
37
38#ifdef ENABLE_READLINE
39
40static const char history_file_name[] = ".mirb_history";
41
42static char *
43get_history_path(mrb_state *mrb)
44{
45 char *path = NULL;
46 const char *home = getenv("HOME");
47
48#ifdef _WIN32
49 if (home != NULL) {
50 home = getenv("USERPROFILE");
51 }
52#endif
53
54 if (home != NULL) {
55 int len = snprintf(NULL, 0, "%s/%s", home, history_file_name);
56 if (len >= 0) {
57 size_t size = len + 1;
58 path = (char *)mrb_malloc_simple(mrb, size);
59 if (path != NULL) {
60 int n = snprintf(path, size, "%s/%s", home, history_file_name);
61 if (n != len) {
62 mrb_free(mrb, path);
63 path = NULL;
64 }
65 }
66 }
67 }
68
69 return path;
70}
71
72#endif
73
74static void
75p(mrb_state *mrb, mrb_value obj, int prompt)
76{
77 mrb_value val;
78
79 val = mrb_funcall(mrb, obj, "inspect", 0);
80 if (prompt) {
81 if (!mrb->exc) {
82 fputs(" => ", stdout);
83 }
84 else {
85 val = mrb_funcall(mrb, mrb_obj_value(mrb->exc), "inspect", 0);
86 }
87 }
88 if (!mrb_string_p(val)) {
89 val = mrb_obj_as_string(mrb, obj);
90 }
91 fwrite(RSTRING_PTR(val), RSTRING_LEN(val), 1, stdout);
92 putc('\n', stdout);
93}
94
95/* Guess if the user might want to enter more
96 * or if he wants an evaluation of his code now */
97static mrb_bool
98is_code_block_open(struct mrb_parser_state *parser)
99{
100 mrb_bool code_block_open = FALSE;
101
102 /* check for heredoc */
103 if (parser->parsing_heredoc != NULL) return TRUE;
104 if (parser->heredoc_end_now) {
105 parser->heredoc_end_now = FALSE;
106 return FALSE;
107 }
108
109 /* check for unterminated string */
110 if (parser->lex_strterm) return TRUE;
111
112 /* check if parser error are available */
113 if (0 < parser->nerr) {
114 const char unexpected_end[] = "syntax error, unexpected $end";
115 const char *message = parser->error_buffer[0].message;
116
117 /* a parser error occur, we have to check if */
118 /* we need to read one more line or if there is */
119 /* a different issue which we have to show to */
120 /* the user */
121
122 if (strncmp(message, unexpected_end, sizeof(unexpected_end) - 1) == 0) {
123 code_block_open = TRUE;
124 }
125 else if (strcmp(message, "syntax error, unexpected keyword_end") == 0) {
126 code_block_open = FALSE;
127 }
128 else if (strcmp(message, "syntax error, unexpected tREGEXP_BEG") == 0) {
129 code_block_open = FALSE;
130 }
131 return code_block_open;
132 }
133
134 switch (parser->lstate) {
135
136 /* all states which need more code */
137
138 case EXPR_BEG:
139 /* beginning of a statement, */
140 /* that means previous line ended */
141 code_block_open = FALSE;
142 break;
143 case EXPR_DOT:
144 /* a message dot was the last token, */
145 /* there has to come more */
146 code_block_open = TRUE;
147 break;
148 case EXPR_CLASS:
149 /* a class keyword is not enough! */
150 /* we need also a name of the class */
151 code_block_open = TRUE;
152 break;
153 case EXPR_FNAME:
154 /* a method name is necessary */
155 code_block_open = TRUE;
156 break;
157 case EXPR_VALUE:
158 /* if, elsif, etc. without condition */
159 code_block_open = TRUE;
160 break;
161
162 /* now all the states which are closed */
163
164 case EXPR_ARG:
165 /* an argument is the last token */
166 code_block_open = FALSE;
167 break;
168
169 /* all states which are unsure */
170
171 case EXPR_CMDARG:
172 break;
173 case EXPR_END:
174 /* an expression was ended */
175 break;
176 case EXPR_ENDARG:
177 /* closing parenthese */
178 break;
179 case EXPR_ENDFN:
180 /* definition end */
181 break;
182 case EXPR_MID:
183 /* jump keyword like break, return, ... */
184 break;
185 case EXPR_MAX_STATE:
186 /* don't know what to do with this token */
187 break;
188 default:
189 /* this state is unexpected! */
190 break;
191 }
192
193 return code_block_open;
194}
195
196struct _args {
197 mrb_bool verbose : 1;
198 int argc;
199 char** argv;
200};
201
202static void
203usage(const char *name)
204{
205 static const char *const usage_msg[] = {
206 "switches:",
207 "-v print version number, then run in verbose mode",
208 "--verbose run in verbose mode",
209 "--version print the version",
210 "--copyright print the copyright",
211 NULL
212 };
213 const char *const *p = usage_msg;
214
215 printf("Usage: %s [switches]\n", name);
216 while (*p)
217 printf(" %s\n", *p++);
218}
219
220static int
221parse_args(mrb_state *mrb, int argc, char **argv, struct _args *args)
222{
223 static const struct _args args_zero = {0};
224
225 *args = args_zero;
226
227 for (argc--, argv++; argc > 0; argc--, argv++) {
228 char *item;
229 if (argv[0][0] != '-') break;
230
231 item = argv[0] + 1;
232 switch (*item++) {
233 case 'v':
234 if (!args->verbose) mrb_show_version(mrb);
235 args->verbose = TRUE;
236 break;
237 case '-':
238 if (strcmp((*argv) + 2, "version") == 0) {
239 mrb_show_version(mrb);
240 exit(EXIT_SUCCESS);
241 }
242 else if (strcmp((*argv) + 2, "verbose") == 0) {
243 args->verbose = TRUE;
244 break;
245 }
246 else if (strcmp((*argv) + 2, "copyright") == 0) {
247 mrb_show_copyright(mrb);
248 exit(EXIT_SUCCESS);
249 }
250 default:
251 return EXIT_FAILURE;
252 }
253 }
254 return EXIT_SUCCESS;
255}
256
257static void
258cleanup(mrb_state *mrb, struct _args *args)
259{
260 mrb_close(mrb);
261}
262
263/* Print a short remark for the user */
264static void
265print_hint(void)
266{
267 printf("mirb - Embeddable Interactive Ruby Shell\n\n");
268}
269
270#ifndef ENABLE_READLINE
271/* Print the command line prompt of the REPL */
272static void
273print_cmdline(int code_block_open)
274{
275 if (code_block_open) {
276 printf("* ");
277 }
278 else {
279 printf("> ");
280 }
281 fflush(stdout);
282}
283#endif
284
285void mrb_codedump_all(mrb_state*, struct RProc*);
286
287static int
288check_keyword(const char *buf, const char *word)
289{
290 const char *p = buf;
291 size_t len = strlen(word);
292
293 /* skip preceding spaces */
294 while (*p && isspace((unsigned char)*p)) {
295 p++;
296 }
297 /* check keyword */
298 if (strncmp(p, word, len) != 0) {
299 return 0;
300 }
301 p += len;
302 /* skip trailing spaces */
303 while (*p) {
304 if (!isspace((unsigned char)*p)) return 0;
305 p++;
306 }
307 return 1;
308}
309
310int
311mirb_main(int argc, char **argv)
312{
313 char ruby_code[1024] = {0};
314 char last_code_line[1024] = {0};
315#ifndef ENABLE_READLINE
316 int last_char;
317 int char_index;
318#else
319 char *history_path;
320#endif
321 mrbc_context *cxt;
322 struct mrb_parser_state *parser;
323 mrb_state *mrb;
324 mrb_value result;
325 struct _args args;
326 int n;
327 mrb_bool code_block_open = FALSE;
328 int ai;
329 unsigned int stack_keep = 0;
330
331 /* new interpreter instance */
332 mrb = mrb_open();
333 if (mrb == NULL) {
334 fputs("Invalid mrb interpreter, exiting mirb\n", stderr);
335 return EXIT_FAILURE;
336 }
337 mrb_define_global_const(mrb, "ARGV", mrb_ary_new_capa(mrb, 0));
338
339 n = parse_args(mrb, argc, argv, &args);
340 if (n == EXIT_FAILURE) {
341 cleanup(mrb, &args);
342 usage(argv[0]);
343 return n;
344 }
345
346#ifdef ENABLE_READLINE
347 history_path = get_history_path(mrb);
348 if (history_path == NULL) {
349 fputs("failed to get history path\n", stderr);
350 mrb_close(mrb);
351 return EXIT_FAILURE;
352 }
353
354 MIRB_USING_HISTORY();
355 MIRB_READ_HISTORY(history_path);
356#endif
357
358 print_hint();
359
360 cxt = mrbc_context_new(mrb);
361 cxt->capture_errors = TRUE;
362 cxt->lineno = 1;
363 mrbc_filename(mrb, cxt, "(mirb)");
364 if (args.verbose) cxt->dump_result = TRUE;
365
366 ai = mrb_gc_arena_save(mrb);
367
368 while (TRUE) {
369 char *utf8;
370
371#ifndef ENABLE_READLINE
372 print_cmdline(code_block_open);
373
374 char_index = 0;
375 while ((last_char = getchar()) != '\n') {
376 if (last_char == EOF) break;
377 if (char_index > sizeof(last_code_line) - 2) {
378 fputs("input string too long\n", stderr);
379 continue;
380 }
381 last_code_line[char_index++] = last_char;
382 }
383 if (last_char == EOF) {
384 fputs("\n", stdout);
385 break;
386 }
387
388 last_code_line[char_index++] = '\n';
389 last_code_line[char_index] = '\0';
390#else
391 char* line = MIRB_READLINE(code_block_open ? "* " : "> ");
392 if (line == NULL) {
393 printf("\n");
394 break;
395 }
396 if (strlen(line) > sizeof(last_code_line) - 2) {
397 fputs("input string too long\n", stderr);
398 continue;
399 }
400 strlcpy(last_code_line, line, sizeof(last_code_line));
401 strlcat(last_code_line, "\n", sizeof(last_code_line));
402 MIRB_ADD_HISTORY(line);
403 free(line);
404#endif
405
406 if (code_block_open) {
407 if (strlen(ruby_code) + strlen(last_code_line) > sizeof(ruby_code) - 1) {
408 fputs("concatenated input string too long\n", stderr);
409 continue;
410 }
411 strlcat(ruby_code, last_code_line, sizeof(ruby_code));
412 }
413 else {
414 if (check_keyword(last_code_line, "quit") || check_keyword(last_code_line, "exit")) {
415 break;
416 }
417 strlcpy(ruby_code, last_code_line, sizeof(ruby_code));
418 }
419
420 utf8 = mrb_utf8_from_locale(ruby_code, -1);
421 if (!utf8) abort();
422
423 /* parse code */
424 parser = mrb_parser_new(mrb);
425 if (parser == NULL) {
426 fputs("create parser state error\n", stderr);
427 break;
428 }
429 parser->s = utf8;
430 parser->send = utf8 + strlen(utf8);
431 parser->lineno = cxt->lineno;
432 mrb_parser_parse(parser, cxt);
433 code_block_open = is_code_block_open(parser);
434 mrb_utf8_free(utf8);
435
436 if (code_block_open) {
437 /* no evaluation of code */
438 }
439 else {
440 if (0 < parser->nerr) {
441 /* syntax error */
442 printf("line %d: %s\n", parser->error_buffer[0].lineno, parser->error_buffer[0].message);
443 }
444 else {
445 /* generate bytecode */
446 struct RProc *proc = mrb_generate_code(mrb, parser);
447 if (proc == NULL) {
448 fputs("codegen error\n", stderr);
449 mrb_parser_free(parser);
450 break;
451 }
452
453 if (args.verbose) {
454 mrb_codedump_all(mrb, proc);
455 }
456 /* pass a proc for evaulation */
457 /* evaluate the bytecode */
458 result = mrb_context_run(mrb,
459 proc,
460 mrb_top_self(mrb),
461 stack_keep);
462 stack_keep = proc->body.irep->nlocals;
463 /* did an exception occur? */
464 if (mrb->exc) {
465 p(mrb, mrb_obj_value(mrb->exc), 0);
466 mrb->exc = 0;
467 }
468 else {
469 /* no */
470 if (!mrb_respond_to(mrb, result, mrb_intern_lit(mrb, "inspect"))) {
471 result = mrb_any_to_s(mrb, result);
472 }
473 p(mrb, result, 1);
474 }
475 }
476 ruby_code[0] = '\0';
477 last_code_line[0] = '\0';
478 mrb_gc_arena_restore(mrb, ai);
479 }
480 mrb_parser_free(parser);
481 cxt->lineno++;
482 }
483
484#ifdef ENABLE_READLINE
485 MIRB_WRITE_HISTORY(history_path);
486 mrb_free(mrb, history_path);
487#endif
488
489 mrbc_context_free(mrb, cxt);
490 mrb_close(mrb);
491
492 return 0;
493}
Note: See TracBrowser for help on using the repository browser.