diff options
Diffstat (limited to 'mrbgems/mruby-bin-mirb/tools/mirb/mirb.c')
| -rw-r--r-- | mrbgems/mruby-bin-mirb/tools/mirb/mirb.c | 399 |
1 files changed, 334 insertions, 65 deletions
diff --git a/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c b/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c index f96508d55..db92d7266 100644 --- a/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c +++ b/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c @@ -6,59 +6,135 @@ ** immediately. It's a REPL... */ +#include <mruby.h> + +#ifdef MRB_NO_STDIO +# error mruby-bin-mirb conflicts 'MRB_NO_STDIO' in your build configuration +#endif + +#include <mruby/array.h> +#include <mruby/proc.h> +#include <mruby/compile.h> +#include <mruby/dump.h> +#include <mruby/string.h> +#include <mruby/variable.h> +#include <mruby/presym.h> + #include <stdlib.h> #include <string.h> -#include <stdio.h> #include <ctype.h> +#include <signal.h> +#include <setjmp.h> + +/* obsolete configuration */ #ifdef ENABLE_READLINE -#include <readline/readline.h> -#include <readline/history.h> +# define MRB_USE_READLINE +#endif +#ifdef ENABLE_LINENOISE +# define MRB_USE_LINENOISE +#endif +#ifdef DISABLE_MIRB_UNDERSCORE +# define MRB_NO_MIRB_UNDERSCORE +#endif + +#ifdef MRB_USE_READLINE +#include MRB_READLINE_HEADER +#include MRB_READLINE_HISTORY #define MIRB_ADD_HISTORY(line) add_history(line) #define MIRB_READLINE(ch) readline(ch) +#if !defined(RL_READLINE_VERSION) || RL_READLINE_VERSION < 0x600 +/* libedit & older readline do not have rl_free() */ +#define MIRB_LINE_FREE(line) free(line) +#else +#define MIRB_LINE_FREE(line) rl_free(line) +#endif #define MIRB_WRITE_HISTORY(path) write_history(path) #define MIRB_READ_HISTORY(path) read_history(path) #define MIRB_USING_HISTORY() using_history() -#elif defined(ENABLE_LINENOISE) -#define ENABLE_READLINE +#elif defined(MRB_USE_LINENOISE) +#define MRB_USE_READLINE #include <linenoise.h> #define MIRB_ADD_HISTORY(line) linenoiseHistoryAdd(line) #define MIRB_READLINE(ch) linenoise(ch) +#define MIRB_LINE_FREE(line) linenoiseFree(line) #define MIRB_WRITE_HISTORY(path) linenoiseHistorySave(path) #define MIRB_READ_HISTORY(path) linenoiseHistoryLoad(history_path) #define MIRB_USING_HISTORY() #endif -#ifdef ENABLE_READLINE -#include <limits.h> -static const char *history_file_name = ".mirb_history"; -char history_path[PATH_MAX]; +#if !defined(_WIN32) && defined(_POSIX_C_SOURCE) +#define MIRB_SIGSETJMP(env) sigsetjmp(env, 1) +#define MIRB_SIGLONGJMP(env, val) siglongjmp(env, val) +#define SIGJMP_BUF sigjmp_buf +#else +#define MIRB_SIGSETJMP(env) setjmp(env) +#define MIRB_SIGLONGJMP(env, val) longjmp(env, val) +#define SIGJMP_BUF jmp_buf #endif -#include "mruby.h" -#include "mruby/array.h" -#include "mruby/proc.h" -#include "mruby/compile.h" -#include "mruby/string.h" +#ifdef MRB_USE_READLINE + +static const char history_file_name[] = ".mirb_history"; + +static char * +get_history_path(mrb_state *mrb) +{ + char *path = NULL; + const char *home = getenv("HOME"); + +#ifdef _WIN32 + if (home != NULL) { + home = getenv("USERPROFILE"); + } +#endif + + if (home != NULL) { + int len = snprintf(NULL, 0, "%s/%s", home, history_file_name); + if (len >= 0) { + size_t size = len + 1; + path = (char *)mrb_malloc_simple(mrb, size); + if (path != NULL) { + int n = snprintf(path, size, "%s/%s", home, history_file_name); + if (n != len) { + mrb_free(mrb, path); + path = NULL; + } + } + } + } + + return path; +} + +#endif static void p(mrb_state *mrb, mrb_value obj, int prompt) { - obj = mrb_funcall(mrb, obj, "inspect", 0); + mrb_value val; + char* msg; + + val = mrb_funcall_id(mrb, obj, MRB_SYM(inspect), 0); if (prompt) { if (!mrb->exc) { fputs(" => ", stdout); } else { - obj = mrb_funcall(mrb, mrb_obj_value(mrb->exc), "inspect", 0); + val = mrb_funcall_id(mrb, mrb_obj_value(mrb->exc), MRB_SYM(inspect), 0); } } - fwrite(RSTRING_PTR(obj), RSTRING_LEN(obj), 1, stdout); + if (!mrb_string_p(val)) { + val = mrb_obj_as_string(mrb, obj); + } + msg = mrb_locale_from_utf8(RSTRING_PTR(val), (int)RSTRING_LEN(val)); + fwrite(msg, strlen(msg), 1, stdout); + mrb_locale_free(msg); putc('\n', stdout); } /* Guess if the user might want to enter more - * or if he wants an evaluation of his code now */ + * or if they wants an evaluation of their code now */ static mrb_bool is_code_block_open(struct mrb_parser_state *parser) { @@ -66,17 +142,13 @@ is_code_block_open(struct mrb_parser_state *parser) /* check for heredoc */ if (parser->parsing_heredoc != NULL) return TRUE; - if (parser->heredoc_end_now) { - parser->heredoc_end_now = FALSE; - return FALSE; - } /* check for unterminated string */ if (parser->lex_strterm) return TRUE; /* check if parser error are available */ if (0 < parser->nerr) { - const char *unexpected_end = "syntax error, unexpected $end"; + 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 */ @@ -84,7 +156,7 @@ is_code_block_open(struct mrb_parser_state *parser) /* a different issue which we have to show to */ /* the user */ - if (strncmp(message, unexpected_end, strlen(unexpected_end)) == 0) { + if (strncmp(message, unexpected_end, sizeof(unexpected_end) - 1) == 0) { code_block_open = TRUE; } else if (strcmp(message, "syntax error, unexpected keyword_end") == 0) { @@ -139,7 +211,7 @@ is_code_block_open(struct mrb_parser_state *parser) /* an expression was ended */ break; case EXPR_ENDARG: - /* closing parenthese */ + /* closing parenthesis */ break; case EXPR_ENDFN: /* definition end */ @@ -158,13 +230,14 @@ is_code_block_open(struct mrb_parser_state *parser) return code_block_open; } -void mrb_show_version(mrb_state *); -void mrb_show_copyright(mrb_state *); - struct _args { + FILE *rfp; mrb_bool verbose : 1; + mrb_bool debug : 1; int argc; char** argv; + int libc; + char **libv; }; static void @@ -172,6 +245,8 @@ usage(const char *name) { static const char *const usage_msg[] = { "switches:", + "-d set $DEBUG to true (same as `mruby -d`)", + "-r library same as `mruby -r`", "-v print version number, then run in verbose mode", "--verbose run in verbose mode", "--version print the version", @@ -180,14 +255,24 @@ usage(const char *name) }; const char *const *p = usage_msg; - printf("Usage: %s [switches]\n", name); + printf("Usage: %s [switches] [programfile] [arguments]\n", name); while (*p) printf(" %s\n", *p++); } +static char * +dup_arg_item(mrb_state *mrb, const char *item) +{ + size_t buflen = strlen(item) + 1; + char *buf = (char*)mrb_malloc(mrb, buflen); + memcpy(buf, item, buflen); + return buf; +} + static int parse_args(mrb_state *mrb, int argc, char **argv, struct _args *args) { + char **origargv = argv; static const struct _args args_zero = { 0 }; *args = args_zero; @@ -198,6 +283,26 @@ parse_args(mrb_state *mrb, int argc, char **argv, struct _args *args) item = argv[0] + 1; switch (*item++) { + case 'd': + args->debug = TRUE; + break; + case 'r': + if (!item[0]) { + if (argc <= 1) { + printf("%s: No library specified for -r\n", *origargv); + return EXIT_FAILURE; + } + argc--; argv++; + item = argv[0]; + } + if (args->libc == 0) { + args->libv = (char**)mrb_malloc(mrb, sizeof(char*)); + } + else { + args->libv = (char**)mrb_realloc(mrb, args->libv, sizeof(char*) * (args->libc + 1)); + } + args->libv[args->libc++] = dup_arg_item(mrb, item); + break; case 'v': if (!args->verbose) mrb_show_version(mrb); args->verbose = TRUE; @@ -219,12 +324,36 @@ parse_args(mrb_state *mrb, int argc, char **argv, struct _args *args) return EXIT_FAILURE; } } + + if (args->rfp == NULL) { + if (*argv != NULL) { + args->rfp = fopen(argv[0], "r"); + if (args->rfp == NULL) { + printf("Cannot open program file. (%s)\n", *argv); + return EXIT_FAILURE; + } + argc--; argv++; + } + } + args->argv = (char **)mrb_realloc(mrb, args->argv, sizeof(char*) * (argc + 1)); + memcpy(args->argv, argv, (argc+1) * sizeof(char*)); + args->argc = argc; + return EXIT_SUCCESS; } static void cleanup(mrb_state *mrb, struct _args *args) { + if (args->rfp) + fclose(args->rfp); + mrb_free(mrb, args->argv); + if (args->libc) { + while (args->libc--) { + mrb_free(mrb, args->libv[args->libc]); + } + mrb_free(mrb, args->libv); + } mrb_close(mrb); } @@ -235,7 +364,7 @@ print_hint(void) printf("mirb - Embeddable Interactive Ruby Shell\n\n"); } -#ifndef ENABLE_READLINE +#ifndef MRB_USE_READLINE /* Print the command line prompt of the REPL */ static void print_cmdline(int code_block_open) @@ -246,6 +375,7 @@ print_cmdline(int code_block_open) else { printf("> "); } + fflush(stdout); } #endif @@ -258,7 +388,7 @@ check_keyword(const char *buf, const char *word) size_t len = strlen(word); /* skip preceding spaces */ - while (*p && isspace((unsigned char)*p)) { + while (*p && ISSPACE(*p)) { p++; } /* check keyword */ @@ -268,29 +398,69 @@ check_keyword(const char *buf, const char *word) p += len; /* skip trailing spaces */ while (*p) { - if (!isspace((unsigned char)*p)) return 0; + if (!ISSPACE(*p)) return 0; p++; } return 1; } + +#ifndef MRB_USE_READLINE +volatile sig_atomic_t input_canceled = 0; +void +ctrl_c_handler(int signo) +{ + input_canceled = 1; +} +#else +SIGJMP_BUF ctrl_c_buf; +void +ctrl_c_handler(int signo) +{ + MIRB_SIGLONGJMP(ctrl_c_buf, 1); +} +#endif + +#ifndef MRB_NO_MIRB_UNDERSCORE +void decl_lv_underscore(mrb_state *mrb, mrbc_context *cxt) +{ + struct RProc *proc; + struct mrb_parser_state *parser; + + parser = mrb_parse_string(mrb, "_=nil", cxt); + if (parser == NULL) { + fputs("create parser state error\n", stderr); + mrb_close(mrb); + exit(EXIT_FAILURE); + } + + proc = mrb_generate_code(mrb, parser); + mrb_vm_run(mrb, proc, mrb_top_self(mrb), 0); + + mrb_parser_free(parser); +} +#endif + int main(int argc, char **argv) { - char ruby_code[1024] = { 0 }; + char ruby_code[4096] = { 0 }; char last_code_line[1024] = { 0 }; -#ifndef ENABLE_READLINE +#ifndef MRB_USE_READLINE int last_char; - int char_index; + size_t char_index; #else - char *home = NULL; + char *history_path; + char* line; #endif mrbc_context *cxt; struct mrb_parser_state *parser; mrb_state *mrb; mrb_value result; struct _args args; + mrb_value ARGV; int n; + int i; mrb_bool code_block_open = FALSE; int ai; unsigned int stack_keep = 0; @@ -301,7 +471,6 @@ main(int argc, char **argv) fputs("Invalid mrb interpreter, exiting mirb\n", stderr); return EXIT_FAILURE; } - mrb_define_global_const(mrb, "ARGV", mrb_ary_new_capa(mrb, 0)); n = parse_args(mrb, argc, argv, &args); if (n == EXIT_FAILURE) { @@ -310,9 +479,54 @@ main(int argc, char **argv) return n; } + ARGV = mrb_ary_new_capa(mrb, args.argc); + for (i = 0; i < args.argc; i++) { + char* utf8 = mrb_utf8_from_locale(args.argv[i], -1); + if (utf8) { + mrb_ary_push(mrb, ARGV, mrb_str_new_cstr(mrb, utf8)); + mrb_utf8_free(utf8); + } + } + mrb_define_global_const(mrb, "ARGV", ARGV); + mrb_gv_set(mrb, mrb_intern_lit(mrb, "$DEBUG"), mrb_bool_value(args.debug)); + +#ifdef MRB_USE_READLINE + history_path = get_history_path(mrb); + if (history_path == NULL) { + fputs("failed to get history path\n", stderr); + mrb_close(mrb); + return EXIT_FAILURE; + } + + MIRB_USING_HISTORY(); + MIRB_READ_HISTORY(history_path); +#endif + print_hint(); cxt = mrbc_context_new(mrb); + + /* Load libraries */ + for (i = 0; i < args.libc; i++) { + struct REnv *e; + FILE *lfp = fopen(args.libv[i], "r"); + if (lfp == NULL) { + printf("Cannot open library file. (%s)\n", args.libv[i]); + cleanup(mrb, &args); + return EXIT_FAILURE; + } + mrb_load_file_cxt(mrb, lfp, cxt); + fclose(lfp); + e = mrb_vm_ci_env(mrb->c->cibase); + mrb_vm_ci_env_set(mrb->c->cibase, NULL); + mrb_env_unshare(mrb, e); + mrbc_cleanup_local_variables(mrb, cxt); + } + +#ifndef MRB_NO_MIRB_UNDERSCORE + decl_lv_underscore(mrb, cxt); +#endif + cxt->capture_errors = TRUE; cxt->lineno = 1; mrbc_filename(mrb, cxt, "(mirb)"); @@ -320,35 +534,37 @@ main(int argc, char **argv) ai = mrb_gc_arena_save(mrb); -#ifdef ENABLE_READLINE - MIRB_USING_HISTORY(); - home = getenv("HOME"); -#ifdef _WIN32 - if (!home) - home = getenv("USERPROFILE"); -#endif - if (home) { - strcpy(history_path, home); - strcat(history_path, "/"); - strcat(history_path, history_file_name); - MIRB_READ_HISTORY(history_path); - } -#endif + while (TRUE) { + char *utf8; + if (args.rfp) { + if (fgets(last_code_line, sizeof(last_code_line)-1, args.rfp) != NULL) + goto done; + break; + } - while (TRUE) { -#ifndef ENABLE_READLINE +#ifndef MRB_USE_READLINE print_cmdline(code_block_open); + signal(SIGINT, ctrl_c_handler); char_index = 0; while ((last_char = getchar()) != '\n') { if (last_char == EOF) break; - if (char_index > sizeof(last_code_line)-2) { + if (char_index >= sizeof(last_code_line)-2) { fputs("input string too long\n", stderr); continue; } last_code_line[char_index++] = last_char; } + signal(SIGINT, SIG_DFL); + if (input_canceled) { + ruby_code[0] = '\0'; + last_code_line[0] = '\0'; + code_block_open = FALSE; + puts("^C"); + input_canceled = 0; + continue; + } if (last_char == EOF) { fputs("\n", stdout); break; @@ -357,7 +573,19 @@ main(int argc, char **argv) last_code_line[char_index++] = '\n'; last_code_line[char_index] = '\0'; #else - char* line = MIRB_READLINE(code_block_open ? "* " : "> "); + if (MIRB_SIGSETJMP(ctrl_c_buf) == 0) { + ; + } + else { + ruby_code[0] = '\0'; + last_code_line[0] = '\0'; + code_block_open = FALSE; + puts("^C"); + } + signal(SIGINT, ctrl_c_handler); + line = MIRB_READLINE(code_block_open ? "* " : "> "); + signal(SIGINT, SIG_DFL); + if (line == NULL) { printf("\n"); break; @@ -369,9 +597,10 @@ main(int argc, char **argv) strcpy(last_code_line, line); strcat(last_code_line, "\n"); MIRB_ADD_HISTORY(line); - free(line); + MIRB_LINE_FREE(line); #endif + done: if (code_block_open) { if (strlen(ruby_code)+strlen(last_code_line) > sizeof(ruby_code)-1) { fputs("concatenated input string too long\n", stderr); @@ -386,32 +615,59 @@ main(int argc, char **argv) strcpy(ruby_code, last_code_line); } + utf8 = mrb_utf8_from_locale(ruby_code, -1); + if (!utf8) abort(); + /* parse code */ parser = mrb_parser_new(mrb); - parser->s = ruby_code; - parser->send = ruby_code + strlen(ruby_code); + if (parser == NULL) { + fputs("create parser state error\n", stderr); + break; + } + parser->s = utf8; + parser->send = utf8 + strlen(utf8); parser->lineno = cxt->lineno; mrb_parser_parse(parser, cxt); code_block_open = is_code_block_open(parser); + mrb_utf8_free(utf8); if (code_block_open) { /* no evaluation of code */ } else { + if (0 < parser->nwarn) { + /* warning */ + char* msg = mrb_locale_from_utf8(parser->warn_buffer[0].message, -1); + printf("line %d: %s\n", parser->warn_buffer[0].lineno, msg); + mrb_locale_free(msg); + } if (0 < parser->nerr) { /* syntax error */ - printf("line %d: %s\n", parser->error_buffer[0].lineno, parser->error_buffer[0].message); + char* msg = mrb_locale_from_utf8(parser->error_buffer[0].message, -1); + printf("line %d: %s\n", parser->error_buffer[0].lineno, msg); + mrb_locale_free(msg); } else { /* generate bytecode */ struct RProc *proc = mrb_generate_code(mrb, parser); + if (proc == NULL) { + mrb_parser_free(parser); + continue; + } if (args.verbose) { mrb_codedump_all(mrb, proc); } - /* pass a proc for evaulation */ + /* adjust stack length of toplevel environment */ + if (mrb->c->cibase->u.env) { + struct REnv *e = mrb_vm_ci_env(mrb->c->cibase); + if (e && MRB_ENV_LEN(e) < proc->body.irep->nlocals) { + MRB_ENV_SET_LEN(e, proc->body.irep->nlocals); + } + } + /* pass a proc for evaluation */ /* evaluate the bytecode */ - result = mrb_context_run(mrb, + result = mrb_vm_run(mrb, proc, mrb_top_self(mrb), stack_keep); @@ -423,10 +679,13 @@ main(int argc, char **argv) } else { /* no */ - if (!mrb_respond_to(mrb, result, mrb_intern_lit(mrb, "inspect"))){ + if (!mrb_respond_to(mrb, result, MRB_SYM(inspect))){ result = mrb_any_to_s(mrb, result); } p(mrb, result, 1); +#ifndef MRB_NO_MIRB_UNDERSCORE + *(mrb->c->ci->stack + 1) = result; +#endif } } ruby_code[0] = '\0'; @@ -436,12 +695,22 @@ main(int argc, char **argv) mrb_parser_free(parser); cxt->lineno++; } - mrbc_context_free(mrb, cxt); - mrb_close(mrb); -#ifdef ENABLE_READLINE +#ifdef MRB_USE_READLINE MIRB_WRITE_HISTORY(history_path); + mrb_free(mrb, history_path); #endif + if (args.rfp) fclose(args.rfp); + mrb_free(mrb, args.argv); + if (args.libv) { + for (i = 0; i < args.libc; ++i) { + mrb_free(mrb, args.libv[i]); + } + mrb_free(mrb, args.libv); + } + mrbc_context_free(mrb, cxt); + mrb_close(mrb); + return 0; } |
