From 2554fd9c945d5b5281a52d40146b348353e3707c Mon Sep 17 00:00:00 2001 From: cremno Date: Fri, 29 May 2015 11:29:11 +0200 Subject: remove unreachable code It's just a copy of the code in the previous case (NODE_CALL). --- src/codegen.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'src') diff --git a/src/codegen.c b/src/codegen.c index 222dec9b6..6f1f75e88 100644 --- a/src/codegen.c +++ b/src/codegen.c @@ -957,13 +957,6 @@ gen_assignment(codegen_scope *s, node *tree, int sp, int val) gen_vmassignment(s, tree->car, sp, val); break; - push(); - gen_call(s, tree, attrsym(s, sym(tree->cdr->car)), sp, NOVAL); - pop(); - if (val) { - genop_peep(s, MKOP_AB(OP_MOVE, cursp(), sp), val); - } - break; /* splat without assignment */ case NODE_NIL: break; -- cgit v1.2.3 From 59ea323976557408ba1c5f694213c7a31bbb6e39 Mon Sep 17 00:00:00 2001 From: cremno Date: Fri, 29 May 2015 14:51:48 +0200 Subject: check if outer is a class or module For modules this check didn't exist yet. Also call #inspect. --- src/class.c | 24 +++++++++++++++--------- test/t/class.rb | 5 +++++ test/t/module.rb | 4 ++++ 3 files changed, 24 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/class.c b/src/class.c index 3246564ec..35c3aa040 100644 --- a/src/class.c +++ b/src/class.c @@ -131,6 +131,19 @@ mrb_class_outer_module(mrb_state *mrb, struct RClass *c) return mrb_class_ptr(outer); } +static void +check_if_class_or_module(mrb_state *mrb, mrb_value obj) +{ + switch (mrb_type(obj)) { + case MRB_TT_CLASS: + case MRB_TT_SCLASS: + case MRB_TT_MODULE: + return; + default: + mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a class/module", mrb_inspect(mrb, obj)); + } +} + static struct RClass* define_module(mrb_state *mrb, mrb_sym name, struct RClass *outer) { @@ -160,6 +173,7 @@ mrb_define_module(mrb_state *mrb, const char *name) MRB_API struct RClass* mrb_vm_define_module(mrb_state *mrb, mrb_value outer, mrb_sym id) { + check_if_class_or_module(mrb, outer); return define_module(mrb, id, mrb_class_ptr(outer)); } @@ -232,15 +246,7 @@ mrb_vm_define_class(mrb_state *mrb, mrb_value outer, mrb_value super, mrb_sym id else { s = 0; } - switch (mrb_type(outer)) { - case MRB_TT_CLASS: - case MRB_TT_SCLASS: - case MRB_TT_MODULE: - break; - default: - mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a class/module", outer); - break; - } + check_if_class_or_module(mrb, outer); c = define_class(mrb, id, s, mrb_class_ptr(outer)); mrb_class_inherited(mrb, mrb_class_real(c->super), c); diff --git a/test/t/class.rb b/test/t/class.rb index d4ecf99d0..720fd37fa 100644 --- a/test/t/class.rb +++ b/test/t/class.rb @@ -383,3 +383,8 @@ assert('class variable and class << self style class method') do assert_equal("value", ClassVariableTest.class_variable) end + +assert('class with non-class/module outer raises TypeError') do + assert_raise(TypeError) { class 0::C1; end } + assert_raise(TypeError) { class []::C2; end } +end diff --git a/test/t/module.rb b/test/t/module.rb index 9852328ce..ecb969475 100644 --- a/test/t/module.rb +++ b/test/t/module.rb @@ -533,3 +533,7 @@ assert('Module#module_function') do assert_true M.respond_to?(:modfunc) end +assert('module with non-class/module outer raises TypeError') do + assert_raise(TypeError) { module 0::M1 end } + assert_raise(TypeError) { module []::M2 end } +end -- cgit v1.2.3 From 6a1978c7e1f58d3cda8cca390d65e5f64580b169 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Sun, 31 May 2015 18:30:37 +0900 Subject: fix OP_APOST bug for no pre arg cases; fix #2810 --- src/vm.c | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/vm.c b/src/vm.c index dd0d0ba43..1f3b05df2 100644 --- a/src/vm.c +++ b/src/vm.c @@ -2125,33 +2125,28 @@ RETRY_TRY_BLOCK: int pre = GETARG_B(i); int post = GETARG_C(i); + struct RArray *ary; + int len, idx; + if (!mrb_array_p(v)) { - regs[a++] = mrb_ary_new_capa(mrb, 0); + v = mrb_ary_new_from_values(mrb, 1, ®s[a]); + } + ary = mrb_ary_ptr(v); + len = ary->len; + if (len > pre + post) { + regs[a++] = mrb_ary_new_from_values(mrb, len - pre - post, ary->ptr+pre); while (post--) { - SET_NIL_VALUE(regs[a]); - a++; + regs[a++] = ary->ptr[len-post-1]; } } else { - struct RArray *ary = mrb_ary_ptr(v); - int len = ary->len; - int idx; - - if (len > pre + post) { - regs[a++] = mrb_ary_new_from_values(mrb, len - pre - post, ary->ptr+pre); - while (post--) { - regs[a++] = ary->ptr[len-post-1]; - } + regs[a++] = mrb_ary_new_capa(mrb, 0); + for (idx=0; idx+preptr[pre+idx]; } - else { - regs[a++] = mrb_ary_new_capa(mrb, 0); - for (idx=0; idx+preptr[pre+idx]; - } - while (idx < post) { - SET_NIL_VALUE(regs[a+idx]); - idx++; - } + while (idx < post) { + SET_NIL_VALUE(regs[a+idx]); + idx++; } } ARENA_RESTORE(mrb, ai); -- cgit v1.2.3 From 5cd877be7f875546dcc03d80aeeddd4bbbcffe3d Mon Sep 17 00:00:00 2001 From: cremno Date: Sun, 31 May 2015 13:30:49 +0200 Subject: fix masgn nosplat array rhs bug The rest lhs variable has to be an empty array if rhs is an array with less elements than pre + post lhs variables. The codegen generated OP_ARRAY with an invalid length (such as 127 for *a, b = []) because rn was negative. --- src/codegen.c | 8 +++++++- test/t/syntax.rb | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/codegen.c b/src/codegen.c index 6f1f75e88..be630b9a8 100644 --- a/src/codegen.c +++ b/src/codegen.c @@ -1615,8 +1615,14 @@ codegen(codegen_scope *s, node *tree, int val) } } if (t->car) { /* rest (len - pre - post) */ - int rn = len - post - n; + int rn; + if (len < post + n) { + rn = 0; + } + else { + rn = len - post - n; + } genop(s, MKOP_ABC(OP_ARRAY, cursp(), rhs+n, rn)); gen_assignment(s, t->car, cursp(), NOVAL); n += rn; diff --git a/test/t/syntax.rb b/test/t/syntax.rb index 070fcbe3b..dc1a4a3b9 100644 --- a/test/t/syntax.rb +++ b/test/t/syntax.rb @@ -234,6 +234,20 @@ assert('multiple assignment (rest+post)') do assert_equal 3, d end +assert('multiple assignment (nosplat array rhs)') do + a, *b = [] + *c, d = [0] + e, *f, g = [1, 2] + + assert_nil a + assert_equal [], b + assert_equal [], c + assert_equal 0, d + assert_equal 1, e + assert_equal [], f + assert_equal 2, g +end + assert('Return values of case statements') do a = [] << case 1 when 3 then 2 -- cgit v1.2.3 From bd2686d82d233774003fe683e9396f366b152b6d Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Mon, 1 Jun 2015 00:13:12 +0900 Subject: singleton_class should not be cloned; close #2815 --- src/kernel.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/kernel.c b/src/kernel.c index d1e10a7f8..9d056178e 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -330,6 +330,9 @@ mrb_obj_clone(mrb_state *mrb, mrb_value self) if (mrb_immediate_p(self)) { mrb_raisef(mrb, E_TYPE_ERROR, "can't clone %S", self); } + if (mrb_type(self) == MRB_TT_SCLASS) { + mrb_raise(mrb, E_TYPE_ERROR, "can't clone singleton class"); + } p = (struct RObject*)mrb_obj_alloc(mrb, mrb_type(self), mrb_obj_class(mrb, self)); p->c = mrb_singleton_class_clone(mrb, self); clone = mrb_obj_value(p); -- cgit v1.2.3 From dc0e33566410489db639a0523b9cb25b04f73080 Mon Sep 17 00:00:00 2001 From: cremno Date: Mon, 1 Jun 2015 13:23:43 +0200 Subject: fix two potential cases of signed integer overflow --- src/array.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/array.c b/src/array.c index 48dc1ff10..1ca7dd2a4 100644 --- a/src/array.c +++ b/src/array.c @@ -298,6 +298,9 @@ mrb_ary_plus(mrb_state *mrb, mrb_value self) mrb_int blen; mrb_get_args(mrb, "a", &ptr, &blen); + if (ARY_MAX_SIZE - blen < a1->len) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big"); + } ary = mrb_ary_new_capa(mrb, a1->len + blen); a2 = mrb_ary_ptr(ary); array_copy(a2->ptr, a1->ptr, a1->len); @@ -351,7 +354,9 @@ mrb_ary_times(mrb_state *mrb, mrb_value self) mrb_raise(mrb, E_ARGUMENT_ERROR, "negative argument"); } if (times == 0) return mrb_ary_new(mrb); - + if (ARY_MAX_SIZE / times < a1->len) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big"); + } ary = mrb_ary_new_capa(mrb, a1->len * times); a2 = mrb_ary_ptr(ary); ptr = a2->ptr; -- cgit v1.2.3 From 7e8fb15457f08faf0fd6b7e1d0823140ecee2a60 Mon Sep 17 00:00:00 2001 From: cremno Date: Mon, 1 Jun 2015 13:54:20 +0200 Subject: only generate gets of non-local vars in VAL mode This fixes a crash for code like "#{@a;1}". Unlike CRuby globals are excluded too since mruby doesn't implement hooking. --- src/codegen.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/codegen.c b/src/codegen.c index be630b9a8..16233347c 100644 --- a/src/codegen.c +++ b/src/codegen.c @@ -1911,7 +1911,7 @@ codegen(codegen_scope *s, node *tree, int val) break; case NODE_GVAR: - { + if (val) { int sym = new_sym(s, sym(tree)); genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym)); @@ -1920,7 +1920,7 @@ codegen(codegen_scope *s, node *tree, int val) break; case NODE_IVAR: - { + if (val) { int sym = new_sym(s, sym(tree)); genop(s, MKOP_ABx(OP_GETIV, cursp(), sym)); @@ -1929,7 +1929,7 @@ codegen(codegen_scope *s, node *tree, int val) break; case NODE_CVAR: - { + if (val) { int sym = new_sym(s, sym(tree)); genop(s, MKOP_ABx(OP_GETCV, cursp(), sym)); @@ -1951,7 +1951,7 @@ codegen(codegen_scope *s, node *tree, int val) break; case NODE_BACK_REF: - { + if (val) { char buf[2] = { '$' }; mrb_value str; int sym; @@ -1965,7 +1965,7 @@ codegen(codegen_scope *s, node *tree, int val) break; case NODE_NTH_REF: - { + if (val) { int sym; mrb_state *mrb = s->mrb; mrb_value fix = mrb_fixnum_value((intptr_t)tree); -- cgit v1.2.3 From b6121adc7dbedcf0e6fd262de536c2f8c9217120 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Mon, 1 Jun 2015 21:30:07 +0900 Subject: singleton_class should not be duped; fix #2815 --- src/kernel.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/kernel.c b/src/kernel.c index 9d056178e..b5b13f874 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -369,6 +369,9 @@ mrb_obj_dup(mrb_state *mrb, mrb_value obj) if (mrb_immediate_p(obj)) { mrb_raisef(mrb, E_TYPE_ERROR, "can't dup %S", obj); } + if (mrb_type(obj) == MRB_TT_SCLASS) { + mrb_raise(mrb, E_TYPE_ERROR, "can't dup singleton class"); + } p = mrb_obj_alloc(mrb, mrb_type(obj), mrb_obj_class(mrb, obj)); dup = mrb_obj_value(p); init_copy(mrb, dup, obj); -- cgit v1.2.3 From 6460ef77bcceb17d80d1b46a07b28fada19737c8 Mon Sep 17 00:00:00 2001 From: take_cheeze Date: Mon, 6 Apr 2015 15:20:13 +0900 Subject: Compile mruby compiler as mrbgem. Compiler codes is moved to "mruby-compiler". Executable `mrbc` is moved to "mruby-bin-mrbc". --- Rakefile | 1 - bintest/mrbc.rb | 12 - mrbgems/mruby-bin-mrbc/mrbgem.rake | 16 + mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c | 337 ++ mrbgems/mruby-compiler/bintest/mrbc.rb | 12 + mrbgems/mruby-compiler/core/codegen.c | 2720 +++++++++++++ mrbgems/mruby-compiler/core/keywords | 50 + mrbgems/mruby-compiler/core/lex.def | 212 + mrbgems/mruby-compiler/core/node.h | 117 + mrbgems/mruby-compiler/core/parse.y | 6420 ++++++++++++++++++++++++++++++ mrbgems/mruby-compiler/mrbgem.rake | 40 + mrbgems/mruby-eval/mrbgem.rake | 2 + src/codegen.c | 2720 ------------- src/keywords | 50 - src/lex.def | 212 - src/mruby_core.rake | 63 +- src/node.h | 117 - src/parse.y | 6420 ------------------------------ tasks/mruby_build.rake | 41 +- tools/mrbc/mrbc.c | 337 -- tools/mrbc/mrbc.rake | 14 - travis_config.rb | 4 + 22 files changed, 9971 insertions(+), 9946 deletions(-) delete mode 100644 bintest/mrbc.rb create mode 100644 mrbgems/mruby-bin-mrbc/mrbgem.rake create mode 100644 mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c create mode 100644 mrbgems/mruby-compiler/bintest/mrbc.rb create mode 100644 mrbgems/mruby-compiler/core/codegen.c create mode 100644 mrbgems/mruby-compiler/core/keywords create mode 100644 mrbgems/mruby-compiler/core/lex.def create mode 100644 mrbgems/mruby-compiler/core/node.h create mode 100644 mrbgems/mruby-compiler/core/parse.y create mode 100644 mrbgems/mruby-compiler/mrbgem.rake delete mode 100644 src/codegen.c delete mode 100644 src/keywords delete mode 100644 src/lex.def delete mode 100644 src/node.h delete mode 100644 src/parse.y delete mode 100644 tools/mrbc/mrbc.c delete mode 100644 tools/mrbc/mrbc.rake (limited to 'src') diff --git a/Rakefile b/Rakefile index 66f54a4e2..0f33c5ee8 100644 --- a/Rakefile +++ b/Rakefile @@ -21,7 +21,6 @@ end # load custom rules load "#{MRUBY_ROOT}/src/mruby_core.rake" load "#{MRUBY_ROOT}/mrblib/mrblib.rake" -load "#{MRUBY_ROOT}/tools/mrbc/mrbc.rake" load "#{MRUBY_ROOT}/tasks/mrbgems.rake" load "#{MRUBY_ROOT}/tasks/libmruby.rake" diff --git a/bintest/mrbc.rb b/bintest/mrbc.rb deleted file mode 100644 index b016378a1..000000000 --- a/bintest/mrbc.rb +++ /dev/null @@ -1,12 +0,0 @@ -require 'tempfile' - -assert('Compiling multiple files without new line in last line. #2361') do - a, b, out = Tempfile.new('a.rb'), Tempfile.new('b.rb'), Tempfile.new('out.mrb') - a.write('module A; end') - a.flush - b.write('module B; end') - b.flush - result = `bin/mrbc -c -o #{out.path} #{a.path} #{b.path} 2>&1` - assert_equal "bin/mrbc:#{a.path}:Syntax OK", result.chomp - assert_equal 0, $?.exitstatus -end diff --git a/mrbgems/mruby-bin-mrbc/mrbgem.rake b/mrbgems/mruby-bin-mrbc/mrbgem.rake new file mode 100644 index 000000000..e710b5a49 --- /dev/null +++ b/mrbgems/mruby-bin-mrbc/mrbgem.rake @@ -0,0 +1,16 @@ +MRuby::Gem::Specification.new 'mruby-bin-mrbc' do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' + spec.summary = 'mruby compiler executable' + + spec.add_dependency 'mruby-compiler', :core => 'mruby-compiler' + + exec = exefile("#{build.build_dir}/bin/mrbc") + mrbc_objs = Dir.glob("#{spec.dir}/tools/mrbc/*.c").map { |f| objfile(f.pathmap("#{spec.build_dir}/tools/mrbc/%n")) }.flatten + + file exec => mrbc_objs + [libfile("#{build.build_dir}/lib/libmruby_core")] do |t| + build.linker.run t.name, t.prerequisites + end + + build.bins << 'mrbc' unless build.bins.find { |v| v == 'mrbc' } +end diff --git a/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c b/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c new file mode 100644 index 000000000..f27f87a5d --- /dev/null +++ b/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c @@ -0,0 +1,337 @@ +#include +#include +#include +#include "mruby.h" +#include "mruby/compile.h" +#include "mruby/dump.h" +#include "mruby/proc.h" + +#define RITEBIN_EXT ".mrb" +#define C_EXT ".c" + +struct mrbc_args { + int argc; + char **argv; + int idx; + const char *prog; + const char *outfile; + const char *initname; + mrb_bool check_syntax : 1; + mrb_bool verbose : 1; + unsigned int flags : 4; +}; + +static void +usage(const char *name) +{ + static const char *const usage_msg[] = { + "switches:", + "-c check syntax only", + "-o place the output into ", + "-v print version number, then turn on verbose mode", + "-g produce debugging information", + "-B binary output in C language format", + "-e generate little endian iseq data", + "-E generate big endian iseq data", + "--verbose run at verbose mode", + "--version print the version", + "--copyright print the copyright", + NULL + }; + const char *const *p = usage_msg; + + printf("Usage: %s [switches] programfile\n", name); + while (*p) + printf(" %s\n", *p++); +} + +static char * +get_outfilename(mrb_state *mrb, char *infile, const char *ext) +{ + size_t infilelen; + size_t extlen; + char *outfile; + char *p; + + infilelen = strlen(infile); + extlen = strlen(ext); + outfile = (char*)mrb_malloc(mrb, infilelen + extlen + 1); + memcpy(outfile, infile, infilelen + 1); + if (*ext) { + if ((p = strrchr(outfile, '.')) == NULL) + p = outfile + infilelen; + memcpy(p, ext, extlen + 1); + } + + return outfile; +} + +static int +parse_args(mrb_state *mrb, int argc, char **argv, struct mrbc_args *args) +{ + char *outfile = NULL; + static const struct mrbc_args args_zero = { 0 }; + int i; + + *args = args_zero; + args->argc = argc; + args->argv = argv; + args->prog = argv[0]; + + for (i=1; ioutfile) { + fprintf(stderr, "%s: an output file is already specified. (%s)\n", + args->prog, outfile); + return -1; + } + if (argv[i][2] == '\0' && argv[i+1]) { + i++; + args->outfile = get_outfilename(mrb, argv[i], ""); + } + else { + args->outfile = get_outfilename(mrb, argv[i] + 2, ""); + } + break; + case 'B': + if (argv[i][2] == '\0' && argv[i+1]) { + i++; + args->initname = argv[i]; + } + else { + args->initname = argv[i]+2; + } + if (*args->initname == '\0') { + fprintf(stderr, "%s: function name is not specified.\n", args->prog); + return -1; + } + break; + case 'c': + args->check_syntax = TRUE; + break; + case 'v': + if (!args->verbose) mrb_show_version(mrb); + args->verbose = TRUE; + break; + case 'g': + args->flags |= DUMP_DEBUG_INFO; + break; + case 'E': + args->flags = DUMP_ENDIAN_BIG | (args->flags & DUMP_DEBUG_INFO); + break; + case 'e': + args->flags = DUMP_ENDIAN_LIL | (args->flags & DUMP_DEBUG_INFO); + break; + case 'h': + return -1; + case '-': + if (argv[i][1] == '\n') { + return i; + } + if (strcmp(argv[i] + 2, "version") == 0) { + mrb_show_version(mrb); + exit(EXIT_SUCCESS); + } + else if (strcmp(argv[i] + 2, "verbose") == 0) { + args->verbose = TRUE; + break; + } + else if (strcmp(argv[i] + 2, "copyright") == 0) { + mrb_show_copyright(mrb); + exit(EXIT_SUCCESS); + } + return -1; + default: + return i; + } + } + else { + break; + } + } + if (args->verbose && args->initname && (args->flags & DUMP_ENDIAN_MASK) == 0) { + fprintf(stderr, "%s: generating %s endian C file. specify -e/-E for cross compiling.\n", + args->prog, bigendian_p() ? "big" : "little"); + } + return i; +} + +static void +cleanup(mrb_state *mrb, struct mrbc_args *args) +{ + if (args->outfile) + mrb_free(mrb, (void*)args->outfile); + mrb_close(mrb); +} + +static int +partial_hook(struct mrb_parser_state *p) +{ + mrbc_context *c = p->cxt; + struct mrbc_args *args = (struct mrbc_args *)c->partial_data; + const char *fn; + + if (p->f) fclose(p->f); + if (args->idx >= args->argc) { + p->f = NULL; + return -1; + } + fn = args->argv[args->idx++]; + p->f = fopen(fn, "r"); + if (p->f == NULL) { + fprintf(stderr, "%s: cannot open program file. (%s)\n", args->prog, fn); + return -1; + } + mrb_parser_set_filename(p, fn); + return 0; +} + +static mrb_value +load_file(mrb_state *mrb, struct mrbc_args *args) +{ + mrbc_context *c; + mrb_value result; + char *input = args->argv[args->idx]; + FILE *infile; + mrb_bool need_close = FALSE; + + c = mrbc_context_new(mrb); + if (args->verbose) + c->dump_result = TRUE; + c->no_exec = TRUE; + if (input[0] == '-' && input[1] == '\0') { + infile = stdin; + } + else { + need_close = TRUE; + if ((infile = fopen(input, "r")) == NULL) { + fprintf(stderr, "%s: cannot open program file. (%s)\n", args->prog, input); + return mrb_nil_value(); + } + } + mrbc_filename(mrb, c, input); + args->idx++; + if (args->idx < args->argc) { + need_close = FALSE; + mrbc_partial_hook(mrb, c, partial_hook, (void*)args); + } + + result = mrb_load_file_cxt(mrb, infile, c); + if (need_close) fclose(infile); + mrbc_context_free(mrb, c); + if (mrb_undef_p(result)) { + return mrb_nil_value(); + } + return result; +} + +static int +dump_file(mrb_state *mrb, FILE *wfp, const char *outfile, struct RProc *proc, struct mrbc_args *args) +{ + int n = MRB_DUMP_OK; + mrb_irep *irep = proc->body.irep; + + if (args->initname) { + n = mrb_dump_irep_cfunc(mrb, irep, args->flags, wfp, args->initname); + if (n == MRB_DUMP_INVALID_ARGUMENT) { + fprintf(stderr, "%s: invalid C language symbol name\n", args->initname); + } + } + else { + n = mrb_dump_irep_binary(mrb, irep, args->flags, wfp); + } + if (n != MRB_DUMP_OK) { + fprintf(stderr, "%s: error in mrb dump (%s) %d\n", args->prog, outfile, n); + } + return n; +} + +int +main(int argc, char **argv) +{ + mrb_state *mrb = mrb_open(); + int n, result; + struct mrbc_args args; + FILE *wfp; + mrb_value load; + + if (mrb == NULL) { + fputs("Invalid mrb_state, exiting mrbc\n", stderr); + return EXIT_FAILURE; + } + + n = parse_args(mrb, argc, argv, &args); + if (n < 0) { + cleanup(mrb, &args); + usage(argv[0]); + return EXIT_FAILURE; + } + if (n == argc) { + fprintf(stderr, "%s: no program file given\n", args.prog); + return EXIT_FAILURE; + } + if (args.outfile == NULL && !args.check_syntax) { + if (n + 1 == argc) { + args.outfile = get_outfilename(mrb, argv[n], args.initname ? C_EXT : RITEBIN_EXT); + } + else { + fprintf(stderr, "%s: output file should be specified to compile multiple files\n", args.prog); + return EXIT_FAILURE; + } + } + + args.idx = n; + load = load_file(mrb, &args); + if (mrb_nil_p(load)) { + cleanup(mrb, &args); + return EXIT_FAILURE; + } + if (args.check_syntax) { + printf("%s:%s:Syntax OK\n", args.prog, argv[n]); + } + + if (args.check_syntax) { + cleanup(mrb, &args); + return EXIT_SUCCESS; + } + + if (args.outfile) { + if (strcmp("-", args.outfile) == 0) { + wfp = stdout; + } + else if ((wfp = fopen(args.outfile, "wb")) == NULL) { + fprintf(stderr, "%s: cannot open output file:(%s)\n", args.prog, args.outfile); + return EXIT_FAILURE; + } + } + else { + fprintf(stderr, "Output file is required\n"); + return EXIT_FAILURE; + } + result = dump_file(mrb, wfp, args.outfile, mrb_proc_ptr(load), &args); + fclose(wfp); + cleanup(mrb, &args); + if (result != MRB_DUMP_OK) { + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} + +void +mrb_init_mrblib(mrb_state *mrb) +{ +} + +#ifndef DISABLE_GEMS +void +mrb_init_mrbgems(mrb_state *mrb) +{ +} + +void +mrb_final_mrbgems(mrb_state *mrb) +{ +} +#endif diff --git a/mrbgems/mruby-compiler/bintest/mrbc.rb b/mrbgems/mruby-compiler/bintest/mrbc.rb new file mode 100644 index 000000000..b016378a1 --- /dev/null +++ b/mrbgems/mruby-compiler/bintest/mrbc.rb @@ -0,0 +1,12 @@ +require 'tempfile' + +assert('Compiling multiple files without new line in last line. #2361') do + a, b, out = Tempfile.new('a.rb'), Tempfile.new('b.rb'), Tempfile.new('out.mrb') + a.write('module A; end') + a.flush + b.write('module B; end') + b.flush + result = `bin/mrbc -c -o #{out.path} #{a.path} #{b.path} 2>&1` + assert_equal "bin/mrbc:#{a.path}:Syntax OK", result.chomp + assert_equal 0, $?.exitstatus +end diff --git a/mrbgems/mruby-compiler/core/codegen.c b/mrbgems/mruby-compiler/core/codegen.c new file mode 100644 index 000000000..16233347c --- /dev/null +++ b/mrbgems/mruby-compiler/core/codegen.c @@ -0,0 +1,2720 @@ +/* +** codegen.c - mruby code generator +** +** See Copyright Notice in mruby.h +*/ + +#include +#include +#include +#include +#include "mruby.h" +#include "mruby/compile.h" +#include "mruby/proc.h" +#include "mruby/numeric.h" +#include "mruby/string.h" +#include "mruby/debug.h" +#include "node.h" +#include "mruby/opcode.h" +#include "mruby/re.h" +#include "mruby/throw.h" + +typedef mrb_ast_node node; +typedef struct mrb_parser_state parser_state; + +enum looptype { + LOOP_NORMAL, + LOOP_BLOCK, + LOOP_FOR, + LOOP_BEGIN, + LOOP_RESCUE, +}; + +struct loopinfo { + enum looptype type; + int pc1, pc2, pc3, acc; + int ensure_level; + struct loopinfo *prev; +}; + +typedef struct scope { + mrb_state *mrb; + mrb_pool *mpool; + struct mrb_jmpbuf jmp; + + struct scope *prev; + + node *lv; + + int sp; + int pc; + int lastlabel; + int ainfo:15; + mrb_bool mscope:1; + + struct loopinfo *loop; + int ensure_level; + char const *filename; + uint16_t lineno; + + mrb_code *iseq; + uint16_t *lines; + int icapa; + + mrb_irep *irep; + size_t pcapa; + size_t scapa; + size_t rcapa; + + uint16_t nlocals; + uint16_t nregs; + int ai; + + int debug_start_pos; + uint16_t filename_index; + parser_state* parser; +} codegen_scope; + +static codegen_scope* scope_new(mrb_state *mrb, codegen_scope *prev, node *lv); +static void scope_finish(codegen_scope *s); +static struct loopinfo *loop_push(codegen_scope *s, enum looptype t); +static void loop_break(codegen_scope *s, node *tree); +static void loop_pop(codegen_scope *s, int val); + +static void gen_assignment(codegen_scope *s, node *tree, int sp, int val); +static void gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val); + +static void codegen(codegen_scope *s, node *tree, int val); +static void raise_error(codegen_scope *s, const char *msg); + +static void +codegen_error(codegen_scope *s, const char *message) +{ + if (!s) return; + while (s->prev) { + codegen_scope *tmp = s->prev; + mrb_pool_close(s->mpool); + s = tmp; + } +#ifdef ENABLE_STDIO + if (s->filename && s->lineno) { + fprintf(stderr, "codegen error:%s:%d: %s\n", s->filename, s->lineno, message); + } + else { + fprintf(stderr, "codegen error: %s\n", message); + } +#endif + MRB_THROW(&s->jmp); +} + +static void* +codegen_palloc(codegen_scope *s, size_t len) +{ + void *p = mrb_pool_alloc(s->mpool, len); + + if (!p) codegen_error(s, "pool memory allocation"); + return p; +} + +static void* +codegen_malloc(codegen_scope *s, size_t len) +{ + void *p = mrb_malloc_simple(s->mrb, len); + + if (!p) codegen_error(s, "mrb_malloc"); + return p; +} + +static void* +codegen_realloc(codegen_scope *s, void *p, size_t len) +{ + p = mrb_realloc_simple(s->mrb, p, len); + + if (!p && len > 0) codegen_error(s, "mrb_realloc"); + return p; +} + +static int +new_label(codegen_scope *s) +{ + s->lastlabel = s->pc; + return s->pc; +} + +static inline int +genop(codegen_scope *s, mrb_code i) +{ + if (s->pc == s->icapa) { + s->icapa *= 2; + s->iseq = (mrb_code *)codegen_realloc(s, s->iseq, sizeof(mrb_code)*s->icapa); + if (s->lines) { + s->lines = (uint16_t*)codegen_realloc(s, s->lines, sizeof(short)*s->icapa); + s->irep->lines = s->lines; + } + } + s->iseq[s->pc] = i; + if (s->lines) { + s->lines[s->pc] = s->lineno; + } + return s->pc++; +} + +#define NOVAL 0 +#define VAL 1 + +static mrb_bool +no_optimize(codegen_scope *s) +{ + if (s && s->parser && s->parser->no_optimize) + return TRUE; + return FALSE; +} + +static int +genop_peep(codegen_scope *s, mrb_code i, int val) +{ + /* peephole optimization */ + if (!no_optimize(s) && s->lastlabel != s->pc && s->pc > 0) { + mrb_code i0 = s->iseq[s->pc-1]; + int c1 = GET_OPCODE(i); + int c0 = GET_OPCODE(i0); + + switch (c1) { + case OP_MOVE: + if (GETARG_A(i) == GETARG_B(i)) { + /* skip useless OP_MOVE */ + return 0; + } + if (val) break; + switch (c0) { + case OP_MOVE: + if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i) == GETARG_B(i0)) { + /* skip swapping OP_MOVE */ + return 0; + } + if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) { + s->pc--; + return genop_peep(s, MKOP_AB(OP_MOVE, GETARG_A(i), GETARG_B(i0)), val); + } + break; + case OP_LOADI: + if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) { + s->iseq[s->pc-1] = MKOP_AsBx(OP_LOADI, GETARG_A(i), GETARG_sBx(i0)); + return 0; + } + break; + case OP_ARRAY: + case OP_HASH: + case OP_RANGE: + case OP_AREF: + case OP_GETUPVAR: + if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) { + s->iseq[s->pc-1] = MKOP_ABC(c0, GETARG_A(i), GETARG_B(i0), GETARG_C(i0)); + return 0; + } + break; + case OP_LOADSYM: + case OP_GETGLOBAL: + case OP_GETIV: + case OP_GETCV: + case OP_GETCONST: + case OP_GETSPECIAL: + case OP_LOADL: + case OP_STRING: + if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) { + s->iseq[s->pc-1] = MKOP_ABx(c0, GETARG_A(i), GETARG_Bx(i0)); + return 0; + } + break; + case OP_SCLASS: + if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) { + s->iseq[s->pc-1] = MKOP_AB(c0, GETARG_A(i), GETARG_B(i0)); + return 0; + } + break; + case OP_LOADNIL: + case OP_LOADSELF: + case OP_LOADT: + case OP_LOADF: + case OP_OCLASS: + if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) { + s->iseq[s->pc-1] = MKOP_A(c0, GETARG_A(i)); + return 0; + } + break; + default: + break; + } + break; + case OP_SETIV: + case OP_SETCV: + case OP_SETCONST: + case OP_SETMCNST: + case OP_SETGLOBAL: + if (val) break; + if (c0 == OP_MOVE) { + if (GETARG_A(i) == GETARG_A(i0)) { + s->iseq[s->pc-1] = MKOP_ABx(c1, GETARG_B(i0), GETARG_Bx(i)); + return 0; + } + } + break; + case OP_SETUPVAR: + if (val) break; + if (c0 == OP_MOVE) { + if (GETARG_A(i) == GETARG_A(i0)) { + s->iseq[s->pc-1] = MKOP_ABC(c1, GETARG_B(i0), GETARG_B(i), GETARG_C(i)); + return 0; + } + } + break; + case OP_EPOP: + if (c0 == OP_EPOP) { + s->iseq[s->pc-1] = MKOP_A(OP_EPOP, GETARG_A(i0)+GETARG_A(i)); + return 0; + } + break; + case OP_POPERR: + if (c0 == OP_POPERR) { + s->iseq[s->pc-1] = MKOP_A(OP_POPERR, GETARG_A(i0)+GETARG_A(i)); + return 0; + } + break; + case OP_RETURN: + switch (c0) { + case OP_RETURN: + return 0; + case OP_MOVE: + if (GETARG_A(i0) >= s->nlocals) { + s->iseq[s->pc-1] = MKOP_AB(OP_RETURN, GETARG_B(i0), OP_R_NORMAL); + return 0; + } + break; + case OP_SETIV: + case OP_SETCV: + case OP_SETCONST: + case OP_SETMCNST: + case OP_SETUPVAR: + case OP_SETGLOBAL: + s->pc--; + genop_peep(s, i0, NOVAL); + i0 = s->iseq[s->pc-1]; + return genop(s, MKOP_AB(OP_RETURN, GETARG_A(i0), OP_R_NORMAL)); +#if 0 + case OP_SEND: + if (GETARG_B(i) == OP_R_NORMAL && GETARG_A(i) == GETARG_A(i0)) { + s->iseq[s->pc-1] = MKOP_ABC(OP_TAILCALL, GETARG_A(i0), GETARG_B(i0), GETARG_C(i0)); + return; + } + break; +#endif + default: + break; + } + break; + case OP_ADD: + case OP_SUB: + if (c0 == OP_LOADI) { + int c = GETARG_sBx(i0); + + if (c1 == OP_SUB) c = -c; + if (c > 127 || c < -127) break; + if (0 <= c) + s->iseq[s->pc-1] = MKOP_ABC(OP_ADDI, GETARG_A(i), GETARG_B(i), c); + else + s->iseq[s->pc-1] = MKOP_ABC(OP_SUBI, GETARG_A(i), GETARG_B(i), -c); + return 0; + } + case OP_STRCAT: + if (c0 == OP_STRING) { + mrb_value v = s->irep->pool[GETARG_Bx(i0)]; + + if (mrb_string_p(v) && RSTRING_LEN(v) == 0) { + s->pc--; + return 0; + } + } + break; + case OP_JMPIF: + case OP_JMPNOT: + if (c0 == OP_MOVE && GETARG_A(i) == GETARG_A(i0)) { + s->iseq[s->pc-1] = MKOP_AsBx(c1, GETARG_B(i0), GETARG_sBx(i)); + return s->pc-1; + } + break; + default: + break; + } + } + return genop(s, i); +} + +static void +scope_error(codegen_scope *s) +{ + exit(EXIT_FAILURE); +} + +static inline void +dispatch(codegen_scope *s, int pc) +{ + int diff = s->pc - pc; + mrb_code i = s->iseq[pc]; + int c = GET_OPCODE(i); + + s->lastlabel = s->pc; + switch (c) { + case OP_JMP: + case OP_JMPIF: + case OP_JMPNOT: + case OP_ONERR: + break; + default: +#ifdef ENABLE_STDIO + fprintf(stderr, "bug: dispatch on non JMP op\n"); +#endif + scope_error(s); + break; + } + s->iseq[pc] = MKOP_AsBx(c, GETARG_A(i), diff); +} + +static void +dispatch_linked(codegen_scope *s, int pc) +{ + mrb_code i; + int pos; + + if (!pc) return; + for (;;) { + i = s->iseq[pc]; + pos = GETARG_sBx(i); + dispatch(s, pc); + if (!pos) break; + pc = pos; + } +} + +#define nregs_update do {if (s->sp > s->nregs) s->nregs = s->sp;} while (0) +static void +push_(codegen_scope *s) +{ + if (s->sp > 511) { + codegen_error(s, "too complex expression"); + } + s->sp++; + nregs_update; +} + +#define push() push_(s) +#define pop_(s) ((s)->sp--) +#define pop() pop_(s) +#define pop_n(n) (s->sp-=(n)) +#define cursp() (s->sp) + +static inline int +new_lit(codegen_scope *s, mrb_value val) +{ + size_t i; + mrb_value *pv; + + switch (mrb_type(val)) { + case MRB_TT_STRING: + for (i=0; iirep->plen; i++) { + mrb_int len; + pv = &s->irep->pool[i]; + + if (mrb_type(*pv) != MRB_TT_STRING) continue; + if ((len = RSTRING_LEN(*pv)) != RSTRING_LEN(val)) continue; + if (memcmp(RSTRING_PTR(*pv), RSTRING_PTR(val), len) == 0) + return i; + } + break; + case MRB_TT_FLOAT: + for (i=0; iirep->plen; i++) { + pv = &s->irep->pool[i]; + if (mrb_type(*pv) != MRB_TT_FLOAT) continue; + if (mrb_float(*pv) == mrb_float(val)) return i; + } + break; + case MRB_TT_FIXNUM: + for (i=0; iirep->plen; i++) { + pv = &s->irep->pool[i]; + if (!mrb_fixnum_p(*pv)) continue; + if (mrb_fixnum(*pv) == mrb_fixnum(val)) return i; + } + break; + default: + /* should not happen */ + return 0; + } + + if (s->irep->plen == s->pcapa) { + s->pcapa *= 2; + s->irep->pool = (mrb_value *)codegen_realloc(s, s->irep->pool, sizeof(mrb_value)*s->pcapa); + } + + pv = &s->irep->pool[s->irep->plen]; + i = s->irep->plen++; + + switch (mrb_type(val)) { + case MRB_TT_STRING: + *pv = mrb_str_pool(s->mrb, val); + break; + + case MRB_TT_FLOAT: +#ifdef MRB_WORD_BOXING + *pv = mrb_float_pool(s->mrb, mrb_float(val)); + break; +#endif + case MRB_TT_FIXNUM: + *pv = val; + break; + + default: + /* should not happen */ + break; + } + return i; +} + +static inline int +new_msym(codegen_scope *s, mrb_sym sym) +{ + size_t i, len; + + mrb_assert(s->irep); + + len = s->irep->slen; + if (len > 256) len = 256; + for (i=0; iirep->syms[i] == sym) return i; + if (s->irep->syms[i] == 0) break; + } + if (i == 256) { + codegen_error(s, "too many symbols (max 256)"); + } + s->irep->syms[i] = sym; + if (i == s->irep->slen) s->irep->slen++; + return i; +} + +static inline int +new_sym(codegen_scope *s, mrb_sym sym) +{ + size_t i; + + for (i=0; iirep->slen; i++) { + if (s->irep->syms[i] == sym) return i; + } + if (s->irep->slen > 125 && s->irep->slen < 256) { + s->irep->syms = (mrb_sym *)codegen_realloc(s, s->irep->syms, sizeof(mrb_sym)*65536); + for (i = 0; i < 256 - s->irep->slen; i++) { + static const mrb_sym mrb_sym_zero = { 0 }; + s->irep->syms[i + s->irep->slen] = mrb_sym_zero; + } + s->irep->slen = 256; + } + s->irep->syms[s->irep->slen] = sym; + return s->irep->slen++; +} + +static int +node_len(node *tree) +{ + int n = 0; + + while (tree) { + n++; + tree = tree->cdr; + } + return n; +} + +#define sym(x) ((mrb_sym)(intptr_t)(x)) +#define lv_name(lv) sym((lv)->car) +static int +lv_idx(codegen_scope *s, mrb_sym id) +{ + node *lv = s->lv; + int n = 1; + + while (lv) { + if (lv_name(lv) == id) return n; + n++; + lv = lv->cdr; + } + return 0; +} + +static void +for_body(codegen_scope *s, node *tree) +{ + codegen_scope *prev = s; + int idx; + struct loopinfo *lp; + node *n2; + mrb_code c; + + /* generate receiver */ + codegen(s, tree->cdr->car, VAL); + /* generate loop-block */ + s = scope_new(s->mrb, s, NULL); + if (s == NULL) { + raise_error(prev, "unexpected scope"); + } + + push(); /* push for a block parameter */ + + lp = loop_push(s, LOOP_FOR); + lp->pc1 = new_label(s); + + /* generate loop variable */ + n2 = tree->car; + genop(s, MKOP_Ax(OP_ENTER, 0x40000)); + if (n2->car && !n2->car->cdr && !n2->cdr) { + gen_assignment(s, n2->car->car, 1, NOVAL); + } + else { + gen_vmassignment(s, n2, 1, VAL); + } + codegen(s, tree->cdr->cdr->car, VAL); + pop(); + if (s->pc > 0) { + c = s->iseq[s->pc-1]; + if (GET_OPCODE(c) != OP_RETURN || GETARG_B(c) != OP_R_NORMAL || s->pc == s->lastlabel) + genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL); + } + loop_pop(s, NOVAL); + scope_finish(s); + s = prev; + genop(s, MKOP_Abc(OP_LAMBDA, cursp(), s->irep->rlen-1, OP_L_BLOCK)); + pop(); + idx = new_msym(s, mrb_intern_lit(s->mrb, "each")); + genop(s, MKOP_ABC(OP_SENDB, cursp(), idx, 0)); +} + +static int +lambda_body(codegen_scope *s, node *tree, int blk) +{ + mrb_code c; + codegen_scope *parent = s; + s = scope_new(s->mrb, s, tree->car); + if (s == NULL) { + raise_error(parent, "unexpected scope"); + } + + s->mscope = !blk; + + if (blk) { + struct loopinfo *lp = loop_push(s, LOOP_BLOCK); + lp->pc1 = new_label(s); + } + tree = tree->cdr; + if (tree->car) { + mrb_aspec a; + int ma, oa, ra, pa, ka, kd, ba; + int pos, i; + node *n, *opt; + + ma = node_len(tree->car->car); + n = tree->car->car; + while (n) { + n = n->cdr; + } + oa = node_len(tree->car->cdr->car); + ra = tree->car->cdr->cdr->car ? 1 : 0; + pa = node_len(tree->car->cdr->cdr->cdr->car); + ka = kd = 0; + ba = tree->car->cdr->cdr->cdr->cdr ? 1 : 0; + + a = ((mrb_aspec)(ma & 0x1f) << 18) + | ((mrb_aspec)(oa & 0x1f) << 13) + | ((ra & 1) << 12) + | ((pa & 0x1f) << 7) + | ((ka & 0x1f) << 2) + | ((kd & 1)<< 1) + | (ba & 1); + s->ainfo = (((ma+oa) & 0x3f) << 6) /* (12bits = 6:1:5) */ + | ((ra & 1) << 5) + | (pa & 0x1f); + genop(s, MKOP_Ax(OP_ENTER, a)); + pos = new_label(s); + for (i=0; i 0) { + genop(s, MKOP_sBx(OP_JMP, 0)); + } + opt = tree->car->cdr->car; + i = 0; + while (opt) { + int idx; + + dispatch(s, pos+i); + codegen(s, opt->car->cdr, VAL); + idx = lv_idx(s, (mrb_sym)(intptr_t)opt->car->car); + pop(); + genop_peep(s, MKOP_AB(OP_MOVE, idx, cursp()), NOVAL); + i++; + opt = opt->cdr; + } + if (oa > 0) { + dispatch(s, pos+i); + } + } + codegen(s, tree->cdr->car, VAL); + pop(); + if (s->pc > 0) { + c = s->iseq[s->pc-1]; + if (GET_OPCODE(c) != OP_RETURN || GETARG_B(c) != OP_R_NORMAL || s->pc == s->lastlabel) { + if (s->nregs == 0) { + genop(s, MKOP_A(OP_LOADNIL, 0)); + genop(s, MKOP_AB(OP_RETURN, 0, OP_R_NORMAL)); + } + else { + genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL); + } + } + } + if (blk) { + loop_pop(s, NOVAL); + } + scope_finish(s); + return parent->irep->rlen - 1; +} + +static int +scope_body(codegen_scope *s, node *tree, int val) +{ + codegen_scope *scope = scope_new(s->mrb, s, tree->car); + if (scope == NULL) { + raise_error(s, "unexpected scope"); + } + + codegen(scope, tree->cdr, VAL); + if (!s->iseq) { + genop(scope, MKOP_A(OP_STOP, 0)); + } + else if (!val) { + genop(scope, MKOP_AB(OP_RETURN, 0, OP_R_NORMAL)); + } + else { + if (scope->nregs == 0) { + genop(scope, MKOP_A(OP_LOADNIL, 0)); + genop(scope, MKOP_AB(OP_RETURN, 0, OP_R_NORMAL)); + } + else { + genop_peep(scope, MKOP_AB(OP_RETURN, scope->sp-1, OP_R_NORMAL), NOVAL); + } + } + scope_finish(scope); + if (!s->irep) { + /* should not happen */ + return 0; + } + return s->irep->rlen - 1; +} + +static mrb_bool +nosplat(node *t) +{ + while (t) { + if ((intptr_t)t->car->car == NODE_SPLAT) return FALSE; + t = t->cdr; + } + return TRUE; +} + +static mrb_sym +attrsym(codegen_scope *s, mrb_sym a) +{ + const char *name; + mrb_int len; + char *name2; + + name = mrb_sym2name_len(s->mrb, a, &len); + name2 = (char *)codegen_palloc(s, + (size_t)len + + 1 /* '=' */ + + 1 /* '\0' */ + ); + mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX); + memcpy(name2, name, (size_t)len); + name2[len] = '='; + name2[len+1] = '\0'; + + return mrb_intern(s->mrb, name2, len+1); +} + +static int +gen_values(codegen_scope *s, node *t, int val) +{ + int n = 0; + int is_splat; + + while (t) { + is_splat = (intptr_t)t->car->car == NODE_SPLAT; /* splat mode */ + if (n >= 127 || is_splat) { + if (val) { + pop_n(n); + genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), n)); + push(); + codegen(s, t->car, VAL); + pop(); pop(); + if (is_splat) { + genop(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1)); + } + else { + genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1)); + } + t = t->cdr; + while (t) { + push(); + codegen(s, t->car, VAL); + pop(); pop(); + if ((intptr_t)t->car->car == NODE_SPLAT) { + genop(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1)); + } + else { + genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1)); + } + t = t->cdr; + } + } + else { + codegen(s, t->car->cdr, NOVAL); + t = t->cdr; + while (t) { + codegen(s, t->car, NOVAL); + t = t->cdr; + } + } + return -1; + } + /* normal (no splat) mode */ + codegen(s, t->car, val); + n++; + t = t->cdr; + } + return n; +} + +#define CALL_MAXARGS 127 + +static void +gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val) +{ + mrb_sym sym = name ? name : sym(tree->cdr->car); + int idx; + int n = 0, noop = 0, sendv = 0, blk = 0; + + codegen(s, tree->car, VAL); /* receiver */ + idx = new_msym(s, sym); + tree = tree->cdr->cdr->car; + if (tree) { + n = gen_values(s, tree->car, VAL); + if (n < 0) { + n = noop = sendv = 1; + push(); + } + } + if (sp) { + if (sendv) { + pop(); + genop(s, MKOP_AB(OP_ARYPUSH, cursp(), sp)); + push(); + } + else { + genop(s, MKOP_AB(OP_MOVE, cursp(), sp)); + push(); + n++; + } + } + if (tree && tree->cdr) { + noop = 1; + codegen(s, tree->cdr, VAL); + pop(); + } + else { + blk = cursp(); + } + push();pop(); + pop_n(n+1); + { + mrb_int symlen; + const char *symname = mrb_sym2name_len(s->mrb, sym, &symlen); + + if (!noop && symlen == 1 && symname[0] == '+') { + genop_peep(s, MKOP_ABC(OP_ADD, cursp(), idx, n), val); + } + else if (!noop && symlen == 1 && symname[0] == '-') { + genop_peep(s, MKOP_ABC(OP_SUB, cursp(), idx, n), val); + } + else if (!noop && symlen == 1 && symname[0] == '*') { + genop(s, MKOP_ABC(OP_MUL, cursp(), idx, n)); + } + else if (!noop && symlen == 1 && symname[0] == '/') { + genop(s, MKOP_ABC(OP_DIV, cursp(), idx, n)); + } + else if (!noop && symlen == 1 && symname[0] == '<') { + genop(s, MKOP_ABC(OP_LT, cursp(), idx, n)); + } + else if (!noop && symlen == 2 && symname[0] == '<' && symname[1] == '=') { + genop(s, MKOP_ABC(OP_LE, cursp(), idx, n)); + } + else if (!noop && symlen == 1 && symname[0] == '>') { + genop(s, MKOP_ABC(OP_GT, cursp(), idx, n)); + } + else if (!noop && symlen == 2 && symname[0] == '>' && symname[1] == '=') { + genop(s, MKOP_ABC(OP_GE, cursp(), idx, n)); + } + else if (!noop && symlen == 2 && symname[0] == '=' && symname[1] == '=') { + genop(s, MKOP_ABC(OP_EQ, cursp(), idx, n)); + } + else { + if (sendv) n = CALL_MAXARGS; + if (blk > 0) { /* no block */ + genop(s, MKOP_ABC(OP_SEND, cursp(), idx, n)); + } + else { + genop(s, MKOP_ABC(OP_SENDB, cursp(), idx, n)); + } + } + } + if (val) { + push(); + } +} + +static void +gen_assignment(codegen_scope *s, node *tree, int sp, int val) +{ + int idx; + int type = (intptr_t)tree->car; + + tree = tree->cdr; + switch ((intptr_t)type) { + case NODE_GVAR: + idx = new_sym(s, sym(tree)); + genop_peep(s, MKOP_ABx(OP_SETGLOBAL, sp, idx), val); + break; + case NODE_LVAR: + idx = lv_idx(s, sym(tree)); + if (idx > 0) { + if (idx != sp) { + genop_peep(s, MKOP_AB(OP_MOVE, idx, sp), val); + } + break; + } + else { /* upvar */ + int lv = 0; + codegen_scope *up = s->prev; + + while (up) { + idx = lv_idx(up, sym(tree)); + if (idx > 0) { + genop_peep(s, MKOP_ABC(OP_SETUPVAR, sp, idx, lv), val); + break; + } + lv++; + up = up->prev; + } + } + break; + case NODE_IVAR: + idx = new_sym(s, sym(tree)); + genop_peep(s, MKOP_ABx(OP_SETIV, sp, idx), val); + break; + case NODE_CVAR: + idx = new_sym(s, sym(tree)); + genop_peep(s, MKOP_ABx(OP_SETCV, sp, idx), val); + break; + case NODE_CONST: + idx = new_sym(s, sym(tree)); + genop_peep(s, MKOP_ABx(OP_SETCONST, sp, idx), val); + break; + case NODE_COLON2: + idx = new_sym(s, sym(tree->cdr)); + genop_peep(s, MKOP_AB(OP_MOVE, cursp(), sp), NOVAL); + push(); + codegen(s, tree->car, VAL); + pop_n(2); + genop_peep(s, MKOP_ABx(OP_SETMCNST, cursp(), idx), val); + break; + + case NODE_CALL: + push(); + gen_call(s, tree, attrsym(s, sym(tree->cdr->car)), sp, NOVAL); + pop(); + if (val) { + genop_peep(s, MKOP_AB(OP_MOVE, cursp(), sp), val); + } + break; + + case NODE_MASGN: + gen_vmassignment(s, tree->car, sp, val); + break; + + /* splat without assignment */ + case NODE_NIL: + break; + + default: +#ifdef ENABLE_STDIO + printf("unknown lhs %d\n", type); +#endif + break; + } + if (val) push(); +} + +static void +gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val) +{ + int n = 0, post = 0; + node *t, *p; + + if (tree->car) { /* pre */ + t = tree->car; + n = 0; + while (t) { + genop(s, MKOP_ABC(OP_AREF, cursp(), rhs, n)); + gen_assignment(s, t->car, cursp(), NOVAL); + n++; + t = t->cdr; + } + } + t = tree->cdr; + if (t) { + if (t->cdr) { /* post count */ + p = t->cdr->car; + while (p) { + post++; + p = p->cdr; + } + } + if (val) { + genop(s, MKOP_AB(OP_MOVE, cursp(), rhs)); + } + else { + pop(); + } + genop(s, MKOP_ABC(OP_APOST, cursp(), n, post)); + n = 1; + if (t->car) { /* rest */ + gen_assignment(s, t->car, cursp(), NOVAL); + } + if (t->cdr && t->cdr->car) { + t = t->cdr->car; + while (t) { + gen_assignment(s, t->car, cursp()+n, NOVAL); + t = t->cdr; + n++; + } + } + push(); + } +} + +static void +gen_send_intern(codegen_scope *s) +{ + pop(); + genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "intern")), 0)); + push(); +} +static void +gen_literal_array(codegen_scope *s, node *tree, mrb_bool sym, int val) +{ + if (val) { + int i = 0, j = 0; + + while (tree) { + switch ((intptr_t)tree->car->car) { + case NODE_STR: + if ((tree->cdr == NULL) && ((intptr_t)tree->car->cdr->cdr == 0)) + break; + /* fall through */ + case NODE_BEGIN: + codegen(s, tree->car, VAL); + ++j; + break; + + case NODE_LITERAL_DELIM: + if (j > 0) { + j = 0; + ++i; + if (sym) + gen_send_intern(s); + } + break; + } + if (j >= 2) { + pop(); pop(); + genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL); + push(); + j = 1; + } + tree = tree->cdr; + } + if (j > 0) { + ++i; + if (sym) + gen_send_intern(s); + } + pop_n(i); + genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), i)); + push(); + } + else { + while (tree) { + switch ((intptr_t)tree->car->car) { + case NODE_BEGIN: case NODE_BLOCK: + codegen(s, tree->car, NOVAL); + } + tree = tree->cdr; + } + } +} + +static void +raise_error(codegen_scope *s, const char *msg) +{ + int idx = new_lit(s, mrb_str_new_cstr(s->mrb, msg)); + + genop(s, MKOP_ABx(OP_ERR, 1, idx)); +} + +static double +readint_float(codegen_scope *s, const char *p, int base) +{ + const char *e = p + strlen(p); + double f = 0; + int n; + + if (*p == '+') p++; + while (p < e) { + char c = *p; + c = tolower((unsigned char)c); + for (n=0; n= 2 && base <= 36); + if (*p == '+') p++; + while (p < e) { + char c = *p; + c = tolower((unsigned char)c); + for (n=0; n result) { + *overflow = TRUE; + return 0; + } + result *= base; + result -= n; + } + else { + if ((MRB_INT_MAX - n)/base < result) { + *overflow = TRUE; + return 0; + } + result *= base; + result += n; + } + p++; + } + *overflow = FALSE; + return result; +} + +static void +codegen(codegen_scope *s, node *tree, int val) +{ + int nt; + + if (!tree) return; + + if (s->irep && s->filename_index != tree->filename_index) { + s->irep->filename = mrb_parser_get_filename(s->parser, s->filename_index); + mrb_debug_info_append_file(s->mrb, s->irep, s->debug_start_pos, s->pc); + s->debug_start_pos = s->pc; + s->filename_index = tree->filename_index; + s->filename = mrb_parser_get_filename(s->parser, tree->filename_index); + } + + nt = (intptr_t)tree->car; + s->lineno = tree->lineno; + tree = tree->cdr; + switch (nt) { + case NODE_BEGIN: + if (val && !tree) { + genop(s, MKOP_A(OP_LOADNIL, cursp())); + push(); + } + while (tree) { + codegen(s, tree->car, tree->cdr ? NOVAL : val); + tree = tree->cdr; + } + break; + + case NODE_RESCUE: + { + int onerr, noexc, exend, pos1, pos2, tmp; + struct loopinfo *lp; + + onerr = genop(s, MKOP_Bx(OP_ONERR, 0)); + lp = loop_push(s, LOOP_BEGIN); + lp->pc1 = onerr; + if (tree->car) { + codegen(s, tree->car, val); + if (val) pop(); + } + lp->type = LOOP_RESCUE; + noexc = genop(s, MKOP_Bx(OP_JMP, 0)); + dispatch(s, onerr); + tree = tree->cdr; + exend = 0; + pos1 = 0; + if (tree->car) { + node *n2 = tree->car; + int exc = cursp(); + + genop(s, MKOP_A(OP_RESCUE, exc)); + push(); + while (n2) { + node *n3 = n2->car; + node *n4 = n3->car; + + if (pos1) dispatch(s, pos1); + pos2 = 0; + do { + if (n4) { + codegen(s, n4->car, VAL); + } + else { + genop(s, MKOP_ABx(OP_GETCONST, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "StandardError")))); + push(); + } + genop(s, MKOP_AB(OP_MOVE, cursp(), exc)); + pop(); + if (n4 && n4->car && (intptr_t)n4->car->car == NODE_SPLAT) { + genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__case_eqq")), 1)); + } + else { + genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "===")), 1)); + } + tmp = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), pos2)); + pos2 = tmp; + if (n4) { + n4 = n4->cdr; + } + } while (n4); + pos1 = genop(s, MKOP_sBx(OP_JMP, 0)); + dispatch_linked(s, pos2); + + pop(); + if (n3->cdr->car) { + gen_assignment(s, n3->cdr->car, exc, NOVAL); + } + if (n3->cdr->cdr->car) { + codegen(s, n3->cdr->cdr->car, val); + if (val) pop(); + } + tmp = genop(s, MKOP_sBx(OP_JMP, exend)); + exend = tmp; + n2 = n2->cdr; + push(); + } + if (pos1) { + dispatch(s, pos1); + genop(s, MKOP_A(OP_RAISE, exc)); + } + } + pop(); + tree = tree->cdr; + dispatch(s, noexc); + genop(s, MKOP_A(OP_POPERR, 1)); + if (tree->car) { + codegen(s, tree->car, val); + } + else if (val) { + push(); + } + dispatch_linked(s, exend); + loop_pop(s, NOVAL); + } + break; + + case NODE_ENSURE: + { + int idx; + int epush = s->pc; + + genop(s, MKOP_Bx(OP_EPUSH, 0)); + s->ensure_level++; + codegen(s, tree->car, val); + idx = scope_body(s, tree->cdr, NOVAL); + s->iseq[epush] = MKOP_Bx(OP_EPUSH, idx); + s->ensure_level--; + genop_peep(s, MKOP_A(OP_EPOP, 1), NOVAL); + } + break; + + case NODE_LAMBDA: + { + int idx = lambda_body(s, tree, 1); + + genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_LAMBDA)); + push(); + } + break; + + case NODE_BLOCK: + { + int idx = lambda_body(s, tree, 1); + + genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_BLOCK)); + push(); + } + break; + + case NODE_IF: + { + int pos1, pos2; + node *e = tree->cdr->cdr->car; + + codegen(s, tree->car, VAL); + pop(); + pos1 = genop_peep(s, MKOP_AsBx(OP_JMPNOT, cursp(), 0), NOVAL); + + codegen(s, tree->cdr->car, val); + if (val && !(tree->cdr->car)) { + genop(s, MKOP_A(OP_LOADNIL, cursp())); + push(); + } + if (e) { + if (val) pop(); + pos2 = genop(s, MKOP_sBx(OP_JMP, 0)); + dispatch(s, pos1); + codegen(s, e, val); + dispatch(s, pos2); + } + else { + if (val) { + pop(); + pos2 = genop(s, MKOP_sBx(OP_JMP, 0)); + dispatch(s, pos1); + genop(s, MKOP_A(OP_LOADNIL, cursp())); + dispatch(s, pos2); + push(); + } + else { + dispatch(s, pos1); + } + } + } + break; + + case NODE_AND: + { + int pos; + + codegen(s, tree->car, VAL); + pop(); + pos = genop(s, MKOP_AsBx(OP_JMPNOT, cursp(), 0)); + codegen(s, tree->cdr, val); + dispatch(s, pos); + } + break; + + case NODE_OR: + { + int pos; + + codegen(s, tree->car, VAL); + pop(); + pos = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), 0)); + codegen(s, tree->cdr, val); + dispatch(s, pos); + } + break; + + case NODE_WHILE: + { + struct loopinfo *lp = loop_push(s, LOOP_NORMAL); + + lp->pc1 = genop(s, MKOP_sBx(OP_JMP, 0)); + lp->pc2 = new_label(s); + codegen(s, tree->cdr, NOVAL); + dispatch(s, lp->pc1); + codegen(s, tree->car, VAL); + pop(); + genop(s, MKOP_AsBx(OP_JMPIF, cursp(), lp->pc2 - s->pc)); + + loop_pop(s, val); + } + break; + + case NODE_UNTIL: + { + struct loopinfo *lp = loop_push(s, LOOP_NORMAL); + + lp->pc1 = genop(s, MKOP_sBx(OP_JMP, 0)); + lp->pc2 = new_label(s); + codegen(s, tree->cdr, NOVAL); + dispatch(s, lp->pc1); + codegen(s, tree->car, VAL); + pop(); + genop(s, MKOP_AsBx(OP_JMPNOT, cursp(), lp->pc2 - s->pc)); + + loop_pop(s, val); + } + break; + + case NODE_FOR: + for_body(s, tree); + if (val) push(); + break; + + case NODE_CASE: + { + int head = 0; + int pos1, pos2, pos3, tmp; + node *n; + + pos3 = 0; + if (tree->car) { + head = cursp(); + codegen(s, tree->car, VAL); + } + tree = tree->cdr; + while (tree) { + n = tree->car->car; + pos1 = pos2 = 0; + while (n) { + codegen(s, n->car, VAL); + if (head) { + genop(s, MKOP_AB(OP_MOVE, cursp(), head)); + pop(); + if ((intptr_t)n->car->car == NODE_SPLAT) { + genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__case_eqq")), 1)); + } + else { + genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "===")), 1)); + } + } + else { + pop(); + } + tmp = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), pos2)); + pos2 = tmp; + n = n->cdr; + } + if (tree->car->car) { + pos1 = genop(s, MKOP_sBx(OP_JMP, 0)); + dispatch_linked(s, pos2); + } + codegen(s, tree->car->cdr, val); + if (val) pop(); + tmp = genop(s, MKOP_sBx(OP_JMP, pos3)); + pos3 = tmp; + if (pos1) dispatch(s, pos1); + tree = tree->cdr; + } + if (val) { + int pos = cursp(); + genop(s, MKOP_A(OP_LOADNIL, cursp())); + if (pos3) dispatch_linked(s, pos3); + if (head) pop(); + genop(s, MKOP_AB(OP_MOVE, cursp(), pos)); + push(); + } + else { + if (pos3) { + dispatch_linked(s, pos3); + } + if (head) { + pop(); + } + } + } + break; + + case NODE_SCOPE: + scope_body(s, tree, NOVAL); + break; + + case NODE_FCALL: + case NODE_CALL: + gen_call(s, tree, 0, 0, val); + break; + + case NODE_DOT2: + codegen(s, tree->car, val); + codegen(s, tree->cdr, val); + if (val) { + pop(); pop(); + genop(s, MKOP_ABC(OP_RANGE, cursp(), cursp(), FALSE)); + push(); + } + break; + + case NODE_DOT3: + codegen(s, tree->car, val); + codegen(s, tree->cdr, val); + if (val) { + pop(); pop(); + genop(s, MKOP_ABC(OP_RANGE, cursp(), cursp(), TRUE)); + push(); + } + break; + + case NODE_COLON2: + { + int sym = new_sym(s, sym(tree->cdr)); + + codegen(s, tree->car, VAL); + pop(); + genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym)); + if (val) push(); + } + break; + + case NODE_COLON3: + { + int sym = new_sym(s, sym(tree)); + + genop(s, MKOP_A(OP_OCLASS, cursp())); + genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym)); + if (val) push(); + } + break; + + case NODE_ARRAY: + { + int n; + + n = gen_values(s, tree, val); + if (n >= 0) { + if (val) { + pop_n(n); + genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), n)); + push(); + } + } + else if (val) { + push(); + } + } + break; + + case NODE_HASH: + { + int len = 0; + mrb_bool update = FALSE; + + while (tree) { + codegen(s, tree->car->car, val); + codegen(s, tree->car->cdr, val); + len++; + tree = tree->cdr; + if (val && len == 126) { + pop_n(len*2); + genop(s, MKOP_ABC(OP_HASH, cursp(), cursp(), len)); + if (update) { + pop(); + genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__update")), 1)); + } + push(); + update = TRUE; + len = 0; + } + } + if (val) { + pop_n(len*2); + genop(s, MKOP_ABC(OP_HASH, cursp(), cursp(), len)); + if (update) { + pop(); + genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__update")), 1)); + } + push(); + } + } + break; + + case NODE_SPLAT: + codegen(s, tree, VAL); + break; + + case NODE_ASGN: + codegen(s, tree->cdr, VAL); + pop(); + gen_assignment(s, tree->car, cursp(), val); + break; + + case NODE_MASGN: + { + int len = 0, n = 0, post = 0; + node *t = tree->cdr, *p; + int rhs = cursp(); + + if ((intptr_t)t->car == NODE_ARRAY && nosplat(t->cdr)) { + /* fixed rhs */ + t = t->cdr; + while (t) { + codegen(s, t->car, VAL); + len++; + t = t->cdr; + } + tree = tree->car; + if (tree->car) { /* pre */ + t = tree->car; + n = 0; + while (t) { + gen_assignment(s, t->car, rhs+n, NOVAL); + n++; + t = t->cdr; + } + } + t = tree->cdr; + if (t) { + if (t->cdr) { /* post count */ + p = t->cdr->car; + while (p) { + post++; + p = p->cdr; + } + } + if (t->car) { /* rest (len - pre - post) */ + int rn; + + if (len < post + n) { + rn = 0; + } + else { + rn = len - post - n; + } + genop(s, MKOP_ABC(OP_ARRAY, cursp(), rhs+n, rn)); + gen_assignment(s, t->car, cursp(), NOVAL); + n += rn; + } + if (t->cdr && t->cdr->car) { + t = t->cdr->car; + while (ncar, rhs+n, NOVAL); + t = t->cdr; + n++; + } + } + } + pop_n(len); + if (val) { + genop(s, MKOP_ABC(OP_ARRAY, rhs, rhs, len)); + push(); + } + } + else { + /* variable rhs */ + codegen(s, t, VAL); + gen_vmassignment(s, tree->car, rhs, val); + if (!val) { + pop(); + } + } + } + break; + + case NODE_OP_ASGN: + { + mrb_sym sym = sym(tree->cdr->car); + mrb_int len; + const char *name = mrb_sym2name_len(s->mrb, sym, &len); + int idx; + + codegen(s, tree->car, VAL); + if (len == 2 && + ((name[0] == '|' && name[1] == '|') || + (name[0] == '&' && name[1] == '&'))) { + int pos; + + pop(); + pos = genop_peep(s, MKOP_AsBx(name[0] == '|' ? OP_JMPIF : OP_JMPNOT, cursp(), 0), NOVAL); + codegen(s, tree->cdr->cdr->car, VAL); + pop(); + gen_assignment(s, tree->car, cursp(), val); + dispatch(s, pos); + break; + } + codegen(s, tree->cdr->cdr->car, VAL); + push(); pop(); + pop(); pop(); + + idx = new_msym(s, sym); + if (len == 1 && name[0] == '+') { + genop_peep(s, MKOP_ABC(OP_ADD, cursp(), idx, 1), val); + } + else if (len == 1 && name[0] == '-') { + genop_peep(s, MKOP_ABC(OP_SUB, cursp(), idx, 1), val); + } + else if (len == 1 && name[0] == '*') { + genop(s, MKOP_ABC(OP_MUL, cursp(), idx, 1)); + } + else if (len == 1 && name[0] == '/') { + genop(s, MKOP_ABC(OP_DIV, cursp(), idx, 1)); + } + else if (len == 1 && name[0] == '<') { + genop(s, MKOP_ABC(OP_LT, cursp(), idx, 1)); + } + else if (len == 2 && name[0] == '<' && name[1] == '=') { + genop(s, MKOP_ABC(OP_LE, cursp(), idx, 1)); + } + else if (len == 1 && name[0] == '>') { + genop(s, MKOP_ABC(OP_GT, cursp(), idx, 1)); + } + else if (len == 2 && name[0] == '>' && name[1] == '=') { + genop(s, MKOP_ABC(OP_GE, cursp(), idx, 1)); + } + else { + genop(s, MKOP_ABC(OP_SEND, cursp(), idx, 1)); + } + } + gen_assignment(s, tree->car, cursp(), val); + break; + + case NODE_SUPER: + { + int n = 0, noop = 0, sendv = 0; + + push(); /* room for receiver */ + if (tree) { + node *args = tree->car; + if (args) { + n = gen_values(s, args, VAL); + if (n < 0) { + n = noop = sendv = 1; + push(); + } + } + } + if (tree && tree->cdr) { + codegen(s, tree->cdr, VAL); + pop(); + } + else { + genop(s, MKOP_A(OP_LOADNIL, cursp())); + push(); pop(); + } + pop_n(n+1); + if (sendv) n = CALL_MAXARGS; + genop(s, MKOP_ABC(OP_SUPER, cursp(), 0, n)); + if (val) push(); + } + break; + + case NODE_ZSUPER: + { + codegen_scope *s2 = s; + int lv = 0, ainfo = 0; + + push(); /* room for receiver */ + while (!s2->mscope) { + lv++; + s2 = s2->prev; + if (!s2) break; + } + if (s2) ainfo = s2->ainfo; + genop(s, MKOP_ABx(OP_ARGARY, cursp(), (ainfo<<4)|(lv & 0xf))); + push(); push(); pop(); /* ARGARY pushes two values */ + if (tree && tree->cdr) { + codegen(s, tree->cdr, VAL); + pop(); + } + pop(); pop(); + genop(s, MKOP_ABC(OP_SUPER, cursp(), 0, CALL_MAXARGS)); + if (val) push(); + } + break; + + case NODE_RETURN: + if (tree) { + codegen(s, tree, VAL); + pop(); + } + else { + genop(s, MKOP_A(OP_LOADNIL, cursp())); + } + if (s->loop) { + genop(s, MKOP_AB(OP_RETURN, cursp(), OP_R_RETURN)); + } + else { + genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL); + } + if (val) push(); + break; + + case NODE_YIELD: + { + codegen_scope *s2 = s; + int lv = 0, ainfo = 0; + int n = 0, sendv = 0; + + while (!s2->mscope) { + lv++; + s2 = s2->prev; + if (!s2) break; + } + if (s2) ainfo = s2->ainfo; + genop(s, MKOP_ABx(OP_BLKPUSH, cursp(), (ainfo<<4)|(lv & 0xf))); + push(); + if (tree) { + n = gen_values(s, tree, VAL); + if (n < 0) { + n = sendv = 1; + push(); + } + } + pop_n(n+1); + if (sendv) n = CALL_MAXARGS; + genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "call")), n)); + if (val) push(); + } + break; + + case NODE_BREAK: + loop_break(s, tree); + if (val) push(); + break; + + case NODE_NEXT: + if (!s->loop) { + raise_error(s, "unexpected next"); + } + else if (s->loop->type == LOOP_NORMAL) { + if (s->ensure_level > s->loop->ensure_level) { + genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - s->loop->ensure_level), NOVAL); + } + codegen(s, tree, NOVAL); + genop(s, MKOP_sBx(OP_JMP, s->loop->pc1 - s->pc)); + } + else { + if (tree) { + codegen(s, tree, VAL); + pop(); + } + else { + genop(s, MKOP_A(OP_LOADNIL, cursp())); + } + genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL); + } + if (val) push(); + break; + + case NODE_REDO: + if (!s->loop) { + raise_error(s, "unexpected redo"); + } + else { + if (s->ensure_level > s->loop->ensure_level) { + genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - s->loop->ensure_level), NOVAL); + } + genop(s, MKOP_sBx(OP_JMP, s->loop->pc2 - s->pc)); + } + break; + + case NODE_RETRY: + { + const char *msg = "unexpected retry"; + + if (!s->loop) { + raise_error(s, msg); + } + else { + struct loopinfo *lp = s->loop; + int n = 0; + + while (lp && lp->type != LOOP_RESCUE) { + if (lp->type == LOOP_BEGIN) { + n++; + } + lp = lp->prev; + } + if (!lp) { + raise_error(s, msg); + } + else { + if (n > 0) { + while (n--) { + genop_peep(s, MKOP_A(OP_POPERR, 1), NOVAL); + } + } + if (s->ensure_level > lp->ensure_level) { + genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - lp->ensure_level), NOVAL); + } + genop(s, MKOP_sBx(OP_JMP, lp->pc1 - s->pc)); + } + } + } + break; + + case NODE_LVAR: + if (val) { + int idx = lv_idx(s, sym(tree)); + + if (idx > 0) { + genop_peep(s, MKOP_AB(OP_MOVE, cursp(), idx), NOVAL); + } + else { + int lv = 0; + codegen_scope *up = s->prev; + + while (up) { + idx = lv_idx(up, sym(tree)); + if (idx > 0) { + genop(s, MKOP_ABC(OP_GETUPVAR, cursp(), idx, lv)); + break; + } + lv++; + up = up->prev; + } + } + push(); + } + break; + + case NODE_GVAR: + if (val) { + int sym = new_sym(s, sym(tree)); + + genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym)); + push(); + } + break; + + case NODE_IVAR: + if (val) { + int sym = new_sym(s, sym(tree)); + + genop(s, MKOP_ABx(OP_GETIV, cursp(), sym)); + push(); + } + break; + + case NODE_CVAR: + if (val) { + int sym = new_sym(s, sym(tree)); + + genop(s, MKOP_ABx(OP_GETCV, cursp(), sym)); + push(); + } + break; + + case NODE_CONST: + { + int sym = new_sym(s, sym(tree)); + + genop(s, MKOP_ABx(OP_GETCONST, cursp(), sym)); + push(); + } + break; + + case NODE_DEFINED: + codegen(s, tree, VAL); + break; + + case NODE_BACK_REF: + if (val) { + char buf[2] = { '$' }; + mrb_value str; + int sym; + + buf[1] = (char)(intptr_t)tree; + str = mrb_str_new(s->mrb, buf, 2); + sym = new_sym(s, mrb_intern_str(s->mrb, str)); + genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym)); + push(); + } + break; + + case NODE_NTH_REF: + if (val) { + int sym; + mrb_state *mrb = s->mrb; + mrb_value fix = mrb_fixnum_value((intptr_t)tree); + mrb_value str = mrb_str_buf_new(mrb, 4); + + mrb_str_cat_lit(mrb, str, "$"); + mrb_str_cat_str(mrb, str, mrb_fixnum_to_str(mrb, fix, 10)); + sym = new_sym(s, mrb_intern_str(mrb, str)); + genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym)); + push(); + } + break; + + case NODE_ARG: + /* should not happen */ + break; + + case NODE_BLOCK_ARG: + codegen(s, tree, VAL); + break; + + case NODE_INT: + if (val) { + char *p = (char*)tree->car; + int base = (intptr_t)tree->cdr->car; + mrb_int i; + mrb_code co; + mrb_bool overflow; + + i = readint_mrb_int(s, p, base, FALSE, &overflow); + if (overflow) { + double f = readint_float(s, p, base); + int off = new_lit(s, mrb_float_value(s->mrb, f)); + + genop(s, MKOP_ABx(OP_LOADL, cursp(), off)); + } + else { + if (i < MAXARG_sBx && i > -MAXARG_sBx) { + co = MKOP_AsBx(OP_LOADI, cursp(), i); + } + else { + int off = new_lit(s, mrb_fixnum_value(i)); + co = MKOP_ABx(OP_LOADL, cursp(), off); + } + genop(s, co); + } + push(); + } + break; + + case NODE_FLOAT: + if (val) { + char *p = (char*)tree; + mrb_float f = str_to_mrb_float(p); + int off = new_lit(s, mrb_float_value(s->mrb, f)); + + genop(s, MKOP_ABx(OP_LOADL, cursp(), off)); + push(); + } + break; + + case NODE_NEGATE: + { + nt = (intptr_t)tree->car; + tree = tree->cdr; + switch (nt) { + case NODE_FLOAT: + { + char *p = (char*)tree; + mrb_float f = str_to_mrb_float(p); + int off = new_lit(s, mrb_float_value(s->mrb, -f)); + + genop(s, MKOP_ABx(OP_LOADL, cursp(), off)); + push(); + } + break; + + case NODE_INT: + { + char *p = (char*)tree->car; + int base = (intptr_t)tree->cdr->car; + mrb_int i; + mrb_code co; + mrb_bool overflow; + + i = readint_mrb_int(s, p, base, TRUE, &overflow); + if (overflow) { + double f = readint_float(s, p, base); + int off = new_lit(s, mrb_float_value(s->mrb, -f)); + + genop(s, MKOP_ABx(OP_LOADL, cursp(), off)); + } + else { + if (i < MAXARG_sBx && i > -MAXARG_sBx) { + co = MKOP_AsBx(OP_LOADI, cursp(), i); + } + else { + int off = new_lit(s, mrb_fixnum_value(i)); + co = MKOP_ABx(OP_LOADL, cursp(), off); + } + genop(s, co); + } + push(); + } + break; + + default: + { + int sym = new_msym(s, mrb_intern_lit(s->mrb, "-")); + + genop(s, MKOP_ABx(OP_LOADI, cursp(), 0)); + push(); + codegen(s, tree, VAL); + pop(); pop(); + genop(s, MKOP_ABC(OP_SUB, cursp(), sym, 2)); + } + break; + } + } + break; + + case NODE_STR: + if (val) { + char *p = (char*)tree->car; + size_t len = (intptr_t)tree->cdr; + int ai = mrb_gc_arena_save(s->mrb); + int off = new_lit(s, mrb_str_new(s->mrb, p, len)); + + mrb_gc_arena_restore(s->mrb, ai); + genop(s, MKOP_ABx(OP_STRING, cursp(), off)); + push(); + } + break; + + case NODE_HEREDOC: + tree = ((struct mrb_parser_heredoc_info *)tree)->doc; + /* fall through */ + case NODE_DSTR: + if (val) { + node *n = tree; + + if (!n) break; + codegen(s, n->car, VAL); + n = n->cdr; + while (n) { + codegen(s, n->car, VAL); + pop(); pop(); + genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL); + push(); + n = n->cdr; + } + } + else { + node *n = tree; + + while (n) { + if ((intptr_t)n->car->car != NODE_STR) { + codegen(s, n->car, NOVAL); + } + n = n->cdr; + } + } + break; + + case NODE_WORDS: + gen_literal_array(s, tree, FALSE, val); + break; + + case NODE_SYMBOLS: + gen_literal_array(s, tree, TRUE, val); + break; + + case NODE_DXSTR: + { + node *n; + int ai = mrb_gc_arena_save(s->mrb); + int sym = new_sym(s, mrb_intern_lit(s->mrb, "Kernel")); + + if (val == NOVAL) { push(); } + genop(s, MKOP_A(OP_OCLASS, cursp())); + genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym)); + push(); + codegen(s, tree->car, VAL); + n = tree->cdr; + while (n) { + if ((intptr_t)n->car->car == NODE_XSTR) { + n->car->car = (struct mrb_ast_node*)(intptr_t)NODE_STR; + mrb_assert(!n->cdr); /* must be the end */ + } + codegen(s, n->car, VAL); + pop(); pop(); + genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL); + push(); + n = n->cdr; + } + pop(); + pop(); + sym = new_sym(s, mrb_intern_lit(s->mrb, "`")); + genop(s, MKOP_ABC(OP_SEND, cursp(), sym, 1)); + if (val == NOVAL) { pop(); } + else { push(); } + mrb_gc_arena_restore(s->mrb, ai); + } + break; + + case NODE_XSTR: + { + char *p = (char*)tree->car; + size_t len = (intptr_t)tree->cdr; + int ai = mrb_gc_arena_save(s->mrb); + int sym = new_sym(s, mrb_intern_lit(s->mrb, "Kernel")); + int off = new_lit(s, mrb_str_new(s->mrb, p, len)); + + if (val == NOVAL) { push(); } + genop(s, MKOP_A(OP_OCLASS, cursp())); + genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym)); + push(); + genop(s, MKOP_ABx(OP_STRING, cursp(), off)); + pop(); + sym = new_sym(s, mrb_intern_lit(s->mrb, "`")); + genop(s, MKOP_ABC(OP_SEND, cursp(), sym, 1)); + if (val == NOVAL) { pop(); } + else { push(); } + mrb_gc_arena_restore(s->mrb, ai); + } + break; + + case NODE_REGX: + if (val) { + char *p1 = (char*)tree->car; + char *p2 = (char*)tree->cdr; + int ai = mrb_gc_arena_save(s->mrb); + int sym = new_sym(s, mrb_intern_lit(s->mrb, REGEXP_CLASS)); + int off = new_lit(s, mrb_str_new_cstr(s->mrb, p1)); + int argc = 1; + + genop(s, MKOP_A(OP_OCLASS, cursp())); + genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym)); + push(); + genop(s, MKOP_ABx(OP_STRING, cursp(), off)); + if (p2) { + push(); + off = new_lit(s, mrb_str_new_cstr(s->mrb, p2)); + genop(s, MKOP_ABx(OP_STRING, cursp(), off)); + argc++; + pop(); + } + pop(); + sym = new_sym(s, mrb_intern_lit(s->mrb, "compile")); + genop(s, MKOP_ABC(OP_SEND, cursp(), sym, argc)); + mrb_gc_arena_restore(s->mrb, ai); + push(); + } + break; + + case NODE_DREGX: + if (val) { + node *n = tree->car; + int ai = mrb_gc_arena_save(s->mrb); + int sym = new_sym(s, mrb_intern_lit(s->mrb, REGEXP_CLASS)); + int argc = 1; + int off; + char *p; + + genop(s, MKOP_A(OP_OCLASS, cursp())); + genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym)); + push(); + codegen(s, n->car, VAL); + n = n->cdr; + while (n) { + codegen(s, n->car, VAL); + pop(); pop(); + genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL); + push(); + n = n->cdr; + } + n = tree->cdr->cdr; + if (n->car) { + p = (char*)n->car; + off = new_lit(s, mrb_str_new_cstr(s->mrb, p)); + codegen(s, tree->car, VAL); + genop(s, MKOP_ABx(OP_STRING, cursp(), off)); + pop(); + genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL); + } + if (n->cdr) { + char *p2 = (char*)n->cdr; + + push(); + off = new_lit(s, mrb_str_new_cstr(s->mrb, p2)); + genop(s, MKOP_ABx(OP_STRING, cursp(), off)); + argc++; + pop(); + } + pop(); + sym = new_sym(s, mrb_intern_lit(s->mrb, "compile")); + genop(s, MKOP_ABC(OP_SEND, cursp(), sym, argc)); + mrb_gc_arena_restore(s->mrb, ai); + push(); + } + else { + node *n = tree->car; + + while (n) { + if ((intptr_t)n->car->car != NODE_STR) { + codegen(s, n->car, NOVAL); + } + n = n->cdr; + } + } + break; + + case NODE_SYM: + if (val) { + int sym = new_sym(s, sym(tree)); + + genop(s, MKOP_ABx(OP_LOADSYM, cursp(), sym)); + push(); + } + break; + + case NODE_DSYM: + codegen(s, tree, val); + if (val) { + gen_send_intern(s); + } + break; + + case NODE_SELF: + if (val) { + genop(s, MKOP_A(OP_LOADSELF, cursp())); + push(); + } + break; + + case NODE_NIL: + if (val) { + genop(s, MKOP_A(OP_LOADNIL, cursp())); + push(); + } + break; + + case NODE_TRUE: + if (val) { + genop(s, MKOP_A(OP_LOADT, cursp())); + push(); + } + break; + + case NODE_FALSE: + if (val) { + genop(s, MKOP_A(OP_LOADF, cursp())); + push(); + } + break; + + case NODE_ALIAS: + { + int a = new_msym(s, sym(tree->car)); + int b = new_msym(s, sym(tree->cdr)); + int c = new_msym(s, mrb_intern_lit(s->mrb, "alias_method")); + + genop(s, MKOP_A(OP_TCLASS, cursp())); + push(); + genop(s, MKOP_ABx(OP_LOADSYM, cursp(), a)); + push(); + genop(s, MKOP_ABx(OP_LOADSYM, cursp(), b)); + push(); + genop(s, MKOP_A(OP_LOADNIL, cursp())); + pop_n(3); + genop(s, MKOP_ABC(OP_SEND, cursp(), c, 2)); + if (val) { + push(); + } + } + break; + + case NODE_UNDEF: + { + int undef = new_msym(s, mrb_intern_lit(s->mrb, "undef_method")); + int num = 0; + node *t = tree; + + genop(s, MKOP_A(OP_TCLASS, cursp())); + push(); + while (t) { + int symbol = new_msym(s, sym(t->car)); + genop(s, MKOP_ABx(OP_LOADSYM, cursp(), symbol)); + push(); + t = t->cdr; + num++; + } + pop_n(num + 1); + genop(s, MKOP_ABC(OP_SEND, cursp(), undef, num)); + if (val) { + push(); + } + } + break; + + case NODE_CLASS: + { + int idx; + + if (tree->car->car == (node*)0) { + genop(s, MKOP_A(OP_LOADNIL, cursp())); + push(); + } + else if (tree->car->car == (node*)1) { + genop(s, MKOP_A(OP_OCLASS, cursp())); + push(); + } + else { + codegen(s, tree->car->car, VAL); + } + if (tree->cdr->car) { + codegen(s, tree->cdr->car, VAL); + } + else { + genop(s, MKOP_A(OP_LOADNIL, cursp())); + push(); + } + pop(); pop(); + idx = new_msym(s, sym(tree->car->cdr)); + genop(s, MKOP_AB(OP_CLASS, cursp(), idx)); + idx = scope_body(s, tree->cdr->cdr->car, val); + genop(s, MKOP_ABx(OP_EXEC, cursp(), idx)); + if (val) { + push(); + } + } + break; + + case NODE_MODULE: + { + int idx; + + if (tree->car->car == (node*)0) { + genop(s, MKOP_A(OP_LOADNIL, cursp())); + push(); + } + else if (tree->car->car == (node*)1) { + genop(s, MKOP_A(OP_OCLASS, cursp())); + push(); + } + else { + codegen(s, tree->car->car, VAL); + } + pop(); + idx = new_msym(s, sym(tree->car->cdr)); + genop(s, MKOP_AB(OP_MODULE, cursp(), idx)); + idx = scope_body(s, tree->cdr->car, val); + genop(s, MKOP_ABx(OP_EXEC, cursp(), idx)); + if (val) { + push(); + } + } + break; + + case NODE_SCLASS: + { + int idx; + + codegen(s, tree->car, VAL); + pop(); + genop(s, MKOP_AB(OP_SCLASS, cursp(), cursp())); + idx = scope_body(s, tree->cdr->car, val); + genop(s, MKOP_ABx(OP_EXEC, cursp(), idx)); + if (val) { + push(); + } + } + break; + + case NODE_DEF: + { + int sym = new_msym(s, sym(tree->car)); + int idx = lambda_body(s, tree->cdr, 0); + + genop(s, MKOP_A(OP_TCLASS, cursp())); + push(); + genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_METHOD)); + push(); pop(); + pop(); + genop(s, MKOP_AB(OP_METHOD, cursp(), sym)); + if (val) { + genop(s, MKOP_ABx(OP_LOADSYM, cursp(), sym)); + push(); + } + } + break; + + case NODE_SDEF: + { + node *recv = tree->car; + int sym = new_msym(s, sym(tree->cdr->car)); + int idx = lambda_body(s, tree->cdr->cdr, 0); + + codegen(s, recv, VAL); + pop(); + genop(s, MKOP_AB(OP_SCLASS, cursp(), cursp())); + push(); + genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_METHOD)); + pop(); + genop(s, MKOP_AB(OP_METHOD, cursp(), sym)); + if (val) { + genop(s, MKOP_ABx(OP_LOADSYM, cursp(), sym)); + push(); + } + } + break; + + case NODE_POSTEXE: + codegen(s, tree, NOVAL); + break; + + default: + break; + } +} + +static void +scope_add_irep(codegen_scope *s, mrb_irep *irep) +{ + if (s->irep == NULL) { + s->irep = irep; + return; + } + if (s->irep->rlen == s->rcapa) { + s->rcapa *= 2; + s->irep->reps = (mrb_irep**)codegen_realloc(s, s->irep->reps, sizeof(mrb_irep*)*s->rcapa); + } + s->irep->reps[s->irep->rlen] = irep; + s->irep->rlen++; +} + +static codegen_scope* +scope_new(mrb_state *mrb, codegen_scope *prev, node *lv) +{ + static const codegen_scope codegen_scope_zero = { 0 }; + mrb_pool *pool = mrb_pool_open(mrb); + codegen_scope *p = (codegen_scope *)mrb_pool_alloc(pool, sizeof(codegen_scope)); + + if (!p) return NULL; + *p = codegen_scope_zero; + p->mrb = mrb; + p->mpool = pool; + if (!prev) return p; + p->prev = prev; + p->ainfo = -1; + p->mscope = 0; + + p->irep = mrb_add_irep(mrb); + scope_add_irep(prev, p->irep); + + p->rcapa = 8; + p->irep->reps = (mrb_irep**)mrb_malloc(mrb, sizeof(mrb_irep*)*p->rcapa); + + p->icapa = 1024; + p->iseq = (mrb_code*)mrb_malloc(mrb, sizeof(mrb_code)*p->icapa); + p->irep->iseq = p->iseq; + + p->pcapa = 32; + p->irep->pool = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value)*p->pcapa); + p->irep->plen = 0; + + p->scapa = 256; + p->irep->syms = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym)*p->scapa); + p->irep->slen = 0; + + p->lv = lv; + p->sp += node_len(lv)+1; /* add self */ + p->nlocals = p->sp; + if (lv) { + node *n = lv; + size_t i = 0; + + p->irep->lv = (struct mrb_locals*)mrb_malloc(mrb, sizeof(struct mrb_locals) * (p->nlocals - 1)); + for (i=0, n=lv; n; i++,n=n->cdr) { + p->irep->lv[i].name = lv_name(n); + if (lv_name(n)) { + p->irep->lv[i].r = lv_idx(p, lv_name(n)); + } + else { + p->irep->lv[i].r = 0; + } + } + mrb_assert(i + 1 == p->nlocals); + } + p->ai = mrb_gc_arena_save(mrb); + + p->filename = prev->filename; + if (p->filename) { + p->lines = (uint16_t*)mrb_malloc(mrb, sizeof(short)*p->icapa); + } + p->lineno = prev->lineno; + + /* debug setting */ + p->debug_start_pos = 0; + if (p->filename) { + mrb_debug_info_alloc(mrb, p->irep); + p->irep->filename = p->filename; + p->irep->lines = p->lines; + } + else { + p->irep->debug_info = NULL; + } + p->parser = prev->parser; + p->filename_index = prev->filename_index; + + return p; +} + +static void +scope_finish(codegen_scope *s) +{ + mrb_state *mrb = s->mrb; + mrb_irep *irep = s->irep; + size_t fname_len; + char *fname; + + irep->flags = 0; + if (s->iseq) { + irep->iseq = (mrb_code *)codegen_realloc(s, s->iseq, sizeof(mrb_code)*s->pc); + irep->ilen = s->pc; + if (s->lines) { + irep->lines = (uint16_t *)codegen_realloc(s, s->lines, sizeof(uint16_t)*s->pc); + } + else { + irep->lines = 0; + } + } + irep->pool = (mrb_value*)codegen_realloc(s, irep->pool, sizeof(mrb_value)*irep->plen); + irep->syms = (mrb_sym*)codegen_realloc(s, irep->syms, sizeof(mrb_sym)*irep->slen); + irep->reps = (mrb_irep**)codegen_realloc(s, irep->reps, sizeof(mrb_irep*)*irep->rlen); + if (s->filename) { + s->irep->filename = mrb_parser_get_filename(s->parser, s->filename_index); + mrb_debug_info_append_file(mrb, s->irep, s->debug_start_pos, s->pc); + + fname_len = strlen(s->filename); + fname = (char*)codegen_malloc(s, fname_len + 1); + memcpy(fname, s->filename, fname_len); + fname[fname_len] = '\0'; + irep->filename = fname; + } + + irep->nlocals = s->nlocals; + irep->nregs = s->nregs; + + mrb_gc_arena_restore(mrb, s->ai); + mrb_pool_close(s->mpool); +} + +static struct loopinfo* +loop_push(codegen_scope *s, enum looptype t) +{ + struct loopinfo *p = (struct loopinfo *)codegen_palloc(s, sizeof(struct loopinfo)); + + p->type = t; + p->pc1 = p->pc2 = p->pc3 = 0; + p->prev = s->loop; + p->ensure_level = s->ensure_level; + p->acc = cursp(); + s->loop = p; + + return p; +} + +static void +loop_break(codegen_scope *s, node *tree) +{ + if (!s->loop) { + codegen(s, tree, NOVAL); + raise_error(s, "unexpected break"); + } + else { + struct loopinfo *loop; + + if (tree) { + codegen(s, tree, VAL); + pop(); + } + + loop = s->loop; + while (loop->type == LOOP_BEGIN) { + genop_peep(s, MKOP_A(OP_POPERR, 1), NOVAL); + loop = loop->prev; + } + while (loop->type == LOOP_RESCUE) { + loop = loop->prev; + } + if (loop->type == LOOP_NORMAL) { + int tmp; + + if (s->ensure_level > s->loop->ensure_level) { + genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - s->loop->ensure_level), NOVAL); + } + if (tree) { + genop_peep(s, MKOP_AB(OP_MOVE, loop->acc, cursp()), NOVAL); + } + tmp = genop(s, MKOP_sBx(OP_JMP, loop->pc3)); + loop->pc3 = tmp; + } + else { + genop(s, MKOP_AB(OP_RETURN, cursp(), OP_R_BREAK)); + } + } +} + +static void +loop_pop(codegen_scope *s, int val) +{ + if (val) { + genop(s, MKOP_A(OP_LOADNIL, cursp())); + } + dispatch_linked(s, s->loop->pc3); + s->loop = s->loop->prev; + if (val) push(); +} + +MRB_API struct RProc* +mrb_generate_code(mrb_state *mrb, parser_state *p) +{ + codegen_scope *scope = scope_new(mrb, 0, 0); + struct RProc *proc; + + if (!scope) { + return NULL; + } + scope->mrb = mrb; + scope->parser = p; + scope->filename = p->filename; + scope->filename_index = p->current_filename_index; + + MRB_TRY(&scope->jmp) { + /* prepare irep */ + codegen(scope, p->tree, NOVAL); + proc = mrb_proc_new(mrb, scope->irep); + mrb_irep_decref(mrb, scope->irep); + mrb_pool_close(scope->mpool); + return proc; + } + MRB_CATCH(&scope->jmp) { + if (scope->filename == scope->irep->filename) { + scope->irep->filename = NULL; + } + mrb_irep_decref(mrb, scope->irep); + mrb_pool_close(scope->mpool); + return NULL; + } + MRB_END_EXC(&scope->jmp); +} diff --git a/mrbgems/mruby-compiler/core/keywords b/mrbgems/mruby-compiler/core/keywords new file mode 100644 index 000000000..9cb86608c --- /dev/null +++ b/mrbgems/mruby-compiler/core/keywords @@ -0,0 +1,50 @@ +%{ +struct kwtable {const char *name; int id[2]; enum mrb_lex_state_enum state;}; +const struct kwtable *mrb_reserved_word(const char *, unsigned int); +static const struct kwtable *reserved_word(const char *, unsigned int); +#define mrb_reserved_word(str, len) reserved_word(str, len) +%} + +struct kwtable; +%% +__ENCODING__, {keyword__ENCODING__, keyword__ENCODING__}, EXPR_END +__FILE__, {keyword__FILE__, keyword__FILE__}, EXPR_END +__LINE__, {keyword__LINE__, keyword__LINE__}, EXPR_END +BEGIN, {keyword_BEGIN, keyword_BEGIN}, EXPR_END +END, {keyword_END, keyword_END}, EXPR_END +alias, {keyword_alias, keyword_alias}, EXPR_FNAME +and, {keyword_and, keyword_and}, EXPR_VALUE +begin, {keyword_begin, keyword_begin}, EXPR_BEG +break, {keyword_break, keyword_break}, EXPR_MID +case, {keyword_case, keyword_case}, EXPR_VALUE +class, {keyword_class, keyword_class}, EXPR_CLASS +def, {keyword_def, keyword_def}, EXPR_FNAME +do, {keyword_do, keyword_do}, EXPR_BEG +else, {keyword_else, keyword_else}, EXPR_BEG +elsif, {keyword_elsif, keyword_elsif}, EXPR_VALUE +end, {keyword_end, keyword_end}, EXPR_END +ensure, {keyword_ensure, keyword_ensure}, EXPR_BEG +false, {keyword_false, keyword_false}, EXPR_END +for, {keyword_for, keyword_for}, EXPR_VALUE +if, {keyword_if, modifier_if}, EXPR_VALUE +in, {keyword_in, keyword_in}, EXPR_VALUE +module, {keyword_module, keyword_module}, EXPR_VALUE +next, {keyword_next, keyword_next}, EXPR_MID +nil, {keyword_nil, keyword_nil}, EXPR_END +not, {keyword_not, keyword_not}, EXPR_ARG +or, {keyword_or, keyword_or}, EXPR_VALUE +redo, {keyword_redo, keyword_redo}, EXPR_END +rescue, {keyword_rescue, modifier_rescue}, EXPR_MID +retry, {keyword_retry, keyword_retry}, EXPR_END +return, {keyword_return, keyword_return}, EXPR_MID +self, {keyword_self, keyword_self}, EXPR_END +super, {keyword_super, keyword_super}, EXPR_ARG +then, {keyword_then, keyword_then}, EXPR_BEG +true, {keyword_true, keyword_true}, EXPR_END +undef, {keyword_undef, keyword_undef}, EXPR_FNAME +unless, {keyword_unless, modifier_unless}, EXPR_VALUE +until, {keyword_until, modifier_until}, EXPR_VALUE +when, {keyword_when, keyword_when}, EXPR_VALUE +while, {keyword_while, modifier_while}, EXPR_VALUE +yield, {keyword_yield, keyword_yield}, EXPR_ARG +%% diff --git a/mrbgems/mruby-compiler/core/lex.def b/mrbgems/mruby-compiler/core/lex.def new file mode 100644 index 000000000..ea456a843 --- /dev/null +++ b/mrbgems/mruby-compiler/core/lex.def @@ -0,0 +1,212 @@ +/* ANSI-C code produced by gperf version 3.0.3 */ +/* Command-line: gperf -L ANSI-C -C -p -j1 -i 1 -g -o -t -N mrb_reserved_word -k'1,3,$' src/keywords */ + +#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ + && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ + && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \ + && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \ + && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \ + && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \ + && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \ + && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \ + && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \ + && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \ + && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \ + && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \ + && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \ + && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \ + && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ + && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \ + && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \ + && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \ + && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \ + && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \ + && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \ + && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ + && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) +/* The character set is not based on ISO-646. */ +#error "gperf generated tables don't work with this execution character set. Please report a bug to ." +#endif + +#line 1 "src/keywords" + +struct kwtable {const char *name; int id[2]; enum mrb_lex_state_enum state;}; +const struct kwtable *mrb_reserved_word(const char *, unsigned int); +static const struct kwtable *reserved_word(const char *, unsigned int); +#define mrb_reserved_word(str, len) reserved_word(str, len) +#line 8 "src/keywords" +struct kwtable; + +#define TOTAL_KEYWORDS 40 +#define MIN_WORD_LENGTH 2 +#define MAX_WORD_LENGTH 12 +#define MIN_HASH_VALUE 8 +#define MAX_HASH_VALUE 50 +/* maximum key range = 43, duplicates = 0 */ + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static unsigned int +hash (register const char *str, register unsigned int len) +{ + static const unsigned char asso_values[] = + { + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 14, 51, 16, 8, + 11, 13, 51, 51, 51, 51, 10, 51, 13, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 11, 51, 13, 1, 26, + 4, 1, 8, 28, 51, 23, 51, 1, 1, 27, + 5, 19, 21, 51, 8, 3, 3, 11, 51, 21, + 24, 16, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51 + }; + register int hval = len; + + switch (hval) + { + default: + hval += asso_values[(unsigned char)str[2]]; + /*FALLTHROUGH*/ + case 2: + case 1: + hval += asso_values[(unsigned char)str[0]]; + break; + } + return hval + asso_values[(unsigned char)str[len - 1]]; +} + +#ifdef __GNUC__ +__inline +#ifdef __GNUC_STDC_INLINE__ +__attribute__ ((__gnu_inline__)) +#endif +#endif +const struct kwtable * +mrb_reserved_word (register const char *str, register unsigned int len) +{ + static const struct kwtable wordlist[] = + { + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 18 "src/keywords" + {"break", {keyword_break, keyword_break}, EXPR_MID}, +#line 23 "src/keywords" + {"else", {keyword_else, keyword_else}, EXPR_BEG}, +#line 33 "src/keywords" + {"nil", {keyword_nil, keyword_nil}, EXPR_END}, +#line 26 "src/keywords" + {"ensure", {keyword_ensure, keyword_ensure}, EXPR_BEG}, +#line 25 "src/keywords" + {"end", {keyword_end, keyword_end}, EXPR_END}, +#line 42 "src/keywords" + {"then", {keyword_then, keyword_then}, EXPR_BEG}, +#line 34 "src/keywords" + {"not", {keyword_not, keyword_not}, EXPR_ARG}, +#line 27 "src/keywords" + {"false", {keyword_false, keyword_false}, EXPR_END}, +#line 40 "src/keywords" + {"self", {keyword_self, keyword_self}, EXPR_END}, +#line 24 "src/keywords" + {"elsif", {keyword_elsif, keyword_elsif}, EXPR_VALUE}, +#line 37 "src/keywords" + {"rescue", {keyword_rescue, modifier_rescue}, EXPR_MID}, +#line 43 "src/keywords" + {"true", {keyword_true, keyword_true}, EXPR_END}, +#line 46 "src/keywords" + {"until", {keyword_until, modifier_until}, EXPR_VALUE}, +#line 45 "src/keywords" + {"unless", {keyword_unless, modifier_unless}, EXPR_VALUE}, +#line 39 "src/keywords" + {"return", {keyword_return, keyword_return}, EXPR_MID}, +#line 21 "src/keywords" + {"def", {keyword_def, keyword_def}, EXPR_FNAME}, +#line 16 "src/keywords" + {"and", {keyword_and, keyword_and}, EXPR_VALUE}, +#line 22 "src/keywords" + {"do", {keyword_do, keyword_do}, EXPR_BEG}, +#line 49 "src/keywords" + {"yield", {keyword_yield, keyword_yield}, EXPR_ARG}, +#line 28 "src/keywords" + {"for", {keyword_for, keyword_for}, EXPR_VALUE}, +#line 44 "src/keywords" + {"undef", {keyword_undef, keyword_undef}, EXPR_FNAME}, +#line 35 "src/keywords" + {"or", {keyword_or, keyword_or}, EXPR_VALUE}, +#line 30 "src/keywords" + {"in", {keyword_in, keyword_in}, EXPR_VALUE}, +#line 47 "src/keywords" + {"when", {keyword_when, keyword_when}, EXPR_VALUE}, +#line 38 "src/keywords" + {"retry", {keyword_retry, keyword_retry}, EXPR_END}, +#line 29 "src/keywords" + {"if", {keyword_if, modifier_if}, EXPR_VALUE}, +#line 19 "src/keywords" + {"case", {keyword_case, keyword_case}, EXPR_VALUE}, +#line 36 "src/keywords" + {"redo", {keyword_redo, keyword_redo}, EXPR_END}, +#line 32 "src/keywords" + {"next", {keyword_next, keyword_next}, EXPR_MID}, +#line 41 "src/keywords" + {"super", {keyword_super, keyword_super}, EXPR_ARG}, +#line 31 "src/keywords" + {"module", {keyword_module, keyword_module}, EXPR_VALUE}, +#line 17 "src/keywords" + {"begin", {keyword_begin, keyword_begin}, EXPR_BEG}, +#line 12 "src/keywords" + {"__LINE__", {keyword__LINE__, keyword__LINE__}, EXPR_END}, +#line 11 "src/keywords" + {"__FILE__", {keyword__FILE__, keyword__FILE__}, EXPR_END}, +#line 10 "src/keywords" + {"__ENCODING__", {keyword__ENCODING__, keyword__ENCODING__}, EXPR_END}, +#line 14 "src/keywords" + {"END", {keyword_END, keyword_END}, EXPR_END}, +#line 15 "src/keywords" + {"alias", {keyword_alias, keyword_alias}, EXPR_FNAME}, +#line 13 "src/keywords" + {"BEGIN", {keyword_BEGIN, keyword_BEGIN}, EXPR_END}, + {""}, +#line 20 "src/keywords" + {"class", {keyword_class, keyword_class}, EXPR_CLASS}, + {""}, {""}, +#line 48 "src/keywords" + {"while", {keyword_while, modifier_while}, EXPR_VALUE} + }; + + if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) + { + register int key = hash (str, len); + + if (key <= MAX_HASH_VALUE && key >= 0) + { + register const char *s = wordlist[key].name; + + if (*str == *s && !strcmp (str + 1, s + 1)) + return &wordlist[key]; + } + } + return 0; +} +#line 50 "src/keywords" + diff --git a/mrbgems/mruby-compiler/core/node.h b/mrbgems/mruby-compiler/core/node.h new file mode 100644 index 000000000..532a8323a --- /dev/null +++ b/mrbgems/mruby-compiler/core/node.h @@ -0,0 +1,117 @@ +/* +** node.h - nodes of abstract syntax tree +** +** See Copyright Notice in mruby.h +*/ + +#ifndef NODE_H +#define NODE_H + +enum node_type { + NODE_METHOD, + NODE_FBODY, + NODE_CFUNC, + NODE_SCOPE, + NODE_BLOCK, + NODE_IF, + NODE_CASE, + NODE_WHEN, + NODE_OPT_N, + NODE_WHILE, + NODE_UNTIL, + NODE_ITER, + NODE_FOR, + NODE_BREAK, + NODE_NEXT, + NODE_REDO, + NODE_RETRY, + NODE_BEGIN, + NODE_RESCUE, + NODE_ENSURE, + NODE_AND, + NODE_OR, + NODE_NOT, + NODE_MASGN, + NODE_ASGN, + NODE_CDECL, + NODE_CVASGN, + NODE_CVDECL, + NODE_OP_ASGN, + NODE_CALL, + NODE_FCALL, + NODE_VCALL, + NODE_SUPER, + NODE_ZSUPER, + NODE_ARRAY, + NODE_ZARRAY, + NODE_HASH, + NODE_RETURN, + NODE_YIELD, + NODE_LVAR, + NODE_DVAR, + NODE_GVAR, + NODE_IVAR, + NODE_CONST, + NODE_CVAR, + NODE_NTH_REF, + NODE_BACK_REF, + NODE_MATCH, + NODE_MATCH2, + NODE_MATCH3, + NODE_INT, + NODE_FLOAT, + NODE_NEGATE, + NODE_LAMBDA, + NODE_SYM, + NODE_STR, + NODE_DSTR, + NODE_XSTR, + NODE_DXSTR, + NODE_REGX, + NODE_DREGX, + NODE_DREGX_ONCE, + NODE_LIST, + NODE_ARG, + NODE_ARGSCAT, + NODE_ARGSPUSH, + NODE_SPLAT, + NODE_TO_ARY, + NODE_SVALUE, + NODE_BLOCK_ARG, + NODE_DEF, + NODE_SDEF, + NODE_ALIAS, + NODE_UNDEF, + NODE_CLASS, + NODE_MODULE, + NODE_SCLASS, + NODE_COLON2, + NODE_COLON3, + NODE_CREF, + NODE_DOT2, + NODE_DOT3, + NODE_FLIP2, + NODE_FLIP3, + NODE_ATTRSET, + NODE_SELF, + NODE_NIL, + NODE_TRUE, + NODE_FALSE, + NODE_DEFINED, + NODE_NEWLINE, + NODE_POSTEXE, + NODE_ALLOCA, + NODE_DMETHOD, + NODE_BMETHOD, + NODE_MEMO, + NODE_IFUNC, + NODE_DSYM, + NODE_ATTRASGN, + NODE_HEREDOC, + NODE_LITERAL_DELIM, + NODE_WORDS, + NODE_SYMBOLS, + NODE_LAST +}; + +#endif /* NODE_H */ diff --git a/mrbgems/mruby-compiler/core/parse.y b/mrbgems/mruby-compiler/core/parse.y new file mode 100644 index 000000000..5b17649a9 --- /dev/null +++ b/mrbgems/mruby-compiler/core/parse.y @@ -0,0 +1,6420 @@ +/* +** parse.y - mruby parser +** +** See Copyright Notice in mruby.h +*/ + +%{ +#undef PARSER_DEBUG +#ifdef PARSER_DEBUG +# define YYDEBUG 1 +#endif +#define YYERROR_VERBOSE 1 +/* + * Force yacc to use our memory management. This is a little evil because + * the macros assume that "parser_state *p" is in scope + */ +#define YYMALLOC(n) mrb_malloc(p->mrb, (n)) +#define YYFREE(o) mrb_free(p->mrb, (o)) +#define YYSTACK_USE_ALLOCA 0 + +#include +#include +#include +#include +#include "mruby.h" +#include "mruby/compile.h" +#include "mruby/proc.h" +#include "mruby/error.h" +#include "node.h" +#include "mruby/throw.h" + +#define YYLEX_PARAM p + +typedef mrb_ast_node node; +typedef struct mrb_parser_state parser_state; +typedef struct mrb_parser_heredoc_info parser_heredoc_info; + +static int yyparse(parser_state *p); +static int yylex(void *lval, parser_state *p); +static void yyerror(parser_state *p, const char *s); +static void yywarn(parser_state *p, const char *s); +static void yywarning(parser_state *p, const char *s); +static void backref_error(parser_state *p, node *n); +static void tokadd(parser_state *p, int32_t c); + +#define identchar(c) (ISALNUM(c) || (c) == '_' || !ISASCII(c)) + +typedef unsigned int stack_type; + +#define BITSTACK_PUSH(stack, n) ((stack) = ((stack)<<1)|((n)&1)) +#define BITSTACK_POP(stack) ((stack) = (stack) >> 1) +#define BITSTACK_LEXPOP(stack) ((stack) = ((stack) >> 1) | ((stack) & 1)) +#define BITSTACK_SET_P(stack) ((stack)&1) + +#define COND_PUSH(n) BITSTACK_PUSH(p->cond_stack, (n)) +#define COND_POP() BITSTACK_POP(p->cond_stack) +#define COND_LEXPOP() BITSTACK_LEXPOP(p->cond_stack) +#define COND_P() BITSTACK_SET_P(p->cond_stack) + +#define CMDARG_PUSH(n) BITSTACK_PUSH(p->cmdarg_stack, (n)) +#define CMDARG_POP() BITSTACK_POP(p->cmdarg_stack) +#define CMDARG_LEXPOP() BITSTACK_LEXPOP(p->cmdarg_stack) +#define CMDARG_P() BITSTACK_SET_P(p->cmdarg_stack) + +#define SET_LINENO(c,n) ((c)->lineno = (n)) +#define NODE_LINENO(c,n) do {\ + if (n) {\ + (c)->filename_index = (n)->filename_index;\ + (c)->lineno = (n)->lineno;\ + }\ +} while (0) + +#define sym(x) ((mrb_sym)(intptr_t)(x)) +#define nsym(x) ((node*)(intptr_t)(x)) + +static inline mrb_sym +intern_cstr_gen(parser_state *p, const char *s) +{ + return mrb_intern_cstr(p->mrb, s); +} +#define intern_cstr(s) intern_cstr_gen(p,(s)) + +static inline mrb_sym +intern_gen(parser_state *p, const char *s, size_t len) +{ + return mrb_intern(p->mrb, s, len); +} +#define intern(s,len) intern_gen(p,(s),(len)) + +static inline mrb_sym +intern_gen_c(parser_state *p, const char c) +{ + return mrb_intern(p->mrb, &c, 1); +} +#define intern_c(c) intern_gen_c(p,(c)) + +static void +cons_free_gen(parser_state *p, node *cons) +{ + cons->cdr = p->cells; + p->cells = cons; +} +#define cons_free(c) cons_free_gen(p, (c)) + +static void* +parser_palloc(parser_state *p, size_t size) +{ + void *m = mrb_pool_alloc(p->pool, size); + + if (!m) { + MRB_THROW(p->jmp); + } + return m; +} + +static node* +cons_gen(parser_state *p, node *car, node *cdr) +{ + node *c; + + if (p->cells) { + c = p->cells; + p->cells = p->cells->cdr; + } + else { + c = (node *)parser_palloc(p, sizeof(mrb_ast_node)); + } + + c->car = car; + c->cdr = cdr; + c->lineno = p->lineno; + c->filename_index = p->current_filename_index; + return c; +} +#define cons(a,b) cons_gen(p,(a),(b)) + +static node* +list1_gen(parser_state *p, node *a) +{ + return cons(a, 0); +} +#define list1(a) list1_gen(p, (a)) + +static node* +list2_gen(parser_state *p, node *a, node *b) +{ + return cons(a, cons(b,0)); +} +#define list2(a,b) list2_gen(p, (a),(b)) + +static node* +list3_gen(parser_state *p, node *a, node *b, node *c) +{ + return cons(a, cons(b, cons(c,0))); +} +#define list3(a,b,c) list3_gen(p, (a),(b),(c)) + +static node* +list4_gen(parser_state *p, node *a, node *b, node *c, node *d) +{ + return cons(a, cons(b, cons(c, cons(d, 0)))); +} +#define list4(a,b,c,d) list4_gen(p, (a),(b),(c),(d)) + +static node* +list5_gen(parser_state *p, node *a, node *b, node *c, node *d, node *e) +{ + return cons(a, cons(b, cons(c, cons(d, cons(e, 0))))); +} +#define list5(a,b,c,d,e) list5_gen(p, (a),(b),(c),(d),(e)) + +static node* +list6_gen(parser_state *p, node *a, node *b, node *c, node *d, node *e, node *f) +{ + return cons(a, cons(b, cons(c, cons(d, cons(e, cons(f, 0)))))); +} +#define list6(a,b,c,d,e,f) list6_gen(p, (a),(b),(c),(d),(e),(f)) + +static node* +append_gen(parser_state *p, node *a, node *b) +{ + node *c = a; + + if (!a) return b; + while (c->cdr) { + c = c->cdr; + } + if (b) { + c->cdr = b; + } + return a; +} +#define append(a,b) append_gen(p,(a),(b)) +#define push(a,b) append_gen(p,(a),list1(b)) + +static char* +parser_strndup(parser_state *p, const char *s, size_t len) +{ + char *b = (char *)parser_palloc(p, len+1); + + memcpy(b, s, len); + b[len] = '\0'; + return b; +} +#undef strndup +#define strndup(s,len) parser_strndup(p, s, len) + +static char* +parser_strdup(parser_state *p, const char *s) +{ + return parser_strndup(p, s, strlen(s)); +} +#undef strdup +#define strdup(s) parser_strdup(p, s) + +/* xxx ----------------------------- */ + +static node* +local_switch(parser_state *p) +{ + node *prev = p->locals; + + p->locals = cons(0, 0); + return prev; +} + +static void +local_resume(parser_state *p, node *prev) +{ + p->locals = prev; +} + +static void +local_nest(parser_state *p) +{ + p->locals = cons(0, p->locals); +} + +static void +local_unnest(parser_state *p) +{ + if (p->locals) { + p->locals = p->locals->cdr; + } +} + +static mrb_bool +local_var_p(parser_state *p, mrb_sym sym) +{ + node *l = p->locals; + + while (l) { + node *n = l->car; + while (n) { + if (sym(n->car) == sym) return TRUE; + n = n->cdr; + } + l = l->cdr; + } + return FALSE; +} + +static void +local_add_f(parser_state *p, mrb_sym sym) +{ + if (p->locals) { + p->locals->car = push(p->locals->car, nsym(sym)); + } +} + +static void +local_add(parser_state *p, mrb_sym sym) +{ + if (!local_var_p(p, sym)) { + local_add_f(p, sym); + } +} + +static node* +locals_node(parser_state *p) +{ + return p->locals ? p->locals->car : NULL; +} + +/* (:scope (vars..) (prog...)) */ +static node* +new_scope(parser_state *p, node *body) +{ + return cons((node*)NODE_SCOPE, cons(locals_node(p), body)); +} + +/* (:begin prog...) */ +static node* +new_begin(parser_state *p, node *body) +{ + if (body) { + return list2((node*)NODE_BEGIN, body); + } + return cons((node*)NODE_BEGIN, 0); +} + +#define newline_node(n) (n) + +/* (:rescue body rescue else) */ +static node* +new_rescue(parser_state *p, node *body, node *resq, node *els) +{ + return list4((node*)NODE_RESCUE, body, resq, els); +} + +/* (:ensure body ensure) */ +static node* +new_ensure(parser_state *p, node *a, node *b) +{ + return cons((node*)NODE_ENSURE, cons(a, cons(0, b))); +} + +/* (:nil) */ +static node* +new_nil(parser_state *p) +{ + return list1((node*)NODE_NIL); +} + +/* (:true) */ +static node* +new_true(parser_state *p) +{ + return list1((node*)NODE_TRUE); +} + +/* (:false) */ +static node* +new_false(parser_state *p) +{ + return list1((node*)NODE_FALSE); +} + +/* (:alias new old) */ +static node* +new_alias(parser_state *p, mrb_sym a, mrb_sym b) +{ + return cons((node*)NODE_ALIAS, cons(nsym(a), nsym(b))); +} + +/* (:if cond then else) */ +static node* +new_if(parser_state *p, node *a, node *b, node *c) +{ + return list4((node*)NODE_IF, a, b, c); +} + +/* (:unless cond then else) */ +static node* +new_unless(parser_state *p, node *a, node *b, node *c) +{ + return list4((node*)NODE_IF, a, c, b); +} + +/* (:while cond body) */ +static node* +new_while(parser_state *p, node *a, node *b) +{ + return cons((node*)NODE_WHILE, cons(a, b)); +} + +/* (:until cond body) */ +static node* +new_until(parser_state *p, node *a, node *b) +{ + return cons((node*)NODE_UNTIL, cons(a, b)); +} + +/* (:for var obj body) */ +static node* +new_for(parser_state *p, node *v, node *o, node *b) +{ + return list4((node*)NODE_FOR, v, o, b); +} + +/* (:case a ((when ...) body) ((when...) body)) */ +static node* +new_case(parser_state *p, node *a, node *b) +{ + node *n = list2((node*)NODE_CASE, a); + node *n2 = n; + + while (n2->cdr) { + n2 = n2->cdr; + } + n2->cdr = b; + return n; +} + +/* (:postexe a) */ +static node* +new_postexe(parser_state *p, node *a) +{ + return cons((node*)NODE_POSTEXE, a); +} + +/* (:self) */ +static node* +new_self(parser_state *p) +{ + return list1((node*)NODE_SELF); +} + +/* (:call a b c) */ +static node* +new_call(parser_state *p, node *a, mrb_sym b, node *c) +{ + node *n = list4((node*)NODE_CALL, a, nsym(b), c); + NODE_LINENO(n, a); + return n; +} + +/* (:fcall self mid args) */ +static node* +new_fcall(parser_state *p, mrb_sym b, node *c) +{ + node *n = new_self(p); + NODE_LINENO(n, c); + n = list4((node*)NODE_FCALL, n, nsym(b), c); + NODE_LINENO(n, c); + return n; +} + +/* (:super . c) */ +static node* +new_super(parser_state *p, node *c) +{ + return cons((node*)NODE_SUPER, c); +} + +/* (:zsuper) */ +static node* +new_zsuper(parser_state *p) +{ + return list1((node*)NODE_ZSUPER); +} + +/* (:yield . c) */ +static node* +new_yield(parser_state *p, node *c) +{ + if (c) { + if (c->cdr) { + yyerror(p, "both block arg and actual block given"); + } + return cons((node*)NODE_YIELD, c->car); + } + return cons((node*)NODE_YIELD, 0); +} + +/* (:return . c) */ +static node* +new_return(parser_state *p, node *c) +{ + return cons((node*)NODE_RETURN, c); +} + +/* (:break . c) */ +static node* +new_break(parser_state *p, node *c) +{ + return cons((node*)NODE_BREAK, c); +} + +/* (:next . c) */ +static node* +new_next(parser_state *p, node *c) +{ + return cons((node*)NODE_NEXT, c); +} + +/* (:redo) */ +static node* +new_redo(parser_state *p) +{ + return list1((node*)NODE_REDO); +} + +/* (:retry) */ +static node* +new_retry(parser_state *p) +{ + return list1((node*)NODE_RETRY); +} + +/* (:dot2 a b) */ +static node* +new_dot2(parser_state *p, node *a, node *b) +{ + return cons((node*)NODE_DOT2, cons(a, b)); +} + +/* (:dot3 a b) */ +static node* +new_dot3(parser_state *p, node *a, node *b) +{ + return cons((node*)NODE_DOT3, cons(a, b)); +} + +/* (:colon2 b c) */ +static node* +new_colon2(parser_state *p, node *b, mrb_sym c) +{ + return cons((node*)NODE_COLON2, cons(b, nsym(c))); +} + +/* (:colon3 . c) */ +static node* +new_colon3(parser_state *p, mrb_sym c) +{ + return cons((node*)NODE_COLON3, nsym(c)); +} + +/* (:and a b) */ +static node* +new_and(parser_state *p, node *a, node *b) +{ + return cons((node*)NODE_AND, cons(a, b)); +} + +/* (:or a b) */ +static node* +new_or(parser_state *p, node *a, node *b) +{ + return cons((node*)NODE_OR, cons(a, b)); +} + +/* (:array a...) */ +static node* +new_array(parser_state *p, node *a) +{ + return cons((node*)NODE_ARRAY, a); +} + +/* (:splat . a) */ +static node* +new_splat(parser_state *p, node *a) +{ + return cons((node*)NODE_SPLAT, a); +} + +/* (:hash (k . v) (k . v)...) */ +static node* +new_hash(parser_state *p, node *a) +{ + return cons((node*)NODE_HASH, a); +} + +/* (:sym . a) */ +static node* +new_sym(parser_state *p, mrb_sym sym) +{ + return cons((node*)NODE_SYM, nsym(sym)); +} + +static mrb_sym +new_strsym(parser_state *p, node* str) +{ + const char *s = (const char*)str->cdr->car; + size_t len = (size_t)str->cdr->cdr; + + return mrb_intern(p->mrb, s, len); +} + +/* (:lvar . a) */ +static node* +new_lvar(parser_state *p, mrb_sym sym) +{ + return cons((node*)NODE_LVAR, nsym(sym)); +} + +/* (:gvar . a) */ +static node* +new_gvar(parser_state *p, mrb_sym sym) +{ + return cons((node*)NODE_GVAR, nsym(sym)); +} + +/* (:ivar . a) */ +static node* +new_ivar(parser_state *p, mrb_sym sym) +{ + return cons((node*)NODE_IVAR, nsym(sym)); +} + +/* (:cvar . a) */ +static node* +new_cvar(parser_state *p, mrb_sym sym) +{ + return cons((node*)NODE_CVAR, nsym(sym)); +} + +/* (:const . a) */ +static node* +new_const(parser_state *p, mrb_sym sym) +{ + return cons((node*)NODE_CONST, nsym(sym)); +} + +/* (:undef a...) */ +static node* +new_undef(parser_state *p, mrb_sym sym) +{ + return list2((node*)NODE_UNDEF, nsym(sym)); +} + +/* (:class class super body) */ +static node* +new_class(parser_state *p, node *c, node *s, node *b) +{ + return list4((node*)NODE_CLASS, c, s, cons(locals_node(p), b)); +} + +/* (:sclass obj body) */ +static node* +new_sclass(parser_state *p, node *o, node *b) +{ + return list3((node*)NODE_SCLASS, o, cons(locals_node(p), b)); +} + +/* (:module module body) */ +static node* +new_module(parser_state *p, node *m, node *b) +{ + return list3((node*)NODE_MODULE, m, cons(locals_node(p), b)); +} + +/* (:def m lv (arg . body)) */ +static node* +new_def(parser_state *p, mrb_sym m, node *a, node *b) +{ + return list5((node*)NODE_DEF, nsym(m), locals_node(p), a, b); +} + +/* (:sdef obj m lv (arg . body)) */ +static node* +new_sdef(parser_state *p, node *o, mrb_sym m, node *a, node *b) +{ + return list6((node*)NODE_SDEF, o, nsym(m), locals_node(p), a, b); +} + +/* (:arg . sym) */ +static node* +new_arg(parser_state *p, mrb_sym sym) +{ + return cons((node*)NODE_ARG, nsym(sym)); +} + +/* (m o r m2 b) */ +/* m: (a b c) */ +/* o: ((a . e1) (b . e2)) */ +/* r: a */ +/* m2: (a b c) */ +/* b: a */ +static node* +new_args(parser_state *p, node *m, node *opt, mrb_sym rest, node *m2, mrb_sym blk) +{ + node *n; + + n = cons(m2, nsym(blk)); + n = cons(nsym(rest), n); + n = cons(opt, n); + return cons(m, n); +} + +/* (:block_arg . a) */ +static node* +new_block_arg(parser_state *p, node *a) +{ + return cons((node*)NODE_BLOCK_ARG, a); +} + +/* (:block arg body) */ +static node* +new_block(parser_state *p, node *a, node *b) +{ + return list4((node*)NODE_BLOCK, locals_node(p), a, b); +} + +/* (:lambda arg body) */ +static node* +new_lambda(parser_state *p, node *a, node *b) +{ + return list4((node*)NODE_LAMBDA, locals_node(p), a, b); +} + +/* (:asgn lhs rhs) */ +static node* +new_asgn(parser_state *p, node *a, node *b) +{ + return cons((node*)NODE_ASGN, cons(a, b)); +} + +/* (:masgn mlhs=(pre rest post) mrhs) */ +static node* +new_masgn(parser_state *p, node *a, node *b) +{ + return cons((node*)NODE_MASGN, cons(a, b)); +} + +/* (:asgn lhs rhs) */ +static node* +new_op_asgn(parser_state *p, node *a, mrb_sym op, node *b) +{ + return list4((node*)NODE_OP_ASGN, a, nsym(op), b); +} + +/* (:int . i) */ +static node* +new_int(parser_state *p, const char *s, int base) +{ + return list3((node*)NODE_INT, (node*)strdup(s), (node*)(intptr_t)base); +} + +/* (:float . i) */ +static node* +new_float(parser_state *p, const char *s) +{ + return cons((node*)NODE_FLOAT, (node*)strdup(s)); +} + +/* (:str . (s . len)) */ +static node* +new_str(parser_state *p, const char *s, int len) +{ + return cons((node*)NODE_STR, cons((node*)strndup(s, len), (node*)(intptr_t)len)); +} + +/* (:dstr . a) */ +static node* +new_dstr(parser_state *p, node *a) +{ + return cons((node*)NODE_DSTR, a); +} + +/* (:str . (s . len)) */ +static node* +new_xstr(parser_state *p, const char *s, int len) +{ + return cons((node*)NODE_XSTR, cons((node*)strndup(s, len), (node*)(intptr_t)len)); +} + +/* (:xstr . a) */ +static node* +new_dxstr(parser_state *p, node *a) +{ + return cons((node*)NODE_DXSTR, a); +} + +/* (:dsym . a) */ +static node* +new_dsym(parser_state *p, node *a) +{ + return cons((node*)NODE_DSYM, new_dstr(p, a)); +} + +/* (:str . (a . a)) */ +static node* +new_regx(parser_state *p, const char *p1, const char* p2) +{ + return cons((node*)NODE_REGX, cons((node*)p1, (node*)p2)); +} + +/* (:dregx . a) */ +static node* +new_dregx(parser_state *p, node *a, node *b) +{ + return cons((node*)NODE_DREGX, cons(a, b)); +} + +/* (:backref . n) */ +static node* +new_back_ref(parser_state *p, int n) +{ + return cons((node*)NODE_BACK_REF, (node*)(intptr_t)n); +} + +/* (:nthref . n) */ +static node* +new_nth_ref(parser_state *p, int n) +{ + return cons((node*)NODE_NTH_REF, (node*)(intptr_t)n); +} + +/* (:heredoc . a) */ +static node* +new_heredoc(parser_state *p) +{ + parser_heredoc_info *inf = (parser_heredoc_info *)parser_palloc(p, sizeof(parser_heredoc_info)); + return cons((node*)NODE_HEREDOC, (node*)inf); +} + +static void +new_bv(parser_state *p, mrb_sym id) +{ +} + +static node* +new_literal_delim(parser_state *p) +{ + return cons((node*)NODE_LITERAL_DELIM, 0); +} + +/* (:words . a) */ +static node* +new_words(parser_state *p, node *a) +{ + return cons((node*)NODE_WORDS, a); +} + +/* (:symbols . a) */ +static node* +new_symbols(parser_state *p, node *a) +{ + return cons((node*)NODE_SYMBOLS, a); +} + +/* xxx ----------------------------- */ + +/* (:call a op) */ +static node* +call_uni_op(parser_state *p, node *recv, const char *m) +{ + return new_call(p, recv, intern_cstr(m), 0); +} + +/* (:call a op b) */ +static node* +call_bin_op(parser_state *p, node *recv, const char *m, node *arg1) +{ + return new_call(p, recv, intern_cstr(m), list1(list1(arg1))); +} + +static void +args_with_block(parser_state *p, node *a, node *b) +{ + if (b) { + if (a->cdr) { + yyerror(p, "both block arg and actual block given"); + } + a->cdr = b; + } +} + +static void +call_with_block(parser_state *p, node *a, node *b) +{ + node *n; + + if (a->car == (node*)NODE_SUPER || + a->car == (node*)NODE_ZSUPER) { + if (!a->cdr) a->cdr = cons(0, b); + else { + args_with_block(p, a->cdr, b); + } + } + else { + n = a->cdr->cdr->cdr; + if (!n->car) n->car = cons(0, b); + else { + args_with_block(p, n->car, b); + } + } +} + +static node* +negate_lit(parser_state *p, node *n) +{ + return cons((node*)NODE_NEGATE, n); +} + +static node* +cond(node *n) +{ + return n; +} + +static node* +ret_args(parser_state *p, node *n) +{ + if (n->cdr) { + yyerror(p, "block argument should not be given"); + return NULL; + } + if (!n->car->cdr) return n->car->car; + return new_array(p, n->car); +} + +static void +assignable(parser_state *p, node *lhs) +{ + if ((int)(intptr_t)lhs->car == NODE_LVAR) { + local_add(p, sym(lhs->cdr)); + } +} + +static node* +var_reference(parser_state *p, node *lhs) +{ + node *n; + + if ((int)(intptr_t)lhs->car == NODE_LVAR) { + if (!local_var_p(p, sym(lhs->cdr))) { + n = new_fcall(p, sym(lhs->cdr), 0); + cons_free(lhs); + return n; + } + } + + return lhs; +} + +typedef enum mrb_string_type string_type; + +static node* +new_strterm(parser_state *p, string_type type, int term, int paren) +{ + return cons((node*)(intptr_t)type, cons((node*)0, cons((node*)(intptr_t)paren, (node*)(intptr_t)term))); +} + +static void +end_strterm(parser_state *p) +{ + cons_free(p->lex_strterm->cdr->cdr); + cons_free(p->lex_strterm->cdr); + cons_free(p->lex_strterm); + p->lex_strterm = NULL; +} + +static parser_heredoc_info * +parsing_heredoc_inf(parser_state *p) +{ + node *nd = p->parsing_heredoc; + if (nd == NULL) + return NULL; + /* mrb_assert(nd->car->car == NODE_HEREDOC); */ + return (parser_heredoc_info*)nd->car->cdr; +} + +static void +heredoc_treat_nextline(parser_state *p) +{ + if (p->heredocs_from_nextline == NULL) + return; + if (p->parsing_heredoc == NULL) { + node *n; + p->parsing_heredoc = p->heredocs_from_nextline; + p->lex_strterm_before_heredoc = p->lex_strterm; + p->lex_strterm = new_strterm(p, parsing_heredoc_inf(p)->type, 0, 0); + n = p->all_heredocs; + if (n) { + while (n->cdr) + n = n->cdr; + n->cdr = p->parsing_heredoc; + } + else { + p->all_heredocs = p->parsing_heredoc; + } + } + else { + node *n, *m; + m = p->heredocs_from_nextline; + while (m->cdr) + m = m->cdr; + n = p->all_heredocs; + mrb_assert(n != NULL); + if (n == p->parsing_heredoc) { + m->cdr = n; + p->all_heredocs = p->heredocs_from_nextline; + p->parsing_heredoc = p->heredocs_from_nextline; + } + else { + while (n->cdr != p->parsing_heredoc) { + n = n->cdr; + mrb_assert(n != NULL); + } + m->cdr = n->cdr; + n->cdr = p->heredocs_from_nextline; + p->parsing_heredoc = p->heredocs_from_nextline; + } + } + p->heredocs_from_nextline = NULL; +} + +static void +heredoc_end(parser_state *p) +{ + p->parsing_heredoc = p->parsing_heredoc->cdr; + if (p->parsing_heredoc == NULL) { + p->lstate = EXPR_BEG; + p->cmd_start = TRUE; + end_strterm(p); + p->lex_strterm = p->lex_strterm_before_heredoc; + p->lex_strterm_before_heredoc = NULL; + p->heredoc_end_now = TRUE; + } + else { + /* next heredoc */ + p->lex_strterm->car = (node*)(intptr_t)parsing_heredoc_inf(p)->type; + } +} +#define is_strterm_type(p,str_func) ((int)(intptr_t)((p)->lex_strterm->car) & (str_func)) + +/* xxx ----------------------------- */ + +%} + +%pure-parser +%parse-param {parser_state *p} +%lex-param {parser_state *p} + +%union { + node *nd; + mrb_sym id; + int num; + stack_type stack; + const struct vtable *vars; +} + +%token + keyword_class + keyword_module + keyword_def + keyword_begin + keyword_if + keyword_unless + keyword_while + keyword_until + keyword_for + +%token + keyword_undef + keyword_rescue + keyword_ensure + keyword_end + keyword_then + keyword_elsif + keyword_else + keyword_case + keyword_when + keyword_break + keyword_next + keyword_redo + keyword_retry + keyword_in + keyword_do + keyword_do_cond + keyword_do_block + keyword_do_LAMBDA + keyword_return + keyword_yield + keyword_super + keyword_self + keyword_nil + keyword_true + keyword_false + keyword_and + keyword_or + keyword_not + modifier_if + modifier_unless + modifier_while + modifier_until + modifier_rescue + keyword_alias + keyword_BEGIN + keyword_END + keyword__LINE__ + keyword__FILE__ + keyword__ENCODING__ + +%token tIDENTIFIER tFID tGVAR tIVAR tCONSTANT tCVAR tLABEL +%token tINTEGER tFLOAT tCHAR tXSTRING tREGEXP +%token tSTRING tSTRING_PART tSTRING_MID +%token tNTH_REF tBACK_REF +%token tREGEXP_END + +%type singleton string string_rep string_interp xstring regexp +%type literal numeric cpath symbol +%type top_compstmt top_stmts top_stmt +%type bodystmt compstmt stmts stmt expr arg primary command command_call method_call +%type expr_value arg_value primary_value +%type if_tail opt_else case_body cases opt_rescue exc_list exc_var opt_ensure +%type args call_args opt_call_args +%type paren_args opt_paren_args variable +%type command_args aref_args opt_block_arg block_arg var_ref var_lhs +%type command_asgn mrhs superclass block_call block_command +%type f_block_optarg f_block_opt +%type f_arglist f_args f_arg f_arg_item f_optarg f_marg f_marg_list f_margs +%type assoc_list assocs assoc undef_list backref for_var +%type block_param opt_block_param block_param_def f_opt +%type bv_decls opt_bv_decl bvar f_larglist lambda_body +%type brace_block cmd_brace_block do_block lhs none f_bad_arg +%type mlhs mlhs_list mlhs_post mlhs_basic mlhs_item mlhs_node mlhs_inner +%type fsym sym basic_symbol operation operation2 operation3 +%type cname fname op f_rest_arg f_block_arg opt_f_block_arg f_norm_arg f_opt_asgn +%type heredoc words symbols + +%token tUPLUS /* unary+ */ +%token tUMINUS /* unary- */ +%token tPOW /* ** */ +%token tCMP /* <=> */ +%token tEQ /* == */ +%token tEQQ /* === */ +%token tNEQ /* != */ +%token tGEQ /* >= */ +%token tLEQ /* <= */ +%token tANDOP tOROP /* && and || */ +%token tMATCH tNMATCH /* =~ and !~ */ +%token tDOT2 tDOT3 /* .. and ... */ +%token tAREF tASET /* [] and []= */ +%token tLSHFT tRSHFT /* << and >> */ +%token tCOLON2 /* :: */ +%token tCOLON3 /* :: at EXPR_BEG */ +%token tOP_ASGN /* +=, -= etc. */ +%token tASSOC /* => */ +%token tLPAREN /* ( */ +%token tLPAREN_ARG /* ( */ +%token tRPAREN /* ) */ +%token tLBRACK /* [ */ +%token tLBRACE /* { */ +%token tLBRACE_ARG /* { */ +%token tSTAR /* * */ +%token tAMPER /* & */ +%token tLAMBDA /* -> */ +%token tSYMBEG tREGEXP_BEG tWORDS_BEG tSYMBOLS_BEG +%token tSTRING_BEG tXSTRING_BEG tSTRING_DVAR tLAMBEG +%token tHEREDOC_BEG /* <<, <<- */ +%token tHEREDOC_END tLITERAL_DELIM tHD_LITERAL_DELIM +%token tHD_STRING_PART tHD_STRING_MID + +/* + * precedence table + */ + +%nonassoc tLOWEST +%nonassoc tLBRACE_ARG + +%nonassoc modifier_if modifier_unless modifier_while modifier_until +%left keyword_or keyword_and +%right keyword_not +%right '=' tOP_ASGN +%left modifier_rescue +%right '?' ':' +%nonassoc tDOT2 tDOT3 +%left tOROP +%left tANDOP +%nonassoc tCMP tEQ tEQQ tNEQ tMATCH tNMATCH +%left '>' tGEQ '<' tLEQ +%left '|' '^' +%left '&' +%left tLSHFT tRSHFT +%left '+' '-' +%left '*' '/' '%' +%right tUMINUS_NUM tUMINUS +%right tPOW +%right '!' '~' tUPLUS + +%token tLAST_TOKEN + +%% +program : { + p->lstate = EXPR_BEG; + if (!p->locals) p->locals = cons(0,0); + } + top_compstmt + { + p->tree = new_scope(p, $2); + NODE_LINENO(p->tree, $2); + } + ; + +top_compstmt : top_stmts opt_terms + { + $$ = $1; + } + ; + +top_stmts : none + { + $$ = new_begin(p, 0); + } + | top_stmt + { + $$ = new_begin(p, $1); + NODE_LINENO($$, $1); + } + | top_stmts terms top_stmt + { + $$ = push($1, newline_node($3)); + } + | error top_stmt + { + $$ = new_begin(p, 0); + } + ; + +top_stmt : stmt + | keyword_BEGIN + { + $$ = local_switch(p); + } + '{' top_compstmt '}' + { + yyerror(p, "BEGIN not supported"); + local_resume(p, $2); + $$ = 0; + } + ; + +bodystmt : compstmt + opt_rescue + opt_else + opt_ensure + { + if ($2) { + $$ = new_rescue(p, $1, $2, $3); + NODE_LINENO($$, $1); + } + else if ($3) { + yywarn(p, "else without rescue is useless"); + $$ = push($1, $3); + } + else { + $$ = $1; + } + if ($4) { + if ($$) { + $$ = new_ensure(p, $$, $4); + } + else { + $$ = push($4, new_nil(p)); + } + } + } + ; + +compstmt : stmts opt_terms + { + $$ = $1; + } + ; + +stmts : none + { + $$ = new_begin(p, 0); + } + | stmt + { + $$ = new_begin(p, $1); + NODE_LINENO($$, $1); + } + | stmts terms stmt + { + $$ = push($1, newline_node($3)); + } + | error stmt + { + $$ = new_begin(p, $2); + } + ; + +stmt : keyword_alias fsym {p->lstate = EXPR_FNAME;} fsym + { + $$ = new_alias(p, $2, $4); + } + | keyword_undef undef_list + { + $$ = $2; + } + | stmt modifier_if expr_value + { + $$ = new_if(p, cond($3), $1, 0); + } + | stmt modifier_unless expr_value + { + $$ = new_unless(p, cond($3), $1, 0); + } + | stmt modifier_while expr_value + { + $$ = new_while(p, cond($3), $1); + } + | stmt modifier_until expr_value + { + $$ = new_until(p, cond($3), $1); + } + | stmt modifier_rescue stmt + { + $$ = new_rescue(p, $1, list1(list3(0, 0, $3)), 0); + } + | keyword_END '{' compstmt '}' + { + yyerror(p, "END not suported"); + $$ = new_postexe(p, $3); + } + | command_asgn + | mlhs '=' command_call + { + $$ = new_masgn(p, $1, $3); + } + | var_lhs tOP_ASGN command_call + { + $$ = new_op_asgn(p, $1, $2, $3); + } + | primary_value '[' opt_call_args rbracket tOP_ASGN command_call + { + $$ = new_op_asgn(p, new_call(p, $1, intern("[]",2), $3), $5, $6); + } + | primary_value '.' tIDENTIFIER tOP_ASGN command_call + { + $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5); + } + | primary_value '.' tCONSTANT tOP_ASGN command_call + { + $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5); + } + | primary_value tCOLON2 tCONSTANT tOP_ASGN command_call + { + yyerror(p, "constant re-assignment"); + $$ = 0; + } + | primary_value tCOLON2 tIDENTIFIER tOP_ASGN command_call + { + $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5); + } + | backref tOP_ASGN command_call + { + backref_error(p, $1); + $$ = new_begin(p, 0); + } + | lhs '=' mrhs + { + $$ = new_asgn(p, $1, new_array(p, $3)); + } + | mlhs '=' arg_value + { + $$ = new_masgn(p, $1, $3); + } + | mlhs '=' mrhs + { + $$ = new_masgn(p, $1, new_array(p, $3)); + } + | expr + ; + +command_asgn : lhs '=' command_call + { + $$ = new_asgn(p, $1, $3); + } + | lhs '=' command_asgn + { + $$ = new_asgn(p, $1, $3); + } + ; + + +expr : command_call + | expr keyword_and expr + { + $$ = new_and(p, $1, $3); + } + | expr keyword_or expr + { + $$ = new_or(p, $1, $3); + } + | keyword_not opt_nl expr + { + $$ = call_uni_op(p, cond($3), "!"); + } + | '!' command_call + { + $$ = call_uni_op(p, cond($2), "!"); + } + | arg + ; + +expr_value : expr + { + if (!$1) $$ = new_nil(p); + else $$ = $1; + } + ; + +command_call : command + | block_command + ; + +block_command : block_call + | block_call dot_or_colon operation2 command_args + ; + +cmd_brace_block : tLBRACE_ARG + { + local_nest(p); + } + opt_block_param + compstmt + '}' + { + $$ = new_block(p, $3, $4); + local_unnest(p); + } + ; + +command : operation command_args %prec tLOWEST + { + $$ = new_fcall(p, $1, $2); + } + | operation command_args cmd_brace_block + { + args_with_block(p, $2, $3); + $$ = new_fcall(p, $1, $2); + } + | primary_value '.' operation2 command_args %prec tLOWEST + { + $$ = new_call(p, $1, $3, $4); + } + | primary_value '.' operation2 command_args cmd_brace_block + { + args_with_block(p, $4, $5); + $$ = new_call(p, $1, $3, $4); + } + | primary_value tCOLON2 operation2 command_args %prec tLOWEST + { + $$ = new_call(p, $1, $3, $4); + } + | primary_value tCOLON2 operation2 command_args cmd_brace_block + { + args_with_block(p, $4, $5); + $$ = new_call(p, $1, $3, $4); + } + | keyword_super command_args + { + $$ = new_super(p, $2); + } + | keyword_yield command_args + { + $$ = new_yield(p, $2); + } + | keyword_return call_args + { + $$ = new_return(p, ret_args(p, $2)); + } + | keyword_break call_args + { + $$ = new_break(p, ret_args(p, $2)); + } + | keyword_next call_args + { + $$ = new_next(p, ret_args(p, $2)); + } + ; + +mlhs : mlhs_basic + { + $$ = $1; + } + | tLPAREN mlhs_inner rparen + { + $$ = $2; + } + ; + +mlhs_inner : mlhs_basic + | tLPAREN mlhs_inner rparen + { + $$ = $2; + } + ; + +mlhs_basic : mlhs_list + { + $$ = list1($1); + } + | mlhs_list mlhs_item + { + $$ = list1(push($1,$2)); + } + | mlhs_list tSTAR mlhs_node + { + $$ = list2($1, $3); + } + | mlhs_list tSTAR mlhs_node ',' mlhs_post + { + $$ = list3($1, $3, $5); + } + | mlhs_list tSTAR + { + $$ = list2($1, new_nil(p)); + } + | mlhs_list tSTAR ',' mlhs_post + { + $$ = list3($1, new_nil(p), $4); + } + | tSTAR mlhs_node + { + $$ = list2(0, $2); + } + | tSTAR mlhs_node ',' mlhs_post + { + $$ = list3(0, $2, $4); + } + | tSTAR + { + $$ = list2(0, new_nil(p)); + } + | tSTAR ',' mlhs_post + { + $$ = list3(0, new_nil(p), $3); + } + ; + +mlhs_item : mlhs_node + | tLPAREN mlhs_inner rparen + { + $$ = new_masgn(p, $2, NULL); + } + ; + +mlhs_list : mlhs_item ',' + { + $$ = list1($1); + } + | mlhs_list mlhs_item ',' + { + $$ = push($1, $2); + } + ; + +mlhs_post : mlhs_item + { + $$ = list1($1); + } + | mlhs_list mlhs_item + { + $$ = push($1, $2); + } + ; + +mlhs_node : variable + { + assignable(p, $1); + } + | primary_value '[' opt_call_args rbracket + { + $$ = new_call(p, $1, intern("[]",2), $3); + } + | primary_value '.' tIDENTIFIER + { + $$ = new_call(p, $1, $3, 0); + } + | primary_value tCOLON2 tIDENTIFIER + { + $$ = new_call(p, $1, $3, 0); + } + | primary_value '.' tCONSTANT + { + $$ = new_call(p, $1, $3, 0); + } + | primary_value tCOLON2 tCONSTANT + { + if (p->in_def || p->in_single) + yyerror(p, "dynamic constant assignment"); + $$ = new_colon2(p, $1, $3); + } + | tCOLON3 tCONSTANT + { + if (p->in_def || p->in_single) + yyerror(p, "dynamic constant assignment"); + $$ = new_colon3(p, $2); + } + | backref + { + backref_error(p, $1); + $$ = 0; + } + ; + +lhs : variable + { + assignable(p, $1); + } + | primary_value '[' opt_call_args rbracket + { + $$ = new_call(p, $1, intern("[]",2), $3); + } + | primary_value '.' tIDENTIFIER + { + $$ = new_call(p, $1, $3, 0); + } + | primary_value tCOLON2 tIDENTIFIER + { + $$ = new_call(p, $1, $3, 0); + } + | primary_value '.' tCONSTANT + { + $$ = new_call(p, $1, $3, 0); + } + | primary_value tCOLON2 tCONSTANT + { + if (p->in_def || p->in_single) + yyerror(p, "dynamic constant assignment"); + $$ = new_colon2(p, $1, $3); + } + | tCOLON3 tCONSTANT + { + if (p->in_def || p->in_single) + yyerror(p, "dynamic constant assignment"); + $$ = new_colon3(p, $2); + } + | backref + { + backref_error(p, $1); + $$ = 0; + } + ; + +cname : tIDENTIFIER + { + yyerror(p, "class/module name must be CONSTANT"); + } + | tCONSTANT + ; + +cpath : tCOLON3 cname + { + $$ = cons((node*)1, nsym($2)); + } + | cname + { + $$ = cons((node*)0, nsym($1)); + } + | primary_value tCOLON2 cname + { + $$ = cons($1, nsym($3)); + } + ; + +fname : tIDENTIFIER + | tCONSTANT + | tFID + | op + { + p->lstate = EXPR_ENDFN; + $$ = $1; + } + | reswords + { + p->lstate = EXPR_ENDFN; + $$ = $1; + } + ; + +fsym : fname + | basic_symbol + ; + +undef_list : fsym + { + $$ = new_undef(p, $1); + } + | undef_list ',' {p->lstate = EXPR_FNAME;} fsym + { + $$ = push($1, nsym($4)); + } + ; + +op : '|' { $$ = intern_c('|'); } + | '^' { $$ = intern_c('^'); } + | '&' { $$ = intern_c('&'); } + | tCMP { $$ = intern("<=>",3); } + | tEQ { $$ = intern("==",2); } + | tEQQ { $$ = intern("===",3); } + | tMATCH { $$ = intern("=~",2); } + | tNMATCH { $$ = intern("!~",2); } + | '>' { $$ = intern_c('>'); } + | tGEQ { $$ = intern(">=",2); } + | '<' { $$ = intern_c('<'); } + | tLEQ { $$ = intern("<=",2); } + | tNEQ { $$ = intern("!=",2); } + | tLSHFT { $$ = intern("<<",2); } + | tRSHFT { $$ = intern(">>",2); } + | '+' { $$ = intern_c('+'); } + | '-' { $$ = intern_c('-'); } + | '*' { $$ = intern_c('*'); } + | tSTAR { $$ = intern_c('*'); } + | '/' { $$ = intern_c('/'); } + | '%' { $$ = intern_c('%'); } + | tPOW { $$ = intern("**",2); } + | '!' { $$ = intern_c('!'); } + | '~' { $$ = intern_c('~'); } + | tUPLUS { $$ = intern("+@",2); } + | tUMINUS { $$ = intern("-@",2); } + | tAREF { $$ = intern("[]",2); } + | tASET { $$ = intern("[]=",3); } + | '`' { $$ = intern_c('`'); } + ; + +reswords : keyword__LINE__ | keyword__FILE__ | keyword__ENCODING__ + | keyword_BEGIN | keyword_END + | keyword_alias | keyword_and | keyword_begin + | keyword_break | keyword_case | keyword_class | keyword_def + | keyword_do | keyword_else | keyword_elsif + | keyword_end | keyword_ensure | keyword_false + | keyword_for | keyword_in | keyword_module | keyword_next + | keyword_nil | keyword_not | keyword_or | keyword_redo + | keyword_rescue | keyword_retry | keyword_return | keyword_self + | keyword_super | keyword_then | keyword_true | keyword_undef + | keyword_when | keyword_yield | keyword_if | keyword_unless + | keyword_while | keyword_until + ; + +arg : lhs '=' arg + { + $$ = new_asgn(p, $1, $3); + } + | lhs '=' arg modifier_rescue arg + { + $$ = new_asgn(p, $1, new_rescue(p, $3, list1(list3(0, 0, $5)), 0)); + } + | var_lhs tOP_ASGN arg + { + $$ = new_op_asgn(p, $1, $2, $3); + } + | var_lhs tOP_ASGN arg modifier_rescue arg + { + $$ = new_op_asgn(p, $1, $2, new_rescue(p, $3, list1(list3(0, 0, $5)), 0)); + } + | primary_value '[' opt_call_args rbracket tOP_ASGN arg + { + $$ = new_op_asgn(p, new_call(p, $1, intern("[]",2), $3), $5, $6); + } + | primary_value '.' tIDENTIFIER tOP_ASGN arg + { + $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5); + } + | primary_value '.' tCONSTANT tOP_ASGN arg + { + $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5); + } + | primary_value tCOLON2 tIDENTIFIER tOP_ASGN arg + { + $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5); + } + | primary_value tCOLON2 tCONSTANT tOP_ASGN arg + { + yyerror(p, "constant re-assignment"); + $$ = new_begin(p, 0); + } + | tCOLON3 tCONSTANT tOP_ASGN arg + { + yyerror(p, "constant re-assignment"); + $$ = new_begin(p, 0); + } + | backref tOP_ASGN arg + { + backref_error(p, $1); + $$ = new_begin(p, 0); + } + | arg tDOT2 arg + { + $$ = new_dot2(p, $1, $3); + } + | arg tDOT3 arg + { + $$ = new_dot3(p, $1, $3); + } + | arg '+' arg + { + $$ = call_bin_op(p, $1, "+", $3); + } + | arg '-' arg + { + $$ = call_bin_op(p, $1, "-", $3); + } + | arg '*' arg + { + $$ = call_bin_op(p, $1, "*", $3); + } + | arg '/' arg + { + $$ = call_bin_op(p, $1, "/", $3); + } + | arg '%' arg + { + $$ = call_bin_op(p, $1, "%", $3); + } + | arg tPOW arg + { + $$ = call_bin_op(p, $1, "**", $3); + } + | tUMINUS_NUM tINTEGER tPOW arg + { + $$ = call_uni_op(p, call_bin_op(p, $2, "**", $4), "-@"); + } + | tUMINUS_NUM tFLOAT tPOW arg + { + $$ = call_uni_op(p, call_bin_op(p, $2, "**", $4), "-@"); + } + | tUPLUS arg + { + $$ = call_uni_op(p, $2, "+@"); + } + | tUMINUS arg + { + $$ = call_uni_op(p, $2, "-@"); + } + | arg '|' arg + { + $$ = call_bin_op(p, $1, "|", $3); + } + | arg '^' arg + { + $$ = call_bin_op(p, $1, "^", $3); + } + | arg '&' arg + { + $$ = call_bin_op(p, $1, "&", $3); + } + | arg tCMP arg + { + $$ = call_bin_op(p, $1, "<=>", $3); + } + | arg '>' arg + { + $$ = call_bin_op(p, $1, ">", $3); + } + | arg tGEQ arg + { + $$ = call_bin_op(p, $1, ">=", $3); + } + | arg '<' arg + { + $$ = call_bin_op(p, $1, "<", $3); + } + | arg tLEQ arg + { + $$ = call_bin_op(p, $1, "<=", $3); + } + | arg tEQ arg + { + $$ = call_bin_op(p, $1, "==", $3); + } + | arg tEQQ arg + { + $$ = call_bin_op(p, $1, "===", $3); + } + | arg tNEQ arg + { + $$ = call_bin_op(p, $1, "!=", $3); + } + | arg tMATCH arg + { + $$ = call_bin_op(p, $1, "=~", $3); + } + | arg tNMATCH arg + { + $$ = call_bin_op(p, $1, "!~", $3); + } + | '!' arg + { + $$ = call_uni_op(p, cond($2), "!"); + } + | '~' arg + { + $$ = call_uni_op(p, cond($2), "~"); + } + | arg tLSHFT arg + { + $$ = call_bin_op(p, $1, "<<", $3); + } + | arg tRSHFT arg + { + $$ = call_bin_op(p, $1, ">>", $3); + } + | arg tANDOP arg + { + $$ = new_and(p, $1, $3); + } + | arg tOROP arg + { + $$ = new_or(p, $1, $3); + } + | arg '?' arg opt_nl ':' arg + { + $$ = new_if(p, cond($1), $3, $6); + } + | primary + { + $$ = $1; + } + ; + +arg_value : arg + { + $$ = $1; + if (!$$) $$ = new_nil(p); + } + ; + +aref_args : none + | args trailer + { + $$ = $1; + NODE_LINENO($$, $1); + } + | args ',' assocs trailer + { + $$ = push($1, new_hash(p, $3)); + } + | assocs trailer + { + $$ = cons(new_hash(p, $1), 0); + NODE_LINENO($$, $1); + } + ; + +paren_args : '(' opt_call_args rparen + { + $$ = $2; + } + ; + +opt_paren_args : none + | paren_args + ; + +opt_call_args : none + | call_args + | args ',' + { + $$ = cons($1,0); + NODE_LINENO($$, $1); + } + | args ',' assocs ',' + { + $$ = cons(push($1, new_hash(p, $3)), 0); + NODE_LINENO($$, $1); + } + | assocs ',' + { + $$ = cons(list1(new_hash(p, $1)), 0); + NODE_LINENO($$, $1); + } + ; + +call_args : command + { + $$ = cons(list1($1), 0); + NODE_LINENO($$, $1); + } + | args opt_block_arg + { + $$ = cons($1, $2); + NODE_LINENO($$, $1); + } + | assocs opt_block_arg + { + $$ = cons(list1(new_hash(p, $1)), $2); + NODE_LINENO($$, $1); + } + | args ',' assocs opt_block_arg + { + $$ = cons(push($1, new_hash(p, $3)), $4); + NODE_LINENO($$, $1); + } + | block_arg + { + $$ = cons(0, $1); + NODE_LINENO($$, $1); + } + ; + +command_args : { + $$ = p->cmdarg_stack; + CMDARG_PUSH(1); + } + call_args + { + p->cmdarg_stack = $1; + $$ = $2; + } + ; + +block_arg : tAMPER arg_value + { + $$ = new_block_arg(p, $2); + } + ; + +opt_block_arg : ',' block_arg + { + $$ = $2; + } + | none + { + $$ = 0; + } + ; + +args : arg_value + { + $$ = cons($1, 0); + NODE_LINENO($$, $1); + } + | tSTAR arg_value + { + $$ = cons(new_splat(p, $2), 0); + NODE_LINENO($$, $2); + } + | args ',' arg_value + { + $$ = push($1, $3); + } + | args ',' tSTAR arg_value + { + $$ = push($1, new_splat(p, $4)); + } + | args ',' heredoc_bodies arg_value + { + $$ = push($1, $4); + } + | args ',' heredoc_bodies tSTAR arg_value + { + $$ = push($1, new_splat(p, $5)); + } + ; + +mrhs : args ',' arg_value + { + $$ = push($1, $3); + } + | args ',' tSTAR arg_value + { + $$ = push($1, new_splat(p, $4)); + } + | tSTAR arg_value + { + $$ = list1(new_splat(p, $2)); + } + ; + +primary : literal + | string + | xstring + | regexp + | heredoc + | var_ref + | backref + | tFID + { + $$ = new_fcall(p, $1, 0); + } + | keyword_begin + { + $$ = p->cmdarg_stack; + p->cmdarg_stack = 0; + } + bodystmt + keyword_end + { + p->cmdarg_stack = $2; + $$ = $3; + } + | tLPAREN_ARG + { + $$ = p->cmdarg_stack; + p->cmdarg_stack = 0; + } + expr {p->lstate = EXPR_ENDARG;} rparen + { + p->cmdarg_stack = $2; + $$ = $3; + } + | tLPAREN_ARG {p->lstate = EXPR_ENDARG;} rparen + { + $$ = 0; + } + | tLPAREN compstmt ')' + { + $$ = $2; + } + | primary_value tCOLON2 tCONSTANT + { + $$ = new_colon2(p, $1, $3); + } + | tCOLON3 tCONSTANT + { + $$ = new_colon3(p, $2); + } + | tLBRACK aref_args ']' + { + $$ = new_array(p, $2); + NODE_LINENO($$, $2); + } + | tLBRACE assoc_list '}' + { + $$ = new_hash(p, $2); + NODE_LINENO($$, $2); + } + | keyword_return + { + $$ = new_return(p, 0); + } + | keyword_yield '(' call_args rparen + { + $$ = new_yield(p, $3); + } + | keyword_yield '(' rparen + { + $$ = new_yield(p, 0); + } + | keyword_yield + { + $$ = new_yield(p, 0); + } + | keyword_not '(' expr rparen + { + $$ = call_uni_op(p, cond($3), "!"); + } + | keyword_not '(' rparen + { + $$ = call_uni_op(p, new_nil(p), "!"); + } + | operation brace_block + { + $$ = new_fcall(p, $1, cons(0, $2)); + } + | method_call + | method_call brace_block + { + call_with_block(p, $1, $2); + $$ = $1; + } + | tLAMBDA + { + local_nest(p); + $$ = p->lpar_beg; + p->lpar_beg = ++p->paren_nest; + } + f_larglist + { + $$ = p->cmdarg_stack; + p->cmdarg_stack = 0; + } + lambda_body + { + p->lpar_beg = $2; + $$ = new_lambda(p, $3, $5); + local_unnest(p); + p->cmdarg_stack = $4; + } + | keyword_if expr_value then + compstmt + if_tail + keyword_end + { + $$ = new_if(p, cond($2), $4, $5); + SET_LINENO($$, $1); + } + | keyword_unless expr_value then + compstmt + opt_else + keyword_end + { + $$ = new_unless(p, cond($2), $4, $5); + SET_LINENO($$, $1); + } + | keyword_while {COND_PUSH(1);} expr_value do {COND_POP();} + compstmt + keyword_end + { + $$ = new_while(p, cond($3), $6); + SET_LINENO($$, $1); + } + | keyword_until {COND_PUSH(1);} expr_value do {COND_POP();} + compstmt + keyword_end + { + $$ = new_until(p, cond($3), $6); + SET_LINENO($$, $1); + } + | keyword_case expr_value opt_terms + case_body + keyword_end + { + $$ = new_case(p, $2, $4); + } + | keyword_case opt_terms case_body keyword_end + { + $$ = new_case(p, 0, $3); + } + | keyword_for for_var keyword_in + {COND_PUSH(1);} + expr_value do + {COND_POP();} + compstmt + keyword_end + { + $$ = new_for(p, $2, $5, $8); + SET_LINENO($$, $1); + } + | keyword_class + cpath superclass + { + if (p->in_def || p->in_single) + yyerror(p, "class definition in method body"); + $$ = local_switch(p); + } + bodystmt + keyword_end + { + $$ = new_class(p, $2, $3, $5); + SET_LINENO($$, $1); + local_resume(p, $4); + } + | keyword_class + tLSHFT expr + { + $$ = p->in_def; + p->in_def = 0; + } + term + { + $$ = cons(local_switch(p), (node*)(intptr_t)p->in_single); + p->in_single = 0; + } + bodystmt + keyword_end + { + $$ = new_sclass(p, $3, $7); + SET_LINENO($$, $1); + local_resume(p, $6->car); + p->in_def = $4; + p->in_single = (int)(intptr_t)$6->cdr; + } + | keyword_module + cpath + { + if (p->in_def || p->in_single) + yyerror(p, "module definition in method body"); + $$ = local_switch(p); + } + bodystmt + keyword_end + { + $$ = new_module(p, $2, $4); + SET_LINENO($$, $1); + local_resume(p, $3); + } + | keyword_def fname + { + $$ = p->cmdarg_stack; + p->cmdarg_stack = 0; + } + { + p->in_def++; + $$ = local_switch(p); + } + f_arglist + bodystmt + keyword_end + { + $$ = new_def(p, $2, $5, $6); + SET_LINENO($$, $1); + local_resume(p, $4); + p->in_def--; + p->cmdarg_stack = $3; + } + | keyword_def singleton dot_or_colon + { + p->lstate = EXPR_FNAME; + $$ = p->cmdarg_stack; + p->cmdarg_stack = 0; + } + fname + { + p->in_single++; + p->lstate = EXPR_ENDFN; /* force for args */ + $$ = local_switch(p); + } + f_arglist + bodystmt + keyword_end + { + $$ = new_sdef(p, $2, $5, $7, $8); + SET_LINENO($$, $1); + local_resume(p, $6); + p->in_single--; + p->cmdarg_stack = $4; + } + | keyword_break + { + $$ = new_break(p, 0); + } + | keyword_next + { + $$ = new_next(p, 0); + } + | keyword_redo + { + $$ = new_redo(p); + } + | keyword_retry + { + $$ = new_retry(p); + } + ; + +primary_value : primary + { + $$ = $1; + if (!$$) $$ = new_nil(p); + } + ; + +then : term + | keyword_then + | term keyword_then + ; + +do : term + | keyword_do_cond + ; + +if_tail : opt_else + | keyword_elsif expr_value then + compstmt + if_tail + { + $$ = new_if(p, cond($2), $4, $5); + } + ; + +opt_else : none + | keyword_else compstmt + { + $$ = $2; + } + ; + +for_var : lhs + { + $$ = list1(list1($1)); + } + | mlhs + ; + +f_marg : f_norm_arg + { + $$ = new_arg(p, $1); + } + | tLPAREN f_margs rparen + { + $$ = new_masgn(p, $2, 0); + } + ; + +f_marg_list : f_marg + { + $$ = list1($1); + } + | f_marg_list ',' f_marg + { + $$ = push($1, $3); + } + ; + +f_margs : f_marg_list + { + $$ = list3($1,0,0); + } + | f_marg_list ',' tSTAR f_norm_arg + { + $$ = list3($1, new_arg(p, $4), 0); + } + | f_marg_list ',' tSTAR f_norm_arg ',' f_marg_list + { + $$ = list3($1, new_arg(p, $4), $6); + } + | f_marg_list ',' tSTAR + { + $$ = list3($1, (node*)-1, 0); + } + | f_marg_list ',' tSTAR ',' f_marg_list + { + $$ = list3($1, (node*)-1, $5); + } + | tSTAR f_norm_arg + { + $$ = list3(0, new_arg(p, $2), 0); + } + | tSTAR f_norm_arg ',' f_marg_list + { + $$ = list3(0, new_arg(p, $2), $4); + } + | tSTAR + { + $$ = list3(0, (node*)-1, 0); + } + | tSTAR ',' f_marg_list + { + $$ = list3(0, (node*)-1, $3); + } + ; + +block_param : f_arg ',' f_block_optarg ',' f_rest_arg opt_f_block_arg + { + $$ = new_args(p, $1, $3, $5, 0, $6); + } + | f_arg ',' f_block_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg + { + $$ = new_args(p, $1, $3, $5, $7, $8); + } + | f_arg ',' f_block_optarg opt_f_block_arg + { + $$ = new_args(p, $1, $3, 0, 0, $4); + } + | f_arg ',' f_block_optarg ',' f_arg opt_f_block_arg + { + $$ = new_args(p, $1, $3, 0, $5, $6); + } + | f_arg ',' f_rest_arg opt_f_block_arg + { + $$ = new_args(p, $1, 0, $3, 0, $4); + } + | f_arg ',' + { + $$ = new_args(p, $1, 0, 1, 0, 0); + } + | f_arg ',' f_rest_arg ',' f_arg opt_f_block_arg + { + $$ = new_args(p, $1, 0, $3, $5, $6); + } + | f_arg opt_f_block_arg + { + $$ = new_args(p, $1, 0, 0, 0, $2); + } + | f_block_optarg ',' f_rest_arg opt_f_block_arg + { + $$ = new_args(p, 0, $1, $3, 0, $4); + } + | f_block_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg + { + $$ = new_args(p, 0, $1, $3, $5, $6); + } + | f_block_optarg opt_f_block_arg + { + $$ = new_args(p, 0, $1, 0, 0, $2); + } + | f_block_optarg ',' f_arg opt_f_block_arg + { + $$ = new_args(p, 0, $1, 0, $3, $4); + } + | f_rest_arg opt_f_block_arg + { + $$ = new_args(p, 0, 0, $1, 0, $2); + } + | f_rest_arg ',' f_arg opt_f_block_arg + { + $$ = new_args(p, 0, 0, $1, $3, $4); + } + | f_block_arg + { + $$ = new_args(p, 0, 0, 0, 0, $1); + } + ; + +opt_block_param : none + | block_param_def + { + p->cmd_start = TRUE; + $$ = $1; + } + ; + +block_param_def : '|' opt_bv_decl '|' + { + $$ = 0; + } + | tOROP + { + $$ = 0; + } + | '|' block_param opt_bv_decl '|' + { + $$ = $2; + } + ; + + +opt_bv_decl : opt_nl + { + $$ = 0; + } + | opt_nl ';' bv_decls opt_nl + { + $$ = 0; + } + ; + +bv_decls : bvar + | bv_decls ',' bvar + ; + +bvar : tIDENTIFIER + { + local_add_f(p, $1); + new_bv(p, $1); + } + | f_bad_arg + ; + +f_larglist : '(' f_args opt_bv_decl ')' + { + $$ = $2; + } + | f_args + { + $$ = $1; + } + ; + +lambda_body : tLAMBEG compstmt '}' + { + $$ = $2; + } + | keyword_do_LAMBDA compstmt keyword_end + { + $$ = $2; + } + ; + +do_block : keyword_do_block + { + local_nest(p); + } + opt_block_param + compstmt + keyword_end + { + $$ = new_block(p,$3,$4); + local_unnest(p); + } + ; + +block_call : command do_block + { + if ($1->car == (node*)NODE_YIELD) { + yyerror(p, "block given to yield"); + } + else { + call_with_block(p, $1, $2); + } + $$ = $1; + } + | block_call dot_or_colon operation2 opt_paren_args + { + $$ = new_call(p, $1, $3, $4); + } + | block_call dot_or_colon operation2 opt_paren_args brace_block + { + $$ = new_call(p, $1, $3, $4); + call_with_block(p, $$, $5); + } + | block_call dot_or_colon operation2 command_args do_block + { + $$ = new_call(p, $1, $3, $4); + call_with_block(p, $$, $5); + } + ; + +method_call : operation paren_args + { + $$ = new_fcall(p, $1, $2); + } + | primary_value '.' operation2 opt_paren_args + { + $$ = new_call(p, $1, $3, $4); + } + | primary_value tCOLON2 operation2 paren_args + { + $$ = new_call(p, $1, $3, $4); + } + | primary_value tCOLON2 operation3 + { + $$ = new_call(p, $1, $3, 0); + } + | primary_value '.' paren_args + { + $$ = new_call(p, $1, intern("call",4), $3); + } + | primary_value tCOLON2 paren_args + { + $$ = new_call(p, $1, intern("call",4), $3); + } + | keyword_super paren_args + { + $$ = new_super(p, $2); + } + | keyword_super + { + $$ = new_zsuper(p); + } + | primary_value '[' opt_call_args rbracket + { + $$ = new_call(p, $1, intern("[]",2), $3); + } + ; + +brace_block : '{' + { + local_nest(p); + $$ = p->lineno; + } + opt_block_param + compstmt '}' + { + $$ = new_block(p,$3,$4); + SET_LINENO($$, $2); + local_unnest(p); + } + | keyword_do + { + local_nest(p); + $$ = p->lineno; + } + opt_block_param + compstmt keyword_end + { + $$ = new_block(p,$3,$4); + SET_LINENO($$, $2); + local_unnest(p); + } + ; + +case_body : keyword_when args then + compstmt + cases + { + $$ = cons(cons($2, $4), $5); + } + ; + +cases : opt_else + { + if ($1) { + $$ = cons(cons(0, $1), 0); + } + else { + $$ = 0; + } + } + | case_body + ; + +opt_rescue : keyword_rescue exc_list exc_var then + compstmt + opt_rescue + { + $$ = list1(list3($2, $3, $5)); + if ($6) $$ = append($$, $6); + } + | none + ; + +exc_list : arg_value + { + $$ = list1($1); + } + | mrhs + | none + ; + +exc_var : tASSOC lhs + { + $$ = $2; + } + | none + ; + +opt_ensure : keyword_ensure compstmt + { + $$ = $2; + } + | none + ; + +literal : numeric + | symbol + | words + | symbols + ; + +string : tCHAR + | tSTRING + | tSTRING_BEG tSTRING + { + $$ = $2; + } + | tSTRING_BEG string_rep tSTRING + { + $$ = new_dstr(p, push($2, $3)); + } + ; + +string_rep : string_interp + | string_rep string_interp + { + $$ = append($1, $2); + } + ; + +string_interp : tSTRING_MID + { + $$ = list1($1); + } + | tSTRING_PART + { + $$ = p->lex_strterm; + p->lex_strterm = NULL; + } + compstmt + '}' + { + p->lex_strterm = $2; + $$ = list2($1, $3); + } + | tLITERAL_DELIM + { + $$ = list1(new_literal_delim(p)); + } + | tHD_LITERAL_DELIM heredoc_bodies + { + $$ = list1(new_literal_delim(p)); + } + ; + +xstring : tXSTRING_BEG tXSTRING + { + $$ = $2; + } + | tXSTRING_BEG string_rep tXSTRING + { + $$ = new_dxstr(p, push($2, $3)); + } + ; + +regexp : tREGEXP_BEG tREGEXP + { + $$ = $2; + } + | tREGEXP_BEG string_rep tREGEXP + { + $$ = new_dregx(p, $2, $3); + } + ; + +heredoc : tHEREDOC_BEG + ; + +opt_heredoc_bodies : /* none */ + | heredoc_bodies + ; + +heredoc_bodies : heredoc_body + | heredoc_bodies heredoc_body + ; + +heredoc_body : tHEREDOC_END + { + parser_heredoc_info * inf = parsing_heredoc_inf(p); + inf->doc = push(inf->doc, new_str(p, "", 0)); + heredoc_end(p); + } + | heredoc_string_rep tHEREDOC_END + { + heredoc_end(p); + } + ; + +heredoc_string_rep : heredoc_string_interp + | heredoc_string_rep heredoc_string_interp + ; + +heredoc_string_interp : tHD_STRING_MID + { + parser_heredoc_info * inf = parsing_heredoc_inf(p); + inf->doc = push(inf->doc, $1); + heredoc_treat_nextline(p); + } + | tHD_STRING_PART + { + $$ = p->lex_strterm; + p->lex_strterm = NULL; + } + compstmt + '}' + { + parser_heredoc_info * inf = parsing_heredoc_inf(p); + p->lex_strterm = $2; + inf->doc = push(push(inf->doc, $1), $3); + } + ; + +words : tWORDS_BEG tSTRING + { + $$ = new_words(p, list1($2)); + } + | tWORDS_BEG string_rep tSTRING + { + $$ = new_words(p, push($2, $3)); + } + ; + + +symbol : basic_symbol + { + $$ = new_sym(p, $1); + } + | tSYMBEG tSTRING_BEG string_interp tSTRING + { + p->lstate = EXPR_END; + $$ = new_dsym(p, push($3, $4)); + } + ; + +basic_symbol : tSYMBEG sym + { + p->lstate = EXPR_END; + $$ = $2; + } + ; + +sym : fname + | tIVAR + | tGVAR + | tCVAR + | tSTRING + { + $$ = new_strsym(p, $1); + } + | tSTRING_BEG tSTRING + { + $$ = new_strsym(p, $2); + } + ; + +symbols : tSYMBOLS_BEG tSTRING + { + $$ = new_symbols(p, list1($2)); + } + | tSYMBOLS_BEG string_rep tSTRING + { + $$ = new_symbols(p, push($2, $3)); + } + ; + +numeric : tINTEGER + | tFLOAT + | tUMINUS_NUM tINTEGER %prec tLOWEST + { + $$ = negate_lit(p, $2); + } + | tUMINUS_NUM tFLOAT %prec tLOWEST + { + $$ = negate_lit(p, $2); + } + ; + +variable : tIDENTIFIER + { + $$ = new_lvar(p, $1); + } + | tIVAR + { + $$ = new_ivar(p, $1); + } + | tGVAR + { + $$ = new_gvar(p, $1); + } + | tCVAR + { + $$ = new_cvar(p, $1); + } + | tCONSTANT + { + $$ = new_const(p, $1); + } + ; + +var_lhs : variable + { + assignable(p, $1); + } + ; + +var_ref : variable + { + $$ = var_reference(p, $1); + } + | keyword_nil + { + $$ = new_nil(p); + } + | keyword_self + { + $$ = new_self(p); + } + | keyword_true + { + $$ = new_true(p); + } + | keyword_false + { + $$ = new_false(p); + } + | keyword__FILE__ + { + if (!p->filename) { + p->filename = "(null)"; + } + $$ = new_str(p, p->filename, strlen(p->filename)); + } + | keyword__LINE__ + { + char buf[16]; + + snprintf(buf, sizeof(buf), "%d", p->lineno); + $$ = new_int(p, buf, 10); + } + ; + +backref : tNTH_REF + | tBACK_REF + ; + +superclass : term + { + $$ = 0; + } + | '<' + { + p->lstate = EXPR_BEG; + p->cmd_start = TRUE; + } + expr_value term + { + $$ = $3; + } + | error term + { + yyerrok; + $$ = 0; + } + ; + +f_arglist : '(' f_args rparen + { + $$ = $2; + p->lstate = EXPR_BEG; + p->cmd_start = TRUE; + } + | f_args term + { + $$ = $1; + } + ; + +f_args : f_arg ',' f_optarg ',' f_rest_arg opt_f_block_arg + { + $$ = new_args(p, $1, $3, $5, 0, $6); + } + | f_arg ',' f_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg + { + $$ = new_args(p, $1, $3, $5, $7, $8); + } + | f_arg ',' f_optarg opt_f_block_arg + { + $$ = new_args(p, $1, $3, 0, 0, $4); + } + | f_arg ',' f_optarg ',' f_arg opt_f_block_arg + { + $$ = new_args(p, $1, $3, 0, $5, $6); + } + | f_arg ',' f_rest_arg opt_f_block_arg + { + $$ = new_args(p, $1, 0, $3, 0, $4); + } + | f_arg ',' f_rest_arg ',' f_arg opt_f_block_arg + { + $$ = new_args(p, $1, 0, $3, $5, $6); + } + | f_arg opt_f_block_arg + { + $$ = new_args(p, $1, 0, 0, 0, $2); + } + | f_optarg ',' f_rest_arg opt_f_block_arg + { + $$ = new_args(p, 0, $1, $3, 0, $4); + } + | f_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg + { + $$ = new_args(p, 0, $1, $3, $5, $6); + } + | f_optarg opt_f_block_arg + { + $$ = new_args(p, 0, $1, 0, 0, $2); + } + | f_optarg ',' f_arg opt_f_block_arg + { + $$ = new_args(p, 0, $1, 0, $3, $4); + } + | f_rest_arg opt_f_block_arg + { + $$ = new_args(p, 0, 0, $1, 0, $2); + } + | f_rest_arg ',' f_arg opt_f_block_arg + { + $$ = new_args(p, 0, 0, $1, $3, $4); + } + | f_block_arg + { + $$ = new_args(p, 0, 0, 0, 0, $1); + } + | /* none */ + { + local_add_f(p, 0); + $$ = new_args(p, 0, 0, 0, 0, 0); + } + ; + +f_bad_arg : tCONSTANT + { + yyerror(p, "formal argument cannot be a constant"); + $$ = 0; + } + | tIVAR + { + yyerror(p, "formal argument cannot be an instance variable"); + $$ = 0; + } + | tGVAR + { + yyerror(p, "formal argument cannot be a global variable"); + $$ = 0; + } + | tCVAR + { + yyerror(p, "formal argument cannot be a class variable"); + $$ = 0; + } + ; + +f_norm_arg : f_bad_arg + { + $$ = 0; + } + | tIDENTIFIER + { + local_add_f(p, $1); + $$ = $1; + } + ; + +f_arg_item : f_norm_arg + { + $$ = new_arg(p, $1); + } + | tLPAREN f_margs rparen + { + $$ = new_masgn(p, $2, 0); + } + ; + +f_arg : f_arg_item + { + $$ = list1($1); + } + | f_arg ',' f_arg_item + { + $$ = push($1, $3); + } + ; + +f_opt_asgn : tIDENTIFIER '=' + { + local_add_f(p, $1); + $$ = $1; + } + ; + +f_opt : f_opt_asgn arg_value + { + $$ = cons(nsym($1), $2); + } + ; + +f_block_opt : f_opt_asgn primary_value + { + $$ = cons(nsym($1), $2); + } + ; + +f_block_optarg : f_block_opt + { + $$ = list1($1); + } + | f_block_optarg ',' f_block_opt + { + $$ = push($1, $3); + } + ; + +f_optarg : f_opt + { + $$ = list1($1); + } + | f_optarg ',' f_opt + { + $$ = push($1, $3); + } + ; + +restarg_mark : '*' + | tSTAR + ; + +f_rest_arg : restarg_mark tIDENTIFIER + { + local_add_f(p, $2); + $$ = $2; + } + | restarg_mark + { + local_add_f(p, 0); + $$ = -1; + } + ; + +blkarg_mark : '&' + | tAMPER + ; + +f_block_arg : blkarg_mark tIDENTIFIER + { + local_add_f(p, $2); + $$ = $2; + } + ; + +opt_f_block_arg : ',' f_block_arg + { + $$ = $2; + } + | none + { + local_add_f(p, 0); + $$ = 0; + } + ; + +singleton : var_ref + { + $$ = $1; + if (!$$) $$ = new_nil(p); + } + | '(' {p->lstate = EXPR_BEG;} expr rparen + { + if ($3 == 0) { + yyerror(p, "can't define singleton method for ()."); + } + else { + switch ((enum node_type)(int)(intptr_t)$3->car) { + case NODE_STR: + case NODE_DSTR: + case NODE_XSTR: + case NODE_DXSTR: + case NODE_DREGX: + case NODE_MATCH: + case NODE_FLOAT: + case NODE_ARRAY: + case NODE_HEREDOC: + yyerror(p, "can't define singleton method for literals"); + default: + break; + } + } + $$ = $3; + } + ; + +assoc_list : none + | assocs trailer + { + $$ = $1; + } + ; + +assocs : assoc + { + $$ = list1($1); + NODE_LINENO($$, $1); + } + | assocs ',' assoc + { + $$ = push($1, $3); + } + ; + +assoc : arg_value tASSOC arg_value + { + $$ = cons($1, $3); + } + | tLABEL arg_value + { + $$ = cons(new_sym(p, $1), $2); + } + ; + +operation : tIDENTIFIER + | tCONSTANT + | tFID + ; + +operation2 : tIDENTIFIER + | tCONSTANT + | tFID + | op + ; + +operation3 : tIDENTIFIER + | tFID + | op + ; + +dot_or_colon : '.' + | tCOLON2 + ; + +opt_terms : /* none */ + | terms + ; + +opt_nl : /* none */ + | nl + ; + +rparen : opt_nl ')' + ; + +rbracket : opt_nl ']' + ; + +trailer : /* none */ + | nl + | ',' + ; + +term : ';' {yyerrok;} + | nl + ; + +nl : '\n' + { + p->lineno++; + p->column = 0; + } + opt_heredoc_bodies + +terms : term + | terms ';' {yyerrok;} + ; + +none : /* none */ + { + $$ = 0; + } + ; +%% +#define yylval (*((YYSTYPE*)(p->ylval))) + +static void +yyerror(parser_state *p, const char *s) +{ + char* c; + int n; + + if (! p->capture_errors) { +#ifdef ENABLE_STDIO + if (p->filename) { + fprintf(stderr, "%s:%d:%d: %s\n", p->filename, p->lineno, p->column, s); + } + else { + fprintf(stderr, "line %d:%d: %s\n", p->lineno, p->column, s); + } +#endif + } + else if (p->nerr < sizeof(p->error_buffer) / sizeof(p->error_buffer[0])) { + n = strlen(s); + c = (char *)parser_palloc(p, n + 1); + memcpy(c, s, n + 1); + p->error_buffer[p->nerr].message = c; + p->error_buffer[p->nerr].lineno = p->lineno; + p->error_buffer[p->nerr].column = p->column; + } + p->nerr++; +} + +static void +yyerror_i(parser_state *p, const char *fmt, int i) +{ + char buf[256]; + + snprintf(buf, sizeof(buf), fmt, i); + yyerror(p, buf); +} + +static void +yywarn(parser_state *p, const char *s) +{ + char* c; + int n; + + if (! p->capture_errors) { +#ifdef ENABLE_STDIO + if (p->filename) { + fprintf(stderr, "%s:%d:%d: %s\n", p->filename, p->lineno, p->column, s); + } + else { + fprintf(stderr, "line %d:%d: %s\n", p->lineno, p->column, s); + } +#endif + } + else if (p->nwarn < sizeof(p->warn_buffer) / sizeof(p->warn_buffer[0])) { + n = strlen(s); + c = (char *)parser_palloc(p, n + 1); + memcpy(c, s, n + 1); + p->warn_buffer[p->nwarn].message = c; + p->warn_buffer[p->nwarn].lineno = p->lineno; + p->warn_buffer[p->nwarn].column = p->column; + } + p->nwarn++; +} + +static void +yywarning(parser_state *p, const char *s) +{ + yywarn(p, s); +} + +static void +yywarning_s(parser_state *p, const char *fmt, const char *s) +{ + char buf[256]; + + snprintf(buf, sizeof(buf), fmt, s); + yywarning(p, buf); +} + +static void +backref_error(parser_state *p, node *n) +{ + int c; + + c = (int)(intptr_t)n->car; + + if (c == NODE_NTH_REF) { + yyerror_i(p, "can't set variable $%d", (int)(intptr_t)n->cdr); + } + else if (c == NODE_BACK_REF) { + yyerror_i(p, "can't set variable $%c", (int)(intptr_t)n->cdr); + } + else { + mrb_bug(p->mrb, "Internal error in backref_error() : n=>car == %S", mrb_fixnum_value(c)); + } +} + +static void pushback(parser_state *p, int c); +static mrb_bool peeks(parser_state *p, const char *s); +static mrb_bool skips(parser_state *p, const char *s); + +static inline int +nextc(parser_state *p) +{ + int c; + + if (p->pb) { + node *tmp; + + c = (int)(intptr_t)p->pb->car; + tmp = p->pb; + p->pb = p->pb->cdr; + cons_free(tmp); + } + else { +#ifdef ENABLE_STDIO + if (p->f) { + if (feof(p->f)) goto eof; + c = fgetc(p->f); + if (c == EOF) goto eof; + } + else +#endif + if (!p->s || p->s >= p->send) { + goto eof; + } + else { + c = (unsigned char)*p->s++; + } + } + if (c >= 0) { + p->column++; + } + if (c == '\r') { + c = nextc(p); + if (c != '\n') { + pushback(p, c); + return '\r'; + } + return c; + } + return c; + + eof: + if (!p->cxt) return -1; + else { + if (p->cxt->partial_hook(p) < 0) + return -1; /* end of program(s) */ + return -2; /* end of a file in the program files */ + } +} + +static void +pushback(parser_state *p, int c) +{ + if (c >= 0) { + p->column--; + } + p->pb = cons((node*)(intptr_t)c, p->pb); +} + +static void +skip(parser_state *p, char term) +{ + int c; + + for (;;) { + c = nextc(p); + if (c < 0) break; + if (c == term) break; + } +} + +static int +peekc_n(parser_state *p, int n) +{ + node *list = 0; + int c0; + + do { + c0 = nextc(p); + if (c0 == -1) return c0; /* do not skip partial EOF */ + list = push(list, (node*)(intptr_t)c0); + } while(n--); + if (p->pb) { + p->pb = append((node*)list, p->pb); + } + else { + p->pb = list; + } + return c0; +} + +static mrb_bool +peek_n(parser_state *p, int c, int n) +{ + return peekc_n(p, n) == c && c >= 0; +} +#define peek(p,c) peek_n((p), (c), 0) + +static mrb_bool +peeks(parser_state *p, const char *s) +{ + int len = strlen(s); + +#ifdef ENABLE_STDIO + if (p->f) { + int n = 0; + while (*s) { + if (!peek_n(p, *s++, n++)) return FALSE; + } + return TRUE; + } + else +#endif + if (p->s && p->s + len <= p->send) { + if (memcmp(p->s, s, len) == 0) return TRUE; + } + return FALSE; +} + +static mrb_bool +skips(parser_state *p, const char *s) +{ + int c; + + for (;;) { + /* skip until first char */ + for (;;) { + c = nextc(p); + if (c < 0) return c; + if (c == '\n') { + p->lineno++; + p->column = 0; + } + if (c == *s) break; + } + s++; + if (peeks(p, s)) { + int len = strlen(s); + + while (len--) { + if (nextc(p) == '\n') { + p->lineno++; + p->column = 0; + } + } + return TRUE; + } + else{ + s--; + } + } + return FALSE; +} + + +static int +newtok(parser_state *p) +{ + p->bidx = 0; + return p->column - 1; +} + +static void +tokadd(parser_state *p, int32_t c) +{ + char utf8[4]; + unsigned len; + + /* mrb_assert(-0x10FFFF <= c && c <= 0xFF); */ + if (c >= 0) { + /* Single byte from source or non-Unicode escape */ + utf8[0] = (char)c; + len = 1; + } + else { + /* Unicode character */ + c = -c; + if (c < 0x80) { + utf8[0] = (char)c; + len = 1; + } + else if (c < 0x800) { + utf8[0] = (char)(0xC0 | (c >> 6)); + utf8[1] = (char)(0x80 | (c & 0x3F)); + len = 2; + } + else if (c < 0x10000) { + utf8[0] = (char)(0xE0 | (c >> 12) ); + utf8[1] = (char)(0x80 | ((c >> 6) & 0x3F)); + utf8[2] = (char)(0x80 | ( c & 0x3F)); + len = 3; + } + else { + utf8[0] = (char)(0xF0 | (c >> 18) ); + utf8[1] = (char)(0x80 | ((c >> 12) & 0x3F)); + utf8[2] = (char)(0x80 | ((c >> 6) & 0x3F)); + utf8[3] = (char)(0x80 | ( c & 0x3F)); + len = 4; + } + } + if (p->bidx+len <= MRB_PARSER_BUF_SIZE) { + unsigned i; + for (i = 0; i < len; i++) { + p->buf[p->bidx++] = utf8[i]; + } + } +} + +static int +toklast(parser_state *p) +{ + return p->buf[p->bidx-1]; +} + +static void +tokfix(parser_state *p) +{ + if (p->bidx >= MRB_PARSER_BUF_SIZE) { + yyerror(p, "string too long (truncated)"); + } + p->buf[p->bidx] = '\0'; +} + +static const char* +tok(parser_state *p) +{ + return p->buf; +} + +static int +toklen(parser_state *p) +{ + return p->bidx; +} + +#define IS_ARG() (p->lstate == EXPR_ARG || p->lstate == EXPR_CMDARG) +#define IS_END() (p->lstate == EXPR_END || p->lstate == EXPR_ENDARG || p->lstate == EXPR_ENDFN) +#define IS_BEG() (p->lstate == EXPR_BEG || p->lstate == EXPR_MID || p->lstate == EXPR_VALUE || p->lstate == EXPR_CLASS) +#define IS_SPCARG(c) (IS_ARG() && space_seen && !ISSPACE(c)) +#define IS_LABEL_POSSIBLE() ((p->lstate == EXPR_BEG && !cmd_state) || IS_ARG()) +#define IS_LABEL_SUFFIX(n) (peek_n(p, ':',(n)) && !peek_n(p, ':', (n)+1)) + +static int +scan_oct(const int *start, int len, int *retlen) +{ + const int *s = start; + int retval = 0; + + /* mrb_assert(len <= 3) */ + while (len-- && *s >= '0' && *s <= '7') { + retval <<= 3; + retval |= *s++ - '0'; + } + *retlen = s - start; + + return retval; +} + +static int32_t +scan_hex(const int *start, int len, int *retlen) +{ + static const char hexdigit[] = "0123456789abcdef0123456789ABCDEF"; + const int *s = start; + int32_t retval = 0; + char *tmp; + + /* mrb_assert(len <= 8) */ + while (len-- && *s && (tmp = (char*)strchr(hexdigit, *s))) { + retval <<= 4; + retval |= (tmp - hexdigit) & 15; + s++; + } + *retlen = s - start; + + return retval; +} + +/* Return negative to indicate Unicode code point */ +static int32_t +read_escape(parser_state *p) +{ + int32_t c; + + switch (c = nextc(p)) { + case '\\':/* Backslash */ + return c; + + case 'n':/* newline */ + return '\n'; + + case 't':/* horizontal tab */ + return '\t'; + + case 'r':/* carriage-return */ + return '\r'; + + case 'f':/* form-feed */ + return '\f'; + + case 'v':/* vertical tab */ + return '\13'; + + case 'a':/* alarm(bell) */ + return '\007'; + + case 'e':/* escape */ + return 033; + + case '0': case '1': case '2': case '3': /* octal constant */ + case '4': case '5': case '6': case '7': + { + int buf[3]; + int i; + + buf[0] = c; + for (i=1; i<3; i++) { + buf[i] = nextc(p); + if (buf[i] < 0) goto eof; + if (buf[i] < '0' || '7' < buf[i]) { + pushback(p, buf[i]); + break; + } + } + c = scan_oct(buf, i, &i); + } + return c; + + case 'x': /* hex constant */ + { + int buf[2]; + int i; + + for (i=0; i<2; i++) { + buf[i] = nextc(p); + if (buf[i] < 0) goto eof; + if (!ISXDIGIT(buf[i])) { + pushback(p, buf[i]); + break; + } + } + c = scan_hex(buf, i, &i); + if (i == 0) { + yyerror(p, "Invalid escape character syntax"); + return 0; + } + } + return c; + + case 'u': /* Unicode */ + { + int buf[9]; + int i; + + /* Look for opening brace */ + i = 0; + buf[0] = nextc(p); + if (buf[0] < 0) goto eof; + if (buf[0] == '{') { + /* \u{xxxxxxxx} form */ + for (i=0; i<9; i++) { + buf[i] = nextc(p); + if (buf[i] < 0) goto eof; + if (buf[i] == '}') { + break; + } + else if (!ISXDIGIT(buf[i])) { + yyerror(p, "Invalid escape character syntax"); + pushback(p, buf[i]); + return 0; + } + } + } + else if (ISXDIGIT(buf[0])) { + /* \uxxxx form */ + for (i=1; i<4; i++) { + buf[i] = nextc(p); + if (buf[i] < 0) goto eof; + if (!ISXDIGIT(buf[i])) { + pushback(p, buf[i]); + break; + } + } + } + else { + pushback(p, buf[0]); + } + c = scan_hex(buf, i, &i); + if (i == 0) { + yyerror(p, "Invalid escape character syntax"); + return 0; + } + if (c < 0 || c > 0x10FFFF || (c & 0xFFFFF800) == 0xD800) { + yyerror(p, "Invalid Unicode code point"); + return 0; + } + } + return -c; + + case 'b':/* backspace */ + return '\010'; + + case 's':/* space */ + return ' '; + + case 'M': + if ((c = nextc(p)) != '-') { + yyerror(p, "Invalid escape character syntax"); + pushback(p, c); + return '\0'; + } + if ((c = nextc(p)) == '\\') { + return read_escape(p) | 0x80; + } + else if (c < 0) goto eof; + else { + return ((c & 0xff) | 0x80); + } + + case 'C': + if ((c = nextc(p)) != '-') { + yyerror(p, "Invalid escape character syntax"); + pushback(p, c); + return '\0'; + } + case 'c': + if ((c = nextc(p))== '\\') { + c = read_escape(p); + } + else if (c == '?') + return 0177; + else if (c < 0) goto eof; + return c & 0x9f; + + eof: + case -1: + case -2: /* end of a file */ + yyerror(p, "Invalid escape character syntax"); + return '\0'; + + default: + return c; + } +} + +static int +parse_string(parser_state *p) +{ + int c; + string_type type = (string_type)(intptr_t)p->lex_strterm->car; + int nest_level = (intptr_t)p->lex_strterm->cdr->car; + int beg = (intptr_t)p->lex_strterm->cdr->cdr->car; + int end = (intptr_t)p->lex_strterm->cdr->cdr->cdr; + parser_heredoc_info *hinf = (type & STR_FUNC_HEREDOC) ? parsing_heredoc_inf(p) : NULL; + + newtok(p); + while ((c = nextc(p)) != end || nest_level != 0) { + if (hinf && (c == '\n' || c < 0)) { + mrb_bool line_head; + tokadd(p, '\n'); + tokfix(p); + p->lineno++; + p->column = 0; + line_head = hinf->line_head; + hinf->line_head = TRUE; + if (line_head) { + /* check whether end of heredoc */ + const char *s = tok(p); + int len = toklen(p); + if (hinf->allow_indent) { + while (ISSPACE(*s) && len > 0) { + ++s; + --len; + } + } + if ((len-1 == hinf->term_len) && (strncmp(s, hinf->term, len-1) == 0)) { + return tHEREDOC_END; + } + } + if (c < 0) { + char buf[256]; + snprintf(buf, sizeof(buf), "can't find heredoc delimiter \"%s\" anywhere before EOF", hinf->term); + yyerror(p, buf); + return 0; + } + yylval.nd = new_str(p, tok(p), toklen(p)); + return tHD_STRING_MID; + } + if (c < 0) { + yyerror(p, "unterminated string meets end of file"); + return 0; + } + else if (c == beg) { + nest_level++; + p->lex_strterm->cdr->car = (node*)(intptr_t)nest_level; + } + else if (c == end) { + nest_level--; + p->lex_strterm->cdr->car = (node*)(intptr_t)nest_level; + } + else if (c == '\\') { + c = nextc(p); + if (type & STR_FUNC_EXPAND) { + if (c == end || c == beg) { + tokadd(p, c); + } + else if (c == '\n') { + p->lineno++; + p->column = 0; + if (type & STR_FUNC_ARRAY) { + tokadd(p, '\n'); + } + } + else if (type & STR_FUNC_REGEXP) { + tokadd(p, '\\'); + tokadd(p, c); + } + else { + pushback(p, c); + tokadd(p, read_escape(p)); + if (hinf) + hinf->line_head = FALSE; + } + } + else { + if (c != beg && c != end) { + if (c == '\n') { + p->lineno++; + p->column = 0; + } + if (!(c == '\\' || ((type & STR_FUNC_ARRAY) && ISSPACE(c)))) { + tokadd(p, '\\'); + } + } + tokadd(p, c); + } + continue; + } + else if ((c == '#') && (type & STR_FUNC_EXPAND)) { + c = nextc(p); + if (c == '{') { + tokfix(p); + p->lstate = EXPR_BEG; + p->cmd_start = TRUE; + yylval.nd = new_str(p, tok(p), toklen(p)); + if (hinf) { + hinf->line_head = FALSE; + return tHD_STRING_PART; + } + return tSTRING_PART; + } + tokadd(p, '#'); + pushback(p, c); + continue; + } + if ((type & STR_FUNC_ARRAY) && ISSPACE(c)) { + if (toklen(p) == 0) { + do { + if (c == '\n') { + p->lineno++; + p->column = 0; + heredoc_treat_nextline(p); + if (p->parsing_heredoc != NULL) { + return tHD_LITERAL_DELIM; + } + } + c = nextc(p); + } while (ISSPACE(c)); + pushback(p, c); + return tLITERAL_DELIM; + } + else { + pushback(p, c); + tokfix(p); + yylval.nd = new_str(p, tok(p), toklen(p)); + return tSTRING_MID; + } + } + tokadd(p, c); + } + + tokfix(p); + p->lstate = EXPR_END; + end_strterm(p); + + if (type & STR_FUNC_XQUOTE) { + yylval.nd = new_xstr(p, tok(p), toklen(p)); + return tXSTRING; + } + + if (type & STR_FUNC_REGEXP) { + int f = 0; + int re_opt; + char *s = strndup(tok(p), toklen(p)); + char flags[3]; + char *flag = flags; + char *dup; + + newtok(p); + while (re_opt = nextc(p), re_opt >= 0 && ISALPHA(re_opt)) { + switch (re_opt) { + case 'i': f |= 1; break; + case 'x': f |= 2; break; + case 'm': f |= 4; break; + default: tokadd(p, re_opt); break; + } + } + pushback(p, re_opt); + if (toklen(p)) { + char msg[128]; + tokfix(p); + snprintf(msg, sizeof(msg), "unknown regexp option%s - %s", + toklen(p) > 1 ? "s" : "", tok(p)); + yyerror(p, msg); + } + if (f != 0) { + if (f & 1) *flag++ = 'i'; + if (f & 2) *flag++ = 'x'; + if (f & 4) *flag++ = 'm'; + dup = strndup(flags, (size_t)(flag - flags)); + } + else { + dup = NULL; + } + yylval.nd = new_regx(p, s, dup); + + return tREGEXP; + } + + yylval.nd = new_str(p, tok(p), toklen(p)); + return tSTRING; +} + + +static int +heredoc_identifier(parser_state *p) +{ + int c; + int type = str_heredoc; + mrb_bool indent = FALSE; + mrb_bool quote = FALSE; + node *newnode; + parser_heredoc_info *info; + + c = nextc(p); + if (ISSPACE(c) || c == '=') { + pushback(p, c); + return 0; + } + if (c == '-') { + indent = TRUE; + c = nextc(p); + } + if (c == '\'' || c == '"') { + int term = c; + if (c == '\'') + quote = TRUE; + newtok(p); + while ((c = nextc(p)) >= 0 && c != term) { + if (c == '\n') { + c = -1; + break; + } + tokadd(p, c); + } + if (c < 0) { + yyerror(p, "unterminated here document identifier"); + return 0; + } + } + else { + if (c < 0) { + return 0; /* missing here document identifier */ + } + if (! identchar(c)) { + pushback(p, c); + if (indent) pushback(p, '-'); + return 0; + } + newtok(p); + do { + tokadd(p, c); + } while ((c = nextc(p)) >= 0 && identchar(c)); + pushback(p, c); + } + tokfix(p); + newnode = new_heredoc(p); + info = (parser_heredoc_info*)newnode->cdr; + info->term = strndup(tok(p), toklen(p)); + info->term_len = toklen(p); + if (! quote) + type |= STR_FUNC_EXPAND; + info->type = (string_type)type; + info->allow_indent = indent; + info->line_head = TRUE; + info->doc = NULL; + p->heredocs_from_nextline = push(p->heredocs_from_nextline, newnode); + p->lstate = EXPR_END; + + yylval.nd = newnode; + return tHEREDOC_BEG; +} + +static int +arg_ambiguous(parser_state *p) +{ + yywarning(p, "ambiguous first argument; put parentheses or even spaces"); + return 1; +} + +#include "lex.def" + +static int +parser_yylex(parser_state *p) +{ + int32_t c; + int space_seen = 0; + int cmd_state; + enum mrb_lex_state_enum last_state; + int token_column; + + if (p->lex_strterm) { + if (is_strterm_type(p, STR_FUNC_HEREDOC)) { + if (p->parsing_heredoc != NULL) + return parse_string(p); + } + else + return parse_string(p); + } + cmd_state = p->cmd_start; + p->cmd_start = FALSE; + retry: + last_state = p->lstate; + switch (c = nextc(p)) { + case '\004': /* ^D */ + case '\032': /* ^Z */ + case '\0': /* NUL */ + case -1: /* end of script. */ + if (p->heredocs_from_nextline) + goto maybe_heredoc; + return 0; + + /* white spaces */ + case ' ': case '\t': case '\f': case '\r': + case '\13': /* '\v' */ + space_seen = 1; + goto retry; + + case '#': /* it's a comment */ + skip(p, '\n'); + /* fall through */ + case -2: /* end of a file */ + case '\n': + maybe_heredoc: + heredoc_treat_nextline(p); + switch (p->lstate) { + case EXPR_BEG: + case EXPR_FNAME: + case EXPR_DOT: + case EXPR_CLASS: + case EXPR_VALUE: + p->lineno++; + p->column = 0; + if (p->parsing_heredoc != NULL) { + if (p->lex_strterm) { + return parse_string(p); + } + } + goto retry; + default: + break; + } + if (p->parsing_heredoc != NULL) { + return '\n'; + } + while ((c = nextc(p))) { + switch (c) { + case ' ': case '\t': case '\f': case '\r': + case '\13': /* '\v' */ + space_seen = 1; + break; + case '.': + if ((c = nextc(p)) != '.') { + pushback(p, c); + pushback(p, '.'); + goto retry; + } + case -1: /* EOF */ + case -2: /* end of a file */ + goto normal_newline; + default: + pushback(p, c); + goto normal_newline; + } + } + normal_newline: + p->cmd_start = TRUE; + p->lstate = EXPR_BEG; + return '\n'; + + case '*': + if ((c = nextc(p)) == '*') { + if ((c = nextc(p)) == '=') { + yylval.id = intern("**",2); + p->lstate = EXPR_BEG; + return tOP_ASGN; + } + pushback(p, c); + c = tPOW; + } + else { + if (c == '=') { + yylval.id = intern_c('*'); + p->lstate = EXPR_BEG; + return tOP_ASGN; + } + pushback(p, c); + if (IS_SPCARG(c)) { + yywarning(p, "`*' interpreted as argument prefix"); + c = tSTAR; + } + else if (IS_BEG()) { + c = tSTAR; + } + else { + c = '*'; + } + } + if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { + p->lstate = EXPR_ARG; + } + else { + p->lstate = EXPR_BEG; + } + return c; + + case '!': + c = nextc(p); + if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { + p->lstate = EXPR_ARG; + if (c == '@') { + return '!'; + } + } + else { + p->lstate = EXPR_BEG; + } + if (c == '=') { + return tNEQ; + } + if (c == '~') { + return tNMATCH; + } + pushback(p, c); + return '!'; + + case '=': + if (p->column == 1) { + static const char begin[] = "begin"; + static const char end[] = "\n=end"; + if (peeks(p, begin)) { + c = peekc_n(p, sizeof(begin)-1); + if (c < 0 || ISSPACE(c)) { + do { + if (!skips(p, end)) { + yyerror(p, "embedded document meets end of file"); + return 0; + } + c = nextc(p); + } while (!(c < 0 || ISSPACE(c))); + if (c != '\n') skip(p, '\n'); + p->lineno++; + p->column = 0; + goto retry; + } + } + } + if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { + p->lstate = EXPR_ARG; + } + else { + p->lstate = EXPR_BEG; + } + if ((c = nextc(p)) == '=') { + if ((c = nextc(p)) == '=') { + return tEQQ; + } + pushback(p, c); + return tEQ; + } + if (c == '~') { + return tMATCH; + } + else if (c == '>') { + return tASSOC; + } + pushback(p, c); + return '='; + + case '<': + c = nextc(p); + if (c == '<' && + p->lstate != EXPR_DOT && + p->lstate != EXPR_CLASS && + !IS_END() && + (!IS_ARG() || space_seen)) { + int token = heredoc_identifier(p); + if (token) + return token; + } + if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { + p->lstate = EXPR_ARG; + } + else { + p->lstate = EXPR_BEG; + if (p->lstate == EXPR_CLASS) { + p->cmd_start = TRUE; + } + } + if (c == '=') { + if ((c = nextc(p)) == '>') { + return tCMP; + } + pushback(p, c); + return tLEQ; + } + if (c == '<') { + if ((c = nextc(p)) == '=') { + yylval.id = intern("<<",2); + p->lstate = EXPR_BEG; + return tOP_ASGN; + } + pushback(p, c); + return tLSHFT; + } + pushback(p, c); + return '<'; + + case '>': + if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { + p->lstate = EXPR_ARG; + } + else { + p->lstate = EXPR_BEG; + } + if ((c = nextc(p)) == '=') { + return tGEQ; + } + if (c == '>') { + if ((c = nextc(p)) == '=') { + yylval.id = intern(">>",2); + p->lstate = EXPR_BEG; + return tOP_ASGN; + } + pushback(p, c); + return tRSHFT; + } + pushback(p, c); + return '>'; + + case '"': + p->lex_strterm = new_strterm(p, str_dquote, '"', 0); + return tSTRING_BEG; + + case '\'': + p->lex_strterm = new_strterm(p, str_squote, '\'', 0); + return parse_string(p); + + case '`': + if (p->lstate == EXPR_FNAME) { + p->lstate = EXPR_ENDFN; + return '`'; + } + if (p->lstate == EXPR_DOT) { + if (cmd_state) + p->lstate = EXPR_CMDARG; + else + p->lstate = EXPR_ARG; + return '`'; + } + p->lex_strterm = new_strterm(p, str_xquote, '`', 0); + return tXSTRING_BEG; + + case '?': + if (IS_END()) { + p->lstate = EXPR_VALUE; + return '?'; + } + c = nextc(p); + if (c < 0) { + yyerror(p, "incomplete character syntax"); + return 0; + } + if (ISSPACE(c)) { + if (!IS_ARG()) { + int c2; + switch (c) { + case ' ': + c2 = 's'; + break; + case '\n': + c2 = 'n'; + break; + case '\t': + c2 = 't'; + break; + case '\v': + c2 = 'v'; + break; + case '\r': + c2 = 'r'; + break; + case '\f': + c2 = 'f'; + break; + default: + c2 = 0; + break; + } + if (c2) { + char buf[256]; + snprintf(buf, sizeof(buf), "invalid character syntax; use ?\\%c", c2); + yyerror(p, buf); + } + } + ternary: + pushback(p, c); + p->lstate = EXPR_VALUE; + return '?'; + } + newtok(p); + /* need support UTF-8 if configured */ + if ((isalnum(c) || c == '_')) { + int c2 = nextc(p); + pushback(p, c2); + if ((isalnum(c2) || c2 == '_')) { + goto ternary; + } + } + if (c == '\\') { + c = read_escape(p); + tokadd(p, c); + } + else { + tokadd(p, c); + } + tokfix(p); + yylval.nd = new_str(p, tok(p), toklen(p)); + p->lstate = EXPR_END; + return tCHAR; + + case '&': + if ((c = nextc(p)) == '&') { + p->lstate = EXPR_BEG; + if ((c = nextc(p)) == '=') { + yylval.id = intern("&&",2); + p->lstate = EXPR_BEG; + return tOP_ASGN; + } + pushback(p, c); + return tANDOP; + } + else if (c == '=') { + yylval.id = intern_c('&'); + p->lstate = EXPR_BEG; + return tOP_ASGN; + } + pushback(p, c); + if (IS_SPCARG(c)) { + yywarning(p, "`&' interpreted as argument prefix"); + c = tAMPER; + } + else if (IS_BEG()) { + c = tAMPER; + } + else { + c = '&'; + } + if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { + p->lstate = EXPR_ARG; + } + else { + p->lstate = EXPR_BEG; + } + return c; + + case '|': + if ((c = nextc(p)) == '|') { + p->lstate = EXPR_BEG; + if ((c = nextc(p)) == '=') { + yylval.id = intern("||",2); + p->lstate = EXPR_BEG; + return tOP_ASGN; + } + pushback(p, c); + return tOROP; + } + if (c == '=') { + yylval.id = intern_c('|'); + p->lstate = EXPR_BEG; + return tOP_ASGN; + } + if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { + p->lstate = EXPR_ARG; + } + else { + p->lstate = EXPR_BEG; + } + pushback(p, c); + return '|'; + + case '+': + c = nextc(p); + if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { + p->lstate = EXPR_ARG; + if (c == '@') { + return tUPLUS; + } + pushback(p, c); + return '+'; + } + if (c == '=') { + yylval.id = intern_c('+'); + p->lstate = EXPR_BEG; + return tOP_ASGN; + } + if (IS_BEG() || (IS_SPCARG(c) && arg_ambiguous(p))) { + p->lstate = EXPR_BEG; + pushback(p, c); + if (c >= 0 && ISDIGIT(c)) { + c = '+'; + goto start_num; + } + return tUPLUS; + } + p->lstate = EXPR_BEG; + pushback(p, c); + return '+'; + + case '-': + c = nextc(p); + if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { + p->lstate = EXPR_ARG; + if (c == '@') { + return tUMINUS; + } + pushback(p, c); + return '-'; + } + if (c == '=') { + yylval.id = intern_c('-'); + p->lstate = EXPR_BEG; + return tOP_ASGN; + } + if (c == '>') { + p->lstate = EXPR_ENDFN; + return tLAMBDA; + } + if (IS_BEG() || (IS_SPCARG(c) && arg_ambiguous(p))) { + p->lstate = EXPR_BEG; + pushback(p, c); + if (c >= 0 && ISDIGIT(c)) { + return tUMINUS_NUM; + } + return tUMINUS; + } + p->lstate = EXPR_BEG; + pushback(p, c); + return '-'; + + case '.': + p->lstate = EXPR_BEG; + if ((c = nextc(p)) == '.') { + if ((c = nextc(p)) == '.') { + return tDOT3; + } + pushback(p, c); + return tDOT2; + } + pushback(p, c); + if (c >= 0 && ISDIGIT(c)) { + yyerror(p, "no . floating literal anymore; put 0 before dot"); + } + p->lstate = EXPR_DOT; + return '.'; + + start_num: + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + { + int is_float, seen_point, seen_e, nondigit; + + is_float = seen_point = seen_e = nondigit = 0; + p->lstate = EXPR_END; + newtok(p); + if (c == '-' || c == '+') { + tokadd(p, c); + c = nextc(p); + } + if (c == '0') { +#define no_digits() do {yyerror(p,"numeric literal without digits"); return 0;} while (0) + int start = toklen(p); + c = nextc(p); + if (c == 'x' || c == 'X') { + /* hexadecimal */ + c = nextc(p); + if (c >= 0 && ISXDIGIT(c)) { + do { + if (c == '_') { + if (nondigit) break; + nondigit = c; + continue; + } + if (!ISXDIGIT(c)) break; + nondigit = 0; + tokadd(p, tolower(c)); + } while ((c = nextc(p)) >= 0); + } + pushback(p, c); + tokfix(p); + if (toklen(p) == start) { + no_digits(); + } + else if (nondigit) goto trailing_uc; + yylval.nd = new_int(p, tok(p), 16); + return tINTEGER; + } + if (c == 'b' || c == 'B') { + /* binary */ + c = nextc(p); + if (c == '0' || c == '1') { + do { + if (c == '_') { + if (nondigit) break; + nondigit = c; + continue; + } + if (c != '0' && c != '1') break; + nondigit = 0; + tokadd(p, c); + } while ((c = nextc(p)) >= 0); + } + pushback(p, c); + tokfix(p); + if (toklen(p) == start) { + no_digits(); + } + else if (nondigit) goto trailing_uc; + yylval.nd = new_int(p, tok(p), 2); + return tINTEGER; + } + if (c == 'd' || c == 'D') { + /* decimal */ + c = nextc(p); + if (c >= 0 && ISDIGIT(c)) { + do { + if (c == '_') { + if (nondigit) break; + nondigit = c; + continue; + } + if (!ISDIGIT(c)) break; + nondigit = 0; + tokadd(p, c); + } while ((c = nextc(p)) >= 0); + } + pushback(p, c); + tokfix(p); + if (toklen(p) == start) { + no_digits(); + } + else if (nondigit) goto trailing_uc; + yylval.nd = new_int(p, tok(p), 10); + return tINTEGER; + } + if (c == '_') { + /* 0_0 */ + goto octal_number; + } + if (c == 'o' || c == 'O') { + /* prefixed octal */ + c = nextc(p); + if (c < 0 || c == '_' || !ISDIGIT(c)) { + no_digits(); + } + } + if (c >= '0' && c <= '7') { + /* octal */ + octal_number: + do { + if (c == '_') { + if (nondigit) break; + nondigit = c; + continue; + } + if (c < '0' || c > '9') break; + if (c > '7') goto invalid_octal; + nondigit = 0; + tokadd(p, c); + } while ((c = nextc(p)) >= 0); + + if (toklen(p) > start) { + pushback(p, c); + tokfix(p); + if (nondigit) goto trailing_uc; + yylval.nd = new_int(p, tok(p), 8); + return tINTEGER; + } + if (nondigit) { + pushback(p, c); + goto trailing_uc; + } + } + if (c > '7' && c <= '9') { + invalid_octal: + yyerror(p, "Invalid octal digit"); + } + else if (c == '.' || c == 'e' || c == 'E') { + tokadd(p, '0'); + } + else { + pushback(p, c); + yylval.nd = new_int(p, "0", 10); + return tINTEGER; + } + } + + for (;;) { + switch (c) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + nondigit = 0; + tokadd(p, c); + break; + + case '.': + if (nondigit) goto trailing_uc; + if (seen_point || seen_e) { + goto decode_num; + } + else { + int c0 = nextc(p); + if (c0 < 0 || !ISDIGIT(c0)) { + pushback(p, c0); + goto decode_num; + } + c = c0; + } + tokadd(p, '.'); + tokadd(p, c); + is_float++; + seen_point++; + nondigit = 0; + break; + + case 'e': + case 'E': + if (nondigit) { + pushback(p, c); + c = nondigit; + goto decode_num; + } + if (seen_e) { + goto decode_num; + } + tokadd(p, c); + seen_e++; + is_float++; + nondigit = c; + c = nextc(p); + if (c != '-' && c != '+') continue; + tokadd(p, c); + nondigit = c; + break; + + case '_': /* `_' in number just ignored */ + if (nondigit) goto decode_num; + nondigit = c; + break; + + default: + goto decode_num; + } + c = nextc(p); + } + + decode_num: + pushback(p, c); + if (nondigit) { + trailing_uc: + yyerror_i(p, "trailing `%c' in number", nondigit); + } + tokfix(p); + if (is_float) { + double d; + char *endp; + + errno = 0; + d = strtod(tok(p), &endp); + if (d == 0 && endp == tok(p)) { + yywarning_s(p, "corrupted float value %s", tok(p)); + } + else if (errno == ERANGE) { + yywarning_s(p, "float %s out of range", tok(p)); + errno = 0; + } + yylval.nd = new_float(p, tok(p)); + return tFLOAT; + } + yylval.nd = new_int(p, tok(p), 10); + return tINTEGER; + } + + case ')': + case ']': + p->paren_nest--; + case '}': + COND_LEXPOP(); + CMDARG_LEXPOP(); + if (c == ')') + p->lstate = EXPR_ENDFN; + else + p->lstate = EXPR_ENDARG; + return c; + + case ':': + c = nextc(p); + if (c == ':') { + if (IS_BEG() || p->lstate == EXPR_CLASS || IS_SPCARG(-1)) { + p->lstate = EXPR_BEG; + return tCOLON3; + } + p->lstate = EXPR_DOT; + return tCOLON2; + } + if (IS_END() || ISSPACE(c)) { + pushback(p, c); + p->lstate = EXPR_BEG; + return ':'; + } + pushback(p, c); + p->lstate = EXPR_FNAME; + return tSYMBEG; + + case '/': + if (IS_BEG()) { + p->lex_strterm = new_strterm(p, str_regexp, '/', 0); + return tREGEXP_BEG; + } + if ((c = nextc(p)) == '=') { + yylval.id = intern_c('/'); + p->lstate = EXPR_BEG; + return tOP_ASGN; + } + pushback(p, c); + if (IS_SPCARG(c)) { + p->lex_strterm = new_strterm(p, str_regexp, '/', 0); + return tREGEXP_BEG; + } + if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { + p->lstate = EXPR_ARG; + } + else { + p->lstate = EXPR_BEG; + } + return '/'; + + case '^': + if ((c = nextc(p)) == '=') { + yylval.id = intern_c('^'); + p->lstate = EXPR_BEG; + return tOP_ASGN; + } + if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { + p->lstate = EXPR_ARG; + } + else { + p->lstate = EXPR_BEG; + } + pushback(p, c); + return '^'; + + case ';': + p->lstate = EXPR_BEG; + return ';'; + + case ',': + p->lstate = EXPR_BEG; + return ','; + + case '~': + if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { + if ((c = nextc(p)) != '@') { + pushback(p, c); + } + p->lstate = EXPR_ARG; + } + else { + p->lstate = EXPR_BEG; + } + return '~'; + + case '(': + if (IS_BEG()) { + c = tLPAREN; + } + else if (IS_SPCARG(-1)) { + c = tLPAREN_ARG; + } + p->paren_nest++; + COND_PUSH(0); + CMDARG_PUSH(0); + p->lstate = EXPR_BEG; + return c; + + case '[': + p->paren_nest++; + if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { + p->lstate = EXPR_ARG; + if ((c = nextc(p)) == ']') { + if ((c = nextc(p)) == '=') { + return tASET; + } + pushback(p, c); + return tAREF; + } + pushback(p, c); + return '['; + } + else if (IS_BEG()) { + c = tLBRACK; + } + else if (IS_ARG() && space_seen) { + c = tLBRACK; + } + p->lstate = EXPR_BEG; + COND_PUSH(0); + CMDARG_PUSH(0); + return c; + + case '{': + if (p->lpar_beg && p->lpar_beg == p->paren_nest) { + p->lstate = EXPR_BEG; + p->lpar_beg = 0; + p->paren_nest--; + COND_PUSH(0); + CMDARG_PUSH(0); + return tLAMBEG; + } + if (IS_ARG() || p->lstate == EXPR_END || p->lstate == EXPR_ENDFN) + c = '{'; /* block (primary) */ + else if (p->lstate == EXPR_ENDARG) + c = tLBRACE_ARG; /* block (expr) */ + else + c = tLBRACE; /* hash */ + COND_PUSH(0); + CMDARG_PUSH(0); + p->lstate = EXPR_BEG; + return c; + + case '\\': + c = nextc(p); + if (c == '\n') { + p->lineno++; + p->column = 0; + space_seen = 1; + goto retry; /* skip \\n */ + } + pushback(p, c); + return '\\'; + + case '%': + if (IS_BEG()) { + int term; + int paren; + + c = nextc(p); + quotation: + if (c < 0 || !ISALNUM(c)) { + term = c; + c = 'Q'; + } + else { + term = nextc(p); + if (isalnum(term)) { + yyerror(p, "unknown type of %string"); + return 0; + } + } + if (c < 0 || term < 0) { + yyerror(p, "unterminated quoted string meets end of file"); + return 0; + } + paren = term; + if (term == '(') term = ')'; + else if (term == '[') term = ']'; + else if (term == '{') term = '}'; + else if (term == '<') term = '>'; + else paren = 0; + + switch (c) { + case 'Q': + p->lex_strterm = new_strterm(p, str_dquote, term, paren); + return tSTRING_BEG; + + case 'q': + p->lex_strterm = new_strterm(p, str_squote, term, paren); + return parse_string(p); + + case 'W': + p->lex_strterm = new_strterm(p, str_dword, term, paren); + return tWORDS_BEG; + + case 'w': + p->lex_strterm = new_strterm(p, str_sword, term, paren); + return tWORDS_BEG; + + case 'x': + p->lex_strterm = new_strterm(p, str_xquote, term, paren); + return tXSTRING_BEG; + + case 'r': + p->lex_strterm = new_strterm(p, str_regexp, term, paren); + return tREGEXP_BEG; + + case 's': + p->lex_strterm = new_strterm(p, str_ssym, term, paren); + return tSYMBEG; + + case 'I': + p->lex_strterm = new_strterm(p, str_dsymbols, term, paren); + return tSYMBOLS_BEG; + + case 'i': + p->lex_strterm = new_strterm(p, str_ssymbols, term, paren); + return tSYMBOLS_BEG; + + default: + yyerror(p, "unknown type of %string"); + return 0; + } + } + if ((c = nextc(p)) == '=') { + yylval.id = intern_c('%'); + p->lstate = EXPR_BEG; + return tOP_ASGN; + } + if (IS_SPCARG(c)) { + goto quotation; + } + if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { + p->lstate = EXPR_ARG; + } + else { + p->lstate = EXPR_BEG; + } + pushback(p, c); + return '%'; + + case '$': + p->lstate = EXPR_END; + token_column = newtok(p); + c = nextc(p); + if (c < 0) { + yyerror(p, "incomplete global variable syntax"); + return 0; + } + switch (c) { + case '_': /* $_: last read line string */ + c = nextc(p); + if (c >= 0 && identchar(c)) { /* if there is more after _ it is a variable */ + tokadd(p, '$'); + tokadd(p, c); + break; + } + pushback(p, c); + c = '_'; + /* fall through */ + case '~': /* $~: match-data */ + case '*': /* $*: argv */ + case '$': /* $$: pid */ + case '?': /* $?: last status */ + case '!': /* $!: error string */ + case '@': /* $@: error position */ + case '/': /* $/: input record separator */ + case '\\': /* $\: output record separator */ + case ';': /* $;: field separator */ + case ',': /* $,: output field separator */ + case '.': /* $.: last read line number */ + case '=': /* $=: ignorecase */ + case ':': /* $:: load path */ + case '<': /* $<: reading filename */ + case '>': /* $>: default output handle */ + case '\"': /* $": already loaded files */ + tokadd(p, '$'); + tokadd(p, c); + tokfix(p); + yylval.id = intern_cstr(tok(p)); + return tGVAR; + + case '-': + tokadd(p, '$'); + tokadd(p, c); + c = nextc(p); + pushback(p, c); + gvar: + tokfix(p); + yylval.id = intern_cstr(tok(p)); + return tGVAR; + + case '&': /* $&: last match */ + case '`': /* $`: string before last match */ + case '\'': /* $': string after last match */ + case '+': /* $+: string matches last pattern */ + if (last_state == EXPR_FNAME) { + tokadd(p, '$'); + tokadd(p, c); + goto gvar; + } + yylval.nd = new_back_ref(p, c); + return tBACK_REF; + + case '1': case '2': case '3': + case '4': case '5': case '6': + case '7': case '8': case '9': + do { + tokadd(p, c); + c = nextc(p); + } while (c >= 0 && isdigit(c)); + pushback(p, c); + if (last_state == EXPR_FNAME) goto gvar; + tokfix(p); + { + unsigned long n = strtoul(tok(p), NULL, 10); + if (n > INT_MAX) { + yyerror_i(p, "capture group index must be <= %d", INT_MAX); + return 0; + } + yylval.nd = new_nth_ref(p, (int)n); + } + return tNTH_REF; + + default: + if (!identchar(c)) { + pushback(p, c); + return '$'; + } + case '0': + tokadd(p, '$'); + } + break; + + case '@': + c = nextc(p); + token_column = newtok(p); + tokadd(p, '@'); + if (c == '@') { + tokadd(p, '@'); + c = nextc(p); + } + if (c < 0) { + if (p->bidx == 1) { + yyerror(p, "incomplete instance variable syntax"); + } + else { + yyerror(p, "incomplete class variable syntax"); + } + return 0; + } + else if (isdigit(c)) { + if (p->bidx == 1) { + yyerror_i(p, "`@%c' is not allowed as an instance variable name", c); + } + else { + yyerror_i(p, "`@@%c' is not allowed as a class variable name", c); + } + return 0; + } + if (!identchar(c)) { + pushback(p, c); + return '@'; + } + break; + + case '_': + token_column = newtok(p); + break; + + default: + if (!identchar(c)) { + yyerror_i(p, "Invalid char `\\x%02X' in expression", c); + goto retry; + } + + token_column = newtok(p); + break; + } + + do { + tokadd(p, c); + c = nextc(p); + if (c < 0) break; + } while (identchar(c)); + if (token_column == 0 && toklen(p) == 7 && (c < 0 || c == '\n') && + strncmp(tok(p), "__END__", toklen(p)) == 0) + return -1; + + switch (tok(p)[0]) { + case '@': case '$': + pushback(p, c); + break; + default: + if ((c == '!' || c == '?') && !peek(p, '=')) { + tokadd(p, c); + } + else { + pushback(p, c); + } + } + tokfix(p); + { + int result = 0; + + switch (tok(p)[0]) { + case '$': + p->lstate = EXPR_END; + result = tGVAR; + break; + case '@': + p->lstate = EXPR_END; + if (tok(p)[1] == '@') + result = tCVAR; + else + result = tIVAR; + break; + + default: + if (toklast(p) == '!' || toklast(p) == '?') { + result = tFID; + } + else { + if (p->lstate == EXPR_FNAME) { + if ((c = nextc(p)) == '=' && !peek(p, '~') && !peek(p, '>') && + (!peek(p, '=') || (peek_n(p, '>', 1)))) { + result = tIDENTIFIER; + tokadd(p, c); + tokfix(p); + } + else { + pushback(p, c); + } + } + if (result == 0 && ISUPPER(tok(p)[0])) { + result = tCONSTANT; + } + else { + result = tIDENTIFIER; + } + } + + if (IS_LABEL_POSSIBLE()) { + if (IS_LABEL_SUFFIX(0)) { + p->lstate = EXPR_BEG; + nextc(p); + tokfix(p); + yylval.id = intern_cstr(tok(p)); + return tLABEL; + } + } + if (p->lstate != EXPR_DOT) { + const struct kwtable *kw; + + /* See if it is a reserved word. */ + kw = mrb_reserved_word(tok(p), toklen(p)); + if (kw) { + enum mrb_lex_state_enum state = p->lstate; + yylval.num = p->lineno; + p->lstate = kw->state; + if (state == EXPR_FNAME) { + yylval.id = intern_cstr(kw->name); + return kw->id[0]; + } + if (p->lstate == EXPR_BEG) { + p->cmd_start = TRUE; + } + if (kw->id[0] == keyword_do) { + if (p->lpar_beg && p->lpar_beg == p->paren_nest) { + p->lpar_beg = 0; + p->paren_nest--; + return keyword_do_LAMBDA; + } + if (COND_P()) return keyword_do_cond; + if (CMDARG_P() && state != EXPR_CMDARG) + return keyword_do_block; + if (state == EXPR_ENDARG || state == EXPR_BEG) + return keyword_do_block; + return keyword_do; + } + if (state == EXPR_BEG || state == EXPR_VALUE) + return kw->id[0]; + else { + if (kw->id[0] != kw->id[1]) + p->lstate = EXPR_BEG; + return kw->id[1]; + } + } + } + + if (IS_BEG() || p->lstate == EXPR_DOT || IS_ARG()) { + if (cmd_state) { + p->lstate = EXPR_CMDARG; + } + else { + p->lstate = EXPR_ARG; + } + } + else if (p->lstate == EXPR_FNAME) { + p->lstate = EXPR_ENDFN; + } + else { + p->lstate = EXPR_END; + } + } + { + mrb_sym ident = intern_cstr(tok(p)); + + yylval.id = ident; +#if 0 + if (last_state != EXPR_DOT && islower(tok(p)[0]) && lvar_defined(ident)) { + p->lstate = EXPR_END; + } +#endif + } + return result; + } +} + +static int +yylex(void *lval, parser_state *p) +{ + p->ylval = lval; + return parser_yylex(p); +} + +static void +parser_init_cxt(parser_state *p, mrbc_context *cxt) +{ + if (!cxt) return; + if (cxt->filename) mrb_parser_set_filename(p, cxt->filename); + if (cxt->lineno) p->lineno = cxt->lineno; + if (cxt->syms) { + int i; + + p->locals = cons(0,0); + for (i=0; islen; i++) { + local_add_f(p, cxt->syms[i]); + } + } + p->capture_errors = cxt->capture_errors; + p->no_optimize = cxt->no_optimize; + if (cxt->partial_hook) { + p->cxt = cxt; + } +} + +static void +parser_update_cxt(parser_state *p, mrbc_context *cxt) +{ + node *n, *n0; + int i = 0; + + if (!cxt) return; + if ((int)(intptr_t)p->tree->car != NODE_SCOPE) return; + n0 = n = p->tree->cdr->car; + while (n) { + i++; + n = n->cdr; + } + cxt->syms = (mrb_sym *)mrb_realloc(p->mrb, cxt->syms, i*sizeof(mrb_sym)); + cxt->slen = i; + for (i=0, n=n0; n; i++,n=n->cdr) { + cxt->syms[i] = sym(n->car); + } +} + +void mrb_codedump_all(mrb_state*, struct RProc*); +void mrb_parser_dump(mrb_state *mrb, node *tree, int offset); + +MRB_API void +mrb_parser_parse(parser_state *p, mrbc_context *c) +{ + struct mrb_jmpbuf buf; + p->jmp = &buf; + + MRB_TRY(p->jmp) { + + p->cmd_start = TRUE; + p->in_def = p->in_single = 0; + p->nerr = p->nwarn = 0; + p->lex_strterm = NULL; + + parser_init_cxt(p, c); + yyparse(p); + if (!p->tree) { + p->tree = new_nil(p); + } + parser_update_cxt(p, c); + if (c && c->dump_result) { + mrb_parser_dump(p->mrb, p->tree, 0); + } + + } + MRB_CATCH(p->jmp) { + yyerror(p, "memory allocation error"); + p->nerr++; + p->tree = 0; + return; + } + MRB_END_EXC(p->jmp); +} + +MRB_API parser_state* +mrb_parser_new(mrb_state *mrb) +{ + mrb_pool *pool; + parser_state *p; + static const parser_state parser_state_zero = { 0 }; + + pool = mrb_pool_open(mrb); + if (!pool) return NULL; + p = (parser_state *)mrb_pool_alloc(pool, sizeof(parser_state)); + if (!p) return NULL; + + *p = parser_state_zero; + p->mrb = mrb; + p->pool = pool; + + p->s = p->send = NULL; +#ifdef ENABLE_STDIO + p->f = NULL; +#endif + + p->cmd_start = TRUE; + p->in_def = p->in_single = 0; + + p->capture_errors = FALSE; + p->lineno = 1; + p->column = 0; +#if defined(PARSER_TEST) || defined(PARSER_DEBUG) + yydebug = 1; +#endif + + p->lex_strterm = NULL; + p->all_heredocs = p->parsing_heredoc = NULL; + p->lex_strterm_before_heredoc = NULL; + + p->current_filename_index = -1; + p->filename_table = NULL; + p->filename_table_length = 0; + + return p; +} + +MRB_API void +mrb_parser_free(parser_state *p) { + mrb_pool_close(p->pool); +} + +MRB_API mrbc_context* +mrbc_context_new(mrb_state *mrb) +{ + return (mrbc_context *)mrb_calloc(mrb, 1, sizeof(mrbc_context)); +} + +MRB_API void +mrbc_context_free(mrb_state *mrb, mrbc_context *cxt) +{ + mrb_free(mrb, cxt->syms); + mrb_free(mrb, cxt); +} + +MRB_API const char* +mrbc_filename(mrb_state *mrb, mrbc_context *c, const char *s) +{ + if (s) { + int len = strlen(s); + char *p = (char *)mrb_alloca(mrb, len + 1); + + memcpy(p, s, len + 1); + c->filename = p; + } + return c->filename; +} + +MRB_API void +mrbc_partial_hook(mrb_state *mrb, mrbc_context *c, int (*func)(struct mrb_parser_state*), void *data) +{ + c->partial_hook = func; + c->partial_data = data; +} + +MRB_API void +mrb_parser_set_filename(struct mrb_parser_state *p, const char *f) +{ + mrb_sym sym; + size_t i; + mrb_sym* new_table; + + sym = mrb_intern_cstr(p->mrb, f); + p->filename = mrb_sym2name_len(p->mrb, sym, NULL); + p->lineno = (p->filename_table_length > 0)? 0 : 1; + + for (i = 0; i < p->filename_table_length; ++i) { + if (p->filename_table[i] == sym) { + p->current_filename_index = i; + return; + } + } + + p->current_filename_index = p->filename_table_length++; + + new_table = (mrb_sym*)parser_palloc(p, sizeof(mrb_sym) * p->filename_table_length); + if (p->filename_table) { + memmove(new_table, p->filename_table, sizeof(mrb_sym) * p->filename_table_length); + } + p->filename_table = new_table; + p->filename_table[p->filename_table_length - 1] = sym; +} + +MRB_API char const* +mrb_parser_get_filename(struct mrb_parser_state* p, uint16_t idx) { + if (idx >= p->filename_table_length) { return NULL; } + else { + return mrb_sym2name_len(p->mrb, p->filename_table[idx], NULL); + } +} + +#ifdef ENABLE_STDIO +MRB_API parser_state* +mrb_parse_file(mrb_state *mrb, FILE *f, mrbc_context *c) +{ + parser_state *p; + + p = mrb_parser_new(mrb); + if (!p) return NULL; + p->s = p->send = NULL; + p->f = f; + + mrb_parser_parse(p, c); + return p; +} +#endif + +MRB_API parser_state* +mrb_parse_nstring(mrb_state *mrb, const char *s, int len, mrbc_context *c) +{ + parser_state *p; + + p = mrb_parser_new(mrb); + if (!p) return NULL; + p->s = s; + p->send = s + len; + + mrb_parser_parse(p, c); + return p; +} + +MRB_API parser_state* +mrb_parse_string(mrb_state *mrb, const char *s, mrbc_context *c) +{ + return mrb_parse_nstring(mrb, s, strlen(s), c); +} + +static mrb_value +load_exec(mrb_state *mrb, parser_state *p, mrbc_context *c) +{ + struct RClass *target = mrb->object_class; + struct RProc *proc; + mrb_value v; + unsigned int keep = 0; + + if (!p) { + return mrb_undef_value(); + } + if (!p->tree || p->nerr) { + if (p->capture_errors) { + char buf[256]; + int n; + + n = snprintf(buf, sizeof(buf), "line %d: %s\n", + p->error_buffer[0].lineno, p->error_buffer[0].message); + mrb->exc = mrb_obj_ptr(mrb_exc_new(mrb, E_SYNTAX_ERROR, buf, n)); + mrb_parser_free(p); + return mrb_undef_value(); + } + else { + mrb->exc = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, E_SYNTAX_ERROR, "syntax error")); + mrb_parser_free(p); + return mrb_undef_value(); + } + } + proc = mrb_generate_code(mrb, p); + mrb_parser_free(p); + if (proc == NULL) { + mrb->exc = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, E_SCRIPT_ERROR, "codegen error")); + return mrb_undef_value(); + } + if (c) { + if (c->dump_result) mrb_codedump_all(mrb, proc); + if (c->no_exec) return mrb_obj_value(proc); + if (c->target_class) { + target = c->target_class; + } + if (c->keep_lv) { + keep = c->slen + 1; + } + else { + c->keep_lv = TRUE; + } + } + proc->target_class = target; + if (mrb->c->ci) { + mrb->c->ci->target_class = target; + } + v = mrb_toplevel_run_keep(mrb, proc, keep); + if (mrb->exc) return mrb_nil_value(); + return v; +} + +#ifdef ENABLE_STDIO +MRB_API mrb_value +mrb_load_file_cxt(mrb_state *mrb, FILE *f, mrbc_context *c) +{ + return load_exec(mrb, mrb_parse_file(mrb, f, c), c); +} + +MRB_API mrb_value +mrb_load_file(mrb_state *mrb, FILE *f) +{ + return mrb_load_file_cxt(mrb, f, NULL); +} +#endif + +MRB_API mrb_value +mrb_load_nstring_cxt(mrb_state *mrb, const char *s, int len, mrbc_context *c) +{ + return load_exec(mrb, mrb_parse_nstring(mrb, s, len, c), c); +} + +MRB_API mrb_value +mrb_load_nstring(mrb_state *mrb, const char *s, int len) +{ + return mrb_load_nstring_cxt(mrb, s, len, NULL); +} + +MRB_API mrb_value +mrb_load_string_cxt(mrb_state *mrb, const char *s, mrbc_context *c) +{ + return mrb_load_nstring_cxt(mrb, s, strlen(s), c); +} + +MRB_API mrb_value +mrb_load_string(mrb_state *mrb, const char *s) +{ + return mrb_load_string_cxt(mrb, s, NULL); +} + +#ifdef ENABLE_STDIO + +static void +dump_prefix(node *tree, int offset) +{ + printf("%05d ", tree->lineno); + while (offset--) { + putc(' ', stdout); + putc(' ', stdout); + } +} + +static void +dump_recur(mrb_state *mrb, node *tree, int offset) +{ + while (tree) { + mrb_parser_dump(mrb, tree->car, offset); + tree = tree->cdr; + } +} + +#endif + +void +mrb_parser_dump(mrb_state *mrb, node *tree, int offset) +{ +#ifdef ENABLE_STDIO + int nodetype; + + if (!tree) return; + again: + dump_prefix(tree, offset); + nodetype = (int)(intptr_t)tree->car; + tree = tree->cdr; + switch (nodetype) { + case NODE_BEGIN: + printf("NODE_BEGIN:\n"); + dump_recur(mrb, tree, offset+1); + break; + + case NODE_RESCUE: + printf("NODE_RESCUE:\n"); + if (tree->car) { + dump_prefix(tree, offset+1); + printf("body:\n"); + mrb_parser_dump(mrb, tree->car, offset+2); + } + tree = tree->cdr; + if (tree->car) { + node *n2 = tree->car; + + dump_prefix(n2, offset+1); + printf("rescue:\n"); + while (n2) { + node *n3 = n2->car; + if (n3->car) { + dump_prefix(n2, offset+2); + printf("handle classes:\n"); + dump_recur(mrb, n3->car, offset+3); + } + if (n3->cdr->car) { + dump_prefix(n3, offset+2); + printf("exc_var:\n"); + mrb_parser_dump(mrb, n3->cdr->car, offset+3); + } + if (n3->cdr->cdr->car) { + dump_prefix(n3, offset+2); + printf("rescue body:\n"); + mrb_parser_dump(mrb, n3->cdr->cdr->car, offset+3); + } + n2 = n2->cdr; + } + } + tree = tree->cdr; + if (tree->car) { + dump_prefix(tree, offset+1); + printf("else:\n"); + mrb_parser_dump(mrb, tree->car, offset+2); + } + break; + + case NODE_ENSURE: + printf("NODE_ENSURE:\n"); + dump_prefix(tree, offset+1); + printf("body:\n"); + mrb_parser_dump(mrb, tree->car, offset+2); + dump_prefix(tree, offset+1); + printf("ensure:\n"); + mrb_parser_dump(mrb, tree->cdr->cdr, offset+2); + break; + + case NODE_LAMBDA: + printf("NODE_BLOCK:\n"); + goto block; + + case NODE_BLOCK: + block: + printf("NODE_BLOCK:\n"); + tree = tree->cdr; + if (tree->car) { + node *n = tree->car; + + if (n->car) { + dump_prefix(n, offset+1); + printf("mandatory args:\n"); + dump_recur(mrb, n->car, offset+2); + } + n = n->cdr; + if (n->car) { + dump_prefix(n, offset+1); + printf("optional args:\n"); + { + node *n2 = n->car; + + while (n2) { + dump_prefix(n2, offset+2); + printf("%s=", mrb_sym2name(mrb, sym(n2->car->car))); + mrb_parser_dump(mrb, n2->car->cdr, 0); + n2 = n2->cdr; + } + } + } + n = n->cdr; + if (n->car) { + dump_prefix(n, offset+1); + printf("rest=*%s\n", mrb_sym2name(mrb, sym(n->car))); + } + n = n->cdr; + if (n->car) { + dump_prefix(n, offset+1); + printf("post mandatory args:\n"); + dump_recur(mrb, n->car, offset+2); + } + n = n->cdr; + if (n) { + dump_prefix(n, offset+1); + printf("blk=&%s\n", mrb_sym2name(mrb, sym(n))); + } + } + dump_prefix(tree, offset+1); + printf("body:\n"); + mrb_parser_dump(mrb, tree->cdr->car, offset+2); + break; + + case NODE_IF: + printf("NODE_IF:\n"); + dump_prefix(tree, offset+1); + printf("cond:\n"); + mrb_parser_dump(mrb, tree->car, offset+2); + dump_prefix(tree, offset+1); + printf("then:\n"); + mrb_parser_dump(mrb, tree->cdr->car, offset+2); + if (tree->cdr->cdr->car) { + dump_prefix(tree, offset+1); + printf("else:\n"); + mrb_parser_dump(mrb, tree->cdr->cdr->car, offset+2); + } + break; + + case NODE_AND: + printf("NODE_AND:\n"); + mrb_parser_dump(mrb, tree->car, offset+1); + mrb_parser_dump(mrb, tree->cdr, offset+1); + break; + + case NODE_OR: + printf("NODE_OR:\n"); + mrb_parser_dump(mrb, tree->car, offset+1); + mrb_parser_dump(mrb, tree->cdr, offset+1); + break; + + case NODE_CASE: + printf("NODE_CASE:\n"); + if (tree->car) { + mrb_parser_dump(mrb, tree->car, offset+1); + } + tree = tree->cdr; + while (tree) { + dump_prefix(tree, offset+1); + printf("case:\n"); + dump_recur(mrb, tree->car->car, offset+2); + dump_prefix(tree, offset+1); + printf("body:\n"); + mrb_parser_dump(mrb, tree->car->cdr, offset+2); + tree = tree->cdr; + } + break; + + case NODE_WHILE: + printf("NODE_WHILE:\n"); + dump_prefix(tree, offset+1); + printf("cond:\n"); + mrb_parser_dump(mrb, tree->car, offset+2); + dump_prefix(tree, offset+1); + printf("body:\n"); + mrb_parser_dump(mrb, tree->cdr, offset+2); + break; + + case NODE_UNTIL: + printf("NODE_UNTIL:\n"); + dump_prefix(tree, offset+1); + printf("cond:\n"); + mrb_parser_dump(mrb, tree->car, offset+2); + dump_prefix(tree, offset+1); + printf("body:\n"); + mrb_parser_dump(mrb, tree->cdr, offset+2); + break; + + case NODE_FOR: + printf("NODE_FOR:\n"); + dump_prefix(tree, offset+1); + printf("var:\n"); + { + node *n2 = tree->car; + + if (n2->car) { + dump_prefix(n2, offset+2); + printf("pre:\n"); + dump_recur(mrb, n2->car, offset+3); + } + n2 = n2->cdr; + if (n2) { + if (n2->car) { + dump_prefix(n2, offset+2); + printf("rest:\n"); + mrb_parser_dump(mrb, n2->car, offset+3); + } + n2 = n2->cdr; + if (n2) { + if (n2->car) { + dump_prefix(n2, offset+2); + printf("post:\n"); + dump_recur(mrb, n2->car, offset+3); + } + } + } + } + tree = tree->cdr; + dump_prefix(tree, offset+1); + printf("in:\n"); + mrb_parser_dump(mrb, tree->car, offset+2); + tree = tree->cdr; + dump_prefix(tree, offset+1); + printf("do:\n"); + mrb_parser_dump(mrb, tree->car, offset+2); + break; + + case NODE_SCOPE: + printf("NODE_SCOPE:\n"); + { + node *n2 = tree->car; + mrb_bool first_lval = TRUE; + + if (n2 && (n2->car || n2->cdr)) { + dump_prefix(n2, offset+1); + printf("local variables:\n"); + dump_prefix(n2, offset+2); + while (n2) { + if (n2->car) { + if (!first_lval) printf(", "); + printf("%s", mrb_sym2name(mrb, sym(n2->car))); + first_lval = FALSE; + } + n2 = n2->cdr; + } + printf("\n"); + } + } + tree = tree->cdr; + offset++; + goto again; + + case NODE_FCALL: + case NODE_CALL: + printf("NODE_CALL:\n"); + mrb_parser_dump(mrb, tree->car, offset+1); + dump_prefix(tree, offset+1); + printf("method='%s' (%d)\n", + mrb_sym2name(mrb, sym(tree->cdr->car)), + (int)(intptr_t)tree->cdr->car); + tree = tree->cdr->cdr->car; + if (tree) { + dump_prefix(tree, offset+1); + printf("args:\n"); + dump_recur(mrb, tree->car, offset+2); + if (tree->cdr) { + dump_prefix(tree, offset+1); + printf("block:\n"); + mrb_parser_dump(mrb, tree->cdr, offset+2); + } + } + break; + + case NODE_DOT2: + printf("NODE_DOT2:\n"); + mrb_parser_dump(mrb, tree->car, offset+1); + mrb_parser_dump(mrb, tree->cdr, offset+1); + break; + + case NODE_DOT3: + printf("NODE_DOT3:\n"); + mrb_parser_dump(mrb, tree->car, offset+1); + mrb_parser_dump(mrb, tree->cdr, offset+1); + break; + + case NODE_COLON2: + printf("NODE_COLON2:\n"); + mrb_parser_dump(mrb, tree->car, offset+1); + dump_prefix(tree, offset+1); + printf("::%s\n", mrb_sym2name(mrb, sym(tree->cdr))); + break; + + case NODE_COLON3: + printf("NODE_COLON3:\n"); + dump_prefix(tree, offset+1); + printf("::%s\n", mrb_sym2name(mrb, sym(tree))); + break; + + case NODE_ARRAY: + printf("NODE_ARRAY:\n"); + dump_recur(mrb, tree, offset+1); + break; + + case NODE_HASH: + printf("NODE_HASH:\n"); + while (tree) { + dump_prefix(tree, offset+1); + printf("key:\n"); + mrb_parser_dump(mrb, tree->car->car, offset+2); + dump_prefix(tree, offset+1); + printf("value:\n"); + mrb_parser_dump(mrb, tree->car->cdr, offset+2); + tree = tree->cdr; + } + break; + + case NODE_SPLAT: + printf("NODE_SPLAT:\n"); + mrb_parser_dump(mrb, tree, offset+1); + break; + + case NODE_ASGN: + printf("NODE_ASGN:\n"); + dump_prefix(tree, offset+1); + printf("lhs:\n"); + mrb_parser_dump(mrb, tree->car, offset+2); + dump_prefix(tree, offset+1); + printf("rhs:\n"); + mrb_parser_dump(mrb, tree->cdr, offset+2); + break; + + case NODE_MASGN: + printf("NODE_MASGN:\n"); + dump_prefix(tree, offset+1); + printf("mlhs:\n"); + { + node *n2 = tree->car; + + if (n2->car) { + dump_prefix(tree, offset+2); + printf("pre:\n"); + dump_recur(mrb, n2->car, offset+3); + } + n2 = n2->cdr; + if (n2) { + if (n2->car) { + dump_prefix(n2, offset+2); + printf("rest:\n"); + if (n2->car == (node*)-1) { + dump_prefix(n2, offset+2); + printf("(empty)\n"); + } + else { + mrb_parser_dump(mrb, n2->car, offset+3); + } + } + n2 = n2->cdr; + if (n2) { + if (n2->car) { + dump_prefix(n2, offset+2); + printf("post:\n"); + dump_recur(mrb, n2->car, offset+3); + } + } + } + } + dump_prefix(tree, offset+1); + printf("rhs:\n"); + mrb_parser_dump(mrb, tree->cdr, offset+2); + break; + + case NODE_OP_ASGN: + printf("NODE_OP_ASGN:\n"); + dump_prefix(tree, offset+1); + printf("lhs:\n"); + mrb_parser_dump(mrb, tree->car, offset+2); + tree = tree->cdr; + dump_prefix(tree, offset+1); + printf("op='%s' (%d)\n", mrb_sym2name(mrb, sym(tree->car)), (int)(intptr_t)tree->car); + tree = tree->cdr; + mrb_parser_dump(mrb, tree->car, offset+1); + break; + + case NODE_SUPER: + printf("NODE_SUPER:\n"); + if (tree) { + dump_prefix(tree, offset+1); + printf("args:\n"); + dump_recur(mrb, tree->car, offset+2); + if (tree->cdr) { + dump_prefix(tree, offset+1); + printf("block:\n"); + mrb_parser_dump(mrb, tree->cdr, offset+2); + } + } + break; + + case NODE_ZSUPER: + printf("NODE_ZSUPER\n"); + break; + + case NODE_RETURN: + printf("NODE_RETURN:\n"); + mrb_parser_dump(mrb, tree, offset+1); + break; + + case NODE_YIELD: + printf("NODE_YIELD:\n"); + dump_recur(mrb, tree, offset+1); + break; + + case NODE_BREAK: + printf("NODE_BREAK:\n"); + mrb_parser_dump(mrb, tree, offset+1); + break; + + case NODE_NEXT: + printf("NODE_NEXT:\n"); + mrb_parser_dump(mrb, tree, offset+1); + break; + + case NODE_REDO: + printf("NODE_REDO\n"); + break; + + case NODE_RETRY: + printf("NODE_RETRY\n"); + break; + + case NODE_LVAR: + printf("NODE_LVAR %s\n", mrb_sym2name(mrb, sym(tree))); + break; + + case NODE_GVAR: + printf("NODE_GVAR %s\n", mrb_sym2name(mrb, sym(tree))); + break; + + case NODE_IVAR: + printf("NODE_IVAR %s\n", mrb_sym2name(mrb, sym(tree))); + break; + + case NODE_CVAR: + printf("NODE_CVAR %s\n", mrb_sym2name(mrb, sym(tree))); + break; + + case NODE_CONST: + printf("NODE_CONST %s\n", mrb_sym2name(mrb, sym(tree))); + break; + + case NODE_MATCH: + printf("NODE_MATCH:\n"); + dump_prefix(tree, offset + 1); + printf("lhs:\n"); + mrb_parser_dump(mrb, tree->car, offset + 2); + dump_prefix(tree, offset + 1); + printf("rhs:\n"); + mrb_parser_dump(mrb, tree->cdr, offset + 2); + break; + + case NODE_BACK_REF: + printf("NODE_BACK_REF: $%c\n", (int)(intptr_t)tree); + break; + + case NODE_NTH_REF: + printf("NODE_NTH_REF: $%d\n", (int)(intptr_t)tree); + break; + + case NODE_ARG: + printf("NODE_ARG %s\n", mrb_sym2name(mrb, sym(tree))); + break; + + case NODE_BLOCK_ARG: + printf("NODE_BLOCK_ARG:\n"); + mrb_parser_dump(mrb, tree, offset+1); + break; + + case NODE_INT: + printf("NODE_INT %s base %d\n", (char*)tree->car, (int)(intptr_t)tree->cdr->car); + break; + + case NODE_FLOAT: + printf("NODE_FLOAT %s\n", (char*)tree); + break; + + case NODE_NEGATE: + printf("NODE_NEGATE\n"); + mrb_parser_dump(mrb, tree, offset+1); + break; + + case NODE_STR: + printf("NODE_STR \"%s\" len %d\n", (char*)tree->car, (int)(intptr_t)tree->cdr); + break; + + case NODE_DSTR: + printf("NODE_DSTR\n"); + dump_recur(mrb, tree, offset+1); + break; + + case NODE_XSTR: + printf("NODE_XSTR \"%s\" len %d\n", (char*)tree->car, (int)(intptr_t)tree->cdr); + break; + + case NODE_DXSTR: + printf("NODE_DXSTR\n"); + dump_recur(mrb, tree, offset+1); + break; + + case NODE_REGX: + printf("NODE_REGX /%s/%s\n", (char*)tree->car, (char*)tree->cdr); + break; + + case NODE_DREGX: + printf("NODE_DREGX\n"); + dump_recur(mrb, tree->car, offset+1); + dump_prefix(tree, offset); + printf("tail: %s\n", (char*)tree->cdr->cdr->car); + dump_prefix(tree, offset); + printf("opt: %s\n", (char*)tree->cdr->cdr->cdr); + break; + + case NODE_SYM: + printf("NODE_SYM :%s\n", mrb_sym2name(mrb, sym(tree))); + break; + + case NODE_SELF: + printf("NODE_SELF\n"); + break; + + case NODE_NIL: + printf("NODE_NIL\n"); + break; + + case NODE_TRUE: + printf("NODE_TRUE\n"); + break; + + case NODE_FALSE: + printf("NODE_FALSE\n"); + break; + + case NODE_ALIAS: + printf("NODE_ALIAS %s %s:\n", + mrb_sym2name(mrb, sym(tree->car)), + mrb_sym2name(mrb, sym(tree->cdr))); + break; + + case NODE_UNDEF: + printf("NODE_UNDEF"); + { + node *t = tree; + while (t) { + printf(" %s", mrb_sym2name(mrb, sym(t->car))); + t = t->cdr; + } + } + printf(":\n"); + break; + + case NODE_CLASS: + printf("NODE_CLASS:\n"); + if (tree->car->car == (node*)0) { + dump_prefix(tree, offset+1); + printf(":%s\n", mrb_sym2name(mrb, sym(tree->car->cdr))); + } + else if (tree->car->car == (node*)1) { + dump_prefix(tree, offset+1); + printf("::%s\n", mrb_sym2name(mrb, sym(tree->car->cdr))); + } + else { + mrb_parser_dump(mrb, tree->car->car, offset+1); + dump_prefix(tree, offset+1); + printf("::%s\n", mrb_sym2name(mrb, sym(tree->car->cdr))); + } + if (tree->cdr->car) { + dump_prefix(tree, offset+1); + printf("super:\n"); + mrb_parser_dump(mrb, tree->cdr->car, offset+2); + } + dump_prefix(tree, offset+1); + printf("body:\n"); + mrb_parser_dump(mrb, tree->cdr->cdr->car->cdr, offset+2); + break; + + case NODE_MODULE: + printf("NODE_MODULE:\n"); + if (tree->car->car == (node*)0) { + dump_prefix(tree, offset+1); + printf(":%s\n", mrb_sym2name(mrb, sym(tree->car->cdr))); + } + else if (tree->car->car == (node*)1) { + dump_prefix(tree, offset+1); + printf("::%s\n", mrb_sym2name(mrb, sym(tree->car->cdr))); + } + else { + mrb_parser_dump(mrb, tree->car->car, offset+1); + dump_prefix(tree, offset+1); + printf("::%s\n", mrb_sym2name(mrb, sym(tree->car->cdr))); + } + dump_prefix(tree, offset+1); + printf("body:\n"); + mrb_parser_dump(mrb, tree->cdr->car->cdr, offset+2); + break; + + case NODE_SCLASS: + printf("NODE_SCLASS:\n"); + mrb_parser_dump(mrb, tree->car, offset+1); + dump_prefix(tree, offset+1); + printf("body:\n"); + mrb_parser_dump(mrb, tree->cdr->car->cdr, offset+2); + break; + + case NODE_DEF: + printf("NODE_DEF:\n"); + dump_prefix(tree, offset+1); + printf("%s\n", mrb_sym2name(mrb, sym(tree->car))); + tree = tree->cdr; + { + node *n2 = tree->car; + mrb_bool first_lval = TRUE; + + if (n2 && (n2->car || n2->cdr)) { + dump_prefix(n2, offset+1); + printf("local variables:\n"); + dump_prefix(n2, offset+2); + while (n2) { + if (n2->car) { + if (!first_lval) printf(", "); + printf("%s", mrb_sym2name(mrb, sym(n2->car))); + first_lval = FALSE; + } + n2 = n2->cdr; + } + printf("\n"); + } + } + tree = tree->cdr; + if (tree->car) { + node *n = tree->car; + + if (n->car) { + dump_prefix(n, offset+1); + printf("mandatory args:\n"); + dump_recur(mrb, n->car, offset+2); + } + n = n->cdr; + if (n->car) { + dump_prefix(n, offset+1); + printf("optional args:\n"); + { + node *n2 = n->car; + + while (n2) { + dump_prefix(n2, offset+2); + printf("%s=", mrb_sym2name(mrb, sym(n2->car->car))); + mrb_parser_dump(mrb, n2->car->cdr, 0); + n2 = n2->cdr; + } + } + } + n = n->cdr; + if (n->car) { + dump_prefix(n, offset+1); + printf("rest=*%s\n", mrb_sym2name(mrb, sym(n->car))); + } + n = n->cdr; + if (n->car) { + dump_prefix(n, offset+1); + printf("post mandatory args:\n"); + dump_recur(mrb, n->car, offset+2); + } + if (n->cdr) { + dump_prefix(n, offset+1); + printf("blk=&%s\n", mrb_sym2name(mrb, sym(n->cdr))); + } + } + mrb_parser_dump(mrb, tree->cdr->car, offset+1); + break; + + case NODE_SDEF: + printf("NODE_SDEF:\n"); + mrb_parser_dump(mrb, tree->car, offset+1); + tree = tree->cdr; + dump_prefix(tree, offset+1); + printf(":%s\n", mrb_sym2name(mrb, sym(tree->car))); + tree = tree->cdr->cdr; + if (tree->car) { + node *n = tree->car; + + if (n->car) { + dump_prefix(n, offset+1); + printf("mandatory args:\n"); + dump_recur(mrb, n->car, offset+2); + } + n = n->cdr; + if (n->car) { + dump_prefix(n, offset+1); + printf("optional args:\n"); + { + node *n2 = n->car; + + while (n2) { + dump_prefix(n2, offset+2); + printf("%s=", mrb_sym2name(mrb, sym(n2->car->car))); + mrb_parser_dump(mrb, n2->car->cdr, 0); + n2 = n2->cdr; + } + } + } + n = n->cdr; + if (n->car) { + dump_prefix(n, offset+1); + printf("rest=*%s\n", mrb_sym2name(mrb, sym(n->car))); + } + n = n->cdr; + if (n->car) { + dump_prefix(n, offset+1); + printf("post mandatory args:\n"); + dump_recur(mrb, n->car, offset+2); + } + n = n->cdr; + if (n) { + dump_prefix(n, offset+1); + printf("blk=&%s\n", mrb_sym2name(mrb, sym(n))); + } + } + tree = tree->cdr; + mrb_parser_dump(mrb, tree->car, offset+1); + break; + + case NODE_POSTEXE: + printf("NODE_POSTEXE:\n"); + mrb_parser_dump(mrb, tree, offset+1); + break; + + case NODE_HEREDOC: + printf("NODE_HEREDOC:\n"); + mrb_parser_dump(mrb, ((parser_heredoc_info*)tree)->doc, offset+1); + break; + + default: + printf("node type: %d (0x%x)\n", nodetype, (unsigned)nodetype); + break; + } +#endif +} diff --git a/mrbgems/mruby-compiler/mrbgem.rake b/mrbgems/mruby-compiler/mrbgem.rake new file mode 100644 index 000000000..3a22762fa --- /dev/null +++ b/mrbgems/mruby-compiler/mrbgem.rake @@ -0,0 +1,40 @@ +MRuby::Gem::Specification.new 'mruby-compiler' do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' + spec.summary = 'mruby compiler library' + + current_dir = spec.dir + current_build_dir = spec.build_dir + + lex_def = "#{current_dir}/core/lex.def" + core_objs = Dir.glob("#{current_dir}/core/*.c").map { |f| + next nil if build.cxx_abi_enabled? and f =~ /(codegen).c$/ + objfile(f.pathmap("#{current_build_dir}/core/%n")) + }.compact + + if build.cxx_abi_enabled? + core_objs << + build.compile_as_cxx("#{current_build_dir}/core/y.tab.c", "#{current_build_dir}/core/y.tab.cxx", + objfile("#{current_build_dir}/y.tab"), ["#{current_dir}/core"]) << + build.compile_as_cxx("#{current_dir}/core/codegen.c", "#{current_build_dir}/core/codegen.cxx") + else + core_objs << objfile("#{current_build_dir}/core/y.tab") + file objfile("#{current_build_dir}/core/y.tab") => "#{current_build_dir}/core/y.tab.c" do |t| + cc.run t.name, t.prerequisites.first, [], ["#{current_dir}/core"] + end + end + file objfile("#{current_build_dir}/core/y.tab") => lex_def + + # Parser + file "#{current_build_dir}/core/y.tab.c" => ["#{current_dir}/core/parse.y"] do |t| + yacc.run t.name, t.prerequisites.first + end + + # Lexical analyzer + file lex_def => "#{current_dir}/core/keywords" do |t| + gperf.run t.name, t.prerequisites.first + end + + file libfile("#{build.build_dir}/lib/libmruby_core") => core_objs + build.libmruby << core_objs +end diff --git a/mrbgems/mruby-eval/mrbgem.rake b/mrbgems/mruby-eval/mrbgem.rake index 7c6acc534..cb8835b32 100644 --- a/mrbgems/mruby-eval/mrbgem.rake +++ b/mrbgems/mruby-eval/mrbgem.rake @@ -2,4 +2,6 @@ MRuby::Gem::Specification.new('mruby-eval') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'standard Kernel#eval method' + + add_dependency 'mruby-compiler', :core => 'mruby-compiler' end diff --git a/src/codegen.c b/src/codegen.c deleted file mode 100644 index 16233347c..000000000 --- a/src/codegen.c +++ /dev/null @@ -1,2720 +0,0 @@ -/* -** codegen.c - mruby code generator -** -** See Copyright Notice in mruby.h -*/ - -#include -#include -#include -#include -#include "mruby.h" -#include "mruby/compile.h" -#include "mruby/proc.h" -#include "mruby/numeric.h" -#include "mruby/string.h" -#include "mruby/debug.h" -#include "node.h" -#include "mruby/opcode.h" -#include "mruby/re.h" -#include "mruby/throw.h" - -typedef mrb_ast_node node; -typedef struct mrb_parser_state parser_state; - -enum looptype { - LOOP_NORMAL, - LOOP_BLOCK, - LOOP_FOR, - LOOP_BEGIN, - LOOP_RESCUE, -}; - -struct loopinfo { - enum looptype type; - int pc1, pc2, pc3, acc; - int ensure_level; - struct loopinfo *prev; -}; - -typedef struct scope { - mrb_state *mrb; - mrb_pool *mpool; - struct mrb_jmpbuf jmp; - - struct scope *prev; - - node *lv; - - int sp; - int pc; - int lastlabel; - int ainfo:15; - mrb_bool mscope:1; - - struct loopinfo *loop; - int ensure_level; - char const *filename; - uint16_t lineno; - - mrb_code *iseq; - uint16_t *lines; - int icapa; - - mrb_irep *irep; - size_t pcapa; - size_t scapa; - size_t rcapa; - - uint16_t nlocals; - uint16_t nregs; - int ai; - - int debug_start_pos; - uint16_t filename_index; - parser_state* parser; -} codegen_scope; - -static codegen_scope* scope_new(mrb_state *mrb, codegen_scope *prev, node *lv); -static void scope_finish(codegen_scope *s); -static struct loopinfo *loop_push(codegen_scope *s, enum looptype t); -static void loop_break(codegen_scope *s, node *tree); -static void loop_pop(codegen_scope *s, int val); - -static void gen_assignment(codegen_scope *s, node *tree, int sp, int val); -static void gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val); - -static void codegen(codegen_scope *s, node *tree, int val); -static void raise_error(codegen_scope *s, const char *msg); - -static void -codegen_error(codegen_scope *s, const char *message) -{ - if (!s) return; - while (s->prev) { - codegen_scope *tmp = s->prev; - mrb_pool_close(s->mpool); - s = tmp; - } -#ifdef ENABLE_STDIO - if (s->filename && s->lineno) { - fprintf(stderr, "codegen error:%s:%d: %s\n", s->filename, s->lineno, message); - } - else { - fprintf(stderr, "codegen error: %s\n", message); - } -#endif - MRB_THROW(&s->jmp); -} - -static void* -codegen_palloc(codegen_scope *s, size_t len) -{ - void *p = mrb_pool_alloc(s->mpool, len); - - if (!p) codegen_error(s, "pool memory allocation"); - return p; -} - -static void* -codegen_malloc(codegen_scope *s, size_t len) -{ - void *p = mrb_malloc_simple(s->mrb, len); - - if (!p) codegen_error(s, "mrb_malloc"); - return p; -} - -static void* -codegen_realloc(codegen_scope *s, void *p, size_t len) -{ - p = mrb_realloc_simple(s->mrb, p, len); - - if (!p && len > 0) codegen_error(s, "mrb_realloc"); - return p; -} - -static int -new_label(codegen_scope *s) -{ - s->lastlabel = s->pc; - return s->pc; -} - -static inline int -genop(codegen_scope *s, mrb_code i) -{ - if (s->pc == s->icapa) { - s->icapa *= 2; - s->iseq = (mrb_code *)codegen_realloc(s, s->iseq, sizeof(mrb_code)*s->icapa); - if (s->lines) { - s->lines = (uint16_t*)codegen_realloc(s, s->lines, sizeof(short)*s->icapa); - s->irep->lines = s->lines; - } - } - s->iseq[s->pc] = i; - if (s->lines) { - s->lines[s->pc] = s->lineno; - } - return s->pc++; -} - -#define NOVAL 0 -#define VAL 1 - -static mrb_bool -no_optimize(codegen_scope *s) -{ - if (s && s->parser && s->parser->no_optimize) - return TRUE; - return FALSE; -} - -static int -genop_peep(codegen_scope *s, mrb_code i, int val) -{ - /* peephole optimization */ - if (!no_optimize(s) && s->lastlabel != s->pc && s->pc > 0) { - mrb_code i0 = s->iseq[s->pc-1]; - int c1 = GET_OPCODE(i); - int c0 = GET_OPCODE(i0); - - switch (c1) { - case OP_MOVE: - if (GETARG_A(i) == GETARG_B(i)) { - /* skip useless OP_MOVE */ - return 0; - } - if (val) break; - switch (c0) { - case OP_MOVE: - if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i) == GETARG_B(i0)) { - /* skip swapping OP_MOVE */ - return 0; - } - if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) { - s->pc--; - return genop_peep(s, MKOP_AB(OP_MOVE, GETARG_A(i), GETARG_B(i0)), val); - } - break; - case OP_LOADI: - if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) { - s->iseq[s->pc-1] = MKOP_AsBx(OP_LOADI, GETARG_A(i), GETARG_sBx(i0)); - return 0; - } - break; - case OP_ARRAY: - case OP_HASH: - case OP_RANGE: - case OP_AREF: - case OP_GETUPVAR: - if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) { - s->iseq[s->pc-1] = MKOP_ABC(c0, GETARG_A(i), GETARG_B(i0), GETARG_C(i0)); - return 0; - } - break; - case OP_LOADSYM: - case OP_GETGLOBAL: - case OP_GETIV: - case OP_GETCV: - case OP_GETCONST: - case OP_GETSPECIAL: - case OP_LOADL: - case OP_STRING: - if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) { - s->iseq[s->pc-1] = MKOP_ABx(c0, GETARG_A(i), GETARG_Bx(i0)); - return 0; - } - break; - case OP_SCLASS: - if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) { - s->iseq[s->pc-1] = MKOP_AB(c0, GETARG_A(i), GETARG_B(i0)); - return 0; - } - break; - case OP_LOADNIL: - case OP_LOADSELF: - case OP_LOADT: - case OP_LOADF: - case OP_OCLASS: - if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) { - s->iseq[s->pc-1] = MKOP_A(c0, GETARG_A(i)); - return 0; - } - break; - default: - break; - } - break; - case OP_SETIV: - case OP_SETCV: - case OP_SETCONST: - case OP_SETMCNST: - case OP_SETGLOBAL: - if (val) break; - if (c0 == OP_MOVE) { - if (GETARG_A(i) == GETARG_A(i0)) { - s->iseq[s->pc-1] = MKOP_ABx(c1, GETARG_B(i0), GETARG_Bx(i)); - return 0; - } - } - break; - case OP_SETUPVAR: - if (val) break; - if (c0 == OP_MOVE) { - if (GETARG_A(i) == GETARG_A(i0)) { - s->iseq[s->pc-1] = MKOP_ABC(c1, GETARG_B(i0), GETARG_B(i), GETARG_C(i)); - return 0; - } - } - break; - case OP_EPOP: - if (c0 == OP_EPOP) { - s->iseq[s->pc-1] = MKOP_A(OP_EPOP, GETARG_A(i0)+GETARG_A(i)); - return 0; - } - break; - case OP_POPERR: - if (c0 == OP_POPERR) { - s->iseq[s->pc-1] = MKOP_A(OP_POPERR, GETARG_A(i0)+GETARG_A(i)); - return 0; - } - break; - case OP_RETURN: - switch (c0) { - case OP_RETURN: - return 0; - case OP_MOVE: - if (GETARG_A(i0) >= s->nlocals) { - s->iseq[s->pc-1] = MKOP_AB(OP_RETURN, GETARG_B(i0), OP_R_NORMAL); - return 0; - } - break; - case OP_SETIV: - case OP_SETCV: - case OP_SETCONST: - case OP_SETMCNST: - case OP_SETUPVAR: - case OP_SETGLOBAL: - s->pc--; - genop_peep(s, i0, NOVAL); - i0 = s->iseq[s->pc-1]; - return genop(s, MKOP_AB(OP_RETURN, GETARG_A(i0), OP_R_NORMAL)); -#if 0 - case OP_SEND: - if (GETARG_B(i) == OP_R_NORMAL && GETARG_A(i) == GETARG_A(i0)) { - s->iseq[s->pc-1] = MKOP_ABC(OP_TAILCALL, GETARG_A(i0), GETARG_B(i0), GETARG_C(i0)); - return; - } - break; -#endif - default: - break; - } - break; - case OP_ADD: - case OP_SUB: - if (c0 == OP_LOADI) { - int c = GETARG_sBx(i0); - - if (c1 == OP_SUB) c = -c; - if (c > 127 || c < -127) break; - if (0 <= c) - s->iseq[s->pc-1] = MKOP_ABC(OP_ADDI, GETARG_A(i), GETARG_B(i), c); - else - s->iseq[s->pc-1] = MKOP_ABC(OP_SUBI, GETARG_A(i), GETARG_B(i), -c); - return 0; - } - case OP_STRCAT: - if (c0 == OP_STRING) { - mrb_value v = s->irep->pool[GETARG_Bx(i0)]; - - if (mrb_string_p(v) && RSTRING_LEN(v) == 0) { - s->pc--; - return 0; - } - } - break; - case OP_JMPIF: - case OP_JMPNOT: - if (c0 == OP_MOVE && GETARG_A(i) == GETARG_A(i0)) { - s->iseq[s->pc-1] = MKOP_AsBx(c1, GETARG_B(i0), GETARG_sBx(i)); - return s->pc-1; - } - break; - default: - break; - } - } - return genop(s, i); -} - -static void -scope_error(codegen_scope *s) -{ - exit(EXIT_FAILURE); -} - -static inline void -dispatch(codegen_scope *s, int pc) -{ - int diff = s->pc - pc; - mrb_code i = s->iseq[pc]; - int c = GET_OPCODE(i); - - s->lastlabel = s->pc; - switch (c) { - case OP_JMP: - case OP_JMPIF: - case OP_JMPNOT: - case OP_ONERR: - break; - default: -#ifdef ENABLE_STDIO - fprintf(stderr, "bug: dispatch on non JMP op\n"); -#endif - scope_error(s); - break; - } - s->iseq[pc] = MKOP_AsBx(c, GETARG_A(i), diff); -} - -static void -dispatch_linked(codegen_scope *s, int pc) -{ - mrb_code i; - int pos; - - if (!pc) return; - for (;;) { - i = s->iseq[pc]; - pos = GETARG_sBx(i); - dispatch(s, pc); - if (!pos) break; - pc = pos; - } -} - -#define nregs_update do {if (s->sp > s->nregs) s->nregs = s->sp;} while (0) -static void -push_(codegen_scope *s) -{ - if (s->sp > 511) { - codegen_error(s, "too complex expression"); - } - s->sp++; - nregs_update; -} - -#define push() push_(s) -#define pop_(s) ((s)->sp--) -#define pop() pop_(s) -#define pop_n(n) (s->sp-=(n)) -#define cursp() (s->sp) - -static inline int -new_lit(codegen_scope *s, mrb_value val) -{ - size_t i; - mrb_value *pv; - - switch (mrb_type(val)) { - case MRB_TT_STRING: - for (i=0; iirep->plen; i++) { - mrb_int len; - pv = &s->irep->pool[i]; - - if (mrb_type(*pv) != MRB_TT_STRING) continue; - if ((len = RSTRING_LEN(*pv)) != RSTRING_LEN(val)) continue; - if (memcmp(RSTRING_PTR(*pv), RSTRING_PTR(val), len) == 0) - return i; - } - break; - case MRB_TT_FLOAT: - for (i=0; iirep->plen; i++) { - pv = &s->irep->pool[i]; - if (mrb_type(*pv) != MRB_TT_FLOAT) continue; - if (mrb_float(*pv) == mrb_float(val)) return i; - } - break; - case MRB_TT_FIXNUM: - for (i=0; iirep->plen; i++) { - pv = &s->irep->pool[i]; - if (!mrb_fixnum_p(*pv)) continue; - if (mrb_fixnum(*pv) == mrb_fixnum(val)) return i; - } - break; - default: - /* should not happen */ - return 0; - } - - if (s->irep->plen == s->pcapa) { - s->pcapa *= 2; - s->irep->pool = (mrb_value *)codegen_realloc(s, s->irep->pool, sizeof(mrb_value)*s->pcapa); - } - - pv = &s->irep->pool[s->irep->plen]; - i = s->irep->plen++; - - switch (mrb_type(val)) { - case MRB_TT_STRING: - *pv = mrb_str_pool(s->mrb, val); - break; - - case MRB_TT_FLOAT: -#ifdef MRB_WORD_BOXING - *pv = mrb_float_pool(s->mrb, mrb_float(val)); - break; -#endif - case MRB_TT_FIXNUM: - *pv = val; - break; - - default: - /* should not happen */ - break; - } - return i; -} - -static inline int -new_msym(codegen_scope *s, mrb_sym sym) -{ - size_t i, len; - - mrb_assert(s->irep); - - len = s->irep->slen; - if (len > 256) len = 256; - for (i=0; iirep->syms[i] == sym) return i; - if (s->irep->syms[i] == 0) break; - } - if (i == 256) { - codegen_error(s, "too many symbols (max 256)"); - } - s->irep->syms[i] = sym; - if (i == s->irep->slen) s->irep->slen++; - return i; -} - -static inline int -new_sym(codegen_scope *s, mrb_sym sym) -{ - size_t i; - - for (i=0; iirep->slen; i++) { - if (s->irep->syms[i] == sym) return i; - } - if (s->irep->slen > 125 && s->irep->slen < 256) { - s->irep->syms = (mrb_sym *)codegen_realloc(s, s->irep->syms, sizeof(mrb_sym)*65536); - for (i = 0; i < 256 - s->irep->slen; i++) { - static const mrb_sym mrb_sym_zero = { 0 }; - s->irep->syms[i + s->irep->slen] = mrb_sym_zero; - } - s->irep->slen = 256; - } - s->irep->syms[s->irep->slen] = sym; - return s->irep->slen++; -} - -static int -node_len(node *tree) -{ - int n = 0; - - while (tree) { - n++; - tree = tree->cdr; - } - return n; -} - -#define sym(x) ((mrb_sym)(intptr_t)(x)) -#define lv_name(lv) sym((lv)->car) -static int -lv_idx(codegen_scope *s, mrb_sym id) -{ - node *lv = s->lv; - int n = 1; - - while (lv) { - if (lv_name(lv) == id) return n; - n++; - lv = lv->cdr; - } - return 0; -} - -static void -for_body(codegen_scope *s, node *tree) -{ - codegen_scope *prev = s; - int idx; - struct loopinfo *lp; - node *n2; - mrb_code c; - - /* generate receiver */ - codegen(s, tree->cdr->car, VAL); - /* generate loop-block */ - s = scope_new(s->mrb, s, NULL); - if (s == NULL) { - raise_error(prev, "unexpected scope"); - } - - push(); /* push for a block parameter */ - - lp = loop_push(s, LOOP_FOR); - lp->pc1 = new_label(s); - - /* generate loop variable */ - n2 = tree->car; - genop(s, MKOP_Ax(OP_ENTER, 0x40000)); - if (n2->car && !n2->car->cdr && !n2->cdr) { - gen_assignment(s, n2->car->car, 1, NOVAL); - } - else { - gen_vmassignment(s, n2, 1, VAL); - } - codegen(s, tree->cdr->cdr->car, VAL); - pop(); - if (s->pc > 0) { - c = s->iseq[s->pc-1]; - if (GET_OPCODE(c) != OP_RETURN || GETARG_B(c) != OP_R_NORMAL || s->pc == s->lastlabel) - genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL); - } - loop_pop(s, NOVAL); - scope_finish(s); - s = prev; - genop(s, MKOP_Abc(OP_LAMBDA, cursp(), s->irep->rlen-1, OP_L_BLOCK)); - pop(); - idx = new_msym(s, mrb_intern_lit(s->mrb, "each")); - genop(s, MKOP_ABC(OP_SENDB, cursp(), idx, 0)); -} - -static int -lambda_body(codegen_scope *s, node *tree, int blk) -{ - mrb_code c; - codegen_scope *parent = s; - s = scope_new(s->mrb, s, tree->car); - if (s == NULL) { - raise_error(parent, "unexpected scope"); - } - - s->mscope = !blk; - - if (blk) { - struct loopinfo *lp = loop_push(s, LOOP_BLOCK); - lp->pc1 = new_label(s); - } - tree = tree->cdr; - if (tree->car) { - mrb_aspec a; - int ma, oa, ra, pa, ka, kd, ba; - int pos, i; - node *n, *opt; - - ma = node_len(tree->car->car); - n = tree->car->car; - while (n) { - n = n->cdr; - } - oa = node_len(tree->car->cdr->car); - ra = tree->car->cdr->cdr->car ? 1 : 0; - pa = node_len(tree->car->cdr->cdr->cdr->car); - ka = kd = 0; - ba = tree->car->cdr->cdr->cdr->cdr ? 1 : 0; - - a = ((mrb_aspec)(ma & 0x1f) << 18) - | ((mrb_aspec)(oa & 0x1f) << 13) - | ((ra & 1) << 12) - | ((pa & 0x1f) << 7) - | ((ka & 0x1f) << 2) - | ((kd & 1)<< 1) - | (ba & 1); - s->ainfo = (((ma+oa) & 0x3f) << 6) /* (12bits = 6:1:5) */ - | ((ra & 1) << 5) - | (pa & 0x1f); - genop(s, MKOP_Ax(OP_ENTER, a)); - pos = new_label(s); - for (i=0; i 0) { - genop(s, MKOP_sBx(OP_JMP, 0)); - } - opt = tree->car->cdr->car; - i = 0; - while (opt) { - int idx; - - dispatch(s, pos+i); - codegen(s, opt->car->cdr, VAL); - idx = lv_idx(s, (mrb_sym)(intptr_t)opt->car->car); - pop(); - genop_peep(s, MKOP_AB(OP_MOVE, idx, cursp()), NOVAL); - i++; - opt = opt->cdr; - } - if (oa > 0) { - dispatch(s, pos+i); - } - } - codegen(s, tree->cdr->car, VAL); - pop(); - if (s->pc > 0) { - c = s->iseq[s->pc-1]; - if (GET_OPCODE(c) != OP_RETURN || GETARG_B(c) != OP_R_NORMAL || s->pc == s->lastlabel) { - if (s->nregs == 0) { - genop(s, MKOP_A(OP_LOADNIL, 0)); - genop(s, MKOP_AB(OP_RETURN, 0, OP_R_NORMAL)); - } - else { - genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL); - } - } - } - if (blk) { - loop_pop(s, NOVAL); - } - scope_finish(s); - return parent->irep->rlen - 1; -} - -static int -scope_body(codegen_scope *s, node *tree, int val) -{ - codegen_scope *scope = scope_new(s->mrb, s, tree->car); - if (scope == NULL) { - raise_error(s, "unexpected scope"); - } - - codegen(scope, tree->cdr, VAL); - if (!s->iseq) { - genop(scope, MKOP_A(OP_STOP, 0)); - } - else if (!val) { - genop(scope, MKOP_AB(OP_RETURN, 0, OP_R_NORMAL)); - } - else { - if (scope->nregs == 0) { - genop(scope, MKOP_A(OP_LOADNIL, 0)); - genop(scope, MKOP_AB(OP_RETURN, 0, OP_R_NORMAL)); - } - else { - genop_peep(scope, MKOP_AB(OP_RETURN, scope->sp-1, OP_R_NORMAL), NOVAL); - } - } - scope_finish(scope); - if (!s->irep) { - /* should not happen */ - return 0; - } - return s->irep->rlen - 1; -} - -static mrb_bool -nosplat(node *t) -{ - while (t) { - if ((intptr_t)t->car->car == NODE_SPLAT) return FALSE; - t = t->cdr; - } - return TRUE; -} - -static mrb_sym -attrsym(codegen_scope *s, mrb_sym a) -{ - const char *name; - mrb_int len; - char *name2; - - name = mrb_sym2name_len(s->mrb, a, &len); - name2 = (char *)codegen_palloc(s, - (size_t)len - + 1 /* '=' */ - + 1 /* '\0' */ - ); - mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX); - memcpy(name2, name, (size_t)len); - name2[len] = '='; - name2[len+1] = '\0'; - - return mrb_intern(s->mrb, name2, len+1); -} - -static int -gen_values(codegen_scope *s, node *t, int val) -{ - int n = 0; - int is_splat; - - while (t) { - is_splat = (intptr_t)t->car->car == NODE_SPLAT; /* splat mode */ - if (n >= 127 || is_splat) { - if (val) { - pop_n(n); - genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), n)); - push(); - codegen(s, t->car, VAL); - pop(); pop(); - if (is_splat) { - genop(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1)); - } - else { - genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1)); - } - t = t->cdr; - while (t) { - push(); - codegen(s, t->car, VAL); - pop(); pop(); - if ((intptr_t)t->car->car == NODE_SPLAT) { - genop(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1)); - } - else { - genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1)); - } - t = t->cdr; - } - } - else { - codegen(s, t->car->cdr, NOVAL); - t = t->cdr; - while (t) { - codegen(s, t->car, NOVAL); - t = t->cdr; - } - } - return -1; - } - /* normal (no splat) mode */ - codegen(s, t->car, val); - n++; - t = t->cdr; - } - return n; -} - -#define CALL_MAXARGS 127 - -static void -gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val) -{ - mrb_sym sym = name ? name : sym(tree->cdr->car); - int idx; - int n = 0, noop = 0, sendv = 0, blk = 0; - - codegen(s, tree->car, VAL); /* receiver */ - idx = new_msym(s, sym); - tree = tree->cdr->cdr->car; - if (tree) { - n = gen_values(s, tree->car, VAL); - if (n < 0) { - n = noop = sendv = 1; - push(); - } - } - if (sp) { - if (sendv) { - pop(); - genop(s, MKOP_AB(OP_ARYPUSH, cursp(), sp)); - push(); - } - else { - genop(s, MKOP_AB(OP_MOVE, cursp(), sp)); - push(); - n++; - } - } - if (tree && tree->cdr) { - noop = 1; - codegen(s, tree->cdr, VAL); - pop(); - } - else { - blk = cursp(); - } - push();pop(); - pop_n(n+1); - { - mrb_int symlen; - const char *symname = mrb_sym2name_len(s->mrb, sym, &symlen); - - if (!noop && symlen == 1 && symname[0] == '+') { - genop_peep(s, MKOP_ABC(OP_ADD, cursp(), idx, n), val); - } - else if (!noop && symlen == 1 && symname[0] == '-') { - genop_peep(s, MKOP_ABC(OP_SUB, cursp(), idx, n), val); - } - else if (!noop && symlen == 1 && symname[0] == '*') { - genop(s, MKOP_ABC(OP_MUL, cursp(), idx, n)); - } - else if (!noop && symlen == 1 && symname[0] == '/') { - genop(s, MKOP_ABC(OP_DIV, cursp(), idx, n)); - } - else if (!noop && symlen == 1 && symname[0] == '<') { - genop(s, MKOP_ABC(OP_LT, cursp(), idx, n)); - } - else if (!noop && symlen == 2 && symname[0] == '<' && symname[1] == '=') { - genop(s, MKOP_ABC(OP_LE, cursp(), idx, n)); - } - else if (!noop && symlen == 1 && symname[0] == '>') { - genop(s, MKOP_ABC(OP_GT, cursp(), idx, n)); - } - else if (!noop && symlen == 2 && symname[0] == '>' && symname[1] == '=') { - genop(s, MKOP_ABC(OP_GE, cursp(), idx, n)); - } - else if (!noop && symlen == 2 && symname[0] == '=' && symname[1] == '=') { - genop(s, MKOP_ABC(OP_EQ, cursp(), idx, n)); - } - else { - if (sendv) n = CALL_MAXARGS; - if (blk > 0) { /* no block */ - genop(s, MKOP_ABC(OP_SEND, cursp(), idx, n)); - } - else { - genop(s, MKOP_ABC(OP_SENDB, cursp(), idx, n)); - } - } - } - if (val) { - push(); - } -} - -static void -gen_assignment(codegen_scope *s, node *tree, int sp, int val) -{ - int idx; - int type = (intptr_t)tree->car; - - tree = tree->cdr; - switch ((intptr_t)type) { - case NODE_GVAR: - idx = new_sym(s, sym(tree)); - genop_peep(s, MKOP_ABx(OP_SETGLOBAL, sp, idx), val); - break; - case NODE_LVAR: - idx = lv_idx(s, sym(tree)); - if (idx > 0) { - if (idx != sp) { - genop_peep(s, MKOP_AB(OP_MOVE, idx, sp), val); - } - break; - } - else { /* upvar */ - int lv = 0; - codegen_scope *up = s->prev; - - while (up) { - idx = lv_idx(up, sym(tree)); - if (idx > 0) { - genop_peep(s, MKOP_ABC(OP_SETUPVAR, sp, idx, lv), val); - break; - } - lv++; - up = up->prev; - } - } - break; - case NODE_IVAR: - idx = new_sym(s, sym(tree)); - genop_peep(s, MKOP_ABx(OP_SETIV, sp, idx), val); - break; - case NODE_CVAR: - idx = new_sym(s, sym(tree)); - genop_peep(s, MKOP_ABx(OP_SETCV, sp, idx), val); - break; - case NODE_CONST: - idx = new_sym(s, sym(tree)); - genop_peep(s, MKOP_ABx(OP_SETCONST, sp, idx), val); - break; - case NODE_COLON2: - idx = new_sym(s, sym(tree->cdr)); - genop_peep(s, MKOP_AB(OP_MOVE, cursp(), sp), NOVAL); - push(); - codegen(s, tree->car, VAL); - pop_n(2); - genop_peep(s, MKOP_ABx(OP_SETMCNST, cursp(), idx), val); - break; - - case NODE_CALL: - push(); - gen_call(s, tree, attrsym(s, sym(tree->cdr->car)), sp, NOVAL); - pop(); - if (val) { - genop_peep(s, MKOP_AB(OP_MOVE, cursp(), sp), val); - } - break; - - case NODE_MASGN: - gen_vmassignment(s, tree->car, sp, val); - break; - - /* splat without assignment */ - case NODE_NIL: - break; - - default: -#ifdef ENABLE_STDIO - printf("unknown lhs %d\n", type); -#endif - break; - } - if (val) push(); -} - -static void -gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val) -{ - int n = 0, post = 0; - node *t, *p; - - if (tree->car) { /* pre */ - t = tree->car; - n = 0; - while (t) { - genop(s, MKOP_ABC(OP_AREF, cursp(), rhs, n)); - gen_assignment(s, t->car, cursp(), NOVAL); - n++; - t = t->cdr; - } - } - t = tree->cdr; - if (t) { - if (t->cdr) { /* post count */ - p = t->cdr->car; - while (p) { - post++; - p = p->cdr; - } - } - if (val) { - genop(s, MKOP_AB(OP_MOVE, cursp(), rhs)); - } - else { - pop(); - } - genop(s, MKOP_ABC(OP_APOST, cursp(), n, post)); - n = 1; - if (t->car) { /* rest */ - gen_assignment(s, t->car, cursp(), NOVAL); - } - if (t->cdr && t->cdr->car) { - t = t->cdr->car; - while (t) { - gen_assignment(s, t->car, cursp()+n, NOVAL); - t = t->cdr; - n++; - } - } - push(); - } -} - -static void -gen_send_intern(codegen_scope *s) -{ - pop(); - genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "intern")), 0)); - push(); -} -static void -gen_literal_array(codegen_scope *s, node *tree, mrb_bool sym, int val) -{ - if (val) { - int i = 0, j = 0; - - while (tree) { - switch ((intptr_t)tree->car->car) { - case NODE_STR: - if ((tree->cdr == NULL) && ((intptr_t)tree->car->cdr->cdr == 0)) - break; - /* fall through */ - case NODE_BEGIN: - codegen(s, tree->car, VAL); - ++j; - break; - - case NODE_LITERAL_DELIM: - if (j > 0) { - j = 0; - ++i; - if (sym) - gen_send_intern(s); - } - break; - } - if (j >= 2) { - pop(); pop(); - genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL); - push(); - j = 1; - } - tree = tree->cdr; - } - if (j > 0) { - ++i; - if (sym) - gen_send_intern(s); - } - pop_n(i); - genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), i)); - push(); - } - else { - while (tree) { - switch ((intptr_t)tree->car->car) { - case NODE_BEGIN: case NODE_BLOCK: - codegen(s, tree->car, NOVAL); - } - tree = tree->cdr; - } - } -} - -static void -raise_error(codegen_scope *s, const char *msg) -{ - int idx = new_lit(s, mrb_str_new_cstr(s->mrb, msg)); - - genop(s, MKOP_ABx(OP_ERR, 1, idx)); -} - -static double -readint_float(codegen_scope *s, const char *p, int base) -{ - const char *e = p + strlen(p); - double f = 0; - int n; - - if (*p == '+') p++; - while (p < e) { - char c = *p; - c = tolower((unsigned char)c); - for (n=0; n= 2 && base <= 36); - if (*p == '+') p++; - while (p < e) { - char c = *p; - c = tolower((unsigned char)c); - for (n=0; n result) { - *overflow = TRUE; - return 0; - } - result *= base; - result -= n; - } - else { - if ((MRB_INT_MAX - n)/base < result) { - *overflow = TRUE; - return 0; - } - result *= base; - result += n; - } - p++; - } - *overflow = FALSE; - return result; -} - -static void -codegen(codegen_scope *s, node *tree, int val) -{ - int nt; - - if (!tree) return; - - if (s->irep && s->filename_index != tree->filename_index) { - s->irep->filename = mrb_parser_get_filename(s->parser, s->filename_index); - mrb_debug_info_append_file(s->mrb, s->irep, s->debug_start_pos, s->pc); - s->debug_start_pos = s->pc; - s->filename_index = tree->filename_index; - s->filename = mrb_parser_get_filename(s->parser, tree->filename_index); - } - - nt = (intptr_t)tree->car; - s->lineno = tree->lineno; - tree = tree->cdr; - switch (nt) { - case NODE_BEGIN: - if (val && !tree) { - genop(s, MKOP_A(OP_LOADNIL, cursp())); - push(); - } - while (tree) { - codegen(s, tree->car, tree->cdr ? NOVAL : val); - tree = tree->cdr; - } - break; - - case NODE_RESCUE: - { - int onerr, noexc, exend, pos1, pos2, tmp; - struct loopinfo *lp; - - onerr = genop(s, MKOP_Bx(OP_ONERR, 0)); - lp = loop_push(s, LOOP_BEGIN); - lp->pc1 = onerr; - if (tree->car) { - codegen(s, tree->car, val); - if (val) pop(); - } - lp->type = LOOP_RESCUE; - noexc = genop(s, MKOP_Bx(OP_JMP, 0)); - dispatch(s, onerr); - tree = tree->cdr; - exend = 0; - pos1 = 0; - if (tree->car) { - node *n2 = tree->car; - int exc = cursp(); - - genop(s, MKOP_A(OP_RESCUE, exc)); - push(); - while (n2) { - node *n3 = n2->car; - node *n4 = n3->car; - - if (pos1) dispatch(s, pos1); - pos2 = 0; - do { - if (n4) { - codegen(s, n4->car, VAL); - } - else { - genop(s, MKOP_ABx(OP_GETCONST, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "StandardError")))); - push(); - } - genop(s, MKOP_AB(OP_MOVE, cursp(), exc)); - pop(); - if (n4 && n4->car && (intptr_t)n4->car->car == NODE_SPLAT) { - genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__case_eqq")), 1)); - } - else { - genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "===")), 1)); - } - tmp = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), pos2)); - pos2 = tmp; - if (n4) { - n4 = n4->cdr; - } - } while (n4); - pos1 = genop(s, MKOP_sBx(OP_JMP, 0)); - dispatch_linked(s, pos2); - - pop(); - if (n3->cdr->car) { - gen_assignment(s, n3->cdr->car, exc, NOVAL); - } - if (n3->cdr->cdr->car) { - codegen(s, n3->cdr->cdr->car, val); - if (val) pop(); - } - tmp = genop(s, MKOP_sBx(OP_JMP, exend)); - exend = tmp; - n2 = n2->cdr; - push(); - } - if (pos1) { - dispatch(s, pos1); - genop(s, MKOP_A(OP_RAISE, exc)); - } - } - pop(); - tree = tree->cdr; - dispatch(s, noexc); - genop(s, MKOP_A(OP_POPERR, 1)); - if (tree->car) { - codegen(s, tree->car, val); - } - else if (val) { - push(); - } - dispatch_linked(s, exend); - loop_pop(s, NOVAL); - } - break; - - case NODE_ENSURE: - { - int idx; - int epush = s->pc; - - genop(s, MKOP_Bx(OP_EPUSH, 0)); - s->ensure_level++; - codegen(s, tree->car, val); - idx = scope_body(s, tree->cdr, NOVAL); - s->iseq[epush] = MKOP_Bx(OP_EPUSH, idx); - s->ensure_level--; - genop_peep(s, MKOP_A(OP_EPOP, 1), NOVAL); - } - break; - - case NODE_LAMBDA: - { - int idx = lambda_body(s, tree, 1); - - genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_LAMBDA)); - push(); - } - break; - - case NODE_BLOCK: - { - int idx = lambda_body(s, tree, 1); - - genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_BLOCK)); - push(); - } - break; - - case NODE_IF: - { - int pos1, pos2; - node *e = tree->cdr->cdr->car; - - codegen(s, tree->car, VAL); - pop(); - pos1 = genop_peep(s, MKOP_AsBx(OP_JMPNOT, cursp(), 0), NOVAL); - - codegen(s, tree->cdr->car, val); - if (val && !(tree->cdr->car)) { - genop(s, MKOP_A(OP_LOADNIL, cursp())); - push(); - } - if (e) { - if (val) pop(); - pos2 = genop(s, MKOP_sBx(OP_JMP, 0)); - dispatch(s, pos1); - codegen(s, e, val); - dispatch(s, pos2); - } - else { - if (val) { - pop(); - pos2 = genop(s, MKOP_sBx(OP_JMP, 0)); - dispatch(s, pos1); - genop(s, MKOP_A(OP_LOADNIL, cursp())); - dispatch(s, pos2); - push(); - } - else { - dispatch(s, pos1); - } - } - } - break; - - case NODE_AND: - { - int pos; - - codegen(s, tree->car, VAL); - pop(); - pos = genop(s, MKOP_AsBx(OP_JMPNOT, cursp(), 0)); - codegen(s, tree->cdr, val); - dispatch(s, pos); - } - break; - - case NODE_OR: - { - int pos; - - codegen(s, tree->car, VAL); - pop(); - pos = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), 0)); - codegen(s, tree->cdr, val); - dispatch(s, pos); - } - break; - - case NODE_WHILE: - { - struct loopinfo *lp = loop_push(s, LOOP_NORMAL); - - lp->pc1 = genop(s, MKOP_sBx(OP_JMP, 0)); - lp->pc2 = new_label(s); - codegen(s, tree->cdr, NOVAL); - dispatch(s, lp->pc1); - codegen(s, tree->car, VAL); - pop(); - genop(s, MKOP_AsBx(OP_JMPIF, cursp(), lp->pc2 - s->pc)); - - loop_pop(s, val); - } - break; - - case NODE_UNTIL: - { - struct loopinfo *lp = loop_push(s, LOOP_NORMAL); - - lp->pc1 = genop(s, MKOP_sBx(OP_JMP, 0)); - lp->pc2 = new_label(s); - codegen(s, tree->cdr, NOVAL); - dispatch(s, lp->pc1); - codegen(s, tree->car, VAL); - pop(); - genop(s, MKOP_AsBx(OP_JMPNOT, cursp(), lp->pc2 - s->pc)); - - loop_pop(s, val); - } - break; - - case NODE_FOR: - for_body(s, tree); - if (val) push(); - break; - - case NODE_CASE: - { - int head = 0; - int pos1, pos2, pos3, tmp; - node *n; - - pos3 = 0; - if (tree->car) { - head = cursp(); - codegen(s, tree->car, VAL); - } - tree = tree->cdr; - while (tree) { - n = tree->car->car; - pos1 = pos2 = 0; - while (n) { - codegen(s, n->car, VAL); - if (head) { - genop(s, MKOP_AB(OP_MOVE, cursp(), head)); - pop(); - if ((intptr_t)n->car->car == NODE_SPLAT) { - genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__case_eqq")), 1)); - } - else { - genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "===")), 1)); - } - } - else { - pop(); - } - tmp = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), pos2)); - pos2 = tmp; - n = n->cdr; - } - if (tree->car->car) { - pos1 = genop(s, MKOP_sBx(OP_JMP, 0)); - dispatch_linked(s, pos2); - } - codegen(s, tree->car->cdr, val); - if (val) pop(); - tmp = genop(s, MKOP_sBx(OP_JMP, pos3)); - pos3 = tmp; - if (pos1) dispatch(s, pos1); - tree = tree->cdr; - } - if (val) { - int pos = cursp(); - genop(s, MKOP_A(OP_LOADNIL, cursp())); - if (pos3) dispatch_linked(s, pos3); - if (head) pop(); - genop(s, MKOP_AB(OP_MOVE, cursp(), pos)); - push(); - } - else { - if (pos3) { - dispatch_linked(s, pos3); - } - if (head) { - pop(); - } - } - } - break; - - case NODE_SCOPE: - scope_body(s, tree, NOVAL); - break; - - case NODE_FCALL: - case NODE_CALL: - gen_call(s, tree, 0, 0, val); - break; - - case NODE_DOT2: - codegen(s, tree->car, val); - codegen(s, tree->cdr, val); - if (val) { - pop(); pop(); - genop(s, MKOP_ABC(OP_RANGE, cursp(), cursp(), FALSE)); - push(); - } - break; - - case NODE_DOT3: - codegen(s, tree->car, val); - codegen(s, tree->cdr, val); - if (val) { - pop(); pop(); - genop(s, MKOP_ABC(OP_RANGE, cursp(), cursp(), TRUE)); - push(); - } - break; - - case NODE_COLON2: - { - int sym = new_sym(s, sym(tree->cdr)); - - codegen(s, tree->car, VAL); - pop(); - genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym)); - if (val) push(); - } - break; - - case NODE_COLON3: - { - int sym = new_sym(s, sym(tree)); - - genop(s, MKOP_A(OP_OCLASS, cursp())); - genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym)); - if (val) push(); - } - break; - - case NODE_ARRAY: - { - int n; - - n = gen_values(s, tree, val); - if (n >= 0) { - if (val) { - pop_n(n); - genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), n)); - push(); - } - } - else if (val) { - push(); - } - } - break; - - case NODE_HASH: - { - int len = 0; - mrb_bool update = FALSE; - - while (tree) { - codegen(s, tree->car->car, val); - codegen(s, tree->car->cdr, val); - len++; - tree = tree->cdr; - if (val && len == 126) { - pop_n(len*2); - genop(s, MKOP_ABC(OP_HASH, cursp(), cursp(), len)); - if (update) { - pop(); - genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__update")), 1)); - } - push(); - update = TRUE; - len = 0; - } - } - if (val) { - pop_n(len*2); - genop(s, MKOP_ABC(OP_HASH, cursp(), cursp(), len)); - if (update) { - pop(); - genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__update")), 1)); - } - push(); - } - } - break; - - case NODE_SPLAT: - codegen(s, tree, VAL); - break; - - case NODE_ASGN: - codegen(s, tree->cdr, VAL); - pop(); - gen_assignment(s, tree->car, cursp(), val); - break; - - case NODE_MASGN: - { - int len = 0, n = 0, post = 0; - node *t = tree->cdr, *p; - int rhs = cursp(); - - if ((intptr_t)t->car == NODE_ARRAY && nosplat(t->cdr)) { - /* fixed rhs */ - t = t->cdr; - while (t) { - codegen(s, t->car, VAL); - len++; - t = t->cdr; - } - tree = tree->car; - if (tree->car) { /* pre */ - t = tree->car; - n = 0; - while (t) { - gen_assignment(s, t->car, rhs+n, NOVAL); - n++; - t = t->cdr; - } - } - t = tree->cdr; - if (t) { - if (t->cdr) { /* post count */ - p = t->cdr->car; - while (p) { - post++; - p = p->cdr; - } - } - if (t->car) { /* rest (len - pre - post) */ - int rn; - - if (len < post + n) { - rn = 0; - } - else { - rn = len - post - n; - } - genop(s, MKOP_ABC(OP_ARRAY, cursp(), rhs+n, rn)); - gen_assignment(s, t->car, cursp(), NOVAL); - n += rn; - } - if (t->cdr && t->cdr->car) { - t = t->cdr->car; - while (ncar, rhs+n, NOVAL); - t = t->cdr; - n++; - } - } - } - pop_n(len); - if (val) { - genop(s, MKOP_ABC(OP_ARRAY, rhs, rhs, len)); - push(); - } - } - else { - /* variable rhs */ - codegen(s, t, VAL); - gen_vmassignment(s, tree->car, rhs, val); - if (!val) { - pop(); - } - } - } - break; - - case NODE_OP_ASGN: - { - mrb_sym sym = sym(tree->cdr->car); - mrb_int len; - const char *name = mrb_sym2name_len(s->mrb, sym, &len); - int idx; - - codegen(s, tree->car, VAL); - if (len == 2 && - ((name[0] == '|' && name[1] == '|') || - (name[0] == '&' && name[1] == '&'))) { - int pos; - - pop(); - pos = genop_peep(s, MKOP_AsBx(name[0] == '|' ? OP_JMPIF : OP_JMPNOT, cursp(), 0), NOVAL); - codegen(s, tree->cdr->cdr->car, VAL); - pop(); - gen_assignment(s, tree->car, cursp(), val); - dispatch(s, pos); - break; - } - codegen(s, tree->cdr->cdr->car, VAL); - push(); pop(); - pop(); pop(); - - idx = new_msym(s, sym); - if (len == 1 && name[0] == '+') { - genop_peep(s, MKOP_ABC(OP_ADD, cursp(), idx, 1), val); - } - else if (len == 1 && name[0] == '-') { - genop_peep(s, MKOP_ABC(OP_SUB, cursp(), idx, 1), val); - } - else if (len == 1 && name[0] == '*') { - genop(s, MKOP_ABC(OP_MUL, cursp(), idx, 1)); - } - else if (len == 1 && name[0] == '/') { - genop(s, MKOP_ABC(OP_DIV, cursp(), idx, 1)); - } - else if (len == 1 && name[0] == '<') { - genop(s, MKOP_ABC(OP_LT, cursp(), idx, 1)); - } - else if (len == 2 && name[0] == '<' && name[1] == '=') { - genop(s, MKOP_ABC(OP_LE, cursp(), idx, 1)); - } - else if (len == 1 && name[0] == '>') { - genop(s, MKOP_ABC(OP_GT, cursp(), idx, 1)); - } - else if (len == 2 && name[0] == '>' && name[1] == '=') { - genop(s, MKOP_ABC(OP_GE, cursp(), idx, 1)); - } - else { - genop(s, MKOP_ABC(OP_SEND, cursp(), idx, 1)); - } - } - gen_assignment(s, tree->car, cursp(), val); - break; - - case NODE_SUPER: - { - int n = 0, noop = 0, sendv = 0; - - push(); /* room for receiver */ - if (tree) { - node *args = tree->car; - if (args) { - n = gen_values(s, args, VAL); - if (n < 0) { - n = noop = sendv = 1; - push(); - } - } - } - if (tree && tree->cdr) { - codegen(s, tree->cdr, VAL); - pop(); - } - else { - genop(s, MKOP_A(OP_LOADNIL, cursp())); - push(); pop(); - } - pop_n(n+1); - if (sendv) n = CALL_MAXARGS; - genop(s, MKOP_ABC(OP_SUPER, cursp(), 0, n)); - if (val) push(); - } - break; - - case NODE_ZSUPER: - { - codegen_scope *s2 = s; - int lv = 0, ainfo = 0; - - push(); /* room for receiver */ - while (!s2->mscope) { - lv++; - s2 = s2->prev; - if (!s2) break; - } - if (s2) ainfo = s2->ainfo; - genop(s, MKOP_ABx(OP_ARGARY, cursp(), (ainfo<<4)|(lv & 0xf))); - push(); push(); pop(); /* ARGARY pushes two values */ - if (tree && tree->cdr) { - codegen(s, tree->cdr, VAL); - pop(); - } - pop(); pop(); - genop(s, MKOP_ABC(OP_SUPER, cursp(), 0, CALL_MAXARGS)); - if (val) push(); - } - break; - - case NODE_RETURN: - if (tree) { - codegen(s, tree, VAL); - pop(); - } - else { - genop(s, MKOP_A(OP_LOADNIL, cursp())); - } - if (s->loop) { - genop(s, MKOP_AB(OP_RETURN, cursp(), OP_R_RETURN)); - } - else { - genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL); - } - if (val) push(); - break; - - case NODE_YIELD: - { - codegen_scope *s2 = s; - int lv = 0, ainfo = 0; - int n = 0, sendv = 0; - - while (!s2->mscope) { - lv++; - s2 = s2->prev; - if (!s2) break; - } - if (s2) ainfo = s2->ainfo; - genop(s, MKOP_ABx(OP_BLKPUSH, cursp(), (ainfo<<4)|(lv & 0xf))); - push(); - if (tree) { - n = gen_values(s, tree, VAL); - if (n < 0) { - n = sendv = 1; - push(); - } - } - pop_n(n+1); - if (sendv) n = CALL_MAXARGS; - genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "call")), n)); - if (val) push(); - } - break; - - case NODE_BREAK: - loop_break(s, tree); - if (val) push(); - break; - - case NODE_NEXT: - if (!s->loop) { - raise_error(s, "unexpected next"); - } - else if (s->loop->type == LOOP_NORMAL) { - if (s->ensure_level > s->loop->ensure_level) { - genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - s->loop->ensure_level), NOVAL); - } - codegen(s, tree, NOVAL); - genop(s, MKOP_sBx(OP_JMP, s->loop->pc1 - s->pc)); - } - else { - if (tree) { - codegen(s, tree, VAL); - pop(); - } - else { - genop(s, MKOP_A(OP_LOADNIL, cursp())); - } - genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL); - } - if (val) push(); - break; - - case NODE_REDO: - if (!s->loop) { - raise_error(s, "unexpected redo"); - } - else { - if (s->ensure_level > s->loop->ensure_level) { - genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - s->loop->ensure_level), NOVAL); - } - genop(s, MKOP_sBx(OP_JMP, s->loop->pc2 - s->pc)); - } - break; - - case NODE_RETRY: - { - const char *msg = "unexpected retry"; - - if (!s->loop) { - raise_error(s, msg); - } - else { - struct loopinfo *lp = s->loop; - int n = 0; - - while (lp && lp->type != LOOP_RESCUE) { - if (lp->type == LOOP_BEGIN) { - n++; - } - lp = lp->prev; - } - if (!lp) { - raise_error(s, msg); - } - else { - if (n > 0) { - while (n--) { - genop_peep(s, MKOP_A(OP_POPERR, 1), NOVAL); - } - } - if (s->ensure_level > lp->ensure_level) { - genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - lp->ensure_level), NOVAL); - } - genop(s, MKOP_sBx(OP_JMP, lp->pc1 - s->pc)); - } - } - } - break; - - case NODE_LVAR: - if (val) { - int idx = lv_idx(s, sym(tree)); - - if (idx > 0) { - genop_peep(s, MKOP_AB(OP_MOVE, cursp(), idx), NOVAL); - } - else { - int lv = 0; - codegen_scope *up = s->prev; - - while (up) { - idx = lv_idx(up, sym(tree)); - if (idx > 0) { - genop(s, MKOP_ABC(OP_GETUPVAR, cursp(), idx, lv)); - break; - } - lv++; - up = up->prev; - } - } - push(); - } - break; - - case NODE_GVAR: - if (val) { - int sym = new_sym(s, sym(tree)); - - genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym)); - push(); - } - break; - - case NODE_IVAR: - if (val) { - int sym = new_sym(s, sym(tree)); - - genop(s, MKOP_ABx(OP_GETIV, cursp(), sym)); - push(); - } - break; - - case NODE_CVAR: - if (val) { - int sym = new_sym(s, sym(tree)); - - genop(s, MKOP_ABx(OP_GETCV, cursp(), sym)); - push(); - } - break; - - case NODE_CONST: - { - int sym = new_sym(s, sym(tree)); - - genop(s, MKOP_ABx(OP_GETCONST, cursp(), sym)); - push(); - } - break; - - case NODE_DEFINED: - codegen(s, tree, VAL); - break; - - case NODE_BACK_REF: - if (val) { - char buf[2] = { '$' }; - mrb_value str; - int sym; - - buf[1] = (char)(intptr_t)tree; - str = mrb_str_new(s->mrb, buf, 2); - sym = new_sym(s, mrb_intern_str(s->mrb, str)); - genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym)); - push(); - } - break; - - case NODE_NTH_REF: - if (val) { - int sym; - mrb_state *mrb = s->mrb; - mrb_value fix = mrb_fixnum_value((intptr_t)tree); - mrb_value str = mrb_str_buf_new(mrb, 4); - - mrb_str_cat_lit(mrb, str, "$"); - mrb_str_cat_str(mrb, str, mrb_fixnum_to_str(mrb, fix, 10)); - sym = new_sym(s, mrb_intern_str(mrb, str)); - genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym)); - push(); - } - break; - - case NODE_ARG: - /* should not happen */ - break; - - case NODE_BLOCK_ARG: - codegen(s, tree, VAL); - break; - - case NODE_INT: - if (val) { - char *p = (char*)tree->car; - int base = (intptr_t)tree->cdr->car; - mrb_int i; - mrb_code co; - mrb_bool overflow; - - i = readint_mrb_int(s, p, base, FALSE, &overflow); - if (overflow) { - double f = readint_float(s, p, base); - int off = new_lit(s, mrb_float_value(s->mrb, f)); - - genop(s, MKOP_ABx(OP_LOADL, cursp(), off)); - } - else { - if (i < MAXARG_sBx && i > -MAXARG_sBx) { - co = MKOP_AsBx(OP_LOADI, cursp(), i); - } - else { - int off = new_lit(s, mrb_fixnum_value(i)); - co = MKOP_ABx(OP_LOADL, cursp(), off); - } - genop(s, co); - } - push(); - } - break; - - case NODE_FLOAT: - if (val) { - char *p = (char*)tree; - mrb_float f = str_to_mrb_float(p); - int off = new_lit(s, mrb_float_value(s->mrb, f)); - - genop(s, MKOP_ABx(OP_LOADL, cursp(), off)); - push(); - } - break; - - case NODE_NEGATE: - { - nt = (intptr_t)tree->car; - tree = tree->cdr; - switch (nt) { - case NODE_FLOAT: - { - char *p = (char*)tree; - mrb_float f = str_to_mrb_float(p); - int off = new_lit(s, mrb_float_value(s->mrb, -f)); - - genop(s, MKOP_ABx(OP_LOADL, cursp(), off)); - push(); - } - break; - - case NODE_INT: - { - char *p = (char*)tree->car; - int base = (intptr_t)tree->cdr->car; - mrb_int i; - mrb_code co; - mrb_bool overflow; - - i = readint_mrb_int(s, p, base, TRUE, &overflow); - if (overflow) { - double f = readint_float(s, p, base); - int off = new_lit(s, mrb_float_value(s->mrb, -f)); - - genop(s, MKOP_ABx(OP_LOADL, cursp(), off)); - } - else { - if (i < MAXARG_sBx && i > -MAXARG_sBx) { - co = MKOP_AsBx(OP_LOADI, cursp(), i); - } - else { - int off = new_lit(s, mrb_fixnum_value(i)); - co = MKOP_ABx(OP_LOADL, cursp(), off); - } - genop(s, co); - } - push(); - } - break; - - default: - { - int sym = new_msym(s, mrb_intern_lit(s->mrb, "-")); - - genop(s, MKOP_ABx(OP_LOADI, cursp(), 0)); - push(); - codegen(s, tree, VAL); - pop(); pop(); - genop(s, MKOP_ABC(OP_SUB, cursp(), sym, 2)); - } - break; - } - } - break; - - case NODE_STR: - if (val) { - char *p = (char*)tree->car; - size_t len = (intptr_t)tree->cdr; - int ai = mrb_gc_arena_save(s->mrb); - int off = new_lit(s, mrb_str_new(s->mrb, p, len)); - - mrb_gc_arena_restore(s->mrb, ai); - genop(s, MKOP_ABx(OP_STRING, cursp(), off)); - push(); - } - break; - - case NODE_HEREDOC: - tree = ((struct mrb_parser_heredoc_info *)tree)->doc; - /* fall through */ - case NODE_DSTR: - if (val) { - node *n = tree; - - if (!n) break; - codegen(s, n->car, VAL); - n = n->cdr; - while (n) { - codegen(s, n->car, VAL); - pop(); pop(); - genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL); - push(); - n = n->cdr; - } - } - else { - node *n = tree; - - while (n) { - if ((intptr_t)n->car->car != NODE_STR) { - codegen(s, n->car, NOVAL); - } - n = n->cdr; - } - } - break; - - case NODE_WORDS: - gen_literal_array(s, tree, FALSE, val); - break; - - case NODE_SYMBOLS: - gen_literal_array(s, tree, TRUE, val); - break; - - case NODE_DXSTR: - { - node *n; - int ai = mrb_gc_arena_save(s->mrb); - int sym = new_sym(s, mrb_intern_lit(s->mrb, "Kernel")); - - if (val == NOVAL) { push(); } - genop(s, MKOP_A(OP_OCLASS, cursp())); - genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym)); - push(); - codegen(s, tree->car, VAL); - n = tree->cdr; - while (n) { - if ((intptr_t)n->car->car == NODE_XSTR) { - n->car->car = (struct mrb_ast_node*)(intptr_t)NODE_STR; - mrb_assert(!n->cdr); /* must be the end */ - } - codegen(s, n->car, VAL); - pop(); pop(); - genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL); - push(); - n = n->cdr; - } - pop(); - pop(); - sym = new_sym(s, mrb_intern_lit(s->mrb, "`")); - genop(s, MKOP_ABC(OP_SEND, cursp(), sym, 1)); - if (val == NOVAL) { pop(); } - else { push(); } - mrb_gc_arena_restore(s->mrb, ai); - } - break; - - case NODE_XSTR: - { - char *p = (char*)tree->car; - size_t len = (intptr_t)tree->cdr; - int ai = mrb_gc_arena_save(s->mrb); - int sym = new_sym(s, mrb_intern_lit(s->mrb, "Kernel")); - int off = new_lit(s, mrb_str_new(s->mrb, p, len)); - - if (val == NOVAL) { push(); } - genop(s, MKOP_A(OP_OCLASS, cursp())); - genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym)); - push(); - genop(s, MKOP_ABx(OP_STRING, cursp(), off)); - pop(); - sym = new_sym(s, mrb_intern_lit(s->mrb, "`")); - genop(s, MKOP_ABC(OP_SEND, cursp(), sym, 1)); - if (val == NOVAL) { pop(); } - else { push(); } - mrb_gc_arena_restore(s->mrb, ai); - } - break; - - case NODE_REGX: - if (val) { - char *p1 = (char*)tree->car; - char *p2 = (char*)tree->cdr; - int ai = mrb_gc_arena_save(s->mrb); - int sym = new_sym(s, mrb_intern_lit(s->mrb, REGEXP_CLASS)); - int off = new_lit(s, mrb_str_new_cstr(s->mrb, p1)); - int argc = 1; - - genop(s, MKOP_A(OP_OCLASS, cursp())); - genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym)); - push(); - genop(s, MKOP_ABx(OP_STRING, cursp(), off)); - if (p2) { - push(); - off = new_lit(s, mrb_str_new_cstr(s->mrb, p2)); - genop(s, MKOP_ABx(OP_STRING, cursp(), off)); - argc++; - pop(); - } - pop(); - sym = new_sym(s, mrb_intern_lit(s->mrb, "compile")); - genop(s, MKOP_ABC(OP_SEND, cursp(), sym, argc)); - mrb_gc_arena_restore(s->mrb, ai); - push(); - } - break; - - case NODE_DREGX: - if (val) { - node *n = tree->car; - int ai = mrb_gc_arena_save(s->mrb); - int sym = new_sym(s, mrb_intern_lit(s->mrb, REGEXP_CLASS)); - int argc = 1; - int off; - char *p; - - genop(s, MKOP_A(OP_OCLASS, cursp())); - genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym)); - push(); - codegen(s, n->car, VAL); - n = n->cdr; - while (n) { - codegen(s, n->car, VAL); - pop(); pop(); - genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL); - push(); - n = n->cdr; - } - n = tree->cdr->cdr; - if (n->car) { - p = (char*)n->car; - off = new_lit(s, mrb_str_new_cstr(s->mrb, p)); - codegen(s, tree->car, VAL); - genop(s, MKOP_ABx(OP_STRING, cursp(), off)); - pop(); - genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL); - } - if (n->cdr) { - char *p2 = (char*)n->cdr; - - push(); - off = new_lit(s, mrb_str_new_cstr(s->mrb, p2)); - genop(s, MKOP_ABx(OP_STRING, cursp(), off)); - argc++; - pop(); - } - pop(); - sym = new_sym(s, mrb_intern_lit(s->mrb, "compile")); - genop(s, MKOP_ABC(OP_SEND, cursp(), sym, argc)); - mrb_gc_arena_restore(s->mrb, ai); - push(); - } - else { - node *n = tree->car; - - while (n) { - if ((intptr_t)n->car->car != NODE_STR) { - codegen(s, n->car, NOVAL); - } - n = n->cdr; - } - } - break; - - case NODE_SYM: - if (val) { - int sym = new_sym(s, sym(tree)); - - genop(s, MKOP_ABx(OP_LOADSYM, cursp(), sym)); - push(); - } - break; - - case NODE_DSYM: - codegen(s, tree, val); - if (val) { - gen_send_intern(s); - } - break; - - case NODE_SELF: - if (val) { - genop(s, MKOP_A(OP_LOADSELF, cursp())); - push(); - } - break; - - case NODE_NIL: - if (val) { - genop(s, MKOP_A(OP_LOADNIL, cursp())); - push(); - } - break; - - case NODE_TRUE: - if (val) { - genop(s, MKOP_A(OP_LOADT, cursp())); - push(); - } - break; - - case NODE_FALSE: - if (val) { - genop(s, MKOP_A(OP_LOADF, cursp())); - push(); - } - break; - - case NODE_ALIAS: - { - int a = new_msym(s, sym(tree->car)); - int b = new_msym(s, sym(tree->cdr)); - int c = new_msym(s, mrb_intern_lit(s->mrb, "alias_method")); - - genop(s, MKOP_A(OP_TCLASS, cursp())); - push(); - genop(s, MKOP_ABx(OP_LOADSYM, cursp(), a)); - push(); - genop(s, MKOP_ABx(OP_LOADSYM, cursp(), b)); - push(); - genop(s, MKOP_A(OP_LOADNIL, cursp())); - pop_n(3); - genop(s, MKOP_ABC(OP_SEND, cursp(), c, 2)); - if (val) { - push(); - } - } - break; - - case NODE_UNDEF: - { - int undef = new_msym(s, mrb_intern_lit(s->mrb, "undef_method")); - int num = 0; - node *t = tree; - - genop(s, MKOP_A(OP_TCLASS, cursp())); - push(); - while (t) { - int symbol = new_msym(s, sym(t->car)); - genop(s, MKOP_ABx(OP_LOADSYM, cursp(), symbol)); - push(); - t = t->cdr; - num++; - } - pop_n(num + 1); - genop(s, MKOP_ABC(OP_SEND, cursp(), undef, num)); - if (val) { - push(); - } - } - break; - - case NODE_CLASS: - { - int idx; - - if (tree->car->car == (node*)0) { - genop(s, MKOP_A(OP_LOADNIL, cursp())); - push(); - } - else if (tree->car->car == (node*)1) { - genop(s, MKOP_A(OP_OCLASS, cursp())); - push(); - } - else { - codegen(s, tree->car->car, VAL); - } - if (tree->cdr->car) { - codegen(s, tree->cdr->car, VAL); - } - else { - genop(s, MKOP_A(OP_LOADNIL, cursp())); - push(); - } - pop(); pop(); - idx = new_msym(s, sym(tree->car->cdr)); - genop(s, MKOP_AB(OP_CLASS, cursp(), idx)); - idx = scope_body(s, tree->cdr->cdr->car, val); - genop(s, MKOP_ABx(OP_EXEC, cursp(), idx)); - if (val) { - push(); - } - } - break; - - case NODE_MODULE: - { - int idx; - - if (tree->car->car == (node*)0) { - genop(s, MKOP_A(OP_LOADNIL, cursp())); - push(); - } - else if (tree->car->car == (node*)1) { - genop(s, MKOP_A(OP_OCLASS, cursp())); - push(); - } - else { - codegen(s, tree->car->car, VAL); - } - pop(); - idx = new_msym(s, sym(tree->car->cdr)); - genop(s, MKOP_AB(OP_MODULE, cursp(), idx)); - idx = scope_body(s, tree->cdr->car, val); - genop(s, MKOP_ABx(OP_EXEC, cursp(), idx)); - if (val) { - push(); - } - } - break; - - case NODE_SCLASS: - { - int idx; - - codegen(s, tree->car, VAL); - pop(); - genop(s, MKOP_AB(OP_SCLASS, cursp(), cursp())); - idx = scope_body(s, tree->cdr->car, val); - genop(s, MKOP_ABx(OP_EXEC, cursp(), idx)); - if (val) { - push(); - } - } - break; - - case NODE_DEF: - { - int sym = new_msym(s, sym(tree->car)); - int idx = lambda_body(s, tree->cdr, 0); - - genop(s, MKOP_A(OP_TCLASS, cursp())); - push(); - genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_METHOD)); - push(); pop(); - pop(); - genop(s, MKOP_AB(OP_METHOD, cursp(), sym)); - if (val) { - genop(s, MKOP_ABx(OP_LOADSYM, cursp(), sym)); - push(); - } - } - break; - - case NODE_SDEF: - { - node *recv = tree->car; - int sym = new_msym(s, sym(tree->cdr->car)); - int idx = lambda_body(s, tree->cdr->cdr, 0); - - codegen(s, recv, VAL); - pop(); - genop(s, MKOP_AB(OP_SCLASS, cursp(), cursp())); - push(); - genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_METHOD)); - pop(); - genop(s, MKOP_AB(OP_METHOD, cursp(), sym)); - if (val) { - genop(s, MKOP_ABx(OP_LOADSYM, cursp(), sym)); - push(); - } - } - break; - - case NODE_POSTEXE: - codegen(s, tree, NOVAL); - break; - - default: - break; - } -} - -static void -scope_add_irep(codegen_scope *s, mrb_irep *irep) -{ - if (s->irep == NULL) { - s->irep = irep; - return; - } - if (s->irep->rlen == s->rcapa) { - s->rcapa *= 2; - s->irep->reps = (mrb_irep**)codegen_realloc(s, s->irep->reps, sizeof(mrb_irep*)*s->rcapa); - } - s->irep->reps[s->irep->rlen] = irep; - s->irep->rlen++; -} - -static codegen_scope* -scope_new(mrb_state *mrb, codegen_scope *prev, node *lv) -{ - static const codegen_scope codegen_scope_zero = { 0 }; - mrb_pool *pool = mrb_pool_open(mrb); - codegen_scope *p = (codegen_scope *)mrb_pool_alloc(pool, sizeof(codegen_scope)); - - if (!p) return NULL; - *p = codegen_scope_zero; - p->mrb = mrb; - p->mpool = pool; - if (!prev) return p; - p->prev = prev; - p->ainfo = -1; - p->mscope = 0; - - p->irep = mrb_add_irep(mrb); - scope_add_irep(prev, p->irep); - - p->rcapa = 8; - p->irep->reps = (mrb_irep**)mrb_malloc(mrb, sizeof(mrb_irep*)*p->rcapa); - - p->icapa = 1024; - p->iseq = (mrb_code*)mrb_malloc(mrb, sizeof(mrb_code)*p->icapa); - p->irep->iseq = p->iseq; - - p->pcapa = 32; - p->irep->pool = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value)*p->pcapa); - p->irep->plen = 0; - - p->scapa = 256; - p->irep->syms = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym)*p->scapa); - p->irep->slen = 0; - - p->lv = lv; - p->sp += node_len(lv)+1; /* add self */ - p->nlocals = p->sp; - if (lv) { - node *n = lv; - size_t i = 0; - - p->irep->lv = (struct mrb_locals*)mrb_malloc(mrb, sizeof(struct mrb_locals) * (p->nlocals - 1)); - for (i=0, n=lv; n; i++,n=n->cdr) { - p->irep->lv[i].name = lv_name(n); - if (lv_name(n)) { - p->irep->lv[i].r = lv_idx(p, lv_name(n)); - } - else { - p->irep->lv[i].r = 0; - } - } - mrb_assert(i + 1 == p->nlocals); - } - p->ai = mrb_gc_arena_save(mrb); - - p->filename = prev->filename; - if (p->filename) { - p->lines = (uint16_t*)mrb_malloc(mrb, sizeof(short)*p->icapa); - } - p->lineno = prev->lineno; - - /* debug setting */ - p->debug_start_pos = 0; - if (p->filename) { - mrb_debug_info_alloc(mrb, p->irep); - p->irep->filename = p->filename; - p->irep->lines = p->lines; - } - else { - p->irep->debug_info = NULL; - } - p->parser = prev->parser; - p->filename_index = prev->filename_index; - - return p; -} - -static void -scope_finish(codegen_scope *s) -{ - mrb_state *mrb = s->mrb; - mrb_irep *irep = s->irep; - size_t fname_len; - char *fname; - - irep->flags = 0; - if (s->iseq) { - irep->iseq = (mrb_code *)codegen_realloc(s, s->iseq, sizeof(mrb_code)*s->pc); - irep->ilen = s->pc; - if (s->lines) { - irep->lines = (uint16_t *)codegen_realloc(s, s->lines, sizeof(uint16_t)*s->pc); - } - else { - irep->lines = 0; - } - } - irep->pool = (mrb_value*)codegen_realloc(s, irep->pool, sizeof(mrb_value)*irep->plen); - irep->syms = (mrb_sym*)codegen_realloc(s, irep->syms, sizeof(mrb_sym)*irep->slen); - irep->reps = (mrb_irep**)codegen_realloc(s, irep->reps, sizeof(mrb_irep*)*irep->rlen); - if (s->filename) { - s->irep->filename = mrb_parser_get_filename(s->parser, s->filename_index); - mrb_debug_info_append_file(mrb, s->irep, s->debug_start_pos, s->pc); - - fname_len = strlen(s->filename); - fname = (char*)codegen_malloc(s, fname_len + 1); - memcpy(fname, s->filename, fname_len); - fname[fname_len] = '\0'; - irep->filename = fname; - } - - irep->nlocals = s->nlocals; - irep->nregs = s->nregs; - - mrb_gc_arena_restore(mrb, s->ai); - mrb_pool_close(s->mpool); -} - -static struct loopinfo* -loop_push(codegen_scope *s, enum looptype t) -{ - struct loopinfo *p = (struct loopinfo *)codegen_palloc(s, sizeof(struct loopinfo)); - - p->type = t; - p->pc1 = p->pc2 = p->pc3 = 0; - p->prev = s->loop; - p->ensure_level = s->ensure_level; - p->acc = cursp(); - s->loop = p; - - return p; -} - -static void -loop_break(codegen_scope *s, node *tree) -{ - if (!s->loop) { - codegen(s, tree, NOVAL); - raise_error(s, "unexpected break"); - } - else { - struct loopinfo *loop; - - if (tree) { - codegen(s, tree, VAL); - pop(); - } - - loop = s->loop; - while (loop->type == LOOP_BEGIN) { - genop_peep(s, MKOP_A(OP_POPERR, 1), NOVAL); - loop = loop->prev; - } - while (loop->type == LOOP_RESCUE) { - loop = loop->prev; - } - if (loop->type == LOOP_NORMAL) { - int tmp; - - if (s->ensure_level > s->loop->ensure_level) { - genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - s->loop->ensure_level), NOVAL); - } - if (tree) { - genop_peep(s, MKOP_AB(OP_MOVE, loop->acc, cursp()), NOVAL); - } - tmp = genop(s, MKOP_sBx(OP_JMP, loop->pc3)); - loop->pc3 = tmp; - } - else { - genop(s, MKOP_AB(OP_RETURN, cursp(), OP_R_BREAK)); - } - } -} - -static void -loop_pop(codegen_scope *s, int val) -{ - if (val) { - genop(s, MKOP_A(OP_LOADNIL, cursp())); - } - dispatch_linked(s, s->loop->pc3); - s->loop = s->loop->prev; - if (val) push(); -} - -MRB_API struct RProc* -mrb_generate_code(mrb_state *mrb, parser_state *p) -{ - codegen_scope *scope = scope_new(mrb, 0, 0); - struct RProc *proc; - - if (!scope) { - return NULL; - } - scope->mrb = mrb; - scope->parser = p; - scope->filename = p->filename; - scope->filename_index = p->current_filename_index; - - MRB_TRY(&scope->jmp) { - /* prepare irep */ - codegen(scope, p->tree, NOVAL); - proc = mrb_proc_new(mrb, scope->irep); - mrb_irep_decref(mrb, scope->irep); - mrb_pool_close(scope->mpool); - return proc; - } - MRB_CATCH(&scope->jmp) { - if (scope->filename == scope->irep->filename) { - scope->irep->filename = NULL; - } - mrb_irep_decref(mrb, scope->irep); - mrb_pool_close(scope->mpool); - return NULL; - } - MRB_END_EXC(&scope->jmp); -} diff --git a/src/keywords b/src/keywords deleted file mode 100644 index 9cb86608c..000000000 --- a/src/keywords +++ /dev/null @@ -1,50 +0,0 @@ -%{ -struct kwtable {const char *name; int id[2]; enum mrb_lex_state_enum state;}; -const struct kwtable *mrb_reserved_word(const char *, unsigned int); -static const struct kwtable *reserved_word(const char *, unsigned int); -#define mrb_reserved_word(str, len) reserved_word(str, len) -%} - -struct kwtable; -%% -__ENCODING__, {keyword__ENCODING__, keyword__ENCODING__}, EXPR_END -__FILE__, {keyword__FILE__, keyword__FILE__}, EXPR_END -__LINE__, {keyword__LINE__, keyword__LINE__}, EXPR_END -BEGIN, {keyword_BEGIN, keyword_BEGIN}, EXPR_END -END, {keyword_END, keyword_END}, EXPR_END -alias, {keyword_alias, keyword_alias}, EXPR_FNAME -and, {keyword_and, keyword_and}, EXPR_VALUE -begin, {keyword_begin, keyword_begin}, EXPR_BEG -break, {keyword_break, keyword_break}, EXPR_MID -case, {keyword_case, keyword_case}, EXPR_VALUE -class, {keyword_class, keyword_class}, EXPR_CLASS -def, {keyword_def, keyword_def}, EXPR_FNAME -do, {keyword_do, keyword_do}, EXPR_BEG -else, {keyword_else, keyword_else}, EXPR_BEG -elsif, {keyword_elsif, keyword_elsif}, EXPR_VALUE -end, {keyword_end, keyword_end}, EXPR_END -ensure, {keyword_ensure, keyword_ensure}, EXPR_BEG -false, {keyword_false, keyword_false}, EXPR_END -for, {keyword_for, keyword_for}, EXPR_VALUE -if, {keyword_if, modifier_if}, EXPR_VALUE -in, {keyword_in, keyword_in}, EXPR_VALUE -module, {keyword_module, keyword_module}, EXPR_VALUE -next, {keyword_next, keyword_next}, EXPR_MID -nil, {keyword_nil, keyword_nil}, EXPR_END -not, {keyword_not, keyword_not}, EXPR_ARG -or, {keyword_or, keyword_or}, EXPR_VALUE -redo, {keyword_redo, keyword_redo}, EXPR_END -rescue, {keyword_rescue, modifier_rescue}, EXPR_MID -retry, {keyword_retry, keyword_retry}, EXPR_END -return, {keyword_return, keyword_return}, EXPR_MID -self, {keyword_self, keyword_self}, EXPR_END -super, {keyword_super, keyword_super}, EXPR_ARG -then, {keyword_then, keyword_then}, EXPR_BEG -true, {keyword_true, keyword_true}, EXPR_END -undef, {keyword_undef, keyword_undef}, EXPR_FNAME -unless, {keyword_unless, modifier_unless}, EXPR_VALUE -until, {keyword_until, modifier_until}, EXPR_VALUE -when, {keyword_when, keyword_when}, EXPR_VALUE -while, {keyword_while, modifier_while}, EXPR_VALUE -yield, {keyword_yield, keyword_yield}, EXPR_ARG -%% diff --git a/src/lex.def b/src/lex.def deleted file mode 100644 index ea456a843..000000000 --- a/src/lex.def +++ /dev/null @@ -1,212 +0,0 @@ -/* ANSI-C code produced by gperf version 3.0.3 */ -/* Command-line: gperf -L ANSI-C -C -p -j1 -i 1 -g -o -t -N mrb_reserved_word -k'1,3,$' src/keywords */ - -#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ - && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ - && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \ - && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \ - && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \ - && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \ - && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \ - && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \ - && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \ - && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \ - && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \ - && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \ - && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \ - && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \ - && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ - && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \ - && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \ - && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \ - && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \ - && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \ - && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \ - && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ - && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) -/* The character set is not based on ISO-646. */ -#error "gperf generated tables don't work with this execution character set. Please report a bug to ." -#endif - -#line 1 "src/keywords" - -struct kwtable {const char *name; int id[2]; enum mrb_lex_state_enum state;}; -const struct kwtable *mrb_reserved_word(const char *, unsigned int); -static const struct kwtable *reserved_word(const char *, unsigned int); -#define mrb_reserved_word(str, len) reserved_word(str, len) -#line 8 "src/keywords" -struct kwtable; - -#define TOTAL_KEYWORDS 40 -#define MIN_WORD_LENGTH 2 -#define MAX_WORD_LENGTH 12 -#define MIN_HASH_VALUE 8 -#define MAX_HASH_VALUE 50 -/* maximum key range = 43, duplicates = 0 */ - -#ifdef __GNUC__ -__inline -#else -#ifdef __cplusplus -inline -#endif -#endif -static unsigned int -hash (register const char *str, register unsigned int len) -{ - static const unsigned char asso_values[] = - { - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 14, 51, 16, 8, - 11, 13, 51, 51, 51, 51, 10, 51, 13, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 11, 51, 13, 1, 26, - 4, 1, 8, 28, 51, 23, 51, 1, 1, 27, - 5, 19, 21, 51, 8, 3, 3, 11, 51, 21, - 24, 16, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51 - }; - register int hval = len; - - switch (hval) - { - default: - hval += asso_values[(unsigned char)str[2]]; - /*FALLTHROUGH*/ - case 2: - case 1: - hval += asso_values[(unsigned char)str[0]]; - break; - } - return hval + asso_values[(unsigned char)str[len - 1]]; -} - -#ifdef __GNUC__ -__inline -#ifdef __GNUC_STDC_INLINE__ -__attribute__ ((__gnu_inline__)) -#endif -#endif -const struct kwtable * -mrb_reserved_word (register const char *str, register unsigned int len) -{ - static const struct kwtable wordlist[] = - { - {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, -#line 18 "src/keywords" - {"break", {keyword_break, keyword_break}, EXPR_MID}, -#line 23 "src/keywords" - {"else", {keyword_else, keyword_else}, EXPR_BEG}, -#line 33 "src/keywords" - {"nil", {keyword_nil, keyword_nil}, EXPR_END}, -#line 26 "src/keywords" - {"ensure", {keyword_ensure, keyword_ensure}, EXPR_BEG}, -#line 25 "src/keywords" - {"end", {keyword_end, keyword_end}, EXPR_END}, -#line 42 "src/keywords" - {"then", {keyword_then, keyword_then}, EXPR_BEG}, -#line 34 "src/keywords" - {"not", {keyword_not, keyword_not}, EXPR_ARG}, -#line 27 "src/keywords" - {"false", {keyword_false, keyword_false}, EXPR_END}, -#line 40 "src/keywords" - {"self", {keyword_self, keyword_self}, EXPR_END}, -#line 24 "src/keywords" - {"elsif", {keyword_elsif, keyword_elsif}, EXPR_VALUE}, -#line 37 "src/keywords" - {"rescue", {keyword_rescue, modifier_rescue}, EXPR_MID}, -#line 43 "src/keywords" - {"true", {keyword_true, keyword_true}, EXPR_END}, -#line 46 "src/keywords" - {"until", {keyword_until, modifier_until}, EXPR_VALUE}, -#line 45 "src/keywords" - {"unless", {keyword_unless, modifier_unless}, EXPR_VALUE}, -#line 39 "src/keywords" - {"return", {keyword_return, keyword_return}, EXPR_MID}, -#line 21 "src/keywords" - {"def", {keyword_def, keyword_def}, EXPR_FNAME}, -#line 16 "src/keywords" - {"and", {keyword_and, keyword_and}, EXPR_VALUE}, -#line 22 "src/keywords" - {"do", {keyword_do, keyword_do}, EXPR_BEG}, -#line 49 "src/keywords" - {"yield", {keyword_yield, keyword_yield}, EXPR_ARG}, -#line 28 "src/keywords" - {"for", {keyword_for, keyword_for}, EXPR_VALUE}, -#line 44 "src/keywords" - {"undef", {keyword_undef, keyword_undef}, EXPR_FNAME}, -#line 35 "src/keywords" - {"or", {keyword_or, keyword_or}, EXPR_VALUE}, -#line 30 "src/keywords" - {"in", {keyword_in, keyword_in}, EXPR_VALUE}, -#line 47 "src/keywords" - {"when", {keyword_when, keyword_when}, EXPR_VALUE}, -#line 38 "src/keywords" - {"retry", {keyword_retry, keyword_retry}, EXPR_END}, -#line 29 "src/keywords" - {"if", {keyword_if, modifier_if}, EXPR_VALUE}, -#line 19 "src/keywords" - {"case", {keyword_case, keyword_case}, EXPR_VALUE}, -#line 36 "src/keywords" - {"redo", {keyword_redo, keyword_redo}, EXPR_END}, -#line 32 "src/keywords" - {"next", {keyword_next, keyword_next}, EXPR_MID}, -#line 41 "src/keywords" - {"super", {keyword_super, keyword_super}, EXPR_ARG}, -#line 31 "src/keywords" - {"module", {keyword_module, keyword_module}, EXPR_VALUE}, -#line 17 "src/keywords" - {"begin", {keyword_begin, keyword_begin}, EXPR_BEG}, -#line 12 "src/keywords" - {"__LINE__", {keyword__LINE__, keyword__LINE__}, EXPR_END}, -#line 11 "src/keywords" - {"__FILE__", {keyword__FILE__, keyword__FILE__}, EXPR_END}, -#line 10 "src/keywords" - {"__ENCODING__", {keyword__ENCODING__, keyword__ENCODING__}, EXPR_END}, -#line 14 "src/keywords" - {"END", {keyword_END, keyword_END}, EXPR_END}, -#line 15 "src/keywords" - {"alias", {keyword_alias, keyword_alias}, EXPR_FNAME}, -#line 13 "src/keywords" - {"BEGIN", {keyword_BEGIN, keyword_BEGIN}, EXPR_END}, - {""}, -#line 20 "src/keywords" - {"class", {keyword_class, keyword_class}, EXPR_CLASS}, - {""}, {""}, -#line 48 "src/keywords" - {"while", {keyword_while, modifier_while}, EXPR_VALUE} - }; - - if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) - { - register int key = hash (str, len); - - if (key <= MAX_HASH_VALUE && key >= 0) - { - register const char *s = wordlist[key].name; - - if (*str == *s && !strcmp (str + 1, s + 1)) - return &wordlist[key]; - } - } - return 0; -} -#line 50 "src/keywords" - diff --git a/src/mruby_core.rake b/src/mruby_core.rake index 88fca83fc..abde441d5 100644 --- a/src/mruby_core.rake +++ b/src/mruby_core.rake @@ -3,76 +3,17 @@ MRuby.each_target do relative_from_root = File.dirname(__FILE__).relative_path_from(MRUBY_ROOT) current_build_dir = "#{build_dir}/#{relative_from_root}" - lex_def = "#{current_dir}/lex.def" objs = Dir.glob("#{current_dir}/*.c").map { |f| - next nil if cxx_abi_enabled? and f =~ /(codegen|error|vm).c$/ + next nil if cxx_abi_enabled? and f =~ /(error|vm).c$/ objfile(f.pathmap("#{current_build_dir}/%n")) }.compact if cxx_abi_enabled? - cxx_abi_dependency = %w(codegen error vm) - cxx_abi_objs = cxx_abi_dependency.map { |v| - src = "#{current_build_dir}/#{v}.cxx" - file src => ["#{current_dir}/#{v}.c", __FILE__] do |t| - File.open(t.name, 'w') do |f| - f.write < src do |t| - cxx.run t.name, t.prerequisites.first, [], [current_dir] - end - - objfile src - } - cxx_abi_objs << objfile("#{current_build_dir}/y.tab") - - file "#{current_build_dir}/y.tab.cxx" => ["#{current_build_dir}/y.tab.c", __FILE__] do |t| - File.open(t.name, 'w') do |f| - f.write < ["#{current_build_dir}/y.tab.cxx", lex_def] do |t| - cxx.run t.name, t.prerequisites.first, [], [current_dir] - end - - objs += cxx_abi_objs - else - objs += [objfile("#{current_build_dir}/y.tab")] - file objfile("#{current_build_dir}/y.tab") => ["#{current_build_dir}/y.tab.c", lex_def] do |t| - cc.run t.name, t.prerequisites.first, [], [current_dir] - end + objs += %w(vm error).map { |v| compile_as_cxx "#{current_dir}/#{v}.c", "#{current_build_dir}/#{v}.cxx" } end self.libmruby << objs file libfile("#{build_dir}/lib/libmruby_core") => objs do |t| archiver.run t.name, t.prerequisites end - - # Parser - file "#{current_build_dir}/y.tab.c" => ["#{current_dir}/parse.y"] do |t| - yacc.run t.name, t.prerequisites.first - end - - # Lexical analyzer - file lex_def => "#{current_dir}/keywords" do |t| - gperf.run t.name, t.prerequisites.first - end end diff --git a/src/node.h b/src/node.h deleted file mode 100644 index 532a8323a..000000000 --- a/src/node.h +++ /dev/null @@ -1,117 +0,0 @@ -/* -** node.h - nodes of abstract syntax tree -** -** See Copyright Notice in mruby.h -*/ - -#ifndef NODE_H -#define NODE_H - -enum node_type { - NODE_METHOD, - NODE_FBODY, - NODE_CFUNC, - NODE_SCOPE, - NODE_BLOCK, - NODE_IF, - NODE_CASE, - NODE_WHEN, - NODE_OPT_N, - NODE_WHILE, - NODE_UNTIL, - NODE_ITER, - NODE_FOR, - NODE_BREAK, - NODE_NEXT, - NODE_REDO, - NODE_RETRY, - NODE_BEGIN, - NODE_RESCUE, - NODE_ENSURE, - NODE_AND, - NODE_OR, - NODE_NOT, - NODE_MASGN, - NODE_ASGN, - NODE_CDECL, - NODE_CVASGN, - NODE_CVDECL, - NODE_OP_ASGN, - NODE_CALL, - NODE_FCALL, - NODE_VCALL, - NODE_SUPER, - NODE_ZSUPER, - NODE_ARRAY, - NODE_ZARRAY, - NODE_HASH, - NODE_RETURN, - NODE_YIELD, - NODE_LVAR, - NODE_DVAR, - NODE_GVAR, - NODE_IVAR, - NODE_CONST, - NODE_CVAR, - NODE_NTH_REF, - NODE_BACK_REF, - NODE_MATCH, - NODE_MATCH2, - NODE_MATCH3, - NODE_INT, - NODE_FLOAT, - NODE_NEGATE, - NODE_LAMBDA, - NODE_SYM, - NODE_STR, - NODE_DSTR, - NODE_XSTR, - NODE_DXSTR, - NODE_REGX, - NODE_DREGX, - NODE_DREGX_ONCE, - NODE_LIST, - NODE_ARG, - NODE_ARGSCAT, - NODE_ARGSPUSH, - NODE_SPLAT, - NODE_TO_ARY, - NODE_SVALUE, - NODE_BLOCK_ARG, - NODE_DEF, - NODE_SDEF, - NODE_ALIAS, - NODE_UNDEF, - NODE_CLASS, - NODE_MODULE, - NODE_SCLASS, - NODE_COLON2, - NODE_COLON3, - NODE_CREF, - NODE_DOT2, - NODE_DOT3, - NODE_FLIP2, - NODE_FLIP3, - NODE_ATTRSET, - NODE_SELF, - NODE_NIL, - NODE_TRUE, - NODE_FALSE, - NODE_DEFINED, - NODE_NEWLINE, - NODE_POSTEXE, - NODE_ALLOCA, - NODE_DMETHOD, - NODE_BMETHOD, - NODE_MEMO, - NODE_IFUNC, - NODE_DSYM, - NODE_ATTRASGN, - NODE_HEREDOC, - NODE_LITERAL_DELIM, - NODE_WORDS, - NODE_SYMBOLS, - NODE_LAST -}; - -#endif /* NODE_H */ diff --git a/src/parse.y b/src/parse.y deleted file mode 100644 index 5b17649a9..000000000 --- a/src/parse.y +++ /dev/null @@ -1,6420 +0,0 @@ -/* -** parse.y - mruby parser -** -** See Copyright Notice in mruby.h -*/ - -%{ -#undef PARSER_DEBUG -#ifdef PARSER_DEBUG -# define YYDEBUG 1 -#endif -#define YYERROR_VERBOSE 1 -/* - * Force yacc to use our memory management. This is a little evil because - * the macros assume that "parser_state *p" is in scope - */ -#define YYMALLOC(n) mrb_malloc(p->mrb, (n)) -#define YYFREE(o) mrb_free(p->mrb, (o)) -#define YYSTACK_USE_ALLOCA 0 - -#include -#include -#include -#include -#include "mruby.h" -#include "mruby/compile.h" -#include "mruby/proc.h" -#include "mruby/error.h" -#include "node.h" -#include "mruby/throw.h" - -#define YYLEX_PARAM p - -typedef mrb_ast_node node; -typedef struct mrb_parser_state parser_state; -typedef struct mrb_parser_heredoc_info parser_heredoc_info; - -static int yyparse(parser_state *p); -static int yylex(void *lval, parser_state *p); -static void yyerror(parser_state *p, const char *s); -static void yywarn(parser_state *p, const char *s); -static void yywarning(parser_state *p, const char *s); -static void backref_error(parser_state *p, node *n); -static void tokadd(parser_state *p, int32_t c); - -#define identchar(c) (ISALNUM(c) || (c) == '_' || !ISASCII(c)) - -typedef unsigned int stack_type; - -#define BITSTACK_PUSH(stack, n) ((stack) = ((stack)<<1)|((n)&1)) -#define BITSTACK_POP(stack) ((stack) = (stack) >> 1) -#define BITSTACK_LEXPOP(stack) ((stack) = ((stack) >> 1) | ((stack) & 1)) -#define BITSTACK_SET_P(stack) ((stack)&1) - -#define COND_PUSH(n) BITSTACK_PUSH(p->cond_stack, (n)) -#define COND_POP() BITSTACK_POP(p->cond_stack) -#define COND_LEXPOP() BITSTACK_LEXPOP(p->cond_stack) -#define COND_P() BITSTACK_SET_P(p->cond_stack) - -#define CMDARG_PUSH(n) BITSTACK_PUSH(p->cmdarg_stack, (n)) -#define CMDARG_POP() BITSTACK_POP(p->cmdarg_stack) -#define CMDARG_LEXPOP() BITSTACK_LEXPOP(p->cmdarg_stack) -#define CMDARG_P() BITSTACK_SET_P(p->cmdarg_stack) - -#define SET_LINENO(c,n) ((c)->lineno = (n)) -#define NODE_LINENO(c,n) do {\ - if (n) {\ - (c)->filename_index = (n)->filename_index;\ - (c)->lineno = (n)->lineno;\ - }\ -} while (0) - -#define sym(x) ((mrb_sym)(intptr_t)(x)) -#define nsym(x) ((node*)(intptr_t)(x)) - -static inline mrb_sym -intern_cstr_gen(parser_state *p, const char *s) -{ - return mrb_intern_cstr(p->mrb, s); -} -#define intern_cstr(s) intern_cstr_gen(p,(s)) - -static inline mrb_sym -intern_gen(parser_state *p, const char *s, size_t len) -{ - return mrb_intern(p->mrb, s, len); -} -#define intern(s,len) intern_gen(p,(s),(len)) - -static inline mrb_sym -intern_gen_c(parser_state *p, const char c) -{ - return mrb_intern(p->mrb, &c, 1); -} -#define intern_c(c) intern_gen_c(p,(c)) - -static void -cons_free_gen(parser_state *p, node *cons) -{ - cons->cdr = p->cells; - p->cells = cons; -} -#define cons_free(c) cons_free_gen(p, (c)) - -static void* -parser_palloc(parser_state *p, size_t size) -{ - void *m = mrb_pool_alloc(p->pool, size); - - if (!m) { - MRB_THROW(p->jmp); - } - return m; -} - -static node* -cons_gen(parser_state *p, node *car, node *cdr) -{ - node *c; - - if (p->cells) { - c = p->cells; - p->cells = p->cells->cdr; - } - else { - c = (node *)parser_palloc(p, sizeof(mrb_ast_node)); - } - - c->car = car; - c->cdr = cdr; - c->lineno = p->lineno; - c->filename_index = p->current_filename_index; - return c; -} -#define cons(a,b) cons_gen(p,(a),(b)) - -static node* -list1_gen(parser_state *p, node *a) -{ - return cons(a, 0); -} -#define list1(a) list1_gen(p, (a)) - -static node* -list2_gen(parser_state *p, node *a, node *b) -{ - return cons(a, cons(b,0)); -} -#define list2(a,b) list2_gen(p, (a),(b)) - -static node* -list3_gen(parser_state *p, node *a, node *b, node *c) -{ - return cons(a, cons(b, cons(c,0))); -} -#define list3(a,b,c) list3_gen(p, (a),(b),(c)) - -static node* -list4_gen(parser_state *p, node *a, node *b, node *c, node *d) -{ - return cons(a, cons(b, cons(c, cons(d, 0)))); -} -#define list4(a,b,c,d) list4_gen(p, (a),(b),(c),(d)) - -static node* -list5_gen(parser_state *p, node *a, node *b, node *c, node *d, node *e) -{ - return cons(a, cons(b, cons(c, cons(d, cons(e, 0))))); -} -#define list5(a,b,c,d,e) list5_gen(p, (a),(b),(c),(d),(e)) - -static node* -list6_gen(parser_state *p, node *a, node *b, node *c, node *d, node *e, node *f) -{ - return cons(a, cons(b, cons(c, cons(d, cons(e, cons(f, 0)))))); -} -#define list6(a,b,c,d,e,f) list6_gen(p, (a),(b),(c),(d),(e),(f)) - -static node* -append_gen(parser_state *p, node *a, node *b) -{ - node *c = a; - - if (!a) return b; - while (c->cdr) { - c = c->cdr; - } - if (b) { - c->cdr = b; - } - return a; -} -#define append(a,b) append_gen(p,(a),(b)) -#define push(a,b) append_gen(p,(a),list1(b)) - -static char* -parser_strndup(parser_state *p, const char *s, size_t len) -{ - char *b = (char *)parser_palloc(p, len+1); - - memcpy(b, s, len); - b[len] = '\0'; - return b; -} -#undef strndup -#define strndup(s,len) parser_strndup(p, s, len) - -static char* -parser_strdup(parser_state *p, const char *s) -{ - return parser_strndup(p, s, strlen(s)); -} -#undef strdup -#define strdup(s) parser_strdup(p, s) - -/* xxx ----------------------------- */ - -static node* -local_switch(parser_state *p) -{ - node *prev = p->locals; - - p->locals = cons(0, 0); - return prev; -} - -static void -local_resume(parser_state *p, node *prev) -{ - p->locals = prev; -} - -static void -local_nest(parser_state *p) -{ - p->locals = cons(0, p->locals); -} - -static void -local_unnest(parser_state *p) -{ - if (p->locals) { - p->locals = p->locals->cdr; - } -} - -static mrb_bool -local_var_p(parser_state *p, mrb_sym sym) -{ - node *l = p->locals; - - while (l) { - node *n = l->car; - while (n) { - if (sym(n->car) == sym) return TRUE; - n = n->cdr; - } - l = l->cdr; - } - return FALSE; -} - -static void -local_add_f(parser_state *p, mrb_sym sym) -{ - if (p->locals) { - p->locals->car = push(p->locals->car, nsym(sym)); - } -} - -static void -local_add(parser_state *p, mrb_sym sym) -{ - if (!local_var_p(p, sym)) { - local_add_f(p, sym); - } -} - -static node* -locals_node(parser_state *p) -{ - return p->locals ? p->locals->car : NULL; -} - -/* (:scope (vars..) (prog...)) */ -static node* -new_scope(parser_state *p, node *body) -{ - return cons((node*)NODE_SCOPE, cons(locals_node(p), body)); -} - -/* (:begin prog...) */ -static node* -new_begin(parser_state *p, node *body) -{ - if (body) { - return list2((node*)NODE_BEGIN, body); - } - return cons((node*)NODE_BEGIN, 0); -} - -#define newline_node(n) (n) - -/* (:rescue body rescue else) */ -static node* -new_rescue(parser_state *p, node *body, node *resq, node *els) -{ - return list4((node*)NODE_RESCUE, body, resq, els); -} - -/* (:ensure body ensure) */ -static node* -new_ensure(parser_state *p, node *a, node *b) -{ - return cons((node*)NODE_ENSURE, cons(a, cons(0, b))); -} - -/* (:nil) */ -static node* -new_nil(parser_state *p) -{ - return list1((node*)NODE_NIL); -} - -/* (:true) */ -static node* -new_true(parser_state *p) -{ - return list1((node*)NODE_TRUE); -} - -/* (:false) */ -static node* -new_false(parser_state *p) -{ - return list1((node*)NODE_FALSE); -} - -/* (:alias new old) */ -static node* -new_alias(parser_state *p, mrb_sym a, mrb_sym b) -{ - return cons((node*)NODE_ALIAS, cons(nsym(a), nsym(b))); -} - -/* (:if cond then else) */ -static node* -new_if(parser_state *p, node *a, node *b, node *c) -{ - return list4((node*)NODE_IF, a, b, c); -} - -/* (:unless cond then else) */ -static node* -new_unless(parser_state *p, node *a, node *b, node *c) -{ - return list4((node*)NODE_IF, a, c, b); -} - -/* (:while cond body) */ -static node* -new_while(parser_state *p, node *a, node *b) -{ - return cons((node*)NODE_WHILE, cons(a, b)); -} - -/* (:until cond body) */ -static node* -new_until(parser_state *p, node *a, node *b) -{ - return cons((node*)NODE_UNTIL, cons(a, b)); -} - -/* (:for var obj body) */ -static node* -new_for(parser_state *p, node *v, node *o, node *b) -{ - return list4((node*)NODE_FOR, v, o, b); -} - -/* (:case a ((when ...) body) ((when...) body)) */ -static node* -new_case(parser_state *p, node *a, node *b) -{ - node *n = list2((node*)NODE_CASE, a); - node *n2 = n; - - while (n2->cdr) { - n2 = n2->cdr; - } - n2->cdr = b; - return n; -} - -/* (:postexe a) */ -static node* -new_postexe(parser_state *p, node *a) -{ - return cons((node*)NODE_POSTEXE, a); -} - -/* (:self) */ -static node* -new_self(parser_state *p) -{ - return list1((node*)NODE_SELF); -} - -/* (:call a b c) */ -static node* -new_call(parser_state *p, node *a, mrb_sym b, node *c) -{ - node *n = list4((node*)NODE_CALL, a, nsym(b), c); - NODE_LINENO(n, a); - return n; -} - -/* (:fcall self mid args) */ -static node* -new_fcall(parser_state *p, mrb_sym b, node *c) -{ - node *n = new_self(p); - NODE_LINENO(n, c); - n = list4((node*)NODE_FCALL, n, nsym(b), c); - NODE_LINENO(n, c); - return n; -} - -/* (:super . c) */ -static node* -new_super(parser_state *p, node *c) -{ - return cons((node*)NODE_SUPER, c); -} - -/* (:zsuper) */ -static node* -new_zsuper(parser_state *p) -{ - return list1((node*)NODE_ZSUPER); -} - -/* (:yield . c) */ -static node* -new_yield(parser_state *p, node *c) -{ - if (c) { - if (c->cdr) { - yyerror(p, "both block arg and actual block given"); - } - return cons((node*)NODE_YIELD, c->car); - } - return cons((node*)NODE_YIELD, 0); -} - -/* (:return . c) */ -static node* -new_return(parser_state *p, node *c) -{ - return cons((node*)NODE_RETURN, c); -} - -/* (:break . c) */ -static node* -new_break(parser_state *p, node *c) -{ - return cons((node*)NODE_BREAK, c); -} - -/* (:next . c) */ -static node* -new_next(parser_state *p, node *c) -{ - return cons((node*)NODE_NEXT, c); -} - -/* (:redo) */ -static node* -new_redo(parser_state *p) -{ - return list1((node*)NODE_REDO); -} - -/* (:retry) */ -static node* -new_retry(parser_state *p) -{ - return list1((node*)NODE_RETRY); -} - -/* (:dot2 a b) */ -static node* -new_dot2(parser_state *p, node *a, node *b) -{ - return cons((node*)NODE_DOT2, cons(a, b)); -} - -/* (:dot3 a b) */ -static node* -new_dot3(parser_state *p, node *a, node *b) -{ - return cons((node*)NODE_DOT3, cons(a, b)); -} - -/* (:colon2 b c) */ -static node* -new_colon2(parser_state *p, node *b, mrb_sym c) -{ - return cons((node*)NODE_COLON2, cons(b, nsym(c))); -} - -/* (:colon3 . c) */ -static node* -new_colon3(parser_state *p, mrb_sym c) -{ - return cons((node*)NODE_COLON3, nsym(c)); -} - -/* (:and a b) */ -static node* -new_and(parser_state *p, node *a, node *b) -{ - return cons((node*)NODE_AND, cons(a, b)); -} - -/* (:or a b) */ -static node* -new_or(parser_state *p, node *a, node *b) -{ - return cons((node*)NODE_OR, cons(a, b)); -} - -/* (:array a...) */ -static node* -new_array(parser_state *p, node *a) -{ - return cons((node*)NODE_ARRAY, a); -} - -/* (:splat . a) */ -static node* -new_splat(parser_state *p, node *a) -{ - return cons((node*)NODE_SPLAT, a); -} - -/* (:hash (k . v) (k . v)...) */ -static node* -new_hash(parser_state *p, node *a) -{ - return cons((node*)NODE_HASH, a); -} - -/* (:sym . a) */ -static node* -new_sym(parser_state *p, mrb_sym sym) -{ - return cons((node*)NODE_SYM, nsym(sym)); -} - -static mrb_sym -new_strsym(parser_state *p, node* str) -{ - const char *s = (const char*)str->cdr->car; - size_t len = (size_t)str->cdr->cdr; - - return mrb_intern(p->mrb, s, len); -} - -/* (:lvar . a) */ -static node* -new_lvar(parser_state *p, mrb_sym sym) -{ - return cons((node*)NODE_LVAR, nsym(sym)); -} - -/* (:gvar . a) */ -static node* -new_gvar(parser_state *p, mrb_sym sym) -{ - return cons((node*)NODE_GVAR, nsym(sym)); -} - -/* (:ivar . a) */ -static node* -new_ivar(parser_state *p, mrb_sym sym) -{ - return cons((node*)NODE_IVAR, nsym(sym)); -} - -/* (:cvar . a) */ -static node* -new_cvar(parser_state *p, mrb_sym sym) -{ - return cons((node*)NODE_CVAR, nsym(sym)); -} - -/* (:const . a) */ -static node* -new_const(parser_state *p, mrb_sym sym) -{ - return cons((node*)NODE_CONST, nsym(sym)); -} - -/* (:undef a...) */ -static node* -new_undef(parser_state *p, mrb_sym sym) -{ - return list2((node*)NODE_UNDEF, nsym(sym)); -} - -/* (:class class super body) */ -static node* -new_class(parser_state *p, node *c, node *s, node *b) -{ - return list4((node*)NODE_CLASS, c, s, cons(locals_node(p), b)); -} - -/* (:sclass obj body) */ -static node* -new_sclass(parser_state *p, node *o, node *b) -{ - return list3((node*)NODE_SCLASS, o, cons(locals_node(p), b)); -} - -/* (:module module body) */ -static node* -new_module(parser_state *p, node *m, node *b) -{ - return list3((node*)NODE_MODULE, m, cons(locals_node(p), b)); -} - -/* (:def m lv (arg . body)) */ -static node* -new_def(parser_state *p, mrb_sym m, node *a, node *b) -{ - return list5((node*)NODE_DEF, nsym(m), locals_node(p), a, b); -} - -/* (:sdef obj m lv (arg . body)) */ -static node* -new_sdef(parser_state *p, node *o, mrb_sym m, node *a, node *b) -{ - return list6((node*)NODE_SDEF, o, nsym(m), locals_node(p), a, b); -} - -/* (:arg . sym) */ -static node* -new_arg(parser_state *p, mrb_sym sym) -{ - return cons((node*)NODE_ARG, nsym(sym)); -} - -/* (m o r m2 b) */ -/* m: (a b c) */ -/* o: ((a . e1) (b . e2)) */ -/* r: a */ -/* m2: (a b c) */ -/* b: a */ -static node* -new_args(parser_state *p, node *m, node *opt, mrb_sym rest, node *m2, mrb_sym blk) -{ - node *n; - - n = cons(m2, nsym(blk)); - n = cons(nsym(rest), n); - n = cons(opt, n); - return cons(m, n); -} - -/* (:block_arg . a) */ -static node* -new_block_arg(parser_state *p, node *a) -{ - return cons((node*)NODE_BLOCK_ARG, a); -} - -/* (:block arg body) */ -static node* -new_block(parser_state *p, node *a, node *b) -{ - return list4((node*)NODE_BLOCK, locals_node(p), a, b); -} - -/* (:lambda arg body) */ -static node* -new_lambda(parser_state *p, node *a, node *b) -{ - return list4((node*)NODE_LAMBDA, locals_node(p), a, b); -} - -/* (:asgn lhs rhs) */ -static node* -new_asgn(parser_state *p, node *a, node *b) -{ - return cons((node*)NODE_ASGN, cons(a, b)); -} - -/* (:masgn mlhs=(pre rest post) mrhs) */ -static node* -new_masgn(parser_state *p, node *a, node *b) -{ - return cons((node*)NODE_MASGN, cons(a, b)); -} - -/* (:asgn lhs rhs) */ -static node* -new_op_asgn(parser_state *p, node *a, mrb_sym op, node *b) -{ - return list4((node*)NODE_OP_ASGN, a, nsym(op), b); -} - -/* (:int . i) */ -static node* -new_int(parser_state *p, const char *s, int base) -{ - return list3((node*)NODE_INT, (node*)strdup(s), (node*)(intptr_t)base); -} - -/* (:float . i) */ -static node* -new_float(parser_state *p, const char *s) -{ - return cons((node*)NODE_FLOAT, (node*)strdup(s)); -} - -/* (:str . (s . len)) */ -static node* -new_str(parser_state *p, const char *s, int len) -{ - return cons((node*)NODE_STR, cons((node*)strndup(s, len), (node*)(intptr_t)len)); -} - -/* (:dstr . a) */ -static node* -new_dstr(parser_state *p, node *a) -{ - return cons((node*)NODE_DSTR, a); -} - -/* (:str . (s . len)) */ -static node* -new_xstr(parser_state *p, const char *s, int len) -{ - return cons((node*)NODE_XSTR, cons((node*)strndup(s, len), (node*)(intptr_t)len)); -} - -/* (:xstr . a) */ -static node* -new_dxstr(parser_state *p, node *a) -{ - return cons((node*)NODE_DXSTR, a); -} - -/* (:dsym . a) */ -static node* -new_dsym(parser_state *p, node *a) -{ - return cons((node*)NODE_DSYM, new_dstr(p, a)); -} - -/* (:str . (a . a)) */ -static node* -new_regx(parser_state *p, const char *p1, const char* p2) -{ - return cons((node*)NODE_REGX, cons((node*)p1, (node*)p2)); -} - -/* (:dregx . a) */ -static node* -new_dregx(parser_state *p, node *a, node *b) -{ - return cons((node*)NODE_DREGX, cons(a, b)); -} - -/* (:backref . n) */ -static node* -new_back_ref(parser_state *p, int n) -{ - return cons((node*)NODE_BACK_REF, (node*)(intptr_t)n); -} - -/* (:nthref . n) */ -static node* -new_nth_ref(parser_state *p, int n) -{ - return cons((node*)NODE_NTH_REF, (node*)(intptr_t)n); -} - -/* (:heredoc . a) */ -static node* -new_heredoc(parser_state *p) -{ - parser_heredoc_info *inf = (parser_heredoc_info *)parser_palloc(p, sizeof(parser_heredoc_info)); - return cons((node*)NODE_HEREDOC, (node*)inf); -} - -static void -new_bv(parser_state *p, mrb_sym id) -{ -} - -static node* -new_literal_delim(parser_state *p) -{ - return cons((node*)NODE_LITERAL_DELIM, 0); -} - -/* (:words . a) */ -static node* -new_words(parser_state *p, node *a) -{ - return cons((node*)NODE_WORDS, a); -} - -/* (:symbols . a) */ -static node* -new_symbols(parser_state *p, node *a) -{ - return cons((node*)NODE_SYMBOLS, a); -} - -/* xxx ----------------------------- */ - -/* (:call a op) */ -static node* -call_uni_op(parser_state *p, node *recv, const char *m) -{ - return new_call(p, recv, intern_cstr(m), 0); -} - -/* (:call a op b) */ -static node* -call_bin_op(parser_state *p, node *recv, const char *m, node *arg1) -{ - return new_call(p, recv, intern_cstr(m), list1(list1(arg1))); -} - -static void -args_with_block(parser_state *p, node *a, node *b) -{ - if (b) { - if (a->cdr) { - yyerror(p, "both block arg and actual block given"); - } - a->cdr = b; - } -} - -static void -call_with_block(parser_state *p, node *a, node *b) -{ - node *n; - - if (a->car == (node*)NODE_SUPER || - a->car == (node*)NODE_ZSUPER) { - if (!a->cdr) a->cdr = cons(0, b); - else { - args_with_block(p, a->cdr, b); - } - } - else { - n = a->cdr->cdr->cdr; - if (!n->car) n->car = cons(0, b); - else { - args_with_block(p, n->car, b); - } - } -} - -static node* -negate_lit(parser_state *p, node *n) -{ - return cons((node*)NODE_NEGATE, n); -} - -static node* -cond(node *n) -{ - return n; -} - -static node* -ret_args(parser_state *p, node *n) -{ - if (n->cdr) { - yyerror(p, "block argument should not be given"); - return NULL; - } - if (!n->car->cdr) return n->car->car; - return new_array(p, n->car); -} - -static void -assignable(parser_state *p, node *lhs) -{ - if ((int)(intptr_t)lhs->car == NODE_LVAR) { - local_add(p, sym(lhs->cdr)); - } -} - -static node* -var_reference(parser_state *p, node *lhs) -{ - node *n; - - if ((int)(intptr_t)lhs->car == NODE_LVAR) { - if (!local_var_p(p, sym(lhs->cdr))) { - n = new_fcall(p, sym(lhs->cdr), 0); - cons_free(lhs); - return n; - } - } - - return lhs; -} - -typedef enum mrb_string_type string_type; - -static node* -new_strterm(parser_state *p, string_type type, int term, int paren) -{ - return cons((node*)(intptr_t)type, cons((node*)0, cons((node*)(intptr_t)paren, (node*)(intptr_t)term))); -} - -static void -end_strterm(parser_state *p) -{ - cons_free(p->lex_strterm->cdr->cdr); - cons_free(p->lex_strterm->cdr); - cons_free(p->lex_strterm); - p->lex_strterm = NULL; -} - -static parser_heredoc_info * -parsing_heredoc_inf(parser_state *p) -{ - node *nd = p->parsing_heredoc; - if (nd == NULL) - return NULL; - /* mrb_assert(nd->car->car == NODE_HEREDOC); */ - return (parser_heredoc_info*)nd->car->cdr; -} - -static void -heredoc_treat_nextline(parser_state *p) -{ - if (p->heredocs_from_nextline == NULL) - return; - if (p->parsing_heredoc == NULL) { - node *n; - p->parsing_heredoc = p->heredocs_from_nextline; - p->lex_strterm_before_heredoc = p->lex_strterm; - p->lex_strterm = new_strterm(p, parsing_heredoc_inf(p)->type, 0, 0); - n = p->all_heredocs; - if (n) { - while (n->cdr) - n = n->cdr; - n->cdr = p->parsing_heredoc; - } - else { - p->all_heredocs = p->parsing_heredoc; - } - } - else { - node *n, *m; - m = p->heredocs_from_nextline; - while (m->cdr) - m = m->cdr; - n = p->all_heredocs; - mrb_assert(n != NULL); - if (n == p->parsing_heredoc) { - m->cdr = n; - p->all_heredocs = p->heredocs_from_nextline; - p->parsing_heredoc = p->heredocs_from_nextline; - } - else { - while (n->cdr != p->parsing_heredoc) { - n = n->cdr; - mrb_assert(n != NULL); - } - m->cdr = n->cdr; - n->cdr = p->heredocs_from_nextline; - p->parsing_heredoc = p->heredocs_from_nextline; - } - } - p->heredocs_from_nextline = NULL; -} - -static void -heredoc_end(parser_state *p) -{ - p->parsing_heredoc = p->parsing_heredoc->cdr; - if (p->parsing_heredoc == NULL) { - p->lstate = EXPR_BEG; - p->cmd_start = TRUE; - end_strterm(p); - p->lex_strterm = p->lex_strterm_before_heredoc; - p->lex_strterm_before_heredoc = NULL; - p->heredoc_end_now = TRUE; - } - else { - /* next heredoc */ - p->lex_strterm->car = (node*)(intptr_t)parsing_heredoc_inf(p)->type; - } -} -#define is_strterm_type(p,str_func) ((int)(intptr_t)((p)->lex_strterm->car) & (str_func)) - -/* xxx ----------------------------- */ - -%} - -%pure-parser -%parse-param {parser_state *p} -%lex-param {parser_state *p} - -%union { - node *nd; - mrb_sym id; - int num; - stack_type stack; - const struct vtable *vars; -} - -%token - keyword_class - keyword_module - keyword_def - keyword_begin - keyword_if - keyword_unless - keyword_while - keyword_until - keyword_for - -%token - keyword_undef - keyword_rescue - keyword_ensure - keyword_end - keyword_then - keyword_elsif - keyword_else - keyword_case - keyword_when - keyword_break - keyword_next - keyword_redo - keyword_retry - keyword_in - keyword_do - keyword_do_cond - keyword_do_block - keyword_do_LAMBDA - keyword_return - keyword_yield - keyword_super - keyword_self - keyword_nil - keyword_true - keyword_false - keyword_and - keyword_or - keyword_not - modifier_if - modifier_unless - modifier_while - modifier_until - modifier_rescue - keyword_alias - keyword_BEGIN - keyword_END - keyword__LINE__ - keyword__FILE__ - keyword__ENCODING__ - -%token tIDENTIFIER tFID tGVAR tIVAR tCONSTANT tCVAR tLABEL -%token tINTEGER tFLOAT tCHAR tXSTRING tREGEXP -%token tSTRING tSTRING_PART tSTRING_MID -%token tNTH_REF tBACK_REF -%token tREGEXP_END - -%type singleton string string_rep string_interp xstring regexp -%type literal numeric cpath symbol -%type top_compstmt top_stmts top_stmt -%type bodystmt compstmt stmts stmt expr arg primary command command_call method_call -%type expr_value arg_value primary_value -%type if_tail opt_else case_body cases opt_rescue exc_list exc_var opt_ensure -%type args call_args opt_call_args -%type paren_args opt_paren_args variable -%type command_args aref_args opt_block_arg block_arg var_ref var_lhs -%type command_asgn mrhs superclass block_call block_command -%type f_block_optarg f_block_opt -%type f_arglist f_args f_arg f_arg_item f_optarg f_marg f_marg_list f_margs -%type assoc_list assocs assoc undef_list backref for_var -%type block_param opt_block_param block_param_def f_opt -%type bv_decls opt_bv_decl bvar f_larglist lambda_body -%type brace_block cmd_brace_block do_block lhs none f_bad_arg -%type mlhs mlhs_list mlhs_post mlhs_basic mlhs_item mlhs_node mlhs_inner -%type fsym sym basic_symbol operation operation2 operation3 -%type cname fname op f_rest_arg f_block_arg opt_f_block_arg f_norm_arg f_opt_asgn -%type heredoc words symbols - -%token tUPLUS /* unary+ */ -%token tUMINUS /* unary- */ -%token tPOW /* ** */ -%token tCMP /* <=> */ -%token tEQ /* == */ -%token tEQQ /* === */ -%token tNEQ /* != */ -%token tGEQ /* >= */ -%token tLEQ /* <= */ -%token tANDOP tOROP /* && and || */ -%token tMATCH tNMATCH /* =~ and !~ */ -%token tDOT2 tDOT3 /* .. and ... */ -%token tAREF tASET /* [] and []= */ -%token tLSHFT tRSHFT /* << and >> */ -%token tCOLON2 /* :: */ -%token tCOLON3 /* :: at EXPR_BEG */ -%token tOP_ASGN /* +=, -= etc. */ -%token tASSOC /* => */ -%token tLPAREN /* ( */ -%token tLPAREN_ARG /* ( */ -%token tRPAREN /* ) */ -%token tLBRACK /* [ */ -%token tLBRACE /* { */ -%token tLBRACE_ARG /* { */ -%token tSTAR /* * */ -%token tAMPER /* & */ -%token tLAMBDA /* -> */ -%token tSYMBEG tREGEXP_BEG tWORDS_BEG tSYMBOLS_BEG -%token tSTRING_BEG tXSTRING_BEG tSTRING_DVAR tLAMBEG -%token tHEREDOC_BEG /* <<, <<- */ -%token tHEREDOC_END tLITERAL_DELIM tHD_LITERAL_DELIM -%token tHD_STRING_PART tHD_STRING_MID - -/* - * precedence table - */ - -%nonassoc tLOWEST -%nonassoc tLBRACE_ARG - -%nonassoc modifier_if modifier_unless modifier_while modifier_until -%left keyword_or keyword_and -%right keyword_not -%right '=' tOP_ASGN -%left modifier_rescue -%right '?' ':' -%nonassoc tDOT2 tDOT3 -%left tOROP -%left tANDOP -%nonassoc tCMP tEQ tEQQ tNEQ tMATCH tNMATCH -%left '>' tGEQ '<' tLEQ -%left '|' '^' -%left '&' -%left tLSHFT tRSHFT -%left '+' '-' -%left '*' '/' '%' -%right tUMINUS_NUM tUMINUS -%right tPOW -%right '!' '~' tUPLUS - -%token tLAST_TOKEN - -%% -program : { - p->lstate = EXPR_BEG; - if (!p->locals) p->locals = cons(0,0); - } - top_compstmt - { - p->tree = new_scope(p, $2); - NODE_LINENO(p->tree, $2); - } - ; - -top_compstmt : top_stmts opt_terms - { - $$ = $1; - } - ; - -top_stmts : none - { - $$ = new_begin(p, 0); - } - | top_stmt - { - $$ = new_begin(p, $1); - NODE_LINENO($$, $1); - } - | top_stmts terms top_stmt - { - $$ = push($1, newline_node($3)); - } - | error top_stmt - { - $$ = new_begin(p, 0); - } - ; - -top_stmt : stmt - | keyword_BEGIN - { - $$ = local_switch(p); - } - '{' top_compstmt '}' - { - yyerror(p, "BEGIN not supported"); - local_resume(p, $2); - $$ = 0; - } - ; - -bodystmt : compstmt - opt_rescue - opt_else - opt_ensure - { - if ($2) { - $$ = new_rescue(p, $1, $2, $3); - NODE_LINENO($$, $1); - } - else if ($3) { - yywarn(p, "else without rescue is useless"); - $$ = push($1, $3); - } - else { - $$ = $1; - } - if ($4) { - if ($$) { - $$ = new_ensure(p, $$, $4); - } - else { - $$ = push($4, new_nil(p)); - } - } - } - ; - -compstmt : stmts opt_terms - { - $$ = $1; - } - ; - -stmts : none - { - $$ = new_begin(p, 0); - } - | stmt - { - $$ = new_begin(p, $1); - NODE_LINENO($$, $1); - } - | stmts terms stmt - { - $$ = push($1, newline_node($3)); - } - | error stmt - { - $$ = new_begin(p, $2); - } - ; - -stmt : keyword_alias fsym {p->lstate = EXPR_FNAME;} fsym - { - $$ = new_alias(p, $2, $4); - } - | keyword_undef undef_list - { - $$ = $2; - } - | stmt modifier_if expr_value - { - $$ = new_if(p, cond($3), $1, 0); - } - | stmt modifier_unless expr_value - { - $$ = new_unless(p, cond($3), $1, 0); - } - | stmt modifier_while expr_value - { - $$ = new_while(p, cond($3), $1); - } - | stmt modifier_until expr_value - { - $$ = new_until(p, cond($3), $1); - } - | stmt modifier_rescue stmt - { - $$ = new_rescue(p, $1, list1(list3(0, 0, $3)), 0); - } - | keyword_END '{' compstmt '}' - { - yyerror(p, "END not suported"); - $$ = new_postexe(p, $3); - } - | command_asgn - | mlhs '=' command_call - { - $$ = new_masgn(p, $1, $3); - } - | var_lhs tOP_ASGN command_call - { - $$ = new_op_asgn(p, $1, $2, $3); - } - | primary_value '[' opt_call_args rbracket tOP_ASGN command_call - { - $$ = new_op_asgn(p, new_call(p, $1, intern("[]",2), $3), $5, $6); - } - | primary_value '.' tIDENTIFIER tOP_ASGN command_call - { - $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5); - } - | primary_value '.' tCONSTANT tOP_ASGN command_call - { - $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5); - } - | primary_value tCOLON2 tCONSTANT tOP_ASGN command_call - { - yyerror(p, "constant re-assignment"); - $$ = 0; - } - | primary_value tCOLON2 tIDENTIFIER tOP_ASGN command_call - { - $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5); - } - | backref tOP_ASGN command_call - { - backref_error(p, $1); - $$ = new_begin(p, 0); - } - | lhs '=' mrhs - { - $$ = new_asgn(p, $1, new_array(p, $3)); - } - | mlhs '=' arg_value - { - $$ = new_masgn(p, $1, $3); - } - | mlhs '=' mrhs - { - $$ = new_masgn(p, $1, new_array(p, $3)); - } - | expr - ; - -command_asgn : lhs '=' command_call - { - $$ = new_asgn(p, $1, $3); - } - | lhs '=' command_asgn - { - $$ = new_asgn(p, $1, $3); - } - ; - - -expr : command_call - | expr keyword_and expr - { - $$ = new_and(p, $1, $3); - } - | expr keyword_or expr - { - $$ = new_or(p, $1, $3); - } - | keyword_not opt_nl expr - { - $$ = call_uni_op(p, cond($3), "!"); - } - | '!' command_call - { - $$ = call_uni_op(p, cond($2), "!"); - } - | arg - ; - -expr_value : expr - { - if (!$1) $$ = new_nil(p); - else $$ = $1; - } - ; - -command_call : command - | block_command - ; - -block_command : block_call - | block_call dot_or_colon operation2 command_args - ; - -cmd_brace_block : tLBRACE_ARG - { - local_nest(p); - } - opt_block_param - compstmt - '}' - { - $$ = new_block(p, $3, $4); - local_unnest(p); - } - ; - -command : operation command_args %prec tLOWEST - { - $$ = new_fcall(p, $1, $2); - } - | operation command_args cmd_brace_block - { - args_with_block(p, $2, $3); - $$ = new_fcall(p, $1, $2); - } - | primary_value '.' operation2 command_args %prec tLOWEST - { - $$ = new_call(p, $1, $3, $4); - } - | primary_value '.' operation2 command_args cmd_brace_block - { - args_with_block(p, $4, $5); - $$ = new_call(p, $1, $3, $4); - } - | primary_value tCOLON2 operation2 command_args %prec tLOWEST - { - $$ = new_call(p, $1, $3, $4); - } - | primary_value tCOLON2 operation2 command_args cmd_brace_block - { - args_with_block(p, $4, $5); - $$ = new_call(p, $1, $3, $4); - } - | keyword_super command_args - { - $$ = new_super(p, $2); - } - | keyword_yield command_args - { - $$ = new_yield(p, $2); - } - | keyword_return call_args - { - $$ = new_return(p, ret_args(p, $2)); - } - | keyword_break call_args - { - $$ = new_break(p, ret_args(p, $2)); - } - | keyword_next call_args - { - $$ = new_next(p, ret_args(p, $2)); - } - ; - -mlhs : mlhs_basic - { - $$ = $1; - } - | tLPAREN mlhs_inner rparen - { - $$ = $2; - } - ; - -mlhs_inner : mlhs_basic - | tLPAREN mlhs_inner rparen - { - $$ = $2; - } - ; - -mlhs_basic : mlhs_list - { - $$ = list1($1); - } - | mlhs_list mlhs_item - { - $$ = list1(push($1,$2)); - } - | mlhs_list tSTAR mlhs_node - { - $$ = list2($1, $3); - } - | mlhs_list tSTAR mlhs_node ',' mlhs_post - { - $$ = list3($1, $3, $5); - } - | mlhs_list tSTAR - { - $$ = list2($1, new_nil(p)); - } - | mlhs_list tSTAR ',' mlhs_post - { - $$ = list3($1, new_nil(p), $4); - } - | tSTAR mlhs_node - { - $$ = list2(0, $2); - } - | tSTAR mlhs_node ',' mlhs_post - { - $$ = list3(0, $2, $4); - } - | tSTAR - { - $$ = list2(0, new_nil(p)); - } - | tSTAR ',' mlhs_post - { - $$ = list3(0, new_nil(p), $3); - } - ; - -mlhs_item : mlhs_node - | tLPAREN mlhs_inner rparen - { - $$ = new_masgn(p, $2, NULL); - } - ; - -mlhs_list : mlhs_item ',' - { - $$ = list1($1); - } - | mlhs_list mlhs_item ',' - { - $$ = push($1, $2); - } - ; - -mlhs_post : mlhs_item - { - $$ = list1($1); - } - | mlhs_list mlhs_item - { - $$ = push($1, $2); - } - ; - -mlhs_node : variable - { - assignable(p, $1); - } - | primary_value '[' opt_call_args rbracket - { - $$ = new_call(p, $1, intern("[]",2), $3); - } - | primary_value '.' tIDENTIFIER - { - $$ = new_call(p, $1, $3, 0); - } - | primary_value tCOLON2 tIDENTIFIER - { - $$ = new_call(p, $1, $3, 0); - } - | primary_value '.' tCONSTANT - { - $$ = new_call(p, $1, $3, 0); - } - | primary_value tCOLON2 tCONSTANT - { - if (p->in_def || p->in_single) - yyerror(p, "dynamic constant assignment"); - $$ = new_colon2(p, $1, $3); - } - | tCOLON3 tCONSTANT - { - if (p->in_def || p->in_single) - yyerror(p, "dynamic constant assignment"); - $$ = new_colon3(p, $2); - } - | backref - { - backref_error(p, $1); - $$ = 0; - } - ; - -lhs : variable - { - assignable(p, $1); - } - | primary_value '[' opt_call_args rbracket - { - $$ = new_call(p, $1, intern("[]",2), $3); - } - | primary_value '.' tIDENTIFIER - { - $$ = new_call(p, $1, $3, 0); - } - | primary_value tCOLON2 tIDENTIFIER - { - $$ = new_call(p, $1, $3, 0); - } - | primary_value '.' tCONSTANT - { - $$ = new_call(p, $1, $3, 0); - } - | primary_value tCOLON2 tCONSTANT - { - if (p->in_def || p->in_single) - yyerror(p, "dynamic constant assignment"); - $$ = new_colon2(p, $1, $3); - } - | tCOLON3 tCONSTANT - { - if (p->in_def || p->in_single) - yyerror(p, "dynamic constant assignment"); - $$ = new_colon3(p, $2); - } - | backref - { - backref_error(p, $1); - $$ = 0; - } - ; - -cname : tIDENTIFIER - { - yyerror(p, "class/module name must be CONSTANT"); - } - | tCONSTANT - ; - -cpath : tCOLON3 cname - { - $$ = cons((node*)1, nsym($2)); - } - | cname - { - $$ = cons((node*)0, nsym($1)); - } - | primary_value tCOLON2 cname - { - $$ = cons($1, nsym($3)); - } - ; - -fname : tIDENTIFIER - | tCONSTANT - | tFID - | op - { - p->lstate = EXPR_ENDFN; - $$ = $1; - } - | reswords - { - p->lstate = EXPR_ENDFN; - $$ = $1; - } - ; - -fsym : fname - | basic_symbol - ; - -undef_list : fsym - { - $$ = new_undef(p, $1); - } - | undef_list ',' {p->lstate = EXPR_FNAME;} fsym - { - $$ = push($1, nsym($4)); - } - ; - -op : '|' { $$ = intern_c('|'); } - | '^' { $$ = intern_c('^'); } - | '&' { $$ = intern_c('&'); } - | tCMP { $$ = intern("<=>",3); } - | tEQ { $$ = intern("==",2); } - | tEQQ { $$ = intern("===",3); } - | tMATCH { $$ = intern("=~",2); } - | tNMATCH { $$ = intern("!~",2); } - | '>' { $$ = intern_c('>'); } - | tGEQ { $$ = intern(">=",2); } - | '<' { $$ = intern_c('<'); } - | tLEQ { $$ = intern("<=",2); } - | tNEQ { $$ = intern("!=",2); } - | tLSHFT { $$ = intern("<<",2); } - | tRSHFT { $$ = intern(">>",2); } - | '+' { $$ = intern_c('+'); } - | '-' { $$ = intern_c('-'); } - | '*' { $$ = intern_c('*'); } - | tSTAR { $$ = intern_c('*'); } - | '/' { $$ = intern_c('/'); } - | '%' { $$ = intern_c('%'); } - | tPOW { $$ = intern("**",2); } - | '!' { $$ = intern_c('!'); } - | '~' { $$ = intern_c('~'); } - | tUPLUS { $$ = intern("+@",2); } - | tUMINUS { $$ = intern("-@",2); } - | tAREF { $$ = intern("[]",2); } - | tASET { $$ = intern("[]=",3); } - | '`' { $$ = intern_c('`'); } - ; - -reswords : keyword__LINE__ | keyword__FILE__ | keyword__ENCODING__ - | keyword_BEGIN | keyword_END - | keyword_alias | keyword_and | keyword_begin - | keyword_break | keyword_case | keyword_class | keyword_def - | keyword_do | keyword_else | keyword_elsif - | keyword_end | keyword_ensure | keyword_false - | keyword_for | keyword_in | keyword_module | keyword_next - | keyword_nil | keyword_not | keyword_or | keyword_redo - | keyword_rescue | keyword_retry | keyword_return | keyword_self - | keyword_super | keyword_then | keyword_true | keyword_undef - | keyword_when | keyword_yield | keyword_if | keyword_unless - | keyword_while | keyword_until - ; - -arg : lhs '=' arg - { - $$ = new_asgn(p, $1, $3); - } - | lhs '=' arg modifier_rescue arg - { - $$ = new_asgn(p, $1, new_rescue(p, $3, list1(list3(0, 0, $5)), 0)); - } - | var_lhs tOP_ASGN arg - { - $$ = new_op_asgn(p, $1, $2, $3); - } - | var_lhs tOP_ASGN arg modifier_rescue arg - { - $$ = new_op_asgn(p, $1, $2, new_rescue(p, $3, list1(list3(0, 0, $5)), 0)); - } - | primary_value '[' opt_call_args rbracket tOP_ASGN arg - { - $$ = new_op_asgn(p, new_call(p, $1, intern("[]",2), $3), $5, $6); - } - | primary_value '.' tIDENTIFIER tOP_ASGN arg - { - $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5); - } - | primary_value '.' tCONSTANT tOP_ASGN arg - { - $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5); - } - | primary_value tCOLON2 tIDENTIFIER tOP_ASGN arg - { - $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5); - } - | primary_value tCOLON2 tCONSTANT tOP_ASGN arg - { - yyerror(p, "constant re-assignment"); - $$ = new_begin(p, 0); - } - | tCOLON3 tCONSTANT tOP_ASGN arg - { - yyerror(p, "constant re-assignment"); - $$ = new_begin(p, 0); - } - | backref tOP_ASGN arg - { - backref_error(p, $1); - $$ = new_begin(p, 0); - } - | arg tDOT2 arg - { - $$ = new_dot2(p, $1, $3); - } - | arg tDOT3 arg - { - $$ = new_dot3(p, $1, $3); - } - | arg '+' arg - { - $$ = call_bin_op(p, $1, "+", $3); - } - | arg '-' arg - { - $$ = call_bin_op(p, $1, "-", $3); - } - | arg '*' arg - { - $$ = call_bin_op(p, $1, "*", $3); - } - | arg '/' arg - { - $$ = call_bin_op(p, $1, "/", $3); - } - | arg '%' arg - { - $$ = call_bin_op(p, $1, "%", $3); - } - | arg tPOW arg - { - $$ = call_bin_op(p, $1, "**", $3); - } - | tUMINUS_NUM tINTEGER tPOW arg - { - $$ = call_uni_op(p, call_bin_op(p, $2, "**", $4), "-@"); - } - | tUMINUS_NUM tFLOAT tPOW arg - { - $$ = call_uni_op(p, call_bin_op(p, $2, "**", $4), "-@"); - } - | tUPLUS arg - { - $$ = call_uni_op(p, $2, "+@"); - } - | tUMINUS arg - { - $$ = call_uni_op(p, $2, "-@"); - } - | arg '|' arg - { - $$ = call_bin_op(p, $1, "|", $3); - } - | arg '^' arg - { - $$ = call_bin_op(p, $1, "^", $3); - } - | arg '&' arg - { - $$ = call_bin_op(p, $1, "&", $3); - } - | arg tCMP arg - { - $$ = call_bin_op(p, $1, "<=>", $3); - } - | arg '>' arg - { - $$ = call_bin_op(p, $1, ">", $3); - } - | arg tGEQ arg - { - $$ = call_bin_op(p, $1, ">=", $3); - } - | arg '<' arg - { - $$ = call_bin_op(p, $1, "<", $3); - } - | arg tLEQ arg - { - $$ = call_bin_op(p, $1, "<=", $3); - } - | arg tEQ arg - { - $$ = call_bin_op(p, $1, "==", $3); - } - | arg tEQQ arg - { - $$ = call_bin_op(p, $1, "===", $3); - } - | arg tNEQ arg - { - $$ = call_bin_op(p, $1, "!=", $3); - } - | arg tMATCH arg - { - $$ = call_bin_op(p, $1, "=~", $3); - } - | arg tNMATCH arg - { - $$ = call_bin_op(p, $1, "!~", $3); - } - | '!' arg - { - $$ = call_uni_op(p, cond($2), "!"); - } - | '~' arg - { - $$ = call_uni_op(p, cond($2), "~"); - } - | arg tLSHFT arg - { - $$ = call_bin_op(p, $1, "<<", $3); - } - | arg tRSHFT arg - { - $$ = call_bin_op(p, $1, ">>", $3); - } - | arg tANDOP arg - { - $$ = new_and(p, $1, $3); - } - | arg tOROP arg - { - $$ = new_or(p, $1, $3); - } - | arg '?' arg opt_nl ':' arg - { - $$ = new_if(p, cond($1), $3, $6); - } - | primary - { - $$ = $1; - } - ; - -arg_value : arg - { - $$ = $1; - if (!$$) $$ = new_nil(p); - } - ; - -aref_args : none - | args trailer - { - $$ = $1; - NODE_LINENO($$, $1); - } - | args ',' assocs trailer - { - $$ = push($1, new_hash(p, $3)); - } - | assocs trailer - { - $$ = cons(new_hash(p, $1), 0); - NODE_LINENO($$, $1); - } - ; - -paren_args : '(' opt_call_args rparen - { - $$ = $2; - } - ; - -opt_paren_args : none - | paren_args - ; - -opt_call_args : none - | call_args - | args ',' - { - $$ = cons($1,0); - NODE_LINENO($$, $1); - } - | args ',' assocs ',' - { - $$ = cons(push($1, new_hash(p, $3)), 0); - NODE_LINENO($$, $1); - } - | assocs ',' - { - $$ = cons(list1(new_hash(p, $1)), 0); - NODE_LINENO($$, $1); - } - ; - -call_args : command - { - $$ = cons(list1($1), 0); - NODE_LINENO($$, $1); - } - | args opt_block_arg - { - $$ = cons($1, $2); - NODE_LINENO($$, $1); - } - | assocs opt_block_arg - { - $$ = cons(list1(new_hash(p, $1)), $2); - NODE_LINENO($$, $1); - } - | args ',' assocs opt_block_arg - { - $$ = cons(push($1, new_hash(p, $3)), $4); - NODE_LINENO($$, $1); - } - | block_arg - { - $$ = cons(0, $1); - NODE_LINENO($$, $1); - } - ; - -command_args : { - $$ = p->cmdarg_stack; - CMDARG_PUSH(1); - } - call_args - { - p->cmdarg_stack = $1; - $$ = $2; - } - ; - -block_arg : tAMPER arg_value - { - $$ = new_block_arg(p, $2); - } - ; - -opt_block_arg : ',' block_arg - { - $$ = $2; - } - | none - { - $$ = 0; - } - ; - -args : arg_value - { - $$ = cons($1, 0); - NODE_LINENO($$, $1); - } - | tSTAR arg_value - { - $$ = cons(new_splat(p, $2), 0); - NODE_LINENO($$, $2); - } - | args ',' arg_value - { - $$ = push($1, $3); - } - | args ',' tSTAR arg_value - { - $$ = push($1, new_splat(p, $4)); - } - | args ',' heredoc_bodies arg_value - { - $$ = push($1, $4); - } - | args ',' heredoc_bodies tSTAR arg_value - { - $$ = push($1, new_splat(p, $5)); - } - ; - -mrhs : args ',' arg_value - { - $$ = push($1, $3); - } - | args ',' tSTAR arg_value - { - $$ = push($1, new_splat(p, $4)); - } - | tSTAR arg_value - { - $$ = list1(new_splat(p, $2)); - } - ; - -primary : literal - | string - | xstring - | regexp - | heredoc - | var_ref - | backref - | tFID - { - $$ = new_fcall(p, $1, 0); - } - | keyword_begin - { - $$ = p->cmdarg_stack; - p->cmdarg_stack = 0; - } - bodystmt - keyword_end - { - p->cmdarg_stack = $2; - $$ = $3; - } - | tLPAREN_ARG - { - $$ = p->cmdarg_stack; - p->cmdarg_stack = 0; - } - expr {p->lstate = EXPR_ENDARG;} rparen - { - p->cmdarg_stack = $2; - $$ = $3; - } - | tLPAREN_ARG {p->lstate = EXPR_ENDARG;} rparen - { - $$ = 0; - } - | tLPAREN compstmt ')' - { - $$ = $2; - } - | primary_value tCOLON2 tCONSTANT - { - $$ = new_colon2(p, $1, $3); - } - | tCOLON3 tCONSTANT - { - $$ = new_colon3(p, $2); - } - | tLBRACK aref_args ']' - { - $$ = new_array(p, $2); - NODE_LINENO($$, $2); - } - | tLBRACE assoc_list '}' - { - $$ = new_hash(p, $2); - NODE_LINENO($$, $2); - } - | keyword_return - { - $$ = new_return(p, 0); - } - | keyword_yield '(' call_args rparen - { - $$ = new_yield(p, $3); - } - | keyword_yield '(' rparen - { - $$ = new_yield(p, 0); - } - | keyword_yield - { - $$ = new_yield(p, 0); - } - | keyword_not '(' expr rparen - { - $$ = call_uni_op(p, cond($3), "!"); - } - | keyword_not '(' rparen - { - $$ = call_uni_op(p, new_nil(p), "!"); - } - | operation brace_block - { - $$ = new_fcall(p, $1, cons(0, $2)); - } - | method_call - | method_call brace_block - { - call_with_block(p, $1, $2); - $$ = $1; - } - | tLAMBDA - { - local_nest(p); - $$ = p->lpar_beg; - p->lpar_beg = ++p->paren_nest; - } - f_larglist - { - $$ = p->cmdarg_stack; - p->cmdarg_stack = 0; - } - lambda_body - { - p->lpar_beg = $2; - $$ = new_lambda(p, $3, $5); - local_unnest(p); - p->cmdarg_stack = $4; - } - | keyword_if expr_value then - compstmt - if_tail - keyword_end - { - $$ = new_if(p, cond($2), $4, $5); - SET_LINENO($$, $1); - } - | keyword_unless expr_value then - compstmt - opt_else - keyword_end - { - $$ = new_unless(p, cond($2), $4, $5); - SET_LINENO($$, $1); - } - | keyword_while {COND_PUSH(1);} expr_value do {COND_POP();} - compstmt - keyword_end - { - $$ = new_while(p, cond($3), $6); - SET_LINENO($$, $1); - } - | keyword_until {COND_PUSH(1);} expr_value do {COND_POP();} - compstmt - keyword_end - { - $$ = new_until(p, cond($3), $6); - SET_LINENO($$, $1); - } - | keyword_case expr_value opt_terms - case_body - keyword_end - { - $$ = new_case(p, $2, $4); - } - | keyword_case opt_terms case_body keyword_end - { - $$ = new_case(p, 0, $3); - } - | keyword_for for_var keyword_in - {COND_PUSH(1);} - expr_value do - {COND_POP();} - compstmt - keyword_end - { - $$ = new_for(p, $2, $5, $8); - SET_LINENO($$, $1); - } - | keyword_class - cpath superclass - { - if (p->in_def || p->in_single) - yyerror(p, "class definition in method body"); - $$ = local_switch(p); - } - bodystmt - keyword_end - { - $$ = new_class(p, $2, $3, $5); - SET_LINENO($$, $1); - local_resume(p, $4); - } - | keyword_class - tLSHFT expr - { - $$ = p->in_def; - p->in_def = 0; - } - term - { - $$ = cons(local_switch(p), (node*)(intptr_t)p->in_single); - p->in_single = 0; - } - bodystmt - keyword_end - { - $$ = new_sclass(p, $3, $7); - SET_LINENO($$, $1); - local_resume(p, $6->car); - p->in_def = $4; - p->in_single = (int)(intptr_t)$6->cdr; - } - | keyword_module - cpath - { - if (p->in_def || p->in_single) - yyerror(p, "module definition in method body"); - $$ = local_switch(p); - } - bodystmt - keyword_end - { - $$ = new_module(p, $2, $4); - SET_LINENO($$, $1); - local_resume(p, $3); - } - | keyword_def fname - { - $$ = p->cmdarg_stack; - p->cmdarg_stack = 0; - } - { - p->in_def++; - $$ = local_switch(p); - } - f_arglist - bodystmt - keyword_end - { - $$ = new_def(p, $2, $5, $6); - SET_LINENO($$, $1); - local_resume(p, $4); - p->in_def--; - p->cmdarg_stack = $3; - } - | keyword_def singleton dot_or_colon - { - p->lstate = EXPR_FNAME; - $$ = p->cmdarg_stack; - p->cmdarg_stack = 0; - } - fname - { - p->in_single++; - p->lstate = EXPR_ENDFN; /* force for args */ - $$ = local_switch(p); - } - f_arglist - bodystmt - keyword_end - { - $$ = new_sdef(p, $2, $5, $7, $8); - SET_LINENO($$, $1); - local_resume(p, $6); - p->in_single--; - p->cmdarg_stack = $4; - } - | keyword_break - { - $$ = new_break(p, 0); - } - | keyword_next - { - $$ = new_next(p, 0); - } - | keyword_redo - { - $$ = new_redo(p); - } - | keyword_retry - { - $$ = new_retry(p); - } - ; - -primary_value : primary - { - $$ = $1; - if (!$$) $$ = new_nil(p); - } - ; - -then : term - | keyword_then - | term keyword_then - ; - -do : term - | keyword_do_cond - ; - -if_tail : opt_else - | keyword_elsif expr_value then - compstmt - if_tail - { - $$ = new_if(p, cond($2), $4, $5); - } - ; - -opt_else : none - | keyword_else compstmt - { - $$ = $2; - } - ; - -for_var : lhs - { - $$ = list1(list1($1)); - } - | mlhs - ; - -f_marg : f_norm_arg - { - $$ = new_arg(p, $1); - } - | tLPAREN f_margs rparen - { - $$ = new_masgn(p, $2, 0); - } - ; - -f_marg_list : f_marg - { - $$ = list1($1); - } - | f_marg_list ',' f_marg - { - $$ = push($1, $3); - } - ; - -f_margs : f_marg_list - { - $$ = list3($1,0,0); - } - | f_marg_list ',' tSTAR f_norm_arg - { - $$ = list3($1, new_arg(p, $4), 0); - } - | f_marg_list ',' tSTAR f_norm_arg ',' f_marg_list - { - $$ = list3($1, new_arg(p, $4), $6); - } - | f_marg_list ',' tSTAR - { - $$ = list3($1, (node*)-1, 0); - } - | f_marg_list ',' tSTAR ',' f_marg_list - { - $$ = list3($1, (node*)-1, $5); - } - | tSTAR f_norm_arg - { - $$ = list3(0, new_arg(p, $2), 0); - } - | tSTAR f_norm_arg ',' f_marg_list - { - $$ = list3(0, new_arg(p, $2), $4); - } - | tSTAR - { - $$ = list3(0, (node*)-1, 0); - } - | tSTAR ',' f_marg_list - { - $$ = list3(0, (node*)-1, $3); - } - ; - -block_param : f_arg ',' f_block_optarg ',' f_rest_arg opt_f_block_arg - { - $$ = new_args(p, $1, $3, $5, 0, $6); - } - | f_arg ',' f_block_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg - { - $$ = new_args(p, $1, $3, $5, $7, $8); - } - | f_arg ',' f_block_optarg opt_f_block_arg - { - $$ = new_args(p, $1, $3, 0, 0, $4); - } - | f_arg ',' f_block_optarg ',' f_arg opt_f_block_arg - { - $$ = new_args(p, $1, $3, 0, $5, $6); - } - | f_arg ',' f_rest_arg opt_f_block_arg - { - $$ = new_args(p, $1, 0, $3, 0, $4); - } - | f_arg ',' - { - $$ = new_args(p, $1, 0, 1, 0, 0); - } - | f_arg ',' f_rest_arg ',' f_arg opt_f_block_arg - { - $$ = new_args(p, $1, 0, $3, $5, $6); - } - | f_arg opt_f_block_arg - { - $$ = new_args(p, $1, 0, 0, 0, $2); - } - | f_block_optarg ',' f_rest_arg opt_f_block_arg - { - $$ = new_args(p, 0, $1, $3, 0, $4); - } - | f_block_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg - { - $$ = new_args(p, 0, $1, $3, $5, $6); - } - | f_block_optarg opt_f_block_arg - { - $$ = new_args(p, 0, $1, 0, 0, $2); - } - | f_block_optarg ',' f_arg opt_f_block_arg - { - $$ = new_args(p, 0, $1, 0, $3, $4); - } - | f_rest_arg opt_f_block_arg - { - $$ = new_args(p, 0, 0, $1, 0, $2); - } - | f_rest_arg ',' f_arg opt_f_block_arg - { - $$ = new_args(p, 0, 0, $1, $3, $4); - } - | f_block_arg - { - $$ = new_args(p, 0, 0, 0, 0, $1); - } - ; - -opt_block_param : none - | block_param_def - { - p->cmd_start = TRUE; - $$ = $1; - } - ; - -block_param_def : '|' opt_bv_decl '|' - { - $$ = 0; - } - | tOROP - { - $$ = 0; - } - | '|' block_param opt_bv_decl '|' - { - $$ = $2; - } - ; - - -opt_bv_decl : opt_nl - { - $$ = 0; - } - | opt_nl ';' bv_decls opt_nl - { - $$ = 0; - } - ; - -bv_decls : bvar - | bv_decls ',' bvar - ; - -bvar : tIDENTIFIER - { - local_add_f(p, $1); - new_bv(p, $1); - } - | f_bad_arg - ; - -f_larglist : '(' f_args opt_bv_decl ')' - { - $$ = $2; - } - | f_args - { - $$ = $1; - } - ; - -lambda_body : tLAMBEG compstmt '}' - { - $$ = $2; - } - | keyword_do_LAMBDA compstmt keyword_end - { - $$ = $2; - } - ; - -do_block : keyword_do_block - { - local_nest(p); - } - opt_block_param - compstmt - keyword_end - { - $$ = new_block(p,$3,$4); - local_unnest(p); - } - ; - -block_call : command do_block - { - if ($1->car == (node*)NODE_YIELD) { - yyerror(p, "block given to yield"); - } - else { - call_with_block(p, $1, $2); - } - $$ = $1; - } - | block_call dot_or_colon operation2 opt_paren_args - { - $$ = new_call(p, $1, $3, $4); - } - | block_call dot_or_colon operation2 opt_paren_args brace_block - { - $$ = new_call(p, $1, $3, $4); - call_with_block(p, $$, $5); - } - | block_call dot_or_colon operation2 command_args do_block - { - $$ = new_call(p, $1, $3, $4); - call_with_block(p, $$, $5); - } - ; - -method_call : operation paren_args - { - $$ = new_fcall(p, $1, $2); - } - | primary_value '.' operation2 opt_paren_args - { - $$ = new_call(p, $1, $3, $4); - } - | primary_value tCOLON2 operation2 paren_args - { - $$ = new_call(p, $1, $3, $4); - } - | primary_value tCOLON2 operation3 - { - $$ = new_call(p, $1, $3, 0); - } - | primary_value '.' paren_args - { - $$ = new_call(p, $1, intern("call",4), $3); - } - | primary_value tCOLON2 paren_args - { - $$ = new_call(p, $1, intern("call",4), $3); - } - | keyword_super paren_args - { - $$ = new_super(p, $2); - } - | keyword_super - { - $$ = new_zsuper(p); - } - | primary_value '[' opt_call_args rbracket - { - $$ = new_call(p, $1, intern("[]",2), $3); - } - ; - -brace_block : '{' - { - local_nest(p); - $$ = p->lineno; - } - opt_block_param - compstmt '}' - { - $$ = new_block(p,$3,$4); - SET_LINENO($$, $2); - local_unnest(p); - } - | keyword_do - { - local_nest(p); - $$ = p->lineno; - } - opt_block_param - compstmt keyword_end - { - $$ = new_block(p,$3,$4); - SET_LINENO($$, $2); - local_unnest(p); - } - ; - -case_body : keyword_when args then - compstmt - cases - { - $$ = cons(cons($2, $4), $5); - } - ; - -cases : opt_else - { - if ($1) { - $$ = cons(cons(0, $1), 0); - } - else { - $$ = 0; - } - } - | case_body - ; - -opt_rescue : keyword_rescue exc_list exc_var then - compstmt - opt_rescue - { - $$ = list1(list3($2, $3, $5)); - if ($6) $$ = append($$, $6); - } - | none - ; - -exc_list : arg_value - { - $$ = list1($1); - } - | mrhs - | none - ; - -exc_var : tASSOC lhs - { - $$ = $2; - } - | none - ; - -opt_ensure : keyword_ensure compstmt - { - $$ = $2; - } - | none - ; - -literal : numeric - | symbol - | words - | symbols - ; - -string : tCHAR - | tSTRING - | tSTRING_BEG tSTRING - { - $$ = $2; - } - | tSTRING_BEG string_rep tSTRING - { - $$ = new_dstr(p, push($2, $3)); - } - ; - -string_rep : string_interp - | string_rep string_interp - { - $$ = append($1, $2); - } - ; - -string_interp : tSTRING_MID - { - $$ = list1($1); - } - | tSTRING_PART - { - $$ = p->lex_strterm; - p->lex_strterm = NULL; - } - compstmt - '}' - { - p->lex_strterm = $2; - $$ = list2($1, $3); - } - | tLITERAL_DELIM - { - $$ = list1(new_literal_delim(p)); - } - | tHD_LITERAL_DELIM heredoc_bodies - { - $$ = list1(new_literal_delim(p)); - } - ; - -xstring : tXSTRING_BEG tXSTRING - { - $$ = $2; - } - | tXSTRING_BEG string_rep tXSTRING - { - $$ = new_dxstr(p, push($2, $3)); - } - ; - -regexp : tREGEXP_BEG tREGEXP - { - $$ = $2; - } - | tREGEXP_BEG string_rep tREGEXP - { - $$ = new_dregx(p, $2, $3); - } - ; - -heredoc : tHEREDOC_BEG - ; - -opt_heredoc_bodies : /* none */ - | heredoc_bodies - ; - -heredoc_bodies : heredoc_body - | heredoc_bodies heredoc_body - ; - -heredoc_body : tHEREDOC_END - { - parser_heredoc_info * inf = parsing_heredoc_inf(p); - inf->doc = push(inf->doc, new_str(p, "", 0)); - heredoc_end(p); - } - | heredoc_string_rep tHEREDOC_END - { - heredoc_end(p); - } - ; - -heredoc_string_rep : heredoc_string_interp - | heredoc_string_rep heredoc_string_interp - ; - -heredoc_string_interp : tHD_STRING_MID - { - parser_heredoc_info * inf = parsing_heredoc_inf(p); - inf->doc = push(inf->doc, $1); - heredoc_treat_nextline(p); - } - | tHD_STRING_PART - { - $$ = p->lex_strterm; - p->lex_strterm = NULL; - } - compstmt - '}' - { - parser_heredoc_info * inf = parsing_heredoc_inf(p); - p->lex_strterm = $2; - inf->doc = push(push(inf->doc, $1), $3); - } - ; - -words : tWORDS_BEG tSTRING - { - $$ = new_words(p, list1($2)); - } - | tWORDS_BEG string_rep tSTRING - { - $$ = new_words(p, push($2, $3)); - } - ; - - -symbol : basic_symbol - { - $$ = new_sym(p, $1); - } - | tSYMBEG tSTRING_BEG string_interp tSTRING - { - p->lstate = EXPR_END; - $$ = new_dsym(p, push($3, $4)); - } - ; - -basic_symbol : tSYMBEG sym - { - p->lstate = EXPR_END; - $$ = $2; - } - ; - -sym : fname - | tIVAR - | tGVAR - | tCVAR - | tSTRING - { - $$ = new_strsym(p, $1); - } - | tSTRING_BEG tSTRING - { - $$ = new_strsym(p, $2); - } - ; - -symbols : tSYMBOLS_BEG tSTRING - { - $$ = new_symbols(p, list1($2)); - } - | tSYMBOLS_BEG string_rep tSTRING - { - $$ = new_symbols(p, push($2, $3)); - } - ; - -numeric : tINTEGER - | tFLOAT - | tUMINUS_NUM tINTEGER %prec tLOWEST - { - $$ = negate_lit(p, $2); - } - | tUMINUS_NUM tFLOAT %prec tLOWEST - { - $$ = negate_lit(p, $2); - } - ; - -variable : tIDENTIFIER - { - $$ = new_lvar(p, $1); - } - | tIVAR - { - $$ = new_ivar(p, $1); - } - | tGVAR - { - $$ = new_gvar(p, $1); - } - | tCVAR - { - $$ = new_cvar(p, $1); - } - | tCONSTANT - { - $$ = new_const(p, $1); - } - ; - -var_lhs : variable - { - assignable(p, $1); - } - ; - -var_ref : variable - { - $$ = var_reference(p, $1); - } - | keyword_nil - { - $$ = new_nil(p); - } - | keyword_self - { - $$ = new_self(p); - } - | keyword_true - { - $$ = new_true(p); - } - | keyword_false - { - $$ = new_false(p); - } - | keyword__FILE__ - { - if (!p->filename) { - p->filename = "(null)"; - } - $$ = new_str(p, p->filename, strlen(p->filename)); - } - | keyword__LINE__ - { - char buf[16]; - - snprintf(buf, sizeof(buf), "%d", p->lineno); - $$ = new_int(p, buf, 10); - } - ; - -backref : tNTH_REF - | tBACK_REF - ; - -superclass : term - { - $$ = 0; - } - | '<' - { - p->lstate = EXPR_BEG; - p->cmd_start = TRUE; - } - expr_value term - { - $$ = $3; - } - | error term - { - yyerrok; - $$ = 0; - } - ; - -f_arglist : '(' f_args rparen - { - $$ = $2; - p->lstate = EXPR_BEG; - p->cmd_start = TRUE; - } - | f_args term - { - $$ = $1; - } - ; - -f_args : f_arg ',' f_optarg ',' f_rest_arg opt_f_block_arg - { - $$ = new_args(p, $1, $3, $5, 0, $6); - } - | f_arg ',' f_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg - { - $$ = new_args(p, $1, $3, $5, $7, $8); - } - | f_arg ',' f_optarg opt_f_block_arg - { - $$ = new_args(p, $1, $3, 0, 0, $4); - } - | f_arg ',' f_optarg ',' f_arg opt_f_block_arg - { - $$ = new_args(p, $1, $3, 0, $5, $6); - } - | f_arg ',' f_rest_arg opt_f_block_arg - { - $$ = new_args(p, $1, 0, $3, 0, $4); - } - | f_arg ',' f_rest_arg ',' f_arg opt_f_block_arg - { - $$ = new_args(p, $1, 0, $3, $5, $6); - } - | f_arg opt_f_block_arg - { - $$ = new_args(p, $1, 0, 0, 0, $2); - } - | f_optarg ',' f_rest_arg opt_f_block_arg - { - $$ = new_args(p, 0, $1, $3, 0, $4); - } - | f_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg - { - $$ = new_args(p, 0, $1, $3, $5, $6); - } - | f_optarg opt_f_block_arg - { - $$ = new_args(p, 0, $1, 0, 0, $2); - } - | f_optarg ',' f_arg opt_f_block_arg - { - $$ = new_args(p, 0, $1, 0, $3, $4); - } - | f_rest_arg opt_f_block_arg - { - $$ = new_args(p, 0, 0, $1, 0, $2); - } - | f_rest_arg ',' f_arg opt_f_block_arg - { - $$ = new_args(p, 0, 0, $1, $3, $4); - } - | f_block_arg - { - $$ = new_args(p, 0, 0, 0, 0, $1); - } - | /* none */ - { - local_add_f(p, 0); - $$ = new_args(p, 0, 0, 0, 0, 0); - } - ; - -f_bad_arg : tCONSTANT - { - yyerror(p, "formal argument cannot be a constant"); - $$ = 0; - } - | tIVAR - { - yyerror(p, "formal argument cannot be an instance variable"); - $$ = 0; - } - | tGVAR - { - yyerror(p, "formal argument cannot be a global variable"); - $$ = 0; - } - | tCVAR - { - yyerror(p, "formal argument cannot be a class variable"); - $$ = 0; - } - ; - -f_norm_arg : f_bad_arg - { - $$ = 0; - } - | tIDENTIFIER - { - local_add_f(p, $1); - $$ = $1; - } - ; - -f_arg_item : f_norm_arg - { - $$ = new_arg(p, $1); - } - | tLPAREN f_margs rparen - { - $$ = new_masgn(p, $2, 0); - } - ; - -f_arg : f_arg_item - { - $$ = list1($1); - } - | f_arg ',' f_arg_item - { - $$ = push($1, $3); - } - ; - -f_opt_asgn : tIDENTIFIER '=' - { - local_add_f(p, $1); - $$ = $1; - } - ; - -f_opt : f_opt_asgn arg_value - { - $$ = cons(nsym($1), $2); - } - ; - -f_block_opt : f_opt_asgn primary_value - { - $$ = cons(nsym($1), $2); - } - ; - -f_block_optarg : f_block_opt - { - $$ = list1($1); - } - | f_block_optarg ',' f_block_opt - { - $$ = push($1, $3); - } - ; - -f_optarg : f_opt - { - $$ = list1($1); - } - | f_optarg ',' f_opt - { - $$ = push($1, $3); - } - ; - -restarg_mark : '*' - | tSTAR - ; - -f_rest_arg : restarg_mark tIDENTIFIER - { - local_add_f(p, $2); - $$ = $2; - } - | restarg_mark - { - local_add_f(p, 0); - $$ = -1; - } - ; - -blkarg_mark : '&' - | tAMPER - ; - -f_block_arg : blkarg_mark tIDENTIFIER - { - local_add_f(p, $2); - $$ = $2; - } - ; - -opt_f_block_arg : ',' f_block_arg - { - $$ = $2; - } - | none - { - local_add_f(p, 0); - $$ = 0; - } - ; - -singleton : var_ref - { - $$ = $1; - if (!$$) $$ = new_nil(p); - } - | '(' {p->lstate = EXPR_BEG;} expr rparen - { - if ($3 == 0) { - yyerror(p, "can't define singleton method for ()."); - } - else { - switch ((enum node_type)(int)(intptr_t)$3->car) { - case NODE_STR: - case NODE_DSTR: - case NODE_XSTR: - case NODE_DXSTR: - case NODE_DREGX: - case NODE_MATCH: - case NODE_FLOAT: - case NODE_ARRAY: - case NODE_HEREDOC: - yyerror(p, "can't define singleton method for literals"); - default: - break; - } - } - $$ = $3; - } - ; - -assoc_list : none - | assocs trailer - { - $$ = $1; - } - ; - -assocs : assoc - { - $$ = list1($1); - NODE_LINENO($$, $1); - } - | assocs ',' assoc - { - $$ = push($1, $3); - } - ; - -assoc : arg_value tASSOC arg_value - { - $$ = cons($1, $3); - } - | tLABEL arg_value - { - $$ = cons(new_sym(p, $1), $2); - } - ; - -operation : tIDENTIFIER - | tCONSTANT - | tFID - ; - -operation2 : tIDENTIFIER - | tCONSTANT - | tFID - | op - ; - -operation3 : tIDENTIFIER - | tFID - | op - ; - -dot_or_colon : '.' - | tCOLON2 - ; - -opt_terms : /* none */ - | terms - ; - -opt_nl : /* none */ - | nl - ; - -rparen : opt_nl ')' - ; - -rbracket : opt_nl ']' - ; - -trailer : /* none */ - | nl - | ',' - ; - -term : ';' {yyerrok;} - | nl - ; - -nl : '\n' - { - p->lineno++; - p->column = 0; - } - opt_heredoc_bodies - -terms : term - | terms ';' {yyerrok;} - ; - -none : /* none */ - { - $$ = 0; - } - ; -%% -#define yylval (*((YYSTYPE*)(p->ylval))) - -static void -yyerror(parser_state *p, const char *s) -{ - char* c; - int n; - - if (! p->capture_errors) { -#ifdef ENABLE_STDIO - if (p->filename) { - fprintf(stderr, "%s:%d:%d: %s\n", p->filename, p->lineno, p->column, s); - } - else { - fprintf(stderr, "line %d:%d: %s\n", p->lineno, p->column, s); - } -#endif - } - else if (p->nerr < sizeof(p->error_buffer) / sizeof(p->error_buffer[0])) { - n = strlen(s); - c = (char *)parser_palloc(p, n + 1); - memcpy(c, s, n + 1); - p->error_buffer[p->nerr].message = c; - p->error_buffer[p->nerr].lineno = p->lineno; - p->error_buffer[p->nerr].column = p->column; - } - p->nerr++; -} - -static void -yyerror_i(parser_state *p, const char *fmt, int i) -{ - char buf[256]; - - snprintf(buf, sizeof(buf), fmt, i); - yyerror(p, buf); -} - -static void -yywarn(parser_state *p, const char *s) -{ - char* c; - int n; - - if (! p->capture_errors) { -#ifdef ENABLE_STDIO - if (p->filename) { - fprintf(stderr, "%s:%d:%d: %s\n", p->filename, p->lineno, p->column, s); - } - else { - fprintf(stderr, "line %d:%d: %s\n", p->lineno, p->column, s); - } -#endif - } - else if (p->nwarn < sizeof(p->warn_buffer) / sizeof(p->warn_buffer[0])) { - n = strlen(s); - c = (char *)parser_palloc(p, n + 1); - memcpy(c, s, n + 1); - p->warn_buffer[p->nwarn].message = c; - p->warn_buffer[p->nwarn].lineno = p->lineno; - p->warn_buffer[p->nwarn].column = p->column; - } - p->nwarn++; -} - -static void -yywarning(parser_state *p, const char *s) -{ - yywarn(p, s); -} - -static void -yywarning_s(parser_state *p, const char *fmt, const char *s) -{ - char buf[256]; - - snprintf(buf, sizeof(buf), fmt, s); - yywarning(p, buf); -} - -static void -backref_error(parser_state *p, node *n) -{ - int c; - - c = (int)(intptr_t)n->car; - - if (c == NODE_NTH_REF) { - yyerror_i(p, "can't set variable $%d", (int)(intptr_t)n->cdr); - } - else if (c == NODE_BACK_REF) { - yyerror_i(p, "can't set variable $%c", (int)(intptr_t)n->cdr); - } - else { - mrb_bug(p->mrb, "Internal error in backref_error() : n=>car == %S", mrb_fixnum_value(c)); - } -} - -static void pushback(parser_state *p, int c); -static mrb_bool peeks(parser_state *p, const char *s); -static mrb_bool skips(parser_state *p, const char *s); - -static inline int -nextc(parser_state *p) -{ - int c; - - if (p->pb) { - node *tmp; - - c = (int)(intptr_t)p->pb->car; - tmp = p->pb; - p->pb = p->pb->cdr; - cons_free(tmp); - } - else { -#ifdef ENABLE_STDIO - if (p->f) { - if (feof(p->f)) goto eof; - c = fgetc(p->f); - if (c == EOF) goto eof; - } - else -#endif - if (!p->s || p->s >= p->send) { - goto eof; - } - else { - c = (unsigned char)*p->s++; - } - } - if (c >= 0) { - p->column++; - } - if (c == '\r') { - c = nextc(p); - if (c != '\n') { - pushback(p, c); - return '\r'; - } - return c; - } - return c; - - eof: - if (!p->cxt) return -1; - else { - if (p->cxt->partial_hook(p) < 0) - return -1; /* end of program(s) */ - return -2; /* end of a file in the program files */ - } -} - -static void -pushback(parser_state *p, int c) -{ - if (c >= 0) { - p->column--; - } - p->pb = cons((node*)(intptr_t)c, p->pb); -} - -static void -skip(parser_state *p, char term) -{ - int c; - - for (;;) { - c = nextc(p); - if (c < 0) break; - if (c == term) break; - } -} - -static int -peekc_n(parser_state *p, int n) -{ - node *list = 0; - int c0; - - do { - c0 = nextc(p); - if (c0 == -1) return c0; /* do not skip partial EOF */ - list = push(list, (node*)(intptr_t)c0); - } while(n--); - if (p->pb) { - p->pb = append((node*)list, p->pb); - } - else { - p->pb = list; - } - return c0; -} - -static mrb_bool -peek_n(parser_state *p, int c, int n) -{ - return peekc_n(p, n) == c && c >= 0; -} -#define peek(p,c) peek_n((p), (c), 0) - -static mrb_bool -peeks(parser_state *p, const char *s) -{ - int len = strlen(s); - -#ifdef ENABLE_STDIO - if (p->f) { - int n = 0; - while (*s) { - if (!peek_n(p, *s++, n++)) return FALSE; - } - return TRUE; - } - else -#endif - if (p->s && p->s + len <= p->send) { - if (memcmp(p->s, s, len) == 0) return TRUE; - } - return FALSE; -} - -static mrb_bool -skips(parser_state *p, const char *s) -{ - int c; - - for (;;) { - /* skip until first char */ - for (;;) { - c = nextc(p); - if (c < 0) return c; - if (c == '\n') { - p->lineno++; - p->column = 0; - } - if (c == *s) break; - } - s++; - if (peeks(p, s)) { - int len = strlen(s); - - while (len--) { - if (nextc(p) == '\n') { - p->lineno++; - p->column = 0; - } - } - return TRUE; - } - else{ - s--; - } - } - return FALSE; -} - - -static int -newtok(parser_state *p) -{ - p->bidx = 0; - return p->column - 1; -} - -static void -tokadd(parser_state *p, int32_t c) -{ - char utf8[4]; - unsigned len; - - /* mrb_assert(-0x10FFFF <= c && c <= 0xFF); */ - if (c >= 0) { - /* Single byte from source or non-Unicode escape */ - utf8[0] = (char)c; - len = 1; - } - else { - /* Unicode character */ - c = -c; - if (c < 0x80) { - utf8[0] = (char)c; - len = 1; - } - else if (c < 0x800) { - utf8[0] = (char)(0xC0 | (c >> 6)); - utf8[1] = (char)(0x80 | (c & 0x3F)); - len = 2; - } - else if (c < 0x10000) { - utf8[0] = (char)(0xE0 | (c >> 12) ); - utf8[1] = (char)(0x80 | ((c >> 6) & 0x3F)); - utf8[2] = (char)(0x80 | ( c & 0x3F)); - len = 3; - } - else { - utf8[0] = (char)(0xF0 | (c >> 18) ); - utf8[1] = (char)(0x80 | ((c >> 12) & 0x3F)); - utf8[2] = (char)(0x80 | ((c >> 6) & 0x3F)); - utf8[3] = (char)(0x80 | ( c & 0x3F)); - len = 4; - } - } - if (p->bidx+len <= MRB_PARSER_BUF_SIZE) { - unsigned i; - for (i = 0; i < len; i++) { - p->buf[p->bidx++] = utf8[i]; - } - } -} - -static int -toklast(parser_state *p) -{ - return p->buf[p->bidx-1]; -} - -static void -tokfix(parser_state *p) -{ - if (p->bidx >= MRB_PARSER_BUF_SIZE) { - yyerror(p, "string too long (truncated)"); - } - p->buf[p->bidx] = '\0'; -} - -static const char* -tok(parser_state *p) -{ - return p->buf; -} - -static int -toklen(parser_state *p) -{ - return p->bidx; -} - -#define IS_ARG() (p->lstate == EXPR_ARG || p->lstate == EXPR_CMDARG) -#define IS_END() (p->lstate == EXPR_END || p->lstate == EXPR_ENDARG || p->lstate == EXPR_ENDFN) -#define IS_BEG() (p->lstate == EXPR_BEG || p->lstate == EXPR_MID || p->lstate == EXPR_VALUE || p->lstate == EXPR_CLASS) -#define IS_SPCARG(c) (IS_ARG() && space_seen && !ISSPACE(c)) -#define IS_LABEL_POSSIBLE() ((p->lstate == EXPR_BEG && !cmd_state) || IS_ARG()) -#define IS_LABEL_SUFFIX(n) (peek_n(p, ':',(n)) && !peek_n(p, ':', (n)+1)) - -static int -scan_oct(const int *start, int len, int *retlen) -{ - const int *s = start; - int retval = 0; - - /* mrb_assert(len <= 3) */ - while (len-- && *s >= '0' && *s <= '7') { - retval <<= 3; - retval |= *s++ - '0'; - } - *retlen = s - start; - - return retval; -} - -static int32_t -scan_hex(const int *start, int len, int *retlen) -{ - static const char hexdigit[] = "0123456789abcdef0123456789ABCDEF"; - const int *s = start; - int32_t retval = 0; - char *tmp; - - /* mrb_assert(len <= 8) */ - while (len-- && *s && (tmp = (char*)strchr(hexdigit, *s))) { - retval <<= 4; - retval |= (tmp - hexdigit) & 15; - s++; - } - *retlen = s - start; - - return retval; -} - -/* Return negative to indicate Unicode code point */ -static int32_t -read_escape(parser_state *p) -{ - int32_t c; - - switch (c = nextc(p)) { - case '\\':/* Backslash */ - return c; - - case 'n':/* newline */ - return '\n'; - - case 't':/* horizontal tab */ - return '\t'; - - case 'r':/* carriage-return */ - return '\r'; - - case 'f':/* form-feed */ - return '\f'; - - case 'v':/* vertical tab */ - return '\13'; - - case 'a':/* alarm(bell) */ - return '\007'; - - case 'e':/* escape */ - return 033; - - case '0': case '1': case '2': case '3': /* octal constant */ - case '4': case '5': case '6': case '7': - { - int buf[3]; - int i; - - buf[0] = c; - for (i=1; i<3; i++) { - buf[i] = nextc(p); - if (buf[i] < 0) goto eof; - if (buf[i] < '0' || '7' < buf[i]) { - pushback(p, buf[i]); - break; - } - } - c = scan_oct(buf, i, &i); - } - return c; - - case 'x': /* hex constant */ - { - int buf[2]; - int i; - - for (i=0; i<2; i++) { - buf[i] = nextc(p); - if (buf[i] < 0) goto eof; - if (!ISXDIGIT(buf[i])) { - pushback(p, buf[i]); - break; - } - } - c = scan_hex(buf, i, &i); - if (i == 0) { - yyerror(p, "Invalid escape character syntax"); - return 0; - } - } - return c; - - case 'u': /* Unicode */ - { - int buf[9]; - int i; - - /* Look for opening brace */ - i = 0; - buf[0] = nextc(p); - if (buf[0] < 0) goto eof; - if (buf[0] == '{') { - /* \u{xxxxxxxx} form */ - for (i=0; i<9; i++) { - buf[i] = nextc(p); - if (buf[i] < 0) goto eof; - if (buf[i] == '}') { - break; - } - else if (!ISXDIGIT(buf[i])) { - yyerror(p, "Invalid escape character syntax"); - pushback(p, buf[i]); - return 0; - } - } - } - else if (ISXDIGIT(buf[0])) { - /* \uxxxx form */ - for (i=1; i<4; i++) { - buf[i] = nextc(p); - if (buf[i] < 0) goto eof; - if (!ISXDIGIT(buf[i])) { - pushback(p, buf[i]); - break; - } - } - } - else { - pushback(p, buf[0]); - } - c = scan_hex(buf, i, &i); - if (i == 0) { - yyerror(p, "Invalid escape character syntax"); - return 0; - } - if (c < 0 || c > 0x10FFFF || (c & 0xFFFFF800) == 0xD800) { - yyerror(p, "Invalid Unicode code point"); - return 0; - } - } - return -c; - - case 'b':/* backspace */ - return '\010'; - - case 's':/* space */ - return ' '; - - case 'M': - if ((c = nextc(p)) != '-') { - yyerror(p, "Invalid escape character syntax"); - pushback(p, c); - return '\0'; - } - if ((c = nextc(p)) == '\\') { - return read_escape(p) | 0x80; - } - else if (c < 0) goto eof; - else { - return ((c & 0xff) | 0x80); - } - - case 'C': - if ((c = nextc(p)) != '-') { - yyerror(p, "Invalid escape character syntax"); - pushback(p, c); - return '\0'; - } - case 'c': - if ((c = nextc(p))== '\\') { - c = read_escape(p); - } - else if (c == '?') - return 0177; - else if (c < 0) goto eof; - return c & 0x9f; - - eof: - case -1: - case -2: /* end of a file */ - yyerror(p, "Invalid escape character syntax"); - return '\0'; - - default: - return c; - } -} - -static int -parse_string(parser_state *p) -{ - int c; - string_type type = (string_type)(intptr_t)p->lex_strterm->car; - int nest_level = (intptr_t)p->lex_strterm->cdr->car; - int beg = (intptr_t)p->lex_strterm->cdr->cdr->car; - int end = (intptr_t)p->lex_strterm->cdr->cdr->cdr; - parser_heredoc_info *hinf = (type & STR_FUNC_HEREDOC) ? parsing_heredoc_inf(p) : NULL; - - newtok(p); - while ((c = nextc(p)) != end || nest_level != 0) { - if (hinf && (c == '\n' || c < 0)) { - mrb_bool line_head; - tokadd(p, '\n'); - tokfix(p); - p->lineno++; - p->column = 0; - line_head = hinf->line_head; - hinf->line_head = TRUE; - if (line_head) { - /* check whether end of heredoc */ - const char *s = tok(p); - int len = toklen(p); - if (hinf->allow_indent) { - while (ISSPACE(*s) && len > 0) { - ++s; - --len; - } - } - if ((len-1 == hinf->term_len) && (strncmp(s, hinf->term, len-1) == 0)) { - return tHEREDOC_END; - } - } - if (c < 0) { - char buf[256]; - snprintf(buf, sizeof(buf), "can't find heredoc delimiter \"%s\" anywhere before EOF", hinf->term); - yyerror(p, buf); - return 0; - } - yylval.nd = new_str(p, tok(p), toklen(p)); - return tHD_STRING_MID; - } - if (c < 0) { - yyerror(p, "unterminated string meets end of file"); - return 0; - } - else if (c == beg) { - nest_level++; - p->lex_strterm->cdr->car = (node*)(intptr_t)nest_level; - } - else if (c == end) { - nest_level--; - p->lex_strterm->cdr->car = (node*)(intptr_t)nest_level; - } - else if (c == '\\') { - c = nextc(p); - if (type & STR_FUNC_EXPAND) { - if (c == end || c == beg) { - tokadd(p, c); - } - else if (c == '\n') { - p->lineno++; - p->column = 0; - if (type & STR_FUNC_ARRAY) { - tokadd(p, '\n'); - } - } - else if (type & STR_FUNC_REGEXP) { - tokadd(p, '\\'); - tokadd(p, c); - } - else { - pushback(p, c); - tokadd(p, read_escape(p)); - if (hinf) - hinf->line_head = FALSE; - } - } - else { - if (c != beg && c != end) { - if (c == '\n') { - p->lineno++; - p->column = 0; - } - if (!(c == '\\' || ((type & STR_FUNC_ARRAY) && ISSPACE(c)))) { - tokadd(p, '\\'); - } - } - tokadd(p, c); - } - continue; - } - else if ((c == '#') && (type & STR_FUNC_EXPAND)) { - c = nextc(p); - if (c == '{') { - tokfix(p); - p->lstate = EXPR_BEG; - p->cmd_start = TRUE; - yylval.nd = new_str(p, tok(p), toklen(p)); - if (hinf) { - hinf->line_head = FALSE; - return tHD_STRING_PART; - } - return tSTRING_PART; - } - tokadd(p, '#'); - pushback(p, c); - continue; - } - if ((type & STR_FUNC_ARRAY) && ISSPACE(c)) { - if (toklen(p) == 0) { - do { - if (c == '\n') { - p->lineno++; - p->column = 0; - heredoc_treat_nextline(p); - if (p->parsing_heredoc != NULL) { - return tHD_LITERAL_DELIM; - } - } - c = nextc(p); - } while (ISSPACE(c)); - pushback(p, c); - return tLITERAL_DELIM; - } - else { - pushback(p, c); - tokfix(p); - yylval.nd = new_str(p, tok(p), toklen(p)); - return tSTRING_MID; - } - } - tokadd(p, c); - } - - tokfix(p); - p->lstate = EXPR_END; - end_strterm(p); - - if (type & STR_FUNC_XQUOTE) { - yylval.nd = new_xstr(p, tok(p), toklen(p)); - return tXSTRING; - } - - if (type & STR_FUNC_REGEXP) { - int f = 0; - int re_opt; - char *s = strndup(tok(p), toklen(p)); - char flags[3]; - char *flag = flags; - char *dup; - - newtok(p); - while (re_opt = nextc(p), re_opt >= 0 && ISALPHA(re_opt)) { - switch (re_opt) { - case 'i': f |= 1; break; - case 'x': f |= 2; break; - case 'm': f |= 4; break; - default: tokadd(p, re_opt); break; - } - } - pushback(p, re_opt); - if (toklen(p)) { - char msg[128]; - tokfix(p); - snprintf(msg, sizeof(msg), "unknown regexp option%s - %s", - toklen(p) > 1 ? "s" : "", tok(p)); - yyerror(p, msg); - } - if (f != 0) { - if (f & 1) *flag++ = 'i'; - if (f & 2) *flag++ = 'x'; - if (f & 4) *flag++ = 'm'; - dup = strndup(flags, (size_t)(flag - flags)); - } - else { - dup = NULL; - } - yylval.nd = new_regx(p, s, dup); - - return tREGEXP; - } - - yylval.nd = new_str(p, tok(p), toklen(p)); - return tSTRING; -} - - -static int -heredoc_identifier(parser_state *p) -{ - int c; - int type = str_heredoc; - mrb_bool indent = FALSE; - mrb_bool quote = FALSE; - node *newnode; - parser_heredoc_info *info; - - c = nextc(p); - if (ISSPACE(c) || c == '=') { - pushback(p, c); - return 0; - } - if (c == '-') { - indent = TRUE; - c = nextc(p); - } - if (c == '\'' || c == '"') { - int term = c; - if (c == '\'') - quote = TRUE; - newtok(p); - while ((c = nextc(p)) >= 0 && c != term) { - if (c == '\n') { - c = -1; - break; - } - tokadd(p, c); - } - if (c < 0) { - yyerror(p, "unterminated here document identifier"); - return 0; - } - } - else { - if (c < 0) { - return 0; /* missing here document identifier */ - } - if (! identchar(c)) { - pushback(p, c); - if (indent) pushback(p, '-'); - return 0; - } - newtok(p); - do { - tokadd(p, c); - } while ((c = nextc(p)) >= 0 && identchar(c)); - pushback(p, c); - } - tokfix(p); - newnode = new_heredoc(p); - info = (parser_heredoc_info*)newnode->cdr; - info->term = strndup(tok(p), toklen(p)); - info->term_len = toklen(p); - if (! quote) - type |= STR_FUNC_EXPAND; - info->type = (string_type)type; - info->allow_indent = indent; - info->line_head = TRUE; - info->doc = NULL; - p->heredocs_from_nextline = push(p->heredocs_from_nextline, newnode); - p->lstate = EXPR_END; - - yylval.nd = newnode; - return tHEREDOC_BEG; -} - -static int -arg_ambiguous(parser_state *p) -{ - yywarning(p, "ambiguous first argument; put parentheses or even spaces"); - return 1; -} - -#include "lex.def" - -static int -parser_yylex(parser_state *p) -{ - int32_t c; - int space_seen = 0; - int cmd_state; - enum mrb_lex_state_enum last_state; - int token_column; - - if (p->lex_strterm) { - if (is_strterm_type(p, STR_FUNC_HEREDOC)) { - if (p->parsing_heredoc != NULL) - return parse_string(p); - } - else - return parse_string(p); - } - cmd_state = p->cmd_start; - p->cmd_start = FALSE; - retry: - last_state = p->lstate; - switch (c = nextc(p)) { - case '\004': /* ^D */ - case '\032': /* ^Z */ - case '\0': /* NUL */ - case -1: /* end of script. */ - if (p->heredocs_from_nextline) - goto maybe_heredoc; - return 0; - - /* white spaces */ - case ' ': case '\t': case '\f': case '\r': - case '\13': /* '\v' */ - space_seen = 1; - goto retry; - - case '#': /* it's a comment */ - skip(p, '\n'); - /* fall through */ - case -2: /* end of a file */ - case '\n': - maybe_heredoc: - heredoc_treat_nextline(p); - switch (p->lstate) { - case EXPR_BEG: - case EXPR_FNAME: - case EXPR_DOT: - case EXPR_CLASS: - case EXPR_VALUE: - p->lineno++; - p->column = 0; - if (p->parsing_heredoc != NULL) { - if (p->lex_strterm) { - return parse_string(p); - } - } - goto retry; - default: - break; - } - if (p->parsing_heredoc != NULL) { - return '\n'; - } - while ((c = nextc(p))) { - switch (c) { - case ' ': case '\t': case '\f': case '\r': - case '\13': /* '\v' */ - space_seen = 1; - break; - case '.': - if ((c = nextc(p)) != '.') { - pushback(p, c); - pushback(p, '.'); - goto retry; - } - case -1: /* EOF */ - case -2: /* end of a file */ - goto normal_newline; - default: - pushback(p, c); - goto normal_newline; - } - } - normal_newline: - p->cmd_start = TRUE; - p->lstate = EXPR_BEG; - return '\n'; - - case '*': - if ((c = nextc(p)) == '*') { - if ((c = nextc(p)) == '=') { - yylval.id = intern("**",2); - p->lstate = EXPR_BEG; - return tOP_ASGN; - } - pushback(p, c); - c = tPOW; - } - else { - if (c == '=') { - yylval.id = intern_c('*'); - p->lstate = EXPR_BEG; - return tOP_ASGN; - } - pushback(p, c); - if (IS_SPCARG(c)) { - yywarning(p, "`*' interpreted as argument prefix"); - c = tSTAR; - } - else if (IS_BEG()) { - c = tSTAR; - } - else { - c = '*'; - } - } - if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { - p->lstate = EXPR_ARG; - } - else { - p->lstate = EXPR_BEG; - } - return c; - - case '!': - c = nextc(p); - if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { - p->lstate = EXPR_ARG; - if (c == '@') { - return '!'; - } - } - else { - p->lstate = EXPR_BEG; - } - if (c == '=') { - return tNEQ; - } - if (c == '~') { - return tNMATCH; - } - pushback(p, c); - return '!'; - - case '=': - if (p->column == 1) { - static const char begin[] = "begin"; - static const char end[] = "\n=end"; - if (peeks(p, begin)) { - c = peekc_n(p, sizeof(begin)-1); - if (c < 0 || ISSPACE(c)) { - do { - if (!skips(p, end)) { - yyerror(p, "embedded document meets end of file"); - return 0; - } - c = nextc(p); - } while (!(c < 0 || ISSPACE(c))); - if (c != '\n') skip(p, '\n'); - p->lineno++; - p->column = 0; - goto retry; - } - } - } - if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { - p->lstate = EXPR_ARG; - } - else { - p->lstate = EXPR_BEG; - } - if ((c = nextc(p)) == '=') { - if ((c = nextc(p)) == '=') { - return tEQQ; - } - pushback(p, c); - return tEQ; - } - if (c == '~') { - return tMATCH; - } - else if (c == '>') { - return tASSOC; - } - pushback(p, c); - return '='; - - case '<': - c = nextc(p); - if (c == '<' && - p->lstate != EXPR_DOT && - p->lstate != EXPR_CLASS && - !IS_END() && - (!IS_ARG() || space_seen)) { - int token = heredoc_identifier(p); - if (token) - return token; - } - if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { - p->lstate = EXPR_ARG; - } - else { - p->lstate = EXPR_BEG; - if (p->lstate == EXPR_CLASS) { - p->cmd_start = TRUE; - } - } - if (c == '=') { - if ((c = nextc(p)) == '>') { - return tCMP; - } - pushback(p, c); - return tLEQ; - } - if (c == '<') { - if ((c = nextc(p)) == '=') { - yylval.id = intern("<<",2); - p->lstate = EXPR_BEG; - return tOP_ASGN; - } - pushback(p, c); - return tLSHFT; - } - pushback(p, c); - return '<'; - - case '>': - if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { - p->lstate = EXPR_ARG; - } - else { - p->lstate = EXPR_BEG; - } - if ((c = nextc(p)) == '=') { - return tGEQ; - } - if (c == '>') { - if ((c = nextc(p)) == '=') { - yylval.id = intern(">>",2); - p->lstate = EXPR_BEG; - return tOP_ASGN; - } - pushback(p, c); - return tRSHFT; - } - pushback(p, c); - return '>'; - - case '"': - p->lex_strterm = new_strterm(p, str_dquote, '"', 0); - return tSTRING_BEG; - - case '\'': - p->lex_strterm = new_strterm(p, str_squote, '\'', 0); - return parse_string(p); - - case '`': - if (p->lstate == EXPR_FNAME) { - p->lstate = EXPR_ENDFN; - return '`'; - } - if (p->lstate == EXPR_DOT) { - if (cmd_state) - p->lstate = EXPR_CMDARG; - else - p->lstate = EXPR_ARG; - return '`'; - } - p->lex_strterm = new_strterm(p, str_xquote, '`', 0); - return tXSTRING_BEG; - - case '?': - if (IS_END()) { - p->lstate = EXPR_VALUE; - return '?'; - } - c = nextc(p); - if (c < 0) { - yyerror(p, "incomplete character syntax"); - return 0; - } - if (ISSPACE(c)) { - if (!IS_ARG()) { - int c2; - switch (c) { - case ' ': - c2 = 's'; - break; - case '\n': - c2 = 'n'; - break; - case '\t': - c2 = 't'; - break; - case '\v': - c2 = 'v'; - break; - case '\r': - c2 = 'r'; - break; - case '\f': - c2 = 'f'; - break; - default: - c2 = 0; - break; - } - if (c2) { - char buf[256]; - snprintf(buf, sizeof(buf), "invalid character syntax; use ?\\%c", c2); - yyerror(p, buf); - } - } - ternary: - pushback(p, c); - p->lstate = EXPR_VALUE; - return '?'; - } - newtok(p); - /* need support UTF-8 if configured */ - if ((isalnum(c) || c == '_')) { - int c2 = nextc(p); - pushback(p, c2); - if ((isalnum(c2) || c2 == '_')) { - goto ternary; - } - } - if (c == '\\') { - c = read_escape(p); - tokadd(p, c); - } - else { - tokadd(p, c); - } - tokfix(p); - yylval.nd = new_str(p, tok(p), toklen(p)); - p->lstate = EXPR_END; - return tCHAR; - - case '&': - if ((c = nextc(p)) == '&') { - p->lstate = EXPR_BEG; - if ((c = nextc(p)) == '=') { - yylval.id = intern("&&",2); - p->lstate = EXPR_BEG; - return tOP_ASGN; - } - pushback(p, c); - return tANDOP; - } - else if (c == '=') { - yylval.id = intern_c('&'); - p->lstate = EXPR_BEG; - return tOP_ASGN; - } - pushback(p, c); - if (IS_SPCARG(c)) { - yywarning(p, "`&' interpreted as argument prefix"); - c = tAMPER; - } - else if (IS_BEG()) { - c = tAMPER; - } - else { - c = '&'; - } - if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { - p->lstate = EXPR_ARG; - } - else { - p->lstate = EXPR_BEG; - } - return c; - - case '|': - if ((c = nextc(p)) == '|') { - p->lstate = EXPR_BEG; - if ((c = nextc(p)) == '=') { - yylval.id = intern("||",2); - p->lstate = EXPR_BEG; - return tOP_ASGN; - } - pushback(p, c); - return tOROP; - } - if (c == '=') { - yylval.id = intern_c('|'); - p->lstate = EXPR_BEG; - return tOP_ASGN; - } - if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { - p->lstate = EXPR_ARG; - } - else { - p->lstate = EXPR_BEG; - } - pushback(p, c); - return '|'; - - case '+': - c = nextc(p); - if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { - p->lstate = EXPR_ARG; - if (c == '@') { - return tUPLUS; - } - pushback(p, c); - return '+'; - } - if (c == '=') { - yylval.id = intern_c('+'); - p->lstate = EXPR_BEG; - return tOP_ASGN; - } - if (IS_BEG() || (IS_SPCARG(c) && arg_ambiguous(p))) { - p->lstate = EXPR_BEG; - pushback(p, c); - if (c >= 0 && ISDIGIT(c)) { - c = '+'; - goto start_num; - } - return tUPLUS; - } - p->lstate = EXPR_BEG; - pushback(p, c); - return '+'; - - case '-': - c = nextc(p); - if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { - p->lstate = EXPR_ARG; - if (c == '@') { - return tUMINUS; - } - pushback(p, c); - return '-'; - } - if (c == '=') { - yylval.id = intern_c('-'); - p->lstate = EXPR_BEG; - return tOP_ASGN; - } - if (c == '>') { - p->lstate = EXPR_ENDFN; - return tLAMBDA; - } - if (IS_BEG() || (IS_SPCARG(c) && arg_ambiguous(p))) { - p->lstate = EXPR_BEG; - pushback(p, c); - if (c >= 0 && ISDIGIT(c)) { - return tUMINUS_NUM; - } - return tUMINUS; - } - p->lstate = EXPR_BEG; - pushback(p, c); - return '-'; - - case '.': - p->lstate = EXPR_BEG; - if ((c = nextc(p)) == '.') { - if ((c = nextc(p)) == '.') { - return tDOT3; - } - pushback(p, c); - return tDOT2; - } - pushback(p, c); - if (c >= 0 && ISDIGIT(c)) { - yyerror(p, "no . floating literal anymore; put 0 before dot"); - } - p->lstate = EXPR_DOT; - return '.'; - - start_num: - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - { - int is_float, seen_point, seen_e, nondigit; - - is_float = seen_point = seen_e = nondigit = 0; - p->lstate = EXPR_END; - newtok(p); - if (c == '-' || c == '+') { - tokadd(p, c); - c = nextc(p); - } - if (c == '0') { -#define no_digits() do {yyerror(p,"numeric literal without digits"); return 0;} while (0) - int start = toklen(p); - c = nextc(p); - if (c == 'x' || c == 'X') { - /* hexadecimal */ - c = nextc(p); - if (c >= 0 && ISXDIGIT(c)) { - do { - if (c == '_') { - if (nondigit) break; - nondigit = c; - continue; - } - if (!ISXDIGIT(c)) break; - nondigit = 0; - tokadd(p, tolower(c)); - } while ((c = nextc(p)) >= 0); - } - pushback(p, c); - tokfix(p); - if (toklen(p) == start) { - no_digits(); - } - else if (nondigit) goto trailing_uc; - yylval.nd = new_int(p, tok(p), 16); - return tINTEGER; - } - if (c == 'b' || c == 'B') { - /* binary */ - c = nextc(p); - if (c == '0' || c == '1') { - do { - if (c == '_') { - if (nondigit) break; - nondigit = c; - continue; - } - if (c != '0' && c != '1') break; - nondigit = 0; - tokadd(p, c); - } while ((c = nextc(p)) >= 0); - } - pushback(p, c); - tokfix(p); - if (toklen(p) == start) { - no_digits(); - } - else if (nondigit) goto trailing_uc; - yylval.nd = new_int(p, tok(p), 2); - return tINTEGER; - } - if (c == 'd' || c == 'D') { - /* decimal */ - c = nextc(p); - if (c >= 0 && ISDIGIT(c)) { - do { - if (c == '_') { - if (nondigit) break; - nondigit = c; - continue; - } - if (!ISDIGIT(c)) break; - nondigit = 0; - tokadd(p, c); - } while ((c = nextc(p)) >= 0); - } - pushback(p, c); - tokfix(p); - if (toklen(p) == start) { - no_digits(); - } - else if (nondigit) goto trailing_uc; - yylval.nd = new_int(p, tok(p), 10); - return tINTEGER; - } - if (c == '_') { - /* 0_0 */ - goto octal_number; - } - if (c == 'o' || c == 'O') { - /* prefixed octal */ - c = nextc(p); - if (c < 0 || c == '_' || !ISDIGIT(c)) { - no_digits(); - } - } - if (c >= '0' && c <= '7') { - /* octal */ - octal_number: - do { - if (c == '_') { - if (nondigit) break; - nondigit = c; - continue; - } - if (c < '0' || c > '9') break; - if (c > '7') goto invalid_octal; - nondigit = 0; - tokadd(p, c); - } while ((c = nextc(p)) >= 0); - - if (toklen(p) > start) { - pushback(p, c); - tokfix(p); - if (nondigit) goto trailing_uc; - yylval.nd = new_int(p, tok(p), 8); - return tINTEGER; - } - if (nondigit) { - pushback(p, c); - goto trailing_uc; - } - } - if (c > '7' && c <= '9') { - invalid_octal: - yyerror(p, "Invalid octal digit"); - } - else if (c == '.' || c == 'e' || c == 'E') { - tokadd(p, '0'); - } - else { - pushback(p, c); - yylval.nd = new_int(p, "0", 10); - return tINTEGER; - } - } - - for (;;) { - switch (c) { - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - nondigit = 0; - tokadd(p, c); - break; - - case '.': - if (nondigit) goto trailing_uc; - if (seen_point || seen_e) { - goto decode_num; - } - else { - int c0 = nextc(p); - if (c0 < 0 || !ISDIGIT(c0)) { - pushback(p, c0); - goto decode_num; - } - c = c0; - } - tokadd(p, '.'); - tokadd(p, c); - is_float++; - seen_point++; - nondigit = 0; - break; - - case 'e': - case 'E': - if (nondigit) { - pushback(p, c); - c = nondigit; - goto decode_num; - } - if (seen_e) { - goto decode_num; - } - tokadd(p, c); - seen_e++; - is_float++; - nondigit = c; - c = nextc(p); - if (c != '-' && c != '+') continue; - tokadd(p, c); - nondigit = c; - break; - - case '_': /* `_' in number just ignored */ - if (nondigit) goto decode_num; - nondigit = c; - break; - - default: - goto decode_num; - } - c = nextc(p); - } - - decode_num: - pushback(p, c); - if (nondigit) { - trailing_uc: - yyerror_i(p, "trailing `%c' in number", nondigit); - } - tokfix(p); - if (is_float) { - double d; - char *endp; - - errno = 0; - d = strtod(tok(p), &endp); - if (d == 0 && endp == tok(p)) { - yywarning_s(p, "corrupted float value %s", tok(p)); - } - else if (errno == ERANGE) { - yywarning_s(p, "float %s out of range", tok(p)); - errno = 0; - } - yylval.nd = new_float(p, tok(p)); - return tFLOAT; - } - yylval.nd = new_int(p, tok(p), 10); - return tINTEGER; - } - - case ')': - case ']': - p->paren_nest--; - case '}': - COND_LEXPOP(); - CMDARG_LEXPOP(); - if (c == ')') - p->lstate = EXPR_ENDFN; - else - p->lstate = EXPR_ENDARG; - return c; - - case ':': - c = nextc(p); - if (c == ':') { - if (IS_BEG() || p->lstate == EXPR_CLASS || IS_SPCARG(-1)) { - p->lstate = EXPR_BEG; - return tCOLON3; - } - p->lstate = EXPR_DOT; - return tCOLON2; - } - if (IS_END() || ISSPACE(c)) { - pushback(p, c); - p->lstate = EXPR_BEG; - return ':'; - } - pushback(p, c); - p->lstate = EXPR_FNAME; - return tSYMBEG; - - case '/': - if (IS_BEG()) { - p->lex_strterm = new_strterm(p, str_regexp, '/', 0); - return tREGEXP_BEG; - } - if ((c = nextc(p)) == '=') { - yylval.id = intern_c('/'); - p->lstate = EXPR_BEG; - return tOP_ASGN; - } - pushback(p, c); - if (IS_SPCARG(c)) { - p->lex_strterm = new_strterm(p, str_regexp, '/', 0); - return tREGEXP_BEG; - } - if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { - p->lstate = EXPR_ARG; - } - else { - p->lstate = EXPR_BEG; - } - return '/'; - - case '^': - if ((c = nextc(p)) == '=') { - yylval.id = intern_c('^'); - p->lstate = EXPR_BEG; - return tOP_ASGN; - } - if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { - p->lstate = EXPR_ARG; - } - else { - p->lstate = EXPR_BEG; - } - pushback(p, c); - return '^'; - - case ';': - p->lstate = EXPR_BEG; - return ';'; - - case ',': - p->lstate = EXPR_BEG; - return ','; - - case '~': - if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { - if ((c = nextc(p)) != '@') { - pushback(p, c); - } - p->lstate = EXPR_ARG; - } - else { - p->lstate = EXPR_BEG; - } - return '~'; - - case '(': - if (IS_BEG()) { - c = tLPAREN; - } - else if (IS_SPCARG(-1)) { - c = tLPAREN_ARG; - } - p->paren_nest++; - COND_PUSH(0); - CMDARG_PUSH(0); - p->lstate = EXPR_BEG; - return c; - - case '[': - p->paren_nest++; - if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { - p->lstate = EXPR_ARG; - if ((c = nextc(p)) == ']') { - if ((c = nextc(p)) == '=') { - return tASET; - } - pushback(p, c); - return tAREF; - } - pushback(p, c); - return '['; - } - else if (IS_BEG()) { - c = tLBRACK; - } - else if (IS_ARG() && space_seen) { - c = tLBRACK; - } - p->lstate = EXPR_BEG; - COND_PUSH(0); - CMDARG_PUSH(0); - return c; - - case '{': - if (p->lpar_beg && p->lpar_beg == p->paren_nest) { - p->lstate = EXPR_BEG; - p->lpar_beg = 0; - p->paren_nest--; - COND_PUSH(0); - CMDARG_PUSH(0); - return tLAMBEG; - } - if (IS_ARG() || p->lstate == EXPR_END || p->lstate == EXPR_ENDFN) - c = '{'; /* block (primary) */ - else if (p->lstate == EXPR_ENDARG) - c = tLBRACE_ARG; /* block (expr) */ - else - c = tLBRACE; /* hash */ - COND_PUSH(0); - CMDARG_PUSH(0); - p->lstate = EXPR_BEG; - return c; - - case '\\': - c = nextc(p); - if (c == '\n') { - p->lineno++; - p->column = 0; - space_seen = 1; - goto retry; /* skip \\n */ - } - pushback(p, c); - return '\\'; - - case '%': - if (IS_BEG()) { - int term; - int paren; - - c = nextc(p); - quotation: - if (c < 0 || !ISALNUM(c)) { - term = c; - c = 'Q'; - } - else { - term = nextc(p); - if (isalnum(term)) { - yyerror(p, "unknown type of %string"); - return 0; - } - } - if (c < 0 || term < 0) { - yyerror(p, "unterminated quoted string meets end of file"); - return 0; - } - paren = term; - if (term == '(') term = ')'; - else if (term == '[') term = ']'; - else if (term == '{') term = '}'; - else if (term == '<') term = '>'; - else paren = 0; - - switch (c) { - case 'Q': - p->lex_strterm = new_strterm(p, str_dquote, term, paren); - return tSTRING_BEG; - - case 'q': - p->lex_strterm = new_strterm(p, str_squote, term, paren); - return parse_string(p); - - case 'W': - p->lex_strterm = new_strterm(p, str_dword, term, paren); - return tWORDS_BEG; - - case 'w': - p->lex_strterm = new_strterm(p, str_sword, term, paren); - return tWORDS_BEG; - - case 'x': - p->lex_strterm = new_strterm(p, str_xquote, term, paren); - return tXSTRING_BEG; - - case 'r': - p->lex_strterm = new_strterm(p, str_regexp, term, paren); - return tREGEXP_BEG; - - case 's': - p->lex_strterm = new_strterm(p, str_ssym, term, paren); - return tSYMBEG; - - case 'I': - p->lex_strterm = new_strterm(p, str_dsymbols, term, paren); - return tSYMBOLS_BEG; - - case 'i': - p->lex_strterm = new_strterm(p, str_ssymbols, term, paren); - return tSYMBOLS_BEG; - - default: - yyerror(p, "unknown type of %string"); - return 0; - } - } - if ((c = nextc(p)) == '=') { - yylval.id = intern_c('%'); - p->lstate = EXPR_BEG; - return tOP_ASGN; - } - if (IS_SPCARG(c)) { - goto quotation; - } - if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { - p->lstate = EXPR_ARG; - } - else { - p->lstate = EXPR_BEG; - } - pushback(p, c); - return '%'; - - case '$': - p->lstate = EXPR_END; - token_column = newtok(p); - c = nextc(p); - if (c < 0) { - yyerror(p, "incomplete global variable syntax"); - return 0; - } - switch (c) { - case '_': /* $_: last read line string */ - c = nextc(p); - if (c >= 0 && identchar(c)) { /* if there is more after _ it is a variable */ - tokadd(p, '$'); - tokadd(p, c); - break; - } - pushback(p, c); - c = '_'; - /* fall through */ - case '~': /* $~: match-data */ - case '*': /* $*: argv */ - case '$': /* $$: pid */ - case '?': /* $?: last status */ - case '!': /* $!: error string */ - case '@': /* $@: error position */ - case '/': /* $/: input record separator */ - case '\\': /* $\: output record separator */ - case ';': /* $;: field separator */ - case ',': /* $,: output field separator */ - case '.': /* $.: last read line number */ - case '=': /* $=: ignorecase */ - case ':': /* $:: load path */ - case '<': /* $<: reading filename */ - case '>': /* $>: default output handle */ - case '\"': /* $": already loaded files */ - tokadd(p, '$'); - tokadd(p, c); - tokfix(p); - yylval.id = intern_cstr(tok(p)); - return tGVAR; - - case '-': - tokadd(p, '$'); - tokadd(p, c); - c = nextc(p); - pushback(p, c); - gvar: - tokfix(p); - yylval.id = intern_cstr(tok(p)); - return tGVAR; - - case '&': /* $&: last match */ - case '`': /* $`: string before last match */ - case '\'': /* $': string after last match */ - case '+': /* $+: string matches last pattern */ - if (last_state == EXPR_FNAME) { - tokadd(p, '$'); - tokadd(p, c); - goto gvar; - } - yylval.nd = new_back_ref(p, c); - return tBACK_REF; - - case '1': case '2': case '3': - case '4': case '5': case '6': - case '7': case '8': case '9': - do { - tokadd(p, c); - c = nextc(p); - } while (c >= 0 && isdigit(c)); - pushback(p, c); - if (last_state == EXPR_FNAME) goto gvar; - tokfix(p); - { - unsigned long n = strtoul(tok(p), NULL, 10); - if (n > INT_MAX) { - yyerror_i(p, "capture group index must be <= %d", INT_MAX); - return 0; - } - yylval.nd = new_nth_ref(p, (int)n); - } - return tNTH_REF; - - default: - if (!identchar(c)) { - pushback(p, c); - return '$'; - } - case '0': - tokadd(p, '$'); - } - break; - - case '@': - c = nextc(p); - token_column = newtok(p); - tokadd(p, '@'); - if (c == '@') { - tokadd(p, '@'); - c = nextc(p); - } - if (c < 0) { - if (p->bidx == 1) { - yyerror(p, "incomplete instance variable syntax"); - } - else { - yyerror(p, "incomplete class variable syntax"); - } - return 0; - } - else if (isdigit(c)) { - if (p->bidx == 1) { - yyerror_i(p, "`@%c' is not allowed as an instance variable name", c); - } - else { - yyerror_i(p, "`@@%c' is not allowed as a class variable name", c); - } - return 0; - } - if (!identchar(c)) { - pushback(p, c); - return '@'; - } - break; - - case '_': - token_column = newtok(p); - break; - - default: - if (!identchar(c)) { - yyerror_i(p, "Invalid char `\\x%02X' in expression", c); - goto retry; - } - - token_column = newtok(p); - break; - } - - do { - tokadd(p, c); - c = nextc(p); - if (c < 0) break; - } while (identchar(c)); - if (token_column == 0 && toklen(p) == 7 && (c < 0 || c == '\n') && - strncmp(tok(p), "__END__", toklen(p)) == 0) - return -1; - - switch (tok(p)[0]) { - case '@': case '$': - pushback(p, c); - break; - default: - if ((c == '!' || c == '?') && !peek(p, '=')) { - tokadd(p, c); - } - else { - pushback(p, c); - } - } - tokfix(p); - { - int result = 0; - - switch (tok(p)[0]) { - case '$': - p->lstate = EXPR_END; - result = tGVAR; - break; - case '@': - p->lstate = EXPR_END; - if (tok(p)[1] == '@') - result = tCVAR; - else - result = tIVAR; - break; - - default: - if (toklast(p) == '!' || toklast(p) == '?') { - result = tFID; - } - else { - if (p->lstate == EXPR_FNAME) { - if ((c = nextc(p)) == '=' && !peek(p, '~') && !peek(p, '>') && - (!peek(p, '=') || (peek_n(p, '>', 1)))) { - result = tIDENTIFIER; - tokadd(p, c); - tokfix(p); - } - else { - pushback(p, c); - } - } - if (result == 0 && ISUPPER(tok(p)[0])) { - result = tCONSTANT; - } - else { - result = tIDENTIFIER; - } - } - - if (IS_LABEL_POSSIBLE()) { - if (IS_LABEL_SUFFIX(0)) { - p->lstate = EXPR_BEG; - nextc(p); - tokfix(p); - yylval.id = intern_cstr(tok(p)); - return tLABEL; - } - } - if (p->lstate != EXPR_DOT) { - const struct kwtable *kw; - - /* See if it is a reserved word. */ - kw = mrb_reserved_word(tok(p), toklen(p)); - if (kw) { - enum mrb_lex_state_enum state = p->lstate; - yylval.num = p->lineno; - p->lstate = kw->state; - if (state == EXPR_FNAME) { - yylval.id = intern_cstr(kw->name); - return kw->id[0]; - } - if (p->lstate == EXPR_BEG) { - p->cmd_start = TRUE; - } - if (kw->id[0] == keyword_do) { - if (p->lpar_beg && p->lpar_beg == p->paren_nest) { - p->lpar_beg = 0; - p->paren_nest--; - return keyword_do_LAMBDA; - } - if (COND_P()) return keyword_do_cond; - if (CMDARG_P() && state != EXPR_CMDARG) - return keyword_do_block; - if (state == EXPR_ENDARG || state == EXPR_BEG) - return keyword_do_block; - return keyword_do; - } - if (state == EXPR_BEG || state == EXPR_VALUE) - return kw->id[0]; - else { - if (kw->id[0] != kw->id[1]) - p->lstate = EXPR_BEG; - return kw->id[1]; - } - } - } - - if (IS_BEG() || p->lstate == EXPR_DOT || IS_ARG()) { - if (cmd_state) { - p->lstate = EXPR_CMDARG; - } - else { - p->lstate = EXPR_ARG; - } - } - else if (p->lstate == EXPR_FNAME) { - p->lstate = EXPR_ENDFN; - } - else { - p->lstate = EXPR_END; - } - } - { - mrb_sym ident = intern_cstr(tok(p)); - - yylval.id = ident; -#if 0 - if (last_state != EXPR_DOT && islower(tok(p)[0]) && lvar_defined(ident)) { - p->lstate = EXPR_END; - } -#endif - } - return result; - } -} - -static int -yylex(void *lval, parser_state *p) -{ - p->ylval = lval; - return parser_yylex(p); -} - -static void -parser_init_cxt(parser_state *p, mrbc_context *cxt) -{ - if (!cxt) return; - if (cxt->filename) mrb_parser_set_filename(p, cxt->filename); - if (cxt->lineno) p->lineno = cxt->lineno; - if (cxt->syms) { - int i; - - p->locals = cons(0,0); - for (i=0; islen; i++) { - local_add_f(p, cxt->syms[i]); - } - } - p->capture_errors = cxt->capture_errors; - p->no_optimize = cxt->no_optimize; - if (cxt->partial_hook) { - p->cxt = cxt; - } -} - -static void -parser_update_cxt(parser_state *p, mrbc_context *cxt) -{ - node *n, *n0; - int i = 0; - - if (!cxt) return; - if ((int)(intptr_t)p->tree->car != NODE_SCOPE) return; - n0 = n = p->tree->cdr->car; - while (n) { - i++; - n = n->cdr; - } - cxt->syms = (mrb_sym *)mrb_realloc(p->mrb, cxt->syms, i*sizeof(mrb_sym)); - cxt->slen = i; - for (i=0, n=n0; n; i++,n=n->cdr) { - cxt->syms[i] = sym(n->car); - } -} - -void mrb_codedump_all(mrb_state*, struct RProc*); -void mrb_parser_dump(mrb_state *mrb, node *tree, int offset); - -MRB_API void -mrb_parser_parse(parser_state *p, mrbc_context *c) -{ - struct mrb_jmpbuf buf; - p->jmp = &buf; - - MRB_TRY(p->jmp) { - - p->cmd_start = TRUE; - p->in_def = p->in_single = 0; - p->nerr = p->nwarn = 0; - p->lex_strterm = NULL; - - parser_init_cxt(p, c); - yyparse(p); - if (!p->tree) { - p->tree = new_nil(p); - } - parser_update_cxt(p, c); - if (c && c->dump_result) { - mrb_parser_dump(p->mrb, p->tree, 0); - } - - } - MRB_CATCH(p->jmp) { - yyerror(p, "memory allocation error"); - p->nerr++; - p->tree = 0; - return; - } - MRB_END_EXC(p->jmp); -} - -MRB_API parser_state* -mrb_parser_new(mrb_state *mrb) -{ - mrb_pool *pool; - parser_state *p; - static const parser_state parser_state_zero = { 0 }; - - pool = mrb_pool_open(mrb); - if (!pool) return NULL; - p = (parser_state *)mrb_pool_alloc(pool, sizeof(parser_state)); - if (!p) return NULL; - - *p = parser_state_zero; - p->mrb = mrb; - p->pool = pool; - - p->s = p->send = NULL; -#ifdef ENABLE_STDIO - p->f = NULL; -#endif - - p->cmd_start = TRUE; - p->in_def = p->in_single = 0; - - p->capture_errors = FALSE; - p->lineno = 1; - p->column = 0; -#if defined(PARSER_TEST) || defined(PARSER_DEBUG) - yydebug = 1; -#endif - - p->lex_strterm = NULL; - p->all_heredocs = p->parsing_heredoc = NULL; - p->lex_strterm_before_heredoc = NULL; - - p->current_filename_index = -1; - p->filename_table = NULL; - p->filename_table_length = 0; - - return p; -} - -MRB_API void -mrb_parser_free(parser_state *p) { - mrb_pool_close(p->pool); -} - -MRB_API mrbc_context* -mrbc_context_new(mrb_state *mrb) -{ - return (mrbc_context *)mrb_calloc(mrb, 1, sizeof(mrbc_context)); -} - -MRB_API void -mrbc_context_free(mrb_state *mrb, mrbc_context *cxt) -{ - mrb_free(mrb, cxt->syms); - mrb_free(mrb, cxt); -} - -MRB_API const char* -mrbc_filename(mrb_state *mrb, mrbc_context *c, const char *s) -{ - if (s) { - int len = strlen(s); - char *p = (char *)mrb_alloca(mrb, len + 1); - - memcpy(p, s, len + 1); - c->filename = p; - } - return c->filename; -} - -MRB_API void -mrbc_partial_hook(mrb_state *mrb, mrbc_context *c, int (*func)(struct mrb_parser_state*), void *data) -{ - c->partial_hook = func; - c->partial_data = data; -} - -MRB_API void -mrb_parser_set_filename(struct mrb_parser_state *p, const char *f) -{ - mrb_sym sym; - size_t i; - mrb_sym* new_table; - - sym = mrb_intern_cstr(p->mrb, f); - p->filename = mrb_sym2name_len(p->mrb, sym, NULL); - p->lineno = (p->filename_table_length > 0)? 0 : 1; - - for (i = 0; i < p->filename_table_length; ++i) { - if (p->filename_table[i] == sym) { - p->current_filename_index = i; - return; - } - } - - p->current_filename_index = p->filename_table_length++; - - new_table = (mrb_sym*)parser_palloc(p, sizeof(mrb_sym) * p->filename_table_length); - if (p->filename_table) { - memmove(new_table, p->filename_table, sizeof(mrb_sym) * p->filename_table_length); - } - p->filename_table = new_table; - p->filename_table[p->filename_table_length - 1] = sym; -} - -MRB_API char const* -mrb_parser_get_filename(struct mrb_parser_state* p, uint16_t idx) { - if (idx >= p->filename_table_length) { return NULL; } - else { - return mrb_sym2name_len(p->mrb, p->filename_table[idx], NULL); - } -} - -#ifdef ENABLE_STDIO -MRB_API parser_state* -mrb_parse_file(mrb_state *mrb, FILE *f, mrbc_context *c) -{ - parser_state *p; - - p = mrb_parser_new(mrb); - if (!p) return NULL; - p->s = p->send = NULL; - p->f = f; - - mrb_parser_parse(p, c); - return p; -} -#endif - -MRB_API parser_state* -mrb_parse_nstring(mrb_state *mrb, const char *s, int len, mrbc_context *c) -{ - parser_state *p; - - p = mrb_parser_new(mrb); - if (!p) return NULL; - p->s = s; - p->send = s + len; - - mrb_parser_parse(p, c); - return p; -} - -MRB_API parser_state* -mrb_parse_string(mrb_state *mrb, const char *s, mrbc_context *c) -{ - return mrb_parse_nstring(mrb, s, strlen(s), c); -} - -static mrb_value -load_exec(mrb_state *mrb, parser_state *p, mrbc_context *c) -{ - struct RClass *target = mrb->object_class; - struct RProc *proc; - mrb_value v; - unsigned int keep = 0; - - if (!p) { - return mrb_undef_value(); - } - if (!p->tree || p->nerr) { - if (p->capture_errors) { - char buf[256]; - int n; - - n = snprintf(buf, sizeof(buf), "line %d: %s\n", - p->error_buffer[0].lineno, p->error_buffer[0].message); - mrb->exc = mrb_obj_ptr(mrb_exc_new(mrb, E_SYNTAX_ERROR, buf, n)); - mrb_parser_free(p); - return mrb_undef_value(); - } - else { - mrb->exc = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, E_SYNTAX_ERROR, "syntax error")); - mrb_parser_free(p); - return mrb_undef_value(); - } - } - proc = mrb_generate_code(mrb, p); - mrb_parser_free(p); - if (proc == NULL) { - mrb->exc = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, E_SCRIPT_ERROR, "codegen error")); - return mrb_undef_value(); - } - if (c) { - if (c->dump_result) mrb_codedump_all(mrb, proc); - if (c->no_exec) return mrb_obj_value(proc); - if (c->target_class) { - target = c->target_class; - } - if (c->keep_lv) { - keep = c->slen + 1; - } - else { - c->keep_lv = TRUE; - } - } - proc->target_class = target; - if (mrb->c->ci) { - mrb->c->ci->target_class = target; - } - v = mrb_toplevel_run_keep(mrb, proc, keep); - if (mrb->exc) return mrb_nil_value(); - return v; -} - -#ifdef ENABLE_STDIO -MRB_API mrb_value -mrb_load_file_cxt(mrb_state *mrb, FILE *f, mrbc_context *c) -{ - return load_exec(mrb, mrb_parse_file(mrb, f, c), c); -} - -MRB_API mrb_value -mrb_load_file(mrb_state *mrb, FILE *f) -{ - return mrb_load_file_cxt(mrb, f, NULL); -} -#endif - -MRB_API mrb_value -mrb_load_nstring_cxt(mrb_state *mrb, const char *s, int len, mrbc_context *c) -{ - return load_exec(mrb, mrb_parse_nstring(mrb, s, len, c), c); -} - -MRB_API mrb_value -mrb_load_nstring(mrb_state *mrb, const char *s, int len) -{ - return mrb_load_nstring_cxt(mrb, s, len, NULL); -} - -MRB_API mrb_value -mrb_load_string_cxt(mrb_state *mrb, const char *s, mrbc_context *c) -{ - return mrb_load_nstring_cxt(mrb, s, strlen(s), c); -} - -MRB_API mrb_value -mrb_load_string(mrb_state *mrb, const char *s) -{ - return mrb_load_string_cxt(mrb, s, NULL); -} - -#ifdef ENABLE_STDIO - -static void -dump_prefix(node *tree, int offset) -{ - printf("%05d ", tree->lineno); - while (offset--) { - putc(' ', stdout); - putc(' ', stdout); - } -} - -static void -dump_recur(mrb_state *mrb, node *tree, int offset) -{ - while (tree) { - mrb_parser_dump(mrb, tree->car, offset); - tree = tree->cdr; - } -} - -#endif - -void -mrb_parser_dump(mrb_state *mrb, node *tree, int offset) -{ -#ifdef ENABLE_STDIO - int nodetype; - - if (!tree) return; - again: - dump_prefix(tree, offset); - nodetype = (int)(intptr_t)tree->car; - tree = tree->cdr; - switch (nodetype) { - case NODE_BEGIN: - printf("NODE_BEGIN:\n"); - dump_recur(mrb, tree, offset+1); - break; - - case NODE_RESCUE: - printf("NODE_RESCUE:\n"); - if (tree->car) { - dump_prefix(tree, offset+1); - printf("body:\n"); - mrb_parser_dump(mrb, tree->car, offset+2); - } - tree = tree->cdr; - if (tree->car) { - node *n2 = tree->car; - - dump_prefix(n2, offset+1); - printf("rescue:\n"); - while (n2) { - node *n3 = n2->car; - if (n3->car) { - dump_prefix(n2, offset+2); - printf("handle classes:\n"); - dump_recur(mrb, n3->car, offset+3); - } - if (n3->cdr->car) { - dump_prefix(n3, offset+2); - printf("exc_var:\n"); - mrb_parser_dump(mrb, n3->cdr->car, offset+3); - } - if (n3->cdr->cdr->car) { - dump_prefix(n3, offset+2); - printf("rescue body:\n"); - mrb_parser_dump(mrb, n3->cdr->cdr->car, offset+3); - } - n2 = n2->cdr; - } - } - tree = tree->cdr; - if (tree->car) { - dump_prefix(tree, offset+1); - printf("else:\n"); - mrb_parser_dump(mrb, tree->car, offset+2); - } - break; - - case NODE_ENSURE: - printf("NODE_ENSURE:\n"); - dump_prefix(tree, offset+1); - printf("body:\n"); - mrb_parser_dump(mrb, tree->car, offset+2); - dump_prefix(tree, offset+1); - printf("ensure:\n"); - mrb_parser_dump(mrb, tree->cdr->cdr, offset+2); - break; - - case NODE_LAMBDA: - printf("NODE_BLOCK:\n"); - goto block; - - case NODE_BLOCK: - block: - printf("NODE_BLOCK:\n"); - tree = tree->cdr; - if (tree->car) { - node *n = tree->car; - - if (n->car) { - dump_prefix(n, offset+1); - printf("mandatory args:\n"); - dump_recur(mrb, n->car, offset+2); - } - n = n->cdr; - if (n->car) { - dump_prefix(n, offset+1); - printf("optional args:\n"); - { - node *n2 = n->car; - - while (n2) { - dump_prefix(n2, offset+2); - printf("%s=", mrb_sym2name(mrb, sym(n2->car->car))); - mrb_parser_dump(mrb, n2->car->cdr, 0); - n2 = n2->cdr; - } - } - } - n = n->cdr; - if (n->car) { - dump_prefix(n, offset+1); - printf("rest=*%s\n", mrb_sym2name(mrb, sym(n->car))); - } - n = n->cdr; - if (n->car) { - dump_prefix(n, offset+1); - printf("post mandatory args:\n"); - dump_recur(mrb, n->car, offset+2); - } - n = n->cdr; - if (n) { - dump_prefix(n, offset+1); - printf("blk=&%s\n", mrb_sym2name(mrb, sym(n))); - } - } - dump_prefix(tree, offset+1); - printf("body:\n"); - mrb_parser_dump(mrb, tree->cdr->car, offset+2); - break; - - case NODE_IF: - printf("NODE_IF:\n"); - dump_prefix(tree, offset+1); - printf("cond:\n"); - mrb_parser_dump(mrb, tree->car, offset+2); - dump_prefix(tree, offset+1); - printf("then:\n"); - mrb_parser_dump(mrb, tree->cdr->car, offset+2); - if (tree->cdr->cdr->car) { - dump_prefix(tree, offset+1); - printf("else:\n"); - mrb_parser_dump(mrb, tree->cdr->cdr->car, offset+2); - } - break; - - case NODE_AND: - printf("NODE_AND:\n"); - mrb_parser_dump(mrb, tree->car, offset+1); - mrb_parser_dump(mrb, tree->cdr, offset+1); - break; - - case NODE_OR: - printf("NODE_OR:\n"); - mrb_parser_dump(mrb, tree->car, offset+1); - mrb_parser_dump(mrb, tree->cdr, offset+1); - break; - - case NODE_CASE: - printf("NODE_CASE:\n"); - if (tree->car) { - mrb_parser_dump(mrb, tree->car, offset+1); - } - tree = tree->cdr; - while (tree) { - dump_prefix(tree, offset+1); - printf("case:\n"); - dump_recur(mrb, tree->car->car, offset+2); - dump_prefix(tree, offset+1); - printf("body:\n"); - mrb_parser_dump(mrb, tree->car->cdr, offset+2); - tree = tree->cdr; - } - break; - - case NODE_WHILE: - printf("NODE_WHILE:\n"); - dump_prefix(tree, offset+1); - printf("cond:\n"); - mrb_parser_dump(mrb, tree->car, offset+2); - dump_prefix(tree, offset+1); - printf("body:\n"); - mrb_parser_dump(mrb, tree->cdr, offset+2); - break; - - case NODE_UNTIL: - printf("NODE_UNTIL:\n"); - dump_prefix(tree, offset+1); - printf("cond:\n"); - mrb_parser_dump(mrb, tree->car, offset+2); - dump_prefix(tree, offset+1); - printf("body:\n"); - mrb_parser_dump(mrb, tree->cdr, offset+2); - break; - - case NODE_FOR: - printf("NODE_FOR:\n"); - dump_prefix(tree, offset+1); - printf("var:\n"); - { - node *n2 = tree->car; - - if (n2->car) { - dump_prefix(n2, offset+2); - printf("pre:\n"); - dump_recur(mrb, n2->car, offset+3); - } - n2 = n2->cdr; - if (n2) { - if (n2->car) { - dump_prefix(n2, offset+2); - printf("rest:\n"); - mrb_parser_dump(mrb, n2->car, offset+3); - } - n2 = n2->cdr; - if (n2) { - if (n2->car) { - dump_prefix(n2, offset+2); - printf("post:\n"); - dump_recur(mrb, n2->car, offset+3); - } - } - } - } - tree = tree->cdr; - dump_prefix(tree, offset+1); - printf("in:\n"); - mrb_parser_dump(mrb, tree->car, offset+2); - tree = tree->cdr; - dump_prefix(tree, offset+1); - printf("do:\n"); - mrb_parser_dump(mrb, tree->car, offset+2); - break; - - case NODE_SCOPE: - printf("NODE_SCOPE:\n"); - { - node *n2 = tree->car; - mrb_bool first_lval = TRUE; - - if (n2 && (n2->car || n2->cdr)) { - dump_prefix(n2, offset+1); - printf("local variables:\n"); - dump_prefix(n2, offset+2); - while (n2) { - if (n2->car) { - if (!first_lval) printf(", "); - printf("%s", mrb_sym2name(mrb, sym(n2->car))); - first_lval = FALSE; - } - n2 = n2->cdr; - } - printf("\n"); - } - } - tree = tree->cdr; - offset++; - goto again; - - case NODE_FCALL: - case NODE_CALL: - printf("NODE_CALL:\n"); - mrb_parser_dump(mrb, tree->car, offset+1); - dump_prefix(tree, offset+1); - printf("method='%s' (%d)\n", - mrb_sym2name(mrb, sym(tree->cdr->car)), - (int)(intptr_t)tree->cdr->car); - tree = tree->cdr->cdr->car; - if (tree) { - dump_prefix(tree, offset+1); - printf("args:\n"); - dump_recur(mrb, tree->car, offset+2); - if (tree->cdr) { - dump_prefix(tree, offset+1); - printf("block:\n"); - mrb_parser_dump(mrb, tree->cdr, offset+2); - } - } - break; - - case NODE_DOT2: - printf("NODE_DOT2:\n"); - mrb_parser_dump(mrb, tree->car, offset+1); - mrb_parser_dump(mrb, tree->cdr, offset+1); - break; - - case NODE_DOT3: - printf("NODE_DOT3:\n"); - mrb_parser_dump(mrb, tree->car, offset+1); - mrb_parser_dump(mrb, tree->cdr, offset+1); - break; - - case NODE_COLON2: - printf("NODE_COLON2:\n"); - mrb_parser_dump(mrb, tree->car, offset+1); - dump_prefix(tree, offset+1); - printf("::%s\n", mrb_sym2name(mrb, sym(tree->cdr))); - break; - - case NODE_COLON3: - printf("NODE_COLON3:\n"); - dump_prefix(tree, offset+1); - printf("::%s\n", mrb_sym2name(mrb, sym(tree))); - break; - - case NODE_ARRAY: - printf("NODE_ARRAY:\n"); - dump_recur(mrb, tree, offset+1); - break; - - case NODE_HASH: - printf("NODE_HASH:\n"); - while (tree) { - dump_prefix(tree, offset+1); - printf("key:\n"); - mrb_parser_dump(mrb, tree->car->car, offset+2); - dump_prefix(tree, offset+1); - printf("value:\n"); - mrb_parser_dump(mrb, tree->car->cdr, offset+2); - tree = tree->cdr; - } - break; - - case NODE_SPLAT: - printf("NODE_SPLAT:\n"); - mrb_parser_dump(mrb, tree, offset+1); - break; - - case NODE_ASGN: - printf("NODE_ASGN:\n"); - dump_prefix(tree, offset+1); - printf("lhs:\n"); - mrb_parser_dump(mrb, tree->car, offset+2); - dump_prefix(tree, offset+1); - printf("rhs:\n"); - mrb_parser_dump(mrb, tree->cdr, offset+2); - break; - - case NODE_MASGN: - printf("NODE_MASGN:\n"); - dump_prefix(tree, offset+1); - printf("mlhs:\n"); - { - node *n2 = tree->car; - - if (n2->car) { - dump_prefix(tree, offset+2); - printf("pre:\n"); - dump_recur(mrb, n2->car, offset+3); - } - n2 = n2->cdr; - if (n2) { - if (n2->car) { - dump_prefix(n2, offset+2); - printf("rest:\n"); - if (n2->car == (node*)-1) { - dump_prefix(n2, offset+2); - printf("(empty)\n"); - } - else { - mrb_parser_dump(mrb, n2->car, offset+3); - } - } - n2 = n2->cdr; - if (n2) { - if (n2->car) { - dump_prefix(n2, offset+2); - printf("post:\n"); - dump_recur(mrb, n2->car, offset+3); - } - } - } - } - dump_prefix(tree, offset+1); - printf("rhs:\n"); - mrb_parser_dump(mrb, tree->cdr, offset+2); - break; - - case NODE_OP_ASGN: - printf("NODE_OP_ASGN:\n"); - dump_prefix(tree, offset+1); - printf("lhs:\n"); - mrb_parser_dump(mrb, tree->car, offset+2); - tree = tree->cdr; - dump_prefix(tree, offset+1); - printf("op='%s' (%d)\n", mrb_sym2name(mrb, sym(tree->car)), (int)(intptr_t)tree->car); - tree = tree->cdr; - mrb_parser_dump(mrb, tree->car, offset+1); - break; - - case NODE_SUPER: - printf("NODE_SUPER:\n"); - if (tree) { - dump_prefix(tree, offset+1); - printf("args:\n"); - dump_recur(mrb, tree->car, offset+2); - if (tree->cdr) { - dump_prefix(tree, offset+1); - printf("block:\n"); - mrb_parser_dump(mrb, tree->cdr, offset+2); - } - } - break; - - case NODE_ZSUPER: - printf("NODE_ZSUPER\n"); - break; - - case NODE_RETURN: - printf("NODE_RETURN:\n"); - mrb_parser_dump(mrb, tree, offset+1); - break; - - case NODE_YIELD: - printf("NODE_YIELD:\n"); - dump_recur(mrb, tree, offset+1); - break; - - case NODE_BREAK: - printf("NODE_BREAK:\n"); - mrb_parser_dump(mrb, tree, offset+1); - break; - - case NODE_NEXT: - printf("NODE_NEXT:\n"); - mrb_parser_dump(mrb, tree, offset+1); - break; - - case NODE_REDO: - printf("NODE_REDO\n"); - break; - - case NODE_RETRY: - printf("NODE_RETRY\n"); - break; - - case NODE_LVAR: - printf("NODE_LVAR %s\n", mrb_sym2name(mrb, sym(tree))); - break; - - case NODE_GVAR: - printf("NODE_GVAR %s\n", mrb_sym2name(mrb, sym(tree))); - break; - - case NODE_IVAR: - printf("NODE_IVAR %s\n", mrb_sym2name(mrb, sym(tree))); - break; - - case NODE_CVAR: - printf("NODE_CVAR %s\n", mrb_sym2name(mrb, sym(tree))); - break; - - case NODE_CONST: - printf("NODE_CONST %s\n", mrb_sym2name(mrb, sym(tree))); - break; - - case NODE_MATCH: - printf("NODE_MATCH:\n"); - dump_prefix(tree, offset + 1); - printf("lhs:\n"); - mrb_parser_dump(mrb, tree->car, offset + 2); - dump_prefix(tree, offset + 1); - printf("rhs:\n"); - mrb_parser_dump(mrb, tree->cdr, offset + 2); - break; - - case NODE_BACK_REF: - printf("NODE_BACK_REF: $%c\n", (int)(intptr_t)tree); - break; - - case NODE_NTH_REF: - printf("NODE_NTH_REF: $%d\n", (int)(intptr_t)tree); - break; - - case NODE_ARG: - printf("NODE_ARG %s\n", mrb_sym2name(mrb, sym(tree))); - break; - - case NODE_BLOCK_ARG: - printf("NODE_BLOCK_ARG:\n"); - mrb_parser_dump(mrb, tree, offset+1); - break; - - case NODE_INT: - printf("NODE_INT %s base %d\n", (char*)tree->car, (int)(intptr_t)tree->cdr->car); - break; - - case NODE_FLOAT: - printf("NODE_FLOAT %s\n", (char*)tree); - break; - - case NODE_NEGATE: - printf("NODE_NEGATE\n"); - mrb_parser_dump(mrb, tree, offset+1); - break; - - case NODE_STR: - printf("NODE_STR \"%s\" len %d\n", (char*)tree->car, (int)(intptr_t)tree->cdr); - break; - - case NODE_DSTR: - printf("NODE_DSTR\n"); - dump_recur(mrb, tree, offset+1); - break; - - case NODE_XSTR: - printf("NODE_XSTR \"%s\" len %d\n", (char*)tree->car, (int)(intptr_t)tree->cdr); - break; - - case NODE_DXSTR: - printf("NODE_DXSTR\n"); - dump_recur(mrb, tree, offset+1); - break; - - case NODE_REGX: - printf("NODE_REGX /%s/%s\n", (char*)tree->car, (char*)tree->cdr); - break; - - case NODE_DREGX: - printf("NODE_DREGX\n"); - dump_recur(mrb, tree->car, offset+1); - dump_prefix(tree, offset); - printf("tail: %s\n", (char*)tree->cdr->cdr->car); - dump_prefix(tree, offset); - printf("opt: %s\n", (char*)tree->cdr->cdr->cdr); - break; - - case NODE_SYM: - printf("NODE_SYM :%s\n", mrb_sym2name(mrb, sym(tree))); - break; - - case NODE_SELF: - printf("NODE_SELF\n"); - break; - - case NODE_NIL: - printf("NODE_NIL\n"); - break; - - case NODE_TRUE: - printf("NODE_TRUE\n"); - break; - - case NODE_FALSE: - printf("NODE_FALSE\n"); - break; - - case NODE_ALIAS: - printf("NODE_ALIAS %s %s:\n", - mrb_sym2name(mrb, sym(tree->car)), - mrb_sym2name(mrb, sym(tree->cdr))); - break; - - case NODE_UNDEF: - printf("NODE_UNDEF"); - { - node *t = tree; - while (t) { - printf(" %s", mrb_sym2name(mrb, sym(t->car))); - t = t->cdr; - } - } - printf(":\n"); - break; - - case NODE_CLASS: - printf("NODE_CLASS:\n"); - if (tree->car->car == (node*)0) { - dump_prefix(tree, offset+1); - printf(":%s\n", mrb_sym2name(mrb, sym(tree->car->cdr))); - } - else if (tree->car->car == (node*)1) { - dump_prefix(tree, offset+1); - printf("::%s\n", mrb_sym2name(mrb, sym(tree->car->cdr))); - } - else { - mrb_parser_dump(mrb, tree->car->car, offset+1); - dump_prefix(tree, offset+1); - printf("::%s\n", mrb_sym2name(mrb, sym(tree->car->cdr))); - } - if (tree->cdr->car) { - dump_prefix(tree, offset+1); - printf("super:\n"); - mrb_parser_dump(mrb, tree->cdr->car, offset+2); - } - dump_prefix(tree, offset+1); - printf("body:\n"); - mrb_parser_dump(mrb, tree->cdr->cdr->car->cdr, offset+2); - break; - - case NODE_MODULE: - printf("NODE_MODULE:\n"); - if (tree->car->car == (node*)0) { - dump_prefix(tree, offset+1); - printf(":%s\n", mrb_sym2name(mrb, sym(tree->car->cdr))); - } - else if (tree->car->car == (node*)1) { - dump_prefix(tree, offset+1); - printf("::%s\n", mrb_sym2name(mrb, sym(tree->car->cdr))); - } - else { - mrb_parser_dump(mrb, tree->car->car, offset+1); - dump_prefix(tree, offset+1); - printf("::%s\n", mrb_sym2name(mrb, sym(tree->car->cdr))); - } - dump_prefix(tree, offset+1); - printf("body:\n"); - mrb_parser_dump(mrb, tree->cdr->car->cdr, offset+2); - break; - - case NODE_SCLASS: - printf("NODE_SCLASS:\n"); - mrb_parser_dump(mrb, tree->car, offset+1); - dump_prefix(tree, offset+1); - printf("body:\n"); - mrb_parser_dump(mrb, tree->cdr->car->cdr, offset+2); - break; - - case NODE_DEF: - printf("NODE_DEF:\n"); - dump_prefix(tree, offset+1); - printf("%s\n", mrb_sym2name(mrb, sym(tree->car))); - tree = tree->cdr; - { - node *n2 = tree->car; - mrb_bool first_lval = TRUE; - - if (n2 && (n2->car || n2->cdr)) { - dump_prefix(n2, offset+1); - printf("local variables:\n"); - dump_prefix(n2, offset+2); - while (n2) { - if (n2->car) { - if (!first_lval) printf(", "); - printf("%s", mrb_sym2name(mrb, sym(n2->car))); - first_lval = FALSE; - } - n2 = n2->cdr; - } - printf("\n"); - } - } - tree = tree->cdr; - if (tree->car) { - node *n = tree->car; - - if (n->car) { - dump_prefix(n, offset+1); - printf("mandatory args:\n"); - dump_recur(mrb, n->car, offset+2); - } - n = n->cdr; - if (n->car) { - dump_prefix(n, offset+1); - printf("optional args:\n"); - { - node *n2 = n->car; - - while (n2) { - dump_prefix(n2, offset+2); - printf("%s=", mrb_sym2name(mrb, sym(n2->car->car))); - mrb_parser_dump(mrb, n2->car->cdr, 0); - n2 = n2->cdr; - } - } - } - n = n->cdr; - if (n->car) { - dump_prefix(n, offset+1); - printf("rest=*%s\n", mrb_sym2name(mrb, sym(n->car))); - } - n = n->cdr; - if (n->car) { - dump_prefix(n, offset+1); - printf("post mandatory args:\n"); - dump_recur(mrb, n->car, offset+2); - } - if (n->cdr) { - dump_prefix(n, offset+1); - printf("blk=&%s\n", mrb_sym2name(mrb, sym(n->cdr))); - } - } - mrb_parser_dump(mrb, tree->cdr->car, offset+1); - break; - - case NODE_SDEF: - printf("NODE_SDEF:\n"); - mrb_parser_dump(mrb, tree->car, offset+1); - tree = tree->cdr; - dump_prefix(tree, offset+1); - printf(":%s\n", mrb_sym2name(mrb, sym(tree->car))); - tree = tree->cdr->cdr; - if (tree->car) { - node *n = tree->car; - - if (n->car) { - dump_prefix(n, offset+1); - printf("mandatory args:\n"); - dump_recur(mrb, n->car, offset+2); - } - n = n->cdr; - if (n->car) { - dump_prefix(n, offset+1); - printf("optional args:\n"); - { - node *n2 = n->car; - - while (n2) { - dump_prefix(n2, offset+2); - printf("%s=", mrb_sym2name(mrb, sym(n2->car->car))); - mrb_parser_dump(mrb, n2->car->cdr, 0); - n2 = n2->cdr; - } - } - } - n = n->cdr; - if (n->car) { - dump_prefix(n, offset+1); - printf("rest=*%s\n", mrb_sym2name(mrb, sym(n->car))); - } - n = n->cdr; - if (n->car) { - dump_prefix(n, offset+1); - printf("post mandatory args:\n"); - dump_recur(mrb, n->car, offset+2); - } - n = n->cdr; - if (n) { - dump_prefix(n, offset+1); - printf("blk=&%s\n", mrb_sym2name(mrb, sym(n))); - } - } - tree = tree->cdr; - mrb_parser_dump(mrb, tree->car, offset+1); - break; - - case NODE_POSTEXE: - printf("NODE_POSTEXE:\n"); - mrb_parser_dump(mrb, tree, offset+1); - break; - - case NODE_HEREDOC: - printf("NODE_HEREDOC:\n"); - mrb_parser_dump(mrb, ((parser_heredoc_info*)tree)->doc, offset+1); - break; - - default: - printf("node type: %d (0x%x)\n", nodetype, (unsigned)nodetype); - break; - } -#endif -} diff --git a/tasks/mruby_build.rake b/tasks/mruby_build.rake index 66608286d..50bed0fbe 100644 --- a/tasks/mruby_build.rake +++ b/tasks/mruby_build.rake @@ -80,7 +80,7 @@ module MRuby @git = Command::Git.new(self) @mrbc = Command::Mrbc.new(self) - @bins = %w(mrbc) + @bins = [] @gems, @libmruby = MRuby::Gem::List.new, [] @build_mrbtest_lib_only = false @cxx_abi_enabled = false @@ -92,6 +92,8 @@ module MRuby MRuby::Build.current = MRuby.targets[@name] MRuby.targets[@name].instance_eval(&block) + + build_mrbc_exec if name == 'host' end def enable_debug @@ -119,6 +121,33 @@ module MRuby @cxx_abi_enabled = true end + def compile_as_cxx src, cxx_src, obj = nil, includes = [] + src = File.absolute_path src + cxx_src = File.absolute_path cxx_src + obj = objfile(cxx_src) if obj.nil? + + file cxx_src => [src, __FILE__] do |t| + File.open(t.name, 'w') do |f| + f.write < cxx_src do |t| + cxx.run t.name, t.prerequisites.first, [], ["#{MRUBY_ROOT}/src"] + includes + end + + obj + end + def enable_bintest @enable_bintest = true end @@ -142,8 +171,16 @@ module MRuby MRUBY_ROOT end + def build_mrbc_exec + gem :core => 'mruby-bin-mrbc' + end + def mrbcfile - MRuby.targets[@name].exefile("#{MRuby.targets[@name].build_dir}/bin/mrbc") + return @mrbcfile if @mrbcfile + + mrbc_build = MRuby.targets['host'] + gems.each { |v| mrbc_build = self if v.name == 'mruby-bin-mrbc' } + @mrbcfile = mrbc_build.exefile("#{mrbc_build.build_dir}/bin/mrbc") end def compilers diff --git a/tools/mrbc/mrbc.c b/tools/mrbc/mrbc.c deleted file mode 100644 index f27f87a5d..000000000 --- a/tools/mrbc/mrbc.c +++ /dev/null @@ -1,337 +0,0 @@ -#include -#include -#include -#include "mruby.h" -#include "mruby/compile.h" -#include "mruby/dump.h" -#include "mruby/proc.h" - -#define RITEBIN_EXT ".mrb" -#define C_EXT ".c" - -struct mrbc_args { - int argc; - char **argv; - int idx; - const char *prog; - const char *outfile; - const char *initname; - mrb_bool check_syntax : 1; - mrb_bool verbose : 1; - unsigned int flags : 4; -}; - -static void -usage(const char *name) -{ - static const char *const usage_msg[] = { - "switches:", - "-c check syntax only", - "-o place the output into ", - "-v print version number, then turn on verbose mode", - "-g produce debugging information", - "-B binary output in C language format", - "-e generate little endian iseq data", - "-E generate big endian iseq data", - "--verbose run at verbose mode", - "--version print the version", - "--copyright print the copyright", - NULL - }; - const char *const *p = usage_msg; - - printf("Usage: %s [switches] programfile\n", name); - while (*p) - printf(" %s\n", *p++); -} - -static char * -get_outfilename(mrb_state *mrb, char *infile, const char *ext) -{ - size_t infilelen; - size_t extlen; - char *outfile; - char *p; - - infilelen = strlen(infile); - extlen = strlen(ext); - outfile = (char*)mrb_malloc(mrb, infilelen + extlen + 1); - memcpy(outfile, infile, infilelen + 1); - if (*ext) { - if ((p = strrchr(outfile, '.')) == NULL) - p = outfile + infilelen; - memcpy(p, ext, extlen + 1); - } - - return outfile; -} - -static int -parse_args(mrb_state *mrb, int argc, char **argv, struct mrbc_args *args) -{ - char *outfile = NULL; - static const struct mrbc_args args_zero = { 0 }; - int i; - - *args = args_zero; - args->argc = argc; - args->argv = argv; - args->prog = argv[0]; - - for (i=1; ioutfile) { - fprintf(stderr, "%s: an output file is already specified. (%s)\n", - args->prog, outfile); - return -1; - } - if (argv[i][2] == '\0' && argv[i+1]) { - i++; - args->outfile = get_outfilename(mrb, argv[i], ""); - } - else { - args->outfile = get_outfilename(mrb, argv[i] + 2, ""); - } - break; - case 'B': - if (argv[i][2] == '\0' && argv[i+1]) { - i++; - args->initname = argv[i]; - } - else { - args->initname = argv[i]+2; - } - if (*args->initname == '\0') { - fprintf(stderr, "%s: function name is not specified.\n", args->prog); - return -1; - } - break; - case 'c': - args->check_syntax = TRUE; - break; - case 'v': - if (!args->verbose) mrb_show_version(mrb); - args->verbose = TRUE; - break; - case 'g': - args->flags |= DUMP_DEBUG_INFO; - break; - case 'E': - args->flags = DUMP_ENDIAN_BIG | (args->flags & DUMP_DEBUG_INFO); - break; - case 'e': - args->flags = DUMP_ENDIAN_LIL | (args->flags & DUMP_DEBUG_INFO); - break; - case 'h': - return -1; - case '-': - if (argv[i][1] == '\n') { - return i; - } - if (strcmp(argv[i] + 2, "version") == 0) { - mrb_show_version(mrb); - exit(EXIT_SUCCESS); - } - else if (strcmp(argv[i] + 2, "verbose") == 0) { - args->verbose = TRUE; - break; - } - else if (strcmp(argv[i] + 2, "copyright") == 0) { - mrb_show_copyright(mrb); - exit(EXIT_SUCCESS); - } - return -1; - default: - return i; - } - } - else { - break; - } - } - if (args->verbose && args->initname && (args->flags & DUMP_ENDIAN_MASK) == 0) { - fprintf(stderr, "%s: generating %s endian C file. specify -e/-E for cross compiling.\n", - args->prog, bigendian_p() ? "big" : "little"); - } - return i; -} - -static void -cleanup(mrb_state *mrb, struct mrbc_args *args) -{ - if (args->outfile) - mrb_free(mrb, (void*)args->outfile); - mrb_close(mrb); -} - -static int -partial_hook(struct mrb_parser_state *p) -{ - mrbc_context *c = p->cxt; - struct mrbc_args *args = (struct mrbc_args *)c->partial_data; - const char *fn; - - if (p->f) fclose(p->f); - if (args->idx >= args->argc) { - p->f = NULL; - return -1; - } - fn = args->argv[args->idx++]; - p->f = fopen(fn, "r"); - if (p->f == NULL) { - fprintf(stderr, "%s: cannot open program file. (%s)\n", args->prog, fn); - return -1; - } - mrb_parser_set_filename(p, fn); - return 0; -} - -static mrb_value -load_file(mrb_state *mrb, struct mrbc_args *args) -{ - mrbc_context *c; - mrb_value result; - char *input = args->argv[args->idx]; - FILE *infile; - mrb_bool need_close = FALSE; - - c = mrbc_context_new(mrb); - if (args->verbose) - c->dump_result = TRUE; - c->no_exec = TRUE; - if (input[0] == '-' && input[1] == '\0') { - infile = stdin; - } - else { - need_close = TRUE; - if ((infile = fopen(input, "r")) == NULL) { - fprintf(stderr, "%s: cannot open program file. (%s)\n", args->prog, input); - return mrb_nil_value(); - } - } - mrbc_filename(mrb, c, input); - args->idx++; - if (args->idx < args->argc) { - need_close = FALSE; - mrbc_partial_hook(mrb, c, partial_hook, (void*)args); - } - - result = mrb_load_file_cxt(mrb, infile, c); - if (need_close) fclose(infile); - mrbc_context_free(mrb, c); - if (mrb_undef_p(result)) { - return mrb_nil_value(); - } - return result; -} - -static int -dump_file(mrb_state *mrb, FILE *wfp, const char *outfile, struct RProc *proc, struct mrbc_args *args) -{ - int n = MRB_DUMP_OK; - mrb_irep *irep = proc->body.irep; - - if (args->initname) { - n = mrb_dump_irep_cfunc(mrb, irep, args->flags, wfp, args->initname); - if (n == MRB_DUMP_INVALID_ARGUMENT) { - fprintf(stderr, "%s: invalid C language symbol name\n", args->initname); - } - } - else { - n = mrb_dump_irep_binary(mrb, irep, args->flags, wfp); - } - if (n != MRB_DUMP_OK) { - fprintf(stderr, "%s: error in mrb dump (%s) %d\n", args->prog, outfile, n); - } - return n; -} - -int -main(int argc, char **argv) -{ - mrb_state *mrb = mrb_open(); - int n, result; - struct mrbc_args args; - FILE *wfp; - mrb_value load; - - if (mrb == NULL) { - fputs("Invalid mrb_state, exiting mrbc\n", stderr); - return EXIT_FAILURE; - } - - n = parse_args(mrb, argc, argv, &args); - if (n < 0) { - cleanup(mrb, &args); - usage(argv[0]); - return EXIT_FAILURE; - } - if (n == argc) { - fprintf(stderr, "%s: no program file given\n", args.prog); - return EXIT_FAILURE; - } - if (args.outfile == NULL && !args.check_syntax) { - if (n + 1 == argc) { - args.outfile = get_outfilename(mrb, argv[n], args.initname ? C_EXT : RITEBIN_EXT); - } - else { - fprintf(stderr, "%s: output file should be specified to compile multiple files\n", args.prog); - return EXIT_FAILURE; - } - } - - args.idx = n; - load = load_file(mrb, &args); - if (mrb_nil_p(load)) { - cleanup(mrb, &args); - return EXIT_FAILURE; - } - if (args.check_syntax) { - printf("%s:%s:Syntax OK\n", args.prog, argv[n]); - } - - if (args.check_syntax) { - cleanup(mrb, &args); - return EXIT_SUCCESS; - } - - if (args.outfile) { - if (strcmp("-", args.outfile) == 0) { - wfp = stdout; - } - else if ((wfp = fopen(args.outfile, "wb")) == NULL) { - fprintf(stderr, "%s: cannot open output file:(%s)\n", args.prog, args.outfile); - return EXIT_FAILURE; - } - } - else { - fprintf(stderr, "Output file is required\n"); - return EXIT_FAILURE; - } - result = dump_file(mrb, wfp, args.outfile, mrb_proc_ptr(load), &args); - fclose(wfp); - cleanup(mrb, &args); - if (result != MRB_DUMP_OK) { - return EXIT_FAILURE; - } - return EXIT_SUCCESS; -} - -void -mrb_init_mrblib(mrb_state *mrb) -{ -} - -#ifndef DISABLE_GEMS -void -mrb_init_mrbgems(mrb_state *mrb) -{ -} - -void -mrb_final_mrbgems(mrb_state *mrb) -{ -} -#endif diff --git a/tools/mrbc/mrbc.rake b/tools/mrbc/mrbc.rake deleted file mode 100644 index 1a0309a0d..000000000 --- a/tools/mrbc/mrbc.rake +++ /dev/null @@ -1,14 +0,0 @@ -MRuby.each_target do - current_dir = File.dirname(__FILE__).relative_path_from(Dir.pwd) - relative_from_root = File.dirname(__FILE__).relative_path_from(MRUBY_ROOT) - current_build_dir = "#{build_dir}/#{relative_from_root}" - - if bins.find { |s| s.to_s == 'mrbc' } - exec = exefile("#{build_dir}/bin/mrbc") - objs = Dir.glob("#{current_dir}/*.c").map { |f| objfile(f.pathmap("#{current_build_dir}/%n")) }.flatten - - file exec => objs + [libfile("#{build_dir}/lib/libmruby_core")] do |t| - linker.run t.name, t.prerequisites - end - end -end diff --git a/travis_config.rb b/travis_config.rb index fadafd8c1..2b4059cf1 100644 --- a/travis_config.rb +++ b/travis_config.rb @@ -8,6 +8,8 @@ MRuby::Build.new('debug') do |conf| conf.compilers.each do |c| c.defines += %w(MRB_GC_STRESS MRB_GC_FIXED_ARENA) end + + build_mrbc_exec end MRuby::Build.new do |conf| @@ -33,4 +35,6 @@ MRuby::Build.new('cxx_abi') do |conf| conf.enable_bintest enable_cxx_abi + + build_mrbc_exec end -- cgit v1.2.3 From 599d141cc8fa7b04398d564b09893f6754906f3e Mon Sep 17 00:00:00 2001 From: cremno Date: Tue, 2 Jun 2015 08:34:36 +0200 Subject: directly call ary_new_capa() --- src/array.c | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/array.c b/src/array.c index 1ca7dd2a4..f2584114d 100644 --- a/src/array.c +++ b/src/array.c @@ -88,15 +88,12 @@ array_copy(mrb_value *dst, const mrb_value *src, mrb_int size) MRB_API mrb_value mrb_ary_new_from_values(mrb_state *mrb, mrb_int size, const mrb_value *vals) { - mrb_value ary; - struct RArray *a; + struct RArray *a = ary_new_capa(mrb, size); - ary = mrb_ary_new_capa(mrb, size); - a = mrb_ary_ptr(ary); array_copy(a->ptr, vals, size); a->len = size; - return ary; + return mrb_obj_value(a); } MRB_API mrb_value @@ -293,7 +290,6 @@ mrb_ary_plus(mrb_state *mrb, mrb_value self) { struct RArray *a1 = mrb_ary_ptr(self); struct RArray *a2; - mrb_value ary; mrb_value *ptr; mrb_int blen; @@ -301,13 +297,12 @@ mrb_ary_plus(mrb_state *mrb, mrb_value self) if (ARY_MAX_SIZE - blen < a1->len) { mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big"); } - ary = mrb_ary_new_capa(mrb, a1->len + blen); - a2 = mrb_ary_ptr(ary); + a2 = ary_new_capa(mrb, a1->len + blen); array_copy(a2->ptr, a1->ptr, a1->len); array_copy(a2->ptr + a1->len, ptr, blen); a2->len = a1->len + blen; - return ary; + return mrb_obj_value(a2); } static void @@ -345,7 +340,6 @@ mrb_ary_times(mrb_state *mrb, mrb_value self) { struct RArray *a1 = mrb_ary_ptr(self); struct RArray *a2; - mrb_value ary; mrb_value *ptr; mrb_int times; @@ -357,8 +351,7 @@ mrb_ary_times(mrb_state *mrb, mrb_value self) if (ARY_MAX_SIZE / times < a1->len) { mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big"); } - ary = mrb_ary_new_capa(mrb, a1->len * times); - a2 = mrb_ary_ptr(ary); + a2 = ary_new_capa(mrb, a1->len * times); ptr = a2->ptr; while (times--) { array_copy(ptr, a1->ptr, a1->len); @@ -366,7 +359,7 @@ mrb_ary_times(mrb_state *mrb, mrb_value self) a2->len += a1->len; } - return ary; + return mrb_obj_value(a2); } static mrb_value @@ -393,11 +386,8 @@ mrb_ary_reverse_bang(mrb_state *mrb, mrb_value self) static mrb_value mrb_ary_reverse(mrb_state *mrb, mrb_value self) { - struct RArray *a = mrb_ary_ptr(self), *b; - mrb_value ary; + struct RArray *a = mrb_ary_ptr(self), *b = ary_new_capa(mrb, a->len); - ary = mrb_ary_new_capa(mrb, a->len); - b = mrb_ary_ptr(ary); if (a->len > 0) { mrb_value *p1, *p2, *e; @@ -409,7 +399,7 @@ mrb_ary_reverse(mrb_state *mrb, mrb_value self) } b->len = a->len; } - return ary; + return mrb_obj_value(b); } MRB_API void -- cgit v1.2.3 From ef0fc90cd007d613be6c268cc6adbecc8d7a0a8f Mon Sep 17 00:00:00 2001 From: cremno Date: Tue, 2 Jun 2015 08:56:41 +0200 Subject: remove unnecessary mrb_immediate_p() `!mrb_array_p(ary2)` and `mrb_type(obj) != MRB_TT_DATA` are sufficient. --- src/array.c | 2 -- src/etc.c | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/array.c b/src/array.c index 1ca7dd2a4..c97e7a53b 100644 --- a/src/array.c +++ b/src/array.c @@ -1052,7 +1052,6 @@ mrb_ary_eq(mrb_state *mrb, mrb_value ary1) mrb_get_args(mrb, "o", &ary2); if (mrb_obj_equal(mrb, ary1, ary2)) return mrb_true_value(); - if (mrb_immediate_p(ary2)) return mrb_false_value(); if (!mrb_array_p(ary2)) { return mrb_false_value(); } @@ -1068,7 +1067,6 @@ mrb_ary_cmp(mrb_state *mrb, mrb_value ary1) mrb_get_args(mrb, "o", &ary2); if (mrb_obj_equal(mrb, ary1, ary2)) return mrb_fixnum_value(0); - if (mrb_immediate_p(ary2)) return mrb_nil_value(); if (!mrb_array_p(ary2)) { return mrb_nil_value(); } diff --git a/src/etc.c b/src/etc.c index 635052b67..a8a21e740 100644 --- a/src/etc.c +++ b/src/etc.c @@ -26,7 +26,7 @@ mrb_data_object_alloc(mrb_state *mrb, struct RClass *klass, void *ptr, const mrb MRB_API void mrb_data_check_type(mrb_state *mrb, mrb_value obj, const mrb_data_type *type) { - if (mrb_immediate_p(obj) || (mrb_type(obj) != MRB_TT_DATA)) { + if (mrb_type(obj) != MRB_TT_DATA) { mrb_check_type(mrb, obj, MRB_TT_DATA); } if (DATA_TYPE(obj) != type) { @@ -48,7 +48,7 @@ mrb_data_check_type(mrb_state *mrb, mrb_value obj, const mrb_data_type *type) MRB_API void* mrb_data_check_get_ptr(mrb_state *mrb, mrb_value obj, const mrb_data_type *type) { - if (mrb_immediate_p(obj) || (mrb_type(obj) != MRB_TT_DATA)) { + if (mrb_type(obj) != MRB_TT_DATA) { return NULL; } if (DATA_TYPE(obj) != type) { -- cgit v1.2.3 From 3bcf570a17d25783187c8aa3413bf48425b29619 Mon Sep 17 00:00:00 2001 From: Nobuhiro Iwamatsu Date: Sat, 6 Jun 2015 08:57:33 +0900 Subject: Fix build on MIPS of linux MIPS of Linux platform is supported frexpl(3). This fixes to use the frexpl that are provided with gcc if user wants to build on MIPS of Linux platform. Signe-doff-by: Nobuhiro Iwamatsu --- src/fmt_fp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/fmt_fp.c b/src/fmt_fp.c index ef2f19dd5..a634edb34 100644 --- a/src/fmt_fp.c +++ b/src/fmt_fp.c @@ -90,7 +90,7 @@ fmt_u(uint32_t x, char *s) typedef char compiler_defines_long_double_incorrectly[9-(int)sizeof(long double)]; #endif -#if defined(__CYGWIN32__) || defined(__NetBSD__) || defined(mips) +#if (defined(__CYGWIN32__) || defined(__NetBSD__) || defined(mips)) && !defined(__linux__) static long double frexpl (long double x, int *eptr) { -- cgit v1.2.3 From 5e8d2a4b841d59ceab9f6fcf3ae294d93b8332b5 Mon Sep 17 00:00:00 2001 From: cremno Date: Sat, 13 Jun 2015 14:59:57 +0200 Subject: refactor code to call mrb_inspect() instead mrb_inspect() also calls mrb_obj_as_string() after #inspect to ensure the mrb_value is a string. --- mrbgems/mruby-bin-mruby/tools/mruby/mruby.c | 6 +----- src/print.c | 6 +----- 2 files changed, 2 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c b/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c index 44ad9bb06..141ea151b 100644 --- a/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c +++ b/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c @@ -11,12 +11,8 @@ static void p(mrb_state *mrb, mrb_value obj) { - mrb_value val; + mrb_value val = mrb_inspect(mrb, obj); - val = mrb_funcall(mrb, obj, "inspect", 0); - if (!mrb_string_p(val)) { - val = mrb_obj_as_string(mrb, obj); - } fwrite(RSTRING_PTR(val), RSTRING_LEN(val), 1, stdout); putc('\n', stdout); } diff --git a/src/print.c b/src/print.c index b43936b13..c7c1ccd39 100644 --- a/src/print.c +++ b/src/print.c @@ -27,12 +27,8 @@ MRB_API void mrb_p(mrb_state *mrb, mrb_value obj) { #ifdef ENABLE_STDIO - mrb_value val; + mrb_value val = mrb_inspect(mrb, obj); - val = mrb_funcall(mrb, obj, "inspect", 0); - if (!mrb_string_p(val)) { - val = mrb_obj_as_string(mrb, obj); - } printstr(mrb, val); putc('\n', stdout); #endif -- cgit v1.2.3 From d1e6d647bd3a91d58567657471f3d945836cde85 Mon Sep 17 00:00:00 2001 From: yui-knk Date: Fri, 19 Jun 2015 20:34:32 +0900 Subject: [ci skip] Remove some Srting#split samples mruby not support regexp, so remove these samples. --- src/string.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'src') diff --git a/src/string.c b/src/string.c index 16e7f9ffd..e3a359b49 100644 --- a/src/string.c +++ b/src/string.c @@ -1714,10 +1714,8 @@ mrb_str_rindex_m(mrb_state *mrb, mrb_value str) * " now's the time".split #=> ["now's", "the", "time"] * " now's the time".split(' ') #=> ["now's", "the", "time"] * " now's the time".split(/ /) #=> ["", "now's", "", "the", "time"] - * "1, 2.34,56, 7".split(%r{,\s*}) #=> ["1", "2.34", "56", "7"] * "hello".split(//) #=> ["h", "e", "l", "l", "o"] * "hello".split(//, 3) #=> ["h", "e", "llo"] - * "hi mom".split(%r{\s*}) #=> ["h", "i", "m", "o", "m"] * * "mellow yellow".split("ello") #=> ["m", "w y", "w"] * "1,2,,3,4,,".split(',') #=> ["1", "2", "", "3", "4"] -- cgit v1.2.3 From f0eaf9eaf53ce659c44ce6beeedbeb8bfc5b4efa Mon Sep 17 00:00:00 2001 From: cremno Date: Mon, 22 Jun 2015 13:34:24 +0200 Subject: fix arity of lambdas with optional arguments From the CRuby 2.2.2 Proc#arity documentation: If the block has optional arguments, returns -n-1, where n is the number of mandatory arguments, with the exception for blocks that are not lambdas and have only a finite number of optional arguments; in this latter case, returns n. --- src/proc.c | 2 +- test/t/proc.rb | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/proc.c b/src/proc.c index 4cb9ffe18..61524f00c 100644 --- a/src/proc.c +++ b/src/proc.c @@ -216,7 +216,7 @@ mrb_proc_arity(mrb_state *mrb, mrb_value self) ma = MRB_ASPEC_REQ(aspec); ra = MRB_ASPEC_REST(aspec); pa = MRB_ASPEC_POST(aspec); - arity = ra ? -(ma + pa + 1) : ma + pa; + arity = ra || MRB_PROC_STRICT_P(p) ? -(ma + pa + 1) : ma + pa; return mrb_fixnum_value(arity); } diff --git a/test/t/proc.rb b/test/t/proc.rb index 22ccceb68..888b7d56a 100644 --- a/test/t/proc.rb +++ b/test/t/proc.rb @@ -36,6 +36,14 @@ assert('Proc#arity', '15.2.17.4.2') do assert_equal(-3, b) assert_equal 1, c assert_equal 1, d + + e = ->(x=0, y){}.arity + f = ->((x, y), z=0){}.arity + g = ->(x=0){}.arity + + assert_equal(-2, e) + assert_equal(-2, f) + assert_equal(-1, g) end assert('Proc#call', '15.2.17.4.3') do -- cgit v1.2.3 From 338ca305ae558e4c6f929fdb619f586fb030ba7f Mon Sep 17 00:00:00 2001 From: Jared Breeden Date: Mon, 22 Jun 2015 18:37:14 -0700 Subject: Calling mrb_str_to_str in mrb_string_value_cstr. Fixes #2847 --- src/string.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/string.c b/src/string.c index 0a28f2565..9a1a6fba1 100644 --- a/src/string.c +++ b/src/string.c @@ -1985,7 +1985,8 @@ bad: MRB_API const char* mrb_string_value_cstr(mrb_state *mrb, mrb_value *ptr) { - struct RString *ps = mrb_str_ptr(*ptr); + mrb_value str = mrb_str_to_str(mrb, *ptr); + struct RString *ps = mrb_str_ptr(str); mrb_int len = mrb_str_strlen(mrb, ps); char *p = RSTR_PTR(ps); -- cgit v1.2.3 From 9553e20d34c204826fde86620d2f433b3d59f1c1 Mon Sep 17 00:00:00 2001 From: Jared Breeden Date: Mon, 22 Jun 2015 19:07:25 -0700 Subject: Removing redundant mrb_str_to_str call --- src/string.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/string.c b/src/string.c index 9a1a6fba1..9f753a0cf 100644 --- a/src/string.c +++ b/src/string.c @@ -2003,7 +2003,6 @@ mrb_str_to_inum(mrb_state *mrb, mrb_value str, mrb_int base, mrb_bool badcheck) const char *s; mrb_int len; - str = mrb_str_to_str(mrb, str); if (badcheck) { s = mrb_string_value_cstr(mrb, &str); } -- cgit v1.2.3 From 264a059aef01b079d9788c8569182482edeab82a Mon Sep 17 00:00:00 2001 From: Jared Breeden Date: Mon, 22 Jun 2015 19:15:52 -0700 Subject: Removing unneeded bad_checks for verified strings --- mrbgems/mruby-sprintf/src/sprintf.c | 2 +- src/object.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/mrbgems/mruby-sprintf/src/sprintf.c b/mrbgems/mruby-sprintf/src/sprintf.c index d88e242c6..ca4ecfbbd 100644 --- a/mrbgems/mruby-sprintf/src/sprintf.c +++ b/mrbgems/mruby-sprintf/src/sprintf.c @@ -808,7 +808,7 @@ retry: if (mrb_fixnum_p(val)) goto bin_retry; break; case MRB_TT_STRING: - val = mrb_str_to_inum(mrb, val, 0, TRUE); + val = mrb_str_to_inum(mrb, val, 0, FALSE); goto bin_retry; case MRB_TT_FIXNUM: v = mrb_fixnum(val); diff --git a/src/object.c b/src/object.c index c5fb74575..df7d77b9e 100644 --- a/src/object.c +++ b/src/object.c @@ -539,7 +539,7 @@ mrb_convert_to_integer(mrb_state *mrb, mrb_value val, int base) case MRB_TT_STRING: string_conv: - return mrb_str_to_inum(mrb, val, base, TRUE); + return mrb_str_to_inum(mrb, val, base, FALSE); default: break; -- cgit v1.2.3 From cfcca2a727e2af8aa789d7a29bbe612628e84893 Mon Sep 17 00:00:00 2001 From: Jared Breeden Date: Mon, 22 Jun 2015 19:49:12 -0700 Subject: Reverting overzealous changes --- mrbgems/mruby-sprintf/src/sprintf.c | 2 +- src/object.c | 2 +- src/string.c | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/mrbgems/mruby-sprintf/src/sprintf.c b/mrbgems/mruby-sprintf/src/sprintf.c index ca4ecfbbd..d88e242c6 100644 --- a/mrbgems/mruby-sprintf/src/sprintf.c +++ b/mrbgems/mruby-sprintf/src/sprintf.c @@ -808,7 +808,7 @@ retry: if (mrb_fixnum_p(val)) goto bin_retry; break; case MRB_TT_STRING: - val = mrb_str_to_inum(mrb, val, 0, FALSE); + val = mrb_str_to_inum(mrb, val, 0, TRUE); goto bin_retry; case MRB_TT_FIXNUM: v = mrb_fixnum(val); diff --git a/src/object.c b/src/object.c index df7d77b9e..c5fb74575 100644 --- a/src/object.c +++ b/src/object.c @@ -539,7 +539,7 @@ mrb_convert_to_integer(mrb_state *mrb, mrb_value val, int base) case MRB_TT_STRING: string_conv: - return mrb_str_to_inum(mrb, val, base, FALSE); + return mrb_str_to_inum(mrb, val, base, TRUE); default: break; diff --git a/src/string.c b/src/string.c index 9f753a0cf..9a1a6fba1 100644 --- a/src/string.c +++ b/src/string.c @@ -2003,6 +2003,7 @@ mrb_str_to_inum(mrb_state *mrb, mrb_value str, mrb_int base, mrb_bool badcheck) const char *s; mrb_int len; + str = mrb_str_to_str(mrb, str); if (badcheck) { s = mrb_string_value_cstr(mrb, &str); } -- cgit v1.2.3 From 9ad632559efc6392d4650eedb27090ec27f78776 Mon Sep 17 00:00:00 2001 From: Jared Breeden Date: Mon, 22 Jun 2015 20:49:09 -0700 Subject: *Correctly* removing extra mrb_str_to_str call --- src/string.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/string.c b/src/string.c index 9a1a6fba1..22a289ade 100644 --- a/src/string.c +++ b/src/string.c @@ -2003,12 +2003,12 @@ mrb_str_to_inum(mrb_state *mrb, mrb_value str, mrb_int base, mrb_bool badcheck) const char *s; mrb_int len; - str = mrb_str_to_str(mrb, str); if (badcheck) { + /* Raises if the string contains a null character (the badcheck) */ s = mrb_string_value_cstr(mrb, &str); } else { - s = RSTRING_PTR(str); + s = mrb_string_value_ptr(mrb, str); } if (s) { len = RSTRING_LEN(str); -- cgit v1.2.3 From 937b5b546201b874e7aba6a371f28456a784b39a Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Tue, 23 Jun 2015 14:42:58 +0900 Subject: fix Proc#curry test failure; ref #2848 --- src/proc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/proc.c b/src/proc.c index 61524f00c..f98998f68 100644 --- a/src/proc.c +++ b/src/proc.c @@ -200,7 +200,7 @@ mrb_proc_arity(mrb_state *mrb, mrb_value self) struct RProc *p = mrb_proc_ptr(self); mrb_code *iseq = mrb_proc_iseq(mrb, p); mrb_aspec aspec; - int ma, ra, pa, arity; + int ma, op, ra, pa, arity; if (MRB_PROC_CFUNC_P(p)) { /* TODO cfunc aspec not implemented yet */ @@ -214,9 +214,10 @@ mrb_proc_arity(mrb_state *mrb, mrb_value self) aspec = GETARG_Ax(*iseq); ma = MRB_ASPEC_REQ(aspec); + op = MRB_ASPEC_OPT(aspec); ra = MRB_ASPEC_REST(aspec); pa = MRB_ASPEC_POST(aspec); - arity = ra || MRB_PROC_STRICT_P(p) ? -(ma + pa + 1) : ma + pa; + arity = ra || (MRB_PROC_STRICT_P(p) && op) ? -(ma + pa + 1) : ma + pa; return mrb_fixnum_value(arity); } -- cgit v1.2.3 From 9781580c7134fcee7bf5a6d4356bc47593549da8 Mon Sep 17 00:00:00 2001 From: Jared Breeden Date: Mon, 22 Jun 2015 23:19:58 -0700 Subject: Fixes #912 --- src/class.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/class.c b/src/class.c index 35c3aa040..05b549b3e 100644 --- a/src/class.c +++ b/src/class.c @@ -1174,11 +1174,11 @@ mrb_instance_alloc(mrb_state *mrb, mrb_value cv) * call-seq: * class.new(args, ...) -> obj * - * Calls allocate to create a new object of - * class's class, then invokes that object's - * initialize method, passing it args. - * This is the method that ends up getting called whenever - * an object is constructed using .new. + * Creates a new object of class's class, then + * invokes that object's initialize method, + * passing it args. This is the method that ends + * up getting called whenever an object is constructed using + * `.new`. * */ -- cgit v1.2.3 From 6ed64e23678620f99e836eb07a58903760cf657c Mon Sep 17 00:00:00 2001 From: cremno Date: Tue, 23 Jun 2015 12:44:28 +0200 Subject: rewrite printstr() to get rid of code duplication --- src/print.c | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/print.c b/src/print.c index c7c1ccd39..077fa4f06 100644 --- a/src/print.c +++ b/src/print.c @@ -8,45 +8,35 @@ #include "mruby/string.h" #include "mruby/variable.h" +#ifdef ENABLE_STDIO static void -printstr(mrb_state *mrb, mrb_value obj) +printstr(mrb_value obj, FILE *stream) { -#ifdef ENABLE_STDIO - char *s; - int len; - if (mrb_string_p(obj)) { - s = RSTRING_PTR(obj); - len = RSTRING_LEN(obj); - fwrite(s, len, 1, stdout); + fwrite(RSTRING_PTR(obj), RSTRING_LEN(obj), 1, stream); + putc('\n', stream); } -#endif } +#else +# define printstr(obj, stream) (void)0 +#endif MRB_API void mrb_p(mrb_state *mrb, mrb_value obj) { -#ifdef ENABLE_STDIO mrb_value val = mrb_inspect(mrb, obj); - printstr(mrb, val); - putc('\n', stdout); -#endif + printstr(val, stdout); } MRB_API void mrb_print_error(mrb_state *mrb) { -#ifdef ENABLE_STDIO mrb_value s; mrb_print_backtrace(mrb); s = mrb_funcall(mrb, mrb_obj_value(mrb->exc), "inspect", 0); - if (mrb_string_p(s)) { - fwrite(RSTRING_PTR(s), RSTRING_LEN(s), 1, stderr); - putc('\n', stderr); - } -#endif + printstr(s, stderr); } MRB_API void @@ -55,8 +45,7 @@ mrb_show_version(mrb_state *mrb) mrb_value msg; msg = mrb_const_get(mrb, mrb_obj_value(mrb->object_class), mrb_intern_lit(mrb, "MRUBY_DESCRIPTION")); - printstr(mrb, msg); - printstr(mrb, mrb_str_new_lit(mrb, "\n")); + printstr(msg, stdout); } MRB_API void @@ -65,6 +54,5 @@ mrb_show_copyright(mrb_state *mrb) mrb_value msg; msg = mrb_const_get(mrb, mrb_obj_value(mrb->object_class), mrb_intern_lit(mrb, "MRUBY_COPYRIGHT")); - printstr(mrb, msg); - printstr(mrb, mrb_str_new_lit(mrb, "\n")); + printstr(msg, stdout); } -- cgit v1.2.3 From 25885072858582d3d2f985b405a8e84d58f716e8 Mon Sep 17 00:00:00 2001 From: Franck Verrot Date: Wed, 24 Jun 2015 13:07:07 +0200 Subject: Remove unnecessary backticks. Dr Markus Kuhn published in 1999 an article [1] explaining in details why we shouldn't use the ASCII grave accent (0x60) as a left quotation. Backticks have been used most notably to produce nice-looking LaTeX documents but it doesn't seem to be an issue on modern platforms and for the oldest ones, there are workarounds as mentioned by Dr Kuhn. [1]: https://www.cl.cam.ac.uk/~mgk25/ucs/quotes.html --- include/mruby/compile.h | 4 +- include/mruby/opcode.h | 2 +- mrbgems/mruby-compiler/core/parse.y | 14 +++--- mrbgems/mruby-hash-ext/mrblib/hash.rb | 2 +- mrbgems/mruby-sprintf/src/sprintf.c | 80 +++++++++++++++++------------------ mrbgems/mruby-struct/src/struct.c | 8 ++-- mrblib/array.rb | 2 +- src/class.c | 8 ++-- src/numeric.c | 4 +- src/object.c | 2 +- src/string.c | 6 +-- src/variable.c | 2 +- 12 files changed, 67 insertions(+), 67 deletions(-) (limited to 'src') diff --git a/include/mruby/compile.h b/include/mruby/compile.h index e20473298..1fb81782d 100644 --- a/include/mruby/compile.h +++ b/include/mruby/compile.h @@ -55,8 +55,8 @@ enum mrb_lex_state_enum { EXPR_CMDARG, /* newline significant, +/- is an operator. */ EXPR_MID, /* newline significant, +/- is an operator. */ EXPR_FNAME, /* ignore newline, no reserved words. */ - EXPR_DOT, /* right after `.' or `::', no reserved words. */ - EXPR_CLASS, /* immediate after `class', no here document. */ + EXPR_DOT, /* right after '.' or '::', no reserved words. */ + EXPR_CLASS, /* immediate after 'class', no here document. */ EXPR_VALUE, /* alike EXPR_BEG but label is disallowed. */ EXPR_MAX_STATE }; diff --git a/include/mruby/opcode.h b/include/mruby/opcode.h index 4774e78c6..9dfa7f75d 100644 --- a/include/mruby/opcode.h +++ b/include/mruby/opcode.h @@ -8,7 +8,7 @@ #define MRUBY_OPCODE_H #define MAXARG_Bx (0xffff) -#define MAXARG_sBx (MAXARG_Bx>>1) /* `sBx' is signed */ +#define MAXARG_sBx (MAXARG_Bx>>1) /* 'sBx' is signed */ /* instructions: packed 32 bit */ /* ------------------------------- */ diff --git a/mrbgems/mruby-compiler/core/parse.y b/mrbgems/mruby-compiler/core/parse.y index 5b17649a9..f6a43d32b 100644 --- a/mrbgems/mruby-compiler/core/parse.y +++ b/mrbgems/mruby-compiler/core/parse.y @@ -4204,7 +4204,7 @@ parser_yylex(parser_state *p) } pushback(p, c); if (IS_SPCARG(c)) { - yywarning(p, "`*' interpreted as argument prefix"); + yywarning(p, "'*' interpreted as argument prefix"); c = tSTAR; } else if (IS_BEG()) { @@ -4455,7 +4455,7 @@ parser_yylex(parser_state *p) } pushback(p, c); if (IS_SPCARG(c)) { - yywarning(p, "`&' interpreted as argument prefix"); + yywarning(p, "'&' interpreted as argument prefix"); c = tAMPER; } else if (IS_BEG()) { @@ -4761,7 +4761,7 @@ parser_yylex(parser_state *p) nondigit = c; break; - case '_': /* `_' in number just ignored */ + case '_': /* '_' in number just ignored */ if (nondigit) goto decode_num; nondigit = c; break; @@ -4776,7 +4776,7 @@ parser_yylex(parser_state *p) pushback(p, c); if (nondigit) { trailing_uc: - yyerror_i(p, "trailing `%c' in number", nondigit); + yyerror_i(p, "trailing '%c' in number", nondigit); } tokfix(p); if (is_float) { @@ -5157,10 +5157,10 @@ parser_yylex(parser_state *p) } else if (isdigit(c)) { if (p->bidx == 1) { - yyerror_i(p, "`@%c' is not allowed as an instance variable name", c); + yyerror_i(p, "'@%c' is not allowed as an instance variable name", c); } else { - yyerror_i(p, "`@@%c' is not allowed as a class variable name", c); + yyerror_i(p, "'@@%c' is not allowed as a class variable name", c); } return 0; } @@ -5176,7 +5176,7 @@ parser_yylex(parser_state *p) default: if (!identchar(c)) { - yyerror_i(p, "Invalid char `\\x%02X' in expression", c); + yyerror_i(p, "Invalid char '\\x%02X' in expression", c); goto retry; } diff --git a/mrbgems/mruby-hash-ext/mrblib/hash.rb b/mrbgems/mruby-hash-ext/mrblib/hash.rb index ea5e6bc1b..c970b9d02 100644 --- a/mrbgems/mruby-hash-ext/mrblib/hash.rb +++ b/mrbgems/mruby-hash-ext/mrblib/hash.rb @@ -119,7 +119,7 @@ class Hash # # produces: # - # prog.rb:2:in `fetch': key not found (KeyError) + # prog.rb:2:in 'fetch': key not found (KeyError) # from prog.rb:2 # diff --git a/mrbgems/mruby-sprintf/src/sprintf.c b/mrbgems/mruby-sprintf/src/sprintf.c index d88e242c6..de216f69f 100644 --- a/mrbgems/mruby-sprintf/src/sprintf.c +++ b/mrbgems/mruby-sprintf/src/sprintf.c @@ -234,20 +234,20 @@ get_hash(mrb_state *mrb, mrb_value *hash, int argc, const mrb_value *argv) * ------+-------------------------------------------------------------- * b | Convert argument as a binary number. * | Negative numbers will be displayed as a two's complement - * | prefixed with `..1'. - * B | Equivalent to `b', but uses an uppercase 0B for prefix + * | prefixed with '..1'. + * B | Equivalent to 'b', but uses an uppercase 0B for prefix * | in the alternative format by #. * d | Convert argument as a decimal number. - * i | Identical to `d'. + * i | Identical to 'd'. * o | Convert argument as an octal number. * | Negative numbers will be displayed as a two's complement - * | prefixed with `..7'. - * u | Identical to `d'. + * | prefixed with '..7'. + * u | Identical to 'd'. * x | Convert argument as a hexadecimal number. * | Negative numbers will be displayed as a two's complement - * | prefixed with `..f' (representing an infinite string of + * | prefixed with '..f' (representing an infinite string of * | leading 'ff's). - * X | Equivalent to `x', but uses uppercase letters. + * X | Equivalent to 'x', but uses uppercase letters. * * Field | Float Format * ------+-------------------------------------------------------------- @@ -255,7 +255,7 @@ get_hash(mrb_state *mrb, mrb_value *hash, int argc, const mrb_value *argv) * | with one digit before the decimal point as [-]d.dddddde[+-]dd. * | The precision specifies the number of digits after the decimal * | point (defaulting to six). - * E | Equivalent to `e', but uses an uppercase E to indicate + * E | Equivalent to 'e', but uses an uppercase E to indicate * | the exponent. * f | Convert floating point argument as [-]ddd.dddddd, * | where the precision specifies the number of digits after @@ -264,11 +264,11 @@ get_hash(mrb_state *mrb, mrb_value *hash, int argc, const mrb_value *argv) * | if the exponent is less than -4 or greater than or * | equal to the precision, or in dd.dddd form otherwise. * | The precision specifies the number of significant digits. - * G | Equivalent to `g', but use an uppercase `E' in exponent form. + * G | Equivalent to 'g', but use an uppercase 'E' in exponent form. * a | Convert floating point argument as [-]0xh.hhhhp[+-]dd, * | which is consisted from optional sign, "0x", fraction part * | as hexadecimal, "p", and exponential part as decimal. - * A | Equivalent to `a', but use uppercase `X' and `P'. + * A | Equivalent to 'a', but use uppercase 'X' and 'P'. * * Field | Other Format * ------+-------------------------------------------------------------- @@ -287,7 +287,7 @@ get_hash(mrb_state *mrb, mrb_value *hash, int argc, const mrb_value *argv) * ---------+---------------+----------------------------------------- * space | bBdiouxX | Leave a space at the start of * | aAeEfgG | non-negative numbers. - * | (numeric fmt) | For `o', `x', `X', `b' and `B', use + * | (numeric fmt) | For 'o', 'x', 'X', 'b' and 'B', use * | | a minus sign with absolute value for * | | negative values. * ---------+---------------+----------------------------------------- @@ -297,27 +297,27 @@ get_hash(mrb_state *mrb, mrb_value *hash, int argc, const mrb_value *argv) * | | sprintf string. * ---------+---------------+----------------------------------------- * # | bBoxX | Use an alternative format. - * | aAeEfgG | For the conversions `o', increase the precision - * | | until the first digit will be `0' if + * | aAeEfgG | For the conversions 'o', increase the precision + * | | until the first digit will be '0' if * | | it is not formatted as complements. - * | | For the conversions `x', `X', `b' and `B' - * | | on non-zero, prefix the result with ``0x'', - * | | ``0X'', ``0b'' and ``0B'', respectively. - * | | For `a', `A', `e', `E', `f', `g', and 'G', + * | | For the conversions 'x', 'X', 'b' and 'B' + * | | on non-zero, prefix the result with "0x", + * | | "0X", "0b" and "0B", respectively. + * | | For 'a', 'A', 'e', 'E', 'f', 'g', and 'G', * | | force a decimal point to be added, * | | even if no digits follow. - * | | For `g' and 'G', do not remove trailing zeros. + * | | For 'g' and 'G', do not remove trailing zeros. * ---------+---------------+----------------------------------------- * + | bBdiouxX | Add a leading plus sign to non-negative * | aAeEfgG | numbers. - * | (numeric fmt) | For `o', `x', `X', `b' and `B', use + * | (numeric fmt) | For 'o', 'x', 'X', 'b' and 'B', use * | | a minus sign with absolute value for * | | negative values. * ---------+---------------+----------------------------------------- * - | all | Left-justify the result of this conversion. * ---------+---------------+----------------------------------------- * 0 (zero) | bBdiouxX | Pad with zeros, not spaces. - * | aAeEfgG | For `o', `x', `X', `b' and `B', radix-1 + * | aAeEfgG | For 'o', 'x', 'X', 'b' and 'B', radix-1 * | (numeric fmt) | is used for negative numbers formatted as * | | complements. * ---------+---------------+----------------------------------------- @@ -328,21 +328,21 @@ get_hash(mrb_state *mrb, mrb_value *hash, int argc, const mrb_value *argv) * * Examples of flags: * - * # `+' and space flag specifies the sign of non-negative numbers. + * # '+' and space flag specifies the sign of non-negative numbers. * sprintf("%d", 123) #=> "123" * sprintf("%+d", 123) #=> "+123" * sprintf("% d", 123) #=> " 123" * - * # `#' flag for `o' increases number of digits to show `0'. - * # `+' and space flag changes format of negative numbers. + * # '#' flag for 'o' increases number of digits to show '0'. + * # '+' and space flag changes format of negative numbers. * sprintf("%o", 123) #=> "173" * sprintf("%#o", 123) #=> "0173" * sprintf("%+o", -123) #=> "-173" * sprintf("%o", -123) #=> "..7605" * sprintf("%#o", -123) #=> "..7605" * - * # `#' flag for `x' add a prefix `0x' for non-zero numbers. - * # `+' and space flag disables complements for negative numbers. + * # '#' flag for 'x' add a prefix '0x' for non-zero numbers. + * # '+' and space flag disables complements for negative numbers. * sprintf("%x", 123) #=> "7b" * sprintf("%#x", 123) #=> "0x7b" * sprintf("%+x", -123) #=> "-7b" @@ -350,12 +350,12 @@ get_hash(mrb_state *mrb, mrb_value *hash, int argc, const mrb_value *argv) * sprintf("%#x", -123) #=> "0x..f85" * sprintf("%#x", 0) #=> "0" * - * # `#' for `X' uses the prefix `0X'. + * # '#' for 'X' uses the prefix '0X'. * sprintf("%X", 123) #=> "7B" * sprintf("%#X", 123) #=> "0X7B" * - * # `#' flag for `b' add a prefix `0b' for non-zero numbers. - * # `+' and space flag disables complements for negative numbers. + * # '#' flag for 'b' add a prefix '0b' for non-zero numbers. + * # '+' and space flag disables complements for negative numbers. * sprintf("%b", 123) #=> "1111011" * sprintf("%#b", 123) #=> "0b1111011" * sprintf("%+b", -123) #=> "-1111011" @@ -363,19 +363,19 @@ get_hash(mrb_state *mrb, mrb_value *hash, int argc, const mrb_value *argv) * sprintf("%#b", -123) #=> "0b..10000101" * sprintf("%#b", 0) #=> "0" * - * # `#' for `B' uses the prefix `0B'. + * # '#' for 'B' uses the prefix '0B'. * sprintf("%B", 123) #=> "1111011" * sprintf("%#B", 123) #=> "0B1111011" * - * # `#' for `e' forces to show the decimal point. + * # '#' for 'e' forces to show the decimal point. * sprintf("%.0e", 1) #=> "1e+00" * sprintf("%#.0e", 1) #=> "1.e+00" * - * # `#' for `f' forces to show the decimal point. + * # '#' for 'f' forces to show the decimal point. * sprintf("%.0f", 1234) #=> "1234" * sprintf("%#.0f", 1234) #=> "1234." * - * # `#' for `g' forces to show the decimal point. + * # '#' for 'g' forces to show the decimal point. * # It also disables stripping lowest zeros. * sprintf("%g", 123.4) #=> "123.4" * sprintf("%#g", 123.4) #=> "123.400" @@ -409,7 +409,7 @@ get_hash(mrb_state *mrb, mrb_value *hash, int argc, const mrb_value *argv) * * Examples of precisions: * - * # precision for `d', 'o', 'x' and 'b' is + * # precision for 'd', 'o', 'x' and 'b' is * # minimum number of digits <------> * sprintf("%20.8d", 123) #=> " 00000123" * sprintf("%20.8o", 123) #=> " 00000173" @@ -420,8 +420,8 @@ get_hash(mrb_state *mrb, mrb_value *hash, int argc, const mrb_value *argv) * sprintf("%20.8x", -123) #=> " ..ffff85" * sprintf("%20.8b", -11) #=> " ..110101" * - * # "0x" and "0b" for `#x' and `#b' is not counted for - * # precision but "0" for `#o' is counted. <------> + * # "0x" and "0b" for '#x' and '#b' is not counted for + * # precision but "0" for '#o' is counted. <------> * sprintf("%#20.8d", 123) #=> " 00000123" * sprintf("%#20.8o", 123) #=> " 00000173" * sprintf("%#20.8x", 123) #=> " 0x0000007b" @@ -431,22 +431,22 @@ get_hash(mrb_state *mrb, mrb_value *hash, int argc, const mrb_value *argv) * sprintf("%#20.8x", -123) #=> " 0x..ffff85" * sprintf("%#20.8b", -11) #=> " 0b..110101" * - * # precision for `e' is number of + * # precision for 'e' is number of * # digits after the decimal point <------> * sprintf("%20.8e", 1234.56789) #=> " 1.23456789e+03" * - * # precision for `f' is number of + * # precision for 'f' is number of * # digits after the decimal point <------> * sprintf("%20.8f", 1234.56789) #=> " 1234.56789000" * - * # precision for `g' is number of + * # precision for 'g' is number of * # significant digits <-------> * sprintf("%20.8g", 1234.56789) #=> " 1234.5679" * * # <-------> * sprintf("%20.8g", 123456789) #=> " 1.2345679e+08" * - * # precision for `s' is + * # precision for 's' is * # maximum number of characters <------> * sprintf("%20.8s", "string test") #=> " string t" * @@ -539,7 +539,7 @@ mrb_str_format(mrb_state *mrb, int argc, const mrb_value *argv, mrb_value fmt) if (t >= end) goto sprint_exit; /* end of fmt string */ - p = t + 1; /* skip `%' */ + p = t + 1; /* skip '%' */ width = prec = -1; nextvalue = mrb_undef_value(); diff --git a/mrbgems/mruby-struct/src/struct.c b/mrbgems/mruby-struct/src/struct.c index d2187a2d1..ce8d8d832 100644 --- a/mrbgems/mruby-struct/src/struct.c +++ b/mrbgems/mruby-struct/src/struct.c @@ -114,7 +114,7 @@ mrb_struct_getmember(mrb_state *mrb, mrb_value obj, mrb_sym id) return ptr[i]; } } - mrb_raisef(mrb, E_INDEX_ERROR, "`%S' is not a struct member", mrb_sym2str(mrb, id)); + mrb_raisef(mrb, E_INDEX_ERROR, "'%S' is not a struct member", mrb_sym2str(mrb, id)); return mrb_nil_value(); /* not reached */ } @@ -193,7 +193,7 @@ mrb_struct_set(mrb_state *mrb, mrb_value obj, mrb_value val) return ptr[i] = val; } } - mrb_raisef(mrb, E_INDEX_ERROR, "`%S' is not a struct member", mrb_sym2str(mrb, mid)); + mrb_raisef(mrb, E_INDEX_ERROR, "'%S' is not a struct member", mrb_sym2str(mrb, mid)); return mrb_nil_value(); /* not reached */ } @@ -749,8 +749,8 @@ mrb_struct_values_at(mrb_state *mrb, mrb_value self) * The Struct class is a generator of specific classes, * each one of which is defined to hold a set of variables and their * accessors. In these examples, we'll call the generated class - * ``CustomerClass,'' and we'll show an example instance of that - * class as ``CustomerInst.'' + * "CustomerClass," and we'll show an example instance of that + * class as "CustomerInst." * * In the descriptions that follow, the parameter symbol refers * to a symbol, which is either a quoted string or a diff --git a/mrblib/array.rb b/mrblib/array.rb index 83a42c62d..933f822db 100644 --- a/mrblib/array.rb +++ b/mrblib/array.rb @@ -146,7 +146,7 @@ class Array # equal, then that inequality is the return value. If all the # values found are equal, then the return is based on a # comparison of the array lengths. Thus, two arrays are - # ``equal'' according to Array#<=> if and only if they have + # "equal" according to Array#<=> if and only if they have # the same length and the value of each element is equal to the # value of the corresponding element in the other array. # diff --git a/src/class.c b/src/class.c index 05b549b3e..e9cbc592d 100644 --- a/src/class.c +++ b/src/class.c @@ -212,7 +212,7 @@ MRB_API struct RClass* mrb_define_class_id(mrb_state *mrb, mrb_sym name, struct RClass *super) { if (!super) { - mrb_warn(mrb, "no super class for `%S', Object assumed", mrb_sym2str(mrb, name)); + mrb_warn(mrb, "no super class for '%S', Object assumed", mrb_sym2str(mrb, name)); } return define_class(mrb, name, super, mrb->object_class); } @@ -311,7 +311,7 @@ mrb_define_class_under(mrb_state *mrb, struct RClass *outer, const char *name, s #if 0 if (!super) { - mrb_warn(mrb, "no super class for `%S::%S', Object assumed", + mrb_warn(mrb, "no super class for '%S::%S', Object assumed", mrb_obj_value(outer), mrb_sym2str(mrb, id)); } #endif @@ -1658,7 +1658,7 @@ check_cv_name_str(mrb_state *mrb, mrb_value str) mrb_int len = RSTRING_LEN(str); if (len < 3 || !(s[0] == '@' && s[1] == '@')) { - mrb_name_error(mrb, mrb_intern_str(mrb, str), "`%S' is not allowed as a class variable name", str); + mrb_name_error(mrb, mrb_intern_str(mrb, str), "'%S' is not allowed as a class variable name", str); } } @@ -1846,7 +1846,7 @@ remove_method(mrb_state *mrb, mrb_value mod, mrb_sym mid) } } - mrb_name_error(mrb, mid, "method `%S' not defined in %S", + mrb_name_error(mrb, mid, "method '%S' not defined in %S", mrb_sym2str(mrb, mid), mod); } diff --git a/src/numeric.c b/src/numeric.c index 8b6ec4c88..013273232 100644 --- a/src/numeric.c +++ b/src/numeric.c @@ -110,8 +110,8 @@ num_div(mrb_state *mrb, mrb_value x) * * Returns a string containing a representation of self. As well as a * fixed or exponential form of the number, the call may return - * ``NaN'', ``Infinity'', and - * ``-Infinity''. + * "NaN", "Infinity", and + * "-Infinity". */ static mrb_value diff --git a/src/object.c b/src/object.c index c5fb74575..f8f41bfe8 100644 --- a/src/object.c +++ b/src/object.c @@ -428,7 +428,7 @@ mrb_check_type(mrb_state *mrb, mrb_value x, enum mrb_vtype t) * Returns a string representing obj. The default * to_s prints the object's class and an encoding of the * object id. As a special case, the top-level object that is the - * initial execution context of Ruby programs returns ``main.'' + * initial execution context of Ruby programs returns "main." */ MRB_API mrb_value diff --git a/src/string.c b/src/string.c index 22a289ade..8df79d4c0 100644 --- a/src/string.c +++ b/src/string.c @@ -1100,7 +1100,7 @@ mrb_str_downcase_bang(mrb_state *mrb, mrb_value str) * * Returns a copy of str with all uppercase letters replaced with their * lowercase counterparts. The operation is locale insensitive---only - * characters ``A'' to ``Z'' are affected. + * characters 'A' to 'Z' are affected. * * "hEllO".downcase #=> "hello" */ @@ -1703,7 +1703,7 @@ mrb_str_rindex_m(mrb_state *mrb, mrb_value str) * * If pattern is omitted, the value of $; is used. If * $; is nil (which is the default), str is - * split on whitespace as if ` ' were specified. + * split on whitespace as if ' ' were specified. * * If the limit parameter is omitted, trailing null fields are * suppressed. If limit is a positive number, at most that number of @@ -2211,7 +2211,7 @@ mrb_str_upcase_bang(mrb_state *mrb, mrb_value str) * * Returns a copy of str with all lowercase letters replaced with their * uppercase counterparts. The operation is locale insensitive---only - * characters ``a'' to ``z'' are affected. + * characters 'a' to 'z' are affected. * * "hEllO".upcase #=> "HELLO" */ diff --git a/src/variable.c b/src/variable.c index 3e451df05..1b2ad56a7 100644 --- a/src/variable.c +++ b/src/variable.c @@ -563,7 +563,7 @@ MRB_API void mrb_iv_check(mrb_state *mrb, mrb_sym iv_name) { if (!mrb_iv_p(mrb, iv_name)) { - mrb_name_error(mrb, iv_name, "`%S' is not allowed as an instance variable name", mrb_sym2str(mrb, iv_name)); + mrb_name_error(mrb, iv_name, "'%S' is not allowed as an instance variable name", mrb_sym2str(mrb, iv_name)); } } -- cgit v1.2.3 From 52db92de53a9af1adbac9e116bd19aeecd2485c1 Mon Sep 17 00:00:00 2001 From: Kouhei Sutou Date: Mon, 29 Jun 2015 14:39:30 +0900 Subject: Fix a crash bug when GC is ran while creating a proc with env mrb_proc_new_cfunc_with_env() allocates RProc with RProc::env as NULL then allocates REnv and sets it to RProc::env of the allocated RProc. If incremental GC is ran before "allocates REnv and sets it to RProc::env of the allocated RProc", the allocated RProc's GC status is "marked" (Black) and the allocated REnv's GC status is "unmarked" (White). The next incremental GC sweeps the allocated REnv without re-marking the allocated RProc. Because the RProc is Black and the REnv is White. We need to implement write barrier for the case. We can force to cause the above situation by the following patch: diff --git a/src/proc.c b/src/proc.c index f98998f..4f4e25c 100644 --- a/src/proc.c +++ b/src/proc.c @@ -92,6 +92,7 @@ mrb_proc_new_cfunc_with_env(mrb_state *mrb, mrb_func_t func, mrb_int argc, const struct REnv *e; int i; + mrb_incremental_gc(mrb); p->env = e = env_new(mrb, argc); MRB_ENV_UNSHARE_STACK(e); e->stack = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value) * argc); With this patch, "rake test" causes segmentation fault. --- src/proc.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/proc.c b/src/proc.c index f98998f68..8a2b6bbb6 100644 --- a/src/proc.c +++ b/src/proc.c @@ -93,6 +93,7 @@ mrb_proc_new_cfunc_with_env(mrb_state *mrb, mrb_func_t func, mrb_int argc, const int i; p->env = e = env_new(mrb, argc); + mrb_field_write_barrier(mrb, (struct RBasic *)p, (struct RBasic *)p->env); MRB_ENV_UNSHARE_STACK(e); e->stack = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value) * argc); if (argv) { -- cgit v1.2.3 From d6865a9cc76c67b39bb29def7a62f9b9f752363c Mon Sep 17 00:00:00 2001 From: Simon Génier Date: Mon, 29 Jun 2015 13:37:16 -0400 Subject: Avoid a narrowing cast in flo_round under MRB_INT64. --- src/numeric.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/numeric.c b/src/numeric.c index 013273232..b9aef51d9 100644 --- a/src/numeric.c +++ b/src/numeric.c @@ -436,7 +436,7 @@ flo_round(mrb_state *mrb, mrb_value num) { double number, f; mrb_int ndigits = 0; - int i; + mrb_int i; mrb_get_args(mrb, "|i", &ndigits); number = mrb_float(num); @@ -451,7 +451,7 @@ flo_round(mrb_state *mrb, mrb_value num) } f = 1.0; - i = abs(ndigits); + i = ndigits >= 0 ? ndigits : -ndigits; while (--i >= 0) f = f*10.0; -- cgit v1.2.3 From 43df1d7e3937162dd406dafeab79cd54dea13762 Mon Sep 17 00:00:00 2001 From: cremno Date: Fri, 3 Jul 2015 01:14:06 +0200 Subject: remove unnecessary default case --- src/dump.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/dump.c b/src/dump.c index d9410ec18..2f2e5edcb 100644 --- a/src/dump.c +++ b/src/dump.c @@ -819,7 +819,6 @@ write_rite_binary_header(mrb_state *mrb, size_t binary_size, uint8_t *bin, uint8 uint32_t offset; switch (flags & DUMP_ENDIAN_NAT) { - default: endian_big: case DUMP_ENDIAN_BIG: memcpy(header->binary_ident, RITE_BINARY_IDENT, sizeof(header->binary_ident)); -- cgit v1.2.3 From 7206a8c4f91bd54390b6e244d6a32f9766b8e05f Mon Sep 17 00:00:00 2001 From: cremno Date: Fri, 3 Jul 2015 02:48:22 +0200 Subject: fix dangling pointer CID 75872 (#3-2 of 3): Pointer to local outside scope (RETURN_LOCAL) 38. use_invalid: Using p, which points to an out-of-scope variable buf. --- src/string.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/string.c b/src/string.c index 8df79d4c0..757317bdc 100644 --- a/src/string.c +++ b/src/string.c @@ -2057,6 +2057,7 @@ MRB_API double mrb_cstr_to_dbl(mrb_state *mrb, const char * p, mrb_bool badcheck) { char *end; + char buf[DBL_DIG * 4 + 10]; double d; enum {max_width = 20}; @@ -2077,7 +2078,6 @@ bad: return d; } if (*end) { - char buf[DBL_DIG * 4 + 10]; char *n = buf; char *e = buf + sizeof(buf) - 1; char prev = 0; -- cgit v1.2.3 From 03ae38df4e18c33e6ce4f5614bd4ec7b1ba9f192 Mon Sep 17 00:00:00 2001 From: Anton Davydov Date: Sun, 5 Jul 2015 13:49:37 +0300 Subject: Fix typo in comment in String#mrb_cstr_to_inum [skip ci] --- src/string.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/string.c b/src/string.c index 8df79d4c0..57b5eef9a 100644 --- a/src/string.c +++ b/src/string.c @@ -1927,7 +1927,7 @@ mrb_cstr_to_inum(mrb_state *mrb, const char *str, int base, int badcheck) } break; } /* end of switch (base) { */ - if (*str == '0') { /* squeeze preceeding 0s */ + if (*str == '0') { /* squeeze preceding 0s */ uscore = 0; while ((c = *++str) == '0' || c == '_') { if (c == '_') { -- cgit v1.2.3 From 0b50b4a5005c3233b88b09f196034352ebdfc515 Mon Sep 17 00:00:00 2001 From: xuejianqing Date: Mon, 6 Jul 2015 12:51:57 +0800 Subject: fix androideabi cross-compile error : undefined reference to 'frexpl' --- src/fmt_fp.c | 9 +++------ tasks/toolchains/androideabi.rake | 4 ++-- 2 files changed, 5 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/fmt_fp.c b/src/fmt_fp.c index a634edb34..b467435a3 100644 --- a/src/fmt_fp.c +++ b/src/fmt_fp.c @@ -90,12 +90,9 @@ fmt_u(uint32_t x, char *s) typedef char compiler_defines_long_double_incorrectly[9-(int)sizeof(long double)]; #endif -#if (defined(__CYGWIN32__) || defined(__NetBSD__) || defined(mips)) && !defined(__linux__) -static long double -frexpl (long double x, int *eptr) -{ - return frexp(x, eptr); -} +#if ((defined(__CYGWIN32__) || defined(__NetBSD__) || defined(mips)) && !defined(__linux__)) || defined(__android__) +#undef frexpl +#define frexpl frexp #endif static int diff --git a/tasks/toolchains/androideabi.rake b/tasks/toolchains/androideabi.rake index 61881ca31..7cdb9e43a 100644 --- a/tasks/toolchains/androideabi.rake +++ b/tasks/toolchains/androideabi.rake @@ -109,8 +109,8 @@ MRuby::Toolchain.new(:androideabi) do |conf| ANDROID_CC = path_to_toolchain + '/bin/' + toolchain_prefix + 'gcc' ANDROID_LD = path_to_toolchain + '/bin/' + toolchain_prefix + 'gcc' ANDROID_AR = path_to_toolchain + '/bin/' + toolchain_prefix + 'ar' - ANDROID_CFLAGS = GCC_COMMON_CFLAGS + %W(-mandroid --sysroot="#{SYSROOT}") + ARCH_CFLAGS - ANDROID_LDFLAGS = GCC_COMMON_LDFLAGS + %W(-mandroid --sysroot="#{SYSROOT}") + ARCH_LDFLAGS + ANDROID_CFLAGS = GCC_COMMON_CFLAGS + %W(-D__android__ -mandroid --sysroot="#{SYSROOT}") + ARCH_CFLAGS + ANDROID_LDFLAGS = GCC_COMMON_LDFLAGS + %W(-D__android__ -mandroid --sysroot="#{SYSROOT}") + ARCH_LDFLAGS when 'clang' then # clang is not supported yet. when 'clang31', 'clang3.1' then -- cgit v1.2.3 From 40252169fc9762f222106f116f2bcc510b038bf9 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Tue, 7 Jul 2015 10:32:37 +0900 Subject: method_missing definition may be undefined; fix #2878 --- src/vm.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/vm.c b/src/vm.c index 1f3b05df2..15a3926e3 100644 --- a/src/vm.c +++ b/src/vm.c @@ -1074,9 +1074,14 @@ RETRY_TRY_BLOCK: m = mrb_method_search_vm(mrb, &c, mid); if (!m) { mrb_value sym = mrb_symbol_value(mid); + mrb_sym missing = mrb_intern_lit(mrb, "method_missing"); - mid = mrb_intern_lit(mrb, "method_missing"); - m = mrb_method_search_vm(mrb, &c, mid); + m = mrb_method_search_vm(mrb, &c, missing); + if (!m) { + mrb_no_method_error(mrb, mid, n, regs+a+1, + "undefined method '%S' for %S", mrb_sym2str(mrb, mid), recv); + } + mid = missing; if (n == CALL_MAXARGS) { mrb_ary_unshift(mrb, regs[a+1], sym); } -- cgit v1.2.3 From bdbd696c486f88a2239420a027f7e1772713bbe0 Mon Sep 17 00:00:00 2001 From: jbreeden Date: Sat, 11 Jul 2015 21:03:04 -0700 Subject: Patching array join --- src/array.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/array.c b/src/array.c index f48719310..ff0bb8a85 100644 --- a/src/array.c +++ b/src/array.c @@ -295,7 +295,7 @@ mrb_ary_plus(mrb_state *mrb, mrb_value self) mrb_get_args(mrb, "a", &ptr, &blen); if (ARY_MAX_SIZE - blen < a1->len) { - mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big"); + mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big"); } a2 = ary_new_capa(mrb, a1->len + blen); array_copy(a2->ptr, a1->ptr, a1->len); @@ -349,7 +349,7 @@ mrb_ary_times(mrb_state *mrb, mrb_value self) } if (times == 0) return mrb_ary_new(mrb); if (ARY_MAX_SIZE / times < a1->len) { - mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big"); + mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big"); } a2 = ary_new_capa(mrb, a1->len * times); ptr = a2->ptr; @@ -1031,7 +1031,13 @@ mrb_ary_join_m(mrb_state *mrb, mrb_value ary) { mrb_value sep = mrb_nil_value(); - mrb_get_args(mrb, "|S", &sep); + mrb_get_args(mrb, "|o", &sep); + if (mrb_nil_p(sep)) { + sep = mrb_str_to_str(mrb, sep); + } else if (mrb_type(sep) != MRB_TT_STRING) { + mrb_raise(mrb, E_TYPE_ERROR, "expected String"); + return mrb_nil_value(); + } return mrb_ary_join(mrb, ary, sep); } -- cgit v1.2.3 From dd558a108d20d2198cb52cf743e2ecfc868cfa1c Mon Sep 17 00:00:00 2001 From: jbreeden Date: Sat, 11 Jul 2015 21:15:33 -0700 Subject: Removing unndeeded to_s --- src/array.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'src') diff --git a/src/array.c b/src/array.c index ff0bb8a85..2fb662c92 100644 --- a/src/array.c +++ b/src/array.c @@ -1032,9 +1032,7 @@ mrb_ary_join_m(mrb_state *mrb, mrb_value ary) mrb_value sep = mrb_nil_value(); mrb_get_args(mrb, "|o", &sep); - if (mrb_nil_p(sep)) { - sep = mrb_str_to_str(mrb, sep); - } else if (mrb_type(sep) != MRB_TT_STRING) { + if (!(mrb_nil_p(sep) || mrb_type(sep) == MRB_TT_STRING)) { mrb_raise(mrb, E_TYPE_ERROR, "expected String"); return mrb_nil_value(); } -- cgit v1.2.3 From 9c311ddc938ad2cc88e4119374e47cd496e15a94 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Mon, 13 Jul 2015 07:08:01 +0900 Subject: refactor mrb_bob_missing to share raising NoMethodError code; fix #2878 Note: arguments of mrb_no_method_error() has changed. You need to replace 3rd and 4th argument (say n, argv) to mrb_ary_new_from_values(mrb, n, argv). --- include/mruby/error.h | 2 +- src/class.c | 45 ++++++++++++++++++++++++++------------------- src/error.c | 5 ++--- src/vm.c | 13 +++++++++++-- 4 files changed, 40 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/include/mruby/error.h b/include/mruby/error.h index 52f6772bd..282be0a24 100644 --- a/include/mruby/error.h +++ b/include/mruby/error.h @@ -24,7 +24,7 @@ MRB_API mrb_value mrb_exc_new_str(mrb_state *mrb, struct RClass* c, mrb_value st MRB_API mrb_value mrb_make_exception(mrb_state *mrb, int argc, const mrb_value *argv); MRB_API mrb_value mrb_exc_backtrace(mrb_state *mrb, mrb_value exc); MRB_API mrb_value mrb_get_backtrace(mrb_state *mrb); -MRB_API mrb_noreturn void mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_int argc, const mrb_value *argv, const char *fmt, ...); +MRB_API mrb_noreturn void mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_value args, const char *fmt, ...); /* declaration for fail method */ MRB_API mrb_value mrb_f_raise(mrb_state*, mrb_value); diff --git a/src/class.c b/src/class.c index e9cbc592d..0f9a77b2a 100644 --- a/src/class.c +++ b/src/class.c @@ -1262,6 +1262,31 @@ mrb_bob_not(mrb_state *mrb, mrb_value cv) return mrb_bool_value(!mrb_test(cv)); } +void +mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args) +{ + mrb_sym inspect; + mrb_value repr; + + inspect = mrb_intern_lit(mrb, "inspect"); + if (mrb->c->ci > mrb->c->cibase && mrb->c->ci[-1].mid == inspect) { + /* method missing in inspect; avoid recursion */ + repr = mrb_any_to_s(mrb, self); + } + else if (mrb_respond_to(mrb, self, inspect) && mrb->c->ci - mrb->c->cibase < 64) { + repr = mrb_funcall_argv(mrb, self, inspect, 0, 0); + if (mrb_string_p(repr) && RSTRING_LEN(repr) > 64) { + repr = mrb_any_to_s(mrb, self); + } + } + else { + repr = mrb_any_to_s(mrb, self); + } + + mrb_no_method_error(mrb, name, args, "undefined method '%S' for %S", + mrb_sym2str(mrb, name), repr); +} + /* 15.3.1.3.30 */ /* * call-seq: @@ -1301,27 +1326,9 @@ mrb_bob_missing(mrb_state *mrb, mrb_value mod) mrb_sym name; mrb_value *a; mrb_int alen; - mrb_sym inspect; - mrb_value repr; mrb_get_args(mrb, "n*", &name, &a, &alen); - - inspect = mrb_intern_lit(mrb, "inspect"); - if (mrb->c->ci > mrb->c->cibase && mrb->c->ci[-1].mid == inspect) { - /* method missing in inspect; avoid recursion */ - repr = mrb_any_to_s(mrb, mod); - } - else if (mrb_respond_to(mrb, mod, inspect) && mrb->c->ci - mrb->c->cibase < 64) { - repr = mrb_funcall_argv(mrb, mod, inspect, 0, 0); - if (mrb_string_p(repr) && RSTRING_LEN(repr) > 64) { - repr = mrb_any_to_s(mrb, mod); - } - } - else { - repr = mrb_any_to_s(mrb, mod); - } - - mrb_no_method_error(mrb, name, alen, a, "undefined method '%S' for %S", mrb_sym2str(mrb, name), repr); + mrb_method_missing(mrb, name, mod, mrb_ary_new_from_values(mrb, alen, a)); /* not reached */ return mrb_nil_value(); } diff --git a/src/error.c b/src/error.c index a800f77f9..20c63bd43 100644 --- a/src/error.c +++ b/src/error.c @@ -424,15 +424,14 @@ mrb_sys_fail(mrb_state *mrb, const char *mesg) } MRB_API mrb_noreturn void -mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_int argc, const mrb_value *argv, char const* fmt, ...) +mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_value args, char const* fmt, ...) { mrb_value exc; va_list ap; va_start(ap, fmt); exc = mrb_funcall(mrb, mrb_obj_value(E_NOMETHOD_ERROR), "new", 3, - mrb_vformat(mrb, fmt, ap), mrb_symbol_value(id), - mrb_ary_new_from_values(mrb, argc, argv)); + mrb_vformat(mrb, fmt, ap), mrb_symbol_value(id), args); va_end(ap); mrb_exc_raise(mrb, exc); } diff --git a/src/vm.c b/src/vm.c index 15a3926e3..22ea177e0 100644 --- a/src/vm.c +++ b/src/vm.c @@ -723,6 +723,8 @@ argnum_error(mrb_state *mrb, mrb_int num) #define CALL_MAXARGS 127 +void mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args); + MRB_API mrb_value mrb_context_run(mrb_state *mrb, struct RProc *proc, mrb_value self, unsigned int stack_keep) { @@ -1078,8 +1080,15 @@ RETRY_TRY_BLOCK: m = mrb_method_search_vm(mrb, &c, missing); if (!m) { - mrb_no_method_error(mrb, mid, n, regs+a+1, - "undefined method '%S' for %S", mrb_sym2str(mrb, mid), recv); + mrb_value args; + + if (n == CALL_MAXARGS) { + args = regs[a+1]; + } + else { + args = mrb_ary_new_from_values(mrb, n, regs+a+1); + } + mrb_method_missing(mrb, mid, recv, args); } mid = missing; if (n == CALL_MAXARGS) { -- cgit v1.2.3 From 02474daa14623f3ee24a5235e9b7b88c33d43455 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Mon, 13 Jul 2015 11:14:50 +0900 Subject: allow '!' after 'S' specifier of mrb_get_args() that allow nil. thus "S!" means String|nil type. you have to check nil before dereferencing the value. this is added to address #2882 while keeping code simplicity. besides that current #2882 fix lose polymorphism provided by mrb_get_args(). --- src/class.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/class.c b/src/class.c index 0f9a77b2a..8a9fdaca6 100644 --- a/src/class.c +++ b/src/class.c @@ -435,7 +435,7 @@ to_sym(mrb_state *mrb, mrb_value ss) ---------------------------------------------------------------------------------------------- o: Object [mrb_value] C: class/module [mrb_value] - S: String [mrb_value] + S: String [mrb_value] when ! follows the value may be nil A: Array [mrb_value] H: Hash [mrb_value] s: String [char*,mrb_int] Receive two arguments. @@ -525,6 +525,14 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) mrb_value *p; p = va_arg(ap, mrb_value*); + if (*format == '!') { + format++; + if (mrb_nil_p(*sp)) { + *p = *sp++; + i++; + break; + } + } if (i < argc) { *p = to_str(mrb, *sp++); i++; -- cgit v1.2.3 From d0e67aada795620c2bce49db8c73e87718753614 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Mon, 13 Jul 2015 11:18:44 +0900 Subject: use "S!" specifier of mrb_get_args() to improve #2882 fix. --- src/array.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'src') diff --git a/src/array.c b/src/array.c index 2fb662c92..0a99040c0 100644 --- a/src/array.c +++ b/src/array.c @@ -1031,11 +1031,7 @@ mrb_ary_join_m(mrb_state *mrb, mrb_value ary) { mrb_value sep = mrb_nil_value(); - mrb_get_args(mrb, "|o", &sep); - if (!(mrb_nil_p(sep) || mrb_type(sep) == MRB_TT_STRING)) { - mrb_raise(mrb, E_TYPE_ERROR, "expected String"); - return mrb_nil_value(); - } + mrb_get_args(mrb, "|S!", &sep); return mrb_ary_join(mrb, ary, sep); } -- cgit v1.2.3 From f962890a928b566c0f5ca7fdff5ef4ce19207e65 Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Thu, 9 Jul 2015 23:46:54 +0200 Subject: Implement Module#prepend. --- include/mruby/class.h | 1 + src/class.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++-- test/t/module.rb | 33 +++++++++++++++++++++++++ 3 files changed, 99 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/include/mruby/class.h b/include/mruby/class.h index 9d5260a24..60310ae9d 100644 --- a/include/mruby/class.h +++ b/include/mruby/class.h @@ -16,6 +16,7 @@ struct RClass { struct iv_tbl *iv; struct kh_mt *mt; struct RClass *super; + struct RClass *origin; }; #define mrb_class_ptr(v) ((struct RClass*)(mrb_ptr(v))) diff --git a/src/class.c b/src/class.c index 8a9fdaca6..3b1ea2321 100644 --- a/src/class.c +++ b/src/class.c @@ -194,6 +194,7 @@ define_class(mrb_state *mrb, mrb_sym name, struct RClass *super, struct RClass * if (mrb_const_defined_at(mrb, mrb_obj_value(outer), name)) { c = class_from_sym(mrb, outer, name); + c = c->origin; if (super && mrb_class_real(c->super) != super) { mrb_raisef(mrb, E_TYPE_ERROR, "superclass mismatch for Class %S (%S not %S)", mrb_sym2str(mrb, name), @@ -763,12 +764,13 @@ boot_defclass(mrb_state *mrb, struct RClass *super) else { c->super = mrb->object_class; } + c->origin = c; c->mt = kh_init(mt, mrb); return c; } -MRB_API void -mrb_include_module(mrb_state *mrb, struct RClass *c, struct RClass *m) +MRB_API inline void +include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *m, int search_super) { struct RClass *ins_pos; @@ -782,6 +784,7 @@ mrb_include_module(mrb_state *mrb, struct RClass *c, struct RClass *m) } while (p) { if (c != p && p->tt == MRB_TT_CLASS) { + if (!search_super) break; superclass_seen = 1; } else if (p->mt == m->mt) { @@ -810,6 +813,63 @@ mrb_include_module(mrb_state *mrb, struct RClass *c, struct RClass *m) } } +MRB_API void +mrb_include_module(mrb_state *mrb, struct RClass *c, struct RClass *m) +{ + include_module_at(mrb, c, m, FALSE); +} + +MRB_API void +mrb_prepend_module(mrb_state *mrb, struct RClass *c, struct RClass *m) +{ + struct RClass *origin; + int changed = 0; + + origin = c->origin; + if (origin == c) { + origin = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, c); + //OBJ_WB_UNPROTECT(origin); /* TODO: conservative shading. Need more survey. */ + origin->super = c->super; + c->super = origin; + c->origin = origin; + origin->mt = c->mt; + c->mt = kh_init(mt, mrb); + } + include_module_at(mrb, c, m, FALSE); // changed = + if (changed) { + //rb_vm_check_redefinition_by_prepend(klass); + } +} + +static mrb_value +mrb_mod_prepend_features(mrb_state *mrb, mrb_value mod) +{ + mrb_value klass; + + mrb_check_type(mrb, mod, MRB_TT_MODULE); + mrb_get_args(mrb, "C", &klass); + mrb_prepend_module(mrb, mrb_class_ptr(klass), mrb_class_ptr(mod)); + return mod; +} + +static mrb_value +mrb_mod_prepend(mrb_state *mrb, mrb_value klass) +{ + mrb_value *argv; + mrb_int argc, i; + + mrb_get_args(mrb, "*", &argv, &argc); + for (i=0; i Date: Fri, 10 Jul 2015 21:00:14 +0200 Subject: Expose insert position, which should be at origin for include and klass for prepend. --- src/class.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/class.c b/src/class.c index 3b1ea2321..c1d377e86 100644 --- a/src/class.c +++ b/src/class.c @@ -770,11 +770,8 @@ boot_defclass(mrb_state *mrb, struct RClass *super) } MRB_API inline void -include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *m, int search_super) +include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *ins_pos, struct RClass *m, int search_super) { - struct RClass *ins_pos; - - ins_pos = c; while (m) { struct RClass *p = c, *ic; int superclass_seen = 0; @@ -816,7 +813,7 @@ include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *m, int search MRB_API void mrb_include_module(mrb_state *mrb, struct RClass *c, struct RClass *m) { - include_module_at(mrb, c, m, FALSE); + include_module_at(mrb, c, c->origin, m, FALSE); } MRB_API void @@ -835,7 +832,7 @@ mrb_prepend_module(mrb_state *mrb, struct RClass *c, struct RClass *m) origin->mt = c->mt; c->mt = kh_init(mt, mrb); } - include_module_at(mrb, c, m, FALSE); // changed = + include_module_at(mrb, c, c, m, FALSE); // changed = if (changed) { //rb_vm_check_redefinition_by_prepend(klass); } -- cgit v1.2.3 From 2e617e37e134b428ce7a979446cc2d768d1cf2fe Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Fri, 10 Jul 2015 21:14:28 +0200 Subject: origin must be initialized --- src/class.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/class.c b/src/class.c index c1d377e86..9f21ae395 100644 --- a/src/class.c +++ b/src/class.c @@ -76,6 +76,7 @@ prepare_singleton_class(mrb_state *mrb, struct RBasic *o) if (o->c->tt == MRB_TT_SCLASS) return; sc = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_SCLASS, mrb->class_class); + sc->origin = sc; sc->mt = 0; sc->iv = 0; if (o->tt == MRB_TT_CLASS) { @@ -799,6 +800,7 @@ include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *ins_pos, stru else { ic->c = m; } + ic->origin = ic; ic->mt = m->mt; ic->iv = m->iv; ic->super = ins_pos->super; @@ -825,6 +827,7 @@ mrb_prepend_module(mrb_state *mrb, struct RClass *c, struct RClass *m) origin = c->origin; if (origin == c) { origin = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, c); + origin->origin = origin; //OBJ_WB_UNPROTECT(origin); /* TODO: conservative shading. Need more survey. */ origin->super = c->super; c->super = origin; @@ -1547,6 +1550,7 @@ MRB_API struct RClass* mrb_module_new(mrb_state *mrb) { struct RClass *m = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_MODULE, mrb->module_class); + m->origin = m; m->mt = kh_init(mt, mrb); return m; -- cgit v1.2.3 From 97529c2a9a7b75a838234a420bbe2c6dc59c56ba Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Fri, 10 Jul 2015 22:01:07 +0200 Subject: Comment in a refactor to match MRI, but that fails 320 tests. --- src/class.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/class.c b/src/class.c index 9f21ae395..ca4582041 100644 --- a/src/class.c +++ b/src/class.c @@ -776,11 +776,25 @@ include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *ins_pos, stru while (m) { struct RClass *p = c, *ic; int superclass_seen = 0; - + + //if (m->origin != m) + // goto skip; if (c->mt && c->mt == m->mt) { mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic include detected"); } while (p) { + /*if (p->tt == MRB_TT_ICLASS) { + if (!superclass_seen) { + ins_pos = p; // move insert point + } + goto skip; + } else if (p->tt == MRB_TT_CLASS) { + if (p->mt == m->mt) { + if (!search_super) break; + superclass_seen = 1; + } + }*/ + // if (c != p && p->tt == MRB_TT_CLASS) { if (!search_super) break; superclass_seen = 1; -- cgit v1.2.3 From 005cacf18b8ce2cf854872aa8cb803819478a47d Mon Sep 17 00:00:00 2001 From: Corey Powell Date: Sat, 11 Jul 2015 11:15:18 +0200 Subject: Additional patches to make this work --- src/class.c | 129 ++++++++++++++++++++++++++++++++--------------------------- src/kernel.c | 12 ++++++ 2 files changed, 83 insertions(+), 58 deletions(-) (limited to 'src') diff --git a/src/class.c b/src/class.c index ca4582041..73e6687c0 100644 --- a/src/class.c +++ b/src/class.c @@ -325,9 +325,16 @@ mrb_define_class_under(mrb_state *mrb, struct RClass *outer, const char *name, s MRB_API void mrb_define_method_raw(mrb_state *mrb, struct RClass *c, mrb_sym mid, struct RProc *p) { - khash_t(mt) *h = c->mt; + khash_t(mt) *h; khiter_t k; + if (!c->origin) { + printf("Warning, class %s does not have valid origin\n", mrb_class_name(mrb, c)); + mrb_raisef(mrb, E_RUNTIME_ERROR, "Invalid origin"); + c->origin = c; + } + h = c->origin->mt; + if (!h) h = c->mt = kh_init(mt, mrb); k = kh_put(mt, mrb, h, mid); kh_value(h, k) = p; @@ -758,6 +765,7 @@ boot_defclass(mrb_state *mrb, struct RClass *super) struct RClass *c; c = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_CLASS, mrb->class_class); + c->origin = c; if (super) { c->super = super; mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)super); @@ -765,71 +773,74 @@ boot_defclass(mrb_state *mrb, struct RClass *super) else { c->super = mrb->object_class; } - c->origin = c; c->mt = kh_init(mt, mrb); return c; } -MRB_API inline void -include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *ins_pos, struct RClass *m, int search_super) +static struct RClass* +include_class_new(mrb_state *mrb, struct RClass *m, struct RClass *super) +{ + struct RClass *ic = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, mrb->class_class); + if (m->tt == MRB_TT_ICLASS) { + m = m->c; + } + ic->origin = ic; + ic->iv = m->iv; + ic->mt = m->origin->mt; + ic->super = super; + if (m->tt == MRB_TT_ICLASS) { + ic->c = m->c; + } else { + ic->c = m; + } + return ic; +} + +MRB_API int +include_module_at(mrb_state *mrb, struct RClass *klass, struct RClass *c, struct RClass *module, int search_super) { - while (m) { - struct RClass *p = c, *ic; + struct RClass *p, *iclass; + void *klass_mt = klass->origin->mt; + + while (module) { int superclass_seen = 0; - - //if (m->origin != m) - // goto skip; - if (c->mt && c->mt == m->mt) { - mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic include detected"); - } - while (p) { - /*if (p->tt == MRB_TT_ICLASS) { - if (!superclass_seen) { - ins_pos = p; // move insert point + + if (module->origin != module) + goto skip; + + if (klass_mt && klass_mt == module->mt) + return -1; + + p = klass->super; + while(p) { + if (p->tt == MRB_TT_ICLASS) { + if (p->mt == module->mt) { + if (!superclass_seen) { + c = p; // move insert point + } + goto skip; } - goto skip; } else if (p->tt == MRB_TT_CLASS) { - if (p->mt == m->mt) { - if (!search_super) break; - superclass_seen = 1; - } - }*/ - // - if (c != p && p->tt == MRB_TT_CLASS) { if (!search_super) break; superclass_seen = 1; } - else if (p->mt == m->mt) { - if (p->tt == MRB_TT_ICLASS && !superclass_seen) { - ins_pos = p; - } - goto skip; - } p = p->super; } - ic = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, mrb->class_class); - if (m->tt == MRB_TT_ICLASS) { - ic->c = m->c; - } - else { - ic->c = m; - } - ic->origin = ic; - ic->mt = m->mt; - ic->iv = m->iv; - ic->super = ins_pos->super; - ins_pos->super = ic; - mrb_field_write_barrier(mrb, (struct RBasic*)ins_pos, (struct RBasic*)ic); - ins_pos = ic; + + iclass = include_class_new(mrb, module, c->super); + c->super = iclass; + mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)c->super); + c = iclass; skip: - m = m->super; + module = module->super; } + return 0; } MRB_API void mrb_include_module(mrb_state *mrb, struct RClass *c, struct RClass *m) { - include_module_at(mrb, c, c->origin, m, FALSE); + include_module_at(mrb, c, c->origin, m, 1); } MRB_API void @@ -842,16 +853,16 @@ mrb_prepend_module(mrb_state *mrb, struct RClass *c, struct RClass *m) if (origin == c) { origin = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, c); origin->origin = origin; - //OBJ_WB_UNPROTECT(origin); /* TODO: conservative shading. Need more survey. */ origin->super = c->super; c->super = origin; c->origin = origin; origin->mt = c->mt; c->mt = kh_init(mt, mrb); + mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)c->origin); } - include_module_at(mrb, c, c, m, FALSE); // changed = - if (changed) { - //rb_vm_check_redefinition_by_prepend(klass); + changed = include_module_at(mrb, c, c, m, 0); + if (changed < 0) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic prepend detected"); } } @@ -955,15 +966,12 @@ mrb_mod_ancestors(mrb_state *mrb, mrb_value self) { mrb_value result; struct RClass *c = mrb_class_ptr(self); - result = mrb_ary_new(mrb); - mrb_ary_push(mrb, result, mrb_obj_value(c)); - c = c->super; while (c) { if (c->tt == MRB_TT_ICLASS) { mrb_ary_push(mrb, result, mrb_obj_value(c->c)); } - else if (c->tt != MRB_TT_SCLASS) { + else if (c->origin == c) { mrb_ary_push(mrb, result, mrb_obj_value(c)); } c = c->super; @@ -1005,9 +1013,14 @@ mrb_mod_initialize(mrb_state *mrb, mrb_value mod) { mrb_value b; + /* hack, fix missing module->origin */ + struct RClass *m = mrb_class_ptr(mod); + if (!m->origin) + m->origin = m; + mrb_get_args(mrb, "&", &b); if (!mrb_nil_p(b)) { - mrb_yield_with_class(mrb, b, 1, &mod, mod, mrb_class_ptr(mod)); + mrb_yield_with_class(mrb, b, 1, &mod, mod, m); } return mod; } @@ -1324,9 +1337,9 @@ mrb_class_superclass(mrb_state *mrb, mrb_value klass) struct RClass *c; c = mrb_class_ptr(klass); - c = c->super; + c = c->origin->super; while (c && c->tt == MRB_TT_ICLASS) { - c = c->super; + c = c->origin->super; } if (!c) return mrb_nil_value(); return mrb_obj_value(c); @@ -1925,7 +1938,7 @@ static void remove_method(mrb_state *mrb, mrb_value mod, mrb_sym mid) { struct RClass *c = mrb_class_ptr(mod); - khash_t(mt) *h = c->mt; + khash_t(mt) *h = c->origin->mt; khiter_t k; if (h) { diff --git a/src/kernel.c b/src/kernel.c index b5b13f874..bafab6e76 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -248,6 +248,11 @@ mrb_singleton_class_clone(mrb_state *mrb, mrb_value obj) clone->c = mrb_singleton_class_clone(mrb, mrb_obj_value(klass)); } + if (klass->origin != klass) + clone->origin = klass->origin; + else + clone->origin = clone; + clone->super = klass->super; if (klass->iv) { mrb_iv_copy(mrb, mrb_obj_value(clone), mrb_obj_value(klass)); @@ -269,6 +274,13 @@ copy_class(mrb_state *mrb, mrb_value dst, mrb_value src) { struct RClass *dc = mrb_class_ptr(dst); struct RClass *sc = mrb_class_ptr(src); + /* if the origin is not the same as the class, then the origin and + the current class need to be copied */ + if (sc->origin != sc) { + dc->origin = mrb_class_ptr(mrb_obj_dup(mrb, mrb_obj_value(sc->origin))); + } else { + dc->origin = dc; + } dc->mt = kh_copy(mt, mrb, sc->mt); dc->super = sc->super; } -- cgit v1.2.3 From ce6672717706365929b1c2c3c2c278e75a3c4be4 Mon Sep 17 00:00:00 2001 From: Corey Powell Date: Sat, 11 Jul 2015 08:16:04 -0500 Subject: Remove some remnant debug code --- src/class.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'src') diff --git a/src/class.c b/src/class.c index 73e6687c0..e9df1dae4 100644 --- a/src/class.c +++ b/src/class.c @@ -327,12 +327,6 @@ mrb_define_method_raw(mrb_state *mrb, struct RClass *c, mrb_sym mid, struct RPro { khash_t(mt) *h; khiter_t k; - - if (!c->origin) { - printf("Warning, class %s does not have valid origin\n", mrb_class_name(mrb, c)); - mrb_raisef(mrb, E_RUNTIME_ERROR, "Invalid origin"); - c->origin = c; - } h = c->origin->mt; if (!h) h = c->mt = kh_init(mt, mrb); -- cgit v1.2.3 From 40f48034d512a8e4f24f8d67373f18093e96513c Mon Sep 17 00:00:00 2001 From: Corey Powell Date: Sat, 11 Jul 2015 08:25:43 -0500 Subject: Added changed check to the mrb_include_module --- src/class.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/class.c b/src/class.c index e9df1dae4..4a64e5b23 100644 --- a/src/class.c +++ b/src/class.c @@ -834,7 +834,10 @@ include_module_at(mrb_state *mrb, struct RClass *klass, struct RClass *c, struct MRB_API void mrb_include_module(mrb_state *mrb, struct RClass *c, struct RClass *m) { - include_module_at(mrb, c, c->origin, m, 1); + int changed = include_module_at(mrb, c, c->origin, m, 1); + if (changed < 0) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic include detected"); + } } MRB_API void -- cgit v1.2.3 From d4b009b394d3933ee1b828d0e91d3c6aaa522339 Mon Sep 17 00:00:00 2001 From: Corey Powell Date: Sat, 11 Jul 2015 11:09:13 -0500 Subject: Fixed Modules not being fully initialized before #initialize was called --- src/class.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/class.c b/src/class.c index 4a64e5b23..d83216cb4 100644 --- a/src/class.c +++ b/src/class.c @@ -77,7 +77,7 @@ prepare_singleton_class(mrb_state *mrb, struct RBasic *o) if (o->c->tt == MRB_TT_SCLASS) return; sc = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_SCLASS, mrb->class_class); sc->origin = sc; - sc->mt = 0; + sc->mt = kh_init(mt, mrb); sc->iv = 0; if (o->tt == MRB_TT_CLASS) { c = (struct RClass*)o; @@ -771,6 +771,13 @@ boot_defclass(mrb_state *mrb, struct RClass *super) return c; } +static void +boot_initmod(mrb_state *mrb, struct RClass *mod) +{ + mod->origin = mod; + mod->mt = kh_init(mt, mrb); +} + static struct RClass* include_class_new(mrb_state *mrb, struct RClass *m, struct RClass *super) { @@ -1012,10 +1019,8 @@ mrb_mod_initialize(mrb_state *mrb, mrb_value mod) /* hack, fix missing module->origin */ struct RClass *m = mrb_class_ptr(mod); - if (!m->origin) - m->origin = m; - - mrb_get_args(mrb, "&", &b); + boot_initmod(mrb, m); // bootstrap a newly initialized module + mrb_get_args(mrb, "|&", &b); if (!mrb_nil_p(b)) { mrb_yield_with_class(mrb, b, 1, &mod, mod, m); } @@ -1574,9 +1579,7 @@ MRB_API struct RClass* mrb_module_new(mrb_state *mrb) { struct RClass *m = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_MODULE, mrb->module_class); - m->origin = m; - m->mt = kh_init(mt, mrb); - + boot_initmod(mrb, m); return m; } -- cgit v1.2.3 From a725cb909333335ad178997795e84c332e555c65 Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Sun, 12 Jul 2015 22:07:16 +0200 Subject: Include prepended methods in the instance_methods list. --- src/kernel.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/kernel.c b/src/kernel.c index bafab6e76..36ad683ee 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -653,13 +653,19 @@ mrb_class_instance_method_list(mrb_state *mrb, mrb_bool recur, struct RClass* kl { khint_t i; mrb_value ary; + mrb_bool prepended; struct RClass* oldklass; khash_t(st)* set = kh_init(st, mrb); + if (!recur && klass->origin != klass) { + klass = klass->origin; + prepended = 1; + } + oldklass = 0; while (klass && (klass != oldklass)) { method_entry_loop(mrb, klass, set); - if ((klass->tt == MRB_TT_ICLASS) || + if ((klass->tt == MRB_TT_ICLASS && !prepended) || (klass->tt == MRB_TT_SCLASS)) { } else { -- cgit v1.2.3 From 668153092f48275279f85d2618fff6eb9730332c Mon Sep 17 00:00:00 2001 From: Corey Powell Date: Mon, 13 Jul 2015 09:38:24 -0500 Subject: Added Module#method_removed hook --- src/class.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/class.c b/src/class.c index d83216cb4..0b00a6484 100644 --- a/src/class.c +++ b/src/class.c @@ -1945,6 +1945,7 @@ remove_method(mrb_state *mrb, mrb_value mod, mrb_sym mid) k = kh_get(mt, mrb, h, mid); if (k != kh_end(h)) { kh_del(mt, mrb, h, k); + mrb_funcall(mrb, mod, "method_removed", 1, mrb_symbol_value(mid)); return; } } @@ -2197,6 +2198,7 @@ mrb_init_class(mrb_state *mrb) mrb_define_method(mrb, mod, "public", mrb_mod_dummy_visibility, MRB_ARGS_ANY()); /* 15.2.2.4.38 */ mrb_define_method(mrb, mod, "remove_class_variable", mrb_mod_remove_cvar, MRB_ARGS_REQ(1)); /* 15.2.2.4.39 */ mrb_define_method(mrb, mod, "remove_method", mrb_mod_remove_method, MRB_ARGS_ANY()); /* 15.2.2.4.41 */ + mrb_define_method(mrb, mod, "method_removed", mrb_bob_init, MRB_ARGS_REQ(1)); /* 15.2.2.4.41 */ mrb_define_method(mrb, mod, "attr_reader", mrb_mod_attr_reader, MRB_ARGS_ANY()); /* 15.2.2.4.13 */ mrb_define_method(mrb, mod, "attr_writer", mrb_mod_attr_writer, MRB_ARGS_ANY()); /* 15.2.2.4.14 */ mrb_define_method(mrb, mod, "to_s", mrb_mod_to_s, MRB_ARGS_NONE()); -- cgit v1.2.3 From 1f678a4acefc14531a2ed25c33f62a0d628b1474 Mon Sep 17 00:00:00 2001 From: Corey Powell Date: Mon, 13 Jul 2015 09:40:40 -0500 Subject: Removed comment beside method_removed Not sure if this apart of the ISO standard, so make sure its not misrepresented --- src/class.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/class.c b/src/class.c index 0b00a6484..a13da63ac 100644 --- a/src/class.c +++ b/src/class.c @@ -2198,7 +2198,7 @@ mrb_init_class(mrb_state *mrb) mrb_define_method(mrb, mod, "public", mrb_mod_dummy_visibility, MRB_ARGS_ANY()); /* 15.2.2.4.38 */ mrb_define_method(mrb, mod, "remove_class_variable", mrb_mod_remove_cvar, MRB_ARGS_REQ(1)); /* 15.2.2.4.39 */ mrb_define_method(mrb, mod, "remove_method", mrb_mod_remove_method, MRB_ARGS_ANY()); /* 15.2.2.4.41 */ - mrb_define_method(mrb, mod, "method_removed", mrb_bob_init, MRB_ARGS_REQ(1)); /* 15.2.2.4.41 */ + mrb_define_method(mrb, mod, "method_removed", mrb_bob_init, MRB_ARGS_REQ(1)); mrb_define_method(mrb, mod, "attr_reader", mrb_mod_attr_reader, MRB_ARGS_ANY()); /* 15.2.2.4.13 */ mrb_define_method(mrb, mod, "attr_writer", mrb_mod_attr_writer, MRB_ARGS_ANY()); /* 15.2.2.4.14 */ mrb_define_method(mrb, mod, "to_s", mrb_mod_to_s, MRB_ARGS_NONE()); -- cgit v1.2.3 From 199a808e36197bd6f893112bbd43f18f2c25345f Mon Sep 17 00:00:00 2001 From: Corey Powell Date: Mon, 13 Jul 2015 09:49:18 -0500 Subject: Bugfix, included_modules would include classes --- src/class.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/class.c b/src/class.c index a13da63ac..5450f3a20 100644 --- a/src/class.c +++ b/src/class.c @@ -1004,7 +1004,9 @@ mrb_mod_included_modules(mrb_state *mrb, mrb_value self) result = mrb_ary_new(mrb); while (c) { if (c->tt == MRB_TT_ICLASS) { - mrb_ary_push(mrb, result, mrb_obj_value(c->c)); + if (c->c->tt == MRB_TT_MODULE) { + mrb_ary_push(mrb, result, mrb_obj_value(c->c)); + } } c = c->super; } -- cgit v1.2.3 From 81a2b3431c63c969f73e6c0eeaa968acbace0e44 Mon Sep 17 00:00:00 2001 From: Corey Powell Date: Mon, 13 Jul 2015 09:49:51 -0500 Subject: included_modules, origin fix Prepended modules would include their origin ICLASS --- src/class.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/class.c b/src/class.c index 5450f3a20..cde5736e0 100644 --- a/src/class.c +++ b/src/class.c @@ -1000,10 +1000,11 @@ mrb_mod_included_modules(mrb_state *mrb, mrb_value self) { mrb_value result; struct RClass *c = mrb_class_ptr(self); + struct RClass *origin = c->origin; result = mrb_ary_new(mrb); while (c) { - if (c->tt == MRB_TT_ICLASS) { + if (c != origin && c->tt == MRB_TT_ICLASS) { if (c->c->tt == MRB_TT_MODULE) { mrb_ary_push(mrb, result, mrb_obj_value(c->c)); } -- cgit v1.2.3 From 8c13e2b7c6e6c1c02771e4f9e5aebda109892503 Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Mon, 13 Jul 2015 23:35:30 +0200 Subject: Set origin when doing kind_of? comparisons --- src/object.c | 1 + test/t/module.rb | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/object.c b/src/object.c index f8f41bfe8..c834ee04f 100644 --- a/src/object.c +++ b/src/object.c @@ -487,6 +487,7 @@ mrb_obj_is_kind_of(mrb_state *mrb, mrb_value obj, struct RClass *c) mrb_raise(mrb, E_TYPE_ERROR, "class or module required"); } + c = c->origin; while (cl) { if (cl == c || cl->mt == c->mt) return TRUE; diff --git a/test/t/module.rb b/test/t/module.rb index 7b4a8fec7..a1996f5f6 100644 --- a/test/t/module.rb +++ b/test/t/module.rb @@ -543,21 +543,21 @@ assert('Module#prepend') do b = labeled_module('b') { include a } c = labeled_module('c') { prepend b } - assert bug6654 do + #assert bug6654 do # the Module#< operator should be used here instead, but we don't have it assert_include(c.ancestors, a) assert_include(c.ancestors, b) - end + #end bug8357 = '[ruby-core:54736] [Bug #8357]' b = labeled_module('b') { prepend a } c = labeled_class('c') { include b } - assert bug8357 do + #assert bug8357 do # the Module#< operator should be used here instead, but we don't have it assert_include(c.ancestors, a) assert_include(c.ancestors, b) - end + #end bug8357 = '[ruby-core:54742] [Bug #8357]' assert_kind_of(b, c.new, bug8357) -- cgit v1.2.3 From a0754986134fbe6b65992b19338c97d596f4ef60 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Tue, 14 Jul 2015 13:22:42 +0900 Subject: mrb_get_args: allow A,H,s,z,a,d specifiers to take ! to accept nil. S,A,H gives nil, s,z,a,d gives NULL for nil argument. ref 02474da --- src/class.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/class.c b/src/class.c index 8a9fdaca6..b5f5f5287 100644 --- a/src/class.c +++ b/src/class.c @@ -435,9 +435,9 @@ to_sym(mrb_state *mrb, mrb_value ss) ---------------------------------------------------------------------------------------------- o: Object [mrb_value] C: class/module [mrb_value] - S: String [mrb_value] when ! follows the value may be nil - A: Array [mrb_value] - H: Hash [mrb_value] + S: String [mrb_value] when ! follows, the value may be nil + A: Array [mrb_value] when ! follows, the value may be nil + H: Hash [mrb_value] when ! follows, the value may be nil s: String [char*,mrb_int] Receive two arguments. z: String [char*] NUL terminated string. a: Array [mrb_value*,mrb_int] Receive two arguments. @@ -527,7 +527,7 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) p = va_arg(ap, mrb_value*); if (*format == '!') { format++; - if (mrb_nil_p(*sp)) { + if (i < argc && mrb_nil_p(*sp)) { *p = *sp++; i++; break; @@ -544,6 +544,14 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) mrb_value *p; p = va_arg(ap, mrb_value*); + if (*format == '!') { + format++; + if (i < argc && mrb_nil_p(*sp)) { + *p = *sp++; + i++; + break; + } + } if (i < argc) { *p = to_ary(mrb, *sp++); i++; @@ -555,6 +563,14 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) mrb_value *p; p = va_arg(ap, mrb_value*); + if (*format == '!') { + format++; + if (i < argc && mrb_nil_p(*sp)) { + *p = *sp++; + i++; + break; + } + } if (i < argc) { *p = to_hash(mrb, *sp++); i++; @@ -569,6 +585,15 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) ps = va_arg(ap, char**); pl = va_arg(ap, mrb_int*); + if (*format == '!') { + format++; + if (i < argc && mrb_nil_p(*sp)) { + *ps = NULL; + *pl = 0; + i++; + break; + } + } if (i < argc) { ss = to_str(mrb, *sp++); *ps = RSTRING_PTR(ss); @@ -583,6 +608,14 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) const char **ps; ps = va_arg(ap, const char**); + if (*format == '!') { + format++; + if (i < argc && mrb_nil_p(*sp)) { + *ps = NULL; + i++; + break; + } + } if (i < argc) { ss = to_str(mrb, *sp++); *ps = mrb_string_value_cstr(mrb, &ss); @@ -599,6 +632,15 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) pb = va_arg(ap, mrb_value**); pl = va_arg(ap, mrb_int*); + if (*format == '!') { + format++; + if (i < argc && mrb_nil_p(*sp)) { + *pb = 0; + *pl = 0; + i++; + break; + } + } if (i < argc) { aa = to_ary(mrb, *sp++); a = mrb_ary_ptr(aa); -- cgit v1.2.3 From 4e7050fe0c56a9403f182d3f79a22a4196d377f0 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Tue, 14 Jul 2015 13:34:21 +0900 Subject: mrb_get_args: allow d! --- src/class.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src') diff --git a/src/class.c b/src/class.c index b5f5f5287..b306cb489 100644 --- a/src/class.c +++ b/src/class.c @@ -726,6 +726,14 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) datap = va_arg(ap, void**); type = va_arg(ap, struct mrb_data_type const*); + if (*format == '!') { + format++; + if (i < argc && mrb_nil_p(*sp)) { + *datap = 0; + i++; + break; + } + } if (i < argc) { *datap = mrb_data_get_ptr(mrb, *sp++, type); ++i; -- cgit v1.2.3 From 53a81da0fafc2adbc791d9f161207faa29bccb4e Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Tue, 14 Jul 2015 13:35:06 +0900 Subject: mrb_get_args: improve function description about ! --- src/class.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/class.c b/src/class.c index b306cb489..1d54c91f4 100644 --- a/src/class.c +++ b/src/class.c @@ -438,9 +438,9 @@ to_sym(mrb_state *mrb, mrb_value ss) S: String [mrb_value] when ! follows, the value may be nil A: Array [mrb_value] when ! follows, the value may be nil H: Hash [mrb_value] when ! follows, the value may be nil - s: String [char*,mrb_int] Receive two arguments. - z: String [char*] NUL terminated string. - a: Array [mrb_value*,mrb_int] Receive two arguments. + s: String [char*,mrb_int] Receive two arguments; s! gives (NULL,0) for nil + z: String [char*] NUL terminated string; z! gives NULL for nil + a: Array [mrb_value*,mrb_int] Receive two arguments; a! gives (NULL,0) for nil f: Float [mrb_float] i: Integer [mrb_int] b: Boolean [mrb_bool] -- cgit v1.2.3 From f0e920baf07e17fd1073566d7e9605f9192d766d Mon Sep 17 00:00:00 2001 From: Corey Powell Date: Tue, 14 Jul 2015 08:55:53 -0500 Subject: Renamed parameters in include_module_at --- src/class.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/class.c b/src/class.c index cde5736e0..14d81495e 100644 --- a/src/class.c +++ b/src/class.c @@ -798,26 +798,26 @@ include_class_new(mrb_state *mrb, struct RClass *m, struct RClass *super) } MRB_API int -include_module_at(mrb_state *mrb, struct RClass *klass, struct RClass *c, struct RClass *module, int search_super) +include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *ins_pos, struct RClass *m, int search_super) { - struct RClass *p, *iclass; - void *klass_mt = klass->origin->mt; + struct RClass *p, *ic; + void *klass_mt = c->origin->mt; - while (module) { + while (m) { int superclass_seen = 0; - if (module->origin != module) + if (m->origin != m) goto skip; - if (klass_mt && klass_mt == module->mt) + if (klass_mt && klass_mt == m->mt) return -1; - p = klass->super; + p = c->super; while(p) { if (p->tt == MRB_TT_ICLASS) { - if (p->mt == module->mt) { + if (p->mt == m->mt) { if (!superclass_seen) { - c = p; // move insert point + ins_pos = p; // move insert point } goto skip; } @@ -828,12 +828,12 @@ include_module_at(mrb_state *mrb, struct RClass *klass, struct RClass *c, struct p = p->super; } - iclass = include_class_new(mrb, module, c->super); - c->super = iclass; - mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)c->super); - c = iclass; + ic = include_class_new(mrb, m, ins_pos->super); + ins_pos->super = ic; + mrb_field_write_barrier(mrb, (struct RBasic*)ins_pos, (struct RBasic*)ins_pos->super); + ins_pos = ic; skip: - module = module->super; + m = m->super; } return 0; } -- cgit v1.2.3 From eb172c28d79b4fdf978e78fc7e929caa855dd29b Mon Sep 17 00:00:00 2001 From: Corey Powell Date: Tue, 14 Jul 2015 09:44:04 -0500 Subject: Applied gc patch to fix ORIGIN ICLASS method table leak Based on the gc patch by ko1 https://github.com/ruby/ruby/commit/5922c954614e5947a548780bb3b894626affe6dd --- include/mruby/class.h | 7 +++++-- include/mruby/object.h | 2 ++ src/class.c | 1 + src/gc.c | 12 ++++++++++-- 4 files changed, 18 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/include/mruby/class.h b/include/mruby/class.h index 60310ae9d..9f2c32bb0 100644 --- a/include/mruby/class.h +++ b/include/mruby/class.h @@ -49,8 +49,11 @@ mrb_class(mrb_state *mrb, mrb_value v) } } -#define MRB_SET_INSTANCE_TT(c, tt) c->flags = ((c->flags & ~0xff) | (char)tt) -#define MRB_INSTANCE_TT(c) (enum mrb_vtype)(c->flags & 0xff) +// TODO: figure out where to put user flags +#define MRB_FLAG_IS_ORIGIN (1 << 20) +#define MRB_FLAG_IS_INSTANCE (0xFF) +#define MRB_SET_INSTANCE_TT(c, tt) c->flags = ((c->flags & ~MRB_FLAG_IS_INSTANCE) | (char)tt) +#define MRB_INSTANCE_TT(c) (enum mrb_vtype)(c->flags & MRB_FLAG_IS_INSTANCE) MRB_API struct RClass* mrb_define_class_id(mrb_state*, mrb_sym, struct RClass*); MRB_API struct RClass* mrb_define_module_id(mrb_state*, mrb_sym); diff --git a/include/mruby/object.h b/include/mruby/object.h index fe55620fe..6633a23e8 100644 --- a/include/mruby/object.h +++ b/include/mruby/object.h @@ -14,6 +14,8 @@ struct RClass *c;\ struct RBasic *gcnext +#define MRB_FLAG_TEST(obj, flag) ((obj)->flags & flag) + /* white: 011, black: 100, gray: 000 */ #define MRB_GC_GRAY 0 #define MRB_GC_WHITE_A 1 diff --git a/src/class.c b/src/class.c index 14d81495e..58742299b 100644 --- a/src/class.c +++ b/src/class.c @@ -856,6 +856,7 @@ mrb_prepend_module(mrb_state *mrb, struct RClass *c, struct RClass *m) origin = c->origin; if (origin == c) { origin = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, c); + origin->flags |= MRB_FLAG_IS_ORIGIN; origin->origin = origin; origin->super = c->super; c->super = origin; diff --git a/src/gc.c b/src/gc.c index 8bd8243f1..15e1bd423 100644 --- a/src/gc.c +++ b/src/gc.c @@ -498,7 +498,12 @@ gc_mark_children(mrb_state *mrb, struct RBasic *obj) mrb_gc_mark(mrb, (struct RBasic*)obj->c); switch (obj->tt) { case MRB_TT_ICLASS: - mrb_gc_mark(mrb, (struct RBasic*)((struct RClass*)obj)->super); + { + struct RClass *c = (struct RClass*)obj; + if (MRB_FLAG_TEST(c, MRB_FLAG_IS_ORIGIN)) + mrb_gc_mark_mt(mrb, c); + mrb_gc_mark(mrb, (struct RBasic*)((struct RClass*)obj)->super); + } break; case MRB_TT_CLASS: @@ -624,7 +629,10 @@ obj_free(mrb_state *mrb, struct RBasic *obj) mrb_gc_free_mt(mrb, (struct RClass*)obj); mrb_gc_free_iv(mrb, (struct RObject*)obj); break; - + case MRB_TT_ICLASS: + if (MRB_FLAG_TEST(obj, MRB_FLAG_IS_ORIGIN)) + mrb_gc_free_mt(mrb, (struct RClass*)obj); + break; case MRB_TT_ENV: { struct REnv *e = (struct REnv*)obj; -- cgit v1.2.3 From dbbf2e26231c04d76badb1889627869d1f9361bb Mon Sep 17 00:00:00 2001 From: Corey Powell Date: Tue, 14 Jul 2015 14:12:23 -0500 Subject: Remove non-applicable "hack" comment --- src/class.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'src') diff --git a/src/class.c b/src/class.c index 58742299b..f342ded20 100644 --- a/src/class.c +++ b/src/class.c @@ -1020,8 +1020,6 @@ static mrb_value mrb_mod_initialize(mrb_state *mrb, mrb_value mod) { mrb_value b; - - /* hack, fix missing module->origin */ struct RClass *m = mrb_class_ptr(mod); boot_initmod(mrb, m); // bootstrap a newly initialized module mrb_get_args(mrb, "|&", &b); -- cgit v1.2.3 From 938ed044f4e053c1832be3868230d80ccba190e8 Mon Sep 17 00:00:00 2001 From: cremno Date: Thu, 16 Jul 2015 03:56:31 +0200 Subject: use mrb_str_cat_str() instead of mrb_str_append() If the argument is always a string, then mrb_str_cat_str() can be directly called instead of indirectly by mrb_str_append(). mrb_any_to_s(), mrb_obj_as_string(), mrb_inspect() always return a string. --- src/class.c | 4 ++-- src/error.c | 4 ++-- src/range.c | 4 ++-- src/variable.c | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/class.c b/src/class.c index 1d54c91f4..33fb61211 100644 --- a/src/class.c +++ b/src/class.c @@ -1608,10 +1608,10 @@ mrb_mod_to_s(mrb_state *mrb, mrb_value klass) case MRB_TT_CLASS: case MRB_TT_MODULE: case MRB_TT_SCLASS: - mrb_str_append(mrb, str, mrb_inspect(mrb, v)); + mrb_str_cat_str(mrb, str, mrb_inspect(mrb, v)); break; default: - mrb_str_append(mrb, str, mrb_any_to_s(mrb, v)); + mrb_str_cat_str(mrb, str, mrb_any_to_s(mrb, v)); break; } return mrb_str_cat_lit(mrb, str, ">"); diff --git a/src/error.c b/src/error.c index 20c63bd43..359e5737b 100644 --- a/src/error.c +++ b/src/error.c @@ -152,7 +152,7 @@ exc_inspect(mrb_state *mrb, mrb_value exc) mrb_str_append(mrb, str, line); mrb_str_cat_lit(mrb, str, ": "); if (append_mesg) { - mrb_str_append(mrb, str, mesg); + mrb_str_cat_str(mrb, str, mesg); mrb_str_cat_lit(mrb, str, " ("); } mrb_str_cat_cstr(mrb, str, mrb_obj_classname(mrb, exc)); @@ -165,7 +165,7 @@ exc_inspect(mrb_state *mrb, mrb_value exc) str = mrb_str_new_cstr(mrb, cname); mrb_str_cat_lit(mrb, str, ": "); if (append_mesg) { - mrb_str_append(mrb, str, mesg); + mrb_str_cat_str(mrb, str, mesg); } else { mrb_str_cat_cstr(mrb, str, cname); diff --git a/src/range.c b/src/range.c index b427dc1b7..b58b6a1c8 100644 --- a/src/range.c +++ b/src/range.c @@ -290,7 +290,7 @@ range_to_s(mrb_state *mrb, mrb_value range) str2 = mrb_obj_as_string(mrb, r->edges->end); str = mrb_str_dup(mrb, str); mrb_str_cat(mrb, str, "...", r->excl ? 3 : 2); - mrb_str_append(mrb, str, str2); + mrb_str_cat_str(mrb, str, str2); return str; } @@ -315,7 +315,7 @@ range_inspect(mrb_state *mrb, mrb_value range) str2 = mrb_inspect(mrb, r->edges->end); str = mrb_str_dup(mrb, str); mrb_str_cat(mrb, str, "...", r->excl ? 3 : 2); - mrb_str_append(mrb, str, str2); + mrb_str_cat_str(mrb, str, str2); return str; } diff --git a/src/variable.c b/src/variable.c index 1b2ad56a7..efe6fad12 100644 --- a/src/variable.c +++ b/src/variable.c @@ -609,7 +609,7 @@ inspect_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p) else { ins = mrb_inspect(mrb, v); } - mrb_str_append(mrb, str, ins); + mrb_str_cat_str(mrb, str, ins); return 0; } -- cgit v1.2.3 From 0f284091d161dbca6e07de85896cbdc9abd2de6b Mon Sep 17 00:00:00 2001 From: cremno Date: Thu, 16 Jul 2015 04:58:21 +0200 Subject: delete mrb_free()-related non-NULL checks No need to optimize since a program only exits once and errors are rare. Also the mruby source code doesn't have these kind of checks elsewhere. The ones in {Time,Random}#initialize are kept because there it actually matters since initialization always happens and re-initialization is unlikely. --- mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c | 3 +-- mrbgems/mruby-bin-mruby/tools/mruby/mruby.c | 5 ++--- src/dump.c | 8 ++------ 3 files changed, 5 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c b/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c index f27f87a5d..301dde1c6 100644 --- a/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c +++ b/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c @@ -161,8 +161,7 @@ parse_args(mrb_state *mrb, int argc, char **argv, struct mrbc_args *args) static void cleanup(mrb_state *mrb, struct mrbc_args *args) { - if (args->outfile) - mrb_free(mrb, (void*)args->outfile); + mrb_free(mrb, (void*)args->outfile); mrb_close(mrb); } diff --git a/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c b/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c index 141ea151b..5ca744388 100644 --- a/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c +++ b/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c @@ -159,10 +159,9 @@ cleanup(mrb_state *mrb, struct _args *args) { if (args->rfp && args->rfp != stdin) fclose(args->rfp); - if (args->cmdline && !args->fname) + if (!args->fname) mrb_free(mrb, args->cmdline); - if (args->argv) - mrb_free(mrb, args->argv); + mrb_free(mrb, args->argv); mrb_close(mrb); } diff --git a/src/dump.c b/src/dump.c index 2f2e5edcb..462e036b4 100644 --- a/src/dump.c +++ b/src/dump.c @@ -978,12 +978,8 @@ error_exit: mrb_free(mrb, *bin); *bin = NULL; } - if (lv_syms) { - mrb_free(mrb, lv_syms); - } - if (filenames) { - mrb_free(mrb, filenames); - } + mrb_free(mrb, lv_syms); + mrb_free(mrb, filenames); return result; } -- cgit v1.2.3 From 67e187f0f883789131c596ccc56980c32471bbbf Mon Sep 17 00:00:00 2001 From: takkaw Date: Thu, 16 Jul 2015 18:40:07 +0900 Subject: 64bit Cygwin also doesn't have frexpl(3) --- src/fmt_fp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/fmt_fp.c b/src/fmt_fp.c index b467435a3..b27ebd6e9 100644 --- a/src/fmt_fp.c +++ b/src/fmt_fp.c @@ -90,7 +90,7 @@ fmt_u(uint32_t x, char *s) typedef char compiler_defines_long_double_incorrectly[9-(int)sizeof(long double)]; #endif -#if ((defined(__CYGWIN32__) || defined(__NetBSD__) || defined(mips)) && !defined(__linux__)) || defined(__android__) +#if ((defined(__CYGWIN__) || defined(__NetBSD__) || defined(mips)) && !defined(__linux__)) || defined(__android__) #undef frexpl #define frexpl frexp #endif -- cgit v1.2.3 From ae1ece72282fdfa558810e8422bb690bbc6d2193 Mon Sep 17 00:00:00 2001 From: Corey Powell Date: Thu, 16 Jul 2015 15:25:14 -0500 Subject: Make include_module_at static Since I can't forsee any reason to use it directly inplace of using prepend/include --- src/class.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/class.c b/src/class.c index f342ded20..90c463579 100644 --- a/src/class.c +++ b/src/class.c @@ -797,7 +797,7 @@ include_class_new(mrb_state *mrb, struct RClass *m, struct RClass *super) return ic; } -MRB_API int +static int include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *ins_pos, struct RClass *m, int search_super) { struct RClass *p, *ic; -- cgit v1.2.3 From 89ebb0c4f15db5ed0e5e6d0715bfcf4fb0a1beac Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Thu, 30 Jul 2015 16:45:31 +0900 Subject: vm: execute ensure at the top of the fiber; fix #2903 --- src/vm.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/vm.c b/src/vm.c index 22ea177e0..765512a34 100644 --- a/src/vm.c +++ b/src/vm.c @@ -1497,6 +1497,9 @@ RETRY_TRY_BLOCK: MRB_THROW(prev_jmp); } if (ci == mrb->c->cibase) { + while (eidx > 0) { + ecall(mrb, --eidx); + } if (ci->ridx == 0) { if (mrb->c == mrb->root_c) { regs = mrb->c->stack = mrb->c->stbase; -- cgit v1.2.3 From f0040b537136e8f36ff3a6de2508ca8c7c8974a6 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Thu, 30 Jul 2015 16:46:31 +0900 Subject: vm: execute ensure without exception at the top of the fiber; fix #2904 --- src/vm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/vm.c b/src/vm.c index 765512a34..025b487bc 100644 --- a/src/vm.c +++ b/src/vm.c @@ -1032,7 +1032,7 @@ RETRY_TRY_BLOCK: mrb_callinfo *ci = mrb->c->ci; int n, eidx = ci->eidx; - for (n=0; n ci[-1].eidx; n++) { + for (n=0; nc->cibase || eidx > ci[-1].eidx); n++) { ecall(mrb, --eidx); ARENA_RESTORE(mrb, ai); } -- cgit v1.2.3 From 0c7d29dff68b4d85fbbc9c6f8c0328ddb303f000 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Fri, 7 Aug 2015 15:30:52 +0900 Subject: FIXABLE() may work wrong on MRB_INT64; fix #2909 --- src/numeric.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/numeric.c b/src/numeric.c index b9aef51d9..14c0b76a6 100644 --- a/src/numeric.c +++ b/src/numeric.c @@ -55,7 +55,8 @@ num_pow(mrb_state *mrb, mrb_value x) mrb_get_args(mrb, "o", &y); yv = mrb_to_flo(mrb, y); d = pow(mrb_to_flo(mrb, x), yv); - if (mrb_fixnum_p(x) && mrb_fixnum_p(y) && FIXABLE(d) && yv > 0) + if (mrb_fixnum_p(x) && mrb_fixnum_p(y) && FIXABLE(d) && yv > 0 && + (d < 0 || (d > 0 && (mrb_int)d > 0))) return mrb_fixnum_value((mrb_int)d); return mrb_float_value(mrb, d); } -- cgit v1.2.3 From 5af770cd59f6edb6f111f497fef4df3fe956cc62 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Mon, 10 Aug 2015 11:41:01 +0900 Subject: prevent out-of-bounds ensure clause access; fix #2910 --- src/vm.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/vm.c b/src/vm.c index 025b487bc..00636a870 100644 --- a/src/vm.c +++ b/src/vm.c @@ -266,6 +266,7 @@ ecall(mrb_state *mrb, int i) mrb_value *self = mrb->c->stack; struct RObject *exc; + if (i<0) return; p = mrb->c->ensure[i]; if (!p) return; if (mrb->c->ci->eidx > i) -- cgit v1.2.3 From 96c948d812f06c1ad1024f6db5089e47dbd58d23 Mon Sep 17 00:00:00 2001 From: "go.kikuta" Date: Tue, 11 Aug 2015 18:02:12 +0900 Subject: Remove non-need tmp value --- src/etc.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/etc.c b/src/etc.c index a8a21e740..f5a502795 100644 --- a/src/etc.c +++ b/src/etc.c @@ -67,17 +67,15 @@ mrb_data_get_ptr(mrb_state *mrb, mrb_value obj, const mrb_data_type *type) MRB_API mrb_sym mrb_obj_to_sym(mrb_state *mrb, mrb_value name) { - mrb_value tmp; mrb_sym id; switch (mrb_type(name)) { default: - tmp = mrb_check_string_type(mrb, name); - if (mrb_nil_p(tmp)) { - tmp = mrb_inspect(mrb, name); - mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a symbol", tmp); + name = mrb_check_string_type(mrb, name); + if (mrb_nil_p(name)) { + name = mrb_inspect(mrb, name); + mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a symbol", name); } - name = tmp; /* fall through */ case MRB_TT_STRING: name = mrb_str_intern(mrb, name); -- cgit v1.2.3 From c7fad5aa33f77d1869d9b879c9f8aeb5e688d64c Mon Sep 17 00:00:00 2001 From: "go.kikuta" Date: Wed, 12 Aug 2015 10:20:55 +0900 Subject: Remove duplicated RARRAY_LEN check --- src/array.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/array.c b/src/array.c index 0a99040c0..2622ee528 100644 --- a/src/array.c +++ b/src/array.c @@ -19,7 +19,6 @@ static inline mrb_value ary_elt(mrb_value ary, mrb_int offset) { - if (RARRAY_LEN(ary) == 0) return mrb_nil_value(); if (offset < 0 || RARRAY_LEN(ary) <= offset) { return mrb_nil_value(); } -- cgit v1.2.3 From ad50a6c4103b6a95e25ed7eaaf99b79a220d36cf Mon Sep 17 00:00:00 2001 From: cremno Date: Wed, 29 Jul 2015 21:54:06 +0200 Subject: fix irep float dump format string for MRB_USE_FLOAT IEC 60559 single format has 6 to 9 significant decimal digits precision. However the printf conversion specifier e (and E, of course) already writes 1 digit - the one before the decimal point - and precision specifies the number of digits to write after the decimal point. --- src/dump.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/dump.c b/src/dump.c index 462e036b4..734f38043 100644 --- a/src/dump.c +++ b/src/dump.c @@ -16,7 +16,7 @@ #define FLAG_BYTEORDER_NONATIVE 0 #ifdef MRB_USE_FLOAT -#define MRB_FLOAT_FMT "%.9e" +#define MRB_FLOAT_FMT "%.8e" #else #define MRB_FLOAT_FMT "%.16e" #endif -- cgit v1.2.3 From 1a45447b8b66159a4a7d0348c4a01a175b3778fd Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Thu, 27 Aug 2015 17:54:30 +0900 Subject: add String#freeze to the core --- include/mruby/string.h | 11 ++++++++--- src/string.c | 21 +++++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/include/mruby/string.h b/include/mruby/string.h index 5228dcbca..c4b31216e 100644 --- a/include/mruby/string.h +++ b/include/mruby/string.h @@ -59,6 +59,10 @@ struct RString { #define RSTR_SET_NOFREE_FLAG(s) ((s)->flags |= MRB_STR_NOFREE) #define RSTR_UNSET_NOFREE_FLAG(s) ((s)->flags &= ~MRB_STR_NOFREE) +#define RSTR_FROZEN_P(s) ((s)->flags & MRB_STR_FROZEN) +#define RSTR_SET_FROZEN_FLAG(s) ((s)->flags |= MRB_STR_FROZEN) +#define RSTR_UNSET_FROZEN_FLAG(s) ((s)->flags &= ~MRB_STR_FROZEN) + #define mrb_str_ptr(s) ((struct RString*)(mrb_ptr(s))) #define RSTRING(s) mrb_str_ptr(s) #define RSTRING_PTR(s) RSTR_PTR(RSTRING(s)) @@ -70,9 +74,10 @@ mrb_int mrb_str_strlen(mrb_state*, struct RString*); #define MRB_STR_SHARED 1 #define MRB_STR_NOFREE 2 -#define MRB_STR_EMBED 4 -#define MRB_STR_EMBED_LEN_MASK 0xf8 -#define MRB_STR_EMBED_LEN_SHIFT 3 +#define MRB_STR_FROZEN 4 +#define MRB_STR_EMBED 8 +#define MRB_STR_EMBED_LEN_MASK 0x1f0 +#define MRB_STR_EMBED_LEN_SHIFT 4 void mrb_gc_free_str(mrb_state*, struct RString*); MRB_API void mrb_str_modify(mrb_state*, struct RString*); diff --git a/src/string.c b/src/string.c index 45ba38c9d..e5f446bde 100644 --- a/src/string.c +++ b/src/string.c @@ -75,9 +75,18 @@ str_decref(mrb_state *mrb, mrb_shared_string *shared) } } +static void +check_frozen(mrb_state *mrb, struct RString *s) +{ + if (RSTR_FROZEN_P(s)) { + mrb_raise(mrb, E_RUNTIME_ERROR, "can't modify frozen string"); + } +} + MRB_API void mrb_str_modify(mrb_state *mrb, struct RString *s) { + check_frozen(mrb, s); if (RSTR_SHARED_P(s)) { mrb_shared_string *shared = s->as.heap.aux.shared; @@ -119,6 +128,15 @@ mrb_str_modify(mrb_state *mrb, struct RString *s) } } +static mrb_value +mrb_str_freeze(mrb_state *mrb, mrb_value str) +{ + struct RString *s = mrb_str_ptr(str); + + RSTR_SET_FROZEN_FLAG(s); + return str; +} + MRB_API mrb_value mrb_str_resize(mrb_state *mrb, mrb_value str, mrb_int len) { @@ -1345,6 +1363,7 @@ str_replace(mrb_state *mrb, struct RString *s1, struct RString *s2) { long len; + check_frozen(mrb, s1); len = RSTR_LEN(s2); if (RSTR_SHARED_P(s1)) { str_decref(mrb, s1->as.heap.aux.shared); @@ -2514,4 +2533,6 @@ mrb_init_string(mrb_state *mrb) mrb_define_method(mrb, s, "upcase!", mrb_str_upcase_bang, MRB_ARGS_NONE()); /* 15.2.10.5.43 */ mrb_define_method(mrb, s, "inspect", mrb_str_inspect, MRB_ARGS_NONE()); /* 15.2.10.5.46(x) */ mrb_define_method(mrb, s, "bytes", mrb_str_bytes, MRB_ARGS_NONE()); + + mrb_define_method(mrb, s, "freeze", mrb_str_freeze, MRB_ARGS_NONE()); } -- cgit v1.2.3 From e9b4cb11cf7b6b8b45e5a0a03e6eda3f12a93407 Mon Sep 17 00:00:00 2001 From: Jun Hiroe Date: Mon, 31 Aug 2015 20:32:30 +0900 Subject: Add a global const variable MRUBY_RELEASE_NO --- src/version.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/version.c b/src/version.c index 7aac44d62..fc3b2fc7a 100644 --- a/src/version.c +++ b/src/version.c @@ -7,6 +7,7 @@ mrb_init_version(mrb_state* mrb) mrb_define_global_const(mrb, "RUBY_VERSION", mrb_str_new_lit(mrb, MRUBY_RUBY_VERSION)); mrb_define_global_const(mrb, "RUBY_ENGINE", mrb_str_new_lit(mrb, MRUBY_RUBY_ENGINE)); mrb_define_global_const(mrb, "MRUBY_VERSION", mrb_str_new_lit(mrb, MRUBY_VERSION)); + mrb_define_global_const(mrb, "MRUBY_RELEASE_NO", mrb_fixnum_value(MRUBY_RELEASE_NO)); mrb_define_global_const(mrb, "MRUBY_RELEASE_DATE", mrb_str_new_lit(mrb, MRUBY_RELEASE_DATE)); mrb_define_global_const(mrb, "MRUBY_DESCRIPTION", mrb_str_new_lit(mrb, MRUBY_DESCRIPTION)); mrb_define_global_const(mrb, "MRUBY_COPYRIGHT", mrb_str_new_lit(mrb, MRUBY_COPYRIGHT)); -- cgit v1.2.3 From 14f0e4a4e4657fe0dc6512cf735c9c75201bd406 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Wed, 2 Sep 2015 15:09:50 +0900 Subject: update string.h.md; ref #2931 --- doc/api/mruby/string.h.md | 2 +- src/string.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/doc/api/mruby/string.h.md b/doc/api/mruby/string.h.md index 1268792e0..7bf94df5b 100644 --- a/doc/api/mruby/string.h.md +++ b/doc/api/mruby/string.h.md @@ -69,7 +69,7 @@ Returns a concated string comprised of a Ruby string and a C string. Returns a concated string comprised of a Ruby string and a C string(A shorter alternative to mrb_str_cat). ### mrb_str_append ```C - mrb_value mrb_str_append(mrb_state *mrb, mrb_value str, mrb_value str2); + mrb_value mrb_str_append(mrb_state *mrb, mrb_value str1, mrb_value str2); ``` Adds str2 to the end of str1. ### mrb_str_cmp diff --git a/src/string.c b/src/string.c index e5f446bde..73ef341bb 100644 --- a/src/string.c +++ b/src/string.c @@ -2385,10 +2385,10 @@ mrb_str_cat_str(mrb_state *mrb, mrb_value str, mrb_value str2) } MRB_API mrb_value -mrb_str_append(mrb_state *mrb, mrb_value str, mrb_value str2) +mrb_str_append(mrb_state *mrb, mrb_value str1, mrb_value str2) { str2 = mrb_str_to_str(mrb, str2); - return mrb_str_cat_str(mrb, str, str2); + return mrb_str_cat_str(mrb, str1, str2); } #define CHAR_ESC_LEN 13 /* sizeof(\x{ hex of 32bit unsigned int } \0) */ -- cgit v1.2.3 From 6ddd79fd0ebfd88a9b36be08d509e665a9322567 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Wed, 2 Sep 2015 21:46:51 +0900 Subject: ensure must not be called before rescue; fix #2933 --- src/vm.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'src') diff --git a/src/vm.c b/src/vm.c index 00636a870..8419931d0 100644 --- a/src/vm.c +++ b/src/vm.c @@ -1486,9 +1486,6 @@ RETRY_TRY_BLOCK: if (ci->ridx == 0) goto L_STOP; goto L_RESCUE; } - while (eidx > ci[-1].eidx) { - ecall(mrb, --eidx); - } while (ci[0].ridx == ci[-1].ridx) { cipop(mrb); ci = mrb->c->ci; -- cgit v1.2.3 From 3a462fe4687fa2a52d2c9c20d10ae46901292b99 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Wed, 2 Sep 2015 23:41:51 +0900 Subject: Integer << and >> to use Float instead of raising RangeError --- src/numeric.c | 12 +++++++----- test/t/integer.rb | 10 ---------- 2 files changed, 7 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/numeric.c b/src/numeric.c index 14c0b76a6..1a3c903f0 100644 --- a/src/numeric.c +++ b/src/numeric.c @@ -820,11 +820,13 @@ fix_xor(mrb_state *mrb, mrb_value x) static mrb_value lshift(mrb_state *mrb, mrb_int val, mrb_int width) { - mrb_assert(width >= 0); + mrb_assert(width > 0); if (width > NUMERIC_SHIFT_WIDTH_MAX) { - mrb_raisef(mrb, E_RANGE_ERROR, "width(%S) > (%S:MRB_INT_BIT-1)", - mrb_fixnum_value(width), - mrb_fixnum_value(NUMERIC_SHIFT_WIDTH_MAX)); + mrb_float f = (mrb_float)val; + while (width--) { + f *= 2; + } + return mrb_float_value(mrb, f); } return mrb_fixnum_value(val << width); } @@ -832,7 +834,7 @@ lshift(mrb_state *mrb, mrb_int val, mrb_int width) static mrb_value rshift(mrb_int val, mrb_int width) { - mrb_assert(width >= 0); + mrb_assert(width > 0); if (width >= NUMERIC_SHIFT_WIDTH_MAX) { if (val < 0) { return mrb_fixnum_value(-1); diff --git a/test/t/integer.rb b/test/t/integer.rb index 6b8cc308d..be3c13db2 100644 --- a/test/t/integer.rb +++ b/test/t/integer.rb @@ -147,11 +147,6 @@ assert('Integer#<<', '15.2.8.3.12') do # Left Shift by a negative is Right Shift assert_equal 23, 46 << -1 - - # Raise when shift is too large - assert_raise(RangeError) do - 2 << 128 - end end assert('Integer#>>', '15.2.8.3.13') do @@ -165,11 +160,6 @@ assert('Integer#>>', '15.2.8.3.13') do # Don't raise on large Right Shift assert_equal 0, 23 >> 128 - - # Raise when shift is too large - assert_raise(RangeError) do - 2 >> -128 - end end assert('Integer#ceil', '15.2.8.3.14') do -- cgit v1.2.3 From e35c3aff83d400dfe27fe105b7e282ac81b1197a Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Thu, 3 Sep 2015 00:14:37 +0900 Subject: unsigned long may be smaller than mrb_int; use uint64_t instead; fix #2935 --- mrbgems/mruby-sprintf/src/sprintf.c | 2 +- src/string.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/mrbgems/mruby-sprintf/src/sprintf.c b/mrbgems/mruby-sprintf/src/sprintf.c index de216f69f..81b48b10d 100644 --- a/mrbgems/mruby-sprintf/src/sprintf.c +++ b/mrbgems/mruby-sprintf/src/sprintf.c @@ -73,7 +73,7 @@ mrb_fix2binstr(mrb_state *mrb, mrb_value x, int base) { char buf[64], *b = buf + sizeof buf; mrb_int num = mrb_fixnum(x); - unsigned long val = (unsigned long)num; + uint64_t val = (uint64_t)num; char d; if (base != 2) { diff --git a/src/string.c b/src/string.c index 73ef341bb..08caf3bae 100644 --- a/src/string.c +++ b/src/string.c @@ -1863,7 +1863,7 @@ mrb_cstr_to_inum(mrb_state *mrb, const char *str, int base, int badcheck) const char *p; char sign = 1; int c, uscore; - unsigned long n = 0; + uint64_t n = 0; mrb_int val; #define conv_digit(c) \ @@ -1983,9 +1983,9 @@ mrb_cstr_to_inum(mrb_state *mrb, const char *str, int base, int badcheck) } n *= base; n += c; - } - if (n > MRB_INT_MAX) { - mrb_raisef(mrb, E_ARGUMENT_ERROR, "string (%S) too big for integer", mrb_str_new_cstr(mrb, str)); + if (n > MRB_INT_MAX) { + mrb_raisef(mrb, E_ARGUMENT_ERROR, "string (%S) too big for integer", mrb_str_new_cstr(mrb, str)); + } } val = n; if (badcheck) { -- cgit v1.2.3 From 7b5f8b07285eeb9900ef1a85cb2c764fbbd34461 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Thu, 3 Sep 2015 01:39:17 +0900 Subject: remove trailing spaces from bc9c47d5 --- build_config.rb | 3 ++- include/mruby/dump.h | 4 ++-- src/load.c | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/build_config.rb b/build_config.rb index e1178d6b2..34b92637c 100644 --- a/build_config.rb +++ b/build_config.rb @@ -21,6 +21,7 @@ MRuby::Build.new do |conf| # include the default GEMs conf.gembox 'default' + conf.gem :core => 'mruby-eval' # C compiler settings # conf.cc do |cc| @@ -105,7 +106,7 @@ MRuby::Build.new('host-debug') do |conf| conf.gem :core => "mruby-bin-debugger" # bintest - # conf.enable_bintest + conf.enable_bintest end MRuby::Build.new('test') do |conf| diff --git a/include/mruby/dump.h b/include/mruby/dump.h index 45774d872..4cee3c0ac 100644 --- a/include/mruby/dump.h +++ b/include/mruby/dump.h @@ -17,8 +17,8 @@ extern "C" { #define DUMP_DEBUG_INFO 1 #define DUMP_ENDIAN_BIG 2 #define DUMP_ENDIAN_LIL 4 -#define DUMP_ENDIAN_NAT 6 -#define DUMP_ENDIAN_MASK 6 +#define DUMP_ENDIAN_NAT 6 +#define DUMP_ENDIAN_MASK 6 int mrb_dump_irep(mrb_state *mrb, mrb_irep *irep, uint8_t flags, uint8_t **bin, size_t *bin_size); #ifdef ENABLE_STDIO diff --git a/src/load.c b/src/load.c index a9f1641bf..36fae9aee 100644 --- a/src/load.c +++ b/src/load.c @@ -529,7 +529,7 @@ read_binary_header(const uint8_t *bin, size_t *bin_size, uint16_t *crc, uint8_t else if (memcmp(header->binary_ident, RITE_BINARY_IDENT_LIL, sizeof(header->binary_ident)) == 0) { if (bigendian_p()) *flags |= FLAG_BYTEORDER_LIL; - else + else *flags |= FLAG_BYTEORDER_NATIVE; } else { -- cgit v1.2.3 From 2550edd570f1d7485e862ce11ceb50ea59dee3c5 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Sat, 5 Sep 2015 02:01:02 +0900 Subject: remove `origin` member to implement prepend from struct RClass; ref #2885 instead origin is saved in ICLASS with MRB_FLAG_IS_ORIGIN set. --- include/mruby/class.h | 10 +++++++++- src/class.c | 44 ++++++++++++++++++++++++-------------------- src/kernel.c | 29 +++++++++++++++-------------- src/object.c | 2 +- 4 files changed, 49 insertions(+), 36 deletions(-) (limited to 'src') diff --git a/include/mruby/class.h b/include/mruby/class.h index 80a0cbe35..85f3e12c6 100644 --- a/include/mruby/class.h +++ b/include/mruby/class.h @@ -16,7 +16,6 @@ struct RClass { struct iv_tbl *iv; struct kh_mt *mt; struct RClass *super; - struct RClass *origin; }; #define mrb_class_ptr(v) ((struct RClass*)(mrb_ptr(v))) @@ -50,7 +49,16 @@ mrb_class(mrb_state *mrb, mrb_value v) } // TODO: figure out where to put user flags +#define MRB_FLAG_IS_PREPENDED (1 << 19) #define MRB_FLAG_IS_ORIGIN (1 << 20) +#define MRB_CLASS_ORIGIN(c) do {\ + if (c->flags & MRB_FLAG_IS_PREPENDED) {\ + c = c->super;\ + while (!(c->flags & MRB_FLAG_IS_ORIGIN)) {\ + c = c->super;\ + }\ + }\ +} while (0) #define MRB_INSTANCE_TT_MASK (0xFF) #define MRB_SET_INSTANCE_TT(c, tt) c->flags = ((c->flags & ~MRB_INSTANCE_TT_MASK) | (char)tt) #define MRB_INSTANCE_TT(c) (enum mrb_vtype)(c->flags & MRB_INSTANCE_TT_MASK) diff --git a/src/class.c b/src/class.c index 462ab40b5..c3c3e0b8f 100644 --- a/src/class.c +++ b/src/class.c @@ -76,7 +76,6 @@ prepare_singleton_class(mrb_state *mrb, struct RBasic *o) if (o->c->tt == MRB_TT_SCLASS) return; sc = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_SCLASS, mrb->class_class); - sc->origin = sc; sc->mt = kh_init(mt, mrb); sc->iv = 0; if (o->tt == MRB_TT_CLASS) { @@ -188,6 +187,13 @@ mrb_define_module_under(mrb_state *mrb, struct RClass *outer, const char *name) return c; } +static struct RClass* +find_origin(struct RClass *c) +{ + MRB_CLASS_ORIGIN(c); + return c; +} + static struct RClass* define_class(mrb_state *mrb, mrb_sym name, struct RClass *super, struct RClass *outer) { @@ -195,7 +201,7 @@ define_class(mrb_state *mrb, mrb_sym name, struct RClass *super, struct RClass * if (mrb_const_defined_at(mrb, mrb_obj_value(outer), name)) { c = class_from_sym(mrb, outer, name); - c = c->origin; + MRB_CLASS_ORIGIN(c); if (super && mrb_class_real(c->super) != super) { mrb_raisef(mrb, E_TYPE_ERROR, "superclass mismatch for Class %S (%S not %S)", mrb_sym2str(mrb, name), @@ -327,7 +333,8 @@ mrb_define_method_raw(mrb_state *mrb, struct RClass *c, mrb_sym mid, struct RPro { khash_t(mt) *h; khiter_t k; - h = c->origin->mt; + MRB_CLASS_ORIGIN(c); + h = c->mt; if (!h) h = c->mt = kh_init(mt, mrb); k = kh_put(mt, mrb, h, mid); @@ -809,7 +816,6 @@ boot_defclass(mrb_state *mrb, struct RClass *super) struct RClass *c; c = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_CLASS, mrb->class_class); - c->origin = c; if (super) { c->super = super; mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)super); @@ -824,7 +830,6 @@ boot_defclass(mrb_state *mrb, struct RClass *super) static void boot_initmod(mrb_state *mrb, struct RClass *mod) { - mod->origin = mod; mod->mt = kh_init(mt, mrb); } @@ -835,9 +840,9 @@ include_class_new(mrb_state *mrb, struct RClass *m, struct RClass *super) if (m->tt == MRB_TT_ICLASS) { m = m->c; } - ic->origin = ic; + MRB_CLASS_ORIGIN(m); ic->iv = m->iv; - ic->mt = m->origin->mt; + ic->mt = m->mt; ic->super = super; if (m->tt == MRB_TT_ICLASS) { ic->c = m->c; @@ -851,12 +856,12 @@ static int include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *ins_pos, struct RClass *m, int search_super) { struct RClass *p, *ic; - void *klass_mt = c->origin->mt; + void *klass_mt = find_origin(c)->mt; while (m) { int superclass_seen = 0; - if (m->origin != m) + if (m->flags & MRB_FLAG_IS_PREPENDED) goto skip; if (klass_mt && klass_mt == m->mt) @@ -891,7 +896,7 @@ include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *ins_pos, stru MRB_API void mrb_include_module(mrb_state *mrb, struct RClass *c, struct RClass *m) { - int changed = include_module_at(mrb, c, c->origin, m, 1); + int changed = include_module_at(mrb, c, find_origin(c), m, 1); if (changed < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic include detected"); } @@ -903,17 +908,15 @@ mrb_prepend_module(mrb_state *mrb, struct RClass *c, struct RClass *m) struct RClass *origin; int changed = 0; - origin = c->origin; - if (origin == c) { + if (!(c->flags & MRB_FLAG_IS_PREPENDED)) { origin = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, c); origin->flags |= MRB_FLAG_IS_ORIGIN; - origin->origin = origin; origin->super = c->super; c->super = origin; - c->origin = origin; origin->mt = c->mt; c->mt = kh_init(mt, mrb); - mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)c->origin); + mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)origin); + c->flags |= MRB_FLAG_IS_PREPENDED; } changed = include_module_at(mrb, c, c, m, 0); if (changed < 0) { @@ -1026,7 +1029,7 @@ mrb_mod_ancestors(mrb_state *mrb, mrb_value self) if (c->tt == MRB_TT_ICLASS) { mrb_ary_push(mrb, result, mrb_obj_value(c->c)); } - else if (c->origin == c) { + else if (!(c->flags & MRB_FLAG_IS_PREPENDED)) { mrb_ary_push(mrb, result, mrb_obj_value(c)); } c = c->super; @@ -1051,8 +1054,9 @@ mrb_mod_included_modules(mrb_state *mrb, mrb_value self) { mrb_value result; struct RClass *c = mrb_class_ptr(self); - struct RClass *origin = c->origin; + struct RClass *origin = c; + MRB_CLASS_ORIGIN(origin); result = mrb_ary_new(mrb); while (c) { if (c != origin && c->tt == MRB_TT_ICLASS) { @@ -1391,9 +1395,9 @@ mrb_class_superclass(mrb_state *mrb, mrb_value klass) struct RClass *c; c = mrb_class_ptr(klass); - c = c->origin->super; + c = find_origin(c)->super; while (c && c->tt == MRB_TT_ICLASS) { - c = c->origin->super; + c = find_origin(c)->super; } if (!c) return mrb_nil_value(); return mrb_obj_value(c); @@ -1990,7 +1994,7 @@ static void remove_method(mrb_state *mrb, mrb_value mod, mrb_sym mid) { struct RClass *c = mrb_class_ptr(mod); - khash_t(mt) *h = c->origin->mt; + khash_t(mt) *h = find_origin(c)->mt; khiter_t k; if (h) { diff --git a/src/kernel.c b/src/kernel.c index 36ad683ee..225f7fa54 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -240,19 +240,12 @@ mrb_singleton_class_clone(mrb_state *mrb, mrb_value obj) /* copy singleton(unnamed) class */ struct RClass *clone = (struct RClass*)mrb_obj_alloc(mrb, klass->tt, mrb->class_class); - if ((mrb_type(obj) == MRB_TT_CLASS) || - (mrb_type(obj) == MRB_TT_SCLASS)) { /* BUILTIN_TYPE(obj) == T_CLASS */ + if ((mrb_type(obj) == MRB_TT_CLASS) || (mrb_type(obj) == MRB_TT_SCLASS)) { clone->c = clone; } else { clone->c = mrb_singleton_class_clone(mrb, mrb_obj_value(klass)); } - - if (klass->origin != klass) - clone->origin = klass->origin; - else - clone->origin = clone; - clone->super = klass->super; if (klass->iv) { mrb_iv_copy(mrb, mrb_obj_value(clone), mrb_obj_value(klass)); @@ -276,10 +269,18 @@ copy_class(mrb_state *mrb, mrb_value dst, mrb_value src) struct RClass *sc = mrb_class_ptr(src); /* if the origin is not the same as the class, then the origin and the current class need to be copied */ - if (sc->origin != sc) { - dc->origin = mrb_class_ptr(mrb_obj_dup(mrb, mrb_obj_value(sc->origin))); - } else { - dc->origin = dc; + if (sc->flags & MRB_FLAG_IS_PREPENDED) { + struct RClass *c0 = sc->super; + struct RClass *c1 = dc; + + /* copy prepended iclasses */ + while (!(c0->flags & MRB_FLAG_IS_ORIGIN)) { + c1->super = mrb_class_ptr(mrb_obj_dup(mrb, mrb_obj_value(c0))); + c1 = c1->super; + c0 = c0->super; + } + c1->super = mrb_class_ptr(mrb_obj_dup(mrb, mrb_obj_value(c0))); + c1->super->flags |= MRB_FLAG_IS_ORIGIN; } dc->mt = kh_copy(mt, mrb, sc->mt); dc->super = sc->super; @@ -657,8 +658,8 @@ mrb_class_instance_method_list(mrb_state *mrb, mrb_bool recur, struct RClass* kl struct RClass* oldklass; khash_t(st)* set = kh_init(st, mrb); - if (!recur && klass->origin != klass) { - klass = klass->origin; + if (!recur && (klass->flags & MRB_FLAG_IS_PREPENDED)) { + MRB_CLASS_ORIGIN(klass); prepended = 1; } diff --git a/src/object.c b/src/object.c index c834ee04f..2e0bd245f 100644 --- a/src/object.c +++ b/src/object.c @@ -487,7 +487,7 @@ mrb_obj_is_kind_of(mrb_state *mrb, mrb_value obj, struct RClass *c) mrb_raise(mrb, E_TYPE_ERROR, "class or module required"); } - c = c->origin; + MRB_CLASS_ORIGIN(c); while (cl) { if (cl == c || cl->mt == c->mt) return TRUE; -- cgit v1.2.3 From 44eb1492bed2f9584623ec10ecf14cc46af4bf12 Mon Sep 17 00:00:00 2001 From: Kazuho Oku Date: Thu, 10 Sep 2015 22:24:10 +0900 Subject: freeze the hash key (fixes #2945) --- src/hash.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/hash.c b/src/hash.c index 0bda2b48b..42b40482a 100644 --- a/src/hash.c +++ b/src/hash.c @@ -104,9 +104,11 @@ static void mrb_hash_modify(mrb_state *mrb, mrb_value hash); static inline mrb_value mrb_hash_ht_key(mrb_state *mrb, mrb_value key) { - if (mrb_string_p(key)) - return mrb_str_dup(mrb, key); - else + if (mrb_string_p(key)) { + key = mrb_str_dup(mrb, key); + RSTR_SET_FROZEN_FLAG(mrb_str_ptr(key)); + return key; + } else return key; } -- cgit v1.2.3 From 8256d77c7fe551011cc744ddb204ca20c4eea175 Mon Sep 17 00:00:00 2001 From: Kazuho Oku Date: Thu, 10 Sep 2015 22:50:01 +0900 Subject: avoid unnecessary string duplications by checking the frozen flag --- src/hash.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/hash.c b/src/hash.c index 42b40482a..ffb8bd931 100644 --- a/src/hash.c +++ b/src/hash.c @@ -104,12 +104,11 @@ static void mrb_hash_modify(mrb_state *mrb, mrb_value hash); static inline mrb_value mrb_hash_ht_key(mrb_state *mrb, mrb_value key) { - if (mrb_string_p(key)) { + if (mrb_string_p(key) && !RSTR_FROZEN_P(mrb_str_ptr(key))) { key = mrb_str_dup(mrb, key); RSTR_SET_FROZEN_FLAG(mrb_str_ptr(key)); - return key; - } else - return key; + } + return key; } #define KEY(key) mrb_hash_ht_key(mrb, key) -- cgit v1.2.3 From 8277e950eee4e8c6135eca281a7d5ca91077d2b4 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Fri, 11 Sep 2015 11:12:03 +0900 Subject: Support windows locale Add mrb_utf8_from_locale, mrb_utf8_free, mrb_locale_from_utf8, mrb_locale_free. Just works for windows. --- include/mruby.h | 12 ++++++ mrbgems/mruby-bin-mirb/tools/mirb/mirb.c | 10 ++++- mrbgems/mruby-bin-mruby/tools/mruby/mruby.c | 11 ++++- mrbgems/mruby-print/src/print.c | 13 +++--- src/string.c | 63 +++++++++++++++++++++++++++++ 5 files changed, 99 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/include/mruby.h b/include/mruby.h index b4ec13fdc..dedbd0748 100644 --- a/include/mruby.h +++ b/include/mruby.h @@ -292,6 +292,18 @@ MRB_API mrb_value mrb_str_new_cstr(mrb_state*, const char*); MRB_API mrb_value mrb_str_new_static(mrb_state *mrb, const char *p, size_t len); #define mrb_str_new_lit(mrb, lit) mrb_str_new_static(mrb, (lit), mrb_strlen_lit(lit)) +#ifdef _WIN32 +char* mrb_utf8_from_locale(const char *p, size_t len); +char* mrb_locale_from_utf8(const char *p, size_t len); +#define mrb_locale_free(p) free(p) +#define mrb_utf8_free(p) free(p) +#else +#define mrb_utf8_from_locale(p, l) (p) +#define mrb_locale_from_utf8(p, l) (p) +#define mrb_locale_free(p) +#define mrb_utf8_free(p) +#endif + MRB_API mrb_state* mrb_open(void); MRB_API mrb_state* mrb_open_allocf(mrb_allocf, void *ud); MRB_API mrb_state* mrb_open_core(mrb_allocf, void *ud); diff --git a/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c b/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c index 0f3649a35..37fda352c 100644 --- a/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c +++ b/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c @@ -366,6 +366,8 @@ main(int argc, char **argv) ai = mrb_gc_arena_save(mrb); while (TRUE) { + char *utf8; + #ifndef ENABLE_READLINE print_cmdline(code_block_open); @@ -415,17 +417,21 @@ 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); if (parser == NULL) { fputs("create parser state error\n", stderr); break; } - parser->s = ruby_code; - parser->send = ruby_code + strlen(ruby_code); + 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 */ diff --git a/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c b/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c index 5ca744388..cc1ca3055 100644 --- a/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c +++ b/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c @@ -191,7 +191,11 @@ main(int argc, char **argv) ARGV = mrb_ary_new_capa(mrb, args.argc); for (i = 0; i < args.argc; i++) { - mrb_ary_push(mrb, ARGV, mrb_str_new_cstr(mrb, args.argv[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); @@ -222,7 +226,10 @@ main(int argc, char **argv) v = mrb_load_file_cxt(mrb, args.rfp, c); } else { - v = mrb_load_string_cxt(mrb, args.cmdline, c); + char* utf8 = mrb_utf8_from_locale(args.cmdline, -1); + if (!utf8) abort(); + v = mrb_load_string_cxt(mrb, utf8, c); + mrb_utf8_free(utf8); } mrbc_context_free(mrb, c); diff --git a/mrbgems/mruby-print/src/print.c b/mrbgems/mruby-print/src/print.c index 673ba2172..e7e21dd4b 100644 --- a/mrbgems/mruby-print/src/print.c +++ b/mrbgems/mruby-print/src/print.c @@ -1,17 +1,18 @@ #include "mruby.h" #include "mruby/string.h" #include +#include +#include static void printstr(mrb_state *mrb, mrb_value obj) { - char *s; - mrb_int len; - if (mrb_string_p(obj)) { - s = RSTRING_PTR(obj); - len = RSTRING_LEN(obj); - fwrite(s, len, 1, stdout); + char* ptr = mrb_locale_from_utf8(RSTRING_PTR(obj), RSTRING_LEN(obj)); + if (ptr) { + fwrite(ptr, strlen(ptr), 1, stdout); + mrb_locale_free(ptr); + } } } diff --git a/src/string.c b/src/string.c index 08caf3bae..e93fd4606 100644 --- a/src/string.c +++ b/src/string.c @@ -43,6 +43,69 @@ mrb_str_strlen(mrb_state *mrb, struct RString *s) return max; } +#ifdef _WIN32 +#include + +char* +mrb_utf8_from_locale(const char *str, size_t len) +{ + wchar_t* wcsp; + char* mbsp; + size_t mbssize, wcssize; + + if (len == 0) + return strdup(""); + if (len == -1) + len = strlen(str); + wcssize = MultiByteToWideChar(GetACP(), 0, str, len, NULL, 0); + wcsp = (wchar_t*) malloc((wcssize + 1) * sizeof(wchar_t)); + if (!wcsp) + return NULL; + wcssize = MultiByteToWideChar(GetACP(), 0, str, len, wcsp, wcssize + 1); + wcsp[wcssize] = 0; + + mbssize = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR) wcsp, -1, NULL, 0, NULL, NULL); + mbsp = (char*) malloc((mbssize + 1)); + if (!mbsp) { + free(wcsp); + return NULL; + } + mbssize = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR) wcsp, -1, mbsp, mbssize, NULL, NULL); + mbsp[mbssize] = 0; + free(wcsp); + return mbsp; +} + +char* +mrb_locale_from_utf8(const char *utf8, size_t len) +{ + wchar_t* wcsp; + char* mbsp; + size_t mbssize, wcssize; + + if (len == 0) + return strdup(""); + if (len == -1) + len = strlen(utf8); + wcssize = MultiByteToWideChar(CP_UTF8, 0, utf8, len, NULL, 0); + wcsp = (wchar_t*) malloc((wcssize + 1) * sizeof(wchar_t)); + if (!wcsp) + return NULL; + wcssize = MultiByteToWideChar(CP_UTF8, 0, utf8, len, wcsp, wcssize + 1); + wcsp[wcssize] = 0; + mbssize = WideCharToMultiByte(GetACP(), 0, (LPCWSTR) wcsp, -1, NULL, 0, NULL, NULL); + mbsp = (char*) malloc((mbssize + 1)); + if (!mbsp) { + free(wcsp); + return NULL; + } + mbssize = WideCharToMultiByte(GetACP(), 0, (LPCWSTR) wcsp, -1, mbsp, mbssize, NULL, NULL); + mbsp[mbssize] = 0; + free(wcsp); + return mbsp; +} +#endif + static inline void resize_capa(mrb_state *mrb, struct RString *s, mrb_int capacity) { -- cgit v1.2.3 From a085c04e3741825ab4c4ceda60b640e250ae56d5 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sat, 12 Sep 2015 12:11:10 +0900 Subject: Don't crash if NULL is passed to mrb_close Sometimes it is very useful just return from mrb_close if NULL is passed as mrb. This is the same spirit of free(3), which just does nothing if NULL is passed. --- src/state.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/state.c b/src/state.c index 2efd34334..bfd99e4c3 100644 --- a/src/state.c +++ b/src/state.c @@ -234,6 +234,7 @@ mrb_free_context(mrb_state *mrb, struct mrb_context *c) MRB_API void mrb_close(mrb_state *mrb) { + if (!mrb) return; if (mrb->atexit_stack_len > 0) { mrb_int i; for (i = mrb->atexit_stack_len; i > 0; --i) { -- cgit v1.2.3