Groonga 3.0.9 Source Code Document
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
mirb.c
Go to the documentation of this file.
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 
12 #include <mruby.h>
13 #include "mruby/array.h"
14 #include <mruby/proc.h>
15 #include <mruby/data.h>
16 #include <mruby/compile.h>
17 #ifdef ENABLE_READLINE
18 #include <limits.h>
19 #include <readline/readline.h>
20 #include <readline/history.h>
21 #endif
22 #include <mruby/string.h>
23 
24 
25 #ifdef ENABLE_READLINE
26 static const char *history_file_name = ".mirb_history";
27 char history_path[PATH_MAX];
28 #endif
29 
30 
31 static void
32 p(mrb_state *mrb, mrb_value obj, int prompt)
33 {
34  obj = mrb_funcall(mrb, obj, "inspect", 0);
35  if (prompt) {
36  if (!mrb->exc) {
37  fputs(" => ", stdout);
38  }
39  else {
40  obj = mrb_funcall(mrb, mrb_obj_value(mrb->exc), "inspect", 0);
41  }
42  }
43  fwrite(RSTRING_PTR(obj), RSTRING_LEN(obj), 1, stdout);
44  putc('\n', stdout);
45 }
46 
47 /* Guess if the user might want to enter more
48  * or if he wants an evaluation of his code now */
51 {
52  int code_block_open = FALSE;
53 
54  /* check for heredoc */
55  if (parser->parsing_heredoc != NULL) return TRUE;
56  if (parser->heredoc_end_now) {
57  parser->heredoc_end_now = FALSE;
58  return FALSE;
59  }
60 
61  /* check if parser error are available */
62  if (0 < parser->nerr) {
63  const char *unexpected_end = "syntax error, unexpected $end";
64  const char *message = parser->error_buffer[0].message;
65 
66  /* a parser error occur, we have to check if */
67  /* we need to read one more line or if there is */
68  /* a different issue which we have to show to */
69  /* the user */
70 
71  if (strncmp(message, unexpected_end, strlen(unexpected_end)) == 0) {
72  code_block_open = TRUE;
73  }
74  else if (strcmp(message, "syntax error, unexpected keyword_end") == 0) {
75  code_block_open = FALSE;
76  }
77  else if (strcmp(message, "syntax error, unexpected tREGEXP_BEG") == 0) {
78  code_block_open = FALSE;
79  }
80  return code_block_open;
81  }
82 
83  /* check for unterminated string */
84  if (parser->lex_strterm) return TRUE;
85 
86  switch (parser->lstate) {
87 
88  /* all states which need more code */
89 
90  case EXPR_BEG:
91  /* an expression was just started, */
92  /* we can't end it like this */
93  code_block_open = TRUE;
94  break;
95  case EXPR_DOT:
96  /* a message dot was the last token, */
97  /* there has to come more */
98  code_block_open = TRUE;
99  break;
100  case EXPR_CLASS:
101  /* a class keyword is not enough! */
102  /* we need also a name of the class */
103  code_block_open = TRUE;
104  break;
105  case EXPR_FNAME:
106  /* a method name is necessary */
107  code_block_open = TRUE;
108  break;
109  case EXPR_VALUE:
110  /* if, elsif, etc. without condition */
111  code_block_open = TRUE;
112  break;
113 
114  /* now all the states which are closed */
115 
116  case EXPR_ARG:
117  /* an argument is the last token */
118  code_block_open = FALSE;
119  break;
120 
121  /* all states which are unsure */
122 
123  case EXPR_CMDARG:
124  break;
125  case EXPR_END:
126  /* an expression was ended */
127  break;
128  case EXPR_ENDARG:
129  /* closing parenthese */
130  break;
131  case EXPR_ENDFN:
132  /* definition end */
133  break;
134  case EXPR_MID:
135  /* jump keyword like break, return, ... */
136  break;
137  case EXPR_MAX_STATE:
138  /* don't know what to do with this token */
139  break;
140  default:
141  /* this state is unexpected! */
142  break;
143  }
144 
145  return code_block_open;
146 }
147 
150 
151 struct _args {
153  int argc;
154  char** argv;
155 };
156 
157 static void
158 usage(const char *name)
159 {
160  static const char *const usage_msg[] = {
161  "switches:",
162  "-v print version number, then run in verbose mode",
163  "--verbose run in verbose mode",
164  "--version print the version",
165  "--copyright print the copyright",
166  NULL
167  };
168  const char *const *p = usage_msg;
169 
170  printf("Usage: %s [switches]\n", name);
171  while (*p)
172  printf(" %s\n", *p++);
173 }
174 
175 static int
176 parse_args(mrb_state *mrb, int argc, char **argv, struct _args *args)
177 {
178  static const struct _args args_zero = { 0 };
179 
180  *args = args_zero;
181 
182  for (argc--,argv++; argc > 0; argc--,argv++) {
183  char *item;
184  if (argv[0][0] != '-') break;
185 
186  item = argv[0] + 1;
187  switch (*item++) {
188  case 'v':
189  if (!args->verbose) mrb_show_version(mrb);
190  args->verbose = 1;
191  break;
192  case '-':
193  if (strcmp((*argv) + 2, "version") == 0) {
194  mrb_show_version(mrb);
195  exit(EXIT_SUCCESS);
196  }
197  else if (strcmp((*argv) + 2, "verbose") == 0) {
198  args->verbose = 1;
199  break;
200  }
201  else if (strcmp((*argv) + 2, "copyright") == 0) {
202  mrb_show_copyright(mrb);
203  exit(EXIT_SUCCESS);
204  }
205  default:
206  return EXIT_FAILURE;
207  }
208  }
209  return EXIT_SUCCESS;
210 }
211 
212 static void
213 cleanup(mrb_state *mrb, struct _args *args)
214 {
215  mrb_close(mrb);
216 }
217 
218 /* Print a short remark for the user */
219 static void
220 print_hint(void)
221 {
222  printf("mirb - Embeddable Interactive Ruby Shell\n");
223  printf("\nThis is a very early version, please test and report errors.\n");
224  printf("Thanks :)\n\n");
225 }
226 
227 /* Print the command line prompt of the REPL */
228 void
229 print_cmdline(int code_block_open)
230 {
231  if (code_block_open) {
232  printf("* ");
233  }
234  else {
235  printf("> ");
236  }
237 }
238 
239 int
240 main(int argc, char **argv)
241 {
242  char ruby_code[1024] = { 0 };
243  char last_code_line[1024] = { 0 };
244 #ifndef ENABLE_READLINE
245  int last_char;
246  int char_index;
247 #else
248  char *home = NULL;
249 #endif
250  mrbc_context *cxt;
251  struct mrb_parser_state *parser;
252  mrb_state *mrb;
253  mrb_value result;
254  struct _args args;
255  int n;
256  int code_block_open = FALSE;
257  int ai;
258 
259  /* new interpreter instance */
260  mrb = mrb_open();
261  if (mrb == NULL) {
262  fputs("Invalid mrb interpreter, exiting mirb\n", stderr);
263  return EXIT_FAILURE;
264  }
265  mrb_define_global_const(mrb, "ARGV", mrb_ary_new_capa(mrb, 0));
266 
267  n = parse_args(mrb, argc, argv, &args);
268  if (n == EXIT_FAILURE) {
269  cleanup(mrb, &args);
270  usage(argv[0]);
271  return n;
272  }
273 
274  print_hint();
275 
276  cxt = mrbc_context_new(mrb);
277  cxt->capture_errors = 1;
278  cxt->lineno = 1;
279  mrbc_filename(mrb, cxt, "(mirb)");
280  if (args.verbose) cxt->dump_result = 1;
281 
282  ai = mrb_gc_arena_save(mrb);
283 
284 #ifdef ENABLE_READLINE
285  using_history();
286  home = getenv("HOME");
287 #ifdef _WIN32
288  if (!home)
289  home = getenv("USERPROFILE");
290 #endif
291  if (home) {
292  strcpy(history_path, home);
293  strcat(history_path, "/");
294  strcat(history_path, history_file_name);
295  read_history(history_path);
296  }
297 #endif
298 
299 
300  while (TRUE) {
301 #ifndef ENABLE_READLINE
302  print_cmdline(code_block_open);
303 
304  char_index = 0;
305  while ((last_char = getchar()) != '\n') {
306  if (last_char == EOF) break;
307  last_code_line[char_index++] = last_char;
308  }
309  if (last_char == EOF) {
310  fputs("\n", stdout);
311  break;
312  }
313 
314  last_code_line[char_index] = '\0';
315 #else
316  char* line = readline(code_block_open ? "* " : "> ");
317  if (line == NULL) {
318  printf("\n");
319  break;
320  }
321  strncpy(last_code_line, line, sizeof(last_code_line)-1);
322  add_history(line);
323  free(line);
324 #endif
325 
326  if ((strcmp(last_code_line, "quit") == 0) || (strcmp(last_code_line, "exit") == 0)) {
327  if (!code_block_open) {
328  break;
329  }
330  else{
331  /* count the quit/exit commands as strings if in a quote block */
332  strcat(ruby_code, "\n");
333  strcat(ruby_code, last_code_line);
334  }
335  }
336  else {
337  if (code_block_open) {
338  strcat(ruby_code, "\n");
339  strcat(ruby_code, last_code_line);
340  }
341  else {
342  strcpy(ruby_code, last_code_line);
343  }
344  }
345 
346  /* parse code */
347  parser = mrb_parser_new(mrb);
348  parser->s = ruby_code;
349  parser->send = ruby_code + strlen(ruby_code);
350  parser->lineno = cxt->lineno;
351  mrb_parser_parse(parser, cxt);
352  code_block_open = is_code_block_open(parser);
353 
354  if (code_block_open) {
355  /* no evaluation of code */
356  }
357  else {
358  if (0 < parser->nerr) {
359  /* syntax error */
360  printf("line %d: %s\n", parser->error_buffer[0].lineno, parser->error_buffer[0].message);
361  }
362  else {
363  /* generate bytecode */
364  n = mrb_generate_code(mrb, parser);
365 
366  /* evaluate the bytecode */
367  result = mrb_run(mrb,
368  /* pass a proc for evaulation */
369  mrb_proc_new(mrb, mrb->irep[n]),
370  mrb_top_self(mrb));
371  /* did an exception occur? */
372  if (mrb->exc) {
373  p(mrb, mrb_obj_value(mrb->exc), 0);
374  mrb->exc = 0;
375  }
376  else {
377  /* no */
378  if (!mrb_respond_to(mrb, result, mrb_intern2(mrb, "inspect", 7))){
379  result = mrb_any_to_s(mrb,result);
380  }
381  p(mrb, result, 1);
382  }
383  }
384  ruby_code[0] = '\0';
385  last_code_line[0] = '\0';
386  mrb_gc_arena_restore(mrb, ai);
387  }
388  mrb_parser_free(parser);
389  cxt->lineno++;
390  }
391  mrbc_context_free(mrb, cxt);
392  mrb_close(mrb);
393 
394 #ifdef ENABLE_READLINE
395  write_history(history_path);
396 #endif
397 
398  return 0;
399 }