/* ** mirb - Embeddable Interactive Ruby Shell ** ** This program takes code from the user in ** an interactive way and executes it ** immediately. It's a REPL... */ #include #include #include #include #include /* Guess if the user might want to enter more * or if he wants an evaluation of his code now */ int is_code_block_open(struct mrb_parser_state *parser) { int code_block_open = FALSE; /* check for unterminated string */ if (parser->sterm) return TRUE; /* check if parser error are available */ if (0 < parser->nerr) { const char *unexpected_end = "syntax error, unexpected $end"; const char *message = parser->error_buffer[0].message; /* a parser error occur, we have to check if */ /* we need to read one more line or if there is */ /* a different issue which we have to show to */ /* the user */ if (strncmp(message, unexpected_end, strlen(unexpected_end)) == 0) { code_block_open = TRUE; } else if (strcmp(message, "syntax error, unexpected keyword_end") == 0) { code_block_open = TRUE; } else if (strcmp(message, "syntax error, unexpected tREGEXP_BEG") == 0) { code_block_open = TRUE; } return code_block_open; } switch (parser->lstate) { /* all states which need more code */ case EXPR_BEG: /* an expression was just started, */ /* we can't end it like this */ code_block_open = TRUE; break; case EXPR_DOT: /* a message dot was the last token, */ /* there has to come more */ code_block_open = TRUE; break; case EXPR_CLASS: /* a class keyword is not enough! */ /* we need also a name of the class */ code_block_open = TRUE; break; case EXPR_FNAME: /* a method name is necessary */ code_block_open = TRUE; break; case EXPR_VALUE: /* if, elsif, etc. without condition */ code_block_open = TRUE; break; /* now all the states which are closed */ case EXPR_ARG: /* an argument is the last token */ code_block_open = FALSE; break; /* all states which are unsure */ case EXPR_CMDARG: break; case EXPR_END: /* an expression was ended */ break; case EXPR_ENDARG: /* closing parenthese */ break; case EXPR_ENDFN: /* definition end */ break; case EXPR_MID: /* jump keyword like break, return, ... */ break; case EXPR_MAX_STATE: /* don't know what to do with this token */ break; default: /* this state is unexpected! */ break; } return code_block_open; } /* Print a short remark for the user */ void print_hint(void) { printf("mirb - Embeddable Interactive Ruby Shell\n"); printf("\nThis is a very early version, please test and report errors.\n"); printf("Thanks :)\n\n"); } /* Print the command line prompt of the REPL */ void print_cmdline(int code_block_open) { if (code_block_open) { printf("* "); } else { printf("> "); } } int main(void) { char last_char, ruby_code[1024], last_code_line[1024]; int char_index; mrbc_context *cxt; struct mrb_parser_state *parser; mrb_state *mrb; mrb_value result; int n; int code_block_open = FALSE; print_hint(); /* new interpreter instance */ mrb = mrb_open(); if (mrb == NULL) { fprintf(stderr, "Invalid mrb interpreter, exiting mirb"); return EXIT_FAILURE; } cxt = mrbc_context_new(mrb); cxt->capture_errors = 1; memset(ruby_code, 0, sizeof(*ruby_code)); memset(last_code_line, 0, sizeof(*last_code_line)); while (TRUE) { print_cmdline(code_block_open); char_index = 0; while ((last_char = getchar()) != '\n') { if (last_char == EOF) break; last_code_line[char_index++] = last_char; } if (last_char == EOF) { printf("\n"); break; } last_code_line[char_index] = '\0'; if ((strcmp(last_code_line, "quit") == 0) || (strcmp(last_code_line, "exit") == 0)) { if (code_block_open) { /* cancel the current block and reset */ code_block_open = FALSE; memset(ruby_code, 0, sizeof(*ruby_code)); memset(last_code_line, 0, sizeof(*last_code_line)); continue; } else { /* quit the program */ break; } } else { if (code_block_open) { strcat(ruby_code, "\n"); strcat(ruby_code, last_code_line); } else { memset(ruby_code, 0, sizeof(*ruby_code)); strcat(ruby_code, last_code_line); } /* parse code */ parser = mrb_parser_new(mrb); parser->s = ruby_code; parser->send = ruby_code + strlen(ruby_code); parser->lineno = 1; mrb_parser_parse(parser, cxt); code_block_open = is_code_block_open(parser); if (code_block_open) { /* no evaluation of code */ } else { if (0 < parser->nerr) { /* syntax error */ printf("line %d: %s\n", parser->error_buffer[0].lineno, parser->error_buffer[0].message); } else { /* generate bytecode */ n = mrb_generate_code(mrb, parser->tree); /* evaluate the bytecode */ result = mrb_run(mrb, /* pass a proc for evaulation */ mrb_proc_new(mrb, mrb->irep[n]), mrb_top_self(mrb)); /* did an exception occur? */ if (mrb->exc) { mrb_p(mrb, mrb_obj_value(mrb->exc)); mrb->exc = 0; } else { /* no */ printf(" => "); mrb_p(mrb, result); } } memset(ruby_code, 0, sizeof(*ruby_code)); memset(ruby_code, 0, sizeof(*last_code_line)); } mrb_parser_free(parser); } } mrbc_context_free(mrb, cxt); mrb_close(mrb); return 0; }