diff options
| author | Daniel Bovensiepen <[email protected]> | 2012-05-12 00:27:45 +0800 |
|---|---|---|
| committer | Daniel Bovensiepen <[email protected]> | 2012-05-12 00:27:45 +0800 |
| commit | 917d70e31bb25f5917d06bb5a81190dfb6f17e26 (patch) | |
| tree | 75bd14d8f394a13a0f8a2bf2d6e83c316516fefe | |
| parent | 95d1d002d90abc6e55eefc00a4844c5c7b4c16d9 (diff) | |
| download | mruby-917d70e31bb25f5917d06bb5a81190dfb6f17e26.tar.gz mruby-917d70e31bb25f5917d06bb5a81190dfb6f17e26.zip | |
mirb - Embeddable Interactive Ruby Shell
| -rw-r--r-- | Makefile | 8 | ||||
| -rw-r--r-- | tools/mirb/Makefile | 74 | ||||
| -rw-r--r-- | tools/mirb/mirb.c | 230 |
3 files changed, 312 insertions, 0 deletions
@@ -26,8 +26,16 @@ all : @$(MAKE) -C mrblib $(MAKE_FLAGS) @$(MAKE) -C tools/mruby $(MAKE_FLAGS) +.PHONY : mirb +mirb : + @$(MAKE) -C src $(MAKE_FLAGS) + @$(MAKE) -C mrblib $(MAKE_FLAGS) + @$(MAKE) -C tools/mirb $(MAKE_FLAGS) + # clean up .PHONY : clean clean : @$(MAKE) clean -C src $(MAKE_FLAGS) @$(MAKE) clean -C tools/mruby $(MAKE_FLAGS) + @$(MAKE) clean -C tools/mirb $(MAKE_FLAGS) + diff --git a/tools/mirb/Makefile b/tools/mirb/Makefile new file mode 100644 index 000000000..37086879d --- /dev/null +++ b/tools/mirb/Makefile @@ -0,0 +1,74 @@ +# makefile discription. +# basic build file for mirb executable + +# project-specific macros +# extension of the executable-file is modifiable(.exe .out ...) +BASEDIR = ../../src +TARGET := ../../bin/mirb +LIBR := ../../lib/libmruby.a +ifeq ($(OS),Windows_NT) + EXE := $(TARGET).exe +else + EXE := $(TARGET) +endif +OBJ0 := $(patsubst %.c,%.o,$(wildcard $(BASEDIR)/../tools/mirb/*.c)) +OBJS := $(OBJ0) + +# ext libraries +EXTS := $(EXT1) + +# libraries, includes +LIBS = -lm +INCLUDES = -I$(BASEDIR) -I$(BASEDIR)/../include + +# compiler, linker (gcc) +CC = gcc +LL = gcc +YACC = bison +DEBUG_MODE = 1 +ifeq ($(DEBUG_MODE),1) + CFLAGS = -g -O3 +else + CFLAGS = -O3 +endif +ALL_CFLAGS = -Wall -Werror-implicit-function-declaration $(CFLAGS) +ifeq ($(OS),Windows_NT) + MAKE_FLAGS = CC=$(CC) LL=$(LL) ALL_CFLAGS="$(ALL_CFLAGS)" +else + MAKE_FLAGS = CC='$(CC)' LL='$(LL)' ALL_CFLAGS='$(ALL_CFLAGS)' +endif + +############################## +# generic build targets, rules + +.PHONY : all +all : $(LIBR) $(EXE) + @echo "make: built targets of `pwd`" + +# executable constructed using linker from object files +$(EXE) : $(LIBR) $(OBJS) $(EXTS) + $(LL) -o $@ $(CFLAGS) $(OBJS) $(LIBR) $(EXTS) $(LIBS) + +-include $(OBJS:.o=.d) + +# objects compiled from source +$(OBJS) : %.o : %.c + $(CC) $(ALL_CFLAGS) -MMD $(INCLUDES) -c $< -o $@ + +# C library compile +$(LIBR) : + @$(MAKE) -C $(BASEDIR) $(MAKE_FLAGS) + +# mruby library compile +# extend libraries complile +$(EXTS) : %.o : %.c + $(CC) $(ALL_CFLAGS) -MMD $(INCLUDES) -c $< -o $@ + +# clean up +.PHONY : clean #cleandep +clean : + $(MAKE) clean -C ../../mrblib $(MAKE_FLAGS) + $(MAKE) clean -C ../mrbc $(MAKE_FLAGS) + @echo "make: removing targets, objects and depend files of `pwd`" + -rm -f $(EXE) $(OBJS) + -rm -f $(OBJS:.o=.d) diff --git a/tools/mirb/mirb.c b/tools/mirb/mirb.c new file mode 100644 index 000000000..3089d288f --- /dev/null +++ b/tools/mirb/mirb.c @@ -0,0 +1,230 @@ +/* +** 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 <string.h> +#include <stdbool.h> +#include <unistd.h> +#include <fcntl.h> + +#include <mruby.h> +#include <mruby/proc.h> +#include <mruby/data.h> +#include <compile.h> + +/* Guess if the user might want to enter more + * or if he wants an evaluation of his code now */ +bool is_code_block_open(struct mrb_parser_state *parser) { + bool code_block_open = false; + + 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; + case EXPR_CMDARG: + code_block_open = false; + break; + + // all states which are unsure + + 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; + } + + if (!code_block_open) { + // based on the last parser state the code + // block seems to be open + + // now check if parser error are available + if (0 < parser->nerr) { + // 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 (strcmp(parser->error_buffer[0].message, + "syntax error, unexpected $end, expecting ';' or '\\n'") == 0) { + code_block_open = true; + } else if (strcmp(parser->error_buffer[0].message, + "syntax error, unexpected $end, expecting keyword_end") == 0) { + code_block_open = true; + } else if (strcmp(parser->error_buffer[0].message, + "syntax error, unexpected $end, expecting '<' or ';' or '\\n'") == 0) { + code_block_open = true; + } else if (strcmp(parser->error_buffer[0].message, + "syntax error, unexpected keyword_end") == 0) { + code_block_open = true; + } else if (strcmp(parser->error_buffer[0].message, + "syntax error, unexpected $end, expecting keyword_then or ';' or '\\n'") == 0) { + code_block_open = true; + } else if (strcmp(parser->error_buffer[0].message, + "syntax error, unexpected tREGEXP_BEG") == 0) { + code_block_open = true; + } + } + } else { + // last parser state suggest that this code + // block is open, WE NEED MORE CODE!! + } + + 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(bool 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; + struct mrb_parser_state *parser; + mrb_state *mrb_interpreter; + mrb_value mrb_return_value; + int byte_code; + bool code_block_open = false; + + print_hint(); + + // new interpreter instance + mrb_interpreter = mrb_open(); + 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, "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_parse_nstring_ext(mrb_interpreter, ruby_code, strlen(ruby_code)); + code_block_open = is_code_block_open(parser); + + if (code_block_open) { + // no evaluation of code + } else { + if (0 < parser->nerr) { + // syntax error + printf("%s\n", parser->error_buffer[0].message); + } else { + // generate bytecode + byte_code = mrb_generate_code(mrb_interpreter, parser->tree); + + // evaluate the bytecode + mrb_return_value = mrb_run(mrb_interpreter, + // pass a proc for evaulation + mrb_proc_new(mrb_interpreter, mrb_interpreter->irep[byte_code]), + mrb_top_self(mrb_interpreter)); + //mrb_nil_value()); + // did an exception occur? + if (mrb_interpreter->exc) { + mrb_p(mrb_interpreter, mrb_obj_value(mrb_interpreter->exc)); + mrb_interpreter->exc = 0; + } else { + // no + printf(" => "); + mrb_p(mrb_interpreter, mrb_return_value); + } + } + + memset(ruby_code, 0, sizeof(*ruby_code)); + memset(ruby_code, 0, sizeof(*last_code_line)); + } + } + } + + return 0; +} |
