pax_global_header00006660000000000000000000000064134036141240014510gustar00rootroot0000000000000052 comment=1c09046c13fc0a763583c6da0f1d350f6c41c4ca mruby-2.0.0/000077500000000000000000000000001340361412400126455ustar00rootroot00000000000000mruby-2.0.0/.gitignore000066400000000000000000000003611340361412400146350ustar00rootroot00000000000000# / *.bak *.d *.o /benchmark/**/*.dat /benchmark/*.pdf /benchmark/*.png *.orig *.pdb *.rej *.sav *.swp *.tmp *~ .DS_Store .ccmalloc .svn .vscode /.git cscope.files cscope.out tags /src/y.tab.c /bin /build /mruby-source-*.gem doc/api .yardoc mruby-2.0.0/.gitlab-ci.yml000066400000000000000000002566131340361412400153160ustar00rootroot00000000000000--- stages: - pretest - test pretest: stage: pretest image: registry.gitlab.com/dabroz/mruby:gcc47_0.1 tags: - linux variables: CC: gcc-4.7 CXX: g++-4.7 LD: gcc-4.7 script: "./minirake all test" Test gcc-4.7 32bit: stage: test image: registry.gitlab.com/dabroz/mruby:gcc47_0.7 variables: CC: gcc-4.7 CXX: g++-4.7 LD: gcc-4.7 CFLAGS: "-m32 -DMRB_WORD_BOXING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.7 32bit_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc47_0.7 variables: CC: gcc-4.7 CXX: g++-4.7 LD: gcc-4.7 CFLAGS: "-m32 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.7 32bit_nan: stage: test image: registry.gitlab.com/dabroz/mruby:gcc47_0.7 variables: CC: gcc-4.7 CXX: g++-4.7 LD: gcc-4.7 CFLAGS: "-m32 -DMRB_NAN_BOXING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.7 32bit_nan_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc47_0.7 variables: CC: gcc-4.7 CXX: g++-4.7 LD: gcc-4.7 CFLAGS: "-m32 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.7 32bit_int16: stage: test image: registry.gitlab.com/dabroz/mruby:gcc47_0.7 variables: CC: gcc-4.7 CXX: g++-4.7 LD: gcc-4.7 CFLAGS: "-m32 -DMRB_INT16=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.7 32bit_int16_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc47_0.7 variables: CC: gcc-4.7 CXX: g++-4.7 LD: gcc-4.7 CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.7 32bit_int16_nan: stage: test image: registry.gitlab.com/dabroz/mruby:gcc47_0.7 variables: CC: gcc-4.7 CXX: g++-4.7 LD: gcc-4.7 CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_NAN_BOXING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.7 32bit_int16_nan_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc47_0.7 variables: CC: gcc-4.7 CXX: g++-4.7 LD: gcc-4.7 CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.7 32bit_int64: stage: test image: registry.gitlab.com/dabroz/mruby:gcc47_0.7 variables: CC: gcc-4.7 CXX: g++-4.7 LD: gcc-4.7 CFLAGS: "-m32 -DMRB_INT64=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.7 32bit_int64_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc47_0.7 variables: CC: gcc-4.7 CXX: g++-4.7 LD: gcc-4.7 CFLAGS: "-m32 -DMRB_INT64=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.7 32bit_float: stage: test image: registry.gitlab.com/dabroz/mruby:gcc47_0.7 variables: CC: gcc-4.7 CXX: g++-4.7 LD: gcc-4.7 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.7 32bit_float_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc47_0.7 variables: CC: gcc-4.7 CXX: g++-4.7 LD: gcc-4.7 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.7 32bit_float_int16: stage: test image: registry.gitlab.com/dabroz/mruby:gcc47_0.7 variables: CC: gcc-4.7 CXX: g++-4.7 LD: gcc-4.7 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT16=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.7 32bit_float_int16_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc47_0.7 variables: CC: gcc-4.7 CXX: g++-4.7 LD: gcc-4.7 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT16=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.7 32bit_float_int64: stage: test image: registry.gitlab.com/dabroz/mruby:gcc47_0.7 variables: CC: gcc-4.7 CXX: g++-4.7 LD: gcc-4.7 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT64=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.7 32bit_float_int64_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc47_0.7 variables: CC: gcc-4.7 CXX: g++-4.7 LD: gcc-4.7 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.7 64bit: stage: test image: registry.gitlab.com/dabroz/mruby:gcc47_0.7 variables: CC: gcc-4.7 CXX: g++-4.7 LD: gcc-4.7 CFLAGS: "-DMRB_WORD_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.7 64bit_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc47_0.7 variables: CC: gcc-4.7 CXX: g++-4.7 LD: gcc-4.7 CFLAGS: "-DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.7 64bit_nan: stage: test image: registry.gitlab.com/dabroz/mruby:gcc47_0.7 variables: CC: gcc-4.7 CXX: g++-4.7 LD: gcc-4.7 CFLAGS: "-DMRB_NAN_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.7 64bit_nan_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc47_0.7 variables: CC: gcc-4.7 CXX: g++-4.7 LD: gcc-4.7 CFLAGS: "-DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.7 64bit_int16: stage: test image: registry.gitlab.com/dabroz/mruby:gcc47_0.7 variables: CC: gcc-4.7 CXX: g++-4.7 LD: gcc-4.7 CFLAGS: "-DMRB_INT16=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.7 64bit_int16_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc47_0.7 variables: CC: gcc-4.7 CXX: g++-4.7 LD: gcc-4.7 CFLAGS: "-DMRB_INT16=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.7 64bit_int16_nan: stage: test image: registry.gitlab.com/dabroz/mruby:gcc47_0.7 variables: CC: gcc-4.7 CXX: g++-4.7 LD: gcc-4.7 CFLAGS: "-DMRB_INT16=1 -DMRB_NAN_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.7 64bit_int16_nan_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc47_0.7 variables: CC: gcc-4.7 CXX: g++-4.7 LD: gcc-4.7 CFLAGS: "-DMRB_INT16=1 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.7 64bit_int64: stage: test image: registry.gitlab.com/dabroz/mruby:gcc47_0.7 variables: CC: gcc-4.7 CXX: g++-4.7 LD: gcc-4.7 CFLAGS: "-DMRB_INT64=1 -DMRB_WORD_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.7 64bit_int64_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc47_0.7 variables: CC: gcc-4.7 CXX: g++-4.7 LD: gcc-4.7 CFLAGS: "-DMRB_INT64=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.7 64bit_float: stage: test image: registry.gitlab.com/dabroz/mruby:gcc47_0.7 variables: CC: gcc-4.7 CXX: g++-4.7 LD: gcc-4.7 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.7 64bit_float_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc47_0.7 variables: CC: gcc-4.7 CXX: g++-4.7 LD: gcc-4.7 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.7 64bit_float_int16: stage: test image: registry.gitlab.com/dabroz/mruby:gcc47_0.7 variables: CC: gcc-4.7 CXX: g++-4.7 LD: gcc-4.7 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT16=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.7 64bit_float_int16_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc47_0.7 variables: CC: gcc-4.7 CXX: g++-4.7 LD: gcc-4.7 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT16=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.7 64bit_float_int64: stage: test image: registry.gitlab.com/dabroz/mruby:gcc47_0.7 variables: CC: gcc-4.7 CXX: g++-4.7 LD: gcc-4.7 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_WORD_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.7 64bit_float_int64_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc47_0.7 variables: CC: gcc-4.7 CXX: g++-4.7 LD: gcc-4.7 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.8 32bit: stage: test image: registry.gitlab.com/dabroz/mruby:gcc48_0.7 variables: CC: gcc-4.8 CXX: g++-4.8 LD: gcc-4.8 CFLAGS: "-m32 -DMRB_WORD_BOXING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.8 32bit_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc48_0.7 variables: CC: gcc-4.8 CXX: g++-4.8 LD: gcc-4.8 CFLAGS: "-m32 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.8 32bit_nan: stage: test image: registry.gitlab.com/dabroz/mruby:gcc48_0.7 variables: CC: gcc-4.8 CXX: g++-4.8 LD: gcc-4.8 CFLAGS: "-m32 -DMRB_NAN_BOXING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.8 32bit_nan_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc48_0.7 variables: CC: gcc-4.8 CXX: g++-4.8 LD: gcc-4.8 CFLAGS: "-m32 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.8 32bit_int16: stage: test image: registry.gitlab.com/dabroz/mruby:gcc48_0.7 variables: CC: gcc-4.8 CXX: g++-4.8 LD: gcc-4.8 CFLAGS: "-m32 -DMRB_INT16=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.8 32bit_int16_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc48_0.7 variables: CC: gcc-4.8 CXX: g++-4.8 LD: gcc-4.8 CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.8 32bit_int16_nan: stage: test image: registry.gitlab.com/dabroz/mruby:gcc48_0.7 variables: CC: gcc-4.8 CXX: g++-4.8 LD: gcc-4.8 CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_NAN_BOXING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.8 32bit_int16_nan_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc48_0.7 variables: CC: gcc-4.8 CXX: g++-4.8 LD: gcc-4.8 CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.8 32bit_int64: stage: test image: registry.gitlab.com/dabroz/mruby:gcc48_0.7 variables: CC: gcc-4.8 CXX: g++-4.8 LD: gcc-4.8 CFLAGS: "-m32 -DMRB_INT64=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.8 32bit_int64_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc48_0.7 variables: CC: gcc-4.8 CXX: g++-4.8 LD: gcc-4.8 CFLAGS: "-m32 -DMRB_INT64=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.8 32bit_float: stage: test image: registry.gitlab.com/dabroz/mruby:gcc48_0.7 variables: CC: gcc-4.8 CXX: g++-4.8 LD: gcc-4.8 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.8 32bit_float_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc48_0.7 variables: CC: gcc-4.8 CXX: g++-4.8 LD: gcc-4.8 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.8 32bit_float_int16: stage: test image: registry.gitlab.com/dabroz/mruby:gcc48_0.7 variables: CC: gcc-4.8 CXX: g++-4.8 LD: gcc-4.8 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT16=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.8 32bit_float_int16_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc48_0.7 variables: CC: gcc-4.8 CXX: g++-4.8 LD: gcc-4.8 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT16=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.8 32bit_float_int64: stage: test image: registry.gitlab.com/dabroz/mruby:gcc48_0.7 variables: CC: gcc-4.8 CXX: g++-4.8 LD: gcc-4.8 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT64=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.8 32bit_float_int64_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc48_0.7 variables: CC: gcc-4.8 CXX: g++-4.8 LD: gcc-4.8 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.8 64bit: stage: test image: registry.gitlab.com/dabroz/mruby:gcc48_0.7 variables: CC: gcc-4.8 CXX: g++-4.8 LD: gcc-4.8 CFLAGS: "-DMRB_WORD_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.8 64bit_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc48_0.7 variables: CC: gcc-4.8 CXX: g++-4.8 LD: gcc-4.8 CFLAGS: "-DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.8 64bit_nan: stage: test image: registry.gitlab.com/dabroz/mruby:gcc48_0.7 variables: CC: gcc-4.8 CXX: g++-4.8 LD: gcc-4.8 CFLAGS: "-DMRB_NAN_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.8 64bit_nan_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc48_0.7 variables: CC: gcc-4.8 CXX: g++-4.8 LD: gcc-4.8 CFLAGS: "-DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.8 64bit_int16: stage: test image: registry.gitlab.com/dabroz/mruby:gcc48_0.7 variables: CC: gcc-4.8 CXX: g++-4.8 LD: gcc-4.8 CFLAGS: "-DMRB_INT16=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.8 64bit_int16_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc48_0.7 variables: CC: gcc-4.8 CXX: g++-4.8 LD: gcc-4.8 CFLAGS: "-DMRB_INT16=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.8 64bit_int16_nan: stage: test image: registry.gitlab.com/dabroz/mruby:gcc48_0.7 variables: CC: gcc-4.8 CXX: g++-4.8 LD: gcc-4.8 CFLAGS: "-DMRB_INT16=1 -DMRB_NAN_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.8 64bit_int16_nan_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc48_0.7 variables: CC: gcc-4.8 CXX: g++-4.8 LD: gcc-4.8 CFLAGS: "-DMRB_INT16=1 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.8 64bit_int64: stage: test image: registry.gitlab.com/dabroz/mruby:gcc48_0.7 variables: CC: gcc-4.8 CXX: g++-4.8 LD: gcc-4.8 CFLAGS: "-DMRB_INT64=1 -DMRB_WORD_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.8 64bit_int64_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc48_0.7 variables: CC: gcc-4.8 CXX: g++-4.8 LD: gcc-4.8 CFLAGS: "-DMRB_INT64=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.8 64bit_float: stage: test image: registry.gitlab.com/dabroz/mruby:gcc48_0.7 variables: CC: gcc-4.8 CXX: g++-4.8 LD: gcc-4.8 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.8 64bit_float_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc48_0.7 variables: CC: gcc-4.8 CXX: g++-4.8 LD: gcc-4.8 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.8 64bit_float_int16: stage: test image: registry.gitlab.com/dabroz/mruby:gcc48_0.7 variables: CC: gcc-4.8 CXX: g++-4.8 LD: gcc-4.8 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT16=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.8 64bit_float_int16_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc48_0.7 variables: CC: gcc-4.8 CXX: g++-4.8 LD: gcc-4.8 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT16=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.8 64bit_float_int64: stage: test image: registry.gitlab.com/dabroz/mruby:gcc48_0.7 variables: CC: gcc-4.8 CXX: g++-4.8 LD: gcc-4.8 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_WORD_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.8 64bit_float_int64_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc48_0.7 variables: CC: gcc-4.8 CXX: g++-4.8 LD: gcc-4.8 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.9 32bit: stage: test image: registry.gitlab.com/dabroz/mruby:gcc49_0.7 variables: CC: gcc-4.9 CXX: g++-4.9 LD: gcc-4.9 CFLAGS: "-m32 -DMRB_WORD_BOXING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.9 32bit_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc49_0.7 variables: CC: gcc-4.9 CXX: g++-4.9 LD: gcc-4.9 CFLAGS: "-m32 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.9 32bit_nan: stage: test image: registry.gitlab.com/dabroz/mruby:gcc49_0.7 variables: CC: gcc-4.9 CXX: g++-4.9 LD: gcc-4.9 CFLAGS: "-m32 -DMRB_NAN_BOXING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.9 32bit_nan_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc49_0.7 variables: CC: gcc-4.9 CXX: g++-4.9 LD: gcc-4.9 CFLAGS: "-m32 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.9 32bit_int16: stage: test image: registry.gitlab.com/dabroz/mruby:gcc49_0.7 variables: CC: gcc-4.9 CXX: g++-4.9 LD: gcc-4.9 CFLAGS: "-m32 -DMRB_INT16=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.9 32bit_int16_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc49_0.7 variables: CC: gcc-4.9 CXX: g++-4.9 LD: gcc-4.9 CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.9 32bit_int16_nan: stage: test image: registry.gitlab.com/dabroz/mruby:gcc49_0.7 variables: CC: gcc-4.9 CXX: g++-4.9 LD: gcc-4.9 CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_NAN_BOXING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.9 32bit_int16_nan_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc49_0.7 variables: CC: gcc-4.9 CXX: g++-4.9 LD: gcc-4.9 CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.9 32bit_int64: stage: test image: registry.gitlab.com/dabroz/mruby:gcc49_0.7 variables: CC: gcc-4.9 CXX: g++-4.9 LD: gcc-4.9 CFLAGS: "-m32 -DMRB_INT64=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.9 32bit_int64_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc49_0.7 variables: CC: gcc-4.9 CXX: g++-4.9 LD: gcc-4.9 CFLAGS: "-m32 -DMRB_INT64=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.9 32bit_float: stage: test image: registry.gitlab.com/dabroz/mruby:gcc49_0.7 variables: CC: gcc-4.9 CXX: g++-4.9 LD: gcc-4.9 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.9 32bit_float_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc49_0.7 variables: CC: gcc-4.9 CXX: g++-4.9 LD: gcc-4.9 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.9 32bit_float_int16: stage: test image: registry.gitlab.com/dabroz/mruby:gcc49_0.7 variables: CC: gcc-4.9 CXX: g++-4.9 LD: gcc-4.9 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT16=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.9 32bit_float_int16_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc49_0.7 variables: CC: gcc-4.9 CXX: g++-4.9 LD: gcc-4.9 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT16=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.9 32bit_float_int64: stage: test image: registry.gitlab.com/dabroz/mruby:gcc49_0.7 variables: CC: gcc-4.9 CXX: g++-4.9 LD: gcc-4.9 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT64=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.9 32bit_float_int64_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc49_0.7 variables: CC: gcc-4.9 CXX: g++-4.9 LD: gcc-4.9 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-4.9 64bit: stage: test image: registry.gitlab.com/dabroz/mruby:gcc49_0.7 variables: CC: gcc-4.9 CXX: g++-4.9 LD: gcc-4.9 CFLAGS: "-DMRB_WORD_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.9 64bit_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc49_0.7 variables: CC: gcc-4.9 CXX: g++-4.9 LD: gcc-4.9 CFLAGS: "-DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.9 64bit_nan: stage: test image: registry.gitlab.com/dabroz/mruby:gcc49_0.7 variables: CC: gcc-4.9 CXX: g++-4.9 LD: gcc-4.9 CFLAGS: "-DMRB_NAN_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.9 64bit_nan_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc49_0.7 variables: CC: gcc-4.9 CXX: g++-4.9 LD: gcc-4.9 CFLAGS: "-DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.9 64bit_int16: stage: test image: registry.gitlab.com/dabroz/mruby:gcc49_0.7 variables: CC: gcc-4.9 CXX: g++-4.9 LD: gcc-4.9 CFLAGS: "-DMRB_INT16=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.9 64bit_int16_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc49_0.7 variables: CC: gcc-4.9 CXX: g++-4.9 LD: gcc-4.9 CFLAGS: "-DMRB_INT16=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.9 64bit_int16_nan: stage: test image: registry.gitlab.com/dabroz/mruby:gcc49_0.7 variables: CC: gcc-4.9 CXX: g++-4.9 LD: gcc-4.9 CFLAGS: "-DMRB_INT16=1 -DMRB_NAN_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.9 64bit_int16_nan_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc49_0.7 variables: CC: gcc-4.9 CXX: g++-4.9 LD: gcc-4.9 CFLAGS: "-DMRB_INT16=1 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.9 64bit_int64: stage: test image: registry.gitlab.com/dabroz/mruby:gcc49_0.7 variables: CC: gcc-4.9 CXX: g++-4.9 LD: gcc-4.9 CFLAGS: "-DMRB_INT64=1 -DMRB_WORD_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.9 64bit_int64_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc49_0.7 variables: CC: gcc-4.9 CXX: g++-4.9 LD: gcc-4.9 CFLAGS: "-DMRB_INT64=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.9 64bit_float: stage: test image: registry.gitlab.com/dabroz/mruby:gcc49_0.7 variables: CC: gcc-4.9 CXX: g++-4.9 LD: gcc-4.9 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.9 64bit_float_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc49_0.7 variables: CC: gcc-4.9 CXX: g++-4.9 LD: gcc-4.9 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.9 64bit_float_int16: stage: test image: registry.gitlab.com/dabroz/mruby:gcc49_0.7 variables: CC: gcc-4.9 CXX: g++-4.9 LD: gcc-4.9 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT16=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.9 64bit_float_int16_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc49_0.7 variables: CC: gcc-4.9 CXX: g++-4.9 LD: gcc-4.9 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT16=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.9 64bit_float_int64: stage: test image: registry.gitlab.com/dabroz/mruby:gcc49_0.7 variables: CC: gcc-4.9 CXX: g++-4.9 LD: gcc-4.9 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_WORD_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-4.9 64bit_float_int64_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc49_0.7 variables: CC: gcc-4.9 CXX: g++-4.9 LD: gcc-4.9 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-5 32bit: stage: test image: registry.gitlab.com/dabroz/mruby:gcc5_0.7 variables: CC: gcc-5 CXX: g++-5 LD: gcc-5 CFLAGS: "-m32 -DMRB_WORD_BOXING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-5 32bit_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc5_0.7 variables: CC: gcc-5 CXX: g++-5 LD: gcc-5 CFLAGS: "-m32 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-5 32bit_nan: stage: test image: registry.gitlab.com/dabroz/mruby:gcc5_0.7 variables: CC: gcc-5 CXX: g++-5 LD: gcc-5 CFLAGS: "-m32 -DMRB_NAN_BOXING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-5 32bit_nan_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc5_0.7 variables: CC: gcc-5 CXX: g++-5 LD: gcc-5 CFLAGS: "-m32 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-5 32bit_int16: stage: test image: registry.gitlab.com/dabroz/mruby:gcc5_0.7 variables: CC: gcc-5 CXX: g++-5 LD: gcc-5 CFLAGS: "-m32 -DMRB_INT16=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-5 32bit_int16_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc5_0.7 variables: CC: gcc-5 CXX: g++-5 LD: gcc-5 CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-5 32bit_int16_nan: stage: test image: registry.gitlab.com/dabroz/mruby:gcc5_0.7 variables: CC: gcc-5 CXX: g++-5 LD: gcc-5 CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_NAN_BOXING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-5 32bit_int16_nan_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc5_0.7 variables: CC: gcc-5 CXX: g++-5 LD: gcc-5 CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-5 32bit_int64: stage: test image: registry.gitlab.com/dabroz/mruby:gcc5_0.7 variables: CC: gcc-5 CXX: g++-5 LD: gcc-5 CFLAGS: "-m32 -DMRB_INT64=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-5 32bit_int64_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc5_0.7 variables: CC: gcc-5 CXX: g++-5 LD: gcc-5 CFLAGS: "-m32 -DMRB_INT64=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-5 32bit_float: stage: test image: registry.gitlab.com/dabroz/mruby:gcc5_0.7 variables: CC: gcc-5 CXX: g++-5 LD: gcc-5 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-5 32bit_float_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc5_0.7 variables: CC: gcc-5 CXX: g++-5 LD: gcc-5 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-5 32bit_float_int16: stage: test image: registry.gitlab.com/dabroz/mruby:gcc5_0.7 variables: CC: gcc-5 CXX: g++-5 LD: gcc-5 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT16=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-5 32bit_float_int16_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc5_0.7 variables: CC: gcc-5 CXX: g++-5 LD: gcc-5 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT16=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-5 32bit_float_int64: stage: test image: registry.gitlab.com/dabroz/mruby:gcc5_0.7 variables: CC: gcc-5 CXX: g++-5 LD: gcc-5 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT64=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-5 32bit_float_int64_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc5_0.7 variables: CC: gcc-5 CXX: g++-5 LD: gcc-5 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-5 64bit: stage: test image: registry.gitlab.com/dabroz/mruby:gcc5_0.7 variables: CC: gcc-5 CXX: g++-5 LD: gcc-5 CFLAGS: "-DMRB_WORD_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-5 64bit_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc5_0.7 variables: CC: gcc-5 CXX: g++-5 LD: gcc-5 CFLAGS: "-DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-5 64bit_nan: stage: test image: registry.gitlab.com/dabroz/mruby:gcc5_0.7 variables: CC: gcc-5 CXX: g++-5 LD: gcc-5 CFLAGS: "-DMRB_NAN_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-5 64bit_nan_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc5_0.7 variables: CC: gcc-5 CXX: g++-5 LD: gcc-5 CFLAGS: "-DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-5 64bit_int16: stage: test image: registry.gitlab.com/dabroz/mruby:gcc5_0.7 variables: CC: gcc-5 CXX: g++-5 LD: gcc-5 CFLAGS: "-DMRB_INT16=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-5 64bit_int16_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc5_0.7 variables: CC: gcc-5 CXX: g++-5 LD: gcc-5 CFLAGS: "-DMRB_INT16=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-5 64bit_int16_nan: stage: test image: registry.gitlab.com/dabroz/mruby:gcc5_0.7 variables: CC: gcc-5 CXX: g++-5 LD: gcc-5 CFLAGS: "-DMRB_INT16=1 -DMRB_NAN_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-5 64bit_int16_nan_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc5_0.7 variables: CC: gcc-5 CXX: g++-5 LD: gcc-5 CFLAGS: "-DMRB_INT16=1 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-5 64bit_int64: stage: test image: registry.gitlab.com/dabroz/mruby:gcc5_0.7 variables: CC: gcc-5 CXX: g++-5 LD: gcc-5 CFLAGS: "-DMRB_INT64=1 -DMRB_WORD_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-5 64bit_int64_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc5_0.7 variables: CC: gcc-5 CXX: g++-5 LD: gcc-5 CFLAGS: "-DMRB_INT64=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-5 64bit_float: stage: test image: registry.gitlab.com/dabroz/mruby:gcc5_0.7 variables: CC: gcc-5 CXX: g++-5 LD: gcc-5 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-5 64bit_float_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc5_0.7 variables: CC: gcc-5 CXX: g++-5 LD: gcc-5 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-5 64bit_float_int16: stage: test image: registry.gitlab.com/dabroz/mruby:gcc5_0.7 variables: CC: gcc-5 CXX: g++-5 LD: gcc-5 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT16=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-5 64bit_float_int16_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc5_0.7 variables: CC: gcc-5 CXX: g++-5 LD: gcc-5 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT16=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-5 64bit_float_int64: stage: test image: registry.gitlab.com/dabroz/mruby:gcc5_0.7 variables: CC: gcc-5 CXX: g++-5 LD: gcc-5 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_WORD_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-5 64bit_float_int64_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc5_0.7 variables: CC: gcc-5 CXX: g++-5 LD: gcc-5 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-6 32bit: stage: test image: registry.gitlab.com/dabroz/mruby:gcc6_0.7 variables: CC: gcc-6 CXX: g++-6 LD: gcc-6 CFLAGS: "-m32 -DMRB_WORD_BOXING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-6 32bit_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc6_0.7 variables: CC: gcc-6 CXX: g++-6 LD: gcc-6 CFLAGS: "-m32 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-6 32bit_nan: stage: test image: registry.gitlab.com/dabroz/mruby:gcc6_0.7 variables: CC: gcc-6 CXX: g++-6 LD: gcc-6 CFLAGS: "-m32 -DMRB_NAN_BOXING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-6 32bit_nan_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc6_0.7 variables: CC: gcc-6 CXX: g++-6 LD: gcc-6 CFLAGS: "-m32 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-6 32bit_int16: stage: test image: registry.gitlab.com/dabroz/mruby:gcc6_0.7 variables: CC: gcc-6 CXX: g++-6 LD: gcc-6 CFLAGS: "-m32 -DMRB_INT16=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-6 32bit_int16_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc6_0.7 variables: CC: gcc-6 CXX: g++-6 LD: gcc-6 CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-6 32bit_int16_nan: stage: test image: registry.gitlab.com/dabroz/mruby:gcc6_0.7 variables: CC: gcc-6 CXX: g++-6 LD: gcc-6 CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_NAN_BOXING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-6 32bit_int16_nan_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc6_0.7 variables: CC: gcc-6 CXX: g++-6 LD: gcc-6 CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-6 32bit_int64: stage: test image: registry.gitlab.com/dabroz/mruby:gcc6_0.7 variables: CC: gcc-6 CXX: g++-6 LD: gcc-6 CFLAGS: "-m32 -DMRB_INT64=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-6 32bit_int64_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc6_0.7 variables: CC: gcc-6 CXX: g++-6 LD: gcc-6 CFLAGS: "-m32 -DMRB_INT64=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-6 32bit_float: stage: test image: registry.gitlab.com/dabroz/mruby:gcc6_0.7 variables: CC: gcc-6 CXX: g++-6 LD: gcc-6 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-6 32bit_float_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc6_0.7 variables: CC: gcc-6 CXX: g++-6 LD: gcc-6 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-6 32bit_float_int16: stage: test image: registry.gitlab.com/dabroz/mruby:gcc6_0.7 variables: CC: gcc-6 CXX: g++-6 LD: gcc-6 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT16=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-6 32bit_float_int16_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc6_0.7 variables: CC: gcc-6 CXX: g++-6 LD: gcc-6 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT16=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-6 32bit_float_int64: stage: test image: registry.gitlab.com/dabroz/mruby:gcc6_0.7 variables: CC: gcc-6 CXX: g++-6 LD: gcc-6 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT64=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-6 32bit_float_int64_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc6_0.7 variables: CC: gcc-6 CXX: g++-6 LD: gcc-6 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test gcc-6 64bit: stage: test image: registry.gitlab.com/dabroz/mruby:gcc6_0.7 variables: CC: gcc-6 CXX: g++-6 LD: gcc-6 CFLAGS: "-DMRB_WORD_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-6 64bit_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc6_0.7 variables: CC: gcc-6 CXX: g++-6 LD: gcc-6 CFLAGS: "-DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-6 64bit_nan: stage: test image: registry.gitlab.com/dabroz/mruby:gcc6_0.7 variables: CC: gcc-6 CXX: g++-6 LD: gcc-6 CFLAGS: "-DMRB_NAN_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-6 64bit_nan_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc6_0.7 variables: CC: gcc-6 CXX: g++-6 LD: gcc-6 CFLAGS: "-DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-6 64bit_int16: stage: test image: registry.gitlab.com/dabroz/mruby:gcc6_0.7 variables: CC: gcc-6 CXX: g++-6 LD: gcc-6 CFLAGS: "-DMRB_INT16=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-6 64bit_int16_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc6_0.7 variables: CC: gcc-6 CXX: g++-6 LD: gcc-6 CFLAGS: "-DMRB_INT16=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-6 64bit_int16_nan: stage: test image: registry.gitlab.com/dabroz/mruby:gcc6_0.7 variables: CC: gcc-6 CXX: g++-6 LD: gcc-6 CFLAGS: "-DMRB_INT16=1 -DMRB_NAN_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-6 64bit_int16_nan_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc6_0.7 variables: CC: gcc-6 CXX: g++-6 LD: gcc-6 CFLAGS: "-DMRB_INT16=1 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-6 64bit_int64: stage: test image: registry.gitlab.com/dabroz/mruby:gcc6_0.7 variables: CC: gcc-6 CXX: g++-6 LD: gcc-6 CFLAGS: "-DMRB_INT64=1 -DMRB_WORD_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-6 64bit_int64_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc6_0.7 variables: CC: gcc-6 CXX: g++-6 LD: gcc-6 CFLAGS: "-DMRB_INT64=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-6 64bit_float: stage: test image: registry.gitlab.com/dabroz/mruby:gcc6_0.7 variables: CC: gcc-6 CXX: g++-6 LD: gcc-6 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-6 64bit_float_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc6_0.7 variables: CC: gcc-6 CXX: g++-6 LD: gcc-6 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-6 64bit_float_int16: stage: test image: registry.gitlab.com/dabroz/mruby:gcc6_0.7 variables: CC: gcc-6 CXX: g++-6 LD: gcc-6 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT16=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-6 64bit_float_int16_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc6_0.7 variables: CC: gcc-6 CXX: g++-6 LD: gcc-6 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT16=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-6 64bit_float_int64: stage: test image: registry.gitlab.com/dabroz/mruby:gcc6_0.7 variables: CC: gcc-6 CXX: g++-6 LD: gcc-6 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_WORD_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test gcc-6 64bit_float_int64_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:gcc6_0.7 variables: CC: gcc-6 CXX: g++-6 LD: gcc-6 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.5 32bit: stage: test image: registry.gitlab.com/dabroz/mruby:clang35_0.7 variables: CC: clang-3.5 CXX: clang++-3.5 LD: clang-3.5 CFLAGS: "-m32 -DMRB_WORD_BOXING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.5 32bit_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang35_0.7 variables: CC: clang-3.5 CXX: clang++-3.5 LD: clang-3.5 CFLAGS: "-m32 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.5 32bit_nan: stage: test image: registry.gitlab.com/dabroz/mruby:clang35_0.7 variables: CC: clang-3.5 CXX: clang++-3.5 LD: clang-3.5 CFLAGS: "-m32 -DMRB_NAN_BOXING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.5 32bit_nan_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang35_0.7 variables: CC: clang-3.5 CXX: clang++-3.5 LD: clang-3.5 CFLAGS: "-m32 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.5 32bit_int16: stage: test image: registry.gitlab.com/dabroz/mruby:clang35_0.7 variables: CC: clang-3.5 CXX: clang++-3.5 LD: clang-3.5 CFLAGS: "-m32 -DMRB_INT16=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.5 32bit_int16_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang35_0.7 variables: CC: clang-3.5 CXX: clang++-3.5 LD: clang-3.5 CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.5 32bit_int16_nan: stage: test image: registry.gitlab.com/dabroz/mruby:clang35_0.7 variables: CC: clang-3.5 CXX: clang++-3.5 LD: clang-3.5 CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_NAN_BOXING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.5 32bit_int16_nan_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang35_0.7 variables: CC: clang-3.5 CXX: clang++-3.5 LD: clang-3.5 CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.5 32bit_int64: stage: test image: registry.gitlab.com/dabroz/mruby:clang35_0.7 variables: CC: clang-3.5 CXX: clang++-3.5 LD: clang-3.5 CFLAGS: "-m32 -DMRB_INT64=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.5 32bit_int64_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang35_0.7 variables: CC: clang-3.5 CXX: clang++-3.5 LD: clang-3.5 CFLAGS: "-m32 -DMRB_INT64=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.5 32bit_float: stage: test image: registry.gitlab.com/dabroz/mruby:clang35_0.7 variables: CC: clang-3.5 CXX: clang++-3.5 LD: clang-3.5 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.5 32bit_float_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang35_0.7 variables: CC: clang-3.5 CXX: clang++-3.5 LD: clang-3.5 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.5 32bit_float_int16: stage: test image: registry.gitlab.com/dabroz/mruby:clang35_0.7 variables: CC: clang-3.5 CXX: clang++-3.5 LD: clang-3.5 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT16=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.5 32bit_float_int16_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang35_0.7 variables: CC: clang-3.5 CXX: clang++-3.5 LD: clang-3.5 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT16=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.5 32bit_float_int64: stage: test image: registry.gitlab.com/dabroz/mruby:clang35_0.7 variables: CC: clang-3.5 CXX: clang++-3.5 LD: clang-3.5 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT64=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.5 32bit_float_int64_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang35_0.7 variables: CC: clang-3.5 CXX: clang++-3.5 LD: clang-3.5 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.5 64bit: stage: test image: registry.gitlab.com/dabroz/mruby:clang35_0.7 variables: CC: clang-3.5 CXX: clang++-3.5 LD: clang-3.5 CFLAGS: "-DMRB_WORD_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.5 64bit_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang35_0.7 variables: CC: clang-3.5 CXX: clang++-3.5 LD: clang-3.5 CFLAGS: "-DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.5 64bit_nan: stage: test image: registry.gitlab.com/dabroz/mruby:clang35_0.7 variables: CC: clang-3.5 CXX: clang++-3.5 LD: clang-3.5 CFLAGS: "-DMRB_NAN_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.5 64bit_nan_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang35_0.7 variables: CC: clang-3.5 CXX: clang++-3.5 LD: clang-3.5 CFLAGS: "-DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.5 64bit_int16: stage: test image: registry.gitlab.com/dabroz/mruby:clang35_0.7 variables: CC: clang-3.5 CXX: clang++-3.5 LD: clang-3.5 CFLAGS: "-DMRB_INT16=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.5 64bit_int16_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang35_0.7 variables: CC: clang-3.5 CXX: clang++-3.5 LD: clang-3.5 CFLAGS: "-DMRB_INT16=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.5 64bit_int16_nan: stage: test image: registry.gitlab.com/dabroz/mruby:clang35_0.7 variables: CC: clang-3.5 CXX: clang++-3.5 LD: clang-3.5 CFLAGS: "-DMRB_INT16=1 -DMRB_NAN_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.5 64bit_int16_nan_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang35_0.7 variables: CC: clang-3.5 CXX: clang++-3.5 LD: clang-3.5 CFLAGS: "-DMRB_INT16=1 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.5 64bit_int64: stage: test image: registry.gitlab.com/dabroz/mruby:clang35_0.7 variables: CC: clang-3.5 CXX: clang++-3.5 LD: clang-3.5 CFLAGS: "-DMRB_INT64=1 -DMRB_WORD_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.5 64bit_int64_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang35_0.7 variables: CC: clang-3.5 CXX: clang++-3.5 LD: clang-3.5 CFLAGS: "-DMRB_INT64=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.5 64bit_float: stage: test image: registry.gitlab.com/dabroz/mruby:clang35_0.7 variables: CC: clang-3.5 CXX: clang++-3.5 LD: clang-3.5 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.5 64bit_float_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang35_0.7 variables: CC: clang-3.5 CXX: clang++-3.5 LD: clang-3.5 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.5 64bit_float_int16: stage: test image: registry.gitlab.com/dabroz/mruby:clang35_0.7 variables: CC: clang-3.5 CXX: clang++-3.5 LD: clang-3.5 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT16=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.5 64bit_float_int16_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang35_0.7 variables: CC: clang-3.5 CXX: clang++-3.5 LD: clang-3.5 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT16=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.5 64bit_float_int64: stage: test image: registry.gitlab.com/dabroz/mruby:clang35_0.7 variables: CC: clang-3.5 CXX: clang++-3.5 LD: clang-3.5 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_WORD_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.5 64bit_float_int64_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang35_0.7 variables: CC: clang-3.5 CXX: clang++-3.5 LD: clang-3.5 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.6 32bit: stage: test image: registry.gitlab.com/dabroz/mruby:clang36_0.7 variables: CC: clang-3.6 CXX: clang++-3.6 LD: clang-3.6 CFLAGS: "-m32 -DMRB_WORD_BOXING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.6 32bit_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang36_0.7 variables: CC: clang-3.6 CXX: clang++-3.6 LD: clang-3.6 CFLAGS: "-m32 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.6 32bit_nan: stage: test image: registry.gitlab.com/dabroz/mruby:clang36_0.7 variables: CC: clang-3.6 CXX: clang++-3.6 LD: clang-3.6 CFLAGS: "-m32 -DMRB_NAN_BOXING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.6 32bit_nan_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang36_0.7 variables: CC: clang-3.6 CXX: clang++-3.6 LD: clang-3.6 CFLAGS: "-m32 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.6 32bit_int16: stage: test image: registry.gitlab.com/dabroz/mruby:clang36_0.7 variables: CC: clang-3.6 CXX: clang++-3.6 LD: clang-3.6 CFLAGS: "-m32 -DMRB_INT16=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.6 32bit_int16_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang36_0.7 variables: CC: clang-3.6 CXX: clang++-3.6 LD: clang-3.6 CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.6 32bit_int16_nan: stage: test image: registry.gitlab.com/dabroz/mruby:clang36_0.7 variables: CC: clang-3.6 CXX: clang++-3.6 LD: clang-3.6 CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_NAN_BOXING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.6 32bit_int16_nan_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang36_0.7 variables: CC: clang-3.6 CXX: clang++-3.6 LD: clang-3.6 CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.6 32bit_int64: stage: test image: registry.gitlab.com/dabroz/mruby:clang36_0.7 variables: CC: clang-3.6 CXX: clang++-3.6 LD: clang-3.6 CFLAGS: "-m32 -DMRB_INT64=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.6 32bit_int64_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang36_0.7 variables: CC: clang-3.6 CXX: clang++-3.6 LD: clang-3.6 CFLAGS: "-m32 -DMRB_INT64=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.6 32bit_float: stage: test image: registry.gitlab.com/dabroz/mruby:clang36_0.7 variables: CC: clang-3.6 CXX: clang++-3.6 LD: clang-3.6 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.6 32bit_float_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang36_0.7 variables: CC: clang-3.6 CXX: clang++-3.6 LD: clang-3.6 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.6 32bit_float_int16: stage: test image: registry.gitlab.com/dabroz/mruby:clang36_0.7 variables: CC: clang-3.6 CXX: clang++-3.6 LD: clang-3.6 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT16=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.6 32bit_float_int16_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang36_0.7 variables: CC: clang-3.6 CXX: clang++-3.6 LD: clang-3.6 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT16=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.6 32bit_float_int64: stage: test image: registry.gitlab.com/dabroz/mruby:clang36_0.7 variables: CC: clang-3.6 CXX: clang++-3.6 LD: clang-3.6 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT64=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.6 32bit_float_int64_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang36_0.7 variables: CC: clang-3.6 CXX: clang++-3.6 LD: clang-3.6 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.6 64bit: stage: test image: registry.gitlab.com/dabroz/mruby:clang36_0.7 variables: CC: clang-3.6 CXX: clang++-3.6 LD: clang-3.6 CFLAGS: "-DMRB_WORD_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.6 64bit_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang36_0.7 variables: CC: clang-3.6 CXX: clang++-3.6 LD: clang-3.6 CFLAGS: "-DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.6 64bit_nan: stage: test image: registry.gitlab.com/dabroz/mruby:clang36_0.7 variables: CC: clang-3.6 CXX: clang++-3.6 LD: clang-3.6 CFLAGS: "-DMRB_NAN_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.6 64bit_nan_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang36_0.7 variables: CC: clang-3.6 CXX: clang++-3.6 LD: clang-3.6 CFLAGS: "-DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.6 64bit_int16: stage: test image: registry.gitlab.com/dabroz/mruby:clang36_0.7 variables: CC: clang-3.6 CXX: clang++-3.6 LD: clang-3.6 CFLAGS: "-DMRB_INT16=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.6 64bit_int16_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang36_0.7 variables: CC: clang-3.6 CXX: clang++-3.6 LD: clang-3.6 CFLAGS: "-DMRB_INT16=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.6 64bit_int16_nan: stage: test image: registry.gitlab.com/dabroz/mruby:clang36_0.7 variables: CC: clang-3.6 CXX: clang++-3.6 LD: clang-3.6 CFLAGS: "-DMRB_INT16=1 -DMRB_NAN_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.6 64bit_int16_nan_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang36_0.7 variables: CC: clang-3.6 CXX: clang++-3.6 LD: clang-3.6 CFLAGS: "-DMRB_INT16=1 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.6 64bit_int64: stage: test image: registry.gitlab.com/dabroz/mruby:clang36_0.7 variables: CC: clang-3.6 CXX: clang++-3.6 LD: clang-3.6 CFLAGS: "-DMRB_INT64=1 -DMRB_WORD_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.6 64bit_int64_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang36_0.7 variables: CC: clang-3.6 CXX: clang++-3.6 LD: clang-3.6 CFLAGS: "-DMRB_INT64=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.6 64bit_float: stage: test image: registry.gitlab.com/dabroz/mruby:clang36_0.7 variables: CC: clang-3.6 CXX: clang++-3.6 LD: clang-3.6 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.6 64bit_float_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang36_0.7 variables: CC: clang-3.6 CXX: clang++-3.6 LD: clang-3.6 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.6 64bit_float_int16: stage: test image: registry.gitlab.com/dabroz/mruby:clang36_0.7 variables: CC: clang-3.6 CXX: clang++-3.6 LD: clang-3.6 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT16=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.6 64bit_float_int16_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang36_0.7 variables: CC: clang-3.6 CXX: clang++-3.6 LD: clang-3.6 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT16=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.6 64bit_float_int64: stage: test image: registry.gitlab.com/dabroz/mruby:clang36_0.7 variables: CC: clang-3.6 CXX: clang++-3.6 LD: clang-3.6 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_WORD_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.6 64bit_float_int64_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang36_0.7 variables: CC: clang-3.6 CXX: clang++-3.6 LD: clang-3.6 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.7 32bit: stage: test image: registry.gitlab.com/dabroz/mruby:clang37_0.7 variables: CC: clang-3.7 CXX: clang++-3.7 LD: clang-3.7 CFLAGS: "-m32 -DMRB_WORD_BOXING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.7 32bit_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang37_0.7 variables: CC: clang-3.7 CXX: clang++-3.7 LD: clang-3.7 CFLAGS: "-m32 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.7 32bit_nan: stage: test image: registry.gitlab.com/dabroz/mruby:clang37_0.7 variables: CC: clang-3.7 CXX: clang++-3.7 LD: clang-3.7 CFLAGS: "-m32 -DMRB_NAN_BOXING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.7 32bit_nan_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang37_0.7 variables: CC: clang-3.7 CXX: clang++-3.7 LD: clang-3.7 CFLAGS: "-m32 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.7 32bit_int16: stage: test image: registry.gitlab.com/dabroz/mruby:clang37_0.7 variables: CC: clang-3.7 CXX: clang++-3.7 LD: clang-3.7 CFLAGS: "-m32 -DMRB_INT16=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.7 32bit_int16_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang37_0.7 variables: CC: clang-3.7 CXX: clang++-3.7 LD: clang-3.7 CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.7 32bit_int16_nan: stage: test image: registry.gitlab.com/dabroz/mruby:clang37_0.7 variables: CC: clang-3.7 CXX: clang++-3.7 LD: clang-3.7 CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_NAN_BOXING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.7 32bit_int16_nan_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang37_0.7 variables: CC: clang-3.7 CXX: clang++-3.7 LD: clang-3.7 CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.7 32bit_int64: stage: test image: registry.gitlab.com/dabroz/mruby:clang37_0.7 variables: CC: clang-3.7 CXX: clang++-3.7 LD: clang-3.7 CFLAGS: "-m32 -DMRB_INT64=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.7 32bit_int64_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang37_0.7 variables: CC: clang-3.7 CXX: clang++-3.7 LD: clang-3.7 CFLAGS: "-m32 -DMRB_INT64=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.7 32bit_float: stage: test image: registry.gitlab.com/dabroz/mruby:clang37_0.7 variables: CC: clang-3.7 CXX: clang++-3.7 LD: clang-3.7 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.7 32bit_float_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang37_0.7 variables: CC: clang-3.7 CXX: clang++-3.7 LD: clang-3.7 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.7 32bit_float_int16: stage: test image: registry.gitlab.com/dabroz/mruby:clang37_0.7 variables: CC: clang-3.7 CXX: clang++-3.7 LD: clang-3.7 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT16=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.7 32bit_float_int16_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang37_0.7 variables: CC: clang-3.7 CXX: clang++-3.7 LD: clang-3.7 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT16=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.7 32bit_float_int64: stage: test image: registry.gitlab.com/dabroz/mruby:clang37_0.7 variables: CC: clang-3.7 CXX: clang++-3.7 LD: clang-3.7 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT64=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.7 32bit_float_int64_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang37_0.7 variables: CC: clang-3.7 CXX: clang++-3.7 LD: clang-3.7 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.7 64bit: stage: test image: registry.gitlab.com/dabroz/mruby:clang37_0.7 variables: CC: clang-3.7 CXX: clang++-3.7 LD: clang-3.7 CFLAGS: "-DMRB_WORD_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.7 64bit_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang37_0.7 variables: CC: clang-3.7 CXX: clang++-3.7 LD: clang-3.7 CFLAGS: "-DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.7 64bit_nan: stage: test image: registry.gitlab.com/dabroz/mruby:clang37_0.7 variables: CC: clang-3.7 CXX: clang++-3.7 LD: clang-3.7 CFLAGS: "-DMRB_NAN_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.7 64bit_nan_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang37_0.7 variables: CC: clang-3.7 CXX: clang++-3.7 LD: clang-3.7 CFLAGS: "-DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.7 64bit_int16: stage: test image: registry.gitlab.com/dabroz/mruby:clang37_0.7 variables: CC: clang-3.7 CXX: clang++-3.7 LD: clang-3.7 CFLAGS: "-DMRB_INT16=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.7 64bit_int16_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang37_0.7 variables: CC: clang-3.7 CXX: clang++-3.7 LD: clang-3.7 CFLAGS: "-DMRB_INT16=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.7 64bit_int16_nan: stage: test image: registry.gitlab.com/dabroz/mruby:clang37_0.7 variables: CC: clang-3.7 CXX: clang++-3.7 LD: clang-3.7 CFLAGS: "-DMRB_INT16=1 -DMRB_NAN_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.7 64bit_int16_nan_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang37_0.7 variables: CC: clang-3.7 CXX: clang++-3.7 LD: clang-3.7 CFLAGS: "-DMRB_INT16=1 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.7 64bit_int64: stage: test image: registry.gitlab.com/dabroz/mruby:clang37_0.7 variables: CC: clang-3.7 CXX: clang++-3.7 LD: clang-3.7 CFLAGS: "-DMRB_INT64=1 -DMRB_WORD_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.7 64bit_int64_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang37_0.7 variables: CC: clang-3.7 CXX: clang++-3.7 LD: clang-3.7 CFLAGS: "-DMRB_INT64=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.7 64bit_float: stage: test image: registry.gitlab.com/dabroz/mruby:clang37_0.7 variables: CC: clang-3.7 CXX: clang++-3.7 LD: clang-3.7 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.7 64bit_float_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang37_0.7 variables: CC: clang-3.7 CXX: clang++-3.7 LD: clang-3.7 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.7 64bit_float_int16: stage: test image: registry.gitlab.com/dabroz/mruby:clang37_0.7 variables: CC: clang-3.7 CXX: clang++-3.7 LD: clang-3.7 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT16=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.7 64bit_float_int16_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang37_0.7 variables: CC: clang-3.7 CXX: clang++-3.7 LD: clang-3.7 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT16=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.7 64bit_float_int64: stage: test image: registry.gitlab.com/dabroz/mruby:clang37_0.7 variables: CC: clang-3.7 CXX: clang++-3.7 LD: clang-3.7 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_WORD_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.7 64bit_float_int64_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang37_0.7 variables: CC: clang-3.7 CXX: clang++-3.7 LD: clang-3.7 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.8 32bit: stage: test image: registry.gitlab.com/dabroz/mruby:clang38_0.7 variables: CC: clang-3.8 CXX: clang++-3.8 LD: clang-3.8 CFLAGS: "-m32 -DMRB_WORD_BOXING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.8 32bit_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang38_0.7 variables: CC: clang-3.8 CXX: clang++-3.8 LD: clang-3.8 CFLAGS: "-m32 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.8 32bit_nan: stage: test image: registry.gitlab.com/dabroz/mruby:clang38_0.7 variables: CC: clang-3.8 CXX: clang++-3.8 LD: clang-3.8 CFLAGS: "-m32 -DMRB_NAN_BOXING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.8 32bit_nan_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang38_0.7 variables: CC: clang-3.8 CXX: clang++-3.8 LD: clang-3.8 CFLAGS: "-m32 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.8 32bit_int16: stage: test image: registry.gitlab.com/dabroz/mruby:clang38_0.7 variables: CC: clang-3.8 CXX: clang++-3.8 LD: clang-3.8 CFLAGS: "-m32 -DMRB_INT16=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.8 32bit_int16_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang38_0.7 variables: CC: clang-3.8 CXX: clang++-3.8 LD: clang-3.8 CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.8 32bit_int16_nan: stage: test image: registry.gitlab.com/dabroz/mruby:clang38_0.7 variables: CC: clang-3.8 CXX: clang++-3.8 LD: clang-3.8 CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_NAN_BOXING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.8 32bit_int16_nan_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang38_0.7 variables: CC: clang-3.8 CXX: clang++-3.8 LD: clang-3.8 CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.8 32bit_int64: stage: test image: registry.gitlab.com/dabroz/mruby:clang38_0.7 variables: CC: clang-3.8 CXX: clang++-3.8 LD: clang-3.8 CFLAGS: "-m32 -DMRB_INT64=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.8 32bit_int64_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang38_0.7 variables: CC: clang-3.8 CXX: clang++-3.8 LD: clang-3.8 CFLAGS: "-m32 -DMRB_INT64=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.8 32bit_float: stage: test image: registry.gitlab.com/dabroz/mruby:clang38_0.7 variables: CC: clang-3.8 CXX: clang++-3.8 LD: clang-3.8 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.8 32bit_float_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang38_0.7 variables: CC: clang-3.8 CXX: clang++-3.8 LD: clang-3.8 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.8 32bit_float_int16: stage: test image: registry.gitlab.com/dabroz/mruby:clang38_0.7 variables: CC: clang-3.8 CXX: clang++-3.8 LD: clang-3.8 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT16=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.8 32bit_float_int16_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang38_0.7 variables: CC: clang-3.8 CXX: clang++-3.8 LD: clang-3.8 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT16=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.8 32bit_float_int64: stage: test image: registry.gitlab.com/dabroz/mruby:clang38_0.7 variables: CC: clang-3.8 CXX: clang++-3.8 LD: clang-3.8 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT64=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.8 32bit_float_int64_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang38_0.7 variables: CC: clang-3.8 CXX: clang++-3.8 LD: clang-3.8 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.8 64bit: stage: test image: registry.gitlab.com/dabroz/mruby:clang38_0.7 variables: CC: clang-3.8 CXX: clang++-3.8 LD: clang-3.8 CFLAGS: "-DMRB_WORD_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.8 64bit_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang38_0.7 variables: CC: clang-3.8 CXX: clang++-3.8 LD: clang-3.8 CFLAGS: "-DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.8 64bit_nan: stage: test image: registry.gitlab.com/dabroz/mruby:clang38_0.7 variables: CC: clang-3.8 CXX: clang++-3.8 LD: clang-3.8 CFLAGS: "-DMRB_NAN_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.8 64bit_nan_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang38_0.7 variables: CC: clang-3.8 CXX: clang++-3.8 LD: clang-3.8 CFLAGS: "-DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.8 64bit_int16: stage: test image: registry.gitlab.com/dabroz/mruby:clang38_0.7 variables: CC: clang-3.8 CXX: clang++-3.8 LD: clang-3.8 CFLAGS: "-DMRB_INT16=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.8 64bit_int16_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang38_0.7 variables: CC: clang-3.8 CXX: clang++-3.8 LD: clang-3.8 CFLAGS: "-DMRB_INT16=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.8 64bit_int16_nan: stage: test image: registry.gitlab.com/dabroz/mruby:clang38_0.7 variables: CC: clang-3.8 CXX: clang++-3.8 LD: clang-3.8 CFLAGS: "-DMRB_INT16=1 -DMRB_NAN_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.8 64bit_int16_nan_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang38_0.7 variables: CC: clang-3.8 CXX: clang++-3.8 LD: clang-3.8 CFLAGS: "-DMRB_INT16=1 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.8 64bit_int64: stage: test image: registry.gitlab.com/dabroz/mruby:clang38_0.7 variables: CC: clang-3.8 CXX: clang++-3.8 LD: clang-3.8 CFLAGS: "-DMRB_INT64=1 -DMRB_WORD_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.8 64bit_int64_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang38_0.7 variables: CC: clang-3.8 CXX: clang++-3.8 LD: clang-3.8 CFLAGS: "-DMRB_INT64=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.8 64bit_float: stage: test image: registry.gitlab.com/dabroz/mruby:clang38_0.7 variables: CC: clang-3.8 CXX: clang++-3.8 LD: clang-3.8 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.8 64bit_float_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang38_0.7 variables: CC: clang-3.8 CXX: clang++-3.8 LD: clang-3.8 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.8 64bit_float_int16: stage: test image: registry.gitlab.com/dabroz/mruby:clang38_0.7 variables: CC: clang-3.8 CXX: clang++-3.8 LD: clang-3.8 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT16=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.8 64bit_float_int16_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang38_0.7 variables: CC: clang-3.8 CXX: clang++-3.8 LD: clang-3.8 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT16=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.8 64bit_float_int64: stage: test image: registry.gitlab.com/dabroz/mruby:clang38_0.7 variables: CC: clang-3.8 CXX: clang++-3.8 LD: clang-3.8 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_WORD_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.8 64bit_float_int64_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang38_0.7 variables: CC: clang-3.8 CXX: clang++-3.8 LD: clang-3.8 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.9 32bit: stage: test image: registry.gitlab.com/dabroz/mruby:clang39_0.7 variables: CC: clang-3.9 CXX: clang++-3.9 LD: clang-3.9 CFLAGS: "-m32 -DMRB_WORD_BOXING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.9 32bit_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang39_0.7 variables: CC: clang-3.9 CXX: clang++-3.9 LD: clang-3.9 CFLAGS: "-m32 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.9 32bit_nan: stage: test image: registry.gitlab.com/dabroz/mruby:clang39_0.7 variables: CC: clang-3.9 CXX: clang++-3.9 LD: clang-3.9 CFLAGS: "-m32 -DMRB_NAN_BOXING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.9 32bit_nan_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang39_0.7 variables: CC: clang-3.9 CXX: clang++-3.9 LD: clang-3.9 CFLAGS: "-m32 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.9 32bit_int16: stage: test image: registry.gitlab.com/dabroz/mruby:clang39_0.7 variables: CC: clang-3.9 CXX: clang++-3.9 LD: clang-3.9 CFLAGS: "-m32 -DMRB_INT16=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.9 32bit_int16_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang39_0.7 variables: CC: clang-3.9 CXX: clang++-3.9 LD: clang-3.9 CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.9 32bit_int16_nan: stage: test image: registry.gitlab.com/dabroz/mruby:clang39_0.7 variables: CC: clang-3.9 CXX: clang++-3.9 LD: clang-3.9 CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_NAN_BOXING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.9 32bit_int16_nan_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang39_0.7 variables: CC: clang-3.9 CXX: clang++-3.9 LD: clang-3.9 CFLAGS: "-m32 -DMRB_INT16=1 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.9 32bit_int64: stage: test image: registry.gitlab.com/dabroz/mruby:clang39_0.7 variables: CC: clang-3.9 CXX: clang++-3.9 LD: clang-3.9 CFLAGS: "-m32 -DMRB_INT64=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.9 32bit_int64_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang39_0.7 variables: CC: clang-3.9 CXX: clang++-3.9 LD: clang-3.9 CFLAGS: "-m32 -DMRB_INT64=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.9 32bit_float: stage: test image: registry.gitlab.com/dabroz/mruby:clang39_0.7 variables: CC: clang-3.9 CXX: clang++-3.9 LD: clang-3.9 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.9 32bit_float_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang39_0.7 variables: CC: clang-3.9 CXX: clang++-3.9 LD: clang-3.9 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.9 32bit_float_int16: stage: test image: registry.gitlab.com/dabroz/mruby:clang39_0.7 variables: CC: clang-3.9 CXX: clang++-3.9 LD: clang-3.9 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT16=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.9 32bit_float_int16_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang39_0.7 variables: CC: clang-3.9 CXX: clang++-3.9 LD: clang-3.9 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT16=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.9 32bit_float_int64: stage: test image: registry.gitlab.com/dabroz/mruby:clang39_0.7 variables: CC: clang-3.9 CXX: clang++-3.9 LD: clang-3.9 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT64=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.9 32bit_float_int64_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang39_0.7 variables: CC: clang-3.9 CXX: clang++-3.9 LD: clang-3.9 CFLAGS: "-m32 -DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_UTF8_STRING=1" LDFLAGS: "-m32" script: env; ./minirake --verbose all test Test clang-3.9 64bit: stage: test image: registry.gitlab.com/dabroz/mruby:clang39_0.7 variables: CC: clang-3.9 CXX: clang++-3.9 LD: clang-3.9 CFLAGS: "-DMRB_WORD_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.9 64bit_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang39_0.7 variables: CC: clang-3.9 CXX: clang++-3.9 LD: clang-3.9 CFLAGS: "-DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.9 64bit_nan: stage: test image: registry.gitlab.com/dabroz/mruby:clang39_0.7 variables: CC: clang-3.9 CXX: clang++-3.9 LD: clang-3.9 CFLAGS: "-DMRB_NAN_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.9 64bit_nan_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang39_0.7 variables: CC: clang-3.9 CXX: clang++-3.9 LD: clang-3.9 CFLAGS: "-DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.9 64bit_int16: stage: test image: registry.gitlab.com/dabroz/mruby:clang39_0.7 variables: CC: clang-3.9 CXX: clang++-3.9 LD: clang-3.9 CFLAGS: "-DMRB_INT16=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.9 64bit_int16_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang39_0.7 variables: CC: clang-3.9 CXX: clang++-3.9 LD: clang-3.9 CFLAGS: "-DMRB_INT16=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.9 64bit_int16_nan: stage: test image: registry.gitlab.com/dabroz/mruby:clang39_0.7 variables: CC: clang-3.9 CXX: clang++-3.9 LD: clang-3.9 CFLAGS: "-DMRB_INT16=1 -DMRB_NAN_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.9 64bit_int16_nan_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang39_0.7 variables: CC: clang-3.9 CXX: clang++-3.9 LD: clang-3.9 CFLAGS: "-DMRB_INT16=1 -DMRB_NAN_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.9 64bit_int64: stage: test image: registry.gitlab.com/dabroz/mruby:clang39_0.7 variables: CC: clang-3.9 CXX: clang++-3.9 LD: clang-3.9 CFLAGS: "-DMRB_INT64=1 -DMRB_WORD_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.9 64bit_int64_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang39_0.7 variables: CC: clang-3.9 CXX: clang++-3.9 LD: clang-3.9 CFLAGS: "-DMRB_INT64=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.9 64bit_float: stage: test image: registry.gitlab.com/dabroz/mruby:clang39_0.7 variables: CC: clang-3.9 CXX: clang++-3.9 LD: clang-3.9 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.9 64bit_float_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang39_0.7 variables: CC: clang-3.9 CXX: clang++-3.9 LD: clang-3.9 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.9 64bit_float_int16: stage: test image: registry.gitlab.com/dabroz/mruby:clang39_0.7 variables: CC: clang-3.9 CXX: clang++-3.9 LD: clang-3.9 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT16=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.9 64bit_float_int16_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang39_0.7 variables: CC: clang-3.9 CXX: clang++-3.9 LD: clang-3.9 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT16=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.9 64bit_float_int64: stage: test image: registry.gitlab.com/dabroz/mruby:clang39_0.7 variables: CC: clang-3.9 CXX: clang++-3.9 LD: clang-3.9 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_WORD_BOXING=1" LDFLAGS: '' script: env; ./minirake --verbose all test Test clang-3.9 64bit_float_int64_utf8: stage: test image: registry.gitlab.com/dabroz/mruby:clang39_0.7 variables: CC: clang-3.9 CXX: clang++-3.9 LD: clang-3.9 CFLAGS: "-DMRB_USE_FLOAT=1 -DMRB_INT64=1 -DMRB_WORD_BOXING=1 -DMRB_UTF8_STRING=1" LDFLAGS: '' script: env; ./minirake --verbose all test mruby-2.0.0/.travis.yml000066400000000000000000000003551340361412400147610ustar00rootroot00000000000000language: c sudo: false matrix: include: - os: linux sudo: false - os: osx osx_image: xcode7.1 addons: apt: packages: - gperf env: - MRUBY_CONFIG=travis_config.rb script: "./minirake -j4 all test" mruby-2.0.0/.yardopts000066400000000000000000000003501340361412400145110ustar00rootroot00000000000000--markup markdown --plugin mruby --plugin coderay --output-dir doc/api src/**/*.c mrblib/**/*.rb include/**/*.h mrbgems/*/src/**/*.c mrbgems/*/mrblib/**/*.rb mrbgems/*/include/**/*.h - AUTHORS MITL CONTRIBUTING.md doc/guides/*.md mruby-2.0.0/AUTHORS000066400000000000000000000016261340361412400137220ustar00rootroot00000000000000Original Authors "mruby developers" are: Yukihiro Matsumoto SCSK KYUSHU CORPORATION Kyushu Institute of Technology Network Applied Communication Laboratory, Inc. Daniel Bovensiepen Jon Maken Bjorn De Meyer Yuichiro MASUI Masamitsu MURASE Masaki Muranaka Internet Initiative Japan Inc. Tadashi FUKUZAWA MATSUMOTO Ryosuke Yasuhiro Matsumoto Koji Yoshioka Jun Hiroe Narihiro Nakamura Yuichi Nishiwaki Tatsuhiko Kubo Takeshi Watanabe Yuki Kurihara specified non-profit corporation mruby Forum Kazuaki Tanaka Hiromasa Ishii Hiroshi Mimaki Satoshi Odawara Mitsubishi Electric Micro-Computer Application Software Co.,Ltd. Ralph Desir(Mav7) Hiroyuki Matsuzaki Yuhei Okazaki Manycolors, Inc. Shota Nakano Yuichi Osawa Terence Lee Zachary Scott Tomasz Dąbrowski Christopher Aue Masahiro Wakame YAMAMOTO Masaya mruby-2.0.0/CONTRIBUTING.md000066400000000000000000000044651340361412400151070ustar00rootroot00000000000000# How to contribute mruby is an open-source project which is looking forward to each contribution. ## Your Pull Request To make it easy to review and understand your change please keep the following things in mind before submitting your pull request: * Work on the latest possible state of **mruby/master** * Create a branch which is dedicated to your change * Test your changes before creating a pull request (```./minirake test```) * If possible write a test case which confirms your change * Don't mix several features or bug-fixes in one pull request * Create a meaningful commit message * Explain your change (i.e. with a link to the issue you are fixing) * Use mrbgem to provide non ISO features (classes, modules and methods) unless you have a special reason to implement them in the core ## Coding conventions How to style your C and Ruby code which you want to submit. ### C code The core part (parser, bytecode-interpreter, core-lib, etc.) of mruby is written in the C programming language. Please note the following hints for your C code: #### Comply with C99 (ISO/IEC 9899:1999) mruby should be highly portable to other systems and compilers. For this it is recommended to keep your code as close as possible to the C99 standard (http://www.open-std.org/jtc1/sc22/WG14/www/docs/n1256.pdf). Although we target C99, Visual C++ is also an important target for mruby. For this reason a declaration of a local variable has to be at the beginning of a scope block. #### Reduce library dependencies to a minimum The dependencies to libraries should be kept to an absolute minimum. This increases the portability but makes it also easier to cut away parts of mruby on-demand. #### Don't use C++ style comments /* This is the preferred comment style */ Use C++ style comments only for temporary comment e.g. commenting out some code lines. #### Insert a break after the method return value: int main(void) { ... } ### Ruby code Parts of the standard library of mruby are written in the Ruby programming language itself. Please note the following hints for your Ruby code: #### Comply with the Ruby standard (ISO/IEC 30170:2012) mruby is currently targeting to execute Ruby code which complies to ISO/IEC 30170:2012 (http://www.iso.org/iso/iso_catalogue/catalogue_tc/catalogue_detail.htm?csnumber=59579). mruby-2.0.0/LEGAL000066400000000000000000000002601340361412400134120ustar00rootroot00000000000000LEGAL NOTICE INFORMATION ------------------------ All the files in this distribution are covered under the MIT license (see the file MITL) except some files mentioned below: mruby-2.0.0/MITL000066400000000000000000000020451340361412400133360ustar00rootroot00000000000000Copyright (c) 2018 mruby developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. mruby-2.0.0/Makefile000066400000000000000000000004251340361412400143060ustar00rootroot00000000000000# mruby is using Rake (http://rake.rubyforge.org) as a build tool. # We provide a minimalistic version called minirake inside of our # codebase. RAKE = ruby ./minirake all : $(RAKE) .PHONY : all test : all $(RAKE) test .PHONY : test clean : $(RAKE) clean .PHONY : clean mruby-2.0.0/NEWS000066400000000000000000000005271340361412400133500ustar00rootroot00000000000000* NEWS This document is a list of user visible feature changes made between releases except for bug fixes. Note that each entry is kept so brief that no reason behind or reference information is supplied with. For a full list of changes with all sufficient information, see the ChangeLog file. ** Information about first release v1.0.0 mruby-2.0.0/README.md000066400000000000000000000074321340361412400141320ustar00rootroot00000000000000[![Build Status][build-status-img]][travis-ci] ## What is mruby mruby is the lightweight implementation of the Ruby language complying to (part of) the [ISO standard][ISO-standard]. Its syntax is Ruby 1.9 compatible. mruby can be linked and embedded within your application. We provide the interpreter program "mruby" and the interactive mruby shell "mirb" as examples. You can also compile Ruby programs into compiled byte code using the mruby compiler "mrbc". All those tools reside in the "bin" directory. "mrbc" is also able to generate compiled byte code in a C source file, see the "mrbtest" program under the "test" directory for an example. This achievement was sponsored by the Regional Innovation Creation R&D Programs of the Ministry of Economy, Trade and Industry of Japan. ## How to get mruby The stable version 2.0.0 of mruby can be downloaded via the following URL: [https://github.com/mruby/mruby/archive/2.0.0.zip](https://github.com/mruby/mruby/archive/2.0.0.zip) The latest development version of mruby can be downloaded via the following URL: [https://github.com/mruby/mruby/zipball/master](https://github.com/mruby/mruby/zipball/master) The trunk of the mruby source tree can be checked out with the following command: $ git clone https://github.com/mruby/mruby.git You can also install and compile mruby using [ruby-install](https://github.com/postmodern/ruby-install), [ruby-build](https://github.com/rbenv/ruby-build) or [rvm](https://github.com/rvm/rvm). ## mruby home-page The URL of the mruby home-page is: [http://www.mruby.org](http://www.mruby.org). ## Mailing list We don't have a mailing list, but you can use [GitHub issues](https://github.com/mruby/mruby). ## How to compile and install (mruby and gems) See the [doc/guides/compile.md](doc/guides/compile.md) file. ## Running Tests To run the tests, execute the following from the project's root directory. $ make test Or $ ruby ./minirake test ## How to customize mruby (mrbgems) mruby contains a package manager called *mrbgems*. To create extensions in C and/or Ruby you should create a *GEM*. For a documentation of how to use mrbgems consult the file [doc/guides/mrbgems.md](doc/guides/mrbgems.md). For example code of how to use mrbgems look into the folder *examples/mrbgems/*. ## License mruby is released under the [MIT License](MITL). ## Note for License mruby has chosen a MIT License due to its permissive license allowing developers to target various environments such as embedded systems. However, the license requires the display of the copyright notice and license information in manuals for instance. Doing so for big projects can be complicated or troublesome. This is why mruby has decided to display "mruby developers" as the copyright name to make it simple conventionally. In the future, mruby might ask you to distribute your new code (that you will commit,) under the MIT License as a member of "mruby developers" but contributors will keep their copyright. (We did not intend for contributors to transfer or waive their copyrights, Actual copyright holder name (contributors) will be listed in the AUTHORS file.) Please ask us if you want to distribute your code under another license. ## How to Contribute See the [contribution guidelines][contribution-guidelines], and then send a pull request to . We consider you have granted non-exclusive right to your contributed code under MIT license. If you want to be named as one of mruby developers, please include an update to the AUTHORS file in your pull request. [ISO-standard]: http://www.iso.org/iso/iso_catalogue/catalogue_tc/catalogue_detail.htm?csnumber=59579 [build-status-img]: https://travis-ci.org/mruby/mruby.svg?branch=master [contribution-guidelines]: CONTRIBUTING.md [travis-ci]: https://travis-ci.org/mruby/mruby mruby-2.0.0/Rakefile000066400000000000000000000113241340361412400143130ustar00rootroot00000000000000# encoding: utf-8 # Build description. # basic build file for mruby MRUBY_ROOT = File.dirname(File.expand_path(__FILE__)) MRUBY_BUILD_HOST_IS_CYGWIN = RUBY_PLATFORM.include?('cygwin') MRUBY_BUILD_HOST_IS_OPENBSD = RUBY_PLATFORM.include?('openbsd') $LOAD_PATH << File.join(MRUBY_ROOT, "lib") # load build systems require "mruby-core-ext" require "mruby/build" require "mruby/gem" # load configuration file MRUBY_CONFIG = (ENV['MRUBY_CONFIG'] && ENV['MRUBY_CONFIG'] != '') ? ENV['MRUBY_CONFIG'] : "#{MRUBY_ROOT}/build_config.rb" load MRUBY_CONFIG # load basic rules MRuby.each_target do |build| build.define_rules end # load custom rules load "#{MRUBY_ROOT}/src/mruby_core.rake" load "#{MRUBY_ROOT}/mrblib/mrblib.rake" load "#{MRUBY_ROOT}/tasks/mrbgems.rake" load "#{MRUBY_ROOT}/tasks/libmruby.rake" load "#{MRUBY_ROOT}/tasks/benchmark.rake" load "#{MRUBY_ROOT}/tasks/gitlab.rake" ############################## # generic build targets, rules task :default => :all bin_path = ENV['INSTALL_DIR'] || "#{MRUBY_ROOT}/bin" FileUtils.mkdir_p bin_path, { :verbose => $verbose } depfiles = MRuby.targets['host'].bins.map do |bin| install_path = MRuby.targets['host'].exefile("#{bin_path}/#{bin}") source_path = MRuby.targets['host'].exefile("#{MRuby.targets['host'].build_dir}/bin/#{bin}") file install_path => source_path do |t| FileUtils.rm_f t.name, { :verbose => $verbose } FileUtils.cp t.prerequisites.first, t.name, { :verbose => $verbose } end install_path end MRuby.each_target do |target| gems.map do |gem| current_dir = gem.dir.relative_path_from(Dir.pwd) relative_from_root = gem.dir.relative_path_from(MRUBY_ROOT) current_build_dir = File.expand_path "#{build_dir}/#{relative_from_root}" if current_build_dir !~ /^#{build_dir}/ current_build_dir = "#{build_dir}/mrbgems/#{gem.name}" end gem.bins.each do |bin| exec = exefile("#{build_dir}/bin/#{bin}") objs = Dir.glob("#{current_dir}/tools/#{bin}/*.{c,cpp,cxx,cc}").map { |f| objfile(f.pathmap("#{current_build_dir}/tools/#{bin}/%n")) } file exec => objs + target.libraries do |t| gem_flags = gems.map { |g| g.linker.flags } gem_flags_before_libraries = gems.map { |g| g.linker.flags_before_libraries } gem_flags_after_libraries = gems.map { |g| g.linker.flags_after_libraries } gem_libraries = gems.map { |g| g.linker.libraries } gem_library_paths = gems.map { |g| g.linker.library_paths } linker.run t.name, t.prerequisites, gem_libraries, gem_library_paths, gem_flags, gem_flags_before_libraries, gem_flags_after_libraries end if target == MRuby.targets['host'] install_path = MRuby.targets['host'].exefile("#{bin_path}/#{bin}") file install_path => exec do |t| FileUtils.rm_f t.name, { :verbose => $verbose } FileUtils.cp t.prerequisites.first, t.name, { :verbose => $verbose } end depfiles += [ install_path ] elsif target == MRuby.targets['host-debug'] unless MRuby.targets['host'].gems.map {|g| g.bins}.include?([bin]) install_path = MRuby.targets['host-debug'].exefile("#{bin_path}/#{bin}") file install_path => exec do |t| FileUtils.rm_f t.name, { :verbose => $verbose } FileUtils.cp t.prerequisites.first, t.name, { :verbose => $verbose } end depfiles += [ install_path ] end else depfiles += [ exec ] end end end end depfiles += MRuby.targets.map { |n, t| t.libraries }.flatten depfiles += MRuby.targets.reject { |n, t| n == 'host' }.map { |n, t| t.bins.map { |bin| t.exefile("#{t.build_dir}/bin/#{bin}") } }.flatten desc "build all targets, install (locally) in-repo" task :all => depfiles do puts puts "Build summary:" puts MRuby.each_target do print_build_summary end end desc "run all mruby tests" MRuby.each_target do next unless test_enabled? t = :"test_#{self.name}" task t => ["all"] do run_test end task :test => t next unless bintest_enabled? t = :"bintest_#{self.name}" task t => ["all"] do run_bintest end task :test => t end desc "clean all built and in-repo installed artifacts" task :clean do MRuby.each_target do |t| FileUtils.rm_rf t.build_dir, { :verbose => $verbose } end FileUtils.rm_f depfiles, { :verbose => $verbose } puts "Cleaned up target build folder" end desc "clean everything!" task :deep_clean => ["clean"] do MRuby.each_target do |t| FileUtils.rm_rf t.gem_clone_dir, { :verbose => $verbose } end puts "Cleaned up mrbgems build folder" end desc 'generate document' task :doc do begin sh "mrbdoc" rescue puts "ERROR: To generate documents, you should install yard-mruby gem." puts " $ gem install yard-mruby" end end mruby-2.0.0/TODO000066400000000000000000000003751340361412400133420ustar00rootroot00000000000000Things to do (Things that are not done yet) * special variables ($1,$2..) * super in aliased methods * multi-assignment decomposing * keyword arguments in def statement Things to improve (Done but things to fix) * Make additions as they are noticed. mruby-2.0.0/appveyor.yml000066400000000000000000000016121340361412400152350ustar00rootroot00000000000000version: "{build}" os: Visual Studio 2015 clone_depth: 50 cache: - win_flex_bison-2.5.10.zip environment: matrix: # Visual Studio 2015 64bit - visualcpp: C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat machine: amd64 # Visual Studio 2013 64bit - visualcpp: C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat machine: amd64 init: - call "%visualcpp%" %machine% # For using Rubyinstaller's Ruby 2.4 64bit - set PATH=C:\Ruby24-x64\bin;%PATH% - ruby --version install: - if not exist win_flex_bison-2.5.10.zip appveyor DownloadFile "https://github.com/lexxmark/winflexbison/releases/download/v.2.5.10/win_flex_bison-2.5.10.zip" - 7z x -y -owin_flex_bison win_flex_bison-2.5.10.zip > nul build_script: - set YACC=.\win_flex_bison\win_bison.exe - set MRUBY_CONFIG=appveyor_config.rb - ruby .\minirake test all mruby-2.0.0/appveyor_config.rb000066400000000000000000000016411340361412400163660ustar00rootroot00000000000000MRuby::Build.new('debug') do |conf| toolchain :visualcpp enable_debug # include all core GEMs conf.gembox 'full-core' conf.compilers.each do |c| c.defines += %w(MRB_GC_STRESS MRB_GC_FIXED_ARENA MRB_METHOD_CACHE) end build_mrbc_exec end MRuby::Build.new('full-debug') do |conf| toolchain :visualcpp enable_debug # include all core GEMs conf.gembox 'full-core' conf.cc.defines = %w(MRB_ENABLE_DEBUG_HOOK) conf.enable_test end MRuby::Build.new do |conf| toolchain :visualcpp # include all core GEMs conf.gembox 'full-core' conf.compilers.each do |c| c.defines += %w(MRB_GC_FIXED_ARENA) end conf.enable_bintest conf.enable_test end MRuby::Build.new('cxx_abi') do |conf| toolchain :visualcpp conf.gembox 'full-core' conf.compilers.each do |c| c.defines += %w(MRB_GC_FIXED_ARENA) end conf.enable_bintest conf.enable_test enable_cxx_abi build_mrbc_exec end mruby-2.0.0/benchmark/000077500000000000000000000000001340361412400145775ustar00rootroot00000000000000mruby-2.0.0/benchmark/bm_ao_render.rb000066400000000000000000000151551340361412400175470ustar00rootroot00000000000000# AO render benchmark # Original program (C) Syoyo Fujita in Javascript (and other languages) # https://code.google.com/p/aobench/ # Ruby(yarv2llvm) version by Hideki Miura # mruby version by Hideki Miura # IMAGE_WIDTH = 64 IMAGE_HEIGHT = 64 NSUBSAMPLES = 2 NAO_SAMPLES = 8 module Rand # Use xorshift @@x = 123456789 @@y = 362436069 @@z = 521288629 @@w = 88675123 BNUM = 1 << 29 BNUMF = BNUM.to_f def self.rand x = @@x t = x ^ ((x & 0xfffff) << 11) w = @@w @@x, @@y, @@z = @@y, @@z, w w = @@w = (w ^ (w >> 19) ^ (t ^ (t >> 8))) (w % BNUM) / BNUMF end end class Vec def initialize(x, y, z) @x = x @y = y @z = z end def x=(v); @x = v; end def y=(v); @y = v; end def z=(v); @z = v; end def x; @x; end def y; @y; end def z; @z; end def vadd(b) Vec.new(@x + b.x, @y + b.y, @z + b.z) end def vsub(b) Vec.new(@x - b.x, @y - b.y, @z - b.z) end def vcross(b) Vec.new(@y * b.z - @z * b.y, @z * b.x - @x * b.z, @x * b.y - @y * b.x) end def vdot(b) r = @x * b.x + @y * b.y + @z * b.z r end def vlength Math.sqrt(@x * @x + @y * @y + @z * @z) end def vnormalize len = vlength v = Vec.new(@x, @y, @z) if len > 1.0e-17 v.x = v.x / len v.y = v.y / len v.z = v.z / len end v end end class Sphere def initialize(center, radius) @center = center @radius = radius end def center; @center; end def radius; @radius; end def intersect(ray, isect) rs = ray.org.vsub(@center) b = rs.vdot(ray.dir) c = rs.vdot(rs) - (@radius * @radius) d = b * b - c if d > 0.0 t = - b - Math.sqrt(d) if t > 0.0 and t < isect.t isect.t = t isect.hit = true isect.pl = Vec.new(ray.org.x + ray.dir.x * t, ray.org.y + ray.dir.y * t, ray.org.z + ray.dir.z * t) n = isect.pl.vsub(@center) isect.n = n.vnormalize end end end end class Plane def initialize(p, n) @p = p @n = n end def intersect(ray, isect) d = -@p.vdot(@n) v = ray.dir.vdot(@n) v0 = v if v < 0.0 v0 = -v end if v0 < 1.0e-17 return end t = -(ray.org.vdot(@n) + d) / v if t > 0.0 and t < isect.t isect.hit = true isect.t = t isect.n = @n isect.pl = Vec.new(ray.org.x + t * ray.dir.x, ray.org.y + t * ray.dir.y, ray.org.z + t * ray.dir.z) end end end class Ray def initialize(org, dir) @org = org @dir = dir end def org; @org; end def org=(v); @org = v; end def dir; @dir; end def dir=(v); @dir = v; end end class Isect def initialize @t = 10000000.0 @hit = false @pl = Vec.new(0.0, 0.0, 0.0) @n = Vec.new(0.0, 0.0, 0.0) end def t; @t; end def t=(v); @t = v; end def hit; @hit; end def hit=(v); @hit = v; end def pl; @pl; end def pl=(v); @pl = v; end def n; @n; end def n=(v); @n = v; end end def clamp(f) i = f * 255.5 if i > 255.0 i = 255.0 end if i < 0.0 i = 0.0 end i.to_i end def otherBasis(basis, n) basis[2] = Vec.new(n.x, n.y, n.z) basis[1] = Vec.new(0.0, 0.0, 0.0) if n.x < 0.6 and n.x > -0.6 basis[1].x = 1.0 elsif n.y < 0.6 and n.y > -0.6 basis[1].y = 1.0 elsif n.z < 0.6 and n.z > -0.6 basis[1].z = 1.0 else basis[1].x = 1.0 end basis[0] = basis[1].vcross(basis[2]) basis[0] = basis[0].vnormalize basis[1] = basis[2].vcross(basis[0]) basis[1] = basis[1].vnormalize end class Scene def initialize @spheres = Array.new @spheres[0] = Sphere.new(Vec.new(-2.0, 0.0, -3.5), 0.5) @spheres[1] = Sphere.new(Vec.new(-0.5, 0.0, -3.0), 0.5) @spheres[2] = Sphere.new(Vec.new(1.0, 0.0, -2.2), 0.5) @plane = Plane.new(Vec.new(0.0, -0.5, 0.0), Vec.new(0.0, 1.0, 0.0)) end def ambient_occlusion(isect) basis = Array.new(3) otherBasis(basis, isect.n) ntheta = NAO_SAMPLES nphi = NAO_SAMPLES eps = 0.0001 occlusion = 0.0 p0 = Vec.new(isect.pl.x + eps * isect.n.x, isect.pl.y + eps * isect.n.y, isect.pl.z + eps * isect.n.z) nphi.times do ntheta.times do r = Rand::rand phi = 2.0 * 3.14159265 * Rand::rand x = Math.cos(phi) * Math.sqrt(1.0 - r) y = Math.sin(phi) * Math.sqrt(1.0 - r) z = Math.sqrt(r) rx = x * basis[0].x + y * basis[1].x + z * basis[2].x ry = x * basis[0].y + y * basis[1].y + z * basis[2].y rz = x * basis[0].z + y * basis[1].z + z * basis[2].z raydir = Vec.new(rx, ry, rz) ray = Ray.new(p0, raydir) occisect = Isect.new @spheres[0].intersect(ray, occisect) @spheres[1].intersect(ray, occisect) @spheres[2].intersect(ray, occisect) @plane.intersect(ray, occisect) if occisect.hit occlusion = occlusion + 1.0 else 0.0 end end end occlusion = (ntheta.to_f * nphi.to_f - occlusion) / (ntheta.to_f * nphi.to_f) Vec.new(occlusion, occlusion, occlusion) end def render(w, h, nsubsamples) cnt = 0 nsf = nsubsamples.to_f h.times do |y| w.times do |x| rad = Vec.new(0.0, 0.0, 0.0) # Subsmpling nsubsamples.times do |v| nsubsamples.times do |u| cnt = cnt + 1 wf = w.to_f hf = h.to_f xf = x.to_f yf = y.to_f uf = u.to_f vf = v.to_f px = (xf + (uf / nsf) - (wf / 2.0)) / (wf / 2.0) py = -(yf + (vf / nsf) - (hf / 2.0)) / (hf / 2.0) eye = Vec.new(px, py, -1.0).vnormalize ray = Ray.new(Vec.new(0.0, 0.0, 0.0), eye) isect = Isect.new @spheres[0].intersect(ray, isect) @spheres[1].intersect(ray, isect) @spheres[2].intersect(ray, isect) @plane.intersect(ray, isect) if isect.hit col = ambient_occlusion(isect) rad.x = rad.x + col.x rad.y = rad.y + col.y rad.z = rad.z + col.z else 0.0 end end end r = rad.x / (nsf * nsf) g = rad.y / (nsf * nsf) b = rad.z / (nsf * nsf) printf("%c", clamp(r)) printf("%c", clamp(g)) printf("%c", clamp(b)) end end end end # File.open("ao.ppm", "w") do |fp| printf("P6\n") printf("%d %d\n", IMAGE_WIDTH, IMAGE_HEIGHT) printf("255\n", IMAGE_WIDTH, IMAGE_HEIGHT) Scene.new.render(IMAGE_WIDTH, IMAGE_HEIGHT, NSUBSAMPLES) # Scene.new.render(256, 256, 2) # end mruby-2.0.0/benchmark/bm_app_lc_fizzbuzz.rb000066400000000000000000000367301340361412400210260ustar00rootroot00000000000000# # FizzBuzz program using only lambda calculus # # This program is quoted from # "Understanding Computation" by Tom Stuart # http://computationbook.com/ # # You can understand why this program works fine by reading this book. # solution = -> k { -> f { -> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> l { -> x { -> g { -> b { b }[-> p { p[-> x { -> y { x } }] }[l]][x][-> y { g[f[-> l { -> p { p[-> x { -> y { y } }] }[-> p { p[-> x { -> y { y } }] }[l]] }[l]][x][g]][-> l { -> p { p[-> x { -> y { x } }] }[-> p { p[-> x { -> y { y } }] }[l]] }[l]][y] }] } } } }][k][-> x { -> y { -> f { f[x][y] } } }[-> x { -> y { x } }][-> x { -> y { x } }]][-> l { -> x { -> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[l][f[x]] } }] } }[-> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> m { -> n { -> b { b }[-> m { -> n { -> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]] } }[m][n]][-> x { -> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[f[-> n { -> p { -> x { p[n[p][x]] } } }[m]][n]][m][x] }][-> x { -> y { -> f { f[x][y] } } }[-> x { -> y { x } }][-> x { -> y { x } }]] } } }][-> p { -> x { p[x] } }][-> p { -> x { p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[x]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] } }]][-> n { -> b { b }[-> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> m { -> n { -> b { b }[-> m { -> n { -> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]] } }[n][m]][-> x { f[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]][n][x] }][m] } } }][n][-> p { -> x { p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[x]]]]]]]]]]]]]]] } }]]][-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> x { -> y { -> f { f[x][y] } } }[-> x { -> y { x } }][-> x { -> y { x } }]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]][-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]][-> b { b }[-> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> m { -> n { -> b { b }[-> m { -> n { -> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]] } }[n][m]][-> x { f[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]][n][x] }][m] } } }][n][-> p { -> x { p[p[p[x]]] } }]]][-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> x { -> y { -> f { f[x][y] } } }[-> x { -> y { x } }][-> x { -> y { x } }]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]][-> b { b }[-> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> m { -> n { -> b { b }[-> m { -> n { -> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]] } }[n][m]][-> x { f[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]][n][x] }][m] } } }][n][-> p { -> x { p[p[p[p[p[x]]]]] } }]]][-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> x { -> y { -> f { f[x][y] } } }[-> x { -> y { x } }][-> x { -> y { x } }]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]][-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]][-> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> n { -> l { -> x { -> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> l { -> x { -> g { -> b { b }[-> p { p[-> x { -> y { x } }] }[l]][x][-> y { g[f[-> l { -> p { p[-> x { -> y { y } }] }[-> p { p[-> x { -> y { y } }] }[l]] }[l]][x][g]][-> l { -> p { p[-> x { -> y { x } }] }[-> p { p[-> x { -> y { y } }] }[l]] }[l]][y] }] } } } }][l][-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> x { -> y { -> f { f[x][y] } } }[-> x { -> y { x } }][-> x { -> y { x } }]][x]][-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }] } }[-> b { b }[-> m { -> n { -> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]] } }[n][-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]][-> x { -> y { -> f { f[x][y] } } }[-> x { -> y { x } }][-> x { -> y { x } }]][-> x { f[-> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> m { -> n { -> b { b }[-> m { -> n { -> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]] } }[n][m]][-> x { -> n { -> p { -> x { p[n[p][x]] } } }[f[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]][n]][x] }][-> p { -> x { x } }] } } }][n][-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]][x] }]][-> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> m { -> n { -> b { b }[-> m { -> n { -> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]] } }[n][m]][-> x { f[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]][n][x] }][m] } } }][n][-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]] } }][n]]]] }] FIRST = -> l { LEFT[RIGHT[l]] } IF = -> b { b } LEFT = -> p { p[-> x { -> y { x } } ] } RIGHT = -> p { p[-> x { -> y { y } } ] } IS_EMPTY = LEFT REST = -> l { RIGHT[RIGHT[l]] } def to_integer(proc) proc[-> n { n + 1 }][0] end def to_boolean(proc) IF[proc][true][false] end def to_array(proc) array = [] until to_boolean(IS_EMPTY[proc]) array.push(FIRST[proc]) proc = REST[proc] end array end def to_char(c) '0123456789BFiuz'.slice(to_integer(c)) end def to_string(s) to_array(s).map { |c| to_char(c) }.join end answer = to_array(solution).map do |p| to_string(p) end answer_str = answer.to_a # puts answer_str mruby-2.0.0/benchmark/bm_fib.rb000066400000000000000000000001071340361412400163400ustar00rootroot00000000000000 def fib n return n if n < 2 fib(n-2) + fib(n-1) end puts fib(37) mruby-2.0.0/benchmark/bm_so_lists.rb000066400000000000000000000017311340361412400174430ustar00rootroot00000000000000#from http://www.bagley.org/~doug/shootout/bench/lists/lists.ruby NUM = 300 SIZE = 10000 def test_lists() # create a list of integers (Li1) from 1 to SIZE li1 = (1..SIZE).to_a # copy the list to li2 (not by individual items) li2 = li1.dup # remove each individual item from left side of li2 and # append to right side of li3 (preserving order) li3 = Array.new while (not li2.empty?) li3.push(li2.shift) end # li2 must now be empty # remove each individual item from right side of li3 and # append to right side of li2 (reversing list) until li3.empty? li2.push(li3.pop) end # li3 must now be empty # reverse li1 in place li1.reverse! # check that first item is now SIZE if li1[0] != SIZE p "not SIZE" 0 else # compare li1 and li2 for equality if li1 != li2 return(0) else # return the length of the list li1.length end end end i = 0 while i 'mruby-eval' # conf.gem :mgem => 'mruby-io' # conf.gem :github => 'iij/mruby-io' # conf.gem :git => 'git@github.com:iij/mruby-io.git', :branch => 'master', :options => '-v' # include the default GEMs conf.gembox 'default' # C compiler settings # conf.cc do |cc| # cc.command = ENV['CC'] || 'gcc' # cc.flags = [ENV['CFLAGS'] || %w()] # cc.include_paths = ["#{root}/include"] # cc.defines = %w(DISABLE_GEMS) # cc.option_include_path = '-I%s' # cc.option_define = '-D%s' # cc.compile_options = "%{flags} -MMD -o %{outfile} -c %{infile}" # end # mrbc settings # conf.mrbc do |mrbc| # mrbc.compile_options = "-g -B%{funcname} -o-" # The -g option is required for line numbers # end # Linker settings # conf.linker do |linker| # linker.command = ENV['LD'] || 'gcc' # linker.flags = [ENV['LDFLAGS'] || []] # linker.flags_before_libraries = [] # linker.libraries = %w() # linker.flags_after_libraries = [] # linker.library_paths = [] # linker.option_library = '-l%s' # linker.option_library_path = '-L%s' # linker.link_options = "%{flags} -o %{outfile} %{objs} %{libs}" # end # Archiver settings # conf.archiver do |archiver| # archiver.command = ENV['AR'] || 'ar' # archiver.archive_options = 'rs %{outfile} %{objs}' # end # Parser generator settings # conf.yacc do |yacc| # yacc.command = ENV['YACC'] || 'bison' # yacc.compile_options = '-o %{outfile} %{infile}' # end # gperf settings # conf.gperf do |gperf| # gperf.command = 'gperf' # gperf.compile_options = '-L ANSI-C -C -p -j1 -i 1 -g -o -t -N mrb_reserved_word -k"1,3,$" %{infile} > %{outfile}' # end # file extensions # conf.exts do |exts| # exts.object = '.o' # exts.executable = '' # '.exe' if Windows # exts.library = '.a' # end # file separetor # conf.file_separator = '/' # bintest # conf.enable_bintest end MRuby::Build.new('host-debug') do |conf| # load specific toolchain settings # Gets set by the VS command prompts. if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR'] toolchain :visualcpp else toolchain :gcc end enable_debug # include the default GEMs conf.gembox 'default' # C compiler settings conf.cc.defines = %w(MRB_ENABLE_DEBUG_HOOK) # Generate mruby debugger command (require mruby-eval) conf.gem :core => "mruby-bin-debugger" # bintest # conf.enable_bintest end MRuby::Build.new('test') do |conf| # Gets set by the VS command prompts. if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR'] toolchain :visualcpp else toolchain :gcc end enable_debug conf.enable_bintest conf.enable_test conf.gembox 'default' end #MRuby::Build.new('bench') do |conf| # # Gets set by the VS command prompts. # if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR'] # toolchain :visualcpp # else # toolchain :gcc # conf.cc.flags << '-O3' # end # # conf.gembox 'default' #end # Define cross build settings # MRuby::CrossBuild.new('32bit') do |conf| # toolchain :gcc # # conf.cc.flags << "-m32" # conf.linker.flags << "-m32" # # conf.build_mrbtest_lib_only # # conf.gem 'examples/mrbgems/c_and_ruby_extension_example' # # conf.test_runner.command = 'env' # end mruby-2.0.0/doc/000077500000000000000000000000001340361412400134125ustar00rootroot00000000000000mruby-2.0.0/doc/guides/000077500000000000000000000000001340361412400146725ustar00rootroot00000000000000mruby-2.0.0/doc/guides/compile.md000066400000000000000000000340731340361412400166530ustar00rootroot00000000000000# Compile mruby uses Rake to compile and cross-compile all libraries and binaries. ## Prerequisites To compile mruby out of the source code you need the following tools: * C Compiler (i.e. ```gcc```) * Linker (i.e. ```gcc```) * Archive utility (i.e. ```ar```) * Parser generator (i.e. ```bison```) * Ruby 1.8 or 1.9 (i.e. ```ruby``` or ```jruby```) Optional: * GIT (to update mruby source and integrate mrbgems easier) * C++ compiler (to use GEMs which include \*.cpp, \*.cxx, \*.cc) * Assembler (to use GEMs which include \*.asm) ## Usage Inside of the root directory of the mruby source a file exists called *build_config.rb*. This file contains the build configuration of mruby and looks like this for example: ```ruby MRuby::Build.new do |conf| toolchain :gcc end ``` All tools necessary to compile mruby can be set or modified here. In case you want to maintain an additional *build_config.rb* you can define a customized path using the *$MRUBY_CONFIG* environment variable. To compile just call ```./minirake``` inside of the mruby source root. To generate and execute the test tools call ```./minirake test```. To clean all build files call ```./minirake clean```. To see full command line on build, call ```./minirake -v```. ## Build Configuration Inside of the *build_config.rb* the following options can be configured based on your environment. ### Toolchains The mruby build system already contains a set of toolchain templates which configure the build environment for specific compiler infrastructures. #### GCC Toolchain configuration for the GNU C Compiler. ```ruby toolchain :gcc ``` #### clang Toolchain configuration for the LLVM C Compiler clang. Mainly equal to the GCC toolchain. ```ruby toolchain :clang ``` #### Visual Studio 2010, 2012 and 2013 Toolchain configuration for Visual Studio on Windows. If you use the [Visual Studio Command Prompt](http://msdn.microsoft.com/en-us/library/ms229859\(v=vs.110\).aspx), you normally do not have to specify this manually, since it gets automatically detected by our build process. ```ruby toolchain :visualcpp ``` #### Android Toolchain configuration for Android. ```ruby toolchain :android ``` Requires the custom standalone Android NDK and the toolchain path in ```ANDROID_STANDALONE_TOOLCHAIN```. ### Binaries It is possible to select which tools should be compiled during the compilation process. The following tools can be selected: * mruby (mruby interpreter) * mirb (mruby interactive shell) To select them declare conf.gem as follows: ```ruby conf.gem "#{root}/mrbgems/mruby-bin-mruby" conf.gem "#{root}/mrbgems/mruby-bin-mirb" ``` ### File Separator Some environments require a different file separator character. It is possible to set the character via ```conf.file_separator```. ```ruby conf.file_separator = '/' ``` ### C Compiler Configuration of the C compiler binary, flags and include paths. ```ruby conf.cc do |cc| cc.command = ... cc.flags = ... cc.include_paths = ... cc.defines = ... cc.option_include_path = ... cc.option_define = ... cc.compile_options = ... end ``` C Compiler has header searcher to detect installed library. If you need a include path of header file use ```search_header_path```: ```ruby # Searches ```iconv.h```. # If found it will return include path of the header file. # Otherwise it will return nil . fail 'iconv.h not found' unless conf.cc.search_header_path 'iconv.h' ``` If you need a full file name of header file use ```search_header```: ```ruby # Searches ```iconv.h```. # If found it will return full path of the header file. # Otherwise it will return nil . iconv_h = conf.cc.search_header 'iconv.h' print "iconv.h found: #{iconv_h}\n" ``` Header searcher uses compiler's ```include_paths``` by default. When you are using GCC toolchain (including clang toolchain since its base is gcc toolchain) it will use compiler specific include paths too. (For example ```/usr/local/include```, ```/usr/include```) If you need a special header search paths define a singleton method ```header_search_paths``` to C compiler: ```ruby def conf.cc.header_search_paths ['/opt/local/include'] + include_paths end ``` ### Linker Configuration of the Linker binary, flags and library paths. ```ruby conf.linker do |linker| linker.command = ... linker.flags = ... linker.flags_before_libraries = ... linker.libraries = ... linker.flags_after_libraries = ... linker.library_paths = .... linker.option_library = ... linker.option_library_path = ... linker.link_options = ... end ``` ### Archiver Configuration of the Archiver binary and flags. ```ruby conf.archiver do |archiver| archiver.command = ... archiver.archive_options = ... end ``` ### Parser Generator Configuration of the Parser Generator binary and flags. ```ruby conf.yacc do |yacc| yacc.command = ... yacc.compile_options = ... end ``` ### GPerf Configuration of the GPerf binary and flags. ```ruby conf.gperf do |gperf| gperf.command = ... gperf.compile_options = ... end ``` ### File Extensions ```ruby conf.exts do |exts| exts.object = ... exts.executable = ... exts.library = ... end ``` ### Mrbgems Integrate GEMs in the build process. ```ruby # Integrate GEM with additional configuration conf.gem 'path/to/gem' do |g| g.cc.flags << ... end # Integrate GEM without additional configuration conf.gem 'path/to/another/gem' ``` See doc/mrbgems/README.md for more option about mrbgems. ### Mrbtest Configuration Mrbtest build process. If you want mrbtest.a only, You should set ```conf.build_mrbtest_lib_only``` ```ruby conf.build_mrbtest_lib_only ``` ### Bintest Tests for mrbgem tools using CRuby. To have bintests place \*.rb scripts to ```bintest/``` directory of mrbgems. See ```mruby-bin-*/bintest/*.rb``` if you need examples. If you want a temporary files use `tempfile` module of CRuby instead of ```/tmp/```. You can enable it with following: ```ruby conf.enable_bintest ``` ### C++ ABI By default, mruby uses setjmp/longjmp to implement its exceptions. But it doesn't release C++ stack object correctly. To support mrbgems written in C++, mruby can be configured to use C++ exception. There are two levels of C++ exception handling. The one is ```enable_cxx_exception``` that enables C++ exception, but uses C ABI. The other is ```enable_cxx_abi``` where all files are compiled by C++ compiler. When you mix C++ code, C++ exception would be enabled automatically. If you need to enable C++ exception explicitly add the following: ```ruby conf.enable_cxx_exception ``` #### C++ exception disabling. If your compiler does not support C++ and you want to ensure you don't use mrbgem written in C++, you can explicitly disable C++ exception, add following: ```ruby conf.disable_cxx_exception ``` and you will get an error when you try to use C++ gem. Note that it must be called before ```enable_cxx_exception``` or ```gem``` method. ### Debugging mode To enable debugging mode add the following: ```ruby conf.enable_debug ``` When debugging mode is enabled * Macro ```MRB_DEBUG``` would be defined. * Which means ```mrb_assert()``` macro is enabled. * Debug information of irep would be generated by ```mrbc```. * Because ```-g``` flag would be added to ```mrbc``` runner. * You can have better backtrace of mruby scripts with this. ## Cross-Compilation mruby can also be cross-compiled from one platform to another. To achieve this the *build_config.rb* needs to contain an instance of ```MRuby::CrossBuild```. This instance defines the compilation tools and flags for the target platform. An example could look like this: ```ruby MRuby::CrossBuild.new('32bit') do |conf| toolchain :gcc conf.cc.flags << "-m32" conf.linker.flags << "-m32" end ``` All configuration options of ```MRuby::Build``` can also be used in ```MRuby::CrossBuild```. ### Mrbtest in Cross-Compilation In cross compilation, you can run ```mrbtest``` on emulator if you have it by changing configuration of test runner. ```ruby conf.test_runner do |t| t.command = ... # set emulator. this value must be non nil or false t.flags = ... # set flags of emulator def t.run(bin) # override `run` if you need to change the behavior of it ... # `bin` is the full path of mrbtest end end ``` ## Build process During the build process the directory *build* will be created in the root directory. The structure of this directory will look like this: +- build | +- host | +- bin <- Binaries (mirb, mrbc and mruby) | +- lib <- Libraries (libmruby.a and libmruby_core.a) | +- mrblib | +- src | +- test <- mrbtest tool | +- tools | +- mirb | +- mrbc | +- mruby The compilation workflow will look like this: * compile all files under *src* (object files will be stored in *build/host/src*) * generate parser grammar out of *src/parse.y* (generated result will be stored in *build/host/src/y.tab.c*) * compile *build/host/src/y.tab.c* to *build/host/src/y.tab.o* * create *build/host/lib/libmruby_core.a* out of all object files (C only) * create ```build/host/bin/mrbc``` by compiling *tools/mrbc/mrbc.c* and linking with *build/host/lib/libmruby_core.a* * create *build/host/mrblib/mrblib.c* by compiling all \*.rb files under *mrblib* with ```build/host/bin/mrbc``` * compile *build/host/mrblib/mrblib.c* to *build/host/mrblib/mrblib.o* * create *build/host/lib/libmruby.a* out of all object files (C and Ruby) * create ```build/host/bin/mruby``` by compiling *mrbgems/mruby-bin-mruby/tools/mruby/mruby.c* and linking with *build/host/lib/libmruby.a* * create ```build/host/bin/mirb``` by compiling *mrbgems/mruby-bin-mirb/tools/mirb/mirb.c* and linking with *build/host/lib/libmruby.a* ``` _____ _____ ______ ____ ____ _____ _____ ____ | CC |->|GEN |->|AR |->|CC |->|CC |->|AR |->|CC |->|CC | | *.c | |y.tab| |core.a| |mrbc| |*.rb| |lib.a| |mruby| |mirb| ----- ----- ------ ---- ---- ----- ----- ---- ``` ### Cross-Compilation In case of a cross-compilation to *i386* the *build* directory structure looks like this: +- build | +- host | | | +- bin <- Native Binaries | | | +- lib <- Native Libraries | | | +- mrblib | | | +- src | | | +- test <- Native mrbtest tool | | | +- tools | | | +- mirb | | | +- mrbc | | | +- mruby +- i386 | +- bin <- Cross-compiled Binaries | +- lib <- Cross-compiled Libraries | +- mrblib | +- src | +- test <- Cross-compiled mrbtest tool | +- tools | +- mirb | +- mrbc | +- mruby An extra directory is created for the target platform. In case you compile for *i386* a directory called *i386* is created under the build directory. The cross compilation workflow starts in the same way as the normal compilation by compiling all *native* libraries and binaries. Afterwards the cross compilation process proceeds like this: * cross-compile all files under *src* (object files will be stored in *build/i386/src*) * generate parser grammar out of *src/parse.y* (generated result will be stored in *build/i386/src/y.tab.c*) * cross-compile *build/i386/src/y.tab.c* to *build/i386/src/y.tab.o* * create *build/i386/mrblib/mrblib.c* by compiling all \*.rb files under *mrblib* with the native ```build/host/bin/mrbc``` * cross-compile *build/host/mrblib/mrblib.c* to *build/host/mrblib/mrblib.o* * create *build/i386/lib/libmruby.a* out of all object files (C and Ruby) * create ```build/i386/bin/mruby``` by cross-compiling *mrbgems/mruby-bin-mruby/tools/mruby/mruby.c* and linking with *build/i386/lib/libmruby.a* * create ```build/i386/bin/mirb``` by cross-compiling *mrbgems/mruby-bin-mirb/tools/mirb/mirb.c* and linking with *build/i386/lib/libmruby.a* * create *build/i386/lib/libmruby_core.a* out of all object files (C only) * create ```build/i386/bin/mrbc``` by cross-compiling *tools/mrbc/mrbc.c* and linking with *build/i386/lib/libmruby_core.a* ``` _______________________________________________________________ | Native Compilation for Host System | | _____ ______ _____ ____ ____ _____ | | | CC | -> |AR | -> |GEN | -> |CC | -> |CC | -> |AR | | | | *.c | |core.a| |y.tab| |mrbc| |*.rb| |lib.a| | | ----- ------ ----- ---- ---- ----- | --------------------------------------------------------------- || \||/ \/ ________________________________________________________________ | Cross Compilation for Target System | | _____ _____ _____ ____ ______ _____ | | | CC | -> |AR | -> |CC | -> |CC | -> |AR | -> |CC | | | | *.c | |lib.a| |mruby| |mirb| |core.a| |mrbc | | | ----- ----- ----- ---- ------ ----- | ---------------------------------------------------------------- ``` ## Build Configuration Examples ### Minimal Library To build a minimal mruby library you need to use the Cross Compiling feature due to the reason that there are functions (i.e. stdio) which can't be disabled for the main build. ```ruby MRuby::CrossBuild.new('Minimal') do |conf| toolchain :gcc conf.cc.defines = %w(MRB_DISABLE_STDIO) conf.bins = [] end ``` This configuration defines a cross compile build called 'Minimal' which is using the GCC and compiles for the host machine. It also disables all usages of stdio and doesn't compile any binaries (i.e. mrbc). ## Test Environment mruby's build process includes a test environment. In case you start the testing of mruby, a native binary called ```mrbtest``` will be generated and executed. This binary contains all test cases which are defined under *test/t*. In case of a cross-compilation an additional cross-compiled *mrbtest* binary is generated. You can copy this binary and run on your target system. mruby-2.0.0/doc/guides/debugger.md000066400000000000000000000153511340361412400170050ustar00rootroot00000000000000# How to Use the mruby Debugger copyright (c) 2014 Specified Non-Profit Corporation mruby Forum ## 1. Summary This file documents the mruby debugger ('mrdb') methods. ## 2 Debugging with mrdb ## 2.1 Building mrdb The trunk of the mruby source tree, with the most recent mrdb, can be checked out with the following command: ```bash $ git clone https://github.com/mruby/mruby.git ``` To run the `make` command: ```bash $ cd mruby $ make ``` By default, the `make` command will install the debugger files into mruby/bin. You can add the path for mrdb on your host environment with the following command: ```bash $ echo "export PATH=\$PATH:MRUBY_ROOT/bin" >> ~/.bashrc $ source ~/.bashrc ``` `*MRUBY_ROOT` is the directory in which mruby source code will be installed. To confirm mrdb was installed properly, run mrdb with the `--version` option: ```bash $ mrdb --version mruby 2.0.0 (2018-12-11) ``` ## 2.2 Basic Operation ### 2.2.1 Debugging mruby Script Files (rb file) with mrdb To invoke the mruby debugger, just type `mrdb`. To specify the script file: ```bash $ mrdb [option] file name ``` For example: Debugging sample.rb ```bash $ mrdb sample.rb ``` You can execute the shell commands listed below: |command|description| |:-:|:--| |run|execute programs| |step|execute stepping| |continue|execute continuing program| |break|configure the breaking point| |delete|deleting the breaking points| |disable|disabling the breaking points| |enable|enabling the breaking points| |info breakpoints|showing list of the breaking points| |print|evaluating and printing the values of the mruby expressions in the script| |list|displaying the source cords| |help|showing help| |quit|terminating the mruby debugger| ### 2.2.2 Debugging mruby Binary Files (mrb file) with mrdb You can debug the mruby binary files. #### 2.2.2.1 Debugging the binary files * notice To debug mruby binary files, you need to compile mruby files with option `-g`. ```bash $ mrbc -g sample.rb ``` You can debug the mruby binary files with following command and the option `-b`. ```bash $ mrdb -b sample.mrb ``` Then you can execute all debugger shell commands. #### Break Command You can use any breakpoint to stop the program by specifying the line number and method name. The breakpoint list will be displayed after you have set the breakpoint successfully. Usage: ``` break [file:]linenum b [file:]linenum break [class:]method b [class:]method ``` The breakpoint will be ordered in serial from 1. The number, which was given to the deleted breakpoint, will never be given to another breakpoint again. You can give multiple breakpoints to specified the line number and method. Be ware that breakpoint command will not check the validity of the class name and method name. You can get the current breakpoint information by the following options. breakpoint breakpoint number : file name. line number breakpoint breakpoint number : [class name,] method name #### Continue Command Usage: ``` continue [N] c [N] ``` N: the next breakpoint number When resuming the program, it will stop at breakpoint N (N-1 breakpoint will be ignored). When you run the `continue` command without specifying N, the program will be stopped at the next breakpoint. Example: ``` (foo.rb:1) continue 3 ``` This will resume the program and stop it at the third breakpoint. #### Delete Command This will delete the specified breakpoint. Usage: ``` delete [breakpoint-no] d [breakpoint-no] ``` breakpoint-no: breakpoint number Example: ``` (foo.rb:1) delete ``` This will delete all of the breakpoints. ``` (foo.rb:1) delete 1 3 ``` This will delete the breakpoint at 1 and 3. #### Disable Command This will disable the specified breakpoint. Usage: ``` disable [breakpoint-no] dis [breakpoint-no] ``` reappointing: breakpoint number Example: ``` (foo.rb:1) disable ``` Use `disable` if you would like to disable all of the breakpoints. ``` (foo.rb:1) disable 1 3 ``` This will disable the breakpoints at 1 and 3. #### Enable Command This will enable the specified breakpoints. Usage: ``` enable [breakpoint-no] e [breakpoint-no] ``` breakpoint-no: breakpoint number Example: ``` (foo.rb:1) enable ``` Enabling all breakpoints ``` (foo.rb:1) enable 1 3 ``` Enabling the breakpoint 1 and 3 #### eval command Evaluating the string as source code and printing the value. Same as print command, please see print command. #### help command Displaying the help message. Usage: ``` help [command] h [command] ``` Typing `help` without any options will display the command list. #### Info Breakpoints Command Displaying the specified breakpoint information. Usage: ``` info breakpoints [breakpoint-no] i b [breakpoint-no] ``` breakpoint-no: breakpoint number Typing "info breakpoints" without ant option will display all breakpoint information. Example: ``` (sample.rb:1) info breakpoints Num Type Enb What 1 breakpoint y at sample.rb:3 -> file name,line number 2 breakpoint n in Sample_class:sample_class_method -> [class:]method name 3 breakpoint y in sample_global_method ``` Displaying the specified breakpoint number: ``` (foo.rb:1) info breakpoints 1 3 Num Type Enb What 1 breakpoint y at sample.rb:3 3 breakpoint y in sample_global_method ``` #### List Command To display the code of the source file. Usage: ``` list [filename:]first[,last] l [filename]:first[,last] ``` first: the opening row number last : the closing row number When you specify the `first`, but not the `last` option, you will receive 10 rows. When you do not specify both the `first` and `last` options, you will receive the next 10 rows. Example: ``` Specifying file name and first row number sample.rb:1) list sample2.rb:5 ``` Specifying the file name and the first and last row number: ``` (sample.rb:1) list sample2.rb:6,7 ``` #### Print Command Evaluating the string as source code and printing the value. Usage: ``` print [expr] p [expr] ``` expr: expression The expression is mandatory. The displayed expressions will be serially ordered from 1. If an exception occurs, the exception information will be displayed and the debugging will be continued. Example: ``` (sample.rb:1) print 1+2 $1 = 3 (sample.rb:1) print self $2 = main ``` Below is the case of the exception: ``` (sample.rb:1) print (1+2 $1 = SyntaxError: line 1: syntax error, unexpected $end, expecting ')' ``` #### Quit Command Quitting the debugger. Usage: ``` quit q ``` #### Run Command Running the program and stopping at the first breakpoint. Usage: ``` run r ``` #### Step Command This will run the program step by step. When the method and the block are invoked, the program will be stop at the first row. The program, which is developed in C, will be ignored. mruby-2.0.0/doc/guides/gc-arena-howto.md000066400000000000000000000151121340361412400200270ustar00rootroot00000000000000# How to use `mrb_gc_arena_save()`/`mrb_gc_arena_restore()`/`mrb_gc_protect()` _This is an English translation of [Matz's blog post][matz blog post] written in Japanese._ _Some parts are updated to reflect recent changes._ [matz blog post]: http://www.rubyist.net/~matz/20130731.html When you are extending mruby using C language, you may encounter mysterious "arena overflow error" or memory leak or very slow execution speed. This is an error indicating overflow of "GC arena" implementing "conservative GC". GC (garbage collector) must ensure that object is "alive", in other words, that it is referenced by somewhere from program. This can be determined by checking if the object can be directly or indirectly referenced by root. The local variables, global variables and constants etc are root. If program execution is performed inside mruby VM, there is nothing to worry about because GC can access all roots owned by VM. The problem arises when executing C functions. The object referenced by C variable is also "alive", but mruby GC cannot aware of this, so it might mistakenly recognize the objects referenced by only C variables as dead. This can be a fatal bug if the GC tries to collect a live object. In CRuby, we scan C stack area, and use C variable as root to check whether object is alive or not. Of course, because we are accessing C stack just as memory region, we never know it is an integer or a pointer. We workaround this by assuming that if it looks like a pointer, then assume it as a pointer. We call it "conservative". By the way, CRuby's "conservative GC" has some problems. The biggest problem is we have no way to access to the stack area in portable way. Therefore, we cannot use this method if we'd like to implement highly portable runtime, like mruby. So we came up with an another plan to implement "conservative GC" in mruby. Again, the problem is when an object which was created in C function, becomes no longer referenced in the Ruby world, and cannot be treated as garbage. In mruby, we recognize all objects created in C function are alive. Then we have no problem such as confusing a live object as dead. This means that because we cannot collect truly dead object, we may lose efficiency, but as a trade-off the GC itself is highly portable. We can say goodbye to the problem that GC deletes live objects due to optimization which sometimes occurs in CRuby. According to this idea, we have a table, called "GC arena", which remembers objects created in C function. The arena is stack structure, when C function execution is returned to mruby VM, all objects registered in the arena are popped. This works very well, but can cause another problem: "arena overflow error" or memory leak. As of this writing, mruby automatically extend arena to remember objects (See `MRB_GC_FIXED_ARENA` and `MRB_GC_ARENA_SIZE` in doc/guides/mrbconf.md). If you create many objects in C functions, memory usage will increase, since GC never kick in. This memory usage may look like memory leak, but will also make execution slower as more memory will need to be allocated. With the build time configuration, you can limit the maximum size of arena (e.g., 100). Then if you create many objects, arena overflows, thus you will get an "arena overflow error". To workaround these problems, we have `mrb_gc_arena_save()` and `mrb_gc_arena_restore()` functions. `int mrb_gc_arena_save(mrb)` returns the current position of the stack top of GC arena, and `void mrb_gc_arena_restore(mrb, idx)` sets the stack top position to back to given `idx`. We can use them like this: ```c int arena_idx = mrb_gc_arena_save(mrb); // ...create objects... mrb_gc_arena_restore(mrb, arena_idx); ``` In mruby, C function calls are surrounded by this save/restore, but we can further optimize memory usage by surrounding save/restore, and can avoid creating arena overflow bugs. Let's take a real example. Here is the source code of `Array#inspect`: ```c static mrb_value inspect_ary(mrb_state *mrb, mrb_value ary, mrb_value list) { mrb_int i; mrb_value s, arystr; char head[] = { '[' }; char sep[] = { ',', ' ' }; char tail[] = { ']' }; /* check recursive */ for(i=0; i 0) { mrb_str_cat(mrb, arystr, sep, sizeof(sep)); } if (mrb_array_p(RARRAY_PTR(ary)[i])) { s = inspect_ary(mrb, RARRAY_PTR(ary)[i], list); } else { s = mrb_inspect(mrb, RARRAY_PTR(ary)[i]); } mrb_str_cat(mrb, arystr, RSTRING_PTR(s), RSTRING_LEN(s)); mrb_gc_arena_restore(mrb, ai); } mrb_str_cat(mrb, arystr, tail, sizeof(tail)); mrb_ary_pop(mrb, list); return arystr; } ``` This is a real example, so a little bit complicated, but bear with me. The essence of `Array#inspect` is that after stringifying each element of array using `inspect` method, we join them together so that we can get `inspect` representation of the entire array. After the `inspect` representation is created, we no longer require the individual string representation. This means that we don't have to register these temporal objects into GC arena. Therefore, in order to keep the arena size small; the `ary_inspect()` function will do the following: * save the position of the stack top using `mrb_gc_arena_save()`. * get `inspect` representation of each element. * append it to the constructing entire `inspect` representation of array. * restore stack top position using `mrb_gc_arena_restore()`. Please note that the final `inspect` representation of entire array was created before the call of `mrb_gc_arena_restore()`. Otherwise, required temporal object may be deleted by GC. We may have a usecase where after creating many temporal objects, we'd like to keep some of them. In this case, we cannot use the same idea in `ary_inspect()` like appending objects to existing one. Instead, after `mrb_gc_arena_restore()`, we must re-register the objects we want to keep in the arena using `mrb_gc_protect(mrb, obj)`. Use `mrb_gc_protect()` with caution because it could also lead to an "arena overflow error". We must also mention that when `mrb_funcall` is called in top level, the return value is also registered to GC arena, so repeated use of `mrb_funcall` may eventually lead to an "arena overflow error". Use `mrb_gc_arena_save()` and `mrb_gc_arena_restore()` or possible use of `mrb_gc_protect()` to workaround this. mruby-2.0.0/doc/guides/mrbconf.md000066400000000000000000000113651340361412400166500ustar00rootroot00000000000000# mruby configuration macros. ## How to use these macros. You can use mrbconfs with following ways: * Write them in `mrbconf.h`. * Using compiler flags is preferred when building a cross binaries or multiple mruby binaries since it's easier to use different mrbconf per each `MRuby::Build`. * Most flags can be enabled by just commenting in. * Pass them as compiler flags. * Make sure you pass the same flags to all compilers since some mrbconf(e.g., `MRB_GC_FIXED_ARENA`) changes `struct` layout and cause memory access error when C and other language(e.g., C++) is mixed. ## stdio setting. `MRB_DISABLE_STDIO` * When defined `` functions won't be used. * Some features will be disabled when this is enabled: * `mrb_irep` load/dump from/to file. * Compiling mruby script from file. * Printing features in **src/print.c**. ## Debug macros. `MRB_ENABLE_DEBUG_HOOK` * When defined code fetch hook and debug OP hook will be enabled. * When using any of the hook set function pointer `code_fetch_hook` and/or `debug_op_hook` of `mrb_state`. * Fetch hook will be called before any OP. * Debug OP hook will be called when dispatching `OP_DEBUG`. `MRB_DEBUG` * When defined `mrb_assert*` macro will be defined with macros from ``. * Could be enabled via `enable_debug` method of `MRuby::Build`. ## Stack configuration `MRB_STACK_EXTEND_DOUBLING` * If defined doubles the stack size when extending it. * Else extends stack with `MRB_STACK_GROWTH`. `MRB_STACK_GROWTH` * Default value is `128`. * Used in stack extending. * Ignored when `MRB_STACK_EXTEND_DOUBLING` is defined. `MRB_STACK_MAX` * Default value is `0x40000 - MRB_STACK_GROWTH`. * Raises `RuntimeError` when stack size exceeds this value. ## Primitive type configuration. `MRB_USE_FLOAT` * When defined single precision floating point type(C type `float`) is used as `mrb_float`. * Else double precision floating point type(C type `double`) is used as `mrb_float`. `MRB_INT16` * When defined `int16_t` will be defined as `mrb_int`. * Conflicts with `MRB_INT64`. `MRB_INT64` * When defined `int64_t` will be defined as `mrb_int`. * Conflicts with `MRB_INT16`. * When `MRB_INT16` or `MRB_INT64` isn't defined `int`(most of the times 32-bit integer) will be defined as `mrb_int`. ## Garbage collector configuration. `MRB_GC_STRESS` * When defined full GC is emitted per each `RBasic` allocation. * Mainly used in memory manager debugging. `MRB_GC_TURN_OFF_GENERATIONAL` * When defined turns generational GC by default. `MRB_GC_FIXED_ARENA` * When defined used fixed size GC arena. * Raises `RuntimeError` when this is defined and GC arena size exceeds `MRB_GC_ARENA_SIZE`. * Useful tracking unnecessary mruby object allocation. `MRB_GC_ARENA_SIZE` * Default value is `100`. * Ignored when `MRB_GC_FIXED_ARENA` isn't defined. * Defines fixed GC arena size. `MRB_HEAP_PAGE_SIZE` * Defines value is `1024`. * Specifies number of `RBasic` per each heap page. ## Memory pool configuration. `POOL_ALIGNMENT` * Default value is `4`. * If you're allocating data types that requires alignment more than default value define the largest value of required alignment. `POOL_PAGE_SIZE` * Default value is `16000`. * Specifies page size of pool page. * Smaller the value is increases memory overhead. ## State atexit configuration. `MRB_FIXED_STATE_ATEXIT_STACK` * If defined enables fixed size `mrb_state` atexit stack. * Raises `RuntimeError` when `mrb_state_atexit` call count to same `mrb_state` exceeds `MRB_FIXED_STATE_ATEXIT_STACK_SIZE`'s value. `MRB_FIXED_STATE_ATEXIT_STACK_SIZE` * Default value is `5`. * If `MRB_FIXED_STATE_ATEXIT_STACK` isn't defined this macro is ignored. ## `mrb_value` configuration. `MRB_ENDIAN_BIG` * If defined compiles mruby for big endian machines. * Used in `MRB_NAN_BOXING`. * Some mrbgem use this mrbconf. `MRB_NAN_BOXING` * If defined represent `mrb_value` in boxed `double`. * Conflicts with `MRB_USE_FLOAT`. `MRB_WORD_BOXING` * If defined represent `mrb_value` as a word. * If defined `Float` will be a mruby object with `RBasic`. ## Instance variable configuration. `MRB_IV_SEGMENT_SIZE` * Default value is `4`. * Specifies size of each segment in segment list. ## Other configuration. `MRB_UTF8_STRING` * Adds UTF-8 encoding support to character-oriented String instance methods. * If it isn't defined, they only support the US-ASCII encoding. `MRB_FUNCALL_ARGC_MAX` * Default value is `16`. * Specifies 4th argument(`argc`) max value of `mrb_funcall`. * Raises `ArgumentError` when the `argc` argument is bigger then this value `mrb_funcall`. `KHASH_DEFAULT_SIZE` * Default value is `32`. * Specifies default size of khash table bucket. * Used in `kh_init_ ## name` function. `MRB_STR_BUF_MIN_SIZE` * Default value is `128`. * Specifies initial capacity of `RString` created by `mrb_str_buf_new` function.. mruby-2.0.0/doc/guides/mrbgems.md000066400000000000000000000265041340361412400166570ustar00rootroot00000000000000# mrbgems mrbgems is a library manager to integrate C and Ruby extension in an easy and standardised way into mruby. ## Usage By default mrbgems is currently deactivated. As soon as you add a GEM to your build configuration (i.e. *build_config.rb*), mrbgems will be activated and the extension integrated. To add a GEM into the *build_config.rb* add the following line for example: ```ruby conf.gem '/path/to/your/gem/dir' ``` You can also use a relative path which would be relative from the mruby root: ```ruby conf.gem 'examples/mrbgems/ruby_extension_example' ``` A remote GIT repository location for a GEM is also supported: ```ruby conf.gem :git => 'https://github.com/masuidrive/mrbgems-example.git', :branch => 'master' conf.gem :github => 'masuidrive/mrbgems-example', :branch => 'master' conf.gem :bitbucket => 'mruby/mrbgems-example', :branch => 'master' ``` You can specify the sub directory of the repository with `:path` option: ```ruby conf.gem github: 'mruby/mruby', path: 'mrbgems/mruby-socket' ``` To use mrbgem from [mgem-list](https://github.com/mruby/mgem-list) use `:mgem` option: ```ruby conf.gem :mgem => 'mruby-yaml' conf.gem :mgem => 'yaml' # 'mruby-' prefix could be omitted ``` If there is missing dependencies, mrbgem dependencies solver will reference mrbgem from core or mgem-list. To pull all gems from remote GIT repository on build, call ```./minirake -p```, or ```./minirake --pull-gems```. NOTE: `:bitbucket` option supports only git. Hg is unsupported in this version. ## GemBox There are instances when you wish to add a collection of mrbgems into mruby at once, or be able to substitute mrbgems based on configuration, without having to add each gem to the *build_config.rb* file. A packaged collection of mrbgems is called a GemBox. A GemBox is a file that contains a list of mrbgems to load into mruby, in the same format as if you were adding them to *build_config.rb* via `config.gem`, but wrapped in an `MRuby::GemBox` object. GemBoxes are loaded into mruby via `config.gembox 'boxname'`. Below we have created a GemBox containing *mruby-time* and *mrbgems-example*: ```ruby MRuby::GemBox.new do |conf| conf.gem "#{root}/mrbgems/mruby-time" conf.gem :github => 'masuidrive/mrbgems-example' end ``` As mentioned, the GemBox uses the same conventions as `MRuby::Build`. The GemBox must be saved with a *.gembox* extension inside the *mrbgems* directory to to be picked up by mruby. To use this example GemBox, we save it as `custom.gembox` inside the *mrbgems* directory in mruby, and add the following to our *build_config.rb* file inside the build block: ```ruby conf.gembox 'custom' ``` This will cause the *custom* GemBox to be read in during the build process, adding *mruby-time* and *mrbgems-example* to the build. If you want, you can put GemBox outside of mruby directory. In that case you must specify an absolute path like below. ```ruby conf.gembox "#{ENV["HOME"]}/mygemboxes/custom" ``` There are two GemBoxes that ship with mruby: [default](../../mrbgems/default.gembox) and [full-core](../../mrbgems/full-core.gembox). The [default](../../mrbgems/default.gembox) GemBox contains several core components of mruby, and [full-core](../../mrbgems/full-core.gembox) contains every gem found in the *mrbgems* directory. ## GEM Structure The maximal GEM structure looks like this: +- GEM_NAME <- Name of GEM | +- include/ <- Header for Ruby extension (will exported) | +- mrblib/ <- Source for Ruby extension | +- src/ <- Source for C extension | +- test/ <- Test code (Ruby) | +- mrbgem.rake <- GEM Specification | +- README.md <- Readme for GEM The folder *mrblib* contains pure Ruby files to extend mruby. The folder *src* contains C/C++ files to extend mruby. The folder *include* contains C/C++ header files. The folder *test* contains C/C++ and pure Ruby files for testing purposes which will be used by `mrbtest`. *mrbgem.rake* contains the specification to compile C and Ruby files. *README.md* is a short description of your GEM. ## Build process mrbgems expects a specification file called *mrbgem.rake* inside of your GEM directory. A typical GEM specification could look like this for example: ```ruby MRuby::Gem::Specification.new('c_and_ruby_extension_example') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Example mrbgem using C and ruby' end ``` The mrbgems build process will use this specification to compile Object and Ruby files. The compilation results will be added to *lib/libmruby.a*. This file exposes the GEM functionality to tools like `mruby` and `mirb`. The following properties can be set inside of your `MRuby::Gem::Specification` for information purpose: * `spec.license` or `spec.licenses` (A single license or a list of them under which this GEM is licensed) * `spec.author` or `spec.authors` (Developer name or a list of them) * `spec.version` (Current version) * `spec.description` (Detailed description) * `spec.summary` * One line short description of mrbgem. * Printed in build summary of rake when set. * `spec.homepage` (Homepage) * `spec.requirements` (External requirements as information for user) The `license` and `author` properties are required in every GEM! In case your GEM is depending on other GEMs please use `spec.add_dependency(gem, *requirements[, default_get_info])` like: ```ruby MRuby::Gem::Specification.new('c_and_ruby_extension_example') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' # Add GEM dependency mruby-parser. # The version must be between 1.0.0 and 1.5.2 . spec.add_dependency('mruby-parser', '>= 1.0.0', '<= 1.5.2') # Use any version of mruby-uv from github. spec.add_dependency('mruby-uv', '>= 0.0.0', :github => 'mattn/mruby-uv') # Use latest mruby-onig-regexp from github. (version requirements can be omitted) spec.add_dependency('mruby-onig-regexp', :github => 'mattn/mruby-onig-regexp') # You can add extra mgems active only on test spec.add_test_dependency('mruby-process', :github => 'iij/mruby-process') end ``` The version requirements and default gem information are optional. Version requirement supports following operators: * '=': is equal * '!=': is not equal * '>': is greater * '<': is lesser * '>=': is equal or greater * '<=': is equal or lesser * '~>': is equal or greater and is lesser than the next major version * example 1: '~> 2.2.2' means '>= 2.2.2' and '< 2.3.0' * example 2: '~> 2.2' means '>= 2.2.0' and '< 3.0.0' When more than one version requirements is passed, the dependency must satisfy all of it. You can have default gem to use as dependency when it's not defined in *build_config.rb*. When the last argument of `add_dependency` call is `Hash`, it will be treated as default gem information. Its format is same as argument of method `MRuby::Build#gem`, expect that it can't be treated as path gem location. When a special version of dependency is required, use `MRuby::Build#gem` in *build_config.rb* to override default gem. If you have conflicting GEMs use the following method: * `spec.add_conflict(gem, *requirements)` * The `requirements` argument is same as in `add_dependency` method. like following code: ```ruby MRuby::Gem::Specification.new 'some-regexp-binding' do |spec| spec.license = 'BSD' spec.author = 'John Doe' spec.add_conflict 'mruby-onig-regexp', '> 0.0.0' spec.add_conflict 'mruby-hs-regexp' spec.add_conflict 'mruby-pcre-regexp' spec.add_conflict 'mruby-regexp-pcre' end ``` In case your GEM has more complex build requirements you can use the following options additionally inside of your GEM specification: * `spec.cc.flags` (C compiler flags) * `spec.cc.defines` (C compiler defines) * `spec.cc.include_paths` (C compiler include paths) * `spec.linker.flags` (Linker flags) * `spec.linker.libraries` (Linker libraries) * `spec.linker.library_paths` (Linker additional library path) * `spec.bins` (Generate binary file) * `spec.rbfiles` (Ruby files to compile) * `spec.objs` (Object files to compile) * `spec.test_rbfiles` (Ruby test files for integration into mrbtest) * `spec.test_objs` (Object test files for integration into mrbtest) * `spec.test_preload` (Initialization files for mrbtest) You also can use `spec.mruby.cc` and `spec.mruby.linker` to add extra global parameters for compiler and linker. ### include_paths and dependency Your GEM can export include paths to another GEMs that depends on your GEM. By default, `/...absolute path.../{GEM_NAME}/include` will be exported. So it is recommended not to put GEM's local header files on include/. These exports are retroactive. For example: when B depends to C and A depends to B, A will get include paths exported by C. Exported include_paths are automatically appended to GEM local include_paths by Minirake. You can use `spec.export_include_paths` accessor if you want more complex build. ## C Extension mruby can be extended with C. This is possible by using the C API to integrate C libraries into mruby. ### Preconditions mrbgems expects that you have implemented a C method called `mrb_YOURGEMNAME_gem_init(mrb_state)`. `YOURGEMNAME` will be replaced by the name of your GEM. If you call your GEM *c_extension_example*, your initialisation method could look like this: ```C void mrb_c_extension_example_gem_init(mrb_state* mrb) { struct RClass *class_cextension = mrb_define_module(mrb, "CExtension"); mrb_define_class_method(mrb, class_cextension, "c_method", mrb_c_method, MRB_ARGS_NONE()); } ``` ### Finalize mrbgems expects that you have implemented a C method called `mrb_YOURGEMNAME_gem_final(mrb_state)`. `YOURGEMNAME` will be replaced by the name of your GEM. If you call your GEM *c_extension_example*, your finalizer method could look like this: ```C void mrb_c_extension_example_gem_final(mrb_state* mrb) { free(someone); } ``` ### Example +- c_extension_example/ | +- src/ | | | +- example.c <- C extension source | +- test/ | | | +- example.rb <- Test code for C extension | +- mrbgem.rake <- GEM specification | +- README.md ## Ruby Extension mruby can be extended with pure Ruby. It is possible to override existing classes or add new ones in this way. Put all Ruby files into the *mrblib* folder. ### Pre-Conditions none ### Example +- ruby_extension_example/ | +- mrblib/ | | | +- example.rb <- Ruby extension source | +- test/ | | | +- example.rb <- Test code for Ruby extension | +- mrbgem.rake <- GEM specification | +- README.md ## C and Ruby Extension mruby can be extended with C and Ruby at the same time. It is possible to override existing classes or add new ones in this way. Put all Ruby files into the *mrblib* folder and all C files into the *src* folder. mruby codes under *mrblib* directory would be executed after gem init C function is called. Make sure *mruby script* depends on *C code* and *C code* doesn't depend on *mruby script*. ### Pre-Conditions See C and Ruby example. ### Example +- c_and_ruby_extension_example/ | +- mrblib/ | | | +- example.rb <- Ruby extension source | +- src/ | | | +- example.c <- C extension source | +- test/ | | | +- example.rb <- Test code for C and Ruby extension | +- mrbgem.rake <- GEM specification | +- README.md mruby-2.0.0/doc/limitations.md000066400000000000000000000114731340361412400162760ustar00rootroot00000000000000# Limitations and Differences The philosophy of mruby is to be a lightweight implementation of the Ruby ISO standard. These two objectives are partially contradicting. Ruby is an expressive language with complex implementation details which are difficult to implement in a lightweight manner. To cope with this, limitations to the "Ruby Compatibility" are defined. This document is collecting these limitations. ## Integrity This document does not contain a complete list of limitations. Please help to improve it by submitting your findings. ## ```1/2``` gives ```0.5``` Since mruby does not have ```Bignum```, bigger integers are represented by ```Float``` numbers. To enhance interoperability between ```Fixnum``` and ```Float```, mruby provides ```Float#upto``` and other iterating methods for the ```Float``` class. As a side effect, ```1/2``` gives ```0.5``` not ```0```. ## ```Array``` passed to ```puts``` Passing an Array to ```puts``` results in different output. ```ruby puts [1,2,3] ``` #### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)] ``` 1 2 3 ``` #### mruby [2.0.0 (2018-12-11)] ``` [1, 2, 3] ``` ## ```Kernel.raise``` in rescue clause ```Kernel.raise``` without arguments does not raise the current exception within a rescue clause. ```ruby begin 1 / 0 rescue raise end ``` #### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)] ```ZeroDivisionError``` is raised. #### mruby [2.0.0 (2018-12-11)] No exception is raised. ## Fiber execution can't cross C function boundary mruby's ```Fiber``` is implemented in a similar way to Lua's co-routine. This results in the consequence that you can't switch context within C functions. Only exception is ```mrb_fiber_yield``` at return. ## ```Array``` does not support instance variables To reduce memory consumption ```Array``` does not support instance variables. ```ruby class Liste < Array def initialize(str = nil) @feld = str end end p Liste.new "foobar" ``` #### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)] ``` [] ``` #### mruby [1.4.1 (2018-4-27)] ```ArgumentError``` is raised. ## Method visibility For simplicity reasons no method visibility (public/private/protected) is supported. ```ruby class VisibleTest def public_method; end private def private_method; end end p VisibleTest.new.respond_to?(:private_method, false) p VisibleTest.new.respond_to?(:private_method, true) ``` #### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)] ``` false true ``` #### mruby [2.0.0 (2018-12-11)] ``` true true ``` ## defined? The ```defined?``` keyword is considered too complex to be fully implemented. It is recommended to use ```const_defined?``` and other reflection methods instead. ```ruby defined?(Foo) ``` #### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)] ``` nil ``` #### mruby [2.0.0 (2018-12-11)] ```NameError``` is raised. ## ```alias``` on global variables Aliasing a global variable works in CRuby but is not part of the ISO standard. ```ruby alias $a $__a__ ``` #### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)] ``` nil ``` #### mruby [2.0.0 (2018-12-11)] Syntax error ## Operator modification An operator can't be overwritten by the user. ```ruby class String def + end end 'a' + 'b' ``` #### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)] ```ArgumentError``` is raised. The re-defined ```+``` operator does not accept any arguments. #### mruby [2.0.0 (2018-12-11)] ``` 'ab' ``` Behavior of the operator wasn't changed. ## Kernel#binding is not supported `Kernel#binding` method is not supported. #### Ruby [ruby 2.5.1p57 (2018-03-29 revision 63029)] ``` $ ruby -e 'puts Proc.new {}.binding' # ``` #### mruby [2.0.0 (2018-12-11)] ``` $ ./bin/mruby -e 'puts Proc.new {}.binding' trace (most recent call last): [0] -e:1 -e:1: undefined method 'binding' (NoMethodError) ``` ## Keyword arguments mruby keyword arguments behave slightly different from CRuby 2.5 to make the behavior simpler and less confusing. Maybe in the future, the simpler behavior will be adopted to CRuby as well. #### Ruby [ruby 2.5.1p57 (2018-03-29 revision 63029)] ``` $ ruby -e 'def m(*r,**k) p [r,k] end; m("a"=>1,:b=>2)' [[{"a"=>1}], {:b=>2}] ``` #### mruby [mruby 2.0.0] ``` $ ./bin/mruby -e 'def m(*r,**k) p [r,k] end; m("a"=>1,:b=>2)' trace (most recent call last): [0] -e:1 -e:1: keyword argument hash with non symbol keys (ArgumentError) ``` ## Argument Destructuring ```ruby def m(a,(b,c),d); p [a,b,c,d]; end m(1,[2,3],4) # => [1,2,3,4] ``` Destructured arguments (`b` and `c` in above example) cannot be accessed from the default expression of optional arguments and keyword arguments, since actual assignment is done after the evaluation of those default expressions. Thus: ```ruby def f(a,(b,c),d=b) p [a,b,c,d] end f(1,[2,3]) ``` CRuby gives `[1,2,3,nil]`. mruby raises `NoMethodError` for `b`. mruby-2.0.0/doc/opcode.md000066400000000000000000000143121340361412400152060ustar00rootroot00000000000000# The new bytecode We will reimplement VM to use 8bit instruction code. By bytecode, we mean real byte code. The whole purpose is reducing the memory consumption of mruby VM. # Instructions Instructions are bytes. There can be 256 instructions. Currently we have 94 instructions. Instructions can take 0 to 3 operands. ## operands The size of operands can be either 8bits, 16bits or 24bits. In the table.1 below, the second field describes the size (and sign) of operands. * B: 8bit * sB: signed 8bit * S: 16bit * sS: signed 16bit * W: 24bit First two byte operands may be extended to 16bit. When those byte operands are bigger than 256, the instruction will be prefixed by `OP_EXT1` (means 1st operand is 16bit) or `OP_EXT2` (means 2nd operand is 16bit) or `OP_EXT3` (means 1st and 2nd operands are 16bit). For instructions marked by `'`, `OP_EXT1` can be prefixed. For those with `"`, either `OP_EXT1` or `OP_EXT2` or `OP_EXT2` can be prefixed. ## table.1 Instruction Table |Instruction Name |Operand type |Semantics |-----------------|-------------|----------------- |OP_NOP | - | |OP_MOVE" |BB |R(a) = R(b) |OP_LOADL" |BB |R(a) = Pool(b) |OP_LOADI" |BsB |R(a) = mrb_int(b) |OP_LOADI_0' |B |R(a) = 0 |OP_LOADI_1' |B |R(a) = 1 |OP_LOADI_2' |B |R(a) = 2 |OP_LOADI_3' |B |R(a) = 3 |OP_LOADSYM" |BB |R(a) = Syms(b) |OP_LOADNIL' |B |R(a) = nil |OP_LOADSELF' |B |R(a) = self |OP_LOADT' |B |R(a) = true |OP_LOADF' |B |R(a) = false |OP_GETGV" |BB |R(a) = getglobal(Syms(b)) |OP_SETGV" |BB |setglobal(Syms(b), R(a)) |OP_GETSV" |BB |R(a) = Special[b] |OP_SETSV" |BB |Special[b] = R(a) |OP_GETIV" |BB |R(a) = ivget(Syms(b)) |OP_SETIV" |BB |ivset(Syms(b),R(a)) |OP_GETCV" |BB |R(a) = cvget(Syms(b)) |OP_SETCV" |BB |cvset(Syms(b),R(a)) |OP_GETCONST" |BB |R(a) = constget(Syms(b)) |OP_SETCONST" |BB |constset(Syms(b),R(a)) |OP_GETMCNST" |BB |R(a) = R(a)::Syms(b) |OP_SETMCNST" |BB |R(a+1)::Syms(b) = R(a) |OP_GETUPVAR' |BBB |R(a) = uvget(b,c) |OP_SETUPVAR' |BBB |uvset(b,c,R(a)) |OP_JMP |S |pc+=a |OP_JMPIF' |SB |if R(b) pc+=a |OP_JMPNOT' |SB |if !R(b) pc+=a |OP_ONERR |sS |rescue_push(pc+a) |OP_EXCEPT' |B |R(a) = exc |OP_RESCUE" |BB |R(b) = R(a).isa?(R(b)) |OP_POPERR |B |a.times{rescue_pop()} |OP_RAISE' |B |raise(R(a)) |OP_EPUSH' |B |ensure_push(SEQ[a]) |OP_EPOP |B |A.times{ensure_pop().call} |OP_SENDV" |BB |R(a) = call(R(a),Syms(b),*R(a+1)) |OP_SENDVB" |BB |R(a) = call(R(a),Syms(b),*R(a+1),&R(a+2)) |OP_SEND" |BBB |R(a) = call(R(a),Syms(b),R(a+1),...,R(a+c)) |OP_SENDB" |BBB |R(a) = call(R(a),Syms(Bx),R(a+1),...,R(a+c),&R(a+c+1)) |OP_CALL' |B |R(a) = self.call(frame.argc, frame.argv) |OP_SUPER' |BB |R(a) = super(R(a+1),... ,R(a+b+1)) |OP_ARGARY' |BS |R(a) = argument array (16=5:1:5:1:4) |OP_ENTER |W |arg setup according to flags (23=5:5:1:5:5:1:1) |OP_KARG" |BB |R(a) = kdict[Syms(Bx)] # todo |OP_KARG2" |BB |R(a) = kdict[Syms(Bx)]; kdict.rm(Syms(b)) # todo |OP_RETURN' |B |return R(a) (normal) |OP_RETURN_BLK' |B |return R(a) (in-block return) |OP_BREAK' |B |break R(a) |OP_BLKPUSH' |BS |R(a) = block (16=5:1:5:1:4) |OP_ADD" |BB |R(a) = R(a)+R(a+1) |OP_ADDI" |BBB |R(a) = R(a)+mrb_int(c) |OP_SUB" |BB |R(a) = R(a)-R(a+1) |OP_SUBI" |BB |R(a) = R(a)-C |OP_MUL" |BB |R(a) = R(a)*R(a+1) |OP_DIV" |BB |R(a) = R(a)/R(a+1) |OP_EQ" |BB |R(a) = R(a)==R(a+1) |OP_LT" |BB |R(a) = R(a)R(a+1) |OP_GE" |BB |R(a) = R(a)>=R(a+1) |OP_ARRAY' |BB |R(a) = ary_new(R(a),R(a+1)..R(a+b)) |OP_ARRAY2" |BB |R(a) = ary_new(R(b),R(b+1)..R(b+c)) |OP_ARYCAT' |B |ary_cat(R(a),R(a+1)) |OP_ARYPUSH' |B |ary_push(R(a),R(a+1)) |OP_AREF' |BB |R(a) = R(a)[b] |OP_ASET' |BB |R(a)[b] = R(a+1) |OP_APOST' |BB |*R(a),R(A+1)..R(A+C) = R(a)[B..] |OP_STRING" |BB |R(a) = str_dup(Lit(b)) |OP_STRCAT' |B |str_cat(R(a),R(a+1)) |OP_HASH' |BB |R(a) = hash_new(R(a),R(a+1)..R(a+b)) |OP_HASHADD' |BB |R(a) = hash_push(R(a),R(a+1)..R(a+b)) |OP_LAMBDA" |BB |R(a) = lambda(SEQ[b],OP_L_LAMBDA) |OP_BLOCK" |BB |R(a) = lambda(SEQ[b],OP_L_BLOCK) |OP_METHOD" |BB |R(a) = lambda(SEQ[b],OP_L_METHOD) |OP_RANGE_INC' |B |R(a) = range_new(R(a),R(a+1),FALSE) |OP_RANGE_EXC' |B |R(a) = range_new(R(a),R(a+1),TRUE) |OP_OCLASS' |B |R(a) = ::Object |OP_CLASS" |BB |R(a) = newclass(R(a),Syms(b),R(a+1)) |OP_MODULE" |BB |R(a) = newmodule(R(a),Syms(b)) |OP_EXEC" |BB |R(a) = blockexec(R(a),SEQ[b]) |OP_DEF" |BB |R(a).newmethod(Syms(b),R(a+1)) |OP_ALIAS' |B |alias_method(R(a),R(a+1),R(a+2)) |OP_UNDEF" |BB |undef_method(R(a),Syms(b)) |OP_SCLASS' |B |R(a) = R(a).singleton_class |OP_TCLASS' |B |R(a) = target_class |OP_ERR' |B |raise(RuntimeError, Lit(Bx)) |OP_EXT1 |- |make 1st operand 16bit |OP_EXT2 |- |make 2nd operand 16bit |OP_EXT3 |- |make 1st and 2nd operands 16bit |OP_STOP |- |stop VM mruby-2.0.0/examples/000077500000000000000000000000001340361412400144635ustar00rootroot00000000000000mruby-2.0.0/examples/mrbgems/000077500000000000000000000000001340361412400161175ustar00rootroot00000000000000mruby-2.0.0/examples/mrbgems/c_and_ruby_extension_example/000077500000000000000000000000001340361412400240335ustar00rootroot00000000000000mruby-2.0.0/examples/mrbgems/c_and_ruby_extension_example/README.md000066400000000000000000000001501340361412400253060ustar00rootroot00000000000000C and Ruby Extension Example ========= This is an example gem which implements a C and Ruby extension. mruby-2.0.0/examples/mrbgems/c_and_ruby_extension_example/mrbgem.rake000066400000000000000000000015111340361412400261460ustar00rootroot00000000000000MRuby::Gem::Specification.new('c_and_ruby_extension_example') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' # Add compile flags # spec.cc.flags << '' # Add cflags to all # spec.mruby.cc.flags << '-g' # Add libraries # spec.linker.libraries << 'external_lib' # Default build files # spec.rbfiles = Dir.glob("#{dir}/mrblib/*.rb") # spec.objs = Dir.glob("#{dir}/src/*.{c,cpp,m,asm,S}").map { |f| objfile(f.relative_path_from(dir).pathmap("#{build_dir}/%X")) } # spec.test_rbfiles = Dir.glob("#{dir}/test/*.rb") # spec.test_objs = Dir.glob("#{dir}/test/*.{c,cpp,m,asm,S}").map { |f| objfile(f.relative_path_from(dir).pathmap("#{build_dir}/%X")) } # spec.test_preload = 'test/assert.rb' # Values accessible as TEST_ARGS inside test scripts # spec.test_args = {'tmp_dir' => Dir::tmpdir} end mruby-2.0.0/examples/mrbgems/c_and_ruby_extension_example/mrblib/000077500000000000000000000000001340361412400253025ustar00rootroot00000000000000mruby-2.0.0/examples/mrbgems/c_and_ruby_extension_example/mrblib/example.rb000066400000000000000000000001351340361412400272610ustar00rootroot00000000000000module CRubyExtension def CRubyExtension.ruby_method puts "A Ruby Extension" end end mruby-2.0.0/examples/mrbgems/c_and_ruby_extension_example/src/000077500000000000000000000000001340361412400246225ustar00rootroot00000000000000mruby-2.0.0/examples/mrbgems/c_and_ruby_extension_example/src/example.c000066400000000000000000000007271340361412400264270ustar00rootroot00000000000000#include #include static mrb_value mrb_c_method(mrb_state *mrb, mrb_value self) { puts("A C Extension"); return self; } void mrb_c_and_ruby_extension_example_gem_init(mrb_state* mrb) { struct RClass *class_cextension = mrb_define_module(mrb, "CRubyExtension"); mrb_define_class_method(mrb, class_cextension, "c_method", mrb_c_method, MRB_ARGS_NONE()); } void mrb_c_and_ruby_extension_example_gem_final(mrb_state* mrb) { /* finalizer */ } mruby-2.0.0/examples/mrbgems/c_and_ruby_extension_example/test/000077500000000000000000000000001340361412400250125ustar00rootroot00000000000000mruby-2.0.0/examples/mrbgems/c_and_ruby_extension_example/test/example.rb000066400000000000000000000002621340361412400267720ustar00rootroot00000000000000assert('C and Ruby Extension Example 1') do CRubyExtension.respond_to? :c_method end assert('C and Ruby Extension Example 2') do CRubyExtension.respond_to? :ruby_method end mruby-2.0.0/examples/mrbgems/c_extension_example/000077500000000000000000000000001340361412400221505ustar00rootroot00000000000000mruby-2.0.0/examples/mrbgems/c_extension_example/README.md000066400000000000000000000001261340361412400234260ustar00rootroot00000000000000C Extension Example ========= This is an example gem which implements a C extension. mruby-2.0.0/examples/mrbgems/c_extension_example/mrbgem.rake000066400000000000000000000015021340361412400242630ustar00rootroot00000000000000MRuby::Gem::Specification.new('c_extension_example') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' # Add compile flags # spec.cc.flags << '-g' # Add cflags to all # spec.mruby.cc.flags << '-g' # Add libraries # spec.linker.libraries << 'external_lib' # Default build files # spec.rbfiles = Dir.glob("#{dir}/mrblib/*.rb") # spec.objs = Dir.glob("#{dir}/src/*.{c,cpp,m,asm,S}").map { |f| objfile(f.relative_path_from(dir).pathmap("#{build_dir}/%X")) } # spec.test_rbfiles = Dir.glob("#{dir}/test/*.rb") # spec.test_objs = Dir.glob("#{dir}/test/*.{c,cpp,m,asm,S}").map { |f| objfile(f.relative_path_from(dir).pathmap("#{build_dir}/%X")) } # spec.test_preload = 'test/assert.rb' # Values accessible as TEST_ARGS inside test scripts # spec.test_args = {'tmp_dir' => Dir::tmpdir} end mruby-2.0.0/examples/mrbgems/c_extension_example/src/000077500000000000000000000000001340361412400227375ustar00rootroot00000000000000mruby-2.0.0/examples/mrbgems/c_extension_example/src/example.c000066400000000000000000000007011340361412400245340ustar00rootroot00000000000000#include #include static mrb_value mrb_c_method(mrb_state *mrb, mrb_value self) { puts("A C Extension"); return self; } void mrb_c_extension_example_gem_init(mrb_state* mrb) { struct RClass *class_cextension = mrb_define_module(mrb, "CExtension"); mrb_define_class_method(mrb, class_cextension, "c_method", mrb_c_method, MRB_ARGS_NONE()); } void mrb_c_extension_example_gem_final(mrb_state* mrb) { /* finalizer */ } mruby-2.0.0/examples/mrbgems/c_extension_example/test/000077500000000000000000000000001340361412400231275ustar00rootroot00000000000000mruby-2.0.0/examples/mrbgems/c_extension_example/test/example.c000066400000000000000000000001541340361412400247260ustar00rootroot00000000000000#include void mrb_c_extension_example_gem_test(mrb_state *mrb) { /* test initializer in C */ } mruby-2.0.0/examples/mrbgems/c_extension_example/test/example.rb000066400000000000000000000001101340361412400250770ustar00rootroot00000000000000assert('C Extension Example') do CExtension.respond_to? :c_method end mruby-2.0.0/examples/mrbgems/ruby_extension_example/000077500000000000000000000000001340361412400227075ustar00rootroot00000000000000mruby-2.0.0/examples/mrbgems/ruby_extension_example/README.md000066400000000000000000000001461340361412400241670ustar00rootroot00000000000000Pure Ruby Extension Example ========= This is an example gem which implements a pure Ruby extension. mruby-2.0.0/examples/mrbgems/ruby_extension_example/mrbgem.rake000066400000000000000000000016011340361412400250220ustar00rootroot00000000000000MRuby::Gem::Specification.new('ruby_extension_example') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' # Add compile flags # spec.cc.flags << '' # Add cflags to all # spec.mruby.cc.flags << '-g' # Add libraries # spec.linker.libraries << 'external_lib' spec.add_dependency('mruby-print', :core => 'mruby-print') # Default build files # spec.rbfiles = Dir.glob("#{dir}/mrblib/*.rb") # spec.objs = Dir.glob("#{dir}/src/*.{c,cpp,m,asm,S}").map { |f| objfile(f.relative_path_from(dir).pathmap("#{build_dir}/%X")) } # spec.test_rbfiles = Dir.glob("#{dir}/test/*.rb") # spec.test_objs = Dir.glob("#{dir}/test/*.{c,cpp,m,asm,S}").map { |f| objfile(f.relative_path_from(dir).pathmap("#{build_dir}/%X")) } # spec.test_preload = 'test/assert.rb' # Values accessible as TEST_ARGS inside test scripts # spec.test_args = {'tmp_dir' => Dir::tmpdir} end mruby-2.0.0/examples/mrbgems/ruby_extension_example/mrblib/000077500000000000000000000000001340361412400241565ustar00rootroot00000000000000mruby-2.0.0/examples/mrbgems/ruby_extension_example/mrblib/example.rb000066400000000000000000000001321340361412400261320ustar00rootroot00000000000000class RubyExtension def RubyExtension.ruby_method puts "A Ruby Extension" end end mruby-2.0.0/examples/mrbgems/ruby_extension_example/test/000077500000000000000000000000001340361412400236665ustar00rootroot00000000000000mruby-2.0.0/examples/mrbgems/ruby_extension_example/test/example.rb000066400000000000000000000001211340361412400256400ustar00rootroot00000000000000assert('Ruby Extension Example') do RubyExtension.respond_to? :ruby_method end mruby-2.0.0/examples/targets/000077500000000000000000000000001340361412400161345ustar00rootroot00000000000000mruby-2.0.0/examples/targets/build_config_ArduinoDue.rb000066400000000000000000000057351340361412400232360ustar00rootroot00000000000000MRuby::Build.new do |conf| # Gets set by the VS command prompts. if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR'] toolchain :visualcpp else toolchain :gcc end enable_debug # include the default GEMs conf.gembox 'default' end # Cross Compiling configuration for Arduino Due # http://arduino.cc/en/Main/ArduinoBoardDue # # Requires Arduino IDE >= 1.5 MRuby::CrossBuild.new("ArduinoDue") do |conf| toolchain :gcc # Mac OS X, Arduino IDE <= 1.5.6 # ARDUINO_PATH = '/Applications/Arduino.app/Contents/Resources/Java' # Mac OS X, Arduino IDE >= 1.5.7 # ARDUINO_PATH = '/Applications/Arduino.app/Contents/Java' # GNU Linux ARDUINO_PATH = '/opt/arduino' # Arduino IDE <= 1.5.6 BIN_PATH = "#{ARDUINO_PATH}/hardware/tools/g++_arm_none_eabi/bin" # Arduino IDE >= 1.5.7 # BIN_PATH = "#{ARDUINO_PATH}/hardware/tools/gcc-arm-none-eabi-4.8.3-2014q1/bin" SAM_PATH = "#{ARDUINO_PATH}/hardware/arduino/sam" TARGET_PATH = "#{SAM_PATH}/variants/arduino_due_x" conf.cc do |cc| cc.command = "#{BIN_PATH}/arm-none-eabi-gcc" cc.include_paths << ["#{SAM_PATH}/system/libsam", "#{SAM_PATH}/system/CMSIS/CMSIS/Include/", "#{SAM_PATH}/system/CMSIS/Device/ATMEL/", "#{SAM_PATH}/cores/arduino", "#{SAM_PATH}/libraries","#{TARGET_PATH}"] cc.flags = %w(-g -Os -w -ffunction-sections -fdata-sections -nostdlib --param max-inline-insns-single=500 -Dprintf=iprintf -mcpu=cortex-m3 -DF_CPU=84000000L -DARDUINO=156 -DARDUINO_SAM_DUE -DARDUINO_ARCH_SAM -D__SAM3X8E__ -mthumb -DUSB_PID=0x003e -DUSB_VID=0x2341 -DUSBCON -DUSB_MANUFACTURER="Unknown" -DUSB_PRODUCT="Arduino Due") cc.compile_options = "%{flags} -o %{outfile} -c %{infile}" #configuration for low memory environment cc.defines << %w(MRB_HEAP_PAGE_SIZE=64) cc.defines << %w(KHASH_DEFAULT_SIZE=8) cc.defines << %w(MRB_STR_BUF_MIN_SIZE=20) cc.defines << %w(MRB_GC_STRESS) #cc.defines << %w(MRB_DISABLE_STDIO) #if you dont need stdio. #cc.defines << %w(POOL_PAGE_SIZE=1000) #effective only for use with mruby-eval end conf.cxx do |cxx| cxx.command = conf.cc.command.dup cxx.include_paths = conf.cc.include_paths.dup cxx.flags = conf.cc.flags.dup cxx.flags << %w(-fno-rtti -fno-exceptions) cxx.defines = conf.cc.defines.dup cxx.compile_options = conf.cc.compile_options.dup end conf.archiver do |archiver| archiver.command = "#{BIN_PATH}/arm-none-eabi-ar" archiver.archive_options = 'rcs %{outfile} %{objs}' end #no executables conf.bins = [] #do not build executable test conf.build_mrbtest_lib_only #disable C++ exception conf.disable_cxx_exception #gems from core conf.gem :core => "mruby-print" conf.gem :core => "mruby-math" conf.gem :core => "mruby-enum-ext" #light-weight regular expression conf.gem :github => "masamitsu-murase/mruby-hs-regexp", :branch => "master" #Arduino API #conf.gem :github =>"kyab/mruby-arduino", :branch => "master" end mruby-2.0.0/examples/targets/build_config_IntelEdison.rb000066400000000000000000000044411340361412400234050ustar00rootroot00000000000000# Cross-compiling setup for Intel Edison (poky linux) platform # Get SDK from here: https://software.intel.com/en-us/iot/hardware/edison/downloads # REMEMBER to check and update the SDK root in the constant POKY_EDISON_PATH MRuby::Build.new do |conf| toolchain :gcc conf.gembox 'default' conf.cc.defines = %w(ENABLE_READLINE) conf.gembox 'default' #lightweight regular expression conf.gem :github => "pbosetti/mruby-hs-regexp", :branch => "master" end # Define cross build settings MRuby::CrossBuild.new('core2-32-poky-linux') do |conf| toolchain :gcc # Mac OS X # POKY_EDISON_PATH = '/opt/poky-edison/1.7.2' POKY_EDISON_SYSROOT = "#{POKY_EDISON_PATH}/sysroots/core2-32-poky-linux" POKY_EDISON_X86_PATH = "#{POKY_EDISON_PATH}/sysroots/i386-pokysdk-darwin" POKY_EDISON_BIN_PATH = "#{POKY_EDISON_X86_PATH}/usr/bin/i586-poky-linux" conf.cc do |cc| cc.command = "#{POKY_EDISON_BIN_PATH}/i586-poky-linux-gcc" cc.include_paths << ["#{POKY_EDISON_SYSROOT}/usr/include", "#{POKY_EDISON_X86_PATH}/usr/include"] cc.flags = %w(-m32 -march=core2 -mtune=core2 -msse3 -mfpmath=sse -mstackrealign -fno-omit-frame-pointer) cc.flags << %w(-O2 -pipe -g -feliminate-unused-debug-types) cc.flags << "--sysroot=#{POKY_EDISON_SYSROOT}" cc.compile_options = "%{flags} -o %{outfile} -c %{infile}" cc.defines = %w(ENABLE_READLINE) end conf.cxx do |cxx| cxx.command = "#{POKY_EDISON_BIN_PATH}/i586-poky-linux-g++" cxx.include_paths = conf.cc.include_paths.dup cxx.include_paths << ["#{POKY_EDISON_SYSROOT}/usr/include/c++/4.9.1"] cxx.flags = conf.cc.flags.dup cxx.defines = conf.cc.defines.dup cxx.compile_options = conf.cc.compile_options.dup end conf.archiver do |archiver| archiver.command = "#{POKY_EDISON_BIN_PATH}/i586-poky-linux-ar" archiver.archive_options = 'rcs %{outfile} %{objs}' end conf.linker do |linker| linker.command = "#{POKY_EDISON_BIN_PATH}/i586-poky-linux-g++" linker.flags = %w(-m32 -march=i586) linker.flags << "--sysroot=#{POKY_EDISON_SYSROOT}" linker.flags << %w(-O1) linker.libraries = %w(m pthread) end #do not build executable test conf.build_mrbtest_lib_only conf.gembox 'default' #lightweight regular expression conf.gem :github => "pbosetti/mruby-hs-regexp", :branch => "master" end mruby-2.0.0/examples/targets/build_config_IntelGalileo.rb000066400000000000000000000067701340361412400235470ustar00rootroot00000000000000MRuby::Build.new do |conf| # Gets set by the VS command prompts. if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR'] toolchain :visualcpp else toolchain :gcc end enable_debug # include the default GEMs conf.gembox 'default' end # Cross Compiling configuration for Intel Galileo on Arduino environment # http://arduino.cc/en/ArduinoCertified/IntelGalileo # # Requires Arduino IDE for Intel Galileo MRuby::CrossBuild.new("Galileo") do |conf| toolchain :gcc # Mac OS X # Assume you renamed Arduino.app to Arduino_Galileo.app GALILEO_ARDUINO_PATH = '/Applications/Arduino_Galileo.app/Contents/Resources/Java' # GNU Linux #ARDUINO_GALILEO_PATH = '/opt/arduino' GALILEO_BIN_PATH = "#{GALILEO_ARDUINO_PATH}/hardware/tools/x86/i386-pokysdk-darwin/usr/bin/i586-poky-linux-uclibc" GALILEO_SYSROOT = "#{GALILEO_ARDUINO_PATH}/hardware/tools/x86/i586-poky-linux-uclibc" GALILEO_X86_PATH = "#{GALILEO_ARDUINO_PATH}/hardware/arduino/x86" conf.cc do |cc| cc.command = "#{GALILEO_BIN_PATH}/i586-poky-linux-uclibc-gcc" cc.include_paths << ["#{GALILEO_X86_PATH}/cores/arduino", "#{GALILEO_X86_PATH}/variants/galileo_fab_d"] cc.flags = %w(-m32 -march=i586 -c -g -Os -w -ffunction-sections -fdata-sections -MMD -DARDUINO=153) cc.flags << "--sysroot=#{GALILEO_SYSROOT}" cc.compile_options = "%{flags} -o %{outfile} -c %{infile}" end conf.cxx do |cxx| cxx.command = "#{GALILEO_BIN_PATH}/i586-poky-linux-uclibc-g++" cxx.include_paths = conf.cc.include_paths.dup cxx.include_paths << "#{GALILEO_ARDUINO_PATH}/hardware/tools/x86/i586-poky-linux-uclibc/usr/include/c++" cxx.include_paths << "#{GALILEO_ARDUINO_PATH}/hardware/tools/x86/i586-poky-linux-uclibc/usr/include/c++/i586-poky-linux-uclibc" cxx.flags = conf.cc.flags.dup cxx.defines = conf.cc.defines.dup cxx.compile_options = conf.cc.compile_options.dup end conf.archiver do |archiver| archiver.command = "#{GALILEO_BIN_PATH}/i586-poky-linux-uclibc-ar" archiver.archive_options = 'rcs %{outfile} %{objs}' end conf.linker do |linker| linker.command = "#{GALILEO_BIN_PATH}/i586-poky-linux-uclibc-g++" linker.flags = %w(-m32 -march=i586) linker.flags << "--sysroot=#{GALILEO_SYSROOT}" linker.flags << %w(-Os -Wl,--gc-sections) linker.libraries = %w(m pthread) end #no executables conf.bins = [] #do not build executable test conf.build_mrbtest_lib_only #official mrbgems conf.gem :core => "mruby-sprintf" conf.gem :core => "mruby-print" conf.gem :core => "mruby-math" conf.gem :core => "mruby-time" conf.gem :core => "mruby-struct" conf.gem :core => "mruby-enum-ext" conf.gem :core => "mruby-string-ext" conf.gem :core => "mruby-numeric-ext" conf.gem :core => "mruby-array-ext" conf.gem :core => "mruby-hash-ext" conf.gem :core => "mruby-range-ext" conf.gem :core => "mruby-proc-ext" conf.gem :core => "mruby-symbol-ext" conf.gem :core => "mruby-random" conf.gem :core => "mruby-object-ext" conf.gem :core => "mruby-objectspace" conf.gem :core => "mruby-fiber" conf.gem :core => "mruby-toplevel-ext" #lightweigh regular expression conf.gem :github => "masamitsu-murase/mruby-hs-regexp", :branch => "master" #Arduino API #conf.gem :github =>"kyab/mruby-arduino", :branch => "master" do |g| # g.cxx.include_paths << "#{GALILEO_X86_PATH}/libraries/Wire" # g.cxx.include_paths << "#{GALILEO_X86_PATH}/libraries/Servo" #enable unsupported Servo class # g.cxx.defines << "MRUBY_ARDUINO_GALILEO_ENABLE_SERVO" #end end mruby-2.0.0/examples/targets/build_config_RX630.rb000066400000000000000000000040331340361412400217470ustar00rootroot00000000000000MRuby::Build.new do |conf| # Gets set by the VS command prompts. if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR'] toolchain :visualcpp else toolchain :gcc end enable_debug # include the default GEMs conf.gembox 'default' end # Cross Compiling configuration for RX630 # http://gadget.renesas.com/ # # Requires gnurx_v14.03 MRuby::CrossBuild.new("RX630") do |conf| toolchain :gcc # Linux BIN_PATH = "/usr/share/gnurx_v14.03_elf-1/bin" conf.cc do |cc| cc.command = "#{BIN_PATH}/rx-elf-gcc" cc.flags = "-Wall -g -O2 -flto -mcpu=rx600 -m64bit-doubles" cc.compile_options = "%{flags} -o %{outfile} -c %{infile}" #configuration for low memory environment cc.defines << %w(MRB_USE_FLOAT) cc.defines << %w(MRB_HEAP_PAGE_SIZE=64) cc.defines << %w(KHASH_DEFAULT_SIZE=8) cc.defines << %w(MRB_STR_BUF_MIN_SIZE=20) cc.defines << %w(MRB_GC_STRESS) cc.defines << %w(MRB_DISABLE_STDIO) #if you dont need stdio. #cc.defines << %w(POOL_PAGE_SIZE=1000) #effective only for use with mruby-eval end conf.cxx do |cxx| cxx.command = conf.cc.command.dup cxx.include_paths = conf.cc.include_paths.dup cxx.flags = conf.cc.flags.dup cxx.defines = conf.cc.defines.dup cxx.compile_options = conf.cc.compile_options.dup end conf.linker do |linker| linker.command="#{BIN_PATH}/rx-elf-ld" end conf.archiver do |archiver| archiver.command = "#{BIN_PATH}/rx-elf-ar" archiver.archive_options = 'rcs %{outfile} %{objs}' end #no executables conf.bins = [] #do not build executable test conf.build_mrbtest_lib_only #disable C++ exception conf.disable_cxx_exception #gems from core conf.gem :core => "mruby-sprintf" conf.gem :core => "mruby-print" conf.gem :core => "mruby-math" conf.gem :core => "mruby-enum-ext" conf.gem :core => "mruby-numeric-ext" #light-weight regular expression #conf.gem :github => "masamitsu-murase/mruby-hs-regexp", :branch => "master" #Arduino API #conf.gem :github =>"kyab/mruby-arduino", :branch => "master" end mruby-2.0.0/examples/targets/build_config_android_arm64-v8a.rb000066400000000000000000000007671340361412400243240ustar00rootroot00000000000000MRuby::Build.new do |conf| # Gets set by the VS command prompts. if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR'] toolchain :visualcpp else toolchain :gcc end enable_debug # include the default GEMs conf.gembox 'default' end # Requires Android NDK r13 or later. MRuby::CrossBuild.new('android-arm64-v8a') do |conf| params = { :arch => 'arm64-v8a', :platform => 'android-24', :toolchain => :clang, } toolchain :android, params conf.gembox 'default' end mruby-2.0.0/examples/targets/build_config_android_armeabi.rb000066400000000000000000000007631340361412400242730ustar00rootroot00000000000000MRuby::Build.new do |conf| # Gets set by the VS command prompts. if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR'] toolchain :visualcpp else toolchain :gcc end enable_debug # include the default GEMs conf.gembox 'default' end # Requires Android NDK r13 or later. MRuby::CrossBuild.new('android-armeabi') do |conf| params = { :arch => 'armeabi', :platform => 'android-24', :toolchain => :clang, } toolchain :android, params conf.gembox 'default' end mruby-2.0.0/examples/targets/build_config_android_armeabi_v7a_neon_hard.rb000066400000000000000000000010651340361412400270610ustar00rootroot00000000000000MRuby::Build.new do |conf| # Gets set by the VS command prompts. if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR'] toolchain :visualcpp else toolchain :gcc end enable_debug # include the default GEMs conf.gembox 'default' end # Requires Android NDK r13 or later. MRuby::CrossBuild.new('android-armeabi-v7a-neon-hard') do |conf| params = { :arch => 'armeabi-v7a', :mfpu => 'neon', :mfloat_abi => 'hard', :platform => 'android-24', :toolchain => :clang, } toolchain :android, params conf.gembox 'default' end mruby-2.0.0/examples/targets/build_config_chipKITMax32.rb000066400000000000000000000050651340361412400233010ustar00rootroot00000000000000MRuby::Build.new do |conf| # Gets set by the VS command prompts. if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR'] toolchain :visualcpp else toolchain :gcc end enable_debug # include the default GEMs conf.gembox 'default' end # Cross Compiling configuration for Digilent chipKIT Max32 # http://www.digilentinc.com/Products/Detail.cfm?Prod=CHIPKIT-MAX32 # # Requires MPIDE (https://github.com/chipKIT32/chipKIT32-MAX) # # This configuration is based on @kyab's version # http://d.hatena.ne.jp/kyab/20130201 MRuby::CrossBuild.new("chipKITMax32") do |conf| toolchain :gcc # Mac OS X # MPIDE_PATH = '/Applications/Mpide.app/Contents/Resources/Java' # GNU Linux MPIDE_PATH = '/opt/mpide-0023-linux-20120903' PIC32_PATH = "#{MPIDE_PATH}/hardware/pic32" conf.cc do |cc| cc.command = "#{PIC32_PATH}/compiler/pic32-tools/bin/pic32-gcc" cc.include_paths << ["#{PIC32_PATH}/cores/pic32", "#{PIC32_PATH}/variants/Max32", "#{PIC32_PATH}/libraries"] cc.flags = %w(-O2 -mno-smart-io -w -ffunction-sections -fdata-sections -g -mdebugger -Wcast-align -fno-short-double -mprocessor=32MX795F512L -DF_CPU=80000000L -DARDUINO=23 -D_BOARD_MEGA_ -DMPIDEVER=0x01000202 -DMPIDE=23) cc.compile_options = "%{flags} -o %{outfile} -c %{infile}" #configuration for low memory environment cc.defines << %w(MRB_HEAP_PAGE_SIZE=64) cc.defines << %w(KHASH_DEFAULT_SIZE=8) cc.defines << %w(MRB_STR_BUF_MIN_SIZE=20) cc.defines << %w(MRB_GC_STRESS) #cc.defines << %w(MRB_DISABLE_STDIO) #if you dont need stdio. #cc.defines << %w(POOL_PAGE_SIZE=1000) #effective only for use with mruby-eval end conf.cxx do |cxx| cxx.command = conf.cc.command.dup cxx.include_paths = conf.cc.include_paths.dup cxx.flags = conf.cc.flags.dup cxx.defines = conf.cc.defines.dup cxx.compile_options = conf.cc.compile_options.dup end conf.archiver do |archiver| archiver.command = "#{PIC32_PATH}/compiler/pic32-tools/bin/pic32-ar" archiver.archive_options = 'rcs %{outfile} %{objs}' end #no executables conf.bins = [] #do not build test executable conf.build_mrbtest_lib_only #disable C++ exception conf.disable_cxx_exception #gems from core conf.gem :core => "mruby-print" conf.gem :core => "mruby-math" conf.gem :core => "mruby-enum-ext" #light-weight regular expression conf.gem :github => "masamitsu-murase/mruby-hs-regexp", :branch => "master" #Arduino API #conf.gem :github =>"kyab/mruby-arduino", :branch => "master" end mruby-2.0.0/include/000077500000000000000000000000001340361412400142705ustar00rootroot00000000000000mruby-2.0.0/include/mrbconf.h000066400000000000000000000072441340361412400160760ustar00rootroot00000000000000/* ** mrbconf.h - mruby core configuration ** ** See Copyright Notice in mruby.h */ #ifndef MRUBYCONF_H #define MRUBYCONF_H #include #include /* architecture selection: */ /* specify -DMRB_32BIT or -DMRB_64BIT to override */ #if !defined(MRB_32BIT) && !defined(MRB_64BIT) #if UINT64_MAX == SIZE_MAX #define MRB_64BIT #else #define MRB_32BIT #endif #endif #if defined(MRB_32BIT) && defined(MRB_64BIT) #error Cannot build for 32 and 64 bit architecture at the same time #endif /* configuration options: */ /* add -DMRB_USE_FLOAT to use float instead of double for floating point numbers */ //#define MRB_USE_FLOAT /* exclude floating point numbers */ //#define MRB_WITHOUT_FLOAT /* add -DMRB_METHOD_CACHE to use method cache to improve performance */ //#define MRB_METHOD_CACHE /* size of the method cache (need to be the power of 2) */ //#define MRB_METHOD_CACHE_SIZE (1<<7) /* add -DMRB_METHOD_TABLE_INLINE to reduce the size of method table */ /* MRB_METHOD_TABLE_INLINE requires LSB of function pointers to be zero */ /* you might need to specify --falign-functions=n (where n>1) */ //#define MRB_METHOD_TABLE_INLINE /* add -DMRB_INT16 to use 16bit integer for mrb_int; conflict with MRB_INT64 */ //#define MRB_INT16 /* add -DMRB_INT64 to use 64bit integer for mrb_int; conflict with MRB_INT16 */ //#define MRB_INT64 /* if no specific integer type is chosen */ #if !defined(MRB_INT16) && !defined(MRB_INT32) && !defined(MRB_INT64) # if defined(MRB_64BIT) && !defined(MRB_NAN_BOXING) /* Use 64bit integers on 64bit architecture (without MRB_NAN_BOXING) */ # define MRB_INT64 # else /* Otherwise use 32bit integers */ # define MRB_INT32 # endif #endif /* represent mrb_value in boxed double; conflict with MRB_USE_FLOAT and MRB_WITHOUT_FLOAT */ //#define MRB_NAN_BOXING /* define on big endian machines; used by MRB_NAN_BOXING */ //#define MRB_ENDIAN_BIG /* represent mrb_value as a word (natural unit of data for the processor) */ //#define MRB_WORD_BOXING /* string class to handle UTF-8 encoding */ //#define MRB_UTF8_STRING /* argv max size in mrb_funcall */ //#define MRB_FUNCALL_ARGC_MAX 16 /* number of object per heap page */ //#define MRB_HEAP_PAGE_SIZE 1024 /* if _etext and _edata available, mruby can reduce memory used by symbols */ //#define MRB_USE_ETEXT_EDATA /* do not use __init_array_start to determine readonly data section; effective only when MRB_USE_ETEXT_EDATA is defined */ //#define MRB_NO_INIT_ARRAY_START /* turn off generational GC by default */ //#define MRB_GC_TURN_OFF_GENERATIONAL /* default size of khash table bucket */ //#define KHASH_DEFAULT_SIZE 32 /* allocated memory address alignment */ //#define POOL_ALIGNMENT 4 /* page size of memory pool */ //#define POOL_PAGE_SIZE 16000 /* initial minimum size for string buffer */ //#define MRB_STR_BUF_MIN_SIZE 128 /* arena size */ //#define MRB_GC_ARENA_SIZE 100 /* fixed size GC arena */ //#define MRB_GC_FIXED_ARENA /* state atexit stack size */ //#define MRB_FIXED_STATE_ATEXIT_STACK_SIZE 5 /* fixed size state atexit stack */ //#define MRB_FIXED_STATE_ATEXIT_STACK /* -DMRB_DISABLE_XXXX to drop following features */ //#define MRB_DISABLE_STDIO /* use of stdio */ /* -DMRB_ENABLE_XXXX to enable following features */ //#define MRB_ENABLE_DEBUG_HOOK /* hooks for debugger */ /* end of configuration */ /* define MRB_DISABLE_XXXX from DISABLE_XXX (for compatibility) */ #ifdef DISABLE_STDIO #define MRB_DISABLE_STDIO #endif /* define MRB_ENABLE_XXXX from ENABLE_XXX (for compatibility) */ #ifdef ENABLE_DEBUG #define MRB_ENABLE_DEBUG_HOOK #endif #ifndef MRB_DISABLE_STDIO # include #endif #ifndef FALSE # define FALSE 0 #endif #ifndef TRUE # define TRUE 1 #endif #endif /* MRUBYCONF_H */ mruby-2.0.0/include/mruby.h000066400000000000000000001244771340361412400156160ustar00rootroot00000000000000/* ** mruby - An embeddable Ruby implementation ** ** Copyright (c) mruby developers 2010-2018 ** ** Permission is hereby granted, free of charge, to any person obtaining ** a copy of this software and associated documentation files (the ** "Software"), to deal in the Software without restriction, including ** without limitation the rights to use, copy, modify, merge, publish, ** distribute, sublicense, and/or sell copies of the Software, and to ** permit persons to whom the Software is furnished to do so, subject to ** the following conditions: ** ** The above copyright notice and this permission notice shall be ** included in all copies or substantial portions of the Software. ** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ** ** [ MIT license: http://www.opensource.org/licenses/mit-license.php ] */ #ifndef MRUBY_H #define MRUBY_H #ifdef __cplusplus #define __STDC_LIMIT_MACROS #define __STDC_CONSTANT_MACROS #define __STDC_FORMAT_MACROS #endif #include #include #include #ifdef __cplusplus #ifndef SIZE_MAX #ifdef __SIZE_MAX__ #define SIZE_MAX __SIZE_MAX__ #else #define SIZE_MAX std::numeric_limits::max() #endif #endif #endif #ifdef MRB_DEBUG #include #define mrb_assert(p) assert(p) #define mrb_assert_int_fit(t1,n,t2,max) assert((n)>=0 && ((sizeof(n)<=sizeof(t2))||(n<=(t1)(max)))) #else #define mrb_assert(p) ((void)0) #define mrb_assert_int_fit(t1,n,t2,max) ((void)0) #endif #if __STDC_VERSION__ >= 201112L #define mrb_static_assert(exp, str) _Static_assert(exp, str) #else #define mrb_static_assert(exp, str) mrb_assert(exp) #endif #include "mrbconf.h" #ifndef MRB_WITHOUT_FLOAT #ifndef FLT_EPSILON #define FLT_EPSILON (1.19209290e-07f) #endif #ifndef DBL_EPSILON #define DBL_EPSILON ((double)2.22044604925031308085e-16L) #endif #ifndef LDBL_EPSILON #define LDBL_EPSILON (1.08420217248550443401e-19L) #endif #ifdef MRB_USE_FLOAT #define MRB_FLOAT_EPSILON FLT_EPSILON #else #define MRB_FLOAT_EPSILON DBL_EPSILON #endif #endif #include "mruby/common.h" #include #include #include /** * MRuby C API entry point */ MRB_BEGIN_DECL typedef uint8_t mrb_code; /** * Required arguments signature type. */ typedef uint32_t mrb_aspec; struct mrb_irep; struct mrb_state; /** * Function pointer type of custom allocator used in @see mrb_open_allocf. * * The function pointing it must behave similarly as realloc except: * - If ptr is NULL it must allocate new space. * - If s is NULL, ptr must be freed. * * See @see mrb_default_allocf for the default implementation. */ typedef void* (*mrb_allocf) (struct mrb_state *mrb, void*, size_t, void *ud); #ifndef MRB_FIXED_STATE_ATEXIT_STACK_SIZE #define MRB_FIXED_STATE_ATEXIT_STACK_SIZE 5 #endif typedef struct { mrb_sym mid; struct RProc *proc; mrb_value *stackent; uint16_t ridx; uint16_t epos; struct REnv *env; mrb_code *pc; /* return address */ mrb_code *err; /* error position */ int argc; int acc; struct RClass *target_class; } mrb_callinfo; enum mrb_fiber_state { MRB_FIBER_CREATED = 0, MRB_FIBER_RUNNING, MRB_FIBER_RESUMED, MRB_FIBER_SUSPENDED, MRB_FIBER_TRANSFERRED, MRB_FIBER_TERMINATED, }; struct mrb_context { struct mrb_context *prev; mrb_value *stack; /* stack of virtual machine */ mrb_value *stbase, *stend; mrb_callinfo *ci; mrb_callinfo *cibase, *ciend; uint16_t *rescue; /* exception handler stack */ uint16_t rsize; struct RProc **ensure; /* ensure handler stack */ uint16_t esize, eidx; enum mrb_fiber_state status; mrb_bool vmexec; struct RFiber *fib; }; #ifdef MRB_METHOD_CACHE_SIZE # define MRB_METHOD_CACHE #else /* default method cache size: 128 */ /* cache size needs to be power of 2 */ # define MRB_METHOD_CACHE_SIZE (1<<7) #endif typedef mrb_value (*mrb_func_t)(struct mrb_state *mrb, mrb_value); #ifdef MRB_METHOD_TABLE_INLINE typedef uintptr_t mrb_method_t; #else typedef struct { mrb_bool func_p; union { struct RProc *proc; mrb_func_t func; }; } mrb_method_t; #endif #ifdef MRB_METHOD_CACHE struct mrb_cache_entry { struct RClass *c, *c0; mrb_sym mid; mrb_method_t m; }; #endif struct mrb_jmpbuf; typedef void (*mrb_atexit_func)(struct mrb_state*); #define MRB_STATE_NO_REGEXP 1 #define MRB_STATE_REGEXP 2 typedef struct mrb_state { struct mrb_jmpbuf *jmp; uint32_t flags; mrb_allocf allocf; /* memory allocation function */ void *allocf_ud; /* auxiliary data of allocf */ struct mrb_context *c; struct mrb_context *root_c; struct iv_tbl *globals; /* global variable table */ struct RObject *exc; /* exception */ struct RObject *top_self; struct RClass *object_class; /* Object class */ struct RClass *class_class; struct RClass *module_class; struct RClass *proc_class; struct RClass *string_class; struct RClass *array_class; struct RClass *hash_class; struct RClass *range_class; #ifndef MRB_WITHOUT_FLOAT struct RClass *float_class; #endif struct RClass *fixnum_class; struct RClass *true_class; struct RClass *false_class; struct RClass *nil_class; struct RClass *symbol_class; struct RClass *kernel_module; struct alloca_header *mems; mrb_gc gc; #ifdef MRB_METHOD_CACHE struct mrb_cache_entry cache[MRB_METHOD_CACHE_SIZE]; #endif mrb_sym symidx; struct kh_n2s *name2sym; /* symbol hash */ struct symbol_name *symtbl; /* symbol table */ size_t symcapa; #ifdef MRB_ENABLE_DEBUG_HOOK void (*code_fetch_hook)(struct mrb_state* mrb, struct mrb_irep *irep, mrb_code *pc, mrb_value *regs); void (*debug_op_hook)(struct mrb_state* mrb, struct mrb_irep *irep, mrb_code *pc, mrb_value *regs); #endif #ifdef MRB_BYTECODE_DECODE_OPTION mrb_code (*bytecode_decoder)(struct mrb_state* mrb, mrb_code code); #endif struct RClass *eException_class; struct RClass *eStandardError_class; struct RObject *nomem_err; /* pre-allocated NoMemoryError */ struct RObject *stack_err; /* pre-allocated SysStackError */ #ifdef MRB_GC_FIXED_ARENA struct RObject *arena_err; /* pre-allocated arena overfow error */ #endif void *ud; /* auxiliary data */ #ifdef MRB_FIXED_STATE_ATEXIT_STACK mrb_atexit_func atexit_stack[MRB_FIXED_STATE_ATEXIT_STACK_SIZE]; #else mrb_atexit_func *atexit_stack; #endif uint16_t atexit_stack_len; uint16_t ecall_nest; /* prevent infinite recursive ecall() */ } mrb_state; /** * Defines a new class. * * If you're creating a gem it may look something like this: * * !!!c * void mrb_example_gem_init(mrb_state* mrb) { * struct RClass *example_class; * example_class = mrb_define_class(mrb, "Example_Class", mrb->object_class); * } * * void mrb_example_gem_final(mrb_state* mrb) { * //free(TheAnimals); * } * * @param [mrb_state *] mrb The current mruby state. * @param [const char *] name The name of the defined class. * @param [struct RClass *] super The new class parent. * @return [struct RClass *] Reference to the newly defined class. * @see mrb_define_class_under */ MRB_API struct RClass *mrb_define_class(mrb_state *mrb, const char *name, struct RClass *super); /** * Defines a new module. * * @param [mrb_state *] mrb_state* The current mruby state. * @param [const char *] char* The name of the module. * @return [struct RClass *] Reference to the newly defined module. */ MRB_API struct RClass *mrb_define_module(mrb_state *, const char*); MRB_API mrb_value mrb_singleton_class(mrb_state*, mrb_value); /** * Include a module in another class or module. * Equivalent to: * * module B * include A * end * @param [mrb_state *] mrb_state* The current mruby state. * @param [struct RClass *] RClass* A reference to module or a class. * @param [struct RClass *] RClass* A reference to the module to be included. */ MRB_API void mrb_include_module(mrb_state*, struct RClass*, struct RClass*); /** * Prepends a module in another class or module. * * Equivalent to: * module B * prepend A * end * @param [mrb_state *] mrb_state* The current mruby state. * @param [struct RClass *] RClass* A reference to module or a class. * @param [struct RClass *] RClass* A reference to the module to be prepended. */ MRB_API void mrb_prepend_module(mrb_state*, struct RClass*, struct RClass*); /** * Defines a global function in ruby. * * If you're creating a gem it may look something like this * * Example: * * !!!c * mrb_value example_method(mrb_state* mrb, mrb_value self) * { * puts("Executing example command!"); * return self; * } * * void mrb_example_gem_init(mrb_state* mrb) * { * mrb_define_method(mrb, mrb->kernel_module, "example_method", example_method, MRB_ARGS_NONE()); * } * * @param [mrb_state *] mrb The MRuby state reference. * @param [struct RClass *] cla The class pointer where the method will be defined. * @param [const char *] name The name of the method being defined. * @param [mrb_func_t] func The function pointer to the method definition. * @param [mrb_aspec] aspec The method parameters declaration. */ MRB_API void mrb_define_method(mrb_state *mrb, struct RClass *cla, const char *name, mrb_func_t func, mrb_aspec aspec); /** * Defines a class method. * * Example: * * # Ruby style * class Foo * def Foo.bar * end * end * // C style * mrb_value bar_method(mrb_state* mrb, mrb_value self){ * return mrb_nil_value(); * } * void mrb_example_gem_init(mrb_state* mrb){ * struct RClass *foo; * foo = mrb_define_class(mrb, "Foo", mrb->object_class); * mrb_define_class_method(mrb, foo, "bar", bar_method, MRB_ARGS_NONE()); * } * @param [mrb_state *] mrb_state* The MRuby state reference. * @param [struct RClass *] RClass* The class where the class method will be defined. * @param [const char *] char* The name of the class method being defined. * @param [mrb_func_t] mrb_func_t The function pointer to the class method definition. * @param [mrb_aspec] mrb_aspec The method parameters declaration. */ MRB_API void mrb_define_class_method(mrb_state *, struct RClass *, const char *, mrb_func_t, mrb_aspec); MRB_API void mrb_define_singleton_method(mrb_state*, struct RObject*, const char*, mrb_func_t, mrb_aspec); /** * Defines a module function. * * Example: * * # Ruby style * module Foo * def Foo.bar * end * end * // C style * mrb_value bar_method(mrb_state* mrb, mrb_value self){ * return mrb_nil_value(); * } * void mrb_example_gem_init(mrb_state* mrb){ * struct RClass *foo; * foo = mrb_define_module(mrb, "Foo"); * mrb_define_module_function(mrb, foo, "bar", bar_method, MRB_ARGS_NONE()); * } * @param [mrb_state *] mrb_state* The MRuby state reference. * @param [struct RClass *] RClass* The module where the module function will be defined. * @param [const char *] char* The name of the module function being defined. * @param [mrb_func_t] mrb_func_t The function pointer to the module function definition. * @param [mrb_aspec] mrb_aspec The method parameters declaration. */ MRB_API void mrb_define_module_function(mrb_state*, struct RClass*, const char*, mrb_func_t, mrb_aspec); /** * Defines a constant. * * Example: * * # Ruby style * class ExampleClass * AGE = 22 * end * // C style * #include * #include * * void * mrb_example_gem_init(mrb_state* mrb){ * mrb_define_const(mrb, mrb->kernel_module, "AGE", mrb_fixnum_value(22)); * } * * mrb_value * mrb_example_gem_final(mrb_state* mrb){ * } * @param [mrb_state *] mrb_state* The MRuby state reference. * @param [struct RClass *] RClass* A class or module the constant is defined in. * @param [const char *] name The name of the constant being defined. * @param [mrb_value] mrb_value The value for the constant. */ MRB_API void mrb_define_const(mrb_state*, struct RClass*, const char *name, mrb_value); /** * Undefines a method. * * Example: * * # Ruby style * * class ExampleClassA * def example_method * "example" * end * end * ExampleClassA.new.example_method # => example * * class ExampleClassB < ExampleClassA * undef_method :example_method * end * * ExampleClassB.new.example_method # => undefined method 'example_method' for ExampleClassB (NoMethodError) * * // C style * #include * #include * * mrb_value * mrb_example_method(mrb_state *mrb){ * return mrb_str_new_lit(mrb, "example"); * } * * void * mrb_example_gem_init(mrb_state* mrb){ * struct RClass *example_class_a; * struct RClass *example_class_b; * struct RClass *example_class_c; * * example_class_a = mrb_define_class(mrb, "ExampleClassA", mrb->object_class); * mrb_define_method(mrb, example_class_a, "example_method", mrb_example_method, MRB_ARGS_NONE()); * example_class_b = mrb_define_class(mrb, "ExampleClassB", example_class_a); * example_class_c = mrb_define_class(mrb, "ExampleClassC", example_class_b); * mrb_undef_method(mrb, example_class_c, "example_method"); * } * * mrb_example_gem_final(mrb_state* mrb){ * } * @param [mrb_state*] mrb_state* The mruby state reference. * @param [struct RClass*] RClass* A class the method will be undefined from. * @param [const char] const char* The name of the method to be undefined. */ MRB_API void mrb_undef_method(mrb_state*, struct RClass*, const char*); MRB_API void mrb_undef_method_id(mrb_state*, struct RClass*, mrb_sym); /** * Undefine a class method. * Example: * * # Ruby style * class ExampleClass * def self.example_method * "example" * end * end * * ExampleClass.example_method * * // C style * #include * #include * * mrb_value * mrb_example_method(mrb_state *mrb){ * return mrb_str_new_lit(mrb, "example"); * } * * void * mrb_example_gem_init(mrb_state* mrb){ * struct RClass *example_class; * example_class = mrb_define_class(mrb, "ExampleClass", mrb->object_class); * mrb_define_class_method(mrb, example_class, "example_method", mrb_example_method, MRB_ARGS_NONE()); * mrb_undef_class_method(mrb, example_class, "example_method"); * } * * void * mrb_example_gem_final(mrb_state* mrb){ * } * @param [mrb_state*] mrb_state* The mruby state reference. * @param [RClass*] RClass* A class the class method will be undefined from. * @param [constchar*] constchar* The name of the class method to be undefined. */ MRB_API void mrb_undef_class_method(mrb_state*, struct RClass*, const char*); /** * Initialize a new object instance of c class. * * Example: * * # Ruby style * class ExampleClass * end * * p ExampleClass # => # * // C style * #include * #include * * void * mrb_example_gem_init(mrb_state* mrb) { * struct RClass *example_class; * mrb_value obj; * example_class = mrb_define_class(mrb, "ExampleClass", mrb->object_class); # => class ExampleClass; end * obj = mrb_obj_new(mrb, example_class, 0, NULL); # => ExampleClass.new * mrb_p(mrb, obj); // => Kernel#p * } * @param [mrb_state*] mrb The current mruby state. * @param [RClass*] c Reference to the class of the new object. * @param [mrb_int] argc Number of arguments in argv * @param [const mrb_value *] argv Array of mrb_value to initialize the object * @return [mrb_value] The newly initialized object */ MRB_API mrb_value mrb_obj_new(mrb_state *mrb, struct RClass *c, mrb_int argc, const mrb_value *argv); /** @see mrb_obj_new */ MRB_INLINE mrb_value mrb_class_new_instance(mrb_state *mrb, mrb_int argc, const mrb_value *argv, struct RClass *c) { return mrb_obj_new(mrb,c,argc,argv); } MRB_API mrb_value mrb_instance_new(mrb_state *mrb, mrb_value cv); /** * Creates a new instance of Class, Class. * * Example: * * void * mrb_example_gem_init(mrb_state* mrb) { * struct RClass *example_class; * * mrb_value obj; * example_class = mrb_class_new(mrb, mrb->object_class); * obj = mrb_obj_new(mrb, example_class, 0, NULL); // => #<#:0x9a94588> * mrb_p(mrb, obj); // => Kernel#p * } * * @param [mrb_state*] mrb The current mruby state. * @param [struct RClass *] super The super class or parent. * @return [struct RClass *] Reference to the new class. */ MRB_API struct RClass * mrb_class_new(mrb_state *mrb, struct RClass *super); /** * Creates a new module, Module. * * Example: * void * mrb_example_gem_init(mrb_state* mrb) { * struct RClass *example_module; * * example_module = mrb_module_new(mrb); * } * * @param [mrb_state*] mrb The current mruby state. * @return [struct RClass *] Reference to the new module. */ MRB_API struct RClass * mrb_module_new(mrb_state *mrb); /** * Returns an mrb_bool. True if class was defined, and false if the class was not defined. * * Example: * void * mrb_example_gem_init(mrb_state* mrb) { * struct RClass *example_class; * mrb_bool cd; * * example_class = mrb_define_class(mrb, "ExampleClass", mrb->object_class); * cd = mrb_class_defined(mrb, "ExampleClass"); * * // If mrb_class_defined returns 1 then puts "True" * // If mrb_class_defined returns 0 then puts "False" * if (cd == 1){ * puts("True"); * } * else { * puts("False"); * } * } * * @param [mrb_state*] mrb The current mruby state. * @param [const char *] name A string representing the name of the class. * @return [mrb_bool] A boolean value. */ MRB_API mrb_bool mrb_class_defined(mrb_state *mrb, const char *name); /** * Gets a class. * @param [mrb_state*] mrb The current mruby state. * @param [const char *] name The name of the class. * @return [struct RClass *] A reference to the class. */ MRB_API struct RClass * mrb_class_get(mrb_state *mrb, const char *name); /** * Gets a exception class. * @param [mrb_state*] mrb The current mruby state. * @param [const char *] name The name of the class. * @return [struct RClass *] A reference to the class. */ MRB_API struct RClass * mrb_exc_get(mrb_state *mrb, const char *name); /** * Returns an mrb_bool. True if inner class was defined, and false if the inner class was not defined. * * Example: * void * mrb_example_gem_init(mrb_state* mrb) { * struct RClass *example_outer, *example_inner; * mrb_bool cd; * * example_outer = mrb_define_module(mrb, "ExampleOuter"); * * example_inner = mrb_define_class_under(mrb, example_outer, "ExampleInner", mrb->object_class); * cd = mrb_class_defined_under(mrb, example_outer, "ExampleInner"); * * // If mrb_class_defined_under returns 1 then puts "True" * // If mrb_class_defined_under returns 0 then puts "False" * if (cd == 1){ * puts("True"); * } * else { * puts("False"); * } * } * * @param [mrb_state*] mrb The current mruby state. * @param [struct RClass *] outer The name of the outer class. * @param [const char *] name A string representing the name of the inner class. * @return [mrb_bool] A boolean value. */ MRB_API mrb_bool mrb_class_defined_under(mrb_state *mrb, struct RClass *outer, const char *name); /** * Gets a child class. * @param [mrb_state*] mrb The current mruby state. * @param [struct RClass *] outer The name of the parent class. * @param [const char *] name The name of the class. * @return [struct RClass *] A reference to the class. */ MRB_API struct RClass * mrb_class_get_under(mrb_state *mrb, struct RClass *outer, const char *name); /** * Gets a module. * @param [mrb_state*] mrb The current mruby state. * @param [const char *] name The name of the module. * @return [struct RClass *] A reference to the module. */ MRB_API struct RClass * mrb_module_get(mrb_state *mrb, const char *name); /** * Gets a module defined under another module. * @param [mrb_state*] mrb The current mruby state. * @param [struct RClass *] outer The name of the outer module. * @param [const char *] name The name of the module. * @return [struct RClass *] A reference to the module. */ MRB_API struct RClass * mrb_module_get_under(mrb_state *mrb, struct RClass *outer, const char *name); MRB_API mrb_value mrb_notimplement_m(mrb_state*, mrb_value); /** * Duplicate an object. * * Equivalent to: * Object#dup * @param [mrb_state*] mrb The current mruby state. * @param [mrb_value] obj Object to be duplicate. * @return [mrb_value] The newly duplicated object. */ MRB_API mrb_value mrb_obj_dup(mrb_state *mrb, mrb_value obj); /** * Returns true if obj responds to the given method. If the method was defined for that * class it returns true, it returns false otherwise. * * Example: * # Ruby style * class ExampleClass * def example_method * end * end * * ExampleClass.new.respond_to?(:example_method) # => true * * // C style * void * mrb_example_gem_init(mrb_state* mrb) { * struct RClass *example_class; * mrb_sym mid; * mrb_bool obj_resp; * * example_class = mrb_define_class(mrb, "ExampleClass", mrb->object_class); * mrb_define_method(mrb, example_class, "example_method", exampleMethod, MRB_ARGS_NONE()); * mid = mrb_intern_str(mrb, mrb_str_new_lit(mrb, "example_method" )); * obj_resp = mrb_obj_respond_to(mrb, example_class, mid); // => 1(true in Ruby world) * * // If mrb_obj_respond_to returns 1 then puts "True" * // If mrb_obj_respond_to returns 0 then puts "False" * if (obj_resp == 1) { * puts("True"); * } * else if (obj_resp == 0) { * puts("False"); * } * } * * @param [mrb_state*] mrb The current mruby state. * @param [struct RClass *] c A reference to a class. * @param [mrb_sym] mid A symbol referencing a method id. * @return [mrb_bool] A boolean value. */ MRB_API mrb_bool mrb_obj_respond_to(mrb_state *mrb, struct RClass* c, mrb_sym mid); /** * Defines a new class under a given module * * @param [mrb_state*] mrb The current mruby state. * @param [struct RClass *] outer Reference to the module under which the new class will be defined * @param [const char *] name The name of the defined class * @param [struct RClass *] super The new class parent * @return [struct RClass *] Reference to the newly defined class * @see mrb_define_class */ MRB_API struct RClass * mrb_define_class_under(mrb_state *mrb, struct RClass *outer, const char *name, struct RClass *super); MRB_API struct RClass * mrb_define_module_under(mrb_state *mrb, struct RClass *outer, const char *name); /** * Function requires n arguments. * * @param n * The number of required arguments. */ #define MRB_ARGS_REQ(n) ((mrb_aspec)((n)&0x1f) << 18) /** * Function takes n optional arguments * * @param n * The number of optional arguments. */ #define MRB_ARGS_OPT(n) ((mrb_aspec)((n)&0x1f) << 13) /** * Function takes n1 mandatory arguments and n2 optional arguments * * @param n1 * The number of required arguments. * @param n2 * The number of optional arguments. */ #define MRB_ARGS_ARG(n1,n2) (MRB_ARGS_REQ(n1)|MRB_ARGS_OPT(n2)) /** rest argument */ #define MRB_ARGS_REST() ((mrb_aspec)(1 << 12)) /** required arguments after rest */ #define MRB_ARGS_POST(n) ((mrb_aspec)((n)&0x1f) << 7) /** keyword arguments (n of keys, kdict) */ #define MRB_ARGS_KEY(n1,n2) ((mrb_aspec)((((n1)&0x1f) << 2) | ((n2)?(1<<1):0))) /** * Function takes a block argument */ #define MRB_ARGS_BLOCK() ((mrb_aspec)1) /** * Function accepts any number of arguments */ #define MRB_ARGS_ANY() MRB_ARGS_REST() /** * Function accepts no arguments */ #define MRB_ARGS_NONE() ((mrb_aspec)0) /** * Format specifiers for {mrb_get_args} function * * Must be a C string composed of the following format specifiers: * * | char | Ruby type | C types | Notes | * |:----:|----------------|-------------------|----------------------------------------------------| * | `o` | {Object} | {mrb_value} | Could be used to retrieve any type of argument | * | `C` | {Class}/{Module} | {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; `s!` gives (`NULL`,`0`) for `nil` | * | `z` | {String} | char * | `NULL` 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} | | * | `n` | {Symbol} | {mrb_sym} | | * | `&` | block | {mrb_value} | &! raises exception if no block given. | * | `*` | rest arguments | {mrb_value} *, {mrb_int} | Receive the rest of arguments as an array; *! avoid copy of the stack. | * | | | optional | | After this spec following specs would be optional. | * | `?` | optional given | {mrb_bool} | `TRUE` if preceding argument is given. Used to check optional argument is given. | * * @see mrb_get_args */ typedef const char *mrb_args_format; /** * Retrieve arguments from mrb_state. * * @param mrb The current MRuby state. * @param format [mrb_args_format] is a list of format specifiers * @param ... The passing variadic arguments must be a pointer of retrieving type. * @return the number of arguments retrieved. * @see mrb_args_format */ MRB_API mrb_int mrb_get_args(mrb_state *mrb, mrb_args_format format, ...); static inline mrb_sym mrb_get_mid(mrb_state *mrb) /* get method symbol */ { return mrb->c->ci->mid; } /** * Retrieve number of arguments from mrb_state. * * Correctly handles *splat arguments. */ MRB_API mrb_int mrb_get_argc(mrb_state *mrb); MRB_API mrb_value* mrb_get_argv(mrb_state *mrb); /* `strlen` for character string literals (use with caution or `strlen` instead) Adjacent string literals are concatenated in C/C++ in translation phase 6. If `lit` is not one, the compiler will report a syntax error: MSVC: "error C2143: syntax error : missing ')' before 'string'" GCC: "error: expected ')' before string constant" */ #define mrb_strlen_lit(lit) (sizeof(lit "") - 1) /** * Call existing ruby functions. * * #include * #include * #include "mruby/compile.h" * * int * main() * { * mrb_int i = 99; * mrb_state *mrb = mrb_open(); * * if (!mrb) { } * FILE *fp = fopen("test.rb","r"); * mrb_value obj = mrb_load_file(mrb,fp); * mrb_funcall(mrb, obj, "method_name", 1, mrb_fixnum_value(i)); * fclose(fp); * mrb_close(mrb); * } * @param [mrb_state*] mrb_state* The current mruby state. * @param [mrb_value] mrb_value A reference to an mruby value. * @param [const char*] const char* The name of the method. * @param [mrb_int] mrb_int The number of arguments the method has. * @param [...] ... Variadic values(not type safe!). * @return [mrb_value] mrb_value mruby function value. */ MRB_API mrb_value mrb_funcall(mrb_state*, mrb_value, const char*, mrb_int,...); /** * Call existing ruby functions. This is basically the type safe version of mrb_funcall. * * #include * #include * #include "mruby/compile.h" * int * main() * { * mrb_int i = 99; * mrb_state *mrb = mrb_open(); * * if (!mrb) { } * mrb_sym m_sym = mrb_intern_lit(mrb, "method_name"); // Symbol for method. * * FILE *fp = fopen("test.rb","r"); * mrb_value obj = mrb_load_file(mrb,fp); * mrb_funcall_argv(mrb, obj, m_sym, 1, &obj); // Calling ruby function from test.rb. * fclose(fp); * mrb_close(mrb); * } * @param [mrb_state*] mrb_state* The current mruby state. * @param [mrb_value] mrb_value A reference to an mruby value. * @param [mrb_sym] mrb_sym The symbol representing the method. * @param [mrb_int] mrb_int The number of arguments the method has. * @param [const mrb_value*] mrb_value* Pointer to the object. * @return [mrb_value] mrb_value mruby function value. * @see mrb_funcall */ MRB_API mrb_value mrb_funcall_argv(mrb_state*, mrb_value, mrb_sym, mrb_int, const mrb_value*); /** * Call existing ruby functions with a block. */ MRB_API mrb_value mrb_funcall_with_block(mrb_state*, mrb_value, mrb_sym, mrb_int, const mrb_value*, mrb_value); /** * Create a symbol * * # Ruby style: * :pizza # => :pizza * * // C style: * mrb_sym m_sym = mrb_intern_lit(mrb, "pizza"); // => :pizza * @param [mrb_state*] mrb_state* The current mruby state. * @param [const char*] const char* The name of the method. * @return [mrb_sym] mrb_sym A symbol. */ MRB_API mrb_sym mrb_intern_cstr(mrb_state*,const char*); MRB_API mrb_sym mrb_intern(mrb_state*,const char*,size_t); MRB_API mrb_sym mrb_intern_static(mrb_state*,const char*,size_t); #define mrb_intern_lit(mrb, lit) mrb_intern_static(mrb, lit, mrb_strlen_lit(lit)) MRB_API mrb_sym mrb_intern_str(mrb_state*,mrb_value); MRB_API mrb_value mrb_check_intern_cstr(mrb_state*,const char*); MRB_API mrb_value mrb_check_intern(mrb_state*,const char*,size_t); MRB_API mrb_value mrb_check_intern_str(mrb_state*,mrb_value); MRB_API const char *mrb_sym2name(mrb_state*,mrb_sym); MRB_API const char *mrb_sym2name_len(mrb_state*,mrb_sym,mrb_int*); MRB_API mrb_value mrb_sym2str(mrb_state*,mrb_sym); MRB_API void *mrb_malloc(mrb_state*, size_t); /* raise RuntimeError if no mem */ MRB_API void *mrb_calloc(mrb_state*, size_t, size_t); /* ditto */ MRB_API void *mrb_realloc(mrb_state*, void*, size_t); /* ditto */ MRB_API void *mrb_realloc_simple(mrb_state*, void*, size_t); /* return NULL if no memory available */ MRB_API void *mrb_malloc_simple(mrb_state*, size_t); /* return NULL if no memory available */ MRB_API struct RBasic *mrb_obj_alloc(mrb_state*, enum mrb_vtype, struct RClass*); MRB_API void mrb_free(mrb_state*, void*); MRB_API mrb_value mrb_str_new(mrb_state *mrb, const char *p, size_t len); /** * Turns a C string into a Ruby string value. */ 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 MRB_API char* mrb_utf8_from_locale(const char *p, int len); MRB_API char* mrb_locale_from_utf8(const char *p, int len); #define mrb_locale_free(p) free(p) #define mrb_utf8_free(p) free(p) #else #define mrb_utf8_from_locale(p, l) ((char*)p) #define mrb_locale_from_utf8(p, l) ((char*)p) #define mrb_locale_free(p) #define mrb_utf8_free(p) #endif /** * Creates new mrb_state. * * @return * Pointer to the newly created mrb_state. */ MRB_API mrb_state* mrb_open(void); /** * Create new mrb_state with custom allocators. * * @param f * Reference to the allocation function. * @param ud * User data will be passed to custom allocator f. * If user data isn't required just pass NULL. * @return * Pointer to the newly created mrb_state. */ MRB_API mrb_state* mrb_open_allocf(mrb_allocf f, void *ud); /** * Create new mrb_state with just the MRuby core * * @param f * Reference to the allocation function. * Use mrb_default_allocf for the default * @param ud * User data will be passed to custom allocator f. * If user data isn't required just pass NULL. * @return * Pointer to the newly created mrb_state. */ MRB_API mrb_state* mrb_open_core(mrb_allocf f, void *ud); /** * Closes and frees a mrb_state. * * @param mrb * Pointer to the mrb_state to be closed. */ MRB_API void mrb_close(mrb_state *mrb); /** * The default allocation function. * * @see mrb_allocf */ MRB_API void* mrb_default_allocf(mrb_state*, void*, size_t, void*); MRB_API mrb_value mrb_top_self(mrb_state *); MRB_API mrb_value mrb_run(mrb_state*, struct RProc*, mrb_value); MRB_API mrb_value mrb_top_run(mrb_state*, struct RProc*, mrb_value, unsigned int); MRB_API mrb_value mrb_vm_run(mrb_state*, struct RProc*, mrb_value, unsigned int); MRB_API mrb_value mrb_vm_exec(mrb_state*, struct RProc*, mrb_code*); /* compatibility macros */ #define mrb_toplevel_run_keep(m,p,k) mrb_top_run((m),(p),mrb_top_self(m),(k)) #define mrb_toplevel_run(m,p) mrb_toplevel_run_keep((m),(p),0) #define mrb_context_run(m,p,s,k) mrb_vm_run((m),(p),(s),(k)) MRB_API void mrb_p(mrb_state*, mrb_value); MRB_API mrb_int mrb_obj_id(mrb_value obj); MRB_API mrb_sym mrb_obj_to_sym(mrb_state *mrb, mrb_value name); MRB_API mrb_bool mrb_obj_eq(mrb_state*, mrb_value, mrb_value); MRB_API mrb_bool mrb_obj_equal(mrb_state*, mrb_value, mrb_value); MRB_API mrb_bool mrb_equal(mrb_state *mrb, mrb_value obj1, mrb_value obj2); MRB_API mrb_value mrb_convert_to_integer(mrb_state *mrb, mrb_value val, mrb_int base); MRB_API mrb_value mrb_Integer(mrb_state *mrb, mrb_value val); #ifndef MRB_WITHOUT_FLOAT MRB_API mrb_value mrb_Float(mrb_state *mrb, mrb_value val); #endif MRB_API mrb_value mrb_inspect(mrb_state *mrb, mrb_value obj); MRB_API mrb_bool mrb_eql(mrb_state *mrb, mrb_value obj1, mrb_value obj2); static inline int mrb_gc_arena_save(mrb_state*); static inline void mrb_gc_arena_restore(mrb_state*,int); static inline int mrb_gc_arena_save(mrb_state *mrb) { return mrb->gc.arena_idx; } static inline void mrb_gc_arena_restore(mrb_state *mrb, int idx) { mrb->gc.arena_idx = idx; } MRB_API void mrb_garbage_collect(mrb_state*); MRB_API void mrb_full_gc(mrb_state*); MRB_API void mrb_incremental_gc(mrb_state *); MRB_API void mrb_gc_mark(mrb_state*,struct RBasic*); #define mrb_gc_mark_value(mrb,val) do {\ if (!mrb_immediate_p(val)) mrb_gc_mark((mrb), mrb_basic_ptr(val)); \ } while (0) MRB_API void mrb_field_write_barrier(mrb_state *, struct RBasic*, struct RBasic*); #define mrb_field_write_barrier_value(mrb, obj, val) do{\ if (!mrb_immediate_p(val)) mrb_field_write_barrier((mrb), (obj), mrb_basic_ptr(val)); \ } while (0) MRB_API void mrb_write_barrier(mrb_state *, struct RBasic*); MRB_API mrb_value mrb_check_convert_type(mrb_state *mrb, mrb_value val, enum mrb_vtype type, const char *tname, const char *method); MRB_API mrb_value mrb_any_to_s(mrb_state *mrb, mrb_value obj); MRB_API const char * mrb_obj_classname(mrb_state *mrb, mrb_value obj); MRB_API struct RClass* mrb_obj_class(mrb_state *mrb, mrb_value obj); MRB_API mrb_value mrb_class_path(mrb_state *mrb, struct RClass *c); MRB_API mrb_value mrb_convert_type(mrb_state *mrb, mrb_value val, enum mrb_vtype type, const char *tname, const char *method); MRB_API mrb_bool mrb_obj_is_kind_of(mrb_state *mrb, mrb_value obj, struct RClass *c); MRB_API mrb_value mrb_obj_inspect(mrb_state *mrb, mrb_value self); MRB_API mrb_value mrb_obj_clone(mrb_state *mrb, mrb_value self); #ifndef ISPRINT #define ISASCII(c) ((unsigned)(c) <= 0x7f) #define ISPRINT(c) (((unsigned)(c) - 0x20) < 0x5f) #define ISSPACE(c) ((c) == ' ' || (unsigned)(c) - '\t' < 5) #define ISUPPER(c) (((unsigned)(c) - 'A') < 26) #define ISLOWER(c) (((unsigned)(c) - 'a') < 26) #define ISALPHA(c) ((((unsigned)(c) | 0x20) - 'a') < 26) #define ISDIGIT(c) (((unsigned)(c) - '0') < 10) #define ISXDIGIT(c) (ISDIGIT(c) || ((unsigned)(c) | 0x20) - 'a' < 6) #define ISALNUM(c) (ISALPHA(c) || ISDIGIT(c)) #define ISBLANK(c) ((c) == ' ' || (c) == '\t') #define ISCNTRL(c) ((unsigned)(c) < 0x20 || (c) == 0x7f) #define TOUPPER(c) (ISLOWER(c) ? ((c) & 0x5f) : (c)) #define TOLOWER(c) (ISUPPER(c) ? ((c) | 0x20) : (c)) #endif MRB_API mrb_value mrb_exc_new(mrb_state *mrb, struct RClass *c, const char *ptr, size_t len); MRB_API mrb_noreturn void mrb_exc_raise(mrb_state *mrb, mrb_value exc); MRB_API mrb_noreturn void mrb_raise(mrb_state *mrb, struct RClass *c, const char *msg); MRB_API mrb_noreturn void mrb_raisef(mrb_state *mrb, struct RClass *c, const char *fmt, ...); MRB_API mrb_noreturn void mrb_name_error(mrb_state *mrb, mrb_sym id, const char *fmt, ...); MRB_API void mrb_warn(mrb_state *mrb, const char *fmt, ...); MRB_API mrb_noreturn void mrb_bug(mrb_state *mrb, const char *fmt, ...); MRB_API void mrb_print_backtrace(mrb_state *mrb); MRB_API void mrb_print_error(mrb_state *mrb); /* macros to get typical exception objects note: + those E_* macros requires mrb_state* variable named mrb. + exception objects obtained from those macros are local to mrb */ #define E_RUNTIME_ERROR (mrb_exc_get(mrb, "RuntimeError")) #define E_TYPE_ERROR (mrb_exc_get(mrb, "TypeError")) #define E_ARGUMENT_ERROR (mrb_exc_get(mrb, "ArgumentError")) #define E_INDEX_ERROR (mrb_exc_get(mrb, "IndexError")) #define E_RANGE_ERROR (mrb_exc_get(mrb, "RangeError")) #define E_NAME_ERROR (mrb_exc_get(mrb, "NameError")) #define E_NOMETHOD_ERROR (mrb_exc_get(mrb, "NoMethodError")) #define E_SCRIPT_ERROR (mrb_exc_get(mrb, "ScriptError")) #define E_SYNTAX_ERROR (mrb_exc_get(mrb, "SyntaxError")) #define E_LOCALJUMP_ERROR (mrb_exc_get(mrb, "LocalJumpError")) #define E_REGEXP_ERROR (mrb_exc_get(mrb, "RegexpError")) #define E_FROZEN_ERROR (mrb_exc_get(mrb, "FrozenError")) #define E_NOTIMP_ERROR (mrb_exc_get(mrb, "NotImplementedError")) #ifndef MRB_WITHOUT_FLOAT #define E_FLOATDOMAIN_ERROR (mrb_exc_get(mrb, "FloatDomainError")) #endif #define E_KEY_ERROR (mrb_exc_get(mrb, "KeyError")) MRB_API mrb_value mrb_yield(mrb_state *mrb, mrb_value b, mrb_value arg); MRB_API mrb_value mrb_yield_argv(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value *argv); MRB_API mrb_value mrb_yield_with_class(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value *argv, mrb_value self, struct RClass *c); /* continue execution to the proc */ /* this function should always be called as the last function of a method */ /* e.g. return mrb_yield_cont(mrb, proc, self, argc, argv); */ mrb_value mrb_yield_cont(mrb_state *mrb, mrb_value b, mrb_value self, mrb_int argc, const mrb_value *argv); /* mrb_gc_protect() leaves the object in the arena */ MRB_API void mrb_gc_protect(mrb_state *mrb, mrb_value obj); /* mrb_gc_register() keeps the object from GC. */ MRB_API void mrb_gc_register(mrb_state *mrb, mrb_value obj); /* mrb_gc_unregister() removes the object from GC root. */ MRB_API void mrb_gc_unregister(mrb_state *mrb, mrb_value obj); MRB_API mrb_value mrb_to_int(mrb_state *mrb, mrb_value val); #define mrb_int(mrb, val) mrb_fixnum(mrb_to_int(mrb, val)) MRB_API mrb_value mrb_to_str(mrb_state *mrb, mrb_value val); MRB_API void mrb_check_type(mrb_state *mrb, mrb_value x, enum mrb_vtype t); typedef enum call_type { CALL_PUBLIC, CALL_FCALL, CALL_VCALL, CALL_TYPE_MAX } call_type; MRB_API void mrb_define_alias(mrb_state *mrb, struct RClass *c, const char *a, const char *b); MRB_API const char *mrb_class_name(mrb_state *mrb, struct RClass* klass); MRB_API void mrb_define_global_const(mrb_state *mrb, const char *name, mrb_value val); MRB_API mrb_value mrb_attr_get(mrb_state *mrb, mrb_value obj, mrb_sym id); MRB_API mrb_bool mrb_respond_to(mrb_state *mrb, mrb_value obj, mrb_sym mid); MRB_API mrb_bool mrb_obj_is_instance_of(mrb_state *mrb, mrb_value obj, struct RClass* c); MRB_API mrb_bool mrb_func_basic_p(mrb_state *mrb, mrb_value obj, mrb_sym mid, mrb_func_t func); /* * Resume a Fiber * * @mrbgem mruby-fiber */ MRB_API mrb_value mrb_fiber_resume(mrb_state *mrb, mrb_value fib, mrb_int argc, const mrb_value *argv); /* * Yield a Fiber * * @mrbgem mruby-fiber */ MRB_API mrb_value mrb_fiber_yield(mrb_state *mrb, mrb_int argc, const mrb_value *argv); /* * Check if a Fiber is alive * * @mrbgem mruby-fiber */ MRB_API mrb_value mrb_fiber_alive_p(mrb_state *mrb, mrb_value fib); /* * FiberError reference * * @mrbgem mruby-fiber */ #define E_FIBER_ERROR (mrb_exc_get(mrb, "FiberError")) MRB_API void mrb_stack_extend(mrb_state*, mrb_int); /* memory pool implementation */ typedef struct mrb_pool mrb_pool; MRB_API struct mrb_pool* mrb_pool_open(mrb_state*); MRB_API void mrb_pool_close(struct mrb_pool*); MRB_API void* mrb_pool_alloc(struct mrb_pool*, size_t); MRB_API void* mrb_pool_realloc(struct mrb_pool*, void*, size_t oldlen, size_t newlen); MRB_API mrb_bool mrb_pool_can_realloc(struct mrb_pool*, void*, size_t); MRB_API void* mrb_alloca(mrb_state *mrb, size_t); MRB_API void mrb_state_atexit(mrb_state *mrb, mrb_atexit_func func); MRB_API void mrb_show_version(mrb_state *mrb); MRB_API void mrb_show_copyright(mrb_state *mrb); MRB_API mrb_value mrb_format(mrb_state *mrb, const char *format, ...); #if 0 /* memcpy and memset does not work with gdb reverse-next on my box */ /* use naive memcpy and memset instead */ #undef memcpy #undef memset static void* mrbmemcpy(void *dst, const void *src, size_t n) { char *d = (char*)dst; const char *s = (const char*)src; while (n--) *d++ = *s++; return d; } #define memcpy(a,b,c) mrbmemcpy(a,b,c) static void* mrbmemset(void *s, int c, size_t n) { char *t = (char*)s; while (n--) *t++ = c; return s; } #define memset(a,b,c) mrbmemset(a,b,c) #endif MRB_END_DECL #endif /* MRUBY_H */ mruby-2.0.0/include/mruby/000077500000000000000000000000001340361412400154265ustar00rootroot00000000000000mruby-2.0.0/include/mruby/array.h000066400000000000000000000155601340361412400167240ustar00rootroot00000000000000/* ** mruby/array.h - Array class ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_ARRAY_H #define MRUBY_ARRAY_H #include "common.h" /* * Array class */ MRB_BEGIN_DECL typedef struct mrb_shared_array { int refcnt; mrb_int len; mrb_value *ptr; } mrb_shared_array; #define MRB_ARY_EMBED_LEN_MAX ((mrb_int)(sizeof(void*)*3/sizeof(mrb_value))) struct RArray { MRB_OBJECT_HEADER; union { struct { mrb_int len; union { mrb_int capa; mrb_shared_array *shared; } aux; mrb_value *ptr; } heap; mrb_value embed[MRB_ARY_EMBED_LEN_MAX]; } as; }; #define mrb_ary_ptr(v) ((struct RArray*)(mrb_ptr(v))) #define mrb_ary_value(p) mrb_obj_value((void*)(p)) #define RARRAY(v) ((struct RArray*)(mrb_ptr(v))) #define MRB_ARY_EMBED_MASK 7 #define ARY_EMBED_P(a) ((a)->flags & MRB_ARY_EMBED_MASK) #define ARY_UNSET_EMBED_FLAG(a) ((a)->flags &= ~(MRB_ARY_EMBED_MASK)) #define ARY_EMBED_LEN(a) ((mrb_int)(((a)->flags & MRB_ARY_EMBED_MASK) - 1)) #define ARY_SET_EMBED_LEN(a,len) ((a)->flags = ((a)->flags&~MRB_ARY_EMBED_MASK) | ((uint32_t)(len) + 1)) #define ARY_EMBED_PTR(a) (&((a)->as.embed[0])) #define ARY_LEN(a) (ARY_EMBED_P(a)?ARY_EMBED_LEN(a):(a)->as.heap.len) #define ARY_PTR(a) (ARY_EMBED_P(a)?ARY_EMBED_PTR(a):(a)->as.heap.ptr) #define RARRAY_LEN(a) ARY_LEN(RARRAY(a)) #define RARRAY_PTR(a) ARY_PTR(RARRAY(a)) #define ARY_SET_LEN(a,n) do {\ if (ARY_EMBED_P(a)) {\ mrb_assert((n) <= MRB_ARY_EMBED_LEN_MAX); \ ARY_SET_EMBED_LEN(a,n);\ }\ else\ (a)->as.heap.len = (n);\ } while (0) #define ARY_CAPA(a) (ARY_EMBED_P(a)?MRB_ARY_EMBED_LEN_MAX:(a)->as.heap.aux.capa) #define MRB_ARY_SHARED 256 #define ARY_SHARED_P(a) ((a)->flags & MRB_ARY_SHARED) #define ARY_SET_SHARED_FLAG(a) ((a)->flags |= MRB_ARY_SHARED) #define ARY_UNSET_SHARED_FLAG(a) ((a)->flags &= ~MRB_ARY_SHARED) void mrb_ary_decref(mrb_state*, mrb_shared_array*); MRB_API void mrb_ary_modify(mrb_state*, struct RArray*); MRB_API mrb_value mrb_ary_new_capa(mrb_state*, mrb_int); /* * Initializes a new array. * * Equivalent to: * * Array.new * * @param mrb The mruby state reference. * @return The initialized array. */ MRB_API mrb_value mrb_ary_new(mrb_state *mrb); /* * Initializes a new array with initial values * * Equivalent to: * * Array[value1, value2, ...] * * @param mrb The mruby state reference. * @param size The numer of values. * @param vals The actual values. * @return The initialized array. */ MRB_API mrb_value mrb_ary_new_from_values(mrb_state *mrb, mrb_int size, const mrb_value *vals); /* * Initializes a new array with two initial values * * Equivalent to: * * Array[car, cdr] * * @param mrb The mruby state reference. * @param car The first value. * @param cdr The second value. * @return The initialized array. */ MRB_API mrb_value mrb_assoc_new(mrb_state *mrb, mrb_value car, mrb_value cdr); /* * Concatenate two arrays. The target array will be modified * * Equivalent to: * ary.concat(other) * * @param mrb The mruby state reference. * @param self The target array. * @param other The array that will be concatenated to self. */ MRB_API void mrb_ary_concat(mrb_state *mrb, mrb_value self, mrb_value other); /* * Create an array from the input. It tries calling to_a on the * value. If value does not respond to that, it creates a new * array with just this value. * * @param mrb The mruby state reference. * @param value The value to change into an array. * @return An array representation of value. */ MRB_API mrb_value mrb_ary_splat(mrb_state *mrb, mrb_value value); /* * Pushes value into array. * * Equivalent to: * * ary << value * * @param mrb The mruby state reference. * @param ary The array in which the value will be pushed * @param value The value to be pushed into array */ MRB_API void mrb_ary_push(mrb_state *mrb, mrb_value array, mrb_value value); /* * Pops the last element from the array. * * Equivalent to: * * ary.pop * * @param mrb The mruby state reference. * @param ary The array from which the value will be popped. * @return The popped value. */ MRB_API mrb_value mrb_ary_pop(mrb_state *mrb, mrb_value ary); /* * Returns a reference to an element of the array on the given index. * * Equivalent to: * * ary[n] * * @param mrb The mruby state reference. * @param ary The target array. * @param n The array index being referenced * @return The referenced value. */ MRB_API mrb_value mrb_ary_ref(mrb_state *mrb, mrb_value ary, mrb_int n); /* * Sets a value on an array at the given index * * Equivalent to: * * ary[n] = val * * @param mrb The mruby state reference. * @param ary The target array. * @param n The array index being referenced. * @param val The value being setted. */ MRB_API void mrb_ary_set(mrb_state *mrb, mrb_value ary, mrb_int n, mrb_value val); /* * Replace the array with another array * * Equivalent to: * * ary.replace(other) * * @param mrb The mruby state reference * @param self The target array. * @param other The array to replace it with. */ MRB_API void mrb_ary_replace(mrb_state *mrb, mrb_value self, mrb_value other); MRB_API mrb_value mrb_ensure_array_type(mrb_state *mrb, mrb_value self); MRB_API mrb_value mrb_check_array_type(mrb_state *mrb, mrb_value self); /* * Unshift an element into the array * * Equivalent to: * * ary.unshift(item) * * @param mrb The mruby state reference. * @param self The target array. * @param item The item to unshift. */ MRB_API mrb_value mrb_ary_unshift(mrb_state *mrb, mrb_value self, mrb_value item); /* * Get nth element in the array * * Equivalent to: * * ary[offset] * * @param ary The target array. * @param offset The element position (negative counts from the tail). */ MRB_API mrb_value mrb_ary_entry(mrb_value ary, mrb_int offset); /* * Shifts the first element from the array. * * Equivalent to: * * ary.shift * * @param mrb The mruby state reference. * @param self The array from which the value will be shifted. * @return The shifted value. */ MRB_API mrb_value mrb_ary_shift(mrb_state *mrb, mrb_value self); /* * Removes all elements from the array * * Equivalent to: * * ary.clear * * @param mrb The mruby state reference. * @param self The target array. * @return self */ MRB_API mrb_value mrb_ary_clear(mrb_state *mrb, mrb_value self); /* * Join the array elements together in a string * * Equivalent to: * * ary.join(sep="") * * @param mrb The mruby state reference. * @param ary The target array * @param sep The separater, can be NULL */ MRB_API mrb_value mrb_ary_join(mrb_state *mrb, mrb_value ary, mrb_value sep); /* * Update the capacity of the array * * @param mrb The mruby state reference. * @param ary The target array. * @param new_len The new capacity of the array */ MRB_API mrb_value mrb_ary_resize(mrb_state *mrb, mrb_value ary, mrb_int new_len); MRB_END_DECL #endif /* MRUBY_ARRAY_H */ mruby-2.0.0/include/mruby/boxing_nan.h000066400000000000000000000061711340361412400177260ustar00rootroot00000000000000/* ** mruby/boxing_nan.h - nan boxing mrb_value definition ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_BOXING_NAN_H #define MRUBY_BOXING_NAN_H #ifdef MRB_USE_FLOAT # error ---->> MRB_NAN_BOXING and MRB_USE_FLOAT conflict <<---- #endif #ifdef MRB_WITHOUT_FLOAT # error ---->> MRB_NAN_BOXING and MRB_WITHOUT_FLOAT conflict <<---- #endif #ifdef MRB_INT64 # error ---->> MRB_NAN_BOXING and MRB_INT64 conflict <<---- #endif #define MRB_FIXNUM_SHIFT 0 #define MRB_TT_HAS_BASIC MRB_TT_OBJECT #ifdef MRB_ENDIAN_BIG #define MRB_ENDIAN_LOHI(a,b) a b #else #define MRB_ENDIAN_LOHI(a,b) b a #endif /* value representation by nan-boxing: * float : FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF * object: 111111111111TTTT TTPPPPPPPPPPPPPP PPPPPPPPPPPPPPPP PPPPPPPPPPPPPPPP * int : 1111111111110001 0000000000000000 IIIIIIIIIIIIIIII IIIIIIIIIIIIIIII * sym : 1111111111110001 0100000000000000 SSSSSSSSSSSSSSSS SSSSSSSSSSSSSSSS * In order to get enough bit size to save TT, all pointers are shifted 2 bits * in the right direction. Also, TTTTTT is the mrb_vtype + 1; */ typedef struct mrb_value { union { mrb_float f; union { void *p; struct { MRB_ENDIAN_LOHI( uint32_t ttt; ,union { mrb_int i; mrb_sym sym; }; ) }; } value; }; } mrb_value; #define mrb_float_pool(mrb,f) mrb_float_value(mrb,f) #define mrb_tt(o) ((enum mrb_vtype)(((o).value.ttt & 0xfc000)>>14)-1) #define mrb_type(o) (enum mrb_vtype)((uint32_t)0xfff00000 < (o).value.ttt ? mrb_tt(o) : MRB_TT_FLOAT) #define mrb_ptr(o) ((void*)((((uintptr_t)0x3fffffffffff)&((uintptr_t)((o).value.p)))<<2)) #define mrb_float(o) (o).f #define mrb_cptr(o) mrb_ptr(o) #define mrb_fixnum(o) (o).value.i #define mrb_symbol(o) (o).value.sym #ifdef MRB_64BIT #define BOXNAN_SHIFT_LONG_POINTER(v) (((uintptr_t)(v)>>34)&0x3fff) #else #define BOXNAN_SHIFT_LONG_POINTER(v) 0 #endif #define BOXNAN_SET_VALUE(o, tt, attr, v) do {\ (o).attr = (v);\ (o).value.ttt = 0xfff00000 | (((tt)+1)<<14);\ } while (0) #define BOXNAN_SET_OBJ_VALUE(o, tt, v) do {\ (o).value.p = (void*)((uintptr_t)(v)>>2);\ (o).value.ttt = (0xfff00000|(((tt)+1)<<14)|BOXNAN_SHIFT_LONG_POINTER(v));\ } while (0) #define SET_FLOAT_VALUE(mrb,r,v) do { \ if (v != v) { \ (r).value.ttt = 0x7ff80000; \ (r).value.i = 0; \ } \ else { \ (r).f = v; \ }} while(0) #define SET_NIL_VALUE(r) BOXNAN_SET_VALUE(r, MRB_TT_FALSE, value.i, 0) #define SET_FALSE_VALUE(r) BOXNAN_SET_VALUE(r, MRB_TT_FALSE, value.i, 1) #define SET_TRUE_VALUE(r) BOXNAN_SET_VALUE(r, MRB_TT_TRUE, value.i, 1) #define SET_BOOL_VALUE(r,b) BOXNAN_SET_VALUE(r, b ? MRB_TT_TRUE : MRB_TT_FALSE, value.i, 1) #define SET_INT_VALUE(r,n) BOXNAN_SET_VALUE(r, MRB_TT_FIXNUM, value.i, (n)) #define SET_SYM_VALUE(r,v) BOXNAN_SET_VALUE(r, MRB_TT_SYMBOL, value.sym, (v)) #define SET_OBJ_VALUE(r,v) BOXNAN_SET_OBJ_VALUE(r, (((struct RObject*)(v))->tt), (v)) #define SET_CPTR_VALUE(mrb,r,v) BOXNAN_SET_OBJ_VALUE(r, MRB_TT_CPTR, v) #define SET_UNDEF_VALUE(r) BOXNAN_SET_VALUE(r, MRB_TT_UNDEF, value.i, 0) #endif /* MRUBY_BOXING_NAN_H */ mruby-2.0.0/include/mruby/boxing_no.h000066400000000000000000000032101340361412400175550ustar00rootroot00000000000000/* ** mruby/boxing_no.h - unboxed mrb_value definition ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_BOXING_NO_H #define MRUBY_BOXING_NO_H #define MRB_FIXNUM_SHIFT 0 #define MRB_TT_HAS_BASIC MRB_TT_OBJECT typedef struct mrb_value { union { #ifndef MRB_WITHOUT_FLOAT mrb_float f; #endif void *p; mrb_int i; mrb_sym sym; } value; enum mrb_vtype tt; } mrb_value; #ifndef MRB_WITHOUT_FLOAT #define mrb_float_pool(mrb,f) mrb_float_value(mrb,f) #endif #define mrb_ptr(o) (o).value.p #define mrb_cptr(o) mrb_ptr(o) #ifndef MRB_WITHOUT_FLOAT #define mrb_float(o) (o).value.f #endif #define mrb_fixnum(o) (o).value.i #define mrb_symbol(o) (o).value.sym #define mrb_type(o) (o).tt #define BOXNIX_SET_VALUE(o, ttt, attr, v) do {\ (o).tt = ttt;\ (o).attr = v;\ } while (0) #define SET_NIL_VALUE(r) BOXNIX_SET_VALUE(r, MRB_TT_FALSE, value.i, 0) #define SET_FALSE_VALUE(r) BOXNIX_SET_VALUE(r, MRB_TT_FALSE, value.i, 1) #define SET_TRUE_VALUE(r) BOXNIX_SET_VALUE(r, MRB_TT_TRUE, value.i, 1) #define SET_BOOL_VALUE(r,b) BOXNIX_SET_VALUE(r, b ? MRB_TT_TRUE : MRB_TT_FALSE, value.i, 1) #define SET_INT_VALUE(r,n) BOXNIX_SET_VALUE(r, MRB_TT_FIXNUM, value.i, (n)) #ifndef MRB_WITHOUT_FLOAT #define SET_FLOAT_VALUE(mrb,r,v) BOXNIX_SET_VALUE(r, MRB_TT_FLOAT, value.f, (v)) #endif #define SET_SYM_VALUE(r,v) BOXNIX_SET_VALUE(r, MRB_TT_SYMBOL, value.sym, (v)) #define SET_OBJ_VALUE(r,v) BOXNIX_SET_VALUE(r, (((struct RObject*)(v))->tt), value.p, (v)) #define SET_CPTR_VALUE(mrb,r,v) BOXNIX_SET_VALUE(r, MRB_TT_CPTR, value.p, v) #define SET_UNDEF_VALUE(r) BOXNIX_SET_VALUE(r, MRB_TT_UNDEF, value.i, 0) #endif /* MRUBY_BOXING_NO_H */ mruby-2.0.0/include/mruby/boxing_word.h000066400000000000000000000100271340361412400201200ustar00rootroot00000000000000/* ** mruby/boxing_word.h - word boxing mrb_value definition ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_BOXING_WORD_H #define MRUBY_BOXING_WORD_H #if defined(MRB_INT16) # error MRB_INT16 is too small for MRB_WORD_BOXING. #endif #if defined(MRB_INT64) && !defined(MRB_64BIT) #error MRB_INT64 cannot be used with MRB_WORD_BOXING in 32-bit mode. #endif #ifndef MRB_WITHOUT_FLOAT struct RFloat { MRB_OBJECT_HEADER; mrb_float f; }; #endif struct RCptr { MRB_OBJECT_HEADER; void *p; }; #define MRB_FIXNUM_SHIFT 1 #ifdef MRB_WITHOUT_FLOAT #define MRB_TT_HAS_BASIC MRB_TT_CPTR #else #define MRB_TT_HAS_BASIC MRB_TT_FLOAT #endif enum mrb_special_consts { MRB_Qnil = 0, MRB_Qfalse = 2, MRB_Qtrue = 4, MRB_Qundef = 6, }; #define MRB_FIXNUM_FLAG 0x01 #define MRB_SYMBOL_FLAG 0x0e #define MRB_SPECIAL_SHIFT 8 #if defined(MRB_64BIT) #define MRB_SYMBOL_BITSIZE (sizeof(mrb_sym) * CHAR_BIT) #define MRB_SYMBOL_MAX UINT32_MAX #else #define MRB_SYMBOL_BITSIZE (sizeof(mrb_sym) * CHAR_BIT - MRB_SPECIAL_SHIFT) #define MRB_SYMBOL_MAX (UINT32_MAX >> MRB_SPECIAL_SHIFT) #endif typedef union mrb_value { union { void *p; struct { unsigned int i_flag : MRB_FIXNUM_SHIFT; mrb_int i : (MRB_INT_BIT - MRB_FIXNUM_SHIFT); }; struct { unsigned int sym_flag : MRB_SPECIAL_SHIFT; mrb_sym sym : MRB_SYMBOL_BITSIZE; }; struct RBasic *bp; #ifndef MRB_WITHOUT_FLOAT struct RFloat *fp; #endif struct RCptr *vp; } value; unsigned long w; } mrb_value; MRB_API mrb_value mrb_word_boxing_cptr_value(struct mrb_state*, void*); #ifndef MRB_WITHOUT_FLOAT MRB_API mrb_value mrb_word_boxing_float_value(struct mrb_state*, mrb_float); MRB_API mrb_value mrb_word_boxing_float_pool(struct mrb_state*, mrb_float); #endif #ifndef MRB_WITHOUT_FLOAT #define mrb_float_pool(mrb,f) mrb_word_boxing_float_pool(mrb,f) #endif #define mrb_ptr(o) (o).value.p #define mrb_cptr(o) (o).value.vp->p #ifndef MRB_WITHOUT_FLOAT #define mrb_float(o) (o).value.fp->f #endif #define mrb_fixnum(o) ((mrb_int)(o).value.i) #define mrb_symbol(o) (o).value.sym static inline enum mrb_vtype mrb_type(mrb_value o) { switch (o.w) { case MRB_Qfalse: case MRB_Qnil: return MRB_TT_FALSE; case MRB_Qtrue: return MRB_TT_TRUE; case MRB_Qundef: return MRB_TT_UNDEF; } if (o.value.i_flag == MRB_FIXNUM_FLAG) { return MRB_TT_FIXNUM; } if (o.value.sym_flag == MRB_SYMBOL_FLAG) { return MRB_TT_SYMBOL; } return o.value.bp->tt; } #define mrb_bool(o) ((o).w != MRB_Qnil && (o).w != MRB_Qfalse) #define mrb_fixnum_p(o) ((o).value.i_flag == MRB_FIXNUM_FLAG) #define mrb_undef_p(o) ((o).w == MRB_Qundef) #define mrb_nil_p(o) ((o).w == MRB_Qnil) #define BOXWORD_SET_VALUE(o, ttt, attr, v) do { \ switch (ttt) {\ case MRB_TT_FALSE: (o).w = (v) ? MRB_Qfalse : MRB_Qnil; break;\ case MRB_TT_TRUE: (o).w = MRB_Qtrue; break;\ case MRB_TT_UNDEF: (o).w = MRB_Qundef; break;\ case MRB_TT_FIXNUM: (o).w = 0;(o).value.i_flag = MRB_FIXNUM_FLAG; (o).attr = (v); break;\ case MRB_TT_SYMBOL: (o).w = 0;(o).value.sym_flag = MRB_SYMBOL_FLAG; (o).attr = (v); break;\ default: (o).w = 0; (o).attr = (v); if ((o).value.bp) (o).value.bp->tt = ttt; break;\ }\ } while (0) #ifndef MRB_WITHOUT_FLOAT #define SET_FLOAT_VALUE(mrb,r,v) r = mrb_word_boxing_float_value(mrb, v) #endif #define SET_CPTR_VALUE(mrb,r,v) r = mrb_word_boxing_cptr_value(mrb, v) #define SET_NIL_VALUE(r) BOXWORD_SET_VALUE(r, MRB_TT_FALSE, value.i, 0) #define SET_FALSE_VALUE(r) BOXWORD_SET_VALUE(r, MRB_TT_FALSE, value.i, 1) #define SET_TRUE_VALUE(r) BOXWORD_SET_VALUE(r, MRB_TT_TRUE, value.i, 1) #define SET_BOOL_VALUE(r,b) BOXWORD_SET_VALUE(r, b ? MRB_TT_TRUE : MRB_TT_FALSE, value.i, 1) #define SET_INT_VALUE(r,n) BOXWORD_SET_VALUE(r, MRB_TT_FIXNUM, value.i, (n)) #define SET_SYM_VALUE(r,v) BOXWORD_SET_VALUE(r, MRB_TT_SYMBOL, value.sym, (v)) #define SET_OBJ_VALUE(r,v) BOXWORD_SET_VALUE(r, (((struct RObject*)(v))->tt), value.p, (v)) #define SET_UNDEF_VALUE(r) BOXWORD_SET_VALUE(r, MRB_TT_UNDEF, value.i, 0) #endif /* MRUBY_BOXING_WORD_H */ mruby-2.0.0/include/mruby/class.h000066400000000000000000000052671340361412400167160ustar00rootroot00000000000000/* ** mruby/class.h - Class class ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_CLASS_H #define MRUBY_CLASS_H #include "common.h" /** * Class class */ MRB_BEGIN_DECL struct RClass { MRB_OBJECT_HEADER; struct iv_tbl *iv; struct kh_mt *mt; struct RClass *super; }; #define mrb_class_ptr(v) ((struct RClass*)(mrb_ptr(v))) static inline struct RClass* mrb_class(mrb_state *mrb, mrb_value v) { switch (mrb_type(v)) { case MRB_TT_FALSE: if (mrb_fixnum(v)) return mrb->false_class; return mrb->nil_class; case MRB_TT_TRUE: return mrb->true_class; case MRB_TT_SYMBOL: return mrb->symbol_class; case MRB_TT_FIXNUM: return mrb->fixnum_class; #ifndef MRB_WITHOUT_FLOAT case MRB_TT_FLOAT: return mrb->float_class; #endif case MRB_TT_CPTR: return mrb->object_class; case MRB_TT_ENV: return NULL; default: return mrb_obj_ptr(v)->c; } } /* flags: 20: frozen 19: is_prepended 18: is_origin 17: is_inherited (used by method cache) 16: unused 0-15: instance type */ #define MRB_FL_CLASS_IS_PREPENDED (1 << 19) #define MRB_FL_CLASS_IS_ORIGIN (1 << 18) #define MRB_CLASS_ORIGIN(c) do {\ if (c->flags & MRB_FL_CLASS_IS_PREPENDED) {\ c = c->super;\ while (!(c->flags & MRB_FL_CLASS_IS_ORIGIN)) {\ c = c->super;\ }\ }\ } while (0) #define MRB_FL_CLASS_IS_INHERITED (1 << 17) #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) 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); MRB_API struct RClass *mrb_vm_define_class(mrb_state*, mrb_value, mrb_value, mrb_sym); MRB_API struct RClass *mrb_vm_define_module(mrb_state*, mrb_value, mrb_sym); MRB_API void mrb_define_method_raw(mrb_state*, struct RClass*, mrb_sym, mrb_method_t); MRB_API void mrb_define_method_id(mrb_state *mrb, struct RClass *c, mrb_sym mid, mrb_func_t func, mrb_aspec aspec); MRB_API void mrb_alias_method(mrb_state*, struct RClass *c, mrb_sym a, mrb_sym b); MRB_API mrb_method_t mrb_method_search_vm(mrb_state*, struct RClass**, mrb_sym); MRB_API mrb_method_t mrb_method_search(mrb_state*, struct RClass*, mrb_sym); MRB_API struct RClass* mrb_class_real(struct RClass* cl); void mrb_class_name_class(mrb_state*, struct RClass*, struct RClass*, mrb_sym); mrb_value mrb_class_find_path(mrb_state*, struct RClass*); void mrb_gc_mark_mt(mrb_state*, struct RClass*); size_t mrb_gc_mark_mt_size(mrb_state*, struct RClass*); void mrb_gc_free_mt(mrb_state*, struct RClass*); MRB_END_DECL #endif /* MRUBY_CLASS_H */ mruby-2.0.0/include/mruby/common.h000066400000000000000000000030561340361412400170730ustar00rootroot00000000000000/* **"common.h - mruby common platform definition" ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_COMMON_H #define MRUBY_COMMON_H #ifdef __APPLE__ #ifndef __TARGETCONDITIONALS__ #include "TargetConditionals.h" #endif #endif #ifdef __cplusplus #ifdef MRB_ENABLE_CXX_ABI #define MRB_BEGIN_DECL #define MRB_END_DECL #else # define MRB_BEGIN_DECL extern "C" { # define MRB_END_DECL } #endif #else /** Start declarations in C mode */ # define MRB_BEGIN_DECL /** End declarations in C mode */ # define MRB_END_DECL #endif /** * Shared compiler macros */ MRB_BEGIN_DECL /** Declare a function that never returns. */ #if __STDC_VERSION__ >= 201112L # define mrb_noreturn _Noreturn #elif defined __GNUC__ && !defined __STRICT_ANSI__ # define mrb_noreturn __attribute__((noreturn)) #elif defined _MSC_VER # define mrb_noreturn __declspec(noreturn) #else # define mrb_noreturn #endif /** Mark a function as deprecated. */ #if defined __GNUC__ && !defined __STRICT_ANSI__ # define mrb_deprecated __attribute__((deprecated)) #elif defined _MSC_VER # define mrb_deprecated __declspec(deprecated) #else # define mrb_deprecated #endif /** Declare a function as always inlined. */ #if defined(_MSC_VER) # define MRB_INLINE static __inline #else # define MRB_INLINE static inline #endif /** Declare a public MRuby API function. */ #if defined(MRB_BUILD_AS_DLL) #if defined(MRB_CORE) || defined(MRB_LIB) # define MRB_API __declspec(dllexport) #else # define MRB_API __declspec(dllimport) #endif #else # define MRB_API extern #endif MRB_END_DECL #endif /* MRUBY_COMMON_H */ mruby-2.0.0/include/mruby/compile.h000066400000000000000000000134131340361412400172310ustar00rootroot00000000000000/* ** mruby/compile.h - mruby parser ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_COMPILE_H #define MRUBY_COMPILE_H #include "common.h" /** * MRuby Compiler */ MRB_BEGIN_DECL #include struct mrb_jmpbuf; struct mrb_parser_state; /* load context */ typedef struct mrbc_context { mrb_sym *syms; int slen; char *filename; short lineno; int (*partial_hook)(struct mrb_parser_state*); void *partial_data; struct RClass *target_class; mrb_bool capture_errors:1; mrb_bool dump_result:1; mrb_bool no_exec:1; mrb_bool keep_lv:1; mrb_bool no_optimize:1; mrb_bool on_eval:1; size_t parser_nerr; } mrbc_context; MRB_API mrbc_context* mrbc_context_new(mrb_state *mrb); MRB_API void mrbc_context_free(mrb_state *mrb, mrbc_context *cxt); MRB_API const char *mrbc_filename(mrb_state *mrb, mrbc_context *c, const char *s); MRB_API void mrbc_partial_hook(mrb_state *mrb, mrbc_context *c, int (*partial_hook)(struct mrb_parser_state*), void*data); /* AST node structure */ typedef struct mrb_ast_node { struct mrb_ast_node *car, *cdr; uint16_t lineno, filename_index; } mrb_ast_node; /* lexer states */ enum mrb_lex_state_enum { EXPR_BEG, /* ignore newline, +/- is a sign. */ EXPR_END, /* newline significant, +/- is an operator. */ EXPR_ENDARG, /* ditto, and unbound braces. */ EXPR_ENDFN, /* ditto, and unbound braces. */ EXPR_ARG, /* newline significant, +/- is an operator. */ 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_VALUE, /* alike EXPR_BEG but label is disallowed. */ EXPR_MAX_STATE }; /* saved error message */ struct mrb_parser_message { int lineno; int column; char* message; }; #define STR_FUNC_PARSING 0x01 #define STR_FUNC_EXPAND 0x02 #define STR_FUNC_REGEXP 0x04 #define STR_FUNC_WORD 0x08 #define STR_FUNC_SYMBOL 0x10 #define STR_FUNC_ARRAY 0x20 #define STR_FUNC_HEREDOC 0x40 #define STR_FUNC_XQUOTE 0x80 enum mrb_string_type { str_not_parsing = (0), str_squote = (STR_FUNC_PARSING), str_dquote = (STR_FUNC_PARSING|STR_FUNC_EXPAND), str_regexp = (STR_FUNC_PARSING|STR_FUNC_REGEXP|STR_FUNC_EXPAND), str_sword = (STR_FUNC_PARSING|STR_FUNC_WORD|STR_FUNC_ARRAY), str_dword = (STR_FUNC_PARSING|STR_FUNC_WORD|STR_FUNC_ARRAY|STR_FUNC_EXPAND), str_ssym = (STR_FUNC_PARSING|STR_FUNC_SYMBOL), str_ssymbols = (STR_FUNC_PARSING|STR_FUNC_SYMBOL|STR_FUNC_ARRAY), str_dsymbols = (STR_FUNC_PARSING|STR_FUNC_SYMBOL|STR_FUNC_ARRAY|STR_FUNC_EXPAND), str_heredoc = (STR_FUNC_PARSING|STR_FUNC_HEREDOC), str_xquote = (STR_FUNC_PARSING|STR_FUNC_XQUOTE|STR_FUNC_EXPAND), }; /* heredoc structure */ struct mrb_parser_heredoc_info { mrb_bool allow_indent:1; mrb_bool line_head:1; enum mrb_string_type type; const char *term; int term_len; mrb_ast_node *doc; }; #define MRB_PARSER_TOKBUF_MAX 65536 #define MRB_PARSER_TOKBUF_SIZE 256 /* parser structure */ struct mrb_parser_state { mrb_state *mrb; struct mrb_pool *pool; mrb_ast_node *cells; const char *s, *send; #ifndef MRB_DISABLE_STDIO FILE *f; #endif mrbc_context *cxt; char const *filename; int lineno; int column; enum mrb_lex_state_enum lstate; mrb_ast_node *lex_strterm; /* (type nest_level beg . end) */ unsigned int cond_stack; unsigned int cmdarg_stack; int paren_nest; int lpar_beg; int in_def, in_single; mrb_bool cmd_start:1; mrb_ast_node *locals; mrb_ast_node *pb; char *tokbuf; char buf[MRB_PARSER_TOKBUF_SIZE]; int tidx; int tsiz; mrb_ast_node *all_heredocs; /* list of mrb_parser_heredoc_info* */ mrb_ast_node *heredocs_from_nextline; mrb_ast_node *parsing_heredoc; mrb_ast_node *lex_strterm_before_heredoc; void *ylval; size_t nerr; size_t nwarn; mrb_ast_node *tree; mrb_bool no_optimize:1; mrb_bool on_eval:1; mrb_bool capture_errors:1; struct mrb_parser_message error_buffer[10]; struct mrb_parser_message warn_buffer[10]; mrb_sym* filename_table; uint16_t filename_table_length; uint16_t current_filename_index; struct mrb_jmpbuf* jmp; }; MRB_API struct mrb_parser_state* mrb_parser_new(mrb_state*); MRB_API void mrb_parser_free(struct mrb_parser_state*); MRB_API void mrb_parser_parse(struct mrb_parser_state*,mrbc_context*); MRB_API void mrb_parser_set_filename(struct mrb_parser_state*, char const*); MRB_API char const* mrb_parser_get_filename(struct mrb_parser_state*, uint16_t idx); /* utility functions */ #ifndef MRB_DISABLE_STDIO MRB_API struct mrb_parser_state* mrb_parse_file(mrb_state*,FILE*,mrbc_context*); #endif MRB_API struct mrb_parser_state* mrb_parse_string(mrb_state*,const char*,mrbc_context*); MRB_API struct mrb_parser_state* mrb_parse_nstring(mrb_state*,const char*,size_t,mrbc_context*); MRB_API struct RProc* mrb_generate_code(mrb_state*, struct mrb_parser_state*); MRB_API mrb_value mrb_load_exec(mrb_state *mrb, struct mrb_parser_state *p, mrbc_context *c); /* program load functions */ #ifndef MRB_DISABLE_STDIO MRB_API mrb_value mrb_load_file(mrb_state*,FILE*); MRB_API mrb_value mrb_load_file_cxt(mrb_state*,FILE*, mrbc_context *cxt); #endif MRB_API mrb_value mrb_load_string(mrb_state *mrb, const char *s); MRB_API mrb_value mrb_load_nstring(mrb_state *mrb, const char *s, size_t len); MRB_API mrb_value mrb_load_string_cxt(mrb_state *mrb, const char *s, mrbc_context *cxt); MRB_API mrb_value mrb_load_nstring_cxt(mrb_state *mrb, const char *s, size_t len, mrbc_context *cxt); /** @} */ MRB_END_DECL #endif /* MRUBY_COMPILE_H */ mruby-2.0.0/include/mruby/data.h000066400000000000000000000042211340361412400165070ustar00rootroot00000000000000/* ** mruby/data.h - Data class ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_DATA_H #define MRUBY_DATA_H #include "common.h" /** * Custom C wrapped data. * * Defining Ruby wrappers around native objects. */ MRB_BEGIN_DECL /** * Custom data type description. */ typedef struct mrb_data_type { /** data type name */ const char *struct_name; /** data type release function pointer */ void (*dfree)(mrb_state *mrb, void*); } mrb_data_type; struct RData { MRB_OBJECT_HEADER; struct iv_tbl *iv; const mrb_data_type *type; void *data; }; MRB_API struct RData *mrb_data_object_alloc(mrb_state *mrb, struct RClass* klass, void *datap, const mrb_data_type *type); #define Data_Wrap_Struct(mrb,klass,type,ptr)\ mrb_data_object_alloc(mrb,klass,ptr,type) #define Data_Make_Struct(mrb,klass,strct,type,sval,data) do { \ sval = mrb_malloc(mrb, sizeof(strct)); \ { static const strct zero = { 0 }; *sval = zero; };\ data = Data_Wrap_Struct(mrb,klass,type,sval);\ } while (0) #define RDATA(obj) ((struct RData *)(mrb_ptr(obj))) #define DATA_PTR(d) (RDATA(d)->data) #define DATA_TYPE(d) (RDATA(d)->type) MRB_API void mrb_data_check_type(mrb_state *mrb, mrb_value, const mrb_data_type*); MRB_API void *mrb_data_get_ptr(mrb_state *mrb, mrb_value, const mrb_data_type*); #define DATA_GET_PTR(mrb,obj,dtype,type) (type*)mrb_data_get_ptr(mrb,obj,dtype) MRB_API void *mrb_data_check_get_ptr(mrb_state *mrb, mrb_value, const mrb_data_type*); #define DATA_CHECK_GET_PTR(mrb,obj,dtype,type) (type*)mrb_data_check_get_ptr(mrb,obj,dtype) /* obsolete functions and macros */ #define mrb_data_check_and_get(mrb,obj,dtype) mrb_data_get_ptr(mrb,obj,dtype) #define mrb_get_datatype(mrb,val,type) mrb_data_get_ptr(mrb, val, type) #define mrb_check_datatype(mrb,val,type) mrb_data_get_ptr(mrb, val, type) #define Data_Get_Struct(mrb,obj,type,sval) do {\ *(void**)&sval = mrb_data_get_ptr(mrb, obj, type); \ } while (0) static inline void mrb_data_init(mrb_value v, void *ptr, const mrb_data_type *type) { mrb_assert(mrb_type(v) == MRB_TT_DATA); DATA_PTR(v) = ptr; DATA_TYPE(v) = type; } MRB_END_DECL #endif /* MRUBY_DATA_H */ mruby-2.0.0/include/mruby/debug.h000066400000000000000000000030551340361412400166700ustar00rootroot00000000000000/* ** mruby/debug.h - mruby debug info ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_DEBUG_H #define MRUBY_DEBUG_H #include "common.h" /** * MRuby Debugging. */ MRB_BEGIN_DECL typedef enum mrb_debug_line_type { mrb_debug_line_ary = 0, mrb_debug_line_flat_map = 1 } mrb_debug_line_type; typedef struct mrb_irep_debug_info_line { uint32_t start_pos; uint16_t line; } mrb_irep_debug_info_line; typedef struct mrb_irep_debug_info_file { uint32_t start_pos; const char *filename; mrb_sym filename_sym; uint32_t line_entry_count; mrb_debug_line_type line_type; union { void *ptr; mrb_irep_debug_info_line *flat_map; uint16_t *ary; } lines; } mrb_irep_debug_info_file; typedef struct mrb_irep_debug_info { uint32_t pc_count; uint16_t flen; mrb_irep_debug_info_file **files; } mrb_irep_debug_info; /* * get line from irep's debug info and program counter * @return returns NULL if not found */ MRB_API const char *mrb_debug_get_filename(mrb_irep *irep, ptrdiff_t pc); /* * get line from irep's debug info and program counter * @return returns -1 if not found */ MRB_API int32_t mrb_debug_get_line(mrb_irep *irep, ptrdiff_t pc); MRB_API mrb_irep_debug_info *mrb_debug_info_alloc(mrb_state *mrb, mrb_irep *irep); MRB_API mrb_irep_debug_info_file *mrb_debug_info_append_file( mrb_state *mrb, mrb_irep_debug_info *info, const char *filename, uint16_t *lines, uint32_t start_pos, uint32_t end_pos); MRB_API void mrb_debug_info_free(mrb_state *mrb, mrb_irep_debug_info *d); MRB_END_DECL #endif /* MRUBY_DEBUG_H */ mruby-2.0.0/include/mruby/dump.h000066400000000000000000000106551340361412400165530ustar00rootroot00000000000000/* ** mruby/dump.h - mruby binary dumper (mrbc binary format) ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_DUMP_H #define MRUBY_DUMP_H #include #include #include "common.h" /** * Dumping compiled mruby script. */ MRB_BEGIN_DECL #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 int mrb_dump_irep(mrb_state *mrb, mrb_irep *irep, uint8_t flags, uint8_t **bin, size_t *bin_size); #ifndef MRB_DISABLE_STDIO int mrb_dump_irep_binary(mrb_state*, mrb_irep*, uint8_t, FILE*); int mrb_dump_irep_cfunc(mrb_state *mrb, mrb_irep*, uint8_t flags, FILE *f, const char *initname); mrb_irep *mrb_read_irep_file(mrb_state*, FILE*); MRB_API mrb_value mrb_load_irep_file(mrb_state*,FILE*); MRB_API mrb_value mrb_load_irep_file_cxt(mrb_state*, FILE*, mrbc_context*); #endif MRB_API mrb_irep *mrb_read_irep(mrb_state*, const uint8_t*); /* dump/load error code * * NOTE: MRB_DUMP_GENERAL_FAILURE is caused by * unspecified issues like malloc failed. */ #define MRB_DUMP_OK 0 #define MRB_DUMP_GENERAL_FAILURE (-1) #define MRB_DUMP_WRITE_FAULT (-2) #define MRB_DUMP_READ_FAULT (-3) #define MRB_DUMP_CRC_ERROR (-4) #define MRB_DUMP_INVALID_FILE_HEADER (-5) #define MRB_DUMP_INVALID_IREP (-6) #define MRB_DUMP_INVALID_ARGUMENT (-7) /* null symbol length */ #define MRB_DUMP_NULL_SYM_LEN 0xFFFF /* Rite Binary File header */ #define RITE_BINARY_IDENT "RITE" #define RITE_BINARY_IDENT_LIL "ETIR" #define RITE_BINARY_FORMAT_VER "0005" #define RITE_COMPILER_NAME "MATZ" #define RITE_COMPILER_VERSION "0000" #define RITE_VM_VER "0002" #define RITE_BINARY_EOF "END\0" #define RITE_SECTION_IREP_IDENT "IREP" #define RITE_SECTION_LINENO_IDENT "LINE" #define RITE_SECTION_DEBUG_IDENT "DBG\0" #define RITE_SECTION_LV_IDENT "LVAR" #define MRB_DUMP_DEFAULT_STR_LEN 128 #define MRB_DUMP_ALIGNMENT sizeof(uint32_t) /* binary header */ struct rite_binary_header { uint8_t binary_ident[4]; /* Binary Identifier */ uint8_t binary_version[4]; /* Binary Format Version */ uint8_t binary_crc[2]; /* Binary CRC */ uint8_t binary_size[4]; /* Binary Size */ uint8_t compiler_name[4]; /* Compiler name */ uint8_t compiler_version[4]; }; /* section header */ #define RITE_SECTION_HEADER \ uint8_t section_ident[4]; \ uint8_t section_size[4] struct rite_section_header { RITE_SECTION_HEADER; }; struct rite_section_irep_header { RITE_SECTION_HEADER; uint8_t rite_version[4]; /* Rite Instruction Specification Version */ }; struct rite_section_lineno_header { RITE_SECTION_HEADER; }; struct rite_section_debug_header { RITE_SECTION_HEADER; }; struct rite_section_lv_header { RITE_SECTION_HEADER; }; #define RITE_LV_NULL_MARK UINT16_MAX struct rite_binary_footer { RITE_SECTION_HEADER; }; static inline int bigendian_p() { int i; char *p; i = 1; p = (char*)&i; return p[0]?0:1; } static inline size_t uint8_to_bin(uint8_t s, uint8_t *bin) { *bin = s; return sizeof(uint8_t); } static inline size_t uint16_to_bin(uint16_t s, uint8_t *bin) { *bin++ = (s >> 8) & 0xff; *bin = s & 0xff; return sizeof(uint16_t); } static inline size_t uint32_to_bin(uint32_t l, uint8_t *bin) { *bin++ = (l >> 24) & 0xff; *bin++ = (l >> 16) & 0xff; *bin++ = (l >> 8) & 0xff; *bin = l & 0xff; return sizeof(uint32_t); } static inline size_t uint32l_to_bin(uint32_t l, uint8_t *bin) { bin[3] = (l >> 24) & 0xff; bin[2] = (l >> 16) & 0xff; bin[1] = (l >> 8) & 0xff; bin[0] = l & 0xff; return sizeof(uint32_t); } static inline uint32_t bin_to_uint32(const uint8_t *bin) { return (uint32_t)bin[0] << 24 | (uint32_t)bin[1] << 16 | (uint32_t)bin[2] << 8 | (uint32_t)bin[3]; } static inline uint32_t bin_to_uint32l(const uint8_t *bin) { return (uint32_t)bin[3] << 24 | (uint32_t)bin[2] << 16 | (uint32_t)bin[1] << 8 | (uint32_t)bin[0]; } static inline uint16_t bin_to_uint16(const uint8_t *bin) { return (uint16_t)bin[0] << 8 | (uint16_t)bin[1]; } static inline uint8_t bin_to_uint8(const uint8_t *bin) { return (uint8_t)bin[0]; } MRB_END_DECL /** @internal crc.c */ uint16_t calc_crc_16_ccitt(const uint8_t *src, size_t nbytes, uint16_t crc); #endif /* MRUBY_DUMP_H */ mruby-2.0.0/include/mruby/error.h000066400000000000000000000036011340361412400167300ustar00rootroot00000000000000/* ** mruby/error.h - Exception class ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_ERROR_H #define MRUBY_ERROR_H #include "common.h" /** * MRuby error handling. */ MRB_BEGIN_DECL struct RException { MRB_OBJECT_HEADER; struct iv_tbl *iv; }; #define mrb_exc_ptr(v) ((struct RException*)mrb_ptr(v)) MRB_API void mrb_sys_fail(mrb_state *mrb, const char *mesg); MRB_API mrb_value mrb_exc_new_str(mrb_state *mrb, struct RClass* c, mrb_value str); #define mrb_exc_new_str_lit(mrb, c, lit) mrb_exc_new_str(mrb, c, mrb_str_new_lit(mrb, lit)) MRB_API mrb_value mrb_make_exception(mrb_state *mrb, 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_value args, const char *fmt, ...); /* declaration for fail method */ MRB_API mrb_value mrb_f_raise(mrb_state*, mrb_value); struct RBreak { MRB_OBJECT_HEADER; struct RProc *proc; mrb_value val; }; /** * Protect * * @mrbgem mruby-error */ MRB_API mrb_value mrb_protect(mrb_state *mrb, mrb_func_t body, mrb_value data, mrb_bool *state); /** * Ensure * * @mrbgem mruby-error */ MRB_API mrb_value mrb_ensure(mrb_state *mrb, mrb_func_t body, mrb_value b_data, mrb_func_t ensure, mrb_value e_data); /** * Rescue * * @mrbgem mruby-error */ MRB_API mrb_value mrb_rescue(mrb_state *mrb, mrb_func_t body, mrb_value b_data, mrb_func_t rescue, mrb_value r_data); /** * Rescue exception * * @mrbgem mruby-error */ MRB_API mrb_value mrb_rescue_exceptions(mrb_state *mrb, mrb_func_t body, mrb_value b_data, mrb_func_t rescue, mrb_value r_data, mrb_int len, struct RClass **classes); MRB_END_DECL #endif /* MRUBY_ERROR_H */ mruby-2.0.0/include/mruby/gc.h000066400000000000000000000043351340361412400161750ustar00rootroot00000000000000/* ** mruby/gc.h - garbage collector for mruby ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_GC_H #define MRUBY_GC_H #include "common.h" /** * Uncommon memory management stuffs. */ MRB_BEGIN_DECL struct mrb_state; #define MRB_EACH_OBJ_OK 0 #define MRB_EACH_OBJ_BREAK 1 typedef int (mrb_each_object_callback)(struct mrb_state *mrb, struct RBasic *obj, void *data); void mrb_objspace_each_objects(struct mrb_state *mrb, mrb_each_object_callback *callback, void *data); MRB_API void mrb_free_context(struct mrb_state *mrb, struct mrb_context *c); #ifndef MRB_GC_ARENA_SIZE #define MRB_GC_ARENA_SIZE 100 #endif typedef enum { MRB_GC_STATE_ROOT = 0, MRB_GC_STATE_MARK, MRB_GC_STATE_SWEEP } mrb_gc_state; /* Disable MSVC warning "C4200: nonstandard extension used: zero-sized array * in struct/union" when in C++ mode */ #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4200) #endif typedef struct mrb_heap_page { struct RBasic *freelist; struct mrb_heap_page *prev; struct mrb_heap_page *next; struct mrb_heap_page *free_next; struct mrb_heap_page *free_prev; mrb_bool old:1; void *objects[]; } mrb_heap_page; #ifdef _MSC_VER #pragma warning(pop) #endif typedef struct mrb_gc { mrb_heap_page *heaps; /* heaps for GC */ mrb_heap_page *sweeps; mrb_heap_page *free_heaps; size_t live; /* count of live objects */ #ifdef MRB_GC_FIXED_ARENA struct RBasic *arena[MRB_GC_ARENA_SIZE]; /* GC protection array */ #else struct RBasic **arena; /* GC protection array */ int arena_capa; #endif int arena_idx; mrb_gc_state state; /* state of gc */ int current_white_part; /* make white object by white_part */ struct RBasic *gray_list; /* list of gray objects to be traversed incrementally */ struct RBasic *atomic_gray_list; /* list of objects to be traversed atomically */ size_t live_after_mark; size_t threshold; int interval_ratio; int step_ratio; mrb_bool iterating :1; mrb_bool disabled :1; mrb_bool full :1; mrb_bool generational :1; mrb_bool out_of_memory :1; size_t majorgc_old_threshold; } mrb_gc; MRB_API mrb_bool mrb_object_dead_p(struct mrb_state *mrb, struct RBasic *object); MRB_END_DECL #endif /* MRUBY_GC_H */ mruby-2.0.0/include/mruby/hash.h000066400000000000000000000115601340361412400165250ustar00rootroot00000000000000/* ** mruby/hash.h - Hash class ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_HASH_H #define MRUBY_HASH_H #include "common.h" #include /** * Hash class */ MRB_BEGIN_DECL struct RHash { MRB_OBJECT_HEADER; struct iv_tbl *iv; struct htable *ht; }; #define mrb_hash_ptr(v) ((struct RHash*)(mrb_ptr(v))) #define mrb_hash_value(p) mrb_obj_value((void*)(p)) MRB_API mrb_value mrb_hash_new_capa(mrb_state*, mrb_int); MRB_API mrb_value mrb_ensure_hash_type(mrb_state *mrb, mrb_value hash); MRB_API mrb_value mrb_check_hash_type(mrb_state *mrb, mrb_value hash); /* * Initializes a new hash. * * Equivalent to: * * Hash.new * * @param mrb The mruby state reference. * @return The initialized hash. */ MRB_API mrb_value mrb_hash_new(mrb_state *mrb); /* * Sets a keys and values to hashes. * * Equivalent to: * * hash[key] = val * * @param mrb The mruby state reference. * @param hash The target hash. * @param key The key to set. * @param val The value to set. * @return The value. */ MRB_API void mrb_hash_set(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value val); /* * Gets a value from a key. If the key is not found, the default of the * hash is used. * * Equivalent to: * * hash[key] * * @param mrb The mruby state reference. * @param hash The target hash. * @param key The key to get. * @return The found value. */ MRB_API mrb_value mrb_hash_get(mrb_state *mrb, mrb_value hash, mrb_value key); /* * Gets a value from a key. If the key is not found, the default parameter is * used. * * Equivalent to: * * hash.key?(key) ? hash[key] : def * * @param mrb The mruby state reference. * @param hash The target hash. * @param key The key to get. * @param def The default value. * @return The found value. */ MRB_API mrb_value mrb_hash_fetch(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value def); /* * Deletes hash key and value pair. * * Equivalent to: * * hash.delete(key) * * @param mrb The mruby state reference. * @param hash The target hash. * @param key The key to delete. * @return The deleted value. */ MRB_API mrb_value mrb_hash_delete_key(mrb_state *mrb, mrb_value hash, mrb_value key); /* * Gets an array of keys. * * Equivalent to: * * hash.keys * * @param mrb The mruby state reference. * @param hash The target hash. * @return An array with the keys of the hash. */ MRB_API mrb_value mrb_hash_keys(mrb_state *mrb, mrb_value hash); /* * Check if the hash has the key. * * Equivalent to: * * hash.key?(key) * * @param mrb The mruby state reference. * @param hash The target hash. * @param key The key to check existence. * @return True if the hash has the key */ MRB_API mrb_bool mrb_hash_key_p(mrb_state *mrb, mrb_value hash, mrb_value key); /* * Check if the hash is empty * * Equivalent to: * * hash.empty? * * @param mrb The mruby state reference. * @param self The target hash. * @return True if the hash is empty, false otherwise. */ MRB_API mrb_bool mrb_hash_empty_p(mrb_state *mrb, mrb_value self); /* * Gets an array of values. * * Equivalent to: * * hash.values * * @param mrb The mruby state reference. * @param hash The target hash. * @return An array with the values of the hash. */ MRB_API mrb_value mrb_hash_values(mrb_state *mrb, mrb_value hash); /* * Clears the hash. * * Equivalent to: * * hash.clear * * @param mrb The mruby state reference. * @param hash The target hash. * @return The hash */ MRB_API mrb_value mrb_hash_clear(mrb_state *mrb, mrb_value hash); /* * Copies the hash. * * * @param mrb The mruby state reference. * @param hash The target hash. * @return The copy of the hash */ MRB_API mrb_value mrb_hash_dup(mrb_state *mrb, mrb_value hash); /* * Merges two hashes. The first hash will be modified by the * second hash. * * @param mrb The mruby state reference. * @param hash1 The target hash. * @param hash2 Updating hash */ MRB_API void mrb_hash_merge(mrb_state *mrb, mrb_value hash1, mrb_value hash2); /* declaration of struct kh_ht */ /* be careful when you touch the internal */ typedef struct { mrb_value v; mrb_int n; } mrb_hash_value; KHASH_DECLARE(ht, mrb_value, mrb_hash_value, TRUE) /* RHASH_TBL allocates st_table if not available. */ #define RHASH(obj) ((struct RHash*)(mrb_ptr(obj))) #define RHASH_TBL(h) (RHASH(h)->ht) #define RHASH_IFNONE(h) mrb_iv_get(mrb, (h), mrb_intern_lit(mrb, "ifnone")) #define RHASH_PROCDEFAULT(h) RHASH_IFNONE(h) #define MRB_HASH_DEFAULT 1 #define MRB_HASH_PROC_DEFAULT 2 #define MRB_RHASH_DEFAULT_P(h) (RHASH(h)->flags & MRB_HASH_DEFAULT) #define MRB_RHASH_PROCDEFAULT_P(h) (RHASH(h)->flags & MRB_HASH_PROC_DEFAULT) /* GC functions */ void mrb_gc_mark_hash(mrb_state*, struct RHash*); size_t mrb_gc_mark_hash_size(mrb_state*, struct RHash*); void mrb_gc_free_hash(mrb_state*, struct RHash*); MRB_END_DECL #endif /* MRUBY_HASH_H */ mruby-2.0.0/include/mruby/irep.h000066400000000000000000000030511340361412400165350ustar00rootroot00000000000000/* ** mruby/irep.h - mrb_irep structure ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_IREP_H #define MRUBY_IREP_H #include "common.h" #include /** * Compiled mruby scripts. */ MRB_BEGIN_DECL enum irep_pool_type { IREP_TT_STRING, IREP_TT_FIXNUM, IREP_TT_FLOAT, }; struct mrb_locals { mrb_sym name; uint16_t r; }; /* Program data array struct */ typedef struct mrb_irep { uint16_t nlocals; /* Number of local variables */ uint16_t nregs; /* Number of register variables */ uint8_t flags; mrb_code *iseq; mrb_value *pool; mrb_sym *syms; struct mrb_irep **reps; struct mrb_locals *lv; /* debug info */ struct mrb_irep_debug_info* debug_info; uint16_t ilen, plen, slen, rlen; uint32_t refcnt; } mrb_irep; #define MRB_ISEQ_NO_FREE 1 MRB_API mrb_irep *mrb_add_irep(mrb_state *mrb); /* @param [const uint8_t*] irep code, expected as a literal */ MRB_API mrb_value mrb_load_irep(mrb_state*, const uint8_t*); /* @param [const uint8_t*] irep code, expected as a literal */ MRB_API mrb_value mrb_load_irep_cxt(mrb_state*, const uint8_t*, mrbc_context*); void mrb_irep_free(mrb_state*, struct mrb_irep*); void mrb_irep_incref(mrb_state*, struct mrb_irep*); void mrb_irep_decref(mrb_state*, struct mrb_irep*); void mrb_irep_cutref(mrb_state*, struct mrb_irep*); void mrb_irep_remove_lv(mrb_state *mrb, mrb_irep *irep); struct mrb_insn_data { uint8_t insn; uint16_t a; uint16_t b; uint8_t c; }; struct mrb_insn_data mrb_decode_insn(mrb_code *pc); MRB_END_DECL #endif /* MRUBY_IREP_H */ mruby-2.0.0/include/mruby/istruct.h000066400000000000000000000016011340361412400172720ustar00rootroot00000000000000/* ** mruby/istruct.h - Inline structures ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_ISTRUCT_H #define MRUBY_ISTRUCT_H #include "common.h" #include /** * Inline structures that fit in RVALUE * * They cannot have finalizer, and cannot have instance variables. */ MRB_BEGIN_DECL #define ISTRUCT_DATA_SIZE (sizeof(void*) * 3) struct RIstruct { MRB_OBJECT_HEADER; char inline_data[ISTRUCT_DATA_SIZE]; }; #define RISTRUCT(obj) ((struct RIstruct*)(mrb_ptr(obj))) #define ISTRUCT_PTR(obj) (RISTRUCT(obj)->inline_data) MRB_INLINE mrb_int mrb_istruct_size() { return ISTRUCT_DATA_SIZE; } MRB_INLINE void* mrb_istruct_ptr(mrb_value object) { return ISTRUCT_PTR(object); } MRB_INLINE void mrb_istruct_copy(mrb_value dest, mrb_value src) { memcpy(ISTRUCT_PTR(dest), ISTRUCT_PTR(src), ISTRUCT_DATA_SIZE); } MRB_END_DECL #endif /* MRUBY_ISTRUCT_H */ mruby-2.0.0/include/mruby/khash.h000066400000000000000000000344061340361412400167040ustar00rootroot00000000000000/* ** mruby/khash.c - Hash for mruby ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_KHASH_H #define MRUBY_KHASH_H #include #include #include "common.h" /** * khash definitions used in mruby's hash table. */ MRB_BEGIN_DECL typedef uint32_t khint_t; typedef khint_t khiter_t; #ifndef KHASH_DEFAULT_SIZE # define KHASH_DEFAULT_SIZE 32 #endif #define KHASH_MIN_SIZE 8 #define UPPER_BOUND(x) ((x)>>2|(x)>>1) /* extern uint8_t __m[]; */ /* mask for flags */ static const uint8_t __m_empty[] = {0x02, 0x08, 0x20, 0x80}; static const uint8_t __m_del[] = {0x01, 0x04, 0x10, 0x40}; static const uint8_t __m_either[] = {0x03, 0x0c, 0x30, 0xc0}; #define __ac_isempty(ed_flag, i) (ed_flag[(i)/4]&__m_empty[(i)%4]) #define __ac_isdel(ed_flag, i) (ed_flag[(i)/4]&__m_del[(i)%4]) #define __ac_iseither(ed_flag, i) (ed_flag[(i)/4]&__m_either[(i)%4]) #define khash_power2(v) do { \ v--;\ v |= v >> 1;\ v |= v >> 2;\ v |= v >> 4;\ v |= v >> 8;\ v |= v >> 16;\ v++;\ } while (0) #define khash_mask(h) ((h)->n_buckets-1) #define khash_upper_bound(h) (UPPER_BOUND((h)->n_buckets)) /* declare struct kh_xxx and kh_xxx_funcs name: hash name khkey_t: key data type khval_t: value data type kh_is_map: (0: hash set / 1: hash map) */ #define KHASH_DECLARE(name, khkey_t, khval_t, kh_is_map) \ typedef struct kh_##name { \ khint_t n_buckets; \ khint_t size; \ khint_t n_occupied; \ uint8_t *ed_flags; \ khkey_t *keys; \ khval_t *vals; \ } kh_##name##_t; \ void kh_alloc_##name(mrb_state *mrb, kh_##name##_t *h); \ kh_##name##_t *kh_init_##name##_size(mrb_state *mrb, khint_t size); \ kh_##name##_t *kh_init_##name(mrb_state *mrb); \ void kh_destroy_##name(mrb_state *mrb, kh_##name##_t *h); \ void kh_clear_##name(mrb_state *mrb, kh_##name##_t *h); \ khint_t kh_get_##name(mrb_state *mrb, kh_##name##_t *h, khkey_t key); \ khint_t kh_put_##name(mrb_state *mrb, kh_##name##_t *h, khkey_t key, int *ret); \ void kh_resize_##name(mrb_state *mrb, kh_##name##_t *h, khint_t new_n_buckets); \ void kh_del_##name(mrb_state *mrb, kh_##name##_t *h, khint_t x); \ kh_##name##_t *kh_copy_##name(mrb_state *mrb, kh_##name##_t *h); static inline void kh_fill_flags(uint8_t *p, uint8_t c, size_t len) { while (len-- > 0) { *p++ = c; } } /* define kh_xxx_funcs name: hash name khkey_t: key data type khval_t: value data type kh_is_map: (0: hash set / 1: hash map) __hash_func: hash function __hash_equal: hash comparation function */ #define KHASH_DEFINE(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ void kh_alloc_##name(mrb_state *mrb, kh_##name##_t *h) \ { \ khint_t sz = h->n_buckets; \ size_t len = sizeof(khkey_t) + (kh_is_map ? sizeof(khval_t) : 0); \ uint8_t *p = (uint8_t*)mrb_malloc(mrb, sizeof(uint8_t)*sz/4+len*sz); \ h->size = h->n_occupied = 0; \ h->keys = (khkey_t *)p; \ h->vals = kh_is_map ? (khval_t *)(p+sizeof(khkey_t)*sz) : NULL; \ h->ed_flags = p+len*sz; \ kh_fill_flags(h->ed_flags, 0xaa, sz/4); \ } \ kh_##name##_t *kh_init_##name##_size(mrb_state *mrb, khint_t size) { \ kh_##name##_t *h = (kh_##name##_t*)mrb_calloc(mrb, 1, sizeof(kh_##name##_t)); \ if (size < KHASH_MIN_SIZE) \ size = KHASH_MIN_SIZE; \ khash_power2(size); \ h->n_buckets = size; \ kh_alloc_##name(mrb, h); \ return h; \ } \ kh_##name##_t *kh_init_##name(mrb_state *mrb) { \ return kh_init_##name##_size(mrb, KHASH_DEFAULT_SIZE); \ } \ void kh_destroy_##name(mrb_state *mrb, kh_##name##_t *h) \ { \ if (h) { \ mrb_free(mrb, h->keys); \ mrb_free(mrb, h); \ } \ } \ void kh_clear_##name(mrb_state *mrb, kh_##name##_t *h) \ { \ (void)mrb; \ if (h && h->ed_flags) { \ kh_fill_flags(h->ed_flags, 0xaa, h->n_buckets/4); \ h->size = h->n_occupied = 0; \ } \ } \ khint_t kh_get_##name(mrb_state *mrb, kh_##name##_t *h, khkey_t key) \ { \ khint_t k = __hash_func(mrb,key) & khash_mask(h), step = 0; \ (void)mrb; \ while (!__ac_isempty(h->ed_flags, k)) { \ if (!__ac_isdel(h->ed_flags, k)) { \ if (__hash_equal(mrb,h->keys[k], key)) return k; \ } \ k = (k+(++step)) & khash_mask(h); \ } \ return kh_end(h); \ } \ void kh_resize_##name(mrb_state *mrb, kh_##name##_t *h, khint_t new_n_buckets) \ { \ if (new_n_buckets < KHASH_MIN_SIZE) \ new_n_buckets = KHASH_MIN_SIZE; \ khash_power2(new_n_buckets); \ { \ kh_##name##_t hh; \ uint8_t *old_ed_flags = h->ed_flags; \ khkey_t *old_keys = h->keys; \ khval_t *old_vals = h->vals; \ khint_t old_n_buckets = h->n_buckets; \ khint_t i; \ hh.n_buckets = new_n_buckets; \ kh_alloc_##name(mrb, &hh); \ /* relocate */ \ for (i=0 ; in_occupied >= khash_upper_bound(h)) { \ kh_resize_##name(mrb, h, h->n_buckets*2); \ } \ k = __hash_func(mrb,key) & khash_mask(h); \ del_k = kh_end(h); \ while (!__ac_isempty(h->ed_flags, k)) { \ if (!__ac_isdel(h->ed_flags, k)) { \ if (__hash_equal(mrb,h->keys[k], key)) { \ if (ret) *ret = 0; \ return k; \ } \ } \ else if (del_k == kh_end(h)) { \ del_k = k; \ } \ k = (k+(++step)) & khash_mask(h); \ } \ if (del_k != kh_end(h)) { \ /* put at del */ \ h->keys[del_k] = key; \ h->ed_flags[del_k/4] &= ~__m_del[del_k%4]; \ h->size++; \ if (ret) *ret = 2; \ return del_k; \ } \ else { \ /* put at empty */ \ h->keys[k] = key; \ h->ed_flags[k/4] &= ~__m_empty[k%4]; \ h->size++; \ h->n_occupied++; \ if (ret) *ret = 1; \ return k; \ } \ } \ void kh_del_##name(mrb_state *mrb, kh_##name##_t *h, khint_t x) \ { \ (void)mrb; \ mrb_assert(x != h->n_buckets && !__ac_iseither(h->ed_flags, x)); \ h->ed_flags[x/4] |= __m_del[x%4]; \ h->size--; \ } \ kh_##name##_t *kh_copy_##name(mrb_state *mrb, kh_##name##_t *h) \ { \ kh_##name##_t *h2; \ khiter_t k, k2; \ \ h2 = kh_init_##name(mrb); \ for (k = kh_begin(h); k != kh_end(h); k++) { \ if (kh_exist(h, k)) { \ k2 = kh_put_##name(mrb, h2, kh_key(h, k), NULL); \ if (kh_is_map) kh_value(h2, k2) = kh_value(h, k); \ } \ } \ return h2; \ } #define khash_t(name) kh_##name##_t #define kh_init_size(name,mrb,size) kh_init_##name##_size(mrb,size) #define kh_init(name,mrb) kh_init_##name(mrb) #define kh_destroy(name, mrb, h) kh_destroy_##name(mrb, h) #define kh_clear(name, mrb, h) kh_clear_##name(mrb, h) #define kh_resize(name, mrb, h, s) kh_resize_##name(mrb, h, s) #define kh_put(name, mrb, h, k) kh_put_##name(mrb, h, k, NULL) #define kh_put2(name, mrb, h, k, r) kh_put_##name(mrb, h, k, r) #define kh_get(name, mrb, h, k) kh_get_##name(mrb, h, k) #define kh_del(name, mrb, h, k) kh_del_##name(mrb, h, k) #define kh_copy(name, mrb, h) kh_copy_##name(mrb, h) #define kh_exist(h, x) (!__ac_iseither((h)->ed_flags, (x))) #define kh_key(h, x) ((h)->keys[x]) #define kh_val(h, x) ((h)->vals[x]) #define kh_value(h, x) ((h)->vals[x]) #define kh_begin(h) (khint_t)(0) #define kh_end(h) ((h)->n_buckets) #define kh_size(h) ((h)->size) #define kh_n_buckets(h) ((h)->n_buckets) #define kh_int_hash_func(mrb,key) (khint_t)((key)^((key)<<2)^((key)>>2)) #define kh_int_hash_equal(mrb,a, b) (a == b) #define kh_int64_hash_func(mrb,key) (khint_t)((key)>>33^(key)^(key)<<11) #define kh_int64_hash_equal(mrb,a, b) (a == b) static inline khint_t __ac_X31_hash_string(const char *s) { khint_t h = *s; if (h) for (++s ; *s; ++s) h = (h << 5) - h + *s; return h; } #define kh_str_hash_func(mrb,key) __ac_X31_hash_string(key) #define kh_str_hash_equal(mrb,a, b) (strcmp(a, b) == 0) typedef const char *kh_cstr_t; MRB_END_DECL #endif /* MRUBY_KHASH_H */ mruby-2.0.0/include/mruby/numeric.h000066400000000000000000000106651340361412400172510ustar00rootroot00000000000000/* ** mruby/numeric.h - Numeric, Integer, Float, Fixnum class ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_NUMERIC_H #define MRUBY_NUMERIC_H #include "common.h" /** * Numeric class and it's sub-classes. * * Integer, Float and Fixnum */ MRB_BEGIN_DECL #define TYPED_POSFIXABLE(f,t) ((f) <= (t)MRB_INT_MAX) #define TYPED_NEGFIXABLE(f,t) ((f) >= (t)MRB_INT_MIN) #define TYPED_FIXABLE(f,t) (TYPED_POSFIXABLE(f,t) && TYPED_NEGFIXABLE(f,t)) #define POSFIXABLE(f) TYPED_POSFIXABLE(f,mrb_int) #define NEGFIXABLE(f) TYPED_NEGFIXABLE(f,mrb_int) #define FIXABLE(f) TYPED_FIXABLE(f,mrb_int) #ifndef MRB_WITHOUT_FLOAT #define FIXABLE_FLOAT(f) TYPED_FIXABLE(f,double) #endif #ifndef MRB_WITHOUT_FLOAT MRB_API mrb_value mrb_flo_to_fixnum(mrb_state *mrb, mrb_value val); #endif MRB_API mrb_value mrb_fixnum_to_str(mrb_state *mrb, mrb_value x, mrb_int base); /* ArgumentError if format string doesn't match /%(\.[0-9]+)?[aAeEfFgG]/ */ #ifndef MRB_WITHOUT_FLOAT MRB_API mrb_value mrb_float_to_str(mrb_state *mrb, mrb_value x, const char *fmt); MRB_API mrb_float mrb_to_flo(mrb_state *mrb, mrb_value x); #endif mrb_value mrb_fixnum_plus(mrb_state *mrb, mrb_value x, mrb_value y); mrb_value mrb_fixnum_minus(mrb_state *mrb, mrb_value x, mrb_value y); mrb_value mrb_fixnum_mul(mrb_state *mrb, mrb_value x, mrb_value y); mrb_value mrb_num_div(mrb_state *mrb, mrb_value x, mrb_value y); #ifndef __has_builtin #define __has_builtin(x) 0 #endif #if (defined(__GNUC__) && __GNUC__ >= 5) || \ (__has_builtin(__builtin_add_overflow) && \ __has_builtin(__builtin_sub_overflow) && \ __has_builtin(__builtin_mul_overflow)) # define MRB_HAVE_TYPE_GENERIC_CHECKED_ARITHMETIC_BUILTINS #endif /* // Clang 3.8 and 3.9 have problem compiling mruby in 32-bit mode, when MRB_INT64 is set // because of missing __mulodi4 and similar functions in its runtime. We need to use custom // implementation for them. */ #ifdef MRB_HAVE_TYPE_GENERIC_CHECKED_ARITHMETIC_BUILTINS #if defined(__clang__) && (__clang_major__ == 3) && (__clang_minor__ >= 8) && \ defined(MRB_32BIT) && defined(MRB_INT64) #undef MRB_HAVE_TYPE_GENERIC_CHECKED_ARITHMETIC_BUILTINS #endif #endif #ifdef MRB_HAVE_TYPE_GENERIC_CHECKED_ARITHMETIC_BUILTINS #ifndef MRB_WORD_BOXING # define WBCHK(x) 0 #else # define WBCHK(x) !FIXABLE(x) #endif static inline mrb_bool mrb_int_add_overflow(mrb_int augend, mrb_int addend, mrb_int *sum) { return __builtin_add_overflow(augend, addend, sum) || WBCHK(*sum); } static inline mrb_bool mrb_int_sub_overflow(mrb_int minuend, mrb_int subtrahend, mrb_int *difference) { return __builtin_sub_overflow(minuend, subtrahend, difference) || WBCHK(*difference); } static inline mrb_bool mrb_int_mul_overflow(mrb_int multiplier, mrb_int multiplicand, mrb_int *product) { return __builtin_mul_overflow(multiplier, multiplicand, product) || WBCHK(*product); } #undef WBCHK #else #define MRB_UINT_MAKE2(n) uint ## n ## _t #define MRB_UINT_MAKE(n) MRB_UINT_MAKE2(n) #define mrb_uint MRB_UINT_MAKE(MRB_INT_BIT) #define MRB_INT_OVERFLOW_MASK ((mrb_uint)1 << (MRB_INT_BIT - 1 - MRB_FIXNUM_SHIFT)) static inline mrb_bool mrb_int_add_overflow(mrb_int augend, mrb_int addend, mrb_int *sum) { mrb_uint x = (mrb_uint)augend; mrb_uint y = (mrb_uint)addend; mrb_uint z = (mrb_uint)(x + y); *sum = (mrb_int)z; return !!(((x ^ z) & (y ^ z)) & MRB_INT_OVERFLOW_MASK); } static inline mrb_bool mrb_int_sub_overflow(mrb_int minuend, mrb_int subtrahend, mrb_int *difference) { mrb_uint x = (mrb_uint)minuend; mrb_uint y = (mrb_uint)subtrahend; mrb_uint z = (mrb_uint)(x - y); *difference = (mrb_int)z; return !!(((x ^ z) & (~y ^ z)) & MRB_INT_OVERFLOW_MASK); } static inline mrb_bool mrb_int_mul_overflow(mrb_int multiplier, mrb_int multiplicand, mrb_int *product) { #if MRB_INT_BIT == 32 int64_t n = (int64_t)multiplier * multiplicand; *product = (mrb_int)n; return !FIXABLE(n); #else if (multiplier > 0) { if (multiplicand > 0) { if (multiplier > MRB_INT_MAX / multiplicand) return TRUE; } else { if (multiplicand < MRB_INT_MAX / multiplier) return TRUE; } } else { if (multiplicand > 0) { if (multiplier < MRB_INT_MAX / multiplicand) return TRUE; } else { if (multiplier != 0 && multiplicand < MRB_INT_MAX / multiplier) return TRUE; } } *product = multiplier * multiplicand; return FALSE; #endif } #undef MRB_INT_OVERFLOW_MASK #undef mrb_uint #undef MRB_UINT_MAKE #undef MRB_UINT_MAKE2 #endif MRB_END_DECL #endif /* MRUBY_NUMERIC_H */ mruby-2.0.0/include/mruby/object.h000066400000000000000000000017461340361412400170550ustar00rootroot00000000000000/* ** mruby/object.h - mruby object definition ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_OBJECT_H #define MRUBY_OBJECT_H #define MRB_OBJECT_HEADER \ enum mrb_vtype tt:8;\ uint32_t color:3;\ uint32_t flags:21;\ struct RClass *c;\ struct RBasic *gcnext #define MRB_FLAG_TEST(obj, flag) ((obj)->flags & flag) struct RBasic { MRB_OBJECT_HEADER; }; #define mrb_basic_ptr(v) ((struct RBasic*)(mrb_ptr(v))) #define MRB_FL_OBJ_IS_FROZEN (1 << 20) #define MRB_FROZEN_P(o) ((o)->flags & MRB_FL_OBJ_IS_FROZEN) #define MRB_SET_FROZEN_FLAG(o) ((o)->flags |= MRB_FL_OBJ_IS_FROZEN) #define MRB_UNSET_FROZEN_FLAG(o) ((o)->flags &= ~MRB_FL_OBJ_IS_FROZEN) struct RObject { MRB_OBJECT_HEADER; struct iv_tbl *iv; }; #define mrb_obj_ptr(v) ((struct RObject*)(mrb_ptr(v))) #define mrb_immediate_p(x) (mrb_type(x) < MRB_TT_HAS_BASIC) #define mrb_special_const_p(x) mrb_immediate_p(x) struct RFiber { MRB_OBJECT_HEADER; struct mrb_context *cxt; }; #endif /* MRUBY_OBJECT_H */ mruby-2.0.0/include/mruby/opcode.h000066400000000000000000000040351340361412400170520ustar00rootroot00000000000000/* ** mruby/opcode.h - RiteVM operation codes ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_OPCODE_H #define MRUBY_OPCODE_H enum mrb_insn { #define OPCODE(x,_) OP_ ## x, #include "mruby/ops.h" #undef OPCODE }; #define OP_L_STRICT 1 #define OP_L_CAPTURE 2 #define OP_L_METHOD OP_L_STRICT #define OP_L_LAMBDA (OP_L_STRICT|OP_L_CAPTURE) #define OP_L_BLOCK OP_L_CAPTURE #define OP_R_NORMAL 0 #define OP_R_BREAK 1 #define OP_R_RETURN 2 #define PEEK_B(pc) (*(pc)) #define PEEK_S(pc) ((pc)[0]<<8|(pc)[1]) #define PEEK_W(pc) ((pc)[0]<<16|(pc)[1]<<8|(pc)[2]) #define READ_B() PEEK_B(pc++) #define READ_S() (pc+=2, PEEK_S(pc-2)) #define READ_W() (pc+=3, PEEK_W(pc-3)) #define FETCH_Z() /* nothing */ #define FETCH_B() do {a=READ_B();} while (0) #define FETCH_BB() do {a=READ_B(); b=READ_B();} while (0) #define FETCH_BBB() do {a=READ_B(); b=READ_B(); c=READ_B();} while (0) #define FETCH_BS() do {a=READ_B(); b=READ_S();} while (0) #define FETCH_S() do {a=READ_S();} while (0) #define FETCH_W() do {a=READ_W();} while (0) /* with OP_EXT1 (1st 16bit) */ #define FETCH_Z_1() FETCH_Z() #define FETCH_B_1() FETCH_S() #define FETCH_BB_1() do {a=READ_S(); b=READ_B();} while (0) #define FETCH_BBB_1() do {a=READ_S(); b=READ_B(); c=READ_B();} while (0) #define FETCH_BS_1() do {a=READ_S(); b=READ_S();} while (0) #define FETCH_S_1() FETCH_S() #define FETCH_W_1() FETCH_W() /* with OP_EXT2 (2nd 16bit) */ #define FETCH_Z_2() FETCH_Z() #define FETCH_B_2() FETCH_B() #define FETCH_BB_2() do {a=READ_B(); b=READ_S();} while (0) #define FETCH_BBB_2() do {a=READ_B(); b=READ_S(); c=READ_B();} while (0) #define FETCH_BS_2() FETCH_BS() #define FETCH_S_2() FETCH_S() #define FETCH_W_2() FETCH_W() /* with OP_EXT3 (1st & 2nd 16bit) */ #define FETCH_Z_3() FETCH_Z() #define FETCH_B_3() FETCH_B() #define FETCH_BB_3() do {a=READ_S(); b=READ_S();} while (0) #define FETCH_BBB_3() do {a=READ_S(); b=READ_S(); c=READ_B();} while (0) #define FETCH_BS_3() do {a=READ_S(); b=READ_S();} while (0) #define FETCH_S_3() FETCH_S() #define FETCH_W_3() FETCH_W() #endif /* MRUBY_OPCODE_H */ mruby-2.0.0/include/mruby/ops.h000066400000000000000000000147021340361412400164040ustar00rootroot00000000000000/* operand types: + Z: no operand (Z,Z,Z,Z) + B: 8bit (B,S,B,B) + BB: 8+8bit (BB,SB,BS,SS) + BBB: 8+8+8bit (BBB,SBB,BSB,SSB) + BS: 8+16bit (BS,SS,BS,BS) + S: 16bit (S,S,S,S) + W: 24bit (W,W,W,W) */ /*----------------------------------------------------------------------- operation code operands semantics ------------------------------------------------------------------------*/ OPCODE(NOP, Z) /* no operation */ OPCODE(MOVE, BB) /* R(a) = R(b) */ OPCODE(LOADL, BB) /* R(a) = Pool(b) */ OPCODE(LOADI, BB) /* R(a) = mrb_int(b) */ OPCODE(LOADINEG, BB) /* R(a) = mrb_int(-b) */ OPCODE(LOADI__1, B) /* R(a) = mrb_int(-1) */ OPCODE(LOADI_0, B) /* R(a) = mrb_int(0) */ OPCODE(LOADI_1, B) /* R(a) = mrb_int(1) */ OPCODE(LOADI_2, B) /* R(a) = mrb_int(2) */ OPCODE(LOADI_3, B) /* R(a) = mrb_int(3) */ OPCODE(LOADI_4, B) /* R(a) = mrb_int(4) */ OPCODE(LOADI_5, B) /* R(a) = mrb_int(5) */ OPCODE(LOADI_6, B) /* R(a) = mrb_int(6) */ OPCODE(LOADI_7, B) /* R(a) = mrb_int(7) */ OPCODE(LOADSYM, BB) /* R(a) = Syms(b) */ OPCODE(LOADNIL, B) /* R(a) = nil */ OPCODE(LOADSELF, B) /* R(a) = self */ OPCODE(LOADT, B) /* R(a) = true */ OPCODE(LOADF, B) /* R(a) = false */ OPCODE(GETGV, BB) /* R(a) = getglobal(Syms(b)) */ OPCODE(SETGV, BB) /* setglobal(Syms(b), R(a)) */ OPCODE(GETSV, BB) /* R(a) = Special[Syms(b)] */ OPCODE(SETSV, BB) /* Special[Syms(b)] = R(a) */ OPCODE(GETIV, BB) /* R(a) = ivget(Syms(b)) */ OPCODE(SETIV, BB) /* ivset(Syms(b),R(a)) */ OPCODE(GETCV, BB) /* R(a) = cvget(Syms(b)) */ OPCODE(SETCV, BB) /* cvset(Syms(b),R(a)) */ OPCODE(GETCONST, BB) /* R(a) = constget(Syms(b)) */ OPCODE(SETCONST, BB) /* constset(Syms(b),R(a)) */ OPCODE(GETMCNST, BB) /* R(a) = R(a)::Syms(b) */ OPCODE(SETMCNST, BB) /* R(a+1)::Syms(b) = R(a) */ OPCODE(GETUPVAR, BBB) /* R(a) = uvget(b,c) */ OPCODE(SETUPVAR, BBB) /* uvset(b,c,R(a)) */ OPCODE(JMP, S) /* pc=a */ OPCODE(JMPIF, BS) /* if R(b) pc=a */ OPCODE(JMPNOT, BS) /* if !R(b) pc=a */ OPCODE(JMPNIL, BS) /* if R(b)==nil pc=a */ OPCODE(ONERR, S) /* rescue_push(a) */ OPCODE(EXCEPT, B) /* R(a) = exc */ OPCODE(RESCUE, BB) /* R(b) = R(a).isa?(R(b)) */ OPCODE(POPERR, B) /* a.times{rescue_pop()} */ OPCODE(RAISE, B) /* raise(R(a)) */ OPCODE(EPUSH, B) /* ensure_push(SEQ[a]) */ OPCODE(EPOP, B) /* A.times{ensure_pop().call} */ OPCODE(SENDV, BB) /* R(a) = call(R(a),Syms(b),*R(a+1)) */ OPCODE(SENDVB, BB) /* R(a) = call(R(a),Syms(b),*R(a+1),&R(a+2)) */ OPCODE(SEND, BBB) /* R(a) = call(R(a),Syms(b),R(a+1),...,R(a+c)) */ OPCODE(SENDB, BBB) /* R(a) = call(R(a),Syms(Bx),R(a+1),...,R(a+c),&R(a+c+1)) */ OPCODE(CALL, Z) /* R(0) = self.call(frame.argc, frame.argv) */ OPCODE(SUPER, BB) /* R(a) = super(R(a+1),... ,R(a+b+1)) */ OPCODE(ARGARY, BS) /* R(a) = argument array (16=m5:r1:m5:d1:lv4) */ OPCODE(ENTER, W) /* arg setup according to flags (23=m5:o5:r1:m5:k5:d1:b1) */ OPCODE(KEY_P, BB) /* R(a) = kdict.key?(Syms(b)) # todo */ OPCODE(KEYEND, Z) /* raise unless kdict.empty? # todo */ OPCODE(KARG, BB) /* R(a) = kdict[Syms(b)]; kdict.delete(Syms(b)) # todo */ OPCODE(RETURN, B) /* return R(a) (normal) */ OPCODE(RETURN_BLK, B) /* return R(a) (in-block return) */ OPCODE(BREAK, B) /* break R(a) */ OPCODE(BLKPUSH, BS) /* R(a) = block (16=m5:r1:m5:d1:lv4) */ OPCODE(ADD, B) /* R(a) = R(a)+R(a+1) */ OPCODE(ADDI, BB) /* R(a) = R(a)+mrb_int(c) */ OPCODE(SUB, B) /* R(a) = R(a)-R(a+1) */ OPCODE(SUBI, BB) /* R(a) = R(a)-C */ OPCODE(MUL, B) /* R(a) = R(a)*R(a+1) */ OPCODE(DIV, B) /* R(a) = R(a)/R(a+1) */ OPCODE(EQ, B) /* R(a) = R(a)==R(a+1) */ OPCODE(LT, B) /* R(a) = R(a)R(a+1) */ OPCODE(GE, B) /* R(a) = R(a)>=R(a+1) */ OPCODE(ARRAY, BB) /* R(a) = ary_new(R(a),R(a+1)..R(a+b)) */ OPCODE(ARRAY2, BBB) /* R(a) = ary_new(R(b),R(b+1)..R(b+c)) */ OPCODE(ARYCAT, B) /* ary_cat(R(a),R(a+1)) */ OPCODE(ARYPUSH, B) /* ary_push(R(a),R(a+1)) */ OPCODE(ARYDUP, B) /* R(a) = ary_dup(R(a)) */ OPCODE(AREF, BBB) /* R(a) = R(b)[c] */ OPCODE(ASET, BBB) /* R(a)[c] = R(b) */ OPCODE(APOST, BBB) /* *R(a),R(a+1)..R(a+C) = R(a)[b..] */ OPCODE(INTERN, B) /* R(a) = intern(R(a)) */ OPCODE(STRING, BB) /* R(a) = str_dup(Lit(b)) */ OPCODE(STRCAT, B) /* str_cat(R(a),R(a+1)) */ OPCODE(HASH, BB) /* R(a) = hash_new(R(a),R(a+1)..R(a+b)) */ OPCODE(HASHADD, BB) /* R(a) = hash_push(R(a),R(a+1)..R(a+b)) */ OPCODE(HASHCAT, B) /* R(a) = hash_cat(R(a),R(a+1)) */ OPCODE(LAMBDA, BB) /* R(a) = lambda(SEQ[b],L_LAMBDA) */ OPCODE(BLOCK, BB) /* R(a) = lambda(SEQ[b],L_BLOCK) */ OPCODE(METHOD, BB) /* R(a) = lambda(SEQ[b],L_METHOD) */ OPCODE(RANGE_INC, B) /* R(a) = range_new(R(a),R(a+1),FALSE) */ OPCODE(RANGE_EXC, B) /* R(a) = range_new(R(a),R(a+1),TRUE) */ OPCODE(OCLASS, B) /* R(a) = ::Object */ OPCODE(CLASS, BB) /* R(a) = newclass(R(a),Syms(b),R(a+1)) */ OPCODE(MODULE, BB) /* R(a) = newmodule(R(a),Syms(b)) */ OPCODE(EXEC, BB) /* R(a) = blockexec(R(a),SEQ[b]) */ OPCODE(DEF, BB) /* R(a).newmethod(Syms(b),R(a+1)) */ OPCODE(ALIAS, BB) /* alias_method(target_class,Syms(a),Syms(b)) */ OPCODE(UNDEF, B) /* undef_method(target_class,Syms(a)) */ OPCODE(SCLASS, B) /* R(a) = R(a).singleton_class */ OPCODE(TCLASS, B) /* R(a) = target_class */ OPCODE(DEBUG, BBB) /* print a,b,c */ OPCODE(ERR, B) /* raise(LocalJumpError, Lit(a)) */ OPCODE(EXT1, Z) /* make 1st operand 16bit */ OPCODE(EXT2, Z) /* make 2nd operand 16bit */ OPCODE(EXT3, Z) /* make 1st and 2nd operands 16bit */ OPCODE(STOP, Z) /* stop VM */ mruby-2.0.0/include/mruby/proc.h000066400000000000000000000111121340361412400165360ustar00rootroot00000000000000/* ** mruby/proc.h - Proc class ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_PROC_H #define MRUBY_PROC_H #include "common.h" #include /** * Proc class */ MRB_BEGIN_DECL struct REnv { MRB_OBJECT_HEADER; mrb_value *stack; struct mrb_context *cxt; mrb_sym mid; }; /* flags (21bits): 1(shared flag):10(cioff/bidx):10(stack_len) */ #define MRB_ENV_SET_STACK_LEN(e,len) (e)->flags = (((e)->flags & ~0x3ff)|((unsigned int)(len) & 0x3ff)) #define MRB_ENV_STACK_LEN(e) ((mrb_int)((e)->flags & 0x3ff)) #define MRB_ENV_STACK_UNSHARED (1<<20) #define MRB_ENV_UNSHARE_STACK(e) (e)->flags |= MRB_ENV_STACK_UNSHARED #define MRB_ENV_STACK_SHARED_P(e) (((e)->flags & MRB_ENV_STACK_UNSHARED) == 0) #define MRB_ENV_BIDX(e) (((e)->flags >> 10) & 0x3ff) #define MRB_ENV_SET_BIDX(e,idx) (e)->flags = (((e)->flags & ~(0x3ff<<10))|((unsigned int)(idx) & 0x3ff)<<10) void mrb_env_unshare(mrb_state*, struct REnv*); struct RProc { MRB_OBJECT_HEADER; union { mrb_irep *irep; mrb_func_t func; } body; struct RProc *upper; union { struct RClass *target_class; struct REnv *env; } e; }; /* aspec access */ #define MRB_ASPEC_REQ(a) (((a) >> 18) & 0x1f) #define MRB_ASPEC_OPT(a) (((a) >> 13) & 0x1f) #define MRB_ASPEC_REST(a) (((a) >> 12) & 0x1) #define MRB_ASPEC_POST(a) (((a) >> 7) & 0x1f) #define MRB_ASPEC_KEY(a) (((a) >> 2) & 0x1f) #define MRB_ASPEC_KDICT(a) ((a) & (1<<1)) #define MRB_ASPEC_BLOCK(a) ((a) & 1) #define MRB_PROC_CFUNC_FL 128 #define MRB_PROC_CFUNC_P(p) (((p)->flags & MRB_PROC_CFUNC_FL) != 0) #define MRB_PROC_CFUNC(p) (p)->body.func #define MRB_PROC_STRICT 256 #define MRB_PROC_STRICT_P(p) (((p)->flags & MRB_PROC_STRICT) != 0) #define MRB_PROC_ORPHAN 512 #define MRB_PROC_ORPHAN_P(p) (((p)->flags & MRB_PROC_ORPHAN) != 0) #define MRB_PROC_ENVSET 1024 #define MRB_PROC_ENV_P(p) (((p)->flags & MRB_PROC_ENVSET) != 0) #define MRB_PROC_ENV(p) (MRB_PROC_ENV_P(p) ? (p)->e.env : NULL) #define MRB_PROC_TARGET_CLASS(p) (MRB_PROC_ENV_P(p) ? (p)->e.env->c : (p)->e.target_class) #define MRB_PROC_SET_TARGET_CLASS(p,tc) do {\ if (MRB_PROC_ENV_P(p)) {\ (p)->e.env->c = (tc);\ mrb_field_write_barrier(mrb, (struct RBasic*)(p)->e.env, (struct RBasic*)tc);\ }\ else {\ (p)->e.target_class = (tc);\ mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)tc);\ }\ } while (0) #define MRB_PROC_SCOPE 2048 #define MRB_PROC_SCOPE_P(p) (((p)->flags & MRB_PROC_SCOPE) != 0) #define mrb_proc_ptr(v) ((struct RProc*)(mrb_ptr(v))) struct RProc *mrb_proc_new(mrb_state*, mrb_irep*); struct RProc *mrb_closure_new(mrb_state*, mrb_irep*); MRB_API struct RProc *mrb_proc_new_cfunc(mrb_state*, mrb_func_t); MRB_API struct RProc *mrb_closure_new_cfunc(mrb_state *mrb, mrb_func_t func, int nlocals); void mrb_proc_copy(struct RProc *a, struct RProc *b); /* implementation of #send method */ mrb_value mrb_f_send(mrb_state *mrb, mrb_value self); /* following functions are defined in mruby-proc-ext so please include it when using */ MRB_API struct RProc *mrb_proc_new_cfunc_with_env(mrb_state*, mrb_func_t, mrb_int, const mrb_value*); MRB_API mrb_value mrb_proc_cfunc_env_get(mrb_state*, mrb_int); /* old name */ #define mrb_cfunc_env_get(mrb, idx) mrb_proc_cfunc_env_get(mrb, idx) #ifdef MRB_METHOD_TABLE_INLINE #define MRB_METHOD_FUNC_FL ((uintptr_t)1) #define MRB_METHOD_FUNC_P(m) (((uintptr_t)(m))&MRB_METHOD_FUNC_FL) #define MRB_METHOD_FUNC(m) ((mrb_func_t)((uintptr_t)(m)&(~MRB_METHOD_FUNC_FL))) #define MRB_METHOD_FROM_FUNC(m,fn) m=(mrb_method_t)((struct RProc*)((uintptr_t)(fn)|MRB_METHOD_FUNC_FL)) #define MRB_METHOD_FROM_PROC(m,pr) m=(mrb_method_t)(struct RProc*)(pr) #define MRB_METHOD_PROC_P(m) (!MRB_METHOD_FUNC_P(m)) #define MRB_METHOD_PROC(m) ((struct RProc*)(m)) #define MRB_METHOD_UNDEF_P(m) ((m)==0) #else #define MRB_METHOD_FUNC_P(m) ((m).func_p) #define MRB_METHOD_FUNC(m) ((m).func) #define MRB_METHOD_FROM_FUNC(m,fn) do{(m).func_p=TRUE;(m).func=(fn);}while(0) #define MRB_METHOD_FROM_PROC(m,pr) do{(m).func_p=FALSE;(m).proc=(pr);}while(0) #define MRB_METHOD_PROC_P(m) (!MRB_METHOD_FUNC_P(m)) #define MRB_METHOD_PROC(m) ((m).proc) #define MRB_METHOD_UNDEF_P(m) ((m).proc==NULL) #endif /* MRB_METHOD_TABLE_INLINE */ #define MRB_METHOD_CFUNC_P(m) (MRB_METHOD_FUNC_P(m)?TRUE:(MRB_METHOD_PROC(m)?(MRB_PROC_CFUNC_P(MRB_METHOD_PROC(m))):FALSE)) #define MRB_METHOD_CFUNC(m) (MRB_METHOD_FUNC_P(m)?MRB_METHOD_FUNC(m):((MRB_METHOD_PROC(m)&&MRB_PROC_CFUNC_P(MRB_METHOD_PROC(m)))?MRB_PROC_CFUNC(MRB_METHOD_PROC(m)):NULL)) #include KHASH_DECLARE(mt, mrb_sym, mrb_method_t, TRUE) MRB_END_DECL #endif /* MRUBY_PROC_H */ mruby-2.0.0/include/mruby/range.h000066400000000000000000000024361340361412400167000ustar00rootroot00000000000000/* ** mruby/range.h - Range class ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_RANGE_H #define MRUBY_RANGE_H #include "common.h" /** * Range class */ MRB_BEGIN_DECL typedef struct mrb_range_edges { mrb_value beg; mrb_value end; } mrb_range_edges; struct RRange { MRB_OBJECT_HEADER; mrb_range_edges *edges; mrb_bool excl : 1; }; MRB_API struct RRange* mrb_range_ptr(mrb_state *mrb, mrb_value v); #define mrb_range_raw_ptr(v) ((struct RRange*)mrb_ptr(v)) #define mrb_range_value(p) mrb_obj_value((void*)(p)) /* * Initializes a Range. * * If the third parameter is FALSE then it includes the last value in the range. * If the third parameter is TRUE then it excludes the last value in the range. * * @param start the beginning value. * @param end the ending value. * @param exclude represents the inclusion or exclusion of the last value. */ MRB_API mrb_value mrb_range_new(mrb_state *mrb, mrb_value start, mrb_value end, mrb_bool exclude); MRB_API mrb_int mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len, mrb_bool trunc); mrb_value mrb_get_values_at(mrb_state *mrb, mrb_value obj, mrb_int olen, mrb_int argc, const mrb_value *argv, mrb_value (*func)(mrb_state*, mrb_value, mrb_int)); MRB_END_DECL #endif /* MRUBY_RANGE_H */ mruby-2.0.0/include/mruby/re.h000066400000000000000000000003121340361412400162010ustar00rootroot00000000000000/* ** mruby/re.h - Regexp class ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_RE_H #define MRUBY_RE_H MRB_BEGIN_DECL #define REGEXP_CLASS "Regexp" MRB_END_DECL #endif /* RE_H */ mruby-2.0.0/include/mruby/string.h000066400000000000000000000312331340361412400171070ustar00rootroot00000000000000/* ** mruby/string.h - String class ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_STRING_H #define MRUBY_STRING_H #include "common.h" /** * String class */ MRB_BEGIN_DECL extern const char mrb_digitmap[]; #define RSTRING_EMBED_LEN_MAX ((mrb_int)(sizeof(void*) * 3 - 1)) struct RString { MRB_OBJECT_HEADER; union { struct { mrb_int len; union { mrb_int capa; struct mrb_shared_string *shared; struct RString *fshared; } aux; char *ptr; } heap; char ary[RSTRING_EMBED_LEN_MAX + 1]; } as; }; #define RSTR_EMBED_P(s) ((s)->flags & MRB_STR_EMBED) #define RSTR_SET_EMBED_FLAG(s) ((s)->flags |= MRB_STR_EMBED) #define RSTR_UNSET_EMBED_FLAG(s) ((s)->flags &= ~(MRB_STR_EMBED|MRB_STR_EMBED_LEN_MASK)) #define RSTR_SET_EMBED_LEN(s, n) do {\ size_t tmp_n = (n);\ s->flags &= ~MRB_STR_EMBED_LEN_MASK;\ s->flags |= (tmp_n) << MRB_STR_EMBED_LEN_SHIFT;\ } while (0) #define RSTR_SET_LEN(s, n) do {\ if (RSTR_EMBED_P(s)) {\ RSTR_SET_EMBED_LEN((s),(n));\ }\ else {\ s->as.heap.len = (mrb_int)(n);\ }\ } while (0) #define RSTR_EMBED_LEN(s)\ (mrb_int)(((s)->flags & MRB_STR_EMBED_LEN_MASK) >> MRB_STR_EMBED_LEN_SHIFT) #define RSTR_PTR(s) ((RSTR_EMBED_P(s)) ? (s)->as.ary : (s)->as.heap.ptr) #define RSTR_LEN(s) ((RSTR_EMBED_P(s)) ? RSTR_EMBED_LEN(s) : (s)->as.heap.len) #define RSTR_CAPA(s) (RSTR_EMBED_P(s) ? RSTRING_EMBED_LEN_MAX : (s)->as.heap.aux.capa) #define RSTR_SHARED_P(s) ((s)->flags & MRB_STR_SHARED) #define RSTR_SET_SHARED_FLAG(s) ((s)->flags |= MRB_STR_SHARED) #define RSTR_UNSET_SHARED_FLAG(s) ((s)->flags &= ~MRB_STR_SHARED) #define RSTR_FSHARED_P(s) ((s)->flags & MRB_STR_FSHARED) #define RSTR_SET_FSHARED_FLAG(s) ((s)->flags |= MRB_STR_FSHARED) #define RSTR_UNSET_FSHARED_FLAG(s) ((s)->flags &= ~MRB_STR_FSHARED) #define RSTR_NOFREE_P(s) ((s)->flags & MRB_STR_NOFREE) #define RSTR_SET_NOFREE_FLAG(s) ((s)->flags |= MRB_STR_NOFREE) #define RSTR_UNSET_NOFREE_FLAG(s) ((s)->flags &= ~MRB_STR_NOFREE) #define RSTR_POOL_P(s) ((s)->flags & MRB_STR_POOL) #define RSTR_SET_POOL_FLAG(s) ((s)->flags |= MRB_STR_POOL) /* * Returns a pointer from a Ruby string */ #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)) #define RSTRING_EMBED_LEN(s) RSTR_EMBED_LEN(RSTRING(s)) #define RSTRING_LEN(s) RSTR_LEN(RSTRING(s)) #define RSTRING_CAPA(s) RSTR_CAPA(RSTRING(s)) #define RSTRING_END(s) (RSTRING_PTR(s) + RSTRING_LEN(s)) MRB_API mrb_int mrb_str_strlen(mrb_state*, struct RString*); #define MRB_STR_SHARED 1 #define MRB_STR_FSHARED 2 #define MRB_STR_NOFREE 4 #define MRB_STR_POOL 8 #define MRB_STR_NO_UTF 16 #define MRB_STR_EMBED 32 #define MRB_STR_EMBED_LEN_MASK 0x7c0 #define MRB_STR_EMBED_LEN_SHIFT 6 void mrb_gc_free_str(mrb_state*, struct RString*); MRB_API void mrb_str_modify(mrb_state*, struct RString*); /* * Finds the index of a substring in a string */ MRB_API mrb_int mrb_str_index(mrb_state*, mrb_value, const char*, mrb_int, mrb_int); #define mrb_str_index_lit(mrb, str, lit, off) mrb_str_index(mrb, str, lit, mrb_strlen_lit(lit), off); /* * Appends self to other. Returns self as a concatenated string. * * * Example: * * !!!c * int * main(int argc, * char **argv) * { * // Variable declarations. * mrb_value str1; * mrb_value str2; * * mrb_state *mrb = mrb_open(); * if (!mrb) * { * // handle error * } * * // Creates new Ruby strings. * str1 = mrb_str_new_lit(mrb, "abc"); * str2 = mrb_str_new_lit(mrb, "def"); * * // Concatenates str2 to str1. * mrb_str_concat(mrb, str1, str2); * * // Prints new Concatenated Ruby string. * mrb_p(mrb, str1); * * mrb_close(mrb); * return 0; * } * * * Result: * * => "abcdef" * * @param [mrb_state] mrb The current mruby state. * @param [mrb_value] self String to concatenate. * @param [mrb_value] other String to append to self. * @return [mrb_value] Returns a new String appending other to self. */ MRB_API void mrb_str_concat(mrb_state*, mrb_value, mrb_value); /* * Adds two strings together. * * * Example: * * !!!c * int * main(int argc, * char **argv) * { * // Variable declarations. * mrb_value a; * mrb_value b; * mrb_value c; * * mrb_state *mrb = mrb_open(); * if (!mrb) * { * // handle error * } * * // Creates two Ruby strings from the passed in C strings. * a = mrb_str_new_lit(mrb, "abc"); * b = mrb_str_new_lit(mrb, "def"); * * // Prints both C strings. * mrb_p(mrb, a); * mrb_p(mrb, b); * * // Concatenates both Ruby strings. * c = mrb_str_plus(mrb, a, b); * * // Prints new Concatenated Ruby string. * mrb_p(mrb, c); * * mrb_close(mrb); * return 0; * } * * * Result: * * => "abc" # First string * => "def" # Second string * => "abcdef" # First & Second concatenated. * * @param [mrb_state] mrb The current mruby state. * @param [mrb_value] a First string to concatenate. * @param [mrb_value] b Second string to concatenate. * @return [mrb_value] Returns a new String containing a concatenated to b. */ MRB_API mrb_value mrb_str_plus(mrb_state*, mrb_value, mrb_value); /* * Converts pointer into a Ruby string. * * @param [mrb_state] mrb The current mruby state. * @param [void*] p The pointer to convert to Ruby string. * @return [mrb_value] Returns a new Ruby String. */ MRB_API mrb_value mrb_ptr_to_str(mrb_state *, void*); /* * Returns an object as a Ruby string. * * @param [mrb_state] mrb The current mruby state. * @param [mrb_value] obj An object to return as a Ruby string. * @return [mrb_value] An object as a Ruby string. */ MRB_API mrb_value mrb_obj_as_string(mrb_state *mrb, mrb_value obj); /* * Resizes the string's length. Returns the amount of characters * in the specified by len. * * Example: * * !!!c * int * main(int argc, * char **argv) * { * // Variable declaration. * mrb_value str; * * mrb_state *mrb = mrb_open(); * if (!mrb) * { * // handle error * } * // Creates a new string. * str = mrb_str_new_lit(mrb, "Hello, world!"); * // Returns 5 characters of * mrb_str_resize(mrb, str, 5); * mrb_p(mrb, str); * * mrb_close(mrb); * return 0; * } * * Result: * * => "Hello" * * @param [mrb_state] mrb The current mruby state. * @param [mrb_value] str The Ruby string to resize. * @param [mrb_value] len The length. * @return [mrb_value] An object as a Ruby string. */ MRB_API mrb_value mrb_str_resize(mrb_state *mrb, mrb_value str, mrb_int len); /* * Returns a sub string. * * Example: * * !!!c * int * main(int argc, * char const **argv) * { * // Variable declarations. * mrb_value str1; * mrb_value str2; * * mrb_state *mrb = mrb_open(); * if (!mrb) * { * // handle error * } * // Creates new string. * str1 = mrb_str_new_lit(mrb, "Hello, world!"); * // Returns a sub-string within the range of 0..2 * str2 = mrb_str_substr(mrb, str1, 0, 2); * * // Prints sub-string. * mrb_p(mrb, str2); * * mrb_close(mrb); * return 0; * } * * Result: * * => "He" * * @param [mrb_state] mrb The current mruby state. * @param [mrb_value] str Ruby string. * @param [mrb_int] beg The beginning point of the sub-string. * @param [mrb_int] len The end point of the sub-string. * @return [mrb_value] An object as a Ruby sub-string. */ MRB_API mrb_value mrb_str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len); /* * Returns a Ruby string type. * * * @param [mrb_state] mrb The current mruby state. * @param [mrb_value] str Ruby string. * @return [mrb_value] A Ruby string. */ MRB_API mrb_value mrb_ensure_string_type(mrb_state *mrb, mrb_value str); MRB_API mrb_value mrb_check_string_type(mrb_state *mrb, mrb_value str); /* obsolete: use mrb_ensure_string_type() instead */ MRB_API mrb_value mrb_string_type(mrb_state *mrb, mrb_value str); MRB_API mrb_value mrb_str_new_capa(mrb_state *mrb, size_t capa); MRB_API mrb_value mrb_str_buf_new(mrb_state *mrb, size_t capa); MRB_API const char *mrb_string_value_cstr(mrb_state *mrb, mrb_value *ptr); MRB_API const char *mrb_string_value_ptr(mrb_state *mrb, mrb_value str); /* * Returns the length of the Ruby string. * * * @param [mrb_state] mrb The current mruby state. * @param [mrb_value] str Ruby string. * @return [mrb_int] The length of the passed in Ruby string. */ MRB_API mrb_int mrb_string_value_len(mrb_state *mrb, mrb_value str); /* * Duplicates a string object. * * * @param [mrb_state] mrb The current mruby state. * @param [mrb_value] str Ruby string. * @return [mrb_value] Duplicated Ruby string. */ MRB_API mrb_value mrb_str_dup(mrb_state *mrb, mrb_value str); /* * Returns a symbol from a passed in Ruby string. * * @param [mrb_state] mrb The current mruby state. * @param [mrb_value] self Ruby string. * @return [mrb_value] A symbol. */ MRB_API mrb_value mrb_str_intern(mrb_state *mrb, mrb_value self); MRB_API mrb_value mrb_str_to_inum(mrb_state *mrb, mrb_value str, mrb_int base, mrb_bool badcheck); MRB_API double mrb_str_to_dbl(mrb_state *mrb, mrb_value str, mrb_bool badcheck); /* * Returns a converted string type. * For type checking, non converting `mrb_to_str` is recommended. */ MRB_API mrb_value mrb_str_to_str(mrb_state *mrb, mrb_value str); /* * Returns true if the strings match and false if the strings don't match. * * @param [mrb_state] mrb The current mruby state. * @param [mrb_value] str1 Ruby string to compare. * @param [mrb_value] str2 Ruby string to compare. * @return [mrb_value] boolean value. */ MRB_API mrb_bool mrb_str_equal(mrb_state *mrb, mrb_value str1, mrb_value str2); /* * Returns a concated string comprised of a Ruby string and a C string. * * @param [mrb_state] mrb The current mruby state. * @param [mrb_value] str Ruby string. * @param [const char *] ptr A C string. * @param [size_t] len length of C string. * @return [mrb_value] A Ruby string. * @see mrb_str_cat_cstr */ MRB_API mrb_value mrb_str_cat(mrb_state *mrb, mrb_value str, const char *ptr, size_t len); /* * Returns a concated string comprised of a Ruby string and a C string. * * @param [mrb_state] mrb The current mruby state. * @param [mrb_value] str Ruby string. * @param [const char *] ptr A C string. * @return [mrb_value] A Ruby string. * @see mrb_str_cat */ MRB_API mrb_value mrb_str_cat_cstr(mrb_state *mrb, mrb_value str, const char *ptr); MRB_API mrb_value mrb_str_cat_str(mrb_state *mrb, mrb_value str, mrb_value str2); #define mrb_str_cat_lit(mrb, str, lit) mrb_str_cat(mrb, str, lit, mrb_strlen_lit(lit)) /* * Adds str2 to the end of str1. */ MRB_API mrb_value mrb_str_append(mrb_state *mrb, mrb_value str, mrb_value str2); /* * Returns 0 if both Ruby strings are equal. Returns a value < 0 if Ruby str1 is less than Ruby str2. Returns a value > 0 if Ruby str2 is greater than Ruby str1. */ MRB_API int mrb_str_cmp(mrb_state *mrb, mrb_value str1, mrb_value str2); /* * Returns a newly allocated C string from a Ruby string. * This is an utility function to pass a Ruby string to C library functions. * * - Returned string does not contain any NUL characters (but terminator). * - It raises an ArgumentError exception if Ruby string contains * NUL characters. * - Retured string will be freed automatically on next GC. * - Caller can modify returned string without affecting Ruby string * (e.g. it can be used for mkstemp(3)). * * @param [mrb_state *] mrb The current mruby state. * @param [mrb_value] str Ruby string. Must be an instance of String. * @return [char *] A newly allocated C string. */ MRB_API char *mrb_str_to_cstr(mrb_state *mrb, mrb_value str); mrb_value mrb_str_pool(mrb_state *mrb, mrb_value str); uint32_t mrb_str_hash(mrb_state *mrb, mrb_value str); mrb_value mrb_str_dump(mrb_state *mrb, mrb_value str); /* * Returns a printable version of str, surrounded by quote marks, with special characters escaped. */ mrb_value mrb_str_inspect(mrb_state *mrb, mrb_value str); void mrb_noregexp(mrb_state *mrb, mrb_value self); void mrb_regexp_check(mrb_state *mrb, mrb_value obj); /* For backward compatibility */ #define mrb_str_cat2(mrb, str, ptr) mrb_str_cat_cstr(mrb, str, ptr) #define mrb_str_buf_cat(mrb, str, ptr, len) mrb_str_cat(mrb, str, ptr, len) #define mrb_str_buf_append(mrb, str, str2) mrb_str_cat_str(mrb, str, str2) MRB_END_DECL #endif /* MRUBY_STRING_H */ mruby-2.0.0/include/mruby/throw.h000066400000000000000000000023011340361412400167360ustar00rootroot00000000000000/* ** mruby/throw.h - mruby exception throwing handler ** ** See Copyright Notice in mruby.h */ #ifndef MRB_THROW_H #define MRB_THROW_H #if defined(MRB_ENABLE_CXX_ABI) # if !defined(__cplusplus) # error Trying to use C++ exception handling in C code # endif #endif #if defined(MRB_ENABLE_CXX_EXCEPTION) && defined(__cplusplus) #define MRB_TRY(buf) try { #define MRB_CATCH(buf) } catch(mrb_jmpbuf_impl e) { if (e != (buf)->impl) { throw e; } #define MRB_END_EXC(buf) } #define MRB_THROW(buf) throw((buf)->impl) typedef mrb_int mrb_jmpbuf_impl; #else #include #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) #define MRB_SETJMP _setjmp #define MRB_LONGJMP _longjmp #else #define MRB_SETJMP setjmp #define MRB_LONGJMP longjmp #endif #define MRB_TRY(buf) if (MRB_SETJMP((buf)->impl) == 0) { #define MRB_CATCH(buf) } else { #define MRB_END_EXC(buf) } #define MRB_THROW(buf) MRB_LONGJMP((buf)->impl, 1); #define mrb_jmpbuf_impl jmp_buf #endif struct mrb_jmpbuf { mrb_jmpbuf_impl impl; #if defined(MRB_ENABLE_CXX_EXCEPTION) && defined(__cplusplus) static mrb_int jmpbuf_id; mrb_jmpbuf() : impl(jmpbuf_id++) {} #endif }; #endif /* MRB_THROW_H */ mruby-2.0.0/include/mruby/value.h000066400000000000000000000152001340361412400167110ustar00rootroot00000000000000/* ** mruby/value.h - mruby value definitions ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_VALUE_H #define MRUBY_VALUE_H #include "common.h" /** * MRuby Value definition functions and macros. */ MRB_BEGIN_DECL typedef uint32_t mrb_sym; typedef uint8_t mrb_bool; struct mrb_state; #if defined(MRB_INT16) && defined(MRB_INT64) # error "You can't define MRB_INT16 and MRB_INT64 at the same time." #endif #if defined _MSC_VER && _MSC_VER < 1800 # define PRIo64 "llo" # define PRId64 "lld" # define PRIx64 "llx" # define PRIo16 "ho" # define PRId16 "hd" # define PRIx16 "hx" # define PRIo32 "o" # define PRId32 "d" # define PRIx32 "x" #else # include #endif #if defined(MRB_INT64) typedef int64_t mrb_int; # define MRB_INT_BIT 64 # define MRB_INT_MIN (INT64_MIN>>MRB_FIXNUM_SHIFT) # define MRB_INT_MAX (INT64_MAX>>MRB_FIXNUM_SHIFT) # define MRB_PRIo PRIo64 # define MRB_PRId PRId64 # define MRB_PRIx PRIx64 #elif defined(MRB_INT16) typedef int16_t mrb_int; # define MRB_INT_BIT 16 # define MRB_INT_MIN (INT16_MIN>>MRB_FIXNUM_SHIFT) # define MRB_INT_MAX (INT16_MAX>>MRB_FIXNUM_SHIFT) # define MRB_PRIo PRIo16 # define MRB_PRId PRId16 # define MRB_PRIx PRIx16 #else typedef int32_t mrb_int; # define MRB_INT_BIT 32 # define MRB_INT_MIN (INT32_MIN>>MRB_FIXNUM_SHIFT) # define MRB_INT_MAX (INT32_MAX>>MRB_FIXNUM_SHIFT) # define MRB_PRIo PRIo32 # define MRB_PRId PRId32 # define MRB_PRIx PRIx32 #endif #ifndef MRB_WITHOUT_FLOAT MRB_API double mrb_float_read(const char*, char**); #ifdef MRB_USE_FLOAT typedef float mrb_float; #else typedef double mrb_float; #endif #endif #if defined _MSC_VER && _MSC_VER < 1900 # ifndef __cplusplus # define inline __inline # endif # include MRB_API int mrb_msvc_vsnprintf(char *s, size_t n, const char *format, va_list arg); MRB_API int mrb_msvc_snprintf(char *s, size_t n, const char *format, ...); # define vsnprintf(s, n, format, arg) mrb_msvc_vsnprintf(s, n, format, arg) # define snprintf(s, n, format, ...) mrb_msvc_snprintf(s, n, format, __VA_ARGS__) # if _MSC_VER < 1800 && !defined MRB_WITHOUT_FLOAT # include # define isfinite(n) _finite(n) # define isnan _isnan # define isinf(n) (!_finite(n) && !_isnan(n)) # define signbit(n) (_copysign(1.0, (n)) < 0.0) static const unsigned int IEEE754_INFINITY_BITS_SINGLE = 0x7F800000; # define INFINITY (*(float *)&IEEE754_INFINITY_BITS_SINGLE) # define NAN ((float)(INFINITY - INFINITY)) # endif #endif enum mrb_vtype { MRB_TT_FALSE = 0, /* 0 */ MRB_TT_FREE, /* 1 */ MRB_TT_TRUE, /* 2 */ MRB_TT_FIXNUM, /* 3 */ MRB_TT_SYMBOL, /* 4 */ MRB_TT_UNDEF, /* 5 */ MRB_TT_FLOAT, /* 6 */ MRB_TT_CPTR, /* 7 */ MRB_TT_OBJECT, /* 8 */ MRB_TT_CLASS, /* 9 */ MRB_TT_MODULE, /* 10 */ MRB_TT_ICLASS, /* 11 */ MRB_TT_SCLASS, /* 12 */ MRB_TT_PROC, /* 13 */ MRB_TT_ARRAY, /* 14 */ MRB_TT_HASH, /* 15 */ MRB_TT_STRING, /* 16 */ MRB_TT_RANGE, /* 17 */ MRB_TT_EXCEPTION, /* 18 */ MRB_TT_FILE, /* 19 */ MRB_TT_ENV, /* 20 */ MRB_TT_DATA, /* 21 */ MRB_TT_FIBER, /* 22 */ MRB_TT_ISTRUCT, /* 23 */ MRB_TT_BREAK, /* 24 */ MRB_TT_MAXDEFINE /* 25 */ }; #include #ifdef MRB_DOCUMENTATION_BLOCK /** * @abstract * MRuby value boxing. * * Actual implementation depends on configured boxing type. * * @see mruby/boxing_no.h Default boxing representation * @see mruby/boxing_word.h Word representation * @see mruby/boxing_nan.h Boxed double representation */ typedef void mrb_value; #endif #if defined(MRB_NAN_BOXING) #include "boxing_nan.h" #elif defined(MRB_WORD_BOXING) #include "boxing_word.h" #else #include "boxing_no.h" #endif #ifndef mrb_fixnum_p #define mrb_fixnum_p(o) (mrb_type(o) == MRB_TT_FIXNUM) #endif #ifndef mrb_undef_p #define mrb_undef_p(o) (mrb_type(o) == MRB_TT_UNDEF) #endif #ifndef mrb_nil_p #define mrb_nil_p(o) (mrb_type(o) == MRB_TT_FALSE && !mrb_fixnum(o)) #endif #ifndef mrb_bool #define mrb_bool(o) (mrb_type(o) != MRB_TT_FALSE) #endif #ifndef MRB_WITHOUT_FLOAT #define mrb_float_p(o) (mrb_type(o) == MRB_TT_FLOAT) #endif #define mrb_symbol_p(o) (mrb_type(o) == MRB_TT_SYMBOL) #define mrb_array_p(o) (mrb_type(o) == MRB_TT_ARRAY) #define mrb_string_p(o) (mrb_type(o) == MRB_TT_STRING) #define mrb_hash_p(o) (mrb_type(o) == MRB_TT_HASH) #define mrb_cptr_p(o) (mrb_type(o) == MRB_TT_CPTR) #define mrb_exception_p(o) (mrb_type(o) == MRB_TT_EXCEPTION) #define mrb_test(o) mrb_bool(o) MRB_API mrb_bool mrb_regexp_p(struct mrb_state*, mrb_value); /* * Returns a float in Ruby. */ #ifndef MRB_WITHOUT_FLOAT MRB_INLINE mrb_value mrb_float_value(struct mrb_state *mrb, mrb_float f) { mrb_value v; (void) mrb; SET_FLOAT_VALUE(mrb, v, f); return v; } #endif static inline mrb_value mrb_cptr_value(struct mrb_state *mrb, void *p) { mrb_value v; (void) mrb; SET_CPTR_VALUE(mrb,v,p); return v; } /* * Returns a fixnum in Ruby. */ MRB_INLINE mrb_value mrb_fixnum_value(mrb_int i) { mrb_value v; SET_INT_VALUE(v, i); return v; } static inline mrb_value mrb_symbol_value(mrb_sym i) { mrb_value v; SET_SYM_VALUE(v, i); return v; } static inline mrb_value mrb_obj_value(void *p) { mrb_value v; SET_OBJ_VALUE(v, (struct RBasic*)p); mrb_assert(p == mrb_ptr(v)); mrb_assert(((struct RBasic*)p)->tt == mrb_type(v)); return v; } /* * Get a nil mrb_value object. * * @return * nil mrb_value object reference. */ MRB_INLINE mrb_value mrb_nil_value(void) { mrb_value v; SET_NIL_VALUE(v); return v; } /* * Returns false in Ruby. */ MRB_INLINE mrb_value mrb_false_value(void) { mrb_value v; SET_FALSE_VALUE(v); return v; } /* * Returns true in Ruby. */ MRB_INLINE mrb_value mrb_true_value(void) { mrb_value v; SET_TRUE_VALUE(v); return v; } static inline mrb_value mrb_bool_value(mrb_bool boolean) { mrb_value v; SET_BOOL_VALUE(v, boolean); return v; } static inline mrb_value mrb_undef_value(void) { mrb_value v; SET_UNDEF_VALUE(v); return v; } #ifdef MRB_USE_ETEXT_EDATA #if (defined(__APPLE__) && defined(__MACH__)) #include static inline mrb_bool mrb_ro_data_p(const char *p) { return (const char*)get_etext() < p && p < (const char*)get_edata(); } #else extern char _etext[]; #ifdef MRB_NO_INIT_ARRAY_START extern char _edata[]; static inline mrb_bool mrb_ro_data_p(const char *p) { return _etext < p && p < _edata; } #else extern char __init_array_start[]; static inline mrb_bool mrb_ro_data_p(const char *p) { return _etext < p && p < (char*)&__init_array_start; } #endif #endif #else # define mrb_ro_data_p(p) FALSE #endif MRB_END_DECL #endif /* MRUBY_VALUE_H */ mruby-2.0.0/include/mruby/variable.h000066400000000000000000000104101340361412400173600ustar00rootroot00000000000000/* ** mruby/variable.h - mruby variables ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_VARIABLE_H #define MRUBY_VARIABLE_H #include "common.h" /** * Functions to access mruby variables. */ MRB_BEGIN_DECL typedef struct global_variable { int counter; mrb_value *data; mrb_value (*getter)(void); void (*setter)(void); /* void (*marker)(); */ /* int block_trace; */ /* struct trace_var *trace; */ } global_variable; struct global_entry { global_variable *var; mrb_sym id; }; mrb_value mrb_vm_special_get(mrb_state*, mrb_sym); void mrb_vm_special_set(mrb_state*, mrb_sym, mrb_value); mrb_value mrb_vm_cv_get(mrb_state*, mrb_sym); void mrb_vm_cv_set(mrb_state*, mrb_sym, mrb_value); mrb_value mrb_vm_const_get(mrb_state*, mrb_sym); void mrb_vm_const_set(mrb_state*, mrb_sym, mrb_value); MRB_API mrb_value mrb_const_get(mrb_state*, mrb_value, mrb_sym); MRB_API void mrb_const_set(mrb_state*, mrb_value, mrb_sym, mrb_value); MRB_API mrb_bool mrb_const_defined(mrb_state*, mrb_value, mrb_sym); MRB_API void mrb_const_remove(mrb_state*, mrb_value, mrb_sym); MRB_API mrb_bool mrb_iv_name_sym_p(mrb_state *mrb, mrb_sym sym); MRB_API void mrb_iv_name_sym_check(mrb_state *mrb, mrb_sym sym); MRB_API mrb_value mrb_obj_iv_get(mrb_state *mrb, struct RObject *obj, mrb_sym sym); MRB_API void mrb_obj_iv_set(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v); MRB_API mrb_bool mrb_obj_iv_defined(mrb_state *mrb, struct RObject *obj, mrb_sym sym); MRB_API mrb_value mrb_iv_get(mrb_state *mrb, mrb_value obj, mrb_sym sym); MRB_API void mrb_iv_set(mrb_state *mrb, mrb_value obj, mrb_sym sym, mrb_value v); MRB_API mrb_bool mrb_iv_defined(mrb_state*, mrb_value, mrb_sym); MRB_API mrb_value mrb_iv_remove(mrb_state *mrb, mrb_value obj, mrb_sym sym); MRB_API void mrb_iv_copy(mrb_state *mrb, mrb_value dst, mrb_value src); MRB_API mrb_bool mrb_const_defined_at(mrb_state *mrb, mrb_value mod, mrb_sym id); /** * Get a global variable. Will return nil if the var does not exist * * Example: * * !!!ruby * # Ruby style * var = $value * * !!!c * // C style * mrb_sym sym = mrb_intern_lit(mrb, "$value"); * mrb_value var = mrb_gv_get(mrb, sym); * * @param mrb The mruby state reference * @param sym The name of the global variable * @return The value of that global variable. May be nil */ MRB_API mrb_value mrb_gv_get(mrb_state *mrb, mrb_sym sym); /** * Set a global variable * * Example: * * !!!ruby * # Ruby style * $value = "foo" * * !!!c * // C style * mrb_sym sym = mrb_intern_lit(mrb, "$value"); * mrb_gv_set(mrb, sym, mrb_str_new_lit("foo")); * * @param mrb The mruby state reference * @param sym The name of the global variable * @param val The value of the global variable */ MRB_API void mrb_gv_set(mrb_state *mrb, mrb_sym sym, mrb_value val); /** * Remove a global variable. * * Example: * * !!!ruby * # Ruby style * $value = nil * * !!!c * // C style * mrb_sym sym = mrb_intern_lit(mrb, "$value"); * mrb_gv_remove(mrb, sym); * * @param mrb The mruby state reference * @param sym The name of the global variable * @param val The value of the global variable */ MRB_API void mrb_gv_remove(mrb_state *mrb, mrb_sym sym); MRB_API mrb_value mrb_cv_get(mrb_state *mrb, mrb_value mod, mrb_sym sym); MRB_API void mrb_mod_cv_set(mrb_state *mrb, struct RClass * c, mrb_sym sym, mrb_value v); MRB_API void mrb_cv_set(mrb_state *mrb, mrb_value mod, mrb_sym sym, mrb_value v); MRB_API mrb_bool mrb_cv_defined(mrb_state *mrb, mrb_value mod, mrb_sym sym); mrb_value mrb_obj_iv_inspect(mrb_state*, struct RObject*); mrb_value mrb_mod_constants(mrb_state *mrb, mrb_value mod); mrb_value mrb_f_global_variables(mrb_state *mrb, mrb_value self); mrb_value mrb_obj_instance_variables(mrb_state*, mrb_value); mrb_value mrb_mod_class_variables(mrb_state*, mrb_value); mrb_value mrb_mod_cv_get(mrb_state *mrb, struct RClass * c, mrb_sym sym); mrb_bool mrb_mod_cv_defined(mrb_state *mrb, struct RClass * c, mrb_sym sym); /* GC functions */ void mrb_gc_mark_gv(mrb_state*); void mrb_gc_free_gv(mrb_state*); void mrb_gc_mark_iv(mrb_state*, struct RObject*); size_t mrb_gc_mark_iv_size(mrb_state*, struct RObject*); void mrb_gc_free_iv(mrb_state*, struct RObject*); MRB_END_DECL #endif /* MRUBY_VARIABLE_H */ mruby-2.0.0/include/mruby/version.h000066400000000000000000000036541340361412400172740ustar00rootroot00000000000000/* ** mruby/version.h - mruby version definition ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_VERSION_H #define MRUBY_VERSION_H #include "common.h" /** * mruby version definition macros */ MRB_BEGIN_DECL /* * A passed in expression. */ #define MRB_STRINGIZE0(expr) #expr /* * Passes in an expression to MRB_STRINGIZE0. */ #define MRB_STRINGIZE(expr) MRB_STRINGIZE0(expr) /* * The version of Ruby used by mruby. */ #define MRUBY_RUBY_VERSION "2.0" /* * Ruby engine. */ #define MRUBY_RUBY_ENGINE "mruby" /* * Major release version number. */ #define MRUBY_RELEASE_MAJOR 2 /* * Minor release version number. */ #define MRUBY_RELEASE_MINOR 0 /* * Tiny release version number. */ #define MRUBY_RELEASE_TEENY 0 /* * The mruby version. */ #define MRUBY_VERSION MRB_STRINGIZE(MRUBY_RELEASE_MAJOR) "." MRB_STRINGIZE(MRUBY_RELEASE_MINOR) "." MRB_STRINGIZE(MRUBY_RELEASE_TEENY) /* * Release number. */ #define MRUBY_RELEASE_NO (MRUBY_RELEASE_MAJOR * 100 * 100 + MRUBY_RELEASE_MINOR * 100 + MRUBY_RELEASE_TEENY) /* * Release year. */ #define MRUBY_RELEASE_YEAR 2018 /* * Release month. */ #define MRUBY_RELEASE_MONTH 12 /* * Release day. */ #define MRUBY_RELEASE_DAY 11 /* * Release date as a string. */ #define MRUBY_RELEASE_DATE MRB_STRINGIZE(MRUBY_RELEASE_YEAR) "-" MRB_STRINGIZE(MRUBY_RELEASE_MONTH) "-" MRB_STRINGIZE(MRUBY_RELEASE_DAY) /* * The year mruby was first created. */ #define MRUBY_BIRTH_YEAR 2010 /* * MRuby's authors. */ #define MRUBY_AUTHOR "mruby developers" /* * mruby's version, and release date. */ #define MRUBY_DESCRIPTION \ "mruby " MRUBY_VERSION \ " (" MRUBY_RELEASE_DATE ") " \ /* * mruby's copyright information. */ #define MRUBY_COPYRIGHT \ "mruby - Copyright (c) " \ MRB_STRINGIZE(MRUBY_BIRTH_YEAR)"-" \ MRB_STRINGIZE(MRUBY_RELEASE_YEAR)" " \ MRUBY_AUTHOR \ MRB_END_DECL #endif /* MRUBY_VERSION_H */ mruby-2.0.0/lib/000077500000000000000000000000001340361412400134135ustar00rootroot00000000000000mruby-2.0.0/lib/mruby-core-ext.rb000066400000000000000000000031401340361412400166200ustar00rootroot00000000000000class Object class << self def attr_block(*syms) syms.flatten.each do |sym| class_eval "def #{sym}(&block);block.call(@#{sym}) if block_given?;@#{sym};end" end end end end class String def relative_path_from(dir) Pathname.new(File.expand_path(self)).relative_path_from(Pathname.new(File.expand_path(dir))).to_s end def relative_path relative_path_from(Dir.pwd) end # Compatible with 1.9 on 1.8 def %(params) if params.is_a?(Hash) str = self.clone params.each do |k, v| str.gsub!("%{#{k}}") { v } end str else if params.is_a?(Array) sprintf(self, *params) else sprintf(self, params) end end end end class Symbol # Compatible with 1.9 on 1.8 def to_proc proc { |obj, *args| obj.send(self, *args) } end end module Enumerable # Compatible with 1.9 on 1.8 def each_with_object(memo) return to_enum :each_with_object, memo unless block_given? each { |obj| yield obj, memo } memo end end $pp_show = true if $verbose.nil? if Rake.respond_to?(:verbose) && !Rake.verbose.nil? if Rake.verbose.class == TrueClass # verbose message logging $pp_show = false else $pp_show = true Rake.verbose(false) end else # could not identify rake version $pp_show = false end else $pp_show = false if $verbose end def _pp(cmd, src, tgt=nil, options={}) return unless $pp_show width = 5 template = options[:indent] ? "%#{width*options[:indent]}s %s %s" : "%-#{width}s %s %s" puts template % [cmd, src, tgt ? "-> #{tgt}" : nil] end mruby-2.0.0/lib/mruby/000077500000000000000000000000001340361412400145515ustar00rootroot00000000000000mruby-2.0.0/lib/mruby/build.rb000066400000000000000000000236061340361412400162040ustar00rootroot00000000000000require "mruby/build/load_gems" require "mruby/build/command" module MRuby class << self def targets @targets ||= {} end def each_target(&block) return to_enum(:each_target) if block.nil? @targets.each do |key, target| target.instance_eval(&block) end end end class Toolchain class << self attr_accessor :toolchains end def initialize(name, &block) @name, @initializer = name.to_s, block MRuby::Toolchain.toolchains ||= {} MRuby::Toolchain.toolchains[@name] = self end def setup(conf,params={}) conf.instance_exec(conf, params, &@initializer) end def self.load Dir.glob("#{MRUBY_ROOT}/tasks/toolchains/*.rake").each do |file| Kernel.load file end end end Toolchain.load class Build class << self attr_accessor :current end include Rake::DSL include LoadGems attr_accessor :name, :bins, :exts, :file_separator, :build_dir, :gem_clone_dir attr_reader :libmruby_objs, :gems, :toolchains attr_writer :enable_bintest, :enable_test alias libmruby libmruby_objs COMPILERS = %w(cc cxx objc asm) COMMANDS = COMPILERS + %w(linker archiver yacc gperf git exts mrbc) attr_block MRuby::Build::COMMANDS Exts = Struct.new(:object, :executable, :library) def initialize(name='host', build_dir=nil, &block) @name = name.to_s unless MRuby.targets[@name] if ENV['OS'] == 'Windows_NT' @exts = Exts.new('.o', '.exe', '.a') else @exts = Exts.new('.o', '', '.a') end build_dir = build_dir || ENV['MRUBY_BUILD_DIR'] || "#{MRUBY_ROOT}/build" @file_separator = '/' @build_dir = "#{build_dir}/#{@name}" @gem_clone_dir = "#{build_dir}/mrbgems" @cc = Command::Compiler.new(self, %w(.c)) @cxx = Command::Compiler.new(self, %w(.cc .cxx .cpp)) @objc = Command::Compiler.new(self, %w(.m)) @asm = Command::Compiler.new(self, %w(.S .asm)) @linker = Command::Linker.new(self) @archiver = Command::Archiver.new(self) @yacc = Command::Yacc.new(self) @gperf = Command::Gperf.new(self) @git = Command::Git.new(self) @mrbc = Command::Mrbc.new(self) @bins = [] @gems, @libmruby_objs = MRuby::Gem::List.new, [] @build_mrbtest_lib_only = false @cxx_exception_enabled = false @cxx_exception_disabled = false @cxx_abi_enabled = false @enable_bintest = false @enable_test = false @toolchains = [] MRuby.targets[@name] = self end MRuby::Build.current = MRuby.targets[@name] MRuby.targets[@name].instance_eval(&block) build_mrbc_exec if name == 'host' build_mrbtest if test_enabled? end def debug_enabled? @enable_debug end def enable_debug compilers.each do |c| c.defines += %w(MRB_DEBUG) if toolchains.any? { |toolchain| toolchain == "gcc" } c.flags += %w(-g3 -O0) end end @mrbc.compile_options += ' -g' @enable_debug = true end def disable_cxx_exception if @cxx_exception_enabled or @cxx_abi_enabled raise "cxx_exception already enabled" end @cxx_exception_disabled = true end def enable_cxx_exception return if @cxx_exception_enabled return if @cxx_abi_enabled if @cxx_exception_disabled raise "cxx_exception disabled" end @cxx_exception_enabled = true compilers.each { |c| c.defines += %w(MRB_ENABLE_CXX_EXCEPTION) c.flags << c.cxx_exception_flag } linker.command = cxx.command if toolchains.find { |v| v == 'gcc' } end def cxx_exception_enabled? @cxx_exception_enabled end def cxx_abi_enabled? @cxx_abi_enabled end def enable_cxx_abi return if @cxx_abi_enabled if @cxx_exception_enabled raise "cxx_exception already enabled" end compilers.each { |c| c.defines += %w(MRB_ENABLE_CXX_EXCEPTION MRB_ENABLE_CXX_ABI) c.flags << c.cxx_compile_flag } compilers.each { |c| c.flags << c.cxx_compile_flag } linker.command = cxx.command if toolchains.find { |v| v == 'gcc' } @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| FileUtils.mkdir_p File.dirname t.name IO.write t.name, < 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 def bintest_enabled? @enable_bintest end def toolchain(name, params={}) tc = Toolchain.toolchains[name.to_s] fail "Unknown #{name} toolchain" unless tc tc.setup(self, params) @toolchains.unshift name.to_s end def primary_toolchain @toolchains.first end def root MRUBY_ROOT end def enable_test @enable_test = true end def test_enabled? @enable_test end def build_mrbtest gem :core => 'mruby-test' end def build_mrbc_exec gem :core => 'mruby-bin-mrbc' end def mrbcfile 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 COMPILERS.map do |c| instance_variable_get("@#{c}") end end def define_rules compilers.each do |compiler| if respond_to?(:enable_gems?) && enable_gems? compiler.defines -= %w(DISABLE_GEMS) else compiler.defines += %w(DISABLE_GEMS) end compiler.define_rules build_dir, File.expand_path(File.join(File.dirname(__FILE__), '..', '..')) end end def filename(name) if name.is_a?(Array) name.flatten.map { |n| filename(n) } else '"%s"' % name.gsub('/', file_separator) end end def cygwin_filename(name) if name.is_a?(Array) name.flatten.map { |n| cygwin_filename(n) } else '"%s"' % `cygpath -w "#{filename(name)}"`.strip end end def exefile(name) if name.is_a?(Array) name.flatten.map { |n| exefile(n) } else "#{name}#{exts.executable}" end end def objfile(name) if name.is_a?(Array) name.flatten.map { |n| objfile(n) } else "#{name}#{exts.object}" end end def libfile(name) if name.is_a?(Array) name.flatten.map { |n| libfile(n) } else "#{name}#{exts.library}" end end def build_mrbtest_lib_only @build_mrbtest_lib_only = true end def build_mrbtest_lib_only? @build_mrbtest_lib_only end def verbose_flag $verbose ? ' -v' : '' end def run_test puts ">>> Test #{name} <<<" mrbtest = exefile("#{build_dir}/bin/mrbtest") sh "#{filename mrbtest.relative_path}#{verbose_flag}" puts end def run_bintest targets = @gems.select { |v| File.directory? "#{v.dir}/bintest" }.map { |v| filename v.dir } targets << filename(".") if File.directory? "./bintest" sh "ruby test/bintest.rb#{verbose_flag} #{targets.join ' '}" end def print_build_summary puts "================================================" puts " Config Name: #{@name}" puts " Output Directory: #{self.build_dir.relative_path}" puts " Binaries: #{@bins.join(', ')}" unless @bins.empty? unless @gems.empty? puts " Included Gems:" @gems.map do |gem| gem_version = " - #{gem.version}" if gem.version != '0.0.0' gem_summary = " - #{gem.summary}" if gem.summary puts " #{gem.name}#{gem_version}#{gem_summary}" puts " - Binaries: #{gem.bins.join(', ')}" unless gem.bins.empty? end end puts "================================================" puts end def libmruby_static libfile("#{build_dir}/lib/libmruby") end def libmruby_core_static libfile("#{build_dir}/lib/libmruby_core") end def libraries [libmruby_static] end end # Build class CrossBuild < Build attr_block %w(test_runner) # cross compiling targets for building native extensions. # host - arch of where the built binary will run # build - arch of the machine building the binary attr_accessor :host_target, :build_target def initialize(name, build_dir=nil, &block) @endian = nil @test_runner = Command::CrossTestRunner.new(self) super end def mrbcfile MRuby.targets['host'].exefile("#{MRuby.targets['host'].build_dir}/bin/mrbc") end def run_test @test_runner.runner_options << ' -v' if $verbose mrbtest = exefile("#{build_dir}/bin/mrbtest") if (@test_runner.command == nil) puts "You should run #{mrbtest} on target device." puts else @test_runner.run(mrbtest) end end def big_endian if @endian puts "Endian has already specified as #{@endian}." return end @endian = :big @mrbc.compile_options += ' -E' compilers.each do |c| c.defines += %w(MRB_ENDIAN_BIG) end end def little_endian if @endian puts "Endian has already specified as #{@endian}." return end @endian = :little @mrbc.compile_options += ' -e' end end # CrossBuild end # MRuby mruby-2.0.0/lib/mruby/build/000077500000000000000000000000001340361412400156505ustar00rootroot00000000000000mruby-2.0.0/lib/mruby/build/command.rb000066400000000000000000000245701340361412400176230ustar00rootroot00000000000000require 'forwardable' module MRuby class Command include Rake::DSL extend Forwardable def_delegators :@build, :filename, :objfile, :libfile, :exefile, :cygwin_filename attr_accessor :build, :command def initialize(build) @build = build end # clone is deep clone without @build def clone target = super excepts = %w(@build) instance_variables.each do |attr| unless excepts.include?(attr.to_s) val = Marshal::load(Marshal.dump(instance_variable_get(attr))) # deep clone target.instance_variable_set(attr, val) end end target end NotFoundCommands = {} private def _run(options, params={}) return sh command + ' ' + ( options % params ) if NotFoundCommands.key? @command begin sh build.filename(command) + ' ' + ( options % params ) rescue RuntimeError NotFoundCommands[@command] = true _run options, params end end end class Command::Compiler < Command attr_accessor :flags, :include_paths, :defines, :source_exts attr_accessor :compile_options, :option_define, :option_include_path, :out_ext attr_accessor :cxx_compile_flag, :cxx_exception_flag def initialize(build, source_exts=[]) super(build) @command = ENV['CC'] || 'cc' @flags = [ENV['CFLAGS'] || []] @source_exts = source_exts @include_paths = ["#{MRUBY_ROOT}/include"] @defines = %w() @option_include_path = '-I%s' @option_define = '-D%s' @compile_options = '%{flags} -o %{outfile} -c %{infile}' end alias header_search_paths include_paths def search_header_path(name) header_search_paths.find do |v| File.exist? build.filename("#{v}/#{name}").sub(/^"(.*)"$/, '\1') end end def search_header(name) path = search_header_path name path && build.filename("#{path}/#{name}").sub(/^"(.*)"$/, '\1') end def all_flags(_defineds=[], _include_paths=[], _flags=[]) define_flags = [defines, _defineds].flatten.map{ |d| option_define % d } include_path_flags = [include_paths, _include_paths].flatten.map do |f| if MRUBY_BUILD_HOST_IS_CYGWIN option_include_path % cygwin_filename(f) else option_include_path % filename(f) end end [flags, define_flags, include_path_flags, _flags].flatten.join(' ') end def run(outfile, infile, _defineds=[], _include_paths=[], _flags=[]) FileUtils.mkdir_p File.dirname(outfile) _pp "CC", infile.relative_path, outfile.relative_path if MRUBY_BUILD_HOST_IS_CYGWIN _run compile_options, { :flags => all_flags(_defineds, _include_paths, _flags), :infile => cygwin_filename(infile), :outfile => cygwin_filename(outfile) } else _run compile_options, { :flags => all_flags(_defineds, _include_paths, _flags), :infile => filename(infile), :outfile => filename(outfile) } end end def define_rules(build_dir, source_dir='') @out_ext = build.exts.object gemrake = File.join(source_dir, "mrbgem.rake") rakedep = File.exist?(gemrake) ? [ gemrake ] : [] if build_dir.include? "mrbgems/" generated_file_matcher = Regexp.new("^#{Regexp.escape build_dir}/(.*)#{Regexp.escape out_ext}$") else generated_file_matcher = Regexp.new("^#{Regexp.escape build_dir}/(?!mrbgems/.+/)(.*)#{Regexp.escape out_ext}$") end source_exts.each do |ext, compile| rule generated_file_matcher => [ proc { |file| file.sub(generated_file_matcher, "#{source_dir}/\\1#{ext}") }, proc { |file| get_dependencies(file) + rakedep } ] do |t| run t.name, t.prerequisites.first end rule generated_file_matcher => [ proc { |file| file.sub(generated_file_matcher, "#{build_dir}/\\1#{ext}") }, proc { |file| get_dependencies(file) + rakedep } ] do |t| run t.name, t.prerequisites.first end end end private def get_dependencies(file) file = file.ext('d') unless File.extname(file) == '.d' if File.exist?(file) File.read(file).gsub("\\\n ", "").scan(/^\S+:\s+(.+)$/).flatten.map {|s| s.split(' ') }.flatten else [] end + [ MRUBY_CONFIG ] end end class Command::Linker < Command attr_accessor :flags, :library_paths, :flags_before_libraries, :libraries, :flags_after_libraries attr_accessor :link_options, :option_library, :option_library_path def initialize(build) super @command = ENV['LD'] || 'ld' @flags = (ENV['LDFLAGS'] || []) @flags_before_libraries, @flags_after_libraries = [], [] @libraries = [] @library_paths = [] @option_library = '-l%s' @option_library_path = '-L%s' @link_options = "%{flags} -o %{outfile} %{objs} %{flags_before_libraries} %{libs} %{flags_after_libraries}" end def all_flags(_library_paths=[], _flags=[]) library_path_flags = [library_paths, _library_paths].flatten.map do |f| if MRUBY_BUILD_HOST_IS_CYGWIN option_library_path % cygwin_filename(f) else option_library_path % filename(f) end end [flags, library_path_flags, _flags].flatten.join(' ') end def library_flags(_libraries) [libraries, _libraries].flatten.map{ |d| option_library % d }.join(' ') end def run(outfile, objfiles, _libraries=[], _library_paths=[], _flags=[], _flags_before_libraries=[], _flags_after_libraries=[]) FileUtils.mkdir_p File.dirname(outfile) library_flags = [libraries, _libraries].flatten.map { |d| option_library % d } _pp "LD", outfile.relative_path if MRUBY_BUILD_HOST_IS_CYGWIN _run link_options, { :flags => all_flags(_library_paths, _flags), :outfile => cygwin_filename(outfile) , :objs => cygwin_filename(objfiles).join(' '), :flags_before_libraries => [flags_before_libraries, _flags_before_libraries].flatten.join(' '), :flags_after_libraries => [flags_after_libraries, _flags_after_libraries].flatten.join(' '), :libs => library_flags.join(' ') } else _run link_options, { :flags => all_flags(_library_paths, _flags), :outfile => filename(outfile) , :objs => filename(objfiles).join(' '), :flags_before_libraries => [flags_before_libraries, _flags_before_libraries].flatten.join(' '), :flags_after_libraries => [flags_after_libraries, _flags_after_libraries].flatten.join(' '), :libs => library_flags.join(' ') } end end end class Command::Archiver < Command attr_accessor :archive_options def initialize(build) super @command = ENV['AR'] || 'ar' @archive_options = 'rs %{outfile} %{objs}' end def run(outfile, objfiles) FileUtils.mkdir_p File.dirname(outfile) _pp "AR", outfile.relative_path if MRUBY_BUILD_HOST_IS_CYGWIN _run archive_options, { :outfile => cygwin_filename(outfile), :objs => cygwin_filename(objfiles).join(' ') } else _run archive_options, { :outfile => filename(outfile), :objs => filename(objfiles).join(' ') } end end end class Command::Yacc < Command attr_accessor :compile_options def initialize(build) super @command = 'bison' @compile_options = '-o %{outfile} %{infile}' end def run(outfile, infile) FileUtils.mkdir_p File.dirname(outfile) _pp "YACC", infile.relative_path, outfile.relative_path _run compile_options, { :outfile => filename(outfile) , :infile => filename(infile) } end end class Command::Gperf < Command attr_accessor :compile_options def initialize(build) super @command = 'gperf' @compile_options = '-L ANSI-C -C -p -j1 -i 1 -g -o -t -N mrb_reserved_word -k"1,3,$" %{infile} > %{outfile}' end def run(outfile, infile) FileUtils.mkdir_p File.dirname(outfile) _pp "GPERF", infile.relative_path, outfile.relative_path _run compile_options, { :outfile => filename(outfile) , :infile => filename(infile) } end end class Command::Git < Command attr_accessor :flags attr_accessor :clone_options, :pull_options, :checkout_options def initialize(build) super @command = 'git' @flags = %w[] @clone_options = "clone %{flags} %{url} %{dir}" @pull_options = "pull" @checkout_options = "checkout %{checksum_hash}" end def run_clone(dir, url, _flags = []) _pp "GIT", url, dir.relative_path _run clone_options, { :flags => [flags, _flags].flatten.join(' '), :url => url, :dir => filename(dir) } end def run_pull(dir, url) root = Dir.pwd Dir.chdir dir _pp "GIT PULL", url, dir.relative_path _run pull_options Dir.chdir root end def run_checkout(dir, checksum_hash) root = Dir.pwd Dir.chdir dir _pp "GIT CHECKOUT", checksum_hash _run checkout_options, { :checksum_hash => checksum_hash } Dir.chdir root end end class Command::Mrbc < Command attr_accessor :compile_options def initialize(build) super @command = nil @compile_options = "-B%{funcname} -o-" end def run(out, infiles, funcname) @command ||= @build.mrbcfile infiles = [infiles].flatten infiles.each do |f| _pp "MRBC", f.relative_path, nil, :indent => 2 end IO.popen("#{filename @command} #{@compile_options % {:funcname => funcname}} #{filename(infiles).join(' ')}", 'r+') do |io| out.puts io.read end # if mrbc execution fail, drop the file if $?.exitstatus != 0 File.delete(out.path) exit(-1) end end end class Command::CrossTestRunner < Command attr_accessor :runner_options attr_accessor :verbose_flag attr_accessor :flags def initialize(build) super @command = nil @runner_options = '%{flags} %{infile}' @verbose_flag = '' @flags = [] end def run(testbinfile) puts "TEST for " + @build.name _run runner_options, { :flags => [flags, verbose_flag].flatten.join(' '), :infile => testbinfile } end end end mruby-2.0.0/lib/mruby/build/load_gems.rb000066400000000000000000000076071340361412400201410ustar00rootroot00000000000000module MRuby module LoadGems def gembox(gemboxfile) gembox = File.expand_path("#{gemboxfile}.gembox", "#{MRUBY_ROOT}/mrbgems") fail "Can't find gembox '#{gembox}'" unless File.exist?(gembox) GemBox.config = self GemBox.path = gembox instance_eval File.read(gembox) GemBox.path = nil end def gem(gemdir, &block) caller_dir = File.expand_path(File.dirname(/^(.*?):\d/.match(caller.first).to_a[1])) if gemdir.is_a?(Hash) gemdir = load_special_path_gem(gemdir) elsif GemBox.path && gemdir.is_a?(String) gemdir = File.expand_path(gemdir, File.dirname(GemBox.path)) else gemdir = File.expand_path(gemdir, caller_dir) end gemrake = File.join(gemdir, "mrbgem.rake") fail "Can't find #{gemrake}" unless File.exist?(gemrake) Gem.current = nil load gemrake return nil unless Gem.current Gem.current.dir = gemdir Gem.current.build = self.is_a?(MRuby::Build) ? self : MRuby::Build.current Gem.current.build_config_initializer = block gems << Gem.current cxx_srcs = ['src', 'test', 'tools'].map do |subdir| Dir.glob("#{Gem.current.dir}/#{subdir}/*.{cpp,cxx,cc}") end.flatten enable_cxx_exception unless cxx_srcs.empty? Gem.current end def load_special_path_gem(params) if params[:github] params[:git] = "https://github.com/#{params[:github]}.git" elsif params[:bitbucket] if params[:method] == "ssh" params[:git] = "git@bitbucket.org:#{params[:bitbucket]}.git" else params[:git] = "https://bitbucket.org/#{params[:bitbucket]}.git" end elsif params[:mgem] mgem_list_dir = "#{gem_clone_dir}/mgem-list" mgem_list_url = 'https://github.com/mruby/mgem-list.git' if File.exist? mgem_list_dir git.run_pull mgem_list_dir, mgem_list_url if $pull_gems else FileUtils.mkdir_p mgem_list_dir git.run_clone mgem_list_dir, mgem_list_url, "--depth 1" end require 'yaml' conf_path = "#{mgem_list_dir}/#{params[:mgem]}.gem" conf_path = "#{mgem_list_dir}/mruby-#{params[:mgem]}.gem" unless File.exist? conf_path fail "mgem not found: #{params[:mgem]}" unless File.exist? conf_path conf = YAML.load File.read conf_path fail "unknown mgem protocol: #{conf['protocol']}" if conf['protocol'] != 'git' params[:git] = conf['repository'] params[:branch] = conf['branch'] if conf['branch'] end if params[:core] gemdir = "#{root}/mrbgems/#{params[:core]}" elsif params[:git] url = params[:git] gemdir = "#{gem_clone_dir}/#{url.match(/([-\w]+)(\.[-\w]+|)$/).to_a[1]}" # by default the 'master' branch is used branch = params[:branch] ? params[:branch] : 'master' if File.exist?(gemdir) if $pull_gems git.run_pull gemdir, url else gemdir end else options = [params[:options]] || [] options << "--recursive" options << "--branch \"#{branch}\"" options << "--depth 1" unless params[:checksum_hash] FileUtils.mkdir_p "#{gem_clone_dir}" git.run_clone gemdir, url, options end if params[:checksum_hash] # Jump to the specified commit git.run_checkout gemdir, params[:checksum_hash] else # Jump to the top of the branch git.run_checkout gemdir, branch if $pull_gems end gemdir << "/#{params[:path]}" if params[:path] elsif params[:path] require 'pathname' gemdir = Pathname.new(params[:path]).absolute? ? params[:path] : "#{root}/#{params[:path]}" else fail "unknown gem option #{params}" end gemdir end def enable_gems? !@gems.empty? end end # LoadGems end # MRuby mruby-2.0.0/lib/mruby/gem.rb000066400000000000000000000326641340361412400156610ustar00rootroot00000000000000require 'pathname' require 'forwardable' require 'tsort' require 'shellwords' module MRuby module Gem class << self attr_accessor :current end LinkerConfig = Struct.new(:libraries, :library_paths, :flags, :flags_before_libraries, :flags_after_libraries) class Specification include Rake::DSL extend Forwardable def_delegators :@build, :filename, :objfile, :libfile, :exefile attr_accessor :name, :dir, :build alias mruby build attr_accessor :build_config_initializer attr_accessor :mrblib_dir, :objs_dir attr_accessor :version attr_accessor :description, :summary attr_accessor :homepage attr_accessor :licenses, :authors alias :license= :licenses= alias :author= :authors= attr_accessor :rbfiles, :objs attr_accessor :test_objs, :test_rbfiles, :test_args attr_accessor :test_preload attr_accessor :bins attr_accessor :requirements attr_reader :dependencies, :conflicts attr_accessor :export_include_paths attr_reader :generate_functions attr_block MRuby::Build::COMMANDS def initialize(name, &block) @name = name @initializer = block @version = "0.0.0" @mrblib_dir = "mrblib" @objs_dir = "src" MRuby::Gem.current = self end def setup MRuby::Gem.current = self MRuby::Build::COMMANDS.each do |command| instance_variable_set("@#{command}", @build.send(command).clone) end @linker = LinkerConfig.new([], [], [], [], []) @rbfiles = Dir.glob("#{@dir}/#{@mrblib_dir}/**/*.rb").sort @objs = Dir.glob("#{@dir}/#{@objs_dir}/*.{c,cpp,cxx,cc,m,asm,s,S}").map do |f| objfile(f.relative_path_from(@dir).to_s.pathmap("#{build_dir}/%X")) end @test_rbfiles = Dir.glob("#{dir}/test/**/*.rb").sort @test_objs = Dir.glob("#{dir}/test/*.{c,cpp,cxx,cc,m,asm,s,S}").map do |f| objfile(f.relative_path_from(dir).to_s.pathmap("#{build_dir}/%X")) end @custom_test_init = !@test_objs.empty? @test_preload = nil # 'test/assert.rb' @test_args = {} @bins = [] @requirements = [] @dependencies, @conflicts = [], [] @export_include_paths = [] @export_include_paths << "#{dir}/include" if File.directory? "#{dir}/include" instance_eval(&@initializer) @generate_functions = !(@rbfiles.empty? && @objs.empty?) @objs << objfile("#{build_dir}/gem_init") if @generate_functions if !name || !licenses || !authors fail "#{name || dir} required to set name, license(s) and author(s)" end build.libmruby_objs << @objs instance_eval(&@build_config_initializer) if @build_config_initializer end def setup_compilers compilers.each do |compiler| compiler.define_rules build_dir, "#{dir}" compiler.defines << %Q[MRBGEM_#{funcname.upcase}_VERSION=#{version}] compiler.include_paths << "#{dir}/include" if File.directory? "#{dir}/include" end define_gem_init_builder if @generate_functions end def add_dependency(name, *requirements) default_gem = requirements.last.kind_of?(Hash) ? requirements.pop : nil requirements = ['>= 0.0.0'] if requirements.empty? requirements.flatten! @dependencies << {:gem => name, :requirements => requirements, :default => default_gem} end def add_test_dependency(*args) add_dependency(*args) if build.test_enabled? end def add_conflict(name, *req) @conflicts << {:gem => name, :requirements => req.empty? ? nil : req} end def self.bin=(bin) @bins = [bin].flatten end def build_dir "#{build.build_dir}/mrbgems/#{name}" end def test_rbireps "#{build_dir}/gem_test.c" end def search_package(name, version_query=nil) package_query = name package_query += " #{version_query}" if version_query _pp "PKG-CONFIG", package_query escaped_package_query = Shellwords.escape(package_query) if system("pkg-config --exists #{escaped_package_query}") cc.flags += [`pkg-config --cflags #{escaped_package_query}`.strip] cxx.flags += [`pkg-config --cflags #{escaped_package_query}`.strip] linker.flags_before_libraries += [`pkg-config --libs #{escaped_package_query}`.strip] true else false end end def funcname @funcname ||= @name.gsub('-', '_') end def compilers MRuby::Build::COMPILERS.map do |c| instance_variable_get("@#{c}") end end def define_gem_init_builder file objfile("#{build_dir}/gem_init") => [ "#{build_dir}/gem_init.c", File.join(dir, "mrbgem.rake") ] file "#{build_dir}/gem_init.c" => [build.mrbcfile, __FILE__] + [rbfiles].flatten do |t| FileUtils.mkdir_p build_dir generate_gem_init("#{build_dir}/gem_init.c") end end def generate_gem_init(fname) open(fname, 'w') do |f| print_gem_init_header f build.mrbc.run f, rbfiles, "gem_mrblib_irep_#{funcname}" unless rbfiles.empty? f.puts %Q[void mrb_#{funcname}_gem_init(mrb_state *mrb);] f.puts %Q[void mrb_#{funcname}_gem_final(mrb_state *mrb);] f.puts %Q[] f.puts %Q[void GENERATED_TMP_mrb_#{funcname}_gem_init(mrb_state *mrb) {] f.puts %Q[ int ai = mrb_gc_arena_save(mrb);] f.puts %Q[ mrb_#{funcname}_gem_init(mrb);] if objs != [objfile("#{build_dir}/gem_init")] unless rbfiles.empty? f.puts %Q[ mrb_load_irep(mrb, gem_mrblib_irep_#{funcname});] f.puts %Q[ if (mrb->exc) {] f.puts %Q[ mrb_print_error(mrb);] f.puts %Q[ mrb_close(mrb);] f.puts %Q[ exit(EXIT_FAILURE);] f.puts %Q[ }] end f.puts %Q[ mrb_gc_arena_restore(mrb, ai);] f.puts %Q[}] f.puts %Q[] f.puts %Q[void GENERATED_TMP_mrb_#{funcname}_gem_final(mrb_state *mrb) {] f.puts %Q[ mrb_#{funcname}_gem_final(mrb);] if objs != [objfile("#{build_dir}/gem_init")] f.puts %Q[}] end end # generate_gem_init def print_gem_comment(f) f.puts %Q[/*] f.puts %Q[ * This file is loading the irep] f.puts %Q[ * Ruby GEM code.] f.puts %Q[ *] f.puts %Q[ * IMPORTANT:] f.puts %Q[ * This file was generated!] f.puts %Q[ * All manual changes will get lost.] f.puts %Q[ */] end def print_gem_init_header(f) print_gem_comment(f) f.puts %Q[#include ] unless rbfiles.empty? f.puts %Q[#include ] f.puts %Q[#include ] unless rbfiles.empty? end def print_gem_test_header(f) print_gem_comment(f) f.puts %Q[#include ] f.puts %Q[#include ] f.puts %Q[#include ] f.puts %Q[#include ] f.puts %Q[#include ] f.puts %Q[#include ] unless test_args.empty? end def test_dependencies [@name] end def custom_test_init? @custom_test_init end def version_ok?(req_versions) req_versions.map do |req| cmp, ver = req.split cmp_result = Version.new(version) <=> Version.new(ver) case cmp when '=' then cmp_result == 0 when '!=' then cmp_result != 0 when '>' then cmp_result == 1 when '<' then cmp_result == -1 when '>=' then cmp_result >= 0 when '<=' then cmp_result <= 0 when '~>' Version.new(version).twiddle_wakka_ok?(Version.new(ver)) else fail "Comparison not possible with '#{cmp}'" end end.all? end end # Specification class Version include Comparable include Enumerable def <=>(other) ret = 0 own = to_enum other.each do |oth| begin ret = own.next <=> oth rescue StopIteration ret = 0 <=> oth end break unless ret == 0 end ret end # ~> compare algorithm # # Example: # ~> 2.2 means >= 2.2.0 and < 3.0.0 # ~> 2.2.0 means >= 2.2.0 and < 2.3.0 def twiddle_wakka_ok?(other) gr_or_eql = (self <=> other) >= 0 still_minor = (self <=> other.skip_minor) < 0 gr_or_eql and still_minor end def skip_minor a = @ary.dup a.slice!(-1) a[-1] = a[-1].succ a end def initialize(str) @str = str @ary = @str.split('.').map(&:to_i) end def each(&block); @ary.each(&block); end def [](index); @ary[index]; end def []=(index, value) @ary[index] = value @str = @ary.join('.') end def slice!(index) @ary.slice!(index) @str = @ary.join('.') end end # Version class List include Enumerable def initialize @ary = [] end def each(&b) @ary.each(&b) end def <<(gem) unless @ary.detect {|g| g.dir == gem.dir } @ary << gem else # GEM was already added to this list end end def empty? @ary.empty? end def default_gem_params dep if dep[:default]; dep elsif File.exist? "#{MRUBY_ROOT}/mrbgems/#{dep[:gem]}" # check core { :gem => dep[:gem], :default => { :core => dep[:gem] } } else # fallback to mgem-list { :gem => dep[:gem], :default => { :mgem => dep[:gem] } } end end def generate_gem_table build gem_table = @ary.reduce({}) { |res,v| res[v.name] = v; res } default_gems = [] each do |g| g.dependencies.each do |dep| default_gems << default_gem_params(dep) unless gem_table.key? dep[:gem] end end until default_gems.empty? def_gem = default_gems.pop spec = build.gem def_gem[:default] fail "Invalid gem name: #{spec.name} (Expected: #{def_gem[:gem]})" if spec.name != def_gem[:gem] spec.setup spec.dependencies.each do |dep| default_gems << default_gem_params(dep) unless gem_table.key? dep[:gem] end gem_table[spec.name] = spec end each do |g| g.dependencies.each do |dep| name = dep[:gem] req_versions = dep[:requirements] dep_g = gem_table[name] # check each GEM dependency against all available GEMs if dep_g.nil? fail "The GEM '#{g.name}' depends on the GEM '#{name}' but it could not be found" end unless dep_g.version_ok? req_versions fail "#{name} version should be #{req_versions.join(' and ')} but was '#{dep_g.version}'" end end cfls = g.conflicts.select { |c| cfl_g = gem_table[c[:gem]] cfl_g and cfl_g.version_ok?(c[:requirements] || ['>= 0.0.0']) }.map { |c| "#{c[:gem]}(#{gem_table[c[:gem]].version})" } fail "Conflicts of gem `#{g.name}` found: #{cfls.join ', '}" unless cfls.empty? end gem_table end def tsort_dependencies ary, table, all_dependency_listed = false unless all_dependency_listed left = ary.dup until left.empty? v = left.pop table[v].dependencies.each do |dep| left.push dep[:gem] ary.push dep[:gem] end end end ary.uniq! table.instance_variable_set :@root_gems, ary class << table include TSort def tsort_each_node &b @root_gems.each &b end def tsort_each_child(n, &b) fetch(n).dependencies.each do |v| b.call v[:gem] end end end begin table.tsort.map { |v| table[v] } rescue TSort::Cyclic => e fail "Circular mrbgem dependency found: #{e.message}" end end def check(build) gem_table = generate_gem_table build @ary = tsort_dependencies gem_table.keys, gem_table, true each(&:setup_compilers) each do |g| import_include_paths(g) end end def import_include_paths(g) gem_table = @ary.reduce({}) { |res,v| res[v.name] = v; res } g.dependencies.each do |dep| dep_g = gem_table[dep[:gem]] # We can do recursive call safely # as circular dependency has already detected in the caller. import_include_paths(dep_g) dep_g.export_include_paths.uniq! g.compilers.each do |compiler| compiler.include_paths += dep_g.export_include_paths g.export_include_paths += dep_g.export_include_paths compiler.include_paths.uniq! g.export_include_paths.uniq! end end end end # List end # Gem GemBox = Object.new class << GemBox attr_accessor :path def new(&block); block.call(self); end def config=(obj); @config = obj; end def gem(gemdir, &block); @config.gem(gemdir, &block); end def gembox(gemfile); @config.gembox(gemfile); end end # GemBox end # MRuby mruby-2.0.0/lib/mruby/source.rb000066400000000000000000000026521340361412400164030ustar00rootroot00000000000000require "pathname" module MRuby module Source # MRuby's source root directory ROOT = Pathname.new(File.expand_path('../../../',__FILE__)) # Reads a constant defined at version.h MRUBY_READ_VERSION_CONSTANT = Proc.new { |name| ROOT.join('include','mruby','version.h').read.match(/^#define #{name} +"?([\w\. ]+)"?$/)[1] } MRUBY_RUBY_VERSION = MRUBY_READ_VERSION_CONSTANT['MRUBY_RUBY_VERSION'] MRUBY_RUBY_ENGINE = MRUBY_READ_VERSION_CONSTANT['MRUBY_RUBY_ENGINE'] MRUBY_RELEASE_MAJOR = Integer(MRUBY_READ_VERSION_CONSTANT['MRUBY_RELEASE_MAJOR']) MRUBY_RELEASE_MINOR = Integer(MRUBY_READ_VERSION_CONSTANT['MRUBY_RELEASE_MINOR']) MRUBY_RELEASE_TEENY = Integer(MRUBY_READ_VERSION_CONSTANT['MRUBY_RELEASE_TEENY']) MRUBY_VERSION = [MRUBY_RELEASE_MAJOR,MRUBY_RELEASE_MINOR,MRUBY_RELEASE_TEENY].join('.') MRUBY_RELEASE_NO = (MRUBY_RELEASE_MAJOR * 100 * 100 + MRUBY_RELEASE_MINOR * 100 + MRUBY_RELEASE_TEENY) MRUBY_RELEASE_YEAR = Integer(MRUBY_READ_VERSION_CONSTANT['MRUBY_RELEASE_YEAR']) MRUBY_RELEASE_MONTH = Integer(MRUBY_READ_VERSION_CONSTANT['MRUBY_RELEASE_MONTH']) MRUBY_RELEASE_DAY = Integer(MRUBY_READ_VERSION_CONSTANT['MRUBY_RELEASE_DAY']) MRUBY_RELEASE_DATE = [MRUBY_RELEASE_YEAR,MRUBY_RELEASE_MONTH,MRUBY_RELEASE_DAY].join('.') MRUBY_BIRTH_YEAR = Integer(MRUBY_READ_VERSION_CONSTANT['MRUBY_BIRTH_YEAR']) MRUBY_AUTHOR = MRUBY_READ_VERSION_CONSTANT['MRUBY_AUTHOR'] end end mruby-2.0.0/minirake000077500000000000000000000374121340361412400144010ustar00rootroot00000000000000#!/usr/bin/env ruby # Original is https://github.com/jimweirich/rake/ # Copyright (c) 2003 Jim Weirich # License: MIT-LICENSE require 'getoptlong' require 'fileutils' $rake_fiber_table = {} $rake_jobs = 1 $rake_failed = [] class String def ext(newext='') return self.dup if ['.', '..'].include? self if newext != '' newext = (newext =~ /^\./) ? newext : ("." + newext) end self.chomp(File.extname(self)) << newext end def pathmap(spec=nil, &block) return self if spec.nil? result = '' spec.scan(/%\{[^}]*\}-?\d*[sdpfnxX%]|%-?\d+d|%.|[^%]+/) do |frag| case frag when '%f' result << File.basename(self) when '%n' result << File.basename(self).ext when '%d' result << File.dirname(self) when '%x' result << File.extname(self) when '%X' result << self.ext when '%p' result << self when '%s' result << (File::ALT_SEPARATOR || File::SEPARATOR) when '%-' # do nothing when '%%' result << "%" when /%(-?\d+)d/ result << pathmap_partial($1.to_i) when /^%\{([^}]*)\}(\d*[dpfnxX])/ patterns, operator = $1, $2 result << pathmap('%' + operator).pathmap_replace(patterns, &block) when /^%/ fail ArgumentError, "Unknown pathmap specifier #{frag} in '#{spec}'" else result << frag end end result end end module MiniRake class Task TASKS = Hash.new RULES = Array.new # List of prerequisites for a task. attr_reader :prerequisites # Source dependency for rule synthesized tasks. Nil if task was not # sythesized from a rule. attr_accessor :source # Create a task named +task_name+ with no actions or prerequisites.. # use +enhance+ to add actions and prerequisites. def initialize(task_name) @name = task_name @prerequisites = [] @actions = [] end # Enhance a task with prerequisites or actions. Returns self. def enhance(deps=nil, &block) @prerequisites |= deps if deps @actions << block if block_given? self end # Name of the task. def name @name.to_s end def done?; @done end def running?; @running end # Invoke the task if it is needed. Prerequisites are invoked first. def invoke puts "Invoke #{name} (already=[#{@already_invoked}], needed=[#{needed?}])" if $trace return if @already_invoked prerequisites = @prerequisites.collect{ |n| n.is_a?(Proc) ? n.call(name) : n }.flatten prerequisites.each do |n| t = Task[n] unless t.done? return prerequisites.select{|v| v = Task[v]; v && (!v.done? || !v.running?) } end end @already_invoked = true if needed? @running = true if $rake_root_fiber return Fiber.new do self.execute $rake_root_fiber.transfer end else self.execute end end @done = true end # Execute the actions associated with this task. def execute puts "Execute #{name}" if $trace self.class.enhance_with_matching_rule(name) if @actions.empty? unless $dryrun @actions.each { |act| act.call(self) } end @done = true @running = false end # Is this task needed? def needed? true end # Timestamp for this task. Basic tasks return the current time for # their time stamp. Other tasks can be more sophisticated. def timestamp Time.now end # Class Methods ---------------------------------------------------- class << self # Clear the task list. This cause rake to immediately forget all # the tasks that have been assigned. (Normally used in the unit # tests.) def clear TASKS.clear RULES.clear end # List of all defined tasks. def tasks TASKS.keys.sort.collect { |tn| Task[tn] } end # Return a task with the given name. If the task is not currently # known, try to synthesize one from the defined rules. If no # rules are found, but an existing file matches the task name, # assume it is a file task with no dependencies or actions. def [](task_name) task_name = task_name.to_s if task = TASKS[task_name] return task end if task = enhance_with_matching_rule(task_name) return task end if File.exist?(task_name) return FileTask.define_task(task_name) end fail "Don't know how to rake #{task_name}" end # Define a task given +args+ and an option block. If a rule with # the given name already exists, the prerequisites and actions are # added to the existing task. def define_task(args, &block) task_name, deps = resolve_args(args) lookup(task_name).enhance([deps].flatten, &block) end # Define a rule for synthesizing tasks. def create_rule(args, &block) pattern, deps = resolve_args(args) pattern = Regexp.new(Regexp.quote(pattern) + '$') if String === pattern RULES << [pattern, deps, block] end # Lookup a task. Return an existing task if found, otherwise # create a task of the current type. def lookup(task_name) name = task_name.to_s TASKS[name] ||= self.new(name) end # If a rule can be found that matches the task name, enhance the # task with the prerequisites and actions from the rule. Set the # source attribute of the task appropriately for the rule. Return # the enhanced task or nil of no rule was found. def enhance_with_matching_rule(task_name) RULES.each do |pattern, extensions, block| if pattern.match(task_name) ext = extensions.first deps = extensions[1..-1] case ext when String source = task_name.sub(/\.[^.]*$/, ext) when Proc source = ext.call(task_name) else fail "Don't know how to handle rule dependent: #{ext.inspect}" end if File.exist?(source) task = FileTask.define_task({task_name => [source]+deps}, &block) task.source = source return task end end end nil end private # Resolve the arguments for a task/rule. def resolve_args(args) case args when Hash fail "Too Many Task Names: #{args.keys.join(' ')}" if args.size > 1 fail "No Task Name Given" if args.size < 1 task_name = args.keys[0] deps = args[task_name] deps = [deps] if (String===deps) || (Regexp===deps) || (Proc===deps) else task_name = args deps = [] end [task_name, deps] end end end ###################################################################### class FileTask < Task # Is this file task needed? Yes if it doesn't exist, or if its time # stamp is out of date. def needed? return true unless File.exist?(name) prerequisites = @prerequisites.collect{ |n| n.is_a?(Proc) ? n.call(name) : n }.flatten latest_prereq = prerequisites.collect{|n| Task[n].timestamp}.max return false if latest_prereq.nil? timestamp < latest_prereq end # Time stamp for file task. def timestamp return Time.at(0) unless File.exist?(name) stat = File::stat(name.to_s) stat.directory? ? Time.at(0) : stat.mtime end end module DSL # Declare a basic task. def task(args, &block) MiniRake::Task.define_task(args, &block) end # Declare a file task. def file(args, &block) MiniRake::FileTask.define_task(args, &block) end # Declare a set of files tasks to create the given directories on # demand. def directory(args, &block) MiniRake::FileTask.define_task(args) do |t| block.call(t) unless block.nil? dir = args.is_a?(Hash) ? args.keys.first : args (dir.split(File::SEPARATOR) + ['']).inject do |acc, part| (acc + File::SEPARATOR).tap do |d| Dir.mkdir(d) unless File.exists? d end + part end end end # Declare a rule for auto-tasks. def rule(args, &block) MiniRake::Task.create_rule(args, &block) end # Write a message to standard out if $verbose is enabled. def log(msg) print " " if $trace && $verbose puts msg if $verbose end # Run the system command +cmd+. def sh(cmd) puts cmd if $verbose if !$rake_root_fiber || Fiber.current == $rake_root_fiber system(cmd) or fail "Command Failed: [#{cmd}]" return end pid = Process.spawn(cmd) $rake_fiber_table[pid] = { fiber: Fiber.current, command: cmd, process_waiter: Process.detach(pid) } $rake_root_fiber.transfer end def desc(text) end end end Rake = MiniRake extend MiniRake::DSL ###################################################################### # Task Definition Functions ... ###################################################################### # Rake main application object. When invoking +rake+ from the command # line, a RakeApp object is created and run. # class RakeApp RAKEFILES = ['rakefile', 'Rakefile'] OPTIONS = [ ['--dry-run', '-n', GetoptLong::NO_ARGUMENT, "Do a dry run without executing actions."], ['--help', '-H', GetoptLong::NO_ARGUMENT, "Display this help message."], ['--libdir', '-I', GetoptLong::REQUIRED_ARGUMENT, "Include LIBDIR in the search path for required modules."], ['--nosearch', '-N', GetoptLong::NO_ARGUMENT, "Do not search parent directories for the Rakefile."], ['--quiet', '-q', GetoptLong::NO_ARGUMENT, "Do not log messages to standard output (default)."], ['--rakefile', '-f', GetoptLong::REQUIRED_ARGUMENT, "Use FILE as the rakefile."], ['--require', '-r', GetoptLong::REQUIRED_ARGUMENT, "Require MODULE before executing rakefile."], ['--tasks', '-T', GetoptLong::NO_ARGUMENT, "Display the tasks and dependencies, then exit."], ['--pull-gems','-p', GetoptLong::NO_ARGUMENT, "Pull all git mrbgems."], ['--trace', '-t', GetoptLong::NO_ARGUMENT, "Turn on invoke/execute tracing."], ['--usage', '-h', GetoptLong::NO_ARGUMENT, "Display usage."], ['--verbose', '-v', GetoptLong::NO_ARGUMENT, "Log message to standard output."], ['--directory', '-C', GetoptLong::REQUIRED_ARGUMENT, "Change executing directory of rakefiles."], ['--jobs', '-j', GetoptLong::REQUIRED_ARGUMENT, 'Execute rake with parallel jobs.'] ] # Create a RakeApp object. def initialize @rakefile = nil @nosearch = false end # True if one of the files in RAKEFILES is in the current directory. # If a match is found, it is copied into @rakefile. def have_rakefile RAKEFILES.each do |fn| if File.exist?(fn) @rakefile = fn return true end end return false end # Display the program usage line. def usage puts "rake [-f rakefile] {options} targets..." end # Display the rake command line help. def help usage puts puts "Options are ..." puts OPTIONS.sort.each do |long, short, mode, desc| if mode == GetoptLong::REQUIRED_ARGUMENT if desc =~ /\b([A-Z]{2,})\b/ long = long + "=#{$1}" end end printf " %-20s (%s)\n", long, short printf " %s\n", desc end end # Display the tasks and dependencies. def display_tasks MiniRake::Task.tasks.each do |t| puts "#{t.class} #{t.name}" t.prerequisites.each { |pre| puts " #{pre}" } end end # Return a list of the command line options supported by the # program. def command_line_options OPTIONS.collect { |lst| lst[0..-2] } end # Do the option defined by +opt+ and +value+. def do_option(opt, value) case opt when '--dry-run' $dryrun = true $trace = true when '--help' help exit when '--libdir' $:.push(value) when '--nosearch' @nosearch = true when '--quiet' $verbose = false when '--rakefile' RAKEFILES.clear RAKEFILES << value when '--require' require value when '--tasks' $show_tasks = true when '--pull-gems' $pull_gems = true when '--trace' $trace = true when '--usage' usage exit when '--verbose' $verbose = true when '--version' puts "rake, version #{RAKEVERSION}" exit when '--directory' Dir.chdir value when '--jobs' $rake_jobs = [value.to_i, 1].max else fail "Unknown option: #{opt}" end end # Read and handle the command line options. def handle_options $verbose = false $pull_gems = false opts = GetoptLong.new(*command_line_options) opts.each { |opt, value| do_option(opt, value) } end # Run the +rake+ application. def run handle_options if $rake_root_fiber require 'fiber' $rake_root_fiber = Fiber.current end begin here = Dir.pwd while ! have_rakefile Dir.chdir("..") if Dir.pwd == here || @nosearch fail "No Rakefile found (looking for: #{RAKEFILES.join(', ')})" end here = Dir.pwd end root_tasks = [] ARGV.each do |task_name| if /^(\w+)=(.*)/.match(task_name) ENV[$1] = $2 else root_tasks << task_name end end puts "(in #{Dir.pwd})" $rakefile = @rakefile load @rakefile if $show_tasks display_tasks else root_tasks.push("default") if root_tasks.empty? # revese tasks for popping root_tasks.reverse! tasks = [] until root_tasks.empty? root_name = root_tasks.pop tasks << root_name until tasks.empty? task_name = tasks.pop t = MiniRake::Task[task_name] f = t.invoke # append additional tasks to task queue if f.kind_of?(Array) tasks.push(*f) tasks.uniq! end unless f.kind_of? Fiber tasks.insert 0, task_name unless t.done? if root_name == task_name wait_process end next end wait_process while $rake_fiber_table.size >= $rake_jobs f.transfer end end wait_process until $rake_fiber_table.empty? end rescue Exception => e begin $rake_failed << e wait_process until $rake_fiber_table.empty? rescue Exception => next_e e = next_e retry end end return if $rake_failed.empty? puts "rake aborted!" $rake_failed.each do |ex| puts ex.message if $trace || $verbose puts ex.backtrace.join("\n") else puts ex.backtrace.find {|str| str =~ /#{@rakefile}/ } || "" end end exit 1 end def wait_process(count = 0) dur = [0.0001 * (10 ** count), 1].min sleep dur exited = [] $rake_fiber_table.each do |pid, v| exited << pid unless v[:process_waiter].alive? end exited.each do |pid| ent = $rake_fiber_table.delete pid st = ent[:process_waiter].value # ignore process that isn't created by `sh` method return if ent.nil? if st.exitstatus != 0 raise "Command Failed: [#{ent[:command]}]" end fail 'task scheduling bug!' if $rake_fiber_table.size >= $rake_jobs ent[:fiber].transfer end wait_process(count + 1) if !$rake_fiber_table.empty? && exited.empty? end end if __FILE__ == $0 then RakeApp.new.run end mruby-2.0.0/mrbgems/000077500000000000000000000000001340361412400143015ustar00rootroot00000000000000mruby-2.0.0/mrbgems/default.gembox000066400000000000000000000041711340361412400171330ustar00rootroot00000000000000MRuby::GemBox.new do |conf| # Meta-programming features conf.gem :core => "mruby-metaprog" # Use standard IO/File class conf.gem :core => "mruby-io" # Use standard Array#pack, String#unpack methods conf.gem :core => "mruby-pack" # Use standard Kernel#sprintf method conf.gem :core => "mruby-sprintf" # Use standard print/puts/p conf.gem :core => "mruby-print" # Use standard Math module conf.gem :core => "mruby-math" # Use standard Time class conf.gem :core => "mruby-time" # Use standard Struct class conf.gem :core => "mruby-struct" # Use Comparable module extension conf.gem :core => "mruby-compar-ext" # Use Enumerable module extension conf.gem :core => "mruby-enum-ext" # Use String class extension conf.gem :core => "mruby-string-ext" # Use Numeric class extension conf.gem :core => "mruby-numeric-ext" # Use Array class extension conf.gem :core => "mruby-array-ext" # Use Hash class extension conf.gem :core => "mruby-hash-ext" # Use Range class extension conf.gem :core => "mruby-range-ext" # Use Proc class extension conf.gem :core => "mruby-proc-ext" # Use Symbol class extension conf.gem :core => "mruby-symbol-ext" # Use Random class conf.gem :core => "mruby-random" # Use Object class extension conf.gem :core => "mruby-object-ext" # Use ObjectSpace class conf.gem :core => "mruby-objectspace" # Use Fiber class conf.gem :core => "mruby-fiber" # Use Enumerator class (require mruby-fiber) conf.gem :core => "mruby-enumerator" # Use Enumerator::Lazy class (require mruby-enumerator) conf.gem :core => "mruby-enum-lazy" # Use toplevel object (main) methods extension conf.gem :core => "mruby-toplevel-ext" # Generate mirb command conf.gem :core => "mruby-bin-mirb" # Generate mruby command conf.gem :core => "mruby-bin-mruby" # Generate mruby-strip command conf.gem :core => "mruby-bin-strip" # Use Kernel module extension conf.gem :core => "mruby-kernel-ext" # Use class/module extension conf.gem :core => "mruby-class-ext" # Use mruby-compiler to build other mrbgems conf.gem :core => "mruby-compiler" end mruby-2.0.0/mrbgems/full-core.gembox000066400000000000000000000004331340361412400173740ustar00rootroot00000000000000MRuby::GemBox.new do |conf| conf.gem :core => "mruby-sprintf" conf.gem :core => "mruby-print" Dir.glob("#{root}/mrbgems/mruby-*/mrbgem.rake") do |x| g = File.basename File.dirname x conf.gem :core => g unless g =~ /^mruby-(print|sprintf|bin-debugger|test)$/ end end mruby-2.0.0/mrbgems/mruby-array-ext/000077500000000000000000000000001340361412400173515ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-array-ext/mrbgem.rake000066400000000000000000000003531340361412400214670ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-array-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Array class extension' spec.add_test_dependency 'mruby-enumerator', core: 'mruby-enumerator' end mruby-2.0.0/mrbgems/mruby-array-ext/mrblib/000077500000000000000000000000001340361412400206205ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-array-ext/mrblib/array.rb000066400000000000000000000613621340361412400222730ustar00rootroot00000000000000class Array ## # call-seq: # ary.uniq! -> ary or nil # ary.uniq! { |item| ... } -> ary or nil # # Removes duplicate elements from +self+. # Returns nil if no changes are made (that is, no # duplicates are found). # # a = [ "a", "a", "b", "b", "c" ] # a.uniq! #=> ["a", "b", "c"] # b = [ "a", "b", "c" ] # b.uniq! #=> nil # c = [["student","sam"], ["student","george"], ["teacher","matz"]] # c.uniq! { |s| s.first } # => [["student", "sam"], ["teacher", "matz"]] # def uniq!(&block) hash = {} if block self.each do |val| key = block.call(val) hash[key] = val unless hash.key?(key) end result = hash.values else hash = {} self.each do |val| hash[val] = val end result = hash.keys end if result.size == self.size nil else self.replace(result) end end ## # call-seq: # ary.uniq -> new_ary # ary.uniq { |item| ... } -> new_ary # # Returns a new array by removing duplicate values in +self+. # # a = [ "a", "a", "b", "b", "c" ] # a.uniq #=> ["a", "b", "c"] # # b = [["student","sam"], ["student","george"], ["teacher","matz"]] # b.uniq { |s| s.first } # => [["student", "sam"], ["teacher", "matz"]] # def uniq(&block) ary = self.dup ary.uniq!(&block) ary end ## # call-seq: # ary - other_ary -> new_ary # # Array Difference---Returns a new array that is a copy of # the original array, removing any items that also appear in # other_ary. (If you need set-like behavior, see the # library class Set.) # # [ 1, 1, 2, 2, 3, 3, 4, 5 ] - [ 1, 2, 4 ] #=> [ 3, 3, 5 ] # def -(elem) raise TypeError, "can't convert #{elem.class} into Array" unless elem.class == Array hash = {} array = [] idx = 0 len = elem.size while idx < len hash[elem[idx]] = true idx += 1 end idx = 0 len = size while idx < len v = self[idx] array << v unless hash[v] idx += 1 end array end ## # call-seq: # ary | other_ary -> new_ary # # Set Union---Returns a new array by joining this array with # other_ary, removing duplicates. # # [ "a", "b", "c" ] | [ "c", "d", "a" ] # #=> [ "a", "b", "c", "d" ] # def |(elem) raise TypeError, "can't convert #{elem.class} into Array" unless elem.class == Array ary = self + elem ary.uniq! or ary end ## # call-seq: # ary.union(other_ary,...) -> new_ary # # Set Union---Returns a new array by joining this array with # other_ary, removing duplicates. # # ["a", "b", "c"].union(["c", "d", "a"], ["a", "c", "e"]) # #=> ["a", "b", "c", "d", "e"] # def union(*args) ary = self.dup args.each do |x| ary.concat(x) ary.uniq! end ary end ## # call-seq: # ary & other_ary -> new_ary # # Set Intersection---Returns a new array # containing elements common to the two arrays, with no duplicates. # # [ 1, 1, 3, 5 ] & [ 1, 2, 3 ] #=> [ 1, 3 ] # def &(elem) raise TypeError, "can't convert #{elem.class} into Array" unless elem.class == Array hash = {} array = [] idx = 0 len = elem.size while idx < len hash[elem[idx]] = true idx += 1 end idx = 0 len = size while idx < len v = self[idx] if hash[v] array << v hash.delete v end idx += 1 end array end ## # call-seq: # ary.flatten -> new_ary # ary.flatten(level) -> new_ary # # Returns a new array that is a one-dimensional flattening of this # array (recursively). That is, for every element that is an array, # extract its elements into the new array. If the optional # level argument determines the level of recursion to flatten. # # s = [ 1, 2, 3 ] #=> [1, 2, 3] # t = [ 4, 5, 6, [7, 8] ] #=> [4, 5, 6, [7, 8]] # a = [ s, t, 9, 10 ] #=> [[1, 2, 3], [4, 5, 6, [7, 8]], 9, 10] # a.flatten #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # a = [ 1, 2, [3, [4, 5] ] ] # a.flatten(1) #=> [1, 2, 3, [4, 5]] # def flatten(depth=nil) res = dup res.flatten! depth res end ## # call-seq: # ary.flatten! -> ary or nil # ary.flatten!(level) -> array or nil # # Flattens +self+ in place. # Returns nil if no modifications were made (i.e., # ary contains no subarrays.) If the optional level # argument determines the level of recursion to flatten. # # a = [ 1, 2, [3, [4, 5] ] ] # a.flatten! #=> [1, 2, 3, 4, 5] # a.flatten! #=> nil # a #=> [1, 2, 3, 4, 5] # a = [ 1, 2, [3, [4, 5] ] ] # a.flatten!(1) #=> [1, 2, 3, [4, 5]] # def flatten!(depth=nil) modified = false ar = [] idx = 0 len = size while idx < len e = self[idx] if e.is_a?(Array) && (depth.nil? || depth > 0) ar += e.flatten(depth.nil? ? nil : depth - 1) modified = true else ar << e end idx += 1 end if modified self.replace(ar) else nil end end ## # call-seq: # ary.compact -> new_ary # # Returns a copy of +self+ with all +nil+ elements removed. # # [ "a", nil, "b", nil, "c", nil ].compact # #=> [ "a", "b", "c" ] # def compact result = self.dup result.compact! result end ## # call-seq: # ary.compact! -> ary or nil # # Removes +nil+ elements from the array. # Returns +nil+ if no changes were made, otherwise returns # ary. # # [ "a", nil, "b", nil, "c" ].compact! #=> [ "a", "b", "c" ] # [ "a", "b", "c" ].compact! #=> nil # def compact! result = self.select { |e| !e.nil? } if result.size == self.size nil else self.replace(result) end end # for efficiency def reverse_each(&block) return to_enum :reverse_each unless block i = self.size - 1 while i>=0 block.call(self[i]) i -= 1 end self end NONE=Object.new ## # call-seq: # ary.fetch(index) -> obj # ary.fetch(index, default) -> obj # ary.fetch(index) { |index| block } -> obj # # Tries to return the element at position +index+, but throws an IndexError # exception if the referenced +index+ lies outside of the array bounds. This # error can be prevented by supplying a second argument, which will act as a # +default+ value. # # Alternatively, if a block is given it will only be executed when an # invalid +index+ is referenced. # # Negative values of +index+ count from the end of the array. # # a = [ 11, 22, 33, 44 ] # a.fetch(1) #=> 22 # a.fetch(-1) #=> 44 # a.fetch(4, 'cat') #=> "cat" # a.fetch(100) { |i| puts "#{i} is out of bounds" } # #=> "100 is out of bounds" # def fetch(n, ifnone=NONE, &block) warn "block supersedes default value argument" if !n.nil? && ifnone != NONE && block idx = n if idx < 0 idx += size end if idx < 0 || size <= idx return block.call(n) if block if ifnone == NONE raise IndexError, "index #{n} outside of array bounds: #{-size}...#{size}" end return ifnone end self[idx] end ## # call-seq: # ary.fill(obj) -> ary # ary.fill(obj, start [, length]) -> ary # ary.fill(obj, range ) -> ary # ary.fill { |index| block } -> ary # ary.fill(start [, length] ) { |index| block } -> ary # ary.fill(range) { |index| block } -> ary # # The first three forms set the selected elements of +self+ (which # may be the entire array) to +obj+. # # A +start+ of +nil+ is equivalent to zero. # # A +length+ of +nil+ is equivalent to the length of the array. # # The last three forms fill the array with the value of the given block, # which is passed the absolute index of each element to be filled. # # Negative values of +start+ count from the end of the array, where +-1+ is # the last element. # # a = [ "a", "b", "c", "d" ] # a.fill("x") #=> ["x", "x", "x", "x"] # a.fill("w", -1) #=> ["x", "x", "x", "w"] # a.fill("z", 2, 2) #=> ["x", "x", "z", "z"] # a.fill("y", 0..1) #=> ["y", "y", "z", "z"] # a.fill { |i| i*i } #=> [0, 1, 4, 9] # a.fill(-2) { |i| i*i*i } #=> [0, 1, 8, 27] # a.fill(1, 2) { |i| i+1 } #=> [0, 2, 3, 27] # a.fill(0..1) { |i| i+1 } #=> [1, 2, 3, 27] # def fill(arg0=nil, arg1=nil, arg2=nil, &block) if arg0.nil? && arg1.nil? && arg2.nil? && !block raise ArgumentError, "wrong number of arguments (0 for 1..3)" end beg = len = 0 ary = [] if block if arg0.nil? && arg1.nil? && arg2.nil? # ary.fill { |index| block } -> ary beg = 0 len = self.size elsif !arg0.nil? && arg0.kind_of?(Range) # ary.fill(range) { |index| block } -> ary beg = arg0.begin beg += self.size if beg < 0 len = arg0.end len += self.size if len < 0 len += 1 unless arg0.exclude_end? elsif !arg0.nil? # ary.fill(start [, length] ) { |index| block } -> ary beg = arg0 beg += self.size if beg < 0 if arg1.nil? len = self.size else len = arg0 + arg1 end end else if !arg0.nil? && arg1.nil? && arg2.nil? # ary.fill(obj) -> ary beg = 0 len = self.size elsif !arg0.nil? && !arg1.nil? && arg1.kind_of?(Range) # ary.fill(obj, range ) -> ary beg = arg1.begin beg += self.size if beg < 0 len = arg1.end len += self.size if len < 0 len += 1 unless arg1.exclude_end? elsif !arg0.nil? && !arg1.nil? # ary.fill(obj, start [, length]) -> ary beg = arg1 beg += self.size if beg < 0 if arg2.nil? len = self.size else len = beg + arg2 end end end i = beg if block while i < len self[i] = block.call(i) i += 1 end else while i < len self[i] = arg0 i += 1 end end self end ## # call-seq: # ary.rotate(count=1) -> new_ary # # Returns a new array by rotating +self+ so that the element at +count+ is # the first element of the new array. # # If +count+ is negative then it rotates in the opposite direction, starting # from the end of +self+ where +-1+ is the last element. # # a = [ "a", "b", "c", "d" ] # a.rotate #=> ["b", "c", "d", "a"] # a #=> ["a", "b", "c", "d"] # a.rotate(2) #=> ["c", "d", "a", "b"] # a.rotate(-3) #=> ["b", "c", "d", "a"] def rotate(count=1) ary = [] len = self.length if len > 0 idx = (count < 0) ? (len - (~count % len) - 1) : (count % len) # rotate count len.times do ary << self[idx] idx += 1 idx = 0 if idx > len-1 end end ary end ## # call-seq: # ary.rotate!(count=1) -> ary # # Rotates +self+ in place so that the element at +count+ comes first, and # returns +self+. # # If +count+ is negative then it rotates in the opposite direction, starting # from the end of the array where +-1+ is the last element. # # a = [ "a", "b", "c", "d" ] # a.rotate! #=> ["b", "c", "d", "a"] # a #=> ["b", "c", "d", "a"] # a.rotate!(2) #=> ["d", "a", "b", "c"] # a.rotate!(-3) #=> ["a", "b", "c", "d"] def rotate!(count=1) self.replace(self.rotate(count)) end ## # call-seq: # ary.delete_if { |item| block } -> ary # ary.delete_if -> Enumerator # # Deletes every element of +self+ for which block evaluates to +true+. # # The array is changed instantly every time the block is called, not after # the iteration is over. # # See also Array#reject! # # If no block is given, an Enumerator is returned instead. # # scores = [ 97, 42, 75 ] # scores.delete_if {|score| score < 80 } #=> [97] def delete_if(&block) return to_enum :delete_if unless block idx = 0 while idx < self.size do if block.call(self[idx]) self.delete_at(idx) else idx += 1 end end self end ## # call-seq: # ary.reject! { |item| block } -> ary or nil # ary.reject! -> Enumerator # # Equivalent to Array#delete_if, deleting elements from +self+ for which the # block evaluates to +true+, but returns +nil+ if no changes were made. # # The array is changed instantly every time the block is called, not after # the iteration is over. # # See also Enumerable#reject and Array#delete_if. # # If no block is given, an Enumerator is returned instead. def reject!(&block) return to_enum :reject! unless block len = self.size idx = 0 while idx < self.size do if block.call(self[idx]) self.delete_at(idx) else idx += 1 end end if self.size == len nil else self end end ## # call-seq: # ary.insert(index, obj...) -> ary # # Inserts the given values before the element with the given +index+. # # Negative indices count backwards from the end of the array, where +-1+ is # the last element. # # a = %w{ a b c d } # a.insert(2, 99) #=> ["a", "b", 99, "c", "d"] # a.insert(-2, 1, 2, 3) #=> ["a", "b", 99, "c", 1, 2, 3, "d"] def insert(idx, *args) idx += self.size + 1 if idx < 0 self[idx, 0] = args self end ## # call-seq: # ary.bsearch {|x| block } -> elem # # By using binary search, finds a value from this array which meets # the given condition in O(log n) where n is the size of the array. # # You can use this method in two use cases: a find-minimum mode and # a find-any mode. In either case, the elements of the array must be # monotone (or sorted) with respect to the block. # # In find-minimum mode (this is a good choice for typical use case), # the block must return true or false, and there must be an index i # (0 <= i <= ary.size) so that: # # - the block returns false for any element whose index is less than # i, and # - the block returns true for any element whose index is greater # than or equal to i. # # This method returns the i-th element. If i is equal to ary.size, # it returns nil. # # ary = [0, 4, 7, 10, 12] # ary.bsearch {|x| x >= 4 } #=> 4 # ary.bsearch {|x| x >= 6 } #=> 7 # ary.bsearch {|x| x >= -1 } #=> 0 # ary.bsearch {|x| x >= 100 } #=> nil # # In find-any mode (this behaves like libc's bsearch(3)), the block # must return a number, and there must be two indices i and j # (0 <= i <= j <= ary.size) so that: # # - the block returns a positive number for ary[k] if 0 <= k < i, # - the block returns zero for ary[k] if i <= k < j, and # - the block returns a negative number for ary[k] if # j <= k < ary.size. # # Under this condition, this method returns any element whose index # is within i...j. If i is equal to j (i.e., there is no element # that satisfies the block), this method returns nil. # # ary = [0, 4, 7, 10, 12] # # try to find v such that 4 <= v < 8 # ary.bsearch {|x| 1 - (x / 4).truncate } #=> 4 or 7 # # try to find v such that 8 <= v < 10 # ary.bsearch {|x| 4 - (x / 2).truncate } #=> nil # # You must not mix the two modes at a time; the block must always # return either true/false, or always return a number. It is # undefined which value is actually picked up at each iteration. def bsearch(&block) return to_enum :bsearch unless block if idx = bsearch_index(&block) self[idx] else nil end end ## # call-seq: # ary.bsearch_index {|x| block } -> int or nil # # By using binary search, finds an index of a value from this array which # meets the given condition in O(log n) where n is the size of the array. # # It supports two modes, depending on the nature of the block and they are # exactly the same as in the case of #bsearch method with the only difference # being that this method returns the index of the element instead of the # element itself. For more details consult the documentation for #bsearch. def bsearch_index(&block) return to_enum :bsearch_index unless block low = 0 high = size satisfied = false while low < high mid = ((low+high)/2).truncate res = block.call self[mid] case res when 0 # find-any mode: Found! return mid when Numeric # find-any mode: Continue... in_lower_half = res < 0 when true # find-min mode in_lower_half = true satisfied = true when false, nil # find-min mode in_lower_half = false else raise TypeError, 'invalid block result (must be numeric, true, false or nil)' end if in_lower_half high = mid else low = mid + 1 end end satisfied ? low : nil end ## # call-seq: # ary.delete_if { |item| block } -> ary # ary.delete_if -> Enumerator # # Deletes every element of +self+ for which block evaluates to +true+. # # The array is changed instantly every time the block is called, not after # the iteration is over. # # See also Array#reject! # # If no block is given, an Enumerator is returned instead. # # scores = [ 97, 42, 75 ] # scores.delete_if {|score| score < 80 } #=> [97] def delete_if(&block) return to_enum :delete_if unless block idx = 0 while idx < self.size do if block.call(self[idx]) self.delete_at(idx) else idx += 1 end end self end ## # call-seq: # ary.keep_if { |item| block } -> ary # ary.keep_if -> Enumerator # # Deletes every element of +self+ for which the given block evaluates to # +false+. # # See also Array#select! # # If no block is given, an Enumerator is returned instead. # # a = [1, 2, 3, 4, 5] # a.keep_if { |val| val > 3 } #=> [4, 5] def keep_if(&block) return to_enum :keep_if unless block idx = 0 len = self.size while idx < self.size do if block.call(self[idx]) idx += 1 else self.delete_at(idx) end end self end ## # call-seq: # ary.select! {|item| block } -> ary or nil # ary.select! -> Enumerator # # Invokes the given block passing in successive elements from +self+, # deleting elements for which the block returns a +false+ value. # # If changes were made, it will return +self+, otherwise it returns +nil+. # # See also Array#keep_if # # If no block is given, an Enumerator is returned instead. def select!(&block) return to_enum :select! unless block result = [] idx = 0 len = size while idx < len elem = self[idx] result << elem if block.call(elem) idx += 1 end return nil if len == result.size self.replace(result) end ## # call-seq: # ary.index(val) -> int or nil # ary.index {|item| block } -> int or nil # # Returns the _index_ of the first object in +ary+ such that the object is # == to +obj+. # # If a block is given instead of an argument, returns the _index_ of the # first object for which the block returns +true+. Returns +nil+ if no # match is found. # # ISO 15.2.12.5.14 def index(val=NONE, &block) return to_enum(:find_index, val) if !block && val == NONE if block idx = 0 len = size while idx < len return idx if block.call self[idx] idx += 1 end else return self.__ary_index(val) end nil end ## # call-seq: # ary.dig(idx, ...) -> object # # Extracts the nested value specified by the sequence of idx # objects by calling +dig+ at each step, returning +nil+ if any # intermediate step is +nil+. # def dig(idx,*args) n = self[idx] if args.size > 0 n&.dig(*args) else n end end ## # call-seq: # ary.permutation { |p| block } -> ary # ary.permutation -> Enumerator # ary.permutation(n) { |p| block } -> ary # ary.permutation(n) -> Enumerator # # When invoked with a block, yield all permutations of length +n+ of the # elements of the array, then return the array itself. # # If +n+ is not specified, yield all permutations of all elements. # # The implementation makes no guarantees about the order in which the # permutations are yielded. # # If no block is given, an Enumerator is returned instead. # # Examples: # # a = [1, 2, 3] # a.permutation.to_a #=> [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] # a.permutation(1).to_a #=> [[1],[2],[3]] # a.permutation(2).to_a #=> [[1,2],[1,3],[2,1],[2,3],[3,1],[3,2]] # a.permutation(3).to_a #=> [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] # a.permutation(0).to_a #=> [[]] # one permutation of length 0 # a.permutation(4).to_a #=> [] # no permutations of length 4 def permutation(n=self.size, &block) size = self.size return to_enum(:permutation, n) unless block return if n > size if n == 0 yield [] else i = 0 while i 0 ary = self[0...i] + self[i+1..-1] ary.permutation(n-1) do |c| yield result + c end else yield result end i += 1 end end end ## # call-seq: # ary.combination(n) { |c| block } -> ary # ary.combination(n) -> Enumerator # # When invoked with a block, yields all combinations of length +n+ of elements # from the array and then returns the array itself. # # The implementation makes no guarantees about the order in which the # combinations are yielded. # # If no block is given, an Enumerator is returned instead. # # Examples: # # a = [1, 2, 3, 4] # a.combination(1).to_a #=> [[1],[2],[3],[4]] # a.combination(2).to_a #=> [[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]] # a.combination(3).to_a #=> [[1,2,3],[1,2,4],[1,3,4],[2,3,4]] # a.combination(4).to_a #=> [[1,2,3,4]] # a.combination(0).to_a #=> [[]] # one combination of length 0 # a.combination(5).to_a #=> [] # no combinations of length 5 def combination(n, &block) size = self.size return to_enum(:combination, n) unless block return if n > size if n == 0 yield [] elsif n == 1 i = 0 while i new_ary # # Assumes that self is an array of arrays and transposes the rows and columns. # # If the length of the subarrays don't match, an IndexError is raised. # # Examples: # # a = [[1,2], [3,4], [5,6]] # a.transpose #=> [[1, 3, 5], [2, 4, 6]] def transpose return [] if empty? column_count = nil self.each do |row| raise TypeError unless row.is_a?(Array) column_count ||= row.count raise IndexError, 'element size differs' unless column_count == row.count end Array.new(column_count) do |column_index| self.map { |row| row[column_index] } end end ## # call-seq: # ary.to_h -> Hash # ary.to_h{|item| ... } -> Hash # # Returns the result of interpreting aray as an array of # [key, value] pairs. If a block is given, it should # return [key, value] pairs to construct a hash. # # [[:foo, :bar], [1, 2]].to_h # # => {:foo => :bar, 1 => 2} # [1, 2].to_h{|x| [x, x*2]} # # => {1 => 2, 2 => 4} # def to_h(&blk) h = {} self.each do |v| v = blk.call(v) if blk raise TypeError, "wrong element type #{v.class}" unless Array === v raise ArgumentError, "wrong array length (expected 2, was #{v.length})" unless v.length == 2 h[v[0]] = v[1] end h end end mruby-2.0.0/mrbgems/mruby-array-ext/src/000077500000000000000000000000001340361412400201405ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-array-ext/src/array.c000066400000000000000000000116731340361412400214320ustar00rootroot00000000000000#include #include #include #include #include /* * call-seq: * ary.assoc(obj) -> new_ary or nil * * Searches through an array whose elements are also arrays * comparing _obj_ with the first element of each contained array * using obj.==. * Returns the first contained array that matches (that * is, the first associated array), * or +nil+ if no match is found. * See also Array#rassoc. * * s1 = [ "colors", "red", "blue", "green" ] * s2 = [ "letters", "a", "b", "c" ] * s3 = "foo" * a = [ s1, s2, s3 ] * a.assoc("letters") #=> [ "letters", "a", "b", "c" ] * a.assoc("foo") #=> nil */ static mrb_value mrb_ary_assoc(mrb_state *mrb, mrb_value ary) { mrb_int i; mrb_value v, k; mrb_get_args(mrb, "o", &k); for (i = 0; i < RARRAY_LEN(ary); ++i) { v = mrb_check_array_type(mrb, RARRAY_PTR(ary)[i]); if (!mrb_nil_p(v) && RARRAY_LEN(v) > 0 && mrb_equal(mrb, RARRAY_PTR(v)[0], k)) return v; } return mrb_nil_value(); } /* * call-seq: * ary.rassoc(obj) -> new_ary or nil * * Searches through the array whose elements are also arrays. Compares * _obj_ with the second element of each contained array using * ==. Returns the first contained array that matches. See * also Array#assoc. * * a = [ [ 1, "one"], [2, "two"], [3, "three"], ["ii", "two"] ] * a.rassoc("two") #=> [2, "two"] * a.rassoc("four") #=> nil */ static mrb_value mrb_ary_rassoc(mrb_state *mrb, mrb_value ary) { mrb_int i; mrb_value v, value; mrb_get_args(mrb, "o", &value); for (i = 0; i < RARRAY_LEN(ary); ++i) { v = RARRAY_PTR(ary)[i]; if (mrb_type(v) == MRB_TT_ARRAY && RARRAY_LEN(v) > 1 && mrb_equal(mrb, RARRAY_PTR(v)[1], value)) return v; } return mrb_nil_value(); } /* * call-seq: * ary.at(index) -> obj or nil * * Returns the element at _index_. A * negative index counts from the end of +self+. Returns +nil+ * if the index is out of range. See also Array#[]. * * a = [ "a", "b", "c", "d", "e" ] * a.at(0) #=> "a" * a.at(-1) #=> "e" */ static mrb_value mrb_ary_at(mrb_state *mrb, mrb_value ary) { mrb_int pos; mrb_get_args(mrb, "i", &pos); return mrb_ary_entry(ary, pos); } static mrb_value mrb_ary_values_at(mrb_state *mrb, mrb_value self) { mrb_int argc; mrb_value *argv; mrb_get_args(mrb, "*", &argv, &argc); return mrb_get_values_at(mrb, self, RARRAY_LEN(self), argc, argv, mrb_ary_ref); } /* * call-seq: * ary.slice!(index) -> obj or nil * ary.slice!(start, length) -> new_ary or nil * ary.slice!(range) -> new_ary or nil * * Deletes the element(s) given by an +index+ (optionally up to +length+ * elements) or by a +range+. * * Returns the deleted object (or objects), or +nil+ if the +index+ is out of * range. * * a = [ "a", "b", "c" ] * a.slice!(1) #=> "b" * a #=> ["a", "c"] * a.slice!(-1) #=> "c" * a #=> ["a"] * a.slice!(100) #=> nil * a #=> ["a"] */ static mrb_value mrb_ary_slice_bang(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); mrb_int i, j, k, len, alen; mrb_value val; mrb_value *ptr; mrb_value ary; mrb_ary_modify(mrb, a); if (mrb_get_argc(mrb) == 1) { mrb_value index; mrb_get_args(mrb, "o|i", &index, &len); switch (mrb_type(index)) { case MRB_TT_RANGE: if (mrb_range_beg_len(mrb, index, &i, &len, ARY_LEN(a), TRUE) == 1) { goto delete_pos_len; } else { return mrb_nil_value(); } case MRB_TT_FIXNUM: val = mrb_funcall(mrb, self, "delete_at", 1, index); return val; default: val = mrb_funcall(mrb, self, "delete_at", 1, index); return val; } } mrb_get_args(mrb, "ii", &i, &len); delete_pos_len: alen = ARY_LEN(a); if (i < 0) i += alen; if (i < 0 || alen < i) return mrb_nil_value(); if (len < 0) return mrb_nil_value(); if (alen == i) return mrb_ary_new(mrb); if (len > alen - i) len = alen - i; ary = mrb_ary_new_capa(mrb, len); ptr = ARY_PTR(a); for (j = i, k = 0; k < len; ++j, ++k) { mrb_ary_push(mrb, ary, ptr[j]); } ptr += i; for (j = i; j < alen - len; ++j) { *ptr = *(ptr+len); ++ptr; } mrb_ary_resize(mrb, self, alen - len); return ary; } void mrb_mruby_array_ext_gem_init(mrb_state* mrb) { struct RClass * a = mrb->array_class; mrb_define_method(mrb, a, "assoc", mrb_ary_assoc, MRB_ARGS_REQ(1)); mrb_define_method(mrb, a, "at", mrb_ary_at, MRB_ARGS_REQ(1)); mrb_define_method(mrb, a, "rassoc", mrb_ary_rassoc, MRB_ARGS_REQ(1)); mrb_define_method(mrb, a, "values_at", mrb_ary_values_at, MRB_ARGS_ANY()); mrb_define_method(mrb, a, "slice!", mrb_ary_slice_bang, MRB_ARGS_ANY()); } void mrb_mruby_array_ext_gem_final(mrb_state* mrb) { } mruby-2.0.0/mrbgems/mruby-array-ext/test/000077500000000000000000000000001340361412400203305ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-array-ext/test/array.rb000066400000000000000000000267721340361412400220110ustar00rootroot00000000000000## # Array(Ext) Test assert("Array#assoc") do s1 = [ "colors", "red", "blue", "green" ] s2 = [ "letters", "a", "b", "c" ] s3 = "foo" a = [ s1, s2, s3 ] assert_equal [ "letters", "a", "b", "c" ], a.assoc("letters") assert_nil a.assoc("foo") end assert("Array#at") do a = [ "a", "b", "c", "d", "e" ] assert_equal "a", a.at(0) assert_equal "e", a.at(-1) end assert("Array#rassoc") do a = [ [ 1, "one"], [2, "two"], [3, "three"], ["ii", "two"] ] assert_equal [2, "two"], a.rassoc("two") assert_nil a.rassoc("four") end assert("Array#uniq!") do a = [1, 2, 3, 1] a.uniq! assert_equal [1, 2, 3], a b = [ "a", "b", "c" ] assert_nil b.uniq! c = [["student","sam"], ["student","george"], ["teacher","matz"]] assert_equal [["student", "sam"], ["teacher", "matz"]], c.uniq! { |s| s.first } d = [["student","sam"], ["teacher","matz"]] assert_nil d.uniq! { |s| s.first } end assert("Array#uniq") do a = [1, 2, 3, 1] assert_equal [1, 2, 3], a.uniq assert_equal [1, 2, 3, 1], a b = [["student","sam"], ["student","george"], ["teacher","matz"]] assert_equal [["student", "sam"], ["teacher", "matz"]], b.uniq { |s| s.first } end assert("Array#-") do a = [1, 2, 3, 1] b = [1] c = 1 assert_raise(TypeError) { a - c } assert_equal [2, 3], (a - b) assert_equal [1, 2, 3, 1], a end assert("Array#|") do a = [1, 2, 3, 1] b = [1, 4] c = 1 assert_raise(TypeError) { a | c } assert_equal [1, 2, 3, 4], (a | b) assert_equal [1, 2, 3, 1], a end assert("Array#union") do a = [1, 2, 3, 1] b = [1, 4] c = [1, 5] assert_equal [1, 2, 3, 4, 5], a.union(b,c) end assert("Array#&") do a = [1, 2, 3, 1] b = [1, 4] c = 1 assert_raise(TypeError) { a & c } assert_equal [1], (a & b) assert_equal [1, 2, 3, 1], a end assert("Array#flatten") do assert_equal [1, 2, "3", {4=>5}, :'6'], [1, 2, "3", {4=>5}, :'6'].flatten assert_equal [1, 2, 3, 4, 5, 6], [1, 2, [3, 4, 5], 6].flatten assert_equal [1, 2, 3, 4, 5, 6], [1, 2, [3, [4, 5], 6]].flatten assert_equal [1, [2, [3, [4, [5, [6]]]]]], [1, [2, [3, [4, [5, [6]]]]]].flatten(0) assert_equal [1, 2, [3, [4, [5, [6]]]]], [1, [2, [3, [4, [5, [6]]]]]].flatten(1) assert_equal [1, 2, 3, [4, [5, [6]]]], [1, [2, [3, [4, [5, [6]]]]]].flatten(2) assert_equal [1, 2, 3, 4, [5, [6]]], [1, [2, [3, [4, [5, [6]]]]]].flatten(3) assert_equal [1, 2, 3, 4, 5, [6]], [1, [2, [3, [4, [5, [6]]]]]].flatten(4) assert_equal [1, 2, 3, 4, 5, 6], [1, [2, [3, [4, [5, [6]]]]]].flatten(5) end assert("Array#flatten!") do assert_equal [1, 2, 3, 4, 5, 6], [1, 2, [3, [4, 5], 6]].flatten! end assert("Array#compact") do a = [1, nil, "2", nil, :t, false, nil] assert_equal [1, "2", :t, false], a.compact assert_equal [1, nil, "2", nil, :t, false, nil], a end assert("Array#compact!") do a = [1, nil, "2", nil, :t, false, nil] a.compact! assert_equal [1, "2", :t, false], a end assert("Array#fetch") do a = [ 11, 22, 33, 44 ] assert_equal 22, a.fetch(1) assert_equal 44, a.fetch(-1) assert_equal 'cat', a.fetch(4, 'cat') ret = 0 a.fetch(100) { |i| ret = i } assert_equal 100, ret assert_raise(IndexError) { a.fetch(100) } end assert("Array#fill") do a = [ "a", "b", "c", "d" ] assert_equal ["x", "x", "x", "x"], a.fill("x") assert_equal ["x", "x", "x", "w"], a.fill("w", -1) assert_equal ["x", "x", "z", "z"], a.fill("z", 2, 2) assert_equal ["y", "y", "z", "z"], a.fill("y", 0..1) assert_equal [0, 1, 4, 9], a.fill { |i| i*i } assert_equal [0, 1, 8, 27], a.fill(-2) { |i| i*i*i } assert_equal [0, 2, 3, 27], a.fill(1, 2) { |i| i+1 } assert_equal [1, 2, 3, 27], a.fill(0..1) { |i| i+1 } assert_raise(ArgumentError) { a.fill } assert_equal([0, 1, 2, 3, -1, 5], [0, 1, 2, 3, 4, 5].fill(-1, -2, 1)) assert_equal([0, 1, 2, 3, -1, -1, -1], [0, 1, 2, 3, 4, 5].fill(-1, -2, 3)) assert_equal([0, 1, 2, -1, -1, 5], [0, 1, 2, 3, 4, 5].fill(-1, 3..4)) assert_equal([0, 1, 2, -1, 4, 5], [0, 1, 2, 3, 4, 5].fill(-1, 3...4)) assert_equal([0, 1, -1, -1, -1, 5], [0, 1, 2, 3, 4, 5].fill(-1, 2..-2)) assert_equal([0, 1, -1, -1, 4, 5], [0, 1, 2, 3, 4, 5].fill(-1, 2...-2)) assert_equal([0, 1, 2, 13, 14, 5], [0, 1, 2, 3, 4, 5].fill(3..4){|i| i+10}) assert_equal([0, 1, 2, 13, 4, 5], [0, 1, 2, 3, 4, 5].fill(3...4){|i| i+10}) assert_equal([0, 1, 12, 13, 14, 5], [0, 1, 2, 3, 4, 5].fill(2..-2){|i| i+10}) assert_equal([0, 1, 12, 13, 4, 5], [0, 1, 2, 3, 4, 5].fill(2...-2){|i| i+10}) assert_equal [1, 2, 3, 4, 'x', 'x'], [1, 2, 3, 4, 5, 6].fill('x', -2..-1) assert_equal [1, 2, 3, 4, 'x', 6], [1, 2, 3, 4, 5, 6].fill('x', -2...-1) assert_equal [1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6].fill('x', -2...-2) assert_equal [1, 2, 3, 4, 'x', 6], [1, 2, 3, 4, 5, 6].fill('x', -2..-2) assert_equal [1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6].fill('x', -2..0) end assert("Array#reverse_each") do a = [ "a", "b", "c", "d" ] b = [] a.reverse_each do |i| b << i end assert_equal [ "d", "c", "b", "a" ], b if Object.const_defined?(:Enumerator) assert_equal [ "d", "c", "b", "a" ], a.reverse_each.to_a else true end end assert("Array#rotate") do a = ["a", "b", "c", "d"] assert_equal ["b", "c", "d", "a"], a.rotate assert_equal ["a", "b", "c", "d"], a assert_equal ["c", "d", "a", "b"], a.rotate(2) assert_equal ["b", "c", "d", "a"], a.rotate(-3) assert_equal ["c", "d", "a", "b"], a.rotate(10) assert_equal [], [].rotate end assert("Array#rotate!") do a = ["a", "b", "c", "d"] assert_equal ["b", "c", "d", "a"], a.rotate! assert_equal ["b", "c", "d", "a"], a assert_equal ["d", "a", "b", "c"], a.rotate!(2) assert_equal ["a", "b", "c", "d"], a.rotate!(-3) assert_equal ["c", "d", "a", "b"], a.rotate(10) assert_equal [], [].rotate! end assert("Array#delete_if") do a = [1, 2, 3, 4, 5] assert_equal [1, 2, 3, 4, 5], a.delete_if { false } assert_equal [1, 2, 3, 4, 5], a a = [1, 2, 3, 4, 5] assert_equal [], a.delete_if { true } assert_equal [], a a = [1, 2, 3, 4, 5] assert_equal [1, 2, 3], a.delete_if { |i| i > 3 } assert_equal [1, 2, 3], a end assert("Array#reject!") do a = [1, 2, 3, 4, 5] assert_nil a.reject! { false } assert_equal [1, 2, 3, 4, 5], a a = [1, 2, 3, 4, 5] assert_equal [], a.reject! { true } assert_equal [], a a = [1, 2, 3, 4, 5] assert_equal [1, 2, 3], a.reject! { |val| val > 3 } assert_equal [1, 2, 3], a end assert("Array#insert") do a = ["a", "b", "c", "d"] assert_equal ["a", "b", 99, "c", "d"], a.insert(2, 99) assert_equal ["a", "b", 99, "c", 1, 2, 3, "d"], a.insert(-2, 1, 2, 3) b = ["a", "b", "c", "d"] assert_equal ["a", "b", "c", "d", nil, nil, 99], b.insert(6, 99) end assert("Array#bsearch") do # Find minimum mode a = [0, 2, 4] assert_equal 0, a.bsearch{ |x| x >= -1 } assert_equal 0, a.bsearch{ |x| x >= 0 } assert_equal 2, a.bsearch{ |x| x >= 1 } assert_equal 2, a.bsearch{ |x| x >= 2 } assert_equal 4, a.bsearch{ |x| x >= 3 } assert_equal 4, a.bsearch{ |x| x >= 4 } assert_nil a.bsearch{ |x| x >= 5 } # Find any mode a = [0, 4, 8] def between(lo, x, hi) if x < lo 1 elsif x > hi -1 else 0 end end assert_nil a.bsearch{ |x| between(-3, x, -1) } assert_equal 0, a.bsearch{ |x| between(-1, x, 1) } assert_nil a.bsearch{ |x| between( 1, x, 3) } assert_equal 4, a.bsearch{ |x| between( 3, x, 5) } assert_nil a.bsearch{ |x| between( 5, x, 7) } assert_equal 8, a.bsearch{ |x| between( 7, x, 9) } assert_nil a.bsearch{ |x| between( 9, x, 11) } assert_equal 0, a.bsearch{ |x| between( 0, x, 3) } assert_equal 4, a.bsearch{ |x| between( 0, x, 4) } assert_equal 4, a.bsearch{ |x| between( 4, x, 8) } assert_equal 8, a.bsearch{ |x| between( 5, x, 8) } # Invalid block result assert_raise TypeError, 'invalid block result (must be numeric, true, false or nil)' do a.bsearch{ 'I like to watch the world burn' } end end assert("Array#bsearch_index") do # tested through Array#bsearch end assert("Array#delete_if") do a = [1, 2, 3, 4, 5] assert_equal [1, 2, 3, 4, 5], a.delete_if { false } assert_equal [1, 2, 3, 4, 5], a a = [1, 2, 3, 4, 5] assert_equal [], a.delete_if { true } assert_equal [], a a = [ 1, 2, 3, 4, 5 ] assert_equal [1, 2, 3], a.delete_if { |val| val > 3 } end assert("Array#keep_if") do a = [1, 2, 3, 4, 5] assert_equal [1, 2, 3, 4, 5], a.keep_if { true } assert_equal [1, 2, 3, 4, 5], a a = [1, 2, 3, 4, 5] assert_equal [], a.keep_if { false } assert_equal [], a a = [1, 2, 3, 4, 5] assert_equal [4, 5], a.keep_if { |val| val > 3 } assert_equal [4, 5], a end assert("Array#select!") do a = [1, 2, 3, 4, 5] assert_nil a.select! { true } assert_equal [1, 2, 3, 4, 5], a a = [1, 2, 3, 4, 5] assert_equal [], a.select! { false } assert_equal [], a a = [1, 2, 3, 4, 5] assert_equal [4, 5], a.select! { |val| val > 3 } assert_equal [4, 5], a end assert('Array#values_at') do a = %w{red green purple white none} assert_equal %w{red purple none}, a.values_at(0, 2, 4) assert_equal ['green', 'white', nil, nil], a.values_at(1, 3, 5, 7) assert_equal ['none', 'white', 'white', nil], a.values_at(-1, -2, -2, -7) assert_equal ['none', nil, nil, 'red', 'green', 'purple'], a.values_at(4..6, 0...3) assert_raise(TypeError) { a.values_at 'tt' } end assert('Array#to_h') do assert_equal({}, [].to_h) assert_equal({a: 1, b:2}, [[:a, 1], [:b, 2]].to_h) assert_raise(TypeError) { [1].to_h } assert_raise(ArgumentError) { [[1]].to_h } end assert("Array#index (block)") do assert_nil (1..10).to_a.index { |i| i % 5 == 0 and i % 7 == 0 } assert_equal 34, (1..100).to_a.index { |i| i % 5 == 0 and i % 7 == 0 } end assert("Array#dig") do h = [[[1]], 0] assert_equal(1, h.dig(0, 0, 0)) assert_nil(h.dig(2, 0)) assert_raise(TypeError) {h.dig(:a)} end assert("Array#slice!") do a = [1, 2, 3] b = a.slice!(0) c = [1, 2, 3, 4, 5] d = c.slice!(0, 2) e = [1, 2, 3, 4, 5] f = e.slice!(1..3) g = [1, 2, 3] h = g.slice!(-1) i = [1, 2, 3] j = i.slice!(0, -1) assert_equal(a, [2, 3]) assert_equal(b, 1) assert_equal(c, [3, 4, 5]) assert_equal(d, [1, 2]) assert_equal(e, [1, 5]) assert_equal(f, [2, 3, 4]) assert_equal(g, [1, 2]) assert_equal(h, 3) assert_equal(i, [1, 2, 3]) assert_equal(j, nil) end assert("Array#permutation") do a = [1, 2, 3] assert_equal([[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]], a.permutation.to_a) assert_equal([[1],[2],[3]], a.permutation(1).to_a) assert_equal([[1,2],[1,3],[2,1],[2,3],[3,1],[3,2]], a.permutation(2).to_a) assert_equal([[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]], a.permutation(3).to_a) assert_equal([[]], a.permutation(0).to_a) assert_equal([], a.permutation(4).to_a) end assert("Array#combination") do a = [1, 2, 3, 4] assert_equal([[1],[2],[3],[4]], a.combination(1).to_a) assert_equal([[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]], a.combination(2).to_a) assert_equal([[1,2,3],[1,2,4],[1,3,4],[2,3,4]], a.combination(3).to_a) assert_equal([[1,2,3,4]], a.combination(4).to_a) assert_equal([[]], a.combination(0).to_a) assert_equal([], a.combination(5).to_a) end assert('Array#transpose') do assert_equal([].transpose, []) assert_equal([[]].transpose, []) assert_equal([[1]].transpose, [[1]]) assert_equal([[1,2,3]].transpose, [[1], [2], [3]]) assert_equal([[1], [2], [3]].transpose, [[1,2,3]]) assert_equal([[1,2], [3,4], [5,6]].transpose, [[1,3,5], [2,4,6]]) assert_raise(TypeError) { [1].transpose } assert_raise(IndexError) { [[1], [2,3,4]].transpose } end mruby-2.0.0/mrbgems/mruby-bin-debugger/000077500000000000000000000000001340361412400177675ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-bin-debugger/bintest/000077500000000000000000000000001340361412400214375ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-bin-debugger/bintest/mrdb.rb000066400000000000000000000207571340361412400227230ustar00rootroot00000000000000require 'open3' require 'tempfile' class BinTest_MrubyBinDebugger @debug1=false @debug2=true @debug3=true def self.test(rubysource, testcase) script, bin = Tempfile.new(['test', '.rb']), Tempfile.new(['test', '.mrb']) # .rb script.write rubysource script.flush # compile `./bin/mrbc -g -o "#{bin.path}" "#{script.path}"` # add mrdb quit testcase << {:cmd=>"quit"} stdin_data = testcase.map{|t| t[:cmd]}.join("\n") << "\n" ["bin/mrdb #{script.path}","bin/mrdb -b #{bin.path}"].each do |cmd| o, s = Open3.capture2(cmd, :stdin_data => stdin_data) exp_vals = testcase.map{|t| t.fetch(:exp, nil)} unexp_vals = testcase.map{|t| t.fetch(:unexp, nil)} if @debug1 o.split("\n").each_with_index do |i,actual| p [i,actual] end end # compare actual / expected o.split("\n").each do |actual| next if actual.empty? exp = exp_vals.shift if @debug2 a = true a = actual.include?(exp) unless exp.nil? p [actual, exp] unless a end assert_true actual.include?(exp) unless exp.nil? end # compare actual / unexpected o.split("\n").each do |actual| next if actual.empty? unexp = unexp_vals.shift if @debug3 a = false a = actual.include?(unexp) unless unexp.nil? p [actual, unexp] if a end assert_false actual.include?(unexp) unless unexp.nil? end end end end INVCMD = "invalid command" assert('mruby-bin-debugger(mrdb) command line') do # ruby source src = "foo = 'foo'\n" str = "" 103.times { str += "1234567890" } cmd = "p a=#{str}" # test case BinTest_MrubyBinDebugger.test(src, [{:cmd=>cmd[0...1023], :unexp=>'command line too long.'}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>cmd[0...1024], :unexp=>'command line too long.'}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>cmd[0...1025], :exp=>'command line too long.'}]) end assert('mruby-bin-debugger(mrdb) command: "break"') do # ruby source src = "foo = 'foo'\n" # test case tc = [] tc << {:cmd=>"b", :unexp=>INVCMD} tc << {:cmd=>"br", :unexp=>INVCMD} tc << {:cmd=>"brea", :unexp=>INVCMD} tc << {:cmd=>"break", :unexp=>INVCMD} BinTest_MrubyBinDebugger.test(src, tc) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"bl", :exp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"breaka", :exp=>INVCMD}]) end assert('mruby-bin-debugger(mrdb) command: "continue"') do # ruby source src = "foo = 'foo'\n" # test case BinTest_MrubyBinDebugger.test(src, [{:cmd=>"c", :unexp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"co", :unexp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"continu", :unexp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"continue", :unexp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"cn", :exp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"continuee", :exp=>INVCMD}]) end assert('mruby-bin-debugger(mrdb) command: "delete"') do # ruby source src = "foo = 'foo'\n" # test case tc = [] tc << {:cmd=>"d 1", :unexp=>INVCMD} tc << {:cmd=>"de 1", :unexp=>INVCMD} tc << {:cmd=>"delet 1", :unexp=>INVCMD} tc << {:cmd=>"delete 1", :unexp=>INVCMD} BinTest_MrubyBinDebugger.test(src, tc) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"dd 1", :exp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"deletee 1", :exp=>INVCMD}]) end assert('mruby-bin-debugger(mrdb) command: "disable"') do # ruby source src = "foo = 'foo'\n" # test case tc = [] tc << {:cmd=>"dis", :unexp=>INVCMD} tc << {:cmd=>"disa", :unexp=>INVCMD} tc << {:cmd=>"disabl", :unexp=>INVCMD} tc << {:cmd=>"disable", :unexp=>INVCMD} BinTest_MrubyBinDebugger.test(src, tc) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"di", :exp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"disb", :exp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"disablee", :exp=>INVCMD}]) end assert('mruby-bin-debugger(mrdb) command: "enable"') do # ruby source src = "foo = 'foo'\n" # test case tc = [] tc << {:cmd=>"en", :unexp=>INVCMD} tc << {:cmd=>"ena", :unexp=>INVCMD} tc << {:cmd=>"enabl", :unexp=>INVCMD} tc << {:cmd=>"enable", :unexp=>INVCMD} BinTest_MrubyBinDebugger.test(src, tc) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"e", :exp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"enb", :exp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"enablee", :exp=>INVCMD}]) end assert('mruby-bin-debugger(mrdb) command: "eval"') do # ruby source src = "foo = 'foo'\n" # test case tc = [] tc << {:cmd=>"ev", :unexp=>INVCMD} tc << {:cmd=>"eva", :unexp=>INVCMD} tc << {:cmd=>"eval", :unexp=>INVCMD} BinTest_MrubyBinDebugger.test(src, tc) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"e", :exp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"evl", :exp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"evall", :exp=>INVCMD}]) end assert('mruby-bin-debugger(mrdb) command: "help"') do # ruby source src = "foo = 'foo'\n" # test case tc = [] tc << {:cmd=>"h", :unexp=>INVCMD} tc << {:cmd=>"he", :unexp=>INVCMD} tc << {:cmd=>"hel", :unexp=>INVCMD} tc << {:cmd=>"help", :unexp=>INVCMD} BinTest_MrubyBinDebugger.test(src, tc) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"hl", :exp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"helpp", :exp=>INVCMD}]) end assert('mruby-bin-debugger(mrdb) command: "info breakpoints"') do # ruby source src = "foo = 'foo'\n" # test case tc = [] tc << {:cmd=>"i b", :unexp=>INVCMD} tc << {:cmd=>"in b", :unexp=>INVCMD} tc << {:cmd=>"i br", :unexp=>INVCMD} tc << {:cmd=>"inf breakpoint", :unexp=>INVCMD} tc << {:cmd=>"info breakpoints", :unexp=>INVCMD} BinTest_MrubyBinDebugger.test(src, tc) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"ii b", :exp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"i bb", :exp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"infoo breakpoints", :exp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"info breakpointss", :exp=>INVCMD}]) end assert('mruby-bin-debugger(mrdb) command: "list"') do # ruby source src = "foo = 'foo'\n" # test case tc = [] tc << {:cmd=>"l", :unexp=>INVCMD} tc << {:cmd=>"li", :unexp=>INVCMD} tc << {:cmd=>"lis", :unexp=>INVCMD} tc << {:cmd=>"list", :unexp=>INVCMD} BinTest_MrubyBinDebugger.test(src, tc) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"ll", :exp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"listt", :exp=>INVCMD}]) end assert('mruby-bin-debugger(mrdb) command: "print"') do # ruby source src = "foo = 'foo'\n" # test case tc = [] tc << {:cmd=>"p", :unexp=>INVCMD} tc << {:cmd=>"pr", :unexp=>INVCMD} tc << {:cmd=>"prin", :unexp=>INVCMD} tc << {:cmd=>"print", :unexp=>INVCMD} BinTest_MrubyBinDebugger.test(src, tc) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"pp", :exp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"printt", :exp=>INVCMD}]) end assert('mruby-bin-debugger(mrdb) command: "quit"') do # ruby source src = "foo = 'foo'\n" # test case BinTest_MrubyBinDebugger.test(src, [{:cmd=>"q", :unexp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"qu", :unexp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"qui", :unexp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"quit", :unexp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"qq", :exp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"quitt", :exp=>INVCMD}]) end assert('mruby-bin-debugger(mrdb) command: "run"') do # ruby source src = "foo = 'foo'\n" # test case BinTest_MrubyBinDebugger.test(src, [{:cmd=>"r", :unexp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"ru", :unexp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"run", :unexp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"rr", :exp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"runn", :exp=>INVCMD}]) end assert('mruby-bin-debugger(mrdb) command: "step"') do # ruby source src = <<"SRC" while true foo = 'foo' end SRC # test case tc = [] tc << {:cmd=>"s", :unexp=>INVCMD} tc << {:cmd=>"st", :unexp=>INVCMD} tc << {:cmd=>"ste", :unexp=>INVCMD} tc << {:cmd=>"step", :unexp=>INVCMD} BinTest_MrubyBinDebugger.test(src, tc) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"ss", :exp=>INVCMD}]) BinTest_MrubyBinDebugger.test(src, [{:cmd=>"stepp", :exp=>INVCMD}]) end mruby-2.0.0/mrbgems/mruby-bin-debugger/bintest/print.rb000066400000000000000000000422021340361412400231200ustar00rootroot00000000000000require 'open3' require 'tempfile' class BinTest_MrubyBinDebugger @debug1=false @debug2=true def self.test(rubysource, testcase) script, bin = Tempfile.new(['test', '.rb']), Tempfile.new(['test', '.mrb']) # .rb script.write rubysource script.flush # compile `./bin/mrbc -g -o "#{bin.path}" "#{script.path}"` # add mrdb quit testcase << {:cmd=>"quit"} stdin_data = testcase.map{|t| t[:cmd]}.join("\n") << "\n" ["bin/mrdb #{script.path}","bin/mrdb -b #{bin.path}"].each do |cmd| o, s = Open3.capture2(cmd, :stdin_data => stdin_data) exp_vals = testcase.map{|t| t.fetch(:exp, nil)} =begin if @debug1 o.split("\n").each_with_index do |i,actual| p [i,actual] end end # compare actual / expected o.split("\n").each do |actual| next if actual.empty? exp = exp_vals.shift if @debug2 a = true a = actual.include?(exp) unless exp.nil? p [actual, exp] unless a end assert_true actual.include?(exp) unless exp.nil? end =end idx = 0 exp_vals.each do |exp| next if exp.nil? idx = o.index(exp, idx) assert_false idx.nil? break unless idx idx += 1 end end end end assert('mruby-bin-debugger(print) invalid arguments') do # ruby source src = "foo = 'foo'\n" # test case tc = [] tc << {:cmd=>"p", :exp=>"Parameter not specified."} BinTest_MrubyBinDebugger.test(src, tc) end assert('mruby-bin-debugger(print) nomal') do # ruby source src = <<"SRC" foo = 'foo' bar = foo baz = bar SRC # test case tc = [] tc << {:cmd=>"s"} tc << {:cmd=>"p (1+2)", :exp=>'$1 = 3'} tc << {:cmd=>"p foo", :exp=>'$2 = "foo"'} tc << {:cmd=>"p foo*=2", :exp=>'$3 = "foofoo"'} tc << {:cmd=>"s"} tc << {:cmd=>"p bar", :exp=>'$4 = "foofoo"'} BinTest_MrubyBinDebugger.test(src, tc) end assert('mruby-bin-debugger(print) error') do # ruby source src = "foo = 'foo'\n" # test case tc = [] tc << {:cmd=>"p (1+2", :exp=>'$1 = SyntaxError'} tc << {:cmd=>"p bar", :exp=>'$2 = (eval):2: undefined method'} BinTest_MrubyBinDebugger.test(src, tc) end # Kernel#instance_eval(string) does't work multiple statements. =begin assert('mruby-bin-debugger(print) multiple statements') do # ruby source src = <<"SRC" x = 0 y = 0 z = 0 SRC # test case tc = [] tc << {:cmd=>"s",} tc << {:cmd=>"p x=1;x+=2", :exp=>"3"} tc << {:cmd=>"s",} tc << {:cmd=>"p x", :exp=>"3"} BinTest_MrubyBinDebugger.test(src, tc) end =end assert('mruby-bin-debugger(print) scope:top') do # ruby source (bp is break point) src = "bp=nil\n" # test case tc = [] tc << {:cmd=>"p self", :exp=>'$1 = main'} BinTest_MrubyBinDebugger.test(src, tc) end assert('mruby-bin-debugger(print) scope:class') do # ruby source (bp is break point) src = <<"SRC" class TestClassScope bp = nil end SRC # test case tc = [] tc << {:cmd=>"s"} tc << {:cmd=>"p self", :exp=>'$1 = TestClassScope'} BinTest_MrubyBinDebugger.test(src, tc) end assert('mruby-bin-debugger(print) scope:module') do # ruby source (bp is break point) src = <<"SRC" class TestModuleScope bp = nil end SRC # test case tc = [] tc << {:cmd=>"s"} tc << {:cmd=>"p self", :exp=>'$1 = TestModuleScope'} BinTest_MrubyBinDebugger.test(src, tc) end assert('mruby-bin-debugger(print) scope:instance method') do # ruby source (bp is break point) src = <<"SRC" class TestMethodScope def m bp = nil end end TestMethodScope.new.m SRC tc = [] tc << {:cmd=>"b 3"} tc << {:cmd=>"r"} tc << {:cmd=>"p self", :exp=>'$1 = #"b 3"} tc << {:cmd=>"r"} tc << {:cmd=>"p self", :exp=>'$1 = TestClassMethodScope'} BinTest_MrubyBinDebugger.test(src, tc) end assert('mruby-bin-debugger(print) scope:block') do # ruby source (bp is break point) src = <<"SRC" 1.times do bp = nil end class TestBlockScope 1.times do bp = nil end def m 1.times do bp = nil end end end TestBlockScope.new.m SRC tc = [] tc << {:cmd=>"b 2"} tc << {:cmd=>"b 6"} tc << {:cmd=>"b 10"} tc << {:cmd=>"c"} tc << {:cmd=>"p self", :exp=>'$1 = main'} tc << {:cmd=>"c"} tc << {:cmd=>"p self", :exp=>'$2 = TestBlockScope'} tc << {:cmd=>"c"} tc << {:cmd=>"p self", :exp=>'$3 = #"b 6"} tc << {:cmd=>"b 8"} tc << {:cmd=>"b 11"} tc << {:cmd=>"r"} tc << {:cmd=>"p lv", :exp=>'$1 = "class"'} tc << {:cmd=>"c"} tc << {:cmd=>"p lv", :exp=>'$2 = "instance method"'} tc << {:cmd=>"c"} tc << {:cmd=>"p lv", :exp=>'$3 = "top"'} BinTest_MrubyBinDebugger.test(src, tc) end assert('mruby-bin-debugger(print) same name:instance variabe') do # ruby source (bp is break point) src = <<"SRC" @iv = 'top' class TestInstanceVariableName def initialize(v) @iv = v end def m bp = nil end end i1 = TestInstanceVariableName.new('instance1') i2 = TestInstanceVariableName.new('instance2') i1.m i2.m bp = nil SRC tc = [] tc << {:cmd=>"b 7"} tc << {:cmd=>"b 14"} tc << {:cmd=>"r"} tc << {:cmd=>"p @iv", :exp=>'$1 = "instance1"'} tc << {:cmd=>"c"} tc << {:cmd=>"p @iv", :exp=>'$2 = "instance2"'} tc << {:cmd=>"c"} tc << {:cmd=>"p @iv", :exp=>'$3 = "top"'} BinTest_MrubyBinDebugger.test(src, tc) end # Kernel#instance_eval(string) does't work const. =begin assert('mruby-bin-debugger(print) same name:const') do # ruby source (bp is break point) src = <<"SRC" CONST='top' class TestConstNameSuperClass CONST='super class' def m bp = nil end end class TestConstNameSubClass < TestConstNameSuperClass CONST='sub class' def m bp = nil end end TestConstNameSuperClass.new.m() TestConstNameSubClass.new.m() bp = nil SRC # todo: wait for 'break' to be implemented tc = [] 9.times { tc << {:cmd=>"s"} } tc << {:cmd=>"p CONST", :exp=>"super class"} 3.times { tc << {:cmd=>"s"} } tc << {:cmd=>"p CONST", :exp=>"sub class"} 1.times { tc << {:cmd=>"s"} } tc << {:cmd=>"p CONST", :exp=>"top"} BinTest_MrubyBinDebugger.test(src, tc) end =end assert('mruby-bin-debugger(print) Literal:Numeric') do # ruby source src = "foo = 'foo'\n" # test case tc = [] tc << {:cmd=>"p 100", :exp=>'$1 = 100'} tc << {:cmd=>"p -0b100", :exp=>'$2 = -4'} tc << {:cmd=>"p +0100", :exp=>'$3 = 64'} tc << {:cmd=>"p 0x100", :exp=>'$4 = 256'} tc << {:cmd=>"p 1_234", :exp=>'$5 = 1234'} tc << {:cmd=>"p 0b1000_0000", :exp=>"$6 = #{0b1000_0000}"} tc << {:cmd=>"p 0x1000_0000", :exp=>"$7 = #{0x1000_0000}"} tc << {:cmd=>"p 3.14", :exp=>'$8 = 3.14'} tc << {:cmd=>"p -12.3", :exp=>'$9 = -12.3'} tc << {:cmd=>"p +12.000", :exp=>'$10 = 12'} tc << {:cmd=>"p 1e4", :exp=>'$11 = 10000'} tc << {:cmd=>"p -0.1e-2", :exp=>'$12 = -0.001'} BinTest_MrubyBinDebugger.test(src, tc) end assert('mruby-bin-debugger(print) Literal:String') do # ruby source src = <<"SRC" foo = 'foo' bar = "bar" baz = "baz" SRC # test case tc = [] tc << {:cmd=>"s"} tc << {:cmd=>"s"} tc << {:cmd=>'p "str"', :exp=>'$1 = "str"'} tc << {:cmd=>'p "s\tt\rr\n"', :exp=>'$2 = "s\\tt\\rr\\n"'} tc << {:cmd=>'p "\C-a\C-z"', :exp=>'$3 = "\\x01\\x1a"'} tc << {:cmd=>'p "#{foo+bar}"', :exp=>'$4 = "foobar"'} tc << {:cmd=>'p \'str\'', :exp=>'$5 = "str"'} tc << {:cmd=>'p \'s\\tt\\rr\\n\'', :exp=>'$6 = "s\\\\tt\\\\rr\\\\n"'} tc << {:cmd=>'p \'\\C-a\\C-z\'', :exp=>'$7 = "\\\\C-a\\\\C-z"'} tc << {:cmd=>'p \'#{foo+bar}\'', :exp=>'$8 = "\\#{foo+bar}"'} tc << {:cmd=>'p %!str!', :exp=>'$9 = "str"'} tc << {:cmd=>'p %!s\tt\rr\n!', :exp=>'$10 = "s\\tt\\rr\\n"'} tc << {:cmd=>'p %!\C-a\C-z!', :exp=>'$11 = "\\x01\\x1a"'} tc << {:cmd=>'p %!#{foo+bar}!', :exp=>'$12 = "foobar"'} tc << {:cmd=>'p %Q!str!', :exp=>'$13 = "str"'} tc << {:cmd=>'p %Q!s\tt\rr\n!', :exp=>'$14 = "s\\tt\\rr\\n"'} tc << {:cmd=>'p %Q!\C-a\C-z!', :exp=>'$15 = "\\x01\\x1a"'} tc << {:cmd=>'p %Q!#{foo+bar}!', :exp=>'$16 = "foobar"'} tc << {:cmd=>'p %q!str!', :exp=>'$17 = "str"'} tc << {:cmd=>'p %q!s\\tt\\rr\\n!', :exp=>'$18 = "s\\\\tt\\\\rr\\\\n"'} tc << {:cmd=>'p %q!\\C-a\\C-z!', :exp=>'$19 = "\\\\C-a\\\\C-z"'} tc << {:cmd=>'p %q!#{foo+bar}!', :exp=>'$20 = "\\#{foo+bar}"'} BinTest_MrubyBinDebugger.test(src, tc) end assert('mruby-bin-debugger(print) Literal:Array') do # ruby source src = <<"SRC" foo = 'foo' bar = "bar" baz = "baz" SRC # test case tc = [] tc << {:cmd=>"s"} tc << {:cmd=>"s"} tc << {:cmd=>'p []', :exp=>'$1 = []'} tc << {:cmd=>'p [ 5, 12, 8, 10, ]', :exp=>'$2 = [5, 12, 8, 10]'} tc << {:cmd=>'p [1,2.5,"#{foo+bar}"]', :exp=>'$3 = [1, 2.5, "foobar"]'} tc << {:cmd=>'p %w[3.14 A\ &\ B #{foo}]', :exp=>'$4 = ["3.14", "A & B", "\#{foo}"]'} tc << {:cmd=>'p %W[3.14 A\ &\ B #{foo}]', :exp=>'$5 = ["3.14", "A & B", "foo"]'} BinTest_MrubyBinDebugger.test(src, tc) end assert('mruby-bin-debugger(print) Literal:Hash') do # ruby source src = <<"SRC" foo = 'foo' bar = "bar" baz = "baz" SRC # test case tc = [] tc << {:cmd=>"s"} tc << {:cmd=>"s"} tc << {:cmd=>'p {}', :exp=>'$1 = {}'} tc << {:cmd=>'p {"one"=>1,"two"=>2}', :exp=>'$2 = {"one"=>1, "two"=>2}'} tc << {:cmd=>'p {:eins=>"1", :zwei=>"2", }', :exp=>'$3 = {:eins=>"1", :zwei=>"2"}'} tc << {:cmd=>'p {uno:"one", dos: 2}', :exp=>'$4 = {:uno=>"one", :dos=>2}'} tc << {:cmd=>'p {"one"=>1, :zwei=>2, tres:3}', :exp=>'$5 = {"one"=>1, :zwei=>2, :tres=>3}'} tc << {:cmd=>'p {:foo=>"#{foo}",:bar=>"#{bar}"}', :exp=>'$6 = {:foo=>"foo", :bar=>"bar"}'} BinTest_MrubyBinDebugger.test(src, tc) end assert('mruby-bin-debugger(print) Literal:Range') do # ruby source src = "foo = 'foo'\n" # test case tc = [] tc << {:cmd=>'p 1..10', :exp=>'$1 = 1..10'} tc << {:cmd=>'p 1...10', :exp=>'$2 = 1...10'} tc << {:cmd=>'p 100..10', :exp=>'$3 = 100..10'} tc << {:cmd=>'p 1 ... 10', :exp=>'$4 = 1...10'} tc << {:cmd=>'p "1" .. "9"', :exp=>'$5 = "1".."9"'} tc << {:cmd=>'p "A" ... "Z"', :exp=>'$6 = "A"..."Z"'} BinTest_MrubyBinDebugger.test(src, tc) end assert('mruby-bin-debugger(print) Literal:Symbol') do # ruby source src = <<"SRC" foo = 'foo' bar = "bar" baz = "baz" SRC # test case tc = [] tc << {:cmd=>"s"} tc << {:cmd=>"s"} tc << {:cmd=>'p :sym', :exp=>'$1 = :sym'} tc << {:cmd=>'p :"sd"', :exp=>'$2 = :sd'} tc << {:cmd=>"p :'ss'", :exp=>'$3 = :ss'} tc << {:cmd=>'p :"123"', :exp=>'$4 = :"123"'} tc << {:cmd=>'p :"#{foo} baz"', :exp=>'$5 = :"foo baz"'} tc << {:cmd=>'p %s!symsym!', :exp=>'$6 = :symsym'} BinTest_MrubyBinDebugger.test(src, tc) end assert('mruby-bin-debugger(print) Unary operation') do # ruby source src = "foo = 'foo'\n" # test case tc = [] tc << {:cmd=>'p +10', :exp=>'$1 = 10'} tc << {:cmd=>'p -100', :exp=>'$2 = -100'} tc << {:cmd=>'p !true', :exp=>'$3 = false'} tc << {:cmd=>'p !false', :exp=>'$4 = true'} tc << {:cmd=>'p !nil', :exp=>'$5 = true'} tc << {:cmd=>'p !1', :exp=>'$6 = false'} BinTest_MrubyBinDebugger.test(src, tc) end assert('mruby-bin-debugger(print) Binary operation') do # ruby source src = <<"SRC" CONST = 100 a,b,c = 1, 5, 8 foo,bar,baz = 'foo','bar','baz' ary = [] SRC # test case tc = [] tc << {:cmd=>'s'} tc << {:cmd=>'s'} tc << {:cmd=>'s'} tc << {:cmd=>'p a+1', :exp=>'$1 = 2'} tc << {:cmd=>'p 2-b', :exp=>'$2 = -3'} tc << {:cmd=>'p c * 3', :exp=>'$3 = 24'} tc << {:cmd=>'p a/b', :exp=>'$4 = 0.2'} tc << {:cmd=>'p c%b', :exp=>'$5 = 3'} tc << {:cmd=>'p 2**10', :exp=>'$6 = 1024'} tc << {:cmd=>'p ~3', :exp=>'$7 = -4'} tc << {:cmd=>'p 1<<2', :exp=>'$8 = 4'} tc << {:cmd=>'p 64>>5', :exp=>'$9 = 2'} tc << {:cmd=>'p a|c', :exp=>'$10 = 9'} tc << {:cmd=>'p a&b', :exp=>'$11 = 1'} tc << {:cmd=>'p a^b', :exp=>'$12 = 4'} tc << {:cmd=>'p a>b', :exp=>'$13 = false'} tc << {:cmd=>'p a'$14 = true'} tc << {:cmd=>'p b>=5', :exp=>'$15 = true'} tc << {:cmd=>'p b<=5', :exp=>'$16 = true'} tc << {:cmd=>'p "A"<=>"B"', :exp=>'$17 = -1'} tc << {:cmd=>'p "A"=="B"', :exp=>'$18 = false'} tc << {:cmd=>'p "A"==="B"', :exp=>'$19 = false'} tc << {:cmd=>'p "A"!="B"', :exp=>'$20 = true'} tc << {:cmd=>'p false || true', :exp=>'$21 = true'} tc << {:cmd=>'p false && true', :exp=>'$22 = false'} tc << {:cmd=>'p not nil', :exp=>'$23 = true'} tc << {:cmd=>'p false or true', :exp=>'$24 = true'} tc << {:cmd=>'p false and true', :exp=>'$25 = false'} BinTest_MrubyBinDebugger.test(src, tc) end assert('mruby-bin-debugger(print) Ternary operation') do # ruby source src = <<"SRC" CONST = 100 a,b,c = 1, 5, -10 foo,bar,baz = 'foo','bar','baz' ary = [] SRC # test case tc = [] tc << {:cmd=>'s'} tc << {:cmd=>'s'} tc << {:cmd=>'s'} tc << {:cmd=>'p (a < b) ? a : b', :exp=>'$1 = 1'} tc << {:cmd=>'p (a > b) ? a : b', :exp=>'$2 = 5'} tc << {:cmd=>'p true ? "true" : "false"', :exp=>'$3 = "true"'} tc << {:cmd=>'p false ? "true" : "false"', :exp=>'$4 = "false"'} tc << {:cmd=>'p nil ? "true" : "false"', :exp=>'$5 = "false"'} BinTest_MrubyBinDebugger.test(src, tc) end assert('mruby-bin-debugger(print) Substitution:simple') do # ruby source src = <<"SRC" CONST = 100 a,b,c = 1, 5, -10 foo,bar,baz = 'foo','bar','baz' ary = [] SRC # test case tc = [] tc << {:cmd=>'s'} tc << {:cmd=>'s'} tc << {:cmd=>'s'} tc << {:cmd=>'p a=2', :exp=>'$1 = 2'} tc << {:cmd=>'p foo=[foo,bar,baz]', :exp=>'$2 = ["foo", "bar", "baz"]'} tc << {:cmd=>'p undefined=-1', :exp=>'$3 = -1'} tc << {:cmd=>'p "#{undefined}"', :exp=>'$4 = (eval):2: undefined method'} BinTest_MrubyBinDebugger.test(src, tc) end assert('mruby-bin-debugger(print) Substitution:self') do # ruby source src = <<"SRC" CONST = 100 a,b,c = 1, 5, -10 foo,bar,baz = 'foo','bar','baz' ary = [] SRC # test case tc = [] tc << {:cmd=>'s'} tc << {:cmd=>'s'} tc << {:cmd=>'s'} tc << {:cmd=>'p a+=9', :exp=>'$1 = 10'} tc << {:cmd=>'p b-=c', :exp=>'$2 = 15'} tc << {:cmd=>'p bar*=2', :exp=>'$3 = "barbar"'} tc << {:cmd=>'p a/=4', :exp=>'$4 = 2.5'} tc << {:cmd=>'p c%=4', :exp=>'$5 = 2'} tc << {:cmd=>'p b&=0b0101', :exp=>'$6 = 5'} tc << {:cmd=>'p c|=0x10', :exp=>'$7 = 18'} tc << {:cmd=>'p "#{a} #{b} #{c}"', :exp=>'$8 = "2.5 5 18"'} tc << {:cmd=>'p "#{foo}#{bar}#{baz}"', :exp=>'$9 = "foobarbarbaz"'} tc << {:cmd=>'p a,b,c=[10,20,30]',:exp=>'$10 = [10, 20, 30]'} tc << {:cmd=>'p [a,b,c]', :exp=>'$11 = [10, 20, 30]'} tc << {:cmd=>'p a,b=b,a', :exp=>'$12 = [20, 10]'} tc << {:cmd=>'p [a,b]', :exp=>'$13 = [20, 10]'} tc << {:cmd=>'p undefined=-1', :exp=>'$14 = -1'} tc << {:cmd=>'p "#{undefined}"', :exp=>'$15 = (eval):2: undefined method'} BinTest_MrubyBinDebugger.test(src, tc) end assert('mruby-bin-debugger(print) Substitution:multiple') do # ruby source src = <<"SRC" CONST = 100 a,b,c = 1, 5, -10 foo,bar,baz = 'foo','bar','baz' ary = [] SRC # test case tc = [] tc << {:cmd=>'s'} tc << {:cmd=>'s'} tc << {:cmd=>'s'} tc << {:cmd=>'p a,b=[10,20]', :exp=>'$1 = [10, 20]'} tc << {:cmd=>'p [a,b,c]', :exp=>'$2 = [10, 20, -10]'} tc << {:cmd=>'p foo,bar=["FOO","BAR","BAZ"]', :exp=>'$3 = ["FOO", "BAR", "BAZ"]'} tc << {:cmd=>'p [foo,bar,baz]', :exp=>'$4 = ["FOO", "BAR", "baz"]'} tc << {:cmd=>'p a,foo=foo,a', :exp=>'$5 = ["FOO", 10]'} tc << {:cmd=>'p [a,foo]', :exp=>'$6 = ["FOO", 10]'} # tc << {:cmd=>'p a,*b=[123, 456, 789]'} # tc << {:cmd=>'p [a,b]', :exp=>'[123, [456, 789]]'} BinTest_MrubyBinDebugger.test(src, tc) end assert('mruby-bin-debugger(print) Substitution:self') do # ruby source src = <<"SRC" CONST = 100 a,b,c = 1, 5, -10 foo,bar,baz = 'foo','bar','baz' ary = [] SRC # test case tc = [] tc << {:cmd=>'s'} tc << {:cmd=>'s'} tc << {:cmd=>'s'} tc << {:cmd=>'p a+=9', :exp=>'$1 = 10'} tc << {:cmd=>'p b-=c', :exp=>'$2 = 15'} tc << {:cmd=>'p bar*=2', :exp=>'$3 = "barbar"'} tc << {:cmd=>'p a/=4', :exp=>'$4 = 2.5'} tc << {:cmd=>'p c%=4', :exp=>'$5 = 2'} tc << {:cmd=>'p b&=0b0101', :exp=>'$6 = 5'} tc << {:cmd=>'p c|=0x10', :exp=>'$7 = 18'} tc << {:cmd=>'p "#{a} #{b} #{c}"', :exp=>'$8 = "2.5 5 18"'} tc << {:cmd=>'p "#{foo}#{bar}#{baz}"', :exp=>'$9 = "foobarbarbaz"'} tc << {:cmd=>'p a,b,c=[10,20,30]',:exp=>'$10 = [10, 20, 30]'} tc << {:cmd=>'p [a,b,c]', :exp=>'$11 = [10, 20, 30]'} tc << {:cmd=>'p a,b=b,a', :exp=>'$12 = [20, 10]'} tc << {:cmd=>'p [a,b]', :exp=>'$13 = [20, 10]'} tc << {:cmd=>'p undefined=-1', :exp=>'$14 = -1'} tc << {:cmd=>'p "#{undefined}"', :exp=>'$15 = (eval):2: undefined method'} BinTest_MrubyBinDebugger.test(src, tc) end mruby-2.0.0/mrbgems/mruby-bin-debugger/mrbgem.rake000066400000000000000000000003731340361412400221070ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-bin-debugger') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'mruby debugger command' spec.add_dependency('mruby-eval', :core => 'mruby-eval') spec.bins = %w(mrdb) end mruby-2.0.0/mrbgems/mruby-bin-debugger/tools/000077500000000000000000000000001340361412400211275ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-bin-debugger/tools/mrdb/000077500000000000000000000000001340361412400220535ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c000066400000000000000000000262071340361412400240040ustar00rootroot00000000000000/* ** apibreak.c ** */ #include #include #include #include "mrdb.h" #include #include #include #include #include #include "mrdberror.h" #include "apibreak.h" #define MAX_BREAKPOINTNO (MAX_BREAKPOINT * 1024) #define MRB_DEBUG_BP_FILE_OK (0x0001) #define MRB_DEBUG_BP_LINENO_OK (0x0002) static uint16_t check_lineno(mrb_irep_debug_info_file *info_file, uint16_t lineno) { uint32_t count = info_file->line_entry_count; uint16_t l_idx; if (info_file->line_type == mrb_debug_line_ary) { for (l_idx = 0; l_idx < count; ++l_idx) { if (lineno == info_file->lines.ary[l_idx]) { return lineno; } } } else { for (l_idx = 0; l_idx < count; ++l_idx) { if (lineno == info_file->lines.flat_map[l_idx].line) { return lineno; } } } return 0; } static int32_t get_break_index(mrb_debug_context *dbg, uint32_t bpno) { uint32_t i; int32_t index; char hit = FALSE; for(i = 0 ; i < dbg->bpnum; i++) { if (dbg->bp[i].bpno == bpno) { hit = TRUE; index = i; break; } } if (hit == FALSE) { return MRB_DEBUG_BREAK_INVALID_NO; } return index; } static void free_breakpoint(mrb_state *mrb, mrb_debug_breakpoint *bp) { switch(bp->type) { case MRB_DEBUG_BPTYPE_LINE: mrb_free(mrb, (void*)bp->point.linepoint.file); break; case MRB_DEBUG_BPTYPE_METHOD: mrb_free(mrb, (void*)bp->point.methodpoint.method_name); if (bp->point.methodpoint.class_name != NULL) { mrb_free(mrb, (void*)bp->point.methodpoint.class_name); } break; default: break; } } static uint16_t check_file_lineno(struct mrb_irep *irep, const char *file, uint16_t lineno) { mrb_irep_debug_info_file *info_file; uint16_t result = 0; uint16_t f_idx; uint16_t fix_lineno; uint16_t i; for (f_idx = 0; f_idx < irep->debug_info->flen; ++f_idx) { info_file = irep->debug_info->files[f_idx]; if (!strcmp(info_file->filename, file)) { result = MRB_DEBUG_BP_FILE_OK; fix_lineno = check_lineno(info_file, lineno); if (fix_lineno != 0) { return result | MRB_DEBUG_BP_LINENO_OK; } } for (i=0; i < irep->rlen; ++i) { result |= check_file_lineno(irep->reps[i], file, lineno); if (result == (MRB_DEBUG_BP_FILE_OK | MRB_DEBUG_BP_LINENO_OK)) { return result; } } } return result; } static int32_t compare_break_method(mrb_state *mrb, mrb_debug_breakpoint *bp, struct RClass *class_obj, mrb_sym method_sym, mrb_bool* isCfunc) { const char* class_name; const char* method_name; mrb_method_t m; struct RClass* sc; const char* sn; mrb_sym ssym; mrb_debug_methodpoint *method_p; mrb_bool is_defined; method_name = mrb_sym2name(mrb, method_sym); method_p = &bp->point.methodpoint; if (strcmp(method_p->method_name, method_name) == 0) { class_name = mrb_class_name(mrb, class_obj); if (class_name == NULL) { if (method_p->class_name == NULL) { return bp->bpno; } } else if (method_p->class_name != NULL) { m = mrb_method_search_vm(mrb, &class_obj, method_sym); if (MRB_METHOD_UNDEF_P(m)) { return MRB_DEBUG_OK; } if (MRB_METHOD_CFUNC_P(m)) { *isCfunc = TRUE; } is_defined = mrb_class_defined(mrb, method_p->class_name); if (is_defined == FALSE) { return MRB_DEBUG_OK; } sc = mrb_class_get(mrb, method_p->class_name); ssym = mrb_symbol(mrb_check_intern_cstr(mrb, method_p->method_name)); m = mrb_method_search_vm(mrb, &sc, ssym); if (MRB_METHOD_UNDEF_P(m)) { return MRB_DEBUG_OK; } class_name = mrb_class_name(mrb, class_obj); sn = mrb_class_name(mrb, sc); if (strcmp(sn, class_name) == 0) { return bp->bpno; } } } return MRB_DEBUG_OK; } int32_t mrb_debug_set_break_line(mrb_state *mrb, mrb_debug_context *dbg, const char *file, uint16_t lineno) { int32_t index; char* set_file; uint16_t result; if ((mrb == NULL)||(dbg == NULL)||(file == NULL)) { return MRB_DEBUG_INVALID_ARGUMENT; } if (dbg->bpnum >= MAX_BREAKPOINT) { return MRB_DEBUG_BREAK_NUM_OVER; } if (dbg->next_bpno > MAX_BREAKPOINTNO) { return MRB_DEBUG_BREAK_NO_OVER; } /* file and lineno check (line type mrb_debug_line_ary only.) */ result = check_file_lineno(dbg->root_irep, file, lineno); if (result == 0) { return MRB_DEBUG_BREAK_INVALID_FILE; } else if (result == MRB_DEBUG_BP_FILE_OK) { return MRB_DEBUG_BREAK_INVALID_LINENO; } set_file = (char*)mrb_malloc(mrb, strlen(file) + 1); index = dbg->bpnum; dbg->bp[index].bpno = dbg->next_bpno; dbg->next_bpno++; dbg->bp[index].enable = TRUE; dbg->bp[index].type = MRB_DEBUG_BPTYPE_LINE; dbg->bp[index].point.linepoint.lineno = lineno; dbg->bpnum++; strncpy(set_file, file, strlen(file) + 1); dbg->bp[index].point.linepoint.file = set_file; return dbg->bp[index].bpno; } int32_t mrb_debug_set_break_method(mrb_state *mrb, mrb_debug_context *dbg, const char *class_name, const char *method_name) { int32_t index; char* set_class; char* set_method; if ((mrb == NULL) || (dbg == NULL) || (method_name == NULL)) { return MRB_DEBUG_INVALID_ARGUMENT; } if (dbg->bpnum >= MAX_BREAKPOINT) { return MRB_DEBUG_BREAK_NUM_OVER; } if (dbg->next_bpno > MAX_BREAKPOINTNO) { return MRB_DEBUG_BREAK_NO_OVER; } if (class_name != NULL) { set_class = (char*)mrb_malloc(mrb, strlen(class_name) + 1); strncpy(set_class, class_name, strlen(class_name) + 1); } else { set_class = NULL; } set_method = (char*)mrb_malloc(mrb, strlen(method_name) + 1); strncpy(set_method, method_name, strlen(method_name) + 1); index = dbg->bpnum; dbg->bp[index].bpno = dbg->next_bpno; dbg->next_bpno++; dbg->bp[index].enable = TRUE; dbg->bp[index].type = MRB_DEBUG_BPTYPE_METHOD; dbg->bp[index].point.methodpoint.method_name = set_method; dbg->bp[index].point.methodpoint.class_name = set_class; dbg->bpnum++; return dbg->bp[index].bpno; } int32_t mrb_debug_get_breaknum(mrb_state *mrb, mrb_debug_context *dbg) { if ((mrb == NULL) || (dbg == NULL)) { return MRB_DEBUG_INVALID_ARGUMENT; } return dbg->bpnum; } int32_t mrb_debug_get_break_all(mrb_state *mrb, mrb_debug_context *dbg, uint32_t size, mrb_debug_breakpoint *bp) { uint32_t get_size = 0; if ((mrb == NULL) || (dbg == NULL) || (bp == NULL)) { return MRB_DEBUG_INVALID_ARGUMENT; } if (dbg->bpnum >= size) { get_size = size; } else { get_size = dbg->bpnum; } memcpy(bp, dbg->bp, sizeof(mrb_debug_breakpoint) * get_size); return get_size; } int32_t mrb_debug_get_break(mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno, mrb_debug_breakpoint *bp) { int32_t index; if ((mrb == NULL) || (dbg == NULL) || (bp == NULL)) { return MRB_DEBUG_INVALID_ARGUMENT; } index = get_break_index(dbg, bpno); if (index == MRB_DEBUG_BREAK_INVALID_NO) { return MRB_DEBUG_BREAK_INVALID_NO; } bp->bpno = dbg->bp[index].bpno; bp->enable = dbg->bp[index].enable; bp->point = dbg->bp[index].point; bp->type = dbg->bp[index].type; return 0; } int32_t mrb_debug_delete_break(mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno) { uint32_t i; int32_t index; if ((mrb == NULL) ||(dbg == NULL)) { return MRB_DEBUG_INVALID_ARGUMENT; } index = get_break_index(dbg, bpno); if (index == MRB_DEBUG_BREAK_INVALID_NO) { return MRB_DEBUG_BREAK_INVALID_NO; } free_breakpoint(mrb, &dbg->bp[index]); for(i = index ; i < dbg->bpnum; i++) { if ((i + 1) == dbg->bpnum) { memset(&dbg->bp[i], 0, sizeof(mrb_debug_breakpoint)); } else { memcpy(&dbg->bp[i], &dbg->bp[i + 1], sizeof(mrb_debug_breakpoint)); } } dbg->bpnum--; return MRB_DEBUG_OK; } int32_t mrb_debug_delete_break_all(mrb_state *mrb, mrb_debug_context *dbg) { uint32_t i; if ((mrb == NULL) || (dbg == NULL)) { return MRB_DEBUG_INVALID_ARGUMENT; } for(i = 0 ; i < dbg->bpnum ; i++) { free_breakpoint(mrb, &dbg->bp[i]); } dbg->bpnum = 0; return MRB_DEBUG_OK; } int32_t mrb_debug_enable_break(mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno) { int32_t index = 0; if ((mrb == NULL) || (dbg == NULL)) { return MRB_DEBUG_INVALID_ARGUMENT; } index = get_break_index(dbg, bpno); if (index == MRB_DEBUG_BREAK_INVALID_NO) { return MRB_DEBUG_BREAK_INVALID_NO; } dbg->bp[index].enable = TRUE; return MRB_DEBUG_OK; } int32_t mrb_debug_enable_break_all(mrb_state *mrb, mrb_debug_context *dbg) { uint32_t i; if ((mrb == NULL) || (dbg == NULL)) { return MRB_DEBUG_INVALID_ARGUMENT; } for(i = 0 ; i < dbg->bpnum; i++) { dbg->bp[i].enable = TRUE; } return MRB_DEBUG_OK; } int32_t mrb_debug_disable_break(mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno) { int32_t index = 0; if ((mrb == NULL) || (dbg == NULL)) { return MRB_DEBUG_INVALID_ARGUMENT; } index = get_break_index(dbg, bpno); if (index == MRB_DEBUG_BREAK_INVALID_NO) { return MRB_DEBUG_BREAK_INVALID_NO; } dbg->bp[index].enable = FALSE; return MRB_DEBUG_OK; } int32_t mrb_debug_disable_break_all(mrb_state *mrb, mrb_debug_context *dbg) { uint32_t i; if ((mrb == NULL) || (dbg == NULL)) { return MRB_DEBUG_INVALID_ARGUMENT; } for(i = 0 ; i < dbg->bpnum; i++) { dbg->bp[i].enable = FALSE; } return MRB_DEBUG_OK; } static mrb_bool check_start_pc_for_line(mrb_irep *irep, mrb_code *pc, uint16_t line) { if (pc > irep->iseq) { if (line == mrb_debug_get_line(irep, pc - irep->iseq - 1)) { return FALSE; } } return TRUE; } int32_t mrb_debug_check_breakpoint_line(mrb_state *mrb, mrb_debug_context *dbg, const char *file, uint16_t line) { mrb_debug_breakpoint *bp; mrb_debug_linepoint *line_p; uint32_t i; if ((mrb == NULL) || (dbg == NULL) || (file == NULL) || (line <= 0)) { return MRB_DEBUG_INVALID_ARGUMENT; } if (!check_start_pc_for_line(dbg->irep, dbg->pc, line)) { return MRB_DEBUG_OK; } bp = dbg->bp; for(i=0; ibpnum; i++) { switch (bp->type) { case MRB_DEBUG_BPTYPE_LINE: if (bp->enable == TRUE) { line_p = &bp->point.linepoint; if ((strcmp(line_p->file, file) == 0) && (line_p->lineno == line)) { return bp->bpno; } } break; case MRB_DEBUG_BPTYPE_METHOD: break; case MRB_DEBUG_BPTYPE_NONE: default: return MRB_DEBUG_OK; } bp++; } return MRB_DEBUG_OK; } int32_t mrb_debug_check_breakpoint_method(mrb_state *mrb, mrb_debug_context *dbg, struct RClass *class_obj, mrb_sym method_sym, mrb_bool* isCfunc) { mrb_debug_breakpoint *bp; int32_t bpno; uint32_t i; if ((mrb == NULL) || (dbg == NULL) || (class_obj == NULL)) { return MRB_DEBUG_INVALID_ARGUMENT; } bp = dbg->bp; for(i=0; ibpnum; i++) { if (bp->type == MRB_DEBUG_BPTYPE_METHOD) { if (bp->enable == TRUE) { bpno = compare_break_method(mrb, bp, class_obj, method_sym, isCfunc); if (bpno > 0) { return bpno; } } } else if (bp->type == MRB_DEBUG_BPTYPE_NONE) { break; } bp++; } return 0; } mruby-2.0.0/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.h000066400000000000000000000023271340361412400240060ustar00rootroot00000000000000/* ** apibreak.h ** */ #ifndef APIBREAK_H_ #define APIBREAK_H_ #include #include "mrdb.h" int32_t mrb_debug_set_break_line(mrb_state *, mrb_debug_context *, const char *, uint16_t); int32_t mrb_debug_set_break_method(mrb_state *, mrb_debug_context *, const char *, const char *); int32_t mrb_debug_get_breaknum(mrb_state *, mrb_debug_context *); int32_t mrb_debug_get_break_all(mrb_state *, mrb_debug_context *, uint32_t, mrb_debug_breakpoint bp[]); int32_t mrb_debug_get_break(mrb_state *, mrb_debug_context *, uint32_t, mrb_debug_breakpoint *); int32_t mrb_debug_delete_break(mrb_state *, mrb_debug_context *, uint32_t); int32_t mrb_debug_delete_break_all(mrb_state *, mrb_debug_context *); int32_t mrb_debug_enable_break(mrb_state *, mrb_debug_context *, uint32_t); int32_t mrb_debug_enable_break_all(mrb_state *, mrb_debug_context *); int32_t mrb_debug_disable_break(mrb_state *, mrb_debug_context *, uint32_t); int32_t mrb_debug_disable_break_all(mrb_state *, mrb_debug_context *); int32_t mrb_debug_check_breakpoint_line(mrb_state *, mrb_debug_context *, const char *, uint16_t); int32_t mrb_debug_check_breakpoint_method(mrb_state *, mrb_debug_context *, struct RClass *, mrb_sym, mrb_bool*); #endif /* APIBREAK_H_ */ mruby-2.0.0/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c000066400000000000000000000107321340361412400236670ustar00rootroot00000000000000/* * apilist.c */ #include #include #include #include "mrdb.h" #include "mrdberror.h" #include "apilist.h" #include #include #include #define LINE_BUF_SIZE MAX_COMMAND_LINE typedef struct source_file { char *path; uint16_t lineno; FILE *fp; } source_file; static void source_file_free(mrb_state *mrb, source_file *file) { if (file != NULL) { if (file->path != NULL) { mrb_free(mrb, file->path); } if (file->fp != NULL) { fclose(file->fp); file->fp = NULL; } mrb_free(mrb, file); } } static char* build_path(mrb_state *mrb, const char *dir, const char *base) { int len; char *path = NULL; len = strlen(base) + 1; if (strcmp(dir, ".")) { len += strlen(dir) + sizeof("/") - 1; } path = (char*)mrb_malloc(mrb, len); memset(path, 0, len); if (strcmp(dir, ".")) { strcat(path, dir); strcat(path, "/"); } strcat(path, base); return path; } static char* dirname(mrb_state *mrb, const char *path) { size_t len; const char *p; char *dir; if (path == NULL) { return NULL; } p = strrchr(path, '/'); len = p != NULL ? (size_t)(p - path) : strlen(path); dir = (char*)mrb_malloc(mrb, len + 1); strncpy(dir, path, len); dir[len] = '\0'; return dir; } static source_file* source_file_new(mrb_state *mrb, mrb_debug_context *dbg, char *filename) { source_file *file; file = (source_file*)mrb_malloc(mrb, sizeof(source_file)); memset(file, '\0', sizeof(source_file)); file->fp = fopen(filename, "rb"); if (file->fp == NULL) { source_file_free(mrb, file); return NULL; } file->lineno = 1; file->path = (char*)mrb_malloc(mrb, strlen(filename) + 1); strcpy(file->path, filename); return file; } static mrb_bool remove_newlines(char *s, FILE *fp) { int c; char *p; size_t len; if ((len = strlen(s)) == 0) { return FALSE; } p = s + len - 1; if (*p != '\r' && *p != '\n') { return FALSE; } if (*p == '\r') { /* peek the next character and skip '\n' */ if ((c = fgetc(fp)) != '\n') { ungetc(c, fp); } } /* remove trailing newline characters */ while (s <= p && (*p == '\r' || *p == '\n')) { *p-- = '\0'; } return TRUE; } static void show_lines(source_file *file, uint16_t line_min, uint16_t line_max) { char buf[LINE_BUF_SIZE]; int show_lineno = 1, found_newline = 0, is_printed = 0; if (file->fp == NULL) { return; } while (fgets(buf, sizeof(buf), file->fp) != NULL) { found_newline = remove_newlines(buf, file->fp); if (line_min <= file->lineno) { if (show_lineno) { printf("%-8d", file->lineno); } show_lineno = found_newline; printf(found_newline ? "%s\n" : "%s", buf); is_printed = 1; } if (found_newline) { if (line_max < ++file->lineno) { break; } } } if (is_printed && !found_newline) { printf("\n"); } } char* mrb_debug_get_source(mrb_state *mrb, mrdb_state *mrdb, const char *srcpath, const char *filename) { int i; FILE *fp; const char *search_path[3]; char *path = NULL; const char *srcname = strrchr(filename, '/'); if (srcname) srcname++; else srcname = filename; search_path[0] = srcpath; search_path[1] = dirname(mrb, mrb_debug_get_filename(mrdb->dbg->irep, 0)); search_path[2] = "."; for (i = 0; i < 3; i++) { if (search_path[i] == NULL) { continue; } if ((path = build_path(mrb, search_path[i], srcname)) == NULL) { continue; } if ((fp = fopen(path, "rb")) == NULL) { mrb_free(mrb, path); path = NULL; continue; } fclose(fp); break; } mrb_free(mrb, (void *)search_path[1]); return path; } int32_t mrb_debug_list(mrb_state *mrb, mrb_debug_context *dbg, char *filename, uint16_t line_min, uint16_t line_max) { char *ext; source_file *file; if (mrb == NULL || dbg == NULL || filename == NULL) { return MRB_DEBUG_INVALID_ARGUMENT; } ext = strrchr(filename, '.'); if (ext == NULL || strcmp(ext, ".rb")) { printf("List command only supports .rb file.\n"); return MRB_DEBUG_INVALID_ARGUMENT; } if (line_min > line_max) { return MRB_DEBUG_INVALID_ARGUMENT; } if ((file = source_file_new(mrb, dbg, filename)) != NULL) { show_lines(file, line_min, line_max); source_file_free(mrb, file); return MRB_DEBUG_OK; } else { printf("Invalid source file named %s.\n", filename); return MRB_DEBUG_INVALID_ARGUMENT; } } mruby-2.0.0/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.h000066400000000000000000000004441340361412400236730ustar00rootroot00000000000000/* * apilist.h */ #ifndef APILIST_H_ #define APILIST_H_ #include #include "mrdb.h" int32_t mrb_debug_list(mrb_state *, mrb_debug_context *, char *, uint16_t, uint16_t); char* mrb_debug_get_source(mrb_state *, mrdb_state *, const char *, const char *); #endif /* APILIST_H_ */ mruby-2.0.0/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.c000066400000000000000000000031671340361412400240540ustar00rootroot00000000000000/* ** apiprint.c ** */ #include #include "mrdb.h" #include #include #include #include #include #include #include "apiprint.h" static void mrdb_check_syntax(mrb_state *mrb, mrb_debug_context *dbg, const char *expr, size_t len) { mrbc_context *c; c = mrbc_context_new(mrb); c->no_exec = TRUE; c->capture_errors = TRUE; mrbc_filename(mrb, c, (const char*)dbg->prvfile); c->lineno = dbg->prvline; /* Load program */ mrb_load_nstring_cxt(mrb, expr, len, c); mrbc_context_free(mrb, c); } mrb_value mrb_debug_eval(mrb_state *mrb, mrb_debug_context *dbg, const char *expr, size_t len, mrb_bool *exc) { void (*tmp)(struct mrb_state *, struct mrb_irep *, mrb_code *, mrb_value *); mrb_value ruby_code; mrb_value s; mrb_value v; mrb_value recv; /* disable code_fetch_hook */ tmp = mrb->code_fetch_hook; mrb->code_fetch_hook = NULL; mrdb_check_syntax(mrb, dbg, expr, len); if (mrb->exc) { v = mrb_obj_value(mrb->exc); mrb->exc = 0; } else { /* * begin * expr * rescue => e * e * end */ ruby_code = mrb_str_new_lit(mrb, "begin\n"); ruby_code = mrb_str_cat(mrb, ruby_code, expr, len); ruby_code = mrb_str_cat_lit(mrb, ruby_code, "\nrescue => e\ne\nend"); recv = dbg->regs[0]; v = mrb_funcall(mrb, recv, "instance_eval", 1, ruby_code); } if (exc) { *exc = mrb_obj_is_kind_of(mrb, v, mrb->eException_class); } s = mrb_funcall(mrb, v, "inspect", 0); /* enable code_fetch_hook */ mrb->code_fetch_hook = tmp; return s; } mruby-2.0.0/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.h000066400000000000000000000003311340361412400240470ustar00rootroot00000000000000/* * apiprint.h */ #ifndef APIPRINT_H_ #define APIPRINT_H_ #include #include "mrdb.h" mrb_value mrb_debug_eval(mrb_state*, mrb_debug_context*, const char*, size_t, mrb_bool*); #endif /* APIPRINT_H_ */ mruby-2.0.0/mrbgems/mruby-bin-debugger/tools/mrdb/cmdbreak.c000066400000000000000000000255301340361412400237740ustar00rootroot00000000000000/* ** cmdbreak.c ** */ #include #include #include #include #include #include #include "mrdb.h" #include "mrdberror.h" #include "apibreak.h" #define BREAK_SET_MSG_LINE "Breakpoint %d: file %s, line %d.\n" #define BREAK_SET_MSG_METHOD "Breakpoint %d: method %s.\n" #define BREAK_SET_MSG_CLASS_METHOD "Breakpoint %d: class %s, method %s.\n" #define BREAK_INFO_MSG_HEADER "Num Type Enb What" #define BREAK_INFO_MSG_LINEBREAK "%-8ubreakpoint %s at %s:%u\n" #define BREAK_INFO_MSG_METHODBREAK "%-8ubreakpoint %s in %s:%s\n" #define BREAK_INFO_MSG_METHODBREAK_NOCLASS "%-8ubreakpoint %s in %s\n" #define BREAK_INFO_MSG_ENABLE "y" #define BREAK_INFO_MSG_DISABLE "n" #define BREAK_ERR_MSG_INVALIDARG "Internal error." #define BREAK_ERR_MSG_BLANK "Try \'help break\' for more information." #define BREAK_ERR_MSG_RANGEOVER "The line number range is from 1 to 65535." #define BREAK_ERR_MSG_NUMOVER "Exceeded the setable number of breakpoint." #define BREAK_ERR_MSG_NOOVER "Breakno is over the available number.Please 'quit' and restart mrdb." #define BREAK_ERR_MSG_INVALIDSTR "String \'%s\' is invalid.\n" #define BREAK_ERR_MSG_INVALIDLINENO "Line %d in file \"%s\" is unavailable.\n" #define BREAK_ERR_MSG_INVALIDCLASS "Class name \'%s\' is invalid.\n" #define BREAK_ERR_MSG_INVALIDMETHOD "Method name \'%s\' is invalid.\n" #define BREAK_ERR_MSG_INVALIDFILE "Source file named \"%s\" is unavailable.\n" #define BREAK_ERR_MSG_INVALIDBPNO "warning: bad breakpoint number at or near '%s'\n" #define BREAK_ERR_MSG_INVALIDBPNO_INFO "Args must be numbers variables." #define BREAK_ERR_MSG_NOBPNO "No breakpoint number %d.\n" #define BREAK_ERR_MSG_NOBPNO_INFO "No breakpoint matching '%d'\n" #define BREAK_ERR_MSG_NOBPNO_INFOALL "No breakpoints." #define LINENO_MAX_DIGIT 6 #define BPNO_LETTER_NUM 9 typedef int32_t (*all_command_func)(mrb_state *, mrb_debug_context *); typedef int32_t (*select_command_func)(mrb_state *, mrb_debug_context *, uint32_t); static void print_api_common_error(int32_t error) { switch(error) { case MRB_DEBUG_INVALID_ARGUMENT: puts(BREAK_ERR_MSG_INVALIDARG); break; default: break; } } #undef STRTOUL #define STRTOUL(ul,s) { \ int i; \ ul = 0; \ for(i=0; ISDIGIT(s[i]); i++) ul = 10*ul + (s[i] -'0'); \ } static int32_t parse_breakpoint_no(char* args) { char* ps = args; uint32_t l; if ((*ps == '0')||(strlen(ps) >= BPNO_LETTER_NUM)) { return 0; } while (!(ISBLANK(*ps)||ISCNTRL(*ps))) { if (!ISDIGIT(*ps)) { return 0; } ps++; } STRTOUL(l, args); return l; } static mrb_bool exe_set_command_all(mrb_state *mrb, mrdb_state *mrdb, all_command_func func) { int32_t ret = MRB_DEBUG_OK; if (mrdb->wcnt == 1) { ret = func(mrb, mrdb->dbg); print_api_common_error(ret); return TRUE; } return FALSE; } static void exe_set_command_select(mrb_state *mrb, mrdb_state *mrdb, select_command_func func) { char* ps; int32_t ret = MRB_DEBUG_OK; int32_t bpno = 0; int32_t i; for(i=1; iwcnt; i++) { ps = mrdb->words[i]; bpno = parse_breakpoint_no(ps); if (bpno == 0) { printf(BREAK_ERR_MSG_INVALIDBPNO, ps); break; } ret = func(mrb, mrdb->dbg, (uint32_t)bpno); if (ret == MRB_DEBUG_BREAK_INVALID_NO) { printf(BREAK_ERR_MSG_NOBPNO, bpno); } else if (ret != MRB_DEBUG_OK) { print_api_common_error(ret); } } } mrb_debug_bptype check_bptype(char* args) { char* ps = args; if (ISBLANK(*ps)||ISCNTRL(*ps)) { puts(BREAK_ERR_MSG_BLANK); return MRB_DEBUG_BPTYPE_NONE; } if (!ISDIGIT(*ps)) { return MRB_DEBUG_BPTYPE_METHOD; } while (!(ISBLANK(*ps)||ISCNTRL(*ps))) { if (!ISDIGIT(*ps)) { printf(BREAK_ERR_MSG_INVALIDSTR, args); return MRB_DEBUG_BPTYPE_NONE; } ps++; } if ((*args == '0')||(strlen(args) >= LINENO_MAX_DIGIT)) { puts(BREAK_ERR_MSG_RANGEOVER); return MRB_DEBUG_BPTYPE_NONE; } return MRB_DEBUG_BPTYPE_LINE; } static void print_breakpoint(mrb_debug_breakpoint *bp) { const char* enable_letter[] = {BREAK_INFO_MSG_DISABLE, BREAK_INFO_MSG_ENABLE}; if (bp->type == MRB_DEBUG_BPTYPE_LINE) { printf(BREAK_INFO_MSG_LINEBREAK, bp->bpno, enable_letter[bp->enable], bp->point.linepoint.file, bp->point.linepoint.lineno); } else { if (bp->point.methodpoint.class_name == NULL) { printf(BREAK_INFO_MSG_METHODBREAK_NOCLASS, bp->bpno, enable_letter[bp->enable], bp->point.methodpoint.method_name); } else { printf(BREAK_INFO_MSG_METHODBREAK, bp->bpno, enable_letter[bp->enable], bp->point.methodpoint.class_name, bp->point.methodpoint.method_name); } } } static void info_break_all(mrb_state *mrb, mrdb_state *mrdb) { int32_t bpnum = 0; int32_t i = 0; int32_t ret = MRB_DEBUG_OK; mrb_debug_breakpoint *bp_list; bpnum = mrb_debug_get_breaknum(mrb, mrdb->dbg); if (bpnum < 0) { print_api_common_error(bpnum); return; } else if (bpnum == 0) { puts(BREAK_ERR_MSG_NOBPNO_INFOALL); return; } bp_list = (mrb_debug_breakpoint*)mrb_malloc(mrb, bpnum * sizeof(mrb_debug_breakpoint)); ret = mrb_debug_get_break_all(mrb, mrdb->dbg, (uint32_t)bpnum, bp_list); if (ret < 0) { print_api_common_error(ret); return; } puts(BREAK_INFO_MSG_HEADER); for(i = 0 ; i < bpnum ; i++) { print_breakpoint(&bp_list[i]); } mrb_free(mrb, bp_list); } static void info_break_select(mrb_state *mrb, mrdb_state *mrdb) { int32_t ret = MRB_DEBUG_OK; int32_t bpno = 0; char* ps = mrdb->command; mrb_debug_breakpoint bp; mrb_bool isFirst = TRUE; int32_t i; for(i=2; iwcnt; i++) { ps = mrdb->words[i]; bpno = parse_breakpoint_no(ps); if (bpno == 0) { puts(BREAK_ERR_MSG_INVALIDBPNO_INFO); break; } ret = mrb_debug_get_break(mrb, mrdb->dbg, bpno, &bp); if (ret == MRB_DEBUG_BREAK_INVALID_NO) { printf(BREAK_ERR_MSG_NOBPNO_INFO, bpno); break; } else if (ret != MRB_DEBUG_OK) { print_api_common_error(ret); break; } else if (isFirst == TRUE) { isFirst = FALSE; puts(BREAK_INFO_MSG_HEADER); } print_breakpoint(&bp); } } mrb_debug_bptype parse_breakcommand(mrdb_state *mrdb, const char **file, uint32_t *line, char **cname, char **method) { mrb_debug_context *dbg = mrdb->dbg; char *args; char *body; mrb_debug_bptype type; uint32_t l; if (mrdb->wcnt <= 1) { puts(BREAK_ERR_MSG_BLANK); return MRB_DEBUG_BPTYPE_NONE; } args = mrdb->words[1]; if ((body = strrchr(args, ':')) == NULL) { body = args; type = check_bptype(body); } else { if (body == args) { printf(BREAK_ERR_MSG_INVALIDSTR, args); return MRB_DEBUG_BPTYPE_NONE; } *body = '\0'; type = check_bptype(++body); } switch(type) { case MRB_DEBUG_BPTYPE_LINE: STRTOUL(l, body); if (l <= 65535) { *line = l; *file = (body == args)? mrb_debug_get_filename(dbg->irep, dbg->pc - dbg->irep->iseq): args; } else { puts(BREAK_ERR_MSG_RANGEOVER); type = MRB_DEBUG_BPTYPE_NONE; } break; case MRB_DEBUG_BPTYPE_METHOD: if (body == args) { /* method only */ if (ISUPPER(*body)||ISLOWER(*body)||(*body == '_')) { *method = body; *cname = NULL; } else { printf(BREAK_ERR_MSG_INVALIDMETHOD, args); type = MRB_DEBUG_BPTYPE_NONE; } } else { if (ISUPPER(*args)) { switch(*body) { case '@': case '$': case '?': case '.': case ',': case ':': case ';': case '#': case '\\': case '\'': case '\"': printf(BREAK_ERR_MSG_INVALIDMETHOD, body); type = MRB_DEBUG_BPTYPE_NONE; break; default: *method = body; *cname = args; break; } } else { printf(BREAK_ERR_MSG_INVALIDCLASS, args); type = MRB_DEBUG_BPTYPE_NONE; } } break; case MRB_DEBUG_BPTYPE_NONE: default: break; } return type; } dbgcmd_state dbgcmd_break(mrb_state *mrb, mrdb_state *mrdb) { mrb_debug_bptype type; mrb_debug_context *dbg = mrdb->dbg; const char *file = NULL; uint32_t line = 0; char *cname = NULL; char *method = NULL; int32_t ret; type = parse_breakcommand(mrdb, &file, &line, &cname, &method); switch (type) { case MRB_DEBUG_BPTYPE_LINE: ret = mrb_debug_set_break_line(mrb, dbg, file, line); break; case MRB_DEBUG_BPTYPE_METHOD: ret = mrb_debug_set_break_method(mrb, dbg, cname, method); break; case MRB_DEBUG_BPTYPE_NONE: default: return DBGST_PROMPT; } if (ret >= 0) { if (type == MRB_DEBUG_BPTYPE_LINE) { printf(BREAK_SET_MSG_LINE, ret, file, line); } else if ((type == MRB_DEBUG_BPTYPE_METHOD)&&(cname == NULL)) { printf(BREAK_SET_MSG_METHOD, ret, method); } else { printf(BREAK_SET_MSG_CLASS_METHOD, ret, cname, method); } } else { switch (ret) { case MRB_DEBUG_BREAK_INVALID_LINENO: printf(BREAK_ERR_MSG_INVALIDLINENO, line, file); break; case MRB_DEBUG_BREAK_INVALID_FILE: printf(BREAK_ERR_MSG_INVALIDFILE, file); break; case MRB_DEBUG_BREAK_NUM_OVER: puts(BREAK_ERR_MSG_NUMOVER); break; case MRB_DEBUG_BREAK_NO_OVER: puts(BREAK_ERR_MSG_NOOVER); break; case MRB_DEBUG_INVALID_ARGUMENT: puts(BREAK_ERR_MSG_INVALIDARG); break; case MRB_DEBUG_NOBUF: puts("T.B.D."); break; default: break; } } return DBGST_PROMPT; } dbgcmd_state dbgcmd_info_break(mrb_state *mrb, mrdb_state *mrdb) { if (mrdb->wcnt == 2) { info_break_all(mrb, mrdb); } else { info_break_select(mrb, mrdb); } return DBGST_PROMPT; } dbgcmd_state dbgcmd_delete(mrb_state *mrb, mrdb_state *mrdb) { mrb_bool ret = FALSE; ret = exe_set_command_all(mrb, mrdb, mrb_debug_delete_break_all); if (ret != TRUE) { exe_set_command_select(mrb, mrdb, mrb_debug_delete_break); } return DBGST_PROMPT; } dbgcmd_state dbgcmd_enable(mrb_state *mrb, mrdb_state *mrdb) { mrb_bool ret = FALSE; ret = exe_set_command_all(mrb, mrdb, mrb_debug_enable_break_all); if (ret != TRUE) { exe_set_command_select(mrb, mrdb, mrb_debug_enable_break); } return DBGST_PROMPT; } dbgcmd_state dbgcmd_disable(mrb_state *mrb, mrdb_state *mrdb) { mrb_bool ret = FALSE; ret = exe_set_command_all(mrb, mrdb, mrb_debug_disable_break_all); if (ret != TRUE) { exe_set_command_select(mrb, mrdb, mrb_debug_disable_break); } return DBGST_PROMPT; } mruby-2.0.0/mrbgems/mruby-bin-debugger/tools/mrdb/cmdmisc.c000066400000000000000000000253301340361412400236410ustar00rootroot00000000000000/* ** cmdmisc.c - mruby debugger miscellaneous command functions ** */ #include #include #include #include "apilist.h" #include typedef struct help_msg { const char *cmd1; const char *cmd2; const char *short_msg; const char *long_msg; } help_msg; static help_msg help_msg_list[] = { { "b[reak]", NULL, "Set breakpoint", "Usage: break [file:]line\n" " break [class:]method\n" "\n" "Set breakpoint at specified line or method.\n" "If \'[file:]line\' is specified, break at start of code for that line (in a file).\n" "If \'[class:]method\' is specified, break at start of code for that method (of the class).\n" }, { "c[ontinue]", NULL, "Continue program being debugged", "Usage: continue [N]\n" "\n" "Continue program stopped by a breakpoint.\n" "If N, which is non negative value, is passed,\n" "proceed program until the N-th breakpoint is coming.\n" "If N is not passed, N is assumed 1.\n" }, { "d[elete]", NULL, "Delete some breakpoints", "Usage: delete [bpno1 [bpno2 [... [bpnoN]]]]\n" "\n" "Delete some breakpoints.\n" "Arguments are breakpoint numbers with spaces in between.\n" "To delete all breakpoints, give no argument.\n" }, { "dis[able]", NULL, "Disable some breakpoints", "Usage: disable [bpno1 [bpno2 [... [bpnoN]]]]\n" "\n" "Disable some breakpoints.\n" "Arguments are breakpoint numbers with spaces in between.\n" "To disable all breakpoints, give no argument.\n" }, { "en[able]", NULL, "Enable some breakpoints", "Usage: enable [bpno1 [bpno2 [... [bpnoN]]]]\n" "\n" "Enable some breakpoints.\n" "Arguments are breakpoint numbers with spaces in between.\n" "To enable all breakpoints, give no argument.\n" }, { "ev[al]", NULL, "Evaluate expression", "Usage: eval expr\n" "\n" "It evaluates and prints the value of the mruby expression.\n" "This is equivalent to the \'print\' command.\n" }, { "h[elp]", NULL, "Print this help", "Usage: help [command]\n" "\n" "With no arguments, help displays a short list of commands.\n" "With a command name as help argument, help displays how to use that command.\n" }, { "i[nfo]", "b[reakpoints]", "Status of breakpoints", "Usage: info breakpoints [bpno1 [bpno2 [... [bpnoN]]]]\n" "\n" "Status of specified breakpoints (all user-settable breakpoints if no argument).\n" "Arguments are breakpoint numbers with spaces in between.\n" }, { "l[ist]", NULL, "List specified line", "Usage: list\n" " list first[,last]\n" " list filename:first[,last]\n" "\n" "Print lines from a source file.\n" "\n" "With first and last, list prints lines from first to last.\n" "When last is empty, it stands for ten lines away from first.\n" "With filename, list prints lines in the specified source file.\n" }, { "p[rint]", NULL, "Print value of expression", "Usage: print expr\n" "\n" "It evaluates and prints the value of the mruby expression.\n" "This is equivalent to the \'eval\' command.\n" }, { "q[uit]", NULL, "Exit mrdb", "Usage: quit\n" "\n" "Exit mrdb.\n" }, { "r[un]", NULL, "Start debugged program", "Usage: run\n" "\n" "Start debugged program.\n" }, { "s[tep]", NULL, "Step program until it reaches a different source line", "Usage: step\n" "\n" "Step program until it reaches a different source line.\n" }, { NULL, NULL, NULL, NULL } }; typedef struct listcmd_parser_state { mrb_bool parse_error; mrb_bool has_line_min; mrb_bool has_line_max; char *filename; uint16_t line_min; uint16_t line_max; } listcmd_parser_state; static listcmd_parser_state* listcmd_parser_state_new(mrb_state *mrb) { listcmd_parser_state *st = (listcmd_parser_state*)mrb_malloc(mrb, sizeof(listcmd_parser_state)); memset(st, 0, sizeof(listcmd_parser_state)); return st; } static void listcmd_parser_state_free(mrb_state *mrb, listcmd_parser_state *st) { if (st != NULL) { if (st->filename != NULL) { mrb_free(mrb, st->filename); } mrb_free(mrb, st); } } static mrb_bool parse_uint(char **sp, uint16_t *n) { char *p; int i; if (*sp == NULL || **sp == '\0') { return FALSE; } for (p = *sp; *p != '\0' && ISDIGIT(*p); p++) ; if (p != *sp && (i = atoi(*sp)) >= 0) { *n = (uint16_t)i; *sp = p; return TRUE; } return FALSE; } static mrb_bool skip_char(char **sp, char c) { if (*sp != NULL && **sp == c) { ++*sp; return TRUE; } return FALSE; } static mrb_bool parse_lineno(mrb_state *mrb, char **sp, listcmd_parser_state *st) { if (*sp == NULL || **sp == '\0') { return FALSE; } st->has_line_min = FALSE; st->has_line_max = FALSE; if (parse_uint(sp, &st->line_min)) { st->has_line_min = TRUE; } else { return FALSE; } if (skip_char(sp, ',')) { if (parse_uint(sp, &st->line_max)) { st->has_line_max = TRUE; } else { st->parse_error = TRUE; return FALSE; } } return TRUE; } static mrb_bool parse_filename(mrb_state *mrb, char **sp, listcmd_parser_state *st) { char *p; int len; if (st->filename != NULL) { mrb_free(mrb, st->filename); st->filename = NULL; } if ((p = strchr(*sp, ':')) != NULL) { len = p - *sp; } else { len = strlen(*sp); } if (len > 0) { st->filename = (char*)mrb_malloc(mrb, len + 1); strncpy(st->filename, *sp, len); st->filename[len] = '\0'; *sp += len; return TRUE; } else { return FALSE; } } char* replace_ext(mrb_state *mrb, const char *filename, const char *ext) { size_t len; const char *p; char *s; if (filename == NULL) { return NULL; } if ((p = strrchr(filename, '.')) != NULL && strchr(p, '/') == NULL) { len = p - filename; } else { len = strlen(filename); } s = (char*)mrb_malloc(mrb, len + strlen(ext) + 1); memset(s, '\0', len + strlen(ext) + 1); strncpy(s, filename, len); strcat(s, ext); return s; } static mrb_bool parse_listcmd_args(mrb_state *mrb, mrdb_state *mrdb, listcmd_parser_state *st) { char *p; switch (mrdb->wcnt) { case 2: p = mrdb->words[1]; /* mrdb->words[1] ::= | ':' | */ if (!parse_lineno(mrb, &p, st)) { if (parse_filename(mrb, &p, st)) { if (skip_char(&p, ':')) { if (!parse_lineno(mrb, &p, st)) { st->parse_error = TRUE; } } } else { st->parse_error = TRUE; } } if (*p != '\0') { st->parse_error = TRUE; } break; case 1: case 0: /* do nothing */ break; default: st->parse_error = TRUE; printf("too many arguments\n"); break; } if (!st->parse_error) { if (!st->has_line_min) { st->line_min = (!st->filename && mrdb->dbg->prvline > 0) ? mrdb->dbg->prvline : 1; } if (!st->has_line_max) { st->line_max = st->line_min + 9; } if (st->filename == NULL) { if (mrdb->dbg->prvfile && strcmp(mrdb->dbg->prvfile, "-")) { st->filename = replace_ext(mrb, mrdb->dbg->prvfile, ".rb"); } } } if (st->parse_error || st->filename == NULL) { return FALSE; } return TRUE; } static mrb_bool check_cmd_pattern(const char *pattern, const char *cmd) { const char *lbracket, *rbracket, *p, *q; if (pattern == NULL && cmd == NULL) { return TRUE; } if (pattern == NULL || cmd == NULL) { return FALSE; } if ((lbracket = strchr(pattern, '[')) == NULL) { return !strcmp(pattern, cmd); } if ((rbracket = strchr(pattern, ']')) == NULL) { return FALSE; } if (strncmp(pattern, cmd, lbracket - pattern)) { return FALSE; } p = lbracket + 1; q = (char *)cmd + (lbracket - pattern); for ( ; p < rbracket && *q != '\0'; p++, q++) { if (*p != *q) { break; } } return *q == '\0'; } static help_msg* get_help_msg(char *cmd1, char *cmd2) { help_msg *p; if (cmd1 == NULL) { return NULL; } for (p = help_msg_list; p->cmd1 != NULL; p++) { if (check_cmd_pattern(p->cmd1, cmd1) && check_cmd_pattern(p->cmd2, cmd2)) { return p; } } return NULL; } static mrb_bool show_short_help(void) { help_msg *p; printf("Commands\n"); for (p = help_msg_list; p->cmd1 != NULL; p++) { if (p->cmd2 == NULL) { printf(" %s -- %s\n", p->cmd1, p->short_msg); } else { printf(" %s %s -- %s\n", p->cmd1, p->cmd2, p->short_msg); } } return TRUE; } static mrb_bool show_long_help(char *cmd1, char *cmd2) { help_msg *help; if ((help = get_help_msg(cmd1, cmd2)) == NULL) { return FALSE; } printf("%s", help->long_msg); return TRUE; } dbgcmd_state dbgcmd_list(mrb_state *mrb, mrdb_state *mrdb) { char *filename; listcmd_parser_state *st = listcmd_parser_state_new(mrb); if (parse_listcmd_args(mrb, mrdb, st)) { if ((filename = mrb_debug_get_source(mrb, mrdb, mrdb->srcpath, st->filename)) == NULL) { filename = st->filename; } mrb_debug_list(mrb, mrdb->dbg, filename, st->line_min, st->line_max); if (filename != NULL && filename != st->filename) { mrb_free(mrb, filename); } listcmd_parser_state_free(mrb, st); } return DBGST_PROMPT; } dbgcmd_state dbgcmd_help(mrb_state *mrb, mrdb_state *mrdb) { mrb_bool is_valid; int i; switch (mrdb->wcnt) { case 0: case 1: is_valid = show_short_help(); break; case 2: is_valid = show_long_help(mrdb->words[1], NULL); break; case 3: is_valid = show_long_help(mrdb->words[1], mrdb->words[2]); break; default: is_valid = FALSE; break; } if (!is_valid) { printf("Invalid command \""); for (i = 1; i < mrdb->wcnt; i++) { printf("%s%s", i == 1 ? "" : " ", mrdb->words[i]); } printf("\". Try \"help\".\n"); } return DBGST_PROMPT; } dbgcmd_state dbgcmd_quit(mrb_state *mrb, mrdb_state *mrdb) { switch (mrdb->dbg->xm) { case DBG_RUN: case DBG_STEP: case DBG_NEXT: while (1) { char c; int buf; printf("The program is running. Exit anyway? (y or n) "); fflush(stdout); if ((buf = getchar()) == EOF) { mrdb->dbg->xm = DBG_QUIT; break; } c = buf; while (buf != '\n' && (buf = getchar()) != EOF) ; if (c == 'y' || c == 'Y') { mrdb->dbg->xm = DBG_QUIT; break; } else if (c == 'n' || c == 'N') { break; } else { printf("Please answer y or n.\n"); } } break; default: mrdb->dbg->xm = DBG_QUIT; break; } if (mrdb->dbg->xm == DBG_QUIT) { struct RClass *exc; exc = mrb_define_class(mrb, "DebuggerExit", mrb_class_get(mrb, "Exception")); mrb_raise(mrb, exc, "Exit mrdb."); } return DBGST_PROMPT; } mruby-2.0.0/mrbgems/mruby-bin-debugger/tools/mrdb/cmdprint.c000066400000000000000000000022611340361412400240400ustar00rootroot00000000000000/* ** cmdprint.c - mruby debugger print command functions ** */ #include #include "mrdb.h" #include #include #include #include #include #include #include "apiprint.h" dbgcmd_state dbgcmd_print(mrb_state *mrb, mrdb_state *mrdb) { mrb_value expr; mrb_value result; mrb_value s; uint8_t wcnt; int ai; if (mrdb->wcnt <= 1) { puts("Parameter not specified."); return DBGST_PROMPT; } ai = mrb_gc_arena_save(mrb); /* eval expr */ expr = mrb_str_new_cstr(mrb, NULL); for (wcnt=1; wcntwcnt; wcnt++) { expr = mrb_str_cat_lit(mrb, expr, " "); expr = mrb_str_cat_cstr(mrb, expr, mrdb->words[wcnt]); } result = mrb_debug_eval(mrb, mrdb->dbg, RSTRING_PTR(expr), RSTRING_LEN(expr), NULL); /* $print_no = result */ s = mrb_str_cat_lit(mrb, result, "\0"); printf("$%lu = %s\n", (unsigned long)mrdb->print_no++, RSTRING_PTR(s)); if (mrdb->print_no == 0) { mrdb->print_no = 1; } mrb_gc_arena_restore(mrb, ai); return DBGST_PROMPT; } dbgcmd_state dbgcmd_eval(mrb_state *mrb, mrdb_state *mrdb) { return dbgcmd_print(mrb, mrdb); } mruby-2.0.0/mrbgems/mruby-bin-debugger/tools/mrdb/cmdrun.c000066400000000000000000000023761340361412400235170ustar00rootroot00000000000000/* ** cmdrun.c - mruby debugger run command functions ** */ #include #include "mrdb.h" dbgcmd_state dbgcmd_run(mrb_state *mrb, mrdb_state *mrdb) { mrb_debug_context *dbg = mrdb->dbg; if (dbg->xm == DBG_INIT){ dbg->xm = DBG_RUN; } else { dbg->xm = DBG_QUIT; if (dbg->xphase == DBG_PHASE_RUNNING){ struct RClass *exc; puts("Start it from the beginning."); exc = mrb_define_class(mrb, "DebuggerRestart", mrb_class_get(mrb, "Exception")); mrb_raise(mrb, exc, "Restart mrdb."); } } return DBGST_RESTART; } dbgcmd_state dbgcmd_continue(mrb_state *mrb, mrdb_state *mrdb) { mrb_debug_context *dbg = mrdb->dbg; int ccnt = 1; if (mrdb->wcnt > 1){ sscanf(mrdb->words[1], "%d", &ccnt); } dbg->ccnt = (uint16_t)(ccnt > 0 ? ccnt : 1); /* count of continue */ if (dbg->xphase == DBG_PHASE_AFTER_RUN){ puts("The program is not running."); dbg->xm = DBG_QUIT; } else { dbg->xm = DBG_RUN; } return DBGST_CONTINUE; } dbgcmd_state dbgcmd_step(mrb_state *mrb, mrdb_state *mrdb) { mrdb->dbg->xm = DBG_STEP; return DBGST_CONTINUE; } dbgcmd_state dbgcmd_next(mrb_state *mrb, mrdb_state *mrdb) { mrdb->dbg->xm = DBG_NEXT; mrdb->dbg->prvci = mrb->c->ci; return DBGST_CONTINUE; } mruby-2.0.0/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c000066400000000000000000000422271340361412400231520ustar00rootroot00000000000000/* ** mrdb.c - mruby debugger ** */ #include #include #include #include #include #include #include #include #include #include #include "mrdb.h" #include "apibreak.h" #include "apilist.h" void mrdb_state_free(mrb_state *); static mrb_debug_context *_debug_context = NULL; static mrdb_state *_mrdb_state = NULL; struct _args { FILE *rfp; char* fname; char* srcpath; int argc; char** argv; mrb_bool mrbfile : 1; }; typedef struct debug_command { const char *cmd1; const char *cmd2; uint8_t len1; uint8_t len2; uint8_t div; debug_command_id id; debug_command_func func; } debug_command; static const debug_command debug_command_list[] = { {"break", NULL, 1, 0, 0, DBGCMD_BREAK, dbgcmd_break}, /* b[reak] */ {"continue", NULL, 1, 0, 0, DBGCMD_CONTINUE, dbgcmd_continue}, /* c[ontinue] */ {"delete", NULL, 1, 0, 1, DBGCMD_DELETE, dbgcmd_delete}, /* d[elete] */ {"disable", NULL, 3, 0, 1, DBGCMD_DISABLE, dbgcmd_disable}, /* dis[able] */ {"enable", NULL, 2, 0, 1, DBGCMD_ENABLE, dbgcmd_enable}, /* en[able] */ {"eval", NULL, 2, 0, 0, DBGCMD_EVAL, dbgcmd_eval}, /* ev[al] */ {"help", NULL, 1, 0, 1, DBGCMD_HELP, dbgcmd_help}, /* h[elp] */ {"info", "breakpoints", 1, 1, 1, DBGCMD_INFO_BREAK, dbgcmd_info_break}, /* i[nfo] b[reakpoints] */ {"list", NULL, 1, 0, 1, DBGCMD_LIST, dbgcmd_list}, /* l[ist] */ {"print", NULL, 1, 0, 0, DBGCMD_PRINT, dbgcmd_print}, /* p[rint] */ {"quit", NULL, 1, 0, 0, DBGCMD_QUIT, dbgcmd_quit}, /* q[uit] */ {"run", NULL, 1, 0, 0, DBGCMD_RUN, dbgcmd_run}, /* r[un] */ {"step", NULL, 1, 0, 1, DBGCMD_STEP, dbgcmd_step}, /* s[tep] */ {"next", NULL, 1, 0, 1, DBGCMD_NEXT, dbgcmd_next}, /* n[ext] */ {NULL} }; static void usage(const char *name) { static const char *const usage_msg[] = { "switches:", "-b load and execute RiteBinary (mrb) file", "-d specify source directory", "--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 int parse_args(mrb_state *mrb, int argc, char **argv, struct _args *args) { char **origargv = argv; static const struct _args args_zero = { 0 }; *args = args_zero; for (argc--,argv++; argc > 0; argc--,argv++) { char *item; if (argv[0][0] != '-') break; item = argv[0] + 1; switch (*item++) { case 'b': args->mrbfile = TRUE; break; case 'd': if (item[0]) { goto append_srcpath; } else if (argc > 1) { argc--; argv++; item = argv[0]; append_srcpath: if (!args->srcpath) { size_t buflen; char *buf; buflen = strlen(item) + 1; buf = (char *)mrb_malloc(mrb, buflen); memcpy(buf, item, buflen); args->srcpath = buf; } else { size_t srcpathlen; size_t itemlen; srcpathlen = strlen(args->srcpath); itemlen = strlen(item); args->srcpath = (char *)mrb_realloc(mrb, args->srcpath, srcpathlen + itemlen + 2); args->srcpath[srcpathlen] = '\n'; memcpy(args->srcpath + srcpathlen + 1, item, itemlen + 1); } } else { printf("%s: No path specified for -d\n", *origargv); return EXIT_SUCCESS; } break; case '-': if (strcmp((*argv) + 2, "version") == 0) { mrb_show_version(mrb); exit(EXIT_SUCCESS); } else if (strcmp((*argv) + 2, "copyright") == 0) { mrb_show_copyright(mrb); exit(EXIT_SUCCESS); } default: return EXIT_FAILURE; } } if (args->rfp == NULL) { if (*argv == NULL) { printf("%s: Program file not specified.\n", *origargv); return EXIT_FAILURE; } else { args->rfp = fopen(argv[0], args->mrbfile ? "rb" : "r"); if (args->rfp == NULL) { printf("%s: Cannot open program file. (%s)\n", *origargv, *argv); return EXIT_FAILURE; } args->fname = argv[0]; argc--; argv++; } } args->argv = (char **)mrb_realloc(mrb, args->argv, sizeof(char*) * (argc + 1)); memcpy(args->argv, argv, (argc+1) * sizeof(char*)); args->argc = argc; return EXIT_SUCCESS; } static void cleanup(mrb_state *mrb, struct _args *args) { if (args->rfp) fclose(args->rfp); if (args->srcpath) mrb_free(mrb, args->srcpath); if (args->argv) mrb_free(mrb, args->argv); mrdb_state_free(mrb); mrb_close(mrb); } static mrb_debug_context* mrb_debug_context_new(mrb_state *mrb) { mrb_debug_context *dbg = (mrb_debug_context*)mrb_malloc(mrb, sizeof(mrb_debug_context)); memset(dbg, 0, sizeof(mrb_debug_context)); dbg->xm = DBG_INIT; dbg->xphase = DBG_PHASE_BEFORE_RUN; dbg->next_bpno = 1; return dbg; } mrb_debug_context* mrb_debug_context_get(mrb_state *mrb) { if (!_debug_context) { _debug_context = mrb_debug_context_new(mrb); } return _debug_context; } void mrb_debug_context_set(mrb_debug_context *dbg) { _debug_context = dbg; } void mrb_debug_context_free(mrb_state *mrb) { if (_debug_context) { mrb_debug_delete_break_all(mrb, _debug_context); mrb_free(mrb, _debug_context); _debug_context = NULL; } } static mrdb_state* mrdb_state_new(mrb_state *mrb) { mrdb_state *mrdb = (mrdb_state*)mrb_malloc(mrb, sizeof(mrdb_state)); memset(mrdb, 0, sizeof(mrdb_state)); mrdb->dbg = mrb_debug_context_get(mrb); mrdb->command = (char*)mrb_malloc(mrb, MAX_COMMAND_LINE+1); mrdb->print_no = 1; return mrdb; } mrdb_state* mrdb_state_get(mrb_state *mrb) { if (!_mrdb_state) { _mrdb_state = mrdb_state_new(mrb); } return _mrdb_state; } void mrdb_state_set(mrdb_state *mrdb) { _mrdb_state = mrdb; } void mrdb_state_free(mrb_state *mrb) { mrb_debug_context_free(mrb); if (_mrdb_state) { mrb_free(mrb, _mrdb_state->command); mrb_free(mrb, _mrdb_state); _mrdb_state = NULL; } } static char* get_command(mrb_state *mrb, mrdb_state *mrdb) { int i; int c; for (i=0; icommand[i] = c; } if (i == 0 && feof(stdin)) { clearerr(stdin); strcpy(mrdb->command, "quit"); i += sizeof("quit") - 1; } if (i == MAX_COMMAND_LINE) { for ( ; (c=getchar()) != EOF && c !='\n'; i++) ; } if (i > MAX_COMMAND_LINE) { printf("command line too long.\n"); i = 0; /* discard command data */ } mrdb->command[i] = '\0'; return mrdb->command; } static char* pick_out_word(mrb_state *mrb, char **pp) { char *ps; for (ps=*pp; ISBLANK(*ps); ps++) ; if (*ps == '\0') { return NULL; } if (*ps == '\"' || *ps == '\'') { *pp = strchr(ps+1, *ps); if (*pp) (*pp)++; } else { *pp = strpbrk(ps, " \t"); } if (!*pp) { *pp = ps + strlen(ps); } if (**pp != '\0') { **pp = '\0'; (*pp)++; } return ps; } static debug_command* parse_command(mrb_state *mrb, mrdb_state *mrdb, char *buf) { debug_command *cmd = NULL; char *p = buf; size_t wlen; /* get word #1 */ mrdb->words[0] = pick_out_word(mrb, &p); if (!mrdb->words[0]) { return NULL; } mrdb->wcnt = 1; /* set remain parameter */ for ( ; *p && ISBLANK(*p); p++) ; if (*p) { mrdb->words[mrdb->wcnt++] = p; } /* check word #1 */ for (cmd=(debug_command*)debug_command_list; cmd->cmd1; cmd++) { wlen = strlen(mrdb->words[0]); if (wlen >= cmd->len1 && strncmp(mrdb->words[0], cmd->cmd1, wlen) == 0) { break; } } if (cmd->cmd2) { if (mrdb->wcnt > 1) { /* get word #2 */ mrdb->words[1] = pick_out_word(mrb, &p); if (mrdb->words[1]) { /* update remain parameter */ for ( ; *p && ISBLANK(*p); p++) ; if (*p) { mrdb->words[mrdb->wcnt++] = p; } } } /* check word #1,#2 */ for ( ; cmd->cmd1; cmd++) { wlen = strlen(mrdb->words[0]); if (wlen < cmd->len1 || strncmp(mrdb->words[0], cmd->cmd1, wlen)) { continue; } if (!cmd->cmd2) break; /* word #1 only */ if (mrdb->wcnt == 1) continue; /* word #2 not specified */ wlen = strlen(mrdb->words[1]); if (wlen >= cmd->len2 && strncmp(mrdb->words[1], cmd->cmd2, wlen) == 0) { break; /* word #1 and #2 */ } } } /* divide remain parameters */ if (cmd->cmd1 && cmd->div) { p = mrdb->words[--mrdb->wcnt]; for ( ; mrdb->wcntwcnt++) { mrdb->words[mrdb->wcnt] = pick_out_word(mrb, &p); if (!mrdb->words[mrdb->wcnt]) { break; } } } return cmd->cmd1 ? cmd : NULL; } static void print_info_stopped_break(mrb_state *mrb, mrdb_state *mrdb) { mrb_debug_breakpoint bp; int32_t ret; uint16_t lineno; const char *file; const char *method_name; const char *class_name; ret = mrb_debug_get_break(mrb, mrdb->dbg, mrdb->dbg->stopped_bpno, &bp); if (ret == 0) { switch(bp.type) { case MRB_DEBUG_BPTYPE_LINE: file = bp.point.linepoint.file; lineno = bp.point.linepoint.lineno; printf("Breakpoint %d, at %s:%d\n", bp.bpno, file, lineno); break; case MRB_DEBUG_BPTYPE_METHOD: method_name = bp.point.methodpoint.method_name; class_name = bp.point.methodpoint.class_name; if (class_name == NULL) { printf("Breakpoint %d, %s\n", bp.bpno, method_name); } else { printf("Breakpoint %d, %s:%s\n", bp.bpno, class_name, method_name); } if (mrdb->dbg->isCfunc) { printf("Stopped before calling the C function.\n"); } break; default: break; } } } static void print_info_stopped_step_next(mrb_state *mrb, mrdb_state *mrdb) { const char* file = mrdb->dbg->prvfile; uint16_t lineno = mrdb->dbg->prvline; printf("%s:%d\n", file, lineno); } static void print_info_stopped_code(mrb_state *mrb, mrdb_state *mrdb) { char* file = mrb_debug_get_source(mrb, mrdb, mrdb->srcpath, mrdb->dbg->prvfile); uint16_t lineno = mrdb->dbg->prvline; if (file != NULL) { mrb_debug_list(mrb, mrdb->dbg, file, lineno, lineno); mrb_free(mrb, file); } } static void print_info_stopped(mrb_state *mrb, mrdb_state *mrdb) { switch(mrdb->dbg->bm) { case BRK_BREAK: print_info_stopped_break(mrb, mrdb); print_info_stopped_code(mrb, mrdb); break; case BRK_STEP: case BRK_NEXT: print_info_stopped_step_next(mrb, mrdb); print_info_stopped_code(mrb, mrdb); break; default: break; } } static debug_command* get_and_parse_command(mrb_state *mrb, mrdb_state *mrdb) { debug_command *cmd = NULL; char *p; int i; while (!cmd) { for (p=NULL; !p || *p=='\0'; ) { printf("(%s:%d) ", mrdb->dbg->prvfile, mrdb->dbg->prvline); fflush(stdout); p = get_command(mrb, mrdb); } cmd = parse_command(mrb, mrdb, p); #ifdef _DBG_MRDB_PARSER_ for (i=0; iwcnt; i++) { printf("%d: %s\n", i, mrdb->words[i]); } #endif if (!cmd) { printf("invalid command ("); for (i=0; iwcnt; i++) { if (i>0) { printf(" "); } printf("%s", mrdb->words[i]); } puts(")"); } } return cmd; } static int32_t check_method_breakpoint(mrb_state *mrb, mrb_irep *irep, mrb_code *pc, mrb_value *regs) { struct RClass* c; mrb_sym sym; int32_t bpno; mrb_bool isCfunc; struct mrb_insn_data insn; mrb_debug_context *dbg = mrb_debug_context_get(mrb); isCfunc = FALSE; bpno = dbg->method_bpno; dbg->method_bpno = 0; insn = mrb_decode_insn(pc); switch(insn.insn) { case OP_SEND: case OP_SENDB: c = mrb_class(mrb, regs[insn.a]); sym = irep->syms[insn.b]; break; case OP_SUPER: c = mrb->c->ci->target_class->super; sym = mrb->c->ci->mid; break; default: sym = 0; break; } if (sym != 0) { dbg->method_bpno = mrb_debug_check_breakpoint_method(mrb, dbg, c, sym, &isCfunc); if (isCfunc) { bpno = dbg->method_bpno; dbg->method_bpno = 0; } } dbg->isCfunc = isCfunc; return bpno; } static void mrb_code_fetch_hook(mrb_state *mrb, mrb_irep *irep, mrb_code *pc, mrb_value *regs) { const char *file; int32_t line; int32_t bpno; mrb_debug_context *dbg = mrb_debug_context_get(mrb); mrb_assert(dbg); dbg->irep = irep; dbg->pc = pc; dbg->regs = regs; if (dbg->xphase == DBG_PHASE_RESTART) { dbg->root_irep = irep; dbg->prvfile = NULL; dbg->prvline = 0; dbg->prvci = NULL; dbg->xm = DBG_RUN; dbg->xphase = DBG_PHASE_RUNNING; } file = mrb_debug_get_filename(irep, pc - irep->iseq); line = mrb_debug_get_line(irep, pc - irep->iseq); switch (dbg->xm) { case DBG_STEP: if (!file || (dbg->prvfile == file && dbg->prvline == line)) { return; } dbg->method_bpno = 0; dbg->bm = BRK_STEP; break; case DBG_NEXT: if (!file || (dbg->prvfile == file && dbg->prvline == line)) { return; } if ((intptr_t)(dbg->prvci) < (intptr_t)(mrb->c->ci)) { return; } dbg->prvci = NULL; dbg->method_bpno = 0; dbg->bm = BRK_NEXT; break; case DBG_RUN: bpno = check_method_breakpoint(mrb, irep, pc, regs); if (bpno > 0) { dbg->stopped_bpno = bpno; dbg->bm = BRK_BREAK; break; } if (dbg->prvfile != file || dbg->prvline != line) { bpno = mrb_debug_check_breakpoint_line(mrb, dbg, file, line); if (bpno > 0) { dbg->stopped_bpno = bpno; dbg->bm = BRK_BREAK; break; } } dbg->prvfile = file; dbg->prvline = line; return; case DBG_INIT: dbg->root_irep = irep; dbg->bm = BRK_INIT; if (!file || line < 0) { puts("Cannot get debugging information."); } break; default: return; } dbg->prvfile = file; dbg->prvline = line; if (dbg->bm == BRK_BREAK && --dbg->ccnt > 0) { return; } dbg->break_hook(mrb, dbg); dbg->xphase = DBG_PHASE_RUNNING; } static mrdb_exemode mrb_debug_break_hook(mrb_state *mrb, mrb_debug_context *dbg) { debug_command *cmd; dbgcmd_state st = DBGST_CONTINUE; mrdb_state *mrdb = mrdb_state_get(mrb); print_info_stopped(mrb, mrdb); while (1) { cmd = get_and_parse_command(mrb, mrdb); mrb_assert(cmd); st = cmd->func(mrb, mrdb); if ((st == DBGST_CONTINUE) || (st == DBGST_RESTART)) break; } return dbg->xm; } int main(int argc, char **argv) { mrb_state *mrb = mrb_open(); int n = -1; struct _args args; mrb_value v; mrdb_state *mrdb; mrdb_state *mrdb_backup; mrb_debug_context* dbg_backup; debug_command *cmd; l_restart: if (mrb == NULL) { fputs("Invalid mrb_state, exiting mruby\n", stderr); return EXIT_FAILURE; } /* parse command parameters */ n = parse_args(mrb, argc, argv, &args); if (n == EXIT_FAILURE || args.rfp == NULL) { cleanup(mrb, &args); usage(argv[0]); return n; } /* initialize debugger information */ mrdb = mrdb_state_get(mrb); mrb_assert(mrdb && mrdb->dbg); mrdb->srcpath = args.srcpath; if (mrdb->dbg->xm == DBG_QUIT) { mrdb->dbg->xphase = DBG_PHASE_RESTART; } else { mrdb->dbg->xphase = DBG_PHASE_BEFORE_RUN; } mrdb->dbg->xm = DBG_INIT; mrdb->dbg->ccnt = 1; /* setup hook functions */ mrb->code_fetch_hook = mrb_code_fetch_hook; mrdb->dbg->break_hook = mrb_debug_break_hook; if (args.mrbfile) { /* .mrb */ v = mrb_load_irep_file(mrb, args.rfp); } else { /* .rb */ mrbc_context *cc = mrbc_context_new(mrb); mrbc_filename(mrb, cc, args.fname); v = mrb_load_file_cxt(mrb, args.rfp, cc); mrbc_context_free(mrb, cc); } if (mrdb->dbg->xm == DBG_QUIT && !mrb_undef_p(v) && mrb->exc) { const char *classname = mrb_obj_classname(mrb, mrb_obj_value(mrb->exc)); if (!strcmp(classname, "DebuggerExit")) { cleanup(mrb, &args); return 0; } if (!strcmp(classname, "DebuggerRestart")) { mrdb_backup = mrdb_state_get(mrb); dbg_backup = mrb_debug_context_get(mrb); mrdb_state_set(NULL); mrb_debug_context_set(NULL); cleanup(mrb, &args); mrb = mrb_open(); mrdb_state_set(mrdb_backup); mrb_debug_context_set(dbg_backup); goto l_restart; } } puts("mruby application exited."); mrdb->dbg->xphase = DBG_PHASE_AFTER_RUN; if (!mrb_undef_p(v)) { if (mrb->exc) { mrb_print_error(mrb); } else { printf(" => "); mrb_p(mrb, v); } } mrdb->dbg->prvfile = "-"; mrdb->dbg->prvline = 0; while (1) { cmd = get_and_parse_command(mrb, mrdb); mrb_assert(cmd); if (cmd->id == DBGCMD_QUIT) { break; } if ( cmd->func(mrb, mrdb) == DBGST_RESTART ) goto l_restart; } cleanup(mrb, &args); return 0; } mruby-2.0.0/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.h000066400000000000000000000064131340361412400231540ustar00rootroot00000000000000/* ** mrdb.h - mruby debugger ** */ #ifndef MRDB_H #define MRDB_H #include #include "mrdbconf.h" #ifdef _MSC_VER # define __func__ __FUNCTION__ #endif #define MAX_COMMAND_WORD (16) typedef enum debug_command_id { DBGCMD_RUN, DBGCMD_CONTINUE, DBGCMD_NEXT, DBGCMD_STEP, DBGCMD_BREAK, DBGCMD_INFO_BREAK, DBGCMD_WATCH, DBGCMD_INFO_WATCH, DBGCMD_ENABLE, DBGCMD_DISABLE, DBGCMD_DELETE, DBGCMD_PRINT, DBGCMD_DISPLAY, DBGCMD_INFO_DISPLAY, DBGCMD_DELETE_DISPLAY, DBGCMD_EVAL, DBGCMD_BACKTRACE, DBGCMD_LIST, DBGCMD_HELP, DBGCMD_QUIT, DBGCMD_UNKNOWN } debug_command_id; typedef enum dbgcmd_state { DBGST_CONTINUE, DBGST_PROMPT, DBGST_COMMAND_ERROR, DBGST_MAX, DBGST_RESTART } dbgcmd_state; typedef enum mrdb_exemode { DBG_INIT, DBG_RUN, DBG_STEP, DBG_NEXT, DBG_QUIT, } mrdb_exemode; typedef enum mrdb_exephase { DBG_PHASE_BEFORE_RUN, DBG_PHASE_RUNNING, DBG_PHASE_AFTER_RUN, DBG_PHASE_RESTART, } mrdb_exephase; typedef enum mrdb_brkmode { BRK_INIT, BRK_BREAK, BRK_STEP, BRK_NEXT, BRK_QUIT, } mrdb_brkmode; typedef enum { MRB_DEBUG_BPTYPE_NONE, MRB_DEBUG_BPTYPE_LINE, MRB_DEBUG_BPTYPE_METHOD, } mrb_debug_bptype; struct mrb_irep; struct mrbc_context; struct mrb_debug_context; typedef struct mrb_debug_linepoint { const char *file; uint16_t lineno; } mrb_debug_linepoint; typedef struct mrb_debug_methodpoint { const char *class_name; const char *method_name; } mrb_debug_methodpoint; typedef struct mrb_debug_breakpoint { uint32_t bpno; uint8_t enable; mrb_debug_bptype type; union point { mrb_debug_linepoint linepoint; mrb_debug_methodpoint methodpoint; } point; } mrb_debug_breakpoint; typedef struct mrb_debug_context { struct mrb_irep *root_irep; struct mrb_irep *irep; mrb_code *pc; mrb_value *regs; const char *prvfile; int32_t prvline; mrb_callinfo *prvci; mrdb_exemode xm; mrdb_exephase xphase; mrdb_brkmode bm; int16_t bmi; uint16_t ccnt; uint16_t scnt; mrb_debug_breakpoint bp[MAX_BREAKPOINT]; uint32_t bpnum; int32_t next_bpno; int32_t method_bpno; int32_t stopped_bpno; mrb_bool isCfunc; mrdb_exemode (*break_hook)(mrb_state *mrb, struct mrb_debug_context *dbg); } mrb_debug_context; typedef struct mrdb_state { char *command; uint8_t wcnt; uint8_t pi; char *words[MAX_COMMAND_WORD]; const char *srcpath; uint32_t print_no; mrb_debug_context *dbg; } mrdb_state; typedef dbgcmd_state (*debug_command_func)(mrb_state*, mrdb_state*); /* cmdrun.c */ dbgcmd_state dbgcmd_run(mrb_state*, mrdb_state*); dbgcmd_state dbgcmd_continue(mrb_state*, mrdb_state*); dbgcmd_state dbgcmd_step(mrb_state*, mrdb_state*); dbgcmd_state dbgcmd_next(mrb_state*, mrdb_state*); /* cmdbreak.c */ dbgcmd_state dbgcmd_break(mrb_state*, mrdb_state*); dbgcmd_state dbgcmd_info_break(mrb_state*, mrdb_state*); dbgcmd_state dbgcmd_delete(mrb_state*, mrdb_state*); dbgcmd_state dbgcmd_enable(mrb_state*, mrdb_state*); dbgcmd_state dbgcmd_disable(mrb_state*, mrdb_state*); /* cmdprint.c */ dbgcmd_state dbgcmd_print(mrb_state*, mrdb_state*); dbgcmd_state dbgcmd_eval(mrb_state*, mrdb_state*); /* cmdmisc.c */ dbgcmd_state dbgcmd_list(mrb_state*, mrdb_state*); dbgcmd_state dbgcmd_help(mrb_state*, mrdb_state*); dbgcmd_state dbgcmd_quit(mrb_state*, mrdb_state*); #endif mruby-2.0.0/mrbgems/mruby-bin-debugger/tools/mrdb/mrdbconf.h000066400000000000000000000004141340361412400240150ustar00rootroot00000000000000/* ** mrdbconf.h - mruby debugger configuration ** */ #ifndef MRDBCONF_H #define MRDBCONF_H /* configuration options: */ /* maximum size for command buffer */ #define MAX_COMMAND_LINE 1024 /* maximum number of setable breakpoint */ #define MAX_BREAKPOINT 5 #endif mruby-2.0.0/mrbgems/mruby-bin-debugger/tools/mrdb/mrdberror.h000066400000000000000000000007141340361412400242240ustar00rootroot00000000000000/* ** mrdberror.h - mruby debugger error code ** */ #ifndef MRDBERROR_H #define MRDBERROR_H #define MRB_DEBUG_OK (0) #define MRB_DEBUG_NOBUF (-1) #define MRB_DEBUG_INVALID_ARGUMENT (-2) #define MRB_DEBUG_BREAK_INVALID_LINENO (-11) #define MRB_DEBUG_BREAK_INVALID_FILE (-12) #define MRB_DEBUG_BREAK_INVALID_NO (-13) #define MRB_DEBUG_BREAK_NUM_OVER (-14) #define MRB_DEBUG_BREAK_NO_OVER (-15) #endif mruby-2.0.0/mrbgems/mruby-bin-mirb/000077500000000000000000000000001340361412400171345ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-bin-mirb/bintest/000077500000000000000000000000001340361412400206045ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-bin-mirb/bintest/mirb.rb000066400000000000000000000015071340361412400220650ustar00rootroot00000000000000require 'open3' assert('mirb normal operations') do o, s = Open3.capture2('bin/mirb', :stdin_data => "a=1\nb=2\na+b\n") assert_true o.include?('=> 3') assert_true o.include?('=> 2') end assert('regression for #1563') do o, s = Open3.capture2('bin/mirb', :stdin_data => "a=1;b=2;c=3\nb\nc") assert_true o.include?('=> 3') end assert('mirb -d option') do o, _ = Open3.capture2('bin/mirb', :stdin_data => "p $DEBUG\n") assert_true o.include?('=> false') o, _ = Open3.capture2('bin/mirb -d', :stdin_data => "p $DEBUG\n") assert_true o.include?('=> true') end assert('mirb -r option') do lib = Tempfile.new('lib.rb') lib.write < "Hoge.new.hoge\n") assert_true o.include?('=> :hoge') end mruby-2.0.0/mrbgems/mruby-bin-mirb/mrbgem.rake000066400000000000000000000020511340361412400212470ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-bin-mirb') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'mirb command' if spec.build.cc.search_header_path 'readline/readline.h' spec.cc.defines << "ENABLE_READLINE" if spec.build.cc.search_header_path 'termcap.h' if MRUBY_BUILD_HOST_IS_CYGWIN || MRUBY_BUILD_HOST_IS_OPENBSD if spec.build.cc.search_header_path 'termcap.h' if MRUBY_BUILD_HOST_IS_CYGWIN then spec.linker.libraries << 'ncurses' else spec.linker.libraries << 'termcap' end end end end if RUBY_PLATFORM.include?('netbsd') spec.linker.libraries << 'edit' else spec.linker.libraries << 'readline' if spec.build.cc.search_header_path 'curses.h' spec.linker.libraries << 'ncurses' end end elsif spec.build.cc.search_header_path 'linenoise.h' spec.cc.defines << "ENABLE_LINENOISE" end spec.bins = %w(mirb) spec.add_dependency('mruby-compiler', :core => 'mruby-compiler') end mruby-2.0.0/mrbgems/mruby-bin-mirb/tools/000077500000000000000000000000001340361412400202745ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-bin-mirb/tools/mirb/000077500000000000000000000000001340361412400212255ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c000066400000000000000000000373441340361412400223350ustar00rootroot00000000000000/* ** mirb - Embeddable Interactive Ruby Shell ** ** This program takes code from the user in ** an interactive way and executes it ** immediately. It's a REPL... */ #include #include #include #include #include #include #ifdef ENABLE_READLINE #include #include #define MIRB_ADD_HISTORY(line) add_history(line) #define MIRB_READLINE(ch) readline(ch) #if !defined(RL_READLINE_VERSION) || RL_READLINE_VERSION < 0x600 /* libedit & older readline do not have rl_free() */ #define MIRB_LINE_FREE(line) free(line) #else #define MIRB_LINE_FREE(line) rl_free(line) #endif #define MIRB_WRITE_HISTORY(path) write_history(path) #define MIRB_READ_HISTORY(path) read_history(path) #define MIRB_USING_HISTORY() using_history() #elif defined(ENABLE_LINENOISE) #define ENABLE_READLINE #include #define MIRB_ADD_HISTORY(line) linenoiseHistoryAdd(line) #define MIRB_READLINE(ch) linenoise(ch) #define MIRB_LINE_FREE(line) linenoiseFree(line) #define MIRB_WRITE_HISTORY(path) linenoiseHistorySave(path) #define MIRB_READ_HISTORY(path) linenoiseHistoryLoad(history_path) #define MIRB_USING_HISTORY() #endif #ifndef _WIN32 #define MIRB_SIGSETJMP(env) sigsetjmp(env, 1) #define MIRB_SIGLONGJMP(env, val) siglongjmp(env, val) #define SIGJMP_BUF sigjmp_buf #else #define MIRB_SIGSETJMP(env) setjmp(env) #define MIRB_SIGLONGJMP(env, val) longjmp(env, val) #define SIGJMP_BUF jmp_buf #endif #include #include #include #include #include #include #include #include #ifdef ENABLE_READLINE static const char history_file_name[] = ".mirb_history"; static char * get_history_path(mrb_state *mrb) { char *path = NULL; const char *home = getenv("HOME"); #ifdef _WIN32 if (home != NULL) { home = getenv("USERPROFILE"); } #endif if (home != NULL) { int len = snprintf(NULL, 0, "%s/%s", home, history_file_name); if (len >= 0) { size_t size = len + 1; path = (char *)mrb_malloc_simple(mrb, size); if (path != NULL) { int n = snprintf(path, size, "%s/%s", home, history_file_name); if (n != len) { mrb_free(mrb, path); path = NULL; } } } } return path; } #endif static void p(mrb_state *mrb, mrb_value obj, int prompt) { mrb_value val; char* msg; val = mrb_funcall(mrb, obj, "inspect", 0); if (prompt) { if (!mrb->exc) { fputs(" => ", stdout); } else { val = mrb_funcall(mrb, mrb_obj_value(mrb->exc), "inspect", 0); } } if (!mrb_string_p(val)) { val = mrb_obj_as_string(mrb, obj); } msg = mrb_locale_from_utf8(RSTRING_PTR(val), (int)RSTRING_LEN(val)); fwrite(msg, strlen(msg), 1, stdout); mrb_locale_free(msg); putc('\n', stdout); } /* Guess if the user might want to enter more * or if he wants an evaluation of his code now */ static mrb_bool is_code_block_open(struct mrb_parser_state *parser) { mrb_bool code_block_open = FALSE; /* check for heredoc */ if (parser->parsing_heredoc != NULL) return TRUE; /* check for unterminated string */ if (parser->lex_strterm) return TRUE; /* check if parser error are available */ if (0 < parser->nerr) { const char unexpected_end[] = "syntax error, unexpected $end"; const char *message = parser->error_buffer[0].message; /* a parser error occur, we have to check if */ /* we need to read one more line or if there is */ /* a different issue which we have to show to */ /* the user */ if (strncmp(message, unexpected_end, sizeof(unexpected_end) - 1) == 0) { code_block_open = TRUE; } else if (strcmp(message, "syntax error, unexpected keyword_end") == 0) { code_block_open = FALSE; } else if (strcmp(message, "syntax error, unexpected tREGEXP_BEG") == 0) { code_block_open = FALSE; } return code_block_open; } switch (parser->lstate) { /* all states which need more code */ case EXPR_BEG: /* beginning of a statement, */ /* that means previous line ended */ code_block_open = FALSE; break; case EXPR_DOT: /* a message dot was the last token, */ /* there has to come more */ code_block_open = TRUE; break; case EXPR_CLASS: /* a class keyword is not enough! */ /* we need also a name of the class */ code_block_open = TRUE; break; case EXPR_FNAME: /* a method name is necessary */ code_block_open = TRUE; break; case EXPR_VALUE: /* if, elsif, etc. without condition */ code_block_open = TRUE; break; /* now all the states which are closed */ case EXPR_ARG: /* an argument is the last token */ code_block_open = FALSE; break; /* all states which are unsure */ case EXPR_CMDARG: break; case EXPR_END: /* an expression was ended */ break; case EXPR_ENDARG: /* closing parenthese */ break; case EXPR_ENDFN: /* definition end */ break; case EXPR_MID: /* jump keyword like break, return, ... */ break; case EXPR_MAX_STATE: /* don't know what to do with this token */ break; default: /* this state is unexpected! */ break; } return code_block_open; } struct _args { FILE *rfp; mrb_bool verbose : 1; mrb_bool debug : 1; int argc; char** argv; int libc; char **libv; }; static void usage(const char *name) { static const char *const usage_msg[] = { "switches:", "-d set $DEBUG to true (same as `mruby -d`)", "-r library same as `mruby -r`", "-v print version number, then run in verbose mode", "--verbose run in verbose mode", "--version print the version", "--copyright print the copyright", NULL }; const char *const *p = usage_msg; printf("Usage: %s [switches]\n", name); while (*p) printf(" %s\n", *p++); } static char * dup_arg_item(mrb_state *mrb, const char *item) { size_t buflen = strlen(item) + 1; char *buf = (char*)mrb_malloc(mrb, buflen); memcpy(buf, item, buflen); return buf; } static int parse_args(mrb_state *mrb, int argc, char **argv, struct _args *args) { char **origargv = argv; static const struct _args args_zero = { 0 }; *args = args_zero; for (argc--,argv++; argc > 0; argc--,argv++) { char *item; if (argv[0][0] != '-') break; item = argv[0] + 1; switch (*item++) { case 'd': args->debug = TRUE; break; case 'r': if (!item[0]) { if (argc <= 1) { printf("%s: No library specified for -r\n", *origargv); return EXIT_FAILURE; } argc--; argv++; item = argv[0]; } if (args->libc == 0) { args->libv = (char**)mrb_malloc(mrb, sizeof(char*)); } else { args->libv = (char**)mrb_realloc(mrb, args->libv, sizeof(char*) * (args->libc + 1)); } args->libv[args->libc++] = dup_arg_item(mrb, item); break; case 'v': if (!args->verbose) mrb_show_version(mrb); args->verbose = TRUE; break; case '-': if (strcmp((*argv) + 2, "version") == 0) { mrb_show_version(mrb); exit(EXIT_SUCCESS); } else if (strcmp((*argv) + 2, "verbose") == 0) { args->verbose = TRUE; break; } else if (strcmp((*argv) + 2, "copyright") == 0) { mrb_show_copyright(mrb); exit(EXIT_SUCCESS); } default: return EXIT_FAILURE; } } if (args->rfp == NULL) { if (*argv != NULL) { args->rfp = fopen(argv[0], "r"); if (args->rfp == NULL) { printf("Cannot open program file. (%s)\n", *argv); return EXIT_FAILURE; } argc--; argv++; } } args->argv = (char **)mrb_realloc(mrb, args->argv, sizeof(char*) * (argc + 1)); memcpy(args->argv, argv, (argc+1) * sizeof(char*)); args->argc = argc; return EXIT_SUCCESS; } static void cleanup(mrb_state *mrb, struct _args *args) { if (args->rfp) fclose(args->rfp); mrb_free(mrb, args->argv); if (args->libc) { while (args->libc--) { mrb_free(mrb, args->libv[args->libc]); } mrb_free(mrb, args->libv); } mrb_close(mrb); } /* Print a short remark for the user */ static void print_hint(void) { printf("mirb - Embeddable Interactive Ruby Shell\n\n"); } #ifndef ENABLE_READLINE /* Print the command line prompt of the REPL */ static void print_cmdline(int code_block_open) { if (code_block_open) { printf("* "); } else { printf("> "); } fflush(stdout); } #endif void mrb_codedump_all(mrb_state*, struct RProc*); static int check_keyword(const char *buf, const char *word) { const char *p = buf; size_t len = strlen(word); /* skip preceding spaces */ while (*p && isspace((unsigned char)*p)) { p++; } /* check keyword */ if (strncmp(p, word, len) != 0) { return 0; } p += len; /* skip trailing spaces */ while (*p) { if (!isspace((unsigned char)*p)) return 0; p++; } return 1; } #ifndef ENABLE_READLINE volatile sig_atomic_t input_canceled = 0; void ctrl_c_handler(int signo) { input_canceled = 1; } #else SIGJMP_BUF ctrl_c_buf; void ctrl_c_handler(int signo) { MIRB_SIGLONGJMP(ctrl_c_buf, 1); } #endif int main(int argc, char **argv) { char ruby_code[4096] = { 0 }; char last_code_line[1024] = { 0 }; #ifndef ENABLE_READLINE int last_char; size_t char_index; #else char *history_path; char* line; #endif mrbc_context *cxt; struct mrb_parser_state *parser; mrb_state *mrb; mrb_value result; struct _args args; mrb_value ARGV; int n; int i; mrb_bool code_block_open = FALSE; int ai; unsigned int stack_keep = 0; /* new interpreter instance */ mrb = mrb_open(); if (mrb == NULL) { fputs("Invalid mrb interpreter, exiting mirb\n", stderr); return EXIT_FAILURE; } n = parse_args(mrb, argc, argv, &args); if (n == EXIT_FAILURE) { cleanup(mrb, &args); usage(argv[0]); return n; } ARGV = mrb_ary_new_capa(mrb, args.argc); for (i = 0; i < args.argc; i++) { char* utf8 = mrb_utf8_from_locale(args.argv[i], -1); if (utf8) { mrb_ary_push(mrb, ARGV, mrb_str_new_cstr(mrb, utf8)); mrb_utf8_free(utf8); } } mrb_define_global_const(mrb, "ARGV", ARGV); mrb_gv_set(mrb, mrb_intern_lit(mrb, "$DEBUG"), mrb_bool_value(args.debug)); #ifdef ENABLE_READLINE history_path = get_history_path(mrb); if (history_path == NULL) { fputs("failed to get history path\n", stderr); mrb_close(mrb); return EXIT_FAILURE; } MIRB_USING_HISTORY(); MIRB_READ_HISTORY(history_path); #endif print_hint(); cxt = mrbc_context_new(mrb); /* Load libraries */ for (i = 0; i < args.libc; i++) { FILE *lfp = fopen(args.libv[i], "r"); if (lfp == NULL) { printf("Cannot open library file. (%s)\n", args.libv[i]); cleanup(mrb, &args); return EXIT_FAILURE; } mrb_load_file_cxt(mrb, lfp, cxt); fclose(lfp); } cxt->capture_errors = TRUE; cxt->lineno = 1; mrbc_filename(mrb, cxt, "(mirb)"); if (args.verbose) cxt->dump_result = TRUE; ai = mrb_gc_arena_save(mrb); while (TRUE) { char *utf8; struct mrb_jmpbuf c_jmp; MRB_TRY(&c_jmp); mrb->jmp = &c_jmp; if (args.rfp) { if (fgets(last_code_line, sizeof(last_code_line)-1, args.rfp) != NULL) goto done; break; } #ifndef ENABLE_READLINE print_cmdline(code_block_open); signal(SIGINT, ctrl_c_handler); char_index = 0; while ((last_char = getchar()) != '\n') { if (last_char == EOF) break; if (char_index >= sizeof(last_code_line)-2) { fputs("input string too long\n", stderr); continue; } last_code_line[char_index++] = last_char; } signal(SIGINT, SIG_DFL); if (input_canceled) { ruby_code[0] = '\0'; last_code_line[0] = '\0'; code_block_open = FALSE; puts("^C"); input_canceled = 0; continue; } if (last_char == EOF) { fputs("\n", stdout); break; } last_code_line[char_index++] = '\n'; last_code_line[char_index] = '\0'; #else if (MIRB_SIGSETJMP(ctrl_c_buf) == 0) { ; } else { ruby_code[0] = '\0'; last_code_line[0] = '\0'; code_block_open = FALSE; puts("^C"); } signal(SIGINT, ctrl_c_handler); line = MIRB_READLINE(code_block_open ? "* " : "> "); signal(SIGINT, SIG_DFL); if (line == NULL) { printf("\n"); break; } if (strlen(line) > sizeof(last_code_line)-2) { fputs("input string too long\n", stderr); continue; } strcpy(last_code_line, line); strcat(last_code_line, "\n"); MIRB_ADD_HISTORY(line); MIRB_LINE_FREE(line); #endif done: if (code_block_open) { if (strlen(ruby_code)+strlen(last_code_line) > sizeof(ruby_code)-1) { fputs("concatenated input string too long\n", stderr); continue; } strcat(ruby_code, last_code_line); } else { if (check_keyword(last_code_line, "quit") || check_keyword(last_code_line, "exit")) { break; } 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 = utf8; parser->send = utf8 + strlen(utf8); parser->lineno = cxt->lineno; mrb_parser_parse(parser, cxt); code_block_open = is_code_block_open(parser); mrb_utf8_free(utf8); if (code_block_open) { /* no evaluation of code */ } else { if (0 < parser->nwarn) { /* warning */ char* msg = mrb_locale_from_utf8(parser->warn_buffer[0].message, -1); printf("line %d: %s\n", parser->warn_buffer[0].lineno, msg); mrb_locale_free(msg); } if (0 < parser->nerr) { /* syntax error */ char* msg = mrb_locale_from_utf8(parser->error_buffer[0].message, -1); printf("line %d: %s\n", parser->error_buffer[0].lineno, msg); mrb_locale_free(msg); } else { /* generate bytecode */ struct RProc *proc = mrb_generate_code(mrb, parser); if (proc == NULL) { fputs("codegen error\n", stderr); mrb_parser_free(parser); break; } if (args.verbose) { mrb_codedump_all(mrb, proc); } /* adjust stack length of toplevel environment */ if (mrb->c->cibase->env) { struct REnv *e = mrb->c->cibase->env; if (e && MRB_ENV_STACK_LEN(e) < proc->body.irep->nlocals) { MRB_ENV_SET_STACK_LEN(e, proc->body.irep->nlocals); } } /* pass a proc for evaluation */ /* evaluate the bytecode */ result = mrb_vm_run(mrb, proc, mrb_top_self(mrb), stack_keep); stack_keep = proc->body.irep->nlocals; /* did an exception occur? */ if (mrb->exc) { p(mrb, mrb_obj_value(mrb->exc), 0); mrb->exc = 0; } else { /* no */ if (!mrb_respond_to(mrb, result, mrb_intern_lit(mrb, "inspect"))){ result = mrb_any_to_s(mrb, result); } p(mrb, result, 1); } } ruby_code[0] = '\0'; last_code_line[0] = '\0'; mrb_gc_arena_restore(mrb, ai); } mrb_parser_free(parser); cxt->lineno++; MRB_CATCH(&c_jmp) { p(mrb, mrb_obj_value(mrb->exc), 0); mrb->exc = 0; } MRB_END_EXC(&c_jmp); } #ifdef ENABLE_READLINE MIRB_WRITE_HISTORY(history_path); mrb_free(mrb, history_path); #endif if (args.rfp) fclose(args.rfp); mrb_free(mrb, args.argv); if (args.libv) { for (i = 0; i < args.libc; ++i) { mrb_free(mrb, args.libv[i]); } mrb_free(mrb, args.libv); } mrbc_context_free(mrb, cxt); mrb_close(mrb); return 0; } mruby-2.0.0/mrbgems/mruby-bin-mrbc/000077500000000000000000000000001340361412400171265ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-bin-mrbc/mrbgem.rake000066400000000000000000000011161340361412400212420ustar00rootroot00000000000000MRuby::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 + [build.libmruby_core_static] do |t| build.linker.run t.name, t.prerequisites end build.bins << 'mrbc' unless build.bins.find { |v| v == 'mrbc' } end mruby-2.0.0/mrbgems/mruby-bin-mrbc/tools/000077500000000000000000000000001340361412400202665ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-bin-mrbc/tools/mrbc/000077500000000000000000000000001340361412400212115ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c000066400000000000000000000200251340361412400222770ustar00rootroot00000000000000#include #include #include #include #include #include #include #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; mrb_bool remove_lv : 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", "--remove-lv remove local variables", "--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_ENDIAN_MASK); break; case 'e': args->flags = DUMP_ENDIAN_LIL | (args->flags & ~DUMP_ENDIAN_MASK); 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); } else if (strcmp(argv[i] + 2, "remove-lv") == 0) { args->remove_lv = TRUE; break; } 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) { 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->remove_lv) { mrb_irep_remove_lv(mrb, 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 mruby-2.0.0/mrbgems/mruby-bin-mruby-config/000077500000000000000000000000001340361412400206045ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-bin-mruby-config/mrbgem.rake000066400000000000000000000017671340361412400227340ustar00rootroot00000000000000module MRuby class Build def exefile(name) if name.is_a?(Array) name.flatten.map { |n| exefile(n) } elsif name !~ /\./ "#{name}#{exts.executable}" else name end end end end MRuby.each_target do next if kind_of? MRuby::CrossBuild mruby_config = 'mruby-config' + (ENV['OS'] == 'Windows_NT' ? '.bat' : '') mruby_config_path = "#{build_dir}/bin/#{mruby_config}" @bins << mruby_config make_cfg = "#{build_dir}/lib/libmruby.flags.mak" file mruby_config_path => [libmruby_static, make_cfg] do |t| FileUtils.copy "#{File.dirname(__FILE__)}/#{mruby_config}", t.name config = Hash[open(make_cfg).read.split("\n").map {|x| a = x.split(/\s*=\s*/, 2); [a[0], a[1].gsub('\\"', '"') ]}] IO.write(t.name, File.open(t.name) {|f| f.read.gsub (/echo (MRUBY_CFLAGS|MRUBY_LIBS|MRUBY_LDFLAGS_BEFORE_LIBS|MRUBY_LDFLAGS|MRUBY_LIBMRUBY_PATH)/) {|x| config[$1].empty? ? '' : "echo #{config[$1]}"} }) FileUtils.chmod(0755, t.name) end end mruby-2.0.0/mrbgems/mruby-bin-mruby-config/mruby-config000066400000000000000000000014121340361412400231260ustar00rootroot00000000000000#!/bin/sh while [ $# -gt 0 ]; do case $1 in --cflags) echo MRUBY_CFLAGS;; --ldflags) echo MRUBY_LDFLAGS;; --ldflags-before-libs) echo MRUBY_LDFLAGS_BEFORE_LIBS;; --libs) echo MRUBY_LIBS;; --libmruby-path) echo MRUBY_LIBMRUBY_PATH;; --help) echo "Usage: mruby-config [switches]" echo " switches:" echo " --cflags print flags passed to compiler" echo " --ldflags print flags passed to linker" echo " --ldflags-before-libs print flags passed to linker before linked libraries" echo " --libs print linked libraries" echo " --libmruby-path print libmruby path" exit 0;; esac shift done mruby-2.0.0/mrbgems/mruby-bin-mruby-config/mruby-config.bat000066400000000000000000000016371340361412400237040ustar00rootroot00000000000000@echo off :top shift if "%0" equ "" goto :eof if "%0" equ "--cflags" goto cflags if "%0" equ "--ldflags" goto ldflags if "%0" equ "--ldflags-before-libs" goto ldflagsbeforelibs if "%0" equ "--libs" goto libs if "%0" equ "--libmruby-path" goto libmrubypath if "%0" equ "--help" goto showhelp echo Invalid Option goto :eof :cflags echo MRUBY_CFLAGS goto top :libs echo MRUBY_LIBS goto top :ldflags echo MRUBY_LDFLAGS goto top :ldflagsbeforelibs echo MRUBY_LDFLAGS_BEFORE_LIBS goto top :libmrubypath echo MRUBY_LIBMRUBY_PATH goto top :showhelp echo Usage: mruby-config [switches] echo switches: echo --cflags print flags passed to compiler echo --ldflags print flags passed to linker echo --ldflags-before-libs print flags passed to linker before linked libraries echo --libs print linked libraries echo --libmruby-path print libmruby path mruby-2.0.0/mrbgems/mruby-bin-mruby/000077500000000000000000000000001340361412400173415ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-bin-mruby/bintest/000077500000000000000000000000001340361412400210115ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-bin-mruby/bintest/mruby.rb000066400000000000000000000041761340361412400225040ustar00rootroot00000000000000require 'tempfile' assert('regression for #1564') do o = `#{cmd('mruby')} -e #{shellquote('<<')} 2>&1` assert_include o, "-e:1:2: syntax error" o = `#{cmd('mruby')} -e #{shellquote('<<-')} 2>&1` assert_include o, "-e:1:3: syntax error" end assert('regression for #1572') do script, bin = Tempfile.new('test.rb'), Tempfile.new('test.mrb') File.write script.path, 'p "ok"' system "#{cmd('mrbc')} -g -o #{bin.path} #{script.path}" o = `#{cmd('mruby')} -b #{bin.path}`.strip assert_equal o, '"ok"' end assert '$0 value' do script, bin = Tempfile.new('test.rb'), Tempfile.new('test.mrb') # .rb script script.write "p $0\n" script.flush assert_equal "\"#{script.path}\"", `#{cmd('mruby')} "#{script.path}"`.chomp # .mrb file `#{cmd('mrbc')} -o "#{bin.path}" "#{script.path}"` assert_equal "\"#{bin.path}\"", `#{cmd('mruby')} -b "#{bin.path}"`.chomp # one liner assert_equal '"-e"', `#{cmd('mruby')} -e #{shellquote('p $0')}`.chomp end assert '__END__', '8.6' do script = Tempfile.new('test.rb') script.write < 'mruby-compiler') spec.add_dependency('mruby-error', :core => 'mruby-error') if build.cxx_exception_enabled? build.compile_as_cxx("#{spec.dir}/tools/mruby/mruby.c", "#{spec.build_dir}/tools/mruby/mruby.cxx") end end mruby-2.0.0/mrbgems/mruby-bin-mruby/tools/000077500000000000000000000000001340361412400205015ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-bin-mruby/tools/mruby/000077500000000000000000000000001340361412400216375ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c000066400000000000000000000165331340361412400231510ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #ifdef MRB_DISABLE_STDIO static void p(mrb_state *mrb, mrb_value obj) { mrb_value val = mrb_inspect(mrb, obj); fwrite(RSTRING_PTR(val), RSTRING_LEN(val), 1, stdout); putc('\n', stdout); } #else #define p(mrb,obj) mrb_p(mrb,obj) #endif struct _args { FILE *rfp; char* cmdline; mrb_bool fname : 1; mrb_bool mrbfile : 1; mrb_bool check_syntax : 1; mrb_bool verbose : 1; mrb_bool debug : 1; int argc; char** argv; int libc; char **libv; }; static void usage(const char *name) { static const char *const usage_msg[] = { "switches:", "-b load and execute RiteBinary (mrb) file", "-c check syntax only", "-d set debugging flags (set $DEBUG to true)", "-e 'command' one line of script", "-r library load the library before executing your script", "-v print version number, then run in verbose mode", "--verbose run in 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 * dup_arg_item(mrb_state *mrb, const char *item) { size_t buflen = strlen(item) + 1; char *buf = (char*)mrb_malloc(mrb, buflen); memcpy(buf, item, buflen); return buf; } static int parse_args(mrb_state *mrb, int argc, char **argv, struct _args *args) { char **origargv = argv; static const struct _args args_zero = { 0 }; *args = args_zero; for (argc--,argv++; argc > 0; argc--,argv++) { char *item; if (argv[0][0] != '-') break; if (strlen(*argv) <= 1) { argc--; argv++; args->rfp = stdin; break; } item = argv[0] + 1; switch (*item++) { case 'b': args->mrbfile = TRUE; break; case 'c': args->check_syntax = TRUE; break; case 'd': args->debug = TRUE; break; case 'e': if (item[0]) { goto append_cmdline; } else if (argc > 1) { argc--; argv++; item = argv[0]; append_cmdline: if (!args->cmdline) { args->cmdline = dup_arg_item(mrb, item); } else { size_t cmdlinelen; size_t itemlen; cmdlinelen = strlen(args->cmdline); itemlen = strlen(item); args->cmdline = (char *)mrb_realloc(mrb, args->cmdline, cmdlinelen + itemlen + 2); args->cmdline[cmdlinelen] = '\n'; memcpy(args->cmdline + cmdlinelen + 1, item, itemlen + 1); } } else { printf("%s: No code specified for -e\n", *origargv); return EXIT_SUCCESS; } break; case 'r': if (!item[0]) { if (argc <= 1) { printf("%s: No library specified for -r\n", *origargv); return EXIT_FAILURE; } argc--; argv++; item = argv[0]; } if (args->libc == 0) { args->libv = (char**)mrb_malloc(mrb, sizeof(char*)); } else { args->libv = (char**)mrb_realloc(mrb, args->libv, sizeof(char*) * (args->libc + 1)); } args->libv[args->libc++] = dup_arg_item(mrb, item); break; case 'v': if (!args->verbose) mrb_show_version(mrb); args->verbose = TRUE; break; case '-': if (strcmp((*argv) + 2, "version") == 0) { mrb_show_version(mrb); exit(EXIT_SUCCESS); } else if (strcmp((*argv) + 2, "verbose") == 0) { args->verbose = TRUE; break; } else if (strcmp((*argv) + 2, "copyright") == 0) { mrb_show_copyright(mrb); exit(EXIT_SUCCESS); } default: return EXIT_FAILURE; } } if (args->rfp == NULL && args->cmdline == NULL) { if (*argv == NULL) args->rfp = stdin; else { args->rfp = fopen(argv[0], args->mrbfile ? "rb" : "r"); if (args->rfp == NULL) { printf("%s: Cannot open program file. (%s)\n", *origargv, *argv); return EXIT_FAILURE; } args->fname = TRUE; args->cmdline = argv[0]; argc--; argv++; } } args->argv = (char **)mrb_realloc(mrb, args->argv, sizeof(char*) * (argc + 1)); memcpy(args->argv, argv, (argc+1) * sizeof(char*)); args->argc = argc; return EXIT_SUCCESS; } static void cleanup(mrb_state *mrb, struct _args *args) { if (args->rfp && args->rfp != stdin) fclose(args->rfp); if (!args->fname) mrb_free(mrb, args->cmdline); mrb_free(mrb, args->argv); if (args->libc) { while (args->libc--) { mrb_free(mrb, args->libv[args->libc]); } mrb_free(mrb, args->libv); } mrb_close(mrb); } int main(int argc, char **argv) { mrb_state *mrb = mrb_open(); int n = -1; int i; struct _args args; mrb_value ARGV; mrbc_context *c; mrb_value v; mrb_sym zero_sym; if (mrb == NULL) { fputs("Invalid mrb_state, exiting mruby\n", stderr); return EXIT_FAILURE; } n = parse_args(mrb, argc, argv, &args); if (n == EXIT_FAILURE || (args.cmdline == NULL && args.rfp == NULL)) { cleanup(mrb, &args); usage(argv[0]); return n; } else { int ai = mrb_gc_arena_save(mrb); ARGV = mrb_ary_new_capa(mrb, args.argc); for (i = 0; i < args.argc; i++) { char* utf8 = mrb_utf8_from_locale(args.argv[i], -1); if (utf8) { mrb_ary_push(mrb, ARGV, mrb_str_new_cstr(mrb, utf8)); mrb_utf8_free(utf8); } } mrb_define_global_const(mrb, "ARGV", ARGV); mrb_gv_set(mrb, mrb_intern_lit(mrb, "$DEBUG"), mrb_bool_value(args.debug)); c = mrbc_context_new(mrb); if (args.verbose) c->dump_result = TRUE; if (args.check_syntax) c->no_exec = TRUE; /* Set $0 */ zero_sym = mrb_intern_lit(mrb, "$0"); if (args.rfp) { const char *cmdline; cmdline = args.cmdline ? args.cmdline : "-"; mrbc_filename(mrb, c, cmdline); mrb_gv_set(mrb, zero_sym, mrb_str_new_cstr(mrb, cmdline)); } else { mrbc_filename(mrb, c, "-e"); mrb_gv_set(mrb, zero_sym, mrb_str_new_lit(mrb, "-e")); } /* Load libraries */ for (i = 0; i < args.libc; i++) { FILE *lfp = fopen(args.libv[i], args.mrbfile ? "rb" : "r"); if (lfp == NULL) { printf("Cannot open library file: %s\n", args.libv[i]); mrbc_context_free(mrb, c); cleanup(mrb, &args); return EXIT_FAILURE; } if (args.mrbfile) { v = mrb_load_irep_file_cxt(mrb, lfp, c); } else { v = mrb_load_file_cxt(mrb, lfp, c); } fclose(lfp); } /* Load program */ if (args.mrbfile) { v = mrb_load_irep_file_cxt(mrb, args.rfp, c); } else if (args.rfp) { v = mrb_load_file_cxt(mrb, args.rfp, c); } else { char* utf8 = mrb_utf8_from_locale(args.cmdline, -1); if (!utf8) abort(); v = mrb_load_string_cxt(mrb, utf8, c); mrb_utf8_free(utf8); } mrb_gc_arena_restore(mrb, ai); mrbc_context_free(mrb, c); if (mrb->exc) { if (mrb_undef_p(v)) { mrb_p(mrb, mrb_obj_value(mrb->exc)); } else { mrb_print_error(mrb); } n = -1; } else if (args.check_syntax) { printf("Syntax OK\n"); } } cleanup(mrb, &args); return n == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } mruby-2.0.0/mrbgems/mruby-bin-strip/000077500000000000000000000000001340361412400173445ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-bin-strip/bintest/000077500000000000000000000000001340361412400210145ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-bin-strip/bintest/mruby-strip.rb000066400000000000000000000043651340361412400236460ustar00rootroot00000000000000require 'tempfile' assert('no files') do o = `#{cmd('mruby-strip')} 2>&1` assert_equal 1, $?.exitstatus assert_equal "no files to strip", o.split("\n")[0] end assert('file not found') do o = `#{cmd('mruby-strip')} not_found.mrb 2>&1` assert_equal 1, $?.exitstatus assert_equal "can't open file for reading not_found.mrb\n", o end assert('not irep file') do t = Tempfile.new('script.rb') t.write 'p test\n' t.flush o = `#{cmd('mruby-strip')} #{t.path} 2>&1` assert_equal 1, $?.exitstatus assert_equal "can't read irep file #{t.path}\n", o end assert('success') do script_file, compiled1, compiled2 = Tempfile.new('script.rb'), Tempfile.new('c1.mrb'), Tempfile.new('c2.mrb') script_file.write "p 'test'\n" script_file.flush `#{cmd('mrbc')} -g -o #{compiled1.path} #{script_file.path}` `#{cmd('mrbc')} -g -o #{compiled2.path} #{script_file.path}` o = `#{cmd('mruby-strip')} #{compiled1.path}` assert_equal 0, $?.exitstatus assert_equal "", o assert_equal `#{cmd('mruby')} #{script_file.path}`, `#{cmd('mruby')} -b #{compiled1.path}` o = `#{cmd('mruby-strip')} #{compiled1.path} #{compiled2.path}` assert_equal 0, $?.exitstatus assert_equal "", o end assert('check debug section') do script_file, with_debug, without_debug = Tempfile.new('script.rb'), Tempfile.new('c1.mrb'), Tempfile.new('c2.mrb') script_file.write "p 'test'\n" script_file.flush `#{cmd('mrbc')} -o #{without_debug.path} #{script_file.path}` `#{cmd('mrbc')} -g -o #{with_debug.path} #{script_file.path}` assert_true with_debug.size >= without_debug.size `#{cmd('mruby-strip')} #{with_debug.path}` assert_equal without_debug.size, with_debug.size end assert('check lv section') do script_file, with_lv, without_lv = Tempfile.new('script.rb'), Tempfile.new('c1.mrb'), Tempfile.new('c2.mrb') script_file.write < #include #include #include #include #include struct strip_args { int argc_start; int argc; char **argv; mrb_bool lvar; }; static void print_usage(const char *f) { printf("Usage: %s [switches] irepfiles\n", f); printf("switches:\n"); printf(" -l, --lvar remove LVAR section too.\n"); } static int parse_args(int argc, char **argv, struct strip_args *args) { int i; args->argc_start = 0; args->argc = argc; args->argv = argv; args->lvar = FALSE; for (i = 1; i < argc; ++i) { const size_t len = strlen(argv[i]); if (len >= 2 && argv[i][0] == '-') { switch (argv[i][1]) { case 'l': args->lvar = TRUE; break; case '-': if (strncmp((*argv) + 2, "lvar", len) == 0) { args->lvar = TRUE; break; } default: return -1; } } else { break; } } args->argc_start = i; return i; } static int strip(mrb_state *mrb, struct strip_args *args) { int i; for (i = args->argc_start; i < args->argc; ++i) { char *filename; FILE *rfile; mrb_irep *irep; FILE *wfile; int dump_result; filename = args->argv[i]; rfile = fopen(filename, "rb"); if (rfile == NULL) { fprintf(stderr, "can't open file for reading %s\n", filename); return EXIT_FAILURE; } irep = mrb_read_irep_file(mrb, rfile); fclose(rfile); if (irep == NULL) { fprintf(stderr, "can't read irep file %s\n", filename); return EXIT_FAILURE; } /* clear lv if --lvar is enabled */ if (args->lvar) { mrb_irep_remove_lv(mrb, irep); } wfile = fopen(filename, "wb"); if (wfile == NULL) { fprintf(stderr, "can't open file for writing %s\n", filename); mrb_irep_decref(mrb, irep); return EXIT_FAILURE; } /* debug flag must always be false */ dump_result = mrb_dump_irep_binary(mrb, irep, FALSE, wfile); fclose(wfile); mrb_irep_decref(mrb, irep); if (dump_result != MRB_DUMP_OK) { fprintf(stderr, "error occurred during dumping %s\n", filename); return EXIT_FAILURE; } } return EXIT_SUCCESS; } int main(int argc, char **argv) { struct strip_args args; int args_result; mrb_state *mrb; int ret; if (argc <= 1) { printf("no files to strip\n"); print_usage(argv[0]); return EXIT_FAILURE; } args_result = parse_args(argc, argv, &args); if (args_result < 0) { print_usage(argv[0]); return EXIT_FAILURE; } mrb = mrb_open_core(mrb_default_allocf, NULL); if (mrb == NULL) { fputs("Invalid mrb_state, exiting mruby-strip\n", stderr); return EXIT_FAILURE; } ret = strip(mrb, &args); mrb_close(mrb); return ret; } mruby-2.0.0/mrbgems/mruby-class-ext/000077500000000000000000000000001340361412400173405ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-class-ext/mrbgem.rake000066400000000000000000000002441340361412400214550ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-class-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'class/module extension' end mruby-2.0.0/mrbgems/mruby-class-ext/src/000077500000000000000000000000001340361412400201275ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-class-ext/src/class.c000066400000000000000000000034251340361412400214040ustar00rootroot00000000000000#include "mruby.h" #include "mruby/class.h" #include "mruby/string.h" static mrb_value mrb_mod_name(mrb_state *mrb, mrb_value self) { mrb_value name = mrb_class_path(mrb, mrb_class_ptr(self)); return mrb_nil_p(name)? name : mrb_str_dup(mrb, name); } static mrb_value mrb_mod_singleton_class_p(mrb_state *mrb, mrb_value self) { return mrb_bool_value(mrb_type(self) == MRB_TT_SCLASS); } /* * call-seq: * module_exec(arg...) {|var...| block } -> obj * class_exec(arg...) {|var...| block } -> obj * * Evaluates the given block in the context of the * class/module. The method defined in the block will belong * to the receiver. Any arguments passed to the method will be * passed to the block. This can be used if the block needs to * access instance variables. * * class Thing * end * Thing.class_exec{ * def hello() "Hello there!" end * } * puts Thing.new.hello() */ static mrb_value mrb_mod_module_exec(mrb_state *mrb, mrb_value self) { const mrb_value *argv; mrb_int argc; mrb_value blk; mrb_get_args(mrb, "*&", &argv, &argc, &blk); if (mrb_nil_p(blk)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given"); } mrb->c->ci->target_class = mrb_class_ptr(self); return mrb_yield_cont(mrb, blk, self, argc, argv); } void mrb_mruby_class_ext_gem_init(mrb_state *mrb) { struct RClass *mod = mrb->module_class; mrb_define_method(mrb, mod, "name", mrb_mod_name, MRB_ARGS_NONE()); mrb_define_method(mrb, mod, "singleton_class?", mrb_mod_singleton_class_p, MRB_ARGS_NONE()); mrb_define_method(mrb, mod, "module_exec", mrb_mod_module_exec, MRB_ARGS_ANY()|MRB_ARGS_BLOCK()); mrb_define_method(mrb, mod, "class_exec", mrb_mod_module_exec, MRB_ARGS_ANY()|MRB_ARGS_BLOCK()); } void mrb_mruby_class_ext_gem_final(mrb_state *mrb) { } mruby-2.0.0/mrbgems/mruby-class-ext/test/000077500000000000000000000000001340361412400203175ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-class-ext/test/module.rb000066400000000000000000000021411340361412400221270ustar00rootroot00000000000000assert 'Module#name' do module Outer class Inner; end const_set :SetInner, Class.new end assert_equal 'Outer', Outer.name assert_equal 'Outer::Inner', Outer::Inner.name assert_equal 'Outer::SetInner', Outer::SetInner.name outer = Module.new do const_set :SetInner, Class.new end Object.const_set :SetOuter, outer assert_equal 'SetOuter', SetOuter.name assert_equal 'SetOuter::SetInner', SetOuter::SetInner.name mod = Module.new cls = Class.new assert_nil mod.name assert_nil cls.name end assert 'Module#singleton_class?' do mod = Module.new cls = Class.new scl = (class <min if obj <=> min is less # than zero, max if obj <=> max is # greater than zero and obj otherwise. # # 12.clamp(0, 100) #=> 12 # 523.clamp(0, 100) #=> 100 # -3.123.clamp(0, 100) #=> 0 # # 'd'.clamp('a', 'f') #=> 'd' # 'z'.clamp('a', 'f') #=> 'f' # def clamp(min, max) if (min <=> max) > 0 raise ArgumentError, "min argument must be smaller than max argument" end c = self <=> min if c == 0 return self elsif c < 0 return min end c = self <=> max if c > 0 return max else return self end end end mruby-2.0.0/mrbgems/mruby-compiler/000077500000000000000000000000001340361412400172475ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-compiler/bintest/000077500000000000000000000000001340361412400207175ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-compiler/bintest/mrbc.rb000066400000000000000000000020351340361412400221670ustar00rootroot00000000000000require '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 = `#{cmd('mrbc')} -c -o #{out.path} #{a.path} #{b.path} 2>&1` assert_equal "#{cmd('mrbc')}:#{a.path}:Syntax OK", result.chomp assert_equal 0, $?.exitstatus end assert('parsing function with void argument') do a, out = Tempfile.new('a.rb'), Tempfile.new('out.mrb') a.write('f ()') a.flush result = `#{cmd('mrbc')} -c -o #{out.path} #{a.path} 2>&1` assert_equal "#{cmd('mrbc')}:#{a.path}:Syntax OK", result.chomp assert_equal 0, $?.exitstatus end assert('embedded document with invalid terminator') do a, out = Tempfile.new('a.rb'), Tempfile.new('out.mrb') a.write("=begin\n=endx\n") a.flush result = `#{cmd('mrbc')} -c -o #{out.path} #{a.path} 2>&1` assert_equal "#{a.path}:3:0: embedded document meets end of file", result.chomp assert_equal 1, $?.exitstatus end mruby-2.0.0/mrbgems/mruby-compiler/core/000077500000000000000000000000001340361412400201775ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-compiler/core/codegen.c000066400000000000000000002133151340361412400217540ustar00rootroot00000000000000/* ** codegen.c - mruby code generator ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #include #include #include #include "node.h" #include #include #include #ifndef MRB_CODEGEN_LEVEL_MAX #define MRB_CODEGEN_LEVEL_MAX 1024 #endif #define MAXARG_S (1<<16) 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 pc0, 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; uint16_t sp; uint16_t pc; uint16_t lastpc; uint16_t 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; uint32_t icapa; mrb_irep *irep; uint32_t pcapa, scapa, rcapa; uint16_t nlocals; uint16_t nregs; int ai; int debug_start_pos; uint16_t filename_index; parser_state* parser; int rlev; /* recursion levels */ } 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_free(s->mrb, s->iseq); mrb_pool_close(s->mpool); s = tmp; } #ifndef MRB_DISABLE_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_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) { return s->lastlabel = s->pc; } static void emit_B(codegen_scope *s, uint32_t pc, uint8_t i) { if (pc >= MAXARG_S || s->icapa >= MAXARG_S) { codegen_error(s, "too big code block"); } if (pc >= s->icapa) { s->icapa *= 2; if (s->icapa > MAXARG_S) { s->icapa = MAXARG_S; } 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(uint16_t)*s->icapa); } } if (s->lines) { if (s->lineno > 0 || pc == 0) s->lines[pc] = s->lineno; else s->lines[pc] = s->lines[pc-1]; } s->iseq[pc] = i; } static void emit_S(codegen_scope *s, int pc, uint16_t i) { uint8_t hi = i>>8; uint8_t lo = i&0xff; emit_B(s, pc, hi); emit_B(s, pc+1, lo); } static void gen_B(codegen_scope *s, uint8_t i) { emit_B(s, s->pc, i); s->pc++; } static void gen_S(codegen_scope *s, uint16_t i) { emit_S(s, s->pc, i); s->pc += 2; } static void genop_0(codegen_scope *s, mrb_code i) { s->lastpc = s->pc; gen_B(s, i); } static void genop_1(codegen_scope *s, mrb_code i, uint16_t a) { s->lastpc = s->pc; if (a > 0xff) { gen_B(s, OP_EXT1); gen_B(s, i); gen_S(s, a); } else { gen_B(s, i); gen_B(s, (uint8_t)a); } } static void genop_2(codegen_scope *s, mrb_code i, uint16_t a, uint16_t b) { s->lastpc = s->pc; if (a > 0xff && b > 0xff) { gen_B(s, OP_EXT3); gen_B(s, i); gen_S(s, a); gen_S(s, b); } else if (b > 0xff) { gen_B(s, OP_EXT2); gen_B(s, i); gen_B(s, (uint8_t)a); gen_S(s, b); } else if (a > 0xff) { gen_B(s, OP_EXT1); gen_B(s, i); gen_S(s, a); gen_B(s, (uint8_t)b); } else { gen_B(s, i); gen_B(s, (uint8_t)a); gen_B(s, (uint8_t)b); } } static void genop_3(codegen_scope *s, mrb_code i, uint16_t a, uint16_t b, uint8_t c) { genop_2(s, i, a, b); gen_B(s, c); } static void genop_2S(codegen_scope *s, mrb_code i, uint16_t a, uint16_t b) { genop_1(s, i, a); gen_S(s, b); } static void genop_W(codegen_scope *s, mrb_code i, uint32_t a) { uint8_t a1 = (a>>16) & 0xff; uint8_t a2 = (a>>8) & 0xff; uint8_t a3 = a & 0xff; s->lastpc = s->pc; gen_B(s, i); gen_B(s, a1); gen_B(s, a2); gen_B(s, a3); } #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 mrb_bool on_eval(codegen_scope *s) { if (s && s->parser && s->parser->on_eval) return TRUE; return FALSE; } struct mrb_insn_data mrb_decode_insn(mrb_code *pc) { struct mrb_insn_data data = { 0 }; mrb_code insn = READ_B(); uint16_t a = 0; uint16_t b = 0; uint8_t c = 0; switch (insn) { #define FETCH_Z() /* empty */ #define OPCODE(i,x) case OP_ ## i: FETCH_ ## x (); break; #include "mruby/ops.h" #undef OPCODE } switch (insn) { case OP_EXT1: insn = READ_B(); switch (insn) { #define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _1 (); break; #include "mruby/ops.h" #undef OPCODE } break; case OP_EXT2: insn = READ_B(); switch (insn) { #define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _2 (); break; #include "mruby/ops.h" #undef OPCODE } break; case OP_EXT3: insn = READ_B(); switch (insn) { #define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _3 (); break; #include "mruby/ops.h" #undef OPCODE } break; default: break; } data.insn = insn; data.a = a; data.b = b; data.c = c; return data; } static struct mrb_insn_data mrb_last_insn(codegen_scope *s) { if (s->pc == s->lastpc) { struct mrb_insn_data data; data.insn = OP_NOP; return data; } return mrb_decode_insn(&s->iseq[s->lastpc]); } static mrb_bool no_peephole(codegen_scope *s) { return no_optimize(s) || s->lastlabel == s->pc || s->pc == 0 || s->pc == s->lastpc; } static uint16_t genjmp(codegen_scope *s, mrb_code i, uint16_t pc) { uint16_t pos; s->lastpc = s->pc; gen_B(s, i); pos = s->pc; gen_S(s, pc); return pos; } static uint16_t genjmp2(codegen_scope *s, mrb_code i, uint16_t a, int pc, int val) { uint16_t pos; if (!no_peephole(s) && !val) { struct mrb_insn_data data = mrb_last_insn(s); if (data.insn == OP_MOVE && data.a == a) { s->pc = s->lastpc; a = data.b; } } s->lastpc = s->pc; if (a > 0xff) { gen_B(s, OP_EXT1); gen_B(s, i); gen_S(s, a); pos = s->pc; gen_S(s, pc); } else { gen_B(s, i); gen_B(s, (uint8_t)a); pos = s->pc; gen_S(s, pc); } return pos; } static void gen_move(codegen_scope *s, uint16_t dst, uint16_t src, int nopeep) { if (no_peephole(s)) { normal: genop_2(s, OP_MOVE, dst, src); if (on_eval(s)) { genop_0(s, OP_NOP); } return; } else { struct mrb_insn_data data = mrb_last_insn(s); switch (data.insn) { case OP_MOVE: if (dst == src) return; /* remove useless MOVE */ if (data.b == dst && data.a == src) /* skip swapping MOVE */ return; goto normal; case OP_LOADNIL: case OP_LOADSELF: case OP_LOADT: case OP_LOADF: case OP_LOADI__1: case OP_LOADI_0: case OP_LOADI_1: case OP_LOADI_2: case OP_LOADI_3: case OP_LOADI_4: case OP_LOADI_5: case OP_LOADI_6: case OP_LOADI_7: if (nopeep || data.a != src || data.a < s->nlocals) goto normal; s->pc = s->lastpc; genop_1(s, data.insn, dst); break; case OP_LOADI: case OP_LOADINEG: case OP_LOADL: case OP_LOADSYM: case OP_GETGV: case OP_GETSV: case OP_GETIV: case OP_GETCV: case OP_GETCONST: case OP_STRING: case OP_LAMBDA: case OP_BLOCK: case OP_METHOD: case OP_BLKPUSH: if (nopeep || data.a != src || data.a < s->nlocals) goto normal; s->pc = s->lastpc; genop_2(s, data.insn, dst, data.b); break; default: goto normal; } } } static void gen_return(codegen_scope *s, uint8_t op, uint16_t src) { if (no_peephole(s)) { genop_1(s, op, src); } else { struct mrb_insn_data data = mrb_last_insn(s); if (data.insn == OP_MOVE && src == data.a) { s->pc = s->lastpc; genop_1(s, op, data.b); } else if (data.insn != OP_RETURN) { genop_1(s, op, src); } } } static void gen_addsub(codegen_scope *s, uint8_t op, uint16_t dst) { if (no_peephole(s)) { normal: genop_1(s, op, dst); return; } else { struct mrb_insn_data data = mrb_last_insn(s); switch (data.insn) { case OP_LOADI__1: if (op == OP_ADD) op = OP_SUB; else op = OP_ADD; data.b = 1; goto replace; case OP_LOADI_0: case OP_LOADI_1: case OP_LOADI_2: case OP_LOADI_3: case OP_LOADI_4: case OP_LOADI_5: case OP_LOADI_6: case OP_LOADI_7: data.b = data.insn - OP_LOADI_0; /* fall through */ case OP_LOADI: replace: if (data.b >= 128) goto normal; s->pc = s->lastpc; if (op == OP_ADD) { genop_2(s, OP_ADDI, dst, (uint8_t)data.b); } else { genop_2(s, OP_SUBI, dst, (uint8_t)data.b); } break; default: goto normal; } } } static int dispatch(codegen_scope *s, uint16_t pos0) { uint16_t newpos; s->lastlabel = s->pc; newpos = PEEK_S(s->iseq+pos0); emit_S(s, pos0, s->pc); return newpos; } static void dispatch_linked(codegen_scope *s, uint16_t pos) { if (pos==0) return; for (;;) { pos = dispatch(s, pos); if (pos==0) break; } } #define nregs_update do {if (s->sp > s->nregs) s->nregs = s->sp;} while (0) static void push_n_(codegen_scope *s, int n) { if (s->sp+n >= 0xffff) { codegen_error(s, "too complex expression"); } s->sp+=n; nregs_update; } static void pop_n_(codegen_scope *s, int n) { if ((int)s->sp-n < 0) { codegen_error(s, "stack pointer underflow"); } s->sp-=n; } #define push() push_n_(s,1) #define push_n(n) push_n_(s,n) #define pop() pop_n_(s,1) #define pop_n(n) pop_n_(s,n) #define cursp() (s->sp) static inline int new_lit(codegen_scope *s, mrb_value val) { int 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; #ifndef MRB_WITHOUT_FLOAT 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; #endif 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; #ifndef MRB_WITHOUT_FLOAT case MRB_TT_FLOAT: #ifdef MRB_WORD_BOXING *pv = mrb_float_pool(s->mrb, mrb_float(val)); break; #endif #endif case MRB_TT_FIXNUM: *pv = val; break; default: /* should not happen */ break; } return i; } /* maximum symbol numbers */ #define MAXSYMLEN 0x10000 static int new_sym(codegen_scope *s, mrb_sym sym) { int i, len; mrb_assert(s->irep); len = s->irep->slen; for (i=0; iirep->syms[i] == sym) return i; } if (s->irep->slen >= s->scapa) { s->scapa *= 2; s->irep->syms = (mrb_sym*)codegen_realloc(s, s->irep->syms, sizeof(mrb_sym)*s->scapa); } 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 nint(x) ((int)(intptr_t)(x)) #define nchar(x) ((char)(intptr_t)(x)) #define nsym(x) ((mrb_sym)(intptr_t)(x)) #define lv_name(lv) nsym((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; /* 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 */ /* generate loop variable */ n2 = tree->car; genop_W(s, 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); } /* construct loop */ lp = loop_push(s, LOOP_FOR); lp->pc2 = new_label(s); /* loop body */ codegen(s, tree->cdr->cdr->car, VAL); pop(); gen_return(s, OP_RETURN, cursp()); loop_pop(s, NOVAL); scope_finish(s); s = prev; genop_2(s, OP_BLOCK, cursp(), s->irep->rlen-1); push();pop(); /* space for a block */ pop(); idx = new_sym(s, mrb_intern_lit(s->mrb, "each")); genop_3(s, OP_SENDB, cursp(), idx, 0); } static int lambda_body(codegen_scope *s, node *tree, int blk) { 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->pc0 = new_label(s); } tree = tree->cdr; if (tree->car) { mrb_aspec a; int ma, oa, ra, pa, ka, kd, ba; int pos, i; node *opt; node *margs, *pargs; node *tail; /* mandatory arguments */ ma = node_len(tree->car->car); margs = tree->car->car; tail = tree->car->cdr->cdr->cdr->cdr; /* optional arguments */ oa = node_len(tree->car->cdr->car); /* rest argument? */ ra = tree->car->cdr->cdr->car ? 1 : 0; /* mandatory arugments after rest argument */ pa = node_len(tree->car->cdr->cdr->cdr->car); pargs = tree->car->cdr->cdr->cdr->car; /* keyword arguments */ ka = tail? node_len(tail->cdr->car) : 0; /* keyword dictionary? */ kd = tail && tail->cdr->cdr->car? 1 : 0; /* block argument? */ ba = tail && tail->cdr->cdr->cdr->car ? 1 : 0; if (ma > 0x1f || oa > 0x1f || pa > 0x1f || ka > 0x1f) { codegen_error(s, "too many formal arguments"); } a = MRB_ARGS_REQ(ma) | MRB_ARGS_OPT(oa) | (ra? MRB_ARGS_REST() : 0) | MRB_ARGS_POST(pa) | MRB_ARGS_KEY(ka, kd) | (ba? MRB_ARGS_BLOCK() : 0); s->ainfo = (((ma+oa) & 0x3f) << 7) /* (12bits = 5:1:5:1) */ | ((ra & 0x1) << 6) | ((pa & 0x1f) << 1) | (kd & 0x1); genop_W(s, OP_ENTER, a); /* generate jump table for optional arguments initializer */ pos = new_label(s); for (i=0; i 0) { genjmp(s, OP_JMP, 0); } opt = tree->car->cdr->car; i = 0; while (opt) { int idx; dispatch(s, pos+i*3+1); codegen(s, opt->car->cdr, VAL); pop(); idx = lv_idx(s, nsym(opt->car->car)); gen_move(s, idx, cursp(), 0); i++; opt = opt->cdr; } if (oa > 0) { dispatch(s, pos+i*3+1); } /* keyword arguments */ if (tail) { node *kwds = tail->cdr->car; int kwrest = 0; if (tail->cdr->cdr->car) { kwrest = 1; } mrb_assert(nint(tail->car) == NODE_ARGS_TAIL); mrb_assert(node_len(tail) == 4); while (kwds) { int jmpif_key_p, jmp_def_set = -1; node *kwd = kwds->car, *def_arg = kwd->cdr->cdr->car; mrb_sym kwd_sym = nsym(kwd->cdr->car); mrb_assert(nint(kwd->car) == NODE_KW_ARG); if (def_arg) { genop_2(s, OP_KEY_P, cursp(), new_sym(s, kwd_sym)); jmpif_key_p = genjmp2(s, OP_JMPIF, cursp(), 0, 0); codegen(s, def_arg, VAL); pop(); gen_move(s, lv_idx(s, kwd_sym), cursp(), 0); jmp_def_set = genjmp(s, OP_JMP, 0); dispatch(s, jmpif_key_p); } genop_2(s, OP_KARG, lv_idx(s, kwd_sym), new_sym(s, kwd_sym)); if (jmp_def_set != -1) { dispatch(s, jmp_def_set); } i++; kwds = kwds->cdr; } if (tail->cdr->car && !kwrest) { genop_0(s, OP_KEYEND); } } /* argument destructuring */ if (margs) { node *n = margs; pos = 1; while (n) { if (nint(n->car->car) == NODE_MASGN) { gen_vmassignment(s, n->car->cdr->car, pos, NOVAL); } pos++; n = n->cdr; } } if (pargs) { node *n = margs; pos = ma+oa+ra+1; while (n) { if (nint(n->car->car) == NODE_MASGN) { gen_vmassignment(s, n->car->cdr->car, pos, NOVAL); } pos++; n = n->cdr; } } } codegen(s, tree->cdr->car, VAL); pop(); if (s->pc > 0) { gen_return(s, OP_RETURN, cursp()); } 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) { codegen_error(s, "unexpected scope"); } codegen(scope, tree->cdr, VAL); gen_return(scope, OP_RETURN, scope->sp-1); if (!s->iseq) { genop_0(scope, OP_STOP); } 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 (nint(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); } #define CALL_MAXARGS 127 static int gen_values(codegen_scope *s, node *t, int val, int extra) { int n = 0; int is_splat; while (t) { is_splat = nint(t->car->car) == NODE_SPLAT; /* splat mode */ if ( n+extra >= CALL_MAXARGS - 1 /* need to subtract one because vm.c expects an array if n == CALL_MAXARGS */ || is_splat) { if (val) { if (is_splat && n == 0 && nint(t->car->cdr->car) == NODE_ARRAY) { codegen(s, t->car->cdr, VAL); pop(); } else { pop_n(n); genop_2(s, OP_ARRAY, cursp(), n); push(); codegen(s, t->car, VAL); pop(); pop(); if (is_splat) { genop_1(s, OP_ARYCAT, cursp()); } else { genop_1(s, OP_ARYPUSH, cursp()); } } t = t->cdr; while (t) { push(); codegen(s, t->car, VAL); pop(); pop(); if (nint(t->car->car) == NODE_SPLAT) { genop_1(s, OP_ARYCAT, cursp()); } else { genop_1(s, OP_ARYPUSH, cursp()); } t = t->cdr; } } else { 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; } static void gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val, int safe) { mrb_sym sym = name ? name : nsym(tree->cdr->car); int skip = 0; int n = 0, noop = 0, sendv = 0, blk = 0; codegen(s, tree->car, VAL); /* receiver */ if (safe) { int recv = cursp()-1; gen_move(s, cursp(), recv, 1); skip = genjmp2(s, OP_JMPNIL, cursp(), 0, val); } tree = tree->cdr->cdr->car; if (tree) { n = gen_values(s, tree->car, VAL, sp?1:0); if (n < 0) { n = noop = sendv = 1; push(); } } if (sp) { /* last argument pushed (attr=) */ if (sendv) { gen_move(s, cursp(), sp, 0); pop(); genop_1(s, OP_ARYPUSH, cursp()); push(); } else { gen_move(s, cursp(), sp, 0); push(); n++; } } if (tree && tree->cdr) { noop = 1; codegen(s, tree->cdr, VAL); pop(); blk = 1; } 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] == '+' && n == 1) { gen_addsub(s, OP_ADD, cursp()); } else if (!noop && symlen == 1 && symname[0] == '-' && n == 1) { gen_addsub(s, OP_SUB, cursp()); } else if (!noop && symlen == 1 && symname[0] == '*' && n == 1) { genop_1(s, OP_MUL, cursp()); } else if (!noop && symlen == 1 && symname[0] == '/' && n == 1) { genop_1(s, OP_DIV, cursp()); } else if (!noop && symlen == 1 && symname[0] == '<' && n == 1) { genop_1(s, OP_LT, cursp()); } else if (!noop && symlen == 2 && symname[0] == '<' && symname[1] == '=' && n == 1) { genop_1(s, OP_LE, cursp()); } else if (!noop && symlen == 1 && symname[0] == '>' && n == 1) { genop_1(s, OP_GT, cursp()); } else if (!noop && symlen == 2 && symname[0] == '>' && symname[1] == '=' && n == 1) { genop_1(s, OP_GE, cursp()); } else if (!noop && symlen == 2 && symname[0] == '=' && symname[1] == '=' && n == 1) { genop_1(s, OP_EQ, cursp()); } else { int idx = new_sym(s, sym); if (sendv) { genop_2(s, blk ? OP_SENDVB : OP_SENDV, cursp(), idx); } else { genop_3(s, blk ? OP_SENDB : OP_SEND, cursp(), idx, n); } } } if (safe) { dispatch(s, skip); } if (val) { push(); } } static void gen_assignment(codegen_scope *s, node *tree, int sp, int val) { int idx; int type = nint(tree->car); tree = tree->cdr; switch (type) { case NODE_GVAR: idx = new_sym(s, nsym(tree)); genop_2(s, OP_SETGV, sp, idx); break; case NODE_ARG: case NODE_LVAR: idx = lv_idx(s, nsym(tree)); if (idx > 0) { if (idx != sp) { gen_move(s, idx, sp, val); if (val && on_eval(s)) genop_0(s, OP_NOP); } break; } else { /* upvar */ int lv = 0; codegen_scope *up = s->prev; while (up) { idx = lv_idx(up, nsym(tree)); if (idx > 0) { genop_3(s, OP_SETUPVAR, sp, idx, lv); break; } lv++; up = up->prev; } } break; case NODE_IVAR: idx = new_sym(s, nsym(tree)); genop_2(s, OP_SETIV, sp, idx); break; case NODE_CVAR: idx = new_sym(s, nsym(tree)); genop_2(s, OP_SETCV, sp, idx); break; case NODE_CONST: idx = new_sym(s, nsym(tree)); genop_2(s, OP_SETCONST, sp, idx); break; case NODE_COLON2: gen_move(s, cursp(), sp, 0); push(); codegen(s, tree->car, VAL); pop_n(2); idx = new_sym(s, nsym(tree->cdr)); genop_2(s, OP_SETMCNST, sp, idx); break; case NODE_CALL: case NODE_SCALL: push(); gen_call(s, tree, attrsym(s, nsym(tree->cdr->car)), sp, NOVAL, type == NODE_SCALL); pop(); if (val) { gen_move(s, cursp(), sp, 0); } break; case NODE_MASGN: gen_vmassignment(s, tree->car, sp, val); break; /* splat without assignment */ case NODE_NIL: break; default: #ifndef MRB_DISABLE_STDIO fprintf(stderr, "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) { int sp = cursp(); genop_3(s, OP_AREF, sp, rhs, n); push(); gen_assignment(s, t->car, sp, NOVAL); pop(); n++; t = t->cdr; } } t = tree->cdr; if (t) { if (t->cdr) { /* post count */ p = t->cdr->car; while (p) { post++; p = p->cdr; } } gen_move(s, cursp(), rhs, val); push_n(post+1); pop_n(post+1); genop_3(s, OP_APOST, cursp(), n, post); n = 1; if (t->car && t->car != (node*)-1) { /* 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++; } } if (val) { gen_move(s, cursp(), rhs, 0); } } } static void gen_intern(codegen_scope *s) { pop(); genop_1(s, OP_INTERN, cursp()); 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 (nint(tree->car->car)) { case NODE_STR: if ((tree->cdr == NULL) && (nint(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_intern(s); } break; } while (j >= 2) { pop(); pop(); genop_1(s, OP_STRCAT, cursp()); push(); j--; } tree = tree->cdr; } if (j > 0) { ++i; if (sym) gen_intern(s); } pop_n(i); genop_2(s, OP_ARRAY, cursp(), i); push(); } else { while (tree) { switch (nint(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_1(s, OP_ERR, idx); } #ifndef MRB_WITHOUT_FLOAT 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 gen_retval(codegen_scope *s, node *tree) { if (nint(tree->car) == NODE_SPLAT) { codegen(s, tree, VAL); pop(); genop_1(s, OP_ARYDUP, cursp()); } else { codegen(s, tree, VAL); pop(); } } static void codegen(codegen_scope *s, node *tree, int val) { int nt; int rlev = s->rlev; if (!tree) { if (val) { genop_1(s, OP_LOADNIL, cursp()); push(); } return; } s->rlev++; if (s->rlev > MRB_CODEGEN_LEVEL_MAX) { codegen_error(s, "too complex expression"); } if (s->irep && s->filename_index != tree->filename_index) { const char *filename = mrb_parser_get_filename(s->parser, s->filename_index); mrb_debug_info_append_file(s->mrb, s->irep->debug_info, filename, s->lines, 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 = nint(tree->car); s->lineno = tree->lineno; tree = tree->cdr; switch (nt) { case NODE_BEGIN: if (val && !tree) { genop_1(s, OP_LOADNIL, cursp()); push(); } while (tree) { codegen(s, tree->car, tree->cdr ? NOVAL : val); tree = tree->cdr; } break; case NODE_RESCUE: { int noexc, exend, pos1, pos2, tmp; struct loopinfo *lp; if (tree->car == NULL) goto exit; lp = loop_push(s, LOOP_BEGIN); lp->pc0 = new_label(s); lp->pc1 = genjmp(s, OP_ONERR, 0); codegen(s, tree->car, VAL); pop(); lp->type = LOOP_RESCUE; noexc = genjmp(s, OP_JMP, 0); dispatch(s, lp->pc1); tree = tree->cdr; exend = 0; pos1 = 0; if (tree->car) { node *n2 = tree->car; int exc = cursp(); genop_1(s, OP_EXCEPT, exc); push(); while (n2) { node *n3 = n2->car; node *n4 = n3->car; if (pos1) dispatch(s, pos1); pos2 = 0; do { if (n4 && n4->car && nint(n4->car->car) == NODE_SPLAT) { codegen(s, n4->car, VAL); gen_move(s, cursp(), exc, 0); push_n(2); pop_n(2); /* space for one arg and a block */ pop(); genop_3(s, OP_SEND, cursp(), new_sym(s, mrb_intern_lit(s->mrb, "__case_eqq")), 1); } else { if (n4) { codegen(s, n4->car, VAL); } else { genop_2(s, OP_GETCONST, cursp(), new_sym(s, mrb_intern_lit(s->mrb, "StandardError"))); push(); } pop(); genop_2(s, OP_RESCUE, exc, cursp()); } tmp = genjmp2(s, OP_JMPIF, cursp(), pos2, val); pos2 = tmp; if (n4) { n4 = n4->cdr; } } while (n4); pos1 = genjmp(s, 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 = genjmp(s, OP_JMP, exend); exend = tmp; n2 = n2->cdr; push(); } if (pos1) { dispatch(s, pos1); genop_1(s, OP_RAISE, exc); } } pop(); tree = tree->cdr; dispatch(s, noexc); genop_1(s, 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: if (!tree->cdr || !tree->cdr->cdr || (nint(tree->cdr->cdr->car) == NODE_BEGIN && tree->cdr->cdr->cdr)) { int idx; s->ensure_level++; idx = scope_body(s, tree->cdr, NOVAL); genop_1(s, OP_EPUSH, idx); codegen(s, tree->car, val); s->ensure_level--; genop_1(s, OP_EPOP, 1); } else { /* empty ensure ignored */ codegen(s, tree->car, val); } break; case NODE_LAMBDA: if (val) { int idx = lambda_body(s, tree, 1); genop_2(s, OP_LAMBDA, cursp(), idx); push(); } break; case NODE_BLOCK: if (val) { int idx = lambda_body(s, tree, 1); genop_2(s, OP_BLOCK, cursp(), idx); push(); } break; case NODE_IF: { int pos1, pos2; node *elsepart = tree->cdr->cdr->car; if (!tree->car) { codegen(s, elsepart, val); goto exit; } switch (nint(tree->car->car)) { case NODE_TRUE: case NODE_INT: case NODE_STR: codegen(s, tree->cdr->car, val); goto exit; case NODE_FALSE: case NODE_NIL: codegen(s, elsepart, val); goto exit; } codegen(s, tree->car, VAL); pop(); pos1 = genjmp2(s, OP_JMPNOT, cursp(), 0, val); codegen(s, tree->cdr->car, val); if (elsepart) { if (val) pop(); pos2 = genjmp(s, OP_JMP, 0); dispatch(s, pos1); codegen(s, elsepart, val); dispatch(s, pos2); } else { if (val) { pop(); pos2 = genjmp(s, OP_JMP, 0); dispatch(s, pos1); genop_1(s, OP_LOADNIL, cursp()); dispatch(s, pos2); push(); } else { dispatch(s, pos1); } } } break; case NODE_AND: { int pos; codegen(s, tree->car, VAL); pop(); pos = genjmp2(s, OP_JMPNOT, cursp(), 0, val); codegen(s, tree->cdr, val); dispatch(s, pos); } break; case NODE_OR: { int pos; codegen(s, tree->car, VAL); pop(); pos = genjmp2(s, OP_JMPIF, cursp(), 0, val); codegen(s, tree->cdr, val); dispatch(s, pos); } break; case NODE_WHILE: { struct loopinfo *lp = loop_push(s, LOOP_NORMAL); lp->pc0 = new_label(s); lp->pc1 = genjmp(s, OP_JMP, 0); lp->pc2 = new_label(s); codegen(s, tree->cdr, NOVAL); dispatch(s, lp->pc1); codegen(s, tree->car, VAL); pop(); genjmp2(s, OP_JMPIF, cursp(), lp->pc2, NOVAL); loop_pop(s, val); } break; case NODE_UNTIL: { struct loopinfo *lp = loop_push(s, LOOP_NORMAL); lp->pc0 = new_label(s); lp->pc1 = genjmp(s, OP_JMP, 0); lp->pc2 = new_label(s); codegen(s, tree->cdr, NOVAL); dispatch(s, lp->pc1); codegen(s, tree->car, VAL); pop(); genjmp2(s, OP_JMPNOT, cursp(), lp->pc2, NOVAL); 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) { gen_move(s, cursp(), head, 0); push(); push(); pop(); pop(); pop(); if (nint(n->car->car) == NODE_SPLAT) { genop_3(s, OP_SEND, cursp(), new_sym(s, mrb_intern_lit(s->mrb, "__case_eqq")), 1); } else { genop_3(s, OP_SEND, cursp(), new_sym(s, mrb_intern_lit(s->mrb, "===")), 1); } } else { pop(); } tmp = genjmp2(s, OP_JMPIF, cursp(), pos2, NOVAL); pos2 = tmp; n = n->cdr; } if (tree->car->car) { pos1 = genjmp(s, OP_JMP, 0); dispatch_linked(s, pos2); } codegen(s, tree->car->cdr, val); if (val) pop(); tmp = genjmp(s, OP_JMP, pos3); pos3 = tmp; if (pos1) dispatch(s, pos1); tree = tree->cdr; } if (val) { int pos = cursp(); genop_1(s, OP_LOADNIL, cursp()); if (pos3) dispatch_linked(s, pos3); if (head) pop(); if (cursp() != pos) { gen_move(s, cursp(), pos, 0); } 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, 0); break; case NODE_SCALL: gen_call(s, tree, 0, 0, val, 1); break; case NODE_DOT2: codegen(s, tree->car, val); codegen(s, tree->cdr, val); if (val) { pop(); pop(); genop_1(s, OP_RANGE_INC, cursp()); push(); } break; case NODE_DOT3: codegen(s, tree->car, val); codegen(s, tree->cdr, val); if (val) { pop(); pop(); genop_1(s, OP_RANGE_EXC, cursp()); push(); } break; case NODE_COLON2: { int sym = new_sym(s, nsym(tree->cdr)); codegen(s, tree->car, VAL); pop(); genop_2(s, OP_GETMCNST, cursp(), sym); if (val) push(); } break; case NODE_COLON3: { int sym = new_sym(s, nsym(tree)); genop_1(s, OP_OCLASS, cursp()); genop_2(s, OP_GETMCNST, cursp(), sym); if (val) push(); } break; case NODE_ARRAY: { int n; n = gen_values(s, tree, val, 0); if (n >= 0) { if (val) { pop_n(n); genop_2(s, OP_ARRAY, cursp(), n); push(); } } else if (val) { push(); } } break; case NODE_HASH: case NODE_KW_HASH: { int len = 0; mrb_bool update = FALSE; while (tree) { if (nint(tree->car->car->car) == NODE_KW_REST_ARGS) { if (len > 0) { pop_n(len*2); if (!update) { genop_2(s, OP_HASH, cursp(), len); } else { pop(); genop_2(s, OP_HASHADD, cursp(), len); } push(); } codegen(s, tree->car->cdr, VAL); if (len > 0 || update) { pop(); pop(); genop_1(s, OP_HASHCAT, cursp()); push(); } update = TRUE; len = 0; } else { codegen(s, tree->car->car, val); codegen(s, tree->car->cdr, val); len++; } tree = tree->cdr; if (val && len == 255) { pop_n(len*2); if (!update) { genop_2(s, OP_HASH, cursp(), len); } else { pop(); genop_2(s, OP_HASHADD, cursp(), len); } push(); update = TRUE; len = 0; } } if (val) { pop_n(len*2); if (!update) { genop_2(s, OP_HASH, cursp(), len); } else { pop(); if (len > 0) { genop_2(s, OP_HASHADD, cursp(), len); } } 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 (nint(t->car) == NODE_ARRAY && t->cdr && 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) { if (n < len) { gen_assignment(s, t->car, rhs+n, NOVAL); n++; } else { genop_1(s, OP_LOADNIL, rhs+n); gen_assignment(s, t->car, rhs+n, NOVAL); } 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_3(s, OP_ARRAY2, 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_2(s, OP_ARRAY, 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 = nsym(tree->cdr->car); mrb_int len; const char *name = mrb_sym2name_len(s->mrb, sym, &len); int idx, callargs = -1, vsp = -1; if ((len == 2 && name[0] == '|' && name[1] == '|') && (nint(tree->car->car) == NODE_CONST || nint(tree->car->car) == NODE_CVAR)) { int onerr, noexc, exc; struct loopinfo *lp; onerr = genjmp(s, OP_ONERR, 0); lp = loop_push(s, LOOP_BEGIN); lp->pc1 = onerr; exc = cursp(); codegen(s, tree->car, VAL); lp->type = LOOP_RESCUE; genop_1(s, OP_POPERR, 1); noexc = genjmp(s, OP_JMP, 0); dispatch(s, onerr); genop_1(s, OP_EXCEPT, exc); genop_1(s, OP_LOADF, exc); dispatch(s, noexc); loop_pop(s, NOVAL); } else if (nint(tree->car->car) == NODE_CALL) { node *n = tree->car->cdr; int base, i, nargs = 0; callargs = 0; if (val) { vsp = cursp(); push(); } codegen(s, n->car, VAL); /* receiver */ idx = new_sym(s, nsym(n->cdr->car)); base = cursp()-1; if (n->cdr->cdr->car) { nargs = gen_values(s, n->cdr->cdr->car->car, VAL, 1); if (nargs >= 0) { callargs = nargs; } else { /* varargs */ push(); nargs = 1; callargs = CALL_MAXARGS; } } /* copy receiver and arguments */ gen_move(s, cursp(), base, 1); for (i=0; icar, VAL); } if (len == 2 && ((name[0] == '|' && name[1] == '|') || (name[0] == '&' && name[1] == '&'))) { int pos; pop(); if (val) { if (vsp >= 0) { gen_move(s, vsp, cursp(), 1); } pos = genjmp2(s, name[0]=='|'?OP_JMPIF:OP_JMPNOT, cursp(), 0, val); } else { pos = genjmp2(s, name[0]=='|'?OP_JMPIF:OP_JMPNOT, cursp(), 0, val); } codegen(s, tree->cdr->cdr->car, VAL); pop(); if (val && vsp >= 0) { gen_move(s, vsp, cursp(), 1); } if (nint(tree->car->car) == NODE_CALL) { if (callargs == CALL_MAXARGS) { pop(); genop_1(s, OP_ARYPUSH, cursp()); } else { pop_n(callargs); callargs++; } pop(); idx = new_sym(s, attrsym(s, nsym(tree->car->cdr->cdr->car))); genop_3(s, OP_SEND, cursp(), idx, callargs); } else { gen_assignment(s, tree->car, cursp(), val); } dispatch(s, pos); goto exit; } codegen(s, tree->cdr->cdr->car, VAL); push(); pop(); pop(); pop(); if (len == 1 && name[0] == '+') { gen_addsub(s, OP_ADD, cursp()); } else if (len == 1 && name[0] == '-') { gen_addsub(s, OP_SUB, cursp()); } else if (len == 1 && name[0] == '*') { genop_1(s, OP_MUL, cursp()); } else if (len == 1 && name[0] == '/') { genop_1(s, OP_DIV, cursp()); } else if (len == 1 && name[0] == '<') { genop_1(s, OP_LT, cursp()); } else if (len == 2 && name[0] == '<' && name[1] == '=') { genop_1(s, OP_LE, cursp()); } else if (len == 1 && name[0] == '>') { genop_1(s, OP_GT, cursp()); } else if (len == 2 && name[0] == '>' && name[1] == '=') { genop_1(s, OP_GE, cursp()); } else { idx = new_sym(s, sym); genop_3(s, OP_SEND, cursp(), idx, 1); } if (callargs < 0) { gen_assignment(s, tree->car, cursp(), val); } else { if (val && vsp >= 0) { gen_move(s, vsp, cursp(), 0); } if (callargs == CALL_MAXARGS) { pop(); genop_1(s, OP_ARYPUSH, cursp()); } else { pop_n(callargs); callargs++; } pop(); idx = new_sym(s, attrsym(s,nsym(tree->car->cdr->cdr->car))); genop_3(s, OP_SEND, cursp(), idx, callargs); } } break; case NODE_SUPER: { codegen_scope *s2 = s; int lv = 0; int n = 0, noop = 0, sendv = 0; push(); /* room for receiver */ while (!s2->mscope) { lv++; s2 = s2->prev; if (!s2) break; } genop_2S(s, OP_ARGARY, cursp(), (lv & 0xf)); push(); push(); /* ARGARY pushes two values */ pop(); pop(); if (tree) { node *args = tree->car; if (args) { n = gen_values(s, args, VAL, 0); if (n < 0) { n = noop = sendv = 1; push(); } } } if (tree && tree->cdr) { codegen(s, tree->cdr, VAL); pop(); } else { genop_1(s, OP_LOADNIL, cursp()); push(); pop(); } pop_n(n+1); if (sendv) n = CALL_MAXARGS; genop_2(s, OP_SUPER, cursp(), 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_2S(s, 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_2(s, OP_SUPER, cursp(), CALL_MAXARGS); if (val) push(); } break; case NODE_RETURN: if (tree) { gen_retval(s, tree); } else { genop_1(s, OP_LOADNIL, cursp()); } if (s->loop) { gen_return(s, OP_RETURN_BLK, cursp()); } else { gen_return(s, OP_RETURN, cursp()); } 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; push(); if (tree) { n = gen_values(s, tree, VAL, 0); if (n < 0) { n = sendv = 1; push(); } } push();pop(); /* space for a block */ pop_n(n+1); genop_2S(s, OP_BLKPUSH, cursp(), (ainfo<<4)|(lv & 0xf)); if (sendv) n = CALL_MAXARGS; genop_3(s, OP_SEND, cursp(), new_sym(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_1(s, OP_EPOP, s->ensure_level - s->loop->ensure_level); } codegen(s, tree, NOVAL); genjmp(s, OP_JMP, s->loop->pc0); } else { if (tree) { codegen(s, tree, VAL); pop(); } else { genop_1(s, OP_LOADNIL, cursp()); } gen_return(s, OP_RETURN, cursp()); } if (val) push(); break; case NODE_REDO: if (!s->loop || s->loop->type == LOOP_BEGIN || s->loop->type == LOOP_RESCUE) { raise_error(s, "unexpected redo"); } else { if (s->ensure_level > s->loop->ensure_level) { genop_1(s, OP_EPOP, s->ensure_level - s->loop->ensure_level); } genjmp(s, OP_JMP, s->loop->pc2); } if (val) push(); 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) { genop_1(s, OP_POPERR, n); } if (s->ensure_level > lp->ensure_level) { genop_1(s, OP_EPOP, s->ensure_level - lp->ensure_level); } genjmp(s, OP_JMP, lp->pc0); } } if (val) push(); } break; case NODE_LVAR: if (val) { int idx = lv_idx(s, nsym(tree)); if (idx > 0) { gen_move(s, cursp(), idx, val); if (val && on_eval(s)) genop_0(s, OP_NOP); } else { int lv = 0; codegen_scope *up = s->prev; while (up) { idx = lv_idx(up, nsym(tree)); if (idx > 0) { genop_3(s, OP_GETUPVAR, cursp(), idx, lv); break; } lv++; up = up->prev; } } push(); } break; case NODE_GVAR: { int sym = new_sym(s, nsym(tree)); genop_2(s, OP_GETGV, cursp(), sym); if (val) push(); } break; case NODE_IVAR: { int sym = new_sym(s, nsym(tree)); genop_2(s, OP_GETIV, cursp(), sym); if (val) push(); } break; case NODE_CVAR: { int sym = new_sym(s, nsym(tree)); genop_2(s, OP_GETCV, cursp(), sym); if (val) push(); } break; case NODE_CONST: { int sym = new_sym(s, nsym(tree)); genop_2(s, OP_GETCONST, cursp(), sym); if (val) push(); } break; case NODE_DEFINED: codegen(s, tree, val); break; case NODE_BACK_REF: if (val) { char buf[3]; int sym; buf[0] = '$'; buf[1] = nchar(tree); buf[2] = 0; sym = new_sym(s, mrb_intern_cstr(s->mrb, buf)); genop_2(s, OP_GETGV, cursp(), sym); push(); } break; case NODE_NTH_REF: if (val) { mrb_state *mrb = s->mrb; mrb_value str; int sym; str = mrb_format(mrb, "$%S", mrb_fixnum_value(nint(tree))); sym = new_sym(s, mrb_intern_str(mrb, str)); genop_2(s, OP_GETGV, 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 = nint(tree->cdr->car); mrb_int i; mrb_bool overflow; i = readint_mrb_int(s, p, base, FALSE, &overflow); #ifndef MRB_WITHOUT_FLOAT if (overflow) { double f = readint_float(s, p, base); int off = new_lit(s, mrb_float_value(s->mrb, f)); genop_2(s, OP_LOADL, cursp(), off); } else #endif { if (i == -1) genop_1(s, OP_LOADI__1, cursp()); else if (i < 0) genop_2(s, OP_LOADINEG, cursp(), (uint16_t)-i); else if (i < 8) genop_1(s, OP_LOADI_0 + (uint8_t)i, cursp()); else if (i <= 0xffff) genop_2(s, OP_LOADI, cursp(), (uint16_t)i); else { int off = new_lit(s, mrb_fixnum_value(i)); genop_2(s, OP_LOADL, cursp(), off); } } push(); } break; #ifndef MRB_WITHOUT_FLOAT case NODE_FLOAT: if (val) { char *p = (char*)tree; mrb_float f = mrb_float_read(p, NULL); int off = new_lit(s, mrb_float_value(s->mrb, f)); genop_2(s, OP_LOADL, cursp(), off); push(); } break; #endif case NODE_NEGATE: { nt = nint(tree->car); switch (nt) { #ifndef MRB_WITHOUT_FLOAT case NODE_FLOAT: if (val) { char *p = (char*)tree->cdr; mrb_float f = mrb_float_read(p, NULL); int off = new_lit(s, mrb_float_value(s->mrb, -f)); genop_2(s, OP_LOADL, cursp(), off); push(); } break; #endif case NODE_INT: if (val) { char *p = (char*)tree->cdr->car; int base = nint(tree->cdr->cdr->car); mrb_int i; mrb_bool overflow; i = readint_mrb_int(s, p, base, TRUE, &overflow); #ifndef MRB_WITHOUT_FLOAT if (overflow) { double f = readint_float(s, p, base); int off = new_lit(s, mrb_float_value(s->mrb, -f)); genop_2(s, OP_LOADL, cursp(), off); } else { #endif if (i == -1) genop_1(s, OP_LOADI__1, cursp()); else if (i >= -0xffff) { genop_2(s, OP_LOADINEG, cursp(), (uint16_t)-i); } else { int off = new_lit(s, mrb_fixnum_value(i)); genop_2(s, OP_LOADL, cursp(), off); } #ifndef MRB_WITHOUT_FLOAT } #endif push(); } break; default: if (val) { int sym = new_sym(s, mrb_intern_lit(s->mrb, "-@")); codegen(s, tree, VAL); pop(); genop_3(s, OP_SEND, cursp(), sym, 0); push(); } else { codegen(s, tree, NOVAL); } 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_2(s, 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) { genop_1(s, OP_LOADNIL, cursp()); push(); break; } codegen(s, n->car, VAL); n = n->cdr; while (n) { codegen(s, n->car, VAL); pop(); pop(); genop_1(s, OP_STRCAT, cursp()); push(); n = n->cdr; } } else { node *n = tree; while (n) { if (nint(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")); genop_1(s, OP_LOADSELF, cursp()); push(); codegen(s, tree->car, VAL); n = tree->cdr; while (n) { if (nint(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_1(s, OP_STRCAT, cursp()); push(); n = n->cdr; } push(); /* for block */ pop_n(3); sym = new_sym(s, mrb_intern_lit(s->mrb, "`")); genop_3(s, OP_SEND, cursp(), sym, 1); if (val) 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 off = new_lit(s, mrb_str_new(s->mrb, p, len)); int sym; genop_1(s, OP_LOADSELF, cursp()); push(); genop_2(s, OP_STRING, cursp(), off); push(); push(); pop_n(3); sym = new_sym(s, mrb_intern_lit(s->mrb, "`")); genop_3(s, OP_SEND, cursp(), sym, 1); if (val) push(); mrb_gc_arena_restore(s->mrb, ai); } break; case NODE_REGX: if (val) { char *p1 = (char*)tree->car; char *p2 = (char*)tree->cdr->car; char *p3 = (char*)tree->cdr->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_1(s, OP_OCLASS, cursp()); genop_2(s, OP_GETMCNST, cursp(), sym); push(); genop_2(s, OP_STRING, cursp(), off); push(); if (p2 || p3) { if (p2) { /* opt */ off = new_lit(s, mrb_str_new_cstr(s->mrb, p2)); genop_2(s, OP_STRING, cursp(), off); } else { genop_1(s, OP_LOADNIL, cursp()); } push(); argc++; if (p3) { /* enc */ off = new_lit(s, mrb_str_new(s->mrb, p3, 1)); genop_2(s, OP_STRING, cursp(), off); push(); argc++; } } push(); /* space for a block */ pop_n(argc+2); sym = new_sym(s, mrb_intern_lit(s->mrb, "compile")); genop_3(s, 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_1(s, OP_OCLASS, cursp()); genop_2(s, OP_GETMCNST, cursp(), sym); push(); codegen(s, n->car, VAL); n = n->cdr; while (n) { codegen(s, n->car, VAL); pop(); pop(); genop_1(s, OP_STRCAT, cursp()); push(); n = n->cdr; } n = tree->cdr->cdr; if (n->car) { /* tail */ p = (char*)n->car; off = new_lit(s, mrb_str_new_cstr(s->mrb, p)); codegen(s, tree->car, VAL); genop_2(s, OP_STRING, cursp(), off); pop(); genop_1(s, OP_STRCAT, cursp()); push(); } if (n->cdr->car) { /* opt */ char *p2 = (char*)n->cdr->car; off = new_lit(s, mrb_str_new_cstr(s->mrb, p2)); genop_2(s, OP_STRING, cursp(), off); push(); argc++; } if (n->cdr->cdr) { /* enc */ char *p2 = (char*)n->cdr->cdr; off = new_lit(s, mrb_str_new_cstr(s->mrb, p2)); genop_2(s, OP_STRING, cursp(), off); push(); argc++; } push(); /* space for a block */ pop_n(argc+2); sym = new_sym(s, mrb_intern_lit(s->mrb, "compile")); genop_3(s, OP_SEND, cursp(), sym, argc); mrb_gc_arena_restore(s->mrb, ai); push(); } else { node *n = tree->car; while (n) { if (nint(n->car->car) != NODE_STR) { codegen(s, n->car, NOVAL); } n = n->cdr; } } break; case NODE_SYM: if (val) { int sym = new_sym(s, nsym(tree)); genop_2(s, OP_LOADSYM, cursp(), sym); push(); } break; case NODE_DSYM: codegen(s, tree, val); if (val) { gen_intern(s); } break; case NODE_SELF: if (val) { genop_1(s, OP_LOADSELF, cursp()); push(); } break; case NODE_NIL: if (val) { genop_1(s, OP_LOADNIL, cursp()); push(); } break; case NODE_TRUE: if (val) { genop_1(s, OP_LOADT, cursp()); push(); } break; case NODE_FALSE: if (val) { genop_1(s, OP_LOADF, cursp()); push(); } break; case NODE_ALIAS: { int a = new_sym(s, nsym(tree->car)); int b = new_sym(s, nsym(tree->cdr)); genop_2(s, OP_ALIAS, a, b); if (val) { genop_1(s, OP_LOADNIL, cursp()); push(); } } break; case NODE_UNDEF: { node *t = tree; while (t) { int symbol = new_sym(s, nsym(t->car)); genop_1(s, OP_UNDEF, symbol); t = t->cdr; } if (val) { genop_1(s, OP_LOADNIL, cursp()); push(); } } break; case NODE_CLASS: { int idx; node *body; if (tree->car->car == (node*)0) { genop_1(s, OP_LOADNIL, cursp()); push(); } else if (tree->car->car == (node*)1) { genop_1(s, OP_OCLASS, cursp()); push(); } else { codegen(s, tree->car->car, VAL); } if (tree->cdr->car) { codegen(s, tree->cdr->car, VAL); } else { genop_1(s, OP_LOADNIL, cursp()); push(); } pop(); pop(); idx = new_sym(s, nsym(tree->car->cdr)); genop_2(s, OP_CLASS, cursp(), idx); body = tree->cdr->cdr->car; if (!(nint(body->cdr->car) == NODE_BEGIN && body->cdr->cdr == NULL)) { idx = scope_body(s, body, val); genop_2(s, OP_EXEC, cursp(), idx); } if (val) { push(); } } break; case NODE_MODULE: { int idx; if (tree->car->car == (node*)0) { genop_1(s, OP_LOADNIL, cursp()); push(); } else if (tree->car->car == (node*)1) { genop_1(s, OP_OCLASS, cursp()); push(); } else { codegen(s, tree->car->car, VAL); } pop(); idx = new_sym(s, nsym(tree->car->cdr)); genop_2(s, OP_MODULE, cursp(), idx); if (!(nint(tree->cdr->car->cdr->car) == NODE_BEGIN && tree->cdr->car->cdr->cdr == NULL)) { idx = scope_body(s, tree->cdr->car, val); genop_2(s, OP_EXEC, cursp(), idx); } if (val) { push(); } } break; case NODE_SCLASS: { int idx; codegen(s, tree->car, VAL); pop(); genop_1(s, OP_SCLASS, cursp()); if (!(nint(tree->cdr->car->cdr->car) == NODE_BEGIN && tree->cdr->car->cdr->cdr == NULL)) { idx = scope_body(s, tree->cdr->car, val); genop_2(s, OP_EXEC, cursp(), idx); } if (val) { push(); } } break; case NODE_DEF: { int sym = new_sym(s, nsym(tree->car)); int idx = lambda_body(s, tree->cdr, 0); genop_1(s, OP_TCLASS, cursp()); push(); genop_2(s, OP_METHOD, cursp(), idx); push(); pop(); pop(); genop_2(s, OP_DEF, cursp(), sym); if (val) { genop_2(s, OP_LOADSYM, cursp(), sym); push(); } } break; case NODE_SDEF: { node *recv = tree->car; int sym = new_sym(s, nsym(tree->cdr->car)); int idx = lambda_body(s, tree->cdr->cdr, 0); codegen(s, recv, VAL); pop(); genop_1(s, OP_SCLASS, cursp()); push(); genop_2(s, OP_METHOD, cursp(), idx); pop(); genop_2(s, OP_DEF, cursp(), sym); if (val) { genop_2(s, OP_LOADSYM, cursp(), sym); push(); } } break; case NODE_POSTEXE: codegen(s, tree, NOVAL); break; default: break; } exit: s->rlev = rlev; } 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 = NULL; 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); } else { p->irep->debug_info = NULL; } p->parser = prev->parser; p->filename_index = prev->filename_index; p->rlev = prev->rlev+1; return p; } static void scope_finish(codegen_scope *s) { mrb_state *mrb = s->mrb; mrb_irep *irep = s->irep; irep->flags = 0; if (s->iseq) { irep->iseq = (mrb_code *)codegen_realloc(s, s->iseq, sizeof(mrb_code)*s->pc); irep->ilen = s->pc; } 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) { const char *filename = mrb_parser_get_filename(s->parser, s->filename_index); mrb_debug_info_append_file(s->mrb, s->irep->debug_info, filename, s->lines, s->debug_start_pos, s->pc); } mrb_free(s->mrb, s->lines); 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->pc0 = 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; int n = 0; if (tree) { gen_retval(s, tree); } loop = s->loop; while (loop) { if (loop->type == LOOP_BEGIN) { n++; loop = loop->prev; } else if (loop->type == LOOP_RESCUE) { loop = loop->prev; } else{ break; } } if (!loop) { raise_error(s, "unexpected break"); return; } if (n > 0) { genop_1(s, OP_POPERR, n); } if (loop->type == LOOP_NORMAL) { int tmp; if (s->ensure_level > s->loop->ensure_level) { genop_1(s, OP_EPOP, s->ensure_level - s->loop->ensure_level); } if (tree) { gen_move(s, loop->acc, cursp(), 0); } tmp = genjmp(s, OP_JMP, loop->pc3); loop->pc3 = tmp; } else { if (!tree) { genop_1(s, OP_LOADNIL, cursp()); } gen_return(s, OP_BREAK, cursp()); } } } static void loop_pop(codegen_scope *s, int val) { if (val) { genop_1(s, OP_LOADNIL, cursp()); } dispatch_linked(s, s->loop->pc3); s->loop = s->loop->prev; if (val) push(); } static struct RProc* generate_code(mrb_state *mrb, parser_state *p, int val) { codegen_scope *scope = scope_new(mrb, 0, 0); struct RProc *proc; struct mrb_jmpbuf *prev_jmp = mrb->jmp; 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) { mrb->jmp = &scope->jmp; /* prepare irep */ codegen(scope, p->tree, val); proc = mrb_proc_new(mrb, scope->irep); mrb_irep_decref(mrb, scope->irep); mrb_pool_close(scope->mpool); proc->c = NULL; if (mrb->c->cibase && mrb->c->cibase->proc == proc->upper) { proc->upper = NULL; } mrb->jmp = prev_jmp; return proc; } MRB_CATCH(&scope->jmp) { mrb_irep_decref(mrb, scope->irep); mrb_pool_close(scope->mpool); mrb->jmp = prev_jmp; return NULL; } MRB_END_EXC(&scope->jmp); } MRB_API struct RProc* mrb_generate_code(mrb_state *mrb, parser_state *p) { return generate_code(mrb, p, VAL); } void mrb_irep_remove_lv(mrb_state *mrb, mrb_irep *irep) { int i; if (irep->lv) { mrb_free(mrb, irep->lv); irep->lv = NULL; } for (i = 0; i < irep->rlen; ++i) { mrb_irep_remove_lv(mrb, irep->reps[i]); } } #undef OPCODE #define Z 1 #define S 3 #define W 4 /* instruction sizes */ uint8_t mrb_insn_size[] = { #define B 2 #define BB 3 #define BBB 4 #define BS 4 #define SB 4 #define OPCODE(_,x) x, #include "mruby/ops.h" #undef OPCODE #undef B #undef BB #undef BS #undef SB #undef BBB }; /* EXT1 instruction sizes */ uint8_t mrb_insn_size1[] = { #define B 3 #define BB 4 #define BBB 5 #define BS 5 #define SB 5 #define OPCODE(_,x) x, #include "mruby/ops.h" #undef OPCODE #undef B }; /* EXT2 instruction sizes */ uint8_t mrb_insn_size2[] = { #define B 2 #define OPCODE(_,x) x, #include "mruby/ops.h" #undef OPCODE #undef BB #undef BBB #undef BS #undef SB }; /* EXT3 instruction sizes */ #define BB 5 #define BBB 6 #define BS 4 #define SB 5 uint8_t mrb_insn_size3[] = { #define OPCODE(_,x) x, #include "mruby/ops.h" }; mruby-2.0.0/mrbgems/mruby-compiler/core/keywords000066400000000000000000000057141340361412400220000ustar00rootroot00000000000000%{ 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 %% mruby-2.0.0/mrbgems/mruby-compiler/core/lex.def000066400000000000000000000252771340361412400214640ustar00rootroot00000000000000/* ANSI-C code produced by gperf version 3.0.4 */ /* Command-line: gperf -L ANSI-C -C -p -j1 -i 1 -g -o -t -N mrb_reserved_word -k'1,3,$' /home/matz/work/mruby/mrbgems/mruby-compiler/core/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 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/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 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/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 (const char *str, 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 }; 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 #if defined __GNUC_STDC_INLINE__ || defined __GNUC_GNU_INLINE__ __attribute__ ((__gnu_inline__)) #endif #endif const struct kwtable * mrb_reserved_word (const char *str, unsigned int len) { static const struct kwtable wordlist[] = { {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, #line 18 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" {"break", {keyword_break, keyword_break}, EXPR_MID}, #line 23 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" {"else", {keyword_else, keyword_else}, EXPR_BEG}, #line 33 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" {"nil", {keyword_nil, keyword_nil}, EXPR_END}, #line 26 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" {"ensure", {keyword_ensure, keyword_ensure}, EXPR_BEG}, #line 25 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" {"end", {keyword_end, keyword_end}, EXPR_END}, #line 42 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" {"then", {keyword_then, keyword_then}, EXPR_BEG}, #line 34 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" {"not", {keyword_not, keyword_not}, EXPR_ARG}, #line 27 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" {"false", {keyword_false, keyword_false}, EXPR_END}, #line 40 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" {"self", {keyword_self, keyword_self}, EXPR_END}, #line 24 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" {"elsif", {keyword_elsif, keyword_elsif}, EXPR_VALUE}, #line 37 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" {"rescue", {keyword_rescue, modifier_rescue}, EXPR_MID}, #line 43 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" {"true", {keyword_true, keyword_true}, EXPR_END}, #line 46 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" {"until", {keyword_until, modifier_until}, EXPR_VALUE}, #line 45 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" {"unless", {keyword_unless, modifier_unless}, EXPR_VALUE}, #line 39 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" {"return", {keyword_return, keyword_return}, EXPR_MID}, #line 21 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" {"def", {keyword_def, keyword_def}, EXPR_FNAME}, #line 16 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" {"and", {keyword_and, keyword_and}, EXPR_VALUE}, #line 22 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" {"do", {keyword_do, keyword_do}, EXPR_BEG}, #line 49 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" {"yield", {keyword_yield, keyword_yield}, EXPR_ARG}, #line 28 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" {"for", {keyword_for, keyword_for}, EXPR_VALUE}, #line 44 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" {"undef", {keyword_undef, keyword_undef}, EXPR_FNAME}, #line 35 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" {"or", {keyword_or, keyword_or}, EXPR_VALUE}, #line 30 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" {"in", {keyword_in, keyword_in}, EXPR_VALUE}, #line 47 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" {"when", {keyword_when, keyword_when}, EXPR_VALUE}, #line 38 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" {"retry", {keyword_retry, keyword_retry}, EXPR_END}, #line 29 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" {"if", {keyword_if, modifier_if}, EXPR_VALUE}, #line 19 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" {"case", {keyword_case, keyword_case}, EXPR_VALUE}, #line 36 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" {"redo", {keyword_redo, keyword_redo}, EXPR_END}, #line 32 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" {"next", {keyword_next, keyword_next}, EXPR_MID}, #line 41 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" {"super", {keyword_super, keyword_super}, EXPR_ARG}, #line 31 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" {"module", {keyword_module, keyword_module}, EXPR_VALUE}, #line 17 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" {"begin", {keyword_begin, keyword_begin}, EXPR_BEG}, #line 12 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" {"__LINE__", {keyword__LINE__, keyword__LINE__}, EXPR_END}, #line 11 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" {"__FILE__", {keyword__FILE__, keyword__FILE__}, EXPR_END}, #line 10 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" {"__ENCODING__", {keyword__ENCODING__, keyword__ENCODING__}, EXPR_END}, #line 14 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" {"END", {keyword_END, keyword_END}, EXPR_END}, #line 15 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" {"alias", {keyword_alias, keyword_alias}, EXPR_FNAME}, #line 13 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" {"BEGIN", {keyword_BEGIN, keyword_BEGIN}, EXPR_END}, {""}, #line 20 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" {"class", {keyword_class, keyword_class}, EXPR_CLASS}, {""}, {""}, #line 48 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" {"while", {keyword_while, modifier_while}, EXPR_VALUE} }; if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) { int key = hash (str, len); if (key <= MAX_HASH_VALUE && key >= 0) { const char *s = wordlist[key].name; if (*str == *s && !strcmp (str + 1, s + 1)) return &wordlist[key]; } } return 0; } #line 50 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords" mruby-2.0.0/mrbgems/mruby-compiler/core/node.h000066400000000000000000000026711340361412400213030ustar00rootroot00000000000000/* ** node.h - nodes of abstract syntax tree ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_COMPILER_NODE_H #define MRUBY_COMPILER_NODE_H enum node_type { NODE_METHOD, NODE_SCOPE, NODE_BLOCK, NODE_IF, NODE_CASE, NODE_WHEN, 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_SCALL, NODE_FCALL, NODE_SUPER, NODE_ZSUPER, NODE_ARRAY, NODE_ZARRAY, NODE_HASH, NODE_KW_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_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_ARG, NODE_ARGS_TAIL, NODE_KW_ARG, NODE_KW_REST_ARGS, 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_DOT2, NODE_DOT3, NODE_SELF, NODE_NIL, NODE_TRUE, NODE_FALSE, NODE_DEFINED, NODE_POSTEXE, NODE_DSYM, NODE_HEREDOC, NODE_LITERAL_DELIM, NODE_WORDS, NODE_SYMBOLS, NODE_LAST }; #endif /* MRUBY_COMPILER_NODE_H */ mruby-2.0.0/mrbgems/mruby-compiler/core/parse.y000066400000000000000000005211231340361412400215070ustar00rootroot00000000000000/* ** 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 #include #include #include #include #include "node.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 void_expr_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)) #define nint(x) ((node*)(intptr_t)(x)) #define intn(x) ((int)(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; /* beginning of next partial file; need to point the previous file */ if (p->lineno == 0 && p->current_filename_index > 0) { c->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); } static node* new_mod_rescue(parser_state *p, node *body, node *resq) { return new_rescue(p, body, list1(list3(0, 0, resq)), 0); } /* (: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) { void_expr_error(p, a); 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) { void_expr_error(p, a); return list4((node*)NODE_IF, a, c, b); } /* (:while cond body) */ static node* new_while(parser_state *p, node *a, node *b) { void_expr_error(p, a); return cons((node*)NODE_WHILE, cons(a, b)); } /* (:until cond body) */ static node* new_until(parser_state *p, node *a, node *b) { void_expr_error(p, a); 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) { void_expr_error(p, o); 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; void_expr_error(p, a); 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, int pass) { node *n = list4(nint(pass?NODE_CALL:NODE_SCALL), a, nsym(b), c); void_expr_error(p, a); 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) { void_expr_error(p, b); 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); } /* (:kw_hash (k . v) (k . v)...) */ static node* new_kw_hash(parser_state *p, node *a) { return cons((node*)NODE_KW_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) { void_expr_error(p, s); 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) { void_expr_error(p, o); 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) { void_expr_error(p, o); 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)); } static void local_add_margs(parser_state *p, node *n) { while (n) { if (n->car->car == (node*)NODE_MASGN) { node *t = n->car->cdr->cdr; n->car->cdr->cdr = NULL; while (t) { local_add_f(p, sym(t->car)); t = t->cdr; } local_add_margs(p, n->car->cdr->car->car); local_add_margs(p, n->car->cdr->car->cdr->cdr->car); } n = n->cdr; } } /* (m o r m2 tail) */ /* 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, node *tail) { node *n; local_add_margs(p, m); local_add_margs(p, m2); n = cons(m2, tail); n = cons(nsym(rest), n); n = cons(opt, n); return cons(m, n); } /* (:args_tail keywords rest_keywords_sym block_sym) */ static node* new_args_tail(parser_state *p, node *kws, node *kwrest, mrb_sym blk) { node *k; /* allocate register for keywords hash */ if (kws || kwrest) { local_add_f(p, (kwrest && kwrest->cdr)? sym(kwrest->cdr) : mrb_intern_lit(p->mrb, "**")); } /* allocate register for block */ local_add_f(p, blk? blk : mrb_intern_lit(p->mrb, "&")); // allocate register for keywords arguments // order is for Proc#parameters for (k = kws; k; k = k->cdr) { if (!k->car->cdr->cdr->car) { // allocate required keywords local_add_f(p, sym(k->car->cdr->car)); } } for (k = kws; k; k = k->cdr) { if (k->car->cdr->cdr->car) { // allocate keywords with default local_add_f(p, sym(k->car->cdr->car)); } } return list4((node*)NODE_ARGS_TAIL, kws, kwrest, nsym(blk)); } /* (:kw_arg kw_sym def_arg) */ static node* new_kw_arg(parser_state *p, mrb_sym kw, node *def_arg) { mrb_assert(kw); return list3((node*)NODE_KW_ARG, nsym(kw), def_arg); } /* (: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) { void_expr_error(p, 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) { void_expr_error(p, 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) { void_expr_error(p, 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), nint(base)); } #ifndef MRB_WITHOUT_FLOAT /* (:float . i) */ static node* new_float(parser_state *p, const char *s) { return cons((node*)NODE_FLOAT, (node*)strdup(s)); } #endif /* (:str . (s . len)) */ static node* new_str(parser_state *p, const char *s, size_t len) { return cons((node*)NODE_STR, cons((node*)strndup(s, len), nint(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), nint(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, a); } /* (:regx . (s . (opt . enc))) */ static node* new_regx(parser_state *p, const char *p1, const char* p2, const char* p3) { return cons((node*)NODE_REGX, cons((node*)p1, cons((node*)p2, (node*)p3))); } /* (:dregx . (a . b)) */ 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, nint(n)); } /* (:nthref . n) */ static node* new_nth_ref(parser_state *p, int n) { return cons((node*)NODE_NTH_REF, nint(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) { void_expr_error(p, recv); return new_call(p, recv, intern_cstr(m), 0, 1); } /* (: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)), 1); } 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; switch ((enum node_type)intn(a->car)) { case NODE_SUPER: case NODE_ZSUPER: if (!a->cdr) a->cdr = cons(0, b); else { args_with_block(p, a->cdr, b); } break; case NODE_CALL: case NODE_FCALL: case NODE_SCALL: n = a->cdr->cdr->cdr; if (!n->car) n->car = cons(0, b); else { args_with_block(p, n->car, b); } break; default: break; } } 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 (intn(lhs->car) == NODE_LVAR) { local_add(p, sym(lhs->cdr)); } } static node* var_reference(parser_state *p, node *lhs) { node *n; if (intn(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(nint(type), cons((node*)0, cons(nint(paren), nint(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; } else { /* next heredoc */ p->lex_strterm->car = nint(parsing_heredoc_inf(p)->type); } } #define is_strterm_type(p,str_func) (intn((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_TAG %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_rhs 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 command_rhs 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_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 %type call_op call_op2 /* 0:'&.', 1:'.', 2:'::' */ %type args_tail opt_args_tail f_kwarg f_kw f_kwrest %type f_block_kwarg f_block_kw block_args_tail opt_block_args_tail %type f_label %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 tDSTAR /* ** */ %token tAMPER /* & */ %token tLAMBDA /* -> */ %token tANDDOT /* &. */ %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 '?' ':' tLABEL_TAG %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_mod_rescue(p, $1, $3); } | keyword_END '{' compstmt '}' { yyerror(p, "END not supported"); $$ = new_postexe(p, $3); } | command_asgn | mlhs '=' command_call { $$ = new_masgn(p, $1, $3); } | lhs '=' mrhs { $$ = new_asgn(p, $1, new_array(p, $3)); } | mlhs '=' arg { $$ = new_masgn(p, $1, $3); } | mlhs '=' mrhs { $$ = new_masgn(p, $1, new_array(p, $3)); } | expr ; command_asgn : lhs '=' command_rhs { $$ = new_asgn(p, $1, $3); } | var_lhs tOP_ASGN command_rhs { $$ = new_op_asgn(p, $1, $2, $3); } | primary_value '[' opt_call_args rbracket tOP_ASGN command_rhs { $$ = new_op_asgn(p, new_call(p, $1, intern("[]",2), $3, '.'), $5, $6); } | primary_value call_op tIDENTIFIER tOP_ASGN command_rhs { $$ = new_op_asgn(p, new_call(p, $1, $3, 0, $2), $4, $5); } | primary_value call_op tCONSTANT tOP_ASGN command_rhs { $$ = new_op_asgn(p, new_call(p, $1, $3, 0, $2), $4, $5); } | primary_value tCOLON2 tCONSTANT tOP_ASGN command_call { yyerror(p, "constant re-assignment"); $$ = 0; } | primary_value tCOLON2 tIDENTIFIER tOP_ASGN command_rhs { $$ = new_op_asgn(p, new_call(p, $1, $3, 0, tCOLON2), $4, $5); } | backref tOP_ASGN command_rhs { backref_error(p, $1); $$ = new_begin(p, 0); } ; command_rhs : command_call %prec tOP_ASGN | command_call modifier_rescue stmt { $$ = new_mod_rescue(p, $1, $3); } | command_asgn ; 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 call_op2 operation2 command_args { $$ = new_call(p, $1, $3, $4, $2); } ; 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 call_op operation2 command_args %prec tLOWEST { $$ = new_call(p, $1, $3, $4, $2); } | primary_value call_op operation2 command_args cmd_brace_block { args_with_block(p, $4, $5); $$ = new_call(p, $1, $3, $4, $2); } | primary_value tCOLON2 operation2 command_args %prec tLOWEST { $$ = new_call(p, $1, $3, $4, tCOLON2); } | primary_value tCOLON2 operation2 command_args cmd_brace_block { args_with_block(p, $4, $5); $$ = new_call(p, $1, $3, $4, tCOLON2); } | 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 call_op tIDENTIFIER { $$ = new_call(p, $1, $3, 0, $2); } | primary_value tCOLON2 tIDENTIFIER { $$ = new_call(p, $1, $3, 0, tCOLON2); } | primary_value call_op tCONSTANT { $$ = new_call(p, $1, $3, 0, $2); } | 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 call_op tIDENTIFIER { $$ = new_call(p, $1, $3, 0, $2); } | primary_value tCOLON2 tIDENTIFIER { $$ = new_call(p, $1, $3, 0, tCOLON2); } | primary_value call_op tCONSTANT { $$ = new_call(p, $1, $3, 0, $2); } | 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 { void_expr_error(p, $1); $$ = 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); } | tDSTAR { $$ = 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_rhs { $$ = new_asgn(p, $1, $3); } | var_lhs tOP_ASGN arg_rhs { $$ = new_op_asgn(p, $1, $2, $3); } | primary_value '[' opt_call_args rbracket tOP_ASGN arg_rhs { $$ = new_op_asgn(p, new_call(p, $1, intern("[]",2), $3, '.'), $5, $6); } | primary_value call_op tIDENTIFIER tOP_ASGN arg_rhs { $$ = new_op_asgn(p, new_call(p, $1, $3, 0, $2), $4, $5); } | primary_value call_op tCONSTANT tOP_ASGN arg_rhs { $$ = new_op_asgn(p, new_call(p, $1, $3, 0, $2), $4, $5); } | primary_value tCOLON2 tIDENTIFIER tOP_ASGN arg_rhs { $$ = new_op_asgn(p, new_call(p, $1, $3, 0, tCOLON2), $4, $5); } | primary_value tCOLON2 tCONSTANT tOP_ASGN arg_rhs { yyerror(p, "constant re-assignment"); $$ = new_begin(p, 0); } | tCOLON3 tCONSTANT tOP_ASGN arg_rhs { yyerror(p, "constant re-assignment"); $$ = new_begin(p, 0); } | backref tOP_ASGN arg_rhs { 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); } | arg '?' arg opt_nl tLABEL_TAG arg { $$ = new_if(p, cond($1), $3, $6); } | primary { $$ = $1; } ; aref_args : none | args trailer { $$ = $1; NODE_LINENO($$, $1); } | args comma assocs trailer { $$ = push($1, new_kw_hash(p, $3)); } | assocs trailer { $$ = cons(new_kw_hash(p, $1), 0); NODE_LINENO($$, $1); } ; arg_rhs : arg %prec tOP_ASGN { $$ = $1; } | arg modifier_rescue arg { void_expr_error(p, $1); void_expr_error(p, $3); $$ = new_mod_rescue(p, $1, $3); } ; 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 comma assocs ',' { $$ = cons(push($1, new_kw_hash(p, $3)), 0); NODE_LINENO($$, $1); } | assocs ',' { $$ = cons(list1(new_kw_hash(p, $1)), 0); NODE_LINENO($$, $1); } ; call_args : command { void_expr_error(p, $1); $$ = cons(list1($1), 0); NODE_LINENO($$, $1); } | args opt_block_arg { $$ = cons($1, $2); NODE_LINENO($$, $1); } | assocs opt_block_arg { $$ = cons(list1(new_kw_hash(p, $1)), $2); NODE_LINENO($$, $1); } | args comma assocs opt_block_arg { $$ = cons(push($1, new_kw_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 { $$ = new_block_arg(p, $2); } ; opt_block_arg : comma block_arg { $$ = $2; } | none { $$ = 0; } ; comma : ',' | ',' heredoc_bodies ; args : arg { void_expr_error(p, $1); $$ = cons($1, 0); NODE_LINENO($$, $1); } | tSTAR arg { void_expr_error(p, $2); $$ = cons(new_splat(p, $2), 0); NODE_LINENO($$, $2); } | args comma arg { void_expr_error(p, $3); $$ = push($1, $3); } | args comma tSTAR arg { void_expr_error(p, $4); $$ = push($1, new_splat(p, $4)); } ; mrhs : args comma arg { void_expr_error(p, $3); $$ = push($1, $3); } | args comma tSTAR arg { void_expr_error(p, $4); $$ = push($1, new_splat(p, $4)); } | tSTAR arg { void_expr_error(p, $2); $$ = 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; } stmt {p->lstate = EXPR_ENDARG;} rparen { p->cmdarg_stack = $2; $$ = $3; } | tLPAREN_ARG {p->lstate = EXPR_ENDARG;} rparen { $$ = new_nil(p); } | 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 opt_paren_args { $$ = new_yield(p, $2); } | 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; CMDARG_LEXPOP(); } | 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), nint(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 = intn($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_margs : f_arg { $$ = list3($1,0,0); } | f_arg ',' tSTAR f_norm_arg { $$ = list3($1, new_arg(p, $4), 0); } | f_arg ',' tSTAR f_norm_arg ',' f_arg { $$ = list3($1, new_arg(p, $4), $6); } | f_arg ',' tSTAR { local_add_f(p, 0); $$ = list3($1, (node*)-1, 0); } | f_arg ',' tSTAR ',' f_arg { $$ = list3($1, (node*)-1, $5); } | tSTAR f_norm_arg { $$ = list3(0, new_arg(p, $2), 0); } | tSTAR f_norm_arg ',' f_arg { $$ = list3(0, new_arg(p, $2), $4); } | tSTAR { local_add_f(p, 0); $$ = list3(0, (node*)-1, 0); } | tSTAR ',' { local_add_f(p, 0); } f_arg { $$ = list3(0, (node*)-1, $4); } ; block_args_tail : f_block_kwarg ',' f_kwrest opt_f_block_arg { $$ = new_args_tail(p, $1, $3, $4); } | f_block_kwarg opt_f_block_arg { $$ = new_args_tail(p, $1, 0, $2); } | f_kwrest opt_f_block_arg { $$ = new_args_tail(p, 0, $1, $2); } | f_block_arg { $$ = new_args_tail(p, 0, 0, $1); } ; opt_block_args_tail : ',' block_args_tail { $$ = $2; } | /* none */ { $$ = new_args_tail(p, 0, 0, 0); } ; block_param : f_arg ',' f_block_optarg ',' f_rest_arg opt_block_args_tail { $$ = new_args(p, $1, $3, $5, 0, $6); } | f_arg ',' f_block_optarg ',' f_rest_arg ',' f_arg opt_block_args_tail { $$ = new_args(p, $1, $3, $5, $7, $8); } | f_arg ',' f_block_optarg opt_block_args_tail { $$ = new_args(p, $1, $3, 0, 0, $4); } | f_arg ',' f_block_optarg ',' f_arg opt_block_args_tail { $$ = new_args(p, $1, $3, 0, $5, $6); } | f_arg ',' f_rest_arg opt_block_args_tail { $$ = new_args(p, $1, 0, $3, 0, $4); } | f_arg ',' { $$ = new_args(p, $1, 0, 0, 0, 0); } | f_arg ',' f_rest_arg ',' f_arg opt_block_args_tail { $$ = new_args(p, $1, 0, $3, $5, $6); } | f_arg opt_block_args_tail { $$ = new_args(p, $1, 0, 0, 0, $2); } | f_block_optarg ',' f_rest_arg opt_block_args_tail { $$ = new_args(p, 0, $1, $3, 0, $4); } | f_block_optarg ',' f_rest_arg ',' f_arg opt_block_args_tail { $$ = new_args(p, 0, $1, $3, $5, $6); } | f_block_optarg opt_block_args_tail { $$ = new_args(p, 0, $1, 0, 0, $2); } | f_block_optarg ',' f_arg opt_block_args_tail { $$ = new_args(p, 0, $1, 0, $3, $4); } | f_rest_arg opt_block_args_tail { $$ = new_args(p, 0, 0, $1, 0, $2); } | f_rest_arg ',' f_arg opt_block_args_tail { $$ = new_args(p, 0, 0, $1, $3, $4); } | block_args_tail { $$ = 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 bodystmt keyword_end { $$ = $2; } ; do_block : keyword_do_block { local_nest(p); } opt_block_param bodystmt 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 call_op2 operation2 opt_paren_args { $$ = new_call(p, $1, $3, $4, $2); } | block_call call_op2 operation2 opt_paren_args brace_block { $$ = new_call(p, $1, $3, $4, $2); call_with_block(p, $$, $5); } | block_call call_op2 operation2 command_args do_block { $$ = new_call(p, $1, $3, $4, $2); call_with_block(p, $$, $5); } ; method_call : operation paren_args { $$ = new_fcall(p, $1, $2); } | primary_value call_op operation2 opt_paren_args { $$ = new_call(p, $1, $3, $4, $2); } | primary_value tCOLON2 operation2 paren_args { $$ = new_call(p, $1, $3, $4, tCOLON2); } | primary_value tCOLON2 operation3 { $$ = new_call(p, $1, $3, 0, tCOLON2); } | primary_value call_op paren_args { $$ = new_call(p, $1, intern("call",4), $3, $2); } | primary_value tCOLON2 paren_args { $$ = new_call(p, $1, intern("call",4), $3, tCOLON2); } | 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 bodystmt 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 { $$ = 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 ; 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 { p->lstate = EXPR_ENDARG; $$ = new_sym(p, $1); } | tSYMBEG tSTRING_BEG string_rep tSTRING { p->lstate = EXPR_ENDARG; $$ = new_dsym(p, new_dstr(p, push($3, $4))); } ; basic_symbol : tSYMBEG sym { $$ = $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__ { const char *fn = p->filename; if (!fn) { fn = "(null)"; } $$ = new_str(p, fn, strlen(fn)); } | keyword__LINE__ { char buf[16]; snprintf(buf, sizeof(buf), "%d", p->lineno); $$ = new_int(p, buf, 10); } | keyword__ENCODING__ { #ifdef MRB_UTF8_STRING const char *enc = "UTF-8"; #else const char *enc = "ASCII-8BIT"; #endif $$ = new_str(p, enc, strlen(enc)); } ; 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_label : tIDENTIFIER tLABEL_TAG ; f_kw : f_label arg { void_expr_error(p, $2); $$ = new_kw_arg(p, $1, $2); } | f_label { $$ = new_kw_arg(p, $1, 0); } ; f_block_kw : f_label primary_value { $$ = new_kw_arg(p, $1, $2); } | f_label { $$ = new_kw_arg(p, $1, 0); } ; f_block_kwarg : f_block_kw { $$ = list1($1); } | f_block_kwarg ',' f_block_kw { $$ = push($1, $3); } ; f_kwarg : f_kw { $$ = list1($1); } | f_kwarg ',' f_kw { $$ = push($1, $3); } ; kwrest_mark : tPOW | tDSTAR ; f_kwrest : kwrest_mark tIDENTIFIER { $$ = cons((node*)NODE_KW_REST_ARGS, nsym($2)); } | kwrest_mark { $$ = cons((node*)NODE_KW_REST_ARGS, 0); } ; args_tail : f_kwarg ',' f_kwrest opt_f_block_arg { $$ = new_args_tail(p, $1, $3, $4); } | f_kwarg opt_f_block_arg { $$ = new_args_tail(p, $1, 0, $2); } | f_kwrest opt_f_block_arg { $$ = new_args_tail(p, 0, $1, $2); } | f_block_arg { $$ = new_args_tail(p, 0, 0, $1); } ; opt_args_tail : ',' args_tail { $$ = $2; } | /* none */ { $$ = new_args_tail(p, 0, 0, 0); } ; f_args : f_arg ',' f_optarg ',' f_rest_arg opt_args_tail { $$ = new_args(p, $1, $3, $5, 0, $6); } | f_arg ',' f_optarg ',' f_rest_arg ',' f_arg opt_args_tail { $$ = new_args(p, $1, $3, $5, $7, $8); } | f_arg ',' f_optarg opt_args_tail { $$ = new_args(p, $1, $3, 0, 0, $4); } | f_arg ',' f_optarg ',' f_arg opt_args_tail { $$ = new_args(p, $1, $3, 0, $5, $6); } | f_arg ',' f_rest_arg opt_args_tail { $$ = new_args(p, $1, 0, $3, 0, $4); } | f_arg ',' f_rest_arg ',' f_arg opt_args_tail { $$ = new_args(p, $1, 0, $3, $5, $6); } | f_arg opt_args_tail { $$ = new_args(p, $1, 0, 0, 0, $2); } | f_optarg ',' f_rest_arg opt_args_tail { $$ = new_args(p, 0, $1, $3, 0, $4); } | f_optarg ',' f_rest_arg ',' f_arg opt_args_tail { $$ = new_args(p, 0, $1, $3, $5, $6); } | f_optarg opt_args_tail { $$ = new_args(p, 0, $1, 0, 0, $2); } | f_optarg ',' f_arg opt_args_tail { $$ = new_args(p, 0, $1, 0, $3, $4); } | f_rest_arg opt_args_tail { $$ = new_args(p, 0, 0, $1, 0, $2); } | f_rest_arg ',' f_arg opt_args_tail { $$ = new_args(p, 0, 0, $1, $3, $4); } | args_tail { $$ = new_args(p, 0, 0, 0, 0, $1); } | /* none */ { local_add_f(p, mrb_intern_lit(p->mrb, "&")); $$ = 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 { $$ = local_switch(p); } f_margs rparen { $$ = new_masgn(p, $3, p->locals->car); local_resume(p, $2); local_add_f(p, 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 { void_expr_error(p, $2); $$ = cons(nsym($1), $2); } ; f_block_opt : f_opt_asgn primary_value { void_expr_error(p, $2); $$ = 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, mrb_intern_lit(p->mrb, "*")); $$ = -1; } ; blkarg_mark : '&' | tAMPER ; f_block_arg : blkarg_mark tIDENTIFIER { $$ = $2; } ; opt_f_block_arg : ',' f_block_arg { $$ = $2; } | none { $$ = 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)intn($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 tASSOC arg { void_expr_error(p, $1); void_expr_error(p, $3); $$ = cons($1, $3); } | tIDENTIFIER tLABEL_TAG arg { void_expr_error(p, $3); $$ = cons(new_sym(p, $1), $3); } | string tLABEL_TAG arg { void_expr_error(p, $3); if ($1->car == (node*)NODE_DSTR) { $$ = cons(new_dsym(p, $1), $3); } else { $$ = cons(new_sym(p, new_strsym(p, $1)), $3); } } | tDSTAR arg { void_expr_error(p, $2); $$ = cons(cons((node*)NODE_KW_REST_ARGS, 0), $2); } ; operation : tIDENTIFIER | tCONSTANT | tFID ; operation2 : tIDENTIFIER | tCONSTANT | tFID | op ; operation3 : tIDENTIFIER | tFID | op ; dot_or_colon : '.' | tCOLON2 ; call_op : '.' { $$ = '.'; } | tANDDOT { $$ = 0; } ; call_op2 : call_op | tCOLON2 { $$ = tCOLON2; } ; opt_terms : /* none */ | terms ; opt_nl : /* none */ | nl ; rparen : opt_nl ')' ; rbracket : opt_nl ']' ; trailer : /* none */ | nl | comma ; term : ';' {yyerrok;} | nl | heredoc_body ; nl : '\n' { p->lineno++; p->column = 0; } ; terms : term | terms term ; none : /* none */ { $$ = 0; } ; %% #define pylval (*((YYSTYPE*)(p->ylval))) static void yyerror(parser_state *p, const char *s) { char* c; size_t n; if (! p->capture_errors) { #ifndef MRB_DISABLE_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; size_t n; if (! p->capture_errors) { #ifndef MRB_DISABLE_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 = intn(n->car); if (c == NODE_NTH_REF) { yyerror_i(p, "can't set variable $%" MRB_PRId, intn(n->cdr)); } else if (c == NODE_BACK_REF) { yyerror_i(p, "can't set variable $%c", intn(n->cdr)); } else { mrb_bug(p->mrb, "Internal error in backref_error() : n=>car == %S", mrb_fixnum_value(c)); } } static void void_expr_error(parser_state *p, node *n) { int c; if (n == NULL) return; c = intn(n->car); switch (c) { case NODE_BREAK: case NODE_RETURN: case NODE_NEXT: case NODE_REDO: case NODE_RETRY: yyerror(p, "void value expression"); break; case NODE_AND: case NODE_OR: void_expr_error(p, n->cdr->car); void_expr_error(p, n->cdr->cdr); break; case NODE_BEGIN: if (n->cdr) { while (n->cdr) { n = n->cdr; } void_expr_error(p, n->car); } break; default: break; } } 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 = intn(p->pb->car); tmp = p->pb; p->pb = p->pb->cdr; cons_free(tmp); } else { #ifndef MRB_DISABLE_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++; } 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(nint(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 */ if (c0 >= 0) --p->column; list = push(list, nint(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) { size_t len = strlen(s); #ifndef MRB_DISABLE_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 FALSE; if (c == '\n') { p->lineno++; p->column = 0; } if (c == *s) break; } s++; if (peeks(p, s)) { size_t 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) { if (p->tokbuf != p->buf) { mrb_free(p->mrb, p->tokbuf); p->tokbuf = p->buf; p->tsiz = MRB_PARSER_TOKBUF_SIZE; } p->tidx = 0; return p->column - 1; } static void tokadd(parser_state *p, int32_t c) { char utf8[4]; int i, 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->tidx+len >= p->tsiz) { if (p->tsiz >= MRB_PARSER_TOKBUF_MAX) { p->tidx += len; return; } p->tsiz *= 2; if (p->tokbuf == p->buf) { p->tokbuf = (char*)mrb_malloc(p->mrb, p->tsiz); memcpy(p->tokbuf, p->buf, MRB_PARSER_TOKBUF_SIZE); } else { p->tokbuf = (char*)mrb_realloc(p->mrb, p->tokbuf, p->tsiz); } } for (i = 0; i < len; i++) { p->tokbuf[p->tidx++] = utf8[i]; } } static int toklast(parser_state *p) { return p->tokbuf[p->tidx-1]; } static void tokfix(parser_state *p) { if (p->tidx >= MRB_PARSER_TOKBUF_MAX) { p->tidx = MRB_PARSER_TOKBUF_MAX-1; yyerror(p, "string too long (truncated)"); } p->tokbuf[p->tidx] = '\0'; } static const char* tok(parser_state *p) { return p->tokbuf; } static int toklen(parser_state *p) { return p->tidx; } #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 int32_t scan_oct(const int *start, int len, int *retlen) { const int *s = start; int32_t retval = 0; /* mrb_assert(len <= 3) */ while (len-- && *s >= '0' && *s <= '7') { retval <<= 3; retval |= *s++ - '0'; } *retlen = (int)(s - start); return retval; } static int32_t scan_hex(parser_state *p, const int *start, int len, int *retlen) { static const char hexdigit[] = "0123456789abcdef0123456789ABCDEF"; const int *s = start; uint32_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 = (int)(s - start); return (int32_t)retval; } static int32_t read_escape_unicode(parser_state *p, int limit) { int buf[9]; int i; int32_t hex; /* Look for opening brace */ i = 0; buf[0] = nextc(p); if (buf[0] < 0) { eof: yyerror(p, "invalid escape character syntax"); return -1; } if (ISXDIGIT(buf[0])) { /* \uxxxx form */ for (i=1; i 0x10FFFF || (hex & 0xFFFFF800) == 0xD800) { yyerror(p, "invalid Unicode code point"); return -1; } return hex; } /* 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; } } if (i == 0) { yyerror(p, "invalid hex escape"); return -1; } return scan_hex(p, buf, i, &i); } case 'u': /* Unicode */ if (peek(p, '{')) { /* \u{xxxxxxxx} form */ nextc(p); c = read_escape_unicode(p, 8); if (c < 0) return 0; if (nextc(p) != '}') goto eof; } else { c = read_escape_unicode(p, 4); if (c < 0) 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 = intn(p->lex_strterm->cdr->car); int beg = intn(p->lex_strterm->cdr->cdr->car); int end = intn(p->lex_strterm->cdr->cdr->cdr); parser_heredoc_info *hinf = (type & STR_FUNC_HEREDOC) ? parsing_heredoc_inf(p) : NULL; if (beg == 0) beg = -3; /* should never happen */ if (end == 0) end = -3; 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)) { if (c < 0) { p->parsing_heredoc = NULL; } else { 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; } pylval.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 = nint(nest_level); } else if (c == end) { nest_level--; p->lex_strterm->cdr->car = nint(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 if (c == 'u' && peek(p, '{')) { /* \u{xxxx xxxx xxxx} form */ nextc(p); while (1) { do c = nextc(p); while (ISSPACE(c)); if (c == '}') break; pushback(p, c); c = read_escape_unicode(p, 8); if (c < 0) break; tokadd(p, -c); } if (hinf) hinf->line_head = FALSE; } 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; pylval.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); pylval.nd = new_str(p, tok(p), toklen(p)); return tSTRING_MID; } } if (c == '\n') { p->lineno++; p->column = 0; } tokadd(p, c); } tokfix(p); p->lstate = EXPR_ENDARG; end_strterm(p); if (type & STR_FUNC_XQUOTE) { pylval.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 enc = '\0'; char *encp; 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; case 'u': f |= 16; break; case 'n': f |= 32; 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'; if (f & 16) enc = 'u'; if (f & 32) enc = 'n'; } if (flag > flags) { dup = strndup(flags, (size_t)(flag - flags)); } else { dup = NULL; } if (enc) { encp = strndup(&enc, 1); } else { encp = NULL; } pylval.nd = new_regx(p, s, dup, encp); return tREGEXP; } pylval.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; pylval.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)) == '=') { pylval.id = intern("**",2); p->lstate = EXPR_BEG; return tOP_ASGN; } pushback(p, c); if (IS_SPCARG(c)) { yywarning(p, "`**' interpreted as argument prefix"); c = tDSTAR; } else if (IS_BEG()) { c = tDSTAR; } else { c = tPOW; /* "**", "argument prefix" */ } } else { if (c == '=') { pylval.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)) == '=') { pylval.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)) == '=') { pylval.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); pylval.nd = new_str(p, tok(p), toklen(p)); p->lstate = EXPR_ENDARG; return tCHAR; case '&': if ((c = nextc(p)) == '&') { p->lstate = EXPR_BEG; if ((c = nextc(p)) == '=') { pylval.id = intern("&&",2); p->lstate = EXPR_BEG; return tOP_ASGN; } pushback(p, c); return tANDOP; } else if (c == '.') { p->lstate = EXPR_DOT; return tANDDOT; } else if (c == '=') { pylval.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)) == '=') { pylval.id = intern("||",2); p->lstate = EXPR_BEG; return tOP_ASGN; } pushback(p, c); return tOROP; } if (c == '=') { pylval.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 == '=') { pylval.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 == '=') { pylval.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_ENDARG; 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; pylval.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; pylval.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; pylval.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; pylval.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); pylval.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) { #ifdef MRB_WITHOUT_FLOAT yywarning_s(p, "floating point numbers are not supported", tok(p)); pylval.nd = new_int(p, "0", 10); return tINTEGER; #else double d; char *endp; errno = 0; d = mrb_float_read(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; } pylval.nd = new_float(p, tok(p)); return tFLOAT; #endif } pylval.nd = new_int(p, tok(p), 10); return tINTEGER; } case ')': case ']': p->paren_nest--; /* fall through */ case '}': COND_LEXPOP(); CMDARG_LEXPOP(); if (c == ')') p->lstate = EXPR_ENDFN; else p->lstate = EXPR_END; 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 (!space_seen && IS_END()) { pushback(p, c); p->lstate = EXPR_BEG; return tLABEL_TAG; } if (!ISSPACE(c) || IS_BEG()) { pushback(p, c); p->lstate = EXPR_FNAME; return tSYMBEG; } pushback(p, c); p->lstate = EXPR_BEG; return ':'; case '/': if (IS_BEG()) { p->lex_strterm = new_strterm(p, str_regexp, '/', 0); return tREGEXP_BEG; } if ((c = nextc(p)) == '=') { pylval.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)) == '=') { pylval.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; } else if (p->lstate == EXPR_END && space_seen) { 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)) == '=') { pylval.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); pylval.id = intern_cstr(tok(p)); return tGVAR; case '-': tokadd(p, '$'); tokadd(p, c); c = nextc(p); pushback(p, c); gvar: tokfix(p); pylval.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; } pylval.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; } pylval.nd = new_nth_ref(p, (int)n); } return tNTH_REF; default: if (!identchar(c)) { pushback(p, c); return '$'; } /* fall through */ 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->tidx == 1) { yyerror(p, "incomplete instance variable syntax"); } else { yyerror(p, "incomplete class variable syntax"); } return 0; } else if (isdigit(c)) { if (p->tidx == 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_END; tokfix(p); pylval.id = intern_cstr(tok(p)); return tIDENTIFIER; } } 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; pylval.num = p->lineno; p->lstate = kw->state; if (state == EXPR_FNAME) { pylval.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)); pylval.id = ident; if (last_state != EXPR_DOT && islower(tok(p)[0]) && local_var_p(p, ident)) { p->lstate = EXPR_END; } } 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; p->on_eval = cxt->on_eval; 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 (intn(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 buf1; p->jmp = &buf1; MRB_TRY(p->jmp) { int n = 1; 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); if (p->mrb->jmp) { n = yyparse(p); } else { struct mrb_jmpbuf buf2; p->mrb->jmp = &buf2; MRB_TRY(p->mrb->jmp) { n = yyparse(p); } MRB_CATCH(p->mrb->jmp) { p->nerr++; } MRB_END_EXC(p->mrb->jmp); p->mrb->jmp = 0; } if (n != 0 || p->nerr > 0) { p->tree = 0; return; } 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; #ifndef MRB_DISABLE_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->tsiz = MRB_PARSER_TOKBUF_SIZE; p->tokbuf = p->buf; 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) { if (p->tokbuf != p->buf) { mrb_free(p->mrb, p->tokbuf); } 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->filename); 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) { size_t len = strlen(s); char *p = (char *)mrb_malloc(mrb, len + 1); memcpy(p, s, len + 1); if (c->filename) { mrb_free(mrb, c->filename); } 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 = (int)i; return; } } if (p->filename_table_length == UINT16_MAX) { yyerror(p, "too many files to compile"); 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->current_filename_index); } 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); } } #ifndef MRB_DISABLE_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, size_t 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); } MRB_API mrb_value mrb_load_exec(mrb_state *mrb, struct 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 (c) c->parser_nerr = 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 { if (mrb->exc == NULL) { 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) { if (mrb->exc == 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; } } MRB_PROC_SET_TARGET_CLASS(proc, target); if (mrb->c->ci) { mrb->c->ci->target_class = target; } v = mrb_top_run(mrb, proc, mrb_top_self(mrb), keep); if (mrb->exc) return mrb_nil_value(); return v; } #ifndef MRB_DISABLE_STDIO MRB_API mrb_value mrb_load_file_cxt(mrb_state *mrb, FILE *f, mrbc_context *c) { return mrb_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, size_t len, mrbc_context *c) { return mrb_load_exec(mrb, mrb_parse_nstring(mrb, s, len, c), c); } MRB_API mrb_value mrb_load_nstring(mrb_state *mrb, const char *s, size_t 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); } #ifndef MRB_DISABLE_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; } } static void dump_args(mrb_state *mrb, node *n, int offset) { 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=\n", mrb_sym2name(mrb, sym(n2->car->car))); mrb_parser_dump(mrb, n2->car->cdr, offset+3); 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) { mrb_assert(intn(n->car) == NODE_ARGS_TAIL); mrb_parser_dump(mrb, n, offset); } } #endif void mrb_parser_dump(mrb_state *mrb, node *tree, int offset) { #ifndef MRB_DISABLE_STDIO int nodetype; if (!tree) return; again: dump_prefix(tree, offset); nodetype = intn(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_LAMBDA:\n"); dump_prefix(tree, offset); goto block; case NODE_BLOCK: block: printf("NODE_BLOCK:\n"); tree = tree->cdr; if (tree->car) { dump_args(mrb, tree->car, offset+1); } 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: case NODE_SCALL: switch (nodetype) { case NODE_FCALL: printf("NODE_FCALL:\n"); break; case NODE_CALL: printf("NODE_CALL(.):\n"); break; case NODE_SCALL: printf("NODE_SCALL(&.):\n"); break; default: break; } 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)), intn(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: ::%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_KW_HASH: printf("NODE_KW_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)), intn(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", intn(tree)); break; case NODE_NTH_REF: printf("NODE_NTH_REF: $%d\n", intn(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, intn(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, intn(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, intn(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); if (tree->cdr->cdr->cdr->car) { dump_prefix(tree, offset); printf("opt: %s\n", (char*)tree->cdr->cdr->cdr->car); } if (tree->cdr->cdr->cdr->cdr) { dump_prefix(tree, offset); printf("enc: %s\n", (char*)tree->cdr->cdr->cdr->cdr); } break; case NODE_SYM: printf("NODE_SYM :%s (%d)\n", mrb_sym2name(mrb, sym(tree)), intn(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) { dump_args(mrb, tree->car, offset); } 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) { dump_args(mrb, tree->car, offset+1); } 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 (<<%s):\n", ((parser_heredoc_info*)tree)->term); dump_recur(mrb, ((parser_heredoc_info*)tree)->doc, offset+1); break; case NODE_ARGS_TAIL: printf("NODE_ARGS_TAIL:\n"); { node *kws = tree->car; while (kws) { mrb_parser_dump(mrb, kws->car, offset+1); kws = kws->cdr; } } tree = tree->cdr; if (tree->car) { mrb_assert(intn(tree->car->car) == NODE_KW_REST_ARGS); mrb_parser_dump(mrb, tree->car, offset+1); } tree = tree->cdr; if (tree->car) { dump_prefix(tree, offset+1); printf("block='%s'\n", mrb_sym2name(mrb, sym(tree->car))); } break; case NODE_KW_ARG: printf("NODE_KW_ARG %s\n", mrb_sym2name(mrb, sym(tree->car))); mrb_parser_dump(mrb, tree->cdr->car, offset + 1); break; case NODE_KW_REST_ARGS: printf("NODE_KW_REST_ARGS %s\n", mrb_sym2name(mrb, sym(tree))); break; default: printf("node type: %d (0x%x)\n", nodetype, (unsigned)nodetype); break; } #endif } mruby-2.0.0/mrbgems/mruby-compiler/mrbgem.rake000066400000000000000000000026741340361412400213750ustar00rootroot00000000000000MRuby::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_exception_enabled? and f =~ /(codegen).c$/ objfile(f.pathmap("#{current_build_dir}/core/%n")) }.compact if build.cxx_exception_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 # Parser file "#{current_build_dir}/core/y.tab.c" => ["#{current_dir}/core/parse.y", lex_def] do |t| FileUtils.mkdir_p File.dirname t.name 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 build.libmruby_core_static => core_objs build.libmruby << core_objs end mruby-2.0.0/mrbgems/mruby-enum-ext/000077500000000000000000000000001340361412400171775ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-enum-ext/mrbgem.rake000066400000000000000000000002501340361412400213110ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-enum-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Enumerable module extension' end mruby-2.0.0/mrbgems/mruby-enum-ext/mrblib/000077500000000000000000000000001340361412400204465ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-enum-ext/mrblib/enum.rb000066400000000000000000000536751340361412400217570ustar00rootroot00000000000000## # Enumerable # module Enumerable ## # call-seq: # enum.drop(n) -> array # # Drops first n elements from enum, and returns rest elements # in an array. # # a = [1, 2, 3, 4, 5, 0] # a.drop(3) #=> [4, 5, 0] def drop(n) n = n.__to_int raise ArgumentError, "attempt to drop negative size" if n < 0 ary = [] self.each {|*val| n == 0 ? ary << val.__svalue : n -= 1 } ary end ## # call-seq: # enum.drop_while {|arr| block } -> array # enum.drop_while -> an_enumerator # # Drops elements up to, but not including, the first element for # which the block returns +nil+ or +false+ and returns an array # containing the remaining elements. # # If no block is given, an enumerator is returned instead. # # a = [1, 2, 3, 4, 5, 0] # a.drop_while {|i| i < 3 } #=> [3, 4, 5, 0] def drop_while(&block) return to_enum :drop_while unless block ary, state = [], false self.each do |*val| state = true if !state and !block.call(*val) ary << val.__svalue if state end ary end ## # call-seq: # enum.take(n) -> array # # Returns first n elements from enum. # # a = [1, 2, 3, 4, 5, 0] # a.take(3) #=> [1, 2, 3] def take(n) n = n.__to_int i = n.to_i raise ArgumentError, "attempt to take negative size" if i < 0 ary = [] return ary if i == 0 self.each do |*val| ary << val.__svalue i -= 1 break if i == 0 end ary end ## # call-seq: # enum.take_while {|arr| block } -> array # enum.take_while -> an_enumerator # # Passes elements to the block until the block returns +nil+ or +false+, # then stops iterating and returns an array of all prior elements. # # If no block is given, an enumerator is returned instead. # # a = [1, 2, 3, 4, 5, 0] # a.take_while {|i| i < 3 } #=> [1, 2] # def take_while(&block) return to_enum :take_while unless block ary = [] self.each do |*val| return ary unless block.call(*val) ary << val.__svalue end ary end ## # Iterates the given block for each array of consecutive # elements. # # @return [nil] # # @example # (1..10).each_cons(3) {|a| p a} # # outputs below # [1, 2, 3] # [2, 3, 4] # [3, 4, 5] # [4, 5, 6] # [5, 6, 7] # [6, 7, 8] # [7, 8, 9] # [8, 9, 10] def each_cons(n, &block) n = n.__to_int raise ArgumentError, "invalid size" if n <= 0 return to_enum(:each_cons,n) unless block ary = [] n = n.to_i self.each do |*val| ary.shift if ary.size == n ary << val.__svalue block.call(ary.dup) if ary.size == n end nil end ## # Iterates the given block for each slice of elements. # # @return [nil] # # @example # (1..10).each_slice(3) {|a| p a} # # outputs below # [1, 2, 3] # [4, 5, 6] # [7, 8, 9] # [10] def each_slice(n, &block) n = n.__to_int raise ArgumentError, "invalid slice size" if n <= 0 return to_enum(:each_slice,n) unless block ary = [] n = n.to_i self.each do |*val| ary << val.__svalue if ary.size == n block.call(ary) ary = [] end end block.call(ary) unless ary.empty? nil end ## # call-seq: # enum.group_by {| obj | block } -> a_hash # enum.group_by -> an_enumerator # # Returns a hash, which keys are evaluated result from the # block, and values are arrays of elements in enum # corresponding to the key. # # (1..6).group_by {|i| i%3} #=> {0=>[3, 6], 1=>[1, 4], 2=>[2, 5]} # def group_by(&block) return to_enum :group_by unless block h = {} self.each do |*val| key = block.call(*val) sv = val.__svalue h.key?(key) ? (h[key] << sv) : (h[key] = [sv]) end h end ## # call-seq: # enum.sort_by { |obj| block } -> array # enum.sort_by -> an_enumerator # # Sorts enum using a set of keys generated by mapping the # values in enum through the given block. # # If no block is given, an enumerator is returned instead. def sort_by(&block) return to_enum :sort_by unless block ary = [] orig = [] self.each_with_index{|e, i| orig.push(e) ary.push([block.call(e), i]) } if ary.size > 1 ary.sort! end ary.collect{|e,i| orig[i]} end NONE = Object.new ## # call-seq: # enum.first -> obj or nil # enum.first(n) -> an_array # # Returns the first element, or the first +n+ elements, of the enumerable. # If the enumerable is empty, the first form returns nil, and the # second form returns an empty array. def first(*args) case args.length when 0 self.each do |*val| return val.__svalue end return nil when 1 i = args[0].__to_int raise ArgumentError, "attempt to take negative size" if i < 0 ary = [] return ary if i == 0 self.each do |*val| ary << val.__svalue i -= 1 break if i == 0 end ary else raise ArgumentError, "wrong number of arguments (given #{args.length}, expected 0..1)" end end ## # call-seq: # enum.count -> int # enum.count(item) -> int # enum.count { |obj| block } -> int # # Returns the number of items in +enum+ through enumeration. # If an argument is given, the number of items in +enum+ that # are equal to +item+ are counted. If a block is given, it # counts the number of elements yielding a true value. def count(v=NONE, &block) count = 0 if block self.each do |*val| count += 1 if block.call(*val) end else if v == NONE self.each { count += 1 } else self.each do |*val| count += 1 if val.__svalue == v end end end count end ## # call-seq: # enum.flat_map { |obj| block } -> array # enum.collect_concat { |obj| block } -> array # enum.flat_map -> an_enumerator # enum.collect_concat -> an_enumerator # # Returns a new array with the concatenated results of running # block once for every element in enum. # # If no block is given, an enumerator is returned instead. # # [1, 2, 3, 4].flat_map { |e| [e, -e] } #=> [1, -1, 2, -2, 3, -3, 4, -4] # [[1, 2], [3, 4]].flat_map { |e| e + [100] } #=> [1, 2, 100, 3, 4, 100] def flat_map(&block) return to_enum :flat_map unless block ary = [] self.each do |*e| e2 = block.call(*e) if e2.respond_to? :each e2.each {|e3| ary.push(e3) } else ary.push(e2) end end ary end alias collect_concat flat_map ## # call-seq: # enum.max_by {|obj| block } -> obj # enum.max_by -> an_enumerator # # Returns the object in enum that gives the maximum # value from the given block. # # If no block is given, an enumerator is returned instead. # # %w[albatross dog horse].max_by {|x| x.length } #=> "albatross" def max_by(&block) return to_enum :max_by unless block first = true max = nil max_cmp = nil self.each do |*val| if first max = val.__svalue max_cmp = block.call(*val) first = false else if (cmp = block.call(*val)) > max_cmp max = val.__svalue max_cmp = cmp end end end max end ## # call-seq: # enum.min_by {|obj| block } -> obj # enum.min_by -> an_enumerator # # Returns the object in enum that gives the minimum # value from the given block. # # If no block is given, an enumerator is returned instead. # # %w[albatross dog horse].min_by {|x| x.length } #=> "dog" def min_by(&block) return to_enum :min_by unless block first = true min = nil min_cmp = nil self.each do |*val| if first min = val.__svalue min_cmp = block.call(*val) first = false else if (cmp = block.call(*val)) < min_cmp min = val.__svalue min_cmp = cmp end end end min end ## # call-seq: # enum.minmax -> [min, max] # enum.minmax { |a, b| block } -> [min, max] # # Returns two elements array which contains the minimum and the # maximum value in the enumerable. The first form assumes all # objects implement Comparable; the second uses the # block to return a <=> b. # # a = %w(albatross dog horse) # a.minmax #=> ["albatross", "horse"] # a.minmax { |a, b| a.length <=> b.length } #=> ["dog", "albatross"] def minmax(&block) max = nil min = nil first = true self.each do |*val| if first val = val.__svalue max = val min = val first = false else val = val.__svalue if block max = val if block.call(val, max) > 0 min = val if block.call(val, min) < 0 else max = val if (val <=> max) > 0 min = val if (val <=> min) < 0 end end end [min, max] end ## # call-seq: # enum.minmax_by { |obj| block } -> [min, max] # enum.minmax_by -> an_enumerator # # Returns a two element array containing the objects in # enum that correspond to the minimum and maximum values respectively # from the given block. # # If no block is given, an enumerator is returned instead. # # %w(albatross dog horse).minmax_by { |x| x.length } #=> ["dog", "albatross"] def minmax_by(&block) return to_enum :minmax_by unless block max = nil max_cmp = nil min = nil min_cmp = nil first = true self.each do |*val| if first max = min = val.__svalue max_cmp = min_cmp = block.call(*val) first = false else if (cmp = block.call(*val)) > max_cmp max = val.__svalue max_cmp = cmp end if (cmp = block.call(*val)) < min_cmp min = val.__svalue min_cmp = cmp end end end [min, max] end ## # call-seq: # enum.none? [{ |obj| block }] -> true or false # enum.none?(pattern) -> true or false # # Passes each element of the collection to the given block. The method # returns true if the block never returns true # for all elements. If the block is not given, none? will return # true only if none of the collection members is true. # # If a pattern is supplied instead, the method returns whether # pattern === element for none of the collection members. # # %w(ant bear cat).none? { |word| word.length == 5 } #=> true # %w(ant bear cat).none? { |word| word.length >= 4 } #=> false # %w{ant bear cat}.none?(/d/) #=> true # [1, 3.14, 42].none?(Float) #=> false # [].none? #=> true # [nil, false].none? #=> true # [nil, true].none? #=> false def none?(pat=NONE, &block) if pat != NONE self.each do |*val| return false if pat === val.__svalue end elsif block self.each do |*val| return false if block.call(*val) end else self.each do |*val| return false if val.__svalue end end true end ## # call-seq: # enum.one? [{ |obj| block }] -> true or false # enum.one?(pattern) -> true or false # # Passes each element of the collection to the given block. The method # returns true if the block returns true # exactly once. If the block is not given, one? will return # true only if exactly one of the collection members is # true. # # If a pattern is supplied instead, the method returns whether # pattern === element for exactly one collection member. # # %w(ant bear cat).one? { |word| word.length == 4 } #=> true # %w(ant bear cat).one? { |word| word.length > 4 } #=> false # %w(ant bear cat).one? { |word| word.length < 4 } #=> false # %w{ant bear cat}.one?(/t/) #=> false # [nil, true, 99].one? #=> false # [nil, true, false].one? #=> true # [ nil, true, 99 ].one?(Integer) #=> true # [].one? #=> false def one?(pat=NONE, &block) count = 0 if pat!=NONE self.each do |*val| count += 1 if pat === val.__svalue return false if count > 1 end elsif block self.each do |*val| count += 1 if block.call(*val) return false if count > 1 end else self.each do |*val| count += 1 if val.__svalue return false if count > 1 end end count == 1 ? true : false end # ISO 15.3.2.2.1 # call-seq: # enum.all? [{ |obj| block } ] -> true or false # enum.all?(pattern) -> true or false # # Passes each element of the collection to the given block. The method # returns true if the block never returns # false or nil. If the block is not given, # Ruby adds an implicit block of { |obj| obj } which will # cause #all? to return +true+ when none of the collection members are # +false+ or +nil+. # # If a pattern is supplied instead, the method returns whether # pattern === element for every collection member. # # %w[ant bear cat].all? { |word| word.length >= 3 } #=> true # %w[ant bear cat].all? { |word| word.length >= 4 } #=> false # %w[ant bear cat].all?(/t/) #=> false # [1, 2i, 3.14].all?(Numeric) #=> true # [nil, true, 99].all? #=> false # def all?(pat=NONE, &block) if pat != NONE self.each{|*val| return false unless pat === val.__svalue} elsif block self.each{|*val| return false unless block.call(*val)} else self.each{|*val| return false unless val.__svalue} end true end # ISO 15.3.2.2.2 # call-seq: # enum.any? [{ |obj| block }] -> true or false # enum.any?(pattern) -> true or false # # Passes each element of the collection to the given block. The method # returns true if the block ever returns a value other # than false or nil. If the block is not # given, Ruby adds an implicit block of { |obj| obj } that # will cause #any? to return +true+ if at least one of the collection # members is not +false+ or +nil+. # # If a pattern is supplied instead, the method returns whether # pattern === element for any collection member. # # %w[ant bear cat].any? { |word| word.length >= 3 } #=> true # %w[ant bear cat].any? { |word| word.length >= 4 } #=> true # %w[ant bear cat].any?(/d/) #=> false # [nil, true, 99].any?(Integer) #=> true # [nil, true, 99].any? #=> true # [].any? #=> false # def any?(pat=NONE, &block) if pat != NONE self.each{|*val| return true if pat === val.__svalue} elsif block self.each{|*val| return true if block.call(*val)} else self.each{|*val| return true if val.__svalue} end false end ## # call-seq: # enum.each_with_object(obj) { |(*args), memo_obj| ... } -> obj # enum.each_with_object(obj) -> an_enumerator # # Iterates the given block for each element with an arbitrary # object given, and returns the initially given object. # # If no block is given, returns an enumerator. # # (1..10).each_with_object([]) { |i, a| a << i*2 } # #=> [2, 4, 6, 8, 10, 12, 14, 16, 18, 20] # def each_with_object(obj, &block) return to_enum(:each_with_object, obj) unless block self.each {|*val| block.call(val.__svalue, obj) } obj end ## # call-seq: # enum.reverse_each { |item| block } -> enum # enum.reverse_each -> an_enumerator # # Builds a temporary array and traverses that array in reverse order. # # If no block is given, an enumerator is returned instead. # # (1..3).reverse_each { |v| p v } # # produces: # # 3 # 2 # 1 # def reverse_each(&block) return to_enum :reverse_each unless block ary = self.to_a i = ary.size - 1 while i>=0 block.call(ary[i]) i -= 1 end self end ## # call-seq: # enum.cycle(n=nil) { |obj| block } -> nil # enum.cycle(n=nil) -> an_enumerator # # Calls block for each element of enum repeatedly _n_ # times or forever if none or +nil+ is given. If a non-positive # number is given or the collection is empty, does nothing. Returns # +nil+ if the loop has finished without getting interrupted. # # Enumerable#cycle saves elements in an internal array so changes # to enum after the first pass have no effect. # # If no block is given, an enumerator is returned instead. # # a = ["a", "b", "c"] # a.cycle { |x| puts x } # print, a, b, c, a, b, c,.. forever. # a.cycle(2) { |x| puts x } # print, a, b, c, a, b, c. # def cycle(nv = nil, &block) return to_enum(:cycle, nv) unless block n = nil if nv.nil? n = -1 else n = nv.__to_int return nil if n <= 0 end ary = [] each do |*i| ary.push(i) yield(*i) end return nil if ary.empty? while n < 0 || 0 < (n -= 1) ary.each do |i| yield(*i) end end nil end ## # call-seq: # enum.find_index(value) -> int or nil # enum.find_index { |obj| block } -> int or nil # enum.find_index -> an_enumerator # # Compares each entry in enum with value or passes # to block. Returns the index for the first for which the # evaluated value is non-false. If no object matches, returns # nil # # If neither block nor argument is given, an enumerator is returned instead. # # (1..10).find_index { |i| i % 5 == 0 and i % 7 == 0 } #=> nil # (1..100).find_index { |i| i % 5 == 0 and i % 7 == 0 } #=> 34 # (1..100).find_index(50) #=> 49 # def find_index(val=NONE, &block) return to_enum(:find_index, val) if !block && val == NONE idx = 0 if block self.each do |*e| return idx if block.call(*e) idx += 1 end else self.each do |*e| return idx if e.__svalue == val idx += 1 end end nil end ## # call-seq: # enum.zip(arg, ...) -> an_array_of_array # enum.zip(arg, ...) { |arr| block } -> nil # # Takes one element from enum and merges corresponding # elements from each args. This generates a sequence of # n-element arrays, where n is one more than the # count of arguments. The length of the resulting sequence will be # enum#size. If the size of any argument is less than # enum#size, nil values are supplied. If # a block is given, it is invoked for each output array, otherwise # an array of arrays is returned. # # a = [ 4, 5, 6 ] # b = [ 7, 8, 9 ] # # a.zip(b) #=> [[4, 7], [5, 8], [6, 9]] # [1, 2, 3].zip(a, b) #=> [[1, 4, 7], [2, 5, 8], [3, 6, 9]] # [1, 2].zip(a, b) #=> [[1, 4, 7], [2, 5, 8]] # a.zip([1, 2], [8]) #=> [[4, 1, 8], [5, 2, nil], [6, nil, nil]] # # c = [] # a.zip(b) { |x, y| c << x + y } #=> nil # c #=> [11, 13, 15] # def zip(*arg, &block) result = block ? nil : [] arg = arg.map do |a| unless a.respond_to?(:to_a) raise TypeError, "wrong argument type #{a.class} (must respond to :to_a)" end a.to_a end i = 0 self.each do |*val| a = [] a.push(val.__svalue) idx = 0 while idx < arg.size a.push(arg[idx][i]) idx += 1 end i += 1 if result.nil? block.call(a) else result.push(a) end end result end ## # call-seq: # enum.to_h -> hash # # Returns the result of interpreting enum as a list of # [key, value] pairs. # # %i[hello world].each_with_index.to_h # # => {:hello => 0, :world => 1} # def to_h(&blk) h = {} if blk self.each do |v| v = blk.call(v) raise TypeError, "wrong element type #{v.class} (expected Array)" unless v.is_a? Array raise ArgumentError, "element has wrong array length (expected 2, was #{v.size})" if v.size != 2 h[v[0]] = v[1] end else self.each do |*v| v = v.__svalue raise TypeError, "wrong element type #{v.class} (expected Array)" unless v.is_a? Array raise ArgumentError, "element has wrong array length (expected 2, was #{v.size})" if v.size != 2 h[v[0]] = v[1] end end h end def nil.to_h {} end def uniq(&block) hash = {} if block self.each do|*v| v = v.__svalue hash[block.call(v)] ||= v end else self.each do|*v| v = v.__svalue hash[v] ||= v end end hash.values end end mruby-2.0.0/mrbgems/mruby-enum-ext/test/000077500000000000000000000000001340361412400201565ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-enum-ext/test/enum.rb000066400000000000000000000117321340361412400214530ustar00rootroot00000000000000## # Enumerable(Ext) Test assert("Enumerable#drop") do a = [1, 2, 3, 4, 5, 0] assert_equal [4, 5, 0], a.drop(3) assert_equal [], a.drop(6) end assert("Enumerable#drop_while") do a = [1, 2, 3, 4, 5, 0] assert_equal [3, 4, 5, 0], a.drop_while {|i| i < 3 } end assert("Enumerable#take") do a = [1, 2, 3, 4, 5, 0] assert_equal [1, 2, 3], a.take(3) end assert("Enumerable#take_while") do a = [1, 2, 3, 4, 5, 0] assert_equal [1, 2], a.take_while {|i| i < 3} end assert("Enumerable#each_cons") do a = [] b = (1..5).each_cons(3){|e| a << e} assert_equal [[1, 2, 3], [2, 3, 4], [3, 4, 5]], a assert_equal nil, b end assert("Enumerable#each_slice") do a = [] b = (1..10).each_slice(3){|e| a << e} assert_equal [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]], a assert_equal nil, b end assert("Enumerable#group_by") do r = (1..6).group_by {|i| i % 3 } assert_equal [3, 6], r[0] assert_equal [1, 4], r[1] assert_equal [2, 5], r[2] end assert("Enumerable#sort_by") do assert_equal ["car", "train", "bicycle"], %w{car bicycle train}.sort_by {|e| e.length} end assert("Enumerable#first") do a = Object.new a.extend Enumerable def a.each yield 1 yield 2 yield 3 end assert_equal 1, a.first assert_equal [1, 2], a.first(2) assert_equal [1, 2, 3], a.first(10) a = Object.new a.extend Enumerable def a.each end assert_nil a.first end assert("Enumerable#count") do a = [1, 2, 4, 2] assert_equal 4, a.count assert_equal 2, a.count(2) assert_equal 3, a.count{|x| x % 2 == 0} end assert("Enumerable#flat_map") do assert_equal [1, 2, 3, 4], [1, 2, 3, 4].flat_map { |e| e } assert_equal [1, -1, 2, -2, 3, -3, 4, -4], [1, 2, 3, 4].flat_map { |e| [e, -e] } assert_equal [1, 2, 100, 3, 4, 100], [[1, 2], [3, 4]].flat_map { |e| e + [100] } end assert("Enumerable#max_by") do assert_equal "albatross", %w[albatross dog horse].max_by { |x| x.length } end assert("Enumerable#min_by") do assert_equal "dog", %w[albatross dog horse].min_by { |x| x.length } end assert("Enumerable#minmax") do a = %w(albatross dog horse) assert_equal ["albatross", "horse"], a.minmax assert_equal ["dog", "albatross"], a.minmax { |a, b| a.length <=> b.length } end assert("Enumerable#minmax_by") do assert_equal ["dog", "albatross"], %w(albatross dog horse).minmax_by { |x| x.length } end assert("Enumerable#none?") do assert_true %w(ant bear cat).none? { |word| word.length == 5 } assert_false %w(ant bear cat).none? { |word| word.length >= 4 } assert_false [1, 3.14, 42].none?(Float) assert_true [].none? assert_true [nil, false].none? assert_false [nil, true].none? end assert("Enumerable#one?") do assert_true %w(ant bear cat).one? { |word| word.length == 4 } assert_false %w(ant bear cat).one? { |word| word.length > 4 } assert_false %w(ant bear cat).one? { |word| word.length < 4 } assert_true [1, 3.14, 42].one?(Float) assert_false [nil, true, 99].one? assert_true [nil, true, false].one? assert_true [ nil, true, 99 ].one?(Integer) assert_false [].one? end assert("Enumerable#all? (enhancement)") do assert_false [1, 2, 3.14].all?(Integer) assert_true [1, 2, 3.14].all?(Numeric) end assert("Enumerable#any? (enhancement)") do assert_false [1, 2, 3].all?(Float) assert_true [nil, true, 99].any?(Integer) end assert("Enumerable#each_with_object") do assert_true [2, 4, 6, 8, 10, 12, 14, 16, 18, 20], (1..10).each_with_object([]) { |i, a| a << i*2 } assert_raise(ArgumentError) { (1..10).each_with_object() { |i, a| a << i*2 } } end assert("Enumerable#reverse_each") do r = (1..3) a = [] assert_equal (1..3), r.reverse_each { |v| a << v } assert_equal [3, 2, 1], a end assert("Enumerable#cycle") do a = [] ["a", "b", "c"].cycle(2) { |v| a << v } assert_equal ["a", "b", "c", "a", "b", "c"], a assert_raise(TypeError) { ["a", "b", "c"].cycle("a") { |v| a << v } } empty = Class.new do include Enumerable def each end end assert_nil empty.new.cycle { break :nope } end assert("Enumerable#find_index") do assert_nil (1..10).find_index { |i| i % 5 == 0 and i % 7 == 0 } assert_equal 34, (1..100).find_index { |i| i % 5 == 0 and i % 7 == 0 } assert_equal 49 ,(1..100).find_index(50) end assert("Enumerable#zip") do a = [ 4, 5, 6 ] b = [ 7, 8, 9 ] assert_equal [[4, 7], [5, 8], [6, 9]], a.zip(b) assert_equal [[1, 4, 7], [2, 5, 8], [3, 6, 9]], [1, 2, 3].zip(a, b) assert_equal [[1, 4, 7], [2, 5, 8]], [1, 2].zip(a, b) assert_equal [[4, 1, 8], [5, 2, nil], [6, nil, nil]], a.zip([1, 2], [8]) ret = [] assert_equal nil, a.zip([1, 2], [8]) { |i| ret << i } assert_equal [[4, 1, 8], [5, 2, nil], [6, nil, nil]], ret assert_raise(TypeError) { [1].zip(1) } end assert("Enumerable#to_h") do c = Class.new { include Enumerable def each yield [1,2] yield [3,4] end } h0 = {1=>2, 3=>4} h = c.new.to_h assert_equal Hash, h.class assert_equal h0, h # mruby-enum-ext also provides nil.to_h assert_equal Hash.new, nil.to_h assert_equal({1=>4,3=>8}, c.new.to_h{|k,v|[k,v*2]}) end mruby-2.0.0/mrbgems/mruby-enum-lazy/000077500000000000000000000000001340361412400173565ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-enum-lazy/mrbgem.rake000066400000000000000000000004561340361412400215000ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-enum-lazy') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Enumerator::Lazy class' spec.add_dependency('mruby-enumerator', :core => 'mruby-enumerator') spec.add_dependency('mruby-enum-ext', :core => 'mruby-enum-ext') end mruby-2.0.0/mrbgems/mruby-enum-lazy/mrblib/000077500000000000000000000000001340361412400206255ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-enum-lazy/mrblib/lazy.rb000066400000000000000000000067331340361412400221420ustar00rootroot00000000000000module Enumerable # = Enumerable#lazy implementation # # Enumerable#lazy returns an instance of Enumerator::Lazy. # You can use it just like as normal Enumerable object, # except these methods act as 'lazy': # # - map collect # - select find_all # - reject # - grep # - drop # - drop_while # - take_while # - flat_map collect_concat # - zip def lazy Enumerator::Lazy.new(self) end end class Enumerator # == Acknowledgements # # Based on https://github.com/yhara/enumerable-lazy # Inspired by https://github.com/antimon2/enumerable_lz # http://jp.rubyist.net/magazine/?0034-Enumerable_lz (ja) class Lazy < Enumerator def initialize(obj, &block) super(){|yielder| begin obj.each{|x| if block block.call(yielder, x) else yielder << x end } rescue StopIteration end } end def to_enum(meth=:each, *args, &block) unless self.respond_to?(meth) raise ArgumentError, "undefined method #{meth}" end lz = Lazy.new(self, &block) lz.obj = self lz.meth = meth lz.args = args lz end alias enum_for to_enum def map(&block) Lazy.new(self){|yielder, val| yielder << block.call(val) } end alias collect map def select(&block) Lazy.new(self){|yielder, val| if block.call(val) yielder << val end } end alias find_all select def reject(&block) Lazy.new(self){|yielder, val| unless block.call(val) yielder << val end } end def grep(pattern) Lazy.new(self){|yielder, val| if pattern === val yielder << val end } end def drop(n) dropped = 0 Lazy.new(self){|yielder, val| if dropped < n dropped += 1 else yielder << val end } end def drop_while(&block) dropping = true Lazy.new(self){|yielder, val| if dropping if not block.call(val) yielder << val dropping = false end else yielder << val end } end def take(n) if n == 0 return Lazy.new(self){raise StopIteration} end taken = 0 Lazy.new(self){|yielder, val| yielder << val taken += 1 if taken >= n raise StopIteration end } end def take_while(&block) Lazy.new(self){|yielder, val| if block.call(val) yielder << val else raise StopIteration end } end def flat_map(&block) Lazy.new(self){|yielder, val| ary = block.call(val) # TODO: check ary is an Array ary.each{|x| yielder << x } } end alias collect_concat flat_map def zip(*args, &block) enums = [self] + args Lazy.new(self){|yielder, val| ary = enums.map{|e| e.next} if block yielder << block.call(ary) else yielder << ary end } end def uniq(&block) hash = {} Lazy.new(self){|yielder, val| if block v = block.call(val) else v = val end unless hash.include?(v) yielder << val hash[v] = val end } end alias force to_a end end mruby-2.0.0/mrbgems/mruby-enum-lazy/test/000077500000000000000000000000001340361412400203355ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-enum-lazy/test/lazy.rb000066400000000000000000000022701340361412400216420ustar00rootroot00000000000000assert("Enumerator::Lazy") do a = [1, 2] assert_equal Enumerator::Lazy, a.lazy.class end assert("Enumerator::Lazy laziness") do a = Object.new def a.each return to_enum :each unless block_given? self.b << 10 yield 1 self.b << 20 yield 2 self.b << 30 yield 3 self.b << 40 yield 4 self.b << 50 yield 5 end def a.b(b=nil) @b = b if b @b end a.b([]) assert_equal [1,2], a.each.lazy.take(2).force assert_equal [10,20], a.b a.b([]) assert_equal [2,4], a.each.lazy.select{|x|x%2==0}.take(2).force assert_equal [10,20,30,40], a.b a.b([]) assert_equal [1], a.each.lazy.take_while{|x|x<2}.take(1).force assert_equal [10], a.b a.b([]) assert_equal [1], a.each.lazy.take_while{|x|x<2}.take(4).force assert_equal [10,20], a.b end assert("Enumerator::Lazy#to_enum") do lazy_enum = (0..Float::INFINITY).lazy.to_enum(:each_slice, 2) assert_kind_of Enumerator::Lazy, lazy_enum assert_equal [0*1, 2*3, 4*5, 6*7], lazy_enum.map { |a| a.first * a.last }.first(4) end assert("Enumerator::Lazy#zip with cycle") do e1 = [1, 2, 3].cycle e2 = [:a, :b].cycle assert_equal [[1,:a],[2,:b],[3,:a]], e1.lazy.zip(e2).first(3) end mruby-2.0.0/mrbgems/mruby-enumerator/000077500000000000000000000000001340361412400176165ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-enumerator/mrbgem.rake000066400000000000000000000004361340361412400217360ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-enumerator') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.add_dependency('mruby-fiber', :core => 'mruby-fiber') spec.add_dependency 'mruby-enum-ext', :core => 'mruby-enum-ext' spec.summary = 'Enumerator class' end mruby-2.0.0/mrbgems/mruby-enumerator/mrblib/000077500000000000000000000000001340361412400210655ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-enumerator/mrblib/enumerator.rb000066400000000000000000000401301340361412400235710ustar00rootroot00000000000000## # enumerator.rb Enumerator class # See Copyright Notice in mruby.h ## # A class which allows both internal and external iteration. # # An Enumerator can be created by the following methods. # - {Kernel#to_enum} # - {Kernel#enum_for} # - {Enumerator#initialize Enumerator.new} # # Most methods have two forms: a block form where the contents # are evaluated for each item in the enumeration, and a non-block form # which returns a new Enumerator wrapping the iteration. # # enumerator = %w(one two three).each # puts enumerator.class # => Enumerator # # enumerator.each_with_object("foo") do |item, obj| # puts "#{obj}: #{item}" # end # # # foo: one # # foo: two # # foo: three # # enum_with_obj = enumerator.each_with_object("foo") # puts enum_with_obj.class # => Enumerator # # enum_with_obj.each do |item, obj| # puts "#{obj}: #{item}" # end # # # foo: one # # foo: two # # foo: three # # This allows you to chain Enumerators together. For example, you # can map a list's elements to strings containing the index # and the element as a string via: # # puts %w[foo bar baz].map.with_index { |w, i| "#{i}:#{w}" } # # => ["0:foo", "1:bar", "2:baz"] # # An Enumerator can also be used as an external iterator. # For example, Enumerator#next returns the next value of the iterator # or raises StopIteration if the Enumerator is at the end. # # e = [1,2,3].each # returns an enumerator object. # puts e.next # => 1 # puts e.next # => 2 # puts e.next # => 3 # puts e.next # raises StopIteration # # You can use this to implement an internal iterator as follows: # # def ext_each(e) # while true # begin # vs = e.next_values # rescue StopIteration # return $!.result # end # y = yield(*vs) # e.feed y # end # end # # o = Object.new # # def o.each # puts yield # puts yield(1) # puts yield(1, 2) # 3 # end # # # use o.each as an internal iterator directly. # puts o.each {|*x| puts x; [:b, *x] } # # => [], [:b], [1], [:b, 1], [1, 2], [:b, 1, 2], 3 # # # convert o.each to an external iterator for # # implementing an internal iterator. # puts ext_each(o.to_enum) {|*x| puts x; [:b, *x] } # # => [], [:b], [1], [:b, 1], [1, 2], [:b, 1, 2], 3 # class Enumerator include Enumerable ## # @overload initialize(size = nil, &block) # @overload initialize(obj, method = :each, *args) # # Creates a new Enumerator object, which can be used as an # Enumerable. # # In the first form, iteration is defined by the given block, in # which a "yielder" object, given as block parameter, can be used to # yield a value by calling the +yield+ method (aliased as +<<+): # # fib = Enumerator.new do |y| # a = b = 1 # loop do # y << a # a, b = b, a + b # end # end # # p fib.take(10) # => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55] # def initialize(obj=nil, meth=:each, *args, &block) if block obj = Generator.new(&block) else raise ArgumentError unless obj end if @obj and !self.respond_to?(meth) raise NoMethodError, "undefined method #{meth}" end @obj = obj @meth = meth @args = args.dup @fib = nil @dst = nil @lookahead = nil @feedvalue = nil @stop_exc = false end attr_accessor :obj, :meth, :args, :fib private :obj, :meth, :args, :fib def initialize_copy(obj) raise TypeError, "can't copy type #{obj.class}" unless obj.kind_of? Enumerator raise TypeError, "can't copy execution context" if obj.fib @obj = obj.obj @meth = obj.meth @args = obj.args @fib = nil @lookahead = nil @feedvalue = nil self end ## # call-seq: # e.with_index(offset = 0) {|(*args), idx| ... } # e.with_index(offset = 0) # # Iterates the given block for each element with an index, which # starts from +offset+. If no block is given, returns a new Enumerator # that includes the index, starting from +offset+ # # +offset+:: the starting index to use # def with_index(offset=0, &block) return to_enum :with_index, offset unless block if offset.nil? offset = 0 else offset = offset.__to_int end n = offset - 1 enumerator_block_call do |*i| n += 1 block.call i.__svalue, n end end ## # call-seq: # e.each_with_index {|(*args), idx| ... } # e.each_with_index # # Same as Enumerator#with_index(0), i.e. there is no starting offset. # # If no block is given, a new Enumerator is returned that includes the index. # def each_with_index(&block) with_index(0, &block) end ## # call-seq: # e.each_with_object(obj) {|(*args), obj| ... } # e.each_with_object(obj) # e.with_object(obj) {|(*args), obj| ... } # e.with_object(obj) # # Iterates the given block for each element with an arbitrary object, +obj+, # and returns +obj+ # # If no block is given, returns a new Enumerator. # # @example # to_three = Enumerator.new do |y| # 3.times do |x| # y << x # end # end # # to_three_with_string = to_three.with_object("foo") # to_three_with_string.each do |x,string| # puts "#{string}: #{x}" # end # # # => foo:0 # # => foo:1 # # => foo:2 # def with_object(object, &block) return to_enum(:with_object, object) unless block enumerator_block_call do |i| block.call [i,object] end object end def inspect return "#<#{self.class}: uninitialized>" unless @obj if @args && @args.size > 0 args = @args.join(", ") "#<#{self.class}: #{@obj}:#{@meth}(#{args})>" else "#<#{self.class}: #{@obj}:#{@meth}>" end end ## # call-seq: # enum.each { |elm| block } -> obj # enum.each -> enum # enum.each(*appending_args) { |elm| block } -> obj # enum.each(*appending_args) -> an_enumerator # # Iterates over the block according to how this Enumerator was constructed. # If no block and no arguments are given, returns self. # # === Examples # # "Hello, world!".scan(/\w+/) #=> ["Hello", "world"] # "Hello, world!".to_enum(:scan, /\w+/).to_a #=> ["Hello", "world"] # "Hello, world!".to_enum(:scan).each(/\w+/).to_a #=> ["Hello", "world"] # # obj = Object.new # # def obj.each_arg(a, b=:b, *rest) # yield a # yield b # yield rest # :method_returned # end # # enum = obj.to_enum :each_arg, :a, :x # # enum.each.to_a #=> [:a, :x, []] # enum.each.equal?(enum) #=> true # enum.each { |elm| elm } #=> :method_returned # # enum.each(:y, :z).to_a #=> [:a, :x, [:y, :z]] # enum.each(:y, :z).equal?(enum) #=> false # enum.each(:y, :z) { |elm| elm } #=> :method_returned # def each(*argv, &block) obj = self if 0 < argv.length obj = self.dup args = obj.args if !args.empty? args = args.dup args.concat argv else args = argv.dup end obj.args = args end return obj unless block enumerator_block_call(&block) end def enumerator_block_call(&block) @obj.__send__ @meth, *@args, &block end private :enumerator_block_call ## # call-seq: # e.next -> object # # Returns the next object in the enumerator, and move the internal position # forward. When the position reached at the end, StopIteration is raised. # # === Example # # a = [1,2,3] # e = a.to_enum # p e.next #=> 1 # p e.next #=> 2 # p e.next #=> 3 # p e.next #raises StopIteration # # Note that enumeration sequence by +next+ does not affect other non-external # enumeration methods, unless the underlying iteration methods itself has # side-effect # def next next_values.__svalue end ## # call-seq: # e.next_values -> array # # Returns the next object as an array in the enumerator, and move the # internal position forward. When the position reached at the end, # StopIteration is raised. # # This method can be used to distinguish yield and yield # nil. # # === Example # # o = Object.new # def o.each # yield # yield 1 # yield 1, 2 # yield nil # yield [1, 2] # end # e = o.to_enum # p e.next_values # p e.next_values # p e.next_values # p e.next_values # p e.next_values # e = o.to_enum # p e.next # p e.next # p e.next # p e.next # p e.next # # ## yield args next_values next # # yield [] nil # # yield 1 [1] 1 # # yield 1, 2 [1, 2] [1, 2] # # yield nil [nil] nil # # yield [1, 2] [[1, 2]] [1, 2] # # Note that +next_values+ does not affect other non-external enumeration # methods unless underlying iteration method itself has side-effect # def next_values if @lookahead vs = @lookahead @lookahead = nil return vs end raise @stop_exc if @stop_exc curr = Fiber.current if !@fib || !@fib.alive? @dst = curr @fib = Fiber.new do result = each do |*args| feedvalue = nil Fiber.yield args if @feedvalue feedvalue = @feedvalue @feedvalue = nil end feedvalue end @stop_exc = StopIteration.new "iteration reached an end" @stop_exc.result = result Fiber.yield nil end @lookahead = nil end vs = @fib.resume curr if @stop_exc @fib = nil @dst = nil @lookahead = nil @feedvalue = nil raise @stop_exc end vs end ## # call-seq: # e.peek -> object # # Returns the next object in the enumerator, but doesn't move the internal # position forward. If the position is already at the end, StopIteration # is raised. # # === Example # # a = [1,2,3] # e = a.to_enum # p e.next #=> 1 # p e.peek #=> 2 # p e.peek #=> 2 # p e.peek #=> 2 # p e.next #=> 2 # p e.next #=> 3 # p e.next #raises StopIteration # def peek peek_values.__svalue end ## # call-seq: # e.peek_values -> array # # Returns the next object as an array, similar to Enumerator#next_values, but # doesn't move the internal position forward. If the position is already at # the end, StopIteration is raised. # # === Example # # o = Object.new # def o.each # yield # yield 1 # yield 1, 2 # end # e = o.to_enum # p e.peek_values #=> [] # e.next # p e.peek_values #=> [1] # p e.peek_values #=> [1] # e.next # p e.peek_values #=> [1, 2] # e.next # p e.peek_values # raises StopIteration # def peek_values if @lookahead.nil? @lookahead = next_values end @lookahead.dup end ## # call-seq: # e.rewind -> e # # Rewinds the enumeration sequence to the beginning. # # If the enclosed object responds to a "rewind" method, it is called. # def rewind @obj.rewind if @obj.respond_to? :rewind @fib = nil @dst = nil @lookahead = nil @feedvalue = nil @stop_exc = false self end ## # call-seq: # e.feed obj -> nil # # Sets the value to be returned by the next yield inside +e+. # # If the value is not set, the yield returns nil. # # This value is cleared after being yielded. # # # Array#map passes the array's elements to "yield" and collects the # # results of "yield" as an array. # # Following example shows that "next" returns the passed elements and # # values passed to "feed" are collected as an array which can be # # obtained by StopIteration#result. # e = [1,2,3].map # p e.next #=> 1 # e.feed "a" # p e.next #=> 2 # e.feed "b" # p e.next #=> 3 # e.feed "c" # begin # e.next # rescue StopIteration # p $!.result #=> ["a", "b", "c"] # end # # o = Object.new # def o.each # x = yield # (2) blocks # p x # (5) => "foo" # x = yield # (6) blocks # p x # (8) => nil # x = yield # (9) blocks # p x # not reached w/o another e.next # end # # e = o.to_enum # e.next # (1) # e.feed "foo" # (3) # e.next # (4) # e.next # (7) # # (10) # def feed(value) raise TypeError, "feed value already set" if @feedvalue @feedvalue = value nil end # just for internal class Generator include Enumerable def initialize(&block) raise TypeError, "wrong argument type #{self.class} (expected Proc)" unless block.kind_of? Proc @proc = block end def each(*args, &block) args.unshift Yielder.new(&block) @proc.call(*args) end end # just for internal class Yielder def initialize(&block) raise LocalJumpError, "no block given" unless block @proc = block end def yield(*args) @proc.call(*args) end def << *args self.yield(*args) self end end end module Kernel ## # call-seq: # obj.to_enum(method = :each, *args) -> enum # obj.enum_for(method = :each, *args) -> enum # obj.to_enum(method = :each, *args) {|*args| block} -> enum # obj.enum_for(method = :each, *args){|*args| block} -> enum # # Creates a new Enumerator which will enumerate by calling +method+ on # +obj+, passing +args+ if any. # # If a block is given, it will be used to calculate the size of # the enumerator without the need to iterate it (see Enumerator#size). # # === Examples # # str = "xyz" # # enum = str.enum_for(:each_byte) # enum.each { |b| puts b } # # => 120 # # => 121 # # => 122 # # # protect an array from being modified by some_method # a = [1, 2, 3] # some_method(a.to_enum) # # It is typical to call to_enum when defining methods for # a generic Enumerable, in case no block is passed. # # Here is such an example, with parameter passing and a sizing block: # # module Enumerable # # a generic method to repeat the values of any enumerable # def repeat(n) # raise ArgumentError, "#{n} is negative!" if n < 0 # unless block_given? # return to_enum(__method__, n) do # __method__ is :repeat here # sz = size # Call size and multiply by n... # sz * n if sz # but return nil if size itself is nil # end # end # each do |*val| # n.times { yield *val } # end # end # end # # %i[hello world].repeat(2) { |w| puts w } # # => Prints 'hello', 'hello', 'world', 'world' # enum = (1..14).repeat(3) # # => returns an Enumerator when called without a block # enum.first(4) # => [1, 1, 1, 2] # def to_enum(meth=:each, *args) unless self.respond_to?(meth) raise ArgumentError, "undefined method #{meth}" end Enumerator.new self, meth, *args end alias enum_for to_enum end module Enumerable # use Enumerator to use infinite sequence def zip(*args, &block) args = args.map do |a| if a.respond_to?(:each) a.to_enum(:each) else raise TypeError, "wrong argument type #{a.class} (must respond to :each)" end end result = block ? nil : [] each do |*val| tmp = [val.__svalue] args.each do |arg| v = if arg.nil? nil else begin arg.next rescue StopIteration nil end end tmp.push(v) end if result.nil? block.call(tmp) else result.push(tmp) end end result end end mruby-2.0.0/mrbgems/mruby-enumerator/test/000077500000000000000000000000001340361412400205755ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-enumerator/test/enumerator.rb000066400000000000000000000267031340361412400233130ustar00rootroot00000000000000@obj = Object.new class << @obj include Enumerable def foo *a a.each { |x| yield x } end end assert 'Enumerator' do assert_equal Class, Enumerator.class end assert 'Enumerator' do assert_equal Object, Enumerator.superclass end assert 'Enumerator.new' do assert_equal [0,1,2], 3.times.map{|i| i}.sort assert_equal [:x,:y,:z], [:x,:y,:z].each.map{|i| i}.sort assert_equal [[:x,1],[:y,2]], {x:1, y:2}.each.map{|i| i}.sort assert_equal [1,2,3], @obj.to_enum(:foo, 1,2,3).to_a assert_equal [1,2,3], Enumerator.new(@obj, :foo, 1,2,3).to_a assert_equal [1,2,3], Enumerator.new { |y| i = 0; loop { y << (i += 1) } }.take(3) assert_raise(ArgumentError) { Enumerator.new } assert_raise(ArgumentError) { @obj.to_enum } # examples fib = Enumerator.new do |y| a = b = 1 loop do y << a a, b = b, a + b end end assert_equal [1,1,2,3,5,8,13,21,34,55], fib.take(10) end assert 'Enumerator#initialize_copy' do assert_equal [1, 2, 3], @obj.to_enum(:foo, 1, 2, 3).dup.to_a e = @obj.to_enum :foo, 1, 2, 3 assert_nothing_raised { assert_equal(1, e.next) } assert_raise(TypeError) { e.dup } e = Enumerator.new { |y| i = 0; loop { y << (i += 1) } }.dup assert_nothing_raised { assert_equal(1, e.next) } assert_raise(TypeError) { e.dup } end assert 'Enumerator#with_index' do assert_equal([[1,0],[2,1],[3,2]], @obj.to_enum(:foo, 1, 2, 3).with_index.to_a) assert_equal([[1,5],[2,6],[3,7]], @obj.to_enum(:foo, 1, 2, 3).with_index(5).to_a) a = [] @obj.to_enum(:foo, 1, 2, 3).with_index(10).with_index(20) { |*i| a << i } assert_equal [[[1, 10], 20], [[2, 11], 21], [[3, 12], 22]], a end assert 'Enumerator#with_index string offset' do assert_raise(TypeError){ @obj.to_enum(:foo, 1, 2, 3).with_index('1').to_a } end assert 'Enumerator#each_with_index' do assert_equal([[1,0],[2,1],[3,2]], @obj.to_enum(:foo, 1, 2, 3).each_with_index.to_a) a = [] @obj.to_enum(:foo, 1, 2, 3).each_with_index {|*i| a << i} assert_equal([[1, 0], [2, 1], [3, 2]], a) end assert 'Enumerator#with_object' do obj = [0, 1] ret = (1..10).each.with_object(obj) {|i, memo| memo[0] += i memo[1] *= i } assert_true(obj.equal?(ret)) assert_equal([55, 3628800], ret) end assert 'Enumerator#with_object arguments' do to_three = Enumerator.new do |y| 3.times do |x| y << x end end a = [] to_three_with_string = to_three.with_object("foo") to_three_with_string.each do |x,string| a << "#{string}:#{x}" end assert_equal ["foo:0","foo:1","foo:2"], a end assert 'Enumerator#inspect' do e = (0..10).each assert_equal("#", e.inspect) e = Enumerator.new("FooObject", :foo, 1) assert_equal("#", e.inspect) e = Enumerator.new("FooObject", :foo, 1, 2, 3) assert_equal("#", e.inspect) end assert 'Enumerator#each' do o = Object.new def o.each(ary) ary << 1 yield end ary = [] e = o.to_enum.each(ary) e.next assert_equal([1], ary) end assert 'Enumerator#each arguments' do obj = Object.new def obj.each_arg(a, b=:b, *rest) yield a yield b yield rest :method_returned end enum = obj.to_enum :each_arg, :a, :x assert_equal [:a, :x, []], enum.each.to_a assert_true enum.each.equal?(enum) assert_equal :method_returned, enum.each { |elm| elm } assert_equal [:a, :x, [:y, :z]], enum.each(:y, :z).to_a assert_false enum.each(:y, :z).equal?(enum) assert_equal :method_returned, enum.each(:y, :z) { |elm| elm } end assert 'Enumerator#next' do e = 3.times 3.times { |i| assert_equal i, e.next } assert_raise(StopIteration) { e.next } end assert 'Enumerator#next_values' do o = Object.new def o.each yield yield 1 yield 1, 2 end e = o.to_enum assert_equal nil, e.next assert_equal 1, e.next assert_equal [1,2], e.next e = o.to_enum assert_equal [], e.next_values assert_equal [1], e.next_values assert_equal [1,2], e.next_values end assert 'Enumerator#peek' do a = [1] e = a.each assert_equal 1, e.peek assert_equal 1, e.peek assert_equal 1, e.next assert_raise(StopIteration) { e.peek } assert_raise(StopIteration) { e.peek } end assert 'Enumerator#peek modify' do o = Object.new def o.each yield 1,2 end e = o.to_enum a = e.peek a << 3 assert_equal([1,2], e.peek) end assert 'Enumerator#peek_values' do o = Object.new def o.each yield yield 1 yield 1, 2 end e = o.to_enum assert_equal nil, e.peek assert_equal nil, e.next assert_equal 1, e.peek assert_equal 1, e.next assert_equal [1,2], e.peek assert_equal [1,2], e.next e = o.to_enum assert_equal [], e.peek_values assert_equal [], e.next_values assert_equal [1], e.peek_values assert_equal [1], e.next_values assert_equal [1,2], e.peek_values assert_equal [1,2], e.next_values e = o.to_enum assert_equal [], e.peek_values assert_equal nil, e.next assert_equal [1], e.peek_values assert_equal 1, e.next assert_equal [1,2], e.peek_values assert_equal [1,2], e.next e = o.to_enum assert_equal nil, e.peek assert_equal [], e.next_values assert_equal 1, e.peek assert_equal [1], e.next_values assert_equal [1,2], e.peek assert_equal [1,2], e.next_values end assert 'Enumerator#peek_values modify' do o = Object.new def o.each yield 1,2 end e = o.to_enum a = e.peek_values a << 3 assert_equal [1,2], e.peek end assert 'Enumerator#feed' do o = Object.new def o.each(ary) ary << yield ary << yield ary << yield end ary = [] e = o.to_enum :each, ary e.next e.feed 1 e.next e.feed 2 e.next e.feed 3 assert_raise(StopIteration) { e.next } assert_equal [1,2,3], ary end assert 'Enumerator#feed mixed' do o = Object.new def o.each(ary) ary << yield ary << yield ary << yield end ary = [] e = o.to_enum :each, ary e.next e.feed 1 e.next e.next e.feed 3 assert_raise(StopIteration) { e.next } assert_equal [1,nil,3], ary end assert 'Enumerator#feed twice' do o = Object.new def o.each(ary) ary << yield ary << yield ary << yield end ary = [] e = o.to_enum :each, ary e.feed 1 assert_raise(TypeError) { e.feed 2 } end assert 'Enumerator#feed before first next' do o = Object.new def o.each(ary) ary << yield ary << yield ary << yield end ary = [] e = o.to_enum :each, ary e.feed 1 e.next e.next assert_equal [1], ary end assert 'Enumerator#feed yielder' do x = nil e = Enumerator.new {|y| x = y.yield; 10 } e.next e.feed 100 assert_raise(StopIteration) { e.next } assert_equal 100, x end assert 'Enumerator#rewind' do e = @obj.to_enum(:foo, 1, 2, 3) assert_equal 1, e.next assert_equal 2, e.next e.rewind assert_equal 1, e.next assert_equal 2, e.next assert_equal 3, e.next assert_raise(StopIteration) { e.next } end assert 'Enumerator#rewind clear feed' do o = Object.new def o.each(ary) ary << yield ary << yield ary << yield end ary = [] e = o.to_enum(:each, ary) e.next e.feed 1 e.next e.feed 2 e.rewind e.next e.next assert_equal([1,nil], ary) end assert 'Enumerator#rewind clear' do o = Object.new def o.each(ary) ary << yield ary << yield ary << yield end ary = [] e = o.to_enum :each, ary e.next e.feed 1 e.next e.feed 2 e.rewind e.next e.next assert_equal [1,nil], ary end assert 'Enumerator::Generator' do # note: Enumerator::Generator is a class just for internal g = Enumerator::Generator.new {|y| y << 1 << 2 << 3; :foo } g2 = g.dup a = [] assert_equal(:foo, g.each {|x| a << x }) assert_equal([1, 2, 3], a) a = [] assert_equal(:foo, g2.each {|x| a << x }) assert_equal([1, 2, 3], a) end assert 'Enumerator::Generator args' do g = Enumerator::Generator.new {|y, x| y << 1 << 2 << 3; x } a = [] assert_equal(:bar, g.each(:bar) {|x| a << x }) assert_equal([1, 2, 3], a) end assert 'Enumerator::Yielder' do # note: Enumerator::Yielder is a class just for internal a = [] y = Enumerator::Yielder.new {|x| a << x } assert_equal(y, y << 1 << 2 << 3) assert_equal([1, 2, 3], a) a = [] y = Enumerator::Yielder.new {|x| a << x } assert_equal([1], y.yield(1)) assert_equal([1, 2], y.yield(2)) assert_equal([1, 2, 3], y.yield(3)) assert_raise(LocalJumpError) { Enumerator::Yielder.new } end assert 'next after StopIteration' do a = [1] e = a.each assert_equal(1, e.next) assert_raise(StopIteration) { e.next } assert_raise(StopIteration) { e.next } e.rewind assert_equal(1, e.next) assert_raise(StopIteration) { e.next } assert_raise(StopIteration) { e.next } end assert 'gc' do assert_nothing_raised do 1.times do foo = [1,2,3].to_enum GC.start end GC.start end end assert 'nested iteration' do def (o = Object.new).each yield :ok1 yield [:ok2, :x].each.next end e = o.to_enum assert_equal :ok1, e.next assert_equal :ok2, e.next assert_raise(StopIteration) { e.next } end assert 'Kernel#to_enum' do assert_equal Enumerator, [].to_enum.class assert_raise(ArgumentError){ nil.to_enum } end assert 'modifying existing methods' do assert_equal Enumerator, loop.class e = 3.times i = 0 loop_ret = loop { assert_equal i, e.next i += 1 } end assert 'Integral#times' do a = 3 b = a.times c = [] b.with_object(c) do |i, obj| obj << i end assert_equal 3, a assert_equal Enumerator, b.class assert_equal [0,1,2], c end assert 'Enumerable#each_with_index' do assert_equal [['a',0],['b',1],['c',2]], ['a','b','c'].each_with_index.to_a end assert 'Enumerable#map' do a = [1,2,3] b = a.map c = b.with_index do |i, index| [i*i, index*index] end assert_equal [1,2,3], a assert_equal [[1,0],[4,1],[9,4]], c end assert 'Enumerable#find_all' do assert_equal [[3,4]], [[1,2],[3,4],[5,6]].find_all.each{ |i| i[1] == 4 } end assert 'Array#each_index' do a = [1,2,3] b = a.each_index c = [] b.with_index do |index1,index2| c << [index1+2,index2+5] end assert_equal [1,2,3], a assert_equal [[2,5],[3,6],[4,7]], c end assert 'Array#map!' do a = [1,2,3] b = a.map! b.with_index do |i, index| [i*i, index*index] end assert_equal [[1,0],[4,1],[9,4]], a end assert 'Hash#each' do a = {a:1,b:2} b = a.each c = [] b.each do |k,v| c << [k,v] end assert_equal [[:a,1], [:b,2]], c.sort end assert 'Hash#each_key' do assert_equal [:a,:b], {a:1,b:2}.each_key.to_a.sort end assert 'Hash#each_value' do assert_equal [1,2], {a:1,b:2}.each_value.to_a.sort end assert 'Hash#select' do h = {1=>2,3=>4,5=>6} hret = h.select.with_index {|a,_b| a[1] == 4} assert_equal({3=>4}, hret) assert_equal({1=>2,3=>4,5=>6}, h) end assert 'Hash#select!' do h = {1=>2,3=>4,5=>6} hret = h.select!.with_index {|a,_b| a[1] == 4} assert_equal h, hret assert_equal({3=>4}, h) end assert 'Hash#reject' do h = {1=>2,3=>4,5=>6} hret = h.reject.with_index {|a,_b| a[1] == 4} assert_equal({1=>2,5=>6}, hret) assert_equal({1=>2,3=>4,5=>6}, h) end assert 'Hash#reject!' do h = {1=>2,3=>4,5=>6} hret = h.reject!.with_index {|a,_b| a[1] == 4} assert_equal h, hret assert_equal({1=>2,5=>6}, h) end assert 'Range#each' do a = (1..5) b = a.each c = [] b.each do |i| c << i end assert_equal [1,2,3,4,5], c end assert 'Enumerable#zip' do assert_equal [[1, 10], [2, 11], [3, 12]], [1,2,3].zip(10..Float::INFINITY) ret = [] assert_equal nil, [1,2,3].zip(10..Float::INFINITY) { |i| ret << i } assert_equal [[1, 10], [2, 11], [3, 12]], ret assert_raise(TypeError) { [1].zip(1) } end mruby-2.0.0/mrbgems/mruby-error/000077500000000000000000000000001340361412400165665ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-error/mrbgem.rake000066400000000000000000000006011340361412400207000ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-error') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'extensional error handling' if build.cxx_exception_enabled? @objs << build.compile_as_cxx("#{spec.dir}/src/exception.c", "#{spec.build_dir}/src/exception.cxx") @objs.delete_if { |v| v == objfile("#{spec.build_dir}/src/exception") } end end mruby-2.0.0/mrbgems/mruby-error/src/000077500000000000000000000000001340361412400173555ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-error/src/exception.c000066400000000000000000000044261340361412400215250ustar00rootroot00000000000000#include #include #include MRB_API mrb_value mrb_protect(mrb_state *mrb, mrb_func_t body, mrb_value data, mrb_bool *state) { struct mrb_jmpbuf *prev_jmp = mrb->jmp; struct mrb_jmpbuf c_jmp; mrb_value result = mrb_nil_value(); if (state) { *state = FALSE; } MRB_TRY(&c_jmp) { mrb->jmp = &c_jmp; result = body(mrb, data); mrb->jmp = prev_jmp; } MRB_CATCH(&c_jmp) { mrb->jmp = prev_jmp; result = mrb_obj_value(mrb->exc); mrb->exc = NULL; if (state) { *state = TRUE; } } MRB_END_EXC(&c_jmp); mrb_gc_protect(mrb, result); return result; } MRB_API mrb_value mrb_ensure(mrb_state *mrb, mrb_func_t body, mrb_value b_data, mrb_func_t ensure, mrb_value e_data) { struct mrb_jmpbuf *prev_jmp = mrb->jmp; struct mrb_jmpbuf c_jmp; mrb_value result; MRB_TRY(&c_jmp) { mrb->jmp = &c_jmp; result = body(mrb, b_data); mrb->jmp = prev_jmp; } MRB_CATCH(&c_jmp) { mrb->jmp = prev_jmp; ensure(mrb, e_data); MRB_THROW(mrb->jmp); /* rethrow catched exceptions */ } MRB_END_EXC(&c_jmp); ensure(mrb, e_data); mrb_gc_protect(mrb, result); return result; } MRB_API mrb_value mrb_rescue(mrb_state *mrb, mrb_func_t body, mrb_value b_data, mrb_func_t rescue, mrb_value r_data) { return mrb_rescue_exceptions(mrb, body, b_data, rescue, r_data, 1, &mrb->eStandardError_class); } MRB_API mrb_value mrb_rescue_exceptions(mrb_state *mrb, mrb_func_t body, mrb_value b_data, mrb_func_t rescue, mrb_value r_data, mrb_int len, struct RClass **classes) { struct mrb_jmpbuf *prev_jmp = mrb->jmp; struct mrb_jmpbuf c_jmp; mrb_value result; mrb_bool error_matched = FALSE; mrb_int i; MRB_TRY(&c_jmp) { mrb->jmp = &c_jmp; result = body(mrb, b_data); mrb->jmp = prev_jmp; } MRB_CATCH(&c_jmp) { mrb->jmp = prev_jmp; for (i = 0; i < len; ++i) { if (mrb_obj_is_kind_of(mrb, mrb_obj_value(mrb->exc), classes[i])) { error_matched = TRUE; break; } } if (!error_matched) { MRB_THROW(mrb->jmp); } mrb->exc = NULL; result = rescue(mrb, r_data); } MRB_END_EXC(&c_jmp); mrb_gc_protect(mrb, result); return result; } void mrb_mruby_error_gem_init(mrb_state *mrb) { } void mrb_mruby_error_gem_final(mrb_state *mrb) { } mruby-2.0.0/mrbgems/mruby-error/test/000077500000000000000000000000001340361412400175455ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-error/test/exception.c000066400000000000000000000030451340361412400217110ustar00rootroot00000000000000#include #include #include static mrb_value protect_cb(mrb_state *mrb, mrb_value b) { return mrb_yield_argv(mrb, b, 0, NULL); } static mrb_value run_protect(mrb_state *mrb, mrb_value self) { mrb_value b; mrb_value ret[2]; mrb_bool state; mrb_get_args(mrb, "&", &b); ret[0] = mrb_protect(mrb, protect_cb, b, &state); ret[1] = mrb_bool_value(state); return mrb_ary_new_from_values(mrb, 2, ret); } static mrb_value run_ensure(mrb_state *mrb, mrb_value self) { mrb_value b, e; mrb_get_args(mrb, "oo", &b, &e); return mrb_ensure(mrb, protect_cb, b, protect_cb, e); } static mrb_value run_rescue(mrb_state *mrb, mrb_value self) { mrb_value b, r; mrb_get_args(mrb, "oo", &b, &r); return mrb_rescue(mrb, protect_cb, b, protect_cb, r); } static mrb_value run_rescue_exceptions(mrb_state *mrb, mrb_value self) { mrb_value b, r; struct RClass *cls[1]; mrb_get_args(mrb, "oo", &b, &r); cls[0] = E_TYPE_ERROR; return mrb_rescue_exceptions(mrb, protect_cb, b, protect_cb, r, 1, cls); } void mrb_mruby_error_gem_test(mrb_state *mrb) { struct RClass *cls; cls = mrb_define_class(mrb, "ExceptionTest", mrb->object_class); mrb_define_module_function(mrb, cls, "mrb_protect", run_protect, MRB_ARGS_NONE() | MRB_ARGS_BLOCK()); mrb_define_module_function(mrb, cls, "mrb_ensure", run_ensure, MRB_ARGS_REQ(2)); mrb_define_module_function(mrb, cls, "mrb_rescue", run_rescue, MRB_ARGS_REQ(2)); mrb_define_module_function(mrb, cls, "mrb_rescue_exceptions", run_rescue_exceptions, MRB_ARGS_REQ(2)); } mruby-2.0.0/mrbgems/mruby-error/test/exception.rb000066400000000000000000000026471340361412400221010ustar00rootroot00000000000000assert 'mrb_protect' do # no failure in protect returns [result, false] assert_equal ['test', false] do ExceptionTest.mrb_protect { 'test' } end # failure in protect returns [exception, true] result = ExceptionTest.mrb_protect { raise 'test' } assert_kind_of RuntimeError, result[0] assert_true result[1] end assert 'mrb_ensure' do a = false assert_equal 'test' do ExceptionTest.mrb_ensure Proc.new { 'test' }, Proc.new { a = true } end assert_true a a = false assert_raise RuntimeError do ExceptionTest.mrb_ensure Proc.new { raise 'test' }, Proc.new { a = true } end assert_true a end assert 'mrb_rescue' do assert_equal 'test' do ExceptionTest.mrb_rescue Proc.new { 'test' }, Proc.new {} end class CustomExp < Exception end assert_raise CustomExp do ExceptionTest.mrb_rescue Proc.new { raise CustomExp.new 'test' }, Proc.new { 'rescue' } end assert_equal 'rescue' do ExceptionTest.mrb_rescue Proc.new { raise 'test' }, Proc.new { 'rescue' } end end assert 'mrb_rescue_exceptions' do assert_equal 'test' do ExceptionTest.mrb_rescue_exceptions Proc.new { 'test' }, Proc.new {} end assert_raise RangeError do ExceptionTest.mrb_rescue_exceptions Proc.new { raise RangeError.new 'test' }, Proc.new { 'rescue' } end assert_equal 'rescue' do ExceptionTest.mrb_rescue_exceptions Proc.new { raise TypeError.new 'test' }, Proc.new { 'rescue' } end end mruby-2.0.0/mrbgems/mruby-eval/000077500000000000000000000000001340361412400163645ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-eval/mrbgem.rake000066400000000000000000000003421340361412400205000ustar00rootroot00000000000000MRuby::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 mruby-2.0.0/mrbgems/mruby-eval/src/000077500000000000000000000000001340361412400171535ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-eval/src/eval.c000066400000000000000000000236441340361412400202570ustar00rootroot00000000000000#include #include #include #include #include #include #include mrb_value mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p); mrb_value mrb_obj_instance_eval(mrb_state *mrb, mrb_value self); static struct mrb_irep * get_closure_irep(mrb_state *mrb, int level) { struct RProc *proc = mrb->c->ci[-1].proc; while (level--) { if (!proc) return NULL; proc = proc->upper; } if (!proc) return NULL; if (MRB_PROC_CFUNC_P(proc)) { return NULL; } return proc->body.irep; } /* search for irep lev above the bottom */ static mrb_irep* search_irep(mrb_irep *top, int bnest, int lev, mrb_irep *bottom) { int i; for (i=0; irlen; i++) { mrb_irep* tmp = top->reps[i]; if (tmp == bottom) return top; tmp = search_irep(tmp, bnest-1, lev, bottom); if (tmp) { if (bnest == lev) return top; return tmp; } } return NULL; } static uint16_t search_variable(mrb_state *mrb, mrb_sym vsym, int bnest) { mrb_irep *virep; int level; int pos; for (level = 0; (virep = get_closure_irep(mrb, level)); level++) { if (virep->lv == NULL) { continue; } for (pos = 0; pos < virep->nlocals - 1; pos++) { if (vsym == virep->lv[pos].name) { return (pos+1)<<8 | (level+bnest); } } } return 0; } static int irep_argc(mrb_irep *irep) { mrb_code c; c = irep->iseq[0]; if (c == OP_ENTER) { mrb_aspec ax = PEEK_W(irep->iseq+1); /* extra 1 means a slot for block */ return MRB_ASPEC_REQ(ax)+MRB_ASPEC_OPT(ax)+MRB_ASPEC_REST(ax)+MRB_ASPEC_POST(ax)+1; } return 0; } static mrb_bool potential_upvar_p(struct mrb_locals *lv, uint16_t v, int argc, uint16_t nlocals) { if (v >= nlocals) return FALSE; /* skip arguments */ if (v < argc+1) return FALSE; return TRUE; } extern uint8_t mrb_insn_size[]; extern uint8_t mrb_insn_size1[]; extern uint8_t mrb_insn_size2[]; extern uint8_t mrb_insn_size3[]; static void patch_irep(mrb_state *mrb, mrb_irep *irep, int bnest, mrb_irep *top) { int i; uint32_t a; uint16_t b; uint8_t c; mrb_code insn; int argc = irep_argc(irep); for (i = 0; i < irep->ilen; ) { insn = irep->iseq[i]; switch(insn){ case OP_EPUSH: b = PEEK_S(irep->iseq+i+1); patch_irep(mrb, irep->reps[b], bnest + 1, top); break; case OP_LAMBDA: case OP_BLOCK: a = PEEK_B(irep->iseq+i+1); b = PEEK_B(irep->iseq+i+2); patch_irep(mrb, irep->reps[b], bnest + 1, top); break; case OP_SEND: b = PEEK_B(irep->iseq+i+2); c = PEEK_B(irep->iseq+i+3); if (c != 0) { break; } else { uint16_t arg = search_variable(mrb, irep->syms[b], bnest); if (arg != 0) { /* must replace */ irep->iseq[i] = OP_GETUPVAR; irep->iseq[i+2] = arg >> 8; irep->iseq[i+3] = arg & 0xff; } } break; case OP_MOVE: a = PEEK_B(irep->iseq+i+1); b = PEEK_B(irep->iseq+i+2); /* src part */ if (potential_upvar_p(irep->lv, b, argc, irep->nlocals)) { uint16_t arg = search_variable(mrb, irep->lv[b - 1].name, bnest); if (arg != 0) { /* must replace */ irep->iseq[i] = insn = OP_GETUPVAR; irep->iseq[i+2] = arg >> 8; irep->iseq[i+3] = arg & 0xff; } } /* dst part */ if (potential_upvar_p(irep->lv, a, argc, irep->nlocals)) { uint16_t arg = search_variable(mrb, irep->lv[a - 1].name, bnest); if (arg != 0) { /* must replace */ irep->iseq[i] = insn = OP_SETUPVAR; irep->iseq[i+1] = (mrb_code)b; irep->iseq[i+2] = arg >> 8; irep->iseq[i+3] = arg & 0xff; } } break; case OP_GETUPVAR: a = PEEK_B(irep->iseq+i+1); b = PEEK_B(irep->iseq+i+2); c = PEEK_B(irep->iseq+i+3); { int lev = c+1; mrb_irep *tmp = search_irep(top, bnest, lev, irep); if (potential_upvar_p(tmp->lv, b, irep_argc(tmp), tmp->nlocals)) { uint16_t arg = search_variable(mrb, tmp->lv[b-1].name, bnest); if (arg != 0) { /* must replace */ irep->iseq[i] = OP_GETUPVAR; irep->iseq[i+2] = arg >> 8; irep->iseq[i+3] = arg & 0xff; } } } break; case OP_SETUPVAR: a = PEEK_B(irep->iseq+i+1); b = PEEK_B(irep->iseq+i+2); c = PEEK_B(irep->iseq+i+3); { int lev = c+1; mrb_irep *tmp = search_irep(top, bnest, lev, irep); if (potential_upvar_p(tmp->lv, b, irep_argc(tmp), tmp->nlocals)) { uint16_t arg = search_variable(mrb, tmp->lv[b-1].name, bnest); if (arg != 0) { /* must replace */ irep->iseq[i] = OP_SETUPVAR; irep->iseq[i+1] = a; irep->iseq[i+2] = arg >> 8; irep->iseq[i+3] = arg & 0xff; } } } break; case OP_EXT1: insn = PEEK_B(irep->iseq+i+1); i += mrb_insn_size1[insn]+1; continue; case OP_EXT2: insn = PEEK_B(irep->iseq+i+1); i += mrb_insn_size2[insn]+1; continue; case OP_EXT3: insn = PEEK_B(irep->iseq+i+1); i += mrb_insn_size3[insn]+1; continue; } i+=mrb_insn_size[insn]; } } void mrb_codedump_all(mrb_state*, struct RProc*); static struct RProc* create_proc_from_string(mrb_state *mrb, char *s, mrb_int len, mrb_value binding, const char *file, mrb_int line) { mrbc_context *cxt; struct mrb_parser_state *p; struct RProc *proc; struct REnv *e; mrb_callinfo *ci; /* callinfo of eval caller */ struct RClass *target_class = NULL; int bidx; if (!mrb_nil_p(binding)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "Binding of eval must be nil."); } cxt = mrbc_context_new(mrb); cxt->lineno = (short)line; mrbc_filename(mrb, cxt, file ? file : "(eval)"); cxt->capture_errors = TRUE; cxt->no_optimize = TRUE; cxt->on_eval = TRUE; p = mrb_parse_nstring(mrb, s, len, cxt); /* only occur when memory ran out */ if (!p) { mrb_raise(mrb, E_RUNTIME_ERROR, "Failed to create parser state."); } if (0 < p->nerr) { /* parse error */ mrb_value str; if (file) { str = mrb_format(mrb, " file %S line %S: %S", mrb_str_new_cstr(mrb, file), mrb_fixnum_value(p->error_buffer[0].lineno), mrb_str_new_cstr(mrb, p->error_buffer[0].message)); } else { str = mrb_format(mrb, " line %S: %S", mrb_fixnum_value(p->error_buffer[0].lineno), mrb_str_new_cstr(mrb, p->error_buffer[0].message)); } mrb_parser_free(p); mrbc_context_free(mrb, cxt); mrb_exc_raise(mrb, mrb_exc_new_str(mrb, E_SYNTAX_ERROR, str)); } proc = mrb_generate_code(mrb, p); if (proc == NULL) { /* codegen error */ mrb_parser_free(p); mrbc_context_free(mrb, cxt); mrb_raise(mrb, E_SCRIPT_ERROR, "codegen error"); } if (mrb->c->ci > mrb->c->cibase) { ci = &mrb->c->ci[-1]; } else { ci = mrb->c->cibase; } if (ci->proc) { target_class = MRB_PROC_TARGET_CLASS(ci->proc); } if (ci->proc && !MRB_PROC_CFUNC_P(ci->proc)) { if (ci->env) { e = ci->env; } else { e = (struct REnv*)mrb_obj_alloc(mrb, MRB_TT_ENV, (struct RClass*)target_class); e->mid = ci->mid; e->stack = ci[1].stackent; e->cxt = mrb->c; MRB_ENV_SET_STACK_LEN(e, ci->proc->body.irep->nlocals); bidx = ci->argc; if (ci->argc < 0) bidx = 2; else bidx += 1; MRB_ENV_SET_BIDX(e, bidx); ci->env = e; } proc->e.env = e; proc->flags |= MRB_PROC_ENVSET; mrb_field_write_barrier(mrb, (struct RBasic*)proc, (struct RBasic*)e); } proc->upper = ci->proc; mrb->c->ci->target_class = target_class; patch_irep(mrb, proc->body.irep, 0, proc->body.irep); /* mrb_codedump_all(mrb, proc); */ mrb_parser_free(p); mrbc_context_free(mrb, cxt); return proc; } static mrb_value exec_irep(mrb_state *mrb, mrb_value self, struct RProc *proc) { /* no argument passed from eval() */ mrb->c->ci->argc = 0; if (mrb->c->ci->acc < 0) { ptrdiff_t cioff = mrb->c->ci - mrb->c->cibase; mrb_value ret = mrb_top_run(mrb, proc, self, 0); if (mrb->exc) { mrb_exc_raise(mrb, mrb_obj_value(mrb->exc)); } mrb->c->ci = mrb->c->cibase + cioff; return ret; } /* clear block */ mrb->c->stack[1] = mrb_nil_value(); return mrb_exec_irep(mrb, self, proc); } static mrb_value f_eval(mrb_state *mrb, mrb_value self) { char *s; mrb_int len; mrb_value binding = mrb_nil_value(); char *file = NULL; mrb_int line = 1; struct RProc *proc; mrb_get_args(mrb, "s|ozi", &s, &len, &binding, &file, &line); proc = create_proc_from_string(mrb, s, len, binding, file, line); mrb_assert(!MRB_PROC_CFUNC_P(proc)); return exec_irep(mrb, self, proc); } static mrb_value f_instance_eval(mrb_state *mrb, mrb_value self) { mrb_value b; mrb_int argc; mrb_value *argv; mrb_get_args(mrb, "*!&", &argv, &argc, &b); if (mrb_nil_p(b)) { char *s; mrb_int len; char *file = NULL; mrb_int line = 1; mrb_value cv; struct RProc *proc; mrb_get_args(mrb, "s|zi", &s, &len, &file, &line); cv = mrb_singleton_class(mrb, self); proc = create_proc_from_string(mrb, s, len, mrb_nil_value(), file, line); MRB_PROC_SET_TARGET_CLASS(proc, mrb_class_ptr(cv)); mrb_assert(!MRB_PROC_CFUNC_P(proc)); mrb->c->ci->target_class = mrb_class_ptr(cv); return exec_irep(mrb, self, proc); } else { mrb_get_args(mrb, "&", &b); return mrb_obj_instance_eval(mrb, self); } } void mrb_mruby_eval_gem_init(mrb_state* mrb) { mrb_define_module_function(mrb, mrb->kernel_module, "eval", f_eval, MRB_ARGS_ARG(1, 3)); mrb_define_method(mrb, mrb->kernel_module, "instance_eval", f_instance_eval, MRB_ARGS_ARG(1, 2)); } void mrb_mruby_eval_gem_final(mrb_state* mrb) { } mruby-2.0.0/mrbgems/mruby-eval/test/000077500000000000000000000000001340361412400173435ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-eval/test/eval.rb000066400000000000000000000055551340361412400206310ustar00rootroot00000000000000assert('Kernel.eval', '15.3.1.2.3') do assert_equal(10) { Kernel.eval '1 * 10' } assert_equal('aaa') { Kernel.eval "'a' * 3" } assert_equal(10) { a = 10 Kernel.eval "a" } assert_equal(20) { a = 10 Kernel.eval "a = 20" a } assert_equal(15) { c = 5 lambda { a = 10 Kernel.eval "c = a + c" }.call c } assert_equal(5) { c = 5 lambda { Kernel.eval 'lambda { c }.call' }.call } assert_equal(15) { c = 5 lambda { a = 10 Kernel.eval 'lambda { c = a + c }.call' }.call c } assert_equal(2) { a = 10 Kernel.eval 'def f(a); b=a+1; end' f(1) } end assert('Kernel#eval', '15.3.1.3.12') do assert_equal(10) { eval '1 * 10' } end assert('rest arguments of eval') do assert_raise(ArgumentError) { Kernel.eval('0', 0, 'test', 0) } assert_equal ['test', 'test.rb', 10] do Kernel.eval('[\'test\', __FILE__, __LINE__]', nil, 'test.rb', 10) end end assert 'eval syntax error' do assert_raise(SyntaxError) do eval 'p "test' end end assert('String instance_eval') do obj = Object.new obj.instance_eval{ @test = 'test' } assert_raise(ArgumentError) { obj.instance_eval(0) { } } assert_raise(ArgumentError) { obj.instance_eval('0', 'test', 0, 'test') } assert_equal(['test.rb', 10]) { obj.instance_eval('[__FILE__, __LINE__]', 'test.rb', 10)} assert_equal('test') { obj.instance_eval('@test') } assert_equal('test') { obj.instance_eval { @test } } o = Object.new assert_equal ['', o, o], o.instance_eval("[''].each { |s| break [s, o, self] }") end assert('Kernel.#eval(string) context') do class TestEvalConstScope EVAL_CONST_CLASS = 'class' def const_string eval 'EVAL_CONST_CLASS' end end obj = TestEvalConstScope.new assert_raise(NameError) { eval 'EVAL_CONST_CLASS' } assert_equal('class') { obj.const_string } end assert('Object#instance_eval with begin-rescue-ensure execution order') do class HellRaiser def raise_hell order = [:enter_raise_hell] begin order.push :begin self.instance_eval("raise 'error'") rescue order.push :rescue ensure order.push :ensure end order end end hell_raiser = HellRaiser.new assert_equal([:enter_raise_hell, :begin, :rescue, :ensure], hell_raiser.raise_hell) end assert('Kernel#instance_eval() to define singleton methods Issue #3141') do foo_class = Class.new do def bar(x) instance_eval "def baz; #{x}; end" end end f1 = foo_class.new f2 = foo_class.new f1.bar 1 f2.bar 2 assert_equal(1){f1.baz} assert_equal(2){f2.baz} end assert('Kernel.#eval(string) Issue #4021') do assert_equal('FOO') { (eval <<'EOS').call } foo = "FOO" Proc.new { foo } EOS assert_equal('FOO') { def do_eval(code) eval(code) end do_eval(<<'EOS').call foo = "FOO" Proc.new { foo } EOS } end mruby-2.0.0/mrbgems/mruby-exit/000077500000000000000000000000001340361412400164065ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-exit/mrbgem.rake000066400000000000000000000002331340361412400205210ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-exit') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Kernel#exit method' end mruby-2.0.0/mrbgems/mruby-exit/src/000077500000000000000000000000001340361412400171755ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-exit/src/mruby-exit.c000066400000000000000000000006221340361412400214460ustar00rootroot00000000000000#include #include static mrb_value f_exit(mrb_state *mrb, mrb_value self) { mrb_int i = EXIT_SUCCESS; mrb_get_args(mrb, "|i", &i); exit((int)i); /* not reached */ return mrb_nil_value(); } void mrb_mruby_exit_gem_init(mrb_state* mrb) { mrb_define_method(mrb, mrb->kernel_module, "exit", f_exit, MRB_ARGS_OPT(1)); } void mrb_mruby_exit_gem_final(mrb_state* mrb) { } mruby-2.0.0/mrbgems/mruby-fiber/000077500000000000000000000000001340361412400165245ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-fiber/mrbgem.rake000066400000000000000000000002251340361412400206400ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-fiber') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Fiber class' end mruby-2.0.0/mrbgems/mruby-fiber/src/000077500000000000000000000000001340361412400173135ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-fiber/src/fiber.c000066400000000000000000000257761340361412400205670ustar00rootroot00000000000000#include #include #include #include #define fiber_ptr(o) ((struct RFiber*)mrb_ptr(o)) #define FIBER_STACK_INIT_SIZE 64 #define FIBER_CI_INIT_SIZE 8 #define CI_ACC_RESUMED -3 /* * call-seq: * Fiber.new{...} -> obj * * Creates a fiber, whose execution is suspend until it is explicitly * resumed using Fiber#resume method. * The code running inside the fiber can give up control by calling * Fiber.yield in which case it yields control back to caller * (the caller of the Fiber#resume). * * Upon yielding or termination the Fiber returns the value of the last * executed expression * * For instance: * * fiber = Fiber.new do * Fiber.yield 1 * 2 * end * * puts fiber.resume * puts fiber.resume * puts fiber.resume * * produces * * 1 * 2 * resuming dead fiber (FiberError) * * The Fiber#resume method accepts an arbitrary number of * parameters, if it is the first call to resume then they * will be passed as block arguments. Otherwise they will be the return * value of the call to Fiber.yield * * Example: * * fiber = Fiber.new do |first| * second = Fiber.yield first + 2 * end * * puts fiber.resume 10 * puts fiber.resume 14 * puts fiber.resume 18 * * produces * * 12 * 14 * resuming dead fiber (FiberError) * */ static mrb_value fiber_init(mrb_state *mrb, mrb_value self) { static const struct mrb_context mrb_context_zero = { 0 }; struct RFiber *f = fiber_ptr(self); struct mrb_context *c; struct RProc *p; mrb_callinfo *ci; mrb_value blk; size_t slen; mrb_get_args(mrb, "&", &blk); if (f->cxt) { mrb_raise(mrb, E_RUNTIME_ERROR, "cannot initialize twice"); } if (mrb_nil_p(blk)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "tried to create Fiber object without a block"); } p = mrb_proc_ptr(blk); if (MRB_PROC_CFUNC_P(p)) { mrb_raise(mrb, E_FIBER_ERROR, "tried to create Fiber from C defined method"); } c = (struct mrb_context*)mrb_malloc(mrb, sizeof(struct mrb_context)); *c = mrb_context_zero; f->cxt = c; /* initialize VM stack */ slen = FIBER_STACK_INIT_SIZE; if (p->body.irep->nregs > slen) { slen += p->body.irep->nregs; } c->stbase = (mrb_value *)mrb_malloc(mrb, slen*sizeof(mrb_value)); c->stend = c->stbase + slen; c->stack = c->stbase; #ifdef MRB_NAN_BOXING { mrb_value *p = c->stbase; mrb_value *pend = c->stend; while (p < pend) { SET_NIL_VALUE(*p); p++; } } #else memset(c->stbase, 0, slen * sizeof(mrb_value)); #endif /* copy receiver from a block */ c->stack[0] = mrb->c->stack[0]; /* initialize callinfo stack */ c->cibase = (mrb_callinfo *)mrb_calloc(mrb, FIBER_CI_INIT_SIZE, sizeof(mrb_callinfo)); c->ciend = c->cibase + FIBER_CI_INIT_SIZE; c->ci = c->cibase; c->ci->stackent = c->stack; /* adjust return callinfo */ ci = c->ci; ci->target_class = MRB_PROC_TARGET_CLASS(p); ci->proc = p; mrb_field_write_barrier(mrb, (struct RBasic*)mrb_obj_ptr(self), (struct RBasic*)p); ci->pc = p->body.irep->iseq; ci[1] = ci[0]; c->ci++; /* push dummy callinfo */ c->fib = f; c->status = MRB_FIBER_CREATED; return self; } static struct mrb_context* fiber_check(mrb_state *mrb, mrb_value fib) { struct RFiber *f = fiber_ptr(fib); mrb_assert(f->tt == MRB_TT_FIBER); if (!f->cxt) { mrb_raise(mrb, E_FIBER_ERROR, "uninitialized Fiber"); } return f->cxt; } static mrb_value fiber_result(mrb_state *mrb, const mrb_value *a, mrb_int len) { if (len == 0) return mrb_nil_value(); if (len == 1) return a[0]; return mrb_ary_new_from_values(mrb, len, a); } /* mark return from context modifying method */ #define MARK_CONTEXT_MODIFY(c) (c)->ci->target_class = NULL static void fiber_check_cfunc(mrb_state *mrb, struct mrb_context *c) { mrb_callinfo *ci; for (ci = c->ci; ci >= c->cibase; ci--) { if (ci->acc < 0) { mrb_raise(mrb, E_FIBER_ERROR, "can't cross C function boundary"); } } } static void fiber_switch_context(mrb_state *mrb, struct mrb_context *c) { if (mrb->c->fib) { mrb_write_barrier(mrb, (struct RBasic*)mrb->c->fib); } c->status = MRB_FIBER_RUNNING; mrb->c = c; } static mrb_value fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mrb_bool resume, mrb_bool vmexec) { struct mrb_context *c = fiber_check(mrb, self); struct mrb_context *old_c = mrb->c; enum mrb_fiber_state status; mrb_value value; fiber_check_cfunc(mrb, c); status = c->status; if (resume && status == MRB_FIBER_TRANSFERRED) { mrb_raise(mrb, E_FIBER_ERROR, "resuming transferred fiber"); } if (status == MRB_FIBER_RUNNING || status == MRB_FIBER_RESUMED) { mrb_raise(mrb, E_FIBER_ERROR, "double resume"); } if (status == MRB_FIBER_TERMINATED) { mrb_raise(mrb, E_FIBER_ERROR, "resuming dead fiber"); } old_c->status = resume ? MRB_FIBER_RESUMED : MRB_FIBER_TRANSFERRED; c->prev = resume ? mrb->c : (c->prev ? c->prev : mrb->root_c); fiber_switch_context(mrb, c); if (status == MRB_FIBER_CREATED) { mrb_value *b, *e; if (!c->ci->proc) { mrb_raise(mrb, E_FIBER_ERROR, "double resume (current)"); } mrb_stack_extend(mrb, len+2); /* for receiver and (optional) block */ b = c->stack+1; e = b + len; while (bcibase->argc = (int)len; value = c->stack[0] = MRB_PROC_ENV(c->ci->proc)->stack[0]; } else { value = fiber_result(mrb, a, len); } if (vmexec) { c->vmexec = TRUE; value = mrb_vm_exec(mrb, c->ci[-1].proc, c->ci->pc); mrb->c = old_c; } else { MARK_CONTEXT_MODIFY(c); } return value; } /* * call-seq: * fiber.resume(args, ...) -> obj * * Resumes the fiber from the point at which the last Fiber.yield * was called, or starts running it if it is the first call to * resume. Arguments passed to resume will be the value of * the Fiber.yield expression or will be passed as block * parameters to the fiber's block if this is the first resume. * * Alternatively, when resume is called it evaluates to the arguments passed * to the next Fiber.yield statement inside the fiber's block * or to the block value if it runs to completion without any * Fiber.yield */ static mrb_value fiber_resume(mrb_state *mrb, mrb_value self) { mrb_value *a; mrb_int len; mrb_bool vmexec = FALSE; mrb_get_args(mrb, "*!", &a, &len); if (mrb->c->ci->acc < 0) { vmexec = TRUE; } return fiber_switch(mrb, self, len, a, TRUE, vmexec); } /* resume thread with given arguments */ MRB_API mrb_value mrb_fiber_resume(mrb_state *mrb, mrb_value fib, mrb_int len, const mrb_value *a) { return fiber_switch(mrb, fib, len, a, TRUE, TRUE); } /* * call-seq: * fiber.alive? -> true or false * * Returns true if the fiber can still be resumed. After finishing * execution of the fiber block this method will always return false. */ MRB_API mrb_value mrb_fiber_alive_p(mrb_state *mrb, mrb_value self) { struct mrb_context *c = fiber_check(mrb, self); return mrb_bool_value(c->status != MRB_FIBER_TERMINATED); } #define fiber_alive_p mrb_fiber_alive_p static mrb_value fiber_eq(mrb_state *mrb, mrb_value self) { mrb_value other; mrb_get_args(mrb, "o", &other); if (mrb_type(other) != MRB_TT_FIBER) { return mrb_false_value(); } return mrb_bool_value(fiber_ptr(self) == fiber_ptr(other)); } /* * call-seq: * fiber.transfer(args, ...) -> obj * * Transfers control to receiver fiber of the method call. * Unlike resume the receiver wouldn't be pushed to call * stack of fibers. Instead it will switch to the call stack of * transferring fiber. * When resuming a fiber that was transferred to another fiber it would * cause double resume error. Though when the fiber is re-transferred * and Fiber.yield is called, the fiber would be resumable. */ static mrb_value fiber_transfer(mrb_state *mrb, mrb_value self) { struct mrb_context *c = fiber_check(mrb, self); mrb_value* a; mrb_int len; fiber_check_cfunc(mrb, mrb->c); mrb_get_args(mrb, "*!", &a, &len); if (c == mrb->root_c) { mrb->c->status = MRB_FIBER_TRANSFERRED; fiber_switch_context(mrb, c); MARK_CONTEXT_MODIFY(c); return fiber_result(mrb, a, len); } if (c == mrb->c) { return fiber_result(mrb, a, len); } return fiber_switch(mrb, self, len, a, FALSE, FALSE); } /* yield values to the caller fiber */ /* mrb_fiber_yield() must be called as `return mrb_fiber_yield(...)` */ MRB_API mrb_value mrb_fiber_yield(mrb_state *mrb, mrb_int len, const mrb_value *a) { struct mrb_context *c = mrb->c; if (!c->prev) { mrb_raise(mrb, E_FIBER_ERROR, "can't yield from root fiber"); } fiber_check_cfunc(mrb, c); c->prev->status = MRB_FIBER_RUNNING; c->status = MRB_FIBER_SUSPENDED; fiber_switch_context(mrb, c->prev); c->prev = NULL; if (c->vmexec) { c->vmexec = FALSE; mrb->c->ci->acc = CI_ACC_RESUMED; } MARK_CONTEXT_MODIFY(mrb->c); return fiber_result(mrb, a, len); } /* * call-seq: * Fiber.yield(args, ...) -> obj * * Yields control back to the context that resumed the fiber, passing * along any arguments that were passed to it. The fiber will resume * processing at this point when resume is called next. * Any arguments passed to the next resume will be the * * mruby limitation: Fiber resume/yield cannot cross C function boundary. * thus you cannot yield from #initialize which is called by mrb_funcall(). */ static mrb_value fiber_yield(mrb_state *mrb, mrb_value self) { mrb_value *a; mrb_int len; mrb_get_args(mrb, "*!", &a, &len); return mrb_fiber_yield(mrb, len, a); } /* * call-seq: * Fiber.current() -> fiber * * Returns the current fiber. If you are not running in the context of * a fiber this method will return the root fiber. */ static mrb_value fiber_current(mrb_state *mrb, mrb_value self) { if (!mrb->c->fib) { struct RFiber *f = (struct RFiber*)mrb_obj_alloc(mrb, MRB_TT_FIBER, mrb_class_ptr(self)); f->cxt = mrb->c; mrb->c->fib = f; } return mrb_obj_value(mrb->c->fib); } void mrb_mruby_fiber_gem_init(mrb_state* mrb) { struct RClass *c; c = mrb_define_class(mrb, "Fiber", mrb->object_class); MRB_SET_INSTANCE_TT(c, MRB_TT_FIBER); mrb_define_method(mrb, c, "initialize", fiber_init, MRB_ARGS_NONE()); mrb_define_method(mrb, c, "resume", fiber_resume, MRB_ARGS_ANY()); mrb_define_method(mrb, c, "transfer", fiber_transfer, MRB_ARGS_ANY()); mrb_define_method(mrb, c, "alive?", fiber_alive_p, MRB_ARGS_NONE()); mrb_define_method(mrb, c, "==", fiber_eq, MRB_ARGS_REQ(1)); mrb_define_class_method(mrb, c, "yield", fiber_yield, MRB_ARGS_ANY()); mrb_define_class_method(mrb, c, "current", fiber_current, MRB_ARGS_NONE()); mrb_define_class(mrb, "FiberError", mrb->eStandardError_class); } void mrb_mruby_fiber_gem_final(mrb_state* mrb) { } mruby-2.0.0/mrbgems/mruby-fiber/test/000077500000000000000000000000001340361412400175035ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-fiber/test/fiber.rb000066400000000000000000000076221340361412400211260ustar00rootroot00000000000000assert('Fiber.new') do f = Fiber.new{} assert_kind_of Fiber, f end assert('Fiber#resume') do f = Fiber.new{|x| x } assert_equal 2, f.resume(2) end assert('Fiber#transfer') do f2 = nil f1 = Fiber.new do |v| Fiber.yield v f2.transfer end f2 = Fiber.new do f1.transfer(1) f1.transfer(1) Fiber.yield 2 end assert_equal 1, f2.resume assert_raise(FiberError) { f2.resume } assert_equal 2, f2.transfer assert_raise(FiberError) { f1.resume } f1.transfer f2.resume assert_false f1.alive? assert_false f2.alive? end assert('Fiber#alive?') do f = Fiber.new{ Fiber.yield } f.resume assert_true f.alive? f.resume assert_false f.alive? end assert('Fiber#==') do root = Fiber.current assert_equal root, root assert_equal root, Fiber.current assert_false root != Fiber.current f = Fiber.new { assert_false root == Fiber.current } f.resume assert_false f == root assert_true f != root end assert('Fiber.yield') do f = Fiber.new{|x| Fiber.yield x } assert_equal 3, f.resume(3) assert_true f.alive? end assert('FiberError') do assert_equal StandardError, FiberError.superclass end assert('Fiber iteration') do f1 = Fiber.new{ [1,2,3].each{|x| Fiber.yield(x)} } f2 = Fiber.new{ [9,8,7].each{|x| Fiber.yield(x)} } a = [] 3.times { a << f1.resume a << f2.resume } assert_equal [1,9,2,8,3,7], a end assert('Fiber with splat in the block argument list') { Fiber.new{|*x|x}.resume(1) == [1] } assert('Fiber raises on resume when dead') do assert_raise(FiberError) do f = Fiber.new{} f.resume assert_false f.alive? f.resume end end assert('Yield raises when called on root fiber') do assert_raise(FiberError) { Fiber.yield } end assert('Double resume of Fiber') do f1 = Fiber.new {} f2 = Fiber.new { f1.resume assert_raise(FiberError) { f2.resume } Fiber.yield 0 } assert_equal 0, f2.resume f2.resume assert_false f1.alive? assert_false f2.alive? end assert('Recursive resume of Fiber') do f1, f2 = nil, nil f1 = Fiber.new { assert_raise(FiberError) { f2.resume } } f2 = Fiber.new { f1.resume Fiber.yield 0 } f3 = Fiber.new { f2.resume } assert_equal 0, f3.resume f2.resume assert_false f1.alive? assert_false f2.alive? assert_false f3.alive? end assert('Root fiber resume') do root = Fiber.current assert_raise(FiberError) { root.resume } f = Fiber.new { assert_raise(FiberError) { root.resume } } f.resume assert_false f.alive? end assert('Fiber without block') do assert_raise(ArgumentError) { Fiber.new } end assert('Transfer to self.') do result = [] f = Fiber.new { result << :start; f.transfer; result << :end } f.transfer assert_equal [:start, :end], result result = [] f = Fiber.new { result << :start; f.transfer; result << :end } f.resume assert_equal [:start, :end], result end assert('Resume transferred fiber') do f = Fiber.new { assert_raise(FiberError) { f.resume } } f.transfer end assert('Root fiber transfer.') do result = nil root = Fiber.current f = Fiber.new { result = :ok root.transfer } f.resume assert_true f.alive? assert_equal :ok, result end assert('Break nested fiber with root fiber transfer') do root = Fiber.current result = nil f2 = nil f1 = Fiber.new { Fiber.yield f2.resume result = :f1 } f2 = Fiber.new { result = :to_root root.transfer :from_f2 result = :f2 } assert_equal :from_f2, f1.resume assert_equal :to_root, result assert_equal :f2, f2.transfer assert_equal :f2, result assert_false f2.alive? assert_equal :f1, f1.resume assert_equal :f1, result assert_false f1.alive? end assert('CRuby Fiber#transfer test.') do ary = [] f2 = nil f1 = Fiber.new{ ary << f2.transfer(:foo) :ok } f2 = Fiber.new{ ary << f1.transfer(:baz) :ng } assert_equal :ok, f1.transfer assert_equal [:baz], ary end mruby-2.0.0/mrbgems/mruby-hash-ext/000077500000000000000000000000001340361412400171565ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-hash-ext/mrbgem.rake000066400000000000000000000005511340361412400212740ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-hash-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Hash class extension' spec.add_dependency 'mruby-enum-ext', core: 'mruby-enum-ext' spec.add_dependency 'mruby-array-ext', core: 'mruby-array-ext' spec.add_test_dependency 'mruby-enumerator', core: 'mruby-enumerator' end mruby-2.0.0/mrbgems/mruby-hash-ext/mrblib/000077500000000000000000000000001340361412400204255ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-hash-ext/mrblib/hash.rb000066400000000000000000000313551340361412400217040ustar00rootroot00000000000000class Hash # ISO does not define Hash#each_pair, so each_pair is defined in gem. alias each_pair each ## # call-seq: # Hash[ key, value, ... ] -> new_hash # Hash[ [ [key, value], ... ] ] -> new_hash # Hash[ object ] -> new_hash # # Creates a new hash populated with the given objects. # # Similar to the literal `{ _key_ => _value_, ... }`. In the first # form, keys and values occur in pairs, so there must be an even number of # arguments. # # The second and third form take a single argument which is either an array # of key-value pairs or an object convertible to a hash. # # Hash["a", 100, "b", 200] #=> {"a"=>100, "b"=>200} # Hash[ [ ["a", 100], ["b", 200] ] ] #=> {"a"=>100, "b"=>200} # Hash["a" => 100, "b" => 200] #=> {"a"=>100, "b"=>200} # def self.[](*object) length = object.length if length == 1 o = object[0] if Hash === o h = self.new o.each { |k, v| h[k] = v } return h elsif o.respond_to?(:to_a) h = self.new o.to_a.each do |i| raise ArgumentError, "wrong element type #{i.class} (expected array)" unless i.respond_to?(:to_a) k, v = nil case i.size when 2 k = i[0] v = i[1] when 1 k = i[0] else raise ArgumentError, "invalid number of elements (#{i.size} for 1..2)" end h[k] = v end return h end end unless length % 2 == 0 raise ArgumentError, 'odd number of arguments for Hash' end h = self.new 0.step(length - 2, 2) do |i| h[object[i]] = object[i + 1] end h end ## # call-seq: # hsh.merge!(other_hash) -> hsh # hsh.merge!(other_hash){|key, oldval, newval| block} -> hsh # # Adds the contents of _other_hash_ to _hsh_. If no block is specified, # entries with duplicate keys are overwritten with the values from # _other_hash_, otherwise the value of each duplicate key is determined by # calling the block with the key, its value in _hsh_ and its value in # _other_hash_. # # h1 = { "a" => 100, "b" => 200 } # h2 = { "b" => 254, "c" => 300 } # h1.merge!(h2) #=> {"a"=>100, "b"=>254, "c"=>300} # # h1 = { "a" => 100, "b" => 200 } # h2 = { "b" => 254, "c" => 300 } # h1.merge!(h2) { |key, v1, v2| v1 } # #=> {"a"=>100, "b"=>200, "c"=>300} # def merge!(other, &block) raise TypeError, "Hash required (#{other.class} given)" unless Hash === other if block other.each_key{|k| self[k] = (self.has_key?(k))? block.call(k, self[k], other[k]): other[k] } else other.each_key{|k| self[k] = other[k]} end self end alias update merge! ## # call-seq: # hsh.compact! -> hsh # # Removes all nil values from the hash. Returns the hash. # Returns nil if the hash does not contain nil values. # # h = { a: 1, b: false, c: nil } # h.compact! #=> { a: 1, b: false } # def compact! keys = self.keys nk = keys.select{|k| self[k] != nil } return nil if (keys.size == nk.size) h = {} nk.each {|k| h[k] = self[k] } h self.replace(h) end ## # call-seq: # hsh.compact -> new_hsh # # Returns a new hash with the nil values/key pairs removed # # h = { a: 1, b: false, c: nil } # h.compact #=> { a: 1, b: false } # h #=> { a: 1, b: false, c: nil } # def compact h = {} self.keys.select{|k| self[k] != nil }.each {|k| h[k] = self[k] } h end ## # call-seq: # hsh.fetch(key [, default] ) -> obj # hsh.fetch(key) {| key | block } -> obj # # Returns a value from the hash for the given key. If the key can't be # found, there are several options: With no other arguments, it will # raise an KeyError exception; if default is # given, then that will be returned; if the optional code block is # specified, then that will be run and its result returned. # # h = { "a" => 100, "b" => 200 } # h.fetch("a") #=> 100 # h.fetch("z", "go fish") #=> "go fish" # h.fetch("z") { |el| "go fish, #{el}"} #=> "go fish, z" # # The following example shows that an exception is raised if the key # is not found and a default value is not supplied. # # h = { "a" => 100, "b" => 200 } # h.fetch("z") # # produces: # # prog.rb:2:in 'fetch': key not found (KeyError) # from prog.rb:2 # def fetch(key, none=NONE, &block) unless self.key?(key) if block block.call(key) elsif none != NONE none else raise KeyError, "Key not found: #{key.inspect}" end else self[key] end end ## # call-seq: # hsh.delete_if {| key, value | block } -> hsh # hsh.delete_if -> an_enumerator # # Deletes every key-value pair from hsh for which block # evaluates to true. # # If no block is given, an enumerator is returned instead. # # h = { "a" => 100, "b" => 200, "c" => 300 } # h.delete_if {|key, value| key >= "b" } #=> {"a"=>100} # def delete_if(&block) return to_enum :delete_if unless block self.each do |k, v| self.delete(k) if block.call(k, v) end self end ## # call-seq: # hash.flatten -> an_array # hash.flatten(level) -> an_array # # Returns a new array that is a one-dimensional flattening of this # hash. That is, for every key or value that is an array, extract # its elements into the new array. Unlike Array#flatten, this # method does not flatten recursively by default. The optional # level argument determines the level of recursion to flatten. # # a = {1=> "one", 2 => [2,"two"], 3 => "three"} # a.flatten # => [1, "one", 2, [2, "two"], 3, "three"] # a.flatten(2) # => [1, "one", 2, 2, "two", 3, "three"] # def flatten(level=1) self.to_a.flatten(level) end ## # call-seq: # hsh.invert -> new_hash # # Returns a new hash created by using hsh's values as keys, and # the keys as values. # # h = { "n" => 100, "m" => 100, "y" => 300, "d" => 200, "a" => 0 } # h.invert #=> {0=>"a", 100=>"m", 200=>"d", 300=>"y"} # def invert h = self.class.new self.each {|k, v| h[v] = k } h end ## # call-seq: # hsh.keep_if {| key, value | block } -> hsh # hsh.keep_if -> an_enumerator # # Deletes every key-value pair from hsh for which block # evaluates to false. # # If no block is given, an enumerator is returned instead. # def keep_if(&block) return to_enum :keep_if unless block keys = [] self.each do |k, v| unless block.call([k, v]) self.delete(k) end end self end ## # call-seq: # hsh.key(value) -> key # # Returns the key of an occurrence of a given value. If the value is # not found, returns nil. # # h = { "a" => 100, "b" => 200, "c" => 300, "d" => 300 } # h.key(200) #=> "b" # h.key(300) #=> "c" # h.key(999) #=> nil # def key(val) self.each do |k, v| return k if v == val end nil end ## # call-seq: # hsh.to_h -> hsh or new_hash # # Returns +self+. If called on a subclass of Hash, converts # the receiver to a Hash object. # def to_h self end ## # call-seq: # hash < other -> true or false # # Returns true if hash is subset of # other. # # h1 = {a:1, b:2} # h2 = {a:1, b:2, c:3} # h1 < h2 #=> true # h2 < h1 #=> false # h1 < h1 #=> false # def <(hash) raise TypeError, "can't convert #{hash.class} to Hash" unless Hash === hash size < hash.size and all? {|key, val| hash.key?(key) and hash[key] == val } end ## # call-seq: # hash <= other -> true or false # # Returns true if hash is subset of # other or equals to other. # # h1 = {a:1, b:2} # h2 = {a:1, b:2, c:3} # h1 <= h2 #=> true # h2 <= h1 #=> false # h1 <= h1 #=> true # def <=(hash) raise TypeError, "can't convert #{hash.class} to Hash" unless Hash === hash size <= hash.size and all? {|key, val| hash.key?(key) and hash[key] == val } end ## # call-seq: # hash > other -> true or false # # Returns true if other is subset of # hash. # # h1 = {a:1, b:2} # h2 = {a:1, b:2, c:3} # h1 > h2 #=> false # h2 > h1 #=> true # h1 > h1 #=> false # def >(hash) raise TypeError, "can't convert #{hash.class} to Hash" unless Hash === hash size > hash.size and hash.all? {|key, val| key?(key) and self[key] == val } end ## # call-seq: # hash >= other -> true or false # # Returns true if other is subset of # hash or equals to hash. # # h1 = {a:1, b:2} # h2 = {a:1, b:2, c:3} # h1 >= h2 #=> false # h2 >= h1 #=> true # h1 >= h1 #=> true # def >=(hash) raise TypeError, "can't convert #{hash.class} to Hash" unless Hash === hash size >= hash.size and hash.all? {|key, val| key?(key) and self[key] == val } end ## # call-seq: # hsh.dig(key,...) -> object # # Extracts the nested value specified by the sequence of key # objects by calling +dig+ at each step, returning +nil+ if any # intermediate step is +nil+. # def dig(idx,*args) n = self[idx] if args.size > 0 n&.dig(*args) else n end end ## # call-seq: # hsh.transform_keys {|key| block } -> new_hash # hsh.transform_keys -> an_enumerator # # Returns a new hash, with the keys computed from running the block # once for each key in the hash, and the values unchanged. # # If no block is given, an enumerator is returned instead. # def transform_keys(&block) return to_enum :transform_keys unless block hash = {} self.keys.each do |k| new_key = block.call(k) hash[new_key] = self[k] end hash end ## # call-seq: # hsh.transform_keys! {|key| block } -> hsh # hsh.transform_keys! -> an_enumerator # # Invokes the given block once for each key in hsh, replacing it # with the new key returned by the block, and then returns hsh. # # If no block is given, an enumerator is returned instead. # def transform_keys!(&block) return to_enum :transform_keys! unless block self.keys.each do |k| value = self[k] self.__delete(k) k = block.call(k) if block self[k] = value end self end ## # call-seq: # hsh.transform_values {|value| block } -> new_hash # hsh.transform_values -> an_enumerator # # Returns a new hash with the results of running the block once for # every value. # This method does not change the keys. # # If no block is given, an enumerator is returned instead. # def transform_values(&b) return to_enum :transform_values unless block_given? hash = {} self.keys.each do |k| hash[k] = yield(self[k]) end hash end ## # call-seq: # hsh.transform_values! {|key| block } -> hsh # hsh.transform_values! -> an_enumerator # # Invokes the given block once for each value in the hash, replacing # with the new value returned by the block, and then returns hsh. # # If no block is given, an enumerator is returned instead. # def transform_values!(&b) return to_enum :transform_values! unless block_given? self.keys.each do |k| self[k] = yield(self[k]) end self end def to_proc ->x{self[x]} end ## # call-seq: # hsh.fetch_values(key, ...) -> array # hsh.fetch_values(key, ...) { |key| block } -> array # # Returns an array containing the values associated with the given keys # but also raises KeyError when one of keys can't be found. # Also see Hash#values_at and Hash#fetch. # # h = { "cat" => "feline", "dog" => "canine", "cow" => "bovine" } # # h.fetch_values("cow", "cat") #=> ["bovine", "feline"] # h.fetch_values("cow", "bird") # raises KeyError # h.fetch_values("cow", "bird") { |k| k.upcase } #=> ["bovine", "BIRD"] # def fetch_values(*keys, &block) keys.map do |k| self.fetch(k, &block) end end end mruby-2.0.0/mrbgems/mruby-hash-ext/src/000077500000000000000000000000001340361412400177455ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-hash-ext/src/hash-ext.c000066400000000000000000000035351340361412400216400ustar00rootroot00000000000000/* ** hash.c - Hash class ** ** See Copyright Notice in mruby.h */ #include #include #include /* * call-seq: * hsh.values_at(key, ...) -> array * * Return an array containing the values associated with the given keys. * Also see Hash.select. * * h = { "cat" => "feline", "dog" => "canine", "cow" => "bovine" } * h.values_at("cow", "cat") #=> ["bovine", "feline"] */ static mrb_value hash_values_at(mrb_state *mrb, mrb_value hash) { mrb_value *argv, result; mrb_int argc, i; int ai; mrb_get_args(mrb, "*", &argv, &argc); result = mrb_ary_new_capa(mrb, argc); ai = mrb_gc_arena_save(mrb); for (i = 0; i < argc; i++) { mrb_ary_push(mrb, result, mrb_hash_get(mrb, hash, argv[i])); mrb_gc_arena_restore(mrb, ai); } return result; } /* * call-seq: * hsh.slice(*keys) -> a_hash * * Returns a hash containing only the given keys and their values. * * h = { a: 100, b: 200, c: 300 } * h.slice(:a) #=> {:a=>100} * h.slice(:b, :c, :d) #=> {:b=>200, :c=>300} */ static mrb_value hash_slice(mrb_state *mrb, mrb_value hash) { mrb_value *argv, result; mrb_int argc, i; mrb_get_args(mrb, "*", &argv, &argc); if (argc == 0) { return mrb_hash_new_capa(mrb, argc); } result = mrb_hash_new_capa(mrb, argc); for (i = 0; i < argc; i++) { mrb_value key = argv[i]; mrb_value val; val = mrb_hash_fetch(mrb, hash, key, mrb_undef_value()); if (!mrb_undef_p(val)) { mrb_hash_set(mrb, result, key, val); } } return result; } void mrb_mruby_hash_ext_gem_init(mrb_state *mrb) { struct RClass *h; h = mrb->hash_class; mrb_define_method(mrb, h, "values_at", hash_values_at, MRB_ARGS_ANY()); mrb_define_method(mrb, h, "slice", hash_slice, MRB_ARGS_ANY()); } void mrb_mruby_hash_ext_gem_final(mrb_state *mrb) { } mruby-2.0.0/mrbgems/mruby-hash-ext/test/000077500000000000000000000000001340361412400201355ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-hash-ext/test/hash.rb000066400000000000000000000157001340361412400214100ustar00rootroot00000000000000## # Hash(Ext) Test assert('Hash.[] Hash') do a = Hash['a_key' => 'a_value'] assert_equal({'a_key' => 'a_value'}, a) end assert('Hash.[] [ [ ["b_key", "b_value" ] ] ]') do a = Hash[ [ ['b_key', 'b_value'] ] ] assert_equal({'b_key' => 'b_value'}, a) a = Hash[ [ ] ] assert_equal({}, a) assert_raise(ArgumentError) do Hash[ [ ['b_key', 'b_value', 'b_over'] ] ] end assert_raise(ArgumentError) do Hash[ [ [] ] ] end end assert('Hash.[] "c_key", "c_value"') do a = Hash['c_key', 'c_value', 'd_key', 1] assert_equal({'c_key' => 'c_value', 'd_key' => 1}, a) a = Hash[] assert_equal({}, a) assert_raise(ArgumentError) do Hash['d_key'] end end assert('Hash.[] for sub class') do sub_hash_class = Class.new(Hash) sub_hash = sub_hash_class[] assert_equal(sub_hash_class, sub_hash.class) end assert('Hash#merge!') do a = { 'abc_key' => 'abc_value', 'cba_key' => 'cba_value' } b = { 'cba_key' => 'XXX', 'xyz_key' => 'xyz_value' } result_1 = a.merge! b a = { 'abc_key' => 'abc_value', 'cba_key' => 'cba_value' } result_2 = a.merge!(b) do |key, original, new| original end assert_equal({'abc_key' => 'abc_value', 'cba_key' => 'XXX', 'xyz_key' => 'xyz_value' }, result_1) assert_equal({'abc_key' => 'abc_value', 'cba_key' => 'cba_value', 'xyz_key' => 'xyz_value' }, result_2) assert_raise(TypeError) do { 'abc_key' => 'abc_value' }.merge! "a" end end assert('Hash#values_at') do h = { "cat" => "feline", "dog" => "canine", "cow" => "bovine" } assert_equal ["bovine", "feline"], h.values_at("cow", "cat") keys = [] (0...1000).each { |v| keys.push "#{v}" } h = Hash.new { |hash,k| hash[k] = k } assert_equal keys, h.values_at(*keys) end assert('Hash#compact') do h = { "cat" => "feline", "dog" => nil, "cow" => false } assert_equal({ "cat" => "feline", "cow" => false }, h.compact) assert_equal({ "cat" => "feline", "dog" => nil, "cow" => false }, h) end assert('Hash#compact!') do h = { "cat" => "feline", "dog" => nil, "cow" => false } h.compact! assert_equal({ "cat" => "feline", "cow" => false }, h) end assert('Hash#fetch') do h = { "cat" => "feline", "dog" => "canine", "cow" => "bovine" } assert_equal "feline", h.fetch("cat") assert_equal "mickey", h.fetch("mouse", "mickey") assert_equal "minny", h.fetch("mouse"){"minny"} assert_equal "mouse", h.fetch("mouse"){|k| k} assert_raise(KeyError) do h.fetch("gnu") end end assert("Hash#delete_if") do base = { 1 => 'one', 2 => false, true => 'true', 'cat' => 99 } h1 = { 1 => 'one', 2 => false, true => 'true' } h2 = { 2 => false, 'cat' => 99 } h3 = { 2 => false } h = base.dup assert_equal(h, h.delete_if { false }) assert_equal({}, h.delete_if { true }) h = base.dup assert_equal(h1, h.delete_if {|k,v| k.instance_of?(String) }) assert_equal(h1, h) h = base.dup assert_equal(h2, h.delete_if {|k,v| v.instance_of?(String) }) assert_equal(h2, h) h = base.dup assert_equal(h3, h.delete_if {|k,v| v }) assert_equal(h3, h) h = base.dup n = 0 h.delete_if {|*a| n += 1 assert_equal(2, a.size) assert_equal(base[a[0]], a[1]) h.shift true } assert_equal(base.size, n) end assert("Hash#flatten") do a = {1=> "one", 2 => [2,"two"], 3 => [3, ["three"]]} assert_equal [1, "one", 2, [2, "two"], 3, [3, ["three"]]], a.flatten assert_equal [[1, "one"], [2, [2, "two"]], [3, [3, ["three"]]]], a.flatten(0) assert_equal [1, "one", 2, [2, "two"], 3, [3, ["three"]]], a.flatten(1) assert_equal [1, "one", 2, 2, "two", 3, 3, ["three"]], a.flatten(2) assert_equal [1, "one", 2, 2, "two", 3, 3, "three"], a.flatten(3) end assert("Hash#invert") do h = { 1 => 'one', 2 => 'two', 3 => 'three', true => 'true', nil => 'nil' }.invert assert_equal 1, h['one'] assert_equal true, h['true'] assert_equal nil, h['nil'] h = { 'a' => 1, 'b' => 2, 'c' => 1 }.invert assert_equal(2, h.length) assert_include(%w[a c], h[1]) assert_equal('b', h[2]) end assert("Hash#invert with sub class") do sub_hash_class = Class.new(Hash) sub_hash = sub_hash_class.new assert_equal(sub_hash_class, sub_hash.invert.class) end assert("Hash#keep_if") do h = { 1 => 2, 3 => 4, 5 => 6 } assert_equal({3=>4,5=>6}, h.keep_if {|k, v| k + v >= 7 }) h = { 1 => 2, 3 => 4, 5 => 6 } assert_equal({ 1 => 2, 3=> 4, 5 =>6} , h.keep_if { true }) end assert("Hash#key") do h = { "a" => 100, "b" => 200, "c" => 300, "d" => 300, nil => 'nil', 'nil' => nil } assert_equal "b", h.key(200) assert_equal "c", h.key(300) assert_nil h.key(999) assert_nil h.key('nil') assert_equal 'nil', h.key(nil) end assert("Hash#to_h") do h = { "a" => 100, "b" => 200 } assert_equal Hash, h.to_h.class assert_equal h, h.to_h end assert('Hash#<') do h1 = {a:1, b:2} h2 = {a:1, b:2, c:3} assert_false(h1 < h1) assert_true(h1 < h2) assert_false(h2 < h1) assert_false(h2 < h2) h1 = {a:1} h2 = {a:2} assert_false(h1 < h1) assert_false(h1 < h2) assert_false(h2 < h1) assert_false(h2 < h2) end assert('Hash#<=') do h1 = {a:1, b:2} h2 = {a:1, b:2, c:3} assert_true(h1 <= h1) assert_true(h1 <= h2) assert_false(h2 <= h1) assert_true(h2 <= h2) h1 = {a:1} h2 = {a:2} assert_true(h1 <= h1) assert_false(h1 <= h2) assert_false(h2 <= h1) assert_true(h2 <= h2) end assert('Hash#>=') do h1 = {a:1, b:2} h2 = {a:1, b:2, c:3} assert_true(h1 >= h1) assert_false(h1 >= h2) assert_true(h2 >= h1) assert_true(h2 >= h2) h1 = {a:1} h2 = {a:2} assert_true(h1 >= h1) assert_false(h1 >= h2) assert_false(h2 >= h1) assert_true(h2 >= h2) end assert('Hash#>') do h1 = {a:1, b:2} h2 = {a:1, b:2, c:3} assert_false(h1 > h1) assert_false(h1 > h2) assert_true(h2 > h1) assert_false(h2 > h2) h1 = {a:1} h2 = {a:2} assert_false(h1 > h1) assert_false(h1 > h2) assert_false(h2 > h1) assert_false(h2 > h2) end assert("Hash#dig") do h = {a:{b:{c:1}}} assert_equal(1, h.dig(:a, :b, :c)) assert_nil(h.dig(:d)) end assert("Hash#transform_keys") do h = {"1" => 100, "2" => 200} assert_equal({"1!" => 100, "2!" => 200}, h.transform_keys{|k| k+"!"}) assert_equal({1 => 100, 2 => 200}, h.transform_keys{|k|k.to_i}) assert_equal({"1.0" => 100, "2.1" => 200}, h.transform_keys.with_index{|k, i| "#{k}.#{i}"}) assert_equal(h, h.transform_keys!{|k|k.to_i}) assert_equal(h, {1 => 100, 2 => 200}) end assert("Hash#transform_values") do h = {a: 1, b: 2, c: 3} assert_equal({a: 2, b: 5, c: 10}, h.transform_values{|v| v * v + 1}) assert_equal({a: "1", b: "2", c: "3"}, h.transform_values{|v|v.to_s}) assert_equal({a: "1.0", b: "2.1", c: "3.2"}, h.transform_values.with_index{|v, i| "#{v}.#{i}"}) assert_equal(h, h.transform_values!{|v|v.to_s}) assert_equal({a: "1", b: "2", c: "3"}, h) end assert("Hash#slice") do h = { a: 100, b: 200, c: 300 } assert_equal({:a=>100}, h.slice(:a)) assert_equal({:b=>200, :c=>300}, h.slice(:b, :c, :d)) end mruby-2.0.0/mrbgems/mruby-inline-struct/000077500000000000000000000000001340361412400202355ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-inline-struct/mrbgem.rake000066400000000000000000000002421340361412400223500ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-inline-struct') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'inline structure' end mruby-2.0.0/mrbgems/mruby-inline-struct/test/000077500000000000000000000000001340361412400212145ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-inline-struct/test/inline.c000066400000000000000000000044531340361412400226440ustar00rootroot00000000000000#include #include #include #include static mrb_value istruct_test_initialize(mrb_state *mrb, mrb_value self) { char *string = (char*)mrb_istruct_ptr(self); mrb_int size = mrb_istruct_size(); mrb_value object; mrb_get_args(mrb, "o", &object); if (mrb_float_p(object)) { strncpy(string, "float", size-1); } else if (mrb_fixnum_p(object)) { strncpy(string, "fixnum", size-1); } else if (mrb_string_p(object)) { strncpy(string, "string", size-1); } else { strncpy(string, "anything", size-1); } string[size - 1] = 0; // force NULL at the end return self; } static mrb_value istruct_test_to_s(mrb_state *mrb, mrb_value self) { return mrb_str_new_cstr(mrb, (const char*)mrb_istruct_ptr(self)); } static mrb_value istruct_test_length(mrb_state *mrb, mrb_value self) { return mrb_fixnum_value(mrb_istruct_size()); } static mrb_value istruct_test_test_receive(mrb_state *mrb, mrb_value self) { mrb_value object; mrb_get_args(mrb, "o", &object); if (mrb_obj_class(mrb, object) != mrb_class_get(mrb, "InlineStructTest")) { mrb_raise(mrb, E_TYPE_ERROR, "Expected InlineStructTest"); } return mrb_bool_value(((char*)mrb_istruct_ptr(object))[0] == 's'); } static mrb_value istruct_test_test_receive_direct(mrb_state *mrb, mrb_value self) { char *ptr; mrb_get_args(mrb, "I", &ptr); return mrb_bool_value(ptr[0] == 's'); } static mrb_value istruct_test_mutate(mrb_state *mrb, mrb_value self) { char *ptr = (char*)mrb_istruct_ptr(self); memcpy(ptr, "mutate", 6); return mrb_nil_value(); } void mrb_mruby_inline_struct_gem_test(mrb_state *mrb) { struct RClass *cls; cls = mrb_define_class(mrb, "InlineStructTest", mrb->object_class); MRB_SET_INSTANCE_TT(cls, MRB_TT_ISTRUCT); mrb_define_method(mrb, cls, "initialize", istruct_test_initialize, MRB_ARGS_REQ(1)); mrb_define_method(mrb, cls, "to_s", istruct_test_to_s, MRB_ARGS_NONE()); mrb_define_method(mrb, cls, "mutate", istruct_test_mutate, MRB_ARGS_NONE()); mrb_define_class_method(mrb, cls, "length", istruct_test_length, MRB_ARGS_NONE()); mrb_define_class_method(mrb, cls, "test_receive", istruct_test_test_receive, MRB_ARGS_REQ(1)); mrb_define_class_method(mrb, cls, "test_receive_direct", istruct_test_test_receive_direct, MRB_ARGS_REQ(1)); } mruby-2.0.0/mrbgems/mruby-inline-struct/test/inline.rb000066400000000000000000000054441340361412400230260ustar00rootroot00000000000000## # InlineStruct Test class InlineStructTest def extra_method :ok end def test_ivar_set @var = :ivar end def test_ivar_get @vat end end assert('InlineStructTest#dup') do obj = InlineStructTest.new(1) assert_equal obj.to_s, 'fixnum' assert_equal obj.dup.to_s, 'fixnum' end assert('InlineStructTest#clone') do obj = InlineStructTest.new(1) assert_equal obj.to_s, 'fixnum' assert_equal obj.clone.to_s, 'fixnum' end assert('InlineStruct#object_id') do obj1 = InlineStructTest.new(1) obj2 = InlineStructTest.new(1) assert_not_equal obj1, obj2 assert_not_equal obj1.object_id, obj2.object_id assert_not_equal obj1.object_id, obj1.dup.object_id assert_not_equal obj1.object_id, obj1.clone.object_id end assert('InlineStructTest#mutate (dup)') do obj1 = InlineStructTest.new("foo") assert_equal obj1.to_s, "string" obj2 = obj1.dup assert_equal obj2.to_s, "string" obj1.mutate assert_equal obj1.to_s, "mutate" assert_equal obj2.to_s, "string" end assert('InlineStructTest#mutate (clone)') do obj1 = InlineStructTest.new("foo") assert_equal obj1.to_s, "string" obj2 = obj1.clone assert_equal obj2.to_s, "string" obj1.mutate assert_equal obj1.to_s, "mutate" assert_equal obj2.to_s, "string" end assert('InlineStructTest#test_receive(string)') do assert_equal InlineStructTest.test_receive(InlineStructTest.new('a')), true end assert('InlineStructTest#test_receive(float)') do assert_equal InlineStructTest.test_receive(InlineStructTest.new(1.25)), false end assert('InlineStructTest#test_receive(invalid object)') do assert_raise(TypeError) do InlineStructTest.test_receive([]) end end assert('InlineStructTest#test_receive(string)') do assert_equal InlineStructTest.test_receive_direct(InlineStructTest.new('a')), true end assert('InlineStructTest#test_receive(float)') do assert_equal InlineStructTest.test_receive_direct(InlineStructTest.new(1.25)), false end assert('InlineStructTest#test_receive(invalid object)') do assert_raise(TypeError) do InlineStructTest.test_receive_direct([]) end end assert('InlineStructTest#extra_method') do assert_equal InlineStructTest.new(1).extra_method, :ok end assert('InlineStructTest instance variable') do obj = InlineStructTest.new(1) assert_raise(ArgumentError) do obj.test_ivar_set end assert_equal obj.test_ivar_get, nil end # 64-bit mode if InlineStructTest.length == 24 assert('InlineStructTest length [64 bit]') do assert_equal InlineStructTest.length, 3 * 8 end end # 32-bit mode if InlineStructTest.length == 12 assert('InlineStructTest length [32 bit]') do assert_equal InlineStructTest.length, 3 * 4 end end # 16-bit mode if InlineStructTest.length == 6 assert('InlineStructTest length [16 bit]') do assert_equal InlineStructTest.length, 3 * 2 end end mruby-2.0.0/mrbgems/mruby-io/000077500000000000000000000000001340361412400160445ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-io/.gitignore000066400000000000000000000000051340361412400200270ustar00rootroot00000000000000/tmp mruby-2.0.0/mrbgems/mruby-io/.travis.yml000066400000000000000000000000501340361412400201500ustar00rootroot00000000000000script: - "ruby run_test.rb all test" mruby-2.0.0/mrbgems/mruby-io/README.md000066400000000000000000000212671340361412400173330ustar00rootroot00000000000000mruby-io ======== [![Build Status](https://travis-ci.org/iij/mruby-io.svg?branch=master)](https://travis-ci.org/iij/mruby-io) `IO` and `File` classes for mruby ## Installation Add the line below to your `build_config.rb`: ``` conf.gem :github => 'iij/mruby-io' ``` ## Implemented methods ### IO - http://doc.ruby-lang.org/ja/1.9.3/class/IO.html | method | mruby-io | memo | | ------------------------- | -------- | ---- | | IO.binread | | | | IO.binwrite | | | | IO.copy_stream | | | | IO.new, IO.for_fd, IO.open | o | | | IO.foreach | | | | IO.pipe | o | | | IO.popen | o | | | IO.read | o | | | IO.readlines | | | | IO.select | o | | | IO.sysopen | o | | | IO.try_convert | | | | IO.write | | | | IO#<< | | | | IO#advise | | | | IO#autoclose= | | | | IO#autoclose? | | | | IO#binmode | | | | IO#binmode? | | | | IO#bytes | | obsolete | | IO#chars | | obsolete | | IO#clone, IO#dup | o | | | IO#close | o | | | IO#close_on_exec= | o | | | IO#close_on_exec? | o | | | IO#close_read | | | | IO#close_write | | | | IO#closed? | o | | | IO#codepoints | | obsolete | | IO#each_byte | o | | | IO#each_char | o | | | IO#each_codepoint | | | | IO#each_line | o | | | IO#eof, IO#eof? | o | | | IO#external_encoding | | | | IO#fcntl | | | | IO#fdatasync | | | | IO#fileno, IO#to_i | o | | | IO#flush | o | | | IO#fsync | | | | IO#getbyte | | | | IO#getc | o | | | IO#gets | o | | | IO#internal_encoding | | | | IO#ioctl | | | | IO#isatty, IO#tty? | o | | | IO#lineno | | | | IO#lineno= | | | | IO#lines | | obsolete | | IO#pid | o | | | IO#pos, IO#tell | o | | | IO#pos= | o | | | IO#print | o | | | IO#printf | o | | | IO#putc | | | | IO#puts | o | | | IO#read | o | | | IO#read_nonblock | | | | IO#readbyte | | | | IO#readchar | o | | | IO#readline | o | | | IO#readlines | o | | | IO#readpartial | | | | IO#reopen | | | | IO#rewind | | | | IO#seek | o | | | IO#set_encoding | | | | IO#stat | | | | IO#sync | o | | | IO#sync= | o | | | IO#sysread | o | | | IO#sysseek | o | | | IO#syswrite | o | | | IO#to_io | | | | IO#ungetbyte | | | | IO#ungetc | o | | | IO#write | o | | | IO#write_nonblock | | | ### File - http://doc.ruby-lang.org/ja/1.9.3/class/File.html | method | mruby-io | memo | | --------------------------- | -------- | ---- | | File.absolute_path | | | | File.atime | | | | File.basename | o | | | File.blockdev? | | FileTest | | File.chardev? | | FileTest | | File.chmod | o | | | File.chown | | | | File.ctime | | | | File.delete, File.unlink | o | | | File.directory? | o | FileTest | | File.dirname | o | | | File.executable? | | FileTest | | File.executable_real? | | FileTest | | File.exist?, exists? | o | FileTest | | File.expand_path | o | | | File.extname | o | | | File.file? | o | FileTest | | File.fnmatch, File.fnmatch? | | | | File.ftype | | | | File.grpowned? | | FileTest | | File.identical? | | FileTest | | File.join | o | | | File.lchmod | | | | File.lchown | | | | File.link | | | | File.lstat | | | | File.mtime | | | | File.new, File.open | o | | | File.owned? | | FileTest | | File.path | | | | File.pipe? | o | FileTest | | File.readable? | | FileTest | | File.readable_real? | | FileTest | | File.readlink | o | | | File.realdirpath | | | | File.realpath | o | | | File.rename | o | | | File.setgid? | | FileTest | | File.setuid? | | FileTest | | File.size | o | | | File.size? | o | FileTest | | File.socket? | o | FileTest | | File.split | | | | File.stat | | | | File.sticky? | | FileTest | | File.symlink | | | | File.symlink? | o | FileTest | | File.truncate | | | | File.umask | o | | | File.utime | | | | File.world_readable? | | | | File.world_writable? | | | | File.writable? | | FileTest | | File.writable_real? | | FileTest | | File.zero? | o | FileTest | | File#atime | | | | File#chmod | | | | File#chown | | | | File#ctime | | | | File#flock | o | | | File#lstat | | | | File#mtime | | | | File#path, File#to_path | o | | | File#size | | | | File#truncate | | | ## License Copyright (c) 2013 Internet Initiative Japan Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. mruby-2.0.0/mrbgems/mruby-io/include/000077500000000000000000000000001340361412400174675ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-io/include/mruby/000077500000000000000000000000001340361412400206255ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-io/include/mruby/ext/000077500000000000000000000000001340361412400214255ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-io/include/mruby/ext/io.h000066400000000000000000000017611340361412400222120ustar00rootroot00000000000000/* ** io.h - IO class */ #ifndef MRUBY_IO_H #define MRUBY_IO_H #if defined(__cplusplus) extern "C" { #endif struct mrb_io { int fd; /* file descriptor, or -1 */ int fd2; /* file descriptor to write if it's different from fd, or -1 */ int pid; /* child's pid (for pipes) */ unsigned int readable:1, writable:1, sync:1, is_socket:1; }; #define FMODE_READABLE 0x00000001 #define FMODE_WRITABLE 0x00000002 #define FMODE_READWRITE (FMODE_READABLE|FMODE_WRITABLE) #define FMODE_BINMODE 0x00000004 #define FMODE_APPEND 0x00000040 #define FMODE_CREATE 0x00000080 #define FMODE_TRUNC 0x00000800 #define E_IO_ERROR (mrb_class_get(mrb, "IOError")) #define E_EOF_ERROR (mrb_class_get(mrb, "EOFError")) mrb_value mrb_io_fileno(mrb_state *mrb, mrb_value io); #if defined(__cplusplus) } /* extern "C" { */ #endif #endif /* MRUBY_IO_H */ mruby-2.0.0/mrbgems/mruby-io/mrbgem.rake000066400000000000000000000011721340361412400201620ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-io') do |spec| spec.license = 'MIT' spec.authors = 'Internet Initiative Japan Inc.' spec.summary = 'IO and File class' spec.cc.include_paths << "#{build.root}/src" case RUBY_PLATFORM when /mingw|mswin/ spec.linker.libraries += ['Ws2_32'] #spec.cc.include_paths += ["C:/Windows/system/include"] spec.linker.library_paths += ["C:/Windows/system"] end if build.kind_of?(MRuby::CrossBuild) && %w(x86_64-w64-mingw32 i686-w64-mingw32).include?(build.host_target) spec.linker.libraries += ['ws2_32'] end spec.add_test_dependency 'mruby-time', core: 'mruby-time' end mruby-2.0.0/mrbgems/mruby-io/mrblib/000077500000000000000000000000001340361412400173135ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-io/mrblib/file.rb000066400000000000000000000114011340361412400205540ustar00rootroot00000000000000class File < IO class FileError < Exception; end class NoFileError < FileError; end class UnableToStat < FileError; end class PermissionError < FileError; end attr_accessor :path def initialize(fd_or_path, mode = "r", perm = 0666) if fd_or_path.kind_of? Fixnum super(fd_or_path, mode) else @path = fd_or_path fd = IO.sysopen(@path, mode, perm) super(fd, mode) end end def self.join(*names) return "" if names.empty? names.map! do |name| case name when String name when Array if names == name raise ArgumentError, "recursive array" end join(*name) else raise TypeError, "no implicit conversion of #{name.class} into String" end end return names[0] if names.size == 1 if names[0][-1] == File::SEPARATOR s = names[0][0..-2] else s = names[0].dup end (1..names.size-2).each { |i| t = names[i] if t[0] == File::SEPARATOR and t[-1] == File::SEPARATOR t = t[1..-2] elsif t[0] == File::SEPARATOR t = t[1..-1] elsif t[-1] == File::SEPARATOR t = t[0..-2] end s += File::SEPARATOR + t if t != "" } if names[-1][0] == File::SEPARATOR s += File::SEPARATOR + names[-1][1..-1] else s += File::SEPARATOR + names[-1] end s end def self.expand_path(path, default_dir = '.') def concat_path(path, base_path) if path[0] == "/" || path[1] == ':' # Windows root! expanded_path = path elsif path[0] == "~" if (path[1] == "/" || path[1] == nil) dir = path[1, path.size] home_dir = _gethome unless home_dir raise ArgumentError, "couldn't find HOME environment -- expanding '~'" end expanded_path = home_dir expanded_path += dir if dir expanded_path += "/" else splitted_path = path.split("/") user = splitted_path[0][1, splitted_path[0].size] dir = "/" + splitted_path[1, splitted_path.size].join("/") home_dir = _gethome(user) unless home_dir raise ArgumentError, "user #{user} doesn't exist" end expanded_path = home_dir expanded_path += dir if dir expanded_path += "/" end else expanded_path = concat_path(base_path, _getwd) expanded_path += "/" + path end expanded_path end expanded_path = concat_path(path, default_dir) drive_prefix = "" if File::ALT_SEPARATOR && expanded_path.size > 2 && ("A".."Z").include?(expanded_path[0].upcase) && expanded_path[1] == ":" drive_prefix = expanded_path[0, 2] expanded_path = expanded_path[2, expanded_path.size] end expand_path_array = [] if File::ALT_SEPARATOR && expanded_path.include?(File::ALT_SEPARATOR) expanded_path.gsub!(File::ALT_SEPARATOR, '/') end while expanded_path.include?('//') expanded_path = expanded_path.gsub('//', '/') end if expanded_path != "/" expanded_path.split('/').each do |path_token| if path_token == '..' if expand_path_array.size > 1 expand_path_array.pop end elsif path_token == '.' # nothing to do. else expand_path_array << path_token end end expanded_path = expand_path_array.join("/") if expanded_path.empty? expanded_path = '/' end end if drive_prefix.empty? expanded_path else drive_prefix + expanded_path.gsub("/", File::ALT_SEPARATOR) end end def self.foreach(file) if block_given? self.open(file) do |f| f.each {|l| yield l} end else return self.new(file) end end def self.directory?(file) FileTest.directory?(file) end def self.exist?(file) FileTest.exist?(file) end def self.exists?(file) FileTest.exists?(file) end def self.file?(file) FileTest.file?(file) end def self.pipe?(file) FileTest.pipe?(file) end def self.size(file) FileTest.size(file) end def self.size?(file) FileTest.size?(file) end def self.socket?(file) FileTest.socket?(file) end def self.symlink?(file) FileTest.symlink?(file) end def self.zero?(file) FileTest.zero?(file) end def self.extname(filename) fname = self.basename(filename) return '' if fname[0] == '.' || fname.index('.').nil? ext = fname.split('.').last ext.empty? ? '' : ".#{ext}" end def self.path(filename) if filename.kind_of?(String) filename elsif filename.respond_to?(:to_path) filename.to_path else raise TypeError, "no implicit conversion of #{filename.class} into String" end end end mruby-2.0.0/mrbgems/mruby-io/mrblib/file_constants.rb000066400000000000000000000006621340361412400226570ustar00rootroot00000000000000class File module Constants RDONLY = 0 WRONLY = 1 RDWR = 2 NONBLOCK = 4 APPEND = 8 BINARY = 0 SYNC = 128 NOFOLLOW = 256 CREAT = 512 TRUNC = 1024 EXCL = 2048 NOCTTY = 131072 DSYNC = 4194304 FNM_SYSCASE = 0 FNM_NOESCAPE = 1 FNM_PATHNAME = 2 FNM_DOTMATCH = 4 FNM_CASEFOLD = 8 end end class File include File::Constants end mruby-2.0.0/mrbgems/mruby-io/mrblib/io.rb000066400000000000000000000150341340361412400202520ustar00rootroot00000000000000## # IO class IOError < StandardError; end class EOFError < IOError; end class IO SEEK_SET = 0 SEEK_CUR = 1 SEEK_END = 2 BUF_SIZE = 4096 def self.open(*args, &block) io = self.new(*args) return io unless block begin yield io ensure begin io.close unless io.closed? rescue StandardError end end end def self.popen(command, mode = 'r', opts={}, &block) if !self.respond_to?(:_popen) raise NotImplementedError, "popen is not supported on this platform" end io = self._popen(command, mode, opts) return io unless block begin yield io ensure begin io.close unless io.closed? rescue IOError # nothing end end end def self.pipe(&block) if !self.respond_to?(:_pipe) raise NotImplementedError, "pipe is not supported on this platform" end if block begin r, w = IO._pipe yield r, w ensure r.close unless r.closed? w.close unless w.closed? end else IO._pipe end end def self.read(path, length=nil, offset=nil, opt=nil) if not opt.nil? # 4 arguments offset ||= 0 elsif not offset.nil? # 3 arguments if offset.is_a? Hash opt = offset offset = 0 else opt = {} end elsif not length.nil? # 2 arguments if length.is_a? Hash opt = length offset = 0 length = nil else offset = 0 opt = {} end else # only 1 argument opt = {} offset = 0 length = nil end str = "" fd = -1 io = nil begin if path[0] == "|" io = IO.popen(path[1..-1], (opt[:mode] || "r")) else mode = opt[:mode] || "r" fd = IO.sysopen(path, mode) io = IO.open(fd, mode) end io.seek(offset) if offset > 0 str = io.read(length) ensure if io io.close elsif fd != -1 IO._sysclose(fd) end end str end def flush # mruby-io always writes immediately (no output buffer). raise IOError, "closed stream" if self.closed? self end def hash # We must define IO#hash here because IO includes Enumerable and # Enumerable#hash will call IO#read... self.__id__ end def write(string) str = string.is_a?(String) ? string : string.to_s return str.size unless str.size > 0 if 0 < @buf.length # reset real pos ignore buf seek(pos, SEEK_SET) end len = syswrite(str) len end def <<(str) write(str) self end def eof? _check_readable begin buf = _read_buf return buf.size == 0 rescue EOFError return true end end alias_method :eof, :eof? def pos raise IOError if closed? sysseek(0, SEEK_CUR) - @buf.length end alias_method :tell, :pos def pos=(i) seek(i, SEEK_SET) end def rewind seek(0, SEEK_SET) end def seek(i, whence = SEEK_SET) raise IOError if closed? sysseek(i, whence) @buf = '' 0 end def _read_buf return @buf if @buf && @buf.size > 0 @buf = sysread(BUF_SIZE) end def ungetc(substr) raise TypeError.new "expect String, got #{substr.class}" unless substr.is_a?(String) if @buf.empty? @buf = substr.dup else @buf = substr + @buf end nil end def read(length = nil, outbuf = "") unless length.nil? unless length.is_a? Fixnum raise TypeError.new "can't convert #{length.class} into Integer" end if length < 0 raise ArgumentError.new "negative length: #{length} given" end if length == 0 return "" # easy case end end array = [] while 1 begin _read_buf rescue EOFError array = nil if array.empty? and (not length.nil?) and length != 0 break end if length consume = (length <= @buf.size) ? length : @buf.size array.push @buf[0, consume] @buf = @buf[consume, @buf.size - consume] length -= consume break if length == 0 else array.push @buf @buf = '' end end if array.nil? outbuf.replace("") nil else outbuf.replace(array.join) end end def readline(arg = $/, limit = nil) case arg when String rs = arg when Fixnum rs = $/ limit = arg else raise ArgumentError end if rs.nil? return read end if rs == "" rs = $/ + $/ end array = [] while 1 begin _read_buf rescue EOFError array = nil if array.empty? break end if limit && limit <= @buf.size array.push @buf[0, limit] @buf = @buf[limit, @buf.size - limit] break elsif idx = @buf.index(rs) len = idx + rs.size array.push @buf[0, len] @buf = @buf[len, @buf.size - len] break else array.push @buf @buf = '' end end raise EOFError.new "end of file reached" if array.nil? array.join end def gets(*args) begin readline(*args) rescue EOFError nil end end def readchar _read_buf c = @buf[0] @buf = @buf[1, @buf.size] c end def getc begin readchar rescue EOFError nil end end # 15.2.20.5.3 def each(&block) while line = self.gets block.call(line) end self end # 15.2.20.5.4 def each_byte(&block) while char = self.getc block.call(char) end self end # 15.2.20.5.5 alias each_line each alias each_char each_byte def readlines ary = [] while (line = gets) ary << line end ary end def puts(*args) i = 0 len = args.size while i < len s = args[i].to_s write s write "\n" if (s[-1] != "\n") i += 1 end write "\n" if len == 0 nil end def print(*args) i = 0 len = args.size while i < len write args[i].to_s i += 1 end end def printf(*args) write sprintf(*args) nil end alias_method :to_i, :fileno alias_method :tty?, :isatty end STDIN = IO.open(0, "r") STDOUT = IO.open(1, "w") STDERR = IO.open(2, "w") $stdin = STDIN $stdout = STDOUT $stderr = STDERR module Kernel def print(*args) $stdout.print(*args) end def puts(*args) $stdout.puts(*args) end def printf(*args) $stdout.printf(*args) end def gets(*args) $stdin.gets(*args) end def getc(*args) $stdin.getc(*args) end end mruby-2.0.0/mrbgems/mruby-io/mrblib/kernel.rb000066400000000000000000000004311340361412400211160ustar00rootroot00000000000000module Kernel def `(cmd) IO.popen(cmd) { |io| io.read } end def open(file, *rest, &block) raise ArgumentError unless file.is_a?(String) if file[0] == "|" IO.popen(file[1..-1], *rest, &block) else File.open(file, *rest, &block) end end end mruby-2.0.0/mrbgems/mruby-io/run_test.rb000066400000000000000000000011241340361412400202320ustar00rootroot00000000000000#!/usr/bin/env ruby # # mrbgems test runner # if __FILE__ == $0 repository, dir = 'https://github.com/mruby/mruby.git', 'tmp/mruby' build_args = ARGV Dir.mkdir 'tmp' unless File.exist?('tmp') unless File.exist?(dir) system "git clone #{repository} #{dir}" end exit system(%Q[cd #{dir}; MRUBY_CONFIG=#{File.expand_path __FILE__} ruby minirake #{build_args.join(' ')}]) end MRuby::Build.new do |conf| toolchain :gcc conf.gembox 'default' conf.gem :git => 'https://github.com/iij/mruby-env.git' conf.enable_test conf.gem File.expand_path(File.dirname(__FILE__)) end mruby-2.0.0/mrbgems/mruby-io/src/000077500000000000000000000000001340361412400166335ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-io/src/file.c000066400000000000000000000323661340361412400177300ustar00rootroot00000000000000/* ** file.c - File class */ #include "mruby.h" #include "mruby/class.h" #include "mruby/data.h" #include "mruby/string.h" #include "mruby/ext/io.h" #if MRUBY_RELEASE_NO < 10000 #include "error.h" #else #include "mruby/error.h" #endif #include #include #include #include #include #include #include #if defined(_WIN32) || defined(_WIN64) #include #include #define NULL_FILE "NUL" #define UNLINK _unlink #define GETCWD _getcwd #define CHMOD(a, b) 0 #define MAXPATHLEN 1024 #if !defined(PATH_MAX) #define PATH_MAX _MAX_PATH #endif #define realpath(N,R) _fullpath((R),(N),_MAX_PATH) #include #else #define NULL_FILE "/dev/null" #include #define UNLINK unlink #define GETCWD getcwd #define CHMOD(a, b) chmod(a,b) #include #include #include #include #endif #define FILE_SEPARATOR "/" #if defined(_WIN32) || defined(_WIN64) #define PATH_SEPARATOR ";" #define FILE_ALT_SEPARATOR "\\" #else #define PATH_SEPARATOR ":" #endif #ifndef LOCK_SH #define LOCK_SH 1 #endif #ifndef LOCK_EX #define LOCK_EX 2 #endif #ifndef LOCK_NB #define LOCK_NB 4 #endif #ifndef LOCK_UN #define LOCK_UN 8 #endif #define STAT(p, s) stat(p, s) #ifdef _WIN32 static int flock(int fd, int operation) { OVERLAPPED ov; HANDLE h = (HANDLE)_get_osfhandle(fd); DWORD flags; flags = ((operation & LOCK_NB) ? LOCKFILE_FAIL_IMMEDIATELY : 0) | ((operation & LOCK_SH) ? LOCKFILE_EXCLUSIVE_LOCK : 0); memset(&ov, 0, sizeof(ov)); return LockFileEx(h, flags, 0, 0xffffffff, 0xffffffff, &ov) ? 0 : -1; } #endif mrb_value mrb_file_s_umask(mrb_state *mrb, mrb_value klass) { #if defined(_WIN32) || defined(_WIN64) /* nothing to do on windows */ return mrb_fixnum_value(0); #else mrb_int mask, omask; if (mrb_get_args(mrb, "|i", &mask) == 0) { omask = umask(0); umask(omask); } else { omask = umask(mask); } return mrb_fixnum_value(omask); #endif } static mrb_value mrb_file_s_unlink(mrb_state *mrb, mrb_value obj) { mrb_value *argv; mrb_value pathv; mrb_int argc, i; char *path; mrb_get_args(mrb, "*", &argv, &argc); for (i = 0; i < argc; i++) { const char *utf8_path; pathv = mrb_ensure_string_type(mrb, argv[i]); utf8_path = mrb_string_value_cstr(mrb, &pathv); path = mrb_locale_from_utf8(utf8_path, -1); if (UNLINK(path) < 0) { mrb_locale_free(path); mrb_sys_fail(mrb, utf8_path); } mrb_locale_free(path); } return mrb_fixnum_value(argc); } static mrb_value mrb_file_s_rename(mrb_state *mrb, mrb_value obj) { mrb_value from, to; char *src, *dst; mrb_get_args(mrb, "SS", &from, &to); src = mrb_locale_from_utf8(mrb_string_value_cstr(mrb, &from), -1); dst = mrb_locale_from_utf8(mrb_string_value_cstr(mrb, &to), -1); if (rename(src, dst) < 0) { #if defined(_WIN32) || defined(_WIN64) if (CHMOD(dst, 0666) == 0 && UNLINK(dst) == 0 && rename(src, dst) == 0) { mrb_locale_free(src); mrb_locale_free(dst); return mrb_fixnum_value(0); } #endif mrb_locale_free(src); mrb_locale_free(dst); mrb_sys_fail(mrb, mrb_str_to_cstr(mrb, mrb_format(mrb, "(%S, %S)", from, to))); } mrb_locale_free(src); mrb_locale_free(dst); return mrb_fixnum_value(0); } static mrb_value mrb_file_dirname(mrb_state *mrb, mrb_value klass) { #if defined(_WIN32) || defined(_WIN64) char dname[_MAX_DIR], vname[_MAX_DRIVE]; char buffer[_MAX_DRIVE + _MAX_DIR]; char *path; size_t ridx; mrb_value s; mrb_get_args(mrb, "S", &s); path = mrb_locale_from_utf8(mrb_str_to_cstr(mrb, s), -1); _splitpath((const char*)path, vname, dname, NULL, NULL); snprintf(buffer, _MAX_DRIVE + _MAX_DIR, "%s%s", vname, dname); mrb_locale_free(path); ridx = strlen(buffer); if (ridx == 0) { strncpy(buffer, ".", 2); /* null terminated */ } else if (ridx > 1) { ridx--; while (ridx > 0 && (buffer[ridx] == '/' || buffer[ridx] == '\\')) { buffer[ridx] = '\0'; /* remove last char */ ridx--; } } return mrb_str_new_cstr(mrb, buffer); #else char *dname, *path; mrb_value s; mrb_get_args(mrb, "S", &s); path = mrb_locale_from_utf8(mrb_str_to_cstr(mrb, s), -1); if ((dname = dirname(path)) == NULL) { mrb_locale_free(path); mrb_sys_fail(mrb, "dirname"); } mrb_locale_free(path); return mrb_str_new_cstr(mrb, dname); #endif } static mrb_value mrb_file_basename(mrb_state *mrb, mrb_value klass) { // NOTE: Do not use mrb_locale_from_utf8 here #if defined(_WIN32) || defined(_WIN64) char bname[_MAX_DIR]; char extname[_MAX_EXT]; char *path; size_t ridx; char buffer[_MAX_DIR + _MAX_EXT]; mrb_value s; mrb_get_args(mrb, "S", &s); path = mrb_str_to_cstr(mrb, s); ridx = strlen(path); if (ridx > 0) { ridx--; while (ridx > 0 && (path[ridx] == '/' || path[ridx] == '\\')) { path[ridx] = '\0'; ridx--; } if (strncmp(path, "/", 2) == 0) { return mrb_str_new_cstr(mrb, path); } } _splitpath((const char*)path, NULL, NULL, bname, extname); snprintf(buffer, _MAX_DIR + _MAX_EXT, "%s%s", bname, extname); return mrb_str_new_cstr(mrb, buffer); #else char *bname, *path; mrb_value s; mrb_get_args(mrb, "S", &s); path = mrb_str_to_cstr(mrb, s); if ((bname = basename(path)) == NULL) { mrb_sys_fail(mrb, "basename"); } if (strncmp(bname, "//", 3) == 0) bname[1] = '\0'; /* patch for Cygwin */ return mrb_str_new_cstr(mrb, bname); #endif } static mrb_value mrb_file_realpath(mrb_state *mrb, mrb_value klass) { mrb_value pathname, dir_string, s, result; mrb_int argc; char *cpath; argc = mrb_get_args(mrb, "S|S", &pathname, &dir_string); if (argc == 2) { s = mrb_str_dup(mrb, dir_string); s = mrb_str_append(mrb, s, mrb_str_new_cstr(mrb, FILE_SEPARATOR)); s = mrb_str_append(mrb, s, pathname); pathname = s; } cpath = mrb_locale_from_utf8(mrb_str_to_cstr(mrb, pathname), -1); result = mrb_str_buf_new(mrb, PATH_MAX); if (realpath(cpath, RSTRING_PTR(result)) == NULL) { mrb_locale_free(cpath); mrb_sys_fail(mrb, cpath); } mrb_locale_free(cpath); mrb_str_resize(mrb, result, strlen(RSTRING_PTR(result))); return result; } mrb_value mrb_file__getwd(mrb_state *mrb, mrb_value klass) { mrb_value path; char buf[MAXPATHLEN], *utf8; if (GETCWD(buf, MAXPATHLEN) == NULL) { mrb_sys_fail(mrb, "getcwd(2)"); } utf8 = mrb_utf8_from_locale(buf, -1); path = mrb_str_new_cstr(mrb, utf8); mrb_utf8_free(utf8); return path; } static int mrb_file_is_absolute_path(const char *path) { return (path[0] == '/'); } static mrb_value mrb_file__gethome(mrb_state *mrb, mrb_value klass) { mrb_int argc; char *home; mrb_value path; #ifndef _WIN32 mrb_value username; argc = mrb_get_args(mrb, "|S", &username); if (argc == 0) { home = getenv("HOME"); if (home == NULL) { return mrb_nil_value(); } if (!mrb_file_is_absolute_path(home)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "non-absolute home"); } } else { const char *cuser = mrb_str_to_cstr(mrb, username); struct passwd *pwd = getpwnam(cuser); if (pwd == NULL) { return mrb_nil_value(); } home = pwd->pw_dir; if (!mrb_file_is_absolute_path(home)) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "non-absolute home of ~%S", username); } } home = mrb_locale_from_utf8(home, -1); path = mrb_str_new_cstr(mrb, home); mrb_locale_free(home); return path; #else argc = mrb_get_argc(mrb); if (argc == 0) { home = getenv("USERPROFILE"); if (home == NULL) { return mrb_nil_value(); } if (!mrb_file_is_absolute_path(home)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "non-absolute home"); } } else { return mrb_nil_value(); } home = mrb_locale_from_utf8(home, -1); path = mrb_str_new_cstr(mrb, home); mrb_locale_free(home); return path; #endif } static mrb_value mrb_file_mtime(mrb_state *mrb, mrb_value self) { mrb_value obj; struct stat st; int fd; obj = mrb_obj_value(mrb_class_get(mrb, "Time")); fd = (int)mrb_fixnum(mrb_io_fileno(mrb, self)); if (fstat(fd, &st) == -1) return mrb_false_value(); return mrb_funcall(mrb, obj, "at", 1, mrb_fixnum_value(st.st_mtime)); } mrb_value mrb_file_flock(mrb_state *mrb, mrb_value self) { #if defined(sun) mrb_raise(mrb, E_NOTIMP_ERROR, "flock is not supported on Illumos/Solaris/Windows"); #else mrb_int operation; int fd; mrb_get_args(mrb, "i", &operation); fd = (int)mrb_fixnum(mrb_io_fileno(mrb, self)); while (flock(fd, (int)operation) == -1) { switch (errno) { case EINTR: /* retry */ break; case EAGAIN: /* NetBSD */ #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN case EWOULDBLOCK: /* FreeBSD OpenBSD Linux */ #endif if (operation & LOCK_NB) { return mrb_false_value(); } /* FALLTHRU - should not happen */ default: mrb_sys_fail(mrb, "flock failed"); break; } } #endif return mrb_fixnum_value(0); } static mrb_value mrb_file_s_symlink(mrb_state *mrb, mrb_value klass) { #if defined(_WIN32) || defined(_WIN64) mrb_raise(mrb, E_NOTIMP_ERROR, "symlink is not supported on this platform"); #else mrb_value from, to; const char *src, *dst; int ai = mrb_gc_arena_save(mrb); mrb_get_args(mrb, "SS", &from, &to); src = mrb_locale_from_utf8(mrb_str_to_cstr(mrb, from), -1); dst = mrb_locale_from_utf8(mrb_str_to_cstr(mrb, to), -1); if (symlink(src, dst) == -1) { mrb_locale_free(src); mrb_locale_free(dst); mrb_sys_fail(mrb, mrb_str_to_cstr(mrb, mrb_format(mrb, "(%S, %S)", from, to))); } mrb_locale_free(src); mrb_locale_free(dst); mrb_gc_arena_restore(mrb, ai); #endif return mrb_fixnum_value(0); } static mrb_value mrb_file_s_chmod(mrb_state *mrb, mrb_value klass) { mrb_int mode; mrb_int argc, i; mrb_value *filenames; int ai = mrb_gc_arena_save(mrb); mrb_get_args(mrb, "i*", &mode, &filenames, &argc); for (i = 0; i < argc; i++) { const char *utf8_path = mrb_str_to_cstr(mrb, filenames[i]); char *path = mrb_locale_from_utf8(utf8_path, -1); if (CHMOD(path, mode) == -1) { mrb_locale_free(path); mrb_sys_fail(mrb, utf8_path); } mrb_locale_free(path); } mrb_gc_arena_restore(mrb, ai); return mrb_fixnum_value(argc); } static mrb_value mrb_file_s_readlink(mrb_state *mrb, mrb_value klass) { #if defined(_WIN32) || defined(_WIN64) mrb_raise(mrb, E_NOTIMP_ERROR, "readlink is not supported on this platform"); return mrb_nil_value(); // unreachable #else char *path, *buf, *tmp; size_t bufsize = 100; ssize_t rc; mrb_value ret; int ai = mrb_gc_arena_save(mrb); mrb_get_args(mrb, "z", &path); tmp = mrb_locale_from_utf8(path, -1); buf = (char *)mrb_malloc(mrb, bufsize); while ((rc = readlink(tmp, buf, bufsize)) == (ssize_t)bufsize && rc != -1) { bufsize *= 2; buf = (char *)mrb_realloc(mrb, buf, bufsize); } mrb_locale_free(tmp); if (rc == -1) { mrb_free(mrb, buf); mrb_sys_fail(mrb, path); } tmp = mrb_utf8_from_locale(buf, -1); ret = mrb_str_new(mrb, tmp, rc); mrb_locale_free(tmp); mrb_free(mrb, buf); mrb_gc_arena_restore(mrb, ai); return ret; #endif } void mrb_init_file(mrb_state *mrb) { struct RClass *io, *file, *cnst; io = mrb_class_get(mrb, "IO"); file = mrb_define_class(mrb, "File", io); MRB_SET_INSTANCE_TT(file, MRB_TT_DATA); mrb_define_class_method(mrb, file, "umask", mrb_file_s_umask, MRB_ARGS_REQ(1)); mrb_define_class_method(mrb, file, "delete", mrb_file_s_unlink, MRB_ARGS_ANY()); mrb_define_class_method(mrb, file, "unlink", mrb_file_s_unlink, MRB_ARGS_ANY()); mrb_define_class_method(mrb, file, "rename", mrb_file_s_rename, MRB_ARGS_REQ(2)); mrb_define_class_method(mrb, file, "symlink", mrb_file_s_symlink, MRB_ARGS_REQ(2)); mrb_define_class_method(mrb, file, "chmod", mrb_file_s_chmod, MRB_ARGS_REQ(1) | MRB_ARGS_REST()); mrb_define_class_method(mrb, file, "readlink", mrb_file_s_readlink, MRB_ARGS_REQ(1)); mrb_define_class_method(mrb, file, "dirname", mrb_file_dirname, MRB_ARGS_REQ(1)); mrb_define_class_method(mrb, file, "basename", mrb_file_basename, MRB_ARGS_REQ(1)); mrb_define_class_method(mrb, file, "realpath", mrb_file_realpath, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1)); mrb_define_class_method(mrb, file, "_getwd", mrb_file__getwd, MRB_ARGS_NONE()); mrb_define_class_method(mrb, file, "_gethome", mrb_file__gethome, MRB_ARGS_OPT(1)); mrb_define_method(mrb, file, "flock", mrb_file_flock, MRB_ARGS_REQ(1)); mrb_define_method(mrb, file, "mtime", mrb_file_mtime, MRB_ARGS_NONE()); cnst = mrb_define_module_under(mrb, file, "Constants"); mrb_define_const(mrb, cnst, "LOCK_SH", mrb_fixnum_value(LOCK_SH)); mrb_define_const(mrb, cnst, "LOCK_EX", mrb_fixnum_value(LOCK_EX)); mrb_define_const(mrb, cnst, "LOCK_UN", mrb_fixnum_value(LOCK_UN)); mrb_define_const(mrb, cnst, "LOCK_NB", mrb_fixnum_value(LOCK_NB)); mrb_define_const(mrb, cnst, "SEPARATOR", mrb_str_new_cstr(mrb, FILE_SEPARATOR)); mrb_define_const(mrb, cnst, "PATH_SEPARATOR", mrb_str_new_cstr(mrb, PATH_SEPARATOR)); #if defined(_WIN32) || defined(_WIN64) mrb_define_const(mrb, cnst, "ALT_SEPARATOR", mrb_str_new_cstr(mrb, FILE_ALT_SEPARATOR)); #else mrb_define_const(mrb, cnst, "ALT_SEPARATOR", mrb_nil_value()); #endif mrb_define_const(mrb, cnst, "NULL", mrb_str_new_cstr(mrb, NULL_FILE)); } mruby-2.0.0/mrbgems/mruby-io/src/file_test.c000066400000000000000000000176531340361412400207710ustar00rootroot00000000000000/* ** file.c - File class */ #include "mruby.h" #include "mruby/class.h" #include "mruby/data.h" #include "mruby/string.h" #include "mruby/ext/io.h" #if MRUBY_RELEASE_NO < 10000 #include "error.h" #else #include "mruby/error.h" #endif #include #include #if defined(_WIN32) || defined(_WIN64) #define LSTAT stat #include #else #define LSTAT lstat #include #include #include #include #include #include #endif #include #include #include #include #include extern struct mrb_data_type mrb_io_type; static int mrb_stat0(mrb_state *mrb, mrb_value obj, struct stat *st, int do_lstat) { mrb_value tmp; mrb_value io_klass, str_klass; io_klass = mrb_obj_value(mrb_class_get(mrb, "IO")); str_klass = mrb_obj_value(mrb_class_get(mrb, "String")); tmp = mrb_funcall(mrb, obj, "is_a?", 1, io_klass); if (mrb_test(tmp)) { struct mrb_io *fptr; fptr = (struct mrb_io *)mrb_get_datatype(mrb, obj, &mrb_io_type); if (fptr && fptr->fd >= 0) { return fstat(fptr->fd, st); } mrb_raise(mrb, E_IO_ERROR, "closed stream"); return -1; } tmp = mrb_funcall(mrb, obj, "is_a?", 1, str_klass); if (mrb_test(tmp)) { char *path = mrb_locale_from_utf8(mrb_str_to_cstr(mrb, obj), -1); int ret; if (do_lstat) { ret = LSTAT(path, st); } else { ret = stat(path, st); } mrb_locale_free(path); return ret; } return -1; } static int mrb_stat(mrb_state *mrb, mrb_value obj, struct stat *st) { return mrb_stat0(mrb, obj, st, 0); } static int mrb_lstat(mrb_state *mrb, mrb_value obj, struct stat *st) { return mrb_stat0(mrb, obj, st, 1); } /* * Document-method: directory? * * call-seq: * File.directory?(file_name) -> true or false * * Returns true if the named file is a directory, * or a symlink that points at a directory, and false * otherwise. * * File.directory?(".") */ mrb_value mrb_filetest_s_directory_p(mrb_state *mrb, mrb_value klass) { #ifndef S_ISDIR # define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) #endif struct stat st; mrb_value obj; mrb_get_args(mrb, "o", &obj); if (mrb_stat(mrb, obj, &st) < 0) return mrb_false_value(); if (S_ISDIR(st.st_mode)) return mrb_true_value(); return mrb_false_value(); } /* * call-seq: * File.pipe?(file_name) -> true or false * * Returns true if the named file is a pipe. */ mrb_value mrb_filetest_s_pipe_p(mrb_state *mrb, mrb_value klass) { #if defined(_WIN32) || defined(_WIN64) mrb_raise(mrb, E_NOTIMP_ERROR, "pipe is not supported on this platform"); #else #ifdef S_IFIFO # ifndef S_ISFIFO # define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) # endif struct stat st; mrb_value obj; mrb_get_args(mrb, "o", &obj); if (mrb_stat(mrb, obj, &st) < 0) return mrb_false_value(); if (S_ISFIFO(st.st_mode)) return mrb_true_value(); #endif return mrb_false_value(); #endif } /* * call-seq: * File.symlink?(file_name) -> true or false * * Returns true if the named file is a symbolic link. */ mrb_value mrb_filetest_s_symlink_p(mrb_state *mrb, mrb_value klass) { #if defined(_WIN32) || defined(_WIN64) mrb_raise(mrb, E_NOTIMP_ERROR, "symlink is not supported on this platform"); #else #ifndef S_ISLNK # ifdef _S_ISLNK # define S_ISLNK(m) _S_ISLNK(m) # else # ifdef _S_IFLNK # define S_ISLNK(m) (((m) & S_IFMT) == _S_IFLNK) # else # ifdef S_IFLNK # define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) # endif # endif # endif #endif #ifdef S_ISLNK struct stat st; mrb_value obj; mrb_get_args(mrb, "o", &obj); if (mrb_lstat(mrb, obj, &st) == -1) return mrb_false_value(); if (S_ISLNK(st.st_mode)) return mrb_true_value(); #endif return mrb_false_value(); #endif } /* * call-seq: * File.socket?(file_name) -> true or false * * Returns true if the named file is a socket. */ mrb_value mrb_filetest_s_socket_p(mrb_state *mrb, mrb_value klass) { #if defined(_WIN32) || defined(_WIN64) mrb_raise(mrb, E_NOTIMP_ERROR, "socket is not supported on this platform"); #else #ifndef S_ISSOCK # ifdef _S_ISSOCK # define S_ISSOCK(m) _S_ISSOCK(m) # else # ifdef _S_IFSOCK # define S_ISSOCK(m) (((m) & S_IFMT) == _S_IFSOCK) # else # ifdef S_IFSOCK # define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) # endif # endif # endif #endif #ifdef S_ISSOCK struct stat st; mrb_value obj; mrb_get_args(mrb, "o", &obj); if (mrb_stat(mrb, obj, &st) < 0) return mrb_false_value(); if (S_ISSOCK(st.st_mode)) return mrb_true_value(); #endif return mrb_false_value(); #endif } /* * call-seq: * File.exist?(file_name) -> true or false * File.exists?(file_name) -> true or false * * Return true if the named file exists. */ mrb_value mrb_filetest_s_exist_p(mrb_state *mrb, mrb_value klass) { struct stat st; mrb_value obj; mrb_get_args(mrb, "o", &obj); if (mrb_stat(mrb, obj, &st) < 0) return mrb_false_value(); return mrb_true_value(); } /* * call-seq: * File.file?(file_name) -> true or false * * Returns true if the named file exists and is a * regular file. */ mrb_value mrb_filetest_s_file_p(mrb_state *mrb, mrb_value klass) { #ifndef S_ISREG # define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) #endif struct stat st; mrb_value obj; mrb_get_args(mrb, "o", &obj); if (mrb_stat(mrb, obj, &st) < 0) return mrb_false_value(); if (S_ISREG(st.st_mode)) return mrb_true_value(); return mrb_false_value(); } /* * call-seq: * File.zero?(file_name) -> true or false * * Returns true if the named file exists and has * a zero size. */ mrb_value mrb_filetest_s_zero_p(mrb_state *mrb, mrb_value klass) { struct stat st; mrb_value obj; mrb_get_args(mrb, "o", &obj); if (mrb_stat(mrb, obj, &st) < 0) return mrb_false_value(); if (st.st_size == 0) return mrb_true_value(); return mrb_false_value(); } /* * call-seq: * File.size(file_name) -> integer * * Returns the size of file_name. * * _file_name_ can be an IO object. */ mrb_value mrb_filetest_s_size(mrb_state *mrb, mrb_value klass) { struct stat st; mrb_value obj; mrb_get_args(mrb, "o", &obj); if (mrb_stat(mrb, obj, &st) < 0) mrb_sys_fail(mrb, "mrb_stat"); return mrb_fixnum_value(st.st_size); } /* * call-seq: * File.size?(file_name) -> Integer or nil * * Returns +nil+ if +file_name+ doesn't exist or has zero size, the size of the * file otherwise. */ mrb_value mrb_filetest_s_size_p(mrb_state *mrb, mrb_value klass) { struct stat st; mrb_value obj; mrb_get_args(mrb, "o", &obj); if (mrb_stat(mrb, obj, &st) < 0) return mrb_nil_value(); if (st.st_size == 0) return mrb_nil_value(); return mrb_fixnum_value(st.st_size); } void mrb_init_file_test(mrb_state *mrb) { struct RClass *f; f = mrb_define_class(mrb, "FileTest", mrb->object_class); mrb_define_class_method(mrb, f, "directory?", mrb_filetest_s_directory_p, MRB_ARGS_REQ(1)); mrb_define_class_method(mrb, f, "exist?", mrb_filetest_s_exist_p, MRB_ARGS_REQ(1)); mrb_define_class_method(mrb, f, "exists?", mrb_filetest_s_exist_p, MRB_ARGS_REQ(1)); mrb_define_class_method(mrb, f, "file?", mrb_filetest_s_file_p, MRB_ARGS_REQ(1)); mrb_define_class_method(mrb, f, "pipe?", mrb_filetest_s_pipe_p, MRB_ARGS_REQ(1)); mrb_define_class_method(mrb, f, "size", mrb_filetest_s_size, MRB_ARGS_REQ(1)); mrb_define_class_method(mrb, f, "size?", mrb_filetest_s_size_p, MRB_ARGS_REQ(1)); mrb_define_class_method(mrb, f, "socket?", mrb_filetest_s_socket_p, MRB_ARGS_REQ(1)); mrb_define_class_method(mrb, f, "symlink?", mrb_filetest_s_symlink_p, MRB_ARGS_REQ(1)); mrb_define_class_method(mrb, f, "zero?", mrb_filetest_s_zero_p, MRB_ARGS_REQ(1)); } mruby-2.0.0/mrbgems/mruby-io/src/io.c000066400000000000000000000775701340361412400174260ustar00rootroot00000000000000/* ** io.c - IO class */ #include "mruby.h" #include "mruby/array.h" #include "mruby/class.h" #include "mruby/data.h" #include "mruby/hash.h" #include "mruby/string.h" #include "mruby/variable.h" #include "mruby/ext/io.h" #if MRUBY_RELEASE_NO < 10000 #include "error.h" #else #include "mruby/error.h" #endif #include #include #if defined(_WIN32) || defined(_WIN64) #include #include #define open _open #define close _close #define dup _dup #define dup2 _dup2 #define read _read #define write _write #define lseek _lseek #define isatty _isatty #define WEXITSTATUS(x) (x) typedef int fsize_t; typedef long ftime_t; typedef long fsuseconds_t; typedef int fmode_t; #else #include #include typedef size_t fsize_t; typedef time_t ftime_t; typedef suseconds_t fsuseconds_t; typedef mode_t fmode_t; #endif #ifdef _MSC_VER typedef mrb_int pid_t; #endif #include #include #include #include static void mrb_io_free(mrb_state *mrb, void *ptr); struct mrb_data_type mrb_io_type = { "IO", mrb_io_free }; static struct mrb_io *io_get_open_fptr(mrb_state *mrb, mrb_value self); static int mrb_io_modestr_to_flags(mrb_state *mrb, const char *modestr); static int mrb_io_flags_to_modenum(mrb_state *mrb, int flags); static void fptr_finalize(mrb_state *mrb, struct mrb_io *fptr, int quiet); #if MRUBY_RELEASE_NO < 10000 static struct RClass * mrb_module_get(mrb_state *mrb, const char *name) { return mrb_class_get(mrb, name); } #endif static struct mrb_io * io_get_open_fptr(mrb_state *mrb, mrb_value self) { struct mrb_io *fptr; fptr = (struct mrb_io *)mrb_get_datatype(mrb, self, &mrb_io_type); if (fptr == NULL) { mrb_raise(mrb, E_IO_ERROR, "uninitialized stream."); } if (fptr->fd < 0) { mrb_raise(mrb, E_IO_ERROR, "closed stream."); } return fptr; } static void io_set_process_status(mrb_state *mrb, pid_t pid, int status) { struct RClass *c_process, *c_status; mrb_value v; c_status = NULL; if (mrb_class_defined(mrb, "Process")) { c_process = mrb_module_get(mrb, "Process"); if (mrb_const_defined(mrb, mrb_obj_value(c_process), mrb_intern_cstr(mrb, "Status"))) { c_status = mrb_class_get_under(mrb, c_process, "Status"); } } if (c_status != NULL) { v = mrb_funcall(mrb, mrb_obj_value(c_status), "new", 2, mrb_fixnum_value(pid), mrb_fixnum_value(status)); } else { v = mrb_fixnum_value(WEXITSTATUS(status)); } mrb_gv_set(mrb, mrb_intern_cstr(mrb, "$?"), v); } static int mrb_io_modestr_to_flags(mrb_state *mrb, const char *mode) { int flags = 0; const char *m = mode; switch (*m++) { case 'r': flags |= FMODE_READABLE; break; case 'w': flags |= FMODE_WRITABLE | FMODE_CREATE | FMODE_TRUNC; break; case 'a': flags |= FMODE_WRITABLE | FMODE_APPEND | FMODE_CREATE; break; default: mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal access mode %S", mrb_str_new_cstr(mrb, mode)); } while (*m) { switch (*m++) { case 'b': flags |= FMODE_BINMODE; break; case '+': flags |= FMODE_READWRITE; break; case ':': /* XXX: PASSTHROUGH*/ default: mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal access mode %S", mrb_str_new_cstr(mrb, mode)); } } return flags; } static int mrb_io_flags_to_modenum(mrb_state *mrb, int flags) { int modenum = 0; switch(flags & (FMODE_READABLE|FMODE_WRITABLE|FMODE_READWRITE)) { case FMODE_READABLE: modenum = O_RDONLY; break; case FMODE_WRITABLE: modenum = O_WRONLY; break; case FMODE_READWRITE: modenum = O_RDWR; break; } if (flags & FMODE_APPEND) { modenum |= O_APPEND; } if (flags & FMODE_TRUNC) { modenum |= O_TRUNC; } if (flags & FMODE_CREATE) { modenum |= O_CREAT; } #ifdef O_BINARY if (flags & FMODE_BINMODE) { modenum |= O_BINARY; } #endif return modenum; } static void mrb_fd_cloexec(mrb_state *mrb, int fd) { #if defined(F_GETFD) && defined(F_SETFD) && defined(FD_CLOEXEC) int flags, flags2; flags = fcntl(fd, F_GETFD); if (flags == -1) { mrb_bug(mrb, "mrb_fd_cloexec: fcntl(%S, F_GETFD) failed: %S", mrb_fixnum_value(fd), mrb_fixnum_value(errno)); } if (fd <= 2) { flags2 = flags & ~FD_CLOEXEC; /* Clear CLOEXEC for standard file descriptors: 0, 1, 2. */ } else { flags2 = flags | FD_CLOEXEC; /* Set CLOEXEC for non-standard file descriptors: 3, 4, 5, ... */ } if (flags != flags2) { if (fcntl(fd, F_SETFD, flags2) == -1) { mrb_bug(mrb, "mrb_fd_cloexec: fcntl(%S, F_SETFD, %S) failed: %S", mrb_fixnum_value(fd), mrb_fixnum_value(flags2), mrb_fixnum_value(errno)); } } #endif } #if !defined(_WIN32) && !TARGET_OS_IPHONE static int mrb_cloexec_pipe(mrb_state *mrb, int fildes[2]) { int ret; ret = pipe(fildes); if (ret == -1) return -1; mrb_fd_cloexec(mrb, fildes[0]); mrb_fd_cloexec(mrb, fildes[1]); return ret; } static int mrb_pipe(mrb_state *mrb, int pipes[2]) { int ret; ret = mrb_cloexec_pipe(mrb, pipes); if (ret == -1) { if (errno == EMFILE || errno == ENFILE) { mrb_garbage_collect(mrb); ret = mrb_cloexec_pipe(mrb, pipes); } } return ret; } static int mrb_proc_exec(const char *pname) { const char *s; s = pname; while (*s == ' ' || *s == '\t' || *s == '\n') s++; if (!*s) { errno = ENOENT; return -1; } execl("/bin/sh", "sh", "-c", pname, (char *)NULL); return -1; } #endif static void mrb_io_free(mrb_state *mrb, void *ptr) { struct mrb_io *io = (struct mrb_io *)ptr; if (io != NULL) { fptr_finalize(mrb, io, TRUE); mrb_free(mrb, io); } } static struct mrb_io * mrb_io_alloc(mrb_state *mrb) { struct mrb_io *fptr; fptr = (struct mrb_io *)mrb_malloc(mrb, sizeof(struct mrb_io)); fptr->fd = -1; fptr->fd2 = -1; fptr->pid = 0; fptr->readable = 0; fptr->writable = 0; fptr->sync = 0; fptr->is_socket = 0; return fptr; } #ifndef NOFILE #define NOFILE 64 #endif static int option_to_fd(mrb_state *mrb, mrb_value obj, const char *key) { mrb_value opt = mrb_funcall(mrb, obj, "[]", 1, mrb_symbol_value(mrb_intern_static(mrb, key, strlen(key)))); if (mrb_nil_p(opt)) { return -1; } switch (mrb_type(opt)) { case MRB_TT_DATA: /* IO */ return (int)mrb_fixnum(mrb_io_fileno(mrb, opt)); case MRB_TT_FIXNUM: return (int)mrb_fixnum(opt); default: mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong exec redirect action"); break; } return -1; /* never reached */ } #ifdef _WIN32 mrb_value mrb_io_s_popen(mrb_state *mrb, mrb_value klass) { mrb_value cmd, io; mrb_value mode = mrb_str_new_cstr(mrb, "r"); mrb_value opt = mrb_hash_new(mrb); struct mrb_io *fptr; const char *pname; int pid = 0, flags; STARTUPINFO si; PROCESS_INFORMATION pi; SECURITY_ATTRIBUTES saAttr; HANDLE ifd[2]; HANDLE ofd[2]; int doexec; int opt_in, opt_out, opt_err; ifd[0] = INVALID_HANDLE_VALUE; ifd[1] = INVALID_HANDLE_VALUE; ofd[0] = INVALID_HANDLE_VALUE; ofd[1] = INVALID_HANDLE_VALUE; mrb_get_args(mrb, "S|SH", &cmd, &mode, &opt); io = mrb_obj_value(mrb_data_object_alloc(mrb, mrb_class_ptr(klass), NULL, &mrb_io_type)); pname = mrb_string_value_cstr(mrb, &cmd); flags = mrb_io_modestr_to_flags(mrb, mrb_string_value_cstr(mrb, &mode)); doexec = (strcmp("-", pname) != 0); opt_in = option_to_fd(mrb, opt, "in"); opt_out = option_to_fd(mrb, opt, "out"); opt_err = option_to_fd(mrb, opt, "err"); saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; saAttr.lpSecurityDescriptor = NULL; if (flags & FMODE_READABLE) { if (!CreatePipe(&ofd[0], &ofd[1], &saAttr, 0) || !SetHandleInformation(ofd[0], HANDLE_FLAG_INHERIT, 0)) { mrb_sys_fail(mrb, "pipe"); } } if (flags & FMODE_WRITABLE) { if (!CreatePipe(&ifd[0], &ifd[1], &saAttr, 0) || !SetHandleInformation(ifd[1], HANDLE_FLAG_INHERIT, 0)) { mrb_sys_fail(mrb, "pipe"); } } if (doexec) { ZeroMemory(&pi, sizeof(pi)); ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); si.dwFlags |= STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE; si.dwFlags |= STARTF_USESTDHANDLES; if (flags & FMODE_READABLE) { si.hStdOutput = ofd[1]; si.hStdError = ofd[1]; } if (flags & FMODE_WRITABLE) { si.hStdInput = ifd[0]; } if (!CreateProcess( NULL, (char*)pname, NULL, NULL, TRUE, CREATE_NEW_PROCESS_GROUP, NULL, NULL, &si, &pi)) { CloseHandle(ifd[0]); CloseHandle(ifd[1]); CloseHandle(ofd[0]); CloseHandle(ofd[1]); mrb_raisef(mrb, E_IO_ERROR, "command not found: %S", cmd); } CloseHandle(pi.hThread); CloseHandle(ifd[0]); CloseHandle(ofd[1]); pid = pi.dwProcessId; } mrb_iv_set(mrb, io, mrb_intern_cstr(mrb, "@buf"), mrb_str_new_cstr(mrb, "")); fptr = mrb_io_alloc(mrb); fptr->fd = _open_osfhandle((intptr_t)ofd[0], 0); fptr->fd2 = _open_osfhandle((intptr_t)ifd[1], 0); fptr->pid = pid; fptr->readable = ((flags & FMODE_READABLE) != 0); fptr->writable = ((flags & FMODE_WRITABLE) != 0); fptr->sync = 0; DATA_TYPE(io) = &mrb_io_type; DATA_PTR(io) = fptr; return io; } #elif TARGET_OS_IPHONE mrb_value mrb_io_s_popen(mrb_state *mrb, mrb_value klass) { mrb_raise(mrb, E_NOTIMP_ERROR, "IO#popen is not supported on the platform"); return mrb_false_value(); } #else mrb_value mrb_io_s_popen(mrb_state *mrb, mrb_value klass) { mrb_value cmd, io, result; mrb_value mode = mrb_str_new_cstr(mrb, "r"); mrb_value opt = mrb_hash_new(mrb); struct mrb_io *fptr; const char *pname; int pid, flags, fd, write_fd = -1; int pr[2] = { -1, -1 }; int pw[2] = { -1, -1 }; int doexec; int saved_errno; int opt_in, opt_out, opt_err; mrb_get_args(mrb, "S|SH", &cmd, &mode, &opt); io = mrb_obj_value(mrb_data_object_alloc(mrb, mrb_class_ptr(klass), NULL, &mrb_io_type)); pname = mrb_string_value_cstr(mrb, &cmd); flags = mrb_io_modestr_to_flags(mrb, mrb_string_value_cstr(mrb, &mode)); doexec = (strcmp("-", pname) != 0); opt_in = option_to_fd(mrb, opt, "in"); opt_out = option_to_fd(mrb, opt, "out"); opt_err = option_to_fd(mrb, opt, "err"); if (flags & FMODE_READABLE) { if (pipe(pr) == -1) { mrb_sys_fail(mrb, "pipe"); } mrb_fd_cloexec(mrb, pr[0]); mrb_fd_cloexec(mrb, pr[1]); } if (flags & FMODE_WRITABLE) { if (pipe(pw) == -1) { if (pr[0] != -1) close(pr[0]); if (pr[1] != -1) close(pr[1]); mrb_sys_fail(mrb, "pipe"); } mrb_fd_cloexec(mrb, pw[0]); mrb_fd_cloexec(mrb, pw[1]); } if (!doexec) { // XXX fflush(stdin); fflush(stdout); fflush(stderr); } result = mrb_nil_value(); switch (pid = fork()) { case 0: /* child */ if (opt_in != -1) { dup2(opt_in, 0); } if (opt_out != -1) { dup2(opt_out, 1); } if (opt_err != -1) { dup2(opt_err, 2); } if (flags & FMODE_READABLE) { close(pr[0]); if (pr[1] != 1) { dup2(pr[1], 1); close(pr[1]); } } if (flags & FMODE_WRITABLE) { close(pw[1]); if (pw[0] != 0) { dup2(pw[0], 0); close(pw[0]); } } if (doexec) { for (fd = 3; fd < NOFILE; fd++) { close(fd); } mrb_proc_exec(pname); mrb_raisef(mrb, E_IO_ERROR, "command not found: %S", cmd); _exit(127); } result = mrb_nil_value(); break; default: /* parent */ if ((flags & FMODE_READABLE) && (flags & FMODE_WRITABLE)) { close(pr[1]); fd = pr[0]; close(pw[0]); write_fd = pw[1]; } else if (flags & FMODE_READABLE) { close(pr[1]); fd = pr[0]; } else { close(pw[0]); fd = pw[1]; } mrb_iv_set(mrb, io, mrb_intern_cstr(mrb, "@buf"), mrb_str_new_cstr(mrb, "")); fptr = mrb_io_alloc(mrb); fptr->fd = fd; fptr->fd2 = write_fd; fptr->pid = pid; fptr->readable = ((flags & FMODE_READABLE) != 0); fptr->writable = ((flags & FMODE_WRITABLE) != 0); fptr->sync = 0; DATA_TYPE(io) = &mrb_io_type; DATA_PTR(io) = fptr; result = io; break; case -1: /* error */ saved_errno = errno; if (flags & FMODE_READABLE) { close(pr[0]); close(pr[1]); } if (flags & FMODE_WRITABLE) { close(pw[0]); close(pw[1]); } errno = saved_errno; mrb_sys_fail(mrb, "pipe_open failed."); break; } return result; } #endif static int mrb_dup(mrb_state *mrb, int fd, mrb_bool *failed) { int new_fd; *failed = TRUE; if (fd < 0) return fd; new_fd = dup(fd); if (new_fd > 0) *failed = FALSE; return new_fd; } mrb_value mrb_io_initialize_copy(mrb_state *mrb, mrb_value copy) { mrb_value orig; mrb_value buf; struct mrb_io *fptr_copy; struct mrb_io *fptr_orig; mrb_bool failed = TRUE; mrb_get_args(mrb, "o", &orig); fptr_orig = io_get_open_fptr(mrb, orig); fptr_copy = (struct mrb_io *)DATA_PTR(copy); if (fptr_orig == fptr_copy) return copy; if (fptr_copy != NULL) { fptr_finalize(mrb, fptr_copy, FALSE); mrb_free(mrb, fptr_copy); } fptr_copy = (struct mrb_io *)mrb_io_alloc(mrb); DATA_TYPE(copy) = &mrb_io_type; DATA_PTR(copy) = fptr_copy; buf = mrb_iv_get(mrb, orig, mrb_intern_cstr(mrb, "@buf")); mrb_iv_set(mrb, copy, mrb_intern_cstr(mrb, "@buf"), buf); fptr_copy->fd = mrb_dup(mrb, fptr_orig->fd, &failed); if (failed) { mrb_sys_fail(mrb, 0); } mrb_fd_cloexec(mrb, fptr_copy->fd); if (fptr_orig->fd2 != -1) { fptr_copy->fd2 = mrb_dup(mrb, fptr_orig->fd2, &failed); if (failed) { close(fptr_copy->fd); mrb_sys_fail(mrb, 0); } mrb_fd_cloexec(mrb, fptr_copy->fd2); } fptr_copy->pid = fptr_orig->pid; fptr_copy->readable = fptr_orig->readable; fptr_copy->writable = fptr_orig->writable; fptr_copy->sync = fptr_orig->sync; fptr_copy->is_socket = fptr_orig->is_socket; return copy; } mrb_value mrb_io_initialize(mrb_state *mrb, mrb_value io) { struct mrb_io *fptr; mrb_int fd; mrb_value mode, opt; int flags; mode = opt = mrb_nil_value(); mrb_get_args(mrb, "i|So", &fd, &mode, &opt); if (mrb_nil_p(mode)) { mode = mrb_str_new_cstr(mrb, "r"); } if (mrb_nil_p(opt)) { opt = mrb_hash_new(mrb); } flags = mrb_io_modestr_to_flags(mrb, mrb_string_value_cstr(mrb, &mode)); mrb_iv_set(mrb, io, mrb_intern_cstr(mrb, "@buf"), mrb_str_new_cstr(mrb, "")); fptr = (struct mrb_io *)DATA_PTR(io); if (fptr != NULL) { fptr_finalize(mrb, fptr, TRUE); mrb_free(mrb, fptr); } fptr = mrb_io_alloc(mrb); DATA_TYPE(io) = &mrb_io_type; DATA_PTR(io) = fptr; fptr->fd = (int)fd; fptr->readable = ((flags & FMODE_READABLE) != 0); fptr->writable = ((flags & FMODE_WRITABLE) != 0); fptr->sync = 0; return io; } static void fptr_finalize(mrb_state *mrb, struct mrb_io *fptr, int quiet) { int saved_errno = 0; if (fptr == NULL) { return; } if (fptr->fd > 2) { #ifdef _WIN32 if (fptr->is_socket) { if (closesocket(fptr->fd) != 0) { saved_errno = WSAGetLastError(); } fptr->fd = -1; } #endif if (fptr->fd != -1) { if (close(fptr->fd) == -1) { saved_errno = errno; } } fptr->fd = -1; } if (fptr->fd2 > 2) { if (close(fptr->fd2) == -1) { if (saved_errno == 0) { saved_errno = errno; } } fptr->fd2 = -1; } if (fptr->pid != 0) { #if !defined(_WIN32) && !defined(_WIN64) pid_t pid; int status; do { pid = waitpid(fptr->pid, &status, 0); } while (pid == -1 && errno == EINTR); if (!quiet && pid == fptr->pid) { io_set_process_status(mrb, pid, status); } #else HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, fptr->pid); DWORD status; if (WaitForSingleObject(h, INFINITE) && GetExitCodeProcess(h, &status)) if (!quiet) io_set_process_status(mrb, fptr->pid, (int)status); CloseHandle(h); #endif fptr->pid = 0; /* Note: we don't raise an exception when waitpid(3) fails */ } if (!quiet && saved_errno != 0) { errno = saved_errno; mrb_sys_fail(mrb, "fptr_finalize failed."); } } mrb_value mrb_io_check_readable(mrb_state *mrb, mrb_value self) { struct mrb_io *fptr = io_get_open_fptr(mrb, self); if (! fptr->readable) { mrb_raise(mrb, E_IO_ERROR, "not opened for reading"); } return mrb_nil_value(); } mrb_value mrb_io_isatty(mrb_state *mrb, mrb_value self) { struct mrb_io *fptr; fptr = io_get_open_fptr(mrb, self); if (isatty(fptr->fd) == 0) return mrb_false_value(); return mrb_true_value(); } mrb_value mrb_io_s_for_fd(mrb_state *mrb, mrb_value klass) { struct RClass *c = mrb_class_ptr(klass); enum mrb_vtype ttype = MRB_INSTANCE_TT(c); mrb_value obj; /* copied from mrb_instance_alloc() */ if (ttype == 0) ttype = MRB_TT_OBJECT; obj = mrb_obj_value((struct RObject*)mrb_obj_alloc(mrb, ttype, c)); return mrb_io_initialize(mrb, obj); } mrb_value mrb_io_s_sysclose(mrb_state *mrb, mrb_value klass) { mrb_int fd; mrb_get_args(mrb, "i", &fd); if (close((int)fd) == -1) { mrb_sys_fail(mrb, "close"); } return mrb_fixnum_value(0); } int mrb_cloexec_open(mrb_state *mrb, const char *pathname, mrb_int flags, mrb_int mode) { mrb_value emsg; int fd, retry = FALSE; char* fname = mrb_locale_from_utf8(pathname, -1); #ifdef O_CLOEXEC /* O_CLOEXEC is available since Linux 2.6.23. Linux 2.6.18 silently ignore it. */ flags |= O_CLOEXEC; #elif defined O_NOINHERIT flags |= O_NOINHERIT; #endif reopen: fd = open(fname, (int)flags, (fmode_t)mode); if (fd == -1) { if (!retry) { switch (errno) { case ENFILE: case EMFILE: mrb_garbage_collect(mrb); retry = TRUE; goto reopen; } } emsg = mrb_format(mrb, "open %S", mrb_str_new_cstr(mrb, pathname)); mrb_str_modify(mrb, mrb_str_ptr(emsg)); mrb_sys_fail(mrb, RSTRING_PTR(emsg)); } mrb_locale_free(fname); if (fd <= 2) { mrb_fd_cloexec(mrb, fd); } return fd; } mrb_value mrb_io_s_sysopen(mrb_state *mrb, mrb_value klass) { mrb_value path = mrb_nil_value(); mrb_value mode = mrb_nil_value(); mrb_int fd, perm = -1; const char *pat; int flags, modenum; mrb_get_args(mrb, "S|Si", &path, &mode, &perm); if (mrb_nil_p(mode)) { mode = mrb_str_new_cstr(mrb, "r"); } if (perm < 0) { perm = 0666; } pat = mrb_string_value_cstr(mrb, &path); flags = mrb_io_modestr_to_flags(mrb, mrb_string_value_cstr(mrb, &mode)); modenum = mrb_io_flags_to_modenum(mrb, flags); fd = mrb_cloexec_open(mrb, pat, modenum, perm); return mrb_fixnum_value(fd); } mrb_value mrb_io_sysread(mrb_state *mrb, mrb_value io) { struct mrb_io *fptr; mrb_value buf = mrb_nil_value(); mrb_int maxlen; int ret; mrb_get_args(mrb, "i|S", &maxlen, &buf); if (maxlen < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "negative expanding string size"); } else if (maxlen == 0) { return mrb_str_new(mrb, NULL, maxlen); } if (mrb_nil_p(buf)) { buf = mrb_str_new(mrb, NULL, maxlen); } if (RSTRING_LEN(buf) != maxlen) { buf = mrb_str_resize(mrb, buf, maxlen); } else { mrb_str_modify(mrb, RSTRING(buf)); } fptr = (struct mrb_io *)io_get_open_fptr(mrb, io); if (!fptr->readable) { mrb_raise(mrb, E_IO_ERROR, "not opened for reading"); } ret = read(fptr->fd, RSTRING_PTR(buf), (fsize_t)maxlen); switch (ret) { case 0: /* EOF */ if (maxlen == 0) { buf = mrb_str_new_cstr(mrb, ""); } else { mrb_raise(mrb, E_EOF_ERROR, "sysread failed: End of File"); } break; case -1: /* Error */ mrb_sys_fail(mrb, "sysread failed"); break; default: if (RSTRING_LEN(buf) != ret) { buf = mrb_str_resize(mrb, buf, ret); } break; } return buf; } mrb_value mrb_io_sysseek(mrb_state *mrb, mrb_value io) { struct mrb_io *fptr; off_t pos; mrb_int offset, whence = -1; mrb_get_args(mrb, "i|i", &offset, &whence); if (whence < 0) { whence = 0; } fptr = io_get_open_fptr(mrb, io); pos = lseek(fptr->fd, (off_t)offset, (int)whence); if (pos == -1) { mrb_sys_fail(mrb, "sysseek"); } if (pos > MRB_INT_MAX) { #ifndef MRB_WITHOUT_FLOAT return mrb_float_value(mrb, (mrb_float)pos); #else mrb_raise(mrb, E_IO_ERROR, "sysseek reached too far for MRB_WITHOUT_FLOAT"); #endif } else { return mrb_fixnum_value(pos); } } mrb_value mrb_io_syswrite(mrb_state *mrb, mrb_value io) { struct mrb_io *fptr; mrb_value str, buf; int fd, length; fptr = io_get_open_fptr(mrb, io); if (! fptr->writable) { mrb_raise(mrb, E_IO_ERROR, "not opened for writing"); } mrb_get_args(mrb, "S", &str); if (mrb_type(str) != MRB_TT_STRING) { buf = mrb_funcall(mrb, str, "to_s", 0); } else { buf = str; } if (fptr->fd2 == -1) { fd = fptr->fd; } else { fd = fptr->fd2; } length = write(fd, RSTRING_PTR(buf), (fsize_t)RSTRING_LEN(buf)); if (length == -1) { mrb_sys_fail(mrb, 0); } return mrb_fixnum_value(length); } mrb_value mrb_io_close(mrb_state *mrb, mrb_value self) { struct mrb_io *fptr; fptr = io_get_open_fptr(mrb, self); fptr_finalize(mrb, fptr, FALSE); return mrb_nil_value(); } mrb_value mrb_io_close_write(mrb_state *mrb, mrb_value self) { struct mrb_io *fptr; fptr = io_get_open_fptr(mrb, self); if (close((int)fptr->fd2) == -1) { mrb_sys_fail(mrb, "close"); } return mrb_nil_value(); } mrb_value mrb_io_closed(mrb_state *mrb, mrb_value io) { struct mrb_io *fptr; fptr = (struct mrb_io *)mrb_get_datatype(mrb, io, &mrb_io_type); if (fptr == NULL || fptr->fd >= 0) { return mrb_false_value(); } return mrb_true_value(); } mrb_value mrb_io_pid(mrb_state *mrb, mrb_value io) { struct mrb_io *fptr; fptr = io_get_open_fptr(mrb, io); if (fptr->pid > 0) { return mrb_fixnum_value(fptr->pid); } return mrb_nil_value(); } static struct timeval time2timeval(mrb_state *mrb, mrb_value time) { struct timeval t = { 0, 0 }; switch (mrb_type(time)) { case MRB_TT_FIXNUM: t.tv_sec = (ftime_t)mrb_fixnum(time); t.tv_usec = 0; break; #ifndef MRB_WITHOUT_FLOAT case MRB_TT_FLOAT: t.tv_sec = (ftime_t)mrb_float(time); t.tv_usec = (fsuseconds_t)((mrb_float(time) - t.tv_sec) * 1000000.0); break; #endif default: mrb_raise(mrb, E_TYPE_ERROR, "wrong argument class"); } return t; } static int mrb_io_read_data_pending(mrb_state *mrb, mrb_value io) { mrb_value buf = mrb_iv_get(mrb, io, mrb_intern_cstr(mrb, "@buf")); if (mrb_type(buf) == MRB_TT_STRING && RSTRING_LEN(buf) > 0) { return 1; } return 0; } #if !defined(_WIN32) && !TARGET_OS_IPHONE static mrb_value mrb_io_s_pipe(mrb_state *mrb, mrb_value klass) { mrb_value r = mrb_nil_value(); mrb_value w = mrb_nil_value(); struct mrb_io *fptr_r; struct mrb_io *fptr_w; int pipes[2]; if (mrb_pipe(mrb, pipes) == -1) { mrb_sys_fail(mrb, "pipe"); } r = mrb_obj_value(mrb_data_object_alloc(mrb, mrb_class_ptr(klass), NULL, &mrb_io_type)); mrb_iv_set(mrb, r, mrb_intern_cstr(mrb, "@buf"), mrb_str_new_cstr(mrb, "")); fptr_r = mrb_io_alloc(mrb); fptr_r->fd = pipes[0]; fptr_r->readable = 1; fptr_r->writable = 0; fptr_r->sync = 0; DATA_TYPE(r) = &mrb_io_type; DATA_PTR(r) = fptr_r; w = mrb_obj_value(mrb_data_object_alloc(mrb, mrb_class_ptr(klass), NULL, &mrb_io_type)); mrb_iv_set(mrb, w, mrb_intern_cstr(mrb, "@buf"), mrb_str_new_cstr(mrb, "")); fptr_w = mrb_io_alloc(mrb); fptr_w->fd = pipes[1]; fptr_w->readable = 0; fptr_w->writable = 1; fptr_w->sync = 1; DATA_TYPE(w) = &mrb_io_type; DATA_PTR(w) = fptr_w; return mrb_assoc_new(mrb, r, w); } #endif static mrb_value mrb_io_s_select(mrb_state *mrb, mrb_value klass) { mrb_value *argv; mrb_int argc; mrb_value read, read_io, write, except, timeout, list; struct timeval *tp, timerec; fd_set pset, rset, wset, eset; fd_set *rp, *wp, *ep; struct mrb_io *fptr; int pending = 0; mrb_value result; int max = 0; int interrupt_flag = 0; int i, n; mrb_get_args(mrb, "*", &argv, &argc); if (argc < 1 || argc > 4) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%S for 1..4)", mrb_fixnum_value(argc)); } timeout = mrb_nil_value(); except = mrb_nil_value(); write = mrb_nil_value(); if (argc > 3) timeout = argv[3]; if (argc > 2) except = argv[2]; if (argc > 1) write = argv[1]; read = argv[0]; if (mrb_nil_p(timeout)) { tp = NULL; } else { timerec = time2timeval(mrb, timeout); tp = &timerec; } FD_ZERO(&pset); if (!mrb_nil_p(read)) { mrb_check_type(mrb, read, MRB_TT_ARRAY); rp = &rset; FD_ZERO(rp); for (i = 0; i < RARRAY_LEN(read); i++) { read_io = RARRAY_PTR(read)[i]; fptr = io_get_open_fptr(mrb, read_io); FD_SET(fptr->fd, rp); if (mrb_io_read_data_pending(mrb, read_io)) { pending++; FD_SET(fptr->fd, &pset); } if (max < fptr->fd) max = fptr->fd; } if (pending) { timerec.tv_sec = timerec.tv_usec = 0; tp = &timerec; } } else { rp = NULL; } if (!mrb_nil_p(write)) { mrb_check_type(mrb, write, MRB_TT_ARRAY); wp = &wset; FD_ZERO(wp); for (i = 0; i < RARRAY_LEN(write); i++) { fptr = io_get_open_fptr(mrb, RARRAY_PTR(write)[i]); FD_SET(fptr->fd, wp); if (max < fptr->fd) max = fptr->fd; if (fptr->fd2 >= 0) { FD_SET(fptr->fd2, wp); if (max < fptr->fd2) max = fptr->fd2; } } } else { wp = NULL; } if (!mrb_nil_p(except)) { mrb_check_type(mrb, except, MRB_TT_ARRAY); ep = &eset; FD_ZERO(ep); for (i = 0; i < RARRAY_LEN(except); i++) { fptr = io_get_open_fptr(mrb, RARRAY_PTR(except)[i]); FD_SET(fptr->fd, ep); if (max < fptr->fd) max = fptr->fd; if (fptr->fd2 >= 0) { FD_SET(fptr->fd2, ep); if (max < fptr->fd2) max = fptr->fd2; } } } else { ep = NULL; } max++; retry: n = select(max, rp, wp, ep, tp); if (n < 0) { if (errno != EINTR) mrb_sys_fail(mrb, "select failed"); if (tp == NULL) goto retry; interrupt_flag = 1; } if (!pending && n == 0) return mrb_nil_value(); result = mrb_ary_new_capa(mrb, 3); mrb_ary_push(mrb, result, rp? mrb_ary_new(mrb) : mrb_ary_new_capa(mrb, 0)); mrb_ary_push(mrb, result, wp? mrb_ary_new(mrb) : mrb_ary_new_capa(mrb, 0)); mrb_ary_push(mrb, result, ep? mrb_ary_new(mrb) : mrb_ary_new_capa(mrb, 0)); if (interrupt_flag == 0) { if (rp) { list = RARRAY_PTR(result)[0]; for (i = 0; i < RARRAY_LEN(read); i++) { fptr = io_get_open_fptr(mrb, RARRAY_PTR(read)[i]); if (FD_ISSET(fptr->fd, rp) || FD_ISSET(fptr->fd, &pset)) { mrb_ary_push(mrb, list, RARRAY_PTR(read)[i]); } } } if (wp) { list = RARRAY_PTR(result)[1]; for (i = 0; i < RARRAY_LEN(write); i++) { fptr = io_get_open_fptr(mrb, RARRAY_PTR(write)[i]); if (FD_ISSET(fptr->fd, wp)) { mrb_ary_push(mrb, list, RARRAY_PTR(write)[i]); } else if (fptr->fd2 >= 0 && FD_ISSET(fptr->fd2, wp)) { mrb_ary_push(mrb, list, RARRAY_PTR(write)[i]); } } } if (ep) { list = RARRAY_PTR(result)[2]; for (i = 0; i < RARRAY_LEN(except); i++) { fptr = io_get_open_fptr(mrb, RARRAY_PTR(except)[i]); if (FD_ISSET(fptr->fd, ep)) { mrb_ary_push(mrb, list, RARRAY_PTR(except)[i]); } else if (fptr->fd2 >= 0 && FD_ISSET(fptr->fd2, ep)) { mrb_ary_push(mrb, list, RARRAY_PTR(except)[i]); } } } } return result; } mrb_value mrb_io_fileno(mrb_state *mrb, mrb_value io) { struct mrb_io *fptr; fptr = io_get_open_fptr(mrb, io); return mrb_fixnum_value(fptr->fd); } mrb_value mrb_io_close_on_exec_p(mrb_state *mrb, mrb_value self) { #if defined(F_GETFD) && defined(F_SETFD) && defined(FD_CLOEXEC) struct mrb_io *fptr; int ret; fptr = io_get_open_fptr(mrb, self); if (fptr->fd2 >= 0) { if ((ret = fcntl(fptr->fd2, F_GETFD)) == -1) mrb_sys_fail(mrb, "F_GETFD failed"); if (!(ret & FD_CLOEXEC)) return mrb_false_value(); } if ((ret = fcntl(fptr->fd, F_GETFD)) == -1) mrb_sys_fail(mrb, "F_GETFD failed"); if (!(ret & FD_CLOEXEC)) return mrb_false_value(); return mrb_true_value(); #else mrb_raise(mrb, E_NOTIMP_ERROR, "IO#close_on_exec? is not supported on the platform"); return mrb_false_value(); #endif } mrb_value mrb_io_set_close_on_exec(mrb_state *mrb, mrb_value self) { #if defined(F_GETFD) && defined(F_SETFD) && defined(FD_CLOEXEC) struct mrb_io *fptr; int flag, ret; mrb_bool b; fptr = io_get_open_fptr(mrb, self); mrb_get_args(mrb, "b", &b); flag = b ? FD_CLOEXEC : 0; if (fptr->fd2 >= 0) { if ((ret = fcntl(fptr->fd2, F_GETFD)) == -1) mrb_sys_fail(mrb, "F_GETFD failed"); if ((ret & FD_CLOEXEC) != flag) { ret = (ret & ~FD_CLOEXEC) | flag; ret = fcntl(fptr->fd2, F_SETFD, ret); if (ret == -1) mrb_sys_fail(mrb, "F_SETFD failed"); } } if ((ret = fcntl(fptr->fd, F_GETFD)) == -1) mrb_sys_fail(mrb, "F_GETFD failed"); if ((ret & FD_CLOEXEC) != flag) { ret = (ret & ~FD_CLOEXEC) | flag; ret = fcntl(fptr->fd, F_SETFD, ret); if (ret == -1) mrb_sys_fail(mrb, "F_SETFD failed"); } return mrb_bool_value(b); #else mrb_raise(mrb, E_NOTIMP_ERROR, "IO#close_on_exec= is not supported on the platform"); return mrb_nil_value(); #endif } mrb_value mrb_io_set_sync(mrb_state *mrb, mrb_value self) { struct mrb_io *fptr; mrb_bool b; fptr = io_get_open_fptr(mrb, self); mrb_get_args(mrb, "b", &b); fptr->sync = b; return mrb_bool_value(b); } mrb_value mrb_io_sync(mrb_state *mrb, mrb_value self) { struct mrb_io *fptr; fptr = io_get_open_fptr(mrb, self); return mrb_bool_value(fptr->sync); } void mrb_init_io(mrb_state *mrb) { struct RClass *io; io = mrb_define_class(mrb, "IO", mrb->object_class); MRB_SET_INSTANCE_TT(io, MRB_TT_DATA); mrb_include_module(mrb, io, mrb_module_get(mrb, "Enumerable")); /* 15.2.20.3 */ mrb_define_class_method(mrb, io, "_popen", mrb_io_s_popen, MRB_ARGS_ANY()); mrb_define_class_method(mrb, io, "_sysclose", mrb_io_s_sysclose, MRB_ARGS_REQ(1)); mrb_define_class_method(mrb, io, "for_fd", mrb_io_s_for_fd, MRB_ARGS_ANY()); mrb_define_class_method(mrb, io, "select", mrb_io_s_select, MRB_ARGS_ANY()); mrb_define_class_method(mrb, io, "sysopen", mrb_io_s_sysopen, MRB_ARGS_ANY()); #if !defined(_WIN32) && !TARGET_OS_IPHONE mrb_define_class_method(mrb, io, "_pipe", mrb_io_s_pipe, MRB_ARGS_NONE()); #endif mrb_define_method(mrb, io, "initialize", mrb_io_initialize, MRB_ARGS_ANY()); /* 15.2.20.5.21 (x)*/ mrb_define_method(mrb, io, "initialize_copy", mrb_io_initialize_copy, MRB_ARGS_REQ(1)); mrb_define_method(mrb, io, "_check_readable", mrb_io_check_readable, MRB_ARGS_NONE()); mrb_define_method(mrb, io, "isatty", mrb_io_isatty, MRB_ARGS_NONE()); mrb_define_method(mrb, io, "sync", mrb_io_sync, MRB_ARGS_NONE()); mrb_define_method(mrb, io, "sync=", mrb_io_set_sync, MRB_ARGS_REQ(1)); mrb_define_method(mrb, io, "sysread", mrb_io_sysread, MRB_ARGS_ANY()); mrb_define_method(mrb, io, "sysseek", mrb_io_sysseek, MRB_ARGS_REQ(1)); mrb_define_method(mrb, io, "syswrite", mrb_io_syswrite, MRB_ARGS_REQ(1)); mrb_define_method(mrb, io, "close", mrb_io_close, MRB_ARGS_NONE()); /* 15.2.20.5.1 */ mrb_define_method(mrb, io, "close_write", mrb_io_close_write, MRB_ARGS_NONE()); mrb_define_method(mrb, io, "close_on_exec=", mrb_io_set_close_on_exec, MRB_ARGS_REQ(1)); mrb_define_method(mrb, io, "close_on_exec?", mrb_io_close_on_exec_p, MRB_ARGS_NONE()); mrb_define_method(mrb, io, "closed?", mrb_io_closed, MRB_ARGS_NONE()); /* 15.2.20.5.2 */ mrb_define_method(mrb, io, "pid", mrb_io_pid, MRB_ARGS_NONE()); /* 15.2.20.5.2 */ mrb_define_method(mrb, io, "fileno", mrb_io_fileno, MRB_ARGS_NONE()); mrb_gv_set(mrb, mrb_intern_cstr(mrb, "$/"), mrb_str_new_cstr(mrb, "\n")); } mruby-2.0.0/mrbgems/mruby-io/src/mruby_io_gem.c000066400000000000000000000005461340361412400214610ustar00rootroot00000000000000#include "mruby.h" void mrb_init_io(mrb_state *mrb); void mrb_init_file(mrb_state *mrb); void mrb_init_file_test(mrb_state *mrb); #define DONE mrb_gc_arena_restore(mrb, 0) void mrb_mruby_io_gem_init(mrb_state* mrb) { mrb_init_io(mrb); DONE; mrb_init_file(mrb); DONE; mrb_init_file_test(mrb); DONE; } void mrb_mruby_io_gem_final(mrb_state* mrb) { } mruby-2.0.0/mrbgems/mruby-io/test/000077500000000000000000000000001340361412400170235ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-io/test/file.rb000066400000000000000000000133371340361412400202760ustar00rootroot00000000000000## # IO Test assert('File', '15.2.21') do File.class == Class end assert('File', '15.2.21.2') do File.superclass == IO end assert('File TEST SETUP') do MRubyIOTestUtil.io_test_setup end assert('File#initialize', '15.2.21.4.1') do io = File.open($mrbtest_io_rfname, "r") assert_nil io.close assert_raise IOError do io.close end end assert('File#path', '15.2.21.4.2') do io = File.open($mrbtest_io_rfname, "r") assert_equal $mrbtest_io_msg, io.read assert_equal $mrbtest_io_rfname, io.path io.close assert_equal $mrbtest_io_rfname, io.path io.closed? end assert('File.basename') do assert_equal '/', File.basename('//') assert_equal 'a', File.basename('/a/') assert_equal 'b', File.basename('/a/b') assert_equal 'b', File.basename('../a/b') end assert('File.dirname') do assert_equal '.', File.dirname('') assert_equal '.', File.dirname('a') assert_equal '/', File.dirname('/a') assert_equal 'a', File.dirname('a/b') assert_equal '/a', File.dirname('/a/b') end assert('File.extname') do assert_equal '.txt', File.extname('foo/foo.txt') assert_equal '.gz', File.extname('foo/foo.tar.gz') assert_equal '', File.extname('foo/bar') assert_equal '', File.extname('foo/.bar') assert_equal '', File.extname('foo.txt/bar') assert_equal '', File.extname('.foo') end assert('File#flock') do f = File.open $mrbtest_io_rfname begin assert_equal(f.flock(File::LOCK_SH), 0) assert_equal(f.flock(File::LOCK_UN), 0) assert_equal(f.flock(File::LOCK_EX | File::LOCK_NB), 0) assert_equal(f.flock(File::LOCK_UN), 0) rescue NotImplementedError => e skip e.message ensure f.close end end assert('File#mtime') do unless Object.const_defined?(:Time) skip "File#mtime require Time" end begin File.open("#{$mrbtest_io_wfname}.mtime", 'w') do |f| assert_equal Time, f.mtime.class File.open("#{$mrbtest_io_wfname}.mtime", 'r') do |f2| assert_equal true, f.mtime == f2.mtime end end ensure File.delete("#{$mrbtest_io_wfname}.mtime") end end assert('File.join') do assert_equal "", File.join() assert_equal "a", File.join("a") assert_equal "/a", File.join("/a") assert_equal "a/", File.join("a/") assert_equal "a/b/c", File.join("a", "b", "c") assert_equal "/a/b/c", File.join("/a", "b", "c") assert_equal "a/b/c/", File.join("a", "b", "c/") assert_equal "a/b/c", File.join("a/", "/b/", "/c") assert_equal "a/b/c", File.join(["a", "b", "c"]) assert_equal "a/b/c", File.join("a", ["b", ["c"]]) end assert('File.realpath') do if File::ALT_SEPARATOR readme_path = File._getwd + File::ALT_SEPARATOR + "README.md" assert_equal readme_path, File.realpath("README.md") else dir = MRubyIOTestUtil.mkdtemp("mruby-io-test.XXXXXX") begin dir1 = File.realpath($mrbtest_io_rfname) dir2 = File.realpath("./#{dir}//./../#{$mrbtest_io_symlinkname}") assert_equal dir1, dir2 ensure MRubyIOTestUtil.rmdir dir end end end assert("File.readlink") do begin assert_equal $mrbtest_io_rfname, File.readlink($mrbtest_io_symlinkname) rescue NotImplementedError => e skip e.message end end assert("File.readlink fails with non-symlink") do skip "readlink is not supported on this platform" if MRubyIOTestUtil.win? begin e2 = nil assert_raise(RuntimeError) { begin File.readlink($mrbtest_io_rfname) rescue => e if Object.const_defined?(:SystemCallError) and e.kind_of?(SystemCallError) raise RuntimeError, "SystemCallError converted to RuntimeError" end raise e rescue NotImplementedError => e e2 = e end } raise e2 if e2 rescue NotImplementedError => e skip e.message end end assert('File.expand_path') do assert_equal "/", File.expand_path("..", "/tmp"), "parent path with base_dir (1)" assert_equal "/tmp", File.expand_path("..", "/tmp/mruby"), "parent path with base_dir (2)" assert_equal "/home", File.expand_path("/home"), "absolute" assert_equal "/home", File.expand_path("/home", "."), "absolute with base_dir" assert_equal "/hoge", File.expand_path("/tmp/..//hoge") assert_equal "/hoge", File.expand_path("////tmp/..///////hoge") assert_equal "/", File.expand_path("../../../..", "/") if File._getwd[1] == ":" drive_letter = File._getwd[0] assert_equal drive_letter + ":\\", File.expand_path(([".."] * 100).join("/")) else assert_equal "/", File.expand_path(([".."] * 100).join("/")) end end assert('File.expand_path (with ENV)') do skip unless Object.const_defined?(:ENV) && ENV['HOME'] assert_equal ENV['HOME'], File.expand_path("~/"), "home" assert_equal ENV['HOME'], File.expand_path("~/", "/"), "home with base_dir" assert_equal "#{ENV['HOME']}/user", File.expand_path("user", ENV['HOME']), "relative with base_dir" end assert('File.path') do assert_equal "", File.path("") assert_equal "a/b/c", File.path("a/b/c") assert_equal "a/../b/./c", File.path("a/../b/./c") assert_raise(TypeError) { File.path(nil) } assert_raise(TypeError) { File.path(123) } end assert('File.symlink') do target_name = "/usr/bin" symlink_name = "test-bin-dummy" if !File.exist?(target_name) skip("target directory of File.symlink is not found") else begin assert_equal 0, File.symlink(target_name, symlink_name) begin assert_equal true, File.symlink?(symlink_name) ensure File.delete symlink_name end rescue NotImplementedError => e skip e.message end end end assert('File.chmod') do File.open('chmod-test', 'w') {} begin assert_equal 1, File.chmod(0400, 'chmod-test') ensure File.delete('chmod-test') end end assert('File TEST CLEANUP') do assert_nil MRubyIOTestUtil.io_test_cleanup end mruby-2.0.0/mrbgems/mruby-io/test/file_test.rb000066400000000000000000000054421340361412400213330ustar00rootroot00000000000000## # FileTest assert('FileTest TEST SETUP') do MRubyIOTestUtil.io_test_setup end assert("FileTest.directory?") do dir = MRubyIOTestUtil.mkdtemp("mruby-io-test.XXXXXX") begin assert_true FileTest.directory?(dir) assert_false FileTest.directory?($mrbtest_io_rfname) ensure MRubyIOTestUtil.rmdir dir end end assert("FileTest.exist?") do assert_equal true, FileTest.exist?($mrbtest_io_rfname), "filename - exist" assert_equal false, FileTest.exist?($mrbtest_io_rfname + "-"), "filename - not exist" io = IO.new(IO.sysopen($mrbtest_io_rfname)) assert_equal true, FileTest.exist?(io), "io obj - exist" io.close assert_equal true, io.closed? assert_raise IOError do FileTest.exist?(io) end end assert("FileTest.file?") do dir = MRubyIOTestUtil.mkdtemp("mruby-io-test.XXXXXX") begin assert_true FileTest.file?($mrbtest_io_rfname) assert_false FileTest.file?(dir) ensure MRubyIOTestUtil.rmdir dir end end assert("FileTest.pipe?") do begin assert_equal false, FileTest.pipe?("/tmp") io = IO.popen("ls") assert_equal true, FileTest.pipe?(io) rescue NotImplementedError => e skip e.message end end assert('FileTest.size') do assert_equal FileTest.size($mrbtest_io_rfname), $mrbtest_io_msg.size assert_equal FileTest.size($mrbtest_io_wfname), 0 end assert("FileTest.size?") do assert_equal $mrbtest_io_msg.size, FileTest.size?($mrbtest_io_rfname) assert_equal nil, FileTest.size?($mrbtest_io_wfname) assert_equal nil, FileTest.size?("not-exist-test-target-file") fp1 = File.open($mrbtest_io_rfname) fp2 = File.open($mrbtest_io_wfname) assert_equal $mrbtest_io_msg.size, FileTest.size?(fp1) assert_equal nil, FileTest.size?(fp2) fp1.close fp2.close assert_raise IOError do FileTest.size?(fp1) end assert_raise IOError do FileTest.size?(fp2) end fp1.closed? && fp2.closed? end assert("FileTest.socket?") do begin assert_true FileTest.socket?($mrbtest_io_socketname) rescue NotImplementedError => e skip e.message end end assert("FileTest.symlink?") do begin assert_true FileTest.symlink?($mrbtest_io_symlinkname) rescue NotImplementedError => e skip e.message end end assert("FileTest.zero?") do assert_equal false, FileTest.zero?($mrbtest_io_rfname) assert_equal true, FileTest.zero?($mrbtest_io_wfname) assert_equal false, FileTest.zero?("not-exist-test-target-file") fp1 = File.open($mrbtest_io_rfname) fp2 = File.open($mrbtest_io_wfname) assert_equal false, FileTest.zero?(fp1) assert_equal true, FileTest.zero?(fp2) fp1.close fp2.close assert_raise IOError do FileTest.zero?(fp1) end assert_raise IOError do FileTest.zero?(fp2) end fp1.closed? && fp2.closed? end assert('FileTest TEST CLEANUP') do assert_nil MRubyIOTestUtil.io_test_cleanup end mruby-2.0.0/mrbgems/mruby-io/test/gc_filedes.sh000066400000000000000000000001021340361412400214340ustar00rootroot00000000000000#!/bin/sh ulimit -n 20 mruby -e '100.times { File.open "'$0'" }' mruby-2.0.0/mrbgems/mruby-io/test/io.rb000066400000000000000000000346111340361412400177640ustar00rootroot00000000000000## # IO Test unless Object.respond_to? :assert_nothing_raised def assert_nothing_raised(*exp) ret = true if $mrbtest_assert $mrbtest_assert_idx += 1 msg = exp.last.class == String ? exp.pop : "" begin yield rescue Exception => e msg = "#{msg} exception raised." diff = " Class: <#{e.class}>\n" + " Message: #{e.message}" $mrbtest_assert.push([$mrbtest_assert_idx, msg, diff]) ret = false end end ret end end assert('IO TEST SETUP') do MRubyIOTestUtil.io_test_setup $cr = MRubyIOTestUtil.win? ? 1 : 0 # "\n" include CR or not end assert('IO', '15.2.20') do assert_equal(Class, IO.class) end assert('IO', '15.2.20.2') do assert_equal(Object, IO.superclass) end assert('IO', '15.2.20.3') do assert_include(IO.ancestors, Enumerable) end assert('IO.open', '15.2.20.4.1') do fd = IO.sysopen $mrbtest_io_rfname assert_equal Fixnum, fd.class io = IO.open fd assert_equal IO, io.class assert_equal $mrbtest_io_msg, io.read io.close fd = IO.sysopen $mrbtest_io_rfname IO.open(fd) do |io| assert_equal $mrbtest_io_msg, io.read end true end assert('IO#close', '15.2.20.5.1') do io = IO.new(IO.sysopen($mrbtest_io_rfname)) assert_nil io.close end assert('IO#closed?', '15.2.20.5.2') do io = IO.new(IO.sysopen($mrbtest_io_rfname)) assert_false io.closed? io.close assert_true io.closed? end #assert('IO#each', '15.2.20.5.3') do #assert('IO#each_byte', '15.2.20.5.4') do #assert('IO#each_line', '15.2.20.5.5') do assert('IO#eof?', '15.2.20.5.6') do io = IO.new(IO.sysopen($mrbtest_io_wfname, 'w'), 'w') assert_raise(IOError) do io.eof? end io.close # empty file io = IO.open(IO.sysopen($mrbtest_io_wfname, 'w'), 'w') io.close io = IO.open(IO.sysopen($mrbtest_io_wfname, 'r'), 'r') assert_true io.eof? io.close # nonempty file io = IO.new(IO.sysopen($mrbtest_io_rfname)) assert_false io.eof? io.readchar assert_false io.eof? io.read assert_true io.eof? io.close true end assert('IO#flush', '15.2.20.5.7') do # Note: mruby-io does not have any buffer to be flushed now. io = IO.new(IO.sysopen($mrbtest_io_wfname)) assert_equal io, io.flush io.close assert_raise(IOError) do io.flush end end assert('IO#getc', '15.2.20.5.8') do io = IO.new(IO.sysopen($mrbtest_io_rfname)) $mrbtest_io_msg.each_char { |ch| assert_equal ch, io.getc } assert_equal nil, io.getc io.close true end #assert('IO#gets', '15.2.20.5.9') do #assert('IO#initialize_copy', '15.2.20.5.10') do #assert('IO#print', '15.2.20.5.11') do #assert('IO#putc', '15.2.20.5.12') do #assert('IO#puts', '15.2.20.5.13') do assert('IO#read', '15.2.20.5.14') do IO.open(IO.sysopen($mrbtest_io_rfname)) do |io| assert_raise(ArgumentError) { io.read(-5) } assert_raise(TypeError) { io.read("str") } len = $mrbtest_io_msg.length assert_equal '', io.read(0) assert_equal 'mruby', io.read(5) assert_equal $mrbtest_io_msg[5,len], io.read(len) assert_equal "", io.read assert_nil io.read(1) end IO.open(IO.sysopen($mrbtest_io_rfname)) do |io| assert_equal $mrbtest_io_msg, io.read end end assert "IO#read(n) with n > IO::BUF_SIZE" do skip "pipe is not supported on this platform" if MRubyIOTestUtil.win? r,w = IO.pipe n = IO::BUF_SIZE+1 w.write 'a'*n assert_equal r.read(n), 'a'*n end assert('IO#readchar', '15.2.20.5.15') do # almost same as IO#getc IO.open(IO.sysopen($mrbtest_io_rfname)) do |io| $mrbtest_io_msg.each_char { |ch| assert_equal ch, io.readchar } assert_raise(EOFError) do io.readchar end end end #assert('IO#readline', '15.2.20.5.16') do #assert('IO#readlines', '15.2.20.5.17') do assert('IO#sync', '15.2.20.5.18') do io = IO.new(IO.sysopen($mrbtest_io_rfname)) s = io.sync assert_true(s == true || s == false) io.close assert_raise(IOError) do io.sync end end assert('IO#sync=', '15.2.20.5.19') do io = IO.new(IO.sysopen($mrbtest_io_rfname)) io.sync = true assert_true io.sync io.sync = false assert_false io.sync io.close assert_raise(IOError) do io.sync = true end end assert('IO#write', '15.2.20.5.20') do io = IO.open(IO.sysopen($mrbtest_io_wfname)) assert_equal 0, io.write("") io.close io = IO.open(IO.sysopen($mrbtest_io_wfname, "r+"), "r+") assert_equal 7, io.write("abcdefg") io.rewind assert_equal "ab", io.read(2) assert_equal 3, io.write("123") io.rewind assert_equal "ab123fg", io.read io.close true end assert('IO#<<') do io = IO.open(IO.sysopen($mrbtest_io_wfname)) io << "" << "" assert_equal 0, io.pos io.close true end assert('IO#dup for readable') do io = IO.new(IO.sysopen($mrbtest_io_rfname)) dup = io.dup assert_true io != dup assert_true io.fileno != dup.fileno begin assert_true dup.close_on_exec? rescue NotImplementedError end assert_equal 'm', dup.sysread(1) assert_equal 'r', io.sysread(1) assert_equal 'u', dup.sysread(1) assert_equal 'b', io.sysread(1) assert_equal 'y', dup.sysread(1) dup.close assert_false io.closed? io.close true end assert('IO#dup for writable') do io = IO.open(IO.sysopen($mrbtest_io_wfname, 'w+'), 'w+') dup = io.dup io.syswrite "mruby" assert_equal 5, dup.sysseek(0, IO::SEEK_CUR) io.sysseek 0, IO::SEEK_SET assert_equal 0, dup.sysseek(0, IO::SEEK_CUR) assert_equal "mruby", dup.sysread(5) dup.close io.close true end assert('IO.for_fd') do fd = IO.sysopen($mrbtest_io_rfname) io = IO.for_fd(fd) assert_equal $mrbtest_io_msg, io.read io.close true end assert('IO.new') do io = IO.new(0) io.close true end assert('IO gc check') do 100.times { IO.new(0) } end assert('IO.sysopen("./nonexistent")') do if Object.const_defined? :Errno eclass = Errno::ENOENT else eclass = RuntimeError end assert_raise eclass do fd = IO.sysopen "./nonexistent" IO._sysclose fd end end assert('IO.sysopen, IO#sysread') do fd = IO.sysopen $mrbtest_io_rfname io = IO.new fd str1 = " " str2 = io.sysread(5, str1) assert_equal $mrbtest_io_msg[0,5], str1 assert_equal $mrbtest_io_msg[0,5], str2 assert_raise EOFError do io.sysread(10000) io.sysread(10000) end assert_raise RuntimeError do io.sysread(5, "abcde".freeze) end io.close assert_equal "", io.sysread(0) assert_raise(IOError) { io.sysread(1) } assert_raise(ArgumentError) { io.sysread(-1) } io.closed? fd = IO.sysopen $mrbtest_io_wfname, "w" io = IO.new fd, "w" assert_raise(IOError) { io.sysread(1) } io.close true end assert('IO.sysopen, IO#syswrite') do fd = IO.sysopen $mrbtest_io_wfname, "w" io = IO.new fd, "w" str = "abcdefg" len = io.syswrite(str) assert_equal str.size, len io.close io = IO.new(IO.sysopen($mrbtest_io_rfname), "r") assert_raise(IOError) { io.syswrite("a") } io.close true end assert('IO#_read_buf') do fd = IO.sysopen $mrbtest_io_rfname io = IO.new fd def io._buf @buf end msg_len = $mrbtest_io_msg.size assert_equal '', io._buf assert_equal $mrbtest_io_msg, io._read_buf assert_equal $mrbtest_io_msg, io._buf assert_equal 'mruby', io.read(5) assert_equal 5, io.pos assert_equal msg_len - 5, io._buf.size assert_equal $mrbtest_io_msg[5,100], io.read assert_equal 0, io._buf.size assert_raise EOFError do io._read_buf end assert_equal true, io.eof assert_equal true, io.eof? io.close io.closed? end assert('IO#isatty') do skip "isatty is not supported on this platform" if MRubyIOTestUtil.win? f1 = File.open("/dev/tty") f2 = File.open($mrbtest_io_rfname) assert_true f1.isatty assert_false f2.isatty f1.close f2.close true end assert('IO#pos=, IO#seek') do fd = IO.sysopen $mrbtest_io_rfname io = IO.new fd def io._buf @buf end assert_equal 'm', io.getc assert_equal 1, io.pos assert_equal 0, io.seek(0) assert_equal 0, io.pos io.close io.closed? end assert('IO#rewind') do fd = IO.sysopen $mrbtest_io_rfname io = IO.new fd assert_equal 'm', io.getc assert_equal 1, io.pos assert_equal 0, io.rewind assert_equal 0, io.pos io.close io.closed? end assert('IO#gets') do fd = IO.sysopen $mrbtest_io_rfname io = IO.new fd # gets without arguments assert_equal $mrbtest_io_msg, io.gets, "gets without arguments" assert_equal nil, io.gets, "gets returns nil, when EOF" # gets with limit io.pos = 0 assert_equal $mrbtest_io_msg[0, 5], io.gets(5), "gets with limit" # gets with rs io.pos = 0 assert_equal $mrbtest_io_msg[0, 6], io.gets(' '), "gets with rs" # gets with rs, limit io.pos = 0 assert_equal $mrbtest_io_msg[0, 5], io.gets(' ', 5), "gets with rs, limit" io.close assert_equal true, io.closed?, "close success" # reading many-lines file. fd = IO.sysopen $mrbtest_io_wfname, "w" io = IO.new fd, "w" io.write "0123456789" * 2 + "\na" assert_equal 22 + $cr, io.pos io.close assert_equal true, io.closed? fd = IO.sysopen $mrbtest_io_wfname io = IO.new fd line = io.gets # gets first line assert_equal "0123456789" * 2 + "\n", line, "gets first line" assert_equal 21, line.size assert_equal 21 + $cr, io.pos # gets second line assert_equal "a", io.gets, "gets second line" # gets third line assert_equal nil, io.gets, "gets third line; returns nil" io.close io.closed? end assert('IO#gets - paragraph mode') do fd = IO.sysopen $mrbtest_io_wfname, "w" io = IO.new fd, "w" io.write "0" * 10 + "\n" io.write "1" * 10 + "\n\n" io.write "2" * 10 + "\n" assert_equal 34 + $cr * 4, io.pos io.close assert_equal true, io.closed? fd = IO.sysopen $mrbtest_io_wfname io = IO.new fd para1 = "#{'0' * 10}\n#{'1' * 10}\n\n" text1 = io.gets("") assert_equal para1, text1 para2 = "#{'2' * 10}\n" text2 = io.gets("") assert_equal para2, text2 io.close io.closed? end assert('IO.popen') do begin $? = nil io = IO.popen("echo mruby-io") assert_true io.close_on_exec? assert_equal Fixnum, io.pid.class out = io.read assert_equal out.class, String assert_include out, 'mruby-io' io.close if Object.const_defined? :Process assert_true $?.success? else assert_equal 0, $? end assert_true io.closed? rescue NotImplementedError => e skip e.message end end assert('IO.popen with in option') do begin IO.pipe do |r, w| w.write 'hello' w.close assert_equal "hello", IO.popen("cat", "r", in: r) { |i| i.read } assert_equal "", r.read end assert_raise(ArgumentError) { IO.popen("hello", "r", in: Object.new) } rescue NotImplementedError => e skip e.message end end assert('IO.popen with out option') do begin IO.pipe do |r, w| IO.popen("echo 'hello'", "w", out: w) {} w.close assert_equal "hello\n", r.read end rescue NotImplementedError => e skip e.message end end assert('IO.popen with err option') do begin IO.pipe do |r, w| assert_equal "", IO.popen("echo 'hello' 1>&2", "r", err: w) { |i| i.read } w.close assert_equal "hello\n", r.read end rescue NotImplementedError => e skip e.message end end assert('IO.read') do # empty file fd = IO.sysopen $mrbtest_io_wfname, "w" io = IO.new fd, "w" io.close assert_equal "", IO.read($mrbtest_io_wfname) assert_equal nil, IO.read($mrbtest_io_wfname, 1) # one byte file fd = IO.sysopen $mrbtest_io_wfname, "w" io = IO.new fd, "w" io.write "123" io.close assert_equal "123", IO.read($mrbtest_io_wfname) assert_equal "", IO.read($mrbtest_io_wfname, 0) assert_equal "1", IO.read($mrbtest_io_wfname, 1) assert_equal "", IO.read($mrbtest_io_wfname, 0, 10) assert_equal "23", IO.read($mrbtest_io_wfname, 2, 1) assert_equal "23", IO.read($mrbtest_io_wfname, 10, 1) assert_equal "", IO.read($mrbtest_io_wfname, nil, 10) assert_equal nil, IO.read($mrbtest_io_wfname, 1, 10) end assert('IO#fileno') do fd = IO.sysopen $mrbtest_io_rfname io = IO.new fd assert_equal io.fileno, fd assert_equal io.to_i, fd io.close io.closed? end assert('IO#close_on_exec') do fd = IO.sysopen $mrbtest_io_wfname, "w" io = IO.new fd, "w" begin # IO.sysopen opens a file descripter with O_CLOEXEC flag. assert_true io.close_on_exec? rescue ScriptError io.close skip "IO\#close_on_exec is not implemented." end io.close_on_exec = false assert_equal(false, io.close_on_exec?) io.close_on_exec = true assert_equal(true, io.close_on_exec?) io.close_on_exec = false assert_equal(false, io.close_on_exec?) io.close io.closed? begin r, w = IO.pipe assert_equal(true, r.close_on_exec?) r.close_on_exec = false assert_equal(false, r.close_on_exec?) r.close_on_exec = true assert_equal(true, r.close_on_exec?) assert_equal(true, w.close_on_exec?) w.close_on_exec = false assert_equal(false, w.close_on_exec?) w.close_on_exec = true assert_equal(true, w.close_on_exec?) ensure r.close unless r.closed? w.close unless w.closed? end end assert('IO#sysseek') do IO.open(IO.sysopen($mrbtest_io_rfname)) do |io| assert_equal 2, io.sysseek(2) assert_equal 5, io.sysseek(3, IO::SEEK_CUR) # 2 + 3 => 5 assert_equal $mrbtest_io_msg.size - 4, io.sysseek(-4, IO::SEEK_END) end end assert('IO.pipe') do begin called = false IO.pipe do |r, w| assert_true r.kind_of?(IO) assert_true w.kind_of?(IO) assert_false r.closed? assert_false w.closed? assert_true FileTest.pipe?(r) assert_true FileTest.pipe?(w) assert_nil r.pid assert_nil w.pid assert_true 2 < r.fileno assert_true 2 < w.fileno assert_true r.fileno != w.fileno assert_false r.sync assert_true w.sync assert_equal 8, w.write('test for') assert_equal 'test', r.read(4) assert_equal ' for', r.read(4) assert_equal 5, w.write(' pipe') assert_equal nil, w.close assert_equal ' pipe', r.read called = true assert_raise(IOError) { r.write 'test' } # TODO: # This assert expect raise IOError but got RuntimeError # Because mruby-io not have flag for I/O readable # assert_raise(IOError) { w.read } end assert_true called assert_nothing_raised do IO.pipe { |r, w| r.close; w.close } end rescue NotImplementedError => e skip e.message end end assert('`cmd`') do begin assert_equal `echo foo`, "foo\n" rescue NotImplementedError => e skip e.message end end assert('IO TEST CLEANUP') do assert_nil MRubyIOTestUtil.io_test_cleanup end mruby-2.0.0/mrbgems/mruby-io/test/mruby_io_test.c000066400000000000000000000153031340361412400220550ustar00rootroot00000000000000#include #include #if defined(_WIN32) || defined(_WIN64) #include #include #include #include #include #include #include #if (!defined __MINGW64__) && (!defined __MINGW32__) typedef int mode_t; #endif #define open _open #define close _close #ifdef _MSC_VER #include static int mkstemp(char *p) { int fd; char* fname = _mktemp(p); if (fname == NULL) return -1; fd = open(fname, O_RDWR | O_CREAT | O_EXCL, _S_IREAD | _S_IWRITE); if (fd >= 0) return fd; return -1; } #endif static char* mkdtemp(char *temp) { char *path = _mktemp(temp); if (path[0] == 0) return NULL; if (_mkdir(path) < 0) return NULL; return path; } #define umask(mode) _umask(mode) #define rmdir(path) _rmdir(path) #else #include #include #include #endif #include #include #include #include "mruby.h" #include "mruby/array.h" #include "mruby/error.h" #include "mruby/string.h" #include "mruby/variable.h" static mrb_value mrb_io_test_io_setup(mrb_state *mrb, mrb_value self) { char rfname[] = "tmp.mruby-io-test-r.XXXXXXXX"; char wfname[] = "tmp.mruby-io-test-w.XXXXXXXX"; char symlinkname[] = "tmp.mruby-io-test-l.XXXXXXXX"; char socketname[] = "tmp.mruby-io-test-s.XXXXXXXX"; char msg[] = "mruby io test\n"; mode_t mask; int fd0, fd1; FILE *fp; #if !defined(_WIN32) && !defined(_WIN64) int fd2, fd3; struct sockaddr_un sun0; #endif mask = umask(077); fd0 = mkstemp(rfname); fd1 = mkstemp(wfname); if (fd0 == -1 || fd1 == -1) { mrb_raise(mrb, E_RUNTIME_ERROR, "can't create temporary file"); return mrb_nil_value(); } close(fd0); close(fd1); #if !defined(_WIN32) && !defined(_WIN64) fd2 = mkstemp(symlinkname); fd3 = mkstemp(socketname); if (fd2 == -1 || fd3 == -1) { mrb_raise(mrb, E_RUNTIME_ERROR, "can't create temporary file"); return mrb_nil_value(); } #endif umask(mask); mrb_gv_set(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_rfname"), mrb_str_new_cstr(mrb, rfname)); mrb_gv_set(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_wfname"), mrb_str_new_cstr(mrb, wfname)); mrb_gv_set(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_symlinkname"), mrb_str_new_cstr(mrb, symlinkname)); mrb_gv_set(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_socketname"), mrb_str_new_cstr(mrb, socketname)); mrb_gv_set(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_msg"), mrb_str_new_cstr(mrb, msg)); fp = fopen(rfname, "wb"); if (fp == NULL) { mrb_raise(mrb, E_RUNTIME_ERROR, "can't open temporary file"); return mrb_nil_value(); } fputs(msg, fp); fclose(fp); fp = fopen(wfname, "wb"); if (fp == NULL) { mrb_raise(mrb, E_RUNTIME_ERROR, "can't open temporary file"); return mrb_nil_value(); } fclose(fp); #if !defined(_WIN32) && !defined(_WIN64) unlink(symlinkname); close(fd2); if (symlink(rfname, symlinkname) == -1) { mrb_raise(mrb, E_RUNTIME_ERROR, "can't make a symbolic link"); } unlink(socketname); close(fd3); fd3 = socket(AF_UNIX, SOCK_STREAM, 0); if (fd3 == -1) { mrb_raise(mrb, E_RUNTIME_ERROR, "can't make a socket"); } sun0.sun_family = AF_UNIX; snprintf(sun0.sun_path, sizeof(sun0.sun_path), "%s", socketname); if (bind(fd3, (struct sockaddr *)&sun0, sizeof(sun0)) == -1) { mrb_raisef(mrb, E_RUNTIME_ERROR, "can't bind AF_UNIX socket to %S: %S", mrb_str_new_cstr(mrb, sun0.sun_path), mrb_fixnum_value(errno)); } close(fd3); #endif return mrb_true_value(); } static mrb_value mrb_io_test_io_cleanup(mrb_state *mrb, mrb_value self) { mrb_value rfname = mrb_gv_get(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_rfname")); mrb_value wfname = mrb_gv_get(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_wfname")); mrb_value symlinkname = mrb_gv_get(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_symlinkname")); mrb_value socketname = mrb_gv_get(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_socketname")); if (mrb_type(rfname) == MRB_TT_STRING) { remove(RSTRING_PTR(rfname)); } if (mrb_type(wfname) == MRB_TT_STRING) { remove(RSTRING_PTR(wfname)); } if (mrb_type(symlinkname) == MRB_TT_STRING) { remove(RSTRING_PTR(symlinkname)); } if (mrb_type(socketname) == MRB_TT_STRING) { remove(RSTRING_PTR(socketname)); } mrb_gv_set(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_rfname"), mrb_nil_value()); mrb_gv_set(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_wfname"), mrb_nil_value()); mrb_gv_set(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_symlinkname"), mrb_nil_value()); mrb_gv_set(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_socketname"), mrb_nil_value()); mrb_gv_set(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_msg"), mrb_nil_value()); return mrb_nil_value(); } static mrb_value mrb_io_test_file_setup(mrb_state *mrb, mrb_value self) { mrb_value ary = mrb_io_test_io_setup(mrb, self); #if !defined(_WIN32) && !defined(_WIN64) if (symlink("/usr/bin", "test-bin") == -1) { mrb_raise(mrb, E_RUNTIME_ERROR, "can't make a symbolic link"); } #endif return ary; } static mrb_value mrb_io_test_file_cleanup(mrb_state *mrb, mrb_value self) { mrb_io_test_io_cleanup(mrb, self); remove("test-bin"); return mrb_nil_value(); } static mrb_value mrb_io_test_mkdtemp(mrb_state *mrb, mrb_value klass) { mrb_value str; char *cp; mrb_get_args(mrb, "S", &str); cp = mrb_str_to_cstr(mrb, str); if (mkdtemp(cp) == NULL) { mrb_sys_fail(mrb, "mkdtemp"); } return mrb_str_new_cstr(mrb, cp); } static mrb_value mrb_io_test_rmdir(mrb_state *mrb, mrb_value klass) { mrb_value str; char *cp; mrb_get_args(mrb, "S", &str); cp = mrb_str_to_cstr(mrb, str); if (rmdir(cp) == -1) { mrb_sys_fail(mrb, "rmdir"); } return mrb_true_value(); } mrb_value mrb_io_win_p(mrb_state *mrb, mrb_value klass) { #if defined(_WIN32) || defined(_WIN64) # if defined(__CYGWIN__) || defined(__CYGWIN32__) return mrb_false_value(); # else return mrb_true_value(); # endif #else return mrb_false_value(); #endif } void mrb_mruby_io_gem_test(mrb_state* mrb) { struct RClass *io_test = mrb_define_module(mrb, "MRubyIOTestUtil"); mrb_define_class_method(mrb, io_test, "io_test_setup", mrb_io_test_io_setup, MRB_ARGS_NONE()); mrb_define_class_method(mrb, io_test, "io_test_cleanup", mrb_io_test_io_cleanup, MRB_ARGS_NONE()); mrb_define_class_method(mrb, io_test, "file_test_setup", mrb_io_test_file_setup, MRB_ARGS_NONE()); mrb_define_class_method(mrb, io_test, "file_test_cleanup", mrb_io_test_file_cleanup, MRB_ARGS_NONE()); mrb_define_class_method(mrb, io_test, "mkdtemp", mrb_io_test_mkdtemp, MRB_ARGS_REQ(1)); mrb_define_class_method(mrb, io_test, "rmdir", mrb_io_test_rmdir, MRB_ARGS_REQ(1)); mrb_define_class_method(mrb, io_test, "win?", mrb_io_win_p, MRB_ARGS_NONE()); } mruby-2.0.0/mrbgems/mruby-kernel-ext/000077500000000000000000000000001340361412400175135ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-kernel-ext/mrbgem.rake000066400000000000000000000002461340361412400216320ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-kernel-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Kernel module extension' end mruby-2.0.0/mrbgems/mruby-kernel-ext/mrblib/000077500000000000000000000000001340361412400207625ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-kernel-ext/mrblib/kernel.rb000066400000000000000000000005511340361412400225700ustar00rootroot00000000000000module Kernel # call-seq: # obj.yield_self {|_obj|...} -> an_object # obj.then {|_obj|...} -> an_object # # Yields obj and returns the result. # # 'my string'.yield_self {|s|s.upcase} #=> "MY STRING" # def yield_self(&block) return to_enum :yield_self unless block block.call(self) end alias then yield_self end mruby-2.0.0/mrbgems/mruby-kernel-ext/src/000077500000000000000000000000001340361412400203025ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-kernel-ext/src/kernel.c000066400000000000000000000141101340361412400217230ustar00rootroot00000000000000#include #include #include #include #include static mrb_value mrb_f_caller(mrb_state *mrb, mrb_value self) { mrb_value bt, v, length; mrb_int bt_len, argc, lev, n; bt = mrb_get_backtrace(mrb); bt_len = RARRAY_LEN(bt); argc = mrb_get_args(mrb, "|oo", &v, &length); switch (argc) { case 0: lev = 1; n = bt_len - lev; break; case 1: if (mrb_type(v) == MRB_TT_RANGE) { mrb_int beg, len; if (mrb_range_beg_len(mrb, v, &beg, &len, bt_len, TRUE) == 1) { lev = beg; n = len; } else { return mrb_nil_value(); } } else { v = mrb_to_int(mrb, v); lev = mrb_fixnum(v); if (lev < 0) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative level (%S)", v); } n = bt_len - lev; } break; case 2: lev = mrb_fixnum(mrb_to_int(mrb, v)); n = mrb_fixnum(mrb_to_int(mrb, length)); if (lev < 0) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative level (%S)", v); } if (n < 0) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative size (%S)", length); } break; default: lev = n = 0; break; } if (n == 0) { return mrb_ary_new(mrb); } return mrb_funcall(mrb, bt, "[]", 2, mrb_fixnum_value(lev), mrb_fixnum_value(n)); } /* * call-seq: * __method__ -> symbol * * Returns the name at the definition of the current method as a * Symbol. * If called outside of a method, it returns nil. * */ static mrb_value mrb_f_method(mrb_state *mrb, mrb_value self) { mrb_callinfo *ci = mrb->c->ci; ci--; if (ci->mid) return mrb_symbol_value(ci->mid); else return mrb_nil_value(); } /* * call-seq: * Integer(arg,base=0) -> integer * * Converts arg to a Fixnum. * Numeric types are converted directly (with floating point numbers * being truncated). base (0, or between 2 and 36) is a base for * integer string representation. If arg is a String, * when base is omitted or equals to zero, radix indicators * (0, 0b, and 0x) are honored. * In any case, strings should be strictly conformed to numeric * representation. This behavior is different from that of * String#to_i. Non string values will be treated as integers. * Passing nil raises a TypeError. * * Integer(123.999) #=> 123 * Integer("0x1a") #=> 26 * Integer(Time.new) #=> 1204973019 * Integer("0930", 10) #=> 930 * Integer("111", 2) #=> 7 * Integer(nil) #=> TypeError */ static mrb_value mrb_f_integer(mrb_state *mrb, mrb_value self) { mrb_value arg; mrb_int base = 0; mrb_get_args(mrb, "o|i", &arg, &base); return mrb_convert_to_integer(mrb, arg, base); } #ifndef MRB_WITHOUT_FLOAT /* * call-seq: * Float(arg) -> float * * Returns arg converted to a float. Numeric types are converted * directly, the rest are converted using arg.to_f. * * Float(1) #=> 1.0 * Float(123.456) #=> 123.456 * Float("123.456") #=> 123.456 * Float(nil) #=> TypeError */ static mrb_value mrb_f_float(mrb_state *mrb, mrb_value self) { mrb_value arg; mrb_get_args(mrb, "o", &arg); return mrb_Float(mrb, arg); } #endif /* * call-seq: * String(arg) -> string * * Returns arg as an String. * converted using to_s method. * * String(self) #=> "main" * String(self.class) #=> "Object" * String(123456) #=> "123456" */ static mrb_value mrb_f_string(mrb_state *mrb, mrb_value self) { mrb_value arg, tmp; mrb_get_args(mrb, "o", &arg); tmp = mrb_convert_type(mrb, arg, MRB_TT_STRING, "String", "to_s"); return tmp; } /* * call-seq: * Array(arg) -> array * * Returns +arg+ as an Array using to_a method. * * Array(1..5) #=> [1, 2, 3, 4, 5] * */ static mrb_value mrb_f_array(mrb_state *mrb, mrb_value self) { mrb_value arg, tmp; mrb_get_args(mrb, "o", &arg); tmp = mrb_check_convert_type(mrb, arg, MRB_TT_ARRAY, "Array", "to_a"); if (mrb_nil_p(tmp)) { return mrb_ary_new_from_values(mrb, 1, &arg); } return tmp; } /* * call-seq: * Hash(arg) -> hash * * Returns a Hash if arg is a Hash. * Returns an empty Hash when arg is nil * or []. * * Hash([]) #=> {} * Hash(nil) #=> {} * Hash(key: :value) #=> {:key => :value} * Hash([1, 2, 3]) #=> TypeError * */ static mrb_value mrb_f_hash(mrb_state *mrb, mrb_value self) { mrb_value arg; mrb_get_args(mrb, "o", &arg); if (mrb_nil_p(arg) || (mrb_array_p(arg) && RARRAY_LEN(arg) == 0)) { return mrb_hash_new(mrb); } return mrb_ensure_hash_type(mrb, arg); } /* * call-seq: * obj.itself -> an_object * * Returns obj. * * string = 'my string' #=> "my string" * string.itself.object_id == string.object_id #=> true * */ static mrb_value mrb_f_itself(mrb_state *mrb, mrb_value self) { return self; } void mrb_mruby_kernel_ext_gem_init(mrb_state *mrb) { struct RClass *krn = mrb->kernel_module; mrb_define_module_function(mrb, krn, "fail", mrb_f_raise, MRB_ARGS_OPT(2)); mrb_define_module_function(mrb, krn, "caller", mrb_f_caller, MRB_ARGS_OPT(2)); mrb_define_method(mrb, krn, "__method__", mrb_f_method, MRB_ARGS_NONE()); mrb_define_module_function(mrb, krn, "Integer", mrb_f_integer, MRB_ARGS_ANY()); #ifndef MRB_WITHOUT_FLOAT mrb_define_module_function(mrb, krn, "Float", mrb_f_float, MRB_ARGS_REQ(1)); #endif mrb_define_module_function(mrb, krn, "String", mrb_f_string, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, krn, "Array", mrb_f_array, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, krn, "Hash", mrb_f_hash, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, krn, "itself", mrb_f_itself, MRB_ARGS_NONE()); } void mrb_mruby_kernel_ext_gem_final(mrb_state *mrb) { } mruby-2.0.0/mrbgems/mruby-kernel-ext/test/000077500000000000000000000000001340361412400204725ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-kernel-ext/test/kernel.rb000066400000000000000000000047261340361412400223100ustar00rootroot00000000000000assert('Kernel.fail, Kernel#fail') do assert_raise(RuntimeError) { fail } assert_raise(RuntimeError) { Kernel.fail } end assert('Kernel.caller, Kernel#caller') do skip "backtrace isn't available" if caller(0).empty? caller_lineno = __LINE__ + 3 c = Class.new do def foo(*args) caller(*args) end def bar(*args) foo(*args) end def baz(*args) bar(*args) end end assert_equal "kernel.rb:#{caller_lineno}:in foo", c.new.baz(0)[0][-19..-1] assert_equal "bar", c.new.baz[0][-3..-1] assert_equal "foo", c.new.baz(0)[0][-3..-1] assert_equal "bar", c.new.baz(1)[0][-3..-1] assert_equal "baz", c.new.baz(2)[0][-3..-1] assert_equal ["foo", "bar"], c.new.baz(0, 2).map { |i| i[-3..-1] } assert_equal ["bar", "baz"], c.new.baz(1..2).map { |i| i[-3..-1] } assert_nil c.new.baz(10..20) assert_raise(ArgumentError) { c.new.baz(-1) } assert_raise(ArgumentError) { c.new.baz(-1, 1) } assert_raise(ArgumentError) { c.new.baz(1, -1) } assert_raise(TypeError) { c.new.baz(nil) } end assert('Kernel#__method__') do assert_equal(:m, Class.new {def m; __method__; end}.new.m) assert_equal(:m, Class.new {define_method(:m) {__method__}}.new.m) c = Class.new do [:m1, :m2].each do |m| define_method(m) do __method__ end end end assert_equal(:m1, c.new.m1) assert_equal(:m2, c.new.m2) end assert('Kernel#Integer') do assert_equal(123, Integer(123.999)) if class_defined?("Float") assert_equal(26, Integer("0x1a")) assert_equal(930, Integer("0930", 10)) assert_equal(7, Integer("111", 2)) assert_equal(0, Integer("0")) assert_equal(0, Integer("00000")) assert_raise(TypeError) { Integer(nil) } end assert('Kernel#Float') do assert_equal(1.0, Float(1)) assert_equal(123.456, Float(123.456)) assert_equal(123.456, Float("123.456")) assert_raise(TypeError) { Float(nil) } end if class_defined?("Float") assert('Kernel#String') do assert_equal("main", String(self)) assert_equal("Object", String(self.class)) assert_equal("123456", String(123456)) end assert('Kernel#Array') do assert_equal([1], Kernel.Array(1)) assert_equal([1, 2, 3, 4, 5], Kernel.Array([1, 2, 3, 4, 5])) assert_equal([1, 2, 3, 4, 5], Kernel.Array(1..5)) assert_equal([[:a, 1], [:b, 2], [:c, 3]], Kernel.Array({a:1, b:2, c:3})) end assert('Kernel#Hash') do assert_equal({}, Hash([])) assert_equal({}, Hash(nil)) assert_equal({:key => :value}, Hash(key: :value)) assert_raise(TypeError) { Hash([1, 2, 3]) } end mruby-2.0.0/mrbgems/mruby-math/000077500000000000000000000000001340361412400163665ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-math/mrbgem.rake000066400000000000000000000002351340361412400205030ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-math') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'standard Math module' end mruby-2.0.0/mrbgems/mruby-math/src/000077500000000000000000000000001340361412400171555ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-math/src/math.c000066400000000000000000000370751340361412400202660ustar00rootroot00000000000000/* ** math.c - Math module ** ** See Copyright Notice in mruby.h */ #include #include #include static void domain_error(mrb_state *mrb, const char *func) { struct RClass *math = mrb_module_get(mrb, "Math"); struct RClass *domainerror = mrb_class_get_under(mrb, math, "DomainError"); mrb_value str = mrb_str_new_cstr(mrb, func); mrb_raisef(mrb, domainerror, "Numerical argument is out of domain - %S", str); } /* math functions not provided by Microsoft Visual C++ 2012 or older */ #if defined _MSC_VER && _MSC_VER <= 1700 #include #define MATH_TOLERANCE 1E-12 double asinh(double x) { double xa, ya, y; /* Basic formula loses precision for x < 0, but asinh is an odd function */ xa = fabs(x); if (xa > 3.16227E+18) { /* Prevent x*x from overflowing; basic formula reduces to log(2*x) */ ya = log(xa) + 0.69314718055994530942; } else { /* Basic formula for asinh */ ya = log(xa + sqrt(xa*xa + 1.0)); } y = _copysign(ya, x); return y; } double acosh(double x) { double y; if (x > 3.16227E+18) { /* Prevent x*x from overflowing; basic formula reduces to log(2*x) */ y = log(x) + 0.69314718055994530942; } else { /* Basic formula for acosh */ y = log(x + sqrt(x*x - 1.0)); } return y; } double atanh(double x) { double y; if (fabs(x) < 1E-2) { /* The sums 1+x and 1-x lose precision for small x. Use the polynomial instead. */ double x2 = x * x; y = x*(1.0 + x2*(1.0/3.0 + x2*(1.0/5.0 + x2*(1.0/7.0)))); } else { /* Basic formula for atanh */ y = 0.5 * (log(1.0+x) - log(1.0-x)); } return y; } double cbrt(double x) { double xa, ya, y; /* pow(x, y) is undefined for x < 0 and y not an integer, but cbrt is an odd function */ xa = fabs(x); ya = pow(xa, 1.0/3.0); y = _copysign(ya, x); return y; } /* Declaration of complementary Error function */ double erfc(double x); /* ** Implementations of error functions ** credits to http://www.digitalmars.com/archives/cplusplus/3634.html */ /* Implementation of Error function */ double erf(double x) { static const double two_sqrtpi = 1.128379167095512574; double sum = x; double term = x; double xsqr = x*x; int j= 1; if (fabs(x) > 2.2) { return 1.0 - erfc(x); } do { term *= xsqr/j; sum -= term/(2*j+1); ++j; term *= xsqr/j; sum += term/(2*j+1); ++j; } while (fabs(term/sum) > MATH_TOLERANCE); return two_sqrtpi*sum; } /* Implementation of complementary Error function */ double erfc(double x) { static const double one_sqrtpi= 0.564189583547756287; double a = 1; double b = x; double c = x; double d = x*x+0.5; double q1; double q2 = b/d; double n = 1.0; double t; if (fabs(x) < 2.2) { return 1.0 - erf(x); } if (x < 0.0) { /*signbit(x)*/ return 2.0 - erfc(-x); } do { t = a*n+b*x; a = b; b = t; t = c*n+d*x; c = d; d = t; n += 0.5; q1 = q2; q2 = b/d; } while (fabs(q1-q2)/q2 > MATH_TOLERANCE); return one_sqrtpi*exp(-x*x)*q2; } #endif #if (defined _MSC_VER && _MSC_VER < 1800) || defined __ANDROID__ || (defined __FreeBSD__ && __FreeBSD_version < 803000) double log2(double x) { return log10(x)/log10(2.0); } #endif /* TRIGONOMETRIC FUNCTIONS */ /* * call-seq: * Math.sin(x) -> float * * Computes the sine of x (expressed in radians). Returns * -1..1. */ static mrb_value math_sin(mrb_state *mrb, mrb_value obj) { mrb_float x; mrb_get_args(mrb, "f", &x); x = sin(x); return mrb_float_value(mrb, x); } /* * call-seq: * Math.cos(x) -> float * * Computes the cosine of x (expressed in radians). Returns * -1..1. */ static mrb_value math_cos(mrb_state *mrb, mrb_value obj) { mrb_float x; mrb_get_args(mrb, "f", &x); x = cos(x); return mrb_float_value(mrb, x); } /* * call-seq: * Math.tan(x) -> float * * Returns the tangent of x (expressed in radians). */ static mrb_value math_tan(mrb_state *mrb, mrb_value obj) { mrb_float x; mrb_get_args(mrb, "f", &x); x = tan(x); return mrb_float_value(mrb, x); } /* INVERSE TRIGONOMETRIC FUNCTIONS */ /* * call-seq: * Math.asin(x) -> float * * Computes the arc sine of x. * @return computed value between `-(PI/2)` and `(PI/2)`. */ static mrb_value math_asin(mrb_state *mrb, mrb_value obj) { mrb_float x; mrb_get_args(mrb, "f", &x); if (x < -1.0 || x > 1.0) { domain_error(mrb, "asin"); } x = asin(x); return mrb_float_value(mrb, x); } /* * call-seq: * Math.acos(x) -> float * * Computes the arc cosine of x. Returns 0..PI. */ static mrb_value math_acos(mrb_state *mrb, mrb_value obj) { mrb_float x; mrb_get_args(mrb, "f", &x); if (x < -1.0 || x > 1.0) { domain_error(mrb, "acos"); } x = acos(x); return mrb_float_value(mrb, x); } /* * call-seq: * Math.atan(x) -> float * * Computes the arc tangent of x. Returns `-(PI/2) .. (PI/2)`. */ static mrb_value math_atan(mrb_state *mrb, mrb_value obj) { mrb_float x; mrb_get_args(mrb, "f", &x); x = atan(x); return mrb_float_value(mrb, x); } /* * call-seq: * Math.atan2(y, x) -> float * * Computes the arc tangent given y and x. Returns * -PI..PI. * * Math.atan2(-0.0, -1.0) #=> -3.141592653589793 * Math.atan2(-1.0, -1.0) #=> -2.356194490192345 * Math.atan2(-1.0, 0.0) #=> -1.5707963267948966 * Math.atan2(-1.0, 1.0) #=> -0.7853981633974483 * Math.atan2(-0.0, 1.0) #=> -0.0 * Math.atan2(0.0, 1.0) #=> 0.0 * Math.atan2(1.0, 1.0) #=> 0.7853981633974483 * Math.atan2(1.0, 0.0) #=> 1.5707963267948966 * Math.atan2(1.0, -1.0) #=> 2.356194490192345 * Math.atan2(0.0, -1.0) #=> 3.141592653589793 * */ static mrb_value math_atan2(mrb_state *mrb, mrb_value obj) { mrb_float x, y; mrb_get_args(mrb, "ff", &x, &y); x = atan2(x, y); return mrb_float_value(mrb, x); } /* HYPERBOLIC TRIG FUNCTIONS */ /* * call-seq: * Math.sinh(x) -> float * * Computes the hyperbolic sine of x (expressed in * radians). */ static mrb_value math_sinh(mrb_state *mrb, mrb_value obj) { mrb_float x; mrb_get_args(mrb, "f", &x); x = sinh(x); return mrb_float_value(mrb, x); } /* * call-seq: * Math.cosh(x) -> float * * Computes the hyperbolic cosine of x (expressed in radians). */ static mrb_value math_cosh(mrb_state *mrb, mrb_value obj) { mrb_float x; mrb_get_args(mrb, "f", &x); x = cosh(x); return mrb_float_value(mrb, x); } /* * call-seq: * Math.tanh() -> float * * Computes the hyperbolic tangent of x (expressed in * radians). */ static mrb_value math_tanh(mrb_state *mrb, mrb_value obj) { mrb_float x; mrb_get_args(mrb, "f", &x); x = tanh(x); return mrb_float_value(mrb, x); } /* INVERSE HYPERBOLIC TRIG FUNCTIONS */ /* * call-seq: * Math.asinh(x) -> float * * Computes the inverse hyperbolic sine of x. */ static mrb_value math_asinh(mrb_state *mrb, mrb_value obj) { mrb_float x; mrb_get_args(mrb, "f", &x); x = asinh(x); return mrb_float_value(mrb, x); } /* * call-seq: * Math.acosh(x) -> float * * Computes the inverse hyperbolic cosine of x. */ static mrb_value math_acosh(mrb_state *mrb, mrb_value obj) { mrb_float x; mrb_get_args(mrb, "f", &x); if (x < 1.0) { domain_error(mrb, "acosh"); } x = acosh(x); return mrb_float_value(mrb, x); } /* * call-seq: * Math.atanh(x) -> float * * Computes the inverse hyperbolic tangent of x. */ static mrb_value math_atanh(mrb_state *mrb, mrb_value obj) { mrb_float x; mrb_get_args(mrb, "f", &x); if (x < -1.0 || x > 1.0) { domain_error(mrb, "atanh"); } x = atanh(x); return mrb_float_value(mrb, x); } /* EXPONENTIALS AND LOGARITHMS */ /* * call-seq: * Math.exp(x) -> float * * Returns e**x. * * Math.exp(0) #=> 1.0 * Math.exp(1) #=> 2.718281828459045 * Math.exp(1.5) #=> 4.4816890703380645 * */ static mrb_value math_exp(mrb_state *mrb, mrb_value obj) { mrb_float x; mrb_get_args(mrb, "f", &x); x = exp(x); return mrb_float_value(mrb, x); } /* * call-seq: * Math.log(numeric) -> float * Math.log(num,base) -> float * * Returns the natural logarithm of numeric. * If additional second argument is given, it will be the base * of logarithm. * * Math.log(1) #=> 0.0 * Math.log(Math::E) #=> 1.0 * Math.log(Math::E**3) #=> 3.0 * Math.log(12,3) #=> 2.2618595071429146 * */ static mrb_value math_log(mrb_state *mrb, mrb_value obj) { mrb_float x, base; mrb_int argc; argc = mrb_get_args(mrb, "f|f", &x, &base); if (x < 0.0) { domain_error(mrb, "log"); } x = log(x); if (argc == 2) { if (base < 0.0) { domain_error(mrb, "log"); } x /= log(base); } return mrb_float_value(mrb, x); } /* * call-seq: * Math.log2(numeric) -> float * * Returns the base 2 logarithm of numeric. * * Math.log2(1) #=> 0.0 * Math.log2(2) #=> 1.0 * Math.log2(32768) #=> 15.0 * Math.log2(65536) #=> 16.0 * */ static mrb_value math_log2(mrb_state *mrb, mrb_value obj) { mrb_float x; mrb_get_args(mrb, "f", &x); if (x < 0.0) { domain_error(mrb, "log2"); } x = log2(x); return mrb_float_value(mrb, x); } /* * call-seq: * Math.log10(numeric) -> float * * Returns the base 10 logarithm of numeric. * * Math.log10(1) #=> 0.0 * Math.log10(10) #=> 1.0 * Math.log10(10**100) #=> 100.0 * */ static mrb_value math_log10(mrb_state *mrb, mrb_value obj) { mrb_float x; mrb_get_args(mrb, "f", &x); if (x < 0.0) { domain_error(mrb, "log10"); } x = log10(x); return mrb_float_value(mrb, x); } /* * call-seq: * Math.sqrt(numeric) -> float * * Returns the square root of numeric. * */ static mrb_value math_sqrt(mrb_state *mrb, mrb_value obj) { mrb_float x; mrb_get_args(mrb, "f", &x); if (x < 0.0) { domain_error(mrb, "sqrt"); } x = sqrt(x); return mrb_float_value(mrb, x); } /* * call-seq: * Math.cbrt(numeric) -> float * * Returns the cube root of numeric. * * -9.upto(9) {|x| * p [x, Math.cbrt(x), Math.cbrt(x)**3] * } * #=> * [-9, -2.0800838230519, -9.0] * [-8, -2.0, -8.0] * [-7, -1.91293118277239, -7.0] * [-6, -1.81712059283214, -6.0] * [-5, -1.7099759466767, -5.0] * [-4, -1.5874010519682, -4.0] * [-3, -1.44224957030741, -3.0] * [-2, -1.25992104989487, -2.0] * [-1, -1.0, -1.0] * [0, 0.0, 0.0] * [1, 1.0, 1.0] * [2, 1.25992104989487, 2.0] * [3, 1.44224957030741, 3.0] * [4, 1.5874010519682, 4.0] * [5, 1.7099759466767, 5.0] * [6, 1.81712059283214, 6.0] * [7, 1.91293118277239, 7.0] * [8, 2.0, 8.0] * [9, 2.0800838230519, 9.0] * */ static mrb_value math_cbrt(mrb_state *mrb, mrb_value obj) { mrb_float x; mrb_get_args(mrb, "f", &x); x = cbrt(x); return mrb_float_value(mrb, x); } /* * call-seq: * Math.frexp(numeric) -> [ fraction, exponent ] * * Returns a two-element array containing the normalized fraction (a * Float) and exponent (a Fixnum) of * numeric. * * fraction, exponent = Math.frexp(1234) #=> [0.6025390625, 11] * fraction * 2**exponent #=> 1234.0 */ static mrb_value math_frexp(mrb_state *mrb, mrb_value obj) { mrb_float x; int exp; mrb_get_args(mrb, "f", &x); x = frexp(x, &exp); return mrb_assoc_new(mrb, mrb_float_value(mrb, x), mrb_fixnum_value(exp)); } /* * call-seq: * Math.ldexp(flt, int) -> float * * Returns the value of flt*(2**int). * * fraction, exponent = Math.frexp(1234) * Math.ldexp(fraction, exponent) #=> 1234.0 */ static mrb_value math_ldexp(mrb_state *mrb, mrb_value obj) { mrb_float x; mrb_int i; mrb_get_args(mrb, "fi", &x, &i); x = ldexp(x, (int)i); return mrb_float_value(mrb, x); } /* * call-seq: * Math.hypot(x, y) -> float * * Returns sqrt(x**2 + y**2), the hypotenuse of a right-angled triangle * with sides x and y. * * Math.hypot(3, 4) #=> 5.0 */ static mrb_value math_hypot(mrb_state *mrb, mrb_value obj) { mrb_float x, y; mrb_get_args(mrb, "ff", &x, &y); x = hypot(x, y); return mrb_float_value(mrb, x); } /* * call-seq: * Math.erf(x) -> float * * Calculates the error function of x. */ static mrb_value math_erf(mrb_state *mrb, mrb_value obj) { mrb_float x; mrb_get_args(mrb, "f", &x); x = erf(x); return mrb_float_value(mrb, x); } /* * call-seq: * Math.erfc(x) -> float * * Calculates the complementary error function of x. */ static mrb_value math_erfc(mrb_state *mrb, mrb_value obj) { mrb_float x; mrb_get_args(mrb, "f", &x); x = erfc(x); return mrb_float_value(mrb, x); } /* ------------------------------------------------------------------------*/ void mrb_mruby_math_gem_init(mrb_state* mrb) { struct RClass *mrb_math; mrb_math = mrb_define_module(mrb, "Math"); mrb_define_class_under(mrb, mrb_math, "DomainError", mrb->eStandardError_class); #ifdef M_PI mrb_define_const(mrb, mrb_math, "PI", mrb_float_value(mrb, M_PI)); #else mrb_define_const(mrb, mrb_math, "PI", mrb_float_value(mrb, atan(1.0)*4.0)); #endif #ifdef M_E mrb_define_const(mrb, mrb_math, "E", mrb_float_value(mrb, M_E)); #else mrb_define_const(mrb, mrb_math, "E", mrb_float_value(mrb, exp(1.0))); #endif #ifdef MRB_USE_FLOAT mrb_define_const(mrb, mrb_math, "TOLERANCE", mrb_float_value(mrb, 1e-5)); #else mrb_define_const(mrb, mrb_math, "TOLERANCE", mrb_float_value(mrb, 1e-12)); #endif mrb_define_module_function(mrb, mrb_math, "sin", math_sin, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, mrb_math, "cos", math_cos, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, mrb_math, "tan", math_tan, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, mrb_math, "asin", math_asin, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, mrb_math, "acos", math_acos, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, mrb_math, "atan", math_atan, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, mrb_math, "atan2", math_atan2, MRB_ARGS_REQ(2)); mrb_define_module_function(mrb, mrb_math, "sinh", math_sinh, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, mrb_math, "cosh", math_cosh, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, mrb_math, "tanh", math_tanh, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, mrb_math, "asinh", math_asinh, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, mrb_math, "acosh", math_acosh, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, mrb_math, "atanh", math_atanh, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, mrb_math, "exp", math_exp, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, mrb_math, "log", math_log, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1)); mrb_define_module_function(mrb, mrb_math, "log2", math_log2, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, mrb_math, "log10", math_log10, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, mrb_math, "sqrt", math_sqrt, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, mrb_math, "cbrt", math_cbrt, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, mrb_math, "frexp", math_frexp, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, mrb_math, "ldexp", math_ldexp, MRB_ARGS_REQ(2)); mrb_define_module_function(mrb, mrb_math, "hypot", math_hypot, MRB_ARGS_REQ(2)); mrb_define_module_function(mrb, mrb_math, "erf", math_erf, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, mrb_math, "erfc", math_erfc, MRB_ARGS_REQ(1)); } void mrb_mruby_math_gem_final(mrb_state* mrb) { } mruby-2.0.0/mrbgems/mruby-math/test/000077500000000000000000000000001340361412400173455ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-math/test/math.rb000066400000000000000000000054541340361412400206330ustar00rootroot00000000000000## # Math Test ## # Performs fuzzy check for equality on methods returning floats # on the basis of the Math::TOLERANCE constant. def check_float(a, b) tolerance = Math::TOLERANCE a = a.to_f b = b.to_f if a.finite? and b.finite? (a-b).abs < tolerance else true end end assert('Math.sin 0') do check_float(Math.sin(0), 0) end assert('Math.sin PI/2') do check_float(Math.sin(Math::PI / 2), 1) end assert('Math.cos 0') do check_float(Math.cos(0), 1) end assert('Math.cos PI/2') do check_float(Math.cos(Math::PI / 2), 0) end assert('Math.tan 0') do check_float(Math.tan(0), 0) end assert('Math.tan PI/4') do check_float(Math.tan(Math::PI / 4), 1) end assert('Fundamental trig identities') do result = true N = 13 N.times do |i| a = Math::PI / N * i ca = Math::PI / 2 - a s = Math.sin(a) c = Math.cos(a) t = Math.tan(a) result &= check_float(s, Math.cos(ca)) result &= check_float(t, 1 / Math.tan(ca)) result &= check_float(s ** 2 + c ** 2, 1) result &= check_float(t ** 2 + 1, (1/c) ** 2) result &= check_float((1/t) ** 2 + 1, (1/s) ** 2) end result end assert('Math.erf 0') do check_float(Math.erf(0), 0) end assert('Math.exp 0') do check_float(Math.exp(0), 1.0) end assert('Math.exp 1') do check_float(Math.exp(1), 2.718281828459045) end assert('Math.exp 1.5') do check_float(Math.exp(1.5), 4.4816890703380645) end assert('Math.log 1') do check_float(Math.log(1), 0) end assert('Math.log E') do check_float(Math.log(Math::E), 1.0) end assert('Math.log E**3') do check_float(Math.log(Math::E**3), 3.0) end assert('Math.log2 1') do check_float(Math.log2(1), 0.0) end assert('Math.log2 2') do check_float(Math.log2(2), 1.0) end assert('Math.log10 1') do check_float(Math.log10(1), 0.0) end assert('Math.log10 10') do check_float(Math.log10(10), 1.0) end assert('Math.log10 10**100') do check_float(Math.log10(10**100), 100.0) end assert('Math.sqrt') do num = [0.0, 1.0, 2.0, 3.0, 4.0] sqr = [0, 1, 4, 9, 16] result = true sqr.each_with_index do |v,i| result &= check_float(Math.sqrt(v), num[i]) end result end assert('Math.cbrt') do num = [-2.0, -1.0, 0.0, 1.0, 2.0] cub = [-8, -1, 0, 1, 8] result = true cub.each_with_index do |v,i| result &= check_float(Math.cbrt(v), num[i]) end result end assert('Math.hypot') do check_float(Math.hypot(3, 4), 5.0) end assert('Math.frexp 1234') do n = 1234 fraction, exponent = Math.frexp(n) check_float(Math.ldexp(fraction, exponent), n) end assert('Math.erf 1') do check_float(Math.erf(1), 0.842700792949715) end assert('Math.erfc 1') do check_float(Math.erfc(1), 0.157299207050285) end assert('Math.erf -1') do check_float(Math.erf(-1), -0.8427007929497148) end assert('Math.erfc -1') do check_float(Math.erfc(-1), 1.8427007929497148) end mruby-2.0.0/mrbgems/mruby-metaprog/000077500000000000000000000000001340361412400172535ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-metaprog/mrbgem.rake000066400000000000000000000002601340361412400213660ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-metaprog') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Meta-programming features for mruby' end mruby-2.0.0/mrbgems/mruby-metaprog/src/000077500000000000000000000000001340361412400200425ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-metaprog/src/metaprog.c000066400000000000000000000456231340361412400220360ustar00rootroot00000000000000#include "mruby.h" #include "mruby/array.h" #include "mruby/hash.h" #include "mruby/variable.h" #include "mruby/proc.h" #include "mruby/class.h" #include "mruby/string.h" typedef enum { NOEX_PUBLIC = 0x00, NOEX_NOSUPER = 0x01, NOEX_PRIVATE = 0x02, NOEX_PROTECTED = 0x04, NOEX_MASK = 0x06, NOEX_BASIC = 0x08, NOEX_UNDEF = NOEX_NOSUPER, NOEX_MODFUNC = 0x12, NOEX_SUPER = 0x20, NOEX_VCALL = 0x40, NOEX_RESPONDS = 0x80 } mrb_method_flag_t; static mrb_value mrb_f_nil(mrb_state *mrb, mrb_value cv) { return mrb_nil_value(); } /* 15.3.1.3.20 */ /* * call-seq: * obj.instance_variable_defined?(symbol) -> true or false * * Returns true if the given instance variable is * defined in obj. * * class Fred * def initialize(p1, p2) * @a, @b = p1, p2 * end * end * fred = Fred.new('cat', 99) * fred.instance_variable_defined?(:@a) #=> true * fred.instance_variable_defined?("@b") #=> true * fred.instance_variable_defined?("@c") #=> false */ static mrb_value mrb_obj_ivar_defined(mrb_state *mrb, mrb_value self) { mrb_sym sym; mrb_get_args(mrb, "n", &sym); mrb_iv_name_sym_check(mrb, sym); return mrb_bool_value(mrb_iv_defined(mrb, self, sym)); } /* 15.3.1.3.21 */ /* * call-seq: * obj.instance_variable_get(symbol) -> obj * * Returns the value of the given instance variable, or nil if the * instance variable is not set. The @ part of the * variable name should be included for regular instance * variables. Throws a NameError exception if the * supplied symbol is not valid as an instance variable name. * * class Fred * def initialize(p1, p2) * @a, @b = p1, p2 * end * end * fred = Fred.new('cat', 99) * fred.instance_variable_get(:@a) #=> "cat" * fred.instance_variable_get("@b") #=> 99 */ static mrb_value mrb_obj_ivar_get(mrb_state *mrb, mrb_value self) { mrb_sym iv_name; mrb_get_args(mrb, "n", &iv_name); mrb_iv_name_sym_check(mrb, iv_name); return mrb_iv_get(mrb, self, iv_name); } /* 15.3.1.3.22 */ /* * call-seq: * obj.instance_variable_set(symbol, obj) -> obj * * Sets the instance variable names by symbol to * object, thereby frustrating the efforts of the class's * author to attempt to provide proper encapsulation. The variable * did not have to exist prior to this call. * * class Fred * def initialize(p1, p2) * @a, @b = p1, p2 * end * end * fred = Fred.new('cat', 99) * fred.instance_variable_set(:@a, 'dog') #=> "dog" * fred.instance_variable_set(:@c, 'cat') #=> "cat" * fred.inspect #=> "#" */ static mrb_value mrb_obj_ivar_set(mrb_state *mrb, mrb_value self) { mrb_sym iv_name; mrb_value val; mrb_get_args(mrb, "no", &iv_name, &val); mrb_iv_name_sym_check(mrb, iv_name); mrb_iv_set(mrb, self, iv_name, val); return val; } /* 15.3.1.2.7 */ /* * call-seq: * local_variables -> array * * Returns the names of local variables in the current scope. * * [mruby limitation] * If variable symbol information was stripped out from * compiled binary files using `mruby-strip -l`, this * method always returns an empty array. */ static mrb_value mrb_local_variables(mrb_state *mrb, mrb_value self) { struct RProc *proc; mrb_irep *irep; mrb_value vars; size_t i; proc = mrb->c->ci[-1].proc; if (MRB_PROC_CFUNC_P(proc)) { return mrb_ary_new(mrb); } vars = mrb_hash_new(mrb); while (proc) { if (MRB_PROC_CFUNC_P(proc)) break; irep = proc->body.irep; if (!irep->lv) break; for (i = 0; i + 1 < irep->nlocals; ++i) { if (irep->lv[i].name) { mrb_sym sym = irep->lv[i].name; const char *name = mrb_sym2name(mrb, sym); switch (name[0]) { case '*': case '&': break; default: mrb_hash_set(mrb, vars, mrb_symbol_value(sym), mrb_true_value()); break; } } } if (!MRB_PROC_ENV_P(proc)) break; proc = proc->upper; //if (MRB_PROC_SCOPE_P(proc)) break; if (!proc->c) break; } return mrb_hash_keys(mrb, vars); } KHASH_DECLARE(st, mrb_sym, char, FALSE) static void method_entry_loop(mrb_state *mrb, struct RClass* klass, khash_t(st)* set) { khint_t i; khash_t(mt) *h = klass->mt; if (!h || kh_size(h) == 0) return; for (i=0;iflags & MRB_FL_CLASS_IS_PREPENDED)) { MRB_CLASS_ORIGIN(klass); prepended = TRUE; } oldklass = 0; while (klass && (klass != oldklass)) { method_entry_loop(mrb, klass, set); if ((klass->tt == MRB_TT_ICLASS && !prepended) || (klass->tt == MRB_TT_SCLASS)) { } else { if (!recur) break; } oldklass = klass; klass = klass->super; } ary = mrb_ary_new_capa(mrb, kh_size(set)); for (i=0;i array * * Returns a list of the names of methods publicly accessible in * obj. This will include all the methods accessible in * obj's ancestors. * * class Klass * def kMethod() * end * end * k = Klass.new * k.methods[0..9] #=> [:kMethod, :respond_to?, :nil?, :is_a?, * # :class, :instance_variable_set, * # :methods, :extend, :__send__, :instance_eval] * k.methods.length #=> 42 */ static mrb_value mrb_obj_methods_m(mrb_state *mrb, mrb_value self) { mrb_bool recur = TRUE; mrb_get_args(mrb, "|b", &recur); return mrb_obj_methods(mrb, recur, self, (mrb_method_flag_t)0); /* everything but private */ } /* 15.3.1.3.36 */ /* * call-seq: * obj.private_methods(all=true) -> array * * Returns the list of private methods accessible to obj. If * the all parameter is set to false, only those methods * in the receiver will be listed. */ static mrb_value mrb_obj_private_methods(mrb_state *mrb, mrb_value self) { mrb_bool recur = TRUE; mrb_get_args(mrb, "|b", &recur); return mrb_obj_methods(mrb, recur, self, NOEX_PRIVATE); /* private attribute not define */ } /* 15.3.1.3.37 */ /* * call-seq: * obj.protected_methods(all=true) -> array * * Returns the list of protected methods accessible to obj. If * the all parameter is set to false, only those methods * in the receiver will be listed. */ static mrb_value mrb_obj_protected_methods(mrb_state *mrb, mrb_value self) { mrb_bool recur = TRUE; mrb_get_args(mrb, "|b", &recur); return mrb_obj_methods(mrb, recur, self, NOEX_PROTECTED); /* protected attribute not define */ } /* 15.3.1.3.38 */ /* * call-seq: * obj.public_methods(all=true) -> array * * Returns the list of public methods accessible to obj. If * the all parameter is set to false, only those methods * in the receiver will be listed. */ static mrb_value mrb_obj_public_methods(mrb_state *mrb, mrb_value self) { mrb_bool recur = TRUE; mrb_get_args(mrb, "|b", &recur); return mrb_obj_methods(mrb, recur, self, NOEX_PUBLIC); /* public attribute not define */ } static mrb_value mrb_obj_singleton_methods(mrb_state *mrb, mrb_bool recur, mrb_value obj) { khint_t i; mrb_value ary; struct RClass* klass; khash_t(st)* set = kh_init(st, mrb); klass = mrb_class(mrb, obj); if (klass && (klass->tt == MRB_TT_SCLASS)) { method_entry_loop(mrb, klass, set); klass = klass->super; } if (recur) { while (klass && ((klass->tt == MRB_TT_SCLASS) || (klass->tt == MRB_TT_ICLASS))) { method_entry_loop(mrb, klass, set); klass = klass->super; } } ary = mrb_ary_new(mrb); for (i=0;i array * * Returns an array of the names of singleton methods for obj. * If the optional all parameter is true, the list will include * methods in modules included in obj. * Only public and protected singleton methods are returned. * * module Other * def three() end * end * * class Single * def Single.four() end * end * * a = Single.new * * def a.one() * end * * class << a * include Other * def two() * end * end * * Single.singleton_methods #=> [:four] * a.singleton_methods(false) #=> [:two, :one] * a.singleton_methods #=> [:two, :one, :three] */ static mrb_value mrb_obj_singleton_methods_m(mrb_state *mrb, mrb_value self) { mrb_bool recur = TRUE; mrb_get_args(mrb, "|b", &recur); return mrb_obj_singleton_methods(mrb, recur, self); } static mrb_value mod_define_singleton_method(mrb_state *mrb, mrb_value self) { struct RProc *p; mrb_method_t m; mrb_sym mid; mrb_value blk = mrb_nil_value(); mrb_get_args(mrb, "n&", &mid, &blk); if (mrb_nil_p(blk)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given"); } p = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb->proc_class); mrb_proc_copy(p, mrb_proc_ptr(blk)); p->flags |= MRB_PROC_STRICT; MRB_METHOD_FROM_PROC(m, p); mrb_define_method_raw(mrb, mrb_class_ptr(mrb_singleton_class(mrb, self)), mid, m); return mrb_symbol_value(mid); } static void check_cv_name_str(mrb_state *mrb, mrb_value str) { const char *s = RSTRING_PTR(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); } } static void check_cv_name_sym(mrb_state *mrb, mrb_sym id) { check_cv_name_str(mrb, mrb_sym2str(mrb, id)); } /* 15.2.2.4.39 */ /* * call-seq: * remove_class_variable(sym) -> obj * * Removes the definition of the sym, returning that * constant's value. * * class Dummy * @@var = 99 * puts @@var * p class_variables * remove_class_variable(:@@var) * p class_variables * end * * produces: * * 99 * [:@@var] * [] */ static mrb_value mrb_mod_remove_cvar(mrb_state *mrb, mrb_value mod) { mrb_value val; mrb_sym id; mrb_get_args(mrb, "n", &id); check_cv_name_sym(mrb, id); val = mrb_iv_remove(mrb, mod, id); if (!mrb_undef_p(val)) return val; if (mrb_cv_defined(mrb, mod, id)) { mrb_name_error(mrb, id, "cannot remove %S for %S", mrb_sym2str(mrb, id), mod); } mrb_name_error(mrb, id, "class variable %S not defined for %S", mrb_sym2str(mrb, id), mod); /* not reached */ return mrb_nil_value(); } /* 15.2.2.4.16 */ /* * call-seq: * obj.class_variable_defined?(symbol) -> true or false * * Returns true if the given class variable is defined * in obj. * * class Fred * @@foo = 99 * end * Fred.class_variable_defined?(:@@foo) #=> true * Fred.class_variable_defined?(:@@bar) #=> false */ static mrb_value mrb_mod_cvar_defined(mrb_state *mrb, mrb_value mod) { mrb_sym id; mrb_get_args(mrb, "n", &id); check_cv_name_sym(mrb, id); return mrb_bool_value(mrb_cv_defined(mrb, mod, id)); } /* 15.2.2.4.17 */ /* * call-seq: * mod.class_variable_get(symbol) -> obj * * Returns the value of the given class variable (or throws a * NameError exception). The @@ part of the * variable name should be included for regular class variables * * class Fred * @@foo = 99 * end * Fred.class_variable_get(:@@foo) #=> 99 */ static mrb_value mrb_mod_cvar_get(mrb_state *mrb, mrb_value mod) { mrb_sym id; mrb_get_args(mrb, "n", &id); check_cv_name_sym(mrb, id); return mrb_cv_get(mrb, mod, id); } /* 15.2.2.4.18 */ /* * call-seq: * obj.class_variable_set(symbol, obj) -> obj * * Sets the class variable names by symbol to * object. * * class Fred * @@foo = 99 * def foo * @@foo * end * end * Fred.class_variable_set(:@@foo, 101) #=> 101 * Fred.new.foo #=> 101 */ static mrb_value mrb_mod_cvar_set(mrb_state *mrb, mrb_value mod) { mrb_value value; mrb_sym id; mrb_get_args(mrb, "no", &id, &value); check_cv_name_sym(mrb, id); mrb_cv_set(mrb, mod, id, value); return value; } static mrb_value mrb_mod_included_modules(mrb_state *mrb, mrb_value self) { mrb_value result; struct RClass *c = mrb_class_ptr(self); struct RClass *origin = c; MRB_CLASS_ORIGIN(origin); result = mrb_ary_new(mrb); while (c) { 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)); } } c = c->super; } return result; } mrb_value mrb_class_instance_method_list(mrb_state*, mrb_bool, struct RClass*, int); /* 15.2.2.4.33 */ /* * call-seq: * mod.instance_methods(include_super=true) -> array * * Returns an array containing the names of the public and protected instance * methods in the receiver. For a module, these are the public and protected methods; * for a class, they are the instance (not singleton) methods. With no * argument, or with an argument that is false, the * instance methods in mod are returned, otherwise the methods * in mod and mod's superclasses are returned. * * module A * def method1() end * end * class B * def method2() end * end * class C < B * def method3() end * end * * A.instance_methods #=> [:method1] * B.instance_methods(false) #=> [:method2] * C.instance_methods(false) #=> [:method3] * C.instance_methods(true).length #=> 43 */ static mrb_value mrb_mod_instance_methods(mrb_state *mrb, mrb_value mod) { struct RClass *c = mrb_class_ptr(mod); mrb_bool recur = TRUE; mrb_get_args(mrb, "|b", &recur); return mrb_class_instance_method_list(mrb, recur, c, 0); } static void remove_method(mrb_state *mrb, mrb_value mod, mrb_sym mid) { struct RClass *c = mrb_class_ptr(mod); khash_t(mt) *h; khiter_t k; MRB_CLASS_ORIGIN(c); h = c->mt; if (h) { 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; } } mrb_name_error(mrb, mid, "method '%S' not defined in %S", mrb_sym2str(mrb, mid), mod); } /* 15.2.2.4.41 */ /* * call-seq: * remove_method(symbol) -> self * * Removes the method identified by _symbol_ from the current * class. For an example, see Module.undef_method. */ static mrb_value mrb_mod_remove_method(mrb_state *mrb, mrb_value mod) { mrb_int argc; mrb_value *argv; mrb_get_args(mrb, "*", &argv, &argc); while (argc--) { remove_method(mrb, mod, mrb_obj_to_sym(mrb, *argv)); argv++; } return mod; } static mrb_value mrb_mod_s_constants(mrb_state *mrb, mrb_value mod) { mrb_raise(mrb, E_NOTIMP_ERROR, "Module.constants not implemented"); return mrb_nil_value(); /* not reached */ } /* implementation of Module.nesting */ mrb_value mrb_mod_s_nesting(mrb_state*, mrb_value); void mrb_mruby_metaprog_gem_init(mrb_state* mrb) { struct RClass *krn = mrb->kernel_module; struct RClass *mod = mrb->module_class; mrb_define_method(mrb, krn, "global_variables", mrb_f_global_variables, MRB_ARGS_NONE()); /* 15.3.1.2.4 */ mrb_define_method(mrb, krn, "local_variables", mrb_local_variables, MRB_ARGS_NONE()); /* 15.3.1.3.28 */ mrb_define_method(mrb, krn, "singleton_class", mrb_singleton_class, MRB_ARGS_NONE()); mrb_define_method(mrb, krn, "instance_variable_defined?", mrb_obj_ivar_defined, MRB_ARGS_REQ(1)); /* 15.3.1.3.20 */ mrb_define_method(mrb, krn, "instance_variable_get", mrb_obj_ivar_get, MRB_ARGS_REQ(1)); /* 15.3.1.3.21 */ mrb_define_method(mrb, krn, "instance_variable_set", mrb_obj_ivar_set, MRB_ARGS_REQ(2)); /* 15.3.1.3.22 */ mrb_define_method(mrb, krn, "instance_variables", mrb_obj_instance_variables, MRB_ARGS_NONE()); /* 15.3.1.3.23 */ mrb_define_method(mrb, krn, "methods", mrb_obj_methods_m, MRB_ARGS_OPT(1)); /* 15.3.1.3.31 */ mrb_define_method(mrb, krn, "private_methods", mrb_obj_private_methods, MRB_ARGS_OPT(1)); /* 15.3.1.3.36 */ mrb_define_method(mrb, krn, "protected_methods", mrb_obj_protected_methods, MRB_ARGS_OPT(1)); /* 15.3.1.3.37 */ mrb_define_method(mrb, krn, "public_methods", mrb_obj_public_methods, MRB_ARGS_OPT(1)); /* 15.3.1.3.38 */ mrb_define_method(mrb, krn, "singleton_methods", mrb_obj_singleton_methods_m, MRB_ARGS_OPT(1)); /* 15.3.1.3.45 */ mrb_define_method(mrb, krn, "define_singleton_method", mod_define_singleton_method, MRB_ARGS_ANY()); mrb_define_method(mrb, krn, "send", mrb_f_send, MRB_ARGS_ANY()); /* 15.3.1.3.44 */ mrb_define_method(mrb, mod, "class_variables", mrb_mod_class_variables, MRB_ARGS_NONE()); /* 15.2.2.4.19 */ 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, "class_variable_defined?", mrb_mod_cvar_defined, MRB_ARGS_REQ(1)); /* 15.2.2.4.16 */ mrb_define_method(mrb, mod, "class_variable_get", mrb_mod_cvar_get, MRB_ARGS_REQ(1)); /* 15.2.2.4.17 */ mrb_define_method(mrb, mod, "class_variable_set", mrb_mod_cvar_set, MRB_ARGS_REQ(2)); /* 15.2.2.4.18 */ mrb_define_method(mrb, mod, "included_modules", mrb_mod_included_modules, MRB_ARGS_NONE()); /* 15.2.2.4.30 */ mrb_define_method(mrb, mod, "instance_methods", mrb_mod_instance_methods, MRB_ARGS_ANY()); /* 15.2.2.4.33 */ 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_f_nil, MRB_ARGS_REQ(1)); mrb_define_method(mrb, mod, "constants", mrb_mod_constants, MRB_ARGS_OPT(1)); /* 15.2.2.4.24 */ mrb_define_class_method(mrb, mod, "constants", mrb_mod_s_constants, MRB_ARGS_ANY()); /* 15.2.2.3.1 */ mrb_define_class_method(mrb, mod, "nesting", mrb_mod_s_nesting, MRB_ARGS_REQ(0)); /* 15.2.2.3.2 */ } void mrb_mruby_metaprog_gem_final(mrb_state* mrb) { } mruby-2.0.0/mrbgems/mruby-metaprog/test/000077500000000000000000000000001340361412400202325ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-metaprog/test/metaprog.rb000066400000000000000000000205251340361412400224010ustar00rootroot00000000000000assert('Kernel#__send__', '15.3.1.3.4') do # test with block l = __send__(:lambda) do true end assert_true l.call assert_equal Proc, l.class # test with argument assert_true __send__(:respond_to?, :nil?) # test without argument and without block assert_equal String, __send__(:to_s).class end assert('Kernel#send', '15.3.1.3.44') do # test with block l = send(:lambda) do true end assert_true l.call assert_equal l.class, Proc # test with argument assert_true send(:respond_to?, :nil?) # test without argument and without block assert_equal send(:to_s).class, String end assert('Kernel#instance_variable_defined?', '15.3.1.3.20') do o = Object.new o.instance_variable_set(:@a, 1) assert_true o.instance_variable_defined?("@a") assert_false o.instance_variable_defined?("@b") assert_true o.instance_variable_defined?("@a"[0,2]) assert_true o.instance_variable_defined?("@abc"[0,2]) end assert('Kernel#instance_variables', '15.3.1.3.23') do o = Object.new o.instance_eval do @a = 11 @b = 12 end ivars = o.instance_variables assert_equal Array, ivars.class, assert_equal(2, ivars.size) assert_true ivars.include?(:@a) assert_true ivars.include?(:@b) end assert('Kernel#methods', '15.3.1.3.31') do assert_equal Array, methods.class end assert('Kernel#private_methods', '15.3.1.3.36') do assert_equal Array, private_methods.class end assert('Kernel#protected_methods', '15.3.1.3.37') do assert_equal Array, protected_methods.class end assert('Kernel#public_methods', '15.3.1.3.38') do assert_equal Array, public_methods.class class Foo def foo end end assert_equal [:foo], Foo.new.public_methods(false) end assert('Kernel#singleton_methods', '15.3.1.3.45') do assert_equal singleton_methods.class, Array end assert('Kernel.local_variables', '15.3.1.2.7') do a, b = 0, 1 a += b vars = Kernel.local_variables.sort assert_equal [:a, :b, :vars], vars assert_equal [:a, :b, :c, :vars], Proc.new { |a, b| c = 2 # Kernel#local_variables: 15.3.1.3.28 local_variables.sort }.call(-1, -2) end assert('Kernel#define_singleton_method') do o = Object.new ret = o.define_singleton_method(:test_method) do :singleton_method_ok end assert_equal :test_method, ret assert_equal :singleton_method_ok, o.test_method end def labeled_module(name, &block) Module.new do (class < ["mruby/mruby/mrblib/enum.rb", 148] ``` # Note `source_location` method need this configuration in build_config.rb ```ruby MRuby::Build.new do |conf| enable_debug end ``` # Supported Methods ## Kernel - `Kernel#method` - `Kernel#singleton_method` ## Module - `Module#instance_method` ## Method class - `Method#name` - `Method#call` - `Method#super_method` - `Method#arity` - `Method#unbind` - `Method#[]` - `Method#owner` - `Method#receiver` - `Method#parameters` - `Method#source_location` - `Method#to_proc` ## UnboundMethod class - `UnboundMethod#name` - `UnboundMethod#bind` - `UnboundMethod#super_method` - `UnboundMethod#arity` - `UnboundMethod#owner` - `UnboundMethod#parameters` - `UnboundMethod#source_location` # See also - https://ruby-doc.org/core-2.3.3/Method.html - https://ruby-doc.org/core-2.3.3/UnboundMethod.html mruby-2.0.0/mrbgems/mruby-method/mrbgem.rake000066400000000000000000000003551340361412400210350ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-method') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Method and UnboundMethod class' spec.add_dependency('mruby-proc-ext', :core => 'mruby-proc-ext') end mruby-2.0.0/mrbgems/mruby-method/mrblib/000077500000000000000000000000001340361412400201645ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-method/mrblib/kernel.rb000066400000000000000000000003261340361412400217720ustar00rootroot00000000000000module Kernel def singleton_method(name) m = method(name) sc = (class <body.func != other_rproc->body.func) return mrb_false_value(); } else { if (MRB_PROC_CFUNC_P(other_rproc)) return mrb_false_value(); if (orig_rproc->body.irep != other_rproc->body.irep) return mrb_false_value(); } return mrb_true_value(); } #undef IV_GET static mrb_value method_call(mrb_state *mrb, mrb_value self) { mrb_value proc = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "proc")); mrb_value name = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@name")); mrb_value recv = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@recv")); struct RClass *owner = mrb_class_ptr(mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@owner"))); mrb_int argc; mrb_value *argv, ret, block; mrb_sym orig_mid; mrb_get_args(mrb, "*&", &argv, &argc, &block); orig_mid = mrb->c->ci->mid; mrb->c->ci->mid = mrb_symbol(name); if (mrb_nil_p(proc)) { mrb_value missing_argv = mrb_ary_new_from_values(mrb, argc, argv); mrb_ary_unshift(mrb, missing_argv, name); ret = mrb_funcall_argv(mrb, recv, mrb_intern_lit(mrb, "method_missing"), argc + 1, RARRAY_PTR(missing_argv)); } else if (!mrb_nil_p(block)) { /* workaround since `mrb_yield_with_class` does not support passing block as parameter need new API that initializes `mrb->c->stack[argc+1]` with block passed by argument */ ret = mrb_funcall_with_block(mrb, recv, mrb_symbol(name), argc, argv, block); } else { ret = mrb_yield_with_class(mrb, proc, argc, argv, recv, owner); } mrb->c->ci->mid = orig_mid; return ret; } static mrb_value method_unbind(mrb_state *mrb, mrb_value self) { struct RObject *ume; mrb_value owner = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@owner")); mrb_value name = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@name")); mrb_value proc = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "proc")); mrb_value klass = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@klass")); ume = method_object_alloc(mrb, mrb_class_get(mrb, "UnboundMethod")); mrb_obj_iv_set(mrb, ume, mrb_intern_lit(mrb, "@owner"), owner); mrb_obj_iv_set(mrb, ume, mrb_intern_lit(mrb, "@recv"), mrb_nil_value()); mrb_obj_iv_set(mrb, ume, mrb_intern_lit(mrb, "@name"), name); mrb_obj_iv_set(mrb, ume, mrb_intern_lit(mrb, "proc"), proc); mrb_obj_iv_set(mrb, ume, mrb_intern_lit(mrb, "@klass"), klass); return mrb_obj_value(ume); } static struct RProc * method_search_vm(mrb_state *mrb, struct RClass **cp, mrb_sym mid) { mrb_method_t m = mrb_method_search_vm(mrb, cp, mid); if (MRB_METHOD_UNDEF_P(m)) return NULL; if (MRB_METHOD_PROC_P(m)) return MRB_METHOD_PROC(m); return mrb_proc_new_cfunc(mrb, MRB_METHOD_FUNC(m)); } static mrb_value method_super_method(mrb_state *mrb, mrb_value self) { mrb_value recv = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@recv")); mrb_value klass = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@klass")); mrb_value owner = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@owner")); mrb_value name = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@name")); struct RClass *super, *rklass; struct RProc *proc; struct RObject *me; switch (mrb_type(klass)) { case MRB_TT_SCLASS: super = mrb_class_ptr(klass)->super->super; break; case MRB_TT_ICLASS: super = mrb_class_ptr(klass)->super; break; default: super = mrb_class_ptr(owner)->super; break; } proc = method_search_vm(mrb, &super, mrb_symbol(name)); if (!proc) return mrb_nil_value(); rklass = super; while (super->tt == MRB_TT_ICLASS) super = super->c; me = method_object_alloc(mrb, mrb_obj_class(mrb, self)); mrb_obj_iv_set(mrb, me, mrb_intern_lit(mrb, "@owner"), mrb_obj_value(super)); mrb_obj_iv_set(mrb, me, mrb_intern_lit(mrb, "@recv"), recv); mrb_obj_iv_set(mrb, me, mrb_intern_lit(mrb, "@name"), name); mrb_obj_iv_set(mrb, me, mrb_intern_lit(mrb, "proc"), mrb_obj_value(proc)); mrb_obj_iv_set(mrb, me, mrb_intern_lit(mrb, "@klass"), mrb_obj_value(rklass)); return mrb_obj_value(me); } static mrb_value method_arity(mrb_state *mrb, mrb_value self) { mrb_value proc = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "proc")); struct RProc *rproc; struct RClass *orig; mrb_value ret; if (mrb_nil_p(proc)) return mrb_fixnum_value(-1); rproc = mrb_proc_ptr(proc); orig = rproc->c; rproc->c = mrb->proc_class; ret = mrb_funcall(mrb, proc, "arity", 0); rproc->c = orig; return ret; } static mrb_value method_source_location(mrb_state *mrb, mrb_value self) { mrb_value proc = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "proc")); struct RProc *rproc; struct RClass *orig; mrb_value ret; if (mrb_nil_p(proc)) return mrb_nil_value(); rproc = mrb_proc_ptr(proc); orig = rproc->c; rproc->c = mrb->proc_class; ret = mrb_funcall(mrb, proc, "source_location", 0); rproc->c = orig; return ret; } static mrb_value method_parameters(mrb_state *mrb, mrb_value self) { mrb_value proc = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "proc")); struct RProc *rproc; struct RClass *orig; mrb_value ret; if (mrb_nil_p(proc)) { mrb_value rest = mrb_symbol_value(mrb_intern_lit(mrb, "rest")); mrb_value arest = mrb_ary_new_from_values(mrb, 1, &rest); return mrb_ary_new_from_values(mrb, 1, &arest); } rproc = mrb_proc_ptr(proc); orig = rproc->c; rproc->c = mrb->proc_class; ret = mrb_funcall(mrb, proc, "parameters", 0); rproc->c = orig; return ret; } static mrb_value method_to_s(mrb_state *mrb, mrb_value self) { mrb_value owner = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@owner")); mrb_value klass = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@klass")); mrb_value name = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@name")); mrb_value str = mrb_str_new_lit(mrb, "#<"); struct RClass *rklass; mrb_str_cat_cstr(mrb, str, mrb_obj_classname(mrb, self)); mrb_str_cat_lit(mrb, str, ": "); rklass = mrb_class_ptr(klass); if (mrb_class_ptr(owner) == rklass) { mrb_str_cat_str(mrb, str, mrb_funcall(mrb, owner, "to_s", 0)); mrb_str_cat_lit(mrb, str, "#"); mrb_str_cat_str(mrb, str, mrb_funcall(mrb, name, "to_s", 0)); } else { mrb_str_cat_cstr(mrb, str, mrb_class_name(mrb, rklass)); mrb_str_cat_lit(mrb, str, "("); mrb_str_cat_str(mrb, str, mrb_funcall(mrb, owner, "to_s", 0)); mrb_str_cat_lit(mrb, str, ")#"); mrb_str_cat_str(mrb, str, mrb_funcall(mrb, name, "to_s", 0)); } mrb_str_cat_lit(mrb, str, ">"); return str; } static void mrb_search_method_owner(mrb_state *mrb, struct RClass *c, mrb_value obj, mrb_sym name, struct RClass **owner, struct RProc **proc, mrb_bool unbound) { mrb_value ret; const char *s; *owner = c; *proc = method_search_vm(mrb, owner, name); if (!*proc) { if (unbound) { goto name_error; } if (!mrb_respond_to(mrb, obj, mrb_intern_lit(mrb, "respond_to_missing?"))) { goto name_error; } ret = mrb_funcall(mrb, obj, "respond_to_missing?", 2, mrb_symbol_value(name), mrb_true_value()); if (!mrb_test(ret)) { goto name_error; } *owner = c; } while ((*owner)->tt == MRB_TT_ICLASS) *owner = (*owner)->c; return; name_error: s = mrb_class_name(mrb, c); mrb_raisef( mrb, E_NAME_ERROR, "undefined method `%S' for class `%S'", mrb_sym2str(mrb, name), mrb_str_new_static(mrb, s, strlen(s)) ); } static mrb_value mrb_kernel_method(mrb_state *mrb, mrb_value self) { struct RClass *owner; struct RProc *proc; struct RObject *me; mrb_sym name; mrb_get_args(mrb, "n", &name); mrb_search_method_owner(mrb, mrb_class(mrb, self), self, name, &owner, &proc, FALSE); me = method_object_alloc(mrb, mrb_class_get(mrb, "Method")); mrb_obj_iv_set(mrb, me, mrb_intern_lit(mrb, "@owner"), mrb_obj_value(owner)); mrb_obj_iv_set(mrb, me, mrb_intern_lit(mrb, "@recv"), self); mrb_obj_iv_set(mrb, me, mrb_intern_lit(mrb, "@name"), mrb_symbol_value(name)); mrb_obj_iv_set(mrb, me, mrb_intern_lit(mrb, "proc"), proc ? mrb_obj_value(proc) : mrb_nil_value()); mrb_obj_iv_set(mrb, me, mrb_intern_lit(mrb, "@klass"), mrb_obj_value(mrb_class(mrb, self))); return mrb_obj_value(me); } static mrb_value mrb_module_instance_method(mrb_state *mrb, mrb_value self) { struct RClass *owner; struct RProc *proc; struct RObject *ume; mrb_sym name; mrb_get_args(mrb, "n", &name); mrb_search_method_owner(mrb, mrb_class_ptr(self), self, name, &owner, &proc, TRUE); ume = method_object_alloc(mrb, mrb_class_get(mrb, "UnboundMethod")); mrb_obj_iv_set(mrb, ume, mrb_intern_lit(mrb, "@owner"), mrb_obj_value(owner)); mrb_obj_iv_set(mrb, ume, mrb_intern_lit(mrb, "@recv"), mrb_nil_value()); mrb_obj_iv_set(mrb, ume, mrb_intern_lit(mrb, "@name"), mrb_symbol_value(name)); mrb_obj_iv_set(mrb, ume, mrb_intern_lit(mrb, "proc"), proc ? mrb_obj_value(proc) : mrb_nil_value()); mrb_obj_iv_set(mrb, ume, mrb_intern_lit(mrb, "@klass"), self); return mrb_obj_value(ume); } void mrb_mruby_method_gem_init(mrb_state* mrb) { struct RClass *unbound_method = mrb_define_class(mrb, "UnboundMethod", mrb->object_class); struct RClass *method = mrb_define_class(mrb, "Method", mrb->object_class); mrb_undef_class_method(mrb, unbound_method, "new"); mrb_define_method(mrb, unbound_method, "bind", unbound_method_bind, MRB_ARGS_REQ(1)); mrb_define_method(mrb, unbound_method, "super_method", method_super_method, MRB_ARGS_NONE()); mrb_define_method(mrb, unbound_method, "==", method_eql, MRB_ARGS_REQ(1)); mrb_define_alias(mrb, unbound_method, "eql?", "=="); mrb_define_method(mrb, unbound_method, "to_s", method_to_s, MRB_ARGS_NONE()); mrb_define_method(mrb, unbound_method, "inspect", method_to_s, MRB_ARGS_NONE()); mrb_define_method(mrb, unbound_method, "arity", method_arity, MRB_ARGS_NONE()); mrb_define_method(mrb, unbound_method, "source_location", method_source_location, MRB_ARGS_NONE()); mrb_define_method(mrb, unbound_method, "parameters", method_parameters, MRB_ARGS_NONE()); mrb_undef_class_method(mrb, method, "new"); mrb_define_method(mrb, method, "==", method_eql, MRB_ARGS_REQ(1)); mrb_define_alias(mrb, method, "eql?", "=="); mrb_define_method(mrb, method, "to_s", method_to_s, MRB_ARGS_NONE()); mrb_define_method(mrb, method, "inspect", method_to_s, MRB_ARGS_NONE()); mrb_define_method(mrb, method, "call", method_call, MRB_ARGS_ANY()); mrb_define_alias(mrb, method, "[]", "call"); mrb_define_method(mrb, method, "unbind", method_unbind, MRB_ARGS_NONE()); mrb_define_method(mrb, method, "super_method", method_super_method, MRB_ARGS_NONE()); mrb_define_method(mrb, method, "arity", method_arity, MRB_ARGS_NONE()); mrb_define_method(mrb, method, "source_location", method_source_location, MRB_ARGS_NONE()); mrb_define_method(mrb, method, "parameters", method_parameters, MRB_ARGS_NONE()); mrb_define_method(mrb, mrb->kernel_module, "method", mrb_kernel_method, MRB_ARGS_REQ(1)); mrb_define_method(mrb, mrb->module_class, "instance_method", mrb_module_instance_method, MRB_ARGS_REQ(1)); } void mrb_mruby_method_gem_final(mrb_state* mrb) { } mruby-2.0.0/mrbgems/mruby-method/test/000077500000000000000000000000001340361412400176745ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-method/test/method.rb000066400000000000000000000253201340361412400215030ustar00rootroot00000000000000class Base def foo() :base end end class Derived < Base def foo() :derived end end class Interpreter attr_accessor :ret def do_a() @ret += "there, "; end def do_d() @ret += "Hello "; end def do_e() @ret += "!\n"; end def do_v() @ret += "Dave"; end Dispatcher = { "a" => instance_method(:do_a), "d" => instance_method(:do_d), "e" => instance_method(:do_e), "v" => instance_method(:do_v) } def interpret(string) @ret = "" string.each_char {|b| Dispatcher[b].bind(self).call } end end assert 'demo' do interpreter = Interpreter.new interpreter.interpret('dave') assert_equal "Hello there, Dave!\n", interpreter.ret end assert 'Method#arity' do Class.new { attr_accessor :done def initialize; @done = false; end def m0() end def m1(a) end def m2(a, b) end def mo1(a = nil, &b) end def mo2(a, b = nil) end def mo3(*a) end def mo4(a, *b, &c) end def mo5(a, *b, c) end def mo6(a, *b, c, &d) end def mo7(a, b = nil, *c, d, &e) end def ma1((a), &b) nil && a end def run assert_equal(0, method(:m0).arity) assert_equal(1, method(:m1).arity) assert_equal(2, method(:m2).arity) assert_equal(-1, method(:mo1).arity) assert_equal(-2, method(:mo2).arity) assert_equal(-1, method(:mo3).arity) assert_equal(-2, method(:mo4).arity) assert_equal(-3, method(:mo5).arity) assert_equal(-3, method(:mo6).arity) assert_equal(-3, method(:mo7).arity) assert_equal(1, method(:ma1).arity) assert_equal(-1, method(:__send__).arity) assert_equal(-1, method(:nothing).arity) end def respond_to_missing?(m, b) m == :nothing end }.new.run end assert 'Method and UnboundMethod should not be have a `new` method' do assert_raise(NoMethodError){ Method.new } assert_raise(NoMethodError){ UnboundMethod.new } end assert 'instance' do assert_kind_of Method, 1.method(:+) assert_kind_of UnboundMethod, Fixnum.instance_method(:+) end assert 'Method#call' do assert_equal 3, 1.method(:+).call(2) assert_equal "ab", "a".method(:+)["b"] klass = Class.new { def foo; 42; end } klass2 = Class.new(klass) { def foo; super; end } assert_equal 42, klass2.new.method(:foo).call i = Class.new { def bar yield 3 end }.new assert_raise(LocalJumpError) { i.method(:bar).call } assert_equal 3, i.method(:bar).call { |i| i } end assert 'Method#call for regression' do obj = BasicObject.new assert_equal String, Kernel.instance_method(:inspect).bind(obj).call().class, "https://github.com/ksss/mruby-method/issues/4" end assert 'Method#call with undefined method' do c = Class.new { attr_accessor :m, :argv def respond_to_missing?(m, b) m == :foo end def method_missing(m, *argv) @m = m @argv = argv super end } cc = c.new assert_raise(NameError) { cc.method(:nothing) } assert_kind_of Method, cc.method(:foo) assert_raise(NoMethodError) { cc.method(:foo).call(:arg1, :arg2) } assert_equal :foo, cc.m assert_equal [:arg1, :arg2], cc.argv cc = c.new m = cc.method(:foo) c.class_eval do def foo :ng end end assert_raise(NoMethodError) { m.call(:arg1, :arg2) } end assert 'Method#source_location' do skip if proc{}.source_location.nil? filename = __FILE__ klass = Class.new lineno = __LINE__ + 1 klass.define_method(:find_me_if_you_can) {} assert_equal [filename, lineno], klass.new.method(:find_me_if_you_can).source_location lineno = __LINE__ + 1 class <", m.unbind.inspect) c = Class.new c.class_eval { def foo; end; } m = c.new.method(:foo) assert_equal("#", m.inspect) m = c.instance_method(:foo) assert_equal("#", m.inspect) end assert 'owner' do c = Class.new do def foo; end def self.bar; end end m = Module.new do def baz; end end c.include(m) c2 = Class.new(c) assert_equal(c, c.instance_method(:foo).owner) assert_equal(c, c2.instance_method(:foo).owner) assert_equal(c, c.new.method(:foo).owner) assert_equal(c, c2.new.method(:foo).owner) assert_equal((class < 0 end def negative? self < 0 end end mruby-2.0.0/mrbgems/mruby-numeric-ext/src/000077500000000000000000000000001340361412400204645ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-numeric-ext/src/numeric_ext.c000066400000000000000000000040351340361412400231540ustar00rootroot00000000000000#include #include static inline mrb_int to_int(mrb_state *mrb, mrb_value x) { x = mrb_to_int(mrb, x); return mrb_fixnum(x); } /* * Document-method: Integer#chr * call-seq: * int.chr -> string * * Returns a string containing the character represented by the +int+'s value * according to +encoding+. * * 65.chr #=> "A" * 230.chr #=> "\xE6" */ static mrb_value mrb_int_chr(mrb_state *mrb, mrb_value x) { mrb_int chr; char c; chr = to_int(mrb, x); if (chr >= (1 << CHAR_BIT)) { mrb_raisef(mrb, E_RANGE_ERROR, "%S out of char range", x); } c = (char)chr; return mrb_str_new(mrb, &c, 1); } /* * call-seq: * int.allbits?(mask) -> true or false * * Returns +true+ if all bits of +int+ & +mask+ are 1. */ static mrb_value mrb_int_allbits(mrb_state *mrb, mrb_value self) { mrb_int n, m; mrb_get_args(mrb, "i", &m); n = to_int(mrb, self); return mrb_bool_value((n & m) == m); } /* * call-seq: * int.anybits?(mask) -> true or false * * Returns +true+ if any bits of +int+ & +mask+ are 1. */ static mrb_value mrb_int_anybits(mrb_state *mrb, mrb_value self) { mrb_int n, m; mrb_get_args(mrb, "i", &m); n = to_int(mrb, self); return mrb_bool_value((n & m) != 0); } /* * call-seq: * int.nobits?(mask) -> true or false * * Returns +true+ if no bits of +int+ & +mask+ are 1. */ static mrb_value mrb_int_nobits(mrb_state *mrb, mrb_value self) { mrb_int n, m; mrb_get_args(mrb, "i", &m); n = to_int(mrb, self); return mrb_bool_value((n & m) == 0); } void mrb_mruby_numeric_ext_gem_init(mrb_state* mrb) { struct RClass *i = mrb_module_get(mrb, "Integral"); mrb_define_method(mrb, i, "chr", mrb_int_chr, MRB_ARGS_NONE()); mrb_define_method(mrb, i, "allbits?", mrb_int_allbits, MRB_ARGS_REQ(1)); mrb_define_method(mrb, i, "anybits?", mrb_int_anybits, MRB_ARGS_REQ(1)); mrb_define_method(mrb, i, "nobits?", mrb_int_nobits, MRB_ARGS_REQ(1)); } void mrb_mruby_numeric_ext_gem_final(mrb_state* mrb) { } mruby-2.0.0/mrbgems/mruby-numeric-ext/test/000077500000000000000000000000001340361412400206545ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-numeric-ext/test/numeric.rb000066400000000000000000000010261340361412400226420ustar00rootroot00000000000000## # Numeric(Ext) Test assert('Integer#chr') do assert_equal("A", 65.chr) assert_equal("B", 0x42.chr) # multibyte encoding (not support yet) assert_raise(RangeError) { 256.chr } end assert('Integer#div') do assert_equal 52, 365.div(7) end assert('Float#div') do assert_float 52, 365.2425.div(7) end if class_defined?("Float") assert('Integer#zero?') do assert_equal true, 0.zero? assert_equal false, 1.zero? end assert('Integer#nonzero?') do assert_equal nil, 0.nonzero? assert_equal 1000, 1000.nonzero? end mruby-2.0.0/mrbgems/mruby-object-ext/000077500000000000000000000000001340361412400175015ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-object-ext/mrbgem.rake000066400000000000000000000002451340361412400216170ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-object-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Object class extension' end mruby-2.0.0/mrbgems/mruby-object-ext/mrblib/000077500000000000000000000000001340361412400207505ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-object-ext/mrblib/object.rb000066400000000000000000000011511340361412400225410ustar00rootroot00000000000000class Object ## # call-seq: # obj.tap{|x|...} -> obj # # Yields x to the block, and then returns x. # The primary purpose of this method is to "tap into" a method chain, # in order to perform operations on intermediate results within the chain. # # (1..10) .tap {|x| puts "original: #{x.inspect}"} # .to_a .tap {|x| puts "array: #{x.inspect}"} # .select {|x| x%2==0} .tap {|x| puts "evens: #{x.inspect}"} # .map { |x| x*x } .tap {|x| puts "squares: #{x.inspect}"} # def tap yield self self end end mruby-2.0.0/mrbgems/mruby-object-ext/src/000077500000000000000000000000001340361412400202705ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-object-ext/src/object.c000066400000000000000000000044331340361412400217060ustar00rootroot00000000000000#include #include #include #include /* * call-seq: * nil.to_a -> [] * * Always returns an empty array. */ static mrb_value nil_to_a(mrb_state *mrb, mrb_value obj) { return mrb_ary_new(mrb); } #ifndef MRB_WITHOUT_FLOAT /* * call-seq: * nil.to_f -> 0.0 * * Always returns zero. */ static mrb_value nil_to_f(mrb_state *mrb, mrb_value obj) { return mrb_float_value(mrb, 0.0); } #endif /* * call-seq: * nil.to_i -> 0 * * Always returns zero. */ static mrb_value nil_to_i(mrb_state *mrb, mrb_value obj) { return mrb_fixnum_value(0); } /* * call-seq: * obj.instance_exec(arg...) {|var...| block } -> obj * * Executes the given block within the context of the receiver * (_obj_). In order to set the context, the variable +self+ is set * to _obj_ while the code is executing, giving the code access to * _obj_'s instance variables. Arguments are passed as block parameters. * * class KlassWithSecret * def initialize * @secret = 99 * end * end * k = KlassWithSecret.new * k.instance_exec(5) {|x| @secret+x } #=> 104 */ static mrb_value mrb_obj_instance_exec(mrb_state *mrb, mrb_value self) { const mrb_value *argv; mrb_int argc; mrb_value blk; struct RClass *c; mrb_get_args(mrb, "*&", &argv, &argc, &blk); if (mrb_nil_p(blk)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given"); } switch (mrb_type(self)) { case MRB_TT_SYMBOL: case MRB_TT_FIXNUM: #ifndef MRB_WITHOUT_FLOAT case MRB_TT_FLOAT: #endif c = NULL; break; default: c = mrb_class_ptr(mrb_singleton_class(mrb, self)); break; } mrb->c->ci->target_class = c; return mrb_yield_cont(mrb, blk, self, argc, argv); } void mrb_mruby_object_ext_gem_init(mrb_state* mrb) { struct RClass * n = mrb->nil_class; mrb_define_method(mrb, n, "to_a", nil_to_a, MRB_ARGS_NONE()); #ifndef MRB_WITHOUT_FLOAT mrb_define_method(mrb, n, "to_f", nil_to_f, MRB_ARGS_NONE()); #endif mrb_define_method(mrb, n, "to_i", nil_to_i, MRB_ARGS_NONE()); mrb_define_method(mrb, mrb->kernel_module, "instance_exec", mrb_obj_instance_exec, MRB_ARGS_ANY() | MRB_ARGS_BLOCK()); } void mrb_mruby_object_ext_gem_final(mrb_state* mrb) { } mruby-2.0.0/mrbgems/mruby-object-ext/test/000077500000000000000000000000001340361412400204605ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-object-ext/test/nil.rb000066400000000000000000000003161340361412400215670ustar00rootroot00000000000000assert('NilClass#to_a') do assert_equal [], nil.to_a end assert('NilClass#to_f') do assert_equal 0.0, nil.to_f end if class_defined?("Float") assert('NilClass#to_i') do assert_equal 0, nil.to_i end mruby-2.0.0/mrbgems/mruby-object-ext/test/object.rb000066400000000000000000000022031340361412400222500ustar00rootroot00000000000000assert('Object#instance_exec') do class KlassWithSecret def initialize @secret = 99 end end k = KlassWithSecret.new assert_equal 104, k.instance_exec(5) {|x| @secret+x } end assert('Object#tap') do ret = [] (1..10) .tap {|x| ret << "original: #{x.inspect}"} .to_a .tap {|x| ret << "array: #{x.inspect}"} .select {|x| x%2==0} .tap {|x| ret << "evens: #{x.inspect}"} .map { |x| x*x } .tap {|x| ret << "squares: #{x.inspect}"} assert_equal [ "original: 1..10", "array: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]", "evens: [2, 4, 6, 8, 10]", "squares: [4, 16, 36, 64, 100]" ], ret assert_equal(:tap_ok, Class.new {def m; tap{return :tap_ok}; end}.new.m) end assert('instance_exec on primitives with class and module definition') do begin class A 1.instance_exec do class B end end end assert_kind_of Class, A::B ensure Object.remove_const :A end begin class A 1.instance_exec do module B end end end assert_kind_of Module, A::B ensure Object.remove_const :A end end mruby-2.0.0/mrbgems/mruby-objectspace/000077500000000000000000000000001340361412400177175ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-objectspace/mrbgem.rake000066400000000000000000000002411340361412400220310ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-objectspace') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'ObjectSpace class' end mruby-2.0.0/mrbgems/mruby-objectspace/src/000077500000000000000000000000001340361412400205065ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-objectspace/src/mruby_objectspace.c000066400000000000000000000107451340361412400243610ustar00rootroot00000000000000#include #include #include #include struct os_count_struct { mrb_int total; mrb_int freed; mrb_int counts[MRB_TT_MAXDEFINE+1]; }; static int os_count_object_type(mrb_state *mrb, struct RBasic *obj, void *data) { struct os_count_struct *obj_count; obj_count = (struct os_count_struct*)data; obj_count->total++; if (mrb_object_dead_p(mrb, obj)) { obj_count->freed++; } else { obj_count->counts[obj->tt]++; } return MRB_EACH_OBJ_OK; } /* * call-seq: * ObjectSpace.count_objects([result_hash]) -> hash * * Counts objects for each type. * * It returns a hash, such as: * { * :TOTAL=>10000, * :FREE=>3011, * :T_OBJECT=>6, * :T_CLASS=>404, * # ... * } * * If the optional argument +result_hash+ is given, * it is overwritten and returned. This is intended to avoid probe effect. * */ static mrb_value os_count_objects(mrb_state *mrb, mrb_value self) { struct os_count_struct obj_count = { 0 }; mrb_int i; mrb_value hash; if (mrb_get_args(mrb, "|H", &hash) == 0) { hash = mrb_hash_new(mrb); } if (!mrb_hash_empty_p(mrb, hash)) { mrb_hash_clear(mrb, hash); } mrb_objspace_each_objects(mrb, os_count_object_type, &obj_count); mrb_hash_set(mrb, hash, mrb_symbol_value(mrb_intern_lit(mrb, "TOTAL")), mrb_fixnum_value(obj_count.total)); mrb_hash_set(mrb, hash, mrb_symbol_value(mrb_intern_lit(mrb, "FREE")), mrb_fixnum_value(obj_count.freed)); for (i = MRB_TT_FALSE; i < MRB_TT_MAXDEFINE; i++) { mrb_value type; switch (i) { #define COUNT_TYPE(t) case (MRB_T ## t): type = mrb_symbol_value(mrb_intern_lit(mrb, #t)); break; COUNT_TYPE(T_FALSE); COUNT_TYPE(T_FREE); COUNT_TYPE(T_TRUE); COUNT_TYPE(T_FIXNUM); COUNT_TYPE(T_SYMBOL); COUNT_TYPE(T_UNDEF); COUNT_TYPE(T_FLOAT); COUNT_TYPE(T_CPTR); COUNT_TYPE(T_OBJECT); COUNT_TYPE(T_CLASS); COUNT_TYPE(T_MODULE); COUNT_TYPE(T_ICLASS); COUNT_TYPE(T_SCLASS); COUNT_TYPE(T_PROC); COUNT_TYPE(T_ARRAY); COUNT_TYPE(T_HASH); COUNT_TYPE(T_STRING); COUNT_TYPE(T_RANGE); COUNT_TYPE(T_EXCEPTION); COUNT_TYPE(T_FILE); COUNT_TYPE(T_ENV); COUNT_TYPE(T_DATA); COUNT_TYPE(T_FIBER); #undef COUNT_TYPE default: type = mrb_fixnum_value(i); break; } if (obj_count.counts[i]) mrb_hash_set(mrb, hash, type, mrb_fixnum_value(obj_count.counts[i])); } return hash; } struct os_each_object_data { mrb_value block; struct RClass *target_module; mrb_int count; }; static int os_each_object_cb(mrb_state *mrb, struct RBasic *obj, void *ud) { struct os_each_object_data *d = (struct os_each_object_data*)ud; /* filter dead objects */ if (mrb_object_dead_p(mrb, obj)) { return MRB_EACH_OBJ_OK; } /* filter internal objects */ switch (obj->tt) { case MRB_TT_ENV: case MRB_TT_ICLASS: return MRB_EACH_OBJ_OK; default: break; } /* filter half baked (or internal) objects */ if (!obj->c) return MRB_EACH_OBJ_OK; /* filter class kind if target module defined */ if (d->target_module && !mrb_obj_is_kind_of(mrb, mrb_obj_value(obj), d->target_module)) { return MRB_EACH_OBJ_OK; } mrb_yield(mrb, d->block, mrb_obj_value(obj)); ++d->count; return MRB_EACH_OBJ_OK; } /* * call-seq: * ObjectSpace.each_object([module]) {|obj| ... } -> fixnum * * Calls the block once for each object in this Ruby process. * Returns the number of objects found. * If the optional argument +module+ is given, * calls the block for only those classes or modules * that match (or are a subclass of) +module+. * * If no block is given, ArgumentError is raised. * */ static mrb_value os_each_object(mrb_state *mrb, mrb_value self) { mrb_value cls = mrb_nil_value(); struct os_each_object_data d; mrb_get_args(mrb, "&|C", &d.block, &cls); if (mrb_nil_p(d.block)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "Expected block in ObjectSpace.each_object."); } d.target_module = mrb_nil_p(cls) ? NULL : mrb_class_ptr(cls); d.count = 0; mrb_objspace_each_objects(mrb, os_each_object_cb, &d); return mrb_fixnum_value(d.count); } void mrb_mruby_objectspace_gem_init(mrb_state *mrb) { struct RClass *os = mrb_define_module(mrb, "ObjectSpace"); mrb_define_class_method(mrb, os, "count_objects", os_count_objects, MRB_ARGS_OPT(1)); mrb_define_class_method(mrb, os, "each_object", os_each_object, MRB_ARGS_OPT(1)); } void mrb_mruby_objectspace_gem_final(mrb_state *mrb) { } mruby-2.0.0/mrbgems/mruby-objectspace/test/000077500000000000000000000000001340361412400206765ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-objectspace/test/objectspace.rb000066400000000000000000000030221340361412400235020ustar00rootroot00000000000000assert('ObjectSpace.count_objects') do h = {} f = Fiber.new {} if Object.const_defined? :Fiber ObjectSpace.count_objects(h) assert_kind_of(Hash, h) assert_true(h.keys.all? {|x| x.is_a?(Symbol) || x.is_a?(Integer) }) assert_true(h.values.all? {|x| x.is_a?(Integer) }) assert_true(h.has_key?(:TOTAL)) assert_true(h.has_key?(:FREE)) assert_true(h.has_key?(:T_FIBER)) if Object.const_defined? :Fiber assert_equal(h[:TOTAL] * 2, h.values.reduce(:+)) h = ObjectSpace.count_objects assert_kind_of(Hash, h) assert_true(h.keys.all? {|x| x.is_a?(Symbol) || x.is_a?(Integer) }) assert_true(h.values.all? {|x| x.is_a?(Integer) }) assert_raise(TypeError) { ObjectSpace.count_objects(1) } h0 = {:T_FOO=>1000} h = ObjectSpace.count_objects(h0) assert_false(h0.has_key?(:T_FOO)) GC.start h_after = {} h_before = ObjectSpace.count_objects objs = [] 1000.times do objs << {} end ObjectSpace.count_objects(h) objs = nil GC.start ObjectSpace.count_objects(h_after) assert_equal(h[:T_HASH], h_before[:T_HASH] + 1000) assert_equal(h_after[:T_HASH], h_before[:T_HASH]) end assert('ObjectSpace.each_object') do objs = [] objs_count = ObjectSpace.each_object { |obj| objs << obj } assert_equal objs.length, objs_count arys = [] arys_count = ObjectSpace.each_object(Array) { |obj| arys << obj } assert_equal arys.length, arys_count assert_true arys.length < objs.length end assert 'Check class pointer of ObjectSpace.each_object.' do ObjectSpace.each_object { |obj| !obj } end mruby-2.0.0/mrbgems/mruby-pack/000077500000000000000000000000001340361412400163535ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-pack/.gitignore000066400000000000000000000000411340361412400203360ustar00rootroot00000000000000gem_* gem-* mrb-*.a src/*.o /tmp mruby-2.0.0/mrbgems/mruby-pack/.travis.yml000066400000000000000000000000501340361412400204570ustar00rootroot00000000000000script: - "ruby run_test.rb all test" mruby-2.0.0/mrbgems/mruby-pack/README.md000066400000000000000000000052341340361412400176360ustar00rootroot00000000000000mruby-pack (pack / unpack) ========= mruby-pack provides `Array#pack` and `String#unpack` for mruby. ## Installation Add the line below into your `build_config.rb`: ``` conf.gem :github => 'iij/mruby-pack' ``` There is no dependency on other mrbgems. ## Supported template string - A : arbitrary binary string (space padded, count is width) - a : arbitrary binary string (null padded, count is width) - C : 8-bit unsigned (unsigned char) - c : 8-bit signed (signed char) - D, d: 64-bit float, native format - E : 64-bit float, little endian byte order - e : 32-bit float, little endian byte order - F, f: 32-bit float, native format - G : 64-bit float, network (big-endian) byte order - g : 32-bit float, network (big-endian) byte order - H : hex string (high nibble first) - h : hex string (low nibble first) - I : unsigned integer, native endian (`unsigned int` in C) - i : signed integer, native endian (`int` in C) - L : 32-bit unsigned, native endian (`uint32_t`) - l : 32-bit signed, native endian (`int32_t`) - m : base64 encoded string (see RFC 2045, count is width) - N : 32-bit unsigned, network (big-endian) byte order - n : 16-bit unsigned, network (big-endian) byte order - Q : 64-bit unsigned, native endian (`uint64_t`) - q : 64-bit signed, native endian (`int64_t`) - S : 16-bit unsigned, native endian (`uint16_t`) - s : 16-bit signed, native endian (`int16_t`) - U : UTF-8 character - V : 32-bit unsigned, VAX (little-endian) byte order - v : 16-bit unsigned, VAX (little-endian) byte order - x : null byte - Z : same as "a", except that null is added with * ## License Copyright (c) 2012 Internet Initiative Japan Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. mruby-2.0.0/mrbgems/mruby-pack/mrbgem.rake000066400000000000000000000003521340361412400204700ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-pack') do |spec| spec.license = 'MIT' spec.authors = 'Internet Initiative Japan Inc.' spec.summary = 'Array#pack and String#unpack method' spec.cc.include_paths << "#{build.root}/src" end mruby-2.0.0/mrbgems/mruby-pack/packtest.rb000066400000000000000000000100731340361412400205170ustar00rootroot00000000000000# encoding: ascii # a = Array, s = String, t = Template def packtest(a, s, t) begin r = a.pack(t) return if r == s puts "#{a.inspect}.pack(#{t.inspect}) -> #{r.inspect} should be #{s.inspect}" rescue => r unless r.is_a? s puts "#{a.inspect}.pack(#{t.inspect}) -> #{r.inspect} should be #{s.inspect}" end end end def unpacktest(a, s, t) r = s.unpack(t) return if r == a puts "#{s.inspect}.unpack(#{t.inspect}) -> #{r.inspect} should be #{a.inspect}" end def pptest(a, s, t) packtest(a, s, t) unpacktest(a, s, t) end pptest [1], "\x01", "C" packtest [1.1], "\x01", "C" packtest [-1], "\xff", "C" packtest [1,2], "\x01\x02", "C2" #packtest [1], "X", ArgumentError unpacktest [48, nil], "0", "CC" unpacktest [160, -96], "\xa0\xa0", "Cc" unpacktest [49, 50, 51], "123", "C*" pptest [12849], "12", "S" unpacktest [nil], "0", "S" unpacktest [12849, nil], "123", "SS" unpacktest [12849], "123", "S*" pptest [10000], "\x27\x10", "s>" pptest [-10000], "\xd8\xf0", "s>" pptest [50000], "\xc3\x50", "S>" pptest [10000], "\x10\x27", "s<" pptest [-10000], "\xf0\xd8", "s<" pptest [50000], "\x50\xc3", "S<" pptest [1000000000], "\x3b\x9a\xca\x00", "l>" pptest [-1000000000], "\xc4\x65\x36\x00", "l>" pptest [1], "\x01\x00\x00\x00", "L<" pptest [258], "\x02\x01\x00\x00", "L<" pptest [66051], "\x03\x02\x01\x00", "L<" pptest [16909060], "\x04\x03\x02\x01", "L<" pptest [16909060], "\x01\x02\x03\x04", "L>" packtest [-1], "\xff\xff\xff\xff", "L<" pptest [1000000000], "\x00\x00\x00\x00\x3b\x9a\xca\x00", "q>" pptest [-1000000000], "\xff\xff\xff\xff\xc4\x65\x36\x00", "q>" if (2**33).is_a? Fixnum pptest [81985529216486895], "\x01\x23\x45\x67\x89\xab\xcd\xef", "q>" pptest [-1167088121787636991], "\x01\x23\x45\x67\x89\xab\xcd\xef", "q<" end pptest [16909060], "\x01\x02\x03\x04", "N" pptest [258], "\x01\x02", "n" pptest [32769], "\x80\x01", "n" pptest [16909060], "\x04\x03\x02\x01", "V" pptest [258], "\x02\x01", "v" packtest [""], "", "m" packtest ["a"], "YQ==\n", "m" packtest ["ab"], "YWI=\n", "m" packtest ["abc"], "YWJj\n", "m" packtest ["abcd"], "YWJjZA==\n", "m" unpacktest [""], "", "m" unpacktest ["a"], "YQ==\n", "m" unpacktest ["ab"], "YWI=\n", "m" unpacktest ["abc"], "YWJj\n", "m" unpacktest ["abcd"], "YWJjZA==\n", "m" packtest [""], "\0", "H" packtest ["3"], "0", "H" packtest ["34"], "", "H0" packtest ["34"], "0", "H" packtest ["34"], "4", "H2" packtest ["34"], "4\0", "H3" packtest ["3456"], "4P", "H3" packtest ["34563"], "4V0", "H*" packtest ["5a"], "Z", "H*" packtest ["5A"], "Z", "H*" unpacktest [""], "", "H" unpacktest [""], "0", "H0" unpacktest ["3"], "0", "H" unpacktest ["30"], "0", "H2" unpacktest ["30"], "0", "H3" unpacktest ["303"], "01", "H3" unpacktest ["303132"], "012", "H*" unpacktest ["3031", 50], "012", "H4C" unpacktest ["5a"], "Z", "H*" packtest [""], "\0", "h" packtest ["3"], "\03", "h" packtest ["34"], "", "h0" packtest ["34"], "\03", "h" packtest ["34"], "C", "h2" packtest ["34"], "C\0", "h3" packtest ["3456"], "C\05", "h3" packtest ["34563"], "Ce\03", "h*" packtest [""], " ", "A" unpacktest [""], "", "A" pptest ["1"], "1", "A" pptest ["1"], "1 ", "A2" unpacktest ["1"], "1", "A2" unpacktest ["1"], "1 ", "A2" unpacktest ["1"], "1\0", "A2" packtest ["12"], "1", "A" unpacktest ["1"], "12", "A" pptest ["123"], "123", "A*" packtest ["1","2"], "2", "A0A" unpacktest ["","2"], "2", "A0A" packtest [""], "\0", "a" unpacktest [""], "", "a" pptest ["1"], "1", "a" pptest ["1 "], "1 ", "a2" pptest ["1\0"], "1\0", "a2" packtest ["1"], "1\0", "a2" pptest ["123"], "123", "a*" packtest [""], "\0", "Z" unpacktest [""], "", "Z" pptest ["1"], "1", "Z" pptest ["1"], "1\0", "Z2" pptest ["1 "], "1 ", "Z2" pptest ["123"], "123\0", "Z*" pptest ["1","2"], "12", "ZZ" pptest ["1","2"], "1\0002", "Z*Z" unpacktest ["1","3"], "1\00023", "Z3Z" packtest [1, 2], "\x01\x02", "CyC" packtest [65], "A", 'U' packtest [59411], "\xEE\xA0\x93", 'U' pptest [1], "\x00\x01", "xC" unpacktest [2], "\xcc\x02", "xC" mruby-2.0.0/mrbgems/mruby-pack/run_test.rb000066400000000000000000000012201340361412400205360ustar00rootroot00000000000000#!/usr/bin/env ruby # # mrbgems test runner # gemname = File.basename(File.dirname(File.expand_path __FILE__)) if __FILE__ == $0 repository, dir = 'https://github.com/mruby/mruby.git', 'tmp/mruby' build_args = ARGV build_args = ['all', 'test'] if build_args.nil? or build_args.empty? Dir.mkdir 'tmp' unless File.exist?('tmp') unless File.exist?(dir) system "git clone #{repository} #{dir}" end exit system(%Q[cd #{dir}; MRUBY_CONFIG=#{File.expand_path __FILE__} ruby minirake #{build_args.join(' ')}]) end MRuby::Build.new do |conf| toolchain :gcc conf.gembox 'default' conf.gem File.expand_path(File.dirname(__FILE__)) end mruby-2.0.0/mrbgems/mruby-pack/src/000077500000000000000000000000001340361412400171425ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-pack/src/pack.c000066400000000000000000000750561340361412400202410ustar00rootroot00000000000000/* ** pack.c - Array#pack, String#unpack */ #include "mruby.h" #include "error.h" #include "mruby/array.h" #include "mruby/class.h" #include "mruby/numeric.h" #include "mruby/string.h" #include "mruby/variable.h" #include #include #include #include #include struct tmpl { mrb_value str; int idx; }; enum { PACK_DIR_CHAR, /* C */ PACK_DIR_SHORT, /* S */ PACK_DIR_LONG, /* L */ PACK_DIR_QUAD, /* Q */ //PACK_DIR_INT, /* i */ //PACK_DIR_VAX, PACK_DIR_UTF8, /* U */ //PACK_DIR_BER, PACK_DIR_DOUBLE, /* E */ PACK_DIR_FLOAT, /* f */ PACK_DIR_STR, /* A */ PACK_DIR_HEX, /* h */ PACK_DIR_BASE64, /* m */ PACK_DIR_NUL, /* x */ PACK_DIR_INVALID }; enum { PACK_TYPE_INTEGER, PACK_TYPE_FLOAT, PACK_TYPE_STRING, PACK_TYPE_NONE }; #define PACK_FLAG_s 0x00000001 /* native size ("_" "!") */ #define PACK_FLAG_a 0x00000002 /* null padding ("a") */ #define PACK_FLAG_Z 0x00000004 /* append nul char ("z") */ #define PACK_FLAG_SIGNED 0x00000008 /* native size ("_" "!") */ #define PACK_FLAG_GT 0x00000010 /* big endian (">") */ #define PACK_FLAG_LT 0x00000020 /* little endian ("<") */ #define PACK_FLAG_WIDTH 0x00000040 /* "count" is "width" */ #define PACK_FLAG_LSB 0x00000080 /* LSB / low nibble first */ #define PACK_FLAG_COUNT2 0x00000100 /* "count" is special... */ #define PACK_FLAG_LITTLEENDIAN 0x00000200 /* little endian actually */ #define PACK_BASE64_IGNORE 0xff #define PACK_BASE64_PADDING 0xfe static int littleendian = 0; const static unsigned char base64chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static unsigned char base64_dec_tab[128]; static int check_little_endian(void) { unsigned int n = 1; return (*(unsigned char *)&n == 1); } static unsigned int hex2int(unsigned char ch) { if (ch >= '0' && ch <= '9') return ch - '0'; else if (ch >= 'A' && ch <= 'F') return 10 + (ch - 'A'); else if (ch >= 'a' && ch <= 'f') return 10 + (ch - 'a'); else return 0; } static void make_base64_dec_tab(void) { int i; memset(base64_dec_tab, PACK_BASE64_IGNORE, sizeof(base64_dec_tab)); for (i = 0; i < 26; i++) base64_dec_tab['A' + i] = i; for (i = 0; i < 26; i++) base64_dec_tab['a' + i] = i + 26; for (i = 0; i < 10; i++) base64_dec_tab['0' + i] = i + 52; base64_dec_tab['+'] = 62; base64_dec_tab['/'] = 63; base64_dec_tab['='] = PACK_BASE64_PADDING; } static mrb_value str_len_ensure(mrb_state *mrb, mrb_value str, mrb_int len) { mrb_int n = RSTRING_LEN(str); if (len < 0) { mrb_raise(mrb, E_RANGE_ERROR, "negative (or overflowed) integer"); } if (len > n) { do { n *= 2; } while (len > n); str = mrb_str_resize(mrb, str, n); } return str; } static int pack_c(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned int flags) { str = str_len_ensure(mrb, str, sidx + 1); RSTRING_PTR(str)[sidx] = (char)mrb_fixnum(o); return 1; } static int unpack_c(mrb_state *mrb, const void *src, int srclen, mrb_value ary, unsigned int flags) { if (flags & PACK_FLAG_SIGNED) mrb_ary_push(mrb, ary, mrb_fixnum_value(*(signed char *)src)); else mrb_ary_push(mrb, ary, mrb_fixnum_value(*(unsigned char *)src)); return 1; } static int pack_s(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned int flags) { uint16_t n; str = str_len_ensure(mrb, str, sidx + 2); n = (uint16_t)mrb_fixnum(o); if (flags & PACK_FLAG_LITTLEENDIAN) { RSTRING_PTR(str)[sidx+0] = n % 256; RSTRING_PTR(str)[sidx+1] = n / 256; } else { RSTRING_PTR(str)[sidx+0] = n / 256; RSTRING_PTR(str)[sidx+1] = n % 256; } return 2; } static int unpack_s(mrb_state *mrb, const unsigned char *src, int srclen, mrb_value ary, unsigned int flags) { int n; if (flags & PACK_FLAG_LITTLEENDIAN) { n = src[1] * 256 + src[0]; } else { n = src[0] * 256 + src[1]; } if ((flags & PACK_FLAG_SIGNED) && (n >= 0x8000)) { n -= 0x10000; } mrb_ary_push(mrb, ary, mrb_fixnum_value(n)); return 2; } static int pack_l(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned int flags) { uint32_t n; str = str_len_ensure(mrb, str, sidx + 4); n = (uint32_t)mrb_fixnum(o); if (flags & PACK_FLAG_LITTLEENDIAN) { RSTRING_PTR(str)[sidx+0] = (char)(n & 0xff); RSTRING_PTR(str)[sidx+1] = (char)(n >> 8); RSTRING_PTR(str)[sidx+2] = (char)(n >> 16); RSTRING_PTR(str)[sidx+3] = (char)(n >> 24); } else { RSTRING_PTR(str)[sidx+0] = (char)(n >> 24); RSTRING_PTR(str)[sidx+1] = (char)(n >> 16); RSTRING_PTR(str)[sidx+2] = (char)(n >> 8); RSTRING_PTR(str)[sidx+3] = (char)(n & 0xff); } return 4; } static int unpack_l(mrb_state *mrb, const unsigned char *src, int srclen, mrb_value ary, unsigned int flags) { #ifndef MRB_INT64 char msg[60]; #endif uint32_t ul; mrb_int n; if (flags & PACK_FLAG_LITTLEENDIAN) { ul = (uint32_t)src[3] * 256*256*256; ul += (uint32_t)src[2] *256*256; ul += (uint32_t)src[1] *256; ul += (uint32_t)src[0]; } else { ul = (uint32_t)src[0] * 256*256*256; ul += (uint32_t)src[1] *256*256; ul += (uint32_t)src[2] *256; ul += (uint32_t)src[3]; } if (flags & PACK_FLAG_SIGNED) { int32_t sl = ul; #ifndef MRB_INT64 if (!FIXABLE(sl)) { snprintf(msg, sizeof(msg), "cannot unpack to Fixnum: %ld", (long)sl); mrb_raise(mrb, E_RANGE_ERROR, msg); } #endif n = sl; } else { #ifndef MRB_INT64 if (!POSFIXABLE(ul)) { snprintf(msg, sizeof(msg), "cannot unpack to Fixnum: %lu", (unsigned long)ul); mrb_raise(mrb, E_RANGE_ERROR, msg); } #endif n = ul; } mrb_ary_push(mrb, ary, mrb_fixnum_value(n)); return 4; } static int pack_q(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned int flags) { uint64_t n; str = str_len_ensure(mrb, str, sidx + 8); n = (uint64_t)mrb_fixnum(o); if (flags & PACK_FLAG_LITTLEENDIAN) { RSTRING_PTR(str)[sidx+0] = (char)(n & 0xff); RSTRING_PTR(str)[sidx+1] = (char)(n >> 8); RSTRING_PTR(str)[sidx+2] = (char)(n >> 16); RSTRING_PTR(str)[sidx+3] = (char)(n >> 24); RSTRING_PTR(str)[sidx+4] = (char)(n >> 32); RSTRING_PTR(str)[sidx+5] = (char)(n >> 40); RSTRING_PTR(str)[sidx+6] = (char)(n >> 48); RSTRING_PTR(str)[sidx+7] = (char)(n >> 56); } else { RSTRING_PTR(str)[sidx+0] = (char)(n >> 56); RSTRING_PTR(str)[sidx+1] = (char)(n >> 48); RSTRING_PTR(str)[sidx+2] = (char)(n >> 40); RSTRING_PTR(str)[sidx+3] = (char)(n >> 32); RSTRING_PTR(str)[sidx+4] = (char)(n >> 24); RSTRING_PTR(str)[sidx+5] = (char)(n >> 16); RSTRING_PTR(str)[sidx+6] = (char)(n >> 8); RSTRING_PTR(str)[sidx+7] = (char)(n & 0xff); } return 8; } static int unpack_q(mrb_state *mrb, const unsigned char *src, int srclen, mrb_value ary, unsigned int flags) { char msg[60]; uint64_t ull; int i, pos, step; mrb_int n; if (flags & PACK_FLAG_LITTLEENDIAN) { pos = 7; step = -1; } else { pos = 0; step = 1; } ull = 0; for (i = 0; i < 8; i++) { ull = ull * 256 + (uint64_t)src[pos]; pos += step; } if (flags & PACK_FLAG_SIGNED) { int64_t sll = ull; if (!FIXABLE(sll)) { snprintf(msg, sizeof(msg), "cannot unpack to Fixnum: %lld", (long long)sll); mrb_raise(mrb, E_RANGE_ERROR, msg); } n = sll; } else { if (!POSFIXABLE(ull)) { snprintf(msg, sizeof(msg), "cannot unpack to Fixnum: %llu", (unsigned long long)ull); mrb_raise(mrb, E_RANGE_ERROR, msg); } n = ull; } mrb_ary_push(mrb, ary, mrb_fixnum_value(n)); return 8; } #ifndef MRB_WITHOUT_FLOAT static int pack_double(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned int flags) { int i; double d; uint8_t *buffer = (uint8_t *)&d; str = str_len_ensure(mrb, str, sidx + 8); d = mrb_float(o); if (flags & PACK_FLAG_LITTLEENDIAN) { #ifdef MRB_ENDIAN_BIG for (i = 0; i < 8; ++i) { RSTRING_PTR(str)[sidx + i] = buffer[8 - i - 1]; } #else memcpy(RSTRING_PTR(str) + sidx, buffer, 8); #endif } else { #ifdef MRB_ENDIAN_BIG memcpy(RSTRING_PTR(str) + sidx, buffer, 8); #else for (i = 0; i < 8; ++i) { RSTRING_PTR(str)[sidx + i] = buffer[8 - i - 1]; } #endif } return 8; } static int unpack_double(mrb_state *mrb, const unsigned char * src, int srclen, mrb_value ary, unsigned int flags) { int i; double d; uint8_t *buffer = (uint8_t *)&d; if (flags & PACK_FLAG_LITTLEENDIAN) { #ifdef MRB_ENDIAN_BIG for (i = 0; i < 8; ++i) { buffer[8 - i - 1] = src[i]; } #else memcpy(buffer, src, 8); #endif } else { #ifdef MRB_ENDIAN_BIG memcpy(buffer, src, 8); #else for (i = 0; i < 8; ++i) { buffer[8 - i - 1] = src[i]; } #endif } mrb_ary_push(mrb, ary, mrb_float_value(mrb, d)); return 8; } static int pack_float(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned int flags) { int i; float f; uint8_t *buffer = (uint8_t *)&f; str = str_len_ensure(mrb, str, sidx + 4); f = (float)mrb_float(o); if (flags & PACK_FLAG_LITTLEENDIAN) { #ifdef MRB_ENDIAN_BIG for (i = 0; i < 4; ++i) { RSTRING_PTR(str)[sidx + i] = buffer[4 - i - 1]; } #else memcpy(RSTRING_PTR(str) + sidx, buffer, 4); #endif } else { #ifdef MRB_ENDIAN_BIG memcpy(RSTRING_PTR(str) + sidx, buffer, 4); #else for (i = 0; i < 4; ++i) { RSTRING_PTR(str)[sidx + i] = buffer[4 - i - 1]; } #endif } return 4; } static int unpack_float(mrb_state *mrb, const unsigned char * src, int srclen, mrb_value ary, unsigned int flags) { int i; float f; uint8_t *buffer = (uint8_t *)&f; if (flags & PACK_FLAG_LITTLEENDIAN) { #ifdef MRB_ENDIAN_BIG for (i = 0; i < 4; ++i) { buffer[4 - i - 1] = src[i]; } #else memcpy(buffer, src, 4); #endif } else { #ifdef MRB_ENDIAN_BIG memcpy(buffer, src, 4); #else for (i = 0; i < 4; ++i) { buffer[4 - i - 1] = src[i]; } #endif } mrb_ary_push(mrb, ary, mrb_float_value(mrb, f)); return 4; } #endif static int pack_utf8(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, long count, unsigned int flags) { char utf8[4]; int len = 0; uint32_t c = 0; #ifndef MRB_WITHOUT_FLOAT if (mrb_float_p(o)) { goto range_error; } #endif c = (uint32_t)mrb_fixnum(o); /* Unicode character */ /* from mruby-compiler gem */ 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 if (c < 0x200000) { 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; } else { #ifndef MRB_WITHOUT_FLOAT range_error: #endif mrb_raise(mrb, E_RANGE_ERROR, "pack(U): value out of range"); } str = str_len_ensure(mrb, str, sidx + len); memcpy(RSTRING_PTR(str) + sidx, utf8, len); return len; } static const unsigned long utf8_limits[] = { 0x0, /* 1 */ 0x80, /* 2 */ 0x800, /* 3 */ 0x10000, /* 4 */ 0x200000, /* 5 */ 0x4000000, /* 6 */ 0x80000000, /* 7 */ }; static unsigned long utf8_to_uv(mrb_state *mrb, const char *p, long *lenp) { int c = *p++ & 0xff; unsigned long uv = c; long n = 1; if (!(uv & 0x80)) { *lenp = 1; return uv; } if (!(uv & 0x40)) { *lenp = 1; mrb_raise(mrb, E_ARGUMENT_ERROR, "malformed UTF-8 character"); } if (!(uv & 0x20)) { n = 2; uv &= 0x1f; } else if (!(uv & 0x10)) { n = 3; uv &= 0x0f; } else if (!(uv & 0x08)) { n = 4; uv &= 0x07; } else if (!(uv & 0x04)) { n = 5; uv &= 0x03; } else if (!(uv & 0x02)) { n = 6; uv &= 0x01; } else { *lenp = 1; mrb_raise(mrb, E_ARGUMENT_ERROR, "malformed UTF-8 character"); } if (n > *lenp) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "malformed UTF-8 character (expected %S bytes, given %S bytes)", mrb_fixnum_value(n), mrb_fixnum_value(*lenp)); } *lenp = n--; if (n != 0) { while (n--) { c = *p++ & 0xff; if ((c & 0xc0) != 0x80) { *lenp -= n + 1; mrb_raise(mrb, E_ARGUMENT_ERROR, "malformed UTF-8 character"); } else { c &= 0x3f; uv = uv << 6 | c; } } } n = *lenp - 1; if (uv < utf8_limits[n]) { mrb_raise(mrb, E_ARGUMENT_ERROR, "redundant UTF-8 sequence"); } return uv; } static int unpack_utf8(mrb_state *mrb, const unsigned char * src, int srclen, mrb_value ary, unsigned int flags) { unsigned long uv; long lenp = srclen; if (srclen == 0) { return 1; } uv = utf8_to_uv(mrb, (const char *)src, &lenp); mrb_ary_push(mrb, ary, mrb_fixnum_value((mrb_int)uv)); return (int)lenp; } static int pack_a(mrb_state *mrb, mrb_value src, mrb_value dst, mrb_int didx, long count, unsigned int flags) { mrb_int copylen, slen, padlen; char *dptr, *dptr0, pad, *sptr; sptr = RSTRING_PTR(src); slen = RSTRING_LEN(src); if ((flags & PACK_FLAG_a) || (flags & PACK_FLAG_Z)) pad = '\0'; else pad = ' '; if (count == 0) { return 0; } else if (count == -1) { copylen = slen; padlen = (flags & PACK_FLAG_Z) ? 1 : 0; } else if (count < slen) { copylen = count; padlen = 0; } else { copylen = slen; padlen = count - slen; } dst = str_len_ensure(mrb, dst, didx + copylen + padlen); dptr0 = dptr = RSTRING_PTR(dst) + didx; memcpy(dptr, sptr, copylen); dptr += copylen; while (padlen-- > 0) { *dptr++ = pad; } return (int)(dptr - dptr0); } static int unpack_a(mrb_state *mrb, const void *src, int slen, mrb_value ary, long count, unsigned int flags) { mrb_value dst; const char *cp, *sptr; int copylen; sptr = (const char *)src; if (count != -1 && count < slen) { slen = count; } copylen = slen; if (slen >= 0 && flags & PACK_FLAG_Z) { /* "Z" */ if ((cp = (const char *)memchr(sptr, '\0', slen)) != NULL) { copylen = (int)(cp - sptr); if (count == -1) { slen = copylen + 1; } } } else if (!(flags & PACK_FLAG_a)) { /* "A" */ while (copylen > 0 && (sptr[copylen - 1] == '\0' || isspace(sptr[copylen - 1]))) { copylen--; } } if (copylen < 0) copylen = 0; dst = mrb_str_new(mrb, sptr, (mrb_int)copylen); mrb_ary_push(mrb, ary, dst); return slen; } static int pack_h(mrb_state *mrb, mrb_value src, mrb_value dst, mrb_int didx, long count, unsigned int flags) { unsigned int a, ashift, b, bshift; long slen; char *dptr, *dptr0, *sptr; sptr = RSTRING_PTR(src); slen = (long)RSTRING_LEN(src); if (flags & PACK_FLAG_LSB) { ashift = 0; bshift = 4; } else { ashift = 4; bshift = 0; } if (count == -1) { count = slen; } else if (slen > count) { slen = count; } dst = str_len_ensure(mrb, dst, didx + count); dptr = RSTRING_PTR(dst) + didx; dptr0 = dptr; for (; count > 0; count -= 2) { a = b = 0; if (slen > 0) { a = hex2int(*sptr++); slen--; } if (slen > 0) { b = hex2int(*sptr++); slen--; } *dptr++ = (a << ashift) + (b << bshift); } return (int)(dptr - dptr0); } static int unpack_h(mrb_state *mrb, const void *src, int slen, mrb_value ary, int count, unsigned int flags) { mrb_value dst; int a, ashift, b, bshift; const char *sptr, *sptr0; char *dptr, *dptr0; const char hexadecimal[] = "0123456789abcdef"; if (flags & PACK_FLAG_LSB) { ashift = 0; bshift = 4; } else { ashift = 4; bshift = 0; } sptr = (const char *)src; if (count == -1) count = slen * 2; dst = mrb_str_new(mrb, NULL, count); dptr = RSTRING_PTR(dst); sptr0 = sptr; dptr0 = dptr; while (slen > 0 && count > 0) { a = (*sptr >> ashift) & 0x0f; b = (*sptr >> bshift) & 0x0f; sptr++; slen--; *dptr++ = hexadecimal[a]; count--; if (count > 0) { *dptr++ = hexadecimal[b]; count--; } } dst = mrb_str_resize(mrb, dst, dptr - dptr0); mrb_ary_push(mrb, ary, dst); return (int)(sptr - sptr0); } static int pack_m(mrb_state *mrb, mrb_value src, mrb_value dst, mrb_int didx, long count, unsigned int flags) { mrb_int dstlen; unsigned long l; mrb_int column, srclen; char *srcptr, *dstptr, *dstptr0; srcptr = RSTRING_PTR(src); srclen = RSTRING_LEN(src); if (srclen == 0) /* easy case */ return 0; if (count != 0 && count < 3) { /* -1, 1 or 2 */ count = 45; } else if (count >= 3) { count -= count % 3; } dstlen = (srclen+2) / 3 * 4; if (count > 0) { dstlen += (srclen / count) + ((srclen % count) == 0 ? 0 : 1); } dst = str_len_ensure(mrb, dst, didx + dstlen); dstptr = RSTRING_PTR(dst) + didx; dstptr0 = dstptr; for (column = 3; srclen >= 3; srclen -= 3, column += 3) { l = (unsigned char)*srcptr++ << 16; l += (unsigned char)*srcptr++ << 8; l += (unsigned char)*srcptr++; *dstptr++ = base64chars[(l >> 18) & 0x3f]; *dstptr++ = base64chars[(l >> 12) & 0x3f]; *dstptr++ = base64chars[(l >> 6) & 0x3f]; *dstptr++ = base64chars[ l & 0x3f]; if (column == count) { *dstptr++ = '\n'; column = 0; } } if (srclen == 1) { l = (unsigned char)*srcptr++ << 16; *dstptr++ = base64chars[(l >> 18) & 0x3f]; *dstptr++ = base64chars[(l >> 12) & 0x3f]; *dstptr++ = '='; *dstptr++ = '='; column += 3; } else if (srclen == 2) { l = (unsigned char)*srcptr++ << 16; l += (unsigned char)*srcptr++ << 8; *dstptr++ = base64chars[(l >> 18) & 0x3f]; *dstptr++ = base64chars[(l >> 12) & 0x3f]; *dstptr++ = base64chars[(l >> 6) & 0x3f]; *dstptr++ = '='; column += 3; } if (column > 0 && count > 0) { *dstptr++ = '\n'; } return (int)(dstptr - dstptr0); } static int unpack_m(mrb_state *mrb, const void *src, int slen, mrb_value ary, unsigned int flags) { mrb_value dst; int dlen; unsigned long l; int i, padding; unsigned char c, ch[4]; const char *sptr, *sptr0; char *dptr, *dptr0; sptr0 = sptr = (const char *)src; dlen = slen / 4 * 3; /* an estimated value - may be shorter */ dst = mrb_str_new(mrb, NULL, dlen); dptr0 = dptr = RSTRING_PTR(dst); padding = 0; while (slen >= 4) { for (i = 0; i < 4; i++) { do { if (slen-- == 0) goto done; c = *sptr++; if (c >= sizeof(base64_dec_tab)) continue; ch[i] = base64_dec_tab[c]; if (ch[i] == PACK_BASE64_PADDING) { ch[i] = 0; padding++; } } while (c >= sizeof(base64_dec_tab) || ch[i] == PACK_BASE64_IGNORE); } l = (ch[0] << 18) + (ch[1] << 12) + (ch[2] << 6) + ch[3]; if (padding == 0) { *dptr++ = (l >> 16) & 0xff; *dptr++ = (l >> 8) & 0xff; *dptr++ = l & 0xff; } else if (padding == 1) { *dptr++ = (l >> 16) & 0xff; *dptr++ = (l >> 8) & 0xff; break; } else { *dptr++ = (l >> 16) & 0xff; break; } } done: dst = mrb_str_resize(mrb, dst, dptr - dptr0); mrb_ary_push(mrb, ary, dst); return (int)(sptr - sptr0); } static int pack_x(mrb_state *mrb, mrb_value src, mrb_value dst, mrb_int didx, long count, unsigned int flags) { long i; if (count < 0) return 0; dst = str_len_ensure(mrb, dst, didx + count); for (i = 0; i < count; i++) { RSTRING_PTR(dst)[didx + i] = '\0'; } return count; } static int unpack_x(mrb_state *mrb, const void *src, int slen, mrb_value ary, int count, unsigned int flags) { if (count < 0) return slen; if (slen < count) { mrb_raise(mrb, E_ARGUMENT_ERROR, "x outside of string"); } return count; } static void prepare_tmpl(mrb_state *mrb, struct tmpl *tmpl) { mrb_get_args(mrb, "S", &tmpl->str); tmpl->idx = 0; } static int has_tmpl(const struct tmpl *tmpl) { return (tmpl->idx < RSTRING_LEN(tmpl->str)); } static void read_tmpl(mrb_state *mrb, struct tmpl *tmpl, int *dirp, int *typep, int *sizep, int *countp, unsigned int *flagsp) { mrb_int t, tlen; int ch, dir, type, size = 0; int count = 1; unsigned int flags = 0; const char *tptr; tptr = RSTRING_PTR(tmpl->str); tlen = RSTRING_LEN(tmpl->str); t = tptr[tmpl->idx++]; alias: switch (t) { case 'A': dir = PACK_DIR_STR; type = PACK_TYPE_STRING; flags |= PACK_FLAG_WIDTH | PACK_FLAG_COUNT2; break; case 'a': dir = PACK_DIR_STR; type = PACK_TYPE_STRING; flags |= PACK_FLAG_WIDTH | PACK_FLAG_COUNT2 | PACK_FLAG_a; break; case 'C': dir = PACK_DIR_CHAR; type = PACK_TYPE_INTEGER; size = 1; break; case 'c': dir = PACK_DIR_CHAR; type = PACK_TYPE_INTEGER; size = 1; flags |= PACK_FLAG_SIGNED; break; case 'D': case 'd': dir = PACK_DIR_DOUBLE; type = PACK_TYPE_FLOAT; size = 8; flags |= PACK_FLAG_SIGNED; break; case 'F': case 'f': dir = PACK_DIR_FLOAT; type = PACK_TYPE_FLOAT; size = 4; flags |= PACK_FLAG_SIGNED; break; case 'E': dir = PACK_DIR_DOUBLE; type = PACK_TYPE_FLOAT; size = 8; flags |= PACK_FLAG_SIGNED | PACK_FLAG_LT; break; case 'e': dir = PACK_DIR_FLOAT; type = PACK_TYPE_FLOAT; size = 4; flags |= PACK_FLAG_SIGNED | PACK_FLAG_LT; break; case 'G': dir = PACK_DIR_DOUBLE; type = PACK_TYPE_FLOAT; size = 8; flags |= PACK_FLAG_SIGNED | PACK_FLAG_GT; break; case 'g': dir = PACK_DIR_FLOAT; type = PACK_TYPE_FLOAT; size = 4; flags |= PACK_FLAG_SIGNED | PACK_FLAG_GT; break; case 'H': dir = PACK_DIR_HEX; type = PACK_TYPE_STRING; flags |= PACK_FLAG_COUNT2; break; case 'h': dir = PACK_DIR_HEX; type = PACK_TYPE_STRING; flags |= PACK_FLAG_COUNT2 | PACK_FLAG_LSB; break; case 'I': switch (sizeof(int)) { case 2: t = 'S'; goto alias; case 4: t = 'L'; goto alias; case 8: t = 'Q'; goto alias; default: mrb_raisef(mrb, E_RUNTIME_ERROR, "mruby-pack does not support sizeof(int) == %S", mrb_fixnum_value(sizeof(int))); } break; case 'i': switch (sizeof(int)) { case 2: t = 's'; goto alias; case 4: t = 'l'; goto alias; case 8: t = 'q'; goto alias; default: mrb_raisef(mrb, E_RUNTIME_ERROR, "mruby-pack does not support sizeof(int) == %S", mrb_fixnum_value(sizeof(int))); } break; case 'L': dir = PACK_DIR_LONG; type = PACK_TYPE_INTEGER; size = 4; break; case 'l': dir = PACK_DIR_LONG; type = PACK_TYPE_INTEGER; size = 4; flags |= PACK_FLAG_SIGNED; break; case 'm': dir = PACK_DIR_BASE64; type = PACK_TYPE_STRING; flags |= PACK_FLAG_WIDTH; break; case 'N': /* = "L>" */ dir = PACK_DIR_LONG; type = PACK_TYPE_INTEGER; size = 4; flags |= PACK_FLAG_GT; break; case 'n': /* = "S>" */ dir = PACK_DIR_SHORT; type = PACK_TYPE_INTEGER; size = 2; flags |= PACK_FLAG_GT; break; case 'Q': dir = PACK_DIR_QUAD; type = PACK_TYPE_INTEGER; size = 8; break; case 'q': dir = PACK_DIR_QUAD; type = PACK_TYPE_INTEGER; size = 8; flags |= PACK_FLAG_SIGNED; break; case 'S': dir = PACK_DIR_SHORT; type = PACK_TYPE_INTEGER; size = 2; break; case 's': dir = PACK_DIR_SHORT; type = PACK_TYPE_INTEGER; size = 2; flags |= PACK_FLAG_SIGNED; break; case 'U': dir = PACK_DIR_UTF8; type = PACK_TYPE_INTEGER; break; case 'V': /* = "L<" */ dir = PACK_DIR_LONG; type = PACK_TYPE_INTEGER; size = 4; flags |= PACK_FLAG_LT; break; case 'v': /* = "S<" */ dir = PACK_DIR_SHORT; type = PACK_TYPE_INTEGER; size = 2; flags |= PACK_FLAG_LT; break; case 'x': dir = PACK_DIR_NUL; type = PACK_TYPE_NONE; break; case 'Z': dir = PACK_DIR_STR; type = PACK_TYPE_STRING; flags |= PACK_FLAG_WIDTH | PACK_FLAG_COUNT2 | PACK_FLAG_Z; break; default: dir = PACK_DIR_INVALID; type = PACK_TYPE_NONE; break; } /* read suffix [0-9*_!<>] */ while (tmpl->idx < tlen) { ch = tptr[tmpl->idx++]; if (isdigit(ch)) { count = ch - '0'; while (tmpl->idx < tlen && isdigit(tptr[tmpl->idx])) { count = count * 10 + (tptr[tmpl->idx++] - '0'); if (count < 0) { mrb_raise(mrb, E_RUNTIME_ERROR, "too big template length"); } } continue; /* special case */ } else if (ch == '*') { count = -1; } else if (ch == '_' || ch == '!' || ch == '<' || ch == '>') { if (strchr("sSiIlLqQ", (int)t) == NULL) { char ch_str = (char)ch; mrb_raisef(mrb, E_ARGUMENT_ERROR, "'%S' allowed only after types sSiIlLqQ", mrb_str_new(mrb, &ch_str, 1)); } if (ch == '_' || ch == '!') { flags |= PACK_FLAG_s; } else if (ch == '<') { flags |= PACK_FLAG_LT; } else if (ch == '>') { flags |= PACK_FLAG_GT; } } else { tmpl->idx--; break; } } if ((flags & PACK_FLAG_LT) || (!(flags & PACK_FLAG_GT) && littleendian)) { flags |= PACK_FLAG_LITTLEENDIAN; } *dirp = dir; *typep = type; *sizep = size; *countp = count; *flagsp = flags; } static mrb_value mrb_pack_pack(mrb_state *mrb, mrb_value ary) { mrb_value o, result; mrb_int aidx; struct tmpl tmpl; int count; unsigned int flags; int dir, ridx, size, type; prepare_tmpl(mrb, &tmpl); result = mrb_str_new(mrb, NULL, 128); /* allocate initial buffer */ aidx = 0; ridx = 0; while (has_tmpl(&tmpl)) { read_tmpl(mrb, &tmpl, &dir, &type, &size, &count, &flags); if (dir == PACK_DIR_INVALID) continue; else if (dir == PACK_DIR_NUL) { ridx += pack_x(mrb, mrb_nil_value(), result, ridx, count, flags); continue; } for (; aidx < RARRAY_LEN(ary); aidx++) { if (count == 0 && !(flags & PACK_FLAG_WIDTH)) break; o = mrb_ary_ref(mrb, ary, aidx); if (type == PACK_TYPE_INTEGER) { o = mrb_to_int(mrb, o); } #ifndef MRB_WITHOUT_FLOAT else if (type == PACK_TYPE_FLOAT) { if (!mrb_float_p(o)) { mrb_float f = mrb_to_flo(mrb, o); o = mrb_float_value(mrb, f); } } #endif else if (type == PACK_TYPE_STRING) { if (!mrb_string_p(o)) { mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S into String", mrb_class_path(mrb, mrb_obj_class(mrb, o))); } } switch (dir) { case PACK_DIR_CHAR: ridx += pack_c(mrb, o, result, ridx, flags); break; case PACK_DIR_SHORT: ridx += pack_s(mrb, o, result, ridx, flags); break; case PACK_DIR_LONG: ridx += pack_l(mrb, o, result, ridx, flags); break; case PACK_DIR_QUAD: ridx += pack_q(mrb, o, result, ridx, flags); break; case PACK_DIR_BASE64: ridx += pack_m(mrb, o, result, ridx, count, flags); break; case PACK_DIR_HEX: ridx += pack_h(mrb, o, result, ridx, count, flags); break; case PACK_DIR_STR: ridx += pack_a(mrb, o, result, ridx, count, flags); break; #ifndef MRB_WITHOUT_FLOAT case PACK_DIR_DOUBLE: ridx += pack_double(mrb, o, result, ridx, flags); break; case PACK_DIR_FLOAT: ridx += pack_float(mrb, o, result, ridx, flags); break; #endif case PACK_DIR_UTF8: ridx += pack_utf8(mrb, o, result, ridx, count, flags); break; default: break; } if (dir == PACK_DIR_STR) { /* always consumes 1 entry */ aidx++; break; } if (count > 0) { count--; } } if (ridx < 0) { mrb_raise(mrb, E_RANGE_ERROR, "negative (or overflowed) template size"); } } mrb_str_resize(mrb, result, ridx); return result; } static mrb_value pack_unpack(mrb_state *mrb, mrb_value str, int single) { mrb_value result; struct tmpl tmpl; int count; unsigned int flags; int dir, size, type; int srcidx, srclen; const unsigned char *sptr; prepare_tmpl(mrb, &tmpl); srcidx = 0; srclen = (int)RSTRING_LEN(str); result = mrb_ary_new(mrb); while (has_tmpl(&tmpl)) { read_tmpl(mrb, &tmpl, &dir, &type, &size, &count, &flags); if (dir == PACK_DIR_INVALID) continue; else if (dir == PACK_DIR_NUL) { srcidx += unpack_x(mrb, sptr, srclen - srcidx, result, count, flags); continue; } if (flags & PACK_FLAG_COUNT2) { sptr = (const unsigned char *)RSTRING_PTR(str) + srcidx; switch (dir) { case PACK_DIR_HEX: srcidx += unpack_h(mrb, sptr, srclen - srcidx, result, count, flags); break; case PACK_DIR_STR: srcidx += unpack_a(mrb, sptr, srclen - srcidx, result, count, flags); break; } continue; } while (count != 0) { if (srclen - srcidx < size) { while (count-- > 0) { mrb_ary_push(mrb, result, mrb_nil_value()); } break; } sptr = (const unsigned char*)RSTRING_PTR(str) + srcidx; switch (dir) { case PACK_DIR_CHAR: srcidx += unpack_c(mrb, sptr, srclen - srcidx, result, flags); break; case PACK_DIR_SHORT: srcidx += unpack_s(mrb, sptr, srclen - srcidx, result, flags); break; case PACK_DIR_LONG: srcidx += unpack_l(mrb, sptr, srclen - srcidx, result, flags); break; case PACK_DIR_QUAD: srcidx += unpack_q(mrb, sptr, srclen - srcidx, result, flags); break; case PACK_DIR_BASE64: srcidx += unpack_m(mrb, sptr, srclen - srcidx, result, flags); break; #ifndef MRB_WITHOUT_FLOAT case PACK_DIR_FLOAT: srcidx += unpack_float(mrb, sptr, srclen - srcidx, result, flags); break; case PACK_DIR_DOUBLE: srcidx += unpack_double(mrb, sptr, srclen - srcidx, result, flags); break; #endif case PACK_DIR_UTF8: srcidx += unpack_utf8(mrb, sptr, srclen - srcidx, result, flags); break; default: mrb_raise(mrb, E_RUNTIME_ERROR, "mruby-pack's bug"); } if (count > 0) { count--; } } if (single) break; } if (single) return RARRAY_PTR(result)[0]; return result; } static mrb_value mrb_pack_unpack(mrb_state *mrb, mrb_value str) { return pack_unpack(mrb, str, 0); } static mrb_value mrb_pack_unpack1(mrb_state *mrb, mrb_value str) { return pack_unpack(mrb, str, 1); } void mrb_mruby_pack_gem_init(mrb_state *mrb) { littleendian = check_little_endian(); make_base64_dec_tab(); mrb_define_method(mrb, mrb->array_class, "pack", mrb_pack_pack, MRB_ARGS_REQ(1)); mrb_define_method(mrb, mrb->string_class, "unpack", mrb_pack_unpack, MRB_ARGS_REQ(1)); mrb_define_method(mrb, mrb->string_class, "unpack1", mrb_pack_unpack1, MRB_ARGS_REQ(1)); } void mrb_mruby_pack_gem_final(mrb_state *mrb) { } mruby-2.0.0/mrbgems/mruby-pack/test/000077500000000000000000000000001340361412400173325ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-pack/test/pack.rb000066400000000000000000000102351340361412400205760ustar00rootroot00000000000000# pack & unpack 'm' (base64) assert('[""].pack("m")') do ary = "" str = "" [ary].pack("m") == str and str.unpack("m") == [ary] end assert('["\0"].pack("m")') do ary = "\0" str = "AA==\n" [ary].pack("m") == str and str.unpack("m") == [ary] end assert('["\0\0"].pack("m")') do ary = "\0\0" str = "AAA=\n" [ary].pack("m") == str and str.unpack("m") == [ary] end assert('["\0\0\0"].pack("m")') do ary = "\0\0\0" str = "AAAA\n" [ary].pack("m") == str and str.unpack("m") == [ary] end assert('["abc..xyzABC..XYZ"].pack("m")') do ["abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"].pack("m") == "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJT\nVFVWV1hZWg==\n" end assert('"YWJ...".unpack("m") should "abc..xyzABC..XYZ"') do str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJT\nVFVWV1hZWg==\n".unpack("m") == [str] and "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWg==\n".unpack("m") == [str] end # pack & unpack 'H' assert('["3031"].pack("H*")') do ary = "3031" str = "01" [ary].pack("H*") == str and str.unpack("H*") == [ary] end assert('["10"].pack("H*")') do ary = "10" str = "\020" [ary].pack("H*") == str and str.unpack("H*") == [ary] end assert('[0,1,127,128,255].pack("C*")') do ary = [ 0, 1, 127, 128, 255 ] str = "\x00\x01\x7F\x80\xFF" ary.pack("C*") == str and str.unpack("C*") == ary end # pack "a" assert('["abc"].pack("a")') do ["abc"].pack("a") == "a" and ["abc"].pack("a*") == "abc" and ["abc"].pack("a4") == "abc\0" end # upack "a" assert('["abc"].pack("a")') do "abc\0".unpack("a4") == ["abc\0"] and "abc ".unpack("a4") == ["abc "] end # pack "A" assert('["abc"].pack("A")') do ["abc"].pack("A") == "a" and ["abc"].pack("A*") == "abc" and ["abc"].pack("A4") == "abc " end # upack "A" assert('["abc"].pack("A")') do "abc\0".unpack("A4") == ["abc"] and "abc ".unpack("A4") == ["abc"] end # regression tests assert('issue #1') do [1, 2].pack("nn") == "\000\001\000\002" end def assert_pack tmpl, packed, unpacked assert_equal packed, unpacked.pack(tmpl) assert_equal unpacked, packed.unpack(tmpl) end PACK_IS_LITTLE_ENDIAN = "\x01\00".unpack('S')[0] == 0x01 assert 'pack float' do assert_pack 'e', "\x00\x00@@", [3.0] assert_pack 'g', "@@\x00\x00", [3.0] if PACK_IS_LITTLE_ENDIAN assert_pack 'f', "\x00\x00@@", [3.0] assert_pack 'F', "\x00\x00@@", [3.0] else assert_pack 'f', "@@\x00\x00", [3.0] assert_pack 'F', "@@\x00\x00", [3.0] end end assert 'pack double' do assert_pack 'E', "\x00\x00\x00\x00\x00\x00\b@", [3.0] assert_pack 'G', "@\b\x00\x00\x00\x00\x00\x00", [3.0] if PACK_IS_LITTLE_ENDIAN assert_pack 'd', "\x00\x00\x00\x00\x00\x00\b@", [3.0] assert_pack 'D', "\x00\x00\x00\x00\x00\x00\b@", [3.0] else assert_pack 'd', "@\b\x00\x00\x00\x00\x00\x00", [3.0] assert_pack 'D', "@\b\x00\x00\x00\x00\x00\x00", [3.0] end end assert 'pack/unpack "i"' do int_size = [0].pack('i').size raise "pack('i').size is too small (#{int_size})" if int_size < 2 if PACK_IS_LITTLE_ENDIAN str = "\xC7\xCF" + "\xFF" * (int_size-2) else str = "\xFF" * (int_size-2) + "\xCF\xC7" end assert_pack 'i', str, [-12345] end assert 'pack/unpack "I"' do uint_size = [0].pack('I').size raise "pack('I').size is too small (#{uint_size})" if uint_size < 2 if PACK_IS_LITTLE_ENDIAN str = "\x39\x30" + "\0" * (uint_size-2) else str = "\0" * (uint_size-2) + "\x30\x39" end assert_pack 'I', str, [12345] end assert 'pack/unpack "U"' do assert_equal [], "".unpack("U") assert_equal [], "".unpack("U*") assert_equal [65, 66], "ABC".unpack("U2") assert_equal [12371, 12435, 12395, 12385, 12399, 19990, 30028], "こんにちは世界".unpack("U*") assert_equal "", [].pack("U") assert_equal "", [].pack("U*") assert_equal "AB", [65, 66, 67].pack("U2") assert_equal "こんにちは世界", [12371, 12435, 12395, 12385, 12399, 19990, 30028].pack("U*") assert_equal "\000", [0].pack("U") assert_raise(RangeError) { [-0x40000000].pack("U") } assert_raise(RangeError) { [-1].pack("U") } assert_raise(RangeError) { [0x40000000].pack("U") } end mruby-2.0.0/mrbgems/mruby-print/000077500000000000000000000000001340361412400165715ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-print/mrbgem.rake000066400000000000000000000002371340361412400207100ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-print') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'standard print/puts/p' end mruby-2.0.0/mrbgems/mruby-print/mrblib/000077500000000000000000000000001340361412400200405ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-print/mrblib/print.rb000066400000000000000000000021321340361412400215170ustar00rootroot00000000000000## # Kernel # # ISO 15.3.1 module Kernel ## # Invoke method +print+ on STDOUT and passing +*args+ # # ISO 15.3.1.2.10 def print(*args) i = 0 len = args.size while i < len __printstr__ args[i].to_s i += 1 end end ## # Invoke method +puts+ on STDOUT and passing +*args*+ # # ISO 15.3.1.2.11 def puts(*args) i = 0 len = args.size while i < len s = args[i].to_s __printstr__ s __printstr__ "\n" if (s[-1] != "\n") i += 1 end __printstr__ "\n" if len == 0 nil end ## # Print human readable object description # # ISO 15.3.1.3.34 def p(*args) i = 0 len = args.size while i < len __printstr__ args[i].inspect __printstr__ "\n" i += 1 end args.__svalue end unless Kernel.respond_to?(:sprintf) def printf(*args) raise NotImplementedError.new('printf not available') end def sprintf(*args) raise NotImplementedError.new('sprintf not available') end else def printf(*args) __printstr__(sprintf(*args)) nil end end end mruby-2.0.0/mrbgems/mruby-print/src/000077500000000000000000000000001340361412400173605ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-print/src/print.c000066400000000000000000000026161340361412400206650ustar00rootroot00000000000000#include #include #include #include #include #if defined(_WIN32) # include # include #ifdef _MSC_VER # define isatty(x) _isatty(x) # define fileno(x) _fileno(x) #endif #endif static void printstr(mrb_state *mrb, mrb_value obj) { if (mrb_string_p(obj)) { #if defined(_WIN32) if (isatty(fileno(stdout))) { DWORD written; int mlen = (int)RSTRING_LEN(obj); char* utf8 = RSTRING_PTR(obj); int wlen = MultiByteToWideChar(CP_UTF8, 0, utf8, mlen, NULL, 0); wchar_t* utf16 = (wchar_t*)mrb_malloc(mrb, (wlen+1) * sizeof(wchar_t)); if (utf16 == NULL) return; if (MultiByteToWideChar(CP_UTF8, 0, utf8, mlen, utf16, wlen) > 0) { utf16[wlen] = 0; WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), utf16, wlen, &written, NULL); } mrb_free(mrb, utf16); } else #endif fwrite(RSTRING_PTR(obj), RSTRING_LEN(obj), 1, stdout); fflush(stdout); } } /* 15.3.1.2.9 */ /* 15.3.1.3.34 */ mrb_value mrb_printstr(mrb_state *mrb, mrb_value self) { mrb_value argv; mrb_get_args(mrb, "o", &argv); printstr(mrb, argv); return argv; } void mrb_mruby_print_gem_init(mrb_state* mrb) { struct RClass *krn; krn = mrb->kernel_module; mrb_define_method(mrb, krn, "__printstr__", mrb_printstr, MRB_ARGS_REQ(1)); } void mrb_mruby_print_gem_final(mrb_state* mrb) { } mruby-2.0.0/mrbgems/mruby-proc-ext/000077500000000000000000000000001340361412400171765ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-proc-ext/mrbgem.rake000066400000000000000000000002411340361412400213100ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-proc-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Proc class extension' end mruby-2.0.0/mrbgems/mruby-proc-ext/mrblib/000077500000000000000000000000001340361412400204455ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-proc-ext/mrblib/proc.rb000066400000000000000000000014531340361412400217400ustar00rootroot00000000000000class Proc def ===(*args) call(*args) end def yield(*args) call(*args) end def to_proc self end def curry(arity=self.arity) type = :proc abs = lambda {|a| a < 0 ? -a - 1 : a} arity = abs[arity] if lambda? type = :lambda self_arity = self.arity if (self_arity >= 0 && arity != self_arity) || (self_arity < 0 && abs[self_arity] > arity) raise ArgumentError, "wrong number of arguments (#{arity} for #{abs[self_arity]})" end end pproc = self make_curry = proc do |given_args=[]| __send__(type) do |*args| new_args = given_args + args if new_args.size >= arity pproc[*new_args] else make_curry[new_args] end end end make_curry.call end end mruby-2.0.0/mrbgems/mruby-proc-ext/src/000077500000000000000000000000001340361412400177655ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-proc-ext/src/proc.c000066400000000000000000000112251340361412400210750ustar00rootroot00000000000000#include #include #include #include #include #include static mrb_value mrb_proc_lambda(mrb_state *mrb, mrb_value self) { struct RProc *p = mrb_proc_ptr(self); return mrb_bool_value(MRB_PROC_STRICT_P(p)); } static mrb_value mrb_proc_source_location(mrb_state *mrb, mrb_value self) { struct RProc *p = mrb_proc_ptr(self); if (MRB_PROC_CFUNC_P(p)) { return mrb_nil_value(); } else { mrb_irep *irep = p->body.irep; int32_t line; const char *filename; filename = mrb_debug_get_filename(irep, 0); line = mrb_debug_get_line(irep, 0); return (!filename && line == -1)? mrb_nil_value() : mrb_assoc_new(mrb, mrb_str_new_cstr(mrb, filename), mrb_fixnum_value(line)); } } static mrb_value mrb_proc_inspect(mrb_state *mrb, mrb_value self) { struct RProc *p = mrb_proc_ptr(self); mrb_value str = mrb_str_new_lit(mrb, "#body.irep; const char *filename; int32_t line; mrb_str_cat_lit(mrb, str, "@"); filename = mrb_debug_get_filename(irep, 0); mrb_str_cat_cstr(mrb, str, filename ? filename : "-"); mrb_str_cat_lit(mrb, str, ":"); line = mrb_debug_get_line(irep, 0); if (line != -1) { str = mrb_format(mrb, "%S:%S", str, mrb_fixnum_value(line)); } else { mrb_str_cat_lit(mrb, str, "-"); } } if (MRB_PROC_STRICT_P(p)) { mrb_str_cat_lit(mrb, str, " (lambda)"); } mrb_str_cat_lit(mrb, str, ">"); return str; } static mrb_value mrb_kernel_proc(mrb_state *mrb, mrb_value self) { mrb_value blk; mrb_get_args(mrb, "&", &blk); if (mrb_nil_p(blk)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "tried to create Proc object without a block"); } return blk; } /* * call-seq: * prc.parameters -> array * * Returns the parameter information of this proc. * * prc = lambda{|x, y=42, *other|} * prc.parameters #=> [[:req, :x], [:opt, :y], [:rest, :other]] */ static mrb_value mrb_proc_parameters(mrb_state *mrb, mrb_value self) { struct parameters_type { int size; const char *name; } *p, parameters_list [] = { {0, "req"}, {0, "opt"}, {0, "rest"}, {0, "req"}, {0, "block"}, {0, NULL} }; const struct RProc *proc = mrb_proc_ptr(self); const struct mrb_irep *irep = proc->body.irep; mrb_aspec aspec; mrb_value sname, parameters; int i, j; int max = -1; if (MRB_PROC_CFUNC_P(proc)) { // TODO cfunc aspec is not implemented yet return mrb_ary_new(mrb); } if (!irep) { return mrb_ary_new(mrb); } if (!irep->lv) { return mrb_ary_new(mrb); } if (*irep->iseq != OP_ENTER) { return mrb_ary_new(mrb); } if (!MRB_PROC_STRICT_P(proc)) { parameters_list[0].name = "opt"; parameters_list[3].name = "opt"; } aspec = PEEK_W(irep->iseq+1); parameters_list[0].size = MRB_ASPEC_REQ(aspec); parameters_list[1].size = MRB_ASPEC_OPT(aspec); parameters_list[2].size = MRB_ASPEC_REST(aspec); parameters_list[3].size = MRB_ASPEC_POST(aspec); parameters_list[4].size = MRB_ASPEC_BLOCK(aspec); parameters = mrb_ary_new_capa(mrb, irep->nlocals-1); max = irep->nlocals-1; for (i = 0, p = parameters_list; p->name; p++) { if (p->size <= 0) continue; sname = mrb_symbol_value(mrb_intern_cstr(mrb, p->name)); for (j = 0; j < p->size; i++, j++) { mrb_value a; a = mrb_ary_new(mrb); mrb_ary_push(mrb, a, sname); if (i < max && irep->lv[i].name) { mrb_sym sym = irep->lv[i].name; const char *name = mrb_sym2name(mrb, sym); switch (name[0]) { case '*': case '&': break; default: mrb_ary_push(mrb, a, mrb_symbol_value(sym)); break; } } mrb_ary_push(mrb, parameters, a); } } return parameters; } void mrb_mruby_proc_ext_gem_init(mrb_state* mrb) { struct RClass *p = mrb->proc_class; mrb_define_method(mrb, p, "lambda?", mrb_proc_lambda, MRB_ARGS_NONE()); mrb_define_method(mrb, p, "source_location", mrb_proc_source_location, MRB_ARGS_NONE()); mrb_define_method(mrb, p, "to_s", mrb_proc_inspect, MRB_ARGS_NONE()); mrb_define_method(mrb, p, "inspect", mrb_proc_inspect, MRB_ARGS_NONE()); mrb_define_method(mrb, p, "parameters", mrb_proc_parameters, MRB_ARGS_NONE()); mrb_define_class_method(mrb, mrb->kernel_module, "proc", mrb_kernel_proc, MRB_ARGS_NONE()); mrb_define_method(mrb, mrb->kernel_module, "proc", mrb_kernel_proc, MRB_ARGS_NONE()); } void mrb_mruby_proc_ext_gem_final(mrb_state* mrb) { } mruby-2.0.0/mrbgems/mruby-proc-ext/test/000077500000000000000000000000001340361412400201555ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-proc-ext/test/proc.c000066400000000000000000000031231340361412400212630ustar00rootroot00000000000000#include #include #include static mrb_value return_func_name(mrb_state *mrb, mrb_value self) { return mrb_cfunc_env_get(mrb, 0); } static mrb_value proc_new_cfunc_with_env(mrb_state *mrb, mrb_value self) { mrb_sym n; mrb_value n_val; mrb_method_t m; struct RProc *p; mrb_get_args(mrb, "n", &n); n_val = mrb_symbol_value(n); p = mrb_proc_new_cfunc_with_env(mrb, return_func_name, 1, &n_val); MRB_METHOD_FROM_PROC(m, p); mrb_define_method_raw(mrb, mrb_class_ptr(self), n, m); return self; } static mrb_value return_env(mrb_state *mrb, mrb_value self) { mrb_int idx; mrb_get_args(mrb, "i", &idx); return mrb_cfunc_env_get(mrb, idx); } static mrb_value cfunc_env_get(mrb_state *mrb, mrb_value self) { mrb_sym n; mrb_value *argv; mrb_int argc; mrb_method_t m; struct RProc *p; mrb_get_args(mrb, "na", &n, &argv, &argc); p = mrb_proc_new_cfunc_with_env(mrb, return_env, argc, argv); MRB_METHOD_FROM_PROC(m, p); mrb_define_method_raw(mrb, mrb_class_ptr(self), n, m); return self; } static mrb_value cfunc_without_env(mrb_state *mrb, mrb_value self) { return mrb_cfunc_env_get(mrb, 0); } void mrb_mruby_proc_ext_gem_test(mrb_state *mrb) { struct RClass *cls; cls = mrb_define_class(mrb, "ProcExtTest", mrb->object_class); mrb_define_module_function(mrb, cls, "mrb_proc_new_cfunc_with_env", proc_new_cfunc_with_env, MRB_ARGS_REQ(1)); mrb_define_module_function(mrb, cls, "mrb_cfunc_env_get", cfunc_env_get, MRB_ARGS_REQ(2)); mrb_define_module_function(mrb, cls, "cfunc_without_env", cfunc_without_env, MRB_ARGS_NONE()); } mruby-2.0.0/mrbgems/mruby-proc-ext/test/proc.rb000066400000000000000000000051071340361412400214500ustar00rootroot00000000000000## # Proc(Ext) Test assert('Proc#source_location') do loc = Proc.new {}.source_location next true if loc.nil? assert_equal loc[0][-7, 7], 'proc.rb' assert_equal loc[1], 5 end assert('Proc#inspect') do ins = Proc.new{}.inspect assert_kind_of String, ins end assert('Proc#lambda?') do assert_true lambda{}.lambda? assert_true !Proc.new{}.lambda? end assert('Proc#===') do proc = Proc.new {|a| a * 2} assert_equal 20, (proc === 10) end assert('Proc#yield') do proc = Proc.new {|a| a * 2} assert_equal 20, proc.yield(10) end assert('Proc#curry') do b = proc {|x, y, z| (x||0) + (y||0) + (z||0) } assert_equal 6, b.curry[1][2][3] assert_equal 6, b.curry[1, 2][3, 4] assert_equal 6, b.curry(5)[1][2][3][4][5] assert_equal 6, b.curry(5)[1, 2][3, 4][5] assert_equal 1, b.curry(1)[1] b = lambda {|x, y, z| (x||0) + (y||0) + (z||0) } assert_equal 6, b.curry[1][2][3] assert_raise(ArgumentError) { b.curry[1, 2][3, 4] } assert_raise(ArgumentError) { b.curry(5) } assert_raise(ArgumentError) { b.curry(1) } assert_false(proc{}.curry.lambda?) assert_true(lambda{}.curry.lambda?) end assert('Proc#parameters') do assert_equal([], Proc.new {}.parameters) assert_equal([], Proc.new {||}.parameters) assert_equal([[:opt, :a]], Proc.new {|a|}.parameters) assert_equal([[:req, :a]], lambda {|a|}.parameters) assert_equal([[:opt, :a]], lambda {|a=nil|}.parameters) assert_equal([[:req, :a]], ->(a){}.parameters) assert_equal([[:rest]], lambda { |*| }.parameters) assert_equal([[:rest, :a]], Proc.new {|*a|}.parameters) assert_equal([[:opt, :a], [:opt, :b], [:opt, :c], [:opt, :d], [:rest, :e], [:opt, :f], [:opt, :g], [:block, :h]], Proc.new {|a,b,c=:c,d=:d,*e,f,g,&h|}.parameters) assert_equal([[:req, :a], [:req, :b], [:opt, :c], [:opt, :d], [:rest, :e], [:req, :f], [:req, :g], [:block, :h]], lambda {|a,b,c=:c,d=:d,*e,f,g,&h|}.parameters) end assert('Proc#to_proc') do proc = Proc.new {} assert_equal proc, proc.to_proc end assert('Kernel#proc') do assert_true !proc{|a|}.lambda? assert_raise LocalJumpError do proc{ break }.call end end assert('mrb_proc_new_cfunc_with_env') do ProcExtTest.mrb_proc_new_cfunc_with_env(:test) ProcExtTest.mrb_proc_new_cfunc_with_env(:mruby) t = ProcExtTest.new assert_equal :test, t.test assert_equal :mruby, t.mruby end assert('mrb_cfunc_env_get') do ProcExtTest.mrb_cfunc_env_get :get_int, [0, 1, 2] t = ProcExtTest.new assert_raise(TypeError) { t.cfunc_without_env } assert_raise(IndexError) { t.get_int(-1) } assert_raise(IndexError) { t.get_int(3) } assert_equal 1, t.get_int(1) end mruby-2.0.0/mrbgems/mruby-random/000077500000000000000000000000001340361412400167155ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-random/mrbgem.rake000066400000000000000000000002271340361412400210330ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-random') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Random class' end mruby-2.0.0/mrbgems/mruby-random/src/000077500000000000000000000000001340361412400175045ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-random/src/mt19937ar.c000066400000000000000000000157611340361412400212420ustar00rootroot00000000000000/* ** mt19937ar.c - MT Random functions ** ** Copyright (C) 1997 - 2016, Makoto Matsumoto and Takuji Nishimura, ** All rights reserved. ** ** Permission is hereby granted, free of charge, to any person obtaining ** a copy of this software and associated documentation files (the ** "Software"), to deal in the Software without restriction, including ** without limitation the rights to use, copy, modify, merge, publish, ** distribute, sublicense, and/or sell copies of the Software, and to ** permit persons to whom the Software is furnished to do so, subject to ** the following conditions: ** ** The above copyright notice and this permission notice shall be ** included in all copies or substantial portions of the Software. ** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ** ** [ MIT license: http://www.opensource.org/licenses/mit-license.php ] ** ** Any feedback is very welcome. ** http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html ** email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space) ** ** This version is modified by mruby developers. If you see any problem, ** contact us first at https://github.com/mruby/mruby/issues */ #include #include "mt19937ar.h" /* Period parameters */ /* #define N 624 */ #define M 397 #define MATRIX_A 0x9908b0dfUL /* constant vector a */ #define UPPER_MASK 0x80000000UL /* most significant w-r bits */ #define LOWER_MASK 0x7fffffffUL /* least significant r bits */ #if 0 /* dead_code */ static unsigned long mt[N]; /* the array for the state vector */ static int mti=N+1; /* mti==N+1 means mt[N] is not initialized */ #endif /* dead_code */ void mrb_random_init_genrand(mt_state *t, unsigned long s) { t->mt[0]= s & 0xffffffffUL; for (t->mti=1; t->mtimti++) { t->mt[t->mti] = (1812433253UL * (t->mt[t->mti-1] ^ (t->mt[t->mti-1] >> 30)) + t->mti); t->mt[t->mti] &= 0xffffffffUL; } } unsigned long mrb_random_genrand_int32(mt_state *t) { unsigned long y; static const unsigned long mag01[2]={0x0UL, MATRIX_A}; /* mag01[x] = x * MATRIX_A for x=0,1 */ if (t->mti >= N) { /* generate N words at one time */ int kk; if (t->mti == N+1) /* if init_genrand() has not been called, */ mrb_random_init_genrand(t, 5489UL); /* a default initial seed is used */ for (kk=0;kkmt[kk]&UPPER_MASK)|(t->mt[kk+1]&LOWER_MASK); t->mt[kk] = t->mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1UL]; } for (;kkmt[kk]&UPPER_MASK)|(t->mt[kk+1]&LOWER_MASK); t->mt[kk] = t->mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1UL]; } y = (t->mt[N-1]&UPPER_MASK)|(t->mt[0]&LOWER_MASK); t->mt[N-1] = t->mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL]; t->mti = 0; } y = t->mt[t->mti++]; /* Tempering */ y ^= (y >> 11); y ^= (y << 7) & 0x9d2c5680UL; y ^= (y << 15) & 0xefc60000UL; y ^= (y >> 18); t->gen.int_ = y; return y; } double mrb_random_genrand_real1(mt_state *t) { mrb_random_genrand_int32(t); t->gen.double_ = t->gen.int_*(1.0/4294967295.0); return t->gen.double_; /* divided by 2^32-1 */ } #if 0 /* dead_code */ /* initializes mt[N] with a seed */ void init_genrand(unsigned long s) { mt[0]= s & 0xffffffffUL; for (mti=1; mti> 30)) + mti); /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */ /* In the previous versions, MSBs of the seed affect */ /* only MSBs of the array mt[]. */ /* 2002/01/09 modified by Makoto Matsumoto */ mt[mti] &= 0xffffffffUL; /* for >32 bit machines */ } } /* initialize by an array with array-length */ /* init_key is the array for initializing keys */ /* key_length is its length */ /* slight change for C++, 2004/2/26 */ void init_by_array(unsigned long init_key[], int key_length) { int i, j, k; init_genrand(19650218UL); i=1; j=0; k = (N>key_length ? N : key_length); for (; k; k--) { mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1664525UL)) + init_key[j] + j; /* non linear */ mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */ i++; j++; if (i>=N) { mt[0] = mt[N-1]; i=1; } if (j>=key_length) j=0; } for (k=N-1; k; k--) { mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941UL)) - i; /* non linear */ mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */ i++; if (i>=N) { mt[0] = mt[N-1]; i=1; } } mt[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */ } /* generates a random number on [0,0xffffffff]-interval */ unsigned long genrand_int32(void) { unsigned long y; static const unsigned long mag01[2]={0x0UL, MATRIX_A}; /* mag01[x] = x * MATRIX_A for x=0,1 */ if (mti >= N) { /* generate N words at one time */ int kk; if (mti == N+1) /* if init_genrand() has not been called, */ init_genrand(5489UL); /* a default initial seed is used */ for (kk=0;kk> 1) ^ mag01[y & 0x1UL]; } for (;kk> 1) ^ mag01[y & 0x1UL]; } y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK); mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL]; mti = 0; } y = mt[mti++]; /* Tempering */ y ^= (y >> 11); y ^= (y << 7) & 0x9d2c5680UL; y ^= (y << 15) & 0xefc60000UL; y ^= (y >> 18); return y; } /* generates a random number on [0,0x7fffffff]-interval */ long genrand_int31(void) { return (long)(genrand_int32()>>1); } /* generates a random number on [0,1]-real-interval */ double genrand_real1(void) { return genrand_int32()*(1.0/4294967295.0); /* divided by 2^32-1 */ } /* generates a random number on [0,1)-real-interval */ double genrand_real2(void) { return genrand_int32()*(1.0/4294967296.0); /* divided by 2^32 */ } /* generates a random number on (0,1)-real-interval */ double genrand_real3(void) { return (((double)genrand_int32()) + 0.5)*(1.0/4294967296.0); /* divided by 2^32 */ } /* generates a random number on [0,1) with 53-bit resolution*/ double genrand_res53(void) { unsigned long a=genrand_int32()>>5, b=genrand_int32()>>6; return(a*67108864.0+b)*(1.0/9007199254740992.0); } /* These real versions are due to Isaku Wada, 2002/01/09 added */ #endif /* dead_code */ mruby-2.0.0/mrbgems/mruby-random/src/mt19937ar.h000066400000000000000000000054021340361412400212360ustar00rootroot00000000000000/* ** mt19937ar.h - MT Random functions ** ** Copyright (C) 1997 - 2016, Makoto Matsumoto and Takuji Nishimura, ** All rights reserved. ** ** Permission is hereby granted, free of charge, to any person obtaining ** a copy of this software and associated documentation files (the ** "Software"), to deal in the Software without restriction, including ** without limitation the rights to use, copy, modify, merge, publish, ** distribute, sublicense, and/or sell copies of the Software, and to ** permit persons to whom the Software is furnished to do so, subject to ** the following conditions: ** ** The above copyright notice and this permission notice shall be ** included in all copies or substantial portions of the Software. ** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ** ** [ MIT license: http://www.opensource.org/licenses/mit-license.php ] ** ** Any feedback is very welcome. ** http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html ** email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space) ** ** This version is modified by mruby developers. If you see any problem, ** contact us first at https://github.com/mruby/mruby/issues */ #define N 624 typedef struct { unsigned long mt[N]; int mti; union { unsigned long int_; double double_; } gen; mrb_int seed; mrb_bool has_seed : 1; } mt_state; void mrb_random_init_genrand(mt_state *, unsigned long); unsigned long mrb_random_genrand_int32(mt_state *); double mrb_random_genrand_real1(mt_state *t); /* initializes mt[N] with a seed */ void init_genrand(unsigned long s); /* initialize by an array with array-length */ /* init_key is the array for initializing keys */ /* key_length is its length */ /* slight change for C++, 2004/2/26 */ void init_by_array(unsigned long init_key[], int key_length); /* generates a random number on [0,0xffffffff]-interval */ unsigned long genrand_int32(void); /* generates a random number on [0,0x7fffffff]-interval */ long genrand_int31(void); /* These real versions are due to Isaku Wada, 2002/01/09 added */ /* generates a random number on [0,1]-real-interval */ double genrand_real1(void); /* generates a random number on [0,1)-real-interval */ double genrand_real2(void); /* generates a random number on (0,1)-real-interval */ double genrand_real3(void); /* generates a random number on [0,1) with 53-bit resolution*/ double genrand_res53(void); mruby-2.0.0/mrbgems/mruby-random/src/random.c000066400000000000000000000173231340361412400211360ustar00rootroot00000000000000/* ** random.c - Random module ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include "mt19937ar.h" #include static char const MT_STATE_KEY[] = "$mrb_i_mt_state"; static const struct mrb_data_type mt_state_type = { MT_STATE_KEY, mrb_free, }; static mrb_value mrb_random_rand(mrb_state *mrb, mrb_value self); static mrb_value mrb_random_srand(mrb_state *mrb, mrb_value self); static void mt_srand(mt_state *t, unsigned long seed) { mrb_random_init_genrand(t, seed); } static unsigned long mt_rand(mt_state *t) { return mrb_random_genrand_int32(t); } static double mt_rand_real(mt_state *t) { return mrb_random_genrand_real1(t); } static mrb_value mrb_random_mt_srand(mrb_state *mrb, mt_state *t, mrb_value seed) { if (mrb_nil_p(seed)) { seed = mrb_fixnum_value((mrb_int)(time(NULL) + mt_rand(t))); if (mrb_fixnum(seed) < 0) { seed = mrb_fixnum_value(0 - mrb_fixnum(seed)); } } mt_srand(t, (unsigned) mrb_fixnum(seed)); return seed; } static mrb_value mrb_random_mt_rand(mrb_state *mrb, mt_state *t, mrb_value max) { mrb_value value; if (mrb_fixnum(max) == 0) { value = mrb_float_value(mrb, mt_rand_real(t)); } else { value = mrb_fixnum_value(mt_rand(t) % mrb_fixnum(max)); } return value; } static mrb_value get_opt(mrb_state* mrb) { mrb_value arg; arg = mrb_nil_value(); mrb_get_args(mrb, "|o", &arg); if (!mrb_nil_p(arg)) { mrb_int i; arg = mrb_to_int(mrb, arg); i = mrb_fixnum(arg); if (i < 0) { arg = mrb_fixnum_value(0 - i); } } return arg; } static mrb_value get_random(mrb_state *mrb) { return mrb_const_get(mrb, mrb_obj_value(mrb_class_get(mrb, "Random")), mrb_intern_lit(mrb, "DEFAULT")); } static mt_state * get_random_state(mrb_state *mrb) { mrb_value random_val = get_random(mrb); return DATA_GET_PTR(mrb, random_val, &mt_state_type, mt_state); } static mrb_value mrb_random_g_rand(mrb_state *mrb, mrb_value self) { mrb_value random = get_random(mrb); return mrb_random_rand(mrb, random); } static mrb_value mrb_random_g_srand(mrb_state *mrb, mrb_value self) { mrb_value random = get_random(mrb); return mrb_random_srand(mrb, random); } static mrb_value mrb_random_init(mrb_state *mrb, mrb_value self) { mrb_value seed; mt_state *t; seed = get_opt(mrb); /* avoid memory leaks */ t = (mt_state*)DATA_PTR(self); if (t) { mrb_free(mrb, t); } mrb_data_init(self, NULL, &mt_state_type); t = (mt_state *)mrb_malloc(mrb, sizeof(mt_state)); t->mti = N + 1; seed = mrb_random_mt_srand(mrb, t, seed); if (mrb_nil_p(seed)) { t->has_seed = FALSE; } else { mrb_assert(mrb_fixnum_p(seed)); t->has_seed = TRUE; t->seed = mrb_fixnum(seed); } mrb_data_init(self, t, &mt_state_type); return self; } static void mrb_random_rand_seed(mrb_state *mrb, mt_state *t) { if (!t->has_seed) { mrb_random_mt_srand(mrb, t, mrb_nil_value()); } } static mrb_value mrb_random_rand(mrb_state *mrb, mrb_value self) { mrb_value max; mt_state *t = DATA_GET_PTR(mrb, self, &mt_state_type, mt_state); max = get_opt(mrb); mrb_random_rand_seed(mrb, t); return mrb_random_mt_rand(mrb, t, max); } static mrb_value mrb_random_srand(mrb_state *mrb, mrb_value self) { mrb_value seed; mrb_value old_seed; mt_state *t = DATA_GET_PTR(mrb, self, &mt_state_type, mt_state); seed = get_opt(mrb); seed = mrb_random_mt_srand(mrb, t, seed); old_seed = t->has_seed? mrb_fixnum_value(t->seed) : mrb_nil_value(); if (mrb_nil_p(seed)) { t->has_seed = FALSE; } else { mrb_assert(mrb_fixnum_p(seed)); t->has_seed = TRUE; t->seed = mrb_fixnum(seed); } return old_seed; } /* * call-seq: * ary.shuffle! -> ary * * Shuffles elements in self in place. */ static mrb_value mrb_ary_shuffle_bang(mrb_state *mrb, mrb_value ary) { mrb_int i; mt_state *random = NULL; if (RARRAY_LEN(ary) > 1) { mrb_get_args(mrb, "|d", &random, &mt_state_type); if (random == NULL) { random = get_random_state(mrb); } mrb_random_rand_seed(mrb, random); mrb_ary_modify(mrb, mrb_ary_ptr(ary)); for (i = RARRAY_LEN(ary) - 1; i > 0; i--) { mrb_int j; mrb_value *ptr = RARRAY_PTR(ary); mrb_value tmp; j = mrb_fixnum(mrb_random_mt_rand(mrb, random, mrb_fixnum_value(RARRAY_LEN(ary)))); tmp = ptr[i]; ptr[i] = ptr[j]; ptr[j] = tmp; } } return ary; } /* * call-seq: * ary.shuffle -> new_ary * * Returns a new array with elements of self shuffled. */ static mrb_value mrb_ary_shuffle(mrb_state *mrb, mrb_value ary) { mrb_value new_ary = mrb_ary_new_from_values(mrb, RARRAY_LEN(ary), RARRAY_PTR(ary)); mrb_ary_shuffle_bang(mrb, new_ary); return new_ary; } /* * call-seq: * ary.sample -> obj * ary.sample(n) -> new_ary * * Choose a random element or +n+ random elements from the array. * * The elements are chosen by using random and unique indices into the array * in order to ensure that an element doesn't repeat itself unless the array * already contained duplicate elements. * * If the array is empty the first form returns +nil+ and the second form * returns an empty array. */ static mrb_value mrb_ary_sample(mrb_state *mrb, mrb_value ary) { mrb_int n = 0; mrb_bool given; mt_state *random = NULL; mrb_int len; mrb_get_args(mrb, "|i?d", &n, &given, &random, &mt_state_type); if (random == NULL) { random = get_random_state(mrb); } mrb_random_rand_seed(mrb, random); mt_rand(random); len = RARRAY_LEN(ary); if (!given) { /* pick one element */ switch (len) { case 0: return mrb_nil_value(); case 1: return RARRAY_PTR(ary)[0]; default: return RARRAY_PTR(ary)[mt_rand(random) % len]; } } else { mrb_value result; mrb_int i, j; if (n < 0) mrb_raise(mrb, E_ARGUMENT_ERROR, "negative sample number"); if (n > len) n = len; result = mrb_ary_new_capa(mrb, n); for (i=0; iarray_class; mrb_define_method(mrb, mrb->kernel_module, "rand", mrb_random_g_rand, MRB_ARGS_OPT(1)); mrb_define_method(mrb, mrb->kernel_module, "srand", mrb_random_g_srand, MRB_ARGS_OPT(1)); random = mrb_define_class(mrb, "Random", mrb->object_class); MRB_SET_INSTANCE_TT(random, MRB_TT_DATA); mrb_define_class_method(mrb, random, "rand", mrb_random_g_rand, MRB_ARGS_OPT(1)); mrb_define_class_method(mrb, random, "srand", mrb_random_g_srand, MRB_ARGS_OPT(1)); mrb_define_method(mrb, random, "initialize", mrb_random_init, MRB_ARGS_OPT(1)); mrb_define_method(mrb, random, "rand", mrb_random_rand, MRB_ARGS_OPT(1)); mrb_define_method(mrb, random, "srand", mrb_random_srand, MRB_ARGS_OPT(1)); mrb_define_method(mrb, array, "shuffle", mrb_ary_shuffle, MRB_ARGS_OPT(1)); mrb_define_method(mrb, array, "shuffle!", mrb_ary_shuffle_bang, MRB_ARGS_OPT(1)); mrb_define_method(mrb, array, "sample", mrb_ary_sample, MRB_ARGS_OPT(2)); mrb_const_set(mrb, mrb_obj_value(random), mrb_intern_lit(mrb, "DEFAULT"), mrb_obj_new(mrb, random, 0, NULL)); } void mrb_mruby_random_gem_final(mrb_state *mrb) { } mruby-2.0.0/mrbgems/mruby-random/src/random.h000066400000000000000000000002601340361412400211330ustar00rootroot00000000000000/* ** random.h - Random module ** ** See Copyright Notice in mruby.h */ #ifndef MRUBY_RANDOM_H #define MRUBY_RANDOM_H void mrb_mruby_random_gem_init(mrb_state *mrb); #endif mruby-2.0.0/mrbgems/mruby-random/test/000077500000000000000000000000001340361412400176745ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-random/test/random.rb000066400000000000000000000033061340361412400215030ustar00rootroot00000000000000## # Random Test assert("Random#srand") do r1 = Random.new(123) r2 = Random.new(123) r1.rand == r2.rand end assert("Kernel::srand") do srand(234) r1 = rand srand(234) r2 = rand r1 == r2 end assert("Random::srand") do Random.srand(345) r1 = rand srand(345) r2 = Random.rand r1 == r2 end assert("fixnum") do rand(3).class == Fixnum end assert("float") do rand.class == Float end assert("Array#shuffle") do ary = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] shuffled = ary.shuffle ary == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] and shuffled != ary and 10.times { |x| ary.include? x } end assert('Array#shuffle!') do ary = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] ary.shuffle! ary != [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] and 10.times { |x| ary.include? x } end assert("Array#shuffle(random)") do assert_raise(TypeError) do # this will cause an exception due to the wrong argument [1, 2].shuffle "Not a Random instance" end # verify that the same seed causes the same results ary1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] shuffle1 = ary1.shuffle Random.new 345 ary2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] shuffle2 = ary2.shuffle Random.new 345 ary1 != shuffle1 and 10.times { |x| shuffle1.include? x } and shuffle1 == shuffle2 end assert('Array#shuffle!(random)') do assert_raise(TypeError) do # this will cause an exception due to the wrong argument [1, 2].shuffle! "Not a Random instance" end # verify that the same seed causes the same results ary1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] ary1.shuffle! Random.new 345 ary2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] ary2.shuffle! Random.new 345 ary1 != [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] and 10.times { |x| ary1.include? x } and ary1 == ary2 end mruby-2.0.0/mrbgems/mruby-range-ext/000077500000000000000000000000001340361412400173275ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-range-ext/mrbgem.rake000066400000000000000000000002431340361412400214430ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-range-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Range class extension' end mruby-2.0.0/mrbgems/mruby-range-ext/mrblib/000077500000000000000000000000001340361412400205765ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-range-ext/mrblib/range.rb000066400000000000000000000012171340361412400222200ustar00rootroot00000000000000class Range ## # call-seq: # rng.first -> obj # rng.first(n) -> an_array # # Returns the first object in the range, or an array of the first +n+ # elements. # # (10..20).first #=> 10 # (10..20).first(3) #=> [10, 11, 12] # def first(*args) return self.begin if args.empty? raise ArgumentError, "wrong number of arguments (given #{args.length}, expected 1)" unless args.length == 1 nv = args[0] n = nv.__to_int raise ArgumentError, "negative array size (or size too big)" unless 0 <= n ary = [] each do |i| break if n <= 0 ary.push(i) n -= 1 end ary end end mruby-2.0.0/mrbgems/mruby-range-ext/src/000077500000000000000000000000001340361412400201165ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-range-ext/src/range.c000066400000000000000000000077471340361412400213750ustar00rootroot00000000000000#include #include #include static mrb_bool r_le(mrb_state *mrb, mrb_value a, mrb_value b) { mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b); /* compare result */ /* output :a < b => -1, a = b => 0, a > b => +1 */ if (mrb_fixnum_p(r)) { mrb_int c = mrb_fixnum(r); if (c == 0 || c == -1) return TRUE; } return FALSE; } static mrb_bool r_lt(mrb_state *mrb, mrb_value a, mrb_value b) { mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b); /* output :a < b => -1, a = b => 0, a > b => +1 */ return mrb_fixnum_p(r) && mrb_fixnum(r) == -1; } /* * call-seq: * rng.cover?(obj) -> true or false * * Returns true if +obj+ is between the begin and end of * the range. * * This tests begin <= obj <= end when #exclude_end? is +false+ * and begin <= obj < end when #exclude_end? is +true+. * * ("a".."z").cover?("c") #=> true * ("a".."z").cover?("5") #=> false * ("a".."z").cover?("cc") #=> true */ static mrb_value mrb_range_cover(mrb_state *mrb, mrb_value range) { mrb_value val; struct RRange *r = mrb_range_ptr(mrb, range); mrb_value beg, end; mrb_get_args(mrb, "o", &val); beg = r->edges->beg; end = r->edges->end; if (r_le(mrb, beg, val)) { if (r->excl) { if (r_lt(mrb, val, end)) return mrb_true_value(); } else { if (r_le(mrb, val, end)) return mrb_true_value(); } } return mrb_false_value(); } /* * call-seq: * rng.last -> obj * rng.last(n) -> an_array * * Returns the last object in the range, * or an array of the last +n+ elements. * * Note that with no arguments +last+ will return the object that defines * the end of the range even if #exclude_end? is +true+. * * (10..20).last #=> 20 * (10...20).last #=> 20 * (10..20).last(3) #=> [18, 19, 20] * (10...20).last(3) #=> [17, 18, 19] */ static mrb_value mrb_range_last(mrb_state *mrb, mrb_value range) { mrb_value num; mrb_value array; struct RRange *r = mrb_range_ptr(mrb, range); if (mrb_get_args(mrb, "|o", &num) == 0) { return r->edges->end; } array = mrb_funcall(mrb, range, "to_a", 0); return mrb_funcall(mrb, array, "last", 1, mrb_to_int(mrb, num)); } /* * call-seq: * rng.size -> num * * Returns the number of elements in the range. Both the begin and the end of * the Range must be Numeric, otherwise nil is returned. * * (10..20).size #=> 11 * ('a'..'z').size #=> nil */ static mrb_value mrb_range_size(mrb_state *mrb, mrb_value range) { struct RRange *r = mrb_range_ptr(mrb, range); mrb_value beg, end; mrb_float beg_f, end_f; mrb_bool num_p = TRUE; mrb_bool excl; beg = r->edges->beg; end = r->edges->end; excl = r->excl; if (mrb_fixnum_p(beg)) { beg_f = (mrb_float)mrb_fixnum(beg); } else if (mrb_float_p(beg)) { beg_f = mrb_float(beg); } else { num_p = FALSE; } if (mrb_fixnum_p(end)) { end_f = (mrb_float)mrb_fixnum(end); } else if (mrb_float_p(end)) { end_f = mrb_float(end); } else { num_p = FALSE; } if (num_p) { mrb_float n = end_f - beg_f; mrb_float err = (fabs(beg_f) + fabs(end_f) + fabs(end_f-beg_f)) * MRB_FLOAT_EPSILON; if (err>0.5) err=0.5; if (excl) { if (n<=0) return mrb_fixnum_value(0); if (n<1) n = 0; else n = floor(n - err); } else { if (n<0) return mrb_fixnum_value(0); n = floor(n + err); } if (isinf(n+1)) return mrb_float_value(mrb, INFINITY); return mrb_fixnum_value((mrb_int)n+1); } return mrb_nil_value(); } void mrb_mruby_range_ext_gem_init(mrb_state* mrb) { struct RClass * s = mrb_class_get(mrb, "Range"); mrb_define_method(mrb, s, "cover?", mrb_range_cover, MRB_ARGS_REQ(1)); mrb_define_method(mrb, s, "last", mrb_range_last, MRB_ARGS_OPT(1)); mrb_define_method(mrb, s, "size", mrb_range_size, MRB_ARGS_NONE()); } void mrb_mruby_range_ext_gem_final(mrb_state* mrb) { } mruby-2.0.0/mrbgems/mruby-range-ext/test/000077500000000000000000000000001340361412400203065ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-range-ext/test/range.rb000066400000000000000000000015051340361412400217300ustar00rootroot00000000000000## # Range(Ext) Test assert('Range#cover?') do assert_true ("a".."z").cover?("c") assert_true !("a".."z").cover?("5") assert_true ("a".."z").cover?("cc") end assert('Range#first') do assert_equal 10, (10..20).first assert_equal [10, 11, 12], (10..20).first(3) assert_equal [0, 1, 2], (0..Float::INFINITY).first(3) end assert('Range#last') do assert_equal 20, (10..20).last assert_equal 20, (10...20).last assert_equal [18, 19, 20], (10..20).last(3) assert_equal [17, 18, 19], (10...20).last(3) end assert('Range#size') do assert_equal 42, (1..42).size assert_equal 41, (1...42).size assert_equal 6, (1...6.3).size assert_equal 5, (1...6.0).size assert_equal 5, (1.1...6).size assert_equal 15, (1.0..15.9).size assert_equal Float::INFINITY, (0..Float::INFINITY).size assert_nil ('a'..'z').size end mruby-2.0.0/mrbgems/mruby-sleep/000077500000000000000000000000001340361412400165455ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-sleep/.gitignore000066400000000000000000000000201340361412400205250ustar00rootroot00000000000000/mruby *.so *.amruby-2.0.0/mrbgems/mruby-sleep/.travis.yml000066400000000000000000000005751340361412400206650ustar00rootroot00000000000000dist: trusty language: c compiler: - gcc - clang env: - MRUBY_VERSION=1.2.0 - MRUBY_VERSION=master matrix: allow_failures: - env: MRUBY_VERSION=master branches: only: - master addons: apt: packages: - rake - bison - git - gperf # - aclocal # - automake # - autoconf # - autotools-dev script: - rake testmruby-2.0.0/mrbgems/mruby-sleep/.travis_build_config.rb000066400000000000000000000001671340361412400231700ustar00rootroot00000000000000MRuby::Build.new do |conf| toolchain :gcc conf.gembox 'default' conf.gem '../mruby-sleep' conf.enable_test end mruby-2.0.0/mrbgems/mruby-sleep/README.md000066400000000000000000000006161340361412400200270ustar00rootroot00000000000000# Sleep Module for mruby mruby sleep module ## install by mrbgems - add conf.gem line to `build_config.rb` ```ruby MRuby::Build.new do |conf| # ... (snip) ... conf.gem :git => 'https://github.com/matsumoto-r/mruby-sleep.git' end ``` ## example ```ruby Sleep::sleep(10) Sleep::usleep(10000) ``` # License under the MIT License: * http://www.opensource.org/licenses/mit-license.php mruby-2.0.0/mrbgems/mruby-sleep/Rakefile000066400000000000000000000013161340361412400202130ustar00rootroot00000000000000MRUBY_CONFIG=File.expand_path(ENV["MRUBY_CONFIG"] || ".travis_build_config.rb") MRUBY_VERSION=ENV["MRUBY_VERSION"] || "1.2.0" file :mruby do cmd = "git clone --depth=1 git://github.com/mruby/mruby.git" if MRUBY_VERSION != 'master' cmd << " && cd mruby" cmd << " && git fetch --tags && git checkout $(git rev-parse #{MRUBY_VERSION})" end sh cmd end desc "compile binary" task :compile => :mruby do sh "cd mruby && MRUBY_CONFIG=#{MRUBY_CONFIG} rake all" end desc "test" task :test => :mruby do sh "cd mruby && MRUBY_CONFIG=#{MRUBY_CONFIG} rake all test" end desc "cleanup" task :clean do exit 0 unless File.directory?('mruby') sh "cd mruby && rake deep_clean" end task :default => :compile mruby-2.0.0/mrbgems/mruby-sleep/example/000077500000000000000000000000001340361412400202005ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-sleep/example/sleep.rb000066400000000000000000000000471340361412400216360ustar00rootroot00000000000000Sleep::sleep(10) Sleep::usleep(10000) mruby-2.0.0/mrbgems/mruby-sleep/mrbgem.rake000066400000000000000000000002201340361412400206540ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-sleep') do |spec| spec.license = 'MIT' spec.authors = 'MATSUMOTO Ryosuke' spec.version = '0.0.1' end mruby-2.0.0/mrbgems/mruby-sleep/src/000077500000000000000000000000001340361412400173345ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-sleep/src/mrb_sleep.c000066400000000000000000000070751340361412400214610ustar00rootroot00000000000000/* ** mrb_sleep - sleep methods for mruby ** ** Copyright (c) mod_mruby developers 2012- ** ** Permission is hereby granted, free of charge, to any person obtaining ** a copy of this software and associated documentation files (the ** "Software"), to deal in the Software without restriction, including ** without limitation the rights to use, copy, modify, merge, publish, ** distribute, sublicense, and/or sell copies of the Software, and to ** permit persons to whom the Software is furnished to do so, subject to ** the following conditions: ** ** The above copyright notice and this permission notice shall be ** included in all copies or substantial portions of the Software. ** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ** ** [ MIT license: http://www.opensource.org/licenses/mit-license.php ] */ #include #ifdef _WIN32 #include #define sleep(x) Sleep(x * 1000) #define usleep(x) Sleep((DWORD)((x)<1000) ? 1 : ((x)/1000)) #else #include #include #endif #include "mruby.h" /* not implemented forever sleep (called without an argument)*/ static mrb_value mrb_f_sleep(mrb_state *mrb, mrb_value self) { time_t beg = time(0); time_t end; #ifndef MRB_WITHOUT_FLOAT mrb_float sec; mrb_get_args(mrb, "f", &sec); if (sec >= 0) { usleep(sec * 1000000); } else { mrb_raise(mrb, E_ARGUMENT_ERROR, "time interval must be positive integer"); } #else mrb_int sec; mrb_get_args(mrb, "i", &sec); if (sec >= 0) { sleep(sec); } else { mrb_raise(mrb, E_ARGUMENT_ERROR, "time interval must be positive integer"); } #endif end = time(0) - beg; return mrb_fixnum_value(end); } /* mruby special; needed for mruby without float numbers */ static mrb_value mrb_f_usleep(mrb_state *mrb, mrb_value self) { mrb_int usec; #ifdef _WIN32 FILETIME st_ft,ed_ft; unsigned __int64 st_time = 0; unsigned __int64 ed_time = 0; #else struct timeval st_tm,ed_tm; #endif time_t slp_tm; #ifdef _WIN32 GetSystemTimeAsFileTime(&st_ft); #else gettimeofday(&st_tm, NULL); #endif /* not implemented forever sleep (called without an argument)*/ mrb_get_args(mrb, "i", &usec); if (usec >= 0) { usleep(usec); } else { mrb_raise(mrb, E_ARGUMENT_ERROR, "time interval must be positive integer"); } #ifdef _WIN32 GetSystemTimeAsFileTime(&ed_ft); st_time |= st_ft.dwHighDateTime; st_time <<=32; st_time |= st_ft.dwLowDateTime; ed_time |= ed_ft.dwHighDateTime; ed_time <<=32; ed_time |= ed_ft.dwLowDateTime; slp_tm = (ed_time - st_time) / 10; #else gettimeofday(&ed_tm, NULL); if (st_tm.tv_usec > ed_tm.tv_usec) { slp_tm = 1000000 + ed_tm.tv_usec - st_tm.tv_usec; } else { slp_tm = ed_tm.tv_usec - st_tm.tv_usec; } #endif return mrb_fixnum_value(slp_tm); } void mrb_mruby_sleep_gem_init(mrb_state *mrb) { mrb_define_method(mrb, mrb->kernel_module, "sleep", mrb_f_sleep, MRB_ARGS_REQ(1)); mrb_define_method(mrb, mrb->kernel_module, "usleep", mrb_f_usleep, MRB_ARGS_REQ(1)); } void mrb_mruby_sleep_gem_final(mrb_state *mrb) { } mruby-2.0.0/mrbgems/mruby-sleep/test/000077500000000000000000000000001340361412400175245ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-sleep/test/sleep_test.rb000066400000000000000000000011541340361412400222210ustar00rootroot00000000000000def run_with_catching_error &b e = nil begin b.call rescue => _e e = _e end return e end assert("sleep works") do e = run_with_catching_error { sleep 1 } assert_nil e end assert("sleep would not accept negative value") do e = run_with_catching_error{ sleep(-1) } assert_not_equal e, nil assert_equal e.class, ArgumentError end assert("usleep works") do e = run_with_catching_error { usleep 100 } assert_nil e end assert("usleep would not accept negative value") do e = run_with_catching_error{ usleep(-100) } assert_not_equal e, nil assert_equal e.class, ArgumentError end mruby-2.0.0/mrbgems/mruby-socket/000077500000000000000000000000001340361412400167255ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-socket/.travis.yml000066400000000000000000000001001340361412400210250ustar00rootroot00000000000000language: c sudo: false script: - "ruby run_test.rb all test" mruby-2.0.0/mrbgems/mruby-socket/README.md000066400000000000000000000034071340361412400202100ustar00rootroot00000000000000mruby-socket ============ "mruby-socket" mrbgem provides BSD socket interface for mruby. API is compatible with CRuby's "socket" library. ## Example ```sh % vi kame.rb s = TCPSocket.open("www.kame.net", 80) s.write("GET / HTTP/1.0\r\n\r\n") puts s.read s.close % mruby kame.rb HTTP/1.1 200 OK Date: Tue, 21 May 2013 04:31:30 GMT ... ``` ## Requirement - [iij/mruby-io](https://github.com/iij/mruby-io) mrbgem - [iij/mruby-mtest](https://github.com/iij/mruby-mtest) mrgbem to run tests - system must have RFC3493 basic socket interface - and some POSIX API... ## TODO - add missing methods - write more tests - fix possible descriptor leakage (see XXX comments) - `UNIXSocket#recv_io` `UNIXSocket#send_io` ## License Copyright (c) 2013 Internet Initiative Japan Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. mruby-2.0.0/mrbgems/mruby-socket/mrbgem.rake000066400000000000000000000010741340361412400210440ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-socket') do |spec| spec.license = 'MIT' spec.authors = 'Internet Initiative Japan' spec.summary = 'standard socket class' spec.cc.include_paths << "#{build.root}/src" #spec.cc.defines << "HAVE_SA_LEN=0" # If Windows, use winsock if ( /mswin|mingw|win32/ =~ RUBY_PLATFORM ) then spec.linker.libraries << "wsock32" spec.linker.libraries << "ws2_32" end spec.add_dependency('mruby-io', :core => 'mruby-io') spec.add_dependency('mruby-pack', :core => 'mruby-pack') # spec.add_dependency('mruby-mtest') end mruby-2.0.0/mrbgems/mruby-socket/mrblib/000077500000000000000000000000001340361412400201745ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-socket/mrblib/socket.rb000066400000000000000000000305501340361412400220140ustar00rootroot00000000000000class Addrinfo def initialize(sockaddr, family=Socket::PF_UNSPEC, socktype=0, protocol=0) @hostname = nil if sockaddr.is_a? Array sary = sockaddr if sary[0] == 'AF_INET' || sary[0] == 'AF_INET6' @sockaddr = Socket.sockaddr_in(sary[1], sary[3]) @hostname = sary[2] elsif sary[0] == 'AF_UNIX' @sockaddr = Socket.sockaddr_un(sary[1]) end else @sockaddr = sockaddr.dup end if family == Socket::PF_UNSPEC or family == nil @family = Socket._sockaddr_family(@sockaddr) else @family = family end @socktype = socktype @protocol = protocol @canonname = nil end def self.foreach(nodename, service, family=nil, socktype=nil, protocol=nil, flags=0, &block) a = self.getaddrinfo(nodename, service, family, socktype, protocol, flags) a.each { |ai| block.call(ai) } a end def self.ip(host) Addrinfo.new(Socket.sockaddr_in(0, host)) end def self.tcp(host, port) Addrinfo.getaddrinfo(host, port, nil, Socket::SOCK_STREAM, Socket::IPPROTO_TCP)[0] end def self.udp(host, port) Addrinfo.getaddrinfo(host, port, nil, Socket::SOCK_DGRAM, Socket::IPPROTO_UDP)[0] end def self.unix(path, socktype=Socket::SOCK_STREAM) Addrinfo.new(Socket.sockaddr_un(path), Socket::AF_UNIX, socktype) end def afamily @family end #def bind attr_reader :canonname #def connect #def connect_from #def connect_to #def family_addrinfo(host, port=nil) #def getnameinfo(flags=0) # Socket.getnameinfo #end def inspect if ipv4? or ipv6? if @protocol == Socket::IPPROTO_TCP or (@socktype == Socket::SOCK_STREAM and @protocol == 0) proto = 'TCP' elsif @protocol == Socket::IPPROTO_UDP or (@socktype == Socket::SOCK_DGRAM and @protocol == 0) proto = 'UDP' else proto = '???' end "#" else "#" end end def inspect_sockaddr if ipv4? a, p = ip_unpack "#{a}:#{p}" elsif ipv6? a, p = ip_unpack "[#{a}]:#{p}" elsif unix? unix_path else '???' end end def ip? ipv4? or ipv6? end def ip_address ip_unpack[0] end def ip_port ip_unpack[1] end def ip_unpack h, p = getnameinfo(Socket::NI_NUMERICHOST|Socket::NI_NUMERICSERV) [ h, p.to_i ] end def ipv4? @family == Socket::AF_INET end #def ipv4_loopback? #def ipv4_multicast? #def ipv4_private? def ipv6? @family == Socket::AF_INET6 end #def ipv6_loopback? #def ipv6_mc_global? #def ipv6_mc_linklocal? #def ipv6_mc_nodelocal? #def ipv6_mc_orilocal? #def ipv6_mc_sitelocal? #def ipv6_multicast? #def ipv6_to_ipv4 #def ipv6_unspecified #def ipv6_v4compat? #def ipv6_v4mapped? #def listen(backlog=5) def pfamily @family end attr_reader :protocol attr_reader :socktype def _to_array case @family when Socket::AF_INET s = "AF_INET" when Socket::AF_INET6 s = "AF_INET6" when Socket::AF_UNIX s = "AF_UNIX" else s = "(unknown AF)" end addr, port = self.getnameinfo(Socket::NI_NUMERICHOST|Socket::NI_NUMERICSERV) [ s, port.to_i, addr, addr ] end def to_sockaddr @sockaddr end alias to_s to_sockaddr def unix? @family == Socket::AF_UNIX end end class BasicSocket < IO @@do_not_reverse_lookup = true def self.do_not_reverse_lookup @@do_not_reverse_lookup end def self.do_not_reverse_lookup=(val) @@do_not_reverse_lookup = val ? true : false end def initialize(*args) super(*args) self._is_socket = true @do_not_reverse_lookup = @@do_not_reverse_lookup end def self.for_fd(fd) super(fd, "r+") end #def connect_address def local_address Addrinfo.new self.getsockname end def recv_nonblock(maxlen, flags=0) begin _setnonblock(true) recv(maxlen, flags) ensure _setnonblock(false) end end def remote_address Addrinfo.new self.getpeername end attr_accessor :do_not_reverse_lookup end class IPSocket < BasicSocket def self.getaddress(host) Addrinfo.ip(host).ip_address end def addr Addrinfo.new(self.getsockname)._to_array end def peeraddr Addrinfo.new(self.getpeername)._to_array end def recvfrom(maxlen, flags=0) msg, sa = _recvfrom(maxlen, flags) [ msg, Addrinfo.new(sa)._to_array ] end end class TCPSocket < IPSocket def initialize(host, service, local_host=nil, local_service=nil) if @init_with_fd super(host, service) else s = nil e = SocketError Addrinfo.foreach(host, service) { |ai| begin s = Socket._socket(ai.afamily, Socket::SOCK_STREAM, 0) if local_host or local_service local_host ||= (ai.afamily == Socket::AF_INET) ? "0.0.0.0" : "::" local_service ||= "0" bi = Addrinfo.getaddrinfo(local_host, local_service, ai.afamily, ai.socktype)[0] Socket._bind(s, bi.to_sockaddr) end Socket._connect(s, ai.to_sockaddr) super(s, "r+") return rescue => e0 e = e0 end } raise e end end def self.new_with_prelude pre, *args o = self._allocate o.instance_eval(&pre) o.initialize(*args) o end #def self.gethostbyname(host) end class TCPServer < TCPSocket def initialize(host=nil, service) ai = Addrinfo.getaddrinfo(host, service, nil, nil, nil, Socket::AI_PASSIVE)[0] @init_with_fd = true super(Socket._socket(ai.afamily, Socket::SOCK_STREAM, 0), "r+") if Socket.const_defined?(:SO_REUSEADDR) self.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true) end Socket._bind(self.fileno, ai.to_sockaddr) listen(5) self end def accept fd = self.sysaccept begin TCPSocket.new_with_prelude(proc { @init_with_fd = true }, fd, "r+") rescue IO._sysclose(fd) rescue nil raise end end def accept_nonblock begin self._setnonblock(true) self.accept ensure self._setnonblock(false) end end def listen(backlog) Socket._listen(self.fileno, backlog) 0 end def sysaccept Socket._accept(self.fileno) end end class UDPSocket < IPSocket def initialize(af=Socket::AF_INET) super(Socket._socket(af, Socket::SOCK_DGRAM, 0), "r+") @af = af self end def bind(host, port) Socket._bind(self.fileno, _sockaddr_in(port, host)) 0 end def connect(host, port) Socket._connect(self.fileno, _sockaddr_in(port, host)) 0 end def recvfrom_nonblock(*args) s = self begin self._setnonblock(true) self.recvfrom(*args) ensure # XXX: self is a SystemcallException here! (should be bug) s._setnonblock(false) end end def send(mesg, flags, host=nil, port=nil) if port super(mesg, flags, _sockaddr_in(port, host)) elsif host super(mesg, flags, host) else super(mesg, flags) end end def _sockaddr_in(port, host) ai = Addrinfo.getaddrinfo(host, port, @af, Socket::SOCK_DGRAM)[0] ai.to_sockaddr end end class Socket < BasicSocket def initialize(domain, type, protocol=0) super(Socket._socket(domain, type, protocol), "r+") end #def self.accept_loop def self.getaddrinfo(nodename, servname, family=nil, socktype=nil, protocol=nil, flags=0) Addrinfo.getaddrinfo(nodename, servname, family, socktype, protocol, flags).map { |ai| ary = ai._to_array ary[2] = nodename ary[4] = ai.afamily ary[5] = ai.socktype ary[6] = ai.protocol ary } end #def self.getnameinfo #def self.ip_address_list def self.open(*args) new(args) end def self.sockaddr_in(port, host) ai = Addrinfo.getaddrinfo(host, port, nil, Socket::SOCK_DGRAM)[0] ai.to_sockaddr end #def self.tcp #def self.tcp_server_loop #def self.tcp_server_sockets #def self.udp_server_loop #def self.udp_server_loop_on #def self.udp_server_recv #def self.udp_server_sockets #def self.unix(path) #def self.unix_server_loop #def self.unix_server_socket def self.unpack_sockaddr_in(sa) Addrinfo.new(sa).ip_unpack.reverse end def self.unpack_sockaddr_un(sa) Addrinfo.new(sa).unix_path end class << self alias pack_sockaddr_in sockaddr_in alias pack_sockaddr_un sockaddr_un alias pair socketpair end def accept fd, addr = self.sysaccept [ Socket.for_fd(fd), addr ] end def accept_nonblock begin self._setnonblock(true) self.accept ensure self._setnonblock(false) end end def bind(sockaddr) sockaddr = sockaddr.to_sockaddr if sockaddr.is_a? Addrinfo Socket._bind(self.fileno, sockaddr) 0 end def connect(sockaddr) sockaddr = sockaddr.to_sockaddr if sockaddr.is_a? Addrinfo Socket._connect(self.fileno, sockaddr) 0 end def connect_nonblock(sockaddr) begin self._setnonblock(true) self.connect(sockaddr) ensure self._setnonblock(false) end end #def ipv6only! def listen(backlog) Socket._listen(self.fileno, backlog) 0 end def recvfrom(maxlen, flags=0) msg, sa = _recvfrom(maxlen, flags) socktype = self.getsockopt(Socket::SOL_SOCKET, Socket::SO_TYPE).int [ msg, Addrinfo.new(sa, Socket::PF_UNSPEC, socktype) ] end def recvfrom_nonblock(*args) begin self._setnonblock(true) self._recvfrom(*args) ensure self._setnonblock(false) end end def sysaccept Socket._accept2(self.fileno) end end class UNIXSocket < BasicSocket def initialize(path, &block) if self.is_a? UNIXServer super(path, "r") else super(Socket._socket(Socket::AF_UNIX, Socket::SOCK_STREAM, 0), "r+") Socket._connect(self.fileno, Socket.sockaddr_un(path)) if block_given? begin yield self ensure begin self.close unless self.closed? rescue StandardError end end end end end def self.socketpair(type=Socket::SOCK_STREAM, protocol=0) a = Socket.socketpair(Socket::AF_UNIX, type, protocol) [ UNIXSocket.for_fd(a[0]), UNIXSocket.for_fd(a[1]) ] end class << self alias pair socketpair end def addr [ "AF_UNIX", path ] end def path Addrinfo.new(self.getsockname).unix_path end def peeraddr [ "AF_UNIX", Addrinfo.new(self.getpeername).unix_path ] end #def recv_io def recvfrom(maxlen, flags=0) msg, sa = _recvfrom(maxlen, flags) path = (sa.size > 0) ? Addrinfo.new(sa).unix_path : "" [ msg, [ "AF_UNIX", path ] ] end #def send_io end class UNIXServer < UNIXSocket def initialize(path) fd = Socket._socket(Socket::AF_UNIX, Socket::SOCK_STREAM, 0) begin super(fd) Socket._bind(fd, Socket.pack_sockaddr_un(path)) self.listen(5) rescue => e IO._sysclose(fd) rescue nil raise e end if block_given? begin yield self ensure self.close rescue nil unless self.closed? end end end def accept fd = self.sysaccept begin sock = UNIXSocket.for_fd(fd) rescue IO._sysclose(fd) rescue nil end sock end def accept_nonblock begin self._setnonblock(true) self.accept ensure self._setnonblock(false) end end def listen(backlog) Socket._listen(self.fileno, backlog) 0 end def sysaccept Socket._accept(self.fileno) end end class Socket include Constants end class Socket class Option def initialize(family, level, optname, data) @family = family @level = level @optname = optname @data = data end def self.bool(family, level, optname, bool) self.new(family, level, optname, [(bool ? 1 : 0)].pack('i')) end def self.int(family, level, optname, integer) self.new(family, level, optname, [integer].pack('i')) end #def self.linger(family, level, optname, integer) #end attr_reader :data, :family, :level, :optname def bool @data.unpack('i')[0] != 0 end def inspect "#" end def int @data.unpack('i')[0] end def linger raise NotImplementedError.new end def unpack(template) raise NotImplementedError.new end end end class SocketError < StandardError; end mruby-2.0.0/mrbgems/mruby-socket/run_test.rb000066400000000000000000000013121340361412400211120ustar00rootroot00000000000000#!/usr/bin/env ruby # # mrbgems test runner # if __FILE__ == $0 repository, dir = 'https://github.com/mruby/mruby.git', 'tmp/mruby' build_args = ARGV Dir.mkdir 'tmp' unless File.exist?('tmp') unless File.exist?(dir) system "git clone #{repository} #{dir}" end exit system(%Q[cd #{dir}; MRUBY_CONFIG=#{File.expand_path __FILE__} ruby minirake #{build_args.join(' ')}]) end MRuby::Build.new do |conf| toolchain :gcc conf.gembox 'default' conf.gem :git => 'https://github.com/iij/mruby-mtest.git' conf.gem :git => 'https://github.com/iij/mruby-io.git' conf.gem :git => 'https://github.com/iij/mruby-pack.git' conf.gem File.expand_path(File.dirname(__FILE__)) conf.enable_test end mruby-2.0.0/mrbgems/mruby-socket/src/000077500000000000000000000000001340361412400175145ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-socket/src/const.cstub000066400000000000000000000232621340361412400217110ustar00rootroot00000000000000#if defined(AF_INET) define_const(AF_INET); #endif #if defined(PF_INET) define_const(PF_INET); #endif #if defined(AF_INET6) define_const(AF_INET6); #endif #if defined(PF_INET6) define_const(PF_INET6); #endif #if defined(AF_LINK) define_const(AF_LINK); #endif #if defined(PF_LINK) define_const(PF_LINK); #endif #if defined(AF_LOCAL) define_const(AF_LOCAL); #endif #if defined(PF_LOCAL) define_const(PF_LOCAL); #endif #if defined(AF_UNIX) define_const(AF_UNIX); #endif #if defined(PF_UNIX) define_const(PF_UNIX); #endif #if defined(AF_MAX) define_const(AF_MAX); #endif #if defined(AF_UNSPEC) define_const(AF_UNSPEC); #endif #if defined(PF_UNSPEC) define_const(PF_UNSPEC); #endif #if defined(AF_ROUTE) define_const(AF_ROUTE); #endif #if defined(PF_ROUTE) define_const(PF_ROUTE); #endif #if defined(AI_CANONNAME) define_const(AI_CANONNAME); #endif #if defined(AI_FQDN) define_const(AI_FQDN); #endif #if defined(AI_NUMERICHOST) define_const(AI_NUMERICHOST); #endif #if defined(AI_NUMERICSERV) define_const(AI_NUMERICSERV); #endif #if defined(AI_PASSIVE) define_const(AI_PASSIVE); #endif #if defined(IP_ADD_MEMBERSHIP) define_const(IP_ADD_MEMBERSHIP); #endif #if defined(IP_ADD_SOURCE_MEMBERSHIP) define_const(IP_ADD_SOURCE_MEMBERSHIP); #endif #if defined(IP_BLOCK_SOURCE) define_const(IP_BLOCK_SOURCE); #endif #if defined(IP_DROP_MEMBERSHIP) define_const(IP_DROP_MEMBERSHIP); #endif #if defined(IP_DROP_SOURCE_MEMBERSHIP) define_const(IP_DROP_SOURCE_MEMBERSHIP); #endif #if defined(IP_FREEBIND) define_const(IP_FREEBIND); #endif #if defined(IP_HDRINCL) define_const(IP_HDRINCL); #endif #if defined(IP_IPSEC_POLICY) define_const(IP_IPSEC_POLICY); #endif #if defined(IP_MINTTL) define_const(IP_MINTTL); #endif #if defined(IP_MSFILTER) define_const(IP_MSFILTER); #endif #if defined(IP_MTU) define_const(IP_MTU); #endif #if defined(IP_MTU_DISCOVER) define_const(IP_MTU_DISCOVER); #endif #if defined(IP_MULTICAST_ALL) define_const(IP_MULTICAST_ALL); #endif #if defined(IP_MULTICAST_IF) define_const(IP_MULTICAST_IF); #endif #if defined(IP_MULTICAST_LOOP) define_const(IP_MULTICAST_LOOP); #endif #if defined(IP_MULTICAST_TTL) define_const(IP_MULTICAST_TTL); #endif #if defined(IP_OPTIONS) define_const(IP_OPTIONS); #endif #if defined(IP_ORIGDSTADDR) define_const(IP_ORIGDSTADDR); #endif #if defined(IP_PASSSEC) define_const(IP_PASSSEC); #endif #if defined(IP_PKTINFO) define_const(IP_PKTINFO); #endif #if defined(IP_PKTOPTIONS) define_const(IP_PKTOPTIONS); #endif #if defined(IP_PMTUDISC_DO) define_const(IP_PMTUDISC_DO); #endif #if defined(IP_PMTUDISC_DONT) define_const(IP_PMTUDISC_DONT); #endif #if defined(IP_PMTUDISC_PROBE) define_const(IP_PMTUDISC_PROBE); #endif #if defined(IP_PMTUDISC_WANT) define_const(IP_PMTUDISC_WANT); #endif #if defined(IP_RECVDSTADDR) define_const(IP_RECVDSTADDR); #endif #if defined(IP_RECVERR) define_const(IP_RECVERR); #endif #if defined(IP_RECVOPTS) define_const(IP_RECVOPTS); #endif #if defined(IP_RECVORIGDSTADDR) define_const(IP_RECVORIGDSTADDR); #endif #if defined(IP_RECVRETOPTS) define_const(IP_RECVRETOPTS); #endif #if defined(IP_RECVTOS) define_const(IP_RECVTOS); #endif #if defined(IP_RECVTTL) define_const(IP_RECVTTL); #endif #if defined(IP_RETOPTS) define_const(IP_RETOPTS); #endif #if defined(IP_ROUTER_ALERT) define_const(IP_ROUTER_ALERT); #endif #if defined(IP_TOS) define_const(IP_TOS); #endif #if defined(IP_TRANSPARENT) define_const(IP_TRANSPARENT); #endif #if defined(IP_TTL) define_const(IP_TTL); #endif #if defined(IP_UNBLOCK_SOURCE) define_const(IP_UNBLOCK_SOURCE); #endif #if defined(IP_XFRM_POLICY) define_const(IP_XFRM_POLICY); #endif #if defined(IPV6_JOIN_GROUP) define_const(IPV6_JOIN_GROUP); #endif #if defined(IPV6_LEAVE_GROUP) define_const(IPV6_LEAVE_GROUP); #endif #if defined(IPV6_MULTICAST_HOPS) define_const(IPV6_MULTICAST_HOPS); #endif #if defined(IPV6_MULTICAST_IF) define_const(IPV6_MULTICAST_IF); #endif #if defined(IPV6_MULTICAST_LOOP) define_const(IPV6_MULTICAST_LOOP); #endif #if defined(IPV6_UNICAST_HOPS) define_const(IPV6_UNICAST_HOPS); #endif #if defined(IPV6_V6ONLY) define_const(IPV6_V6ONLY); #endif #if defined(IPPROTO_AH) || defined(_WINSOCKAPI_) define_const(IPPROTO_AH); #endif #if defined(IPPROTO_DSTOPTS) || defined(_WINSOCKAPI_) define_const(IPPROTO_DSTOPTS); #endif #if defined(IPPROTO_ESP) || defined(_WINSOCKAPI_) define_const(IPPROTO_ESP); #endif #if defined(IPPROTO_FRAGMENT) || defined(_WINSOCKAPI_) define_const(IPPROTO_FRAGMENT); #endif #if defined(IPPROTO_ICMP) || defined(_WINSOCKAPI_) define_const(IPPROTO_ICMP); #endif #if defined(IPPROTO_ICMPV6) || defined(_WINSOCKAPI_) define_const(IPPROTO_ICMPV6); #endif #if defined(IPPROTO_IP) || defined(_WINSOCKAPI_) define_const(IPPROTO_IP); #endif #if defined(IPPROTO_IPV6) || defined(_WINSOCKAPI_) define_const(IPPROTO_IPV6); #endif #if defined(IPPROTO_NONE) || defined(_WINSOCKAPI_) define_const(IPPROTO_NONE); #endif #if defined(IPPROTO_RAW) || defined(_WINSOCKAPI_) define_const(IPPROTO_RAW); #endif #if defined(IPPROTO_ROUTING) || defined(_WINSOCKAPI_) define_const(IPPROTO_ROUTING); #endif #if defined(IPPROTO_TCP) || defined(_WINSOCKAPI_) define_const(IPPROTO_TCP); #endif #if defined(IPPROTO_UDP) || defined(_WINSOCKAPI_) define_const(IPPROTO_UDP); #endif #if defined(MCAST_BLOCK_SOURCE) define_const(MCAST_BLOCK_SOURCE); #endif #if defined(MCAST_JOIN_GROUP) define_const(MCAST_JOIN_GROUP); #endif #if defined(MCAST_JOIN_SOURCE_GROUP) define_const(MCAST_JOIN_SOURCE_GROUP); #endif #if defined(MCAST_LEAVE_GROUP) define_const(MCAST_LEAVE_GROUP); #endif #if defined(MCAST_LEAVE_SOURCE_GROUP) define_const(MCAST_LEAVE_SOURCE_GROUP); #endif #if defined(MCAST_MSFILTER) define_const(MCAST_MSFILTER); #endif #if defined(MCAST_UNBLOCK_SOURCE) define_const(MCAST_UNBLOCK_SOURCE); #endif #if defined(MSG_BCAST) define_const(MSG_BCAST); #endif #if defined(MSG_CTRUNC) define_const(MSG_CTRUNC); #endif #if defined(MSG_DONTROUTE) define_const(MSG_DONTROUTE); #endif #if defined(MSG_DONTWAIT) define_const(MSG_DONTWAIT); #endif #if defined(MSG_EOR) define_const(MSG_EOR); #endif #if defined(MSG_MCAST) define_const(MSG_MCAST); #endif #if defined(MSG_NOSIGNAL) define_const(MSG_NOSIGNAL); #endif #if defined(MSG_OOB) define_const(MSG_OOB); #endif #if defined(MSG_PEEK) define_const(MSG_PEEK); #endif #if defined(MSG_TRUNC) define_const(MSG_TRUNC); #endif #if defined(MSG_WAITALL) define_const(MSG_WAITALL); #endif #if defined(NI_DGRAM) define_const(NI_DGRAM); #endif #if defined(NI_MAXHOST) define_const(NI_MAXHOST); #endif #if defined(NI_MAXSERV) define_const(NI_MAXSERV); #endif #if defined(NI_NAMEREQD) define_const(NI_NAMEREQD); #endif #if defined(NI_NOFQDN) define_const(NI_NOFQDN); #endif #if defined(NI_NUMERICHOST) define_const(NI_NUMERICHOST); #endif #if defined(NI_NUMERICSERV) define_const(NI_NUMERICSERV); #endif #if defined(SHUT_RD) define_const(SHUT_RD); #endif #if defined(SHUT_WR) define_const(SHUT_WR); #endif #if defined(SHUT_RDWR) define_const(SHUT_RDWR); #endif #if defined(SO_BINDANY) define_const(SO_BINDANY); #endif #if defined(SO_BROADCAST) define_const(SO_BROADCAST); #endif #if defined(SO_DEBUG) define_const(SO_DEBUG); #endif #if defined(SO_DONTROUTE) define_const(SO_DONTROUTE); #endif #if defined(SO_ERROR) define_const(SO_ERROR); #endif #if defined(SO_KEEPALIVE) define_const(SO_KEEPALIVE); #endif #if defined(SO_LINGER) define_const(SO_LINGER); #endif #if defined(SO_NOSIGPIPE) define_const(SO_NOSIGPIPE); #endif #if defined(SO_OOBINLINE) define_const(SO_OOBINLINE); #endif #if defined(SO_PEERCRED) define_const(SO_PEERCRED); #endif #if defined(SO_RCVBUF) define_const(SO_RCVBUF); #endif #if defined(SO_RCVLOWAT) define_const(SO_RCVLOWAT); #endif #if defined(SO_RCVTIMEO) define_const(SO_RCVTIMEO); #endif #if defined(SO_REUSEADDR) define_const(SO_REUSEADDR); #endif #if defined(SO_REUSEPORT) define_const(SO_REUSEPORT); #endif #if defined(SO_RTABLE) define_const(SO_RTABLE); #endif #if defined(SO_SNDBUF) define_const(SO_SNDBUF); #endif #if defined(SO_SNDLOWAT) define_const(SO_SNDLOWAT); #endif #if defined(SO_SNDTIMEO) define_const(SO_SNDTIMEO); #endif #if defined(SO_SPLICE) define_const(SO_SPLICE); #endif #if defined(SO_TIMESTAMP) define_const(SO_TIMESTAMP); #endif #if defined(SO_TYPE) define_const(SO_TYPE); #endif #if defined(SOCK_DGRAM) define_const(SOCK_DGRAM); #endif #if defined(SOCK_RAW) define_const(SOCK_RAW); #endif #if defined(SOCK_SEQPACKET) define_const(SOCK_SEQPACKET); #endif #if defined(SOCK_STREAM) define_const(SOCK_STREAM); #endif #if defined(SOL_SOCKET) define_const(SOL_SOCKET); #endif #if defined(SOL_IP) define_const(SOL_IP); #endif #if defined(SOL_TCP) define_const(SOL_TCP); #endif #if defined(TCP_CONGCTL) define_const(TCP_CONGCTL); #endif #if defined(TCP_CONGESTION) define_const(TCP_CONGESTION); #endif #if defined(TCP_CORK) define_const(TCP_CORK); #endif #if defined(TCP_DEFER_ACCEPT) define_const(TCP_DEFER_ACCEPT); #endif #if defined(TCP_INFO) define_const(TCP_INFO); #endif #if defined(TCP_KEEPCNT) define_const(TCP_KEEPCNT); #endif #if defined(TCP_KEEPIDLE) define_const(TCP_KEEPIDLE); #endif #if defined(TCP_KEEPINIT) define_const(TCP_KEEPINIT); #endif #if defined(TCP_KEEPINTVL) define_const(TCP_KEEPINTVL); #endif #if defined(TCP_LINGER2) define_const(TCP_LINGER2); #endif #if defined(TCP_MAXSEG) define_const(TCP_MAXSEG); #endif #if defined(TCP_MD5SIG) define_const(TCP_MD5SIG); #endif #if defined(TCP_NODELAY) define_const(TCP_NODELAY); #endif #if defined(TCP_QUICKACK) define_const(TCP_QUICKACK); #endif #if defined(TCP_SACK_ENABLE) define_const(TCP_SACK_ENABLE); #endif #if defined(TCP_SYNCNT) define_const(TCP_SYNCNT); #endif #if defined(TCP_WINDOW_CLAMP) define_const(TCP_WINDOW_CLAMP); #endif mruby-2.0.0/mrbgems/mruby-socket/src/const.def000066400000000000000000000036641340361412400213330ustar00rootroot00000000000000AF_INET PF_INET AF_INET6 PF_INET6 AF_LINK PF_LINK AF_LOCAL PF_LOCAL AF_UNIX PF_UNIX AF_MAX AF_UNSPEC PF_UNSPEC AF_ROUTE PF_ROUTE AI_CANONNAME AI_FQDN AI_NUMERICHOST AI_NUMERICSERV AI_PASSIVE IP_ADD_MEMBERSHIP IP_ADD_SOURCE_MEMBERSHIP IP_BLOCK_SOURCE IP_DROP_MEMBERSHIP IP_DROP_SOURCE_MEMBERSHIP IP_FREEBIND IP_HDRINCL IP_IPSEC_POLICY IP_MINTTL IP_MSFILTER IP_MTU IP_MTU_DISCOVER IP_MULTICAST_ALL IP_MULTICAST_IF IP_MULTICAST_LOOP IP_MULTICAST_TTL IP_OPTIONS IP_ORIGDSTADDR IP_PASSSEC IP_PKTINFO IP_PKTOPTIONS IP_PMTUDISC_DO IP_PMTUDISC_DONT IP_PMTUDISC_PROBE IP_PMTUDISC_WANT IP_RECVDSTADDR IP_RECVERR IP_RECVOPTS IP_RECVORIGDSTADDR IP_RECVRETOPTS IP_RECVTOS IP_RECVTTL IP_RETOPTS IP_ROUTER_ALERT IP_TOS IP_TRANSPARENT IP_TTL IP_UNBLOCK_SOURCE IP_XFRM_POLICY IPV6_JOIN_GROUP IPV6_LEAVE_GROUP IPV6_MULTICAST_HOPS IPV6_MULTICAST_IF IPV6_MULTICAST_LOOP IPV6_UNICAST_HOPS IPV6_V6ONLY IPPROTO_AH IPPROTO_DSTOPTS IPPROTO_ESP IPPROTO_FRAGMENT IPPROTO_ICMP IPPROTO_ICMPV6 IPPROTO_IP IPPROTO_IPV6 IPPROTO_NONE IPPROTO_RAW IPPROTO_ROUTING IPPROTO_TCP IPPROTO_UDP MCAST_BLOCK_SOURCE MCAST_JOIN_GROUP MCAST_JOIN_SOURCE_GROUP MCAST_LEAVE_GROUP MCAST_LEAVE_SOURCE_GROUP MCAST_MSFILTER MCAST_UNBLOCK_SOURCE MSG_BCAST MSG_CTRUNC MSG_DONTROUTE MSG_DONTWAIT MSG_EOR MSG_MCAST MSG_NOSIGNAL MSG_OOB MSG_PEEK MSG_TRUNC MSG_WAITALL NI_DGRAM NI_MAXHOST NI_MAXSERV NI_NAMEREQD NI_NOFQDN NI_NUMERICHOST NI_NUMERICSERV SHUT_RD SHUT_WR SHUT_RDWR SO_BINDANY SO_BROADCAST SO_DEBUG SO_DONTROUTE SO_ERROR SO_KEEPALIVE SO_LINGER SO_NOSIGPIPE SO_OOBINLINE SO_PEERCRED SO_RCVBUF SO_RCVLOWAT SO_RCVTIMEO SO_REUSEADDR SO_REUSEPORT SO_RTABLE SO_SNDBUF SO_SNDLOWAT SO_SNDTIMEO SO_SPLICE SO_TIMESTAMP SO_TYPE SOCK_DGRAM SOCK_RAW SOCK_SEQPACKET SOCK_STREAM SOL_SOCKET SOL_IP SOL_TCP TCP_CONGCTL TCP_CONGESTION TCP_CORK TCP_DEFER_ACCEPT TCP_INFO TCP_KEEPCNT TCP_KEEPIDLE TCP_KEEPINIT TCP_KEEPINTVL TCP_LINGER2 TCP_MAXSEG TCP_MD5SIG TCP_NODELAY TCP_QUICKACK TCP_SACK_ENABLE TCP_SYNCNT TCP_WINDOW_CLAMP mruby-2.0.0/mrbgems/mruby-socket/src/gen.rb000077500000000000000000000005101340361412400206110ustar00rootroot00000000000000#!/usr/bin/env ruby Dir.chdir(File.dirname($0)) f = File.open("const.cstub", "w") IO.readlines("const.def").each { |name| name.sub(/^#.*/, "") name.strip! next if name.empty? f.write < #include #include #include #define SHUT_RDWR SD_BOTH #ifndef _SSIZE_T_DEFINED typedef int ssize_t; #endif typedef int fsize_t; #else #include #include #include #include #include #include #include #include #include #include typedef size_t fsize_t; #endif #include #include #include "mruby.h" #include "mruby/array.h" #include "mruby/class.h" #include "mruby/data.h" #include "mruby/string.h" #include "mruby/variable.h" #include "error.h" #include "mruby/ext/io.h" #if !defined(HAVE_SA_LEN) #if (defined(BSD) && (BSD >= 199006)) #define HAVE_SA_LEN 1 #else #define HAVE_SA_LEN 0 #endif #endif #define E_SOCKET_ERROR (mrb_class_get(mrb, "SocketError")) #if !defined(mrb_cptr) #define mrb_cptr_value(m,p) mrb_voidp_value((m),(p)) #define mrb_cptr(o) mrb_voidp(o) #define mrb_cptr_p(o) mrb_voidp_p(o) #endif #ifdef _WIN32 static const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt) { if (af == AF_INET) { struct sockaddr_in in; memset(&in, 0, sizeof(in)); in.sin_family = AF_INET; memcpy(&in.sin_addr, src, sizeof(struct in_addr)); getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in), dst, cnt, NULL, 0, NI_NUMERICHOST); return dst; } else if (af == AF_INET6) { struct sockaddr_in6 in; memset(&in, 0, sizeof(in)); in.sin6_family = AF_INET6; memcpy(&in.sin6_addr, src, sizeof(struct in_addr6)); getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in6), dst, cnt, NULL, 0, NI_NUMERICHOST); return dst; } return NULL; } static int inet_pton(int af, const char *src, void *dst) { struct addrinfo hints, *res, *ressave; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = af; if (getaddrinfo(src, NULL, &hints, &res) != 0) { printf("Couldn't resolve host %s\n", src); return -1; } ressave = res; while (res) { memcpy(dst, res->ai_addr, res->ai_addrlen); res = res->ai_next; } freeaddrinfo(ressave); return 0; } #endif static mrb_value mrb_addrinfo_getaddrinfo(mrb_state *mrb, mrb_value klass) { struct addrinfo hints, *res0, *res; mrb_value ai, ary, family, lastai, nodename, protocol, sa, service, socktype; mrb_int flags; int arena_idx, error; const char *hostname = NULL, *servname = NULL; ary = mrb_ary_new(mrb); arena_idx = mrb_gc_arena_save(mrb); /* ary must be on arena! */ family = socktype = protocol = mrb_nil_value(); flags = 0; mrb_get_args(mrb, "oo|oooi", &nodename, &service, &family, &socktype, &protocol, &flags); if (mrb_string_p(nodename)) { hostname = mrb_str_to_cstr(mrb, nodename); } else if (mrb_nil_p(nodename)) { hostname = NULL; } else { mrb_raise(mrb, E_TYPE_ERROR, "nodename must be String or nil"); } if (mrb_string_p(service)) { servname = mrb_str_to_cstr(mrb, service); } else if (mrb_fixnum_p(service)) { servname = mrb_str_to_cstr(mrb, mrb_funcall(mrb, service, "to_s", 0)); } else if (mrb_nil_p(service)) { servname = NULL; } else { mrb_raise(mrb, E_TYPE_ERROR, "service must be String, Fixnum, or nil"); } memset(&hints, 0, sizeof(hints)); hints.ai_flags = (int)flags; if (mrb_fixnum_p(family)) { hints.ai_family = (int)mrb_fixnum(family); } if (mrb_fixnum_p(socktype)) { hints.ai_socktype = (int)mrb_fixnum(socktype); } if (mrb_fixnum_p(protocol)) { hints.ai_protocol = (int)mrb_fixnum(protocol); } lastai = mrb_cv_get(mrb, klass, mrb_intern_lit(mrb, "_lastai")); if (mrb_cptr_p(lastai)) { freeaddrinfo((struct addrinfo*)mrb_cptr(lastai)); mrb_cv_set(mrb, klass, mrb_intern_lit(mrb, "_lastai"), mrb_nil_value()); } error = getaddrinfo(hostname, servname, &hints, &res0); if (error) { mrb_raisef(mrb, E_SOCKET_ERROR, "getaddrinfo: %S", mrb_str_new_cstr(mrb, gai_strerror(error))); } mrb_cv_set(mrb, klass, mrb_intern_lit(mrb, "_lastai"), mrb_cptr_value(mrb, res0)); for (res = res0; res != NULL; res = res->ai_next) { sa = mrb_str_new(mrb, (char*)res->ai_addr, res->ai_addrlen); ai = mrb_funcall(mrb, klass, "new", 4, sa, mrb_fixnum_value(res->ai_family), mrb_fixnum_value(res->ai_socktype), mrb_fixnum_value(res->ai_protocol)); mrb_ary_push(mrb, ary, ai); mrb_gc_arena_restore(mrb, arena_idx); } freeaddrinfo(res0); mrb_cv_set(mrb, klass, mrb_intern_lit(mrb, "_lastai"), mrb_nil_value()); return ary; } static mrb_value mrb_addrinfo_getnameinfo(mrb_state *mrb, mrb_value self) { mrb_int flags; mrb_value ary, host, sastr, serv; int error; flags = 0; mrb_get_args(mrb, "|i", &flags); host = mrb_str_buf_new(mrb, NI_MAXHOST); serv = mrb_str_buf_new(mrb, NI_MAXSERV); sastr = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@sockaddr")); if (!mrb_string_p(sastr)) { mrb_raise(mrb, E_SOCKET_ERROR, "invalid sockaddr"); } error = getnameinfo((struct sockaddr *)RSTRING_PTR(sastr), (socklen_t)RSTRING_LEN(sastr), RSTRING_PTR(host), NI_MAXHOST, RSTRING_PTR(serv), NI_MAXSERV, (int)flags); if (error) { mrb_raisef(mrb, E_SOCKET_ERROR, "getnameinfo: %S", mrb_str_new_cstr(mrb, gai_strerror(error))); } ary = mrb_ary_new_capa(mrb, 2); mrb_str_resize(mrb, host, strlen(RSTRING_PTR(host))); mrb_ary_push(mrb, ary, host); mrb_str_resize(mrb, serv, strlen(RSTRING_PTR(serv))); mrb_ary_push(mrb, ary, serv); return ary; } #ifndef _WIN32 static mrb_value mrb_addrinfo_unix_path(mrb_state *mrb, mrb_value self) { mrb_value sastr; sastr = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@sockaddr")); if (((struct sockaddr *)RSTRING_PTR(sastr))->sa_family != AF_UNIX) mrb_raise(mrb, E_SOCKET_ERROR, "need AF_UNIX address"); if (RSTRING_LEN(sastr) < (mrb_int)offsetof(struct sockaddr_un, sun_path) + 1) { return mrb_str_new(mrb, "", 0); } else { return mrb_str_new_cstr(mrb, ((struct sockaddr_un *)RSTRING_PTR(sastr))->sun_path); } } #endif static mrb_value sa2addrlist(mrb_state *mrb, const struct sockaddr *sa, socklen_t salen) { mrb_value ary, host; unsigned short port; const char *afstr; switch (sa->sa_family) { case AF_INET: afstr = "AF_INET"; port = ((struct sockaddr_in *)sa)->sin_port; break; case AF_INET6: afstr = "AF_INET6"; port = ((struct sockaddr_in6 *)sa)->sin6_port; break; default: mrb_raise(mrb, E_ARGUMENT_ERROR, "bad af"); return mrb_nil_value(); } port = ntohs(port); host = mrb_str_buf_new(mrb, NI_MAXHOST); if (getnameinfo(sa, salen, RSTRING_PTR(host), NI_MAXHOST, NULL, 0, NI_NUMERICHOST) == -1) mrb_sys_fail(mrb, "getnameinfo"); mrb_str_resize(mrb, host, strlen(RSTRING_PTR(host))); ary = mrb_ary_new_capa(mrb, 4); mrb_ary_push(mrb, ary, mrb_str_new_cstr(mrb, afstr)); mrb_ary_push(mrb, ary, mrb_fixnum_value(port)); mrb_ary_push(mrb, ary, host); mrb_ary_push(mrb, ary, host); return ary; } static int socket_fd(mrb_state *mrb, mrb_value sock) { return (int)mrb_fixnum(mrb_funcall(mrb, sock, "fileno", 0)); } static int socket_family(int s) { struct sockaddr_storage ss; socklen_t salen; salen = sizeof(ss); if (getsockname(s, (struct sockaddr *)&ss, &salen) == -1) return AF_UNSPEC; return ss.ss_family; } static mrb_value mrb_basicsocket_getpeereid(mrb_state *mrb, mrb_value self) { #ifdef HAVE_GETPEEREID mrb_value ary; gid_t egid; uid_t euid; int s; s = socket_fd(mrb, self); if (getpeereid(s, &euid, &egid) != 0) mrb_sys_fail(mrb, "getpeereid"); ary = mrb_ary_new_capa(mrb, 2); mrb_ary_push(mrb, ary, mrb_fixnum_value((mrb_int)euid)); mrb_ary_push(mrb, ary, mrb_fixnum_value((mrb_int)egid)); return ary; #else mrb_raise(mrb, E_RUNTIME_ERROR, "getpeereid is not available on this system"); return mrb_nil_value(); #endif } static mrb_value mrb_basicsocket_getpeername(mrb_state *mrb, mrb_value self) { struct sockaddr_storage ss; socklen_t salen; salen = sizeof(ss); if (getpeername(socket_fd(mrb, self), (struct sockaddr *)&ss, &salen) != 0) mrb_sys_fail(mrb, "getpeername"); return mrb_str_new(mrb, (char*)&ss, salen); } static mrb_value mrb_basicsocket_getsockname(mrb_state *mrb, mrb_value self) { struct sockaddr_storage ss; socklen_t salen; salen = sizeof(ss); if (getsockname(socket_fd(mrb, self), (struct sockaddr *)&ss, &salen) != 0) mrb_sys_fail(mrb, "getsockname"); return mrb_str_new(mrb, (char*)&ss, salen); } static mrb_value mrb_basicsocket_getsockopt(mrb_state *mrb, mrb_value self) { char opt[8]; int s; mrb_int family, level, optname; mrb_value c, data; socklen_t optlen; mrb_get_args(mrb, "ii", &level, &optname); s = socket_fd(mrb, self); optlen = sizeof(opt); if (getsockopt(s, (int)level, (int)optname, opt, &optlen) == -1) mrb_sys_fail(mrb, "getsockopt"); c = mrb_const_get(mrb, mrb_obj_value(mrb_class_get(mrb, "Socket")), mrb_intern_lit(mrb, "Option")); family = socket_family(s); data = mrb_str_new(mrb, opt, optlen); return mrb_funcall(mrb, c, "new", 4, mrb_fixnum_value(family), mrb_fixnum_value(level), mrb_fixnum_value(optname), data); } static mrb_value mrb_basicsocket_recv(mrb_state *mrb, mrb_value self) { ssize_t n; mrb_int maxlen, flags = 0; mrb_value buf; mrb_get_args(mrb, "i|i", &maxlen, &flags); buf = mrb_str_buf_new(mrb, maxlen); n = recv(socket_fd(mrb, self), RSTRING_PTR(buf), (fsize_t)maxlen, (int)flags); if (n == -1) mrb_sys_fail(mrb, "recv"); mrb_str_resize(mrb, buf, (mrb_int)n); return buf; } static mrb_value mrb_basicsocket_recvfrom(mrb_state *mrb, mrb_value self) { ssize_t n; mrb_int maxlen, flags = 0; mrb_value ary, buf, sa; socklen_t socklen; mrb_get_args(mrb, "i|i", &maxlen, &flags); buf = mrb_str_buf_new(mrb, maxlen); socklen = sizeof(struct sockaddr_storage); sa = mrb_str_buf_new(mrb, socklen); n = recvfrom(socket_fd(mrb, self), RSTRING_PTR(buf), (fsize_t)maxlen, (int)flags, (struct sockaddr *)RSTRING_PTR(sa), &socklen); if (n == -1) mrb_sys_fail(mrb, "recvfrom"); mrb_str_resize(mrb, buf, (mrb_int)n); mrb_str_resize(mrb, sa, (mrb_int)socklen); ary = mrb_ary_new_capa(mrb, 2); mrb_ary_push(mrb, ary, buf); mrb_ary_push(mrb, ary, sa); return ary; } static mrb_value mrb_basicsocket_send(mrb_state *mrb, mrb_value self) { ssize_t n; mrb_int flags; mrb_value dest, mesg; dest = mrb_nil_value(); mrb_get_args(mrb, "Si|S", &mesg, &flags, &dest); if (mrb_nil_p(dest)) { n = send(socket_fd(mrb, self), RSTRING_PTR(mesg), (fsize_t)RSTRING_LEN(mesg), (int)flags); } else { n = sendto(socket_fd(mrb, self), RSTRING_PTR(mesg), (fsize_t)RSTRING_LEN(mesg), (int)flags, (const struct sockaddr*)RSTRING_PTR(dest), (fsize_t)RSTRING_LEN(dest)); } if (n == -1) mrb_sys_fail(mrb, "send"); return mrb_fixnum_value((mrb_int)n); } static mrb_value mrb_basicsocket_setnonblock(mrb_state *mrb, mrb_value self) { int fd, flags; mrb_bool nonblocking; #ifdef _WIN32 u_long mode = 1; #endif mrb_get_args(mrb, "b", &nonblocking); fd = socket_fd(mrb, self); #ifdef _WIN32 flags = ioctlsocket(fd, FIONBIO, &mode); if (flags != NO_ERROR) mrb_sys_fail(mrb, "ioctlsocket"); #else flags = fcntl(fd, F_GETFL, 0); if (flags == 1) mrb_sys_fail(mrb, "fcntl"); if (nonblocking) flags |= O_NONBLOCK; else flags &= ~O_NONBLOCK; if (fcntl(fd, F_SETFL, flags) == -1) mrb_sys_fail(mrb, "fcntl"); #endif return mrb_nil_value(); } static mrb_value mrb_basicsocket_setsockopt(mrb_state *mrb, mrb_value self) { int s; mrb_int argc, level = 0, optname; mrb_value optval, so; argc = mrb_get_args(mrb, "o|io", &so, &optname, &optval); if (argc == 3) { if (!mrb_fixnum_p(so)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "level is not an integer"); } level = mrb_fixnum(so); if (mrb_string_p(optval)) { /* that's good */ } else if (mrb_type(optval) == MRB_TT_TRUE || mrb_type(optval) == MRB_TT_FALSE) { mrb_int i = mrb_test(optval) ? 1 : 0; optval = mrb_str_new(mrb, (char*)&i, sizeof(i)); } else if (mrb_fixnum_p(optval)) { if (optname == IP_MULTICAST_TTL || optname == IP_MULTICAST_LOOP) { char uc = (char)mrb_fixnum(optval); optval = mrb_str_new(mrb, &uc, sizeof(uc)); } else { mrb_int i = mrb_fixnum(optval); optval = mrb_str_new(mrb, (char*)&i, sizeof(i)); } } else { mrb_raise(mrb, E_ARGUMENT_ERROR, "optval should be true, false, an integer, or a string"); } } else if (argc == 1) { if (strcmp(mrb_obj_classname(mrb, so), "Socket::Option") != 0) mrb_raise(mrb, E_ARGUMENT_ERROR, "not an instance of Socket::Option"); level = mrb_fixnum(mrb_funcall(mrb, so, "level", 0)); optname = mrb_fixnum(mrb_funcall(mrb, so, "optname", 0)); optval = mrb_funcall(mrb, so, "data", 0); } else { mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%S for 3)", mrb_fixnum_value(argc)); } s = socket_fd(mrb, self); if (setsockopt(s, (int)level, (int)optname, RSTRING_PTR(optval), (socklen_t)RSTRING_LEN(optval)) == -1) mrb_sys_fail(mrb, "setsockopt"); return mrb_fixnum_value(0); } static mrb_value mrb_basicsocket_shutdown(mrb_state *mrb, mrb_value self) { mrb_int how = SHUT_RDWR; mrb_get_args(mrb, "|i", &how); if (shutdown(socket_fd(mrb, self), (int)how) != 0) mrb_sys_fail(mrb, "shutdown"); return mrb_fixnum_value(0); } static mrb_value mrb_basicsocket_set_is_socket(mrb_state *mrb, mrb_value self) { mrb_bool b; struct mrb_io *io_p; mrb_get_args(mrb, "b", &b); io_p = (struct mrb_io*)DATA_PTR(self); if (io_p) { io_p->is_socket = b; } return mrb_bool_value(b); } static mrb_value mrb_ipsocket_ntop(mrb_state *mrb, mrb_value klass) { mrb_int af, n; char *addr, buf[50]; mrb_get_args(mrb, "is", &af, &addr, &n); if ((af == AF_INET && n != 4) || (af == AF_INET6 && n != 16)) mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid address"); if (inet_ntop((int)af, addr, buf, sizeof(buf)) == NULL) mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid address"); return mrb_str_new_cstr(mrb, buf); } static mrb_value mrb_ipsocket_pton(mrb_state *mrb, mrb_value klass) { mrb_int af, n; char *bp, buf[50]; mrb_get_args(mrb, "is", &af, &bp, &n); if ((size_t)n > sizeof(buf) - 1) mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid address"); memcpy(buf, bp, n); buf[n] = '\0'; if (af == AF_INET) { struct in_addr in; if (inet_pton(AF_INET, buf, (void *)&in.s_addr) != 1) goto invalid; return mrb_str_new(mrb, (char*)&in.s_addr, 4); } else if (af == AF_INET6) { struct in6_addr in6; if (inet_pton(AF_INET6, buf, (void *)&in6.s6_addr) != 1) goto invalid; return mrb_str_new(mrb, (char*)&in6.s6_addr, 16); } else mrb_raise(mrb, E_ARGUMENT_ERROR, "unsupported address family"); invalid: mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid address"); return mrb_nil_value(); /* dummy */ } static mrb_value mrb_ipsocket_recvfrom(mrb_state *mrb, mrb_value self) { struct sockaddr_storage ss; socklen_t socklen; mrb_value a, buf, pair; mrb_int flags, maxlen; ssize_t n; int fd; fd = socket_fd(mrb, self); flags = 0; mrb_get_args(mrb, "i|i", &maxlen, &flags); buf = mrb_str_buf_new(mrb, maxlen); socklen = sizeof(ss); n = recvfrom(fd, RSTRING_PTR(buf), (fsize_t)maxlen, (int)flags, (struct sockaddr *)&ss, &socklen); if (n == -1) { mrb_sys_fail(mrb, "recvfrom"); } mrb_str_resize(mrb, buf, (mrb_int)n); a = sa2addrlist(mrb, (struct sockaddr *)&ss, socklen); pair = mrb_ary_new_capa(mrb, 2); mrb_ary_push(mrb, pair, buf); mrb_ary_push(mrb, pair, a); return pair; } static mrb_value mrb_socket_gethostname(mrb_state *mrb, mrb_value cls) { mrb_value buf; size_t bufsize; #ifdef HOST_NAME_MAX bufsize = HOST_NAME_MAX + 1; #else bufsize = 256; #endif buf = mrb_str_buf_new(mrb, (mrb_int)bufsize); if (gethostname(RSTRING_PTR(buf), (fsize_t)bufsize) != 0) mrb_sys_fail(mrb, "gethostname"); mrb_str_resize(mrb, buf, (mrb_int)strlen(RSTRING_PTR(buf))); return buf; } static mrb_value mrb_socket_accept(mrb_state *mrb, mrb_value klass) { int s1; mrb_int s0; mrb_get_args(mrb, "i", &s0); s1 = (int)accept(s0, NULL, NULL); if (s1 == -1) { mrb_sys_fail(mrb, "accept"); } return mrb_fixnum_value(s1); } static mrb_value mrb_socket_accept2(mrb_state *mrb, mrb_value klass) { mrb_value ary, sastr; int s1; mrb_int s0; socklen_t socklen; mrb_get_args(mrb, "i", &s0); socklen = sizeof(struct sockaddr_storage); sastr = mrb_str_buf_new(mrb, socklen); s1 = (int)accept(s0, (struct sockaddr *)RSTRING_PTR(sastr), &socklen); if (s1 == -1) { mrb_sys_fail(mrb, "accept"); } // XXX: possible descriptor leakage here! mrb_str_resize(mrb, sastr, socklen); ary = mrb_ary_new_capa(mrb, 2); mrb_ary_push(mrb, ary, mrb_fixnum_value(s1)); mrb_ary_push(mrb, ary, sastr); return ary; } static mrb_value mrb_socket_bind(mrb_state *mrb, mrb_value klass) { mrb_value sastr; mrb_int s; mrb_get_args(mrb, "iS", &s, &sastr); if (bind((int)s, (struct sockaddr *)RSTRING_PTR(sastr), (socklen_t)RSTRING_LEN(sastr)) == -1) { mrb_sys_fail(mrb, "bind"); } return mrb_nil_value(); } static mrb_value mrb_socket_connect(mrb_state *mrb, mrb_value klass) { mrb_value sastr; mrb_int s; mrb_get_args(mrb, "iS", &s, &sastr); if (connect((int)s, (struct sockaddr *)RSTRING_PTR(sastr), (socklen_t)RSTRING_LEN(sastr)) == -1) { mrb_sys_fail(mrb, "connect"); } return mrb_nil_value(); } static mrb_value mrb_socket_listen(mrb_state *mrb, mrb_value klass) { mrb_int backlog, s; mrb_get_args(mrb, "ii", &s, &backlog); if (listen((int)s, (int)backlog) == -1) { mrb_sys_fail(mrb, "listen"); } return mrb_nil_value(); } static mrb_value mrb_socket_sockaddr_family(mrb_state *mrb, mrb_value klass) { const struct sockaddr *sa; mrb_value str; mrb_get_args(mrb, "S", &str); if ((size_t)RSTRING_LEN(str) < offsetof(struct sockaddr, sa_family) + sizeof(sa->sa_family)) { mrb_raise(mrb, E_SOCKET_ERROR, "invalid sockaddr (too short)"); } sa = (const struct sockaddr *)RSTRING_PTR(str); return mrb_fixnum_value(sa->sa_family); } static mrb_value mrb_socket_sockaddr_un(mrb_state *mrb, mrb_value klass) { #ifdef _WIN32 mrb_raise(mrb, E_NOTIMP_ERROR, "sockaddr_un unsupported on Windows"); return mrb_nil_value(); #else struct sockaddr_un *sunp; mrb_value path, s; mrb_get_args(mrb, "S", &path); if ((size_t)RSTRING_LEN(path) > sizeof(sunp->sun_path) - 1) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "too long unix socket path (max: %S bytes)", mrb_fixnum_value(sizeof(sunp->sun_path) - 1)); } s = mrb_str_buf_new(mrb, sizeof(struct sockaddr_un)); sunp = (struct sockaddr_un *)RSTRING_PTR(s); #if HAVE_SA_LEN sunp->sun_len = sizeof(struct sockaddr_un); #endif sunp->sun_family = AF_UNIX; memcpy(sunp->sun_path, RSTRING_PTR(path), RSTRING_LEN(path)); sunp->sun_path[RSTRING_LEN(path)] = '\0'; mrb_str_resize(mrb, s, sizeof(struct sockaddr_un)); return s; #endif } static mrb_value mrb_socket_socketpair(mrb_state *mrb, mrb_value klass) { #ifdef _WIN32 mrb_raise(mrb, E_NOTIMP_ERROR, "socketpair unsupported on Windows"); return mrb_nil_value(); #else mrb_value ary; mrb_int domain, type, protocol; int sv[2]; mrb_get_args(mrb, "iii", &domain, &type, &protocol); if (socketpair(domain, type, protocol, sv) == -1) { mrb_sys_fail(mrb, "socketpair"); } // XXX: possible descriptor leakage here! ary = mrb_ary_new_capa(mrb, 2); mrb_ary_push(mrb, ary, mrb_fixnum_value(sv[0])); mrb_ary_push(mrb, ary, mrb_fixnum_value(sv[1])); return ary; #endif } static mrb_value mrb_socket_socket(mrb_state *mrb, mrb_value klass) { mrb_int domain, type, protocol; int s; mrb_get_args(mrb, "iii", &domain, &type, &protocol); s = (int)socket((int)domain, (int)type, (int)protocol); if (s == -1) mrb_sys_fail(mrb, "socket"); return mrb_fixnum_value(s); } static mrb_value mrb_tcpsocket_allocate(mrb_state *mrb, mrb_value klass) { struct RClass *c = mrb_class_ptr(klass); enum mrb_vtype ttype = MRB_INSTANCE_TT(c); /* copied from mrb_instance_alloc() */ if (ttype == 0) ttype = MRB_TT_OBJECT; return mrb_obj_value((struct RObject*)mrb_obj_alloc(mrb, ttype, c)); } /* Windows overrides for IO methods on BasicSocket objects. * This is because sockets on Windows are not the same as file * descriptors, and thus functions which operate on file descriptors * will break on socket descriptors. */ #ifdef _WIN32 static mrb_value mrb_win32_basicsocket_close(mrb_state *mrb, mrb_value self) { if (closesocket(socket_fd(mrb, self)) != NO_ERROR) mrb_raise(mrb, E_SOCKET_ERROR, "closesocket unsuccessful"); return mrb_nil_value(); } #define E_EOF_ERROR (mrb_class_get(mrb, "EOFError")) static mrb_value mrb_win32_basicsocket_sysread(mrb_state *mrb, mrb_value self) { int sd, ret; mrb_value buf = mrb_nil_value(); mrb_int maxlen; mrb_get_args(mrb, "i|S", &maxlen, &buf); if (maxlen < 0) { return mrb_nil_value(); } if (mrb_nil_p(buf)) { buf = mrb_str_new(mrb, NULL, maxlen); } if (RSTRING_LEN(buf) != maxlen) { buf = mrb_str_resize(mrb, buf, maxlen); } sd = socket_fd(mrb, self); ret = recv(sd, RSTRING_PTR(buf), (int)maxlen, 0); switch (ret) { case 0: /* EOF */ if (maxlen == 0) { buf = mrb_str_new_cstr(mrb, ""); } else { mrb_raise(mrb, E_EOF_ERROR, "sysread failed: End of File"); } break; case SOCKET_ERROR: /* Error */ mrb_sys_fail(mrb, "recv"); break; default: if (RSTRING_LEN(buf) != ret) { buf = mrb_str_resize(mrb, buf, ret); } break; } return buf; } static mrb_value mrb_win32_basicsocket_sysseek(mrb_state *mrb, mrb_value self) { mrb_raise(mrb, E_NOTIMP_ERROR, "sysseek not implemented for windows sockets"); return mrb_nil_value(); } static mrb_value mrb_win32_basicsocket_syswrite(mrb_state *mrb, mrb_value self) { int n; SOCKET sd; mrb_value str; sd = socket_fd(mrb, self); mrb_get_args(mrb, "S", &str); n = send(sd, RSTRING_PTR(str), (int)RSTRING_LEN(str), 0); if (n == SOCKET_ERROR) mrb_sys_fail(mrb, "send"); return mrb_fixnum_value(n); } #endif void mrb_mruby_socket_gem_init(mrb_state* mrb) { struct RClass *io, *ai, *sock, *bsock, *ipsock, *tcpsock; struct RClass *constants; #ifdef _WIN32 WSADATA wsaData; int result; result = WSAStartup(MAKEWORD(2,2), &wsaData); if (result != NO_ERROR) mrb_raise(mrb, E_RUNTIME_ERROR, "WSAStartup failed"); #endif ai = mrb_define_class(mrb, "Addrinfo", mrb->object_class); mrb_mod_cv_set(mrb, ai, mrb_intern_lit(mrb, "_lastai"), mrb_nil_value()); mrb_define_class_method(mrb, ai, "getaddrinfo", mrb_addrinfo_getaddrinfo, MRB_ARGS_REQ(2)|MRB_ARGS_OPT(4)); mrb_define_method(mrb, ai, "getnameinfo", mrb_addrinfo_getnameinfo, MRB_ARGS_OPT(1)); #ifndef _WIN32 mrb_define_method(mrb, ai, "unix_path", mrb_addrinfo_unix_path, MRB_ARGS_NONE()); #endif io = mrb_class_get(mrb, "IO"); bsock = mrb_define_class(mrb, "BasicSocket", io); mrb_define_method(mrb, bsock, "_recvfrom", mrb_basicsocket_recvfrom, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1)); mrb_define_method(mrb, bsock, "_setnonblock", mrb_basicsocket_setnonblock, MRB_ARGS_REQ(1)); mrb_define_method(mrb, bsock, "getpeereid", mrb_basicsocket_getpeereid, MRB_ARGS_NONE()); mrb_define_method(mrb, bsock, "getpeername", mrb_basicsocket_getpeername, MRB_ARGS_NONE()); mrb_define_method(mrb, bsock, "getsockname", mrb_basicsocket_getsockname, MRB_ARGS_NONE()); mrb_define_method(mrb, bsock, "getsockopt", mrb_basicsocket_getsockopt, MRB_ARGS_REQ(2)); mrb_define_method(mrb, bsock, "recv", mrb_basicsocket_recv, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1)); // #recvmsg(maxlen, flags=0) mrb_define_method(mrb, bsock, "send", mrb_basicsocket_send, MRB_ARGS_REQ(2)|MRB_ARGS_OPT(1)); // #sendmsg // #sendmsg_nonblock mrb_define_method(mrb, bsock, "setsockopt", mrb_basicsocket_setsockopt, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(2)); mrb_define_method(mrb, bsock, "shutdown", mrb_basicsocket_shutdown, MRB_ARGS_OPT(1)); mrb_define_method(mrb, bsock, "_is_socket=", mrb_basicsocket_set_is_socket, MRB_ARGS_REQ(1)); ipsock = mrb_define_class(mrb, "IPSocket", bsock); mrb_define_class_method(mrb, ipsock, "ntop", mrb_ipsocket_ntop, MRB_ARGS_REQ(1)); mrb_define_class_method(mrb, ipsock, "pton", mrb_ipsocket_pton, MRB_ARGS_REQ(2)); mrb_define_method(mrb, ipsock, "recvfrom", mrb_ipsocket_recvfrom, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1)); tcpsock = mrb_define_class(mrb, "TCPSocket", ipsock); mrb_define_class_method(mrb, tcpsock, "_allocate", mrb_tcpsocket_allocate, MRB_ARGS_NONE()); mrb_define_class(mrb, "TCPServer", tcpsock); mrb_define_class(mrb, "UDPSocket", ipsock); //#recvfrom_nonblock sock = mrb_define_class(mrb, "Socket", bsock); mrb_define_class_method(mrb, sock, "_accept", mrb_socket_accept, MRB_ARGS_REQ(1)); mrb_define_class_method(mrb, sock, "_accept2", mrb_socket_accept2, MRB_ARGS_REQ(1)); mrb_define_class_method(mrb, sock, "_bind", mrb_socket_bind, MRB_ARGS_REQ(3)); mrb_define_class_method(mrb, sock, "_connect", mrb_socket_connect, MRB_ARGS_REQ(3)); mrb_define_class_method(mrb, sock, "_listen", mrb_socket_listen, MRB_ARGS_REQ(2)); mrb_define_class_method(mrb, sock, "_sockaddr_family", mrb_socket_sockaddr_family, MRB_ARGS_REQ(1)); mrb_define_class_method(mrb, sock, "_socket", mrb_socket_socket, MRB_ARGS_REQ(3)); //mrb_define_class_method(mrb, sock, "gethostbyaddr", mrb_socket_gethostbyaddr, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1)); //mrb_define_class_method(mrb, sock, "gethostbyname", mrb_socket_gethostbyname, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1)); mrb_define_class_method(mrb, sock, "gethostname", mrb_socket_gethostname, MRB_ARGS_NONE()); //mrb_define_class_method(mrb, sock, "getservbyname", mrb_socket_getservbyname, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1)); //mrb_define_class_method(mrb, sock, "getservbyport", mrb_socket_getservbyport, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1)); mrb_define_class_method(mrb, sock, "sockaddr_un", mrb_socket_sockaddr_un, MRB_ARGS_REQ(1)); mrb_define_class_method(mrb, sock, "socketpair", mrb_socket_socketpair, MRB_ARGS_REQ(3)); //mrb_define_method(mrb, sock, "sysaccept", mrb_socket_accept, MRB_ARGS_NONE()); #ifndef _WIN32 mrb_define_class(mrb, "UNIXSocket", bsock); //mrb_define_class_method(mrb, usock, "pair", mrb_unixsocket_open, MRB_ARGS_OPT(2)); //mrb_define_class_method(mrb, usock, "socketpair", mrb_unixsocket_open, MRB_ARGS_OPT(2)); //mrb_define_method(mrb, usock, "recv_io", mrb_unixsocket_peeraddr, MRB_ARGS_NONE()); //mrb_define_method(mrb, usock, "recvfrom", mrb_unixsocket_peeraddr, MRB_ARGS_NONE()); //mrb_define_method(mrb, usock, "send_io", mrb_unixsocket_peeraddr, MRB_ARGS_NONE()); #endif /* Windows IO Method Overrides on BasicSocket */ #ifdef _WIN32 mrb_define_method(mrb, bsock, "close", mrb_win32_basicsocket_close, MRB_ARGS_NONE()); mrb_define_method(mrb, bsock, "sysread", mrb_win32_basicsocket_sysread, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1)); mrb_define_method(mrb, bsock, "sysseek", mrb_win32_basicsocket_sysseek, MRB_ARGS_REQ(1)); mrb_define_method(mrb, bsock, "syswrite", mrb_win32_basicsocket_syswrite, MRB_ARGS_REQ(1)); #endif constants = mrb_define_module_under(mrb, sock, "Constants"); #define define_const(SYM) \ do { \ mrb_define_const(mrb, constants, #SYM, mrb_fixnum_value(SYM)); \ } while (0) #include "const.cstub" } void mrb_mruby_socket_gem_final(mrb_state* mrb) { mrb_value ai; ai = mrb_mod_cv_get(mrb, mrb_class_get(mrb, "Addrinfo"), mrb_intern_lit(mrb, "_lastai")); if (mrb_cptr_p(ai)) { freeaddrinfo((struct addrinfo*)mrb_cptr(ai)); } #ifdef _WIN32 WSACleanup(); #endif } mruby-2.0.0/mrbgems/mruby-socket/test/000077500000000000000000000000001340361412400177045ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-socket/test/addrinfo.rb000066400000000000000000000053331340361412400220230ustar00rootroot00000000000000assert('Addrinfo') do assert_equal(Class, Addrinfo.class) end assert('super class of Addrinfo') do assert_equal(Object, Addrinfo.superclass) end assert('Addrinfo.getaddrinfo') do ary = Addrinfo.getaddrinfo("localhost", 53, Socket::AF_INET, Socket::SOCK_STREAM) assert_true(ary.size >= 1) ai = ary[0] assert_equal(ai.afamily, Socket::AF_INET) assert_equal(ai.pfamily, Socket::PF_INET) assert_equal(ai.socktype, Socket::SOCK_STREAM) assert_equal(ai.ip_address, '127.0.0.1') assert_equal(ai.ip_port, 53) end assert('Addrinfo.foreach') do # assume Addrinfo.getaddrinfo works well a = Addrinfo.getaddrinfo("localhost", 80) b = [] Addrinfo.foreach("localhost", 80) { |ai| b << ai } assert_equal(a.size, b.size) end assert('Addrinfo.ip') do ai = Addrinfo.ip('127.0.0.1') assert_equal('127.0.0.1', ai.ip_address) assert_equal(Socket::AF_INET, ai.afamily) assert_equal(0, ai.ip_port) assert_equal(0, ai.socktype) assert_equal(0, ai.protocol) end assert('Addrinfo.tcp') do ai = Addrinfo.tcp('127.0.0.1', 25) assert_equal('127.0.0.1', ai.ip_address) assert_equal(Socket::AF_INET, ai.afamily) assert_equal(25, ai.ip_port) assert_equal(Socket::SOCK_STREAM, ai.socktype) assert_equal(Socket::IPPROTO_TCP, ai.protocol) end assert('Addrinfo.udp') do ai = Addrinfo.udp('127.0.0.1', 53) assert_equal('127.0.0.1', ai.ip_address) assert_equal(Socket::AF_INET, ai.afamily) assert_equal(53, ai.ip_port) assert_equal(Socket::SOCK_DGRAM, ai.socktype) assert_equal(Socket::IPPROTO_UDP, ai.protocol) end assert('Addrinfo.unix') do skip "unix is not supported on Windows" if SocketTest.win? a1 = Addrinfo.unix('/tmp/sock') assert_true(a1.unix?) assert_equal('/tmp/sock', a1.unix_path) assert_equal(Socket::SOCK_STREAM, a1.socktype) a2 = Addrinfo.unix('/tmp/sock', Socket::SOCK_DGRAM) assert_equal(Socket::SOCK_DGRAM, a2.socktype) end assert('Addrinfo#afamily') do skip "afamily is not supported on Windows" if SocketTest.win? ai4 = Addrinfo.new(Socket.sockaddr_in(1, '127.0.0.1')) ai6 = Addrinfo.new(Socket.sockaddr_in(1, '::1')) aiu = Addrinfo.new(Socket.sockaddr_un('/tmp/sock')) assert_equal(Socket::AF_INET, ai4.afamily) assert_equal(Socket::AF_INET6, ai6.afamily) assert_equal(Socket::AF_UNIX, aiu.afamily) end # assert('Addrinfo#canonname') do # #getnameinfo # assert('Addrinfo#inspect') do # assert('Addrinfo#inspect_socket') do # assert('Addrinfo#ip?') do # assert('Addrinfo#ip_address') do # assert('Addrinfo#ip_port') do # assert('Addrinfo#ip_unpack') do # assert('Addrinfo#ipv4?') do # assert('Addrinfo#ipv6?') do # assert('Addrinfo#pfamily') do # assert('Addrinfo#protocol') do # assert('Addrinfo#socktype') do # assert('Addrinfo#to_sockaddr') do # assert('Addrinfo#unix?') do # #unix_path mruby-2.0.0/mrbgems/mruby-socket/test/basicsocket.rb000066400000000000000000000007171340361412400225300ustar00rootroot00000000000000assert('BasicSocket') do assert_equal(Class, BasicSocket.class) end assert('super class of BasicSocket') do assert_equal(IO, BasicSocket.superclass) end assert('BasicSocket.do_not_reverse_lookup') do assert_equal(BasicSocket.do_not_reverse_lookup, true) end assert('BasicSocket.do_not_reverse_lookup=') do BasicSocket.do_not_reverse_lookup = false assert_equal(BasicSocket.do_not_reverse_lookup, false) BasicSocket.do_not_reverse_lookup = true end mruby-2.0.0/mrbgems/mruby-socket/test/ipsocket.rb000066400000000000000000000016301340361412400220520ustar00rootroot00000000000000unless SocketTest.win? # Note: most of tests below will fail if UDPSocket is broken. assert('IPSocket.getaddress') do l = IPSocket.getaddress("localhost") assert_true (l == "127.0.0.1" or l == "::1") end assert('IPSocket.addr') do localhost = "127.0.0.1" s = UDPSocket.new s.bind(localhost, 0) port = Addrinfo.new(s.getsockname).ip_port a = s.addr assert_equal "AF_INET", a[0] assert_equal port, a[1] assert_equal localhost, a[2] assert_equal localhost, a[3] s.close true end assert('IPSocket.peeraddr') do localhost = "127.0.0.1" server = UDPSocket.new server.bind(localhost, 0) port = server.local_address.ip_port client = UDPSocket.new client.connect(localhost, port) a = client.peeraddr assert_equal "AF_INET", a[0] assert_equal port, a[1] assert_equal localhost, a[2] assert_equal localhost, a[3] client.close server.close true end end # win? mruby-2.0.0/mrbgems/mruby-socket/test/socket.rb000066400000000000000000000021021340361412400215140ustar00rootroot00000000000000unless SocketTest.win? assert('Socket.gethostname') do assert_true(Socket.gethostname.is_a? String) end assert('Socket::getaddrinfo') do ret = Socket.getaddrinfo("localhost", 53, Socket::AF_INET, Socket::SOCK_DGRAM) assert_true ret.size >= 1 a = ret[0] assert_equal "AF_INET", a[0] assert_equal 53, a[1] # documents says it's a hostname but CRuby returns an address #assert_equal "127.0.0.1", a[2] assert_equal "127.0.0.1", a[3] assert_equal Socket::AF_INET, a[4] assert_equal Socket::SOCK_DGRAM, a[5] assert_equal Socket::IPPROTO_UDP, a[6] unless SocketTest.cygwin? end assert('Socket#recvfrom') do begin sstr = "abcdefg" s = Socket.new(Socket::AF_INET, Socket::SOCK_DGRAM, 0) c = Socket.new(Socket::AF_INET, Socket::SOCK_DGRAM, 0) s.bind(Socket.sockaddr_in(0, "127.0.0.1")) c.send sstr, 0, s.getsockname rstr, ai = s.recvfrom sstr.size assert_equal sstr, rstr assert_true "127.0.0.1", ai.ip_address ensure s.close rescue nil c.close rescue nil end end end # win? mruby-2.0.0/mrbgems/mruby-socket/test/sockettest.c000066400000000000000000000030431340361412400222400ustar00rootroot00000000000000#include #include #include "mruby.h" #include "mruby/error.h" #if defined(_WIN32) || defined(_WIN64) #include #ifdef _MSC_VER #include #include #define open _open #define close _close #define unlink _unlink static int mkstemp(char *p) { int fd; char* fname = _mktemp(p); if (fname == NULL) return -1; fd = open(fname, O_RDWR | O_CREAT | O_EXCL, _S_IREAD | _S_IWRITE); if (fd >= 0) return fd; return -1; } #endif #else #include #endif mrb_value mrb_sockettest_tmppath(mrb_state *mrb, mrb_value klass) { char name[] = "mruby-socket.XXXXXXXX"; int fd = mkstemp(name); if (fd == -1) { mrb_sys_fail(mrb, 0); } if (close(fd) == -1) { mrb_sys_fail(mrb, 0); } if (unlink(name) == -1) { mrb_sys_fail(mrb, 0); } return mrb_str_new_cstr(mrb, name); } mrb_value mrb_sockettest_win_p(mrb_state *mrb, mrb_value klass) { #ifdef _WIN32 return mrb_true_value(); #else return mrb_false_value(); #endif } mrb_value mrb_sockettest_cygwin_p(mrb_state *mrb, mrb_value klass) { #if defined(__CYGWIN__) || defined(__CYGWIN32__) return mrb_true_value(); #else return mrb_false_value(); #endif } void mrb_mruby_socket_gem_test(mrb_state* mrb) { struct RClass *c = mrb_define_module(mrb, "SocketTest"); mrb_define_class_method(mrb, c, "tmppath", mrb_sockettest_tmppath, MRB_ARGS_NONE()); mrb_define_class_method(mrb, c, "win?", mrb_sockettest_win_p, MRB_ARGS_NONE()); mrb_define_class_method(mrb, c, "cygwin?", mrb_sockettest_cygwin_p, MRB_ARGS_NONE()); } mruby-2.0.0/mrbgems/mruby-socket/test/tcpsocket.rb000066400000000000000000000001761340361412400222340ustar00rootroot00000000000000#assert('TCPSocket.gethostbyname') do #assert('TCPSocket.new') do #assert('TCPSocket#close') do #assert('TCPSocket#write') do mruby-2.0.0/mrbgems/mruby-socket/test/udpsocket.rb000066400000000000000000000005311340361412400222310ustar00rootroot00000000000000assert('UDPSocket.new') do s = UDPSocket.new assert_true(s.is_a? UDPSocket) s.close s = UDPSocket.new(Socket::AF_INET6) assert_true(s.is_a? UDPSocket) s.close true end #assert('UDPSocket#connect') do #assert('UDPSocket#send') do #assert('UDPSocket#recv') do #assert('UDPSocket#bind') do #assert('UDPSocket#recvfrom_nonblock') do mruby-2.0.0/mrbgems/mruby-socket/test/unix.rb000066400000000000000000000053431340361412400212210ustar00rootroot00000000000000unless SocketTest.win? || SocketTest.cygwin? def unixserver_test_block path = SocketTest.tmppath File.unlink path rescue nil begin result = yield path ensure File.unlink path rescue nil end result end def with_unix_server unixserver_test_block do |path| UNIXServer.open(path) { |server| yield path, server } end end def with_unix_client with_unix_server do |path, server| UNIXSocket.open(path) do |csock| ssock = server.accept begin yield path, server, ssock, csock ensure ssock.close unless ssock.closed? rescue nil end end end end assert('UNIXServer.new') do unixserver_test_block do |path| server = UNIXServer.new(path) assert_true server.is_a? UNIXServer server.close File.unlink path s2 = nil result = UNIXServer.open(path) { |s1| assert_true s1.is_a? UNIXServer s2 = s1 1234 } assert_equal 1234, result assert_true s2.is_a? UNIXServer assert_true s2.closed? end end # assert('UNIXServer#accept_nonblock') - would block if fails assert('UNIXServer#addr') do with_unix_server do |path, server| assert_equal [ "AF_UNIX", path], server.addr end end assert('UNIXServer#path') do with_unix_server do |path, server| assert_equal path, server.path end end # assert('UNIXServer#peeraddr') - will raise a runtime exception assert('UNIXServer#listen') do with_unix_server do |path, server| assert_equal 0, server.listen(1) end end assert('UNIXServer#sysaccept') do with_unix_server do |path, server| UNIXSocket.open(path) do |csock| begin fd = server.sysaccept assert_true fd.kind_of? Integer ensure IO._sysclose(fd) rescue nil end end end end assert('UNIXSocket.new') do with_unix_server do |path, server| c = UNIXSocket.new(path) assert_true c.is_a? UNIXSocket c.close true end end assert('UNIXSocket#addr') do with_unix_client do |path, server, ssock, csock| assert_equal [ "AF_UNIX", path ], ssock.addr assert_equal [ "AF_UNIX", "" ], csock.addr end end assert('UNIXSocket#path') do with_unix_client do |path, server, ssock, csock| assert_equal path, ssock.path assert_equal "", csock.path end end assert('UNIXSocket#peeraddr') do with_unix_client do |path, server, ssock, csock| assert_equal [ "AF_UNIX", "" ], ssock.peeraddr assert_equal [ "AF_UNIX", path ], csock.peeraddr end end assert('UNIXSocket#recvfrom') do with_unix_client do |path, server, ssock, csock| str = "0123456789" ssock.send str, 0 a = csock.recvfrom(8) assert_equal str[0, 8], a[0] assert_equal "AF_UNIX", a[1][0] # a[1][1] would be "" or something end end end # SocketTest.win? mruby-2.0.0/mrbgems/mruby-sprintf/000077500000000000000000000000001340361412400171225ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-sprintf/mrbgem.rake000066400000000000000000000002521340361412400212360ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-sprintf') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'standard Kernel#sprintf method' end mruby-2.0.0/mrbgems/mruby-sprintf/mrblib/000077500000000000000000000000001340361412400203715ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-sprintf/mrblib/string.rb000066400000000000000000000002031340361412400222170ustar00rootroot00000000000000class String def %(args) if args.is_a? Array sprintf(self, *args) else sprintf(self, args) end end end mruby-2.0.0/mrbgems/mruby-sprintf/src/000077500000000000000000000000001340361412400177115ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-sprintf/src/kernel.c000066400000000000000000000011701340361412400213340ustar00rootroot00000000000000/* ** kernel.c - Kernel module suppliment ** ** See Copyright Notice in mruby.h */ #include mrb_value mrb_f_sprintf(mrb_state *mrb, mrb_value obj); /* in sprintf.c */ void mrb_mruby_sprintf_gem_init(mrb_state* mrb) { struct RClass *krn; if (mrb->kernel_module == NULL) { mrb->kernel_module = mrb_define_module(mrb, "Kernel"); /* Might be PARANOID. */ } krn = mrb->kernel_module; mrb_define_method(mrb, krn, "sprintf", mrb_f_sprintf, MRB_ARGS_ANY()); mrb_define_method(mrb, krn, "format", mrb_f_sprintf, MRB_ARGS_ANY()); } void mrb_mruby_sprintf_gem_final(mrb_state* mrb) { /* nothing to do. */ } mruby-2.0.0/mrbgems/mruby-sprintf/src/sprintf.c000066400000000000000000001014561340361412400215510ustar00rootroot00000000000000/* ** sprintf.c - Kernel.#sprintf ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #ifndef MRB_WITHOUT_FLOAT #include #endif #include #define BIT_DIGITS(N) (((N)*146)/485 + 1) /* log2(10) =~ 146/485 */ #define BITSPERDIG MRB_INT_BIT #define EXTENDSIGN(n, l) (((~0U << (n)) >> (((n)*(l)) % BITSPERDIG)) & ~(~0U << (n))) mrb_value mrb_str_format(mrb_state *, mrb_int, const mrb_value *, mrb_value); #ifndef MRB_WITHOUT_FLOAT static void fmt_setup(char*,size_t,int,int,mrb_int,mrb_int); #endif static char* remove_sign_bits(char *str, int base) { char *t; t = str; if (base == 16) { while (*t == 'f') { t++; } } else if (base == 8) { *t |= EXTENDSIGN(3, strlen(t)); while (*t == '7') { t++; } } else if (base == 2) { while (*t == '1') { t++; } } return t; } static char sign_bits(int base, const char *p) { char c; switch (base) { case 16: if (*p == 'X') c = 'F'; else c = 'f'; break; case 8: c = '7'; break; case 2: c = '1'; break; default: c = '.'; break; } return c; } static mrb_value mrb_fix2binstr(mrb_state *mrb, mrb_value x, int base) { char buf[66], *b = buf + sizeof buf; mrb_int num = mrb_fixnum(x); uint64_t val = (uint64_t)num; char d; if (base != 2) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid radix %S", mrb_fixnum_value(base)); } if (val == 0) { return mrb_str_new_lit(mrb, "0"); } *--b = '\0'; do { *--b = mrb_digitmap[(int)(val % base)]; } while (val /= base); if (num < 0) { b = remove_sign_bits(b, base); switch (base) { case 16: d = 'f'; break; case 8: d = '7'; break; case 2: d = '1'; break; default: d = 0; break; } if (d && *b != d) { *--b = d; } } return mrb_str_new_cstr(mrb, b); } #define FNONE 0 #define FSHARP 1 #define FMINUS 2 #define FPLUS 4 #define FZERO 8 #define FSPACE 16 #define FWIDTH 32 #define FPREC 64 #define FPREC0 128 #define CHECK(l) do {\ while ((l) >= bsiz - blen) {\ if (bsiz > MRB_INT_MAX/2) mrb_raise(mrb, E_ARGUMENT_ERROR, "too big specifier"); \ bsiz*=2;\ }\ mrb_str_resize(mrb, result, bsiz);\ buf = RSTRING_PTR(result);\ } while (0) #define PUSH(s, l) do { \ CHECK(l);\ memcpy(&buf[blen], s, l);\ blen += (mrb_int)(l);\ } while (0) #define FILL(c, l) do { \ CHECK(l);\ memset(&buf[blen], c, l);\ blen += (l);\ } while (0) static void check_next_arg(mrb_state *mrb, int posarg, int nextarg) { switch (posarg) { case -1: mrb_raisef(mrb, E_ARGUMENT_ERROR, "unnumbered(%S) mixed with numbered", mrb_fixnum_value(nextarg)); break; case -2: mrb_raisef(mrb, E_ARGUMENT_ERROR, "unnumbered(%S) mixed with named", mrb_fixnum_value(nextarg)); break; default: break; } } static void check_pos_arg(mrb_state *mrb, mrb_int posarg, mrb_int n) { if (posarg > 0) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "numbered(%S) after unnumbered(%S)", mrb_fixnum_value(n), mrb_fixnum_value(posarg)); } if (posarg == -2) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "numbered(%S) after named", mrb_fixnum_value(n)); } if (n < 1) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid index - %S$", mrb_fixnum_value(n)); } } static void check_name_arg(mrb_state *mrb, int posarg, const char *name, mrb_int len) { if (posarg > 0) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "named%S after unnumbered(%S)", mrb_str_new(mrb, (name), (len)), mrb_fixnum_value(posarg)); } if (posarg == -1) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "named%S after numbered", mrb_str_new(mrb, (name), (len))); } } #define GETNEXTARG() (\ check_next_arg(mrb, posarg, nextarg),\ (posarg = nextarg++, GETNTHARG(posarg))) #define GETARG() (!mrb_undef_p(nextvalue) ? nextvalue : GETNEXTARG()) #define GETPOSARG(n) (\ check_pos_arg(mrb, posarg, n),\ (posarg = -1, GETNTHARG(n))) #define GETNTHARG(nth) \ ((nth >= argc) ? (mrb_raise(mrb, E_ARGUMENT_ERROR, "too few arguments"), mrb_undef_value()) : argv[nth]) #define GETNAMEARG(id, name, len) (\ check_name_arg(mrb, posarg, name, len),\ (posarg = -2, mrb_hash_fetch(mrb, get_hash(mrb, &hash, argc, argv), id, mrb_undef_value()))) #define GETNUM(n, val) \ for (; p < end && ISDIGIT(*p); p++) {\ if (n > (MRB_INT_MAX - (*p - '0'))/10) {\ mrb_raise(mrb, E_ARGUMENT_ERROR, #val " too big"); \ } \ n = 10 * n + (*p - '0'); \ } \ if (p >= end) { \ mrb_raise(mrb, E_ARGUMENT_ERROR, "malformed format string - %*[0-9]"); \ } #define GETASTER(num) do { \ mrb_value tmp_v; \ t = p++; \ n = 0; \ GETNUM(n, val); \ if (*p == '$') { \ tmp_v = GETPOSARG(n); \ } \ else { \ tmp_v = GETNEXTARG(); \ p = t; \ } \ num = mrb_int(mrb, tmp_v); \ } while (0) static mrb_value get_hash(mrb_state *mrb, mrb_value *hash, mrb_int argc, const mrb_value *argv) { mrb_value tmp; if (!mrb_undef_p(*hash)) return *hash; if (argc != 2) { mrb_raise(mrb, E_ARGUMENT_ERROR, "one hash required"); } tmp = mrb_check_hash_type(mrb, argv[1]); if (mrb_nil_p(tmp)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "one hash required"); } return (*hash = tmp); } /* * call-seq: * format(format_string [, arguments...] ) -> string * sprintf(format_string [, arguments...] ) -> string * * Returns the string resulting from applying format_string to * any additional arguments. Within the format string, any characters * other than format sequences are copied to the result. * * The syntax of a format sequence is follows. * * %[flags][width][.precision]type * * A format * sequence consists of a percent sign, followed by optional flags, * width, and precision indicators, then terminated with a field type * character. The field type controls how the corresponding * sprintf argument is to be interpreted, while the flags * modify that interpretation. * * The field type characters are: * * Field | Integer Format * ------+-------------------------------------------------------------- * 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 * | in the alternative format by #. * d | Convert argument as a decimal number. * 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'. * 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 * | leading 'ff's). * X | Equivalent to 'x', but uses uppercase letters. * * Field | Float Format * ------+-------------------------------------------------------------- * e | Convert floating point argument into exponential notation * | 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 * | the exponent. * f | Convert floating point argument as [-]ddd.dddddd, * | where the precision specifies the number of digits after * | the decimal point. * g | Convert a floating point number using exponential form * | 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. * 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'. * * Field | Other Format * ------+-------------------------------------------------------------- * c | Argument is the numeric code for a single character or * | a single character string itself. * p | The valuing of argument.inspect. * s | Argument is a string to be substituted. If the format * | sequence contains a precision, at most that many characters * | will be copied. * % | A percent sign itself will be displayed. No argument taken. * * The flags modifies the behavior of the formats. * The flag characters are: * * Flag | Applies to | Meaning * ---------+---------------+----------------------------------------- * space | bBdiouxX | Leave a space at the start of * | aAeEfgG | non-negative numbers. * | (numeric fmt) | For 'o', 'x', 'X', 'b' and 'B', use * | | a minus sign with absolute value for * | | negative values. * ---------+---------------+----------------------------------------- * (digit)$ | all | Specifies the absolute argument number * | | for this field. Absolute and relative * | | argument numbers cannot be mixed in a * | | sprintf string. * ---------+---------------+----------------------------------------- * # | bBoxX | Use an alternative format. * | 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', * | | force a decimal point to be added, * | | even if no digits follow. * | | 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 * | | 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 * | (numeric fmt) | is used for negative numbers formatted as * | | complements. * ---------+---------------+----------------------------------------- * * | all | Use the next argument as the field width. * | | If negative, left-justify the result. If the * | | asterisk is followed by a number and a dollar * | | sign, use the indicated argument as the width. * * Examples of flags: * * # '+' 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. * 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. * sprintf("%x", 123) #=> "7b" * sprintf("%#x", 123) #=> "0x7b" * sprintf("%+x", -123) #=> "-7b" * sprintf("%x", -123) #=> "..f85" * sprintf("%#x", -123) #=> "0x..f85" * sprintf("%#x", 0) #=> "0" * * # '#' 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. * sprintf("%b", 123) #=> "1111011" * sprintf("%#b", 123) #=> "0b1111011" * sprintf("%+b", -123) #=> "-1111011" * sprintf("%b", -123) #=> "..10000101" * sprintf("%#b", -123) #=> "0b..10000101" * sprintf("%#b", 0) #=> "0" * * # '#' for 'B' uses the prefix '0B'. * sprintf("%B", 123) #=> "1111011" * sprintf("%#B", 123) #=> "0B1111011" * * # '#' 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. * sprintf("%.0f", 1234) #=> "1234" * sprintf("%#.0f", 1234) #=> "1234." * * # '#' 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" * sprintf("%g", 123456) #=> "123456" * sprintf("%#g", 123456) #=> "123456." * * The field width is an optional integer, followed optionally by a * period and a precision. The width specifies the minimum number of * characters that will be written to the result for this field. * * Examples of width: * * # padding is done by spaces, width=20 * # 0 or radix-1. <------------------> * sprintf("%20d", 123) #=> " 123" * sprintf("%+20d", 123) #=> " +123" * sprintf("%020d", 123) #=> "00000000000000000123" * sprintf("%+020d", 123) #=> "+0000000000000000123" * sprintf("% 020d", 123) #=> " 0000000000000000123" * sprintf("%-20d", 123) #=> "123 " * sprintf("%-+20d", 123) #=> "+123 " * sprintf("%- 20d", 123) #=> " 123 " * sprintf("%020x", -123) #=> "..ffffffffffffffff85" * * For * numeric fields, the precision controls the number of decimal places * displayed. For string fields, the precision determines the maximum * number of characters to be copied from the string. (Thus, the format * sequence %10.10s will always contribute exactly ten * characters to the result.) * * Examples of precisions: * * # precision for 'd', 'o', 'x' and 'b' is * # minimum number of digits <------> * sprintf("%20.8d", 123) #=> " 00000123" * sprintf("%20.8o", 123) #=> " 00000173" * sprintf("%20.8x", 123) #=> " 0000007b" * sprintf("%20.8b", 123) #=> " 01111011" * sprintf("%20.8d", -123) #=> " -00000123" * sprintf("%20.8o", -123) #=> " ..777605" * 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. <------> * sprintf("%#20.8d", 123) #=> " 00000123" * sprintf("%#20.8o", 123) #=> " 00000173" * sprintf("%#20.8x", 123) #=> " 0x0000007b" * sprintf("%#20.8b", 123) #=> " 0b01111011" * sprintf("%#20.8d", -123) #=> " -00000123" * sprintf("%#20.8o", -123) #=> " ..777605" * sprintf("%#20.8x", -123) #=> " 0x..ffff85" * sprintf("%#20.8b", -11) #=> " 0b..110101" * * # 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 * # digits after the decimal point <------> * sprintf("%20.8f", 1234.56789) #=> " 1234.56789000" * * # 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 * # maximum number of characters <------> * sprintf("%20.8s", "string test") #=> " string t" * * Examples: * * sprintf("%d %04x", 123, 123) #=> "123 007b" * sprintf("%08b '%4s'", 123, 123) #=> "01111011 ' 123'" * sprintf("%1$*2$s %2$d %1$s", "hello", 8) #=> " hello 8 hello" * sprintf("%1$*2$s %2$d", "hello", -8) #=> "hello -8" * sprintf("%+g:% g:%-g", 1.23, 1.23, 1.23) #=> "+1.23: 1.23:1.23" * sprintf("%u", -123) #=> "-123" * * For more complex formatting, Ruby supports a reference by name. * %s style uses format style, but %{name} style doesn't. * * Exapmles: * sprintf("%d : %f", { :foo => 1, :bar => 2 }) * #=> 1 : 2.000000 * sprintf("%{foo}f", { :foo => 1 }) * # => "1f" */ mrb_value mrb_f_sprintf(mrb_state *mrb, mrb_value obj) { mrb_int argc; mrb_value *argv; mrb_get_args(mrb, "*", &argv, &argc); if (argc <= 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "too few arguments"); return mrb_nil_value(); } else { return mrb_str_format(mrb, argc - 1, argv + 1, argv[0]); } } mrb_value mrb_str_format(mrb_state *mrb, mrb_int argc, const mrb_value *argv, mrb_value fmt) { const char *p, *end; char *buf; mrb_int blen; mrb_int bsiz; mrb_value result; mrb_int n; mrb_int width; mrb_int prec; int nextarg = 1; int posarg = 0; mrb_value nextvalue; mrb_value str; mrb_value hash = mrb_undef_value(); #define CHECK_FOR_WIDTH(f) \ if ((f) & FWIDTH) { \ mrb_raise(mrb, E_ARGUMENT_ERROR, "width given twice"); \ } \ if ((f) & FPREC0) { \ mrb_raise(mrb, E_ARGUMENT_ERROR, "width after precision"); \ } #define CHECK_FOR_FLAGS(f) \ if ((f) & FWIDTH) { \ mrb_raise(mrb, E_ARGUMENT_ERROR, "flag after width"); \ } \ if ((f) & FPREC0) { \ mrb_raise(mrb, E_ARGUMENT_ERROR, "flag after precision"); \ } ++argc; --argv; mrb_to_str(mrb, fmt); p = RSTRING_PTR(fmt); end = p + RSTRING_LEN(fmt); blen = 0; bsiz = 120; result = mrb_str_new_capa(mrb, bsiz); buf = RSTRING_PTR(result); memset(buf, 0, bsiz); for (; p < end; p++) { const char *t; mrb_sym id = 0; int flags = FNONE; for (t = p; t < end && *t != '%'; t++) ; if (t + 1 == end) ++t; PUSH(p, t - p); if (t >= end) goto sprint_exit; /* end of fmt string */ p = t + 1; /* skip '%' */ width = prec = -1; nextvalue = mrb_undef_value(); retry: switch (*p) { default: mrb_raisef(mrb, E_ARGUMENT_ERROR, "malformed format string - \\%%S", mrb_str_new(mrb, p, 1)); break; case ' ': CHECK_FOR_FLAGS(flags); flags |= FSPACE; p++; goto retry; case '#': CHECK_FOR_FLAGS(flags); flags |= FSHARP; p++; goto retry; case '+': CHECK_FOR_FLAGS(flags); flags |= FPLUS; p++; goto retry; case '-': CHECK_FOR_FLAGS(flags); flags |= FMINUS; p++; goto retry; case '0': CHECK_FOR_FLAGS(flags); flags |= FZERO; p++; goto retry; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': n = 0; GETNUM(n, width); if (*p == '$') { if (!mrb_undef_p(nextvalue)) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "value given twice - %S$", mrb_fixnum_value(n)); } nextvalue = GETPOSARG(n); p++; goto retry; } CHECK_FOR_WIDTH(flags); width = n; flags |= FWIDTH; goto retry; case '<': case '{': { const char *start = p; char term = (*p == '<') ? '>' : '}'; mrb_value symname; for (; p < end && *p != term; ) p++; if (id) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "name%S after <%S>", mrb_str_new(mrb, start, p - start + 1), mrb_sym2str(mrb, id)); } symname = mrb_str_new(mrb, start + 1, p - start - 1); id = mrb_intern_str(mrb, symname); nextvalue = GETNAMEARG(mrb_symbol_value(id), start, (mrb_int)(p - start + 1)); if (mrb_undef_p(nextvalue)) { mrb_raisef(mrb, E_KEY_ERROR, "key%S not found", mrb_str_new(mrb, start, p - start + 1)); } if (term == '}') goto format_s; p++; goto retry; } case '*': CHECK_FOR_WIDTH(flags); flags |= FWIDTH; GETASTER(width); if (width < 0) { flags |= FMINUS; width = -width; } p++; goto retry; case '.': if (flags & FPREC0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "precision given twice"); } flags |= FPREC|FPREC0; prec = 0; p++; if (*p == '*') { GETASTER(prec); if (prec < 0) { /* ignore negative precision */ flags &= ~FPREC; } p++; goto retry; } GETNUM(prec, precision); goto retry; case '\n': case '\0': p--; /* fallthrough */ case '%': if (flags != FNONE) { mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid format character - %"); } PUSH("%", 1); break; case 'c': { mrb_value val = GETARG(); mrb_value tmp; char *c; tmp = mrb_check_string_type(mrb, val); if (!mrb_nil_p(tmp)) { if (RSTRING_LEN(tmp) != 1) { mrb_raise(mrb, E_ARGUMENT_ERROR, "%c requires a character"); } } else if (mrb_fixnum_p(val)) { mrb_int n = mrb_fixnum(val); if (n < 0x80) { char buf[1]; buf[0] = (char)n; tmp = mrb_str_new(mrb, buf, 1); } else { tmp = mrb_funcall(mrb, val, "chr", 0); mrb_check_type(mrb, tmp, MRB_TT_STRING); } } else { mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid character"); } c = RSTRING_PTR(tmp); n = RSTRING_LEN(tmp); if (!(flags & FWIDTH)) { PUSH(c, n); } else if ((flags & FMINUS)) { PUSH(c, n); if (width>0) FILL(' ', width-1); } else { if (width>0) FILL(' ', width-1); PUSH(c, n); } } break; case 's': case 'p': format_s: { mrb_value arg = GETARG(); mrb_int len; mrb_int slen; if (*p == 'p') arg = mrb_inspect(mrb, arg); str = mrb_obj_as_string(mrb, arg); len = RSTRING_LEN(str); if (RSTRING(result)->flags & MRB_STR_EMBED) { mrb_int tmp_n = len; RSTRING(result)->flags &= ~MRB_STR_EMBED_LEN_MASK; RSTRING(result)->flags |= tmp_n << MRB_STR_EMBED_LEN_SHIFT; } else { RSTRING(result)->as.heap.len = blen; } if (flags&(FPREC|FWIDTH)) { slen = RSTRING_LEN(str); if (slen < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid mbstring sequence"); } if ((flags&FPREC) && (prec < slen)) { char *p = RSTRING_PTR(str) + prec; slen = prec; len = (mrb_int)(p - RSTRING_PTR(str)); } /* need to adjust multi-byte string pos */ if ((flags&FWIDTH) && (width > slen)) { width -= (int)slen; if (!(flags&FMINUS)) { FILL(' ', width); } PUSH(RSTRING_PTR(str), len); if (flags&FMINUS) { FILL(' ', width); } break; } } PUSH(RSTRING_PTR(str), len); } break; case 'd': case 'i': case 'o': case 'x': case 'X': case 'b': case 'B': case 'u': { mrb_value val = GETARG(); char nbuf[68], *s; const char *prefix = NULL; int sign = 0, dots = 0; char sc = 0; mrb_int v = 0; int base; mrb_int len; if (flags & FSHARP) { switch (*p) { case 'o': prefix = "0"; break; case 'x': prefix = "0x"; break; case 'X': prefix = "0X"; break; case 'b': prefix = "0b"; break; case 'B': prefix = "0B"; break; default: break; } } bin_retry: switch (mrb_type(val)) { #ifndef MRB_WITHOUT_FLOAT case MRB_TT_FLOAT: val = mrb_flo_to_fixnum(mrb, val); if (mrb_fixnum_p(val)) goto bin_retry; break; #endif case MRB_TT_STRING: val = mrb_str_to_inum(mrb, val, 0, TRUE); goto bin_retry; case MRB_TT_FIXNUM: v = mrb_fixnum(val); break; default: val = mrb_Integer(mrb, val); goto bin_retry; } switch (*p) { case 'o': base = 8; break; case 'x': case 'X': base = 16; break; case 'b': case 'B': base = 2; break; case 'u': case 'd': case 'i': sign = 1; default: base = 10; break; } if (sign) { if (v >= 0) { if (flags & FPLUS) { sc = '+'; width--; } else if (flags & FSPACE) { sc = ' '; width--; } } else { sc = '-'; width--; } mrb_assert(base == 10); snprintf(nbuf, sizeof(nbuf), "%" MRB_PRId, v); s = nbuf; if (v < 0) s++; /* skip minus sign */ } else { s = nbuf; if (v < 0) { dots = 1; } switch (base) { case 2: if (v < 0) { val = mrb_fix2binstr(mrb, mrb_fixnum_value(v), base); } else { val = mrb_fixnum_to_str(mrb, mrb_fixnum_value(v), base); } strncpy(++s, RSTRING_PTR(val), sizeof(nbuf)-1); break; case 8: snprintf(++s, sizeof(nbuf)-1, "%" MRB_PRIo, v); break; case 16: snprintf(++s, sizeof(nbuf)-1, "%" MRB_PRIx, v); break; } if (v < 0) { char d; s = remove_sign_bits(s, base); switch (base) { case 16: d = 'f'; break; case 8: d = '7'; break; case 2: d = '1'; break; default: d = 0; break; } if (d && *s != d) { *--s = d; } } } { size_t size; size = strlen(s); /* PARANOID: assert(size <= MRB_INT_MAX) */ len = (mrb_int)size; } if (*p == 'X') { char *pp = s; int c; while ((c = (int)(unsigned char)*pp) != 0) { *pp = toupper(c); pp++; } } if (prefix && !prefix[1]) { /* octal */ if (dots) { prefix = NULL; } else if (len == 1 && *s == '0') { len = 0; if (flags & FPREC) prec--; } else if ((flags & FPREC) && (prec > len)) { prefix = NULL; } } else if (len == 1 && *s == '0') { prefix = NULL; } if (prefix) { size_t size; size = strlen(prefix); /* PARANOID: assert(size <= MRB_INT_MAX). * this check is absolutely paranoid. */ width -= (mrb_int)size; } if ((flags & (FZERO|FMINUS|FPREC)) == FZERO) { prec = width; width = 0; } else { if (prec < len) { if (!prefix && prec == 0 && len == 1 && *s == '0') len = 0; prec = len; } width -= prec; } if (!(flags&FMINUS) && width > 0) { FILL(' ', width); width = 0; } if (sc) PUSH(&sc, 1); if (prefix) { int plen = (int)strlen(prefix); PUSH(prefix, plen); } if (dots) { prec -= 2; width -= 2; PUSH("..", 2); } if (prec > len) { CHECK(prec - len); if ((flags & (FMINUS|FPREC)) != FMINUS) { char c = '0'; FILL(c, prec - len); } else if (v < 0) { char c = sign_bits(base, p); FILL(c, prec - len); } } PUSH(s, len); if (width > 0) { FILL(' ', width); } } break; #ifndef MRB_WITHOUT_FLOAT case 'f': case 'g': case 'G': case 'e': case 'E': case 'a': case 'A': { mrb_value val = GETARG(); double fval; mrb_int i; mrb_int need = 6; char fbuf[32]; int frexp_result; fval = mrb_float(mrb_Float(mrb, val)); if (!isfinite(fval)) { const char *expr; const mrb_int elen = 3; char sign = '\0'; if (isnan(fval)) { expr = "NaN"; } else { expr = "Inf"; } need = elen; if (!isnan(fval) && fval < 0.0) sign = '-'; else if (flags & (FPLUS|FSPACE)) sign = (flags & FPLUS) ? '+' : ' '; if (sign) ++need; if ((flags & FWIDTH) && need < width) need = width; if (need < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "width too big"); } FILL(' ', need); if (flags & FMINUS) { if (sign) buf[blen - need--] = sign; memcpy(&buf[blen - need], expr, elen); } else { if (sign) buf[blen - elen - 1] = sign; memcpy(&buf[blen - elen], expr, elen); } break; } fmt_setup(fbuf, sizeof(fbuf), *p, flags, width, prec); need = 0; if (*p != 'e' && *p != 'E') { i = INT_MIN; frexp(fval, &frexp_result); i = (mrb_int)frexp_result; if (i > 0) need = BIT_DIGITS(i); } if (need > MRB_INT_MAX - ((flags&FPREC) ? prec : 6)) { too_big_width: mrb_raise(mrb, E_ARGUMENT_ERROR, (width > prec ? "width too big" : "prec too big")); } need += (flags&FPREC) ? prec : 6; if ((flags&FWIDTH) && need < width) need = width; if (need > MRB_INT_MAX - 20) { goto too_big_width; } need += 20; CHECK(need); n = snprintf(&buf[blen], need, fbuf, fval); if (n < 0 || n >= need) { mrb_raise(mrb, E_RUNTIME_ERROR, "formatting error"); } blen += n; } break; #endif } flags = FNONE; } sprint_exit: #if 0 /* XXX - We cannot validate the number of arguments if (digit)$ style used. */ if (posarg >= 0 && nextarg < argc) { const char *mesg = "too many arguments for format string"; if (mrb_test(ruby_debug)) mrb_raise(mrb, E_ARGUMENT_ERROR, mesg); if (mrb_test(ruby_verbose)) mrb_warn(mrb, "%S", mrb_str_new_cstr(mrb, mesg)); } #endif mrb_str_resize(mrb, result, blen); return result; } #ifndef MRB_WITHOUT_FLOAT static void fmt_setup(char *buf, size_t size, int c, int flags, mrb_int width, mrb_int prec) { char *end = buf + size; int n; *buf++ = '%'; if (flags & FSHARP) *buf++ = '#'; if (flags & FPLUS) *buf++ = '+'; if (flags & FMINUS) *buf++ = '-'; if (flags & FZERO) *buf++ = '0'; if (flags & FSPACE) *buf++ = ' '; if (flags & FWIDTH) { n = snprintf(buf, end - buf, "%d", (int)width); buf += n; } if (flags & FPREC) { n = snprintf(buf, end - buf, ".%d", (int)prec); buf += n; } *buf++ = c; *buf = '\0'; } #endif mruby-2.0.0/mrbgems/mruby-sprintf/test/000077500000000000000000000000001340361412400201015ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-sprintf/test/sprintf.rb000066400000000000000000000052441340361412400221200ustar00rootroot00000000000000#assert('Kernel.sprintf') do #end assert('String#%') do assert_equal "one=1", "one=%d" % 1 assert_equal "1 one", "%d %s" % [ 1, "one" ] assert_equal "1.0", "%3.1f" % 1.01 if class_defined?("Float") assert_equal "123 < 456", "%{num} < %s" % { num: 123, str: "456" } assert_equal 15, ("%b" % (1<<14)).size end assert('String#% with inf') do inf = Float::INFINITY assert_equal "Inf", "%f" % inf assert_equal "Inf", "%2f" % inf assert_equal "Inf", "%3f" % inf assert_equal " Inf", "%4f" % inf assert_equal " Inf", "%5f" % inf assert_equal "+Inf", "%+f" % inf assert_equal "+Inf", "%+2f" % inf assert_equal "+Inf", "%+3f" % inf assert_equal "+Inf", "%+4f" % inf assert_equal " +Inf", "%+5f" % inf assert_equal "Inf", "%-f" % inf assert_equal "Inf", "%-2f" % inf assert_equal "Inf", "%-3f" % inf assert_equal "Inf ", "%-4f" % inf assert_equal "Inf ", "%-5f" % inf assert_equal " Inf", "% f" % inf assert_equal " Inf", "% 2f" % inf assert_equal " Inf", "% 3f" % inf assert_equal " Inf", "% 4f" % inf assert_equal " Inf", "% 5f" % inf end if class_defined?("Float") assert('String#% with nan') do nan = Float::NAN assert_equal "NaN", "%f" % nan assert_equal "NaN", "%2f" % nan assert_equal "NaN", "%3f" % nan assert_equal " NaN", "%4f" % nan assert_equal " NaN", "%5f" % nan assert_equal "+NaN", "%+f" % nan assert_equal "+NaN", "%+2f" % nan assert_equal "+NaN", "%+3f" % nan assert_equal "+NaN", "%+4f" % nan assert_equal " +NaN", "%+5f" % nan assert_equal "NaN", "%-f" % nan assert_equal "NaN", "%-2f" % nan assert_equal "NaN", "%-3f" % nan assert_equal "NaN ", "%-4f" % nan assert_equal "NaN ", "%-5f" % nan assert_equal " NaN", "% f" % nan assert_equal " NaN", "% 2f" % nan assert_equal " NaN", "% 3f" % nan assert_equal " NaN", "% 4f" % nan assert_equal " NaN", "% 5f" % nan end if class_defined?("Float") assert("String#% with invalid chr") do begin class Fixnum alias_method :chr_, :chr if method_defined?(:chr) def chr nil end end assert_raise TypeError do "%c" % 0x80 end ensure class Fixnum if method_defined?(:chr_) alias_method :chr, :chr_ remove_method :chr_ end end end end assert("String#% %b") do assert_equal("..10115", "%0b5" % -5) end assert("String#% %d") do assert_equal(" 10", "%4d" % 10) assert_equal("1000", "%4d" % 1000) assert_equal("10000", "%4d" % 10000) end assert("String#% invalid format") do assert_raise ArgumentError do "%?" % "" end end assert("String#% invalid format shared substring") do fmt = ("x"*30+"%!")[0...-1] assert_equal fmt, sprintf(fmt, "") end mruby-2.0.0/mrbgems/mruby-string-ext/000077500000000000000000000000001340361412400175415ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-string-ext/mrbgem.rake000066400000000000000000000003551340361412400216610ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-string-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'String class extension' spec.add_test_dependency 'mruby-enumerator', core: 'mruby-enumerator' end mruby-2.0.0/mrbgems/mruby-string-ext/mrblib/000077500000000000000000000000001340361412400210105ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-string-ext/mrblib/string.rb000066400000000000000000000270551340361412400226540ustar00rootroot00000000000000class String ## # call-seq: # string.clear -> string # # Makes string empty. # # a = "abcde" # a.clear #=> "" # def clear self.replace("") end ## # call-seq: # str.lstrip -> new_str # # Returns a copy of str with leading whitespace removed. See also # String#rstrip and String#strip. # # " hello ".lstrip #=> "hello " # "hello".lstrip #=> "hello" # def lstrip a = 0 z = self.size - 1 a += 1 while a <= z and " \f\n\r\t\v".include?(self[a]) (z >= 0) ? self[a..z] : "" end ## # call-seq: # str.rstrip -> new_str # # Returns a copy of str with trailing whitespace removed. See also # String#lstrip and String#strip. # # " hello ".rstrip #=> " hello" # "hello".rstrip #=> "hello" # def rstrip a = 0 z = self.size - 1 z -= 1 while a <= z and " \f\n\r\t\v\0".include?(self[z]) (z >= 0) ? self[a..z] : "" end ## # call-seq: # str.strip -> new_str # # Returns a copy of str with leading and trailing whitespace removed. # # " hello ".strip #=> "hello" # "\tgoodbye\r\n".strip #=> "goodbye" # def strip a = 0 z = self.size - 1 a += 1 while a <= z and " \f\n\r\t\v".include?(self[a]) z -= 1 while a <= z and " \f\n\r\t\v\0".include?(self[z]) (z >= 0) ? self[a..z] : "" end ## # call-seq: # str.lstrip! -> self or nil # # Removes leading whitespace from str, returning nil if no # change was made. See also String#rstrip! and # String#strip!. # # " hello ".lstrip #=> "hello " # "hello".lstrip! #=> nil # def lstrip! raise FrozenError, "can't modify frozen String" if frozen? s = self.lstrip (s == self) ? nil : self.replace(s) end ## # call-seq: # str.rstrip! -> self or nil # # Removes trailing whitespace from str, returning nil if # no change was made. See also String#lstrip! and # String#strip!. # # " hello ".rstrip #=> " hello" # "hello".rstrip! #=> nil # def rstrip! raise RuntimeError, "can't modify frozen String" if frozen? s = self.rstrip (s == self) ? nil : self.replace(s) end ## # call-seq: # str.strip! -> str or nil # # Removes leading and trailing whitespace from str. Returns # nil if str was not altered. # def strip! raise FrozenError, "can't modify frozen String" if frozen? s = self.strip (s == self) ? nil : self.replace(s) end ## # call-seq: # str.casecmp(other_str) -> -1, 0, +1 or nil # # Case-insensitive version of String#<=>. # # "abcdef".casecmp("abcde") #=> 1 # "aBcDeF".casecmp("abcdef") #=> 0 # "abcdef".casecmp("abcdefg") #=> -1 # "abcdef".casecmp("ABCDEF") #=> 0 # def casecmp(str) self.downcase <=> str.__to_str.downcase rescue NoMethodError nil end ## # call-seq: # str.casecmp?(other) -> true, false, or nil # # Returns true if str and other_str are equal after case folding, # false if they are not equal, and nil if other_str is not a string. def casecmp?(str) c = self.casecmp(str) return nil if c.nil? return c == 0 end def partition(sep) raise TypeError, "type mismatch: #{sep.class} given" unless sep.is_a? String n = index(sep) unless n.nil? m = n + sep.size [ slice(0, n), sep, slice(m, size - m) ] else [ self, "", "" ] end end def rpartition(sep) raise TypeError, "type mismatch: #{sep.class} given" unless sep.is_a? String n = rindex(sep) unless n.nil? m = n + sep.size [ slice(0, n), sep, slice(m, size - m) ] else [ "", "", self ] end end ## # call-seq: # str.slice!(fixnum) -> new_str or nil # str.slice!(fixnum, fixnum) -> new_str or nil # str.slice!(range) -> new_str or nil # str.slice!(other_str) -> new_str or nil # # Deletes the specified portion from str, and returns the portion # deleted. # # string = "this is a string" # string.slice!(2) #=> "i" # string.slice!(3..6) #=> " is " # string.slice!("r") #=> "r" # string #=> "thsa sting" # def slice!(arg1, arg2=nil) raise FrozenError, "can't modify frozen String" if frozen? raise "wrong number of arguments (for 1..2)" if arg1.nil? && arg2.nil? if !arg1.nil? && !arg2.nil? idx = arg1 idx += self.size if arg1 < 0 if idx >= 0 && idx <= self.size && arg2 > 0 str = self[idx, arg2] else return nil end else validated = false if arg1.kind_of?(Range) beg = arg1.begin ed = arg1.end beg += self.size if beg < 0 ed += self.size if ed < 0 ed -= 1 if arg1.exclude_end? validated = true elsif arg1.kind_of?(String) validated = true else idx = arg1 idx += self.size if arg1 < 0 validated = true if idx >=0 && arg1 < self.size end if validated str = self[arg1] else return nil end end unless str.nil? || str == "" if !arg1.nil? && !arg2.nil? idx = arg1 >= 0 ? arg1 : self.size+arg1 str2 = self[0...idx] + self[idx+arg2..-1].to_s else if arg1.kind_of?(Range) idx = beg >= 0 ? beg : self.size+beg idx2 = ed>= 0 ? ed : self.size+ed str2 = self[0...idx] + self[idx2+1..-1].to_s elsif arg1.kind_of?(String) idx = self.index(arg1) str2 = self[0...idx] + self[idx+arg1.size..-1] unless idx.nil? else idx = arg1 >= 0 ? arg1 : self.size+arg1 str2 = self[0...idx] + self[idx+1..-1].to_s end end self.replace(str2) unless str2.nil? end str end ## # call-seq: # str.insert(index, other_str) -> str # # Inserts other_str before the character at the given # index, modifying str. Negative indices count from the # end of the string, and insert after the given character. # The intent is insert aString so that it starts at the given # index. # # "abcd".insert(0, 'X') #=> "Xabcd" # "abcd".insert(3, 'X') #=> "abcXd" # "abcd".insert(4, 'X') #=> "abcdX" # "abcd".insert(-3, 'X') #=> "abXcd" # "abcd".insert(-1, 'X') #=> "abcdX" # def insert(idx, str) if idx == -1 return self << str elsif idx < 0 idx += 1 end self[idx, 0] = str self end ## # call-seq: # str.ljust(integer, padstr=' ') -> new_str # # If integer is greater than the length of str, returns a new # String of length integer with str left justified # and padded with padstr; otherwise, returns str. # # "hello".ljust(4) #=> "hello" # "hello".ljust(20) #=> "hello " # "hello".ljust(20, '1234') #=> "hello123412341234123" def ljust(idx, padstr = ' ') raise ArgumentError, 'zero width padding' if padstr == '' return self if idx <= self.size pad_repetitions = (idx / padstr.length).ceil padding = (padstr * pad_repetitions)[0...(idx - self.length)] self + padding end ## # call-seq: # str.rjust(integer, padstr=' ') -> new_str # # If integer is greater than the length of str, returns a new # String of length integer with str right justified # and padded with padstr; otherwise, returns str. # # "hello".rjust(4) #=> "hello" # "hello".rjust(20) #=> " hello" # "hello".rjust(20, '1234') #=> "123412341234123hello" def rjust(idx, padstr = ' ') raise ArgumentError, 'zero width padding' if padstr == '' return self if idx <= self.size pad_repetitions = (idx / padstr.length).ceil padding = (padstr * pad_repetitions)[0...(idx - self.length)] padding + self end def chars(&block) if block_given? self.split('').each do |i| block.call(i) end self else self.split('') end end def each_char(&block) return to_enum :each_char unless block split('').each do |i| block.call(i) end self end def codepoints(&block) len = self.size if block_given? self.split('').each do|x| block.call(x.ord) end self else self.split('').map{|x| x.ord} end end alias each_codepoint codepoints ## # call-seq: # str.prepend(other_str) -> str # # Prepend---Prepend the given string to str. # # a = "world" # a.prepend("hello ") #=> "hello world" # a #=> "hello world" def prepend(arg) self[0, 0] = arg self end ## # call-seq: # string.lines -> array of string # string.lines {|s| block} -> array of string # # Returns strings per line; # # a = "abc\ndef" # a.lines #=> ["abc\n", "def"] # # If a block is given, it works the same as each_line. def lines(&blk) lines = self.__lines if blk lines.each do |line| blk.call(line) end end lines end ## # call-seq: # str.upto(other_str, exclusive=false) {|s| block } -> str # str.upto(other_str, exclusive=false) -> an_enumerator # # Iterates through successive values, starting at str and # ending at other_str inclusive, passing each value in turn to # the block. The String#succ method is used to generate # each value. If optional second argument exclusive is omitted or is false, # the last value will be included; otherwise it will be excluded. # # If no block is given, an enumerator is returned instead. # # "a8".upto("b6") {|s| print s, ' ' } # for s in "a8".."b6" # print s, ' ' # end # # produces: # # a8 a9 b0 b1 b2 b3 b4 b5 b6 # a8 a9 b0 b1 b2 b3 b4 b5 b6 # # If str and other_str contains only ascii numeric characters, # both are recognized as decimal numbers. In addition, the width of # string (e.g. leading zeros) is handled appropriately. # # "9".upto("11").to_a #=> ["9", "10", "11"] # "25".upto("5").to_a #=> [] # "07".upto("11").to_a #=> ["07", "08", "09", "10", "11"] def upto(max, exclusive=false, &block) return to_enum(:upto, max, exclusive) unless block raise TypeError, "no implicit conversion of #{max.class} into String" unless max.kind_of? String len = self.length maxlen = max.length # single character if len == 1 and maxlen == 1 c = self.ord e = max.ord while c <= e break if exclusive and c == e yield c.chr c += 1 end return self end # both edges are all digits bi = self.to_i(10) ei = max.to_i(10) len = self.length if (bi > 0 or bi == "0"*len) and (ei > 0 or ei == "0"*maxlen) while bi <= ei break if exclusive and bi == ei s = bi.to_s s = s.rjust(len, "0") if s.length < len yield s bi += 1 end return self end bs = self while true n = (bs <=> max) break if n > 0 break if exclusive and n == 0 yield bs break if n == 0 bs = bs.succ end self end end mruby-2.0.0/mrbgems/mruby-string-ext/src/000077500000000000000000000000001340361412400203305ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-string-ext/src/string.c000066400000000000000000000762271340361412400220200ustar00rootroot00000000000000#include #include #include #include #include #include static mrb_value mrb_str_getbyte(mrb_state *mrb, mrb_value str) { mrb_int pos; mrb_get_args(mrb, "i", &pos); if (pos < 0) pos += RSTRING_LEN(str); if (pos < 0 || RSTRING_LEN(str) <= pos) return mrb_nil_value(); return mrb_fixnum_value((unsigned char)RSTRING_PTR(str)[pos]); } static mrb_value mrb_str_setbyte(mrb_state *mrb, mrb_value str) { mrb_int pos, byte; mrb_int len; mrb_get_args(mrb, "ii", &pos, &byte); len = RSTRING_LEN(str); if (pos < -len || len <= pos) mrb_raisef(mrb, E_INDEX_ERROR, "index %S is out of array", mrb_fixnum_value(pos)); if (pos < 0) pos += len; mrb_str_modify(mrb, mrb_str_ptr(str)); byte &= 0xff; RSTRING_PTR(str)[pos] = (unsigned char)byte; return mrb_fixnum_value((unsigned char)byte); } static mrb_value mrb_str_byteslice(mrb_state *mrb, mrb_value str) { mrb_value a1; mrb_int len; if (mrb_get_argc(mrb) == 2) { mrb_int pos; mrb_get_args(mrb, "ii", &pos, &len); return mrb_str_substr(mrb, str, pos, len); } mrb_get_args(mrb, "o|i", &a1, &len); switch (mrb_type(a1)) { case MRB_TT_RANGE: { mrb_int beg; len = RSTRING_LEN(str); switch (mrb_range_beg_len(mrb, a1, &beg, &len, len, TRUE)) { case 0: /* not range */ break; case 1: /* range */ return mrb_str_substr(mrb, str, beg, len); case 2: /* out of range */ mrb_raisef(mrb, E_RANGE_ERROR, "%S out of range", a1); break; } return mrb_nil_value(); } #ifndef MRB_WITHOUT_FLOAT case MRB_TT_FLOAT: a1 = mrb_fixnum_value((mrb_int)mrb_float(a1)); /* fall through */ #endif case MRB_TT_FIXNUM: return mrb_str_substr(mrb, str, mrb_fixnum(a1), 1); default: mrb_raise(mrb, E_TYPE_ERROR, "wrong type of argument"); } /* not reached */ return mrb_nil_value(); } /* * call-seq: * str.swapcase! -> str or nil * * Equivalent to String#swapcase, but modifies the receiver in * place, returning str, or nil if no changes were made. * Note: case conversion is effective only in ASCII region. */ static mrb_value mrb_str_swapcase_bang(mrb_state *mrb, mrb_value str) { char *p, *pend; int modify = 0; struct RString *s = mrb_str_ptr(str); mrb_str_modify(mrb, s); p = RSTRING_PTR(str); pend = p + RSTRING_LEN(str); while (p < pend) { if (ISUPPER(*p)) { *p = TOLOWER(*p); modify = 1; } else if (ISLOWER(*p)) { *p = TOUPPER(*p); modify = 1; } p++; } if (modify) return str; return mrb_nil_value(); } /* * call-seq: * str.swapcase -> new_str * * Returns a copy of str with uppercase alphabetic characters converted * to lowercase and lowercase characters converted to uppercase. * Note: case conversion is effective only in ASCII region. * * "Hello".swapcase #=> "hELLO" * "cYbEr_PuNk11".swapcase #=> "CyBeR_pUnK11" */ static mrb_value mrb_str_swapcase(mrb_state *mrb, mrb_value self) { mrb_value str; str = mrb_str_dup(mrb, self); mrb_str_swapcase_bang(mrb, str); return str; } static mrb_value mrb_fixnum_chr(mrb_state *mrb, mrb_value num); /* * call-seq: * str << integer -> str * str.concat(integer) -> str * str << obj -> str * str.concat(obj) -> str * * Append---Concatenates the given object to str. If the object is a * Integer, it is considered as a codepoint, and is converted * to a character before concatenation. * * a = "hello " * a << "world" #=> "hello world" * a.concat(33) #=> "hello world!" */ static mrb_value mrb_str_concat_m(mrb_state *mrb, mrb_value self) { mrb_value str; mrb_get_args(mrb, "o", &str); if (mrb_fixnum_p(str)) str = mrb_fixnum_chr(mrb, str); else str = mrb_ensure_string_type(mrb, str); mrb_str_concat(mrb, self, str); return self; } /* * call-seq: * str.start_with?([prefixes]+) -> true or false * * Returns true if +str+ starts with one of the +prefixes+ given. * * "hello".start_with?("hell") #=> true * * # returns true if one of the prefixes matches. * "hello".start_with?("heaven", "hell") #=> true * "hello".start_with?("heaven", "paradise") #=> false * "h".start_with?("heaven", "hell") #=> false */ static mrb_value mrb_str_start_with(mrb_state *mrb, mrb_value self) { mrb_value *argv, sub; mrb_int argc, i; mrb_get_args(mrb, "*", &argv, &argc); for (i = 0; i < argc; i++) { size_t len_l, len_r; int ai = mrb_gc_arena_save(mrb); sub = mrb_ensure_string_type(mrb, argv[i]); mrb_gc_arena_restore(mrb, ai); len_l = RSTRING_LEN(self); len_r = RSTRING_LEN(sub); if (len_l >= len_r) { if (memcmp(RSTRING_PTR(self), RSTRING_PTR(sub), len_r) == 0) { return mrb_true_value(); } } } return mrb_false_value(); } /* * call-seq: * str.end_with?([suffixes]+) -> true or false * * Returns true if +str+ ends with one of the +suffixes+ given. */ static mrb_value mrb_str_end_with(mrb_state *mrb, mrb_value self) { mrb_value *argv, sub; mrb_int argc, i; mrb_get_args(mrb, "*", &argv, &argc); for (i = 0; i < argc; i++) { size_t len_l, len_r; int ai = mrb_gc_arena_save(mrb); sub = mrb_ensure_string_type(mrb, argv[i]); mrb_gc_arena_restore(mrb, ai); len_l = RSTRING_LEN(self); len_r = RSTRING_LEN(sub); if (len_l >= len_r) { if (memcmp(RSTRING_PTR(self) + (len_l - len_r), RSTRING_PTR(sub), len_r) == 0) { return mrb_true_value(); } } } return mrb_false_value(); } enum tr_pattern_type { TR_UNINITIALIZED = 0, TR_IN_ORDER = 1, TR_RANGE = 2, }; /* #tr Pattern syntax ::= ()* | '^' ()* ::= | ::= ()+ ::= '-' */ struct tr_pattern { uint8_t type; // 1:in-order, 2:range mrb_bool flag_reverse : 1; mrb_bool flag_on_heap : 1; uint16_t n; union { uint16_t start_pos; char ch[2]; } val; struct tr_pattern *next; }; #define STATIC_TR_PATTERN { 0 } static inline void tr_free_pattern(mrb_state *mrb, struct tr_pattern *pat) { while (pat) { struct tr_pattern *p = pat->next; if (pat->flag_on_heap) { mrb_free(mrb, pat); } pat = p; } } static struct tr_pattern* tr_parse_pattern(mrb_state *mrb, struct tr_pattern *ret, const mrb_value v_pattern, mrb_bool flag_reverse_enable) { const char *pattern = RSTRING_PTR(v_pattern); mrb_int pattern_length = RSTRING_LEN(v_pattern); mrb_bool flag_reverse = FALSE; struct tr_pattern *pat1; mrb_int i = 0; if(flag_reverse_enable && pattern_length >= 2 && pattern[0] == '^') { flag_reverse = TRUE; i++; } while (i < pattern_length) { /* is range pattern ? */ mrb_bool const ret_uninit = (ret->type == TR_UNINITIALIZED); pat1 = ret_uninit ? ret : (struct tr_pattern*)mrb_malloc_simple(mrb, sizeof(struct tr_pattern)); if ((i+2) < pattern_length && pattern[i] != '\\' && pattern[i+1] == '-') { if (pat1 == NULL && ret) { nomem: tr_free_pattern(mrb, ret); mrb_exc_raise(mrb, mrb_obj_value(mrb->nomem_err)); return NULL; /* not reached */ } pat1->type = TR_RANGE; pat1->flag_reverse = flag_reverse; pat1->flag_on_heap = !ret_uninit; pat1->n = pattern[i+2] - pattern[i] + 1; pat1->next = NULL; pat1->val.ch[0] = pattern[i]; pat1->val.ch[1] = pattern[i+2]; i += 3; } else { /* in order pattern. */ mrb_int start_pos = i++; mrb_int len; while (i < pattern_length) { if ((i+2) < pattern_length && pattern[i] != '\\' && pattern[i+1] == '-') break; i++; } len = i - start_pos; if (len > UINT16_MAX) { mrb_raise(mrb, E_ARGUMENT_ERROR, "tr pattern too long (max 65536)"); } if (pat1 == NULL && ret) { goto nomem; } pat1->type = TR_IN_ORDER; pat1->flag_reverse = flag_reverse; pat1->flag_on_heap = !ret_uninit; pat1->n = len; pat1->next = NULL; pat1->val.start_pos = start_pos; } if (ret == NULL || ret_uninit) { ret = pat1; } else { struct tr_pattern *p = ret; while (p->next != NULL) { p = p->next; } p->next = pat1; } } return ret; } static inline mrb_int tr_find_character(const struct tr_pattern *pat, const char *pat_str, int ch) { mrb_int ret = -1; mrb_int n_sum = 0; mrb_int flag_reverse = pat ? pat->flag_reverse : 0; while (pat != NULL) { if (pat->type == TR_IN_ORDER) { int i; for (i = 0; i < pat->n; i++) { if (pat_str[pat->val.start_pos + i] == ch) ret = n_sum + i; } } else if (pat->type == TR_RANGE) { if (pat->val.ch[0] <= ch && ch <= pat->val.ch[1]) ret = n_sum + ch - pat->val.ch[0]; } else { mrb_assert(pat->type == TR_UNINITIALIZED); } n_sum += pat->n; pat = pat->next; } if (flag_reverse) { return (ret < 0) ? MRB_INT_MAX : -1; } return ret; } static inline mrb_int tr_get_character(const struct tr_pattern *pat, const char *pat_str, mrb_int n_th) { mrb_int n_sum = 0; while (pat != NULL) { if (n_th < (n_sum + pat->n)) { mrb_int i = (n_th - n_sum); switch (pat->type) { case TR_IN_ORDER: return pat_str[pat->val.start_pos + i]; case TR_RANGE: return pat->val.ch[0]+i; case TR_UNINITIALIZED: return -1; } } if (pat->next == NULL) { switch (pat->type) { case TR_IN_ORDER: return pat_str[pat->val.start_pos + pat->n - 1]; case TR_RANGE: return pat->val.ch[1]; case TR_UNINITIALIZED: return -1; } } n_sum += pat->n; pat = pat->next; } return -1; } static inline void tr_bitmap_set(uint8_t bitmap[32], uint8_t ch) { uint8_t idx1 = ch / 8; uint8_t idx2 = ch % 8; bitmap[idx1] |= (1<flag_reverse : 0; int i; for (i=0; i<32; i++) { bitmap[i] = 0; } while (pat != NULL) { if (pat->type == TR_IN_ORDER) { for (i = 0; i < pat->n; i++) { tr_bitmap_set(bitmap, pattern[pat->val.start_pos + i]); } } else if (pat->type == TR_RANGE) { for (i = pat->val.ch[0]; i < pat->val.ch[1]; i++) { tr_bitmap_set(bitmap, i); } } else { mrb_assert(pat->type == TR_UNINITIALIZED); } pat = pat->next; } if (flag_reverse) { for (i=0; i<32; i++) { bitmap[i] ^= 0xff; } } } static mrb_bool str_tr(mrb_state *mrb, mrb_value str, mrb_value p1, mrb_value p2, mrb_bool squeeze) { struct tr_pattern pat = STATIC_TR_PATTERN; struct tr_pattern rep_storage = STATIC_TR_PATTERN; char *s; mrb_int len; mrb_int i; mrb_int j; mrb_bool flag_changed = FALSE; mrb_int lastch = -1; struct tr_pattern *rep; mrb_str_modify(mrb, mrb_str_ptr(str)); tr_parse_pattern(mrb, &pat, p1, TRUE); rep = tr_parse_pattern(mrb, &rep_storage, p2, FALSE); s = RSTRING_PTR(str); len = RSTRING_LEN(str); for (i=j=0; ij) s[j] = s[i]; if (n >= 0) { flag_changed = TRUE; if (rep == NULL) { j--; } else { mrb_int c = tr_get_character(rep, RSTRING_PTR(p2), n); if (c < 0 || (squeeze && c == lastch)) { j--; continue; } if (c > 0x80) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "character (%S) out of range", mrb_fixnum_value((mrb_int)c)); } lastch = c; s[i] = (char)c; } } } tr_free_pattern(mrb, &pat); tr_free_pattern(mrb, rep); if (flag_changed) { RSTR_SET_LEN(RSTRING(str), j); RSTRING_PTR(str)[j] = 0; } return flag_changed; } /* * call-seq: * str.tr(from_str, to_str) => new_str * * Returns a copy of str with the characters in from_str replaced by the * corresponding characters in to_str. If to_str is shorter than from_str, * it is padded with its last character in order to maintain the * correspondence. * * "hello".tr('el', 'ip') #=> "hippo" * "hello".tr('aeiou', '*') #=> "h*ll*" * "hello".tr('aeiou', 'AA*') #=> "hAll*" * * Both strings may use the c1-c2 notation to denote ranges of characters, * and from_str may start with a ^, which denotes all characters except * those listed. * * "hello".tr('a-y', 'b-z') #=> "ifmmp" * "hello".tr('^aeiou', '*') #=> "*e**o" * * The backslash character \ can be used to escape ^ or - and is otherwise * ignored unless it appears at the end of a range or the end of the * from_str or to_str: * * * "hello^world".tr("\\^aeiou", "*") #=> "h*ll**w*rld" * "hello-world".tr("a\\-eo", "*") #=> "h*ll**w*rld" * * "hello\r\nworld".tr("\r", "") #=> "hello\nworld" * "hello\r\nworld".tr("\\r", "") #=> "hello\r\nwold" * "hello\r\nworld".tr("\\\r", "") #=> "hello\nworld" * * "X['\\b']".tr("X\\", "") #=> "['b']" * "X['\\b']".tr("X-\\]", "") #=> "'b'" * * Note: conversion is effective only in ASCII region. */ static mrb_value mrb_str_tr(mrb_state *mrb, mrb_value str) { mrb_value dup; mrb_value p1, p2; mrb_get_args(mrb, "SS", &p1, &p2); dup = mrb_str_dup(mrb, str); str_tr(mrb, dup, p1, p2, FALSE); return dup; } /* * call-seq: * str.tr!(from_str, to_str) -> str or nil * * Translates str in place, using the same rules as String#tr. * Returns str, or nil if no changes were made. */ static mrb_value mrb_str_tr_bang(mrb_state *mrb, mrb_value str) { mrb_value p1, p2; mrb_get_args(mrb, "SS", &p1, &p2); if (str_tr(mrb, str, p1, p2, FALSE)) { return str; } return mrb_nil_value(); } /* * call-seq: * str.tr_s(from_str, to_str) -> new_str * * Processes a copy of str as described under String#tr, then removes * duplicate characters in regions that were affected by the translation. * * "hello".tr_s('l', 'r') #=> "hero" * "hello".tr_s('el', '*') #=> "h*o" * "hello".tr_s('el', 'hx') #=> "hhxo" */ static mrb_value mrb_str_tr_s(mrb_state *mrb, mrb_value str) { mrb_value dup; mrb_value p1, p2; mrb_get_args(mrb, "SS", &p1, &p2); dup = mrb_str_dup(mrb, str); str_tr(mrb, dup, p1, p2, TRUE); return dup; } /* * call-seq: * str.tr_s!(from_str, to_str) -> str or nil * * Performs String#tr_s processing on str in place, returning * str, or nil if no changes were made. */ static mrb_value mrb_str_tr_s_bang(mrb_state *mrb, mrb_value str) { mrb_value p1, p2; mrb_get_args(mrb, "SS", &p1, &p2); if (str_tr(mrb, str, p1, p2, TRUE)) { return str; } return mrb_nil_value(); } static mrb_bool str_squeeze(mrb_state *mrb, mrb_value str, mrb_value v_pat) { struct tr_pattern pat_storage = STATIC_TR_PATTERN; struct tr_pattern *pat = NULL; mrb_int i, j; char *s; mrb_int len; mrb_bool flag_changed = FALSE; mrb_int lastch = -1; uint8_t bitmap[32]; mrb_str_modify(mrb, mrb_str_ptr(str)); if (!mrb_nil_p(v_pat)) { pat = tr_parse_pattern(mrb, &pat_storage, v_pat, TRUE); tr_compile_pattern(pat, v_pat, bitmap); tr_free_pattern(mrb, pat); } s = RSTRING_PTR(str); len = RSTRING_LEN(str); if (pat) { for (i=j=0; ij) s[j] = s[i]; if (tr_bitmap_detect(bitmap, s[i]) && s[i] == lastch) { flag_changed = TRUE; j--; } lastch = s[i]; } } else { for (i=j=0; ij) s[j] = s[i]; if (s[i] >= 0 && s[i] == lastch) { flag_changed = TRUE; j--; } lastch = s[i]; } } if (flag_changed) { RSTR_SET_LEN(RSTRING(str), j); RSTRING_PTR(str)[j] = 0; } return flag_changed; } /* * call-seq: * str.squeeze([other_str]) -> new_str * * Builds a set of characters from the other_str * parameter(s) using the procedure described for String#count. Returns a * new string where runs of the same character that occur in this set are * replaced by a single character. If no arguments are given, all runs of * identical characters are replaced by a single character. * * "yellow moon".squeeze #=> "yelow mon" * " now is the".squeeze(" ") #=> " now is the" * "putters shoot balls".squeeze("m-z") #=> "puters shot balls" */ static mrb_value mrb_str_squeeze(mrb_state *mrb, mrb_value str) { mrb_value pat = mrb_nil_value(); mrb_value dup; mrb_get_args(mrb, "|S", &pat); dup = mrb_str_dup(mrb, str); str_squeeze(mrb, dup, pat); return dup; } /* * call-seq: * str.squeeze!([other_str]) -> str or nil * * Squeezes str in place, returning either str, or nil if no * changes were made. */ static mrb_value mrb_str_squeeze_bang(mrb_state *mrb, mrb_value str) { mrb_value pat = mrb_nil_value(); mrb_get_args(mrb, "|S", &pat); if (str_squeeze(mrb, str, pat)) { return str; } return mrb_nil_value(); } static mrb_bool str_delete(mrb_state *mrb, mrb_value str, mrb_value v_pat) { struct tr_pattern pat = STATIC_TR_PATTERN; mrb_int i, j; char *s; mrb_int len; mrb_bool flag_changed = FALSE; uint8_t bitmap[32]; mrb_str_modify(mrb, mrb_str_ptr(str)); tr_parse_pattern(mrb, &pat, v_pat, TRUE); tr_compile_pattern(&pat, v_pat, bitmap); tr_free_pattern(mrb, &pat); s = RSTRING_PTR(str); len = RSTRING_LEN(str); for (i=j=0; ij) s[j] = s[i]; if (tr_bitmap_detect(bitmap, s[i])) { flag_changed = TRUE; j--; } } if (flag_changed) { RSTR_SET_LEN(RSTRING(str), j); RSTRING_PTR(str)[j] = 0; } return flag_changed; } static mrb_value mrb_str_delete(mrb_state *mrb, mrb_value str) { mrb_value pat; mrb_value dup; mrb_get_args(mrb, "S", &pat); dup = mrb_str_dup(mrb, str); str_delete(mrb, dup, pat); return dup; } static mrb_value mrb_str_delete_bang(mrb_state *mrb, mrb_value str) { mrb_value pat; mrb_get_args(mrb, "S", &pat); if (str_delete(mrb, str, pat)) { return str; } return mrb_nil_value(); } /* * call_seq: * str.count([other_str]) -> integer * * Each other_str parameter defines a set of characters to count. The * intersection of these sets defines the characters to count in str. Any * other_str that starts with a caret ^ is negated. The sequence c1-c2 * means all characters between c1 and c2. The backslash character \ can * be used to escape ^ or - and is otherwise ignored unless it appears at * the end of a sequence or the end of a other_str. */ static mrb_value mrb_str_count(mrb_state *mrb, mrb_value str) { mrb_value v_pat = mrb_nil_value(); mrb_int i; char *s; mrb_int len; mrb_int count = 0; struct tr_pattern pat = STATIC_TR_PATTERN; uint8_t bitmap[32]; mrb_get_args(mrb, "S", &v_pat); tr_parse_pattern(mrb, &pat, v_pat, TRUE); tr_compile_pattern(&pat, v_pat, bitmap); tr_free_pattern(mrb, &pat); s = RSTRING_PTR(str); len = RSTRING_LEN(str); for (i = 0; i < len; i++) { if (tr_bitmap_detect(bitmap, s[i])) count++; } return mrb_fixnum_value(count); } static mrb_value mrb_str_hex(mrb_state *mrb, mrb_value self) { return mrb_str_to_inum(mrb, self, 16, FALSE); } static mrb_value mrb_str_oct(mrb_state *mrb, mrb_value self) { return mrb_str_to_inum(mrb, self, 8, FALSE); } /* * call-seq: * string.chr -> string * * Returns a one-character string at the beginning of the string. * * a = "abcde" * a.chr #=> "a" */ static mrb_value mrb_str_chr(mrb_state *mrb, mrb_value self) { return mrb_str_substr(mrb, self, 0, 1); } static mrb_value mrb_fixnum_chr(mrb_state *mrb, mrb_value num) { mrb_int cp = mrb_fixnum(num); #ifdef MRB_UTF8_STRING char utf8[4]; mrb_int len; if (cp < 0 || 0x10FFFF < cp) { mrb_raisef(mrb, E_RANGE_ERROR, "%S out of char range", num); } if (cp < 0x80) { utf8[0] = (char)cp; len = 1; } else if (cp < 0x800) { utf8[0] = (char)(0xC0 | (cp >> 6)); utf8[1] = (char)(0x80 | (cp & 0x3F)); len = 2; } else if (cp < 0x10000) { utf8[0] = (char)(0xE0 | (cp >> 12)); utf8[1] = (char)(0x80 | ((cp >> 6) & 0x3F)); utf8[2] = (char)(0x80 | ( cp & 0x3F)); len = 3; } else { utf8[0] = (char)(0xF0 | (cp >> 18)); utf8[1] = (char)(0x80 | ((cp >> 12) & 0x3F)); utf8[2] = (char)(0x80 | ((cp >> 6) & 0x3F)); utf8[3] = (char)(0x80 | ( cp & 0x3F)); len = 4; } return mrb_str_new(mrb, utf8, len); #else char c; if (cp < 0 || 0xff < cp) { mrb_raisef(mrb, E_RANGE_ERROR, "%S out of char range", num); } c = (char)cp; return mrb_str_new(mrb, &c, 1); #endif } /* * call-seq: * string.succ -> string * * Returns next sequence of the string; * * a = "abc" * a.succ #=> "abd" */ static mrb_value mrb_str_succ_bang(mrb_state *mrb, mrb_value self) { mrb_value result; unsigned char *p, *e, *b, *t; const char *prepend; struct RString *s = mrb_str_ptr(self); mrb_int l; if (RSTRING_LEN(self) == 0) return self; mrb_str_modify(mrb, s); l = RSTRING_LEN(self); b = p = (unsigned char*) RSTRING_PTR(self); t = e = p + l; *(e--) = 0; // find trailing ascii/number while (e >= b) { if (ISALNUM(*e)) break; e--; } if (e < b) { e = p + l - 1; result = mrb_str_new_lit(mrb, ""); } else { // find leading letter of the ascii/number b = e; while (b > p) { if (!ISALNUM(*b) || (ISALNUM(*b) && *b != '9' && *b != 'z' && *b != 'Z')) break; b--; } if (!ISALNUM(*b)) b++; result = mrb_str_new(mrb, (char*) p, b - p); } while (e >= b) { if (!ISALNUM(*e)) { if (*e == 0xff) { mrb_str_cat_lit(mrb, result, "\x01"); (*e) = 0; } else (*e)++; break; } prepend = NULL; if (*e == '9') { if (e == b) prepend = "1"; *e = '0'; } else if (*e == 'z') { if (e == b) prepend = "a"; *e = 'a'; } else if (*e == 'Z') { if (e == b) prepend = "A"; *e = 'A'; } else { (*e)++; break; } if (prepend) mrb_str_cat_cstr(mrb, result, prepend); e--; } result = mrb_str_cat(mrb, result, (char*) b, t - b); l = RSTRING_LEN(result); mrb_str_resize(mrb, self, l); memcpy(RSTRING_PTR(self), RSTRING_PTR(result), l); return self; } static mrb_value mrb_str_succ(mrb_state *mrb, mrb_value self) { mrb_value str; str = mrb_str_dup(mrb, self); mrb_str_succ_bang(mrb, str); return str; } #ifdef MRB_UTF8_STRING static const char utf8len_codepage_zero[256] = { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0, }; static mrb_int utf8code(unsigned char* p) { mrb_int len; if (p[0] < 0x80) return p[0]; len = utf8len_codepage_zero[p[0]]; if (len > 1 && (p[1] & 0xc0) == 0x80) { if (len == 2) return ((p[0] & 0x1f) << 6) + (p[1] & 0x3f); if ((p[2] & 0xc0) == 0x80) { if (len == 3) return ((p[0] & 0x0f) << 12) + ((p[1] & 0x3f) << 6) + (p[2] & 0x3f); if ((p[3] & 0xc0) == 0x80) { if (len == 4) return ((p[0] & 0x07) << 18) + ((p[1] & 0x3f) << 12) + ((p[2] & 0x3f) << 6) + (p[3] & 0x3f); if ((p[4] & 0xc0) == 0x80) { if (len == 5) return ((p[0] & 0x03) << 24) + ((p[1] & 0x3f) << 18) + ((p[2] & 0x3f) << 12) + ((p[3] & 0x3f) << 6) + (p[4] & 0x3f); if ((p[5] & 0xc0) == 0x80 && len == 6) return ((p[0] & 0x01) << 30) + ((p[1] & 0x3f) << 24) + ((p[2] & 0x3f) << 18) + ((p[3] & 0x3f) << 12) + ((p[4] & 0x3f) << 6) + (p[5] & 0x3f); } } } } return p[0]; } static mrb_value mrb_str_ord(mrb_state* mrb, mrb_value str) { if (RSTRING_LEN(str) == 0) mrb_raise(mrb, E_ARGUMENT_ERROR, "empty string"); return mrb_fixnum_value(utf8code((unsigned char*) RSTRING_PTR(str))); } #else static mrb_value mrb_str_ord(mrb_state* mrb, mrb_value str) { if (RSTRING_LEN(str) == 0) mrb_raise(mrb, E_ARGUMENT_ERROR, "empty string"); return mrb_fixnum_value((unsigned char)RSTRING_PTR(str)[0]); } #endif /* * call-seq: * str.delete_prefix!(prefix) -> self or nil * * Deletes leading prefix from str, returning * nil if no change was made. * * "hello".delete_prefix!("hel") #=> "lo" * "hello".delete_prefix!("llo") #=> nil */ static mrb_value mrb_str_del_prefix_bang(mrb_state *mrb, mrb_value self) { mrb_int plen, slen; char *ptr, *s; struct RString *str = RSTRING(self); mrb_get_args(mrb, "s", &ptr, &plen); slen = RSTR_LEN(str); if (plen > slen) return mrb_nil_value(); s = RSTR_PTR(str); if (memcmp(s, ptr, plen) != 0) return mrb_nil_value(); if (!MRB_FROZEN_P(str) && (RSTR_SHARED_P(str) || RSTR_FSHARED_P(str))) { str->as.heap.ptr += plen; } else { mrb_str_modify(mrb, str); s = RSTR_PTR(str); memmove(s, s+plen, slen-plen); } RSTR_SET_LEN(str, slen-plen); return self; } /* * call-seq: * str.delete_prefix(prefix) -> new_str * * Returns a copy of str with leading prefix deleted. * * "hello".delete_prefix("hel") #=> "lo" * "hello".delete_prefix("llo") #=> "hello" */ static mrb_value mrb_str_del_prefix(mrb_state *mrb, mrb_value self) { mrb_int plen, slen; char *ptr; mrb_get_args(mrb, "s", &ptr, &plen); slen = RSTRING_LEN(self); if (plen > slen) return mrb_str_dup(mrb, self); if (memcmp(RSTRING_PTR(self), ptr, plen) != 0) return mrb_str_dup(mrb, self); return mrb_str_substr(mrb, self, plen, slen-plen); } /* * call-seq: * str.delete_suffix!(suffix) -> self or nil * * Deletes trailing suffix from str, returning * nil if no change was made. * * "hello".delete_suffix!("llo") #=> "he" * "hello".delete_suffix!("hel") #=> nil */ static mrb_value mrb_str_del_suffix_bang(mrb_state *mrb, mrb_value self) { mrb_int plen, slen; char *ptr, *s; struct RString *str = RSTRING(self); mrb_get_args(mrb, "s", &ptr, &plen); slen = RSTR_LEN(str); if (plen > slen) return mrb_nil_value(); s = RSTR_PTR(str); if (memcmp(s+slen-plen, ptr, plen) != 0) return mrb_nil_value(); if (!MRB_FROZEN_P(str) && (RSTR_SHARED_P(str) || RSTR_FSHARED_P(str))) { /* no need to modify string */ } else { mrb_str_modify(mrb, str); } RSTR_SET_LEN(str, slen-plen); return self; } /* * call-seq: * str.delete_suffix(suffix) -> new_str * * Returns a copy of str with leading suffix deleted. * * "hello".delete_suffix("hel") #=> "lo" * "hello".delete_suffix("llo") #=> "hello" */ static mrb_value mrb_str_del_suffix(mrb_state *mrb, mrb_value self) { mrb_int plen, slen; char *ptr; mrb_get_args(mrb, "s", &ptr, &plen); slen = RSTRING_LEN(self); if (plen > slen) return mrb_str_dup(mrb, self); if (memcmp(RSTRING_PTR(self)+slen-plen, ptr, plen) != 0) return mrb_str_dup(mrb, self); return mrb_str_substr(mrb, self, 0, slen-plen); } static mrb_value mrb_str_lines(mrb_state *mrb, mrb_value self) { mrb_value result; int ai; mrb_int len; char *b = RSTRING_PTR(self); char *p = b, *t; char *e = b + RSTRING_LEN(self); mrb_get_args(mrb, ""); result = mrb_ary_new(mrb); ai = mrb_gc_arena_save(mrb); while (p < e) { t = p; while (p < e && *p != '\n') p++; if (*p == '\n') p++; len = (mrb_int) (p - t); mrb_ary_push(mrb, result, mrb_str_new(mrb, t, len)); mrb_gc_arena_restore(mrb, ai); } return result; } void mrb_mruby_string_ext_gem_init(mrb_state* mrb) { struct RClass * s = mrb->string_class; mrb_define_method(mrb, s, "dump", mrb_str_dump, MRB_ARGS_NONE()); mrb_define_method(mrb, s, "getbyte", mrb_str_getbyte, MRB_ARGS_REQ(1)); mrb_define_method(mrb, s, "setbyte", mrb_str_setbyte, MRB_ARGS_REQ(2)); mrb_define_method(mrb, s, "byteslice", mrb_str_byteslice, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1)); mrb_define_method(mrb, s, "swapcase!", mrb_str_swapcase_bang, MRB_ARGS_NONE()); mrb_define_method(mrb, s, "swapcase", mrb_str_swapcase, MRB_ARGS_NONE()); mrb_define_method(mrb, s, "concat", mrb_str_concat_m, MRB_ARGS_REQ(1)); mrb_define_method(mrb, s, "<<", mrb_str_concat_m, MRB_ARGS_REQ(1)); mrb_define_method(mrb, s, "count", mrb_str_count, MRB_ARGS_REQ(1)); mrb_define_method(mrb, s, "tr", mrb_str_tr, MRB_ARGS_REQ(2)); mrb_define_method(mrb, s, "tr!", mrb_str_tr_bang, MRB_ARGS_REQ(2)); mrb_define_method(mrb, s, "tr_s", mrb_str_tr_s, MRB_ARGS_REQ(2)); mrb_define_method(mrb, s, "tr_s!", mrb_str_tr_s_bang, MRB_ARGS_REQ(2)); mrb_define_method(mrb, s, "squeeze", mrb_str_squeeze, MRB_ARGS_OPT(1)); mrb_define_method(mrb, s, "squeeze!", mrb_str_squeeze_bang, MRB_ARGS_OPT(1)); mrb_define_method(mrb, s, "delete", mrb_str_delete, MRB_ARGS_REQ(1)); mrb_define_method(mrb, s, "delete!", mrb_str_delete_bang, MRB_ARGS_REQ(1)); mrb_define_method(mrb, s, "start_with?", mrb_str_start_with, MRB_ARGS_REST()); mrb_define_method(mrb, s, "end_with?", mrb_str_end_with, MRB_ARGS_REST()); mrb_define_method(mrb, s, "hex", mrb_str_hex, MRB_ARGS_NONE()); mrb_define_method(mrb, s, "oct", mrb_str_oct, MRB_ARGS_NONE()); mrb_define_method(mrb, s, "chr", mrb_str_chr, MRB_ARGS_NONE()); mrb_define_method(mrb, s, "succ", mrb_str_succ, MRB_ARGS_NONE()); mrb_define_method(mrb, s, "succ!", mrb_str_succ_bang, MRB_ARGS_NONE()); mrb_define_alias(mrb, s, "next", "succ"); mrb_define_alias(mrb, s, "next!", "succ!"); mrb_define_method(mrb, s, "ord", mrb_str_ord, MRB_ARGS_NONE()); mrb_define_method(mrb, s, "delete_prefix!", mrb_str_del_prefix_bang, MRB_ARGS_REQ(1)); mrb_define_method(mrb, s, "delete_prefix", mrb_str_del_prefix, MRB_ARGS_REQ(1)); mrb_define_method(mrb, s, "delete_suffix!", mrb_str_del_suffix_bang, MRB_ARGS_REQ(1)); mrb_define_method(mrb, s, "delete_suffix", mrb_str_del_suffix, MRB_ARGS_REQ(1)); mrb_define_method(mrb, s, "__lines", mrb_str_lines, MRB_ARGS_NONE()); mrb_define_method(mrb, mrb->fixnum_class, "chr", mrb_fixnum_chr, MRB_ARGS_NONE()); } void mrb_mruby_string_ext_gem_final(mrb_state* mrb) { } mruby-2.0.0/mrbgems/mruby-string-ext/test/000077500000000000000000000000001340361412400205205ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-string-ext/test/string.rb000066400000000000000000000425541340361412400223650ustar00rootroot00000000000000# coding: utf-8 ## # String(Ext) Test UTF8STRING = ("\343\201\202".size == 1) assert('String#getbyte') do str1 = "hello" bytes1 = [104, 101, 108, 108, 111] assert_equal bytes1[0], str1.getbyte(0) assert_equal bytes1[-1], str1.getbyte(-1) assert_equal bytes1[6], str1.getbyte(6) str2 = "\xFF" bytes2 = [0xFF] assert_equal bytes2[0], str2.getbyte(0) end assert('String#setbyte') do str1 = "hello" h = "H".getbyte(0) str1.setbyte(0, h) assert_equal(h, str1.getbyte(0)) assert_equal("Hello", str1) end assert('String#byteslice') do str1 = "hello" assert_equal("e", str1.byteslice(1)) assert_equal("o", str1.byteslice(-1)) assert_equal("ell", str1.byteslice(1..3)) assert_equal("el", str1.byteslice(1...3)) end assert('String#dump') do ("\1" * 100).dump # should not raise an exception - regress #1210 "\0".inspect == "\"\\000\"" and "foo".dump == "\"foo\"" end assert('String#strip') do s = " abc " "".strip == "" and " \t\r\n\f\v".strip == "" and "\0a\0".strip == "\0a" and "abc".strip == "abc" and " abc".strip == "abc" and "abc ".strip == "abc" and " abc ".strip == "abc" and s == " abc " end assert('String#lstrip') do s = " abc " s.lstrip "".lstrip == "" and " \t\r\n\f\v".lstrip == "" and "\0a\0".lstrip == "\0a\0" and "abc".lstrip == "abc" and " abc".lstrip == "abc" and "abc ".lstrip == "abc " and " abc ".lstrip == "abc " and s == " abc " end assert('String#rstrip') do s = " abc " s.rstrip "".rstrip == "" and " \t\r\n\f\v".rstrip == "" and "\0a\0".rstrip == "\0a" and "abc".rstrip == "abc" and " abc".rstrip == " abc" and "abc ".rstrip == "abc" and " abc ".rstrip == " abc" and s == " abc " end assert('String#strip!') do s = " abc " t = "abc" s.strip! == "abc" and s == "abc" and t.strip! == nil end assert('String#lstrip!') do s = " abc " t = "abc " s.lstrip! == "abc " and s == "abc " and t.lstrip! == nil end assert('String#rstrip!') do s = " abc " t = " abc" s.rstrip! == " abc" and s == " abc" and t.rstrip! == nil end assert('String#swapcase') do assert_equal "hELLO", "Hello".swapcase assert_equal "CyBeR_pUnK11", "cYbEr_PuNk11".swapcase end assert('String#swapcase!') do s = "Hello" t = s.clone t.swapcase! assert_equal s.swapcase, t end assert('String#concat') do assert_equal "Hello World!", "Hello " << "World" << 33 assert_equal "Hello World!", "Hello ".concat("World").concat(33) assert_raise(TypeError) { "".concat(Object.new) } end assert('String#casecmp') do assert_equal 1, "abcdef".casecmp("abcde") assert_equal 0, "aBcDeF".casecmp("abcdef") assert_equal(-1, "abcdef".casecmp("abcdefg")) assert_equal 0, "abcdef".casecmp("ABCDEF") end assert('String#count') do s = "abccdeff123" assert_equal 0, s.count("") assert_equal 1, s.count("a") assert_equal 2, s.count("ab") assert_equal 9, s.count("^c") assert_equal 8, s.count("a-z") assert_equal 4, s.count("a0-9") end assert('String#tr') do assert_equal "ABC", "abc".tr('a-z', 'A-Z') assert_equal "hippo", "hello".tr('el', 'ip') assert_equal "Ruby", "Lisp".tr("Lisp", "Ruby") assert_equal "*e**o", "hello".tr('^aeiou', '*') assert_equal "heo", "hello".tr('l', '') end assert('String#tr!') do s = "abcdefghijklmnopqR" assert_equal "ab12222hijklmnopqR", s.tr!("cdefg", "12") assert_equal "ab12222hijklmnopqR", s end assert('String#tr_s') do assert_equal "hero", "hello".tr_s('l', 'r') assert_equal "h*o", "hello".tr_s('el', '*') assert_equal "hhxo", "hello".tr_s('el', 'hx') end assert('String#tr_s!') do s = "hello" assert_equal "hero", s.tr_s!('l', 'r') assert_equal "hero", s assert_nil s.tr_s!('l', 'r') end assert('String#squeeze') do assert_equal "yelow mon", "yellow moon".squeeze assert_equal " now is the", " now is the".squeeze(" ") assert_equal "puters shot balls", "putters shoot balls".squeeze("m-z") end assert('String#squeeze!') do s = " now is the" assert_equal " now is the", s.squeeze!(" ") assert_equal " now is the", s end assert('String#delete') do assert_equal "he", "hello".delete("lo") assert_equal "hll", "hello".delete("aeiou") assert_equal "ll", "hello".delete("^l") assert_equal "ho", "hello".delete("ej-m") end assert('String#delete!') do s = "hello" assert_equal "he", s.delete!("lo") assert_equal "he", s assert_nil s.delete!("lz") end assert('String#start_with?') do assert_true "hello".start_with?("heaven", "hell") assert_true !"hello".start_with?("heaven", "paradise") assert_true !"h".start_with?("heaven", "hell") assert_raise TypeError do "hello".start_with?(true) end end assert('String#end_with?') do assert_true "string".end_with?("ing", "mng") assert_true !"string".end_with?("str", "tri") assert_true !"ng".end_with?("ing", "mng") assert_raise TypeError do "hello".end_with?(true) end end assert('String#partition') do assert_equal ["a", "x", "axa"], "axaxa".partition("x") assert_equal ["aaaaa", "", ""], "aaaaa".partition("x") assert_equal ["", "", "aaaaa"], "aaaaa".partition("") assert_equal ["", "a", "aaaa"], "aaaaa".partition("a") assert_equal ["aaaa", "b", ""], "aaaab".partition("b") assert_equal ["", "b", "aaaa"], "baaaa".partition("b") assert_equal ["", "", ""], "".partition("a") end assert('String#rpartition') do assert_equal ["axa", "x", "a"], "axaxa".rpartition("x") assert_equal ["", "", "aaaaa"], "aaaaa".rpartition("x") assert_equal ["aaaaa", "", ""], "aaaaa".rpartition("") assert_equal ["aaaa", "a", ""], "aaaaa".rpartition("a") assert_equal ["aaaa", "b", ""], "aaaab".rpartition("b") assert_equal ["", "b", "aaaa"], "baaaa".rpartition("b") assert_equal ["", "", ""], "".rpartition("a") end assert('String#hex') do assert_equal 16, "10".hex assert_equal 255, "ff".hex assert_equal 16, "0x10".hex assert_equal (-16), "-0x10".hex assert_equal 0, "xyz".hex assert_equal 16, "10z".hex assert_equal 16, "1_0".hex assert_equal 0, "".hex end assert('String#oct') do assert_equal 8, "10".oct assert_equal 7, "7".oct assert_equal 0, "8".oct assert_equal 0, "9".oct assert_equal 0, "xyz".oct assert_equal 8, "10z".oct assert_equal 8, "1_0".oct assert_equal 8, "010".oct assert_equal (-8), "-10".oct end assert('String#chr') do assert_equal "a", "abcde".chr # test Fixnum#chr as well assert_equal "a", 97.chr end assert('String#lines') do assert_equal ["Hel\n", "lo\n", "World!"], "Hel\nlo\nWorld!".lines assert_equal ["Hel\n", "lo\n", "World!\n"], "Hel\nlo\nWorld!\n".lines assert_equal ["\n", "\n", "\n"], "\n\n\n".lines assert_equal [], "".lines end assert('String#clear') do # embed string s = "foo" assert_equal("", s.clear) assert_equal("", s) # not embed string and not shared string s = "foo" * 100 a = s assert_equal("", s.clear) assert_equal("", s) assert_equal("", a) # shared string s = "foo" * 100 a = s[10, 90] # create shared string assert_equal("", s.clear) # clear assert_equal("", s) # s is cleared assert_not_equal("", a) # a should not be affected end assert('String#slice!') do a = "AooBar" b = a.dup assert_equal "A", a.slice!(0) assert_equal "AooBar", b a = "FooBar" assert_equal "r", a.slice!(-1) assert_equal "FooBa", a a = "FooBar" assert_nil a.slice!(6) assert_nil a.slice!(-7) assert_equal "FooBar", a a = "FooBar" assert_equal "Foo", a.slice!(0, 3) assert_equal "Bar", a a = "FooBar" assert_equal "Bar", a.slice!(-3, 3) assert_equal "Foo", a a = "FooBar" assert_equal "", a.slice!(6, 2) assert_equal "FooBar", a a = "FooBar" assert_nil a.slice!(-7,10) assert_equal "FooBar", a a = "FooBar" assert_equal "Foo", a.slice!(0..2) assert_equal "Bar", a a = "FooBar" assert_equal "Bar", a.slice!(-3..-1) assert_equal "Foo", a a = "FooBar" assert_equal "", a.slice!(6..2) assert_equal "FooBar", a a = "FooBar" assert_nil a.slice!(-10..-7) assert_equal "FooBar", a a = "FooBar" assert_equal "Foo", a.slice!("Foo") assert_equal "Bar", a a = "FooBar" assert_nil a.slice!("xyzzy") assert_equal "FooBar", a assert_raise(ArgumentError) { "foo".slice! } end assert('String#succ') do assert_equal "", "".succ assert_equal "1", "0".succ assert_equal "10", "9".succ assert_equal "01", "00".succ assert_equal "a1", "a0".succ assert_equal "A1", "A0".succ assert_equal "10", "09".succ assert_equal "b0", "a9".succ assert_equal "B0", "A9".succ assert_equal "b", "a".succ assert_equal "aa", "z".succ assert_equal "ab", "aa".succ assert_equal "Ab", "Aa".succ assert_equal "0b", "0a".succ assert_equal "ba", "az".succ assert_equal "Ba", "Az".succ assert_equal "1a", "0z".succ assert_equal "B", "A".succ assert_equal "AA", "Z".succ assert_equal "AB", "AA".succ assert_equal "aB", "aA".succ assert_equal "0B", "0A".succ assert_equal "BA", "AZ".succ assert_equal "bA", "aZ".succ assert_equal "1A", "0Z".succ assert_equal ".", "-".succ assert_equal "\x01\x00", "\xff".succ assert_equal "-b", "-a".succ assert_equal "-aa", "-z".succ assert_equal "-a-b-", "-a-a-".succ assert_equal "-b-", "-a-".succ assert_equal "-aa-", "-z-".succ assert_equal "あb", "あa".succ assert_equal "あba", "あaz".succ a = ""; a.succ! assert_equal "", a a = "0"; a.succ! assert_equal "1", a a = "9"; a.succ! assert_equal "10", a a = "00"; a.succ! assert_equal "01", a a = "a0"; a.succ! assert_equal "a1", a a = "A0"; a.succ! assert_equal "A1", a a = "09"; a.succ! assert_equal "10", a a = "a9"; a.succ! assert_equal "b0", a a = "A9"; a.succ! assert_equal "B0", a a = "a"; a.succ! assert_equal "b", a a = "z"; a.succ! assert_equal "aa", a a = "aa"; a.succ! assert_equal "ab", a a = "Aa"; a.succ! assert_equal "Ab", a a = "0a"; a.succ! assert_equal "0b", a a = "az"; a.succ! assert_equal "ba", a a = "Az"; a.succ! assert_equal "Ba", a a = "0z"; a.succ! assert_equal "1a", a a = "A"; a.succ! assert_equal "B", a a = "Z"; a.succ! assert_equal "AA", a a = "AA"; a.succ! assert_equal "AB", a a = "aA"; a.succ! assert_equal "aB", a a = "0A"; a.succ! assert_equal "0B", a a = "AZ"; a.succ! assert_equal "BA", a a = "aZ"; a.succ! assert_equal "bA", a a = "0Z"; a.succ! assert_equal "1A", a a = "-"; a.succ! assert_equal ".", a a = "\xff"; a.succ! assert_equal "\x01\x00", a a = "-a"; a.succ! assert_equal "-b", a a = "-z"; a.succ! assert_equal "-aa", a a = "-a-a-"; a.succ! assert_equal "-a-b-", a a = "-a-"; a.succ! assert_equal "-b-", a a = "-z-"; a.succ! assert_equal "-aa-", a a = "あb"; a.succ! assert_equal "あc", a a = "あaz"; a.succ! assert_equal "あba", a end assert('String#next') do assert_equal "01", "00".next a = "00"; a.next! assert_equal "01", a end assert('String#insert') do assert_equal "Xabcd", "abcd".insert(0, 'X') assert_equal "abcXd", "abcd".insert(3, 'X') assert_equal "abcdX", "abcd".insert(4, 'X') assert_equal "abXcd", "abcd".insert(-3, 'X') assert_equal "abcdX", "abcd".insert(-1, 'X') assert_raise(IndexError) { "abcd".insert(5, 'X') } assert_raise(IndexError) { "abcd".insert(-6, 'X') } a = "abcd" a.insert(0, 'X') assert_equal "Xabcd", a end assert('String#prepend') do a = "world" assert_equal "hello world", a.prepend("hello ") assert_equal "hello world", a end assert('String#ljust') do assert_equal "hello", "hello".ljust(4) assert_equal "hello ", "hello".ljust(20) assert_equal 20, "hello".ljust(20).length assert_equal "hello123412341234123", "hello".ljust(20, '1234') assert_equal "hello", "hello".ljust(-3) end assert('String#rjust') do assert_equal "hello", "hello".rjust(4) assert_equal " hello", "hello".rjust(20) assert_equal 20, "hello".rjust(20).length assert_equal "123412341234123hello", "hello".rjust(20, '1234') assert_equal "hello", "hello".rjust(-3) end if UTF8STRING assert('String#ljust with UTF8') do assert_equal "helloん ", "helloん".ljust(20) assert_equal "helloó ", "helloó".ljust(34) assert_equal 34, "helloó".ljust(34).length assert_equal "helloんんんんんんんんんんんんんん", "hello".ljust(19, 'ん') assert_equal "helloんんんんんんんんんんんんんんん", "hello".ljust(20, 'ん') end assert('String#rjust with UTF8') do assert_equal " helloん", "helloん".rjust(20) assert_equal " helloó", "helloó".rjust(34) # assert_equal 34, "helloó".rjust(34).length assert_equal "んんんんんんんんんんんんんんhello", "hello".rjust(19, 'ん') assert_equal "んんんんんんんんんんんんんんんhello", "hello".rjust(20, 'ん') end assert('UTF8 byte counting') do ret = ' ' ret[-6..-1] = "helloó" assert_equal 34, ret.length end end assert('String#ljust should not change string') do a = "hello" a.ljust(20) assert_equal "hello", a end assert('String#rjust should not change string') do a = "hello" a.rjust(20) assert_equal "hello", a end assert('String#ljust should raise on zero width padding') do assert_raise(ArgumentError) { "foo".ljust(10, '') } end assert('String#rjust should raise on zero width padding') do assert_raise(ArgumentError) { "foo".rjust(10, '') } end assert('String#upto') do assert_equal %w(a8 a9 b0 b1 b2 b3 b4 b5 b6), "a8".upto("b6").to_a assert_equal ["9", "10", "11"], "9".upto("11").to_a assert_equal [], "25".upto("5").to_a assert_equal ["07", "08", "09", "10", "11"], "07".upto("11").to_a if UTF8STRING assert_equal ["あ", "ぃ", "い", "ぅ", "う", "ぇ", "え", "ぉ", "お"], "あ".upto("お").to_a end assert_equal ["9", ":", ";", "<", "=", ">", "?", "@", "A"], "9".upto("A").to_a a = "aa" start = "aa" count = 0 assert_equal("aa", a.upto("zz") {|s| assert_equal(start, s) start.succ! count += 1 }) assert_equal(676, count) a = "a" start = "a" count = 0 assert_equal("a", a.upto("a") {|s| assert_equal(start, s) start.succ! count += 1 }) assert_equal(1, count) a = "a" start = "a" count = 0 assert_equal("a", a.upto("b", true) {|s| assert_equal(start, s) start.succ! count += 1 }) assert_equal(1, count) a = "0" start = "0" count = 0 assert_equal("0", a.upto("0") {|s| assert_equal(start, s) start.succ! count += 1 }) assert_equal(1, count) a = "0" start = "0" count = 0 assert_equal("0", a.upto("-1") {|s| assert_equal(start, s) start.succ! count += 1 }) assert_equal(0, count) a = "-1" start = "-1" count = 0 assert_equal("-1", a.upto("-2") {|s| assert_equal(start, s) start.succ! count += 1 }) assert_equal(2, count) assert_raise(TypeError) { "a".upto(:c) {} } end assert('String#ord') do got = "hello!".split('').map {|x| x.ord} expect = [104, 101, 108, 108, 111, 33] unless UTF8STRING got << "\xff".ord expect << 0xff end assert_equal expect, got end assert('String#ord(UTF-8)') do got = "こんにちは世界!".split('').map {|x| x.ord} expect = [0x3053,0x3093,0x306b,0x3061,0x306f,0x4e16,0x754c,0x21] assert_equal expect, got end if UTF8STRING assert('String#chr') do assert_equal "h", "hello!".chr end assert('String#chr(UTF-8)') do assert_equal "こ", "こんにちは世界!".chr end if UTF8STRING assert('String#chars') do expect = ["h", "e", "l", "l", "o", "!"] assert_equal expect, "hello!".chars s = "" "hello!".chars do |x| s += x end assert_equal "hello!", s end assert('String#chars(UTF-8)') do expect = ['こ', 'ん', 'に', 'ち', 'は', '世', '界', '!'] assert_equal expect, "こんにちは世界!".chars s = "" "こんにちは世界!".chars do |x| s += x end assert_equal "こんにちは世界!", s end if UTF8STRING assert('String#each_char') do s = "" "hello!".each_char do |x| s += x end assert_equal "hello!", s end assert('String#each_char(UTF-8)') do s = "" "こんにちは世界!".each_char do |x| s += x end assert_equal "こんにちは世界!", s end if UTF8STRING assert('String#codepoints') do expect = [104, 101, 108, 108, 111, 33] assert_equal expect, "hello!".codepoints cp = [] "hello!".codepoints do |x| cp << x end assert_equal expect, cp end assert('String#codepoints(UTF-8)') do expect = [12371, 12435, 12395, 12385, 12399, 19990, 30028, 33] assert_equal expect, "こんにちは世界!".codepoints cp = [] "こんにちは世界!".codepoints do |x| cp << x end assert_equal expect, cp end if UTF8STRING assert('String#each_codepoint') do expect = [104, 101, 108, 108, 111, 33] cp = [] "hello!".each_codepoint do |x| cp << x end assert_equal expect, cp end assert('String#each_codepoint(UTF-8)') do expect = [12371, 12435, 12395, 12385, 12399, 19990, 30028, 33] cp = [] "こんにちは世界!".each_codepoint do |x| cp << x end assert_equal expect, cp end if UTF8STRING assert('String#delete_prefix') do assert_equal "llo", "hello".delete_prefix("he") assert_equal "hello", "hello".delete_prefix("llo") assert_equal "llo", "hello".delete_prefix!("he") assert_nil "hello".delete_prefix!("llo") end assert('String#delete_suffix') do assert_equal "he", "hello".delete_suffix("llo") assert_equal "hello", "hello".delete_suffix("he") assert_equal "he", "hello".delete_suffix!("llo") assert_nil "hello".delete_suffix!("he") end mruby-2.0.0/mrbgems/mruby-struct/000077500000000000000000000000001340361412400167615ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-struct/mrbgem.rake000066400000000000000000000002401340361412400210720ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-struct') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'standard Struct class' end mruby-2.0.0/mrbgems/mruby-struct/mrblib/000077500000000000000000000000001340361412400202305ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-struct/mrblib/struct.rb000066400000000000000000000037331340361412400221070ustar00rootroot00000000000000## # Struct # # ISO 15.2.18 if Object.const_defined?(:Struct) class Struct ## # Calls the given block for each element of +self+ # and pass the respective element. # # ISO 15.2.18.4.4 def each(&block) self.class.members.each{|field| block.call(self[field]) } self end ## # Calls the given block for each element of +self+ # and pass the name and value of the respectiev # element. # # ISO 15.2.18.4.5 def each_pair(&block) self.class.members.each{|field| block.call(field.to_sym, self[field]) } self end ## # Calls the given block for each element of +self+ # and returns an array with all elements of which # block is not false. # # ISO 15.2.18.4.7 def select(&block) ary = [] self.class.members.each{|field| val = self[field] ary.push(val) if block.call(val) } ary end def _inspect name = self.class.to_s if name[0] == "#" str = "#" end ## # call-seq: # struct.to_s -> string # struct.inspect -> string # # Describe the contents of this struct in a string. # # 15.2.18.4.10(x) # def inspect begin self._inspect rescue SystemStackError "#" end end ## # 15.2.18.4.11(x) # alias to_s inspect end ## # call-seq: # hsh.dig(key,...) -> object # # Extracts the nested value specified by the sequence of key # objects by calling +dig+ at each step, returning +nil+ if any # intermediate step is +nil+. # def dig(idx,*args) n = self[idx] if args.size > 0 n&.dig(*args) else n end end end mruby-2.0.0/mrbgems/mruby-struct/src/000077500000000000000000000000001340361412400175505ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-struct/src/struct.c000066400000000000000000000501641340361412400212460ustar00rootroot00000000000000/* ** struct.c - Struct class ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #include #include #define RSTRUCT_LEN(st) RARRAY_LEN(st) #define RSTRUCT_PTR(st) RARRAY_PTR(st) static struct RClass * struct_class(mrb_state *mrb) { return mrb_class_get(mrb, "Struct"); } static inline mrb_value struct_ivar_get(mrb_state *mrb, mrb_value cls, mrb_sym id) { struct RClass* c = mrb_class_ptr(cls); struct RClass* sclass = struct_class(mrb); mrb_value ans; for (;;) { ans = mrb_iv_get(mrb, mrb_obj_value(c), id); if (!mrb_nil_p(ans)) return ans; c = c->super; if (c == sclass || c == 0) return mrb_nil_value(); } } static mrb_value struct_s_members(mrb_state *mrb, struct RClass *klass) { mrb_value members = struct_ivar_get(mrb, mrb_obj_value(klass), mrb_intern_lit(mrb, "__members__")); if (mrb_nil_p(members)) { mrb_raise(mrb, E_TYPE_ERROR, "uninitialized struct"); } if (!mrb_array_p(members)) { mrb_raise(mrb, E_TYPE_ERROR, "corrupted struct"); } return members; } static mrb_value struct_members(mrb_state *mrb, mrb_value s) { mrb_value members = struct_s_members(mrb, mrb_obj_class(mrb, s)); if (!mrb_array_p(s)) { mrb_raise(mrb, E_TYPE_ERROR, "corrupted struct"); } if (RSTRUCT_LEN(s) != RARRAY_LEN(members)) { if (RSTRUCT_LEN(s) == 0) { /* probably uninitialized */ mrb_ary_resize(mrb, s, RARRAY_LEN(members)); } else { mrb_raisef(mrb, E_TYPE_ERROR, "struct size differs (%S required %S given)", mrb_fixnum_value(RARRAY_LEN(members)), mrb_fixnum_value(RSTRUCT_LEN(s))); } } return members; } static mrb_value mrb_struct_s_members_m(mrb_state *mrb, mrb_value klass) { mrb_value members, ary; members = struct_s_members(mrb, mrb_class_ptr(klass)); ary = mrb_ary_new_capa(mrb, RARRAY_LEN(members)); mrb_ary_replace(mrb, ary, members); return ary; } static void mrb_struct_modify(mrb_state *mrb, mrb_value strct) { if (MRB_FROZEN_P(mrb_basic_ptr(strct))) { mrb_raise(mrb, E_FROZEN_ERROR, "can't modify frozen struct"); } mrb_write_barrier(mrb, mrb_basic_ptr(strct)); } /* 15.2.18.4.6 */ /* * call-seq: * struct.members -> array * * Returns an array of strings representing the names of the instance * variables. * * Customer = Struct.new(:name, :address, :zip) * joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) * joe.members #=> [:name, :address, :zip] */ static mrb_value mrb_struct_members(mrb_state *mrb, mrb_value obj) { return mrb_struct_s_members_m(mrb, mrb_obj_value(mrb_obj_class(mrb, obj))); } static mrb_value mrb_struct_ref(mrb_state *mrb, mrb_value obj) { mrb_int i = mrb_fixnum(mrb_proc_cfunc_env_get(mrb, 0)); mrb_value *ptr = RSTRUCT_PTR(obj); if (!ptr) return mrb_nil_value(); return ptr[i]; } static mrb_sym mrb_id_attrset(mrb_state *mrb, mrb_sym id) { const char *name; char *buf; mrb_int len; mrb_sym mid; name = mrb_sym2name_len(mrb, id, &len); buf = (char *)mrb_malloc(mrb, (size_t)len+2); memcpy(buf, name, (size_t)len); buf[len] = '='; buf[len+1] = '\0'; mid = mrb_intern(mrb, buf, len+1); mrb_free(mrb, buf); return mid; } static mrb_value mrb_struct_set_m(mrb_state *mrb, mrb_value obj) { mrb_int i = mrb_fixnum(mrb_proc_cfunc_env_get(mrb, 0)); mrb_value *ptr; mrb_value val; mrb_get_args(mrb, "o", &val); mrb_struct_modify(mrb, obj); ptr = RSTRUCT_PTR(obj); if (ptr == NULL || i >= RSTRUCT_LEN(obj)) { mrb_ary_set(mrb, obj, i, val); } else { ptr[i] = val; } return val; } static mrb_bool is_local_id(mrb_state *mrb, const char *name) { if (!name) return FALSE; return !ISUPPER(name[0]); } static mrb_bool is_const_id(mrb_state *mrb, const char *name) { if (!name) return FALSE; return ISUPPER(name[0]); } static void make_struct_define_accessors(mrb_state *mrb, mrb_value members, struct RClass *c) { const mrb_value *ptr_members = RARRAY_PTR(members); mrb_int i; mrb_int len = RARRAY_LEN(members); int ai = mrb_gc_arena_save(mrb); for (i=0; ibasic.c->super = c->c; */ make_struct_define_accessors(mrb, members, c); return nstr; } /* 15.2.18.3.1 */ /* * call-seq: * Struct.new( [aString] [, aSym]+> ) -> StructClass * StructClass.new(arg, ...) -> obj * StructClass[arg, ...] -> obj * * Creates a new class, named by aString, containing accessor * methods for the given symbols. If the name aString is * omitted, an anonymous structure class will be created. Otherwise, * the name of this struct will appear as a constant in class * Struct, so it must be unique for all * Structs in the system and should start with a capital * letter. Assigning a structure class to a constant effectively gives * the class the name of the constant. * * Struct::new returns a new Class object, * which can then be used to create specific instances of the new * structure. The number of actual parameters must be * less than or equal to the number of attributes defined for this * class; unset parameters default to nil. Passing too many * parameters will raise an ArgumentError. * * The remaining methods listed in this section (class and instance) * are defined for this generated class. * * # Create a structure with a name in Struct * Struct.new("Customer", :name, :address) #=> Struct::Customer * Struct::Customer.new("Dave", "123 Main") #=> # * * # Create a structure named by its constant * Customer = Struct.new(:name, :address) #=> Customer * Customer.new("Dave", "123 Main") #=> # */ static mrb_value mrb_struct_s_def(mrb_state *mrb, mrb_value klass) { mrb_value name, rest; mrb_value *pargv; mrb_int argcnt; mrb_int i; mrb_value b, st; mrb_sym id; mrb_value *argv; mrb_int argc; name = mrb_nil_value(); mrb_get_args(mrb, "*&", &argv, &argc, &b); if (argc == 0) { /* special case to avoid crash */ mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong number of arguments"); } else { pargv = argv; argcnt = argc; if (argc > 0) { name = argv[0]; if (mrb_symbol_p(name)) { /* 1stArgument:symbol -> name=nil rest=argv[0..n] */ name = mrb_nil_value(); } else { pargv++; argcnt--; } } rest = mrb_ary_new_from_values(mrb, argcnt, pargv); for (i=0; i anObject * struct[fixnum] -> anObject * * Attribute Reference---Returns the value of the instance variable * named by symbol, or indexed (0..length-1) by * fixnum. Will raise NameError if the named * variable does not exist, or IndexError if the index is * out of range. * * Customer = Struct.new(:name, :address, :zip) * joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) * * joe["name"] #=> "Joe Smith" * joe[:name] #=> "Joe Smith" * joe[0] #=> "Joe Smith" */ static mrb_value mrb_struct_aref(mrb_state *mrb, mrb_value s) { mrb_value idx; mrb_get_args(mrb, "o", &idx); if (mrb_string_p(idx)) { mrb_value sym = mrb_check_intern_str(mrb, idx); if (mrb_nil_p(sym)) { mrb_name_error(mrb, mrb_intern_str(mrb, idx), "no member '%S' in struct", idx); } idx = sym; } if (mrb_symbol_p(idx)) { return struct_aref_sym(mrb, s, mrb_symbol(idx)); } return struct_aref_int(mrb, s, mrb_int(mrb, idx)); } static mrb_value mrb_struct_aset_sym(mrb_state *mrb, mrb_value s, mrb_sym id, mrb_value val) { mrb_value members, *ptr; const mrb_value *ptr_members; mrb_int i, len; members = struct_members(mrb, s); len = RARRAY_LEN(members); ptr = RSTRUCT_PTR(s); ptr_members = RARRAY_PTR(members); for (i=0; i obj * struct[fixnum] = obj -> obj * * Attribute Assignment---Assigns to the instance variable named by * symbol or fixnum the value obj and * returns it. Will raise a NameError if the named * variable does not exist, or an IndexError if the index * is out of range. * * Customer = Struct.new(:name, :address, :zip) * joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) * * joe["name"] = "Luke" * joe[:zip] = "90210" * * joe.name #=> "Luke" * joe.zip #=> "90210" */ static mrb_value mrb_struct_aset(mrb_state *mrb, mrb_value s) { mrb_int i; mrb_value idx; mrb_value val; mrb_get_args(mrb, "oo", &idx, &val); if (mrb_string_p(idx)) { mrb_value sym = mrb_check_intern_str(mrb, idx); if (mrb_nil_p(sym)) { mrb_name_error(mrb, mrb_intern_str(mrb, idx), "no member '%S' in struct", idx); } idx = sym; } if (mrb_symbol_p(idx)) { return mrb_struct_aset_sym(mrb, s, mrb_symbol(idx), val); } i = mrb_int(mrb, idx); if (i < 0) i = RSTRUCT_LEN(s) + i; if (i < 0) { mrb_raisef(mrb, E_INDEX_ERROR, "offset %S too small for struct(size:%S)", mrb_fixnum_value(i), mrb_fixnum_value(RSTRUCT_LEN(s))); } if (RSTRUCT_LEN(s) <= i) { mrb_raisef(mrb, E_INDEX_ERROR, "offset %S too large for struct(size:%S)", mrb_fixnum_value(i), mrb_fixnum_value(RSTRUCT_LEN(s))); } mrb_struct_modify(mrb, s); return RSTRUCT_PTR(s)[i] = val; } /* 15.2.18.4.1 */ /* * call-seq: * struct == other_struct -> true or false * * Equality---Returns true if other_struct is * equal to this one: they must be of the same class as generated by * Struct::new, and the values of all instance variables * must be equal (according to Object#==). * * Customer = Struct.new(:name, :address, :zip) * joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) * joejr = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) * jane = Customer.new("Jane Doe", "456 Elm, Anytown NC", 12345) * joe == joejr #=> true * joe == jane #=> false */ static mrb_value mrb_struct_equal(mrb_state *mrb, mrb_value s) { mrb_value s2; mrb_value *ptr, *ptr2; mrb_int i, len; mrb_get_args(mrb, "o", &s2); if (mrb_obj_equal(mrb, s, s2)) { return mrb_true_value(); } if (mrb_obj_class(mrb, s) != mrb_obj_class(mrb, s2)) { return mrb_false_value(); } if (RSTRUCT_LEN(s) != RSTRUCT_LEN(s2)) { mrb_bug(mrb, "inconsistent struct"); /* should never happen */ } ptr = RSTRUCT_PTR(s); ptr2 = RSTRUCT_PTR(s2); len = RSTRUCT_LEN(s); for (i=0; i true or false * * Two structures are equal if they are the same object, or if all their * fields are equal (using eql?). */ static mrb_value mrb_struct_eql(mrb_state *mrb, mrb_value s) { mrb_value s2; mrb_value *ptr, *ptr2; mrb_int i, len; mrb_get_args(mrb, "o", &s2); if (mrb_obj_equal(mrb, s, s2)) { return mrb_true_value(); } if (mrb_obj_class(mrb, s) != mrb_obj_class(mrb, s2)) { return mrb_false_value(); } if (RSTRUCT_LEN(s) != RSTRUCT_LEN(s2)) { mrb_bug(mrb, "inconsistent struct"); /* should never happen */ } ptr = RSTRUCT_PTR(s); ptr2 = RSTRUCT_PTR(s2); len = RSTRUCT_LEN(s); for (i=0; i Fixnum * struct.size -> Fixnum * * Returns number of struct members. */ static mrb_value mrb_struct_len(mrb_state *mrb, mrb_value self) { return mrb_fixnum_value(RSTRUCT_LEN(self)); } /* * call-seq: * struct.to_a -> array * struct.values -> array * * Create an array from struct values. */ static mrb_value mrb_struct_to_a(mrb_state *mrb, mrb_value self) { return mrb_ary_new_from_values(mrb, RSTRUCT_LEN(self), RSTRUCT_PTR(self)); } /* * call-seq: * struct.to_h -> hash * * Create a hash from member names and struct values. */ static mrb_value mrb_struct_to_h(mrb_state *mrb, mrb_value self) { mrb_value members, ret; mrb_int i; members = struct_members(mrb, self); ret = mrb_hash_new_capa(mrb, RARRAY_LEN(members)); for (i = 0; i < RARRAY_LEN(members); ++i) { mrb_hash_set(mrb, ret, RARRAY_PTR(members)[i], RSTRUCT_PTR(self)[i]); } return ret; } static mrb_value mrb_struct_values_at(mrb_state *mrb, mrb_value self) { mrb_int argc; mrb_value *argv; mrb_get_args(mrb, "*", &argv, &argc); return mrb_get_values_at(mrb, self, RSTRUCT_LEN(self), argc, argv, struct_aref_int); } /* * A Struct is a convenient way to bundle a number of * attributes together, using accessor methods, without having to write * an explicit class. * * 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." * * In the descriptions that follow, the parameter symbol refers * to a symbol, which is either a quoted string or a * Symbol (such as :name). */ void mrb_mruby_struct_gem_init(mrb_state* mrb) { struct RClass *st; st = mrb_define_class(mrb, "Struct", mrb->object_class); MRB_SET_INSTANCE_TT(st, MRB_TT_ARRAY); mrb_define_class_method(mrb, st, "new", mrb_struct_s_def, MRB_ARGS_ANY()); /* 15.2.18.3.1 */ mrb_define_method(mrb, st, "==", mrb_struct_equal, MRB_ARGS_REQ(1)); /* 15.2.18.4.1 */ mrb_define_method(mrb, st, "[]", mrb_struct_aref, MRB_ARGS_REQ(1)); /* 15.2.18.4.2 */ mrb_define_method(mrb, st, "[]=", mrb_struct_aset, MRB_ARGS_REQ(2)); /* 15.2.18.4.3 */ mrb_define_method(mrb, st, "members", mrb_struct_members, MRB_ARGS_NONE()); /* 15.2.18.4.6 */ mrb_define_method(mrb, st, "initialize", mrb_struct_initialize, MRB_ARGS_ANY()); /* 15.2.18.4.8 */ mrb_define_method(mrb, st, "initialize_copy", mrb_struct_init_copy, MRB_ARGS_REQ(1)); /* 15.2.18.4.9 */ mrb_define_method(mrb, st, "eql?", mrb_struct_eql, MRB_ARGS_REQ(1)); /* 15.2.18.4.12(x) */ mrb_define_method(mrb, st, "size", mrb_struct_len, MRB_ARGS_NONE()); mrb_define_method(mrb, st, "length", mrb_struct_len, MRB_ARGS_NONE()); mrb_define_method(mrb, st, "to_a", mrb_struct_to_a, MRB_ARGS_NONE()); mrb_define_method(mrb, st, "values", mrb_struct_to_a, MRB_ARGS_NONE()); mrb_define_method(mrb, st, "to_h", mrb_struct_to_h, MRB_ARGS_NONE()); mrb_define_method(mrb, st, "values_at", mrb_struct_values_at, MRB_ARGS_ANY()); } void mrb_mruby_struct_gem_final(mrb_state* mrb) { } mruby-2.0.0/mrbgems/mruby-struct/test/000077500000000000000000000000001340361412400177405ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-struct/test/struct.rb000066400000000000000000000112211340361412400216060ustar00rootroot00000000000000## # Struct ISO Test assert('Struct', '15.2.18') do assert_equal Class, Struct.class end assert('Struct.new', '15.2.18.3.1') do c = Struct.new(:m1, :m2) assert_equal Struct, c.superclass assert_equal [:m1, :m2], c.members end assert('Struct#==', '15.2.18.4.1') do c = Struct.new(:m1, :m2) cc1 = c.new(1,2) cc2 = c.new(1,2) assert_true cc1 == cc2 Struct.new(:m1, :m2) { def foo; end } assert_raise(NoMethodError) { Struct.new(:m1).new.foo } end assert('Struct#[]', '15.2.18.4.2') do c = Struct.new(:m1, :m2) cc = c.new(1,2) assert_equal 1, cc[:m1] assert_equal 2, cc["m2"] assert_equal 1, cc[0] assert_equal 2, cc[-1] assert_raise(TypeError) { cc[[]] } assert_raise(IndexError) { cc[2] } assert_raise(NameError) { cc['tama'] } end assert('Struct#[]=', '15.2.18.4.3') do c = Struct.new(:m1, :m2) cc = c.new(1,2) cc[:m1] = 3 assert_equal 3, cc[:m1] cc["m2"] = 3 assert_equal 3, cc["m2"] cc[0] = 4 assert_equal 4, cc[0] cc[-1] = 5 assert_equal 5, cc[-1] assert_raise(TypeError) { cc[[]] = 3 } assert_raise(IndexError) { cc[2] = 7 } assert_raise(NameError) { cc['pochi'] = 8 } end assert('Struct#each', '15.2.18.4.4') do c = Struct.new(:m1, :m2) cc = c.new(1,2) a = [] cc.each{|x| a << x } assert_equal [1, 2], a end assert('Struct#each_pair', '15.2.18.4.5') do c = Struct.new(:m1, :m2) cc = c.new(1,2) a = [] cc.each_pair{|k,v| a << [k,v] } assert_equal [[:m1, 1], [:m2, 2]], a end assert('Struct#members', '15.2.18.4.6') do c = Struct.new(:m1, :m2) assert_equal [:m1, :m2], c.new(1,2).members end assert('Struct#select', '15.2.18.4.7') do c = Struct.new(:m1, :m2) assert_equal([2]) { c.new(1,2).select{|v| v % 2 == 0} } end assert('large struct') do c = Struct.new(:m1, :m2, :m3, :m4, :m5, :m6, :m7, :m8, :m9, :m10, :m11, :m12, :m13) cc = c.new(1,2,3,4,5,6,7,8,9,10,11,12,13) assert_equal 1, cc.m1 assert_equal 2, cc.m2 assert_equal 3, cc.m3 assert_equal 4, cc.m4 assert_equal 5, cc.m5 assert_equal 6, cc.m6 assert_equal 7, cc.m7 assert_equal 8, cc.m8 assert_equal 9, cc.m9 assert_equal 10, cc.m10 assert_equal 13, cc.m13 cc.m13 = 'test' assert_equal 'test', cc.m13 assert_raise(NoMethodError) { cc.m14 } end assert('wrong struct arg count') do c = Struct.new(:m1) assert_raise ArgumentError do cc = c.new(1,2,3) end end assert('struct dup') do c = Struct.new(:m1, :m2, :m3, :m4, :m5) cc = c.new(1,2,3,4,5) assert_nothing_raised { assert_equal(cc, cc.dup) } end assert('struct inspect') do c = Struct.new(:m1, :m2, :m3, :m4, :m5) cc = c.new(1,2,3,4,5) assert_equal "#", cc.inspect end assert('Struct#length, Struct#size') do s = Struct.new(:f1, :f2).new(0, 1) assert_equal 2, s.size assert_equal 2, s.length end assert('Struct#to_a, Struct#values') do s = Struct.new(:mem1, :mem2).new('a', 'b') assert_equal ['a', 'b'], s.to_a assert_equal ['a', 'b'], s.values end assert('Struct#to_h') do s = Struct.new(:white, :red, :green).new('ruuko', 'yuzuki', 'hitoe') assert_equal(:white => 'ruuko', :red => 'yuzuki', :green => 'hitoe') { s.to_h } end assert('Struct#values_at') do a = Struct.new(:blue, :purple).new('aki', 'io') assert_equal ['aki'], a.values_at(0) assert_equal ['io', 'aki'], a.values_at(1, 0) assert_raise(IndexError) { a.values_at 2 } end assert("Struct#dig") do a = Struct.new(:blue, :purple).new('aki', Struct.new(:red).new(1)) assert_equal 'aki', a.dig(:blue) assert_equal 1, a.dig(:purple, :red) assert_equal 1, a.dig(1, 0) end assert("Struct.new removes existing constant") do skip "redefining Struct with same name cause warnings" begin assert_not_equal Struct.new("Test", :a), Struct.new("Test", :a, :b) ensure Struct.remove_const :Test end end assert("Struct#initialize_copy requires struct to be the same type") do begin Struct.new("Test", :a) a = Struct::Test.new("a") Struct.remove_const :Test Struct.new("Test", :a, :b) assert_raise(TypeError) do a.initialize_copy(Struct::Test.new("a", "b")) end ensure Struct.remove_const :Test end end assert("Struct.new does not allow array") do assert_raise(TypeError) do Struct.new("Test", [:a]) end end assert("Struct.new generates subclass of Struct") do begin original_struct = Struct Struct = String assert_equal original_struct, original_struct.new(:foo).superclass ensure Struct = original_struct end end assert 'Struct#freeze' do c = Struct.new :m o = c.new o.m = :test assert_equal :test, o.m o.freeze assert_raise(RuntimeError) { o.m = :modify } assert_raise(RuntimeError) { o[:m] = :modify } assert_equal :test, o.m end mruby-2.0.0/mrbgems/mruby-symbol-ext/000077500000000000000000000000001340361412400175405ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-symbol-ext/mrbgem.rake000066400000000000000000000002451340361412400216560ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-symbol-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Symbol class extension' end mruby-2.0.0/mrbgems/mruby-symbol-ext/mrblib/000077500000000000000000000000001340361412400210075ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-symbol-ext/mrblib/symbol.rb000066400000000000000000000024151340361412400226430ustar00rootroot00000000000000class Symbol include Comparable alias intern to_sym ## # call-seq: # sym.capitalize -> symbol # # Same as sym.to_s.capitalize.intern. def capitalize (self.to_s.capitalize! || self).to_sym end ## # call-seq: # sym.downcase -> symbol # # Same as sym.to_s.downcase.intern. def downcase (self.to_s.downcase! || self).to_sym end ## # call-seq: # sym.upcase -> symbol # # Same as sym.to_s.upcase.intern. def upcase (self.to_s.upcase! || self).to_sym end ## # call-seq: # sym.casecmp(other) -> -1, 0, +1 or nil # # Case-insensitive version of Symbol#<=>. def casecmp(other) return nil unless other.kind_of?(Symbol) lhs = self.to_s; lhs.upcase! rhs = other.to_s.upcase lhs <=> rhs end ## # call-seq: # sym.casecmp?(other) -> true, false, or nil # # Returns true if sym and other_sym are equal after case folding, # false if they are not equal, and nil if other_sym is not a string. def casecmp?(sym) c = self.casecmp(sym) return nil if c.nil? return c == 0 end # # call-seq: # sym.empty? -> true or false # # Returns that _sym_ is :"" or not. def empty? self.length == 0 end end mruby-2.0.0/mrbgems/mruby-symbol-ext/src/000077500000000000000000000000001340361412400203275ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-symbol-ext/src/symbol.c000066400000000000000000000031541340361412400220030ustar00rootroot00000000000000#include #include #include typedef struct symbol_name { size_t len; const char *name; } symbol_name; /* * call-seq: * Symbol.all_symbols => array * * Returns an array of all the symbols currently in Ruby's symbol * table. * * Symbol.all_symbols.size #=> 903 * Symbol.all_symbols[1,20] #=> [:floor, :ARGV, :Binding, :symlink, * :chown, :EOFError, :$;, :String, * :LOCK_SH, :"setuid?", :$<, * :default_proc, :compact, :extend, * :Tms, :getwd, :$=, :ThreadGroup, * :wait2, :$>] */ static mrb_value mrb_sym_all_symbols(mrb_state *mrb, mrb_value self) { mrb_sym i, lim; mrb_value ary = mrb_ary_new_capa(mrb, mrb->symidx); for (i=1, lim=mrb->symidx+1; i integer * * Same as sym.to_s.length. */ static mrb_value mrb_sym_length(mrb_state *mrb, mrb_value self) { mrb_int len; mrb_sym2name_len(mrb, mrb_symbol(self), &len); return mrb_fixnum_value(len); } void mrb_mruby_symbol_ext_gem_init(mrb_state* mrb) { struct RClass *s = mrb->symbol_class; mrb_define_class_method(mrb, s, "all_symbols", mrb_sym_all_symbols, MRB_ARGS_NONE()); mrb_define_method(mrb, s, "length", mrb_sym_length, MRB_ARGS_NONE()); mrb_define_method(mrb, s, "size", mrb_sym_length, MRB_ARGS_NONE()); } void mrb_mruby_symbol_ext_gem_final(mrb_state* mrb) { } mruby-2.0.0/mrbgems/mruby-symbol-ext/test/000077500000000000000000000000001340361412400205175ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-symbol-ext/test/symbol.rb000066400000000000000000000020041340361412400223450ustar00rootroot00000000000000## # Symbol(Ext) Test assert('Symbol.all_symbols') do foo = [:__symbol_test_1, :__symbol_test_2, :__symbol_test_3].sort symbols = Symbol.all_symbols.select{|sym|sym.to_s.include? '__symbol_test'}.sort assert_equal foo, symbols end assert("Symbol#length") do assert_equal 5, :hello.size assert_equal 5, :mruby.length end assert("Symbol#capitalize") do assert_equal :Hello, :hello.capitalize assert_equal :Hello, :HELLO.capitalize assert_equal :Hello, :Hello.capitalize end assert("Symbol#downcase") do assert_equal :hello, :hEllO.downcase assert_equal :hello, :hello.downcase end assert("Symbol#upcase") do assert_equal :HELLO, :hEllO.upcase assert_equal :HELLO, :HELLO.upcase end assert("Symbol#casecmp") do assert_equal 0, :HELLO.casecmp(:hEllO) assert_equal 1, :HELLO.casecmp(:hEllN) assert_equal(-1, :HELLO.casecmp(:hEllP)) assert_nil :HELLO.casecmp("hEllO") end assert("Symbol#empty?") do assert_true :''.empty? end assert('Symbol#intern') do assert_equal :test, :test.intern end mruby-2.0.0/mrbgems/mruby-test/000077500000000000000000000000001340361412400164145ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-test/README.md000066400000000000000000000001721340361412400176730ustar00rootroot00000000000000Running Tests ============= To run the tests, execute the following from the project's root directory. $ make test mruby-2.0.0/mrbgems/mruby-test/driver.c000066400000000000000000000103441340361412400200550ustar00rootroot00000000000000/* ** mrbtest - Test for Embeddable Ruby ** ** This program runs Ruby test programs in test/t directory ** against the current mruby implementation. */ #include #include #include #include #include #include #include #include #include #include void mrb_init_mrbtest(mrb_state *); /* Print a short remark for the user */ static void print_hint(void) { printf("mrbtest - Embeddable Ruby Test\n\n"); } static int check_error(mrb_state *mrb) { /* Error check */ /* $ko_test and $kill_test should be 0 */ mrb_value ko_test = mrb_gv_get(mrb, mrb_intern_lit(mrb, "$ko_test")); mrb_value kill_test = mrb_gv_get(mrb, mrb_intern_lit(mrb, "$kill_test")); return mrb_fixnum_p(ko_test) && mrb_fixnum(ko_test) == 0 && mrb_fixnum_p(kill_test) && mrb_fixnum(kill_test) == 0; } static int eval_test(mrb_state *mrb) { /* evaluate the test */ mrb_funcall(mrb, mrb_top_self(mrb), "report", 0); /* did an exception occur? */ if (mrb->exc) { mrb_print_error(mrb); mrb->exc = 0; return EXIT_FAILURE; } else if (!check_error(mrb)) { return EXIT_FAILURE; } return EXIT_SUCCESS; } static void t_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); fflush(stdout); } } mrb_value mrb_t_printstr(mrb_state *mrb, mrb_value self) { mrb_value argv; mrb_get_args(mrb, "o", &argv); t_printstr(mrb, argv); return argv; } void mrb_init_test_driver(mrb_state *mrb, mrb_bool verbose) { struct RClass *krn, *mrbtest; krn = mrb->kernel_module; mrb_define_method(mrb, krn, "__t_printstr__", mrb_t_printstr, MRB_ARGS_REQ(1)); mrbtest = mrb_define_module(mrb, "Mrbtest"); mrb_define_const(mrb, mrbtest, "FIXNUM_MAX", mrb_fixnum_value(MRB_INT_MAX)); mrb_define_const(mrb, mrbtest, "FIXNUM_MIN", mrb_fixnum_value(MRB_INT_MIN)); mrb_define_const(mrb, mrbtest, "FIXNUM_BIT", mrb_fixnum_value(MRB_INT_BIT)); #ifndef MRB_WITHOUT_FLOAT #ifdef MRB_USE_FLOAT mrb_define_const(mrb, mrbtest, "FLOAT_TOLERANCE", mrb_float_value(mrb, 1e-6)); #else mrb_define_const(mrb, mrbtest, "FLOAT_TOLERANCE", mrb_float_value(mrb, 1e-12)); #endif #endif if (verbose) { mrb_gv_set(mrb, mrb_intern_lit(mrb, "$mrbtest_verbose"), mrb_true_value()); } } void mrb_t_pass_result(mrb_state *mrb_dst, mrb_state *mrb_src) { mrb_value res_src; if (mrb_src->exc) { mrb_print_error(mrb_src); exit(EXIT_FAILURE); } #define TEST_COUNT_PASS(name) \ do { \ res_src = mrb_gv_get(mrb_src, mrb_intern_lit(mrb_src, "$" #name)); \ if (mrb_fixnum_p(res_src)) { \ mrb_value res_dst = mrb_gv_get(mrb_dst, mrb_intern_lit(mrb_dst, "$" #name)); \ mrb_gv_set(mrb_dst, mrb_intern_lit(mrb_dst, "$" #name), mrb_fixnum_value(mrb_fixnum(res_dst) + mrb_fixnum(res_src))); \ } \ } while (FALSE) \ TEST_COUNT_PASS(ok_test); TEST_COUNT_PASS(ko_test); TEST_COUNT_PASS(kill_test); #undef TEST_COUNT_PASS res_src = mrb_gv_get(mrb_src, mrb_intern_lit(mrb_src, "$asserts")); if (mrb_array_p(res_src)) { mrb_int i; mrb_value res_dst = mrb_gv_get(mrb_dst, mrb_intern_lit(mrb_dst, "$asserts")); for (i = 0; i < RARRAY_LEN(res_src); ++i) { mrb_value val_src = RARRAY_PTR(res_src)[i]; mrb_ary_push(mrb_dst, res_dst, mrb_str_new(mrb_dst, RSTRING_PTR(val_src), RSTRING_LEN(val_src))); } } } int main(int argc, char **argv) { mrb_state *mrb; int ret; mrb_bool verbose = FALSE; print_hint(); /* new interpreter instance */ mrb = mrb_open(); if (mrb == NULL) { fprintf(stderr, "Invalid mrb_state, exiting test driver"); return EXIT_FAILURE; } if (argc == 2 && argv[1][0] == '-' && argv[1][1] == 'v') { printf("verbose mode: enable\n\n"); verbose = TRUE; } mrb_init_test_driver(mrb, verbose); mrb_init_mrbtest(mrb); ret = eval_test(mrb); mrb_close(mrb); return ret; } mruby-2.0.0/mrbgems/mruby-test/init_mrbtest.c000066400000000000000000000016661340361412400212740ustar00rootroot00000000000000#include #include #include #include extern const uint8_t mrbtest_assert_irep[]; void mrbgemtest_init(mrb_state* mrb); void mrb_init_test_driver(mrb_state* mrb, mrb_bool verbose); void mrb_t_pass_result(mrb_state *mrb_dst, mrb_state *mrb_src); void mrb_init_mrbtest(mrb_state *mrb) { mrb_state *core_test; mrb_load_irep(mrb, mrbtest_assert_irep); core_test = mrb_open_core(mrb_default_allocf, NULL); if (core_test == NULL) { fprintf(stderr, "Invalid mrb_state, exiting %s", __FUNCTION__); exit(EXIT_FAILURE); } mrb_init_test_driver(core_test, mrb_test(mrb_gv_get(mrb, mrb_intern_lit(mrb, "$mrbtest_verbose")))); mrb_load_irep(core_test, mrbtest_assert_irep); mrb_t_pass_result(mrb, core_test); #ifndef DISABLE_GEMS mrbgemtest_init(mrb); #endif if (mrb->exc) { mrb_print_error(mrb); mrb_close(mrb); exit(EXIT_FAILURE); } mrb_close(core_test); } mruby-2.0.0/mrbgems/mruby-test/mrbgem.rake000066400000000000000000000174221340361412400205370ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-test') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'mruby test' build.bins << 'mrbtest' spec.add_dependency('mruby-compiler', :core => 'mruby-compiler') spec.test_rbfiles = Dir.glob("#{MRUBY_ROOT}/test/t/*.rb") if build.cc.defines.flatten.include?("MRB_WITHOUT_FLOAT") spec.test_rbfiles.delete("#{MRUBY_ROOT}/test/t/float.rb") end clib = "#{build_dir}/mrbtest.c" mlib = clib.ext(exts.object) exec = exefile("#{build.build_dir}/bin/mrbtest") mrbtest_lib = libfile("#{build_dir}/mrbtest") mrbtest_objs = [] driver_obj = objfile("#{build_dir}/driver") # driver = "#{spec.dir}/driver.c" assert_c = "#{build_dir}/assert.c" assert_rb = "#{MRUBY_ROOT}/test/assert.rb" assert_lib = assert_c.ext(exts.object) mrbtest_objs << assert_lib file assert_lib => assert_c file assert_c => [assert_rb, build.mrbcfile] do |t| open(t.name, 'w') do |f| mrbc.run f, assert_rb, 'mrbtest_assert_irep' end end gem_table = build.gems.generate_gem_table build build.gems.each do |g| test_rbobj = g.test_rbireps.ext(exts.object) g.test_objs << test_rbobj dep_list = build.gems.tsort_dependencies(g.test_dependencies, gem_table).select(&:generate_functions) file test_rbobj => g.test_rbireps file g.test_rbireps => [g.test_rbfiles, build.mrbcfile].flatten do |t| FileUtils.mkdir_p File.dirname(t.name) open(t.name, 'w') do |f| g.print_gem_test_header(f) test_preload = g.test_preload and [g.dir, MRUBY_ROOT].map {|dir| File.expand_path(g.test_preload, dir) }.find {|file| File.exist?(file) } f.puts %Q[/*] f.puts %Q[ * This file contains a test code for #{g.name} gem.] f.puts %Q[ *] f.puts %Q[ * IMPORTANT:] f.puts %Q[ * This file was generated!] f.puts %Q[ * All manual changes will get lost.] f.puts %Q[ */] if test_preload.nil? f.puts %Q[extern const uint8_t mrbtest_assert_irep[];] else g.build.mrbc.run f, test_preload, "gem_test_irep_#{g.funcname}_preload" end g.test_rbfiles.flatten.each_with_index do |rbfile, i| g.build.mrbc.run f, rbfile, "gem_test_irep_#{g.funcname}_#{i}" end f.puts %Q[void mrb_#{g.funcname}_gem_test(mrb_state *mrb);] unless g.test_objs.empty? dep_list.each do |d| f.puts %Q[void GENERATED_TMP_mrb_#{d.funcname}_gem_init(mrb_state *mrb);] f.puts %Q[void GENERATED_TMP_mrb_#{d.funcname}_gem_final(mrb_state *mrb);] end f.puts %Q[void mrb_init_test_driver(mrb_state *mrb, mrb_bool verbose);] f.puts %Q[void mrb_t_pass_result(mrb_state *dst, mrb_state *src);] f.puts %Q[void GENERATED_TMP_mrb_#{g.funcname}_gem_test(mrb_state *mrb) {] unless g.test_rbfiles.empty? f.puts %Q[ mrb_state *mrb2;] unless g.test_args.empty? f.puts %Q[ mrb_value test_args_hash;] end f.puts %Q[ int ai;] g.test_rbfiles.count.times do |i| f.puts %Q[ ai = mrb_gc_arena_save(mrb);] f.puts %Q[ mrb2 = mrb_open_core(mrb_default_allocf, NULL);] f.puts %Q[ if (mrb2 == NULL) {] f.puts %Q[ fprintf(stderr, "Invalid mrb_state, exiting \%s", __FUNCTION__);] f.puts %Q[ exit(EXIT_FAILURE);] f.puts %Q[ }] dep_list.each do |d| f.puts %Q[ GENERATED_TMP_mrb_#{d.funcname}_gem_init(mrb2);] f.puts %Q[ mrb_state_atexit(mrb2, GENERATED_TMP_mrb_#{d.funcname}_gem_final);] end f.puts %Q[ mrb_init_test_driver(mrb2, mrb_test(mrb_gv_get(mrb, mrb_intern_lit(mrb, "$mrbtest_verbose"))));] if test_preload.nil? f.puts %Q[ mrb_load_irep(mrb2, mrbtest_assert_irep);] else f.puts %Q[ mrb_load_irep(mrb2, gem_test_irep_#{g.funcname}_preload);] end f.puts %Q[ if (mrb2->exc) {] f.puts %Q[ mrb_print_error(mrb2);] f.puts %Q[ mrb_close(mrb2);] f.puts %Q[ exit(EXIT_FAILURE);] f.puts %Q[ }] f.puts %Q[ mrb_const_set(mrb2, mrb_obj_value(mrb2->object_class), mrb_intern_lit(mrb2, "GEMNAME"), mrb_str_new(mrb2, "#{g.name}", #{g.name.length}));] unless g.test_args.empty? f.puts %Q[ test_args_hash = mrb_hash_new_capa(mrb, #{g.test_args.length}); ] g.test_args.each do |arg_name, arg_value| escaped_arg_name = arg_name.gsub('\\', '\\\\\\\\').gsub('"', '\"') escaped_arg_value = arg_value.gsub('\\', '\\\\\\\\').gsub('"', '\"') f.puts %Q[ mrb_hash_set(mrb2, test_args_hash, mrb_str_new(mrb2, "#{escaped_arg_name.to_s}", #{escaped_arg_name.to_s.length}), mrb_str_new(mrb2, "#{escaped_arg_value.to_s}", #{escaped_arg_value.to_s.length})); ] end f.puts %Q[ mrb_const_set(mrb2, mrb_obj_value(mrb2->object_class), mrb_intern_lit(mrb2, "TEST_ARGS"), test_args_hash); ] end f.puts %Q[ mrb_#{g.funcname}_gem_test(mrb2);] if g.custom_test_init? f.puts %Q[ mrb_load_irep(mrb2, gem_test_irep_#{g.funcname}_#{i});] f.puts %Q[ ] f.puts %Q[ mrb_t_pass_result(mrb, mrb2);] f.puts %Q[ mrb_close(mrb2);] f.puts %Q[ mrb_gc_arena_restore(mrb, ai);] end end f.puts %Q[}] end end end build.gems.each do |v| mrbtest_objs.concat v.test_objs end file mrbtest_lib => mrbtest_objs do |t| build.archiver.run t.name, t.prerequisites end unless build.build_mrbtest_lib_only? file exec => [driver_obj, mlib, mrbtest_lib, build.libmruby_static] do |t| gem_flags = build.gems.map { |g| g.linker.flags } gem_flags_before_libraries = build.gems.map { |g| g.linker.flags_before_libraries } gem_flags_after_libraries = build.gems.map { |g| g.linker.flags_after_libraries } gem_libraries = build.gems.map { |g| g.linker.libraries } gem_library_paths = build.gems.map { |g| g.linker.library_paths } build.linker.run t.name, t.prerequisites, gem_libraries, gem_library_paths, gem_flags, gem_flags_before_libraries, gem_flags_after_libraries end end init = "#{spec.dir}/init_mrbtest.c" # store the last gem selection and make the re-build # of the test gem depending on a change to the gem # selection active_gems_path = "#{build_dir}/active_gems_path.lst" active_gem_list = if File.exist? active_gems_path File.read active_gems_path else FileUtils.mkdir_p File.dirname(active_gems_path) nil end current_gem_list = build.gems.map(&:name).join("\n") task active_gems_path do |_t| FileUtils.mkdir_p File.dirname(active_gems_path) File.write active_gems_path, current_gem_list end file clib => active_gems_path if active_gem_list != current_gem_list file mlib => clib file clib => [init, build.mrbcfile, __FILE__] do |_t| _pp "GEN", "*.rb", "#{clib.relative_path}" FileUtils.mkdir_p File.dirname(clib) open(clib, 'w') do |f| f.puts %Q[/*] f.puts %Q[ * This file contains a list of all] f.puts %Q[ * test functions.] f.puts %Q[ *] f.puts %Q[ * IMPORTANT:] f.puts %Q[ * This file was generated!] f.puts %Q[ * All manual changes will get lost.] f.puts %Q[ */] f.puts %Q[] f.puts IO.read(init) build.gems.each do |g| f.puts %Q[void GENERATED_TMP_mrb_#{g.funcname}_gem_test(mrb_state *mrb);] end f.puts %Q[void mrbgemtest_init(mrb_state* mrb) {] build.gems.each do |g| f.puts %Q[ GENERATED_TMP_mrb_#{g.funcname}_gem_test(mrb);] end f.puts %Q[}] end end end mruby-2.0.0/mrbgems/mruby-time/000077500000000000000000000000001340361412400163735ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-time/mrbgem.rake000066400000000000000000000002341340361412400205070ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-time') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'standard Time class' end mruby-2.0.0/mrbgems/mruby-time/mrblib/000077500000000000000000000000001340361412400176425ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-time/mrblib/time.rb000066400000000000000000000003511340361412400211240ustar00rootroot00000000000000class Time def sunday?; wday == 0 end def monday?; wday == 1 end def tuesday?; wday == 2 end def wednesday?; wday == 3 end def thursday?; wday == 4 end def friday?; wday == 5 end def saturday?; wday == 6 end end mruby-2.0.0/mrbgems/mruby-time/src/000077500000000000000000000000001340361412400171625ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-time/src/time.c000066400000000000000000000613161340361412400202730ustar00rootroot00000000000000/* ** time.c - Time class ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #ifndef MRB_DISABLE_STDIO #include #else #include #endif #define NDIV(x,y) (-(-((x)+1)/(y))-1) #if defined(_MSC_VER) && _MSC_VER < 1800 double round(double x) { return floor(x + 0.5); } #endif #if !defined(__MINGW64__) && defined(_WIN32) # define llround(x) round(x) #endif #if defined(__MINGW64__) || defined(__MINGW32__) # include #endif /** Time class configuration */ /* gettimeofday(2) */ /* C99 does not have gettimeofday that is required to retrieve microseconds */ /* uncomment following macro on platforms without gettimeofday(2) */ /* #define NO_GETTIMEOFDAY */ /* gmtime(3) */ /* C99 does not have reentrant gmtime_r() so it might cause troubles under */ /* multi-threading environment. undef following macro on platforms that */ /* does not have gmtime_r() and localtime_r(). */ /* #define NO_GMTIME_R */ #ifdef _WIN32 #if _MSC_VER /* Win32 platform do not provide gmtime_r/localtime_r; emulate them using gmtime_s/localtime_s */ #define gmtime_r(tp, tm) ((gmtime_s((tm), (tp)) == 0) ? (tm) : NULL) #define localtime_r(tp, tm) ((localtime_s((tm), (tp)) == 0) ? (tm) : NULL) #else #define NO_GMTIME_R #endif #endif /* asctime(3) */ /* mruby usually use its own implementation of struct tm to string conversion */ /* except when DISABLE_STDIO is set. In that case, it uses asctime() or asctime_r(). */ /* By default mruby tries to use asctime_r() which is reentrant. */ /* Undef following macro on platforms that does not have asctime_r(). */ /* #define NO_ASCTIME_R */ /* timegm(3) */ /* mktime() creates tm structure for localtime; timegm() is for UTC time */ /* define following macro to use probably faster timegm() on the platform */ /* #define USE_SYSTEM_TIMEGM */ /* time_t */ /* If your platform supports time_t as uint (e.g. uint32_t, uint64_t), */ /* uncomment following macro. */ /* #define MRB_TIME_T_UINT */ /** end of Time class configuration */ #ifndef NO_GETTIMEOFDAY # ifdef _WIN32 # define WIN32_LEAN_AND_MEAN /* don't include winsock.h */ # include # define gettimeofday my_gettimeofday # ifdef _MSC_VER # define UI64(x) x##ui64 # else # define UI64(x) x##ull # endif typedef long suseconds_t; # if (!defined __MINGW64__) && (!defined __MINGW32__) struct timeval { time_t tv_sec; suseconds_t tv_usec; }; # endif static int gettimeofday(struct timeval *tv, void *tz) { if (tz) { mrb_assert(0); /* timezone is not supported */ } if (tv) { union { FILETIME ft; unsigned __int64 u64; } t; GetSystemTimeAsFileTime(&t.ft); /* 100 ns intervals since Windows epoch */ t.u64 -= UI64(116444736000000000); /* Unix epoch bias */ t.u64 /= 10; /* to microseconds */ tv->tv_sec = (time_t)(t.u64 / (1000 * 1000)); tv->tv_usec = t.u64 % (1000 * 1000); } return 0; } # else # include # endif #endif #ifdef NO_GMTIME_R #define gmtime_r(t,r) gmtime(t) #define localtime_r(t,r) localtime(t) #endif #ifndef USE_SYSTEM_TIMEGM #define timegm my_timgm static unsigned int is_leapyear(unsigned int y) { return (y % 4) == 0 && ((y % 100) != 0 || (y % 400) == 0); } static time_t timegm(struct tm *tm) { static const unsigned int ndays[2][12] = { {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} }; time_t r = 0; int i; unsigned int *nday = (unsigned int*) ndays[is_leapyear(tm->tm_year+1900)]; static const int epoch_year = 70; if(tm->tm_year >= epoch_year) { for (i = epoch_year; i < tm->tm_year; ++i) r += is_leapyear(i+1900) ? 366*24*60*60 : 365*24*60*60; } else { for (i = tm->tm_year; i < epoch_year; ++i) r -= is_leapyear(i+1900) ? 366*24*60*60 : 365*24*60*60; } for (i = 0; i < tm->tm_mon; ++i) r += nday[i] * 24 * 60 * 60; r += (tm->tm_mday - 1) * 24 * 60 * 60; r += tm->tm_hour * 60 * 60; r += tm->tm_min * 60; r += tm->tm_sec; return r; } #endif /* Since we are limited to using ISO C99, this implementation is based * on time_t. That means the resolution of time is only precise to the * second level. Also, there are only 2 timezones, namely UTC and LOCAL. */ enum mrb_timezone { MRB_TIMEZONE_NONE = 0, MRB_TIMEZONE_UTC = 1, MRB_TIMEZONE_LOCAL = 2, MRB_TIMEZONE_LAST = 3 }; typedef struct mrb_timezone_name { const char name[8]; size_t len; } mrb_timezone_name; static const mrb_timezone_name timezone_names[] = { { "none", sizeof("none") - 1 }, { "UTC", sizeof("UTC") - 1 }, { "LOCAL", sizeof("LOCAL") - 1 }, }; #ifndef MRB_DISABLE_STDIO static const char mon_names[12][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", }; static const char wday_names[7][4] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", }; #endif struct mrb_time { time_t sec; time_t usec; enum mrb_timezone timezone; struct tm datetime; }; static const struct mrb_data_type mrb_time_type = { "Time", mrb_free }; /** Updates the datetime of a mrb_time based on it's timezone and seconds setting. Returns self on success, NULL of failure. */ static struct mrb_time* time_update_datetime(mrb_state *mrb, struct mrb_time *self) { struct tm *aid; if (self->timezone == MRB_TIMEZONE_UTC) { aid = gmtime_r(&self->sec, &self->datetime); } else { aid = localtime_r(&self->sec, &self->datetime); } if (!aid) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "%S out of Time range", mrb_float_value(mrb, (mrb_float)self->sec)); /* not reached */ return NULL; } #ifdef NO_GMTIME_R self->datetime = *aid; /* copy data */ #endif return self; } static mrb_value mrb_time_wrap(mrb_state *mrb, struct RClass *tc, struct mrb_time *tm) { return mrb_obj_value(Data_Wrap_Struct(mrb, tc, &mrb_time_type, tm)); } void mrb_check_num_exact(mrb_state *mrb, mrb_float num); /* Allocates a mrb_time object and initializes it. */ static struct mrb_time* time_alloc(mrb_state *mrb, double sec, double usec, enum mrb_timezone timezone) { struct mrb_time *tm; time_t tsec = 0; mrb_check_num_exact(mrb, (mrb_float)sec); mrb_check_num_exact(mrb, (mrb_float)usec); #ifndef MRB_TIME_T_UINT if (sizeof(time_t) == 4 && (sec > (double)INT32_MAX || (double)INT32_MIN > sec)) { goto out_of_range; } if (sizeof(time_t) == 8 && (sec > (double)INT64_MAX || (double)INT64_MIN > sec)) { goto out_of_range; } #else if (sizeof(time_t) == 4 && (sec > (double)UINT32_MAX || (double)0 > sec)) { goto out_of_range; } if (sizeof(time_t) == 8 && (sec > (double)UINT64_MAX || (double)0 > sec)) { goto out_of_range; } #endif tsec = (time_t)sec; if ((sec > 0 && tsec < 0) || (sec < 0 && (double)tsec > sec)) { out_of_range: mrb_raisef(mrb, E_ARGUMENT_ERROR, "%S out of Time range", mrb_float_value(mrb, sec)); } tm = (struct mrb_time *)mrb_malloc(mrb, sizeof(struct mrb_time)); tm->sec = tsec; tm->usec = (time_t)llround((sec - tm->sec) * 1.0e6 + usec); if (tm->usec < 0) { long sec2 = (long)NDIV(usec,1000000); /* negative div */ tm->usec -= sec2 * 1000000; tm->sec += sec2; } else if (tm->usec >= 1000000) { long sec2 = (long)(usec / 1000000); tm->usec -= sec2 * 1000000; tm->sec += sec2; } tm->timezone = timezone; time_update_datetime(mrb, tm); return tm; } static mrb_value mrb_time_make(mrb_state *mrb, struct RClass *c, double sec, double usec, enum mrb_timezone timezone) { return mrb_time_wrap(mrb, c, time_alloc(mrb, sec, usec, timezone)); } static struct mrb_time* current_mrb_time(mrb_state *mrb) { struct mrb_time *tm; tm = (struct mrb_time *)mrb_malloc(mrb, sizeof(*tm)); #if defined(TIME_UTC) { struct timespec ts; if (timespec_get(&ts, TIME_UTC) == 0) { mrb_free(mrb, tm); mrb_raise(mrb, E_RUNTIME_ERROR, "timespec_get() failed for unknown reasons"); } tm->sec = ts.tv_sec; tm->usec = ts.tv_nsec / 1000; } #elif defined(NO_GETTIMEOFDAY) { static time_t last_sec = 0, last_usec = 0; tm->sec = time(NULL); if (tm->sec != last_sec) { last_sec = tm->sec; last_usec = 0; } else { /* add 1 usec to differentiate two times */ last_usec += 1; } tm->usec = last_usec; } #else { struct timeval tv; gettimeofday(&tv, NULL); tm->sec = tv.tv_sec; tm->usec = tv.tv_usec; } #endif tm->timezone = MRB_TIMEZONE_LOCAL; time_update_datetime(mrb, tm); return tm; } /* Allocates a new Time object with given millis value. */ static mrb_value mrb_time_now(mrb_state *mrb, mrb_value self) { return mrb_time_wrap(mrb, mrb_class_ptr(self), current_mrb_time(mrb)); } /* 15.2.19.6.1 */ /* Creates an instance of time at the given time in seconds, etc. */ static mrb_value mrb_time_at(mrb_state *mrb, mrb_value self) { mrb_float f, f2 = 0; mrb_get_args(mrb, "f|f", &f, &f2); return mrb_time_make(mrb, mrb_class_ptr(self), f, f2, MRB_TIMEZONE_LOCAL); } static struct mrb_time* time_mktime(mrb_state *mrb, mrb_int ayear, mrb_int amonth, mrb_int aday, mrb_int ahour, mrb_int amin, mrb_int asec, mrb_int ausec, enum mrb_timezone timezone) { time_t nowsecs; struct tm nowtime = { 0 }; nowtime.tm_year = (int)ayear - 1900; nowtime.tm_mon = (int)amonth - 1; nowtime.tm_mday = (int)aday; nowtime.tm_hour = (int)ahour; nowtime.tm_min = (int)amin; nowtime.tm_sec = (int)asec; nowtime.tm_isdst = -1; if (nowtime.tm_mon < 0 || nowtime.tm_mon > 11 || nowtime.tm_mday < 1 || nowtime.tm_mday > 31 || nowtime.tm_hour < 0 || nowtime.tm_hour > 24 || (nowtime.tm_hour == 24 && (nowtime.tm_min > 0 || nowtime.tm_sec > 0)) || nowtime.tm_min < 0 || nowtime.tm_min > 59 || nowtime.tm_sec < 0 || nowtime.tm_sec > 60) mrb_raise(mrb, E_RUNTIME_ERROR, "argument out of range"); if (timezone == MRB_TIMEZONE_UTC) { nowsecs = timegm(&nowtime); } else { nowsecs = mktime(&nowtime); } if (nowsecs == (time_t)-1) { mrb_raise(mrb, E_ARGUMENT_ERROR, "Not a valid time."); } return time_alloc(mrb, (double)nowsecs, (double)ausec, timezone); } /* 15.2.19.6.2 */ /* Creates an instance of time at the given time in UTC. */ static mrb_value mrb_time_gm(mrb_state *mrb, mrb_value self) { mrb_int ayear = 0, amonth = 1, aday = 1, ahour = 0, amin = 0, asec = 0, ausec = 0; mrb_get_args(mrb, "i|iiiiii", &ayear, &amonth, &aday, &ahour, &amin, &asec, &ausec); return mrb_time_wrap(mrb, mrb_class_ptr(self), time_mktime(mrb, ayear, amonth, aday, ahour, amin, asec, ausec, MRB_TIMEZONE_UTC)); } /* 15.2.19.6.3 */ /* Creates an instance of time at the given time in local time zone. */ static mrb_value mrb_time_local(mrb_state *mrb, mrb_value self) { mrb_int ayear = 0, amonth = 1, aday = 1, ahour = 0, amin = 0, asec = 0, ausec = 0; mrb_get_args(mrb, "i|iiiiii", &ayear, &amonth, &aday, &ahour, &amin, &asec, &ausec); return mrb_time_wrap(mrb, mrb_class_ptr(self), time_mktime(mrb, ayear, amonth, aday, ahour, amin, asec, ausec, MRB_TIMEZONE_LOCAL)); } static struct mrb_time* time_get_ptr(mrb_state *mrb, mrb_value time) { struct mrb_time *tm; tm = DATA_GET_PTR(mrb, time, &mrb_time_type, struct mrb_time); if (!tm) { mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized time"); } return tm; } static mrb_value mrb_time_eq(mrb_state *mrb, mrb_value self) { mrb_value other; struct mrb_time *tm1, *tm2; mrb_bool eq_p; mrb_get_args(mrb, "o", &other); tm1 = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); tm2 = DATA_CHECK_GET_PTR(mrb, other, &mrb_time_type, struct mrb_time); eq_p = tm1 && tm2 && tm1->sec == tm2->sec && tm1->usec == tm2->usec; return mrb_bool_value(eq_p); } static mrb_value mrb_time_cmp(mrb_state *mrb, mrb_value self) { mrb_value other; struct mrb_time *tm1, *tm2; mrb_get_args(mrb, "o", &other); tm1 = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); tm2 = DATA_CHECK_GET_PTR(mrb, other, &mrb_time_type, struct mrb_time); if (!tm1 || !tm2) return mrb_nil_value(); if (tm1->sec > tm2->sec) { return mrb_fixnum_value(1); } else if (tm1->sec < tm2->sec) { return mrb_fixnum_value(-1); } /* tm1->sec == tm2->sec */ if (tm1->usec > tm2->usec) { return mrb_fixnum_value(1); } else if (tm1->usec < tm2->usec) { return mrb_fixnum_value(-1); } return mrb_fixnum_value(0); } static mrb_value mrb_time_plus(mrb_state *mrb, mrb_value self) { mrb_float f; struct mrb_time *tm; mrb_get_args(mrb, "f", &f); tm = time_get_ptr(mrb, self); return mrb_time_make(mrb, mrb_obj_class(mrb, self), (double)tm->sec+f, (double)tm->usec, tm->timezone); } static mrb_value mrb_time_minus(mrb_state *mrb, mrb_value self) { mrb_float f; mrb_value other; struct mrb_time *tm, *tm2; mrb_get_args(mrb, "o", &other); tm = time_get_ptr(mrb, self); tm2 = DATA_CHECK_GET_PTR(mrb, other, &mrb_time_type, struct mrb_time); if (tm2) { f = (mrb_float)(tm->sec - tm2->sec) + (mrb_float)(tm->usec - tm2->usec) / 1.0e6; return mrb_float_value(mrb, f); } else { mrb_get_args(mrb, "f", &f); return mrb_time_make(mrb, mrb_obj_class(mrb, self), (double)tm->sec-f, (double)tm->usec, tm->timezone); } } /* 15.2.19.7.30 */ /* Returns week day number of time. */ static mrb_value mrb_time_wday(mrb_state *mrb, mrb_value self) { struct mrb_time *tm; tm = time_get_ptr(mrb, self); return mrb_fixnum_value(tm->datetime.tm_wday); } /* 15.2.19.7.31 */ /* Returns year day number of time. */ static mrb_value mrb_time_yday(mrb_state *mrb, mrb_value self) { struct mrb_time *tm; tm = time_get_ptr(mrb, self); return mrb_fixnum_value(tm->datetime.tm_yday + 1); } /* 15.2.19.7.32 */ /* Returns year of time. */ static mrb_value mrb_time_year(mrb_state *mrb, mrb_value self) { struct mrb_time *tm; tm = time_get_ptr(mrb, self); return mrb_fixnum_value(tm->datetime.tm_year + 1900); } /* 15.2.19.7.33 */ /* Returns name of time's timezone. */ static mrb_value mrb_time_zone(mrb_state *mrb, mrb_value self) { struct mrb_time *tm; tm = time_get_ptr(mrb, self); if (tm->timezone <= MRB_TIMEZONE_NONE) return mrb_nil_value(); if (tm->timezone >= MRB_TIMEZONE_LAST) return mrb_nil_value(); return mrb_str_new_static(mrb, timezone_names[tm->timezone].name, timezone_names[tm->timezone].len); } /* 15.2.19.7.4 */ /* Returns a string that describes the time. */ static mrb_value mrb_time_asctime(mrb_state *mrb, mrb_value self) { struct mrb_time *tm = time_get_ptr(mrb, self); struct tm *d = &tm->datetime; int len; #if defined(MRB_DISABLE_STDIO) char *s; # ifdef NO_ASCTIME_R s = asctime(d); # else char buf[32]; s = asctime_r(d, buf); # endif len = strlen(s)-1; /* truncate the last newline */ #else char buf[256]; len = snprintf(buf, sizeof(buf), "%s %s %02d %02d:%02d:%02d %s%d", wday_names[d->tm_wday], mon_names[d->tm_mon], d->tm_mday, d->tm_hour, d->tm_min, d->tm_sec, tm->timezone == MRB_TIMEZONE_UTC ? "UTC " : "", d->tm_year + 1900); #endif return mrb_str_new(mrb, buf, len); } /* 15.2.19.7.6 */ /* Returns the day in the month of the time. */ static mrb_value mrb_time_day(mrb_state *mrb, mrb_value self) { struct mrb_time *tm; tm = time_get_ptr(mrb, self); return mrb_fixnum_value(tm->datetime.tm_mday); } /* 15.2.19.7.7 */ /* Returns true if daylight saving was applied for this time. */ static mrb_value mrb_time_dst_p(mrb_state *mrb, mrb_value self) { struct mrb_time *tm; tm = time_get_ptr(mrb, self); return mrb_bool_value(tm->datetime.tm_isdst); } /* 15.2.19.7.8 */ /* 15.2.19.7.10 */ /* Returns the Time object of the UTC(GMT) timezone. */ static mrb_value mrb_time_getutc(mrb_state *mrb, mrb_value self) { struct mrb_time *tm, *tm2; tm = time_get_ptr(mrb, self); tm2 = (struct mrb_time *)mrb_malloc(mrb, sizeof(*tm)); *tm2 = *tm; tm2->timezone = MRB_TIMEZONE_UTC; time_update_datetime(mrb, tm2); return mrb_time_wrap(mrb, mrb_obj_class(mrb, self), tm2); } /* 15.2.19.7.9 */ /* Returns the Time object of the LOCAL timezone. */ static mrb_value mrb_time_getlocal(mrb_state *mrb, mrb_value self) { struct mrb_time *tm, *tm2; tm = time_get_ptr(mrb, self); tm2 = (struct mrb_time *)mrb_malloc(mrb, sizeof(*tm)); *tm2 = *tm; tm2->timezone = MRB_TIMEZONE_LOCAL; time_update_datetime(mrb, tm2); return mrb_time_wrap(mrb, mrb_obj_class(mrb, self), tm2); } /* 15.2.19.7.15 */ /* Returns hour of time. */ static mrb_value mrb_time_hour(mrb_state *mrb, mrb_value self) { struct mrb_time *tm; tm = time_get_ptr(mrb, self); return mrb_fixnum_value(tm->datetime.tm_hour); } /* 15.2.19.7.16 */ /* Initializes a time by setting the amount of milliseconds since the epoch.*/ static mrb_value mrb_time_initialize(mrb_state *mrb, mrb_value self) { mrb_int ayear = 0, amonth = 1, aday = 1, ahour = 0, amin = 0, asec = 0, ausec = 0; mrb_int n; struct mrb_time *tm; n = mrb_get_args(mrb, "|iiiiiii", &ayear, &amonth, &aday, &ahour, &amin, &asec, &ausec); tm = (struct mrb_time*)DATA_PTR(self); if (tm) { mrb_free(mrb, tm); } mrb_data_init(self, NULL, &mrb_time_type); if (n == 0) { tm = current_mrb_time(mrb); } else { tm = time_mktime(mrb, ayear, amonth, aday, ahour, amin, asec, ausec, MRB_TIMEZONE_LOCAL); } mrb_data_init(self, tm, &mrb_time_type); return self; } /* 15.2.19.7.17(x) */ /* Initializes a copy of this time object. */ static mrb_value mrb_time_initialize_copy(mrb_state *mrb, mrb_value copy) { mrb_value src; struct mrb_time *t1, *t2; mrb_get_args(mrb, "o", &src); if (mrb_obj_equal(mrb, copy, src)) return copy; if (!mrb_obj_is_instance_of(mrb, src, mrb_obj_class(mrb, copy))) { mrb_raise(mrb, E_TYPE_ERROR, "wrong argument class"); } t1 = (struct mrb_time *)DATA_PTR(copy); t2 = (struct mrb_time *)DATA_PTR(src); if (!t2) { mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized time"); } if (!t1) { t1 = (struct mrb_time *)mrb_malloc(mrb, sizeof(struct mrb_time)); mrb_data_init(copy, t1, &mrb_time_type); } *t1 = *t2; return copy; } /* 15.2.19.7.18 */ /* Sets the timezone attribute of the Time object to LOCAL. */ static mrb_value mrb_time_localtime(mrb_state *mrb, mrb_value self) { struct mrb_time *tm; tm = time_get_ptr(mrb, self); tm->timezone = MRB_TIMEZONE_LOCAL; time_update_datetime(mrb, tm); return self; } /* 15.2.19.7.19 */ /* Returns day of month of time. */ static mrb_value mrb_time_mday(mrb_state *mrb, mrb_value self) { struct mrb_time *tm; tm = time_get_ptr(mrb, self); return mrb_fixnum_value(tm->datetime.tm_mday); } /* 15.2.19.7.20 */ /* Returns minutes of time. */ static mrb_value mrb_time_min(mrb_state *mrb, mrb_value self) { struct mrb_time *tm; tm = time_get_ptr(mrb, self); return mrb_fixnum_value(tm->datetime.tm_min); } /* 15.2.19.7.21 and 15.2.19.7.22 */ /* Returns month of time. */ static mrb_value mrb_time_mon(mrb_state *mrb, mrb_value self) { struct mrb_time *tm; tm = time_get_ptr(mrb, self); return mrb_fixnum_value(tm->datetime.tm_mon + 1); } /* 15.2.19.7.23 */ /* Returns seconds in minute of time. */ static mrb_value mrb_time_sec(mrb_state *mrb, mrb_value self) { struct mrb_time *tm; tm = time_get_ptr(mrb, self); return mrb_fixnum_value(tm->datetime.tm_sec); } /* 15.2.19.7.24 */ /* Returns a Float with the time since the epoch in seconds. */ static mrb_value mrb_time_to_f(mrb_state *mrb, mrb_value self) { struct mrb_time *tm; tm = time_get_ptr(mrb, self); return mrb_float_value(mrb, (mrb_float)tm->sec + (mrb_float)tm->usec/1.0e6); } /* 15.2.19.7.25 */ /* Returns a Fixnum with the time since the epoch in seconds. */ static mrb_value mrb_time_to_i(mrb_state *mrb, mrb_value self) { struct mrb_time *tm; tm = time_get_ptr(mrb, self); if (tm->sec > MRB_INT_MAX || tm->sec < MRB_INT_MIN) { return mrb_float_value(mrb, (mrb_float)tm->sec); } return mrb_fixnum_value((mrb_int)tm->sec); } /* 15.2.19.7.26 */ /* Returns a Float with the time since the epoch in microseconds. */ static mrb_value mrb_time_usec(mrb_state *mrb, mrb_value self) { struct mrb_time *tm; tm = time_get_ptr(mrb, self); if (tm->usec > MRB_INT_MAX || tm->usec < MRB_INT_MIN) { return mrb_float_value(mrb, (mrb_float)tm->usec); } return mrb_fixnum_value((mrb_int)tm->usec); } /* 15.2.19.7.27 */ /* Sets the timezone attribute of the Time object to UTC. */ static mrb_value mrb_time_utc(mrb_state *mrb, mrb_value self) { struct mrb_time *tm; tm = time_get_ptr(mrb, self); tm->timezone = MRB_TIMEZONE_UTC; time_update_datetime(mrb, tm); return self; } /* 15.2.19.7.28 */ /* Returns true if this time is in the UTC timezone false if not. */ static mrb_value mrb_time_utc_p(mrb_state *mrb, mrb_value self) { struct mrb_time *tm; tm = time_get_ptr(mrb, self); return mrb_bool_value(tm->timezone == MRB_TIMEZONE_UTC); } void mrb_mruby_time_gem_init(mrb_state* mrb) { struct RClass *tc; /* ISO 15.2.19.2 */ tc = mrb_define_class(mrb, "Time", mrb->object_class); MRB_SET_INSTANCE_TT(tc, MRB_TT_DATA); mrb_include_module(mrb, tc, mrb_module_get(mrb, "Comparable")); mrb_define_class_method(mrb, tc, "at", mrb_time_at, MRB_ARGS_ARG(1, 1)); /* 15.2.19.6.1 */ mrb_define_class_method(mrb, tc, "gm", mrb_time_gm, MRB_ARGS_ARG(1,6)); /* 15.2.19.6.2 */ mrb_define_class_method(mrb, tc, "local", mrb_time_local, MRB_ARGS_ARG(1,6)); /* 15.2.19.6.3 */ mrb_define_class_method(mrb, tc, "mktime", mrb_time_local, MRB_ARGS_ARG(1,6));/* 15.2.19.6.4 */ mrb_define_class_method(mrb, tc, "now", mrb_time_now, MRB_ARGS_NONE()); /* 15.2.19.6.5 */ mrb_define_class_method(mrb, tc, "utc", mrb_time_gm, MRB_ARGS_ARG(1,6)); /* 15.2.19.6.6 */ mrb_define_method(mrb, tc, "==" , mrb_time_eq , MRB_ARGS_REQ(1)); mrb_define_method(mrb, tc, "<=>" , mrb_time_cmp , MRB_ARGS_REQ(1)); /* 15.2.19.7.1 */ mrb_define_method(mrb, tc, "+" , mrb_time_plus , MRB_ARGS_REQ(1)); /* 15.2.19.7.2 */ mrb_define_method(mrb, tc, "-" , mrb_time_minus , MRB_ARGS_REQ(1)); /* 15.2.19.7.3 */ mrb_define_method(mrb, tc, "to_s" , mrb_time_asctime, MRB_ARGS_NONE()); mrb_define_method(mrb, tc, "inspect", mrb_time_asctime, MRB_ARGS_NONE()); mrb_define_method(mrb, tc, "asctime", mrb_time_asctime, MRB_ARGS_NONE()); /* 15.2.19.7.4 */ mrb_define_method(mrb, tc, "ctime" , mrb_time_asctime, MRB_ARGS_NONE()); /* 15.2.19.7.5 */ mrb_define_method(mrb, tc, "day" , mrb_time_day , MRB_ARGS_NONE()); /* 15.2.19.7.6 */ mrb_define_method(mrb, tc, "dst?" , mrb_time_dst_p , MRB_ARGS_NONE()); /* 15.2.19.7.7 */ mrb_define_method(mrb, tc, "getgm" , mrb_time_getutc , MRB_ARGS_NONE()); /* 15.2.19.7.8 */ mrb_define_method(mrb, tc, "getlocal",mrb_time_getlocal,MRB_ARGS_NONE()); /* 15.2.19.7.9 */ mrb_define_method(mrb, tc, "getutc" , mrb_time_getutc , MRB_ARGS_NONE()); /* 15.2.19.7.10 */ mrb_define_method(mrb, tc, "gmt?" , mrb_time_utc_p , MRB_ARGS_NONE()); /* 15.2.19.7.11 */ mrb_define_method(mrb, tc, "gmtime" , mrb_time_utc , MRB_ARGS_NONE()); /* 15.2.19.7.13 */ mrb_define_method(mrb, tc, "hour" , mrb_time_hour, MRB_ARGS_NONE()); /* 15.2.19.7.15 */ mrb_define_method(mrb, tc, "localtime", mrb_time_localtime, MRB_ARGS_NONE()); /* 15.2.19.7.18 */ mrb_define_method(mrb, tc, "mday" , mrb_time_mday, MRB_ARGS_NONE()); /* 15.2.19.7.19 */ mrb_define_method(mrb, tc, "min" , mrb_time_min, MRB_ARGS_NONE()); /* 15.2.19.7.20 */ mrb_define_method(mrb, tc, "mon" , mrb_time_mon, MRB_ARGS_NONE()); /* 15.2.19.7.21 */ mrb_define_method(mrb, tc, "month", mrb_time_mon, MRB_ARGS_NONE()); /* 15.2.19.7.22 */ mrb_define_method(mrb, tc, "sec" , mrb_time_sec, MRB_ARGS_NONE()); /* 15.2.19.7.23 */ mrb_define_method(mrb, tc, "to_i", mrb_time_to_i, MRB_ARGS_NONE()); /* 15.2.19.7.25 */ mrb_define_method(mrb, tc, "to_f", mrb_time_to_f, MRB_ARGS_NONE()); /* 15.2.19.7.24 */ mrb_define_method(mrb, tc, "usec", mrb_time_usec, MRB_ARGS_NONE()); /* 15.2.19.7.26 */ mrb_define_method(mrb, tc, "utc" , mrb_time_utc, MRB_ARGS_NONE()); /* 15.2.19.7.27 */ mrb_define_method(mrb, tc, "utc?", mrb_time_utc_p,MRB_ARGS_NONE()); /* 15.2.19.7.28 */ mrb_define_method(mrb, tc, "wday", mrb_time_wday, MRB_ARGS_NONE()); /* 15.2.19.7.30 */ mrb_define_method(mrb, tc, "yday", mrb_time_yday, MRB_ARGS_NONE()); /* 15.2.19.7.31 */ mrb_define_method(mrb, tc, "year", mrb_time_year, MRB_ARGS_NONE()); /* 15.2.19.7.32 */ mrb_define_method(mrb, tc, "zone", mrb_time_zone, MRB_ARGS_NONE()); /* 15.2.19.7.33 */ mrb_define_method(mrb, tc, "initialize", mrb_time_initialize, MRB_ARGS_REQ(1)); /* 15.2.19.7.16 */ mrb_define_method(mrb, tc, "initialize_copy", mrb_time_initialize_copy, MRB_ARGS_REQ(1)); /* 15.2.19.7.17 */ /* methods not available: gmt_offset(15.2.19.7.12) gmtoff(15.2.19.7.14) utc_offset(15.2.19.7.29) */ } void mrb_mruby_time_gem_final(mrb_state* mrb) { } mruby-2.0.0/mrbgems/mruby-time/test/000077500000000000000000000000001340361412400173525ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-time/test/time.rb000066400000000000000000000122271340361412400206410ustar00rootroot00000000000000## # Time ISO Test assert('Time.new', '15.2.3.3.3') do Time.new.class == Time end assert('Time', '15.2.19') do Time.class == Class end assert('Time.at', '15.2.19.6.1') do assert_kind_of(Time, Time.at(1300000000.0)) assert_raise(FloatDomainError) { Time.at(Float::NAN) } assert_raise(FloatDomainError) { Time.at(Float::INFINITY) } assert_raise(FloatDomainError) { Time.at(-Float::INFINITY) } assert_raise(FloatDomainError) { Time.at(0, Float::NAN) } assert_raise(FloatDomainError) { Time.at(0, Float::INFINITY) } assert_raise(FloatDomainError) { Time.at(0, -Float::INFINITY) } end assert('Time.gm', '15.2.19.6.2') do Time.gm(2012, 12, 23) end assert('Time.local', '15.2.19.6.3') do Time.local(2012, 12, 23) end assert('Time.mktime', '15.2.19.6.4') do Time.mktime(2012, 12, 23) end assert('Time.now', '15.2.19.6.5') do Time.now.class == Time end assert('Time.utc', '15.2.19.6.6') do Time.utc(2012, 12, 23) end assert('Time#+', '15.2.19.7.1') do t1 = Time.at(1300000000.0) t2 = t1.+(60) assert_equal(t2.utc.asctime, "Sun Mar 13 07:07:40 UTC 2011") assert_raise(FloatDomainError) { Time.at(0) + Float::NAN } assert_raise(FloatDomainError) { Time.at(0) + Float::INFINITY } assert_raise(FloatDomainError) { Time.at(0) + -Float::INFINITY } end assert('Time#-', '15.2.19.7.2') do t1 = Time.at(1300000000.0) t2 = t1.-(60) assert_equal(t2.utc.asctime, "Sun Mar 13 07:05:40 UTC 2011") assert_raise(FloatDomainError) { Time.at(0) - Float::NAN } assert_raise(FloatDomainError) { Time.at(0) - Float::INFINITY } assert_raise(FloatDomainError) { Time.at(0) - -Float::INFINITY } end assert('Time#<=>', '15.2.19.7.3') do t1 = Time.at(1300000000.0) t2 = Time.at(1400000000.0) t3 = Time.at(1500000000.0) t2.<=>(t1) == 1 and t2.<=>(t2) == 0 and t2.<=>(t3) == -1 and t2.<=>(nil) == nil end assert('Time#asctime', '15.2.19.7.4') do Time.at(1300000000.0).utc.asctime == "Sun Mar 13 07:06:40 UTC 2011" end assert('Time#ctime', '15.2.19.7.5') do Time.at(1300000000.0).utc.ctime == "Sun Mar 13 07:06:40 UTC 2011" end assert('Time#day', '15.2.19.7.6') do Time.gm(2012, 12, 23).day == 23 end assert('Time#dst?', '15.2.19.7.7') do not Time.gm(2012, 12, 23).utc.dst? end assert('Time#getgm', '15.2.19.7.8') do Time.at(1300000000.0).getgm.asctime == "Sun Mar 13 07:06:40 UTC 2011" end assert('Time#getlocal', '15.2.19.7.9') do t1 = Time.at(1300000000.0) t2 = Time.at(1300000000.0) t3 = t1.getlocal t1 == t3 and t3 == t2.getlocal end assert('Time#getutc', '15.2.19.7.10') do Time.at(1300000000.0).getutc.asctime == "Sun Mar 13 07:06:40 UTC 2011" end assert('Time#gmt?', '15.2.19.7.11') do Time.at(1300000000.0).utc.gmt? end # ATM not implemented # assert('Time#gmt_offset', '15.2.19.7.12') do assert('Time#gmtime', '15.2.19.7.13') do Time.at(1300000000.0).gmtime end # ATM not implemented # assert('Time#gmtoff', '15.2.19.7.14') do assert('Time#hour', '15.2.19.7.15') do Time.gm(2012, 12, 23, 7, 6).hour == 7 end # ATM doesn't really work # assert('Time#initialize', '15.2.19.7.16') do assert('Time#initialize_copy', '15.2.19.7.17') do time_tmp_2 = Time.at(7.0e6) time_tmp_2.clone == time_tmp_2 end assert('Time#localtime', '15.2.19.7.18') do t1 = Time.at(1300000000.0) t2 = Time.at(1300000000.0) t1.localtime t1 == t2.getlocal end assert('Time#mday', '15.2.19.7.19') do Time.gm(2012, 12, 23).mday == 23 end assert('Time#min', '15.2.19.7.20') do Time.gm(2012, 12, 23, 7, 6).min == 6 end assert('Time#mon', '15.2.19.7.21') do Time.gm(2012, 12, 23).mon == 12 end assert('Time#month', '15.2.19.7.22') do Time.gm(2012, 12, 23).month == 12 end assert('Times#sec', '15.2.19.7.23') do Time.gm(2012, 12, 23, 7, 6, 40).sec == 40 end assert('Time#to_f', '15.2.19.7.24') do Time.at(1300000000.0).to_f == 1300000000.0 end assert('Time#to_i', '15.2.19.7.25') do Time.at(1300000000.0).to_i == 1300000000 end assert('Time#usec', '15.2.19.7.26') do Time.at(1300000000.0).usec == 0 end assert('Time#utc', '15.2.19.7.27') do Time.at(1300000000.0).utc end assert('Time#utc?', '15.2.19.7.28') do Time.at(1300000000.0).utc.utc? end # ATM not implemented # assert('Time#utc_offset', '15.2.19.7.29') do assert('Time#wday', '15.2.19.7.30') do Time.gm(2012, 12, 23).wday == 0 end assert('Time#yday', '15.2.19.7.31') do Time.gm(2012, 12, 23).yday == 358 end assert('Time#year', '15.2.19.7.32') do Time.gm(2012, 12, 23).year == 2012 end assert('Time#zone', '15.2.19.7.33') do Time.at(1300000000.0).utc.zone == 'UTC' end # Not ISO specified assert('Time#to_s') do Time.at(1300000000.0).utc.to_s == "Sun Mar 13 07:06:40 UTC 2011" end assert('Time#inspect') do Time.at(1300000000.0).utc.inspect == "Sun Mar 13 07:06:40 UTC 2011" end assert('day of week methods') do t = Time.gm(2012, 12, 24) assert_false t.sunday? assert_true t.monday? assert_false t.tuesday? assert_false t.wednesday? assert_false t.thursday? assert_false t.friday? assert_false t.saturday? end assert('2000 times 500us make a second') do t = Time.utc 2015 2000.times do t += 0.0005 end t.usec == 0 end assert('Time.gm with Dec 31 23:59:59 1969 raise ArgumentError') do assert_raise(ArgumentError) {Time.gm(1969, 12, 31, 23, 59, 59)} end mruby-2.0.0/mrbgems/mruby-toplevel-ext/000077500000000000000000000000001340361412400200655ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-toplevel-ext/mrbgem.rake000066400000000000000000000002711340361412400222020ustar00rootroot00000000000000MRuby::Gem::Specification.new('mruby-toplevel-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'toplevel object (main) methods extension' end mruby-2.0.0/mrbgems/mruby-toplevel-ext/mrblib/000077500000000000000000000000001340361412400213345ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-toplevel-ext/mrblib/toplevel.rb000066400000000000000000000002371340361412400235150ustar00rootroot00000000000000 def self.include (*modules) self.class.include(*modules) end def self.private(*methods) end def self.protected(*methods) end def self.public(*methods) end mruby-2.0.0/mrbgems/mruby-toplevel-ext/test/000077500000000000000000000000001340361412400210445ustar00rootroot00000000000000mruby-2.0.0/mrbgems/mruby-toplevel-ext/test/toplevel.rb000066400000000000000000000007321340361412400232250ustar00rootroot00000000000000## # Toplevel Self(Ext) Test assert('Toplevel#include') do module ToplevelTestModule1 def method_foo :foo end CONST_BAR = :bar end module ToplevelTestModule2 CONST_BAR = :bar2 end self.include ToplevelTestModule2, ToplevelTestModule1 assert_true self.class.ancestors.include?( ToplevelTestModule1 ) assert_true self.class.ancestors.include?( ToplevelTestModule2 ) assert_equal :foo, method_foo assert_equal :bar2, CONST_BAR end mruby-2.0.0/mrblib/000077500000000000000000000000001340361412400141145ustar00rootroot00000000000000mruby-2.0.0/mrblib/00class.rb000066400000000000000000000007351340361412400157130ustar00rootroot00000000000000class Module # 15.2.2.4.12 def attr_accessor(*names) attr_reader(*names) attr_writer(*names) end # 15.2.2.4.11 alias attr attr_reader #def attr(name) # attr_reader(name) #end # 15.2.2.4.27 def include(*args) args.reverse.each do |m| m.append_features(self) m.included(self) end self end def prepend(*args) args.reverse.each do |m| m.prepend_features(self) m.prepended(self) end self end end mruby-2.0.0/mrblib/10error.rb000066400000000000000000000016021340361412400157320ustar00rootroot00000000000000# ISO 15.2.24 class ArgumentError < StandardError end # ISO 15.2.25 says "LocalJumpError < StandardError" class LocalJumpError < ScriptError end # ISO 15.2.26 class RangeError < StandardError end class FloatDomainError < RangeError end # ISO 15.2.26 class RegexpError < StandardError end # ISO 15.2.29 class TypeError < StandardError end # ISO 15.2.31 class NameError < StandardError attr_accessor :name def initialize(message=nil, name=nil) @name = name super(message) end end # ISO 15.2.32 class NoMethodError < NameError attr_reader :args def initialize(message=nil, name=nil, args=nil) @args = args super message, name end end # ISO 15.2.33 class IndexError < StandardError end class KeyError < IndexError end class NotImplementedError < ScriptError end class FrozenError < RuntimeError end class StopIteration < IndexError attr_accessor :result end mruby-2.0.0/mrblib/array.rb000066400000000000000000000132661340361412400155670ustar00rootroot00000000000000# coding: utf-8 ## # Array # # ISO 15.2.12 class Array ## # Calls the given block for each element of +self+ # and pass the respective element. # # ISO 15.2.12.5.10 def each(&block) return to_enum :each unless block idx = 0 while idx < length block.call(self[idx]) idx += 1 end self end ## # Calls the given block for each element of +self+ # and pass the index of the respective element. # # ISO 15.2.12.5.11 def each_index(&block) return to_enum :each_index unless block idx = 0 while idx < length block.call(idx) idx += 1 end self end ## # Calls the given block for each element of +self+ # and pass the respective element. Each element will # be replaced by the resulting values. # # ISO 15.2.12.5.7 def collect!(&block) return to_enum :collect! unless block idx = 0 len = size while idx < len self[idx] = block.call self[idx] idx += 1 end self end ## # Alias for collect! # # ISO 15.2.12.5.20 alias map! collect! ## # Private method for Array creation. # # ISO 15.2.12.5.15 def initialize(size=0, obj=nil, &block) size = size.__to_int raise ArgumentError, "negative array size" if size < 0 self.clear if size > 0 self[size - 1] = nil # allocate idx = 0 while idx < size self[idx] = (block)? block.call(idx): obj idx += 1 end end self end def _inspect size = self.size return "[]" if size == 0 ary=[] i=0 while itrue if +self+ and _other_ are the same object, # or are both arrays with the same content. # # ISO 15.2.12.5.34 (x) def eql?(other) other = self.__ary_eq(other) return false if other == false return true if other == true len = self.size i = 0 while i < len return false unless self[i].eql?(other[i]) i += 1 end return true end ## # Comparison---Returns an integer (-1, 0, or +1) # if this array is less than, equal to, or greater than other_ary. # Each object in each array is compared (using <=>). If any value isn't # 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 # the same length and the value of each element is equal to the # value of the corresponding element in the other array. # # ISO 15.2.12.5.36 (x) def <=>(other) other = self.__ary_cmp(other) return 0 if 0 == other return nil if nil == other len = self.size n = other.size len = n if len > n i = 0 while i < len n = (self[i] <=> other[i]) return n if n.nil? || n != 0 i += 1 end len = self.size - other.size if len == 0 0 elsif len > 0 1 else -1 end end ## # Delete element with index +key+ def delete(key, &block) while i = self.index(key) self.delete_at(i) ret = key end return block.call if ret.nil? && block ret end end ## # Array is enumerable class Array # ISO 15.2.12.3 include Enumerable ## # Sort all elements and replace +self+ with these # elements. def sort!(&block) stack = [ [ 0, self.size - 1 ] ] until stack.empty? left, mid, right = stack.pop if right == nil right = mid # sort self[left..right] if left < right if left + 1 == right lval = self[left] rval = self[right] if (block&.call(lval, rval) || (lval <=> rval)) > 0 self[left] = rval self[right] = lval end else mid = ((left + right + 1) / 2).floor stack.push [ left, mid, right ] stack.push [ mid, right ] stack.push [ left, (mid - 1) ] if left < mid - 1 end end else lary = self[left, mid - left] lsize = lary.size # The entity sharing between lary and self may cause a large memory # copy operation in the merge loop below. This harmless operation # cancels the sharing and provides a huge performance gain. lary[0] = lary[0] # merge lidx = 0 ridx = mid (left..right).each { |i| if lidx >= lsize break elsif ridx > right self[i, lsize - lidx] = lary[lidx, lsize - lidx] break else lval = lary[lidx] rval = self[ridx] if (block&.call(lval, rval) || (lval <=> rval)) <= 0 self[i] = lval lidx += 1 else self[i] = rval ridx += 1 end end } end end self end def sort(&block) self.dup.sort!(&block) end end mruby-2.0.0/mrblib/compar.rb000066400000000000000000000030431340361412400157220ustar00rootroot00000000000000## # Comparable # # ISO 15.3.3 module Comparable ## # Return true if +self+ is less # than +other+. Otherwise return # false. # # ISO 15.3.3.2.1 def < other cmp = self <=> other if cmp.nil? raise ArgumentError, "comparison of #{self.class} with #{other.class} failed" end cmp < 0 end ## # Return true if +self+ is less # than or equal to +other+. # Otherwise return false. # # ISO 15.3.3.2.2 def <= other cmp = self <=> other if cmp.nil? raise ArgumentError, "comparison of #{self.class} with #{other.class} failed" end cmp <= 0 end ## # Return true if +self+ is equal # to +other+. Otherwise return # false. # # ISO 15.3.3.2.3 def == other cmp = self <=> other cmp == 0 end ## # Return true if +self+ is greater # than +other+. Otherwise return # false. # # ISO 15.3.3.2.4 def > other cmp = self <=> other if cmp.nil? raise ArgumentError, "comparison of #{self.class} with #{other.class} failed" end cmp > 0 end ## # Return true if +self+ is greater # than or equal to +other+. # Otherwise return false. # # ISO 15.3.3.2.5 def >= other cmp = self <=> other if cmp.nil? raise ArgumentError, "comparison of #{self.class} with #{other.class} failed" end cmp >= 0 end ## # Return true if +self+ is greater # than or equal to +min+ and # less than or equal to +max+. # Otherwise return false. # # ISO 15.3.3.2.6 def between?(min, max) self >= min and self <= max end end mruby-2.0.0/mrblib/enum.rb000066400000000000000000000163011340361412400154060ustar00rootroot00000000000000## # Enumerable # # The Enumerable mixin provides collection classes with # several traversal and searching methods, and with the ability to # sort. The class must provide a method `each`, which # yields successive members of the collection. If # {Enumerable#max}, {#min}, or # {#sort} is used, the objects in the collection must also # implement a meaningful `<=>` operator, as these methods # rely on an ordering between members of the collection. # # @ISO 15.3.2 module Enumerable ## # Call the given block for each element # which is yield by +each+. Return false # if one block value is false. Otherwise # return true. If no block is given and # +self+ is false return false. # # ISO 15.3.2.2.1 def all?(&block) if block self.each{|*val| return false unless block.call(*val)} else self.each{|*val| return false unless val.__svalue} end true end ## # Call the given block for each element # which is yield by +each+. Return true # if one block value is true. Otherwise # return false. If no block is given and # +self+ is true object return true. # # ISO 15.3.2.2.2 def any?(&block) if block self.each{|*val| return true if block.call(*val)} else self.each{|*val| return true if val.__svalue} end false end ## # Call the given block for each element # which is yield by +each+. Append all # values of each block together and # return this value. # # ISO 15.3.2.2.3 def collect(&block) return to_enum :collect unless block ary = [] self.each{|*val| ary.push(block.call(*val))} ary end ## # Call the given block for each element # which is yield by +each+. Return # +ifnone+ if no block value was true. # Otherwise return the first block value # which had was true. # # ISO 15.3.2.2.4 def detect(ifnone=nil, &block) ret = ifnone self.each{|*val| if block.call(*val) ret = val.__svalue break end } ret end ## # Call the given block for each element # which is yield by +each+. Pass an # index to the block which starts at 0 # and increase by 1 for each element. # # ISO 15.3.2.2.5 def each_with_index(&block) return to_enum :each_with_index unless block i = 0 self.each{|*val| block.call(val.__svalue, i) i += 1 } self end ## # Return an array of all elements which # are yield by +each+. # # ISO 15.3.2.2.6 def entries ary = [] self.each{|*val| # __svalue is an internal method ary.push val.__svalue } ary end ## # Alias for find # # ISO 15.3.2.2.7 alias find detect ## # Call the given block for each element # which is yield by +each+. Return an array # which contains all elements whose block # value was true. # # ISO 15.3.2.2.8 def find_all(&block) return to_enum :find_all unless block ary = [] self.each{|*val| ary.push(val.__svalue) if block.call(*val) } ary end ## # Call the given block for each element # which is yield by +each+ and which return # value was true when invoking === with # +pattern+. Return an array with all # elements or the respective block values. # # ISO 15.3.2.2.9 def grep(pattern, &block) ary = [] self.each{|*val| sv = val.__svalue if pattern === sv ary.push((block)? block.call(*val): sv) end } ary end ## # Return true if at least one element which # is yield by +each+ returns a true value # by invoking == with +obj+. Otherwise return # false. # # ISO 15.3.2.2.10 def include?(obj) self.each{|*val| return true if val.__svalue == obj } false end ## # Call the given block for each element # which is yield by +each+. Return value # is the sum of all block values. Pass # to each block the current sum and the # current element. # # ISO 15.3.2.2.11 def inject(*args, &block) raise ArgumentError, "too many arguments" if args.size > 2 if Symbol === args[-1] sym = args[-1] block = ->(x,y){x.__send__(sym,y)} args.pop end if args.empty? flag = true # no initial argument result = nil else flag = false result = args[0] end self.each{|*val| val = val.__svalue if flag # push first element as initial flag = false result = val else result = block.call(result, val) end } result end alias reduce inject ## # Alias for collect # # ISO 15.3.2.2.12 alias map collect ## # Return the maximum value of all elements # yield by +each+. If no block is given <=> # will be invoked to define this value. If # a block is given it will be used instead. # # ISO 15.3.2.2.13 def max(&block) flag = true # 1st element? result = nil self.each{|*val| val = val.__svalue if flag # 1st element result = val flag = false else if block result = val if block.call(val, result) > 0 else result = val if (val <=> result) > 0 end end } result end ## # Return the minimum value of all elements # yield by +each+. If no block is given <=> # will be invoked to define this value. If # a block is given it will be used instead. # # ISO 15.3.2.2.14 def min(&block) flag = true # 1st element? result = nil self.each{|*val| val = val.__svalue if flag # 1st element result = val flag = false else if block result = val if block.call(val, result) < 0 else result = val if (val <=> result) < 0 end end } result end ## # Alias for include? # # ISO 15.3.2.2.15 alias member? include? ## # Call the given block for each element # which is yield by +each+. Return an # array which contains two arrays. The # first array contains all elements # whose block value was true. The second # array contains all elements whose # block value was false. # # ISO 15.3.2.2.16 def partition(&block) ary_T = [] ary_F = [] self.each{|*val| if block.call(*val) ary_T.push(val.__svalue) else ary_F.push(val.__svalue) end } [ary_T, ary_F] end ## # Call the given block for each element # which is yield by +each+. Return an # array which contains only the elements # whose block value was false. # # ISO 15.3.2.2.17 def reject(&block) ary = [] self.each{|*val| ary.push(val.__svalue) unless block.call(*val) } ary end ## # Alias for find_all. # # ISO 15.3.2.2.18 alias select find_all ## # Return a sorted array of all elements # which are yield by +each+. If no block # is given <=> will be invoked on each # element to define the order. Otherwise # the given block will be used for # sorting. # # ISO 15.3.2.2.19 def sort(&block) self.map{|*val| val.__svalue}.sort(&block) end ## # Alias for entries. # # ISO 15.3.2.2.20 alias to_a entries # redefine #hash 15.3.1.3.15 def hash h = 12347 i = 0 self.each do |e| h = __update_hash(h, i, e.hash) i += 1 end h end end mruby-2.0.0/mrblib/float.rb000066400000000000000000000003111340361412400155410ustar00rootroot00000000000000## # Float # # ISO 15.2.9 class Float # mruby special - since mruby integers may be upgraded to floats, # floats should be compatible to integers. include Integral end if class_defined?("Float") mruby-2.0.0/mrblib/hash.rb000066400000000000000000000164411340361412400153720ustar00rootroot00000000000000## # Hash # # ISO 15.2.13 class Hash ## # Equality---Two hashes are equal if they each contain the same number # of keys and if each key-value pair is equal to (according to # Object#==) the corresponding elements in the other # hash. # # ISO 15.2.13.4.1 def ==(hash) return true if self.equal?(hash) unless Hash === hash return false end return false if self.size != hash.size self.each do |k,v| return false unless hash.key?(k) return false unless self[k] == hash[k] end return true end ## # Returns true if hash and other are # both hashes with the same content compared by eql?. # # ISO 15.2.13.4.32 (x) def eql?(hash) return true if self.equal?(hash) unless Hash === hash return false end return false if self.size != hash.size self.each do |k,v| return false unless hash.key?(k) return false unless self[k].eql?(hash[k]) end return true end ## # Delete the element with the key +key+. # Return the value of the element if +key+ # was found. Return nil if nothing was # found. If a block is given, call the # block with the value of the element. # # ISO 15.2.13.4.8 def delete(key, &block) if block && !self.has_key?(key) return block.call(key) end self.__delete(key) end ## # Calls the given block for each element of +self+ # and pass the key and value of each element. # # call-seq: # hsh.each {| key, value | block } -> hsh # hsh.each_pair {| key, value | block } -> hsh # hsh.each -> an_enumerator # hsh.each_pair -> an_enumerator # # # If no block is given, an enumerator is returned instead. # # h = { "a" => 100, "b" => 200 } # h.each {|key, value| puts "#{key} is #{value}" } # # produces: # # a is 100 # b is 200 # # ISO 15.2.13.4.9 def each(&block) return to_enum :each unless block keys = self.keys vals = self.values len = self.size i = 0 while i < len block.call [keys[i], vals[i]] i += 1 end self end ## # Calls the given block for each element of +self+ # and pass the key of each element. # # call-seq: # hsh.each_key {| key | block } -> hsh # hsh.each_key -> an_enumerator # # If no block is given, an enumerator is returned instead. # # h = { "a" => 100, "b" => 200 } # h.each_key {|key| puts key } # # produces: # # a # b # # ISO 15.2.13.4.10 def each_key(&block) return to_enum :each_key unless block self.keys.each{|k| block.call(k)} self end ## # Calls the given block for each element of +self+ # and pass the value of each element. # # call-seq: # hsh.each_value {| value | block } -> hsh # hsh.each_value -> an_enumerator # # If no block is given, an enumerator is returned instead. # # h = { "a" => 100, "b" => 200 } # h.each_value {|value| puts value } # # produces: # # 100 # 200 # # ISO 15.2.13.4.11 def each_value(&block) return to_enum :each_value unless block self.keys.each{|k| block.call(self[k])} self end ## # Replaces the contents of hsh with the contents of other hash # # ISO 15.2.13.4.23 def replace(hash) raise TypeError, "Hash required (#{hash.class} given)" unless Hash === hash self.clear hash.each_key{|k| self[k] = hash[k] } if hash.default_proc self.default_proc = hash.default_proc else self.default = hash.default end self end # ISO 15.2.13.4.17 alias initialize_copy replace ## # Return a hash which contains the content of # +self+ and +other+. If a block is given # it will be called for each element with # a duplicate key. The value of the block # will be the final value of this element. # # ISO 15.2.13.4.22 def merge(other, &block) raise TypeError, "Hash required (#{other.class} given)" unless Hash === other h = self.dup if block other.each_key{|k| h[k] = (self.has_key?(k))? block.call(k, self[k], other[k]): other[k] } else other.each_key{|k| h[k] = other[k]} end h end # internal method for Hash inspection def _inspect return "{}" if self.size == 0 ary=[] keys=self.keys size=keys.size i=0 while i" + self[k]._inspect) i+=1 end "{"+ary.join(", ")+"}" end ## # Return the contents of this hash as a string. # # ISO 15.2.13.4.30 (x) def inspect begin self._inspect rescue SystemStackError "{...}" end end # ISO 15.2.13.4.31 (x) alias to_s inspect ## # call-seq: # hsh.reject! {| key, value | block } -> hsh or nil # hsh.reject! -> an_enumerator # # Equivalent to Hash#delete_if, but returns # nil if no changes were made. # # 1.8/1.9 Hash#reject! returns Hash; ISO says nothing. # def reject!(&block) return to_enum :reject! unless block keys = [] self.each{|k,v| if block.call([k, v]) keys.push(k) end } return nil if keys.size == 0 keys.each{|k| self.delete(k) } self end ## # call-seq: # hsh.reject {|key, value| block} -> a_hash # hsh.reject -> an_enumerator # # Returns a new hash consisting of entries for which the block returns false. # # If no block is given, an enumerator is returned instead. # # h = { "a" => 100, "b" => 200, "c" => 300 } # h.reject {|k,v| k < "b"} #=> {"b" => 200, "c" => 300} # h.reject {|k,v| v > 100} #=> {"a" => 100} # # 1.8/1.9 Hash#reject returns Hash; ISO says nothing. # def reject(&block) return to_enum :reject unless block h = {} self.each{|k,v| unless block.call([k, v]) h[k] = v end } h end ## # call-seq: # hsh.select! {| key, value | block } -> hsh or nil # hsh.select! -> an_enumerator # # Equivalent to Hash#keep_if, but returns # nil if no changes were made. # # 1.9 Hash#select! returns Hash; ISO says nothing. # def select!(&block) return to_enum :select! unless block keys = [] self.each{|k,v| unless block.call([k, v]) keys.push(k) end } return nil if keys.size == 0 keys.each{|k| self.delete(k) } self end ## # call-seq: # hsh.select {|key, value| block} -> a_hash # hsh.select -> an_enumerator # # Returns a new hash consisting of entries for which the block returns true. # # If no block is given, an enumerator is returned instead. # # h = { "a" => 100, "b" => 200, "c" => 300 } # h.select {|k,v| k > "a"} #=> {"b" => 200, "c" => 300} # h.select {|k,v| v < 200} #=> {"a" => 100} # # 1.9 Hash#select returns Hash; ISO says nothing # def select(&block) return to_enum :select unless block h = {} self.each{|k,v| if block.call([k, v]) h[k] = v end } h end end ## # Hash is enumerable # # ISO 15.2.13.3 class Hash include Enumerable end mruby-2.0.0/mrblib/init_mrblib.c000066400000000000000000000002361340361412400165530ustar00rootroot00000000000000#include #include extern const uint8_t mrblib_irep[]; void mrb_init_mrblib(mrb_state *mrb) { mrb_load_irep(mrb, mrblib_irep); } mruby-2.0.0/mrblib/kernel.rb000066400000000000000000000014371340361412400157260ustar00rootroot00000000000000## # Kernel # # ISO 15.3.1 module Kernel # 15.3.1.2.1 Kernel.` # provided by Kernel#` # 15.3.1.3.3 def `(s) raise NotImplementedError.new("backquotes not implemented") end ## # 15.3.1.2.3 Kernel.eval # 15.3.1.3.12 Kernel#eval # NotImplemented by mruby core; use mruby-eval gem ## # ISO 15.3.1.2.8 Kernel.loop # provided by Kernel#loop ## # Calls the given block repetitively. # # ISO 15.3.1.3.29 def loop(&block) return to_enum :loop unless block while true yield end rescue StopIteration => e e.result end # 11.4.4 Step c) def !~(y) !(self =~ y) end # internal method for inspect def _inspect self.inspect end def to_enum(*a) raise NotImplementedError.new("fiber required for enumerator") end end mruby-2.0.0/mrblib/mrblib.rake000066400000000000000000000015121340361412400162260ustar00rootroot00000000000000MRuby.each_target do current_dir = File.dirname(__FILE__) relative_from_root = File.dirname(__FILE__).relative_path_from(MRUBY_ROOT) current_build_dir = "#{build_dir}/#{relative_from_root}" self.libmruby_objs << objfile("#{current_build_dir}/mrblib") file objfile("#{current_build_dir}/mrblib") => "#{current_build_dir}/mrblib.c" file "#{current_build_dir}/mrblib.c" => [mrbcfile, __FILE__] + Dir.glob("#{current_dir}/*.rb").sort do |t| _, _, *rbfiles = t.prerequisites if self.cc.defines.flatten.include?("MRB_WITHOUT_FLOAT") rbfiles.delete("#{current_dir}/float.rb") end FileUtils.mkdir_p File.dirname(t.name) open(t.name, 'w') do |f| _pp "GEN", "*.rb", "#{t.name.relative_path}" f.puts File.read("#{current_dir}/init_mrblib.c") mrbc.run f, rbfiles, 'mrblib_irep' end end end mruby-2.0.0/mrblib/numeric.rb000066400000000000000000000046351340361412400161130ustar00rootroot00000000000000## # Numeric # # ISO 15.2.7 class Numeric include Comparable ## # Returns the receiver simply. # # ISO 15.2.7.4.1 def +@ self end ## # Returns the receiver's value, negated. # # ISO 15.2.7.4.2 def -@ 0 - self end ## # Returns the absolute value of the receiver. # # ISO 15.2.7.4.3 def abs if self < 0 -self else self end end end ## # Integral # # mruby special - module to share methods between Floats and Integers # to make them compatible module Integral ## # Calls the given block once for each Integer # from +self+ downto +num+. # # ISO 15.2.8.3.15 def downto(num, &block) return to_enum(:downto, num) unless block i = self.to_i while i >= num block.call(i) i -= 1 end self end ## # Returns self + 1 # # ISO 15.2.8.3.19 def next self + 1 end # ISO 15.2.8.3.21 alias succ next ## # Calls the given block +self+ times. # # ISO 15.2.8.3.22 def times &block return to_enum :times unless block i = 0 while i < self block.call i i += 1 end self end ## # Calls the given block once for each Integer # from +self+ upto +num+. # # ISO 15.2.8.3.27 def upto(num, &block) return to_enum(:upto, num) unless block i = self.to_i while i <= num block.call(i) i += 1 end self end ## # Calls the given block from +self+ to +num+ # incremented by +step+ (default 1). # def step(num=nil, step=1, &block) raise ArgumentError, "step can't be 0" if step == 0 return to_enum(:step, num, step) unless block i = if class_defined?("Float") && num.kind_of?(Float) then self.to_f else self end if num == nil while true block.call(i) i+=step end return self end if step > 0 while i <= num block.call(i) i += step end else while i >= num block.call(i) i += step end end self end end ## # Integer # # ISO 15.2.8 class Integer include Integral ## # Returns the receiver simply. # # ISO 15.2.8.3.14 def ceil self end ## # Returns the receiver simply. # # ISO 15.2.8.3.17 def floor self end ## # Returns the receiver simply. # # ISO 15.2.8.3.24 alias round floor ## # Returns the receiver simply. # # ISO 15.2.8.3.26 alias truncate floor end mruby-2.0.0/mrblib/range.rb000066400000000000000000000023261340361412400155400ustar00rootroot00000000000000## # Range # # ISO 15.2.14 class Range ## # Calls the given block for each element of +self+ # and pass the respective element. # # ISO 15.2.14.4.4 def each(&block) return to_enum :each unless block val = self.first last = self.last if val.kind_of?(Fixnum) && last.kind_of?(Fixnum) # fixnums are special lim = last lim += 1 unless exclude_end? i = val while i < lim block.call(i) i += 1 end return self end if val.kind_of?(String) && last.kind_of?(String) # fixnums are special if val.respond_to? :upto return val.upto(last, exclude_end?, &block) else str_each = true end end raise TypeError, "can't iterate" unless val.respond_to? :succ return self if (val <=> last) > 0 while (val <=> last) < 0 block.call(val) val = val.succ if str_each break if val.size > last.size end end block.call(val) if !exclude_end? && (val <=> last) == 0 self end # redefine #hash 15.3.1.3.15 def hash h = first.hash ^ last.hash h += 1 if self.exclude_end? h end end ## # Range is enumerable # # ISO 15.2.14.3 class Range include Enumerable end mruby-2.0.0/mrblib/string.rb000066400000000000000000000150351340361412400157530ustar00rootroot00000000000000## # String # # ISO 15.2.10 class String include Comparable ## # Calls the given block for each line # and pass the respective line. # # ISO 15.2.10.5.15 def each_line(rs = "\n", &block) return to_enum(:each_line, rs, &block) unless block return block.call(self) if rs.nil? rs.__to_str offset = 0 rs_len = rs.length this = dup while pos = this.index(rs, offset) block.call(this[offset, pos + rs_len - offset]) offset = pos + rs_len end block.call(this[offset, this.size - offset]) if this.size > offset self end # private method for gsub/sub def __sub_replace(pre, m, post) s = "" i = 0 while j = index("\\", i) break if j == length-1 t = case self[j+1] when "\\" "\\" when "`" pre when "&", "0" m when "'" post when "1", "2", "3", "4", "5", "6", "7", "8", "9" "" else self[j, 2] end s += self[i, j-i] + t i = j + 2 end s + self[i, length-i] end ## # Replace all matches of +pattern+ with +replacement+. # Call block (if given) for each match and replace # +pattern+ with the value of the block. Return the # final value. # # ISO 15.2.10.5.18 def gsub(*args, &block) return to_enum(:gsub, *args) if args.length == 1 && !block raise ArgumentError, "wrong number of arguments" unless (1..2).include?(args.length) pattern, replace = *args plen = pattern.length if args.length == 2 && block block = nil end if !replace.nil? || !block replace.__to_str end offset = 0 result = [] while found = index(pattern, offset) result << self[offset, found - offset] offset = found + plen result << if block block.call(pattern).to_s else replace.__sub_replace(self[0, found], pattern, self[offset..-1] || "") end if plen == 0 result << self[offset, 1] offset += 1 end end result << self[offset..-1] if offset < length result.join end ## # Replace all matches of +pattern+ with +replacement+. # Call block (if given) for each match and replace # +pattern+ with the value of the block. Modify # +self+ with the final value. # # ISO 15.2.10.5.19 def gsub!(*args, &block) raise FrozenError, "can't modify frozen String" if frozen? return to_enum(:gsub!, *args) if args.length == 1 && !block str = self.gsub(*args, &block) return nil unless self.index(args[0]) self.replace(str) end ## # Calls the given block for each match of +pattern+ # If no block is given return an array with all # matches of +pattern+. # # ISO 15.2.10.5.32 def scan(reg, &block) ### *** TODO *** ### unless Object.const_defined?(:Regexp) raise NotImplementedError, "scan not available (yet)" end end ## # Replace only the first match of +pattern+ with # +replacement+. Call block (if given) for each # match and replace +pattern+ with the value of the # block. Return the final value. # # ISO 15.2.10.5.36 def sub(*args, &block) unless (1..2).include?(args.length) raise ArgumentError, "wrong number of arguments (given #{args.length}, expected 2)" end pattern, replace = *args pattern.__to_str if args.length == 2 && block block = nil end unless block replace.__to_str end result = [] this = dup found = index(pattern) return this unless found result << this[0, found] offset = found + pattern.length result << if block block.call(pattern).to_s else replace.__sub_replace(this[0, found], pattern, this[offset..-1] || "") end result << this[offset..-1] if offset < length result.join end ## # Replace only the first match of +pattern+ with # +replacement+. Call block (if given) for each # match and replace +pattern+ with the value of the # block. Modify +self+ with the final value. # # ISO 15.2.10.5.37 def sub!(*args, &block) raise FrozenError, "can't modify frozen String" if frozen? str = self.sub(*args, &block) return nil unless self.index(args[0]) self.replace(str) end ## # Call the given block for each character of # +self+. def each_char(&block) pos = 0 while pos < self.size block.call(self[pos]) pos += 1 end self end ## # Call the given block for each byte of +self+. def each_byte(&block) bytes = self.bytes pos = 0 while pos < bytes.size block.call(bytes[pos]) pos += 1 end self end ## # Modify +self+ by replacing the content of +self+. # The portion of the string affected is determined using the same criteria as +String#[]+. def []=(*args) anum = args.size if anum == 2 pos, value = args case pos when String posnum = self.index(pos) if posnum b = self[0, posnum.to_i] a = self[(posnum + pos.length)..-1] self.replace([b, value, a].join('')) else raise IndexError, "string not matched" end when Range head = pos.begin tail = pos.end tail += self.length if tail < 0 unless pos.exclude_end? tail += 1 end return self[head, tail-head]=value else pos += self.length if pos < 0 if pos < 0 || pos > self.length raise IndexError, "index #{args[0]} out of string" end b = self[0, pos.to_i] a = self[pos + 1..-1] self.replace([b, value, a].join('')) end return value elsif anum == 3 pos, len, value = args pos += self.length if pos < 0 if pos < 0 || pos > self.length raise IndexError, "index #{args[0]} out of string" end if len < 0 raise IndexError, "negative length #{len}" end b = self[0, pos.to_i] a = self[pos + len..-1] self.replace([b, value, a].join('')) return value else raise ArgumentError, "wrong number of arguments (#{anum} for 2..3)" end end ## # ISO 15.2.10.5.3 def =~(re) re =~ self end ## # ISO 15.2.10.5.27 def match(re, &block) if String === re if Object.const_defined?(:Regexp) r = Regexp.new(re) r.match(self, &block) else raise NotImplementedError, "String#match needs Regexp class" end else re.match(self, &block) end end end ## # String is comparable # # ISO 15.2.10.3 module Comparable; end class String include Comparable end mruby-2.0.0/mrblib/symbol.rb000066400000000000000000000001611340361412400157440ustar00rootroot00000000000000class Symbol def to_proc ->(obj,*args,&block) do obj.__send__(self, *args, &block) end end end mruby-2.0.0/mruby-source.gemspec000066400000000000000000000011471340361412400166510ustar00rootroot00000000000000# coding: utf-8 lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'mruby/source' Gem::Specification.new do |spec| spec.name = "mruby-source" spec.version = MRuby::Source::MRUBY_VERSION spec.authors = [ MRuby::Source::MRUBY_AUTHOR ] spec.summary = %q{MRuby source code wrapper.} spec.description = %q{MRuby source code wrapper for use with Ruby libs.} spec.homepage = "http://www.mruby.org/" spec.license = "MIT" spec.files = `git ls-files -z`.split("\x0") spec.require_paths = ["lib"] end mruby-2.0.0/src/000077500000000000000000000000001340361412400134345ustar00rootroot00000000000000mruby-2.0.0/src/array.c000066400000000000000000000777141340361412400147360ustar00rootroot00000000000000/* ** array.c - Array class ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include "value_array.h" #define ARY_DEFAULT_LEN 4 #define ARY_SHRINK_RATIO 5 /* must be larger than 2 */ #define ARY_C_MAX_SIZE (SIZE_MAX / sizeof(mrb_value)) #define ARY_MAX_SIZE ((mrb_int)((ARY_C_MAX_SIZE < (size_t)MRB_INT_MAX) ? ARY_C_MAX_SIZE : MRB_INT_MAX-1)) static struct RArray* ary_new_capa(mrb_state *mrb, mrb_int capa) { struct RArray *a; size_t blen; if (capa > ARY_MAX_SIZE) { mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big"); } blen = capa * sizeof(mrb_value); a = (struct RArray*)mrb_obj_alloc(mrb, MRB_TT_ARRAY, mrb->array_class); if (capa <= MRB_ARY_EMBED_LEN_MAX) { ARY_SET_EMBED_LEN(a, 0); } else { a->as.heap.ptr = (mrb_value *)mrb_malloc(mrb, blen); a->as.heap.aux.capa = capa; a->as.heap.len = 0; } return a; } MRB_API mrb_value mrb_ary_new_capa(mrb_state *mrb, mrb_int capa) { struct RArray *a = ary_new_capa(mrb, capa); return mrb_obj_value(a); } MRB_API mrb_value mrb_ary_new(mrb_state *mrb) { return mrb_ary_new_capa(mrb, 0); } /* * to copy array, use this instead of memcpy because of portability * * gcc on ARM may fail optimization of memcpy * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka3934.html * * gcc on MIPS also fail * http://gcc.gnu.org/bugzilla/show_bug.cgi?id=39755 * * memcpy doesn't exist on freestanding environment * * If you optimize for binary size, use memcpy instead of this at your own risk * of above portability issue. * * see also http://togetter.com/li/462898 * */ static inline void array_copy(mrb_value *dst, const mrb_value *src, mrb_int size) { mrb_int i; for (i = 0; i < size; i++) { dst[i] = src[i]; } } static struct RArray* ary_new_from_values(mrb_state *mrb, mrb_int size, const mrb_value *vals) { struct RArray *a = ary_new_capa(mrb, size); array_copy(ARY_PTR(a), vals, size); ARY_SET_LEN(a, size); return a; } MRB_API mrb_value mrb_ary_new_from_values(mrb_state *mrb, mrb_int size, const mrb_value *vals) { struct RArray *a = ary_new_from_values(mrb, size, vals); return mrb_obj_value(a); } MRB_API mrb_value mrb_assoc_new(mrb_state *mrb, mrb_value car, mrb_value cdr) { struct RArray *a; a = ary_new_capa(mrb, 2); ARY_PTR(a)[0] = car; ARY_PTR(a)[1] = cdr; ARY_SET_LEN(a, 2); return mrb_obj_value(a); } static void ary_fill_with_nil(mrb_value *ptr, mrb_int size) { mrb_value nil = mrb_nil_value(); while (size--) { *ptr++ = nil; } } static void ary_modify_check(mrb_state *mrb, struct RArray *a) { if (MRB_FROZEN_P(a)) { mrb_raise(mrb, E_FROZEN_ERROR, "can't modify frozen array"); } } static void ary_modify(mrb_state *mrb, struct RArray *a) { ary_modify_check(mrb, a); if (ARY_SHARED_P(a)) { mrb_shared_array *shared = a->as.heap.aux.shared; if (shared->refcnt == 1 && a->as.heap.ptr == shared->ptr) { a->as.heap.ptr = shared->ptr; a->as.heap.aux.capa = a->as.heap.len; mrb_free(mrb, shared); } else { mrb_value *ptr, *p; mrb_int len; p = a->as.heap.ptr; len = a->as.heap.len * sizeof(mrb_value); ptr = (mrb_value *)mrb_malloc(mrb, len); if (p) { array_copy(ptr, p, a->as.heap.len); } a->as.heap.ptr = ptr; a->as.heap.aux.capa = a->as.heap.len; mrb_ary_decref(mrb, shared); } ARY_UNSET_SHARED_FLAG(a); } } MRB_API void mrb_ary_modify(mrb_state *mrb, struct RArray* a) { mrb_write_barrier(mrb, (struct RBasic*)a); ary_modify(mrb, a); } static void ary_make_shared(mrb_state *mrb, struct RArray *a) { if (!ARY_SHARED_P(a) && !ARY_EMBED_P(a)) { mrb_shared_array *shared = (mrb_shared_array *)mrb_malloc(mrb, sizeof(mrb_shared_array)); mrb_value *ptr = a->as.heap.ptr; mrb_int len = a->as.heap.len; shared->refcnt = 1; if (a->as.heap.aux.capa > len) { a->as.heap.ptr = shared->ptr = (mrb_value *)mrb_realloc(mrb, ptr, sizeof(mrb_value)*len+1); } else { shared->ptr = ptr; } shared->len = len; a->as.heap.aux.shared = shared; ARY_SET_SHARED_FLAG(a); } } static void ary_expand_capa(mrb_state *mrb, struct RArray *a, mrb_int len) { mrb_int capa = ARY_CAPA(a); if (len > ARY_MAX_SIZE || len < 0) { size_error: mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big"); } if (capa < ARY_DEFAULT_LEN) { capa = ARY_DEFAULT_LEN; } while (capa < len) { if (capa <= ARY_MAX_SIZE / 2) { capa *= 2; } else { capa = len; } } if (capa < len || capa > ARY_MAX_SIZE) { goto size_error; } if (ARY_EMBED_P(a)) { mrb_value *ptr = ARY_EMBED_PTR(a); mrb_int len = ARY_EMBED_LEN(a); mrb_value *expanded_ptr = (mrb_value *)mrb_malloc(mrb, sizeof(mrb_value)*capa); ARY_UNSET_EMBED_FLAG(a); array_copy(expanded_ptr, ptr, len); a->as.heap.len = len; a->as.heap.aux.capa = capa; a->as.heap.ptr = expanded_ptr; } else if (capa > a->as.heap.aux.capa) { mrb_value *expanded_ptr = (mrb_value *)mrb_realloc(mrb, a->as.heap.ptr, sizeof(mrb_value)*capa); a->as.heap.aux.capa = capa; a->as.heap.ptr = expanded_ptr; } } static void ary_shrink_capa(mrb_state *mrb, struct RArray *a) { mrb_int capa; if (ARY_EMBED_P(a)) return; capa = a->as.heap.aux.capa; if (capa < ARY_DEFAULT_LEN * 2) return; if (capa <= a->as.heap.len * ARY_SHRINK_RATIO) return; do { capa /= 2; if (capa < ARY_DEFAULT_LEN) { capa = ARY_DEFAULT_LEN; break; } } while (capa > a->as.heap.len * ARY_SHRINK_RATIO); if (capa > a->as.heap.len && capa < a->as.heap.aux.capa) { a->as.heap.aux.capa = capa; a->as.heap.ptr = (mrb_value *)mrb_realloc(mrb, a->as.heap.ptr, sizeof(mrb_value)*capa); } } MRB_API mrb_value mrb_ary_resize(mrb_state *mrb, mrb_value ary, mrb_int new_len) { mrb_int old_len; struct RArray *a = mrb_ary_ptr(ary); ary_modify(mrb, a); old_len = RARRAY_LEN(ary); if (old_len != new_len) { if (new_len < old_len) { ary_shrink_capa(mrb, a); } else { ary_expand_capa(mrb, a, new_len); ary_fill_with_nil(ARY_PTR(a) + old_len, new_len - old_len); } ARY_SET_LEN(a, new_len); } return ary; } static mrb_value mrb_ary_s_create(mrb_state *mrb, mrb_value klass) { mrb_value ary; mrb_value *vals; mrb_int len; struct RArray *a; mrb_get_args(mrb, "*!", &vals, &len); ary = mrb_ary_new_from_values(mrb, len, vals); a = mrb_ary_ptr(ary); a->c = mrb_class_ptr(klass); return ary; } static void ary_replace(mrb_state*, struct RArray*, struct RArray*); static void ary_concat(mrb_state *mrb, struct RArray *a, struct RArray *a2) { mrb_int len; if (ARY_LEN(a) == 0) { ary_replace(mrb, a, a2); return; } if (ARY_LEN(a2) > ARY_MAX_SIZE - ARY_LEN(a)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big"); } len = ARY_LEN(a) + ARY_LEN(a2); ary_modify(mrb, a); if (ARY_CAPA(a) < len) { ary_expand_capa(mrb, a, len); } array_copy(ARY_PTR(a)+ARY_LEN(a), ARY_PTR(a2), ARY_LEN(a2)); mrb_write_barrier(mrb, (struct RBasic*)a); ARY_SET_LEN(a, len); } MRB_API void mrb_ary_concat(mrb_state *mrb, mrb_value self, mrb_value other) { struct RArray *a2 = mrb_ary_ptr(other); ary_concat(mrb, mrb_ary_ptr(self), a2); } static mrb_value mrb_ary_concat_m(mrb_state *mrb, mrb_value self) { mrb_value ary; mrb_get_args(mrb, "A", &ary); mrb_ary_concat(mrb, self, ary); return self; } static mrb_value mrb_ary_plus(mrb_state *mrb, mrb_value self) { struct RArray *a1 = mrb_ary_ptr(self); struct RArray *a2; mrb_value *ptr; mrb_int blen, len1; mrb_get_args(mrb, "a", &ptr, &blen); if (ARY_MAX_SIZE - blen < ARY_LEN(a1)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big"); } len1 = ARY_LEN(a1); a2 = ary_new_capa(mrb, len1 + blen); array_copy(ARY_PTR(a2), ARY_PTR(a1), len1); array_copy(ARY_PTR(a2) + len1, ptr, blen); ARY_SET_LEN(a2, len1+blen); return mrb_obj_value(a2); } #define ARY_REPLACE_SHARED_MIN 20 static void ary_replace(mrb_state *mrb, struct RArray *a, struct RArray *b) { mrb_int len = ARY_LEN(b); ary_modify_check(mrb, a); if (a == b) return; if (ARY_SHARED_P(a)) { mrb_ary_decref(mrb, a->as.heap.aux.shared); a->as.heap.aux.capa = 0; a->as.heap.len = 0; a->as.heap.ptr = NULL; ARY_UNSET_SHARED_FLAG(a); } if (ARY_SHARED_P(b)) { shared_b: if (ARY_EMBED_P(a)) { ARY_UNSET_EMBED_FLAG(a); } else { mrb_free(mrb, a->as.heap.ptr); } a->as.heap.ptr = b->as.heap.ptr; a->as.heap.len = len; a->as.heap.aux.shared = b->as.heap.aux.shared; a->as.heap.aux.shared->refcnt++; ARY_SET_SHARED_FLAG(a); mrb_write_barrier(mrb, (struct RBasic*)a); return; } if (!MRB_FROZEN_P(b) && len > ARY_REPLACE_SHARED_MIN) { ary_make_shared(mrb, b); goto shared_b; } if (ARY_CAPA(a) < len) ary_expand_capa(mrb, a, len); array_copy(ARY_PTR(a), ARY_PTR(b), len); mrb_write_barrier(mrb, (struct RBasic*)a); ARY_SET_LEN(a, len); } MRB_API void mrb_ary_replace(mrb_state *mrb, mrb_value self, mrb_value other) { struct RArray *a1 = mrb_ary_ptr(self); struct RArray *a2 = mrb_ary_ptr(other); if (a1 != a2) { ary_replace(mrb, a1, a2); } } static mrb_value mrb_ary_replace_m(mrb_state *mrb, mrb_value self) { mrb_value other; mrb_get_args(mrb, "A", &other); mrb_ary_replace(mrb, self, other); return self; } static mrb_value mrb_ary_times(mrb_state *mrb, mrb_value self) { struct RArray *a1 = mrb_ary_ptr(self); struct RArray *a2; mrb_value *ptr; mrb_int times, len1; mrb_get_args(mrb, "i", ×); if (times < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "negative argument"); } if (times == 0) return mrb_ary_new(mrb); if (ARY_MAX_SIZE / times < ARY_LEN(a1)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big"); } len1 = ARY_LEN(a1); a2 = ary_new_capa(mrb, len1 * times); ARY_SET_LEN(a2, len1 * times); ptr = ARY_PTR(a2); while (times--) { array_copy(ptr, ARY_PTR(a1), len1); ptr += len1; } return mrb_obj_value(a2); } static mrb_value mrb_ary_reverse_bang(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); mrb_int len = ARY_LEN(a); if (len > 1) { mrb_value *p1, *p2; ary_modify(mrb, a); p1 = ARY_PTR(a); p2 = p1 + len - 1; while (p1 < p2) { mrb_value tmp = *p1; *p1++ = *p2; *p2-- = tmp; } } return self; } static mrb_value mrb_ary_reverse(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self), *b = ary_new_capa(mrb, ARY_LEN(a)); mrb_int len = ARY_LEN(a); if (len > 0) { mrb_value *p1, *p2, *e; p1 = ARY_PTR(a); e = p1 + len; p2 = ARY_PTR(b) + len - 1; while (p1 < e) { *p2-- = *p1++; } ARY_SET_LEN(b, len); } return mrb_obj_value(b); } MRB_API void mrb_ary_push(mrb_state *mrb, mrb_value ary, mrb_value elem) { struct RArray *a = mrb_ary_ptr(ary); mrb_int len = ARY_LEN(a); ary_modify(mrb, a); if (len == ARY_CAPA(a)) ary_expand_capa(mrb, a, len + 1); ARY_PTR(a)[len] = elem; ARY_SET_LEN(a, len+1); mrb_field_write_barrier_value(mrb, (struct RBasic*)a, elem); } static mrb_value mrb_ary_push_m(mrb_state *mrb, mrb_value self) { mrb_value *argv; mrb_int len, len2, alen; struct RArray *a; mrb_get_args(mrb, "*!", &argv, &alen); a = mrb_ary_ptr(self); ary_modify(mrb, a); len = ARY_LEN(a); len2 = len + alen; if (ARY_CAPA(a) < len2) { ary_expand_capa(mrb, a, len2); } array_copy(ARY_PTR(a)+len, argv, alen); ARY_SET_LEN(a, len2); mrb_write_barrier(mrb, (struct RBasic*)a); return self; } MRB_API mrb_value mrb_ary_pop(mrb_state *mrb, mrb_value ary) { struct RArray *a = mrb_ary_ptr(ary); mrb_int len = ARY_LEN(a); ary_modify_check(mrb, a); if (len == 0) return mrb_nil_value(); ARY_SET_LEN(a, len-1); return ARY_PTR(a)[len-1]; } #define ARY_SHIFT_SHARED_MIN 10 MRB_API mrb_value mrb_ary_shift(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); mrb_int len = ARY_LEN(a); mrb_value val; ary_modify_check(mrb, a); if (len == 0) return mrb_nil_value(); if (ARY_SHARED_P(a)) { L_SHIFT: val = a->as.heap.ptr[0]; a->as.heap.ptr++; a->as.heap.len--; return val; } if (len > ARY_SHIFT_SHARED_MIN) { ary_make_shared(mrb, a); goto L_SHIFT; } else { mrb_value *ptr = ARY_PTR(a); mrb_int size = len; val = *ptr; while (--size) { *ptr = *(ptr+1); ++ptr; } ARY_SET_LEN(a, len-1); } return val; } /* self = [1,2,3] item = 0 self.unshift item p self #=> [0, 1, 2, 3] */ MRB_API mrb_value mrb_ary_unshift(mrb_state *mrb, mrb_value self, mrb_value item) { struct RArray *a = mrb_ary_ptr(self); mrb_int len = ARY_LEN(a); if (ARY_SHARED_P(a) && a->as.heap.aux.shared->refcnt == 1 /* shared only referenced from this array */ && a->as.heap.ptr - a->as.heap.aux.shared->ptr >= 1) /* there's room for unshifted item */ { a->as.heap.ptr--; a->as.heap.ptr[0] = item; } else { mrb_value *ptr; ary_modify(mrb, a); if (ARY_CAPA(a) < len + 1) ary_expand_capa(mrb, a, len + 1); ptr = ARY_PTR(a); value_move(ptr + 1, ptr, len); ptr[0] = item; } ARY_SET_LEN(a, len+1); mrb_field_write_barrier_value(mrb, (struct RBasic*)a, item); return self; } static mrb_value mrb_ary_unshift_m(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); mrb_value *vals, *ptr; mrb_int alen, len; mrb_get_args(mrb, "*!", &vals, &alen); if (alen == 0) { ary_modify_check(mrb, a); return self; } len = ARY_LEN(a); if (alen > ARY_MAX_SIZE - len) { mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big"); } if (ARY_SHARED_P(a) && a->as.heap.aux.shared->refcnt == 1 /* shared only referenced from this array */ && a->as.heap.ptr - a->as.heap.aux.shared->ptr >= alen) /* there's room for unshifted item */ { ary_modify_check(mrb, a); a->as.heap.ptr -= alen; ptr = a->as.heap.ptr; } else { ary_modify(mrb, a); if (ARY_CAPA(a) < len + alen) ary_expand_capa(mrb, a, len + alen); ptr = ARY_PTR(a); value_move(ptr + alen, ptr, len); } array_copy(ptr, vals, alen); ARY_SET_LEN(a, len+alen); while (alen--) { mrb_field_write_barrier_value(mrb, (struct RBasic*)a, vals[alen]); } return self; } MRB_API mrb_value mrb_ary_ref(mrb_state *mrb, mrb_value ary, mrb_int n) { struct RArray *a = mrb_ary_ptr(ary); mrb_int len = ARY_LEN(a); /* range check */ if (n < 0) n += len; if (n < 0 || len <= n) return mrb_nil_value(); return ARY_PTR(a)[n]; } MRB_API void mrb_ary_set(mrb_state *mrb, mrb_value ary, mrb_int n, mrb_value val) { struct RArray *a = mrb_ary_ptr(ary); mrb_int len = ARY_LEN(a); ary_modify(mrb, a); /* range check */ if (n < 0) { n += len; if (n < 0) { mrb_raisef(mrb, E_INDEX_ERROR, "index %S out of array", mrb_fixnum_value(n - len)); } } if (len <= n) { if (ARY_CAPA(a) <= n) ary_expand_capa(mrb, a, n + 1); ary_fill_with_nil(ARY_PTR(a) + len, n + 1 - len); ARY_SET_LEN(a, n+1); } ARY_PTR(a)[n] = val; mrb_field_write_barrier_value(mrb, (struct RBasic*)a, val); } static struct RArray* ary_dup(mrb_state *mrb, struct RArray *a) { return ary_new_from_values(mrb, ARY_LEN(a), ARY_PTR(a)); } MRB_API mrb_value mrb_ary_splice(mrb_state *mrb, mrb_value ary, mrb_int head, mrb_int len, mrb_value rpl) { struct RArray *a = mrb_ary_ptr(ary); mrb_int alen = ARY_LEN(a); const mrb_value *argv; mrb_int argc; mrb_int tail; ary_modify(mrb, a); /* len check */ if (len < 0) mrb_raisef(mrb, E_INDEX_ERROR, "negative length (%S)", mrb_fixnum_value(len)); /* range check */ if (head < 0) { head += alen; if (head < 0) { mrb_raise(mrb, E_INDEX_ERROR, "index is out of array"); } } tail = head + len; if (alen < len || alen < tail) { len = alen - head; } /* size check */ if (mrb_array_p(rpl)) { argc = RARRAY_LEN(rpl); argv = RARRAY_PTR(rpl); if (argv == ARY_PTR(a)) { struct RArray *r; if (argc > 32767) { mrb_raise(mrb, E_ARGUMENT_ERROR, "too big recursive splice"); } r = ary_dup(mrb, a); argv = ARY_PTR(r); } } else { argc = 1; argv = &rpl; } if (head >= alen) { if (head > ARY_MAX_SIZE - argc) { mrb_raisef(mrb, E_INDEX_ERROR, "index %S too big", mrb_fixnum_value(head)); } len = head + argc; if (len > ARY_CAPA(a)) { ary_expand_capa(mrb, a, head + argc); } ary_fill_with_nil(ARY_PTR(a) + alen, head - alen); if (argc > 0) { array_copy(ARY_PTR(a) + head, argv, argc); } ARY_SET_LEN(a, len); } else { mrb_int newlen; if (alen - len > ARY_MAX_SIZE - argc) { mrb_raisef(mrb, E_INDEX_ERROR, "index %S too big", mrb_fixnum_value(alen + argc - len)); } newlen = alen + argc - len; if (newlen > ARY_CAPA(a)) { ary_expand_capa(mrb, a, newlen); } if (len != argc) { mrb_value *ptr = ARY_PTR(a); tail = head + len; value_move(ptr + head + argc, ptr + tail, alen - tail); ARY_SET_LEN(a, newlen); } if (argc > 0) { value_move(ARY_PTR(a) + head, argv, argc); } } mrb_write_barrier(mrb, (struct RBasic*)a); return ary; } void mrb_ary_decref(mrb_state *mrb, mrb_shared_array *shared) { shared->refcnt--; if (shared->refcnt == 0) { mrb_free(mrb, shared->ptr); mrb_free(mrb, shared); } } static mrb_value ary_subseq(mrb_state *mrb, struct RArray *a, mrb_int beg, mrb_int len) { struct RArray *b; if (!ARY_SHARED_P(a) && len <= ARY_SHIFT_SHARED_MIN) { return mrb_ary_new_from_values(mrb, len, ARY_PTR(a)+beg); } ary_make_shared(mrb, a); b = (struct RArray*)mrb_obj_alloc(mrb, MRB_TT_ARRAY, mrb->array_class); b->as.heap.ptr = a->as.heap.ptr + beg; b->as.heap.len = len; b->as.heap.aux.shared = a->as.heap.aux.shared; b->as.heap.aux.shared->refcnt++; ARY_SET_SHARED_FLAG(b); return mrb_obj_value(b); } static mrb_int aget_index(mrb_state *mrb, mrb_value index) { if (mrb_fixnum_p(index)) { return mrb_fixnum(index); } #ifndef MRB_WITHOUT_FLOAT else if (mrb_float_p(index)) { return (mrb_int)mrb_float(index); } #endif else { mrb_int i, argc; mrb_value *argv; mrb_get_args(mrb, "i*!", &i, &argv, &argc); return i; } } /* * call-seq: * ary[index] -> obj or nil * ary[start, length] -> new_ary or nil * ary[range] -> new_ary or nil * ary.slice(index) -> obj or nil * ary.slice(start, length) -> new_ary or nil * ary.slice(range) -> new_ary or nil * * Element Reference --- Returns the element at +index+, or returns a * subarray starting at the +start+ index and continuing for +length+ * elements, or returns a subarray specified by +range+ of indices. * * Negative indices count backward from the end of the array (-1 is the last * element). For +start+ and +range+ cases the starting index is just before * an element. Additionally, an empty array is returned when the starting * index for an element range is at the end of the array. * * Returns +nil+ if the index (or starting index) are out of range. * * a = [ "a", "b", "c", "d", "e" ] * a[1] => "b" * a[1,2] => ["b", "c"] * a[1..-2] => ["b", "c", "d"] * */ static mrb_value mrb_ary_aget(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); mrb_int i, len, alen; mrb_value index; if (mrb_get_args(mrb, "o|i", &index, &len) == 1) { switch (mrb_type(index)) { /* a[n..m] */ case MRB_TT_RANGE: if (mrb_range_beg_len(mrb, index, &i, &len, ARY_LEN(a), TRUE) == 1) { return ary_subseq(mrb, a, i, len); } else { return mrb_nil_value(); } case MRB_TT_FIXNUM: return mrb_ary_ref(mrb, self, mrb_fixnum(index)); default: return mrb_ary_ref(mrb, self, aget_index(mrb, index)); } } i = aget_index(mrb, index); alen = ARY_LEN(a); if (i < 0) i += alen; if (i < 0 || alen < i) return mrb_nil_value(); if (len < 0) return mrb_nil_value(); if (alen == i) return mrb_ary_new(mrb); if (len > alen - i) len = alen - i; return ary_subseq(mrb, a, i, len); } /* * call-seq: * ary[index] = obj -> obj * ary[start, length] = obj or other_ary or nil -> obj or other_ary or nil * ary[range] = obj or other_ary or nil -> obj or other_ary or nil * * Element Assignment --- Sets the element at +index+, or replaces a subarray * from the +start+ index for +length+ elements, or replaces a subarray * specified by the +range+ of indices. * * If indices are greater than the current capacity of the array, the array * grows automatically. Elements are inserted into the array at +start+ if * +length+ is zero. * * Negative indices will count backward from the end of the array. For * +start+ and +range+ cases the starting index is just before an element. * * An IndexError is raised if a negative index points past the beginning of * the array. * * See also Array#push, and Array#unshift. * * a = Array.new * a[4] = "4"; #=> [nil, nil, nil, nil, "4"] * a[0, 3] = [ 'a', 'b', 'c' ] #=> ["a", "b", "c", nil, "4"] * a[1..2] = [ 1, 2 ] #=> ["a", 1, 2, nil, "4"] * a[0, 2] = "?" #=> ["?", 2, nil, "4"] * a[0..2] = "A" #=> ["A", "4"] * a[-1] = "Z" #=> ["A", "Z"] * a[1..-1] = nil #=> ["A", nil] * a[1..-1] = [] #=> ["A"] * a[0, 0] = [ 1, 2 ] #=> [1, 2, "A"] * a[3, 0] = "B" #=> [1, 2, "A", "B"] */ static mrb_value mrb_ary_aset(mrb_state *mrb, mrb_value self) { mrb_value v1, v2, v3; mrb_int i, len; mrb_ary_modify(mrb, mrb_ary_ptr(self)); if (mrb_get_args(mrb, "oo|o", &v1, &v2, &v3) == 2) { /* a[n..m] = v */ switch (mrb_range_beg_len(mrb, v1, &i, &len, RARRAY_LEN(self), FALSE)) { case 0: /* not range */ mrb_ary_set(mrb, self, aget_index(mrb, v1), v2); break; case 1: /* range */ mrb_ary_splice(mrb, self, i, len, v2); break; case 2: /* out of range */ mrb_raisef(mrb, E_RANGE_ERROR, "%S out of range", v1); break; } return v2; } /* a[n,m] = v */ mrb_ary_splice(mrb, self, aget_index(mrb, v1), aget_index(mrb, v2), v3); return v3; } static mrb_value mrb_ary_delete_at(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); mrb_int index; mrb_value val; mrb_value *ptr; mrb_int len, alen; mrb_get_args(mrb, "i", &index); alen = ARY_LEN(a); if (index < 0) index += alen; if (index < 0 || alen <= index) return mrb_nil_value(); ary_modify(mrb, a); ptr = ARY_PTR(a); val = ptr[index]; ptr += index; len = alen - index; while (--len) { *ptr = *(ptr+1); ++ptr; } ARY_SET_LEN(a, alen-1); ary_shrink_capa(mrb, a); return val; } static mrb_value mrb_ary_first(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); mrb_int size, alen; if (mrb_get_argc(mrb) == 0) { return (ARY_LEN(a) > 0)? ARY_PTR(a)[0]: mrb_nil_value(); } mrb_get_args(mrb, "|i", &size); if (size < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "negative array size"); } alen = ARY_LEN(a); if (size > alen) size = alen; if (ARY_SHARED_P(a)) { return ary_subseq(mrb, a, 0, size); } return mrb_ary_new_from_values(mrb, size, ARY_PTR(a)); } static mrb_value mrb_ary_last(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); mrb_int n, size, alen; n = mrb_get_args(mrb, "|i", &size); alen = ARY_LEN(a); if (n == 0) { return (alen > 0) ? ARY_PTR(a)[alen - 1]: mrb_nil_value(); } if (size < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "negative array size"); } if (size > alen) size = alen; if (ARY_SHARED_P(a) || size > ARY_DEFAULT_LEN) { return ary_subseq(mrb, a, alen - size, size); } return mrb_ary_new_from_values(mrb, size, ARY_PTR(a) + alen - size); } static mrb_value mrb_ary_index_m(mrb_state *mrb, mrb_value self) { mrb_value obj; mrb_int i; mrb_get_args(mrb, "o", &obj); for (i = 0; i < RARRAY_LEN(self); i++) { if (mrb_equal(mrb, RARRAY_PTR(self)[i], obj)) { return mrb_fixnum_value(i); } } return mrb_nil_value(); } static mrb_value mrb_ary_rindex_m(mrb_state *mrb, mrb_value self) { mrb_value obj; mrb_int i, len; mrb_get_args(mrb, "o", &obj); for (i = RARRAY_LEN(self) - 1; i >= 0; i--) { if (mrb_equal(mrb, RARRAY_PTR(self)[i], obj)) { return mrb_fixnum_value(i); } if (i > (len = RARRAY_LEN(self))) { i = len; } } return mrb_nil_value(); } MRB_API mrb_value mrb_ary_splat(mrb_state *mrb, mrb_value v) { mrb_value a; if (mrb_array_p(v)) { return v; } if (!mrb_respond_to(mrb, v, mrb_intern_lit(mrb, "to_a"))) { return mrb_ary_new_from_values(mrb, 1, &v); } a = mrb_funcall(mrb, v, "to_a", 0); if (mrb_nil_p(a)) { return mrb_ary_new_from_values(mrb, 1, &v); } mrb_ensure_array_type(mrb, a); return a; } static mrb_value mrb_ary_size(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); return mrb_fixnum_value(ARY_LEN(a)); } MRB_API mrb_value mrb_ary_clear(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); mrb_get_args(mrb, ""); ary_modify(mrb, a); if (ARY_SHARED_P(a)) { mrb_ary_decref(mrb, a->as.heap.aux.shared); ARY_UNSET_SHARED_FLAG(a); } else if (!ARY_EMBED_P(a)){ mrb_free(mrb, a->as.heap.ptr); } ARY_SET_EMBED_LEN(a, 0); return self; } static mrb_value mrb_ary_empty_p(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); return mrb_bool_value(ARY_LEN(a) == 0); } MRB_API mrb_value mrb_ary_entry(mrb_value ary, mrb_int offset) { if (offset < 0) { offset += RARRAY_LEN(ary); } if (offset < 0 || RARRAY_LEN(ary) <= offset) { return mrb_nil_value(); } return RARRAY_PTR(ary)[offset]; } static mrb_value join_ary(mrb_state *mrb, mrb_value ary, mrb_value sep, mrb_value list) { mrb_int i; mrb_value result, val, tmp; /* check recursive */ for (i=0; i 0 && !mrb_nil_p(sep)) { mrb_str_cat_str(mrb, result, sep); } val = RARRAY_PTR(ary)[i]; switch (mrb_type(val)) { case MRB_TT_ARRAY: ary_join: val = join_ary(mrb, val, sep, list); /* fall through */ case MRB_TT_STRING: str_join: mrb_str_cat_str(mrb, result, val); break; default: if (!mrb_immediate_p(val)) { tmp = mrb_check_string_type(mrb, val); if (!mrb_nil_p(tmp)) { val = tmp; goto str_join; } tmp = mrb_check_array_type(mrb, val); if (!mrb_nil_p(tmp)) { val = tmp; goto ary_join; } } val = mrb_obj_as_string(mrb, val); goto str_join; } } mrb_ary_pop(mrb, list); return result; } MRB_API mrb_value mrb_ary_join(mrb_state *mrb, mrb_value ary, mrb_value sep) { if (!mrb_nil_p(sep)) { sep = mrb_obj_as_string(mrb, sep); } return join_ary(mrb, ary, sep, mrb_ary_new(mrb)); } /* * call-seq: * ary.join(sep="") -> str * * Returns a string created by converting each element of the array to * a string, separated by sep. * * [ "a", "b", "c" ].join #=> "abc" * [ "a", "b", "c" ].join("-") #=> "a-b-c" */ static mrb_value mrb_ary_join_m(mrb_state *mrb, mrb_value ary) { mrb_value sep = mrb_nil_value(); mrb_get_args(mrb, "|S!", &sep); return mrb_ary_join(mrb, ary, sep); } static mrb_value mrb_ary_eq(mrb_state *mrb, mrb_value ary1) { mrb_value ary2; mrb_get_args(mrb, "o", &ary2); if (mrb_obj_equal(mrb, ary1, ary2)) return mrb_true_value(); if (!mrb_array_p(ary2)) { return mrb_false_value(); } if (RARRAY_LEN(ary1) != RARRAY_LEN(ary2)) return mrb_false_value(); return ary2; } static mrb_value mrb_ary_cmp(mrb_state *mrb, mrb_value ary1) { mrb_value ary2; mrb_get_args(mrb, "o", &ary2); if (mrb_obj_equal(mrb, ary1, ary2)) return mrb_fixnum_value(0); if (!mrb_array_p(ary2)) { return mrb_nil_value(); } return ary2; } /* internal method to convert multi-value to single value */ static mrb_value mrb_ary_svalue(mrb_state *mrb, mrb_value ary) { mrb_get_args(mrb, ""); switch (RARRAY_LEN(ary)) { case 0: return mrb_nil_value(); case 1: return RARRAY_PTR(ary)[0]; default: return ary; } } void mrb_init_array(mrb_state *mrb) { struct RClass *a; mrb->array_class = a = mrb_define_class(mrb, "Array", mrb->object_class); /* 15.2.12 */ MRB_SET_INSTANCE_TT(a, MRB_TT_ARRAY); mrb_define_class_method(mrb, a, "[]", mrb_ary_s_create, MRB_ARGS_ANY()); /* 15.2.12.4.1 */ mrb_define_method(mrb, a, "+", mrb_ary_plus, MRB_ARGS_REQ(1)); /* 15.2.12.5.1 */ mrb_define_method(mrb, a, "*", mrb_ary_times, MRB_ARGS_REQ(1)); /* 15.2.12.5.2 */ mrb_define_method(mrb, a, "<<", mrb_ary_push_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.3 */ mrb_define_method(mrb, a, "[]", mrb_ary_aget, MRB_ARGS_ANY()); /* 15.2.12.5.4 */ mrb_define_method(mrb, a, "[]=", mrb_ary_aset, MRB_ARGS_ANY()); /* 15.2.12.5.5 */ mrb_define_method(mrb, a, "clear", mrb_ary_clear, MRB_ARGS_NONE()); /* 15.2.12.5.6 */ mrb_define_method(mrb, a, "concat", mrb_ary_concat_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.8 */ mrb_define_method(mrb, a, "delete_at", mrb_ary_delete_at, MRB_ARGS_REQ(1)); /* 15.2.12.5.9 */ mrb_define_method(mrb, a, "empty?", mrb_ary_empty_p, MRB_ARGS_NONE()); /* 15.2.12.5.12 */ mrb_define_method(mrb, a, "first", mrb_ary_first, MRB_ARGS_OPT(1)); /* 15.2.12.5.13 */ mrb_define_method(mrb, a, "index", mrb_ary_index_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.14 */ mrb_define_method(mrb, a, "initialize_copy", mrb_ary_replace_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.16 */ mrb_define_method(mrb, a, "join", mrb_ary_join_m, MRB_ARGS_ANY()); /* 15.2.12.5.17 */ mrb_define_method(mrb, a, "last", mrb_ary_last, MRB_ARGS_ANY()); /* 15.2.12.5.18 */ mrb_define_method(mrb, a, "length", mrb_ary_size, MRB_ARGS_NONE()); /* 15.2.12.5.19 */ mrb_define_method(mrb, a, "pop", mrb_ary_pop, MRB_ARGS_NONE()); /* 15.2.12.5.21 */ mrb_define_method(mrb, a, "push", mrb_ary_push_m, MRB_ARGS_ANY()); /* 15.2.12.5.22 */ mrb_define_method(mrb, a, "append", mrb_ary_push_m, MRB_ARGS_ANY()); mrb_define_method(mrb, a, "replace", mrb_ary_replace_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.23 */ mrb_define_method(mrb, a, "reverse", mrb_ary_reverse, MRB_ARGS_NONE()); /* 15.2.12.5.24 */ mrb_define_method(mrb, a, "reverse!", mrb_ary_reverse_bang, MRB_ARGS_NONE()); /* 15.2.12.5.25 */ mrb_define_method(mrb, a, "rindex", mrb_ary_rindex_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.26 */ mrb_define_method(mrb, a, "shift", mrb_ary_shift, MRB_ARGS_NONE()); /* 15.2.12.5.27 */ mrb_define_method(mrb, a, "size", mrb_ary_size, MRB_ARGS_NONE()); /* 15.2.12.5.28 */ mrb_define_method(mrb, a, "slice", mrb_ary_aget, MRB_ARGS_ANY()); /* 15.2.12.5.29 */ mrb_define_method(mrb, a, "unshift", mrb_ary_unshift_m, MRB_ARGS_ANY()); /* 15.2.12.5.30 */ mrb_define_method(mrb, a, "prepend", mrb_ary_unshift_m, MRB_ARGS_ANY()); mrb_define_method(mrb, a, "__ary_eq", mrb_ary_eq, MRB_ARGS_REQ(1)); mrb_define_method(mrb, a, "__ary_cmp", mrb_ary_cmp, MRB_ARGS_REQ(1)); mrb_define_method(mrb, a, "__ary_index", mrb_ary_index_m, MRB_ARGS_REQ(1)); /* kept for mruby-array-ext */ mrb_define_method(mrb, a, "__svalue", mrb_ary_svalue, MRB_ARGS_NONE()); } mruby-2.0.0/src/backtrace.c000066400000000000000000000151031340361412400155170ustar00rootroot00000000000000/* ** backtrace.c - ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #include #include #include struct backtrace_location { int lineno; const char *filename; mrb_sym method_id; }; typedef void (*each_backtrace_func)(mrb_state*, struct backtrace_location*, void*); static const mrb_data_type bt_type = { "Backtrace", mrb_free }; static void each_backtrace(mrb_state *mrb, ptrdiff_t ciidx, mrb_code *pc0, each_backtrace_func func, void *data) { ptrdiff_t i, j; if (ciidx >= mrb->c->ciend - mrb->c->cibase) ciidx = 10; /* ciidx is broken... */ for (i=ciidx, j=0; i >= 0; i--,j++) { struct backtrace_location loc; mrb_callinfo *ci; mrb_irep *irep; mrb_code *pc; ci = &mrb->c->cibase[i]; if (!ci->proc) continue; if (MRB_PROC_CFUNC_P(ci->proc)) continue; irep = ci->proc->body.irep; if (!irep) continue; if (mrb->c->cibase[i].err) { pc = mrb->c->cibase[i].err; } else if (i+1 <= ciidx) { if (!mrb->c->cibase[i + 1].pc) continue; pc = &mrb->c->cibase[i+1].pc[-1]; } else { pc = pc0; } loc.filename = mrb_debug_get_filename(irep, pc - irep->iseq); loc.lineno = mrb_debug_get_line(irep, pc - irep->iseq); if (loc.lineno == -1) continue; if (!loc.filename) { loc.filename = "(unknown)"; } loc.method_id = ci->mid; func(mrb, &loc, data); } } #ifndef MRB_DISABLE_STDIO static void print_backtrace(mrb_state *mrb, mrb_value backtrace) { int i; mrb_int n; FILE *stream = stderr; if (!mrb_array_p(backtrace)) return; n = RARRAY_LEN(backtrace) - 1; if (n == 0) return; fprintf(stream, "trace (most recent call last):\n"); for (i=0; iflags; if (packed_bt_len(bt, n) == 0) return; fprintf(stream, "trace (most recent call last):\n"); for (i = 0; ifilename == NULL) continue; fprintf(stream, "\t[%d] %s:%d", i, entry->filename, entry->lineno); if (entry->method_id != 0) { const char *method_name; method_name = mrb_sym2name(mrb, entry->method_id); fprintf(stream, ":in %s", method_name); mrb_gc_arena_restore(mrb, ai); } fprintf(stream, "\n"); } } /* mrb_print_backtrace function to retrieve backtrace information from the last exception. */ MRB_API void mrb_print_backtrace(mrb_state *mrb) { mrb_value backtrace; if (!mrb->exc) { return; } backtrace = mrb_obj_iv_get(mrb, mrb->exc, mrb_intern_lit(mrb, "backtrace")); if (mrb_nil_p(backtrace)) return; if (mrb_array_p(backtrace)) { print_backtrace(mrb, backtrace); } else { print_packed_backtrace(mrb, backtrace); } } #else MRB_API void mrb_print_backtrace(mrb_state *mrb) { } #endif static void count_backtrace_i(mrb_state *mrb, struct backtrace_location *loc, void *data) { int *lenp = (int*)data; if (loc->filename == NULL) return; (*lenp)++; } static void pack_backtrace_i(mrb_state *mrb, struct backtrace_location *loc, void *data) { struct backtrace_location **pptr = (struct backtrace_location**)data; struct backtrace_location *ptr = *pptr; if (loc->filename == NULL) return; *ptr = *loc; *pptr = ptr+1; } static mrb_value packed_backtrace(mrb_state *mrb) { struct RData *backtrace; ptrdiff_t ciidx = mrb->c->ci - mrb->c->cibase; int len = 0; int size; void *ptr; each_backtrace(mrb, ciidx, mrb->c->ci->pc, count_backtrace_i, &len); size = len * sizeof(struct backtrace_location); ptr = mrb_malloc(mrb, size); if (ptr) memset(ptr, 0, size); backtrace = mrb_data_object_alloc(mrb, NULL, ptr, &bt_type); backtrace->flags = (unsigned int)len; each_backtrace(mrb, ciidx, mrb->c->ci->pc, pack_backtrace_i, &ptr); return mrb_obj_value(backtrace); } void mrb_keep_backtrace(mrb_state *mrb, mrb_value exc) { mrb_sym sym = mrb_intern_lit(mrb, "backtrace"); mrb_value backtrace; int ai; if (mrb_iv_defined(mrb, exc, sym)) return; ai = mrb_gc_arena_save(mrb); backtrace = packed_backtrace(mrb); mrb_iv_set(mrb, exc, sym, backtrace); mrb_gc_arena_restore(mrb, ai); } mrb_value mrb_unpack_backtrace(mrb_state *mrb, mrb_value backtrace) { struct backtrace_location *bt; mrb_int n, i; int ai; if (mrb_nil_p(backtrace)) { empty_backtrace: return mrb_ary_new_capa(mrb, 0); } if (mrb_array_p(backtrace)) return backtrace; bt = (struct backtrace_location*)mrb_data_check_get_ptr(mrb, backtrace, &bt_type); if (bt == NULL) goto empty_backtrace; n = (mrb_int)RDATA(backtrace)->flags; backtrace = mrb_ary_new_capa(mrb, n); ai = mrb_gc_arena_save(mrb); for (i = 0; i < n; i++) { struct backtrace_location *entry = &bt[i]; mrb_value btline; if (entry->filename == NULL) continue; btline = mrb_format(mrb, "%S:%S", mrb_str_new_cstr(mrb, entry->filename), mrb_fixnum_value(entry->lineno)); if (entry->method_id != 0) { mrb_str_cat_lit(mrb, btline, ":in "); mrb_str_cat_cstr(mrb, btline, mrb_sym2name(mrb, entry->method_id)); } mrb_ary_push(mrb, backtrace, btline); mrb_gc_arena_restore(mrb, ai); } return backtrace; } MRB_API mrb_value mrb_exc_backtrace(mrb_state *mrb, mrb_value exc) { mrb_sym attr_name; mrb_value backtrace; attr_name = mrb_intern_lit(mrb, "backtrace"); backtrace = mrb_iv_get(mrb, exc, attr_name); if (mrb_nil_p(backtrace) || mrb_array_p(backtrace)) { return backtrace; } backtrace = mrb_unpack_backtrace(mrb, backtrace); mrb_iv_set(mrb, exc, attr_name, backtrace); return backtrace; } MRB_API mrb_value mrb_get_backtrace(mrb_state *mrb) { return mrb_unpack_backtrace(mrb, packed_backtrace(mrb)); } mruby-2.0.0/src/class.c000066400000000000000000001645711340361412400147230ustar00rootroot00000000000000/* ** class.c - Class class ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #include #include #include #include KHASH_DEFINE(mt, mrb_sym, mrb_method_t, TRUE, kh_int_hash_func, kh_int_hash_equal) void mrb_gc_mark_mt(mrb_state *mrb, struct RClass *c) { khiter_t k; khash_t(mt) *h = c->mt; if (!h) return; for (k = kh_begin(h); k != kh_end(h); k++) { if (kh_exist(h, k)) { mrb_method_t m = kh_value(h, k); if (MRB_METHOD_PROC_P(m)) { struct RProc *p = MRB_METHOD_PROC(m); mrb_gc_mark(mrb, (struct RBasic*)p); } } } } size_t mrb_gc_mark_mt_size(mrb_state *mrb, struct RClass *c) { khash_t(mt) *h = c->mt; if (!h) return 0; return kh_size(h); } void mrb_gc_free_mt(mrb_state *mrb, struct RClass *c) { kh_destroy(mt, mrb, c->mt); } void mrb_class_name_class(mrb_state *mrb, struct RClass *outer, struct RClass *c, mrb_sym id) { mrb_value name; mrb_sym nsym = mrb_intern_lit(mrb, "__classname__"); if (mrb_obj_iv_defined(mrb, (struct RObject*)c, nsym)) return; if (outer == NULL || outer == mrb->object_class) { name = mrb_symbol_value(id); } else { name = mrb_class_path(mrb, outer); if (mrb_nil_p(name)) { /* unnamed outer class */ if (outer != mrb->object_class && outer != c) { mrb_obj_iv_set(mrb, (struct RObject*)c, mrb_intern_lit(mrb, "__outer__"), mrb_obj_value(outer)); } return; } mrb_str_cat_cstr(mrb, name, "::"); mrb_str_cat_cstr(mrb, name, mrb_sym2name(mrb, id)); } mrb_obj_iv_set(mrb, (struct RObject*)c, nsym, name); } static void setup_class(mrb_state *mrb, struct RClass *outer, struct RClass *c, mrb_sym id) { mrb_class_name_class(mrb, outer, c, id); mrb_obj_iv_set(mrb, (struct RObject*)outer, id, mrb_obj_value(c)); } #define make_metaclass(mrb, c) prepare_singleton_class((mrb), (struct RBasic*)(c)) static void prepare_singleton_class(mrb_state *mrb, struct RBasic *o) { struct RClass *sc, *c; if (o->c->tt == MRB_TT_SCLASS) return; sc = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_SCLASS, mrb->class_class); sc->flags |= MRB_FL_CLASS_IS_INHERITED; sc->mt = kh_init(mt, mrb); sc->iv = 0; if (o->tt == MRB_TT_CLASS) { c = (struct RClass*)o; if (!c->super) { sc->super = mrb->class_class; } else { sc->super = c->super->c; } } else if (o->tt == MRB_TT_SCLASS) { c = (struct RClass*)o; while (c->super->tt == MRB_TT_ICLASS) c = c->super; make_metaclass(mrb, c->super); sc->super = c->super->c; } else { sc->super = o->c; prepare_singleton_class(mrb, (struct RBasic*)sc); } o->c = sc; mrb_field_write_barrier(mrb, (struct RBasic*)o, (struct RBasic*)sc); mrb_field_write_barrier(mrb, (struct RBasic*)sc, (struct RBasic*)o); mrb_obj_iv_set(mrb, (struct RObject*)sc, mrb_intern_lit(mrb, "__attached__"), mrb_obj_value(o)); } static struct RClass* class_from_sym(mrb_state *mrb, struct RClass *klass, mrb_sym id) { mrb_value c = mrb_const_get(mrb, mrb_obj_value(klass), id); mrb_check_type(mrb, c, MRB_TT_CLASS); return mrb_class_ptr(c); } static struct RClass* module_from_sym(mrb_state *mrb, struct RClass *klass, mrb_sym id) { mrb_value c = mrb_const_get(mrb, mrb_obj_value(klass), id); mrb_check_type(mrb, c, MRB_TT_MODULE); return mrb_class_ptr(c); } static mrb_bool class_ptr_p(mrb_value obj) { switch (mrb_type(obj)) { case MRB_TT_CLASS: case MRB_TT_SCLASS: case MRB_TT_MODULE: return TRUE; default: return FALSE; } } static void check_if_class_or_module(mrb_state *mrb, mrb_value obj) { if (!class_ptr_p(obj)) { 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) { struct RClass *m; if (mrb_const_defined_at(mrb, mrb_obj_value(outer), name)) { return module_from_sym(mrb, outer, name); } m = mrb_module_new(mrb); setup_class(mrb, outer, m, name); return m; } MRB_API struct RClass* mrb_define_module_id(mrb_state *mrb, mrb_sym name) { return define_module(mrb, name, mrb->object_class); } MRB_API struct RClass* mrb_define_module(mrb_state *mrb, const char *name) { return define_module(mrb, mrb_intern_cstr(mrb, name), mrb->object_class); } MRB_API struct RClass* mrb_vm_define_module(mrb_state *mrb, mrb_value outer, mrb_sym id) { check_if_class_or_module(mrb, outer); if (mrb_const_defined_at(mrb, outer, id)) { mrb_value old = mrb_const_get(mrb, outer, id); if (mrb_type(old) != MRB_TT_MODULE) { mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a module", mrb_inspect(mrb, old)); } return mrb_class_ptr(old); } return define_module(mrb, id, mrb_class_ptr(outer)); } MRB_API struct RClass* mrb_define_module_under(mrb_state *mrb, struct RClass *outer, const char *name) { mrb_sym id = mrb_intern_cstr(mrb, name); struct RClass * c = define_module(mrb, id, outer); setup_class(mrb, outer, c, id); 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) { struct RClass * c; if (mrb_const_defined_at(mrb, mrb_obj_value(outer), name)) { c = class_from_sym(mrb, outer, name); 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), mrb_obj_value(c->super), mrb_obj_value(super)); } return c; } c = mrb_class_new(mrb, super); setup_class(mrb, outer, c, name); return c; } 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)); } return define_class(mrb, name, super, mrb->object_class); } MRB_API struct RClass* mrb_define_class(mrb_state *mrb, const char *name, struct RClass *super) { return mrb_define_class_id(mrb, mrb_intern_cstr(mrb, name), super); } static mrb_value mrb_bob_init(mrb_state *mrb, mrb_value); #ifdef MRB_METHOD_CACHE static void mc_clear_all(mrb_state *mrb); static void mc_clear_by_class(mrb_state *mrb, struct RClass*); static void mc_clear_by_id(mrb_state *mrb, struct RClass*, mrb_sym); #else #define mc_clear_all(mrb) #define mc_clear_by_class(mrb,c) #define mc_clear_by_id(mrb,c,s) #endif static void mrb_class_inherited(mrb_state *mrb, struct RClass *super, struct RClass *klass) { mrb_value s; mrb_sym mid; if (!super) super = mrb->object_class; super->flags |= MRB_FL_CLASS_IS_INHERITED; s = mrb_obj_value(super); mc_clear_by_class(mrb, klass); mid = mrb_intern_lit(mrb, "inherited"); if (!mrb_func_basic_p(mrb, s, mid, mrb_bob_init)) { mrb_value c = mrb_obj_value(klass); mrb_funcall_argv(mrb, s, mid, 1, &c); } } MRB_API struct RClass* mrb_vm_define_class(mrb_state *mrb, mrb_value outer, mrb_value super, mrb_sym id) { struct RClass *s; struct RClass *c; if (!mrb_nil_p(super)) { if (mrb_type(super) != MRB_TT_CLASS) { mrb_raisef(mrb, E_TYPE_ERROR, "superclass must be a Class (%S given)", mrb_inspect(mrb, super)); } s = mrb_class_ptr(super); } else { s = 0; } check_if_class_or_module(mrb, outer); if (mrb_const_defined_at(mrb, outer, id)) { mrb_value old = mrb_const_get(mrb, outer, id); if (mrb_type(old) != MRB_TT_CLASS) { mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a class", mrb_inspect(mrb, old)); } c = mrb_class_ptr(old); if (s) { /* check super class */ if (mrb_class_real(c->super) != s) { mrb_raisef(mrb, E_TYPE_ERROR, "superclass mismatch for class %S", old); } } return c; } c = define_class(mrb, id, s, mrb_class_ptr(outer)); mrb_class_inherited(mrb, mrb_class_real(c->super), c); return c; } MRB_API mrb_bool mrb_class_defined(mrb_state *mrb, const char *name) { mrb_value sym = mrb_check_intern_cstr(mrb, name); if (mrb_nil_p(sym)) { return FALSE; } return mrb_const_defined(mrb, mrb_obj_value(mrb->object_class), mrb_symbol(sym)); } MRB_API mrb_bool mrb_class_defined_under(mrb_state *mrb, struct RClass *outer, const char *name) { mrb_value sym = mrb_check_intern_cstr(mrb, name); if (mrb_nil_p(sym)) { return FALSE; } return mrb_const_defined_at(mrb, mrb_obj_value(outer), mrb_symbol(sym)); } MRB_API struct RClass* mrb_class_get_under(mrb_state *mrb, struct RClass *outer, const char *name) { return class_from_sym(mrb, outer, mrb_intern_cstr(mrb, name)); } MRB_API struct RClass* mrb_class_get(mrb_state *mrb, const char *name) { return mrb_class_get_under(mrb, mrb->object_class, name); } MRB_API struct RClass* mrb_exc_get(mrb_state *mrb, const char *name) { struct RClass *exc, *e; mrb_value c = mrb_const_get(mrb, mrb_obj_value(mrb->object_class), mrb_intern_cstr(mrb, name)); if (mrb_type(c) != MRB_TT_CLASS) { mrb_raise(mrb, mrb->eException_class, "exception corrupted"); } exc = e = mrb_class_ptr(c); while (e) { if (e == mrb->eException_class) return exc; e = e->super; } return mrb->eException_class; } MRB_API struct RClass* mrb_module_get_under(mrb_state *mrb, struct RClass *outer, const char *name) { return module_from_sym(mrb, outer, mrb_intern_cstr(mrb, name)); } MRB_API struct RClass* mrb_module_get(mrb_state *mrb, const char *name) { return mrb_module_get_under(mrb, mrb->object_class, name); } /*! * Defines a class under the namespace of \a outer. * \param outer a class which contains the new class. * \param id name of the new class * \param super a class from which the new class will derive. * NULL means \c Object class. * \return the created class * \throw TypeError if the constant name \a name is already taken but * the constant is not a \c Class. * \throw NameError if the class is already defined but the class can not * be reopened because its superclass is not \a super. * \post top-level constant named \a name refers the returned class. * * \note if a class named \a name is already defined and its superclass is * \a super, the function just returns the defined class. */ MRB_API struct RClass* mrb_define_class_under(mrb_state *mrb, struct RClass *outer, const char *name, struct RClass *super) { mrb_sym id = mrb_intern_cstr(mrb, name); struct RClass * c; #if 0 if (!super) { mrb_warn(mrb, "no super class for '%S::%S', Object assumed", mrb_obj_value(outer), mrb_sym2str(mrb, id)); } #endif c = define_class(mrb, id, super, outer); setup_class(mrb, outer, c, id); return c; } MRB_API void mrb_define_method_raw(mrb_state *mrb, struct RClass *c, mrb_sym mid, mrb_method_t m) { khash_t(mt) *h; khiter_t k; MRB_CLASS_ORIGIN(c); h = c->mt; if (MRB_FROZEN_P(c)) { if (c->tt == MRB_TT_MODULE) mrb_raise(mrb, E_FROZEN_ERROR, "can't modify frozen module"); else mrb_raise(mrb, E_FROZEN_ERROR, "can't modify frozen class"); } if (!h) h = c->mt = kh_init(mt, mrb); k = kh_put(mt, mrb, h, mid); kh_value(h, k) = m; if (MRB_METHOD_PROC_P(m) && !MRB_METHOD_UNDEF_P(m)) { struct RProc *p = MRB_METHOD_PROC(m); p->flags |= MRB_PROC_SCOPE; p->c = NULL; mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)p); if (!MRB_PROC_ENV_P(p)) { MRB_PROC_SET_TARGET_CLASS(p, c); } } mc_clear_by_id(mrb, c, mid); } MRB_API void mrb_define_method_id(mrb_state *mrb, struct RClass *c, mrb_sym mid, mrb_func_t func, mrb_aspec aspec) { mrb_method_t m; int ai = mrb_gc_arena_save(mrb); MRB_METHOD_FROM_FUNC(m, func); mrb_define_method_raw(mrb, c, mid, m); mrb_gc_arena_restore(mrb, ai); } MRB_API void mrb_define_method(mrb_state *mrb, struct RClass *c, const char *name, mrb_func_t func, mrb_aspec aspec) { mrb_define_method_id(mrb, c, mrb_intern_cstr(mrb, name), func, aspec); } /* a function to raise NotImplementedError with current method name */ MRB_API void mrb_notimplement(mrb_state *mrb) { const char *str; mrb_int len; mrb_callinfo *ci = mrb->c->ci; if (ci->mid) { str = mrb_sym2name_len(mrb, ci->mid, &len); mrb_raisef(mrb, E_NOTIMP_ERROR, "%S() function is unimplemented on this machine", mrb_str_new_static(mrb, str, (size_t)len)); } } /* a function to be replacement of unimplemented method */ MRB_API mrb_value mrb_notimplement_m(mrb_state *mrb, mrb_value self) { mrb_notimplement(mrb); /* not reached */ return mrb_nil_value(); } #define CHECK_TYPE(mrb, val, t, c) do { \ if (mrb_type(val) != (t)) {\ mrb_raisef(mrb, E_TYPE_ERROR, "expected %S", mrb_str_new_lit(mrb, c));\ }\ } while (0) static mrb_value to_str(mrb_state *mrb, mrb_value val) { CHECK_TYPE(mrb, val, MRB_TT_STRING, "String"); return val; } static mrb_value to_ary(mrb_state *mrb, mrb_value val) { CHECK_TYPE(mrb, val, MRB_TT_ARRAY, "Array"); return val; } static mrb_value to_hash(mrb_state *mrb, mrb_value val) { CHECK_TYPE(mrb, val, MRB_TT_HASH, "Hash"); return val; } #define to_sym(mrb, ss) mrb_obj_to_sym(mrb, ss) MRB_API mrb_int mrb_get_argc(mrb_state *mrb) { mrb_int argc = mrb->c->ci->argc; if (argc < 0) { struct RArray *a = mrb_ary_ptr(mrb->c->stack[1]); argc = ARY_LEN(a); } return argc; } MRB_API mrb_value* mrb_get_argv(mrb_state *mrb) { mrb_int argc = mrb->c->ci->argc; mrb_value *array_argv; if (argc < 0) { struct RArray *a = mrb_ary_ptr(mrb->c->stack[1]); array_argv = ARY_PTR(a); } else { array_argv = NULL; } return array_argv; } /* retrieve arguments from mrb_state. mrb_get_args(mrb, format, ...) returns number of arguments parsed. format specifiers: string mruby type C type note ---------------------------------------------------------------------------------------------- o: Object [mrb_value] C: class/module [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; 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] n: Symbol [mrb_sym] d: Data [void*,mrb_data_type const] 2nd argument will be used to check data type so it won't be modified I: Inline struct [void*] &: Block [mrb_value] &! raises exception if no block given *: rest argument [mrb_value*,mrb_int] The rest of the arguments as an array; *! avoid copy of the stack |: optional Following arguments are optional ?: optional given [mrb_bool] true if preceding argument (optional) is given */ MRB_API mrb_int mrb_get_args(mrb_state *mrb, const char *format, ...) { const char *fmt = format; char c; mrb_int i = 0; va_list ap; mrb_int argc = mrb_get_argc(mrb); mrb_int arg_i = 0; mrb_value *array_argv = mrb_get_argv(mrb); mrb_bool opt = FALSE; mrb_bool opt_skip = TRUE; mrb_bool given = TRUE; va_start(ap, format); #define ARGV \ (array_argv ? array_argv : (mrb->c->stack + 1)) while ((c = *fmt++)) { switch (c) { case '|': opt = TRUE; break; case '*': opt_skip = FALSE; goto check_exit; case '!': break; case '&': case '?': if (opt) opt_skip = FALSE; break; default: break; } } check_exit: opt = FALSE; i = 0; while ((c = *format++)) { switch (c) { case '|': case '*': case '&': case '?': break; default: if (argc <= i) { if (opt) { given = FALSE; } else { mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong number of arguments"); } } break; } switch (c) { case 'o': { mrb_value *p; p = va_arg(ap, mrb_value*); if (i < argc) { *p = ARGV[arg_i++]; i++; } } break; case 'C': { mrb_value *p; p = va_arg(ap, mrb_value*); if (i < argc) { mrb_value ss; ss = ARGV[arg_i++]; if (!class_ptr_p(ss)) { mrb_raisef(mrb, E_TYPE_ERROR, "%S is not class/module", ss); } *p = ss; i++; } } break; case 'S': { mrb_value *p; p = va_arg(ap, mrb_value*); if (*format == '!') { format++; if (i < argc && mrb_nil_p(ARGV[arg_i])) { *p = ARGV[arg_i++]; i++; break; } } if (i < argc) { *p = to_str(mrb, ARGV[arg_i++]); i++; } } break; case 'A': { mrb_value *p; p = va_arg(ap, mrb_value*); if (*format == '!') { format++; if (i < argc && mrb_nil_p(ARGV[arg_i])) { *p = ARGV[arg_i++]; i++; break; } } if (i < argc) { *p = to_ary(mrb, ARGV[arg_i++]); i++; } } break; case 'H': { mrb_value *p; p = va_arg(ap, mrb_value*); if (*format == '!') { format++; if (i < argc && mrb_nil_p(ARGV[arg_i])) { *p = ARGV[arg_i++]; i++; break; } } if (i < argc) { *p = to_hash(mrb, ARGV[arg_i++]); i++; } } break; case 's': { mrb_value ss; char **ps = 0; mrb_int *pl = 0; ps = va_arg(ap, char**); pl = va_arg(ap, mrb_int*); if (*format == '!') { format++; if (i < argc && mrb_nil_p(ARGV[arg_i])) { *ps = NULL; *pl = 0; i++; arg_i++; break; } } if (i < argc) { ss = to_str(mrb, ARGV[arg_i++]); *ps = RSTRING_PTR(ss); *pl = RSTRING_LEN(ss); i++; } } break; case 'z': { mrb_value ss; const char **ps; ps = va_arg(ap, const char**); if (*format == '!') { format++; if (i < argc && mrb_nil_p(ARGV[arg_i])) { *ps = NULL; i++; arg_i++; break; } } if (i < argc) { ss = to_str(mrb, ARGV[arg_i++]); *ps = mrb_string_value_cstr(mrb, &ss); i++; } } break; case 'a': { mrb_value aa; struct RArray *a; mrb_value **pb; mrb_int *pl; pb = va_arg(ap, mrb_value**); pl = va_arg(ap, mrb_int*); if (*format == '!') { format++; if (i < argc && mrb_nil_p(ARGV[arg_i])) { *pb = 0; *pl = 0; i++; arg_i++; break; } } if (i < argc) { aa = to_ary(mrb, ARGV[arg_i++]); a = mrb_ary_ptr(aa); *pb = ARY_PTR(a); *pl = ARY_LEN(a); i++; } } break; case 'I': { void* *p; mrb_value ss; p = va_arg(ap, void**); if (i < argc) { ss = ARGV[arg_i]; if (mrb_type(ss) != MRB_TT_ISTRUCT) { mrb_raisef(mrb, E_TYPE_ERROR, "%S is not inline struct", ss); } *p = mrb_istruct_ptr(ss); arg_i++; i++; } } break; #ifndef MRB_WITHOUT_FLOAT case 'f': { mrb_float *p; p = va_arg(ap, mrb_float*); if (i < argc) { *p = mrb_to_flo(mrb, ARGV[arg_i]); arg_i++; i++; } } break; #endif case 'i': { mrb_int *p; p = va_arg(ap, mrb_int*); if (i < argc) { switch (mrb_type(ARGV[arg_i])) { case MRB_TT_FIXNUM: *p = mrb_fixnum(ARGV[arg_i]); break; #ifndef MRB_WITHOUT_FLOAT case MRB_TT_FLOAT: { mrb_float f = mrb_float(ARGV[arg_i]); if (!FIXABLE_FLOAT(f)) { mrb_raise(mrb, E_RANGE_ERROR, "float too big for int"); } *p = (mrb_int)f; } break; #endif case MRB_TT_STRING: mrb_raise(mrb, E_TYPE_ERROR, "no implicit conversion of String into Integer"); break; default: *p = mrb_fixnum(mrb_Integer(mrb, ARGV[arg_i])); break; } arg_i++; i++; } } break; case 'b': { mrb_bool *boolp = va_arg(ap, mrb_bool*); if (i < argc) { mrb_value b = ARGV[arg_i++]; *boolp = mrb_test(b); i++; } } break; case 'n': { mrb_sym *symp; symp = va_arg(ap, mrb_sym*); if (i < argc) { mrb_value ss; ss = ARGV[arg_i++]; *symp = to_sym(mrb, ss); i++; } } break; case 'd': { void** datap; struct mrb_data_type const* type; datap = va_arg(ap, void**); type = va_arg(ap, struct mrb_data_type const*); if (*format == '!') { format++; if (i < argc && mrb_nil_p(ARGV[arg_i])) { *datap = 0; i++; arg_i++; break; } } if (i < argc) { *datap = mrb_data_get_ptr(mrb, ARGV[arg_i++], type); ++i; } } break; case '&': { mrb_value *p, *bp; p = va_arg(ap, mrb_value*); if (mrb->c->ci->argc < 0) { bp = mrb->c->stack + 2; } else { bp = mrb->c->stack + mrb->c->ci->argc + 1; } if (*format == '!') { format ++; if (mrb_nil_p(*bp)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given"); } } *p = *bp; } break; case '|': if (opt_skip && i == argc) return argc; opt = TRUE; break; case '?': { mrb_bool *p; p = va_arg(ap, mrb_bool*); *p = given; } break; case '*': { mrb_value **var; mrb_int *pl; mrb_bool nocopy = array_argv ? TRUE : FALSE; if (*format == '!') { format++; nocopy = TRUE; } var = va_arg(ap, mrb_value**); pl = va_arg(ap, mrb_int*); if (argc > i) { *pl = argc-i; if (*pl > 0) { if (nocopy) { *var = ARGV+arg_i; } else { mrb_value args = mrb_ary_new_from_values(mrb, *pl, ARGV+arg_i); RARRAY(args)->c = NULL; *var = RARRAY_PTR(args); } } i = argc; arg_i += *pl; } else { *pl = 0; *var = NULL; } } break; default: mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid argument specifier %S", mrb_str_new(mrb, &c, 1)); break; } } #undef ARGV if (!c && argc > i) { mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong number of arguments"); } va_end(ap); return i; } static struct RClass* boot_defclass(mrb_state *mrb, struct RClass *super) { struct RClass *c; c = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_CLASS, mrb->class_class); if (super) { c->super = super; mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)super); } else { c->super = mrb->object_class; } c->mt = kh_init(mt, mrb); return c; } static void boot_initmod(mrb_state *mrb, struct RClass *mod) { if (!mod->mt) { mod->mt = kh_init(mt, mrb); } } 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; } MRB_CLASS_ORIGIN(m); ic->iv = m->iv; ic->mt = m->mt; ic->super = super; if (m->tt == MRB_TT_ICLASS) { ic->c = m->c; } else { ic->c = m; } return ic; } 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 = find_origin(c)->mt; while (m) { int superclass_seen = 0; if (m->flags & MRB_FL_CLASS_IS_PREPENDED) goto skip; if (klass_mt && klass_mt == m->mt) return -1; p = c->super; while (p) { if (p->tt == MRB_TT_ICLASS) { if (p->mt == m->mt) { if (!superclass_seen) { ins_pos = p; /* move insert point */ } goto skip; } } else if (p->tt == MRB_TT_CLASS) { if (!search_super) break; superclass_seen = 1; } p = p->super; } ic = include_class_new(mrb, m, ins_pos->super); m->flags |= MRB_FL_CLASS_IS_INHERITED; ins_pos->super = ic; mrb_field_write_barrier(mrb, (struct RBasic*)ins_pos, (struct RBasic*)ic); mc_clear_by_class(mrb, ins_pos); ins_pos = ic; skip: m = m->super; } mc_clear_all(mrb); return 0; } MRB_API void mrb_include_module(mrb_state *mrb, struct RClass *c, struct RClass *m) { int changed = include_module_at(mrb, c, find_origin(c), m, 1); if (changed < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic include detected"); } } MRB_API void mrb_prepend_module(mrb_state *mrb, struct RClass *c, struct RClass *m) { struct RClass *origin; int changed = 0; if (!(c->flags & MRB_FL_CLASS_IS_PREPENDED)) { origin = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, c); origin->flags |= MRB_FL_CLASS_IS_ORIGIN | MRB_FL_CLASS_IS_INHERITED; origin->super = c->super; c->super = origin; origin->mt = c->mt; c->mt = kh_init(mt, mrb); mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)origin); c->flags |= MRB_FL_CLASS_IS_PREPENDED; } changed = include_module_at(mrb, c, c, m, 0); if (changed < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic prepend detected"); } } 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_append_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_include_module(mrb, mrb_class_ptr(klass), mrb_class_ptr(mod)); return mod; } /* 15.2.2.4.28 */ /* * call-seq: * mod.include?(module) -> true or false * * Returns true if module is included in * mod or one of mod's ancestors. * * module A * end * class B * include A * end * class C < B * end * B.include?(A) #=> true * C.include?(A) #=> true * A.include?(A) #=> false */ static mrb_value mrb_mod_include_p(mrb_state *mrb, mrb_value mod) { mrb_value mod2; struct RClass *c = mrb_class_ptr(mod); mrb_get_args(mrb, "C", &mod2); mrb_check_type(mrb, mod2, MRB_TT_MODULE); while (c) { if (c->tt == MRB_TT_ICLASS) { if (c->c == mrb_class_ptr(mod2)) return mrb_true_value(); } c = c->super; } return mrb_false_value(); } static mrb_value mrb_mod_ancestors(mrb_state *mrb, mrb_value self) { mrb_value result; struct RClass *c = mrb_class_ptr(self); result = mrb_ary_new(mrb); while (c) { if (c->tt == MRB_TT_ICLASS) { mrb_ary_push(mrb, result, mrb_obj_value(c->c)); } else if (!(c->flags & MRB_FL_CLASS_IS_PREPENDED)) { mrb_ary_push(mrb, result, mrb_obj_value(c)); } c = c->super; } return result; } static mrb_value mrb_mod_extend_object(mrb_state *mrb, mrb_value mod) { mrb_value obj; mrb_check_type(mrb, mod, MRB_TT_MODULE); mrb_get_args(mrb, "o", &obj); mrb_include_module(mrb, mrb_class_ptr(mrb_singleton_class(mrb, obj)), mrb_class_ptr(mod)); return mod; } static mrb_value mrb_mod_initialize(mrb_state *mrb, mrb_value mod) { mrb_value b; struct RClass *m = mrb_class_ptr(mod); 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); } return mod; } /* implementation of module_eval/class_eval */ mrb_value mrb_mod_module_eval(mrb_state*, mrb_value); static mrb_value mrb_mod_dummy_visibility(mrb_state *mrb, mrb_value mod) { return mod; } MRB_API mrb_value mrb_singleton_class(mrb_state *mrb, mrb_value v) { struct RBasic *obj; switch (mrb_type(v)) { case MRB_TT_FALSE: if (mrb_nil_p(v)) return mrb_obj_value(mrb->nil_class); return mrb_obj_value(mrb->false_class); case MRB_TT_TRUE: return mrb_obj_value(mrb->true_class); case MRB_TT_CPTR: return mrb_obj_value(mrb->object_class); case MRB_TT_SYMBOL: case MRB_TT_FIXNUM: #ifndef MRB_WITHOUT_FLOAT case MRB_TT_FLOAT: #endif mrb_raise(mrb, E_TYPE_ERROR, "can't define singleton"); return mrb_nil_value(); /* not reached */ default: break; } obj = mrb_basic_ptr(v); prepare_singleton_class(mrb, obj); return mrb_obj_value(obj->c); } MRB_API void mrb_define_singleton_method(mrb_state *mrb, struct RObject *o, const char *name, mrb_func_t func, mrb_aspec aspec) { prepare_singleton_class(mrb, (struct RBasic*)o); mrb_define_method_id(mrb, o->c, mrb_intern_cstr(mrb, name), func, aspec); } MRB_API void mrb_define_class_method(mrb_state *mrb, struct RClass *c, const char *name, mrb_func_t func, mrb_aspec aspec) { mrb_define_singleton_method(mrb, (struct RObject*)c, name, func, aspec); } MRB_API void mrb_define_module_function(mrb_state *mrb, struct RClass *c, const char *name, mrb_func_t func, mrb_aspec aspec) { mrb_define_class_method(mrb, c, name, func, aspec); mrb_define_method(mrb, c, name, func, aspec); } #ifdef MRB_METHOD_CACHE static void mc_clear_all(mrb_state *mrb) { struct mrb_cache_entry *mc = mrb->cache; int i; for (i=0; icache; int i; if (c->flags & MRB_FL_CLASS_IS_INHERITED) { mc_clear_all(mrb); c->flags &= ~MRB_FL_CLASS_IS_INHERITED; return; } for (i=0; icache; int i; if (c->flags & MRB_FL_CLASS_IS_INHERITED) { mc_clear_all(mrb); c->flags &= ~MRB_FL_CLASS_IS_INHERITED; return; } for (i=0; icache[h]; if (mc->c == c && mc->mid == mid) { *cp = mc->c0; return mc->m; } #endif while (c) { khash_t(mt) *h = c->mt; if (h) { k = kh_get(mt, mrb, h, mid); if (k != kh_end(h)) { m = kh_value(h, k); if (MRB_METHOD_UNDEF_P(m)) break; *cp = c; #ifdef MRB_METHOD_CACHE mc->c = oc; mc->c0 = c; mc->mid = mid; mc->m = m; #endif return m; } } c = c->super; } MRB_METHOD_FROM_PROC(m, NULL); return m; /* no method */ } MRB_API mrb_method_t mrb_method_search(mrb_state *mrb, struct RClass* c, mrb_sym mid) { mrb_method_t m; m = mrb_method_search_vm(mrb, &c, mid); if (MRB_METHOD_UNDEF_P(m)) { mrb_value inspect = mrb_funcall(mrb, mrb_obj_value(c), "inspect", 0); if (mrb_string_p(inspect) && RSTRING_LEN(inspect) > 64) { inspect = mrb_any_to_s(mrb, mrb_obj_value(c)); } mrb_name_error(mrb, mid, "undefined method '%S' for class %S", mrb_sym2str(mrb, mid), inspect); } return m; } static mrb_value attr_reader(mrb_state *mrb, mrb_value obj) { mrb_value name = mrb_proc_cfunc_env_get(mrb, 0); return mrb_iv_get(mrb, obj, to_sym(mrb, name)); } static mrb_value mrb_mod_attr_reader(mrb_state *mrb, mrb_value mod) { struct RClass *c = mrb_class_ptr(mod); mrb_value *argv; mrb_int argc, i; int ai; mrb_get_args(mrb, "*", &argv, &argc); ai = mrb_gc_arena_save(mrb); for (i=0; i obj * * 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`. * */ MRB_API mrb_value mrb_instance_new(mrb_state *mrb, mrb_value cv) { mrb_value obj, blk; mrb_value *argv; mrb_int argc; mrb_sym init; mrb_method_t m; mrb_get_args(mrb, "*&", &argv, &argc, &blk); obj = mrb_instance_alloc(mrb, cv); init = mrb_intern_lit(mrb, "initialize"); m = mrb_method_search(mrb, mrb_class(mrb, obj), init); if (MRB_METHOD_CFUNC_P(m)) { mrb_func_t f = MRB_METHOD_CFUNC(m); if (f != mrb_bob_init) { f(mrb, obj); } } else { mrb_funcall_with_block(mrb, obj, init, argc, argv, blk); } return obj; } MRB_API mrb_value mrb_obj_new(mrb_state *mrb, struct RClass *c, mrb_int argc, const mrb_value *argv) { mrb_value obj; mrb_sym mid; obj = mrb_instance_alloc(mrb, mrb_obj_value(c)); mid = mrb_intern_lit(mrb, "initialize"); if (!mrb_func_basic_p(mrb, obj, mid, mrb_bob_init)) { mrb_funcall_argv(mrb, obj, mid, argc, argv); } return obj; } static mrb_value mrb_class_initialize(mrb_state *mrb, mrb_value c) { mrb_value a, b; mrb_get_args(mrb, "|C&", &a, &b); if (!mrb_nil_p(b)) { mrb_yield_with_class(mrb, b, 1, &c, c, mrb_class_ptr(c)); } return c; } static mrb_value mrb_class_new_class(mrb_state *mrb, mrb_value cv) { mrb_int n; mrb_value super, blk; mrb_value new_class; mrb_sym mid; n = mrb_get_args(mrb, "|C&", &super, &blk); if (n == 0) { super = mrb_obj_value(mrb->object_class); } new_class = mrb_obj_value(mrb_class_new(mrb, mrb_class_ptr(super))); mid = mrb_intern_lit(mrb, "initialize"); if (!mrb_func_basic_p(mrb, new_class, mid, mrb_bob_init)) { mrb_funcall_with_block(mrb, new_class, mid, n, &super, blk); } mrb_class_inherited(mrb, mrb_class_ptr(super), mrb_class_ptr(new_class)); return new_class; } static mrb_value mrb_class_superclass(mrb_state *mrb, mrb_value klass) { struct RClass *c; c = mrb_class_ptr(klass); c = find_origin(c)->super; while (c && c->tt == MRB_TT_ICLASS) { c = find_origin(c)->super; } if (!c) return mrb_nil_value(); return mrb_obj_value(c); } static mrb_value mrb_bob_init(mrb_state *mrb, mrb_value cv) { return mrb_nil_value(); } static mrb_value mrb_bob_not(mrb_state *mrb, mrb_value cv) { return mrb_bool_value(!mrb_test(cv)); } /* 15.3.1.3.1 */ /* 15.3.1.3.10 */ /* 15.3.1.3.11 */ /* * call-seq: * obj == other -> true or false * obj.equal?(other) -> true or false * obj.eql?(other) -> true or false * * Equality---At the Object level, == returns * true only if obj and other are the * same object. Typically, this method is overridden in descendant * classes to provide class-specific meaning. * * Unlike ==, the equal? method should never be * overridden by subclasses: it is used to determine object identity * (that is, a.equal?(b) iff a is the same * object as b). * * The eql? method returns true if * obj and anObject have the same value. Used by * Hash to test members for equality. For objects of * class Object, eql? is synonymous with * ==. Subclasses normally continue this tradition, but * there are exceptions. Numeric types, for example, * perform type conversion across ==, but not across * eql?, so: * * 1 == 1.0 #=> true * 1.eql? 1.0 #=> false */ mrb_value mrb_obj_equal_m(mrb_state *mrb, mrb_value self) { mrb_value arg; mrb_get_args(mrb, "o", &arg); return mrb_bool_value(mrb_obj_equal(mrb, self, arg)); } static mrb_value mrb_obj_not_equal_m(mrb_state *mrb, mrb_value self) { mrb_value arg; mrb_get_args(mrb, "o", &arg); return mrb_bool_value(!mrb_equal(mrb, self, arg)); } MRB_API mrb_bool mrb_obj_respond_to(mrb_state *mrb, struct RClass* c, mrb_sym mid) { mrb_method_t m; m = mrb_method_search_vm(mrb, &c, mid); if (MRB_METHOD_UNDEF_P(m)) { return FALSE; } return TRUE; } MRB_API mrb_bool mrb_respond_to(mrb_state *mrb, mrb_value obj, mrb_sym mid) { return mrb_obj_respond_to(mrb, mrb_class(mrb, obj), mid); } MRB_API mrb_value mrb_class_path(mrb_state *mrb, struct RClass *c) { mrb_value path; mrb_sym nsym = mrb_intern_lit(mrb, "__classname__"); path = mrb_obj_iv_get(mrb, (struct RObject*)c, nsym); if (mrb_nil_p(path)) { /* no name (yet) */ return mrb_class_find_path(mrb, c); } else if (mrb_symbol_p(path)) { /* toplevel class/module */ const char *str; mrb_int len; str = mrb_sym2name_len(mrb, mrb_symbol(path), &len); return mrb_str_new(mrb, str, len); } return mrb_str_dup(mrb, path); } MRB_API struct RClass* mrb_class_real(struct RClass* cl) { if (cl == 0) return NULL; while ((cl->tt == MRB_TT_SCLASS) || (cl->tt == MRB_TT_ICLASS)) { cl = cl->super; if (cl == 0) return NULL; } return cl; } MRB_API const char* mrb_class_name(mrb_state *mrb, struct RClass* c) { mrb_value path = mrb_class_path(mrb, c); if (mrb_nil_p(path)) { path = c->tt == MRB_TT_MODULE ? mrb_str_new_lit(mrb, "#"); } return RSTRING_PTR(path); } MRB_API const char* mrb_obj_classname(mrb_state *mrb, mrb_value obj) { return mrb_class_name(mrb, mrb_obj_class(mrb, obj)); } /*! * Ensures a class can be derived from super. * * \param super a reference to an object. * \exception TypeError if \a super is not a Class or \a super is a singleton class. */ static void mrb_check_inheritable(mrb_state *mrb, struct RClass *super) { if (super->tt != MRB_TT_CLASS) { mrb_raisef(mrb, E_TYPE_ERROR, "superclass must be a Class (%S given)", mrb_obj_value(super)); } if (super->tt == MRB_TT_SCLASS) { mrb_raise(mrb, E_TYPE_ERROR, "can't make subclass of singleton class"); } if (super == mrb->class_class) { mrb_raise(mrb, E_TYPE_ERROR, "can't make subclass of Class"); } } /*! * Creates a new class. * \param super a class from which the new class derives. * \exception TypeError \a super is not inheritable. * \exception TypeError \a super is the Class class. */ MRB_API struct RClass* mrb_class_new(mrb_state *mrb, struct RClass *super) { struct RClass *c; if (super) { mrb_check_inheritable(mrb, super); } c = boot_defclass(mrb, super); if (super) { MRB_SET_INSTANCE_TT(c, MRB_INSTANCE_TT(super)); } make_metaclass(mrb, c); return c; } /*! * Creates a new module. */ 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); boot_initmod(mrb, m); return m; } /* * call-seq: * obj.class => class * * Returns the class of obj, now preferred over * Object#type, as an object's type in Ruby is only * loosely tied to that object's class. This method must always be * called with an explicit receiver, as class is also a * reserved word in Ruby. * * 1.class #=> Fixnum * self.class #=> Object */ MRB_API struct RClass* mrb_obj_class(mrb_state *mrb, mrb_value obj) { return mrb_class_real(mrb_class(mrb, obj)); } MRB_API void mrb_alias_method(mrb_state *mrb, struct RClass *c, mrb_sym a, mrb_sym b) { mrb_method_t m = mrb_method_search(mrb, c, b); mrb_define_method_raw(mrb, c, a, m); } /*! * Defines an alias of a method. * \param klass the class which the original method belongs to * \param name1 a new name for the method * \param name2 the original name of the method */ MRB_API void mrb_define_alias(mrb_state *mrb, struct RClass *klass, const char *name1, const char *name2) { mrb_alias_method(mrb, klass, mrb_intern_cstr(mrb, name1), mrb_intern_cstr(mrb, name2)); } /* * call-seq: * mod.to_s -> string * * Return a string representing this module or class. For basic * classes and modules, this is the name. For singletons, we * show information on the thing we're attached to as well. */ static mrb_value mrb_mod_to_s(mrb_state *mrb, mrb_value klass) { mrb_value str; if (mrb_type(klass) == MRB_TT_SCLASS) { mrb_value v = mrb_iv_get(mrb, klass, mrb_intern_lit(mrb, "__attached__")); str = mrb_str_new_lit(mrb, "#"); } else { struct RClass *c; mrb_value path; str = mrb_str_new_capa(mrb, 32); c = mrb_class_ptr(klass); path = mrb_class_path(mrb, c); if (mrb_nil_p(path)) { switch (mrb_type(klass)) { case MRB_TT_CLASS: mrb_str_cat_lit(mrb, str, "#"); } else { return path; } } } static mrb_value mrb_mod_alias(mrb_state *mrb, mrb_value mod) { struct RClass *c = mrb_class_ptr(mod); mrb_sym new_name, old_name; mrb_get_args(mrb, "nn", &new_name, &old_name); mrb_alias_method(mrb, c, new_name, old_name); return mrb_nil_value(); } void mrb_undef_method_id(mrb_state *mrb, struct RClass *c, mrb_sym a) { if (!mrb_obj_respond_to(mrb, c, a)) { mrb_name_error(mrb, a, "undefined method '%S' for class '%S'", mrb_sym2str(mrb, a), mrb_obj_value(c)); } else { mrb_method_t m; MRB_METHOD_FROM_PROC(m, NULL); mrb_define_method_raw(mrb, c, a, m); } } MRB_API void mrb_undef_method(mrb_state *mrb, struct RClass *c, const char *name) { mrb_undef_method_id(mrb, c, mrb_intern_cstr(mrb, name)); } MRB_API void mrb_undef_class_method(mrb_state *mrb, struct RClass *c, const char *name) { mrb_undef_method(mrb, mrb_class_ptr(mrb_singleton_class(mrb, mrb_obj_value(c))), name); } static mrb_value mrb_mod_undef(mrb_state *mrb, mrb_value mod) { struct RClass *c = mrb_class_ptr(mod); mrb_int argc; mrb_value *argv; mrb_get_args(mrb, "*", &argv, &argc); while (argc--) { mrb_undef_method_id(mrb, c, to_sym(mrb, *argv)); argv++; } return mrb_nil_value(); } static void check_const_name_str(mrb_state *mrb, mrb_value str) { if (RSTRING_LEN(str) < 1 || !ISUPPER(*RSTRING_PTR(str))) { mrb_name_error(mrb, mrb_intern_str(mrb, str), "wrong constant name %S", str); } } static void check_const_name_sym(mrb_state *mrb, mrb_sym id) { check_const_name_str(mrb, mrb_sym2str(mrb, id)); } static mrb_value mrb_mod_const_defined(mrb_state *mrb, mrb_value mod) { mrb_sym id; mrb_bool inherit = TRUE; mrb_get_args(mrb, "n|b", &id, &inherit); check_const_name_sym(mrb, id); if (inherit) { return mrb_bool_value(mrb_const_defined(mrb, mod, id)); } return mrb_bool_value(mrb_const_defined_at(mrb, mod, id)); } static mrb_value mrb_const_get_sym(mrb_state *mrb, mrb_value mod, mrb_sym id) { check_const_name_sym(mrb, id); return mrb_const_get(mrb, mod, id); } static mrb_value mrb_mod_const_get(mrb_state *mrb, mrb_value mod) { mrb_value path; mrb_sym id; char *ptr; mrb_int off, end, len; mrb_get_args(mrb, "o", &path); if (mrb_symbol_p(path)) { /* const get with symbol */ id = mrb_symbol(path); return mrb_const_get_sym(mrb, mod, id); } /* const get with class path string */ path = mrb_ensure_string_type(mrb, path); ptr = RSTRING_PTR(path); len = RSTRING_LEN(path); off = 0; while (off < len) { end = mrb_str_index_lit(mrb, path, "::", off); end = (end == -1) ? len : end; id = mrb_intern(mrb, ptr+off, end-off); mod = mrb_const_get_sym(mrb, mod, id); if (end == len) off = end; else { off = end + 2; if (off == len) { /* trailing "::" */ mrb_name_error(mrb, id, "wrong constant name '%S'", path); } } } return mod; } static mrb_value mrb_mod_const_set(mrb_state *mrb, mrb_value mod) { mrb_sym id; mrb_value value; mrb_get_args(mrb, "no", &id, &value); check_const_name_sym(mrb, id); mrb_const_set(mrb, mod, id, value); return value; } static mrb_value mrb_mod_remove_const(mrb_state *mrb, mrb_value mod) { mrb_sym id; mrb_value val; mrb_get_args(mrb, "n", &id); check_const_name_sym(mrb, id); val = mrb_iv_remove(mrb, mod, id); if (mrb_undef_p(val)) { mrb_name_error(mrb, id, "constant %S not defined", mrb_sym2str(mrb, id)); } return val; } static mrb_value mrb_mod_const_missing(mrb_state *mrb, mrb_value mod) { mrb_sym sym; mrb_get_args(mrb, "n", &sym); if (mrb_class_real(mrb_class_ptr(mod)) != mrb->object_class) { mrb_name_error(mrb, sym, "uninitialized constant %S::%S", mod, mrb_sym2str(mrb, sym)); } else { mrb_name_error(mrb, sym, "uninitialized constant %S", mrb_sym2str(mrb, sym)); } /* not reached */ return mrb_nil_value(); } /* 15.2.2.4.34 */ /* * call-seq: * mod.method_defined?(symbol) -> true or false * * Returns +true+ if the named method is defined by * _mod_ (or its included modules and, if _mod_ is a class, * its ancestors). Public and protected methods are matched. * * module A * def method1() end * end * class B * def method2() end * end * class C < B * include A * def method3() end * end * * A.method_defined? :method1 #=> true * C.method_defined? "method1" #=> true * C.method_defined? "method2" #=> true * C.method_defined? "method3" #=> true * C.method_defined? "method4" #=> false */ static mrb_value mrb_mod_method_defined(mrb_state *mrb, mrb_value mod) { mrb_sym id; mrb_get_args(mrb, "n", &id); return mrb_bool_value(mrb_obj_respond_to(mrb, mrb_class_ptr(mod), id)); } static mrb_value mod_define_method(mrb_state *mrb, mrb_value self) { struct RClass *c = mrb_class_ptr(self); struct RProc *p; mrb_method_t m; mrb_sym mid; mrb_value proc = mrb_undef_value(); mrb_value blk; mrb_get_args(mrb, "n|o&", &mid, &proc, &blk); switch (mrb_type(proc)) { case MRB_TT_PROC: blk = proc; break; case MRB_TT_UNDEF: /* ignored */ break; default: mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %S (expected Proc)", mrb_obj_value(mrb_obj_class(mrb, proc))); break; } if (mrb_nil_p(blk)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given"); } p = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb->proc_class); mrb_proc_copy(p, mrb_proc_ptr(blk)); p->flags |= MRB_PROC_STRICT; MRB_METHOD_FROM_PROC(m, p); mrb_define_method_raw(mrb, c, mid, m); return mrb_symbol_value(mid); } static mrb_value top_define_method(mrb_state *mrb, mrb_value self) { return mod_define_method(mrb, mrb_obj_value(mrb->object_class)); } static mrb_value mrb_mod_eqq(mrb_state *mrb, mrb_value mod) { mrb_value obj; mrb_bool eqq; mrb_get_args(mrb, "o", &obj); eqq = mrb_obj_is_kind_of(mrb, obj, mrb_class_ptr(mod)); return mrb_bool_value(eqq); } MRB_API mrb_value mrb_mod_module_function(mrb_state *mrb, mrb_value mod) { mrb_value *argv; mrb_int argc, i; mrb_sym mid; mrb_method_t m; struct RClass *rclass; int ai; mrb_check_type(mrb, mod, MRB_TT_MODULE); mrb_get_args(mrb, "*", &argv, &argc); if (argc == 0) { /* set MODFUNC SCOPE if implemented */ return mod; } /* set PRIVATE method visibility if implemented */ /* mrb_mod_dummy_visibility(mrb, mod); */ for (i=0; ic, mid, m); mrb_gc_arena_restore(mrb, ai); } return mod; } /* implementation of __id__ */ mrb_value mrb_obj_id_m(mrb_state *mrb, mrb_value self); /* implementation of instance_eval */ mrb_value mrb_obj_instance_eval(mrb_state*, mrb_value); static mrb_value inspect_main(mrb_state *mrb, mrb_value mod) { return mrb_str_new_lit(mrb, "main"); } void mrb_init_class(mrb_state *mrb) { struct RClass *bob; /* BasicObject */ struct RClass *obj; /* Object */ struct RClass *mod; /* Module */ struct RClass *cls; /* Class */ /* boot class hierarchy */ bob = boot_defclass(mrb, 0); obj = boot_defclass(mrb, bob); mrb->object_class = obj; mod = boot_defclass(mrb, obj); mrb->module_class = mod;/* obj -> mod */ cls = boot_defclass(mrb, mod); mrb->class_class = cls; /* obj -> cls */ /* fix-up loose ends */ bob->c = obj->c = mod->c = cls->c = cls; make_metaclass(mrb, bob); make_metaclass(mrb, obj); make_metaclass(mrb, mod); make_metaclass(mrb, cls); /* name basic classes */ mrb_define_const(mrb, bob, "BasicObject", mrb_obj_value(bob)); mrb_define_const(mrb, obj, "BasicObject", mrb_obj_value(bob)); mrb_define_const(mrb, obj, "Object", mrb_obj_value(obj)); mrb_define_const(mrb, obj, "Module", mrb_obj_value(mod)); mrb_define_const(mrb, obj, "Class", mrb_obj_value(cls)); /* name each classes */ mrb_class_name_class(mrb, NULL, bob, mrb_intern_lit(mrb, "BasicObject")); mrb_class_name_class(mrb, NULL, obj, mrb_intern_lit(mrb, "Object")); /* 15.2.1 */ mrb_class_name_class(mrb, NULL, mod, mrb_intern_lit(mrb, "Module")); /* 15.2.2 */ mrb_class_name_class(mrb, NULL, cls, mrb_intern_lit(mrb, "Class")); /* 15.2.3 */ mrb->proc_class = mrb_define_class(mrb, "Proc", mrb->object_class); /* 15.2.17 */ MRB_SET_INSTANCE_TT(mrb->proc_class, MRB_TT_PROC); MRB_SET_INSTANCE_TT(cls, MRB_TT_CLASS); mrb_define_method(mrb, bob, "initialize", mrb_bob_init, MRB_ARGS_NONE()); mrb_define_method(mrb, bob, "!", mrb_bob_not, MRB_ARGS_NONE()); mrb_define_method(mrb, bob, "==", mrb_obj_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.1 */ mrb_define_method(mrb, bob, "!=", mrb_obj_not_equal_m, MRB_ARGS_REQ(1)); mrb_define_method(mrb, bob, "__id__", mrb_obj_id_m, MRB_ARGS_NONE()); /* 15.3.1.3.4 */ mrb_define_method(mrb, bob, "__send__", mrb_f_send, MRB_ARGS_ANY()); /* 15.3.1.3.5 */ mrb_define_method(mrb, bob, "instance_eval", mrb_obj_instance_eval, MRB_ARGS_ANY()); /* 15.3.1.3.18 */ mrb_define_class_method(mrb, cls, "new", mrb_class_new_class, MRB_ARGS_OPT(1)); mrb_define_method(mrb, cls, "superclass", mrb_class_superclass, MRB_ARGS_NONE()); /* 15.2.3.3.4 */ mrb_define_method(mrb, cls, "new", mrb_instance_new, MRB_ARGS_ANY()); /* 15.2.3.3.3 */ mrb_define_method(mrb, cls, "initialize", mrb_class_initialize, MRB_ARGS_OPT(1)); /* 15.2.3.3.1 */ mrb_define_method(mrb, cls, "inherited", mrb_bob_init, MRB_ARGS_REQ(1)); MRB_SET_INSTANCE_TT(mod, MRB_TT_MODULE); mrb_define_method(mrb, mod, "extend_object", mrb_mod_extend_object, MRB_ARGS_REQ(1)); /* 15.2.2.4.25 */ mrb_define_method(mrb, mod, "extended", mrb_bob_init, MRB_ARGS_REQ(1)); /* 15.2.2.4.26 */ mrb_define_method(mrb, mod, "prepended", mrb_bob_init, MRB_ARGS_REQ(1)); mrb_define_method(mrb, mod, "prepend_features", mrb_mod_prepend_features, MRB_ARGS_REQ(1)); mrb_define_method(mrb, mod, "include?", mrb_mod_include_p, MRB_ARGS_REQ(1)); /* 15.2.2.4.28 */ mrb_define_method(mrb, mod, "append_features", mrb_mod_append_features, MRB_ARGS_REQ(1)); /* 15.2.2.4.10 */ mrb_define_method(mrb, mod, "class_eval", mrb_mod_module_eval, MRB_ARGS_ANY()); /* 15.2.2.4.15 */ mrb_define_method(mrb, mod, "included", mrb_bob_init, MRB_ARGS_REQ(1)); /* 15.2.2.4.29 */ mrb_define_method(mrb, mod, "initialize", mrb_mod_initialize, MRB_ARGS_NONE()); /* 15.2.2.4.31 */ mrb_define_method(mrb, mod, "module_eval", mrb_mod_module_eval, MRB_ARGS_ANY()); /* 15.2.2.4.35 */ mrb_define_method(mrb, mod, "module_function", mrb_mod_module_function, MRB_ARGS_ANY()); mrb_define_method(mrb, mod, "private", mrb_mod_dummy_visibility, MRB_ARGS_ANY()); /* 15.2.2.4.36 */ mrb_define_method(mrb, mod, "protected", mrb_mod_dummy_visibility, MRB_ARGS_ANY()); /* 15.2.2.4.37 */ mrb_define_method(mrb, mod, "public", mrb_mod_dummy_visibility, MRB_ARGS_ANY()); /* 15.2.2.4.38 */ 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()); mrb_define_method(mrb, mod, "inspect", mrb_mod_to_s, MRB_ARGS_NONE()); mrb_define_method(mrb, mod, "alias_method", mrb_mod_alias, MRB_ARGS_ANY()); /* 15.2.2.4.8 */ mrb_define_method(mrb, mod, "ancestors", mrb_mod_ancestors, MRB_ARGS_NONE()); /* 15.2.2.4.9 */ mrb_define_method(mrb, mod, "undef_method", mrb_mod_undef, MRB_ARGS_ANY()); /* 15.2.2.4.41 */ mrb_define_method(mrb, mod, "const_defined?", mrb_mod_const_defined, MRB_ARGS_ARG(1,1)); /* 15.2.2.4.20 */ mrb_define_method(mrb, mod, "const_get", mrb_mod_const_get, MRB_ARGS_REQ(1)); /* 15.2.2.4.21 */ mrb_define_method(mrb, mod, "const_set", mrb_mod_const_set, MRB_ARGS_REQ(2)); /* 15.2.2.4.23 */ mrb_define_method(mrb, mod, "remove_const", mrb_mod_remove_const, MRB_ARGS_REQ(1)); /* 15.2.2.4.40 */ mrb_define_method(mrb, mod, "const_missing", mrb_mod_const_missing, MRB_ARGS_REQ(1)); mrb_define_method(mrb, mod, "method_defined?", mrb_mod_method_defined, MRB_ARGS_REQ(1)); /* 15.2.2.4.34 */ mrb_define_method(mrb, mod, "define_method", mod_define_method, MRB_ARGS_ARG(1,1)); mrb_define_method(mrb, mod, "===", mrb_mod_eqq, MRB_ARGS_REQ(1)); mrb_undef_method(mrb, cls, "append_features"); mrb_undef_method(mrb, cls, "extend_object"); mrb->top_self = (struct RObject*)mrb_obj_alloc(mrb, MRB_TT_OBJECT, mrb->object_class); mrb_define_singleton_method(mrb, mrb->top_self, "inspect", inspect_main, MRB_ARGS_NONE()); mrb_define_singleton_method(mrb, mrb->top_self, "to_s", inspect_main, MRB_ARGS_NONE()); mrb_define_singleton_method(mrb, mrb->top_self, "define_method", top_define_method, MRB_ARGS_ARG(1,1)); } mruby-2.0.0/src/codedump.c000066400000000000000000000350371340361412400154100ustar00rootroot00000000000000#include #include #include #include #include #include #ifndef MRB_DISABLE_STDIO static void print_r(mrb_state *mrb, mrb_irep *irep, size_t n) { size_t i; if (n == 0) return; for (i=0; i+1nlocals; i++) { if (irep->lv[i].r == n) { mrb_sym sym = irep->lv[i].name; printf(" R%d:%s", (int)n, mrb_sym2name(mrb, sym)); break; } } } static void print_lv_a(mrb_state *mrb, mrb_irep *irep, uint16_t a) { if (!irep->lv || a >= irep->nlocals || a == 0) { printf("\n"); return; } printf("\t;"); print_r(mrb, irep, a); printf("\n"); } static void print_lv_ab(mrb_state *mrb, mrb_irep *irep, uint16_t a, uint16_t b) { if (!irep->lv || (a >= irep->nlocals && b >= irep->nlocals) || a+b == 0) { printf("\n"); return; } printf("\t;"); if (a > 0) print_r(mrb, irep, a); if (b > 0) print_r(mrb, irep, b); printf("\n"); } static void print_header(mrb_irep *irep, ptrdiff_t i) { int32_t line; line = mrb_debug_get_line(irep, i); if (line < 0) { printf(" "); } else { printf("%5d ", line); } printf("%03d ", (int)i); } #define CASE(insn,ops) case insn: FETCH_ ## ops (); L_ ## insn static void codedump(mrb_state *mrb, mrb_irep *irep) { int ai; mrb_code *pc, *pcend; mrb_code ins; const char *file = NULL, *next_file; if (!irep) return; printf("irep %p nregs=%d nlocals=%d pools=%d syms=%d reps=%d\n", (void*)irep, irep->nregs, irep->nlocals, (int)irep->plen, (int)irep->slen, (int)irep->rlen); if (irep->lv) { int i; printf("local variable names:\n"); for (i = 1; i < irep->nlocals; ++i) { char const *s = mrb_sym2name(mrb, irep->lv[i - 1].name); int n = irep->lv[i - 1].r ? irep->lv[i - 1].r : i; printf(" R%d:%s\n", n, s ? s : ""); } } pc = irep->iseq; pcend = pc + irep->ilen; while (pc < pcend) { ptrdiff_t i; uint32_t a; uint16_t b; uint8_t c; ai = mrb_gc_arena_save(mrb); i = pc - irep->iseq; next_file = mrb_debug_get_filename(irep, i); if (next_file && file != next_file) { printf("file: %s\n", next_file); file = next_file; } print_header(irep, i); ins = READ_B(); switch (ins) { CASE(OP_NOP, Z): printf("OP_NOP\n"); break; CASE(OP_MOVE, BB): printf("OP_MOVE\tR%d\tR%d\t", a, b); print_lv_ab(mrb, irep, a, b); break; CASE(OP_LOADL, BB): { mrb_value v = irep->pool[b]; mrb_value s = mrb_inspect(mrb, v); printf("OP_LOADL\tR%d\tL(%d)\t; %s", a, b, RSTRING_PTR(s)); } print_lv_a(mrb, irep, a); break; CASE(OP_LOADI, BB): printf("OP_LOADI\tR%d\t%d\t", a, b); print_lv_a(mrb, irep, a); break; CASE(OP_LOADINEG, BB): printf("OP_LOADI\tR%d\t-%d\t", a, b); print_lv_a(mrb, irep, a); break; CASE(OP_LOADI__1, B): printf("OP_LOADI__1\tR%d\t\t", a); print_lv_a(mrb, irep, a); break; CASE(OP_LOADI_0, B): goto L_LOADI; CASE(OP_LOADI_1, B): goto L_LOADI; CASE(OP_LOADI_2, B): goto L_LOADI; CASE(OP_LOADI_3, B): goto L_LOADI; CASE(OP_LOADI_4, B): goto L_LOADI; CASE(OP_LOADI_5, B): goto L_LOADI; CASE(OP_LOADI_6, B): goto L_LOADI; CASE(OP_LOADI_7, B): L_LOADI: printf("OP_LOADI_%d\tR%d\t\t", ins-(int)OP_LOADI_0, a); print_lv_a(mrb, irep, a); break; CASE(OP_LOADSYM, BB): printf("OP_LOADSYM\tR%d\t:%s\t", a, mrb_sym2name(mrb, irep->syms[b])); print_lv_a(mrb, irep, a); break; CASE(OP_LOADNIL, B): printf("OP_LOADNIL\tR%d\t\t", a); print_lv_a(mrb, irep, a); break; CASE(OP_LOADSELF, B): printf("OP_LOADSELF\tR%d\t\t", a); print_lv_a(mrb, irep, a); break; CASE(OP_LOADT, B): printf("OP_LOADT\tR%d\t\t", a); print_lv_a(mrb, irep, a); break; CASE(OP_LOADF, B): printf("OP_LOADF\tR%d\t\t", a); print_lv_a(mrb, irep, a); break; CASE(OP_GETGV, BB): printf("OP_GETGV\tR%d\t:%s", a, mrb_sym2name(mrb, irep->syms[b])); print_lv_a(mrb, irep, a); break; CASE(OP_SETGV, BB): printf("OP_SETGV\t:%s\tR%d", mrb_sym2name(mrb, irep->syms[b]), a); print_lv_a(mrb, irep, a); break; CASE(OP_GETSV, BB): printf("OP_GETSV\tR%d\t:%s", a, mrb_sym2name(mrb, irep->syms[b])); print_lv_a(mrb, irep, a); break; CASE(OP_SETSV, BB): printf("OP_SETSV\t:%s\tR%d", mrb_sym2name(mrb, irep->syms[b]), a); print_lv_a(mrb, irep, a); break; CASE(OP_GETCONST, BB): printf("OP_GETCONST\tR%d\t:%s", a, mrb_sym2name(mrb, irep->syms[b])); print_lv_a(mrb, irep, a); break; CASE(OP_SETCONST, BB): printf("OP_SETCONST\t:%s\tR%d", mrb_sym2name(mrb, irep->syms[b]), a); print_lv_a(mrb, irep, a); break; CASE(OP_GETMCNST, BB): printf("OP_GETMCNST\tR%d\tR%d::%s", a, a, mrb_sym2name(mrb, irep->syms[b])); print_lv_a(mrb, irep, a); break; CASE(OP_SETMCNST, BB): printf("OP_SETMCNST\tR%d::%s\tR%d", a+1, mrb_sym2name(mrb, irep->syms[b]), a); print_lv_a(mrb, irep, a); break; CASE(OP_GETIV, BB): printf("OP_GETIV\tR%d\t%s", a, mrb_sym2name(mrb, irep->syms[b])); print_lv_a(mrb, irep, a); break; CASE(OP_SETIV, BB): printf("OP_SETIV\t%s\tR%d", mrb_sym2name(mrb, irep->syms[b]), a); print_lv_a(mrb, irep, a); break; CASE(OP_GETUPVAR, BBB): printf("OP_GETUPVAR\tR%d\t%d\t%d", a, b, c); print_lv_a(mrb, irep, a); break; CASE(OP_SETUPVAR, BBB): printf("OP_SETUPVAR\tR%d\t%d\t%d", a, b, c); print_lv_a(mrb, irep, a); break; CASE(OP_GETCV, BB): printf("OP_GETCV\tR%d\t%s", a, mrb_sym2name(mrb, irep->syms[b])); print_lv_a(mrb, irep, a); break; CASE(OP_SETCV, BB): printf("OP_SETCV\t%s\tR%d", mrb_sym2name(mrb, irep->syms[b]), a); print_lv_a(mrb, irep, a); break; CASE(OP_JMP, S): printf("OP_JMP\t\t%03d\n", a); break; CASE(OP_JMPIF, BS): printf("OP_JMPIF\tR%d\t%03d\t", a, b); print_lv_a(mrb, irep, a); break; CASE(OP_JMPNOT, BS): printf("OP_JMPNOT\tR%d\t%03d\t", a, b); print_lv_a(mrb, irep, a); break; CASE(OP_JMPNIL, BS): printf("OP_JMPNIL\tR%d\t%03d\t", a, b); print_lv_a(mrb, irep, a); break; CASE(OP_SENDV, BB): printf("OP_SENDV\tR%d\t:%s\n", a, mrb_sym2name(mrb, irep->syms[b])); break; CASE(OP_SENDVB, BB): printf("OP_SENDVB\tR%d\t:%s\n", a, mrb_sym2name(mrb, irep->syms[b])); break; CASE(OP_SEND, BBB): printf("OP_SEND\tR%d\t:%s\t%d\n", a, mrb_sym2name(mrb, irep->syms[b]), c); break; CASE(OP_SENDB, BBB): printf("OP_SENDB\tR%d\t:%s\t%d\n", a, mrb_sym2name(mrb, irep->syms[b]), c); break; CASE(OP_CALL, Z): printf("OP_CALL\n"); break; CASE(OP_SUPER, BB): printf("OP_SUPER\tR%d\t%d\n", a, b); break; CASE(OP_ARGARY, BS): printf("OP_ARGARY\tR%d\t%d:%d:%d:%d (%d)", a, (b>>11)&0x3f, (b>>10)&0x1, (b>>5)&0x1f, (b>>4)&0x1, (b>>0)&0xf); print_lv_a(mrb, irep, a); break; CASE(OP_ENTER, W): printf("OP_ENTER\t%d:%d:%d:%d:%d:%d:%d\n", (a>>18)&0x1f, (a>>13)&0x1f, (a>>12)&0x1, (a>>7)&0x1f, (a>>2)&0x1f, (a>>1)&0x1, a & 0x1); break; CASE(OP_KEY_P, BB): printf("OP_KEY_P\tR%d\t:%s\t", a, mrb_sym2name(mrb, irep->syms[b])); print_lv_a(mrb, irep, a); break; CASE(OP_KEYEND, Z): printf("OP_KEYEND\n"); break; CASE(OP_KARG, BB): printf("OP_KARG\tR%d\t:%s\t", a, mrb_sym2name(mrb, irep->syms[b])); print_lv_a(mrb, irep, a); break; CASE(OP_RETURN, B): printf("OP_RETURN\tR%d\t\t", a); print_lv_a(mrb, irep, a); break; CASE(OP_RETURN_BLK, B): printf("OP_RETURN_BLK\tR%d\t\t", a); print_lv_a(mrb, irep, a); break; CASE(OP_BREAK, B): printf("OP_BREAK\tR%d\t\t", a); print_lv_a(mrb, irep, a); break; CASE(OP_BLKPUSH, BS): printf("OP_BLKPUSH\tR%d\t%d:%d:%d:%d (%d)", a, (b>>11)&0x3f, (b>>10)&0x1, (b>>5)&0x1f, (b>>4)&0x1, (b>>0)&0xf); print_lv_a(mrb, irep, a); break; CASE(OP_LAMBDA, BB): printf("OP_LAMBDA\tR%d\tI(%d:%p)\n", a, b, irep->reps[b]); break; CASE(OP_BLOCK, BB): printf("OP_BLOCK\tR%d\tI(%d:%p)\n", a, b, irep->reps[b]); break; CASE(OP_METHOD, BB): printf("OP_METHOD\tR%d\tI(%d:%p)\n", a, b, irep->reps[b]); break; CASE(OP_RANGE_INC, B): printf("OP_RANGE_INC\tR%d\n", a); break; CASE(OP_RANGE_EXC, B): printf("OP_RANGE_EXC\tR%d\n", a); break; CASE(OP_DEF, BB): printf("OP_DEF\tR%d\t:%s\n", a, mrb_sym2name(mrb, irep->syms[b])); break; CASE(OP_UNDEF, B): printf("OP_UNDEF\t:%s\n", mrb_sym2name(mrb, irep->syms[a])); break; CASE(OP_ALIAS, BB): printf("OP_ALIAS\t:%s\t%s\n", mrb_sym2name(mrb, irep->syms[a]), mrb_sym2name(mrb, irep->syms[b])); break; CASE(OP_ADD, B): printf("OP_ADD\tR%d\t\n", a); break; CASE(OP_ADDI, BB): printf("OP_ADDI\tR%d\t%d\n", a, b); break; CASE(OP_SUB, B): printf("OP_SUB\tR%d\t\n", a); break; CASE(OP_SUBI, BB): printf("OP_SUBI\tR%d\t%d\n", a, b); break; CASE(OP_MUL, B): printf("OP_MUL\tR%d\t\n", a); break; CASE(OP_DIV, B): printf("OP_DIV\tR%d\t\n", a); break; CASE(OP_LT, B): printf("OP_LT\t\tR%d\t\n", a); break; CASE(OP_LE, B): printf("OP_LE\t\tR%d\t\n", a); break; CASE(OP_GT, B): printf("OP_GT\t\tR%d\t\n", a); break; CASE(OP_GE, B): printf("OP_GE\t\tR%d\t\n", a); break; CASE(OP_EQ, B): printf("OP_EQ\t\tR%d\t\n", a); break; CASE(OP_ARRAY, BB): printf("OP_ARRAY\tR%d\t%d\t", a, b); print_lv_a(mrb, irep, a); break; CASE(OP_ARRAY2, BBB): printf("OP_ARRAY\tR%d\tR%d\t%d\t", a, b, c); print_lv_ab(mrb, irep, a, b); break; CASE(OP_ARYCAT, B): printf("OP_ARYCAT\tR%d\t", a); print_lv_a(mrb, irep, a); break; CASE(OP_ARYPUSH, B): printf("OP_ARYPUSH\tR%d\t", a); print_lv_a(mrb, irep, a); break; CASE(OP_ARYDUP, B): printf("OP_ARYDUP\tR%d\t", a); print_lv_a(mrb, irep, a); break; CASE(OP_AREF, BBB): printf("OP_AREF\tR%d\tR%d\t%d", a, b, c); print_lv_ab(mrb, irep, a, b); break; CASE(OP_ASET, BBB): printf("OP_ASET\tR%d\tR%d\t%d", a, b, c); print_lv_ab(mrb, irep, a, b); break; CASE(OP_APOST, BBB): printf("OP_APOST\tR%d\t%d\t%d", a, b, c); print_lv_a(mrb, irep, a); break; CASE(OP_INTERN, B): printf("OP_INTERN\tR%d", a); print_lv_a(mrb, irep, a); break; CASE(OP_STRING, BB): { mrb_value v = irep->pool[b]; mrb_value s = mrb_str_dump(mrb, mrb_str_new(mrb, RSTRING_PTR(v), RSTRING_LEN(v))); printf("OP_STRING\tR%d\tL(%d)\t; %s", a, b, RSTRING_PTR(s)); } print_lv_a(mrb, irep, a); break; CASE(OP_STRCAT, B): printf("OP_STRCAT\tR%d\t", a); print_lv_a(mrb, irep, a); break; CASE(OP_HASH, BB): printf("OP_HASH\tR%d\t%d\t", a, b); print_lv_a(mrb, irep, a); break; CASE(OP_HASHADD, BB): printf("OP_HASHADD\tR%d\t%d\t", a, b); print_lv_a(mrb, irep, a); break; CASE(OP_HASHCAT, B): printf("OP_HASHCAT\tR%d\t", a); print_lv_a(mrb, irep, a); break; CASE(OP_OCLASS, B): printf("OP_OCLASS\tR%d\t\t", a); print_lv_a(mrb, irep, a); break; CASE(OP_CLASS, BB): printf("OP_CLASS\tR%d\t:%s", a, mrb_sym2name(mrb, irep->syms[b])); print_lv_a(mrb, irep, a); break; CASE(OP_MODULE, BB): printf("OP_MODULE\tR%d\t:%s", a, mrb_sym2name(mrb, irep->syms[b])); print_lv_a(mrb, irep, a); break; CASE(OP_EXEC, BB): printf("OP_EXEC\tR%d\tI(%d:%p)", a, b, irep->reps[b]); print_lv_a(mrb, irep, a); break; CASE(OP_SCLASS, B): printf("OP_SCLASS\tR%d\t", a); print_lv_a(mrb, irep, a); break; CASE(OP_TCLASS, B): printf("OP_TCLASS\tR%d\t\t", a); print_lv_a(mrb, irep, a); break; CASE(OP_ERR, B): { mrb_value v = irep->pool[a]; mrb_value s = mrb_str_dump(mrb, mrb_str_new(mrb, RSTRING_PTR(v), RSTRING_LEN(v))); printf("OP_ERR\t%s\n", RSTRING_PTR(s)); } break; CASE(OP_EPUSH, B): printf("OP_EPUSH\t\t:I(%d:%p)\n", a, irep->reps[a]); break; CASE(OP_ONERR, S): printf("OP_ONERR\t%03d\n", a); break; CASE(OP_EXCEPT, B): printf("OP_EXCEPT\tR%d\t\t", a); print_lv_a(mrb, irep, a); break; CASE(OP_RESCUE, BB): printf("OP_RESCUE\tR%d\tR%d", a, b); print_lv_ab(mrb, irep, a, b); break; CASE(OP_RAISE, B): printf("OP_RAISE\tR%d\t\t", a); print_lv_a(mrb, irep, a); break; CASE(OP_POPERR, B): printf("OP_POPERR\t%d\t\t\n", a); break; CASE(OP_EPOP, B): printf("OP_EPOP\t%d\n", a); break; CASE(OP_DEBUG, BBB): printf("OP_DEBUG\t%d\t%d\t%d\n", a, b, c); break; CASE(OP_STOP, Z): printf("OP_STOP\n"); break; CASE(OP_EXT1, Z): ins = READ_B(); printf("OP_EXT1\n"); print_header(irep, pc-irep->iseq-2); switch (ins) { #define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _1 (); goto L_OP_ ## i; #include "mruby/ops.h" #undef OPCODE } break; CASE(OP_EXT2, Z): ins = READ_B(); printf("OP_EXT2\n"); print_header(irep, pc-irep->iseq-2); switch (ins) { #define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _2 (); goto L_OP_ ## i; #include "mruby/ops.h" #undef OPCODE } break; CASE(OP_EXT3, Z): ins = READ_B(); printf("OP_EXT3\n"); print_header(irep, pc-irep->iseq-2); switch (ins) { #define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _3 (); goto L_OP_ ## i; #include "mruby/ops.h" #undef OPCODE } break; default: printf("OP_unknown (0x%x)\n", ins); break; } mrb_gc_arena_restore(mrb, ai); } printf("\n"); } static void codedump_recur(mrb_state *mrb, mrb_irep *irep) { int i; codedump(mrb, irep); for (i=0; irlen; i++) { codedump_recur(mrb, irep->reps[i]); } } #endif void mrb_codedump_all(mrb_state *mrb, struct RProc *proc) { #ifndef MRB_DISABLE_STDIO codedump_recur(mrb, proc->body.irep); #endif } mruby-2.0.0/src/compar.c000066400000000000000000000003041340361412400150560ustar00rootroot00000000000000/* ** compar.c - Comparable module ** ** See Copyright Notice in mruby.h */ #include void mrb_init_comparable(mrb_state *mrb) { mrb_define_module(mrb, "Comparable"); /* 15.3.3 */ } mruby-2.0.0/src/crc.c000066400000000000000000000014721340361412400143530ustar00rootroot00000000000000/* ** crc.c - calculate CRC ** ** See Copyright Notice in mruby.h */ #include #include #include /* Calculate CRC (CRC-16-CCITT) ** ** 0000_0000_0000_0000_0000_0000_0000_0000 ** ^|------- CRC -------|- work --| ** carry */ #define CRC_16_CCITT 0x11021ul /* x^16+x^12+x^5+1 */ #define CRC_XOR_PATTERN (CRC_16_CCITT << 8) #define CRC_CARRY_BIT (0x01000000) uint16_t calc_crc_16_ccitt(const uint8_t *src, size_t nbytes, uint16_t crc) { size_t ibyte; uint32_t ibit; uint32_t crcwk = crc << 8; for (ibyte = 0; ibyte < nbytes; ibyte++) { crcwk |= *src++; for (ibit = 0; ibit < CHAR_BIT; ibit++) { crcwk <<= 1; if (crcwk & CRC_CARRY_BIT) { crcwk ^= CRC_XOR_PATTERN; } } } return (uint16_t)(crcwk >> 8); } mruby-2.0.0/src/debug.c000066400000000000000000000135771340361412400147030ustar00rootroot00000000000000#include #include #include #include static mrb_irep_debug_info_file* get_file(mrb_irep_debug_info *info, uint32_t pc) { mrb_irep_debug_info_file **ret; int32_t count; if (pc >= info->pc_count) { return NULL; } /* get upper bound */ ret = info->files; count = info->flen; while (count > 0) { int32_t step = count / 2; mrb_irep_debug_info_file **it = ret + step; if (!(pc < (*it)->start_pos)) { ret = it + 1; count -= step + 1; } else { count = step; } } --ret; /* check returning file exists inside debug info */ mrb_assert(info->files <= ret && ret < (info->files + info->flen)); /* check pc is within the range of returning file */ mrb_assert((*ret)->start_pos <= pc && pc < (((ret + 1 - info->files) < info->flen) ? (*(ret+1))->start_pos : info->pc_count)); return *ret; } static mrb_debug_line_type select_line_type(const uint16_t *lines, size_t lines_len) { size_t line_count = 0; int prev_line = -1; size_t i; for (i = 0; i < lines_len; ++i) { if (lines[i] != prev_line) { ++line_count; } } return (sizeof(uint16_t) * lines_len) <= (sizeof(mrb_irep_debug_info_line) * line_count) ? mrb_debug_line_ary : mrb_debug_line_flat_map; } MRB_API char const* mrb_debug_get_filename(mrb_irep *irep, ptrdiff_t pc) { if (irep && pc >= 0 && pc < irep->ilen) { mrb_irep_debug_info_file* f = NULL; if (!irep->debug_info) return NULL; else if ((f = get_file(irep->debug_info, (uint32_t)pc))) { return f->filename; } } return NULL; } MRB_API int32_t mrb_debug_get_line(mrb_irep *irep, ptrdiff_t pc) { if (irep && pc >= 0 && pc < irep->ilen) { mrb_irep_debug_info_file* f = NULL; if (!irep->debug_info) { return -1; } else if ((f = get_file(irep->debug_info, (uint32_t)pc))) { switch (f->line_type) { case mrb_debug_line_ary: mrb_assert(f->start_pos <= pc && pc < (f->start_pos + f->line_entry_count)); return f->lines.ary[pc - f->start_pos]; case mrb_debug_line_flat_map: { /* get upper bound */ mrb_irep_debug_info_line *ret = f->lines.flat_map; uint32_t count = f->line_entry_count; while (count > 0) { int32_t step = count / 2; mrb_irep_debug_info_line *it = ret + step; if (!(pc < it->start_pos)) { ret = it + 1; count -= step + 1; } else { count = step; } } --ret; /* check line entry pointer range */ mrb_assert(f->lines.flat_map <= ret && ret < (f->lines.flat_map + f->line_entry_count)); /* check pc range */ mrb_assert(ret->start_pos <= pc && pc < (((uint32_t)(ret + 1 - f->lines.flat_map) < f->line_entry_count) ? (ret+1)->start_pos : irep->debug_info->pc_count)); return ret->line; } } } } return -1; } MRB_API mrb_irep_debug_info* mrb_debug_info_alloc(mrb_state *mrb, mrb_irep *irep) { static const mrb_irep_debug_info initial = { 0, 0, NULL }; mrb_irep_debug_info *ret; mrb_assert(!irep->debug_info); ret = (mrb_irep_debug_info *)mrb_malloc(mrb, sizeof(*ret)); *ret = initial; irep->debug_info = ret; return ret; } MRB_API mrb_irep_debug_info_file* mrb_debug_info_append_file(mrb_state *mrb, mrb_irep_debug_info *d, const char *filename, uint16_t *lines, uint32_t start_pos, uint32_t end_pos) { mrb_irep_debug_info_file *f; uint32_t file_pc_count; size_t fn_len; mrb_int len; uint32_t i; if (!d) return NULL; if (start_pos == end_pos) return NULL; mrb_assert(filename); mrb_assert(lines); if (d->flen > 0 && strcmp(filename, d->files[d->flen - 1]->filename) == 0) { return NULL; } f = (mrb_irep_debug_info_file*)mrb_malloc(mrb, sizeof(*f)); d->files = (mrb_irep_debug_info_file**)( d->files ? mrb_realloc(mrb, d->files, sizeof(mrb_irep_debug_info_file*) * (d->flen + 1)) : mrb_malloc(mrb, sizeof(mrb_irep_debug_info_file*))); d->files[d->flen++] = f; file_pc_count = end_pos - start_pos; f->start_pos = start_pos; d->pc_count = end_pos; fn_len = strlen(filename); f->filename_sym = mrb_intern(mrb, filename, fn_len); len = 0; f->filename = mrb_sym2name_len(mrb, f->filename_sym, &len); f->line_type = select_line_type(lines + start_pos, end_pos - start_pos); f->lines.ptr = NULL; switch (f->line_type) { case mrb_debug_line_ary: f->line_entry_count = file_pc_count; f->lines.ary = (uint16_t*)mrb_malloc(mrb, sizeof(uint16_t) * file_pc_count); for (i = 0; i < file_pc_count; ++i) { f->lines.ary[i] = lines[start_pos + i]; } break; case mrb_debug_line_flat_map: { uint16_t prev_line = 0; mrb_irep_debug_info_line m; f->lines.flat_map = (mrb_irep_debug_info_line*)mrb_malloc(mrb, sizeof(mrb_irep_debug_info_line) * 1); f->line_entry_count = 0; for (i = 0; i < file_pc_count; ++i) { if (lines[start_pos + i] == prev_line) { continue; } f->lines.flat_map = (mrb_irep_debug_info_line*)mrb_realloc( mrb, f->lines.flat_map, sizeof(mrb_irep_debug_info_line) * (f->line_entry_count + 1)); m.start_pos = start_pos + i; m.line = lines[start_pos + i]; f->lines.flat_map[f->line_entry_count] = m; /* update */ ++f->line_entry_count; prev_line = lines[start_pos + i]; } } break; default: mrb_assert(0); break; } return f; } MRB_API void mrb_debug_info_free(mrb_state *mrb, mrb_irep_debug_info *d) { uint32_t i; if (!d) { return; } for (i = 0; i < d->flen; ++i) { mrb_assert(d->files[i]); mrb_free(mrb, d->files[i]->lines.ptr); mrb_free(mrb, d->files[i]); } mrb_free(mrb, d->files); mrb_free(mrb, d); } mruby-2.0.0/src/dump.c000066400000000000000000000607211340361412400145530ustar00rootroot00000000000000/* ** dump.c - mruby binary dumper (mrbc binary format) ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #define FLAG_BYTEORDER_NATIVE 2 #define FLAG_BYTEORDER_NONATIVE 0 #ifndef MRB_WITHOUT_FLOAT #ifdef MRB_USE_FLOAT #define MRB_FLOAT_FMT "%.8e" #else #define MRB_FLOAT_FMT "%.16e" #endif #endif static size_t get_irep_record_size_1(mrb_state *mrb, mrb_irep *irep); #if UINT32_MAX > SIZE_MAX # error This code cannot be built on your environment. #endif static size_t write_padding(uint8_t *buf) { const size_t align = MRB_DUMP_ALIGNMENT; size_t pad_len = -(intptr_t)buf & (align-1); if (pad_len > 0) { memset(buf, 0, pad_len); } return pad_len; } static size_t get_irep_header_size(mrb_state *mrb) { size_t size = 0; size += sizeof(uint32_t) * 1; size += sizeof(uint16_t) * 3; return size; } static ptrdiff_t write_irep_header(mrb_state *mrb, mrb_irep *irep, uint8_t *buf) { uint8_t *cur = buf; cur += uint32_to_bin((uint32_t)get_irep_record_size_1(mrb, irep), cur); /* record size */ cur += uint16_to_bin((uint16_t)irep->nlocals, cur); /* number of local variable */ cur += uint16_to_bin((uint16_t)irep->nregs, cur); /* number of register variable */ cur += uint16_to_bin((uint16_t)irep->rlen, cur); /* number of child irep */ return cur - buf; } static size_t get_iseq_block_size(mrb_state *mrb, mrb_irep *irep) { size_t size = 0; size += sizeof(uint32_t); /* ilen */ size += sizeof(uint32_t); /* max padding */ size += sizeof(uint32_t) * irep->ilen; /* iseq(n) */ return size; } static ptrdiff_t write_iseq_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf, uint8_t flags) { uint8_t *cur = buf; cur += uint32_to_bin(irep->ilen, cur); /* number of opcode */ cur += write_padding(cur); memcpy(cur, irep->iseq, irep->ilen * sizeof(mrb_code)); cur += irep->ilen * sizeof(mrb_code); return cur - buf; } static size_t get_pool_block_size(mrb_state *mrb, mrb_irep *irep) { int pool_no; size_t size = 0; mrb_value str; size += sizeof(uint32_t); /* plen */ size += irep->plen * (sizeof(uint8_t) + sizeof(uint16_t)); /* len(n) */ for (pool_no = 0; pool_no < irep->plen; pool_no++) { int ai = mrb_gc_arena_save(mrb); switch (mrb_type(irep->pool[pool_no])) { case MRB_TT_FIXNUM: str = mrb_fixnum_to_str(mrb, irep->pool[pool_no], 10); { mrb_int len = RSTRING_LEN(str); mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX); size += (size_t)len; } break; #ifndef MRB_WITHOUT_FLOAT case MRB_TT_FLOAT: str = mrb_float_to_str(mrb, irep->pool[pool_no], MRB_FLOAT_FMT); { mrb_int len = RSTRING_LEN(str); mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX); size += (size_t)len; } break; #endif case MRB_TT_STRING: { mrb_int len = RSTRING_LEN(irep->pool[pool_no]); mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX); size += (size_t)len; } break; default: break; } mrb_gc_arena_restore(mrb, ai); } return size; } static ptrdiff_t write_pool_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf) { int pool_no; uint8_t *cur = buf; uint16_t len; mrb_value str; const char *char_ptr; cur += uint32_to_bin(irep->plen, cur); /* number of pool */ for (pool_no = 0; pool_no < irep->plen; pool_no++) { int ai = mrb_gc_arena_save(mrb); switch (mrb_type(irep->pool[pool_no])) { case MRB_TT_FIXNUM: cur += uint8_to_bin(IREP_TT_FIXNUM, cur); /* data type */ str = mrb_fixnum_to_str(mrb, irep->pool[pool_no], 10); break; #ifndef MRB_WITHOUT_FLOAT case MRB_TT_FLOAT: cur += uint8_to_bin(IREP_TT_FLOAT, cur); /* data type */ str = mrb_float_to_str(mrb, irep->pool[pool_no], MRB_FLOAT_FMT); break; #endif case MRB_TT_STRING: cur += uint8_to_bin(IREP_TT_STRING, cur); /* data type */ str = irep->pool[pool_no]; break; default: continue; } char_ptr = RSTRING_PTR(str); { mrb_int tlen = RSTRING_LEN(str); mrb_assert_int_fit(mrb_int, tlen, uint16_t, UINT16_MAX); len = (uint16_t)tlen; } cur += uint16_to_bin(len, cur); /* data length */ memcpy(cur, char_ptr, (size_t)len); cur += len; mrb_gc_arena_restore(mrb, ai); } return cur - buf; } static size_t get_syms_block_size(mrb_state *mrb, mrb_irep *irep) { size_t size = 0; int sym_no; mrb_int len; size += sizeof(uint32_t); /* slen */ for (sym_no = 0; sym_no < irep->slen; sym_no++) { size += sizeof(uint16_t); /* snl(n) */ if (irep->syms[sym_no] != 0) { mrb_sym2name_len(mrb, irep->syms[sym_no], &len); size += len + 1; /* sn(n) + null char */ } } return size; } static ptrdiff_t write_syms_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf) { int sym_no; uint8_t *cur = buf; const char *name; cur += uint32_to_bin(irep->slen, cur); /* number of symbol */ for (sym_no = 0; sym_no < irep->slen; sym_no++) { if (irep->syms[sym_no] != 0) { mrb_int len; name = mrb_sym2name_len(mrb, irep->syms[sym_no], &len); mrb_assert_int_fit(mrb_int, len, uint16_t, UINT16_MAX); cur += uint16_to_bin((uint16_t)len, cur); /* length of symbol name */ memcpy(cur, name, len); /* symbol name */ cur += (uint16_t)len; *cur++ = '\0'; } else { cur += uint16_to_bin(MRB_DUMP_NULL_SYM_LEN, cur); /* length of symbol name */ } } return cur - buf; } static size_t get_irep_record_size_1(mrb_state *mrb, mrb_irep *irep) { size_t size = 0; size += get_irep_header_size(mrb); size += get_iseq_block_size(mrb, irep); size += get_pool_block_size(mrb, irep); size += get_syms_block_size(mrb, irep); return size; } static size_t get_irep_record_size(mrb_state *mrb, mrb_irep *irep) { size_t size = 0; int irep_no; size = get_irep_record_size_1(mrb, irep); for (irep_no = 0; irep_no < irep->rlen; irep_no++) { size += get_irep_record_size(mrb, irep->reps[irep_no]); } return size; } static int write_irep_record(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, size_t *irep_record_size, uint8_t flags) { int i; uint8_t *src = bin; if (irep == NULL) { return MRB_DUMP_INVALID_IREP; } *irep_record_size = get_irep_record_size_1(mrb, irep); if (*irep_record_size == 0) { return MRB_DUMP_GENERAL_FAILURE; } bin += write_irep_header(mrb, irep, bin); bin += write_iseq_block(mrb, irep, bin, flags); bin += write_pool_block(mrb, irep, bin); bin += write_syms_block(mrb, irep, bin); for (i = 0; i < irep->rlen; i++) { int result; size_t rsize; result = write_irep_record(mrb, irep->reps[i], bin, &rsize, flags); if (result != MRB_DUMP_OK) { return result; } bin += rsize; } *irep_record_size = bin - src; return MRB_DUMP_OK; } static uint32_t write_footer(mrb_state *mrb, uint8_t *bin) { struct rite_binary_footer footer; memcpy(footer.section_ident, RITE_BINARY_EOF, sizeof(footer.section_ident)); uint32_to_bin(sizeof(struct rite_binary_footer), footer.section_size); memcpy(bin, &footer, sizeof(struct rite_binary_footer)); return sizeof(struct rite_binary_footer); } static int write_section_irep_header(mrb_state *mrb, size_t section_size, uint8_t *bin) { struct rite_section_irep_header *header = (struct rite_section_irep_header*)bin; memcpy(header->section_ident, RITE_SECTION_IREP_IDENT, sizeof(header->section_ident)); mrb_assert_int_fit(size_t, section_size, uint32_t, UINT32_MAX); uint32_to_bin((uint32_t)section_size, header->section_size); memcpy(header->rite_version, RITE_VM_VER, sizeof(header->rite_version)); return MRB_DUMP_OK; } static int write_section_irep(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, size_t *len_p, uint8_t flags) { int result; size_t rsize = 0; uint8_t *cur = bin; if (mrb == NULL || bin == NULL) { return MRB_DUMP_INVALID_ARGUMENT; } cur += sizeof(struct rite_section_irep_header); result = write_irep_record(mrb, irep, cur, &rsize, flags); if (result != MRB_DUMP_OK) { return result; } *len_p = cur - bin + rsize; write_section_irep_header(mrb, *len_p, bin); return MRB_DUMP_OK; } static size_t get_debug_record_size(mrb_state *mrb, mrb_irep *irep) { size_t ret = 0; uint16_t f_idx; int i; ret += sizeof(uint32_t); /* record size */ ret += sizeof(uint16_t); /* file count */ for (f_idx = 0; f_idx < irep->debug_info->flen; ++f_idx) { mrb_irep_debug_info_file const* file = irep->debug_info->files[f_idx]; ret += sizeof(uint32_t); /* position */ ret += sizeof(uint16_t); /* filename index */ /* lines */ ret += sizeof(uint32_t); /* entry count */ ret += sizeof(uint8_t); /* line type */ switch (file->line_type) { case mrb_debug_line_ary: ret += sizeof(uint16_t) * (size_t)(file->line_entry_count); break; case mrb_debug_line_flat_map: ret += (sizeof(uint32_t) + sizeof(uint16_t)) * (size_t)(file->line_entry_count); break; default: mrb_assert(0); break; } } for (i=0; irlen; i++) { ret += get_debug_record_size(mrb, irep->reps[i]); } return ret; } static int find_filename_index(const mrb_sym *ary, int ary_len, mrb_sym s) { int i; for (i = 0; i < ary_len; ++i) { if (ary[i] == s) { return i; } } return -1; } static size_t get_filename_table_size(mrb_state *mrb, mrb_irep *irep, mrb_sym **fp, uint16_t *lp) { mrb_sym *filenames = *fp; size_t size = 0; mrb_irep_debug_info *di = irep->debug_info; int i; mrb_assert(lp); for (i = 0; i < di->flen; ++i) { mrb_irep_debug_info_file *file; mrb_int filename_len; file = di->files[i]; if (find_filename_index(filenames, *lp, file->filename_sym) == -1) { /* register filename */ *lp += 1; *fp = filenames = (mrb_sym *)mrb_realloc(mrb, filenames, sizeof(mrb_sym) * (*lp)); filenames[*lp - 1] = file->filename_sym; /* filename */ mrb_sym2name_len(mrb, file->filename_sym, &filename_len); size += sizeof(uint16_t) + (size_t)filename_len; } } for (i=0; irlen; i++) { size += get_filename_table_size(mrb, irep->reps[i], fp, lp); } return size; } static size_t write_debug_record_1(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, mrb_sym const* filenames, uint16_t filenames_len) { uint8_t *cur; uint16_t f_idx; ptrdiff_t ret; cur = bin + sizeof(uint32_t); /* skip record size */ cur += uint16_to_bin(irep->debug_info->flen, cur); /* file count */ for (f_idx = 0; f_idx < irep->debug_info->flen; ++f_idx) { int filename_idx; const mrb_irep_debug_info_file *file = irep->debug_info->files[f_idx]; /* position */ cur += uint32_to_bin(file->start_pos, cur); /* filename index */ filename_idx = find_filename_index(filenames, filenames_len, file->filename_sym); mrb_assert_int_fit(int, filename_idx, uint16_t, UINT16_MAX); cur += uint16_to_bin((uint16_t)filename_idx, cur); /* lines */ cur += uint32_to_bin(file->line_entry_count, cur); cur += uint8_to_bin(file->line_type, cur); switch (file->line_type) { case mrb_debug_line_ary: { uint32_t l; for (l = 0; l < file->line_entry_count; ++l) { cur += uint16_to_bin(file->lines.ary[l], cur); } } break; case mrb_debug_line_flat_map: { uint32_t line; for (line = 0; line < file->line_entry_count; ++line) { cur += uint32_to_bin(file->lines.flat_map[line].start_pos, cur); cur += uint16_to_bin(file->lines.flat_map[line].line, cur); } } break; default: mrb_assert(0); break; } } ret = cur - bin; mrb_assert_int_fit(ptrdiff_t, ret, uint32_t, UINT32_MAX); uint32_to_bin((uint32_t)ret, bin); mrb_assert_int_fit(ptrdiff_t, ret, size_t, SIZE_MAX); return (size_t)ret; } static size_t write_debug_record(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, mrb_sym const* filenames, uint16_t filenames_len) { size_t size, len; int irep_no; size = len = write_debug_record_1(mrb, irep, bin, filenames, filenames_len); bin += len; for (irep_no = 0; irep_no < irep->rlen; irep_no++) { len = write_debug_record(mrb, irep->reps[irep_no], bin, filenames, filenames_len); bin += len; size += len; } mrb_assert(size == get_debug_record_size(mrb, irep)); return size; } static int write_section_debug(mrb_state *mrb, mrb_irep *irep, uint8_t *cur, mrb_sym const *filenames, uint16_t filenames_len) { size_t section_size = 0; const uint8_t *bin = cur; struct rite_section_debug_header *header; size_t dlen; uint16_t i; char const *sym; mrb_int sym_len; if (mrb == NULL || cur == NULL) { return MRB_DUMP_INVALID_ARGUMENT; } header = (struct rite_section_debug_header *)bin; cur += sizeof(struct rite_section_debug_header); section_size += sizeof(struct rite_section_debug_header); /* filename table */ cur += uint16_to_bin(filenames_len, cur); section_size += sizeof(uint16_t); for (i = 0; i < filenames_len; ++i) { sym = mrb_sym2name_len(mrb, filenames[i], &sym_len); mrb_assert(sym); cur += uint16_to_bin((uint16_t)sym_len, cur); memcpy(cur, sym, sym_len); cur += sym_len; section_size += sizeof(uint16_t) + sym_len; } /* debug records */ dlen = write_debug_record(mrb, irep, cur, filenames, filenames_len); section_size += dlen; memcpy(header->section_ident, RITE_SECTION_DEBUG_IDENT, sizeof(header->section_ident)); mrb_assert(section_size <= INT32_MAX); uint32_to_bin((uint32_t)section_size, header->section_size); return MRB_DUMP_OK; } static void create_lv_sym_table(mrb_state *mrb, const mrb_irep *irep, mrb_sym **syms, uint32_t *syms_len) { int i; if (*syms == NULL) { *syms = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym) * 1); } for (i = 0; i + 1 < irep->nlocals; ++i) { mrb_sym const name = irep->lv[i].name; if (name == 0) continue; if (find_filename_index(*syms, *syms_len, name) != -1) continue; ++(*syms_len); *syms = (mrb_sym*)mrb_realloc(mrb, *syms, sizeof(mrb_sym) * (*syms_len)); (*syms)[*syms_len - 1] = name; } for (i = 0; i < irep->rlen; ++i) { create_lv_sym_table(mrb, irep->reps[i], syms, syms_len); } } static int write_lv_sym_table(mrb_state *mrb, uint8_t **start, mrb_sym const *syms, uint32_t syms_len) { uint8_t *cur = *start; uint32_t i; const char *str; mrb_int str_len; cur += uint32_to_bin(syms_len, cur); for (i = 0; i < syms_len; ++i) { str = mrb_sym2name_len(mrb, syms[i], &str_len); cur += uint16_to_bin((uint16_t)str_len, cur); memcpy(cur, str, str_len); cur += str_len; } *start = cur; return MRB_DUMP_OK; } static int write_lv_record(mrb_state *mrb, const mrb_irep *irep, uint8_t **start, mrb_sym const *syms, uint32_t syms_len) { uint8_t *cur = *start; int i; for (i = 0; i + 1 < irep->nlocals; ++i) { if (irep->lv[i].name == 0) { cur += uint16_to_bin(RITE_LV_NULL_MARK, cur); cur += uint16_to_bin(0, cur); } else { int const sym_idx = find_filename_index(syms, syms_len, irep->lv[i].name); mrb_assert(sym_idx != -1); /* local variable name must be in syms */ cur += uint16_to_bin(sym_idx, cur); cur += uint16_to_bin(irep->lv[i].r, cur); } } for (i = 0; i < irep->rlen; ++i) { write_lv_record(mrb, irep->reps[i], &cur, syms, syms_len); } *start = cur; return MRB_DUMP_OK; } static size_t get_lv_record_size(mrb_state *mrb, mrb_irep *irep) { size_t ret = 0; int i; ret += (sizeof(uint16_t) + sizeof(uint16_t)) * (irep->nlocals - 1); for (i = 0; i < irep->rlen; ++i) { ret += get_lv_record_size(mrb, irep->reps[i]); } return ret; } static size_t get_lv_section_size(mrb_state *mrb, mrb_irep *irep, mrb_sym const *syms, uint32_t syms_len) { size_t ret = 0, i; ret += sizeof(uint32_t); /* syms_len */ ret += sizeof(uint16_t) * syms_len; /* symbol name lengths */ for (i = 0; i < syms_len; ++i) { mrb_int str_len; mrb_sym2name_len(mrb, syms[i], &str_len); ret += str_len; } ret += get_lv_record_size(mrb, irep); return ret; } static int write_section_lv(mrb_state *mrb, mrb_irep *irep, uint8_t *start, mrb_sym const *syms, uint32_t const syms_len) { uint8_t *cur = start; struct rite_section_lv_header *header; ptrdiff_t diff; int result = MRB_DUMP_OK; if (mrb == NULL || cur == NULL) { return MRB_DUMP_INVALID_ARGUMENT; } header = (struct rite_section_lv_header*)cur; cur += sizeof(struct rite_section_lv_header); result = write_lv_sym_table(mrb, &cur, syms, syms_len); if (result != MRB_DUMP_OK) { goto lv_section_exit; } result = write_lv_record(mrb, irep, &cur, syms, syms_len); if (result != MRB_DUMP_OK) { goto lv_section_exit; } memcpy(header->section_ident, RITE_SECTION_LV_IDENT, sizeof(header->section_ident)); diff = cur - start; mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX); uint32_to_bin((uint32_t)diff, header->section_size); lv_section_exit: return result; } static int write_rite_binary_header(mrb_state *mrb, size_t binary_size, uint8_t *bin, uint8_t flags) { struct rite_binary_header *header = (struct rite_binary_header *)bin; uint16_t crc; uint32_t offset; switch (flags & DUMP_ENDIAN_NAT) { endian_big: case DUMP_ENDIAN_BIG: memcpy(header->binary_ident, RITE_BINARY_IDENT, sizeof(header->binary_ident)); break; endian_little: case DUMP_ENDIAN_LIL: memcpy(header->binary_ident, RITE_BINARY_IDENT_LIL, sizeof(header->binary_ident)); break; case DUMP_ENDIAN_NAT: if (bigendian_p()) goto endian_big; goto endian_little; break; } memcpy(header->binary_version, RITE_BINARY_FORMAT_VER, sizeof(header->binary_version)); memcpy(header->compiler_name, RITE_COMPILER_NAME, sizeof(header->compiler_name)); memcpy(header->compiler_version, RITE_COMPILER_VERSION, sizeof(header->compiler_version)); mrb_assert(binary_size <= UINT32_MAX); uint32_to_bin((uint32_t)binary_size, header->binary_size); offset = (uint32_t)((&(header->binary_crc[0]) - bin) + sizeof(uint16_t)); crc = calc_crc_16_ccitt(bin + offset, binary_size - offset, 0); uint16_to_bin(crc, header->binary_crc); return MRB_DUMP_OK; } static mrb_bool debug_info_defined_p(mrb_irep *irep) { int i; if (!irep->debug_info) return FALSE; for (i=0; irlen; i++) { if (!debug_info_defined_p(irep->reps[i])) return FALSE; } return TRUE; } static mrb_bool lv_defined_p(mrb_irep *irep) { int i; if (irep->lv) { return TRUE; } for (i = 0; i < irep->rlen; ++i) { if (lv_defined_p(irep->reps[i])) { return TRUE; } } return FALSE; } static uint8_t dump_flags(uint8_t flags, uint8_t native) { if (native == FLAG_BYTEORDER_NATIVE) { if ((flags & DUMP_ENDIAN_NAT) == 0) { return (flags & DUMP_DEBUG_INFO) | DUMP_ENDIAN_NAT; } return flags; } if ((flags & DUMP_ENDIAN_NAT) == 0) { return (flags & DUMP_DEBUG_INFO) | DUMP_ENDIAN_BIG; } return flags; } static int dump_irep(mrb_state *mrb, mrb_irep *irep, uint8_t flags, uint8_t **bin, size_t *bin_size) { int result = MRB_DUMP_GENERAL_FAILURE; size_t malloc_size; size_t section_irep_size; size_t section_lineno_size = 0, section_lv_size = 0; uint8_t *cur = NULL; mrb_bool const debug_info_defined = debug_info_defined_p(irep), lv_defined = lv_defined_p(irep); mrb_sym *lv_syms = NULL; uint32_t lv_syms_len = 0; mrb_sym *filenames = NULL; uint16_t filenames_len = 0; if (mrb == NULL) { *bin = NULL; return MRB_DUMP_GENERAL_FAILURE; } section_irep_size = sizeof(struct rite_section_irep_header); section_irep_size += get_irep_record_size(mrb, irep); /* DEBUG section size */ if (flags & DUMP_DEBUG_INFO) { if (debug_info_defined) { section_lineno_size += sizeof(struct rite_section_debug_header); /* filename table */ filenames = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym) + 1); /* filename table size */ section_lineno_size += sizeof(uint16_t); section_lineno_size += get_filename_table_size(mrb, irep, &filenames, &filenames_len); section_lineno_size += get_debug_record_size(mrb, irep); } } if (lv_defined) { section_lv_size += sizeof(struct rite_section_lv_header); create_lv_sym_table(mrb, irep, &lv_syms, &lv_syms_len); section_lv_size += get_lv_section_size(mrb, irep, lv_syms, lv_syms_len); } malloc_size = sizeof(struct rite_binary_header) + section_irep_size + section_lineno_size + section_lv_size + sizeof(struct rite_binary_footer); cur = *bin = (uint8_t*)mrb_malloc(mrb, malloc_size); cur += sizeof(struct rite_binary_header); result = write_section_irep(mrb, irep, cur, §ion_irep_size, flags); if (result != MRB_DUMP_OK) { goto error_exit; } cur += section_irep_size; *bin_size = sizeof(struct rite_binary_header) + section_irep_size + section_lineno_size + section_lv_size + sizeof(struct rite_binary_footer); /* write DEBUG section */ if (flags & DUMP_DEBUG_INFO) { if (debug_info_defined) { result = write_section_debug(mrb, irep, cur, filenames, filenames_len); if (result != MRB_DUMP_OK) { goto error_exit; } } cur += section_lineno_size; } if (lv_defined) { result = write_section_lv(mrb, irep, cur, lv_syms, lv_syms_len); if (result != MRB_DUMP_OK) { goto error_exit; } cur += section_lv_size; } write_footer(mrb, cur); write_rite_binary_header(mrb, *bin_size, *bin, flags); error_exit: if (result != MRB_DUMP_OK) { mrb_free(mrb, *bin); *bin = NULL; } mrb_free(mrb, lv_syms); mrb_free(mrb, filenames); return result; } int mrb_dump_irep(mrb_state *mrb, mrb_irep *irep, uint8_t flags, uint8_t **bin, size_t *bin_size) { return dump_irep(mrb, irep, dump_flags(flags, FLAG_BYTEORDER_NONATIVE), bin, bin_size); } #ifndef MRB_DISABLE_STDIO int mrb_dump_irep_binary(mrb_state *mrb, mrb_irep *irep, uint8_t flags, FILE* fp) { uint8_t *bin = NULL; size_t bin_size = 0; int result; if (fp == NULL) { return MRB_DUMP_INVALID_ARGUMENT; } result = dump_irep(mrb, irep, dump_flags(flags, FLAG_BYTEORDER_NONATIVE), &bin, &bin_size); if (result == MRB_DUMP_OK) { if (fwrite(bin, sizeof(bin[0]), bin_size, fp) != bin_size) { result = MRB_DUMP_WRITE_FAULT; } } mrb_free(mrb, bin); return result; } static mrb_bool dump_bigendian_p(uint8_t flags) { switch (flags & DUMP_ENDIAN_NAT) { case DUMP_ENDIAN_BIG: return TRUE; case DUMP_ENDIAN_LIL: return FALSE; default: case DUMP_ENDIAN_NAT: return bigendian_p(); } } int mrb_dump_irep_cfunc(mrb_state *mrb, mrb_irep *irep, uint8_t flags, FILE *fp, const char *initname) { uint8_t *bin = NULL; size_t bin_size = 0, bin_idx = 0; int result; if (fp == NULL || initname == NULL || initname[0] == '\0') { return MRB_DUMP_INVALID_ARGUMENT; } flags = dump_flags(flags, FLAG_BYTEORDER_NATIVE); result = dump_irep(mrb, irep, flags, &bin, &bin_size); if (result == MRB_DUMP_OK) { if (!dump_bigendian_p(flags)) { if (fprintf(fp, "/* dumped in little endian order.\n" " use `mrbc -E` option for big endian CPU. */\n") < 0) { mrb_free(mrb, bin); return MRB_DUMP_WRITE_FAULT; } } else { if (fprintf(fp, "/* dumped in big endian order.\n" " use `mrbc -e` option for better performance on little endian CPU. */\n") < 0) { mrb_free(mrb, bin); return MRB_DUMP_WRITE_FAULT; } } if (fprintf(fp, "#include \n") < 0) { /* for uint8_t under at least Darwin */ mrb_free(mrb, bin); return MRB_DUMP_WRITE_FAULT; } if (fprintf(fp, "extern const uint8_t %s[];\n" "const uint8_t\n" "#if defined __GNUC__\n" "__attribute__((aligned(%u)))\n" "#elif defined _MSC_VER\n" "__declspec(align(%u))\n" "#endif\n" "%s[] = {", initname, (uint16_t)MRB_DUMP_ALIGNMENT, (uint16_t)MRB_DUMP_ALIGNMENT, initname) < 0) { mrb_free(mrb, bin); return MRB_DUMP_WRITE_FAULT; } while (bin_idx < bin_size) { if (bin_idx % 16 == 0) { if (fputs("\n", fp) == EOF) { mrb_free(mrb, bin); return MRB_DUMP_WRITE_FAULT; } } if (fprintf(fp, "0x%02x,", bin[bin_idx++]) < 0) { mrb_free(mrb, bin); return MRB_DUMP_WRITE_FAULT; } } if (fputs("\n};\n", fp) == EOF) { mrb_free(mrb, bin); return MRB_DUMP_WRITE_FAULT; } } mrb_free(mrb, bin); return result; } #endif /* MRB_DISABLE_STDIO */ mruby-2.0.0/src/enum.c000066400000000000000000000012321340361412400145420ustar00rootroot00000000000000/* ** enum.c - Enumerable module ** ** See Copyright Notice in mruby.h */ #include #include /* internal method `__update_hash(oldhash, index, itemhash)` */ static mrb_value enum_update_hash(mrb_state *mrb, mrb_value self) { mrb_int hash; mrb_int index; mrb_int hv; mrb_get_args(mrb, "iii", &hash, &index, &hv); hash ^= ((uint32_t)hv << (index % 16)); return mrb_fixnum_value(hash); } void mrb_init_enumerable(mrb_state *mrb) { struct RClass *enumerable; enumerable = mrb_define_module(mrb, "Enumerable"); /* 15.3.2 */ mrb_define_module_function(mrb, enumerable, "__update_hash", enum_update_hash, MRB_ARGS_REQ(1)); } mruby-2.0.0/src/error.c000066400000000000000000000310771340361412400147410ustar00rootroot00000000000000/* ** error.c - Exception class ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #include #include #include #include #include #include MRB_API mrb_value mrb_exc_new(mrb_state *mrb, struct RClass *c, const char *ptr, size_t len) { mrb_value arg = mrb_str_new(mrb, ptr, len); return mrb_obj_new(mrb, c, 1, &arg); } MRB_API mrb_value mrb_exc_new_str(mrb_state *mrb, struct RClass* c, mrb_value str) { mrb_to_str(mrb, str); return mrb_obj_new(mrb, c, 1, &str); } /* * call-seq: * Exception.new(msg = nil) -> exception * * Construct a new Exception object, optionally passing in * a message. */ static mrb_value exc_initialize(mrb_state *mrb, mrb_value exc) { mrb_value mesg; mrb_int argc; mrb_value *argv; if (mrb_get_args(mrb, "|o*!", &mesg, &argv, &argc) >= 1) { mrb_iv_set(mrb, exc, mrb_intern_lit(mrb, "mesg"), mesg); } return exc; } /* * Document-method: exception * * call-seq: * exc.exception(string) -> an_exception or exc * * With no argument, or if the argument is the same as the receiver, * return the receiver. Otherwise, create a new * exception object of the same class as the receiver, but with a * message equal to string. * */ static mrb_value exc_exception(mrb_state *mrb, mrb_value self) { mrb_value exc; mrb_value a; mrb_int argc; argc = mrb_get_args(mrb, "|o", &a); if (argc == 0) return self; if (mrb_obj_equal(mrb, self, a)) return self; exc = mrb_obj_clone(mrb, self); mrb_iv_set(mrb, exc, mrb_intern_lit(mrb, "mesg"), a); return exc; } /* * call-seq: * exception.to_s -> string * * Returns exception's message (or the name of the exception if * no message is set). */ static mrb_value exc_to_s(mrb_state *mrb, mrb_value exc) { mrb_value mesg = mrb_attr_get(mrb, exc, mrb_intern_lit(mrb, "mesg")); struct RObject *p; if (!mrb_string_p(mesg)) { return mrb_str_new_cstr(mrb, mrb_obj_classname(mrb, exc)); } p = mrb_obj_ptr(mesg); if (!p->c) { p->c = mrb->string_class; } return mesg; } /* * call-seq: * exception.message -> string * * Returns the result of invoking exception.to_s. * Normally this returns the exception's message or name. */ static mrb_value exc_message(mrb_state *mrb, mrb_value exc) { return mrb_funcall(mrb, exc, "to_s", 0); } /* * call-seq: * exception.inspect -> string * * Returns this exception's file name, line number, * message and class name. * If file name or line number is not set, * returns message and class name. */ static mrb_value exc_inspect(mrb_state *mrb, mrb_value exc) { mrb_value str, mesg, file, line; mrb_bool append_mesg; const char *cname; mesg = mrb_attr_get(mrb, exc, mrb_intern_lit(mrb, "mesg")); file = mrb_attr_get(mrb, exc, mrb_intern_lit(mrb, "file")); line = mrb_attr_get(mrb, exc, mrb_intern_lit(mrb, "line")); append_mesg = !mrb_nil_p(mesg); if (append_mesg) { mesg = mrb_obj_as_string(mrb, mesg); append_mesg = RSTRING_LEN(mesg) > 0; } cname = mrb_obj_classname(mrb, exc); str = mrb_str_new_cstr(mrb, cname); if (mrb_string_p(file) && mrb_fixnum_p(line)) { if (append_mesg) { str = mrb_format(mrb, "%S:%S: %S (%S)", file, line, mesg, str); } else { str = mrb_format(mrb, "%S:%S: %S", file, line, str); } } else if (append_mesg) { str = mrb_format(mrb, "%S: %S", str, mesg); } return str; } void mrb_keep_backtrace(mrb_state *mrb, mrb_value exc); static void set_backtrace(mrb_state *mrb, mrb_value exc, mrb_value backtrace) { if (!mrb_array_p(backtrace)) { type_err: mrb_raise(mrb, E_TYPE_ERROR, "backtrace must be Array of String"); } else { const mrb_value *p = RARRAY_PTR(backtrace); const mrb_value *pend = p + RARRAY_LEN(backtrace); while (p < pend) { if (!mrb_string_p(*p)) goto type_err; p++; } } mrb_iv_set(mrb, exc, mrb_intern_lit(mrb, "backtrace"), backtrace); } static mrb_value exc_set_backtrace(mrb_state *mrb, mrb_value exc) { mrb_value backtrace; mrb_get_args(mrb, "o", &backtrace); set_backtrace(mrb, exc, backtrace); return backtrace; } static void exc_debug_info(mrb_state *mrb, struct RObject *exc) { mrb_callinfo *ci = mrb->c->ci; mrb_code *pc = ci->pc; if (mrb_obj_iv_defined(mrb, exc, mrb_intern_lit(mrb, "file"))) return; while (ci >= mrb->c->cibase) { mrb_code *err = ci->err; if (!err && pc) err = pc - 1; if (err && ci->proc && !MRB_PROC_CFUNC_P(ci->proc)) { mrb_irep *irep = ci->proc->body.irep; int32_t const line = mrb_debug_get_line(irep, err - irep->iseq); char const* file = mrb_debug_get_filename(irep, err - irep->iseq); if (line != -1 && file) { mrb_obj_iv_set(mrb, exc, mrb_intern_lit(mrb, "file"), mrb_str_new_cstr(mrb, file)); mrb_obj_iv_set(mrb, exc, mrb_intern_lit(mrb, "line"), mrb_fixnum_value(line)); return; } } pc = ci->pc; ci--; } } void mrb_exc_set(mrb_state *mrb, mrb_value exc) { if (mrb_nil_p(exc)) { mrb->exc = 0; } else { mrb->exc = mrb_obj_ptr(exc); if (mrb->gc.arena_idx > 0 && (struct RBasic*)mrb->exc == mrb->gc.arena[mrb->gc.arena_idx-1]) { mrb->gc.arena_idx--; } if (!mrb->gc.out_of_memory && !MRB_FROZEN_P(mrb->exc)) { exc_debug_info(mrb, mrb->exc); mrb_keep_backtrace(mrb, exc); } } } MRB_API mrb_noreturn void mrb_exc_raise(mrb_state *mrb, mrb_value exc) { if (!mrb_obj_is_kind_of(mrb, exc, mrb->eException_class)) { mrb_raise(mrb, E_TYPE_ERROR, "exception object expected"); } mrb_exc_set(mrb, exc); if (!mrb->jmp) { mrb_p(mrb, exc); abort(); } MRB_THROW(mrb->jmp); } MRB_API mrb_noreturn void mrb_raise(mrb_state *mrb, struct RClass *c, const char *msg) { mrb_exc_raise(mrb, mrb_exc_new_str(mrb, c, mrb_str_new_cstr(mrb, msg))); } MRB_API mrb_value mrb_vformat(mrb_state *mrb, const char *format, va_list ap) { const char *p = format; const char *b = p; ptrdiff_t size; int ai0 = mrb_gc_arena_save(mrb); mrb_value ary = mrb_ary_new_capa(mrb, 4); int ai = mrb_gc_arena_save(mrb); while (*p) { const char c = *p++; if (c == '%') { if (*p == 'S') { mrb_value val; size = p - b - 1; mrb_ary_push(mrb, ary, mrb_str_new(mrb, b, size)); val = va_arg(ap, mrb_value); mrb_ary_push(mrb, ary, mrb_obj_as_string(mrb, val)); b = p + 1; } } else if (c == '\\') { if (*p) { size = p - b - 1; mrb_ary_push(mrb, ary, mrb_str_new(mrb, b, size)); mrb_ary_push(mrb, ary, mrb_str_new(mrb, p, 1)); b = ++p; } else { break; } } mrb_gc_arena_restore(mrb, ai); } if (b == format) { mrb_gc_arena_restore(mrb, ai0); return mrb_str_new_cstr(mrb, format); } else { mrb_value val; size = p - b; if (size > 0) { mrb_ary_push(mrb, ary, mrb_str_new(mrb, b, size)); } val = mrb_ary_join(mrb, ary, mrb_nil_value()); mrb_gc_arena_restore(mrb, ai0); mrb_gc_protect(mrb, val); return val; } } MRB_API mrb_value mrb_format(mrb_state *mrb, const char *format, ...) { va_list ap; mrb_value str; va_start(ap, format); str = mrb_vformat(mrb, format, ap); va_end(ap); return str; } static mrb_noreturn void raise_va(mrb_state *mrb, struct RClass *c, const char *fmt, va_list ap, int argc, mrb_value *argv) { mrb_value mesg; mesg = mrb_vformat(mrb, fmt, ap); if (argv == NULL) { argv = &mesg; } else { argv[0] = mesg; } mrb_exc_raise(mrb, mrb_obj_new(mrb, c, argc+1, argv)); } MRB_API mrb_noreturn void mrb_raisef(mrb_state *mrb, struct RClass *c, const char *fmt, ...) { va_list args; va_start(args, fmt); raise_va(mrb, c, fmt, args, 0, NULL); va_end(args); } MRB_API mrb_noreturn void mrb_name_error(mrb_state *mrb, mrb_sym id, const char *fmt, ...) { mrb_value argv[2]; va_list args; va_start(args, fmt); argv[1] = mrb_symbol_value(id); raise_va(mrb, E_NAME_ERROR, fmt, args, 1, argv); va_end(args); } MRB_API void mrb_warn(mrb_state *mrb, const char *fmt, ...) { #ifndef MRB_DISABLE_STDIO va_list ap; mrb_value str; va_start(ap, fmt); str = mrb_vformat(mrb, fmt, ap); fputs("warning: ", stderr); fwrite(RSTRING_PTR(str), RSTRING_LEN(str), 1, stderr); va_end(ap); #endif } MRB_API mrb_noreturn void mrb_bug(mrb_state *mrb, const char *fmt, ...) { #ifndef MRB_DISABLE_STDIO va_list ap; mrb_value str; va_start(ap, fmt); str = mrb_vformat(mrb, fmt, ap); fputs("bug: ", stderr); fwrite(RSTRING_PTR(str), RSTRING_LEN(str), 1, stderr); va_end(ap); #endif exit(EXIT_FAILURE); } MRB_API mrb_value mrb_make_exception(mrb_state *mrb, mrb_int argc, const mrb_value *argv) { mrb_value mesg; int n; mesg = mrb_nil_value(); switch (argc) { case 0: break; case 1: if (mrb_nil_p(argv[0])) break; if (mrb_string_p(argv[0])) { mesg = mrb_exc_new_str(mrb, E_RUNTIME_ERROR, argv[0]); break; } n = 0; goto exception_call; case 2: case 3: n = 1; exception_call: { mrb_sym exc = mrb_intern_lit(mrb, "exception"); if (mrb_respond_to(mrb, argv[0], exc)) { mesg = mrb_funcall_argv(mrb, argv[0], exc, n, argv+1); } else { /* undef */ mrb_raise(mrb, E_TYPE_ERROR, "exception class/object expected"); } } break; default: mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%S for 0..3)", mrb_fixnum_value(argc)); break; } if (argc > 0) { if (!mrb_obj_is_kind_of(mrb, mesg, mrb->eException_class)) mrb_raise(mrb, mrb->eException_class, "exception object expected"); if (argc > 2) set_backtrace(mrb, mesg, argv[2]); } return mesg; } MRB_API void mrb_sys_fail(mrb_state *mrb, const char *mesg) { struct RClass *sce; mrb_int no; no = (mrb_int)errno; if (mrb_class_defined(mrb, "SystemCallError")) { sce = mrb_class_get(mrb, "SystemCallError"); if (mesg != NULL) { mrb_funcall(mrb, mrb_obj_value(sce), "_sys_fail", 2, mrb_fixnum_value(no), mrb_str_new_cstr(mrb, mesg)); } else { mrb_funcall(mrb, mrb_obj_value(sce), "_sys_fail", 1, mrb_fixnum_value(no)); } } else { mrb_raise(mrb, E_RUNTIME_ERROR, mesg); } } MRB_API mrb_noreturn void mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_value args, char const* fmt, ...) { mrb_value exc; mrb_value argv[3]; va_list ap; va_start(ap, fmt); argv[0] = mrb_vformat(mrb, fmt, ap); argv[1] = mrb_symbol_value(id); argv[2] = args; va_end(ap); exc = mrb_obj_new(mrb, E_NOMETHOD_ERROR, 3, argv); mrb_exc_raise(mrb, exc); } void mrb_init_exception(mrb_state *mrb) { struct RClass *exception, *script_error, *stack_error, *nomem_error; mrb->eException_class = exception = mrb_define_class(mrb, "Exception", mrb->object_class); /* 15.2.22 */ MRB_SET_INSTANCE_TT(exception, MRB_TT_EXCEPTION); mrb_define_class_method(mrb, exception, "exception", mrb_instance_new, MRB_ARGS_ANY()); mrb_define_method(mrb, exception, "exception", exc_exception, MRB_ARGS_ANY()); mrb_define_method(mrb, exception, "initialize", exc_initialize, MRB_ARGS_ANY()); mrb_define_method(mrb, exception, "to_s", exc_to_s, MRB_ARGS_NONE()); mrb_define_method(mrb, exception, "message", exc_message, MRB_ARGS_NONE()); mrb_define_method(mrb, exception, "inspect", exc_inspect, MRB_ARGS_NONE()); mrb_define_method(mrb, exception, "backtrace", mrb_exc_backtrace, MRB_ARGS_NONE()); mrb_define_method(mrb, exception, "set_backtrace", exc_set_backtrace, MRB_ARGS_REQ(1)); mrb->eStandardError_class = mrb_define_class(mrb, "StandardError", mrb->eException_class); /* 15.2.23 */ mrb_define_class(mrb, "RuntimeError", mrb->eStandardError_class); /* 15.2.28 */ script_error = mrb_define_class(mrb, "ScriptError", mrb->eException_class); /* 15.2.37 */ mrb_define_class(mrb, "SyntaxError", script_error); /* 15.2.38 */ stack_error = mrb_define_class(mrb, "SystemStackError", exception); mrb->stack_err = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, stack_error, "stack level too deep")); nomem_error = mrb_define_class(mrb, "NoMemoryError", exception); mrb->nomem_err = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, nomem_error, "Out of memory")); #ifdef MRB_GC_FIXED_ARENA mrb->arena_err = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, nomem_error, "arena overflow error")); #endif } mruby-2.0.0/src/error.h000066400000000000000000000001621340361412400147350ustar00rootroot00000000000000/* this header file is to be removed soon. added for compatibility purpose (1.0.0) */ #include mruby-2.0.0/src/etc.c000066400000000000000000000124311340361412400143540ustar00rootroot00000000000000/* ** etc.c - ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include MRB_API struct RData* mrb_data_object_alloc(mrb_state *mrb, struct RClass *klass, void *ptr, const mrb_data_type *type) { struct RData *data; data = (struct RData*)mrb_obj_alloc(mrb, MRB_TT_DATA, klass); data->data = ptr; data->type = type; return data; } MRB_API void mrb_data_check_type(mrb_state *mrb, mrb_value obj, const mrb_data_type *type) { if (mrb_type(obj) != MRB_TT_DATA) { mrb_check_type(mrb, obj, MRB_TT_DATA); } if (DATA_TYPE(obj) != type) { const mrb_data_type *t2 = DATA_TYPE(obj); if (t2) { mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %S (expected %S)", mrb_str_new_cstr(mrb, t2->struct_name), mrb_str_new_cstr(mrb, type->struct_name)); } else { struct RClass *c = mrb_class(mrb, obj); mrb_raisef(mrb, E_TYPE_ERROR, "uninitialized %S (expected %S)", mrb_obj_value(c), mrb_str_new_cstr(mrb, type->struct_name)); } } } MRB_API void* mrb_data_check_get_ptr(mrb_state *mrb, mrb_value obj, const mrb_data_type *type) { if (mrb_type(obj) != MRB_TT_DATA) { return NULL; } if (DATA_TYPE(obj) != type) { return NULL; } return DATA_PTR(obj); } MRB_API void* mrb_data_get_ptr(mrb_state *mrb, mrb_value obj, const mrb_data_type *type) { mrb_data_check_type(mrb, obj, type); return DATA_PTR(obj); } MRB_API mrb_sym mrb_obj_to_sym(mrb_state *mrb, mrb_value name) { mrb_sym id; switch (mrb_type(name)) { default: 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); /* not reached */ } /* fall through */ case MRB_TT_STRING: name = mrb_str_intern(mrb, name); /* fall through */ case MRB_TT_SYMBOL: id = mrb_symbol(name); } return id; } MRB_API mrb_int #ifdef MRB_WITHOUT_FLOAT mrb_fixnum_id(mrb_int f) #else mrb_float_id(mrb_float f) #endif { const char *p = (const char*)&f; int len = sizeof(f); uint32_t id = 0; #ifndef MRB_WITHOUT_FLOAT /* normalize -0.0 to 0.0 */ if (f == 0) f = 0.0; #endif while (len--) { id = id*65599 + *p; p++; } id = id + (id>>5); return (mrb_int)id; } MRB_API mrb_int mrb_obj_id(mrb_value obj) { mrb_int tt = mrb_type(obj); #define MakeID2(p,t) (mrb_int)(((intptr_t)(p))^(t)) #define MakeID(p) MakeID2(p,tt) switch (tt) { case MRB_TT_FREE: case MRB_TT_UNDEF: return MakeID(0); /* not define */ case MRB_TT_FALSE: if (mrb_nil_p(obj)) return MakeID(1); return MakeID(0); case MRB_TT_TRUE: return MakeID(1); case MRB_TT_SYMBOL: return MakeID(mrb_symbol(obj)); case MRB_TT_FIXNUM: #ifdef MRB_WITHOUT_FLOAT return MakeID(mrb_fixnum_id(mrb_fixnum(obj))); #else return MakeID2(mrb_float_id((mrb_float)mrb_fixnum(obj)), MRB_TT_FLOAT); case MRB_TT_FLOAT: return MakeID(mrb_float_id(mrb_float(obj))); #endif case MRB_TT_STRING: case MRB_TT_OBJECT: case MRB_TT_CLASS: case MRB_TT_MODULE: case MRB_TT_ICLASS: case MRB_TT_SCLASS: case MRB_TT_PROC: case MRB_TT_ARRAY: case MRB_TT_HASH: case MRB_TT_RANGE: case MRB_TT_EXCEPTION: case MRB_TT_FILE: case MRB_TT_DATA: case MRB_TT_ISTRUCT: default: return MakeID(mrb_ptr(obj)); } } #ifdef MRB_WORD_BOXING #ifndef MRB_WITHOUT_FLOAT MRB_API mrb_value mrb_word_boxing_float_value(mrb_state *mrb, mrb_float f) { mrb_value v; v.value.p = mrb_obj_alloc(mrb, MRB_TT_FLOAT, mrb->float_class); v.value.fp->f = f; return v; } MRB_API mrb_value mrb_word_boxing_float_pool(mrb_state *mrb, mrb_float f) { struct RFloat *nf = (struct RFloat *)mrb_malloc(mrb, sizeof(struct RFloat)); nf->tt = MRB_TT_FLOAT; nf->c = mrb->float_class; nf->f = f; return mrb_obj_value(nf); } #endif /* MRB_WITHOUT_FLOAT */ MRB_API mrb_value mrb_word_boxing_cptr_value(mrb_state *mrb, void *p) { mrb_value v; v.value.p = mrb_obj_alloc(mrb, MRB_TT_CPTR, mrb->object_class); v.value.vp->p = p; return v; } #endif /* MRB_WORD_BOXING */ MRB_API mrb_bool mrb_regexp_p(mrb_state *mrb, mrb_value v) { if (mrb->flags & MRB_STATE_NO_REGEXP) { return FALSE; } if ((mrb->flags & MRB_STATE_REGEXP) || mrb_class_defined(mrb, REGEXP_CLASS)) { mrb->flags |= MRB_STATE_REGEXP; return mrb_obj_is_kind_of(mrb, v, mrb_class_get(mrb, REGEXP_CLASS)); } else { mrb->flags |= MRB_STATE_REGEXP; mrb->flags |= MRB_STATE_NO_REGEXP; } return FALSE; } #if defined _MSC_VER && _MSC_VER < 1900 #ifndef va_copy static void mrb_msvc_va_copy(va_list *dest, va_list src) { *dest = src; } #define va_copy(dest, src) mrb_msvc_va_copy(&(dest), src) #endif MRB_API int mrb_msvc_vsnprintf(char *s, size_t n, const char *format, va_list arg) { int cnt; va_list argcp; va_copy(argcp, arg); if (n == 0 || (cnt = _vsnprintf_s(s, n, _TRUNCATE, format, argcp)) < 0) { cnt = _vscprintf(format, arg); } va_end(argcp); return cnt; } MRB_API int mrb_msvc_snprintf(char *s, size_t n, const char *format, ...) { va_list arg; int ret; va_start(arg, format); ret = mrb_msvc_vsnprintf(s, n, format, arg); va_end(arg); return ret; } #endif /* defined _MSC_VER && _MSC_VER < 1900 */ mruby-2.0.0/src/ext/000077500000000000000000000000001340361412400142345ustar00rootroot00000000000000mruby-2.0.0/src/ext/.gitkeep000066400000000000000000000000001340361412400156530ustar00rootroot00000000000000mruby-2.0.0/src/fmt_fp.c000066400000000000000000000223611340361412400150570ustar00rootroot00000000000000#ifndef MRB_WITHOUT_FLOAT #ifdef MRB_DISABLE_STDIO /* Most code in this file originates from musl (src/stdio/vfprintf.c) which, just like mruby itself, is licensed under the MIT license. Copyright (c) 2005-2014 Rich Felker, et al. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include #include #include #include struct fmt_args { mrb_state *mrb; mrb_value str; }; #define MAX(a,b) ((a)>(b) ? (a) : (b)) #define MIN(a,b) ((a)<(b) ? (a) : (b)) /* Convenient bit representation for modifier flags, which all fall * within 31 codepoints of the space character. */ #define ALT_FORM (1U<<('#'-' ')) #define ZERO_PAD (1U<<('0'-' ')) #define LEFT_ADJ (1U<<('-'-' ')) #define PAD_POS (1U<<(' '-' ')) #define MARK_POS (1U<<('+'-' ')) static void out(struct fmt_args *f, const char *s, size_t l) { mrb_str_cat(f->mrb, f->str, s, l); } #define PAD_SIZE 256 static void pad(struct fmt_args *f, char c, ptrdiff_t w, ptrdiff_t l, uint8_t fl) { char pad[PAD_SIZE]; if (fl & (LEFT_ADJ | ZERO_PAD) || l >= w) return; l = w - l; memset(pad, c, l>PAD_SIZE ? PAD_SIZE : l); for (; l >= PAD_SIZE; l -= PAD_SIZE) out(f, pad, PAD_SIZE); out(f, pad, l); } static const char xdigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; static char* fmt_u(uint32_t x, char *s) { for (; x; x /= 10) *--s = '0' + x % 10; return s; } /* Do not override this check. The floating point printing code below * depends on the float.h constants being right. If they are wrong, it * may overflow the stack. */ #if LDBL_MANT_DIG == 53 typedef char compiler_defines_long_double_incorrectly[9-(int)sizeof(long double)]; #endif static int fmt_fp(struct fmt_args *f, long double y, ptrdiff_t p, uint8_t fl, int t) { uint32_t big[(LDBL_MANT_DIG+28)/29 + 1 // mantissa expansion + (LDBL_MAX_EXP+LDBL_MANT_DIG+28+8)/9]; // exponent expansion uint32_t *a, *d, *r, *z; uint32_t i; int e2=0, e, j; ptrdiff_t l; char buf[9+LDBL_MANT_DIG/4], *s; const char *prefix="-0X+0X 0X-0x+0x 0x"; ptrdiff_t pl; char ebuf0[3*sizeof(int)], *ebuf=&ebuf0[3*sizeof(int)], *estr; pl=1; if (signbit(y)) { y=-y; } else if (fl & MARK_POS) { prefix+=3; } else if (fl & PAD_POS) { prefix+=6; } else prefix++, pl=0; if (!isfinite(y)) { const char *ss = (t&32)?"inf":"INF"; if (y!=y) ss=(t&32)?"nan":"NAN"; pad(f, ' ', 0, 3+pl, fl&~ZERO_PAD); out(f, prefix, pl); out(f, ss, 3); pad(f, ' ', 0, 3+pl, fl^LEFT_ADJ); return 3+(int)pl; } y = frexp((double)y, &e2) * 2; if (y) e2--; if ((t|32)=='a') { long double round = 8.0; ptrdiff_t re; if (t&32) prefix += 9; pl += 2; if (p<0 || p>=LDBL_MANT_DIG/4-1) re=0; else re=LDBL_MANT_DIG/4-1-p; if (re) { while (re--) round*=16; if (*prefix=='-') { y=-y; y-=round; y+=round; y=-y; } else { y+=round; y-=round; } } estr=fmt_u(e2<0 ? -e2 : e2, ebuf); if (estr==ebuf) *--estr='0'; *--estr = (e2<0 ? '-' : '+'); *--estr = t+('p'-'a'); s=buf; do { int x=(int)y; *s++=xdigits[x]|(t&32); y=16*(y-x); if (s-buf==1 && (y||p>0||(fl&ALT_FORM))) *s++='.'; } while (y); if (p && s-buf-2 < p) l = (p+2) + (ebuf-estr); else l = (s-buf) + (ebuf-estr); pad(f, ' ', 0, pl+l, fl); out(f, prefix, pl); pad(f, '0', 0, pl+l, fl^ZERO_PAD); out(f, buf, s-buf); pad(f, '0', l-(ebuf-estr)-(s-buf), 0, 0); out(f, estr, ebuf-estr); pad(f, ' ', 0, pl+l, fl^LEFT_ADJ); return (int)pl+(int)l; } if (p<0) p=6; if (y) y *= 268435456.0, e2-=28; if (e2<0) a=r=z=big; else a=r=z=big+sizeof(big)/sizeof(*big) - LDBL_MANT_DIG - 1; do { *z = (uint32_t)y; y = 1000000000*(y-*z++); } while (y); while (e2>0) { uint32_t carry=0; int sh=MIN(29,e2); for (d=z-1; d>=a; d--) { uint64_t x = ((uint64_t)*d<a && !z[-1]) z--; e2-=sh; } while (e2<0) { uint32_t carry=0, *b; int sh=MIN(9,-e2), need=1+((int)p+LDBL_MANT_DIG/3+8)/9; for (d=a; d>sh) + carry; carry = (1000000000>>sh) * rm; } if (!*a) a++; if (carry) *z++ = carry; /* Avoid (slow!) computation past requested precision */ b = (t|32)=='f' ? r : a; if (z-b > need) z = b+need; e2+=sh; } if (a=i; i*=10, e++); else e=0; /* Perform rounding: j is precision after the radix (possibly neg) */ j = (int)p - ((t|32)!='f')*e - ((t|32)=='g' && p); if (j < 9*(z-r-1)) { uint32_t x; /* We avoid C's broken division of negative numbers */ d = r + 1 + ((j+9*LDBL_MAX_EXP)/9 - LDBL_MAX_EXP); j += 9*LDBL_MAX_EXP; j %= 9; for (i=10, j++; j<9; i*=10, j++); x = *d % i; /* Are there any significant digits past j? */ if (x || d+1!=z) { long double round = 2/LDBL_EPSILON; long double small; if (*d/i & 1) round += 2; if (x 999999999) { *d--=0; if (d=i; i*=10, e++); } } if (z>d+1) z=d+1; } for (; z>a && !z[-1]; z--); if ((t|32)=='g') { if (!p) p++; if (p>e && e>=-4) { t--; p-=e+1; } else { t-=2; p--; } if (!(fl&ALT_FORM)) { /* Count trailing zeros in last place */ if (z>a && z[-1]) for (i=10, j=0; z[-1]%i==0; i*=10, j++); else j=9; if ((t|32)=='f') p = MIN(p,MAX(0,9*(z-r-1)-j)); else p = MIN(p,MAX(0,9*(z-r-1)+e-j)); } } l = 1 + p + (p || (fl&ALT_FORM)); if ((t|32)=='f') { if (e>0) l+=e; } else { estr=fmt_u(e<0 ? -e : e, ebuf); while(ebuf-estr<2) *--estr='0'; *--estr = (e<0 ? '-' : '+'); *--estr = t; l += ebuf-estr; } pad(f, ' ', 0, pl+l, fl); out(f, prefix, pl); pad(f, '0', 0, pl+l, fl^ZERO_PAD); if ((t|32)=='f') { if (a>r) a=r; for (d=a; d<=r; d++) { char *ss = fmt_u(*d, buf+9); if (d!=a) while (ss>buf) *--ss='0'; else if (ss==buf+9) *--ss='0'; out(f, ss, buf+9-ss); } if (p || (fl&ALT_FORM)) out(f, ".", 1); for (; d0; d++, p-=9) { char *ss = fmt_u(*d, buf+9); while (ss>buf) *--ss='0'; out(f, ss, MIN(9,p)); } pad(f, '0', p+9, 9, 0); } else { if (z<=a) z=a+1; for (d=a; d=0; d++) { char *ss = fmt_u(*d, buf+9); if (ss==buf+9) *--ss='0'; if (d!=a) while (ss>buf) *--ss='0'; else { out(f, ss++, 1); if (p>0||(fl&ALT_FORM)) out(f, ".", 1); } out(f, ss, MIN(buf+9-ss, p)); p -= (int)(buf+9-ss); } pad(f, '0', p+18, 18, 0); out(f, estr, ebuf-estr); } pad(f, ' ', 0, pl+l, fl^LEFT_ADJ); return (int)pl+(int)l; } static int fmt_core(struct fmt_args *f, const char *fmt, mrb_float flo) { ptrdiff_t p; if (*fmt != '%') { return -1; } ++fmt; if (*fmt == '.') { ++fmt; for (p = 0; ISDIGIT(*fmt); ++fmt) { p = 10 * p + (*fmt - '0'); } } else { p = -1; } switch (*fmt) { case 'e': case 'f': case 'g': case 'a': case 'E': case 'F': case 'G': case 'A': return fmt_fp(f, flo, p, 0, *fmt); default: return -1; } } mrb_value mrb_float_to_str(mrb_state *mrb, mrb_value flo, const char *fmt) { struct fmt_args f; f.mrb = mrb; f.str = mrb_str_new_capa(mrb, 24); if (fmt_core(&f, fmt, mrb_float(flo)) < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid format string"); } return f.str; } #else /* MRB_DISABLE_STDIO */ #include #include mrb_value mrb_float_to_str(mrb_state *mrb, mrb_value flo, const char *fmt) { char buf[25]; snprintf(buf, sizeof(buf), fmt, mrb_float(flo)); return mrb_str_new_cstr(mrb, buf); } #endif /* MRB_DISABLE_STDIO */ #endif mruby-2.0.0/src/gc.c000066400000000000000000001264671340361412400142110ustar00rootroot00000000000000/* ** gc.c - garbage collector for mruby ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* = Tri-color Incremental Garbage Collection mruby's GC is Tri-color Incremental GC with Mark & Sweep. Algorithm details are omitted. Instead, the implementation part is described below. == Object's Color Each object can be painted in three colors: * White - Unmarked. * Gray - Marked, But the child objects are unmarked. * Black - Marked, the child objects are also marked. == Two White Types There're two white color types in a flip-flop fashion: White-A and White-B, which respectively represent the Current White color (the newly allocated objects in the current GC cycle) and the Sweep Target White color (the dead objects to be swept). A and B will be switched just at the beginning of the next GC cycle. At that time, all the dead objects have been swept, while the newly created objects in the current GC cycle which finally remains White are now regarded as dead objects. Instead of traversing all the White-A objects and painting them as White-B, just switch the meaning of White-A and White-B as this will be much cheaper. As a result, the objects we sweep in the current GC cycle are always left from the previous GC cycle. This allows us to sweep objects incrementally, without the disturbance of the newly created objects. == Execution Timing GC Execution Time and Each step interval are decided by live objects count. List of Adjustment API: * gc_interval_ratio_set * gc_step_ratio_set For details, see the comments for each function. == Write Barrier mruby implementer and C extension library writer must insert a write barrier when updating a reference from a field of an object. When updating a reference from a field of object A to object B, two different types of write barrier are available: * mrb_field_write_barrier - target B object for a mark. * mrb_write_barrier - target A object for a mark. == Generational Mode mruby's GC offers an Generational Mode while re-using the tri-color GC infrastructure. It will treat the Black objects as Old objects after each sweep phase, instead of painting them White. The key ideas are still the same as traditional generational GC: * Minor GC - just traverse the Young objects (Gray objects) in the mark phase, then only sweep the newly created objects, and leave the Old objects live. * Major GC - same as a full regular GC cycle. The difference from "traditional" generational GC is, that the major GC in mruby is triggered incrementally in a tri-color manner. For details, see the comments for each function. */ struct free_obj { MRB_OBJECT_HEADER; struct RBasic *next; }; typedef struct { union { struct free_obj free; struct RBasic basic; struct RObject object; struct RClass klass; struct RString string; struct RArray array; struct RHash hash; struct RRange range; struct RData data; struct RProc proc; struct REnv env; struct RException exc; struct RBreak brk; #ifdef MRB_WORD_BOXING #ifndef MRB_WITHOUT_FLOAT struct RFloat floatv; #endif struct RCptr cptr; #endif } as; } RVALUE; #ifdef GC_PROFILE #include #include static double program_invoke_time = 0; static double gc_time = 0; static double gc_total_time = 0; static double gettimeofday_time(void) { struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec + tv.tv_usec * 1e-6; } #define GC_INVOKE_TIME_REPORT(with) do {\ fprintf(stderr, "%s\n", with);\ fprintf(stderr, "gc_invoke: %19.3f\n", gettimeofday_time() - program_invoke_time);\ fprintf(stderr, "is_generational: %d\n", is_generational(gc));\ fprintf(stderr, "is_major_gc: %d\n", is_major_gc(gc));\ } while(0) #define GC_TIME_START do {\ gc_time = gettimeofday_time();\ } while(0) #define GC_TIME_STOP_AND_REPORT do {\ gc_time = gettimeofday_time() - gc_time;\ gc_total_time += gc_time;\ fprintf(stderr, "gc_state: %d\n", gc->state);\ fprintf(stderr, "live: %zu\n", gc->live);\ fprintf(stderr, "majorgc_old_threshold: %zu\n", gc->majorgc_old_threshold);\ fprintf(stderr, "gc_threshold: %zu\n", gc->threshold);\ fprintf(stderr, "gc_time: %30.20f\n", gc_time);\ fprintf(stderr, "gc_total_time: %30.20f\n\n", gc_total_time);\ } while(0) #else #define GC_INVOKE_TIME_REPORT(s) #define GC_TIME_START #define GC_TIME_STOP_AND_REPORT #endif #ifdef GC_DEBUG #define DEBUG(x) (x) #else #define DEBUG(x) #endif #ifndef MRB_HEAP_PAGE_SIZE #define MRB_HEAP_PAGE_SIZE 1024 #endif #define GC_STEP_SIZE 1024 /* white: 001 or 010, black: 100, gray: 000 */ #define GC_GRAY 0 #define GC_WHITE_A 1 #define GC_WHITE_B (1 << 1) #define GC_BLACK (1 << 2) #define GC_WHITES (GC_WHITE_A | GC_WHITE_B) #define GC_COLOR_MASK 7 #define paint_gray(o) ((o)->color = GC_GRAY) #define paint_black(o) ((o)->color = GC_BLACK) #define paint_white(o) ((o)->color = GC_WHITES) #define paint_partial_white(s, o) ((o)->color = (s)->current_white_part) #define is_gray(o) ((o)->color == GC_GRAY) #define is_white(o) ((o)->color & GC_WHITES) #define is_black(o) ((o)->color & GC_BLACK) #define flip_white_part(s) ((s)->current_white_part = other_white_part(s)) #define other_white_part(s) ((s)->current_white_part ^ GC_WHITES) #define is_dead(s, o) (((o)->color & other_white_part(s) & GC_WHITES) || (o)->tt == MRB_TT_FREE) #define objects(p) ((RVALUE *)p->objects) MRB_API void* mrb_realloc_simple(mrb_state *mrb, void *p, size_t len) { void *p2; p2 = (mrb->allocf)(mrb, p, len, mrb->allocf_ud); if (!p2 && len > 0 && mrb->gc.heaps) { mrb_full_gc(mrb); p2 = (mrb->allocf)(mrb, p, len, mrb->allocf_ud); } return p2; } MRB_API void* mrb_realloc(mrb_state *mrb, void *p, size_t len) { void *p2; p2 = mrb_realloc_simple(mrb, p, len); if (len == 0) return p2; if (p2 == NULL) { if (mrb->gc.out_of_memory) { mrb_exc_raise(mrb, mrb_obj_value(mrb->nomem_err)); /* mrb_panic(mrb); */ } else { mrb->gc.out_of_memory = TRUE; mrb_exc_raise(mrb, mrb_obj_value(mrb->nomem_err)); } } else { mrb->gc.out_of_memory = FALSE; } return p2; } MRB_API void* mrb_malloc(mrb_state *mrb, size_t len) { return mrb_realloc(mrb, 0, len); } MRB_API void* mrb_malloc_simple(mrb_state *mrb, size_t len) { return mrb_realloc_simple(mrb, 0, len); } MRB_API void* mrb_calloc(mrb_state *mrb, size_t nelem, size_t len) { void *p; if (nelem > 0 && len > 0 && nelem <= SIZE_MAX / len) { size_t size; size = nelem * len; p = mrb_malloc(mrb, size); memset(p, 0, size); } else { p = NULL; } return p; } MRB_API void mrb_free(mrb_state *mrb, void *p) { (mrb->allocf)(mrb, p, 0, mrb->allocf_ud); } static mrb_bool heap_p(mrb_gc *gc, struct RBasic *object) { mrb_heap_page* page; page = gc->heaps; while (page) { RVALUE *p; p = objects(page); if (&p[0].as.basic <= object && object <= &p[MRB_HEAP_PAGE_SIZE].as.basic) { return TRUE; } page = page->next; } return FALSE; } MRB_API mrb_bool mrb_object_dead_p(mrb_state *mrb, struct RBasic *object) { mrb_gc *gc = &mrb->gc; if (!heap_p(gc, object)) return TRUE; return is_dead(gc, object); } static void link_heap_page(mrb_gc *gc, mrb_heap_page *page) { page->next = gc->heaps; if (gc->heaps) gc->heaps->prev = page; gc->heaps = page; } static void unlink_heap_page(mrb_gc *gc, mrb_heap_page *page) { if (page->prev) page->prev->next = page->next; if (page->next) page->next->prev = page->prev; if (gc->heaps == page) gc->heaps = page->next; page->prev = NULL; page->next = NULL; } static void link_free_heap_page(mrb_gc *gc, mrb_heap_page *page) { page->free_next = gc->free_heaps; if (gc->free_heaps) { gc->free_heaps->free_prev = page; } gc->free_heaps = page; } static void unlink_free_heap_page(mrb_gc *gc, mrb_heap_page *page) { if (page->free_prev) page->free_prev->free_next = page->free_next; if (page->free_next) page->free_next->free_prev = page->free_prev; if (gc->free_heaps == page) gc->free_heaps = page->free_next; page->free_prev = NULL; page->free_next = NULL; } static void add_heap(mrb_state *mrb, mrb_gc *gc) { mrb_heap_page *page = (mrb_heap_page *)mrb_calloc(mrb, 1, sizeof(mrb_heap_page) + MRB_HEAP_PAGE_SIZE * sizeof(RVALUE)); RVALUE *p, *e; struct RBasic *prev = NULL; for (p = objects(page), e=p+MRB_HEAP_PAGE_SIZE; pas.free.tt = MRB_TT_FREE; p->as.free.next = prev; prev = &p->as.basic; } page->freelist = prev; link_heap_page(gc, page); link_free_heap_page(gc, page); } #define DEFAULT_GC_INTERVAL_RATIO 200 #define DEFAULT_GC_STEP_RATIO 200 #define MAJOR_GC_INC_RATIO 120 #define MAJOR_GC_TOOMANY 10000 #define is_generational(gc) ((gc)->generational) #define is_major_gc(gc) (is_generational(gc) && (gc)->full) #define is_minor_gc(gc) (is_generational(gc) && !(gc)->full) void mrb_gc_init(mrb_state *mrb, mrb_gc *gc) { #ifndef MRB_GC_FIXED_ARENA gc->arena = (struct RBasic**)mrb_malloc(mrb, sizeof(struct RBasic*)*MRB_GC_ARENA_SIZE); gc->arena_capa = MRB_GC_ARENA_SIZE; #endif gc->current_white_part = GC_WHITE_A; gc->heaps = NULL; gc->free_heaps = NULL; add_heap(mrb, gc); gc->interval_ratio = DEFAULT_GC_INTERVAL_RATIO; gc->step_ratio = DEFAULT_GC_STEP_RATIO; #ifndef MRB_GC_TURN_OFF_GENERATIONAL gc->generational = TRUE; gc->full = TRUE; #endif #ifdef GC_PROFILE program_invoke_time = gettimeofday_time(); #endif } static void obj_free(mrb_state *mrb, struct RBasic *obj, int end); void free_heap(mrb_state *mrb, mrb_gc *gc) { mrb_heap_page *page = gc->heaps; mrb_heap_page *tmp; RVALUE *p, *e; while (page) { tmp = page; page = page->next; for (p = objects(tmp), e=p+MRB_HEAP_PAGE_SIZE; pas.free.tt != MRB_TT_FREE) obj_free(mrb, &p->as.basic, TRUE); } mrb_free(mrb, tmp); } } void mrb_gc_destroy(mrb_state *mrb, mrb_gc *gc) { free_heap(mrb, gc); #ifndef MRB_GC_FIXED_ARENA mrb_free(mrb, gc->arena); #endif } static void gc_protect(mrb_state *mrb, mrb_gc *gc, struct RBasic *p) { #ifdef MRB_GC_FIXED_ARENA if (gc->arena_idx >= MRB_GC_ARENA_SIZE) { /* arena overflow error */ gc->arena_idx = MRB_GC_ARENA_SIZE - 4; /* force room in arena */ mrb_exc_raise(mrb, mrb_obj_value(mrb->arena_err)); } #else if (gc->arena_idx >= gc->arena_capa) { /* extend arena */ gc->arena_capa = (int)(gc->arena_capa * 3 / 2); gc->arena = (struct RBasic**)mrb_realloc(mrb, gc->arena, sizeof(struct RBasic*)*gc->arena_capa); } #endif gc->arena[gc->arena_idx++] = p; } /* mrb_gc_protect() leaves the object in the arena */ MRB_API void mrb_gc_protect(mrb_state *mrb, mrb_value obj) { if (mrb_immediate_p(obj)) return; gc_protect(mrb, &mrb->gc, mrb_basic_ptr(obj)); } #define GC_ROOT_NAME "_gc_root_" /* mrb_gc_register() keeps the object from GC. Register your object when it's exported to C world, without reference from Ruby world, e.g. callback arguments. Don't forget to remove the object using mrb_gc_unregister, otherwise your object will leak. */ MRB_API void mrb_gc_register(mrb_state *mrb, mrb_value obj) { mrb_sym root = mrb_intern_lit(mrb, GC_ROOT_NAME); mrb_value table = mrb_gv_get(mrb, root); if (mrb_nil_p(table) || mrb_type(table) != MRB_TT_ARRAY) { table = mrb_ary_new(mrb); mrb_gv_set(mrb, root, table); } mrb_ary_push(mrb, table, obj); } /* mrb_gc_unregister() removes the object from GC root. */ MRB_API void mrb_gc_unregister(mrb_state *mrb, mrb_value obj) { mrb_sym root = mrb_intern_lit(mrb, GC_ROOT_NAME); mrb_value table = mrb_gv_get(mrb, root); struct RArray *a; mrb_int i; if (mrb_nil_p(table)) return; if (mrb_type(table) != MRB_TT_ARRAY) { mrb_gv_set(mrb, root, mrb_nil_value()); return; } a = mrb_ary_ptr(table); mrb_ary_modify(mrb, a); for (i = 0; i < ARY_LEN(a); i++) { if (mrb_obj_eq(mrb, ARY_PTR(a)[i], obj)) { mrb_int len = ARY_LEN(a)-1; mrb_value *ptr = ARY_PTR(a); ARY_SET_LEN(a, len); memmove(&ptr[i], &ptr[i + 1], (len - i) * sizeof(mrb_value)); break; } } } MRB_API struct RBasic* mrb_obj_alloc(mrb_state *mrb, enum mrb_vtype ttype, struct RClass *cls) { struct RBasic *p; static const RVALUE RVALUE_zero = { { { MRB_TT_FALSE } } }; mrb_gc *gc = &mrb->gc; if (cls) { enum mrb_vtype tt; switch (cls->tt) { case MRB_TT_CLASS: case MRB_TT_SCLASS: case MRB_TT_MODULE: case MRB_TT_ENV: break; default: mrb_raise(mrb, E_TYPE_ERROR, "allocation failure"); } tt = MRB_INSTANCE_TT(cls); if (tt != MRB_TT_FALSE && ttype != MRB_TT_SCLASS && ttype != MRB_TT_ICLASS && ttype != MRB_TT_ENV && ttype != tt) { mrb_raisef(mrb, E_TYPE_ERROR, "allocation failure of %S", mrb_obj_value(cls)); } } #ifdef MRB_GC_STRESS mrb_full_gc(mrb); #endif if (gc->threshold < gc->live) { mrb_incremental_gc(mrb); } if (gc->free_heaps == NULL) { add_heap(mrb, gc); } p = gc->free_heaps->freelist; gc->free_heaps->freelist = ((struct free_obj*)p)->next; if (gc->free_heaps->freelist == NULL) { unlink_free_heap_page(gc, gc->free_heaps); } gc->live++; gc_protect(mrb, gc, p); *(RVALUE *)p = RVALUE_zero; p->tt = ttype; p->c = cls; paint_partial_white(gc, p); return p; } static inline void add_gray_list(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj) { #ifdef MRB_GC_STRESS if (obj->tt > MRB_TT_MAXDEFINE) { abort(); } #endif paint_gray(obj); obj->gcnext = gc->gray_list; gc->gray_list = obj; } static int ci_nregs(mrb_callinfo *ci) { struct RProc *p = ci->proc; int n = 0; if (!p) { if (ci->argc < 0) return 3; return ci->argc+2; } if (!MRB_PROC_CFUNC_P(p) && p->body.irep) { n = p->body.irep->nregs; } if (ci->argc < 0) { if (n < 3) n = 3; /* self + args + blk */ } if (ci->argc > n) { n = ci->argc + 2; /* self + blk */ } return n; } static void mark_context_stack(mrb_state *mrb, struct mrb_context *c) { size_t i; size_t e; mrb_value nil; if (c->stack == NULL) return; e = c->stack - c->stbase; if (c->ci) { e += ci_nregs(c->ci); } if (c->stbase + e > c->stend) e = c->stend - c->stbase; for (i=0; istbase[i]; if (!mrb_immediate_p(v)) { mrb_gc_mark(mrb, mrb_basic_ptr(v)); } } e = c->stend - c->stbase; nil = mrb_nil_value(); for (; istbase[i] = nil; } } static void mark_context(mrb_state *mrb, struct mrb_context *c) { int i; mrb_callinfo *ci; start: if (c->status == MRB_FIBER_TERMINATED) return; /* mark VM stack */ mark_context_stack(mrb, c); /* mark call stack */ if (c->cibase) { for (ci = c->cibase; ci <= c->ci; ci++) { mrb_gc_mark(mrb, (struct RBasic*)ci->env); mrb_gc_mark(mrb, (struct RBasic*)ci->proc); mrb_gc_mark(mrb, (struct RBasic*)ci->target_class); } } /* mark ensure stack */ for (i=0; ieidx; i++) { mrb_gc_mark(mrb, (struct RBasic*)c->ensure[i]); } /* mark fibers */ mrb_gc_mark(mrb, (struct RBasic*)c->fib); if (c->prev) { c = c->prev; goto start; } } static void gc_mark_children(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj) { mrb_assert(is_gray(obj)); paint_black(obj); gc->gray_list = obj->gcnext; mrb_gc_mark(mrb, (struct RBasic*)obj->c); switch (obj->tt) { case MRB_TT_ICLASS: { struct RClass *c = (struct RClass*)obj; if (MRB_FLAG_TEST(c, MRB_FL_CLASS_IS_ORIGIN)) mrb_gc_mark_mt(mrb, c); mrb_gc_mark(mrb, (struct RBasic*)((struct RClass*)obj)->super); } break; case MRB_TT_CLASS: case MRB_TT_MODULE: case MRB_TT_SCLASS: { struct RClass *c = (struct RClass*)obj; mrb_gc_mark_mt(mrb, c); mrb_gc_mark(mrb, (struct RBasic*)c->super); } /* fall through */ case MRB_TT_OBJECT: case MRB_TT_DATA: case MRB_TT_EXCEPTION: mrb_gc_mark_iv(mrb, (struct RObject*)obj); break; case MRB_TT_PROC: { struct RProc *p = (struct RProc*)obj; mrb_gc_mark(mrb, (struct RBasic*)p->upper); mrb_gc_mark(mrb, (struct RBasic*)p->e.env); } break; case MRB_TT_ENV: { struct REnv *e = (struct REnv*)obj; mrb_int i, len; if (MRB_ENV_STACK_SHARED_P(e) && e->cxt && e->cxt->fib) { mrb_gc_mark(mrb, (struct RBasic*)e->cxt->fib); } len = MRB_ENV_STACK_LEN(e); for (i=0; istack[i]); } } break; case MRB_TT_FIBER: { struct mrb_context *c = ((struct RFiber*)obj)->cxt; if (c) mark_context(mrb, c); } break; case MRB_TT_ARRAY: { struct RArray *a = (struct RArray*)obj; size_t i, e; for (i=0,e=ARY_LEN(a); ias.heap.aux.fshared); } break; case MRB_TT_RANGE: { struct RRange *r = (struct RRange*)obj; if (r->edges) { mrb_gc_mark_value(mrb, r->edges->beg); mrb_gc_mark_value(mrb, r->edges->end); } } break; default: break; } } MRB_API void mrb_gc_mark(mrb_state *mrb, struct RBasic *obj) { if (obj == 0) return; if (!is_white(obj)) return; mrb_assert((obj)->tt != MRB_TT_FREE); add_gray_list(mrb, &mrb->gc, obj); } static void obj_free(mrb_state *mrb, struct RBasic *obj, int end) { DEBUG(fprintf(stderr, "obj_free(%p,tt=%d)\n",obj,obj->tt)); switch (obj->tt) { /* immediate - no mark */ case MRB_TT_TRUE: case MRB_TT_FIXNUM: case MRB_TT_SYMBOL: /* cannot happen */ return; #ifndef MRB_WITHOUT_FLOAT case MRB_TT_FLOAT: #ifdef MRB_WORD_BOXING break; #else return; #endif #endif case MRB_TT_OBJECT: mrb_gc_free_iv(mrb, (struct RObject*)obj); break; case MRB_TT_EXCEPTION: mrb_gc_free_iv(mrb, (struct RObject*)obj); break; case MRB_TT_CLASS: case MRB_TT_MODULE: case MRB_TT_SCLASS: 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_FL_CLASS_IS_ORIGIN)) mrb_gc_free_mt(mrb, (struct RClass*)obj); break; case MRB_TT_ENV: { struct REnv *e = (struct REnv*)obj; if (MRB_ENV_STACK_SHARED_P(e)) { /* cannot be freed */ e->stack = NULL; break; } mrb_free(mrb, e->stack); e->stack = NULL; } break; case MRB_TT_FIBER: { struct mrb_context *c = ((struct RFiber*)obj)->cxt; if (c && c != mrb->root_c) { if (!end && c->status != MRB_FIBER_TERMINATED) { mrb_callinfo *ci = c->ci; mrb_callinfo *ce = c->cibase; while (ce <= ci) { struct REnv *e = ci->env; if (e && !mrb_object_dead_p(mrb, (struct RBasic*)e) && e->tt == MRB_TT_ENV && MRB_ENV_STACK_SHARED_P(e)) { mrb_env_unshare(mrb, e); } ci--; } } mrb_free_context(mrb, c); } } break; case MRB_TT_ARRAY: if (ARY_SHARED_P(obj)) mrb_ary_decref(mrb, ((struct RArray*)obj)->as.heap.aux.shared); else if (!ARY_EMBED_P(obj)) mrb_free(mrb, ((struct RArray*)obj)->as.heap.ptr); break; case MRB_TT_HASH: mrb_gc_free_iv(mrb, (struct RObject*)obj); mrb_gc_free_hash(mrb, (struct RHash*)obj); break; case MRB_TT_STRING: mrb_gc_free_str(mrb, (struct RString*)obj); break; case MRB_TT_PROC: { struct RProc *p = (struct RProc*)obj; if (!MRB_PROC_CFUNC_P(p) && p->body.irep) { mrb_irep *irep = p->body.irep; if (end) { mrb_irep_cutref(mrb, irep); } mrb_irep_decref(mrb, irep); } } break; case MRB_TT_RANGE: mrb_free(mrb, ((struct RRange*)obj)->edges); break; case MRB_TT_DATA: { struct RData *d = (struct RData*)obj; if (d->type && d->type->dfree) { d->type->dfree(mrb, d->data); } mrb_gc_free_iv(mrb, (struct RObject*)obj); } break; default: break; } obj->tt = MRB_TT_FREE; } static void root_scan_phase(mrb_state *mrb, mrb_gc *gc) { int i, e; if (!is_minor_gc(gc)) { gc->gray_list = NULL; gc->atomic_gray_list = NULL; } mrb_gc_mark_gv(mrb); /* mark arena */ for (i=0,e=gc->arena_idx; iarena[i]); } /* mark class hierarchy */ mrb_gc_mark(mrb, (struct RBasic*)mrb->object_class); /* mark built-in classes */ mrb_gc_mark(mrb, (struct RBasic*)mrb->class_class); mrb_gc_mark(mrb, (struct RBasic*)mrb->module_class); mrb_gc_mark(mrb, (struct RBasic*)mrb->proc_class); mrb_gc_mark(mrb, (struct RBasic*)mrb->string_class); mrb_gc_mark(mrb, (struct RBasic*)mrb->array_class); mrb_gc_mark(mrb, (struct RBasic*)mrb->hash_class); mrb_gc_mark(mrb, (struct RBasic*)mrb->range_class); #ifndef MRB_WITHOUT_FLOAT mrb_gc_mark(mrb, (struct RBasic*)mrb->float_class); #endif mrb_gc_mark(mrb, (struct RBasic*)mrb->fixnum_class); mrb_gc_mark(mrb, (struct RBasic*)mrb->true_class); mrb_gc_mark(mrb, (struct RBasic*)mrb->false_class); mrb_gc_mark(mrb, (struct RBasic*)mrb->nil_class); mrb_gc_mark(mrb, (struct RBasic*)mrb->symbol_class); mrb_gc_mark(mrb, (struct RBasic*)mrb->kernel_module); mrb_gc_mark(mrb, (struct RBasic*)mrb->eException_class); mrb_gc_mark(mrb, (struct RBasic*)mrb->eStandardError_class); /* mark top_self */ mrb_gc_mark(mrb, (struct RBasic*)mrb->top_self); /* mark exception */ mrb_gc_mark(mrb, (struct RBasic*)mrb->exc); /* mark pre-allocated exception */ mrb_gc_mark(mrb, (struct RBasic*)mrb->nomem_err); mrb_gc_mark(mrb, (struct RBasic*)mrb->stack_err); #ifdef MRB_GC_FIXED_ARENA mrb_gc_mark(mrb, (struct RBasic*)mrb->arena_err); #endif mark_context(mrb, mrb->c); if (mrb->root_c != mrb->c) { mark_context(mrb, mrb->root_c); } } static size_t gc_gray_mark(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj) { size_t children = 0; gc_mark_children(mrb, gc, obj); switch (obj->tt) { case MRB_TT_ICLASS: children++; break; case MRB_TT_CLASS: case MRB_TT_SCLASS: case MRB_TT_MODULE: { struct RClass *c = (struct RClass*)obj; children += mrb_gc_mark_iv_size(mrb, (struct RObject*)obj); children += mrb_gc_mark_mt_size(mrb, c); children++; } break; case MRB_TT_OBJECT: case MRB_TT_DATA: case MRB_TT_EXCEPTION: children += mrb_gc_mark_iv_size(mrb, (struct RObject*)obj); break; case MRB_TT_ENV: children += MRB_ENV_STACK_LEN(obj); break; case MRB_TT_FIBER: { struct mrb_context *c = ((struct RFiber*)obj)->cxt; size_t i; mrb_callinfo *ci; if (!c || c->status == MRB_FIBER_TERMINATED) break; /* mark stack */ i = c->stack - c->stbase; if (c->ci) { i += ci_nregs(c->ci); } if (c->stbase + i > c->stend) i = c->stend - c->stbase; children += i; /* mark ensure stack */ children += c->eidx; /* mark closure */ if (c->cibase) { for (i=0, ci = c->cibase; ci <= c->ci; i++, ci++) ; } children += i; } break; case MRB_TT_ARRAY: { struct RArray *a = (struct RArray*)obj; children += ARY_LEN(a); } break; case MRB_TT_HASH: children += mrb_gc_mark_iv_size(mrb, (struct RObject*)obj); children += mrb_gc_mark_hash_size(mrb, (struct RHash*)obj); break; case MRB_TT_PROC: case MRB_TT_RANGE: children+=2; break; default: break; } return children; } static void gc_mark_gray_list(mrb_state *mrb, mrb_gc *gc) { while (gc->gray_list) { if (is_gray(gc->gray_list)) gc_mark_children(mrb, gc, gc->gray_list); else gc->gray_list = gc->gray_list->gcnext; } } static size_t incremental_marking_phase(mrb_state *mrb, mrb_gc *gc, size_t limit) { size_t tried_marks = 0; while (gc->gray_list && tried_marks < limit) { tried_marks += gc_gray_mark(mrb, gc, gc->gray_list); } return tried_marks; } static void final_marking_phase(mrb_state *mrb, mrb_gc *gc) { int i, e; /* mark arena */ for (i=0,e=gc->arena_idx; iarena[i]); } mrb_gc_mark_gv(mrb); mark_context(mrb, mrb->c); mark_context(mrb, mrb->root_c); mrb_gc_mark(mrb, (struct RBasic*)mrb->exc); gc_mark_gray_list(mrb, gc); mrb_assert(gc->gray_list == NULL); gc->gray_list = gc->atomic_gray_list; gc->atomic_gray_list = NULL; gc_mark_gray_list(mrb, gc); mrb_assert(gc->gray_list == NULL); } static void prepare_incremental_sweep(mrb_state *mrb, mrb_gc *gc) { gc->state = MRB_GC_STATE_SWEEP; gc->sweeps = gc->heaps; gc->live_after_mark = gc->live; } static size_t incremental_sweep_phase(mrb_state *mrb, mrb_gc *gc, size_t limit) { mrb_heap_page *page = gc->sweeps; size_t tried_sweep = 0; while (page && (tried_sweep < limit)) { RVALUE *p = objects(page); RVALUE *e = p + MRB_HEAP_PAGE_SIZE; size_t freed = 0; mrb_bool dead_slot = TRUE; mrb_bool full = (page->freelist == NULL); if (is_minor_gc(gc) && page->old) { /* skip a slot which doesn't contain any young object */ p = e; dead_slot = FALSE; } while (pas.basic)) { if (p->as.basic.tt != MRB_TT_FREE) { obj_free(mrb, &p->as.basic, FALSE); if (p->as.basic.tt == MRB_TT_FREE) { p->as.free.next = page->freelist; page->freelist = (struct RBasic*)p; freed++; } else { dead_slot = FALSE; } } } else { if (!is_generational(gc)) paint_partial_white(gc, &p->as.basic); /* next gc target */ dead_slot = FALSE; } p++; } /* free dead slot */ if (dead_slot && freed < MRB_HEAP_PAGE_SIZE) { mrb_heap_page *next = page->next; unlink_heap_page(gc, page); unlink_free_heap_page(gc, page); mrb_free(mrb, page); page = next; } else { if (full && freed > 0) { link_free_heap_page(gc, page); } if (page->freelist == NULL && is_minor_gc(gc)) page->old = TRUE; else page->old = FALSE; page = page->next; } tried_sweep += MRB_HEAP_PAGE_SIZE; gc->live -= freed; gc->live_after_mark -= freed; } gc->sweeps = page; return tried_sweep; } static size_t incremental_gc(mrb_state *mrb, mrb_gc *gc, size_t limit) { switch (gc->state) { case MRB_GC_STATE_ROOT: root_scan_phase(mrb, gc); gc->state = MRB_GC_STATE_MARK; flip_white_part(gc); return 0; case MRB_GC_STATE_MARK: if (gc->gray_list) { return incremental_marking_phase(mrb, gc, limit); } else { final_marking_phase(mrb, gc); prepare_incremental_sweep(mrb, gc); return 0; } case MRB_GC_STATE_SWEEP: { size_t tried_sweep = 0; tried_sweep = incremental_sweep_phase(mrb, gc, limit); if (tried_sweep == 0) gc->state = MRB_GC_STATE_ROOT; return tried_sweep; } default: /* unknown state */ mrb_assert(0); return 0; } } static void incremental_gc_until(mrb_state *mrb, mrb_gc *gc, mrb_gc_state to_state) { do { incremental_gc(mrb, gc, SIZE_MAX); } while (gc->state != to_state); } static void incremental_gc_step(mrb_state *mrb, mrb_gc *gc) { size_t limit = 0, result = 0; limit = (GC_STEP_SIZE/100) * gc->step_ratio; while (result < limit) { result += incremental_gc(mrb, gc, limit); if (gc->state == MRB_GC_STATE_ROOT) break; } gc->threshold = gc->live + GC_STEP_SIZE; } static void clear_all_old(mrb_state *mrb, mrb_gc *gc) { mrb_bool origin_mode = gc->generational; mrb_assert(is_generational(gc)); if (is_major_gc(gc)) { /* finish the half baked GC */ incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT); } /* Sweep the dead objects, then reset all the live objects * (including all the old objects, of course) to white. */ gc->generational = FALSE; prepare_incremental_sweep(mrb, gc); incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT); gc->generational = origin_mode; /* The gray objects have already been painted as white */ gc->atomic_gray_list = gc->gray_list = NULL; } MRB_API void mrb_incremental_gc(mrb_state *mrb) { mrb_gc *gc = &mrb->gc; if (gc->disabled || gc->iterating) return; GC_INVOKE_TIME_REPORT("mrb_incremental_gc()"); GC_TIME_START; if (is_minor_gc(gc)) { incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT); } else { incremental_gc_step(mrb, gc); } if (gc->state == MRB_GC_STATE_ROOT) { mrb_assert(gc->live >= gc->live_after_mark); gc->threshold = (gc->live_after_mark/100) * gc->interval_ratio; if (gc->threshold < GC_STEP_SIZE) { gc->threshold = GC_STEP_SIZE; } if (is_major_gc(gc)) { size_t threshold = gc->live_after_mark/100 * MAJOR_GC_INC_RATIO; gc->full = FALSE; if (threshold < MAJOR_GC_TOOMANY) { gc->majorgc_old_threshold = threshold; } else { /* too many objects allocated during incremental GC, */ /* instead of increasing threshold, invoke full GC. */ mrb_full_gc(mrb); } } else if (is_minor_gc(gc)) { if (gc->live > gc->majorgc_old_threshold) { clear_all_old(mrb, gc); gc->full = TRUE; } } } GC_TIME_STOP_AND_REPORT; } /* Perform a full gc cycle */ MRB_API void mrb_full_gc(mrb_state *mrb) { mrb_gc *gc = &mrb->gc; if (gc->disabled || gc->iterating) return; GC_INVOKE_TIME_REPORT("mrb_full_gc()"); GC_TIME_START; if (is_generational(gc)) { /* clear all the old objects back to young */ clear_all_old(mrb, gc); gc->full = TRUE; } else if (gc->state != MRB_GC_STATE_ROOT) { /* finish half baked GC cycle */ incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT); } incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT); gc->threshold = (gc->live_after_mark/100) * gc->interval_ratio; if (is_generational(gc)) { gc->majorgc_old_threshold = gc->live_after_mark/100 * MAJOR_GC_INC_RATIO; gc->full = FALSE; } GC_TIME_STOP_AND_REPORT; } MRB_API void mrb_garbage_collect(mrb_state *mrb) { mrb_full_gc(mrb); } /* * Field write barrier * Paint obj(Black) -> value(White) to obj(Black) -> value(Gray). */ MRB_API void mrb_field_write_barrier(mrb_state *mrb, struct RBasic *obj, struct RBasic *value) { mrb_gc *gc = &mrb->gc; if (!is_black(obj)) return; if (!is_white(value)) return; mrb_assert(gc->state == MRB_GC_STATE_MARK || (!is_dead(gc, value) && !is_dead(gc, obj))); mrb_assert(is_generational(gc) || gc->state != MRB_GC_STATE_ROOT); if (is_generational(gc) || gc->state == MRB_GC_STATE_MARK) { add_gray_list(mrb, gc, value); } else { mrb_assert(gc->state == MRB_GC_STATE_SWEEP); paint_partial_white(gc, obj); /* for never write barriers */ } } /* * Write barrier * Paint obj(Black) to obj(Gray). * * The object that is painted gray will be traversed atomically in final * mark phase. So you use this write barrier if it's frequency written spot. * e.g. Set element on Array. */ MRB_API void mrb_write_barrier(mrb_state *mrb, struct RBasic *obj) { mrb_gc *gc = &mrb->gc; if (!is_black(obj)) return; mrb_assert(!is_dead(gc, obj)); mrb_assert(is_generational(gc) || gc->state != MRB_GC_STATE_ROOT); paint_gray(obj); obj->gcnext = gc->atomic_gray_list; gc->atomic_gray_list = obj; } /* * call-seq: * GC.start -> nil * * Initiates full garbage collection. * */ static mrb_value gc_start(mrb_state *mrb, mrb_value obj) { mrb_full_gc(mrb); return mrb_nil_value(); } /* * call-seq: * GC.enable -> true or false * * Enables garbage collection, returning true if garbage * collection was previously disabled. * * GC.disable #=> false * GC.enable #=> true * GC.enable #=> false * */ static mrb_value gc_enable(mrb_state *mrb, mrb_value obj) { mrb_bool old = mrb->gc.disabled; mrb->gc.disabled = FALSE; return mrb_bool_value(old); } /* * call-seq: * GC.disable -> true or false * * Disables garbage collection, returning true if garbage * collection was already disabled. * * GC.disable #=> false * GC.disable #=> true * */ static mrb_value gc_disable(mrb_state *mrb, mrb_value obj) { mrb_bool old = mrb->gc.disabled; mrb->gc.disabled = TRUE; return mrb_bool_value(old); } /* * call-seq: * GC.interval_ratio -> fixnum * * Returns ratio of GC interval. Default value is 200(%). * */ static mrb_value gc_interval_ratio_get(mrb_state *mrb, mrb_value obj) { return mrb_fixnum_value(mrb->gc.interval_ratio); } /* * call-seq: * GC.interval_ratio = fixnum -> nil * * Updates ratio of GC interval. Default value is 200(%). * GC start as soon as after end all step of GC if you set 100(%). * */ static mrb_value gc_interval_ratio_set(mrb_state *mrb, mrb_value obj) { mrb_int ratio; mrb_get_args(mrb, "i", &ratio); mrb->gc.interval_ratio = (int)ratio; return mrb_nil_value(); } /* * call-seq: * GC.step_ratio -> fixnum * * Returns step span ratio of Incremental GC. Default value is 200(%). * */ static mrb_value gc_step_ratio_get(mrb_state *mrb, mrb_value obj) { return mrb_fixnum_value(mrb->gc.step_ratio); } /* * call-seq: * GC.step_ratio = fixnum -> nil * * Updates step span ratio of Incremental GC. Default value is 200(%). * 1 step of incrementalGC becomes long if a rate is big. * */ static mrb_value gc_step_ratio_set(mrb_state *mrb, mrb_value obj) { mrb_int ratio; mrb_get_args(mrb, "i", &ratio); mrb->gc.step_ratio = (int)ratio; return mrb_nil_value(); } static void change_gen_gc_mode(mrb_state *mrb, mrb_gc *gc, mrb_bool enable) { if (gc->disabled || gc->iterating) { mrb_raise(mrb, E_RUNTIME_ERROR, "generational mode changed when GC disabled"); return; } if (is_generational(gc) && !enable) { clear_all_old(mrb, gc); mrb_assert(gc->state == MRB_GC_STATE_ROOT); gc->full = FALSE; } else if (!is_generational(gc) && enable) { incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT); gc->majorgc_old_threshold = gc->live_after_mark/100 * MAJOR_GC_INC_RATIO; gc->full = FALSE; } gc->generational = enable; } /* * call-seq: * GC.generational_mode -> true or false * * Returns generational or normal gc mode. * */ static mrb_value gc_generational_mode_get(mrb_state *mrb, mrb_value self) { return mrb_bool_value(mrb->gc.generational); } /* * call-seq: * GC.generational_mode = true or false -> true or false * * Changes to generational or normal gc mode. * */ static mrb_value gc_generational_mode_set(mrb_state *mrb, mrb_value self) { mrb_bool enable; mrb_get_args(mrb, "b", &enable); if (mrb->gc.generational != enable) change_gen_gc_mode(mrb, &mrb->gc, enable); return mrb_bool_value(enable); } static void gc_each_objects(mrb_state *mrb, mrb_gc *gc, mrb_each_object_callback *callback, void *data) { mrb_heap_page* page; page = gc->heaps; while (page != NULL) { RVALUE *p; int i; p = objects(page); for (i=0; i < MRB_HEAP_PAGE_SIZE; i++) { if ((*callback)(mrb, &p[i].as.basic, data) == MRB_EACH_OBJ_BREAK) return; } page = page->next; } } void mrb_objspace_each_objects(mrb_state *mrb, mrb_each_object_callback *callback, void *data) { mrb_bool iterating = mrb->gc.iterating; mrb_full_gc(mrb); mrb->gc.iterating = TRUE; if (iterating) { gc_each_objects(mrb, &mrb->gc, callback, data); } else { struct mrb_jmpbuf *prev_jmp = mrb->jmp; struct mrb_jmpbuf c_jmp; MRB_TRY(&c_jmp) { mrb->jmp = &c_jmp; gc_each_objects(mrb, &mrb->gc, callback, data); mrb->jmp = prev_jmp; mrb->gc.iterating = iterating; } MRB_CATCH(&c_jmp) { mrb->gc.iterating = iterating; mrb->jmp = prev_jmp; MRB_THROW(prev_jmp); } MRB_END_EXC(&c_jmp); } } #ifdef GC_TEST #ifdef GC_DEBUG static mrb_value gc_test(mrb_state *, mrb_value); #endif #endif void mrb_init_gc(mrb_state *mrb) { struct RClass *gc; gc = mrb_define_module(mrb, "GC"); mrb_define_class_method(mrb, gc, "start", gc_start, MRB_ARGS_NONE()); mrb_define_class_method(mrb, gc, "enable", gc_enable, MRB_ARGS_NONE()); mrb_define_class_method(mrb, gc, "disable", gc_disable, MRB_ARGS_NONE()); mrb_define_class_method(mrb, gc, "interval_ratio", gc_interval_ratio_get, MRB_ARGS_NONE()); mrb_define_class_method(mrb, gc, "interval_ratio=", gc_interval_ratio_set, MRB_ARGS_REQ(1)); mrb_define_class_method(mrb, gc, "step_ratio", gc_step_ratio_get, MRB_ARGS_NONE()); mrb_define_class_method(mrb, gc, "step_ratio=", gc_step_ratio_set, MRB_ARGS_REQ(1)); mrb_define_class_method(mrb, gc, "generational_mode=", gc_generational_mode_set, MRB_ARGS_REQ(1)); mrb_define_class_method(mrb, gc, "generational_mode", gc_generational_mode_get, MRB_ARGS_NONE()); #ifdef GC_TEST #ifdef GC_DEBUG mrb_define_class_method(mrb, gc, "test", gc_test, MRB_ARGS_NONE()); #endif #endif } #ifdef GC_TEST #ifdef GC_DEBUG void test_mrb_field_write_barrier(void) { mrb_state *mrb = mrb_open(); struct RBasic *obj, *value; mrb_gc *gc = &mrb->gc; puts("test_mrb_field_write_barrier"); gc->generational = FALSE; obj = mrb_basic_ptr(mrb_ary_new(mrb)); value = mrb_basic_ptr(mrb_str_new_lit(mrb, "value")); paint_black(obj); paint_partial_white(gc, value); puts(" in MRB_GC_STATE_MARK"); gc->state = MRB_GC_STATE_MARK; mrb_field_write_barrier(mrb, obj, value); mrb_assert(is_gray(value)); puts(" in MRB_GC_STATE_SWEEP"); paint_partial_white(gc, value); gc->state = MRB_GC_STATE_SWEEP; mrb_field_write_barrier(mrb, obj, value); mrb_assert(obj->color & gc->current_white_part); mrb_assert(value->color & gc->current_white_part); puts(" fail with black"); gc->state = MRB_GC_STATE_MARK; paint_white(obj); paint_partial_white(gc, value); mrb_field_write_barrier(mrb, obj, value); mrb_assert(obj->color & gc->current_white_part); puts(" fail with gray"); gc->state = MRB_GC_STATE_MARK; paint_black(obj); paint_gray(value); mrb_field_write_barrier(mrb, obj, value); mrb_assert(is_gray(value)); { puts("test_mrb_field_write_barrier_value"); obj = mrb_basic_ptr(mrb_ary_new(mrb)); mrb_value value = mrb_str_new_lit(mrb, "value"); paint_black(obj); paint_partial_white(gc, mrb_basic_ptr(value)); gc->state = MRB_GC_STATE_MARK; mrb_field_write_barrier_value(mrb, obj, value); mrb_assert(is_gray(mrb_basic_ptr(value))); } mrb_close(mrb); } void test_mrb_write_barrier(void) { mrb_state *mrb = mrb_open(); struct RBasic *obj; mrb_gc *gc = &mrb->gc; puts("test_mrb_write_barrier"); obj = mrb_basic_ptr(mrb_ary_new(mrb)); paint_black(obj); puts(" in MRB_GC_STATE_MARK"); gc->state = MRB_GC_STATE_MARK; mrb_write_barrier(mrb, obj); mrb_assert(is_gray(obj)); mrb_assert(gc->atomic_gray_list == obj); puts(" fail with gray"); paint_gray(obj); mrb_write_barrier(mrb, obj); mrb_assert(is_gray(obj)); mrb_close(mrb); } void test_add_gray_list(void) { mrb_state *mrb = mrb_open(); struct RBasic *obj1, *obj2; mrb_gc *gc = &mrb->gc; puts("test_add_gray_list"); change_gen_gc_mode(mrb, gc, FALSE); mrb_assert(gc->gray_list == NULL); obj1 = mrb_basic_ptr(mrb_str_new_lit(mrb, "test")); add_gray_list(mrb, gc, obj1); mrb_assert(gc->gray_list == obj1); mrb_assert(is_gray(obj1)); obj2 = mrb_basic_ptr(mrb_str_new_lit(mrb, "test")); add_gray_list(mrb, gc, obj2); mrb_assert(gc->gray_list == obj2); mrb_assert(gc->gray_list->gcnext == obj1); mrb_assert(is_gray(obj2)); mrb_close(mrb); } void test_gc_gray_mark(void) { mrb_state *mrb = mrb_open(); mrb_value obj_v, value_v; struct RBasic *obj; size_t gray_num = 0; mrb_gc *gc = &mrb->gc; puts("test_gc_gray_mark"); puts(" in MRB_TT_CLASS"); obj = (struct RBasic*)mrb->object_class; paint_gray(obj); gray_num = gc_gray_mark(mrb, gc, obj); mrb_assert(is_black(obj)); mrb_assert(gray_num > 1); puts(" in MRB_TT_ARRAY"); obj_v = mrb_ary_new(mrb); value_v = mrb_str_new_lit(mrb, "test"); paint_gray(mrb_basic_ptr(obj_v)); paint_partial_white(gc, mrb_basic_ptr(value_v)); mrb_ary_push(mrb, obj_v, value_v); gray_num = gc_gray_mark(mrb, gc, mrb_basic_ptr(obj_v)); mrb_assert(is_black(mrb_basic_ptr(obj_v))); mrb_assert(is_gray(mrb_basic_ptr(value_v))); mrb_assert(gray_num == 1); mrb_close(mrb); } void test_incremental_gc(void) { mrb_state *mrb = mrb_open(); size_t max = ~0, live = 0, total = 0, freed = 0; RVALUE *free; mrb_heap_page *page; mrb_gc *gc = &mrb->gc; puts("test_incremental_gc"); change_gen_gc_mode(mrb, gc, FALSE); puts(" in mrb_full_gc"); mrb_full_gc(mrb); mrb_assert(gc->state == MRB_GC_STATE_ROOT); puts(" in MRB_GC_STATE_ROOT"); incremental_gc(mrb, gc, max); mrb_assert(gc->state == MRB_GC_STATE_MARK); puts(" in MRB_GC_STATE_MARK"); incremental_gc_until(mrb, gc, MRB_GC_STATE_SWEEP); mrb_assert(gc->state == MRB_GC_STATE_SWEEP); puts(" in MRB_GC_STATE_SWEEP"); page = gc->heaps; while (page) { RVALUE *p = objects(page); RVALUE *e = p + MRB_HEAP_PAGE_SIZE; while (pas.basic)) { live++; } if (is_gray(&p->as.basic) && !is_dead(gc, &p->as.basic)) { printf("%p\n", &p->as.basic); } p++; } page = page->next; total += MRB_HEAP_PAGE_SIZE; } mrb_assert(gc->gray_list == NULL); incremental_gc(mrb, gc, max); mrb_assert(gc->state == MRB_GC_STATE_SWEEP); incremental_gc(mrb, gc, max); mrb_assert(gc->state == MRB_GC_STATE_ROOT); free = (RVALUE*)gc->heaps->freelist; while (free) { freed++; free = (RVALUE*)free->as.free.next; } mrb_assert(gc->live == live); mrb_assert(gc->live == total-freed); puts("test_incremental_gc(gen)"); incremental_gc_until(mrb, gc, MRB_GC_STATE_SWEEP); change_gen_gc_mode(mrb, gc, TRUE); mrb_assert(gc->full == FALSE); mrb_assert(gc->state == MRB_GC_STATE_ROOT); puts(" in minor"); mrb_assert(is_minor_gc(gc)); mrb_assert(gc->majorgc_old_threshold > 0); gc->majorgc_old_threshold = 0; mrb_incremental_gc(mrb); mrb_assert(gc->full == TRUE); mrb_assert(gc->state == MRB_GC_STATE_ROOT); puts(" in major"); mrb_assert(is_major_gc(gc)); do { mrb_incremental_gc(mrb); } while (gc->state != MRB_GC_STATE_ROOT); mrb_assert(gc->full == FALSE); mrb_close(mrb); } void test_incremental_sweep_phase(void) { mrb_state *mrb = mrb_open(); mrb_gc *gc = &mrb->gc; puts("test_incremental_sweep_phase"); add_heap(mrb, gc); gc->sweeps = gc->heaps; mrb_assert(gc->heaps->next->next == NULL); mrb_assert(gc->free_heaps->next->next == NULL); incremental_sweep_phase(mrb, gc, MRB_HEAP_PAGE_SIZE * 3); mrb_assert(gc->heaps->next == NULL); mrb_assert(gc->heaps == gc->free_heaps); mrb_close(mrb); } static mrb_value gc_test(mrb_state *mrb, mrb_value self) { test_mrb_field_write_barrier(); test_mrb_write_barrier(); test_add_gray_list(); test_gc_gray_mark(); test_incremental_gc(); test_incremental_sweep_phase(); return mrb_nil_value(); } #endif /* GC_DEBUG */ #endif /* GC_TEST */ mruby-2.0.0/src/hash.c000066400000000000000000001020621340361412400145240ustar00rootroot00000000000000/* ** hash.c - Hash class ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #ifndef MRB_WITHOUT_FLOAT /* a function to get hash value of a float number */ mrb_int mrb_float_id(mrb_float f); #endif /* return non zero to break the loop */ typedef int (ht_foreach_func)(mrb_state *mrb,mrb_value key, mrb_value val, void *data); #ifndef MRB_HT_INIT_SIZE #define MRB_HT_INIT_SIZE 4 #endif #define HT_SEG_INCREASE_RATIO 1.2 struct segkv { mrb_value key; mrb_value val; }; typedef struct segment { uint16_t size; struct segment *next; struct segkv e[]; } segment; typedef struct segindex { size_t size; size_t capa; struct segkv *table[]; } segindex; /* Instance variable table structure */ typedef struct htable { segment *rootseg; segment *lastseg; mrb_int size; uint16_t last_len; segindex *index; } htable; static /* inline */ size_t ht_hash_func(mrb_state *mrb, htable *t, mrb_value key) { enum mrb_vtype tt = mrb_type(key); mrb_value hv; size_t h; segindex *index = t->index; size_t capa = index ? index->capa : 0; switch (tt) { case MRB_TT_STRING: h = mrb_str_hash(mrb, key); break; case MRB_TT_TRUE: case MRB_TT_FALSE: case MRB_TT_SYMBOL: case MRB_TT_FIXNUM: #ifndef MRB_WITHOUT_FLOAT case MRB_TT_FLOAT: #endif h = (size_t)mrb_obj_id(key); break; default: hv = mrb_funcall(mrb, key, "hash", 0); h = (size_t)t ^ (size_t)mrb_fixnum(hv); break; } if (index && (index != t->index || capa != index->capa)) { mrb_raise(mrb, E_RUNTIME_ERROR, "hash modified"); } return ((h)^((h)<<2)^((h)>>2)); } static inline mrb_bool ht_hash_equal(mrb_state *mrb, htable *t, mrb_value a, mrb_value b) { enum mrb_vtype tt = mrb_type(a); switch (tt) { case MRB_TT_STRING: return mrb_str_equal(mrb, a, b); case MRB_TT_SYMBOL: if (mrb_type(b) != MRB_TT_SYMBOL) return FALSE; return mrb_symbol(a) == mrb_symbol(b); case MRB_TT_FIXNUM: switch (mrb_type(b)) { case MRB_TT_FIXNUM: return mrb_fixnum(a) == mrb_fixnum(b); #ifndef MRB_WITHOUT_FLOAT case MRB_TT_FLOAT: return (mrb_float)mrb_fixnum(a) == mrb_float(b); #endif default: return FALSE; } #ifndef MRB_WITHOUT_FLOAT case MRB_TT_FLOAT: switch (mrb_type(b)) { case MRB_TT_FIXNUM: return mrb_float(a) == (mrb_float)mrb_fixnum(b); case MRB_TT_FLOAT: return mrb_float(a) == mrb_float(b); default: return FALSE; } #endif default: { segindex *index = t->index; size_t capa = index ? index->capa : 0; mrb_bool eql = mrb_eql(mrb, a, b); if (index && (index != t->index || capa != index->capa)) { mrb_raise(mrb, E_RUNTIME_ERROR, "hash modified"); } return eql; } } } /* Creates the instance variable table. */ static htable* ht_new(mrb_state *mrb) { htable *t; t = (htable*)mrb_malloc(mrb, sizeof(htable)); t->size = 0; t->rootseg = NULL; t->lastseg = NULL; t->last_len = 0; t->index = NULL; return t; } #define power2(v) do { \ v--;\ v |= v >> 1;\ v |= v >> 2;\ v |= v >> 4;\ v |= v >> 8;\ v |= v >> 16;\ v++;\ } while (0) #ifndef UPPER_BOUND #define UPPER_BOUND(x) ((x)>>2|(x)>>1) #endif #define HT_MASK(index) ((index->capa)-1) /* Build index for the hash table */ static void ht_index(mrb_state *mrb, htable *t) { size_t size = (size_t)t->size; size_t mask; segindex *index = t->index; segment *seg; size_t i; /* allocate index table */ if (index && index->size >= UPPER_BOUND(index->capa)) { size = index->capa+1; } power2(size); if (!index || index->capa < size) { index = (segindex*)mrb_realloc_simple(mrb, index, sizeof(segindex)+sizeof(struct segkv*)*size); if (index == NULL) { mrb_free(mrb, index); t->index = NULL; return; } t->index = index; } index->size = t->size; index->capa = size; for (i=0; itable[i] = NULL; } /* rebuld index */ mask = HT_MASK(index); seg = t->rootseg; while (seg) { for (i=0; isize; i++) { mrb_value key; size_t k, step = 0; if (!seg->next && i >= (size_t)t->last_len) { return; } key = seg->e[i].key; if (mrb_undef_p(key)) continue; k = ht_hash_func(mrb, t, key) & mask; while (index->table[k]) { k = (k+(++step)) & mask; } index->table[k] = &seg->e[i]; } seg = seg->next; } } /* Compacts the hash table removing deleted entries. */ static void ht_compact(mrb_state *mrb, htable *t) { segment *seg; mrb_int i; segment *seg2 = NULL; mrb_int i2; mrb_int size = 0; if (t == NULL) return; seg = t->rootseg; if (t->index && (size_t)t->size == t->index->size) { ht_index(mrb, t); return; } while (seg) { for (i=0; isize; i++) { mrb_value k = seg->e[i].key; if (!seg->next && i >= t->last_len) { goto exit; } if (mrb_undef_p(k)) { /* found delete key */ if (seg2 == NULL) { seg2 = seg; i2 = i; } } else { size++; if (seg2 != NULL) { seg2->e[i2++] = seg->e[i]; if (i2 >= seg2->size) { seg2 = seg2->next; i2 = 0; } } } } seg = seg->next; } exit: /* reached at end */ t->size = size; if (seg2) { seg = seg2->next; seg2->next = NULL; t->last_len = i2; t->lastseg = seg2; while (seg) { seg2 = seg->next; mrb_free(mrb, seg); seg = seg2; } } if (t->index) { ht_index(mrb, t); } } static segment* segment_alloc(mrb_state *mrb, segment *seg) { uint32_t size; if (!seg) size = MRB_HT_INIT_SIZE; else { size = seg->size*HT_SEG_INCREASE_RATIO + 1; if (size > UINT16_MAX) size = UINT16_MAX; } seg = (segment*)mrb_malloc(mrb, sizeof(segment)+sizeof(struct segkv)*size); seg->size = size; seg->next = NULL; return seg; } /* Set the value for the key in the indexed table. */ static void ht_index_put(mrb_state *mrb, htable *t, mrb_value key, mrb_value val) { segindex *index = t->index; size_t k, sp, step = 0, mask; segment *seg; if (index->size >= UPPER_BOUND(index->capa)) { /* need to expand table */ ht_compact(mrb, t); index = t->index; } mask = HT_MASK(index); sp = index->capa; k = ht_hash_func(mrb, t, key) & mask; while (index->table[k]) { mrb_value key2 = index->table[k]->key; if (mrb_undef_p(key2)) { if (sp == index->capa) sp = k; } else if (ht_hash_equal(mrb, t, key, key2)) { index->table[k]->val = val; return; } k = (k+(++step)) & mask; } if (sp < index->capa) { k = sp; } /* put the value at the last */ seg = t->lastseg; if (t->last_len < seg->size) { index->table[k] = &seg->e[t->last_len++]; } else { /* append a new segment */ seg->next = segment_alloc(mrb, seg); seg = seg->next; seg->next = NULL; t->lastseg = seg; t->last_len = 1; index->table[k] = &seg->e[0]; } index->table[k]->key = key; index->table[k]->val = val; index->size++; t->size++; } /* Set the value for the key in the table. */ static void ht_put(mrb_state *mrb, htable *t, mrb_value key, mrb_value val) { segment *seg; mrb_int i, deleted = 0; if (t == NULL) return; if (t->index) { ht_index_put(mrb, t, key, val); return; } seg = t->rootseg; while (seg) { for (i=0; isize; i++) { mrb_value k = seg->e[i].key; /* Found room in last segment after last_len */ if (!seg->next && i >= t->last_len) { seg->e[i].key = key; seg->e[i].val = val; t->last_len = i+1; t->size++; return; } if (mrb_undef_p(k)) { deleted++; continue; } if (ht_hash_equal(mrb, t, k, key)) { seg->e[i].val = val; return; } } seg = seg->next; } /* Not found */ /* Compact if last segment has room */ if (deleted > 0 && deleted > MRB_HT_INIT_SIZE) { ht_compact(mrb, t); } t->size++; /* check if thre's room after compaction */ if (t->lastseg && t->last_len < t->lastseg->size) { seg = t->lastseg; i = t->last_len; } else { /* append new segment */ seg = segment_alloc(mrb, t->lastseg); i = 0; if (t->rootseg == NULL) { t->rootseg = seg; } else { t->lastseg->next = seg; } t->lastseg = seg; } seg->e[i].key = key; seg->e[i].val = val; t->last_len = i+1; if (t->index == NULL && t->size > MRB_HT_INIT_SIZE*4) { ht_index(mrb, t); } } /* Get a value for a key from the indexed table. */ static mrb_bool ht_index_get(mrb_state *mrb, htable *t, mrb_value key, mrb_value *vp) { segindex *index = t->index; size_t mask = HT_MASK(index); size_t k = ht_hash_func(mrb, t, key) & mask; size_t step = 0; while (index->table[k]) { mrb_value key2 = index->table[k]->key; if (!mrb_undef_p(key2) && ht_hash_equal(mrb, t, key, key2)) { if (vp) *vp = index->table[k]->val; return TRUE; } k = (k+(++step)) & mask; } return FALSE; } /* Get a value for a key from the hash table. */ static mrb_bool ht_get(mrb_state *mrb, htable *t, mrb_value key, mrb_value *vp) { segment *seg; mrb_int i; if (t == NULL) return FALSE; if (t->index) { return ht_index_get(mrb, t, key, vp); } seg = t->rootseg; while (seg) { for (i=0; isize; i++) { mrb_value k = seg->e[i].key; if (!seg->next && i >= t->last_len) { return FALSE; } if (mrb_undef_p(k)) continue; if (ht_hash_equal(mrb, t, k, key)) { if (vp) *vp = seg->e[i].val; return TRUE; } } seg = seg->next; } return FALSE; } /* Deletes the value for the symbol from the instance variable table. */ /* Deletion is done by overwriting keys by `undef`. */ static mrb_bool ht_del(mrb_state *mrb, htable *t, mrb_value key, mrb_value *vp) { segment *seg; mrb_int i; if (t == NULL) return FALSE; seg = t->rootseg; while (seg) { for (i=0; isize; i++) { mrb_value key2; if (!seg->next && i >= t->last_len) { /* not found */ return FALSE; } key2 = seg->e[i].key; if (!mrb_undef_p(key2) && ht_hash_equal(mrb, t, key, key2)) { if (vp) *vp = seg->e[i].val; seg->e[i].key = mrb_undef_value(); t->size--; return TRUE; } } seg = seg->next; } return FALSE; } /* Iterates over the instance variable table. */ static void ht_foreach(mrb_state *mrb, htable *t, ht_foreach_func *func, void *p) { segment *seg; mrb_int i; if (t == NULL) return; seg = t->rootseg; while (seg) { for (i=0; isize; i++) { /* no value in last segment after last_len */ if (!seg->next && i >= t->last_len) { return; } if (mrb_undef_p(seg->e[i].key)) continue; if ((*func)(mrb, seg->e[i].key, seg->e[i].val, p) != 0) return; } seg = seg->next; } } /* Copy the instance variable table. */ static htable* ht_copy(mrb_state *mrb, htable *t) { segment *seg; htable *t2; mrb_int i; seg = t->rootseg; t2 = ht_new(mrb); while (seg) { for (i=0; isize; i++) { mrb_value key = seg->e[i].key; mrb_value val = seg->e[i].val; if ((seg->next == NULL) && (i >= t->last_len)) { return t2; } ht_put(mrb, t2, key, val); } seg = seg->next; } return t2; } /* Free memory of the instance variable table. */ static void ht_free(mrb_state *mrb, htable *t) { segment *seg; if (!t) return; seg = t->rootseg; while (seg) { segment *p = seg; seg = seg->next; mrb_free(mrb, p); } if (t->index) mrb_free(mrb, t->index); mrb_free(mrb, t); } static void mrb_hash_modify(mrb_state *mrb, mrb_value hash); static inline mrb_value ht_key(mrb_state *mrb, mrb_value key) { if (mrb_string_p(key) && !MRB_FROZEN_P(mrb_str_ptr(key))) { key = mrb_str_dup(mrb, key); MRB_SET_FROZEN_FLAG(mrb_str_ptr(key)); } return key; } #define KEY(key) ht_key(mrb, key) static int hash_mark_i(mrb_state *mrb, mrb_value key, mrb_value val, void *p) { mrb_gc_mark_value(mrb, key); mrb_gc_mark_value(mrb, val); return 0; } void mrb_gc_mark_hash(mrb_state *mrb, struct RHash *hash) { ht_foreach(mrb, hash->ht, hash_mark_i, NULL); } size_t mrb_gc_mark_hash_size(mrb_state *mrb, struct RHash *hash) { if (!hash->ht) return 0; return hash->ht->size*2; } void mrb_gc_free_hash(mrb_state *mrb, struct RHash *hash) { ht_free(mrb, hash->ht); } MRB_API mrb_value mrb_hash_new(mrb_state *mrb) { struct RHash *h; h = (struct RHash*)mrb_obj_alloc(mrb, MRB_TT_HASH, mrb->hash_class); h->ht = 0; h->iv = 0; return mrb_obj_value(h); } MRB_API mrb_value mrb_hash_new_capa(mrb_state *mrb, mrb_int capa) { struct RHash *h; h = (struct RHash*)mrb_obj_alloc(mrb, MRB_TT_HASH, mrb->hash_class); /* preallocate hash table */ h->ht = ht_new(mrb); /* capacity ignored */ h->iv = 0; return mrb_obj_value(h); } static mrb_value mrb_hash_default(mrb_state *mrb, mrb_value hash); static mrb_value hash_default(mrb_state *mrb, mrb_value hash, mrb_value key); static mrb_value mrb_hash_init_copy(mrb_state *mrb, mrb_value self) { mrb_value orig; struct RHash* copy; htable *orig_h; mrb_value ifnone, vret; mrb_get_args(mrb, "o", &orig); if (mrb_obj_equal(mrb, self, orig)) return self; if ((mrb_type(self) != mrb_type(orig)) || (mrb_obj_class(mrb, self) != mrb_obj_class(mrb, orig))) { mrb_raise(mrb, E_TYPE_ERROR, "initialize_copy should take same class object"); } orig_h = RHASH_TBL(self); copy = (struct RHash*)mrb_obj_alloc(mrb, MRB_TT_HASH, mrb->hash_class); copy->ht = ht_copy(mrb, orig_h); if (MRB_RHASH_DEFAULT_P(self)) { copy->flags |= MRB_HASH_DEFAULT; } if (MRB_RHASH_PROCDEFAULT_P(self)) { copy->flags |= MRB_HASH_PROC_DEFAULT; } vret = mrb_obj_value(copy); ifnone = RHASH_IFNONE(self); if (!mrb_nil_p(ifnone)) { mrb_iv_set(mrb, vret, mrb_intern_lit(mrb, "ifnone"), ifnone); } return vret; } static int check_kdict_i(mrb_state *mrb, mrb_value key, mrb_value val, void *data) { if (!mrb_symbol_p(key)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "keyword argument hash with non symbol keys"); } return 0; } void mrb_hash_check_kdict(mrb_state *mrb, mrb_value self) { htable *t; t = RHASH_TBL(self); if (!t || t->size == 0) return; ht_foreach(mrb, t, check_kdict_i, NULL); } MRB_API mrb_value mrb_hash_dup(mrb_state *mrb, mrb_value self) { struct RHash* copy; htable *orig_h; orig_h = RHASH_TBL(self); copy = (struct RHash*)mrb_obj_alloc(mrb, MRB_TT_HASH, mrb->hash_class); copy->ht = orig_h ? ht_copy(mrb, orig_h) : NULL; return mrb_obj_value(copy); } MRB_API mrb_value mrb_hash_get(mrb_state *mrb, mrb_value hash, mrb_value key) { mrb_value val; mrb_sym mid; if (ht_get(mrb, RHASH_TBL(hash), key, &val)) { return val; } mid = mrb_intern_lit(mrb, "default"); if (mrb_func_basic_p(mrb, hash, mid, mrb_hash_default)) { return hash_default(mrb, hash, key); } /* xxx mrb_funcall_tailcall(mrb, hash, "default", 1, key); */ return mrb_funcall_argv(mrb, hash, mid, 1, &key); } MRB_API mrb_value mrb_hash_fetch(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value def) { mrb_value val; if (ht_get(mrb, RHASH_TBL(hash), key, &val)) { return val; } /* not found */ return def; } MRB_API void mrb_hash_set(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value val) { mrb_hash_modify(mrb, hash); key = KEY(key); ht_put(mrb, RHASH_TBL(hash), key, val); mrb_field_write_barrier_value(mrb, (struct RBasic*)RHASH(hash), key); mrb_field_write_barrier_value(mrb, (struct RBasic*)RHASH(hash), val); return; } static void mrb_hash_modify(mrb_state *mrb, mrb_value hash) { if (MRB_FROZEN_P(mrb_hash_ptr(hash))) { mrb_raise(mrb, E_FROZEN_ERROR, "can't modify frozen hash"); } if (!RHASH_TBL(hash)) { RHASH_TBL(hash) = ht_new(mrb); } } /* 15.2.13.4.16 */ /* * call-seq: * Hash.new -> new_hash * Hash.new(obj) -> new_hash * Hash.new {|hash, key| block } -> new_hash * * Returns a new, empty hash. If this hash is subsequently accessed by * a key that doesn't correspond to a hash entry, the value returned * depends on the style of new used to create the hash. In * the first form, the access returns nil. If * obj is specified, this single object will be used for * all default values. If a block is specified, it will be * called with the hash object and the key, and should return the * default value. It is the block's responsibility to store the value * in the hash if required. * * h = Hash.new("Go Fish") * h["a"] = 100 * h["b"] = 200 * h["a"] #=> 100 * h["c"] #=> "Go Fish" * # The following alters the single default object * h["c"].upcase! #=> "GO FISH" * h["d"] #=> "GO FISH" * h.keys #=> ["a", "b"] * * # While this creates a new default object each time * h = Hash.new { |hash, key| hash[key] = "Go Fish: #{key}" } * h["c"] #=> "Go Fish: c" * h["c"].upcase! #=> "GO FISH: C" * h["d"] #=> "Go Fish: d" * h.keys #=> ["c", "d"] * */ static mrb_value mrb_hash_init(mrb_state *mrb, mrb_value hash) { mrb_value block, ifnone; mrb_bool ifnone_p; ifnone = mrb_nil_value(); mrb_get_args(mrb, "&|o?", &block, &ifnone, &ifnone_p); mrb_hash_modify(mrb, hash); if (!mrb_nil_p(block)) { if (ifnone_p) { mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong number of arguments"); } RHASH(hash)->flags |= MRB_HASH_PROC_DEFAULT; ifnone = block; } if (!mrb_nil_p(ifnone)) { RHASH(hash)->flags |= MRB_HASH_DEFAULT; mrb_iv_set(mrb, hash, mrb_intern_lit(mrb, "ifnone"), ifnone); } return hash; } /* 15.2.13.4.2 */ /* * call-seq: * hsh[key] -> value * * Element Reference---Retrieves the value object corresponding * to the key object. If not found, returns the default value (see * Hash::new for details). * * h = { "a" => 100, "b" => 200 } * h["a"] #=> 100 * h["c"] #=> nil * */ static mrb_value mrb_hash_aget(mrb_state *mrb, mrb_value self) { mrb_value key; mrb_get_args(mrb, "o", &key); return mrb_hash_get(mrb, self, key); } static mrb_value hash_default(mrb_state *mrb, mrb_value hash, mrb_value key) { if (MRB_RHASH_DEFAULT_P(hash)) { if (MRB_RHASH_PROCDEFAULT_P(hash)) { return mrb_funcall(mrb, RHASH_PROCDEFAULT(hash), "call", 2, hash, key); } else { return RHASH_IFNONE(hash); } } return mrb_nil_value(); } /* 15.2.13.4.5 */ /* * call-seq: * hsh.default(key=nil) -> obj * * Returns the default value, the value that would be returned by * hsh[key] if key did not exist in hsh. * See also Hash::new and Hash#default=. * * h = Hash.new #=> {} * h.default #=> nil * h.default(2) #=> nil * * h = Hash.new("cat") #=> {} * h.default #=> "cat" * h.default(2) #=> "cat" * * h = Hash.new {|h,k| h[k] = k.to_i*10} #=> {} * h.default #=> nil * h.default(2) #=> 20 */ static mrb_value mrb_hash_default(mrb_state *mrb, mrb_value hash) { mrb_value key; mrb_bool given; mrb_get_args(mrb, "|o?", &key, &given); if (MRB_RHASH_DEFAULT_P(hash)) { if (MRB_RHASH_PROCDEFAULT_P(hash)) { if (!given) return mrb_nil_value(); return mrb_funcall(mrb, RHASH_PROCDEFAULT(hash), "call", 2, hash, key); } else { return RHASH_IFNONE(hash); } } return mrb_nil_value(); } /* 15.2.13.4.6 */ /* * call-seq: * hsh.default = obj -> obj * * Sets the default value, the value returned for a key that does not * exist in the hash. It is not possible to set the default to a * Proc that will be executed on each key lookup. * * h = { "a" => 100, "b" => 200 } * h.default = "Go fish" * h["a"] #=> 100 * h["z"] #=> "Go fish" * # This doesn't do what you might hope... * h.default = proc do |hash, key| * hash[key] = key + key * end * h[2] #=> # * h["cat"] #=> # */ static mrb_value mrb_hash_set_default(mrb_state *mrb, mrb_value hash) { mrb_value ifnone; mrb_get_args(mrb, "o", &ifnone); mrb_hash_modify(mrb, hash); mrb_iv_set(mrb, hash, mrb_intern_lit(mrb, "ifnone"), ifnone); RHASH(hash)->flags &= ~MRB_HASH_PROC_DEFAULT; if (!mrb_nil_p(ifnone)) { RHASH(hash)->flags |= MRB_HASH_DEFAULT; } else { RHASH(hash)->flags &= ~MRB_HASH_DEFAULT; } return ifnone; } /* 15.2.13.4.7 */ /* * call-seq: * hsh.default_proc -> anObject * * If Hash::new was invoked with a block, return that * block, otherwise return nil. * * h = Hash.new {|h,k| h[k] = k*k } #=> {} * p = h.default_proc #=> # * a = [] #=> [] * p.call(a, 2) * a #=> [nil, nil, 4] */ static mrb_value mrb_hash_default_proc(mrb_state *mrb, mrb_value hash) { if (MRB_RHASH_PROCDEFAULT_P(hash)) { return RHASH_PROCDEFAULT(hash); } return mrb_nil_value(); } /* * call-seq: * hsh.default_proc = proc_obj -> proc_obj * * Sets the default proc to be executed on each key lookup. * * h.default_proc = proc do |hash, key| * hash[key] = key + key * end * h[2] #=> 4 * h["cat"] #=> "catcat" */ static mrb_value mrb_hash_set_default_proc(mrb_state *mrb, mrb_value hash) { mrb_value ifnone; mrb_get_args(mrb, "o", &ifnone); mrb_hash_modify(mrb, hash); mrb_iv_set(mrb, hash, mrb_intern_lit(mrb, "ifnone"), ifnone); if (!mrb_nil_p(ifnone)) { RHASH(hash)->flags |= MRB_HASH_PROC_DEFAULT; RHASH(hash)->flags |= MRB_HASH_DEFAULT; } else { RHASH(hash)->flags &= ~MRB_HASH_DEFAULT; RHASH(hash)->flags &= ~MRB_HASH_PROC_DEFAULT; } return ifnone; } MRB_API mrb_value mrb_hash_delete_key(mrb_state *mrb, mrb_value hash, mrb_value key) { htable *t = RHASH_TBL(hash); mrb_value del_val; if (ht_del(mrb, t, key, &del_val)) { return del_val; } /* not found */ return mrb_nil_value(); } static mrb_value mrb_hash_delete(mrb_state *mrb, mrb_value self) { mrb_value key; mrb_get_args(mrb, "o", &key); mrb_hash_modify(mrb, self); return mrb_hash_delete_key(mrb, self, key); } /* find first element in a hash table, and remove it. */ static void ht_shift(mrb_state *mrb, htable *t, mrb_value *kp, mrb_value *vp) { segment *seg = t->rootseg; mrb_int i; while (seg) { for (i=0; isize; i++) { mrb_value key; if (!seg->next && i >= t->last_len) { return; } key = seg->e[i].key; if (mrb_undef_p(key)) continue; *kp = key; *vp = seg->e[i].val; /* delete element */ seg->e[i].key = mrb_undef_value(); t->size--; return; } seg = seg->next; } } /* 15.2.13.4.24 */ /* * call-seq: * hsh.shift -> anArray or obj * * Removes a key-value pair from hsh and returns it as the * two-item array [ key, value ], or * the hash's default value if the hash is empty. * * h = { 1 => "a", 2 => "b", 3 => "c" } * h.shift #=> [1, "a"] * h #=> {2=>"b", 3=>"c"} */ static mrb_value mrb_hash_shift(mrb_state *mrb, mrb_value hash) { htable *t = RHASH_TBL(hash); mrb_hash_modify(mrb, hash); if (t && t->size > 0) { mrb_value del_key, del_val; ht_shift(mrb, t, &del_key, &del_val); mrb_gc_protect(mrb, del_key); mrb_gc_protect(mrb, del_val); return mrb_assoc_new(mrb, del_key, del_val); } if (MRB_RHASH_DEFAULT_P(hash)) { if (MRB_RHASH_PROCDEFAULT_P(hash)) { return mrb_funcall(mrb, RHASH_PROCDEFAULT(hash), "call", 2, hash, mrb_nil_value()); } else { return RHASH_IFNONE(hash); } } return mrb_nil_value(); } /* 15.2.13.4.4 */ /* * call-seq: * hsh.clear -> hsh * * Removes all key-value pairs from `hsh`. * * h = { "a" => 100, "b" => 200 } #=> {"a"=>100, "b"=>200} * h.clear #=> {} * */ MRB_API mrb_value mrb_hash_clear(mrb_state *mrb, mrb_value hash) { htable *t = RHASH_TBL(hash); mrb_hash_modify(mrb, hash); if (t) { ht_free(mrb, t); RHASH_TBL(hash) = NULL; } return hash; } /* 15.2.13.4.3 */ /* 15.2.13.4.26 */ /* * call-seq: * hsh[key] = value -> value * hsh.store(key, value) -> value * * Element Assignment---Associates the value given by * value with the key given by key. * key should not have its value changed while it is in * use as a key (a String passed as a key will be * duplicated and frozen). * * h = { "a" => 100, "b" => 200 } * h["a"] = 9 * h["c"] = 4 * h #=> {"a"=>9, "b"=>200, "c"=>4} * */ static mrb_value mrb_hash_aset(mrb_state *mrb, mrb_value self) { mrb_value key, val; mrb_get_args(mrb, "oo", &key, &val); mrb_hash_set(mrb, self, key, val); return val; } /* 15.2.13.4.20 */ /* 15.2.13.4.25 */ /* * call-seq: * hsh.length -> fixnum * hsh.size -> fixnum * * Returns the number of key-value pairs in the hash. * * h = { "d" => 100, "a" => 200, "v" => 300, "e" => 400 } * h.length #=> 4 * h.delete("a") #=> 200 * h.length #=> 3 */ static mrb_value mrb_hash_size_m(mrb_state *mrb, mrb_value self) { htable *t = RHASH_TBL(self); if (!t) return mrb_fixnum_value(0); return mrb_fixnum_value(t->size); } MRB_API mrb_bool mrb_hash_empty_p(mrb_state *mrb, mrb_value self) { htable *t = RHASH_TBL(self); if (!t) return TRUE; return t->size == 0; } /* 15.2.13.4.12 */ /* * call-seq: * hsh.empty? -> true or false * * Returns true if hsh contains no key-value pairs. * * {}.empty? #=> true * */ static mrb_value mrb_hash_empty_m(mrb_state *mrb, mrb_value self) { return mrb_bool_value(mrb_hash_empty_p(mrb, self)); } static int hash_keys_i(mrb_state *mrb, mrb_value key, mrb_value val, void *p) { mrb_ary_push(mrb, *(mrb_value*)p, key); return 0; } /* 15.2.13.4.19 */ /* * call-seq: * hsh.keys -> array * * Returns a new array populated with the keys from this hash. See also * Hash#values. * * h = { "a" => 100, "b" => 200, "c" => 300, "d" => 400 } * h.keys #=> ["a", "b", "c", "d"] * */ MRB_API mrb_value mrb_hash_keys(mrb_state *mrb, mrb_value hash) { htable *t = RHASH_TBL(hash); mrb_int size; mrb_value ary; if (!t || (size = t->size) == 0) return mrb_ary_new(mrb); ary = mrb_ary_new_capa(mrb, size); ht_foreach(mrb, t, hash_keys_i, (void*)&ary); return ary; } static int hash_vals_i(mrb_state *mrb, mrb_value key, mrb_value val, void *p) { mrb_ary_push(mrb, *(mrb_value*)p, val); return 0; } /* 15.2.13.4.28 */ /* * call-seq: * hsh.values -> array * * Returns a new array populated with the values from hsh. See * also Hash#keys. * * h = { "a" => 100, "b" => 200, "c" => 300 } * h.values #=> [100, 200, 300] * */ MRB_API mrb_value mrb_hash_values(mrb_state *mrb, mrb_value hash) { htable *t = RHASH_TBL(hash); mrb_int size; mrb_value ary; if (!t || (size = t->size) == 0) return mrb_ary_new(mrb); ary = mrb_ary_new_capa(mrb, size); ht_foreach(mrb, t, hash_vals_i, (void*)&ary); return ary; } /* 15.2.13.4.13 */ /* 15.2.13.4.15 */ /* 15.2.13.4.18 */ /* 15.2.13.4.21 */ /* * call-seq: * hsh.has_key?(key) -> true or false * hsh.include?(key) -> true or false * hsh.key?(key) -> true or false * hsh.member?(key) -> true or false * * Returns true if the given key is present in hsh. * * h = { "a" => 100, "b" => 200 } * h.has_key?("a") #=> true * h.has_key?("z") #=> false * */ MRB_API mrb_bool mrb_hash_key_p(mrb_state *mrb, mrb_value hash, mrb_value key) { htable *t; t = RHASH_TBL(hash); if (ht_get(mrb, t, key, NULL)) { return TRUE; } return FALSE; } static mrb_value mrb_hash_has_key(mrb_state *mrb, mrb_value hash) { mrb_value key; mrb_bool key_p; mrb_get_args(mrb, "o", &key); key_p = mrb_hash_key_p(mrb, hash, key); return mrb_bool_value(key_p); } struct has_v_arg { mrb_bool found; mrb_value val; }; static int hash_has_value_i(mrb_state *mrb, mrb_value key, mrb_value val, void *p) { struct has_v_arg *arg = (struct has_v_arg*)p; if (mrb_equal(mrb, arg->val, val)) { arg->found = TRUE; return 1; } return 0; } /* 15.2.13.4.14 */ /* 15.2.13.4.27 */ /* * call-seq: * hsh.has_value?(value) -> true or false * hsh.value?(value) -> true or false * * Returns true if the given value is present for some key * in hsh. * * h = { "a" => 100, "b" => 200 } * h.has_value?(100) #=> true * h.has_value?(999) #=> false */ static mrb_value mrb_hash_has_value(mrb_state *mrb, mrb_value hash) { mrb_value val; struct has_v_arg arg; mrb_get_args(mrb, "o", &val); arg.found = FALSE; arg.val = val; ht_foreach(mrb, RHASH_TBL(hash), hash_has_value_i, &arg); return mrb_bool_value(arg.found); } static int merge_i(mrb_state *mrb, mrb_value key, mrb_value val, void *data) { htable *h1 = (htable*)data; ht_put(mrb, h1, key, val); return 0; } MRB_API void mrb_hash_merge(mrb_state *mrb, mrb_value hash1, mrb_value hash2) { htable *h1, *h2; mrb_hash_modify(mrb, hash1); hash2 = mrb_ensure_hash_type(mrb, hash2); h1 = RHASH_TBL(hash1); h2 = RHASH_TBL(hash2); if (!h2) return; if (!h1) { RHASH_TBL(hash1) = ht_copy(mrb, h2); return; } ht_foreach(mrb, h2, merge_i, h1); mrb_write_barrier(mrb, (struct RBasic*)RHASH(hash1)); return; } /* * call-seq: * hsh.rehash -> hsh * * Rebuilds the hash based on the current hash values for each key. If * values of key objects have changed since they were inserted, this * method will reindex hsh. * * h = {"AAA" => "b"} * h.keys[0].chop! * h.rehash #=> {"AA"=>"b"} * h["AA"] #=> "b" */ static mrb_value mrb_hash_rehash(mrb_state *mrb, mrb_value self) { ht_compact(mrb, RHASH_TBL(self)); return self; } void mrb_init_hash(mrb_state *mrb) { struct RClass *h; mrb->hash_class = h = mrb_define_class(mrb, "Hash", mrb->object_class); /* 15.2.13 */ MRB_SET_INSTANCE_TT(h, MRB_TT_HASH); mrb_define_method(mrb, h, "initialize_copy", mrb_hash_init_copy, MRB_ARGS_REQ(1)); mrb_define_method(mrb, h, "[]", mrb_hash_aget, MRB_ARGS_REQ(1)); /* 15.2.13.4.2 */ mrb_define_method(mrb, h, "[]=", mrb_hash_aset, MRB_ARGS_REQ(2)); /* 15.2.13.4.3 */ mrb_define_method(mrb, h, "clear", mrb_hash_clear, MRB_ARGS_NONE()); /* 15.2.13.4.4 */ mrb_define_method(mrb, h, "default", mrb_hash_default, MRB_ARGS_ANY()); /* 15.2.13.4.5 */ mrb_define_method(mrb, h, "default=", mrb_hash_set_default, MRB_ARGS_REQ(1)); /* 15.2.13.4.6 */ mrb_define_method(mrb, h, "default_proc", mrb_hash_default_proc,MRB_ARGS_NONE()); /* 15.2.13.4.7 */ mrb_define_method(mrb, h, "default_proc=", mrb_hash_set_default_proc,MRB_ARGS_REQ(1)); /* 15.2.13.4.7 */ mrb_define_method(mrb, h, "__delete", mrb_hash_delete, MRB_ARGS_REQ(1)); /* core of 15.2.13.4.8 */ mrb_define_method(mrb, h, "empty?", mrb_hash_empty_m, MRB_ARGS_NONE()); /* 15.2.13.4.12 */ mrb_define_method(mrb, h, "has_key?", mrb_hash_has_key, MRB_ARGS_REQ(1)); /* 15.2.13.4.13 */ mrb_define_method(mrb, h, "has_value?", mrb_hash_has_value, MRB_ARGS_REQ(1)); /* 15.2.13.4.14 */ mrb_define_method(mrb, h, "include?", mrb_hash_has_key, MRB_ARGS_REQ(1)); /* 15.2.13.4.15 */ mrb_define_method(mrb, h, "initialize", mrb_hash_init, MRB_ARGS_OPT(1)); /* 15.2.13.4.16 */ mrb_define_method(mrb, h, "key?", mrb_hash_has_key, MRB_ARGS_REQ(1)); /* 15.2.13.4.18 */ mrb_define_method(mrb, h, "keys", mrb_hash_keys, MRB_ARGS_NONE()); /* 15.2.13.4.19 */ mrb_define_method(mrb, h, "length", mrb_hash_size_m, MRB_ARGS_NONE()); /* 15.2.13.4.20 */ mrb_define_method(mrb, h, "member?", mrb_hash_has_key, MRB_ARGS_REQ(1)); /* 15.2.13.4.21 */ mrb_define_method(mrb, h, "shift", mrb_hash_shift, MRB_ARGS_NONE()); /* 15.2.13.4.24 */ mrb_define_method(mrb, h, "size", mrb_hash_size_m, MRB_ARGS_NONE()); /* 15.2.13.4.25 */ mrb_define_method(mrb, h, "store", mrb_hash_aset, MRB_ARGS_REQ(2)); /* 15.2.13.4.26 */ mrb_define_method(mrb, h, "value?", mrb_hash_has_value, MRB_ARGS_REQ(1)); /* 15.2.13.4.27 */ mrb_define_method(mrb, h, "values", mrb_hash_values, MRB_ARGS_NONE()); /* 15.2.13.4.28 */ mrb_define_method(mrb, h, "rehash", mrb_hash_rehash, MRB_ARGS_NONE()); } mruby-2.0.0/src/init.c000066400000000000000000000024341340361412400145460ustar00rootroot00000000000000/* ** init.c - initialize mruby core ** ** See Copyright Notice in mruby.h */ #include void mrb_init_symtbl(mrb_state*); void mrb_init_class(mrb_state*); void mrb_init_object(mrb_state*); void mrb_init_kernel(mrb_state*); void mrb_init_comparable(mrb_state*); void mrb_init_enumerable(mrb_state*); void mrb_init_symbol(mrb_state*); void mrb_init_string(mrb_state*); void mrb_init_exception(mrb_state*); void mrb_init_proc(mrb_state*); void mrb_init_array(mrb_state*); void mrb_init_hash(mrb_state*); void mrb_init_numeric(mrb_state*); void mrb_init_range(mrb_state*); void mrb_init_gc(mrb_state*); void mrb_init_math(mrb_state*); void mrb_init_version(mrb_state*); void mrb_init_mrblib(mrb_state*); #define DONE mrb_gc_arena_restore(mrb, 0); void mrb_init_core(mrb_state *mrb) { mrb_init_symtbl(mrb); DONE; mrb_init_class(mrb); DONE; mrb_init_object(mrb); DONE; mrb_init_kernel(mrb); DONE; mrb_init_comparable(mrb); DONE; mrb_init_enumerable(mrb); DONE; mrb_init_symbol(mrb); DONE; mrb_init_string(mrb); DONE; mrb_init_exception(mrb); DONE; mrb_init_proc(mrb); DONE; mrb_init_array(mrb); DONE; mrb_init_hash(mrb); DONE; mrb_init_numeric(mrb); DONE; mrb_init_range(mrb); DONE; mrb_init_gc(mrb); DONE; mrb_init_version(mrb); DONE; mrb_init_mrblib(mrb); DONE; } mruby-2.0.0/src/kernel.c000066400000000000000000000615521340361412400150710ustar00rootroot00000000000000/* ** kernel.c - Kernel module ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #include #include MRB_API mrb_bool mrb_func_basic_p(mrb_state *mrb, mrb_value obj, mrb_sym mid, mrb_func_t func) { struct RClass *c = mrb_class(mrb, obj); mrb_method_t m = mrb_method_search_vm(mrb, &c, mid); struct RProc *p; if (MRB_METHOD_UNDEF_P(m)) return FALSE; if (MRB_METHOD_FUNC_P(m)) return MRB_METHOD_FUNC(m) == func; p = MRB_METHOD_PROC(m); if (MRB_PROC_CFUNC_P(p) && (MRB_PROC_CFUNC(p) == func)) return TRUE; return FALSE; } static mrb_bool mrb_obj_basic_to_s_p(mrb_state *mrb, mrb_value obj) { return mrb_func_basic_p(mrb, obj, mrb_intern_lit(mrb, "to_s"), mrb_any_to_s); } /* 15.3.1.3.17 */ /* * call-seq: * obj.inspect -> string * * Returns a string containing a human-readable representation of * obj. If not overridden and no instance variables, uses the * to_s method to generate the string. * obj. If not overridden, uses the to_s method to * generate the string. * * [ 1, 2, 3..4, 'five' ].inspect #=> "[1, 2, 3..4, \"five\"]" * Time.new.inspect #=> "2008-03-08 19:43:39 +0900" */ MRB_API mrb_value mrb_obj_inspect(mrb_state *mrb, mrb_value obj) { if ((mrb_type(obj) == MRB_TT_OBJECT) && mrb_obj_basic_to_s_p(mrb, obj)) { return mrb_obj_iv_inspect(mrb, mrb_obj_ptr(obj)); } return mrb_any_to_s(mrb, obj); } /* 15.3.1.3.2 */ /* * call-seq: * obj === other -> true or false * * Case Equality---For class Object, effectively the same * as calling #==, but typically overridden by descendants * to provide meaningful semantics in case statements. */ static mrb_value mrb_equal_m(mrb_state *mrb, mrb_value self) { mrb_value arg; mrb_get_args(mrb, "o", &arg); return mrb_bool_value(mrb_equal(mrb, self, arg)); } /* 15.3.1.3.3 */ /* 15.3.1.3.33 */ /* * Document-method: __id__ * Document-method: object_id * * call-seq: * obj.__id__ -> fixnum * obj.object_id -> fixnum * * Returns an integer identifier for obj. The same number will * be returned on all calls to id for a given object, and * no two active objects will share an id. * Object#object_id is a different concept from the * :name notation, which returns the symbol id of * name. Replaces the deprecated Object#id. */ mrb_value mrb_obj_id_m(mrb_state *mrb, mrb_value self) { return mrb_fixnum_value(mrb_obj_id(self)); } /* 15.3.1.2.2 */ /* 15.3.1.2.5 */ /* 15.3.1.3.6 */ /* 15.3.1.3.25 */ /* * call-seq: * block_given? -> true or false * iterator? -> true or false * * Returns true if yield would execute a * block in the current context. The iterator? form * is mildly deprecated. * * def try * if block_given? * yield * else * "no block" * end * end * try #=> "no block" * try { "hello" } #=> "hello" * try do "hello" end #=> "hello" */ static mrb_value mrb_f_block_given_p_m(mrb_state *mrb, mrb_value self) { mrb_callinfo *ci = &mrb->c->ci[-1]; mrb_callinfo *cibase = mrb->c->cibase; mrb_value *bp; struct RProc *p; if (ci <= cibase) { /* toplevel does not have block */ return mrb_false_value(); } p = ci->proc; /* search method/class/module proc */ while (p) { if (MRB_PROC_SCOPE_P(p)) break; p = p->upper; } if (p == NULL) return mrb_false_value(); /* search ci corresponding to proc */ while (cibase < ci) { if (ci->proc == p) break; ci--; } if (ci == cibase) { return mrb_false_value(); } else if (ci->env) { struct REnv *e = ci->env; int bidx; /* top-level does not have block slot (always false) */ if (e->stack == mrb->c->stbase) return mrb_false_value(); /* use saved block arg position */ bidx = MRB_ENV_BIDX(e); /* bidx may be useless (e.g. define_method) */ if (bidx >= MRB_ENV_STACK_LEN(e)) return mrb_false_value(); bp = &e->stack[bidx]; } else { bp = ci[1].stackent+1; if (ci->argc >= 0) { bp += ci->argc; } else { bp++; } } if (mrb_nil_p(*bp)) return mrb_false_value(); return mrb_true_value(); } /* 15.3.1.3.7 */ /* * call-seq: * obj.class -> class * * Returns the class of obj. This method must always be * called with an explicit receiver, as class is also a * reserved word in Ruby. * * 1.class #=> Fixnum * self.class #=> Object */ static mrb_value mrb_obj_class_m(mrb_state *mrb, mrb_value self) { return mrb_obj_value(mrb_obj_class(mrb, self)); } static struct RClass* mrb_singleton_class_clone(mrb_state *mrb, mrb_value obj) { struct RClass *klass = mrb_basic_ptr(obj)->c; if (klass->tt != MRB_TT_SCLASS) return klass; else { /* copy singleton(unnamed) class */ struct RClass *clone = (struct RClass*)mrb_obj_alloc(mrb, klass->tt, mrb->class_class); switch (mrb_type(obj)) { case MRB_TT_CLASS: case MRB_TT_SCLASS: break; default: clone->c = mrb_singleton_class_clone(mrb, mrb_obj_value(klass)); break; } clone->super = klass->super; if (klass->iv) { mrb_iv_copy(mrb, mrb_obj_value(clone), mrb_obj_value(klass)); mrb_obj_iv_set(mrb, (struct RObject*)clone, mrb_intern_lit(mrb, "__attached__"), obj); } if (klass->mt) { clone->mt = kh_copy(mt, mrb, klass->mt); } else { clone->mt = kh_init(mt, mrb); } clone->tt = MRB_TT_SCLASS; return clone; } } static void 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->flags & MRB_FL_CLASS_IS_PREPENDED) { struct RClass *c0 = sc->super; struct RClass *c1 = dc; /* copy prepended iclasses */ while (!(c0->flags & MRB_FL_CLASS_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_FL_CLASS_IS_ORIGIN; } if (sc->mt) { dc->mt = kh_copy(mt, mrb, sc->mt); } else { dc->mt = kh_init(mt, mrb); } dc->super = sc->super; MRB_SET_INSTANCE_TT(dc, MRB_INSTANCE_TT(sc)); } static void init_copy(mrb_state *mrb, mrb_value dest, mrb_value obj) { switch (mrb_type(obj)) { case MRB_TT_ICLASS: copy_class(mrb, dest, obj); return; case MRB_TT_CLASS: case MRB_TT_MODULE: copy_class(mrb, dest, obj); mrb_iv_copy(mrb, dest, obj); mrb_iv_remove(mrb, dest, mrb_intern_lit(mrb, "__classname__")); break; case MRB_TT_OBJECT: case MRB_TT_SCLASS: case MRB_TT_HASH: case MRB_TT_DATA: case MRB_TT_EXCEPTION: mrb_iv_copy(mrb, dest, obj); break; case MRB_TT_ISTRUCT: mrb_istruct_copy(dest, obj); break; default: break; } mrb_funcall(mrb, dest, "initialize_copy", 1, obj); } /* 15.3.1.3.8 */ /* * call-seq: * obj.clone -> an_object * * Produces a shallow copy of obj---the instance variables of * obj are copied, but not the objects they reference. Copies * the frozen state of obj. See also the discussion * under Object#dup. * * class Klass * attr_accessor :str * end * s1 = Klass.new #=> # * s1.str = "Hello" #=> "Hello" * s2 = s1.clone #=> # * s2.str[1,4] = "i" #=> "i" * s1.inspect #=> "#" * s2.inspect #=> "#" * * This method may have class-specific behavior. If so, that * behavior will be documented under the #+initialize_copy+ method of * the class. * * Some Class(True False Nil Symbol Fixnum Float) Object cannot clone. */ MRB_API mrb_value mrb_obj_clone(mrb_state *mrb, mrb_value self) { struct RObject *p; mrb_value clone; 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); mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)p->c); clone = mrb_obj_value(p); init_copy(mrb, clone, self); p->flags |= mrb_obj_ptr(self)->flags & MRB_FL_OBJ_IS_FROZEN; return clone; } /* 15.3.1.3.9 */ /* * call-seq: * obj.dup -> an_object * * Produces a shallow copy of obj---the instance variables of * obj are copied, but not the objects they reference. * dup copies the frozen state of obj. See also * the discussion under Object#clone. In general, * clone and dup may have different semantics * in descendant classes. While clone is used to duplicate * an object, including its internal state, dup typically * uses the class of the descendant object to create the new instance. * * This method may have class-specific behavior. If so, that * behavior will be documented under the #+initialize_copy+ method of * the class. */ MRB_API mrb_value mrb_obj_dup(mrb_state *mrb, mrb_value obj) { struct RBasic *p; mrb_value dup; 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); return dup; } static mrb_value mrb_obj_extend(mrb_state *mrb, mrb_int argc, mrb_value *argv, mrb_value obj) { mrb_int i; if (argc == 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (at least 1)"); } for (i = 0; i < argc; i++) { mrb_check_type(mrb, argv[i], MRB_TT_MODULE); } while (argc--) { mrb_funcall(mrb, argv[argc], "extend_object", 1, obj); mrb_funcall(mrb, argv[argc], "extended", 1, obj); } return obj; } /* 15.3.1.3.13 */ /* * call-seq: * obj.extend(module, ...) -> obj * * Adds to _obj_ the instance methods from each module given as a * parameter. * * module Mod * def hello * "Hello from Mod.\n" * end * end * * class Klass * def hello * "Hello from Klass.\n" * end * end * * k = Klass.new * k.hello #=> "Hello from Klass.\n" * k.extend(Mod) #=> # * k.hello #=> "Hello from Mod.\n" */ static mrb_value mrb_obj_extend_m(mrb_state *mrb, mrb_value self) { mrb_value *argv; mrb_int argc; mrb_get_args(mrb, "*", &argv, &argc); return mrb_obj_extend(mrb, argc, argv, self); } static mrb_value mrb_obj_freeze(mrb_state *mrb, mrb_value self) { struct RBasic *b; switch (mrb_type(self)) { case MRB_TT_FALSE: case MRB_TT_TRUE: case MRB_TT_FIXNUM: case MRB_TT_SYMBOL: #ifndef MRB_WITHOUT_FLOAT case MRB_TT_FLOAT: #endif return self; default: break; } b = mrb_basic_ptr(self); if (!MRB_FROZEN_P(b)) { MRB_SET_FROZEN_FLAG(b); } return self; } static mrb_value mrb_obj_frozen(mrb_state *mrb, mrb_value self) { struct RBasic *b; switch (mrb_type(self)) { case MRB_TT_FALSE: case MRB_TT_TRUE: case MRB_TT_FIXNUM: case MRB_TT_SYMBOL: #ifndef MRB_WITHOUT_FLOAT case MRB_TT_FLOAT: #endif return mrb_true_value(); default: break; } b = mrb_basic_ptr(self); if (!MRB_FROZEN_P(b)) { return mrb_false_value(); } return mrb_true_value(); } /* 15.3.1.3.15 */ /* * call-seq: * obj.hash -> fixnum * * Generates a Fixnum hash value for this object. This * function must have the property that a.eql?(b) implies * a.hash == b.hash. The hash value is used by class * Hash. Any hash value that exceeds the capacity of a * Fixnum will be truncated before being used. */ MRB_API mrb_value mrb_obj_hash(mrb_state *mrb, mrb_value self) { return mrb_fixnum_value(mrb_obj_id(self)); } /* 15.3.1.3.16 */ static mrb_value mrb_obj_init_copy(mrb_state *mrb, mrb_value self) { mrb_value orig; mrb_get_args(mrb, "o", &orig); if (mrb_obj_equal(mrb, self, orig)) return self; if ((mrb_type(self) != mrb_type(orig)) || (mrb_obj_class(mrb, self) != mrb_obj_class(mrb, orig))) { mrb_raise(mrb, E_TYPE_ERROR, "initialize_copy should take same class object"); } return self; } MRB_API mrb_bool mrb_obj_is_instance_of(mrb_state *mrb, mrb_value obj, struct RClass* c) { if (mrb_obj_class(mrb, obj) == c) return TRUE; return FALSE; } /* 15.3.1.3.19 */ /* * call-seq: * obj.instance_of?(class) -> true or false * * Returns true if obj is an instance of the given * class. See also Object#kind_of?. */ static mrb_value obj_is_instance_of(mrb_state *mrb, mrb_value self) { mrb_value arg; mrb_get_args(mrb, "C", &arg); return mrb_bool_value(mrb_obj_is_instance_of(mrb, self, mrb_class_ptr(arg))); } /* 15.3.1.3.24 */ /* 15.3.1.3.26 */ /* * call-seq: * obj.is_a?(class) -> true or false * obj.kind_of?(class) -> true or false * * Returns true if class is the class of * obj, or if class is one of the superclasses of * obj or modules included in obj. * * module M; end * class A * include M * end * class B < A; end * class C < B; end * b = B.new * b.instance_of? A #=> false * b.instance_of? B #=> true * b.instance_of? C #=> false * b.instance_of? M #=> false * b.kind_of? A #=> true * b.kind_of? B #=> true * b.kind_of? C #=> false * b.kind_of? M #=> true */ static mrb_value mrb_obj_is_kind_of_m(mrb_state *mrb, mrb_value self) { mrb_value arg; mrb_get_args(mrb, "C", &arg); return mrb_bool_value(mrb_obj_is_kind_of(mrb, self, mrb_class_ptr(arg))); } KHASH_DECLARE(st, mrb_sym, char, FALSE) KHASH_DEFINE(st, mrb_sym, char, FALSE, kh_int_hash_func, kh_int_hash_equal) /* 15.3.1.3.32 */ /* * call_seq: * nil.nil? -> true * .nil? -> false * * Only the object nil responds true to nil?. */ static mrb_value mrb_false(mrb_state *mrb, mrb_value self) { return mrb_false_value(); } /* 15.3.1.2.12 */ /* 15.3.1.3.40 */ /* * call-seq: * raise * raise(string) * raise(exception [, string]) * * With no arguments, raises a RuntimeError * With a single +String+ argument, raises a * +RuntimeError+ with the string as a message. Otherwise, * the first parameter should be the name of an +Exception+ * class (or an object that returns an +Exception+ object when sent * an +exception+ message). The optional second parameter sets the * message associated with the exception, and the third parameter is an * array of callback information. Exceptions are caught by the * +rescue+ clause of begin...end blocks. * * raise "Failed to create socket" * raise ArgumentError, "No parameters", caller */ MRB_API mrb_value mrb_f_raise(mrb_state *mrb, mrb_value self) { mrb_value a[2], exc; mrb_int argc; argc = mrb_get_args(mrb, "|oo", &a[0], &a[1]); switch (argc) { case 0: mrb_raise(mrb, E_RUNTIME_ERROR, ""); break; case 1: if (mrb_string_p(a[0])) { a[1] = a[0]; argc = 2; a[0] = mrb_obj_value(E_RUNTIME_ERROR); } /* fall through */ default: exc = mrb_make_exception(mrb, argc, a); mrb_exc_raise(mrb, exc); break; } return mrb_nil_value(); /* not reached */ } static mrb_value mrb_krn_class_defined(mrb_state *mrb, mrb_value self) { mrb_value str; mrb_get_args(mrb, "S", &str); return mrb_bool_value(mrb_class_defined(mrb, RSTRING_PTR(str))); } /* 15.3.1.3.41 */ /* * call-seq: * obj.remove_instance_variable(symbol) -> obj * * Removes the named instance variable from obj, returning that * variable's value. * * class Dummy * attr_reader :var * def initialize * @var = 99 * end * def remove * remove_instance_variable(:@var) * end * end * d = Dummy.new * d.var #=> 99 * d.remove #=> 99 * d.var #=> nil */ static mrb_value mrb_obj_remove_instance_variable(mrb_state *mrb, mrb_value self) { mrb_sym sym; mrb_value val; mrb_get_args(mrb, "n", &sym); mrb_iv_name_sym_check(mrb, sym); val = mrb_iv_remove(mrb, self, sym); if (mrb_undef_p(val)) { mrb_name_error(mrb, sym, "instance variable %S not defined", mrb_sym2str(mrb, sym)); } return val; } void mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args) { mrb_no_method_error(mrb, name, args, "undefined method '%S'", mrb_sym2str(mrb, name)); } /* 15.3.1.3.30 */ /* * call-seq: * obj.method_missing(symbol [, *args] ) -> result * * Invoked by Ruby when obj is sent a message it cannot handle. * symbol is the symbol for the method called, and args * are any arguments that were passed to it. By default, the interpreter * raises an error when this method is called. However, it is possible * to override the method to provide more dynamic behavior. * If it is decided that a particular method should not be handled, then * super should be called, so that ancestors can pick up the * missing method. * The example below creates * a class Roman, which responds to methods with names * consisting of roman numerals, returning the corresponding integer * values. * * class Roman * def romanToInt(str) * # ... * end * def method_missing(methId) * str = methId.id2name * romanToInt(str) * end * end * * r = Roman.new * r.iv #=> 4 * r.xxiii #=> 23 * r.mm #=> 2000 */ #ifdef MRB_DEFAULT_METHOD_MISSING static mrb_value mrb_obj_missing(mrb_state *mrb, mrb_value mod) { mrb_sym name; mrb_value *a; mrb_int alen; mrb_get_args(mrb, "n*!", &name, &a, &alen); mrb_method_missing(mrb, name, mod, mrb_ary_new_from_values(mrb, alen, a)); /* not reached */ return mrb_nil_value(); } #endif static inline mrb_bool basic_obj_respond_to(mrb_state *mrb, mrb_value obj, mrb_sym id, int pub) { return mrb_respond_to(mrb, obj, id); } /* 15.3.1.3.43 */ /* * call-seq: * obj.respond_to?(symbol, include_private=false) -> true or false * * Returns +true+ if _obj_ responds to the given * method. Private methods are included in the search only if the * optional second parameter evaluates to +true+. * * If the method is not implemented, * as Process.fork on Windows, File.lchmod on GNU/Linux, etc., * false is returned. * * If the method is not defined, respond_to_missing? * method is called and the result is returned. */ static mrb_value obj_respond_to(mrb_state *mrb, mrb_value self) { mrb_sym id, rtm_id; mrb_bool priv = FALSE, respond_to_p; mrb_get_args(mrb, "n|b", &id, &priv); respond_to_p = basic_obj_respond_to(mrb, self, id, !priv); if (!respond_to_p) { rtm_id = mrb_intern_lit(mrb, "respond_to_missing?"); if (basic_obj_respond_to(mrb, self, rtm_id, !priv)) { mrb_value args[2], v; args[0] = mrb_symbol_value(id); args[1] = mrb_bool_value(priv); v = mrb_funcall_argv(mrb, self, rtm_id, 2, args); return mrb_bool_value(mrb_bool(v)); } } return mrb_bool_value(respond_to_p); } static mrb_value mrb_obj_ceqq(mrb_state *mrb, mrb_value self) { mrb_value v; mrb_int i, len; mrb_sym eqq = mrb_intern_lit(mrb, "==="); mrb_value ary = mrb_ary_splat(mrb, self); mrb_get_args(mrb, "o", &v); len = RARRAY_LEN(ary); for (i=0; ikernel_module = krn = mrb_define_module(mrb, "Kernel"); /* 15.3.1 */ mrb_define_class_method(mrb, krn, "block_given?", mrb_f_block_given_p_m, MRB_ARGS_NONE()); /* 15.3.1.2.2 */ mrb_define_class_method(mrb, krn, "iterator?", mrb_f_block_given_p_m, MRB_ARGS_NONE()); /* 15.3.1.2.5 */ ; /* 15.3.1.2.11 */ mrb_define_class_method(mrb, krn, "raise", mrb_f_raise, MRB_ARGS_OPT(2)); /* 15.3.1.2.12 */ mrb_define_method(mrb, krn, "===", mrb_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.2 */ mrb_define_method(mrb, krn, "block_given?", mrb_f_block_given_p_m, MRB_ARGS_NONE()); /* 15.3.1.3.6 */ mrb_define_method(mrb, krn, "class", mrb_obj_class_m, MRB_ARGS_NONE()); /* 15.3.1.3.7 */ mrb_define_method(mrb, krn, "clone", mrb_obj_clone, MRB_ARGS_NONE()); /* 15.3.1.3.8 */ mrb_define_method(mrb, krn, "dup", mrb_obj_dup, MRB_ARGS_NONE()); /* 15.3.1.3.9 */ mrb_define_method(mrb, krn, "eql?", mrb_obj_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.10 */ mrb_define_method(mrb, krn, "equal?", mrb_obj_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.11 */ mrb_define_method(mrb, krn, "extend", mrb_obj_extend_m, MRB_ARGS_ANY()); /* 15.3.1.3.13 */ mrb_define_method(mrb, krn, "freeze", mrb_obj_freeze, MRB_ARGS_NONE()); mrb_define_method(mrb, krn, "frozen?", mrb_obj_frozen, MRB_ARGS_NONE()); mrb_define_method(mrb, krn, "global_variables", mrb_f_global_variables, MRB_ARGS_NONE()); /* 15.3.1.3.14 */ mrb_define_method(mrb, krn, "hash", mrb_obj_hash, MRB_ARGS_NONE()); /* 15.3.1.3.15 */ mrb_define_method(mrb, krn, "initialize_copy", mrb_obj_init_copy, MRB_ARGS_REQ(1)); /* 15.3.1.3.16 */ mrb_define_method(mrb, krn, "inspect", mrb_obj_inspect, MRB_ARGS_NONE()); /* 15.3.1.3.17 */ mrb_define_method(mrb, krn, "instance_of?", obj_is_instance_of, MRB_ARGS_REQ(1)); /* 15.3.1.3.19 */ mrb_define_method(mrb, krn, "is_a?", mrb_obj_is_kind_of_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.24 */ mrb_define_method(mrb, krn, "iterator?", mrb_f_block_given_p_m, MRB_ARGS_NONE()); /* 15.3.1.3.25 */ mrb_define_method(mrb, krn, "kind_of?", mrb_obj_is_kind_of_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.26 */ #ifdef MRB_DEFAULT_METHOD_MISSING mrb_define_method(mrb, krn, "method_missing", mrb_obj_missing, MRB_ARGS_ANY()); /* 15.3.1.3.30 */ #endif mrb_define_method(mrb, krn, "nil?", mrb_false, MRB_ARGS_NONE()); /* 15.3.1.3.32 */ mrb_define_method(mrb, krn, "object_id", mrb_obj_id_m, MRB_ARGS_NONE()); /* 15.3.1.3.33 */ mrb_define_method(mrb, krn, "raise", mrb_f_raise, MRB_ARGS_ANY()); /* 15.3.1.3.40 */ mrb_define_method(mrb, krn, "remove_instance_variable", mrb_obj_remove_instance_variable,MRB_ARGS_REQ(1)); /* 15.3.1.3.41 */ mrb_define_method(mrb, krn, "respond_to?", obj_respond_to, MRB_ARGS_ANY()); /* 15.3.1.3.43 */ mrb_define_method(mrb, krn, "to_s", mrb_any_to_s, MRB_ARGS_NONE()); /* 15.3.1.3.46 */ mrb_define_method(mrb, krn, "__case_eqq", mrb_obj_ceqq, MRB_ARGS_REQ(1)); /* internal */ mrb_define_method(mrb, krn, "__to_int", mrb_to_int, MRB_ARGS_NONE()); /* internal */ mrb_define_method(mrb, krn, "__to_str", mrb_to_str, MRB_ARGS_NONE()); /* internal */ mrb_define_method(mrb, krn, "class_defined?", mrb_krn_class_defined, MRB_ARGS_REQ(1)); mrb_include_module(mrb, mrb->object_class, mrb->kernel_module); mrb_define_alias(mrb, mrb->module_class, "dup", "clone"); /* XXX */ } mruby-2.0.0/src/load.c000066400000000000000000000431201340361412400145170ustar00rootroot00000000000000/* ** load.c - mruby binary loader ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #include #include #if SIZE_MAX < UINT32_MAX # error size_t must be at least 32 bits wide #endif #define FLAG_BYTEORDER_BIG 2 #define FLAG_BYTEORDER_LIL 4 #define FLAG_BYTEORDER_NATIVE 8 #define FLAG_SRC_MALLOC 1 #define FLAG_SRC_STATIC 0 #define SIZE_ERROR_MUL(nmemb, size) ((size_t)(nmemb) > SIZE_MAX / (size)) static size_t skip_padding(const uint8_t *buf) { const size_t align = MRB_DUMP_ALIGNMENT; return -(intptr_t)buf & (align-1); } static size_t offset_crc_body(void) { struct rite_binary_header header; return ((uint8_t *)header.binary_crc - (uint8_t *)&header) + sizeof(header.binary_crc); } static mrb_irep* read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flags) { int i; const uint8_t *src = bin; ptrdiff_t diff; uint16_t tt, pool_data_len, snl; int plen; int ai = mrb_gc_arena_save(mrb); mrb_irep *irep = mrb_add_irep(mrb); /* skip record size */ src += sizeof(uint32_t); /* number of local variable */ irep->nlocals = bin_to_uint16(src); src += sizeof(uint16_t); /* number of register variable */ irep->nregs = bin_to_uint16(src); src += sizeof(uint16_t); /* number of child irep */ irep->rlen = (size_t)bin_to_uint16(src); src += sizeof(uint16_t); /* Binary Data Section */ /* ISEQ BLOCK */ irep->ilen = (uint16_t)bin_to_uint32(src); src += sizeof(uint32_t); src += skip_padding(src); if (irep->ilen > 0) { if (SIZE_ERROR_MUL(irep->ilen, sizeof(mrb_code))) { return NULL; } if ((flags & FLAG_SRC_MALLOC) == 0 && (flags & FLAG_BYTEORDER_NATIVE)) { irep->iseq = (mrb_code*)src; src += sizeof(mrb_code) * irep->ilen; irep->flags |= MRB_ISEQ_NO_FREE; } else { size_t data_len = sizeof(mrb_code) * irep->ilen; irep->iseq = (mrb_code *)mrb_malloc(mrb, data_len); memcpy(irep->iseq, src, data_len); src += data_len; } } /* POOL BLOCK */ plen = bin_to_uint32(src); /* number of pool */ src += sizeof(uint32_t); if (plen > 0) { if (SIZE_ERROR_MUL(plen, sizeof(mrb_value))) { return NULL; } irep->pool = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value) * plen); for (i = 0; i < plen; i++) { mrb_value s; tt = *src++; /* pool TT */ pool_data_len = bin_to_uint16(src); /* pool data length */ src += sizeof(uint16_t); if (flags & FLAG_SRC_MALLOC) { s = mrb_str_new(mrb, (char *)src, pool_data_len); } else { s = mrb_str_new_static(mrb, (char *)src, pool_data_len); } src += pool_data_len; switch (tt) { /* pool data */ case IREP_TT_FIXNUM: { mrb_value num = mrb_str_to_inum(mrb, s, 10, FALSE); #ifdef MRB_WITHOUT_FLOAT irep->pool[i] = num; #else irep->pool[i] = mrb_float_p(num)? mrb_float_pool(mrb, mrb_float(num)) : num; #endif } break; #ifndef MRB_WITHOUT_FLOAT case IREP_TT_FLOAT: irep->pool[i] = mrb_float_pool(mrb, mrb_str_to_dbl(mrb, s, FALSE)); break; #endif case IREP_TT_STRING: irep->pool[i] = mrb_str_pool(mrb, s); break; default: /* should not happen */ irep->pool[i] = mrb_nil_value(); break; } irep->plen++; mrb_gc_arena_restore(mrb, ai); } } /* SYMS BLOCK */ irep->slen = (uint16_t)bin_to_uint32(src); /* syms length */ src += sizeof(uint32_t); if (irep->slen > 0) { if (SIZE_ERROR_MUL(irep->slen, sizeof(mrb_sym))) { return NULL; } irep->syms = (mrb_sym *)mrb_malloc(mrb, sizeof(mrb_sym) * irep->slen); for (i = 0; i < irep->slen; i++) { snl = bin_to_uint16(src); /* symbol name length */ src += sizeof(uint16_t); if (snl == MRB_DUMP_NULL_SYM_LEN) { irep->syms[i] = 0; continue; } if (flags & FLAG_SRC_MALLOC) { irep->syms[i] = mrb_intern(mrb, (char *)src, snl); } else { irep->syms[i] = mrb_intern_static(mrb, (char *)src, snl); } src += snl + 1; mrb_gc_arena_restore(mrb, ai); } } irep->reps = (mrb_irep**)mrb_malloc(mrb, sizeof(mrb_irep*)*irep->rlen); diff = src - bin; mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX); *len = (size_t)diff; return irep; } static mrb_irep* read_irep_record(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flags) { mrb_irep *irep = read_irep_record_1(mrb, bin, len, flags); int i; if (irep == NULL) { return NULL; } bin += *len; for (i=0; irlen; i++) { size_t rlen; irep->reps[i] = read_irep_record(mrb, bin, &rlen, flags); if (irep->reps[i] == NULL) { return NULL; } bin += rlen; *len += rlen; } return irep; } static mrb_irep* read_section_irep(mrb_state *mrb, const uint8_t *bin, uint8_t flags) { size_t len; bin += sizeof(struct rite_section_irep_header); return read_irep_record(mrb, bin, &len, flags); } /* ignore lineno record */ static int read_lineno_record_1(mrb_state *mrb, const uint8_t *bin, mrb_irep *irep, size_t *len) { size_t i, fname_len, niseq; *len = 0; bin += sizeof(uint32_t); /* record size */ *len += sizeof(uint32_t); fname_len = bin_to_uint16(bin); bin += sizeof(uint16_t); *len += sizeof(uint16_t); bin += fname_len; *len += fname_len; niseq = (size_t)bin_to_uint32(bin); bin += sizeof(uint32_t); /* niseq */ *len += sizeof(uint32_t); if (SIZE_ERROR_MUL(niseq, sizeof(uint16_t))) { return MRB_DUMP_GENERAL_FAILURE; } for (i = 0; i < niseq; i++) { bin += sizeof(uint16_t); /* niseq */ *len += sizeof(uint16_t); } return MRB_DUMP_OK; } static int read_lineno_record(mrb_state *mrb, const uint8_t *bin, mrb_irep *irep, size_t *lenp) { int result = read_lineno_record_1(mrb, bin, irep, lenp); int i; if (result != MRB_DUMP_OK) return result; for (i = 0; i < irep->rlen; i++) { size_t len; result = read_lineno_record(mrb, bin, irep->reps[i], &len); if (result != MRB_DUMP_OK) break; bin += len; *lenp += len; } return result; } static int read_section_lineno(mrb_state *mrb, const uint8_t *bin, mrb_irep *irep) { size_t len; len = 0; bin += sizeof(struct rite_section_lineno_header); /* Read Binary Data Section */ return read_lineno_record(mrb, bin, irep, &len); } static int read_debug_record(mrb_state *mrb, const uint8_t *start, mrb_irep* irep, size_t *record_len, const mrb_sym *filenames, size_t filenames_len) { const uint8_t *bin = start; ptrdiff_t diff; size_t record_size; uint16_t f_idx; int i; if (irep->debug_info) { return MRB_DUMP_INVALID_IREP; } irep->debug_info = (mrb_irep_debug_info*)mrb_malloc(mrb, sizeof(mrb_irep_debug_info)); irep->debug_info->pc_count = (uint32_t)irep->ilen; record_size = (size_t)bin_to_uint32(bin); bin += sizeof(uint32_t); irep->debug_info->flen = bin_to_uint16(bin); irep->debug_info->files = (mrb_irep_debug_info_file**)mrb_malloc(mrb, sizeof(mrb_irep_debug_info*) * irep->debug_info->flen); bin += sizeof(uint16_t); for (f_idx = 0; f_idx < irep->debug_info->flen; ++f_idx) { mrb_irep_debug_info_file *file; uint16_t filename_idx; mrb_int len; file = (mrb_irep_debug_info_file *)mrb_malloc(mrb, sizeof(*file)); irep->debug_info->files[f_idx] = file; file->start_pos = bin_to_uint32(bin); bin += sizeof(uint32_t); /* filename */ filename_idx = bin_to_uint16(bin); bin += sizeof(uint16_t); mrb_assert(filename_idx < filenames_len); file->filename_sym = filenames[filename_idx]; len = 0; file->filename = mrb_sym2name_len(mrb, file->filename_sym, &len); file->line_entry_count = bin_to_uint32(bin); bin += sizeof(uint32_t); file->line_type = (mrb_debug_line_type)bin_to_uint8(bin); bin += sizeof(uint8_t); switch (file->line_type) { case mrb_debug_line_ary: { uint32_t l; file->lines.ary = (uint16_t *)mrb_malloc(mrb, sizeof(uint16_t) * (size_t)(file->line_entry_count)); for (l = 0; l < file->line_entry_count; ++l) { file->lines.ary[l] = bin_to_uint16(bin); bin += sizeof(uint16_t); } } break; case mrb_debug_line_flat_map: { uint32_t l; file->lines.flat_map = (mrb_irep_debug_info_line*)mrb_malloc( mrb, sizeof(mrb_irep_debug_info_line) * (size_t)(file->line_entry_count)); for (l = 0; l < file->line_entry_count; ++l) { file->lines.flat_map[l].start_pos = bin_to_uint32(bin); bin += sizeof(uint32_t); file->lines.flat_map[l].line = bin_to_uint16(bin); bin += sizeof(uint16_t); } } break; default: return MRB_DUMP_GENERAL_FAILURE; } } diff = bin - start; mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX); if (record_size != (size_t)diff) { return MRB_DUMP_GENERAL_FAILURE; } for (i = 0; i < irep->rlen; i++) { size_t len; int ret; ret = read_debug_record(mrb, bin, irep->reps[i], &len, filenames, filenames_len); if (ret != MRB_DUMP_OK) return ret; bin += len; } diff = bin - start; mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX); *record_len = (size_t)diff; return MRB_DUMP_OK; } static int read_section_debug(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, uint8_t flags) { const uint8_t *bin; ptrdiff_t diff; struct rite_section_debug_header *header; uint16_t i; size_t len = 0; int result; uint16_t filenames_len; mrb_sym *filenames; bin = start; header = (struct rite_section_debug_header *)bin; bin += sizeof(struct rite_section_debug_header); filenames_len = bin_to_uint16(bin); bin += sizeof(uint16_t); filenames = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym) * (size_t)filenames_len); for (i = 0; i < filenames_len; ++i) { uint16_t f_len = bin_to_uint16(bin); bin += sizeof(uint16_t); if (flags & FLAG_SRC_MALLOC) { filenames[i] = mrb_intern(mrb, (const char *)bin, (size_t)f_len); } else { filenames[i] = mrb_intern_static(mrb, (const char *)bin, (size_t)f_len); } bin += f_len; } result = read_debug_record(mrb, bin, irep, &len, filenames, filenames_len); if (result != MRB_DUMP_OK) goto debug_exit; bin += len; diff = bin - start; mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX); if ((uint32_t)diff != bin_to_uint32(header->section_size)) { result = MRB_DUMP_GENERAL_FAILURE; } debug_exit: mrb_free(mrb, filenames); return result; } static int read_lv_record(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, size_t *record_len, mrb_sym const *syms, uint32_t syms_len) { const uint8_t *bin = start; ptrdiff_t diff; int i; irep->lv = (struct mrb_locals*)mrb_malloc(mrb, sizeof(struct mrb_locals) * (irep->nlocals - 1)); for (i = 0; i + 1< irep->nlocals; ++i) { uint16_t const sym_idx = bin_to_uint16(bin); bin += sizeof(uint16_t); if (sym_idx == RITE_LV_NULL_MARK) { irep->lv[i].name = 0; irep->lv[i].r = 0; } else { if (sym_idx >= syms_len) { return MRB_DUMP_GENERAL_FAILURE; } irep->lv[i].name = syms[sym_idx]; irep->lv[i].r = bin_to_uint16(bin); } bin += sizeof(uint16_t); } for (i = 0; i < irep->rlen; ++i) { size_t len; int ret; ret = read_lv_record(mrb, bin, irep->reps[i], &len, syms, syms_len); if (ret != MRB_DUMP_OK) return ret; bin += len; } diff = bin - start; mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX); *record_len = (size_t)diff; return MRB_DUMP_OK; } static int read_section_lv(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, uint8_t flags) { const uint8_t *bin; ptrdiff_t diff; struct rite_section_lv_header const *header; uint32_t i; size_t len = 0; int result; uint32_t syms_len; mrb_sym *syms; mrb_sym (*intern_func)(mrb_state*, const char*, size_t) = (flags & FLAG_SRC_MALLOC)? mrb_intern : mrb_intern_static; bin = start; header = (struct rite_section_lv_header const*)bin; bin += sizeof(struct rite_section_lv_header); syms_len = bin_to_uint32(bin); bin += sizeof(uint32_t); syms = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym) * (size_t)syms_len); for (i = 0; i < syms_len; ++i) { uint16_t const str_len = bin_to_uint16(bin); bin += sizeof(uint16_t); syms[i] = intern_func(mrb, (const char*)bin, str_len); bin += str_len; } result = read_lv_record(mrb, bin, irep, &len, syms, syms_len); if (result != MRB_DUMP_OK) goto lv_exit; bin += len; diff = bin - start; mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX); if ((uint32_t)diff != bin_to_uint32(header->section_size)) { result = MRB_DUMP_GENERAL_FAILURE; } lv_exit: mrb_free(mrb, syms); return result; } static int read_binary_header(const uint8_t *bin, size_t *bin_size, uint16_t *crc, uint8_t *flags) { const struct rite_binary_header *header = (const struct rite_binary_header *)bin; if (memcmp(header->binary_ident, RITE_BINARY_IDENT, sizeof(header->binary_ident)) == 0) { if (bigendian_p()) *flags |= FLAG_BYTEORDER_NATIVE; else *flags |= FLAG_BYTEORDER_BIG; } else if (memcmp(header->binary_ident, RITE_BINARY_IDENT_LIL, sizeof(header->binary_ident)) == 0) { if (bigendian_p()) *flags |= FLAG_BYTEORDER_LIL; else *flags |= FLAG_BYTEORDER_NATIVE; } else { return MRB_DUMP_INVALID_FILE_HEADER; } if (crc) { *crc = bin_to_uint16(header->binary_crc); } *bin_size = (size_t)bin_to_uint32(header->binary_size); return MRB_DUMP_OK; } static mrb_irep* read_irep(mrb_state *mrb, const uint8_t *bin, uint8_t flags) { int result; mrb_irep *irep = NULL; const struct rite_section_header *section_header; uint16_t crc; size_t bin_size = 0; size_t n; if ((mrb == NULL) || (bin == NULL)) { return NULL; } result = read_binary_header(bin, &bin_size, &crc, &flags); if (result != MRB_DUMP_OK) { return NULL; } n = offset_crc_body(); if (crc != calc_crc_16_ccitt(bin + n, bin_size - n, 0)) { return NULL; } bin += sizeof(struct rite_binary_header); do { section_header = (const struct rite_section_header *)bin; if (memcmp(section_header->section_ident, RITE_SECTION_IREP_IDENT, sizeof(section_header->section_ident)) == 0) { irep = read_section_irep(mrb, bin, flags); if (!irep) return NULL; } else if (memcmp(section_header->section_ident, RITE_SECTION_LINENO_IDENT, sizeof(section_header->section_ident)) == 0) { if (!irep) return NULL; /* corrupted data */ result = read_section_lineno(mrb, bin, irep); if (result < MRB_DUMP_OK) { return NULL; } } else if (memcmp(section_header->section_ident, RITE_SECTION_DEBUG_IDENT, sizeof(section_header->section_ident)) == 0) { if (!irep) return NULL; /* corrupted data */ result = read_section_debug(mrb, bin, irep, flags); if (result < MRB_DUMP_OK) { return NULL; } } else if (memcmp(section_header->section_ident, RITE_SECTION_LV_IDENT, sizeof(section_header->section_ident)) == 0) { if (!irep) return NULL; result = read_section_lv(mrb, bin, irep, flags); if (result < MRB_DUMP_OK) { return NULL; } } bin += bin_to_uint32(section_header->section_size); } while (memcmp(section_header->section_ident, RITE_BINARY_EOF, sizeof(section_header->section_ident)) != 0); return irep; } mrb_irep* mrb_read_irep(mrb_state *mrb, const uint8_t *bin) { #ifdef MRB_USE_ETEXT_EDATA uint8_t flags = mrb_ro_data_p((char*)bin) ? FLAG_SRC_STATIC : FLAG_SRC_MALLOC; #else uint8_t flags = FLAG_SRC_STATIC; #endif return read_irep(mrb, bin, flags); } void mrb_exc_set(mrb_state *mrb, mrb_value exc); static void irep_error(mrb_state *mrb) { mrb_exc_set(mrb, mrb_exc_new_str_lit(mrb, E_SCRIPT_ERROR, "irep load error")); } void mrb_codedump_all(mrb_state*, struct RProc*); static mrb_value load_irep(mrb_state *mrb, mrb_irep *irep, mrbc_context *c) { struct RProc *proc; if (!irep) { irep_error(mrb); return mrb_nil_value(); } proc = mrb_proc_new(mrb, irep); proc->c = NULL; mrb_irep_decref(mrb, irep); if (c && c->dump_result) mrb_codedump_all(mrb, proc); if (c && c->no_exec) return mrb_obj_value(proc); return mrb_top_run(mrb, proc, mrb_top_self(mrb), 0); } MRB_API mrb_value mrb_load_irep_cxt(mrb_state *mrb, const uint8_t *bin, mrbc_context *c) { return load_irep(mrb, mrb_read_irep(mrb, bin), c); } MRB_API mrb_value mrb_load_irep(mrb_state *mrb, const uint8_t *bin) { return mrb_load_irep_cxt(mrb, bin, NULL); } #ifndef MRB_DISABLE_STDIO mrb_irep* mrb_read_irep_file(mrb_state *mrb, FILE* fp) { mrb_irep *irep = NULL; uint8_t *buf; const size_t header_size = sizeof(struct rite_binary_header); size_t buf_size = 0; uint8_t flags = 0; int result; if ((mrb == NULL) || (fp == NULL)) { return NULL; } buf = (uint8_t*)mrb_malloc(mrb, header_size); if (fread(buf, header_size, 1, fp) == 0) { goto irep_exit; } result = read_binary_header(buf, &buf_size, NULL, &flags); if (result != MRB_DUMP_OK || buf_size <= header_size) { goto irep_exit; } buf = (uint8_t*)mrb_realloc(mrb, buf, buf_size); if (fread(buf+header_size, buf_size-header_size, 1, fp) == 0) { goto irep_exit; } irep = read_irep(mrb, buf, FLAG_SRC_MALLOC); irep_exit: mrb_free(mrb, buf); return irep; } MRB_API mrb_value mrb_load_irep_file_cxt(mrb_state *mrb, FILE* fp, mrbc_context *c) { return load_irep(mrb, mrb_read_irep_file(mrb, fp), c); } MRB_API mrb_value mrb_load_irep_file(mrb_state *mrb, FILE* fp) { return mrb_load_irep_file_cxt(mrb, fp, NULL); } #endif /* MRB_DISABLE_STDIO */ mruby-2.0.0/src/mruby_core.rake000066400000000000000000000013701340361412400164470ustar00rootroot00000000000000MRuby.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}" objs = Dir.glob("#{current_dir}/*.c").map { |f| next nil if cxx_exception_enabled? and f =~ /(error|vm).c$/ next nil if self.cc.defines.flatten.include?("MRB_WITHOUT_FLOAT") and f =~ /fmt_fp.c$/ objfile(f.pathmap("#{current_build_dir}/%n")) }.compact if cxx_exception_enabled? objs += %w(vm error).map { |v| compile_as_cxx "#{current_dir}/#{v}.c", "#{current_build_dir}/#{v}.cxx" } end self.libmruby_objs << objs file libmruby_core_static => objs do |t| archiver.run t.name, t.prerequisites end end mruby-2.0.0/src/numeric.c000066400000000000000000001071421340361412400152470ustar00rootroot00000000000000/* ** numeric.c - Numeric, Integer, Float, Fixnum class ** ** See Copyright Notice in mruby.h */ #ifndef MRB_WITHOUT_FLOAT #include #include #endif #include #include #include #include #include #include #include #ifndef MRB_WITHOUT_FLOAT #ifdef MRB_USE_FLOAT #define trunc(f) truncf(f) #define floor(f) floorf(f) #define ceil(f) ceilf(f) #define fmod(x,y) fmodf(x,y) #define MRB_FLO_TO_STR_FMT "%.8g" #else #define MRB_FLO_TO_STR_FMT "%.16g" #endif #endif #ifndef MRB_WITHOUT_FLOAT MRB_API mrb_float mrb_to_flo(mrb_state *mrb, mrb_value val) { switch (mrb_type(val)) { case MRB_TT_FIXNUM: return (mrb_float)mrb_fixnum(val); case MRB_TT_FLOAT: break; default: mrb_raise(mrb, E_TYPE_ERROR, "non float value"); } return mrb_float(val); } #endif /* * call-seq: * * num ** other -> num * * Raises num the other power. * * 2.0**3 #=> 8.0 */ static mrb_value num_pow(mrb_state *mrb, mrb_value x) { mrb_value y; #ifndef MRB_WITHOUT_FLOAT mrb_float d; #endif mrb_get_args(mrb, "o", &y); if (mrb_fixnum_p(x) && mrb_fixnum_p(y)) { /* try ipow() */ mrb_int base = mrb_fixnum(x); mrb_int exp = mrb_fixnum(y); mrb_int result = 1; if (exp < 0) #ifdef MRB_WITHOUT_FLOAT return mrb_fixnum_value(0); #else goto float_pow; #endif for (;;) { if (exp & 1) { if (mrb_int_mul_overflow(result, base, &result)) { #ifndef MRB_WITHOUT_FLOAT goto float_pow; #endif } } exp >>= 1; if (exp == 0) break; if (mrb_int_mul_overflow(base, base, &base)) { #ifndef MRB_WITHOUT_FLOAT goto float_pow; #endif } } return mrb_fixnum_value(result); } #ifdef MRB_WITHOUT_FLOAT mrb_raise(mrb, E_TYPE_ERROR, "non fixnum value"); #else float_pow: d = pow(mrb_to_flo(mrb, x), mrb_to_flo(mrb, y)); return mrb_float_value(mrb, d); #endif } /* 15.2.8.3.4 */ /* 15.2.9.3.4 */ /* * call-seq: * num / other -> num * * Performs division: the class of the resulting object depends on * the class of num and on the magnitude of the * result. */ mrb_value mrb_num_div(mrb_state *mrb, mrb_value x, mrb_value y) { #ifdef MRB_WITHOUT_FLOAT if (!mrb_fixnum_p(y)) { mrb_raise(mrb, E_TYPE_ERROR, "non fixnum value"); } return mrb_fixnum_value(mrb_fixnum(x) / mrb_fixnum(y)); #else return mrb_float_value(mrb, mrb_to_flo(mrb, x) / mrb_to_flo(mrb, y)); #endif } /* 15.2.9.3.19(x) */ /* * call-seq: * num.quo(numeric) -> real * * Returns most exact division. */ static mrb_value num_div(mrb_state *mrb, mrb_value x) { #ifdef MRB_WITHOUT_FLOAT mrb_value y; mrb_get_args(mrb, "o", &y); if (!mrb_fixnum_p(y)) { mrb_raise(mrb, E_TYPE_ERROR, "non fixnum value"); } return mrb_fixnum_value(mrb_fixnum(x) / mrb_fixnum(y)); #else mrb_float y; mrb_get_args(mrb, "f", &y); return mrb_float_value(mrb, mrb_to_flo(mrb, x) / y); #endif } #ifndef MRB_WITHOUT_FLOAT /******************************************************************** * * Document-class: Float * * Float objects represent inexact real numbers using * the native architecture's double-precision floating point * representation. */ /* 15.2.9.3.16(x) */ /* * call-seq: * flt.to_s -> string * * 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". */ static mrb_value flo_to_s(mrb_state *mrb, mrb_value flt) { if (isnan(mrb_float(flt))) { return mrb_str_new_lit(mrb, "NaN"); } return mrb_float_to_str(mrb, flt, MRB_FLO_TO_STR_FMT); } /* 15.2.9.3.2 */ /* * call-seq: * float - other -> float * * Returns a new float which is the difference of float * and other. */ static mrb_value flo_minus(mrb_state *mrb, mrb_value x) { mrb_value y; mrb_get_args(mrb, "o", &y); return mrb_float_value(mrb, mrb_float(x) - mrb_to_flo(mrb, y)); } /* 15.2.9.3.3 */ /* * call-seq: * float * other -> float * * Returns a new float which is the product of float * and other. */ static mrb_value flo_mul(mrb_state *mrb, mrb_value x) { mrb_value y; mrb_get_args(mrb, "o", &y); return mrb_float_value(mrb, mrb_float(x) * mrb_to_flo(mrb, y)); } static void flodivmod(mrb_state *mrb, double x, double y, mrb_float *divp, mrb_float *modp) { double div, mod; if (isnan(y)) { /* y is NaN so all results are NaN */ div = mod = y; goto exit; } if (y == 0.0) { if (x == 0) div = NAN; else if (x > 0.0) div = INFINITY; else div = -INFINITY; /* x < 0.0 */ mod = NAN; goto exit; } if ((x == 0.0) || (isinf(y) && !isinf(x))) { mod = x; } else { mod = fmod(x, y); } if (isinf(x) && !isinf(y)) { div = x; } else { div = (x - mod) / y; if (modp && divp) div = round(div); } if (y*mod < 0) { mod += y; div -= 1.0; } exit: if (modp) *modp = mod; if (divp) *divp = div; } /* 15.2.9.3.5 */ /* * call-seq: * flt % other -> float * flt.modulo(other) -> float * * Return the modulo after division of flt by other. * * 6543.21.modulo(137) #=> 104.21 * 6543.21.modulo(137.24) #=> 92.9299999999996 */ static mrb_value flo_mod(mrb_state *mrb, mrb_value x) { mrb_value y; mrb_float mod; mrb_get_args(mrb, "o", &y); flodivmod(mrb, mrb_float(x), mrb_to_flo(mrb, y), 0, &mod); return mrb_float_value(mrb, mod); } #endif /* 15.2.8.3.16 */ /* * call-seq: * num.eql?(numeric) -> true or false * * Returns true if num and numeric are the * same type and have equal values. * * 1 == 1.0 #=> true * 1.eql?(1.0) #=> false * (1.0).eql?(1.0) #=> true */ static mrb_value fix_eql(mrb_state *mrb, mrb_value x) { mrb_value y; mrb_get_args(mrb, "o", &y); if (!mrb_fixnum_p(y)) return mrb_false_value(); return mrb_bool_value(mrb_fixnum(x) == mrb_fixnum(y)); } #ifndef MRB_WITHOUT_FLOAT static mrb_value flo_eql(mrb_state *mrb, mrb_value x) { mrb_value y; mrb_get_args(mrb, "o", &y); if (!mrb_float_p(y)) return mrb_false_value(); return mrb_bool_value(mrb_float(x) == (mrb_float)mrb_fixnum(y)); } /* 15.2.9.3.7 */ /* * call-seq: * flt == obj -> true or false * * Returns true only if obj has the same value * as flt. Contrast this with Float#eql?, which * requires obj to be a Float. * * 1.0 == 1 #=> true * */ static mrb_value flo_eq(mrb_state *mrb, mrb_value x) { mrb_value y; mrb_get_args(mrb, "o", &y); switch (mrb_type(y)) { case MRB_TT_FIXNUM: return mrb_bool_value(mrb_float(x) == (mrb_float)mrb_fixnum(y)); case MRB_TT_FLOAT: return mrb_bool_value(mrb_float(x) == mrb_float(y)); default: return mrb_false_value(); } } static int64_t value_int64(mrb_state *mrb, mrb_value x) { switch (mrb_type(x)) { case MRB_TT_FIXNUM: return (int64_t)mrb_fixnum(x); break; case MRB_TT_FLOAT: return (int64_t)mrb_float(x); default: mrb_raise(mrb, E_TYPE_ERROR, "cannot convert to Integer"); break; } /* not reached */ return 0; } static mrb_value int64_value(mrb_state *mrb, int64_t v) { if (FIXABLE(v)) { return mrb_fixnum_value((mrb_int)v); } return mrb_float_value(mrb, (mrb_float)v); } static mrb_value flo_rev(mrb_state *mrb, mrb_value x) { int64_t v1; mrb_get_args(mrb, ""); v1 = (int64_t)mrb_float(x); return int64_value(mrb, ~v1); } static mrb_value flo_and(mrb_state *mrb, mrb_value x) { mrb_value y; int64_t v1, v2; mrb_get_args(mrb, "o", &y); v1 = (int64_t)mrb_float(x); v2 = value_int64(mrb, y); return int64_value(mrb, v1 & v2); } static mrb_value flo_or(mrb_state *mrb, mrb_value x) { mrb_value y; int64_t v1, v2; mrb_get_args(mrb, "o", &y); v1 = (int64_t)mrb_float(x); v2 = value_int64(mrb, y); return int64_value(mrb, v1 | v2); } static mrb_value flo_xor(mrb_state *mrb, mrb_value x) { mrb_value y; int64_t v1, v2; mrb_get_args(mrb, "o", &y); v1 = (int64_t)mrb_float(x); v2 = value_int64(mrb, y); return int64_value(mrb, v1 ^ v2); } static mrb_value flo_shift(mrb_state *mrb, mrb_value x, mrb_int width) { mrb_float val; if (width == 0) { return x; } val = mrb_float(x); if (width < 0) { while (width++) { val /= 2; } #if defined(_ISOC99_SOURCE) val = trunc(val); #else if (val > 0){ val = floor(val); } else { val = ceil(val); } #endif if (val == 0 && mrb_float(x) < 0) { return mrb_fixnum_value(-1); } } else { while (width--) { val *= 2; } } if (FIXABLE_FLOAT(val)) { return mrb_fixnum_value((mrb_int)val); } return mrb_float_value(mrb, val); } static mrb_value flo_lshift(mrb_state *mrb, mrb_value x) { mrb_int width; mrb_get_args(mrb, "i", &width); return flo_shift(mrb, x, -width); } static mrb_value flo_rshift(mrb_state *mrb, mrb_value x) { mrb_int width; mrb_get_args(mrb, "i", &width); return flo_shift(mrb, x, width); } /* 15.2.9.3.13 */ /* * call-seq: * flt.to_f -> self * * As flt is already a float, returns +self+. */ static mrb_value flo_to_f(mrb_state *mrb, mrb_value num) { return num; } /* 15.2.9.3.11 */ /* * call-seq: * flt.infinite? -> nil, -1, +1 * * Returns nil, -1, or +1 depending on whether flt * is finite, -infinity, or +infinity. * * (0.0).infinite? #=> nil * (-1.0/0.0).infinite? #=> -1 * (+1.0/0.0).infinite? #=> 1 */ static mrb_value flo_infinite_p(mrb_state *mrb, mrb_value num) { mrb_float value = mrb_float(num); if (isinf(value)) { return mrb_fixnum_value(value < 0 ? -1 : 1); } return mrb_nil_value(); } /* 15.2.9.3.9 */ /* * call-seq: * flt.finite? -> true or false * * Returns true if flt is a valid IEEE floating * point number (it is not infinite, and nan? is * false). * */ static mrb_value flo_finite_p(mrb_state *mrb, mrb_value num) { return mrb_bool_value(isfinite(mrb_float(num))); } void mrb_check_num_exact(mrb_state *mrb, mrb_float num) { if (isinf(num)) { mrb_raise(mrb, E_FLOATDOMAIN_ERROR, num < 0 ? "-Infinity" : "Infinity"); } if (isnan(num)) { mrb_raise(mrb, E_FLOATDOMAIN_ERROR, "NaN"); } } /* 15.2.9.3.10 */ /* * call-seq: * flt.floor -> integer * * Returns the largest integer less than or equal to flt. * * 1.2.floor #=> 1 * 2.0.floor #=> 2 * (-1.2).floor #=> -2 * (-2.0).floor #=> -2 */ static mrb_value flo_floor(mrb_state *mrb, mrb_value num) { mrb_float f = floor(mrb_float(num)); mrb_check_num_exact(mrb, f); if (!FIXABLE_FLOAT(f)) { return mrb_float_value(mrb, f); } return mrb_fixnum_value((mrb_int)f); } /* 15.2.9.3.8 */ /* * call-seq: * flt.ceil -> integer * * Returns the smallest Integer greater than or equal to * flt. * * 1.2.ceil #=> 2 * 2.0.ceil #=> 2 * (-1.2).ceil #=> -1 * (-2.0).ceil #=> -2 */ static mrb_value flo_ceil(mrb_state *mrb, mrb_value num) { mrb_float f = ceil(mrb_float(num)); mrb_check_num_exact(mrb, f); if (!FIXABLE_FLOAT(f)) { return mrb_float_value(mrb, f); } return mrb_fixnum_value((mrb_int)f); } /* 15.2.9.3.12 */ /* * call-seq: * flt.round([ndigits]) -> integer or float * * Rounds flt to a given precision in decimal digits (default 0 digits). * Precision may be negative. Returns a floating point number when ndigits * is more than zero. * * 1.4.round #=> 1 * 1.5.round #=> 2 * 1.6.round #=> 2 * (-1.5).round #=> -2 * * 1.234567.round(2) #=> 1.23 * 1.234567.round(3) #=> 1.235 * 1.234567.round(4) #=> 1.2346 * 1.234567.round(5) #=> 1.23457 * * 34567.89.round(-5) #=> 0 * 34567.89.round(-4) #=> 30000 * 34567.89.round(-3) #=> 35000 * 34567.89.round(-2) #=> 34600 * 34567.89.round(-1) #=> 34570 * 34567.89.round(0) #=> 34568 * 34567.89.round(1) #=> 34567.9 * 34567.89.round(2) #=> 34567.89 * 34567.89.round(3) #=> 34567.89 * */ static mrb_value flo_round(mrb_state *mrb, mrb_value num) { double number, f; mrb_int ndigits = 0; mrb_int i; mrb_get_args(mrb, "|i", &ndigits); number = mrb_float(num); if (0 < ndigits && (isinf(number) || isnan(number))) { return num; } mrb_check_num_exact(mrb, number); f = 1.0; i = ndigits >= 0 ? ndigits : -ndigits; while (--i >= 0) f = f*10.0; if (isinf(f)) { if (ndigits < 0) number = 0; } else { double d; if (ndigits < 0) number /= f; else number *= f; /* home-made inline implementation of round(3) */ if (number > 0.0) { d = floor(number); number = d + (number - d >= 0.5); } else if (number < 0.0) { d = ceil(number); number = d - (d - number >= 0.5); } if (ndigits < 0) number *= f; else number /= f; } if (ndigits > 0) { if (!isfinite(number)) return num; return mrb_float_value(mrb, number); } return mrb_fixnum_value((mrb_int)number); } /* 15.2.9.3.14 */ /* 15.2.9.3.15 */ /* * call-seq: * flt.to_i -> integer * flt.truncate -> integer * * Returns flt truncated to an Integer. */ static mrb_value flo_truncate(mrb_state *mrb, mrb_value num) { mrb_float f = mrb_float(num); if (f > 0.0) f = floor(f); if (f < 0.0) f = ceil(f); mrb_check_num_exact(mrb, f); if (!FIXABLE_FLOAT(f)) { return mrb_float_value(mrb, f); } return mrb_fixnum_value((mrb_int)f); } static mrb_value flo_nan_p(mrb_state *mrb, mrb_value num) { return mrb_bool_value(isnan(mrb_float(num))); } #endif /* * Document-class: Integer * * Integer is the basis for the two concrete classes that * hold whole numbers, Bignum and Fixnum. * */ /* * call-seq: * int.to_i -> integer * * As int is already an Integer, all these * methods simply return the receiver. */ static mrb_value int_to_i(mrb_state *mrb, mrb_value num) { return num; } mrb_value mrb_fixnum_mul(mrb_state *mrb, mrb_value x, mrb_value y) { mrb_int a; a = mrb_fixnum(x); if (mrb_fixnum_p(y)) { mrb_int b, c; if (a == 0) return x; b = mrb_fixnum(y); if (mrb_int_mul_overflow(a, b, &c)) { #ifndef MRB_WITHOUT_FLOAT return mrb_float_value(mrb, (mrb_float)a * (mrb_float)b); #endif } return mrb_fixnum_value(c); } #ifdef MRB_WITHOUT_FLOAT mrb_raise(mrb, E_TYPE_ERROR, "non fixnum value"); #else return mrb_float_value(mrb, (mrb_float)a * mrb_to_flo(mrb, y)); #endif } /* 15.2.8.3.3 */ /* * call-seq: * fix * numeric -> numeric_result * * Performs multiplication: the class of the resulting object depends on * the class of numeric and on the magnitude of the * result. */ static mrb_value fix_mul(mrb_state *mrb, mrb_value x) { mrb_value y; mrb_get_args(mrb, "o", &y); return mrb_fixnum_mul(mrb, x, y); } static void fixdivmod(mrb_state *mrb, mrb_int x, mrb_int y, mrb_int *divp, mrb_int *modp) { mrb_int div, mod; /* TODO: add mrb_assert(y != 0) to make sure */ if (y < 0) { if (x < 0) div = -x / -y; else div = - (x / -y); } else { if (x < 0) div = - (-x / y); else div = x / y; } mod = x - div*y; if ((mod < 0 && y > 0) || (mod > 0 && y < 0)) { mod += y; div -= 1; } if (divp) *divp = div; if (modp) *modp = mod; } /* 15.2.8.3.5 */ /* * call-seq: * fix % other -> real * fix.modulo(other) -> real * * Returns fix modulo other. * See numeric.divmod for more information. */ static mrb_value fix_mod(mrb_state *mrb, mrb_value x) { mrb_value y; mrb_int a; mrb_get_args(mrb, "o", &y); a = mrb_fixnum(x); if (mrb_fixnum_p(y)) { mrb_int b, mod; if ((b=mrb_fixnum(y)) == 0) { #ifdef MRB_WITHOUT_FLOAT /* ZeroDivisionError */ return mrb_fixnum_value(0); #else return mrb_float_value(mrb, NAN); #endif } fixdivmod(mrb, a, b, 0, &mod); return mrb_fixnum_value(mod); } #ifdef MRB_WITHOUT_FLOAT mrb_raise(mrb, E_TYPE_ERROR, "non fixnum value"); #else else { mrb_float mod; flodivmod(mrb, (mrb_float)a, mrb_to_flo(mrb, y), 0, &mod); return mrb_float_value(mrb, mod); } #endif } /* * call-seq: * fix.divmod(numeric) -> array * * See Numeric#divmod. */ static mrb_value fix_divmod(mrb_state *mrb, mrb_value x) { mrb_value y; mrb_get_args(mrb, "o", &y); if (mrb_fixnum_p(y)) { mrb_int div, mod; if (mrb_fixnum(y) == 0) { #ifdef MRB_WITHOUT_FLOAT return mrb_assoc_new(mrb, mrb_fixnum_value(0), mrb_fixnum_value(0)); #else return mrb_assoc_new(mrb, ((mrb_fixnum(x) == 0) ? mrb_float_value(mrb, NAN): mrb_float_value(mrb, INFINITY)), mrb_float_value(mrb, NAN)); #endif } fixdivmod(mrb, mrb_fixnum(x), mrb_fixnum(y), &div, &mod); return mrb_assoc_new(mrb, mrb_fixnum_value(div), mrb_fixnum_value(mod)); } #ifdef MRB_WITHOUT_FLOAT mrb_raise(mrb, E_TYPE_ERROR, "non fixnum value"); #else else { mrb_float div, mod; mrb_value a, b; flodivmod(mrb, (mrb_float)mrb_fixnum(x), mrb_to_flo(mrb, y), &div, &mod); a = mrb_float_value(mrb, div); b = mrb_float_value(mrb, mod); return mrb_assoc_new(mrb, a, b); } #endif } #ifndef MRB_WITHOUT_FLOAT static mrb_value flo_divmod(mrb_state *mrb, mrb_value x) { mrb_value y; mrb_float div, mod; mrb_value a, b; mrb_get_args(mrb, "o", &y); flodivmod(mrb, mrb_float(x), mrb_to_flo(mrb, y), &div, &mod); a = mrb_float_value(mrb, div); b = mrb_float_value(mrb, mod); return mrb_assoc_new(mrb, a, b); } #endif /* 15.2.8.3.7 */ /* * call-seq: * fix == other -> true or false * * Return true if fix equals other * numerically. * * 1 == 2 #=> false * 1 == 1.0 #=> true */ static mrb_value fix_equal(mrb_state *mrb, mrb_value x) { mrb_value y; mrb_get_args(mrb, "o", &y); switch (mrb_type(y)) { case MRB_TT_FIXNUM: return mrb_bool_value(mrb_fixnum(x) == mrb_fixnum(y)); #ifndef MRB_WITHOUT_FLOAT case MRB_TT_FLOAT: return mrb_bool_value((mrb_float)mrb_fixnum(x) == mrb_float(y)); #endif default: return mrb_false_value(); } } /* 15.2.8.3.8 */ /* * call-seq: * ~fix -> integer * * One's complement: returns a number where each bit is flipped. * ex.0---00001 (1)-> 1---11110 (-2) * ex.0---00010 (2)-> 1---11101 (-3) * ex.0---00100 (4)-> 1---11011 (-5) */ static mrb_value fix_rev(mrb_state *mrb, mrb_value num) { mrb_int val = mrb_fixnum(num); return mrb_fixnum_value(~val); } #ifdef MRB_WITHOUT_FLOAT #define bit_op(x,y,op1,op2) do {\ return mrb_fixnum_value(mrb_fixnum(x) op2 mrb_fixnum(y));\ } while(0) #else static mrb_value flo_and(mrb_state *mrb, mrb_value x); static mrb_value flo_or(mrb_state *mrb, mrb_value x); static mrb_value flo_xor(mrb_state *mrb, mrb_value x); #define bit_op(x,y,op1,op2) do {\ if (mrb_fixnum_p(y)) return mrb_fixnum_value(mrb_fixnum(x) op2 mrb_fixnum(y));\ return flo_ ## op1(mrb, mrb_float_value(mrb, (mrb_float)mrb_fixnum(x)));\ } while(0) #endif /* 15.2.8.3.9 */ /* * call-seq: * fix & integer -> integer_result * * Bitwise AND. */ static mrb_value fix_and(mrb_state *mrb, mrb_value x) { mrb_value y; mrb_get_args(mrb, "o", &y); bit_op(x, y, and, &); } /* 15.2.8.3.10 */ /* * call-seq: * fix | integer -> integer_result * * Bitwise OR. */ static mrb_value fix_or(mrb_state *mrb, mrb_value x) { mrb_value y; mrb_get_args(mrb, "o", &y); bit_op(x, y, or, |); } /* 15.2.8.3.11 */ /* * call-seq: * fix ^ integer -> integer_result * * Bitwise EXCLUSIVE OR. */ static mrb_value fix_xor(mrb_state *mrb, mrb_value x) { mrb_value y; mrb_get_args(mrb, "o", &y); bit_op(x, y, or, ^); } #define NUMERIC_SHIFT_WIDTH_MAX (MRB_INT_BIT-1) static mrb_value lshift(mrb_state *mrb, mrb_int val, mrb_int width) { if (width < 0) { /* mrb_int overflow */ #ifdef MRB_WITHOUT_FLOAT return mrb_fixnum_value(0); #else return mrb_float_value(mrb, INFINITY); #endif } if (val > 0) { if ((width > NUMERIC_SHIFT_WIDTH_MAX) || (val > (MRB_INT_MAX >> width))) { #ifdef MRB_WITHOUT_FLOAT return mrb_fixnum_value(-1); #else goto bit_overflow; #endif } return mrb_fixnum_value(val << width); } else { if ((width > NUMERIC_SHIFT_WIDTH_MAX) || (val < (MRB_INT_MIN >> width))) { #ifdef MRB_WITHOUT_FLOAT return mrb_fixnum_value(0); #else goto bit_overflow; #endif } return mrb_fixnum_value(val * ((mrb_int)1 << width)); } #ifndef MRB_WITHOUT_FLOAT bit_overflow: { mrb_float f = (mrb_float)val; while (width--) { f *= 2; } return mrb_float_value(mrb, f); } #endif } static mrb_value rshift(mrb_int val, mrb_int width) { if (width < 0) { /* mrb_int overflow */ return mrb_fixnum_value(0); } if (width >= NUMERIC_SHIFT_WIDTH_MAX) { if (val < 0) { return mrb_fixnum_value(-1); } return mrb_fixnum_value(0); } return mrb_fixnum_value(val >> width); } /* 15.2.8.3.12 */ /* * call-seq: * fix << count -> integer or float * * Shifts _fix_ left _count_ positions (right if _count_ is negative). */ static mrb_value fix_lshift(mrb_state *mrb, mrb_value x) { mrb_int width, val; mrb_get_args(mrb, "i", &width); if (width == 0) { return x; } val = mrb_fixnum(x); if (val == 0) return x; if (width < 0) { return rshift(val, -width); } return lshift(mrb, val, width); } /* 15.2.8.3.13 */ /* * call-seq: * fix >> count -> integer or float * * Shifts _fix_ right _count_ positions (left if _count_ is negative). */ static mrb_value fix_rshift(mrb_state *mrb, mrb_value x) { mrb_int width, val; mrb_get_args(mrb, "i", &width); if (width == 0) { return x; } val = mrb_fixnum(x); if (val == 0) return x; if (width < 0) { return lshift(mrb, val, -width); } return rshift(val, width); } /* 15.2.8.3.23 */ /* * call-seq: * fix.to_f -> float * * Converts fix to a Float. * */ #ifndef MRB_WITHOUT_FLOAT static mrb_value fix_to_f(mrb_state *mrb, mrb_value num) { return mrb_float_value(mrb, (mrb_float)mrb_fixnum(num)); } /* * Document-class: FloatDomainError * * Raised when attempting to convert special float values * (in particular infinite or NaN) * to numerical classes which don't support them. * * Float::INFINITY.to_r * * raises the exception: * * FloatDomainError: Infinity */ /* ------------------------------------------------------------------------*/ MRB_API mrb_value mrb_flo_to_fixnum(mrb_state *mrb, mrb_value x) { mrb_int z = 0; if (!mrb_float_p(x)) { mrb_raise(mrb, E_TYPE_ERROR, "non float value"); z = 0; /* not reached. just suppress warnings. */ } else { mrb_float d = mrb_float(x); if (isinf(d)) { mrb_raise(mrb, E_FLOATDOMAIN_ERROR, d < 0 ? "-Infinity" : "Infinity"); } if (isnan(d)) { mrb_raise(mrb, E_FLOATDOMAIN_ERROR, "NaN"); } if (FIXABLE_FLOAT(d)) { z = (mrb_int)d; } else { mrb_raisef(mrb, E_ARGUMENT_ERROR, "number (%S) too big for integer", x); } } return mrb_fixnum_value(z); } #endif mrb_value mrb_fixnum_plus(mrb_state *mrb, mrb_value x, mrb_value y) { mrb_int a; a = mrb_fixnum(x); if (mrb_fixnum_p(y)) { mrb_int b, c; if (a == 0) return y; b = mrb_fixnum(y); if (mrb_int_add_overflow(a, b, &c)) { #ifndef MRB_WITHOUT_FLOAT return mrb_float_value(mrb, (mrb_float)a + (mrb_float)b); #endif } return mrb_fixnum_value(c); } #ifdef MRB_WITHOUT_FLOAT mrb_raise(mrb, E_TYPE_ERROR, "non fixnum value"); #else return mrb_float_value(mrb, (mrb_float)a + mrb_to_flo(mrb, y)); #endif } /* 15.2.8.3.1 */ /* * call-seq: * fix + numeric -> numeric_result * * Performs addition: the class of the resulting object depends on * the class of numeric and on the magnitude of the * result. */ static mrb_value fix_plus(mrb_state *mrb, mrb_value self) { mrb_value other; mrb_get_args(mrb, "o", &other); return mrb_fixnum_plus(mrb, self, other); } mrb_value mrb_fixnum_minus(mrb_state *mrb, mrb_value x, mrb_value y) { mrb_int a; a = mrb_fixnum(x); if (mrb_fixnum_p(y)) { mrb_int b, c; b = mrb_fixnum(y); if (mrb_int_sub_overflow(a, b, &c)) { #ifndef MRB_WITHOUT_FLOAT return mrb_float_value(mrb, (mrb_float)a - (mrb_float)b); #endif } return mrb_fixnum_value(c); } #ifdef MRB_WITHOUT_FLOAT mrb_raise(mrb, E_TYPE_ERROR, "non fixnum value"); #else return mrb_float_value(mrb, (mrb_float)a - mrb_to_flo(mrb, y)); #endif } /* 15.2.8.3.2 */ /* 15.2.8.3.16 */ /* * call-seq: * fix - numeric -> numeric_result * * Performs subtraction: the class of the resulting object depends on * the class of numeric and on the magnitude of the * result. */ static mrb_value fix_minus(mrb_state *mrb, mrb_value self) { mrb_value other; mrb_get_args(mrb, "o", &other); return mrb_fixnum_minus(mrb, self, other); } MRB_API mrb_value mrb_fixnum_to_str(mrb_state *mrb, mrb_value x, mrb_int base) { char buf[MRB_INT_BIT+1]; char *b = buf + sizeof buf; mrb_int val = mrb_fixnum(x); if (base < 2 || 36 < base) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid radix %S", mrb_fixnum_value(base)); } if (val == 0) { *--b = '0'; } else if (val < 0) { do { *--b = mrb_digitmap[-(val % base)]; } while (val /= base); *--b = '-'; } else { do { *--b = mrb_digitmap[(int)(val % base)]; } while (val /= base); } return mrb_str_new(mrb, b, buf + sizeof(buf) - b); } /* 15.2.8.3.25 */ /* * call-seq: * fix.to_s(base=10) -> string * * Returns a string containing the representation of fix radix * base (between 2 and 36). * * 12345.to_s #=> "12345" * 12345.to_s(2) #=> "11000000111001" * 12345.to_s(8) #=> "30071" * 12345.to_s(10) #=> "12345" * 12345.to_s(16) #=> "3039" * 12345.to_s(36) #=> "9ix" * */ static mrb_value fix_to_s(mrb_state *mrb, mrb_value self) { mrb_int base = 10; mrb_get_args(mrb, "|i", &base); return mrb_fixnum_to_str(mrb, self, base); } /* compare two numbers: (1:0:-1; -2 for error) */ static mrb_int cmpnum(mrb_state *mrb, mrb_value v1, mrb_value v2) { #ifdef MRB_WITHOUT_FLOAT mrb_int x, y; #else mrb_float x, y; #endif #ifdef MRB_WITHOUT_FLOAT x = mrb_fixnum(v1); #else x = mrb_to_flo(mrb, v1); #endif switch (mrb_type(v2)) { case MRB_TT_FIXNUM: #ifdef MRB_WITHOUT_FLOAT y = mrb_fixnum(v2); #else y = (mrb_float)mrb_fixnum(v2); #endif break; #ifndef MRB_WITHOUT_FLOAT case MRB_TT_FLOAT: y = mrb_float(v2); break; #endif default: return -2; } if (x > y) return 1; else { if (x < y) return -1; return 0; } } /* 15.2.9.3.6 */ /* * call-seq: * self.f <=> other.f => -1, 0, +1 * < => -1 * = => 0 * > => +1 * Comparison---Returns -1, 0, or +1 depending on whether fix is * less than, equal to, or greater than numeric. This is the * basis for the tests in Comparable. */ static mrb_value num_cmp(mrb_state *mrb, mrb_value self) { mrb_value other; mrb_int n; mrb_get_args(mrb, "o", &other); n = cmpnum(mrb, self, other); if (n == -2) return mrb_nil_value(); return mrb_fixnum_value(n); } static void cmperr(mrb_state *mrb, mrb_value v1, mrb_value v2) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "comparison of %S with %S failed", mrb_obj_value(mrb_class(mrb, v1)), mrb_obj_value(mrb_class(mrb, v2))); } static mrb_value num_lt(mrb_state *mrb, mrb_value self) { mrb_value other; mrb_int n; mrb_get_args(mrb, "o", &other); n = cmpnum(mrb, self, other); if (n == -2) cmperr(mrb, self, other); if (n < 0) return mrb_true_value(); return mrb_false_value(); } static mrb_value num_le(mrb_state *mrb, mrb_value self) { mrb_value other; mrb_int n; mrb_get_args(mrb, "o", &other); n = cmpnum(mrb, self, other); if (n == -2) cmperr(mrb, self, other); if (n <= 0) return mrb_true_value(); return mrb_false_value(); } static mrb_value num_gt(mrb_state *mrb, mrb_value self) { mrb_value other; mrb_int n; mrb_get_args(mrb, "o", &other); n = cmpnum(mrb, self, other); if (n == -2) cmperr(mrb, self, other); if (n > 0) return mrb_true_value(); return mrb_false_value(); } static mrb_value num_ge(mrb_state *mrb, mrb_value self) { mrb_value other; mrb_int n; mrb_get_args(mrb, "o", &other); n = cmpnum(mrb, self, other); if (n == -2) cmperr(mrb, self, other); if (n >= 0) return mrb_true_value(); return mrb_false_value(); } static mrb_value num_finite_p(mrb_state *mrb, mrb_value self) { mrb_get_args(mrb, ""); return mrb_true_value(); } static mrb_value num_infinite_p(mrb_state *mrb, mrb_value self) { mrb_get_args(mrb, ""); return mrb_false_value(); } /* 15.2.9.3.1 */ /* * call-seq: * float + other -> float * * Returns a new float which is the sum of float * and other. */ #ifndef MRB_WITHOUT_FLOAT static mrb_value flo_plus(mrb_state *mrb, mrb_value x) { mrb_value y; mrb_get_args(mrb, "o", &y); return mrb_float_value(mrb, mrb_float(x) + mrb_to_flo(mrb, y)); } #endif /* ------------------------------------------------------------------------*/ void mrb_init_numeric(mrb_state *mrb) { struct RClass *numeric, *integer, *fixnum; #ifndef MRB_WITHOUT_FLOAT struct RClass *fl; #endif /* Numeric Class */ numeric = mrb_define_class(mrb, "Numeric", mrb->object_class); /* 15.2.7 */ mrb_define_method(mrb, numeric, "**", num_pow, MRB_ARGS_REQ(1)); mrb_define_method(mrb, numeric, "/", num_div, MRB_ARGS_REQ(1)); /* 15.2.8.3.4 */ mrb_define_method(mrb, numeric, "quo", num_div, MRB_ARGS_REQ(1)); /* 15.2.7.4.5 (x) */ mrb_define_method(mrb, numeric, "<=>", num_cmp, MRB_ARGS_REQ(1)); /* 15.2.9.3.6 */ mrb_define_method(mrb, numeric, "<", num_lt, MRB_ARGS_REQ(1)); mrb_define_method(mrb, numeric, "<=", num_le, MRB_ARGS_REQ(1)); mrb_define_method(mrb, numeric, ">", num_gt, MRB_ARGS_REQ(1)); mrb_define_method(mrb, numeric, ">=", num_ge, MRB_ARGS_REQ(1)); mrb_define_method(mrb, numeric, "finite?", num_finite_p, MRB_ARGS_NONE()); mrb_define_method(mrb, numeric, "infinite?",num_infinite_p, MRB_ARGS_NONE()); /* Integer Class */ integer = mrb_define_class(mrb, "Integer", numeric); /* 15.2.8 */ MRB_SET_INSTANCE_TT(integer, MRB_TT_FIXNUM); mrb_undef_class_method(mrb, integer, "new"); mrb_define_method(mrb, integer, "to_i", int_to_i, MRB_ARGS_NONE()); /* 15.2.8.3.24 */ #ifndef MRB_WITHOUT_FLOAT mrb_define_method(mrb, integer, "ceil", int_to_i, MRB_ARGS_REQ(1)); /* 15.2.8.3.8 (x) */ mrb_define_method(mrb, integer, "floor", int_to_i, MRB_ARGS_REQ(1)); /* 15.2.8.3.10 (x) */ mrb_define_method(mrb, integer, "round", int_to_i, MRB_ARGS_REQ(1)); /* 15.2.8.3.12 (x) */ mrb_define_method(mrb, integer, "truncate", int_to_i, MRB_ARGS_REQ(1)); /* 15.2.8.3.15 (x) */ #endif /* Fixnum Class */ mrb->fixnum_class = fixnum = mrb_define_class(mrb, "Fixnum", integer); mrb_define_method(mrb, fixnum, "+", fix_plus, MRB_ARGS_REQ(1)); /* 15.2.8.3.1 */ mrb_define_method(mrb, fixnum, "-", fix_minus, MRB_ARGS_REQ(1)); /* 15.2.8.3.2 */ mrb_define_method(mrb, fixnum, "*", fix_mul, MRB_ARGS_REQ(1)); /* 15.2.8.3.3 */ mrb_define_method(mrb, fixnum, "%", fix_mod, MRB_ARGS_REQ(1)); /* 15.2.8.3.5 */ mrb_define_method(mrb, fixnum, "==", fix_equal, MRB_ARGS_REQ(1)); /* 15.2.8.3.7 */ mrb_define_method(mrb, fixnum, "~", fix_rev, MRB_ARGS_NONE()); /* 15.2.8.3.8 */ mrb_define_method(mrb, fixnum, "&", fix_and, MRB_ARGS_REQ(1)); /* 15.2.8.3.9 */ mrb_define_method(mrb, fixnum, "|", fix_or, MRB_ARGS_REQ(1)); /* 15.2.8.3.10 */ mrb_define_method(mrb, fixnum, "^", fix_xor, MRB_ARGS_REQ(1)); /* 15.2.8.3.11 */ mrb_define_method(mrb, fixnum, "<<", fix_lshift, MRB_ARGS_REQ(1)); /* 15.2.8.3.12 */ mrb_define_method(mrb, fixnum, ">>", fix_rshift, MRB_ARGS_REQ(1)); /* 15.2.8.3.13 */ mrb_define_method(mrb, fixnum, "eql?", fix_eql, MRB_ARGS_REQ(1)); /* 15.2.8.3.16 */ #ifndef MRB_WITHOUT_FLOAT mrb_define_method(mrb, fixnum, "to_f", fix_to_f, MRB_ARGS_NONE()); /* 15.2.8.3.23 */ #endif mrb_define_method(mrb, fixnum, "to_s", fix_to_s, MRB_ARGS_NONE()); /* 15.2.8.3.25 */ mrb_define_method(mrb, fixnum, "inspect", fix_to_s, MRB_ARGS_NONE()); mrb_define_method(mrb, fixnum, "divmod", fix_divmod, MRB_ARGS_REQ(1)); /* 15.2.8.3.30 (x) */ #ifndef MRB_WITHOUT_FLOAT /* Float Class */ mrb->float_class = fl = mrb_define_class(mrb, "Float", numeric); /* 15.2.9 */ MRB_SET_INSTANCE_TT(fl, MRB_TT_FLOAT); mrb_undef_class_method(mrb, fl, "new"); mrb_define_method(mrb, fl, "+", flo_plus, MRB_ARGS_REQ(1)); /* 15.2.9.3.1 */ mrb_define_method(mrb, fl, "-", flo_minus, MRB_ARGS_REQ(1)); /* 15.2.9.3.2 */ mrb_define_method(mrb, fl, "*", flo_mul, MRB_ARGS_REQ(1)); /* 15.2.9.3.3 */ mrb_define_method(mrb, fl, "%", flo_mod, MRB_ARGS_REQ(1)); /* 15.2.9.3.5 */ mrb_define_method(mrb, fl, "==", flo_eq, MRB_ARGS_REQ(1)); /* 15.2.9.3.7 */ mrb_define_method(mrb, fl, "~", flo_rev, MRB_ARGS_NONE()); mrb_define_method(mrb, fl, "&", flo_and, MRB_ARGS_REQ(1)); mrb_define_method(mrb, fl, "|", flo_or, MRB_ARGS_REQ(1)); mrb_define_method(mrb, fl, "^", flo_xor, MRB_ARGS_REQ(1)); mrb_define_method(mrb, fl, ">>", flo_lshift, MRB_ARGS_REQ(1)); mrb_define_method(mrb, fl, "<<", flo_rshift, MRB_ARGS_REQ(1)); mrb_define_method(mrb, fl, "ceil", flo_ceil, MRB_ARGS_NONE()); /* 15.2.9.3.8 */ mrb_define_method(mrb, fl, "finite?", flo_finite_p, MRB_ARGS_NONE()); /* 15.2.9.3.9 */ mrb_define_method(mrb, fl, "floor", flo_floor, MRB_ARGS_NONE()); /* 15.2.9.3.10 */ mrb_define_method(mrb, fl, "infinite?", flo_infinite_p, MRB_ARGS_NONE()); /* 15.2.9.3.11 */ mrb_define_method(mrb, fl, "round", flo_round, MRB_ARGS_OPT(1)); /* 15.2.9.3.12 */ mrb_define_method(mrb, fl, "to_f", flo_to_f, MRB_ARGS_NONE()); /* 15.2.9.3.13 */ mrb_define_method(mrb, fl, "to_i", flo_truncate, MRB_ARGS_NONE()); /* 15.2.9.3.14 */ mrb_define_method(mrb, fl, "truncate", flo_truncate, MRB_ARGS_NONE()); /* 15.2.9.3.15 */ mrb_define_method(mrb, fl, "divmod", flo_divmod, MRB_ARGS_REQ(1)); mrb_define_method(mrb, fl, "eql?", flo_eql, MRB_ARGS_REQ(1)); /* 15.2.8.3.16 */ mrb_define_method(mrb, fl, "to_s", flo_to_s, MRB_ARGS_NONE()); /* 15.2.9.3.16(x) */ mrb_define_method(mrb, fl, "inspect", flo_to_s, MRB_ARGS_NONE()); mrb_define_method(mrb, fl, "nan?", flo_nan_p, MRB_ARGS_NONE()); #ifdef INFINITY mrb_define_const(mrb, fl, "INFINITY", mrb_float_value(mrb, INFINITY)); #endif #ifdef NAN mrb_define_const(mrb, fl, "NAN", mrb_float_value(mrb, NAN)); #endif #endif mrb_define_module(mrb, "Integral"); } mruby-2.0.0/src/object.c000066400000000000000000000400341340361412400150470ustar00rootroot00000000000000/* ** object.c - Object, NilClass, TrueClass, FalseClass class ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include MRB_API mrb_bool mrb_obj_eq(mrb_state *mrb, mrb_value v1, mrb_value v2) { if (mrb_type(v1) != mrb_type(v2)) return FALSE; switch (mrb_type(v1)) { case MRB_TT_TRUE: return TRUE; case MRB_TT_FALSE: case MRB_TT_FIXNUM: return (mrb_fixnum(v1) == mrb_fixnum(v2)); case MRB_TT_SYMBOL: return (mrb_symbol(v1) == mrb_symbol(v2)); #ifndef MRB_WITHOUT_FLOAT case MRB_TT_FLOAT: return (mrb_float(v1) == mrb_float(v2)); #endif default: return (mrb_ptr(v1) == mrb_ptr(v2)); } } MRB_API mrb_bool mrb_obj_equal(mrb_state *mrb, mrb_value v1, mrb_value v2) { /* temporary definition */ return mrb_obj_eq(mrb, v1, v2); } MRB_API mrb_bool mrb_equal(mrb_state *mrb, mrb_value obj1, mrb_value obj2) { mrb_value result; if (mrb_obj_eq(mrb, obj1, obj2)) return TRUE; result = mrb_funcall(mrb, obj1, "==", 1, obj2); if (mrb_test(result)) return TRUE; return FALSE; } /* * Document-class: NilClass * * The class of the singleton object nil. */ /* 15.2.4.3.4 */ /* * call_seq: * nil.nil? -> true * * Only the object nil responds true to nil?. */ static mrb_value mrb_true(mrb_state *mrb, mrb_value obj) { return mrb_true_value(); } /* 15.2.4.3.5 */ /* * call-seq: * nil.to_s -> "" * * Always returns the empty string. */ static mrb_value nil_to_s(mrb_state *mrb, mrb_value obj) { return mrb_str_new(mrb, 0, 0); } static mrb_value nil_inspect(mrb_state *mrb, mrb_value obj) { return mrb_str_new_lit(mrb, "nil"); } /*********************************************************************** * Document-class: TrueClass * * The global value true is the only instance of class * TrueClass and represents a logically true value in * boolean expressions. The class provides operators allowing * true to be used in logical expressions. */ /* 15.2.5.3.1 */ /* * call-seq: * true & obj -> true or false * * And---Returns false if obj is * nil or false, true otherwise. */ static mrb_value true_and(mrb_state *mrb, mrb_value obj) { mrb_bool obj2; mrb_get_args(mrb, "b", &obj2); return mrb_bool_value(obj2); } /* 15.2.5.3.2 */ /* * call-seq: * true ^ obj -> !obj * * Exclusive Or---Returns true if obj is * nil or false, false * otherwise. */ static mrb_value true_xor(mrb_state *mrb, mrb_value obj) { mrb_bool obj2; mrb_get_args(mrb, "b", &obj2); return mrb_bool_value(!obj2); } /* 15.2.5.3.3 */ /* * call-seq: * true.to_s -> "true" * * The string representation of true is "true". */ static mrb_value true_to_s(mrb_state *mrb, mrb_value obj) { return mrb_str_new_lit(mrb, "true"); } /* 15.2.5.3.4 */ /* * call-seq: * true | obj -> true * * Or---Returns true. As anObject is an argument to * a method call, it is always evaluated; there is no short-circuit * evaluation in this case. * * true | puts("or") * true || puts("logical or") * * produces: * * or */ static mrb_value true_or(mrb_state *mrb, mrb_value obj) { return mrb_true_value(); } /* * Document-class: FalseClass * * The global value false is the only instance of class * FalseClass and represents a logically false value in * boolean expressions. The class provides operators allowing * false to participate correctly in logical expressions. * */ /* 15.2.4.3.1 */ /* 15.2.6.3.1 */ /* * call-seq: * false & obj -> false * nil & obj -> false * * And---Returns false. obj is always * evaluated as it is the argument to a method call---there is no * short-circuit evaluation in this case. */ static mrb_value false_and(mrb_state *mrb, mrb_value obj) { return mrb_false_value(); } /* 15.2.4.3.2 */ /* 15.2.6.3.2 */ /* * call-seq: * false ^ obj -> true or false * nil ^ obj -> true or false * * Exclusive Or---If obj is nil or * false, returns false; otherwise, returns * true. * */ static mrb_value false_xor(mrb_state *mrb, mrb_value obj) { mrb_bool obj2; mrb_get_args(mrb, "b", &obj2); return mrb_bool_value(obj2); } /* 15.2.4.3.3 */ /* 15.2.6.3.4 */ /* * call-seq: * false | obj -> true or false * nil | obj -> true or false * * Or---Returns false if obj is * nil or false; true otherwise. */ static mrb_value false_or(mrb_state *mrb, mrb_value obj) { mrb_bool obj2; mrb_get_args(mrb, "b", &obj2); return mrb_bool_value(obj2); } /* 15.2.6.3.3 */ /* * call-seq: * false.to_s -> "false" * * 'nuf said... */ static mrb_value false_to_s(mrb_state *mrb, mrb_value obj) { return mrb_str_new_lit(mrb, "false"); } void mrb_init_object(mrb_state *mrb) { struct RClass *n; struct RClass *t; struct RClass *f; mrb->nil_class = n = mrb_define_class(mrb, "NilClass", mrb->object_class); MRB_SET_INSTANCE_TT(n, MRB_TT_TRUE); mrb_undef_class_method(mrb, n, "new"); mrb_define_method(mrb, n, "&", false_and, MRB_ARGS_REQ(1)); /* 15.2.4.3.1 */ mrb_define_method(mrb, n, "^", false_xor, MRB_ARGS_REQ(1)); /* 15.2.4.3.2 */ mrb_define_method(mrb, n, "|", false_or, MRB_ARGS_REQ(1)); /* 15.2.4.3.3 */ mrb_define_method(mrb, n, "nil?", mrb_true, MRB_ARGS_NONE()); /* 15.2.4.3.4 */ mrb_define_method(mrb, n, "to_s", nil_to_s, MRB_ARGS_NONE()); /* 15.2.4.3.5 */ mrb_define_method(mrb, n, "inspect", nil_inspect, MRB_ARGS_NONE()); mrb->true_class = t = mrb_define_class(mrb, "TrueClass", mrb->object_class); MRB_SET_INSTANCE_TT(t, MRB_TT_TRUE); mrb_undef_class_method(mrb, t, "new"); mrb_define_method(mrb, t, "&", true_and, MRB_ARGS_REQ(1)); /* 15.2.5.3.1 */ mrb_define_method(mrb, t, "^", true_xor, MRB_ARGS_REQ(1)); /* 15.2.5.3.2 */ mrb_define_method(mrb, t, "to_s", true_to_s, MRB_ARGS_NONE()); /* 15.2.5.3.3 */ mrb_define_method(mrb, t, "|", true_or, MRB_ARGS_REQ(1)); /* 15.2.5.3.4 */ mrb_define_method(mrb, t, "inspect", true_to_s, MRB_ARGS_NONE()); mrb->false_class = f = mrb_define_class(mrb, "FalseClass", mrb->object_class); MRB_SET_INSTANCE_TT(f, MRB_TT_TRUE); mrb_undef_class_method(mrb, f, "new"); mrb_define_method(mrb, f, "&", false_and, MRB_ARGS_REQ(1)); /* 15.2.6.3.1 */ mrb_define_method(mrb, f, "^", false_xor, MRB_ARGS_REQ(1)); /* 15.2.6.3.2 */ mrb_define_method(mrb, f, "to_s", false_to_s, MRB_ARGS_NONE()); /* 15.2.6.3.3 */ mrb_define_method(mrb, f, "|", false_or, MRB_ARGS_REQ(1)); /* 15.2.6.3.4 */ mrb_define_method(mrb, f, "inspect", false_to_s, MRB_ARGS_NONE()); } static mrb_value inspect_type(mrb_state *mrb, mrb_value val) { if (mrb_type(val) == MRB_TT_FALSE || mrb_type(val) == MRB_TT_TRUE) { return mrb_inspect(mrb, val); } else { return mrb_str_new_cstr(mrb, mrb_obj_classname(mrb, val)); } } static mrb_value convert_type(mrb_state *mrb, mrb_value val, const char *tname, const char *method, mrb_bool raise) { mrb_sym m = 0; m = mrb_intern_cstr(mrb, method); if (!mrb_respond_to(mrb, val, m)) { if (raise) { mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S into %S", inspect_type(mrb, val), mrb_str_new_cstr(mrb, tname)); } return mrb_nil_value(); } return mrb_funcall_argv(mrb, val, m, 0, 0); } MRB_API mrb_value mrb_convert_type(mrb_state *mrb, mrb_value val, enum mrb_vtype type, const char *tname, const char *method) { mrb_value v; if (mrb_type(val) == type) return val; v = convert_type(mrb, val, tname, method, TRUE); if (mrb_type(v) != type) { mrb_raisef(mrb, E_TYPE_ERROR, "%S cannot be converted to %S by #%S", val, mrb_str_new_cstr(mrb, tname), mrb_str_new_cstr(mrb, method)); } return v; } MRB_API mrb_value mrb_check_convert_type(mrb_state *mrb, mrb_value val, enum mrb_vtype type, const char *tname, const char *method) { mrb_value v; if (mrb_type(val) == type && type != MRB_TT_DATA && type != MRB_TT_ISTRUCT) return val; v = convert_type(mrb, val, tname, method, FALSE); if (mrb_nil_p(v) || mrb_type(v) != type) return mrb_nil_value(); return v; } static const struct types { unsigned char type; const char *name; } builtin_types[] = { /* {MRB_TT_NIL, "nil"}, */ {MRB_TT_FALSE, "false"}, {MRB_TT_TRUE, "true"}, {MRB_TT_FIXNUM, "Fixnum"}, {MRB_TT_SYMBOL, "Symbol"}, /* :symbol */ {MRB_TT_MODULE, "Module"}, {MRB_TT_OBJECT, "Object"}, {MRB_TT_CLASS, "Class"}, {MRB_TT_ICLASS, "iClass"}, /* internal use: mixed-in module holder */ {MRB_TT_SCLASS, "SClass"}, {MRB_TT_PROC, "Proc"}, #ifndef MRB_WITHOUT_FLOAT {MRB_TT_FLOAT, "Float"}, #endif {MRB_TT_ARRAY, "Array"}, {MRB_TT_HASH, "Hash"}, {MRB_TT_STRING, "String"}, {MRB_TT_RANGE, "Range"}, /* {MRB_TT_BIGNUM, "Bignum"}, */ {MRB_TT_FILE, "File"}, {MRB_TT_DATA, "Data"}, /* internal use: wrapped C pointers */ /* {MRB_TT_VARMAP, "Varmap"}, */ /* internal use: dynamic variables */ /* {MRB_TT_NODE, "Node"}, */ /* internal use: syntax tree node */ /* {MRB_TT_UNDEF, "undef"}, */ /* internal use: #undef; should not happen */ {MRB_TT_MAXDEFINE, 0} }; MRB_API void mrb_check_type(mrb_state *mrb, mrb_value x, enum mrb_vtype t) { const struct types *type = builtin_types; enum mrb_vtype xt; xt = mrb_type(x); if ((xt != t) || (xt == MRB_TT_DATA) || (xt == MRB_TT_ISTRUCT)) { while (type->type < MRB_TT_MAXDEFINE) { if (type->type == t) { const char *etype; if (mrb_nil_p(x)) { etype = "nil"; } else if (mrb_fixnum_p(x)) { etype = "Fixnum"; } else if (mrb_type(x) == MRB_TT_SYMBOL) { etype = "Symbol"; } else if (mrb_immediate_p(x)) { etype = RSTRING_PTR(mrb_obj_as_string(mrb, x)); } else { etype = mrb_obj_classname(mrb, x); } mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %S (expected %S)", mrb_str_new_cstr(mrb, etype), mrb_str_new_cstr(mrb, type->name)); } type++; } mrb_raisef(mrb, E_TYPE_ERROR, "unknown type %S (%S given)", mrb_fixnum_value(t), mrb_fixnum_value(mrb_type(x))); } } /* 15.3.1.3.46 */ /* * call-seq: * obj.to_s => string * * 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." */ MRB_API mrb_value mrb_any_to_s(mrb_state *mrb, mrb_value obj) { mrb_value str = mrb_str_new_capa(mrb, 20); const char *cname = mrb_obj_classname(mrb, obj); mrb_str_cat_lit(mrb, str, "#<"); mrb_str_cat_cstr(mrb, str, cname); mrb_str_cat_lit(mrb, str, ":"); mrb_str_concat(mrb, str, mrb_ptr_to_str(mrb, mrb_ptr(obj))); mrb_str_cat_lit(mrb, str, ">"); return str; } /* * call-seq: * obj.is_a?(class) => true or false * obj.kind_of?(class) => true or false * * Returns true if class is the class of * obj, or if class is one of the superclasses of * obj or modules included in obj. * * module M; end * class A * include M * end * class B < A; end * class C < B; end * b = B.new * b.instance_of? A #=> false * b.instance_of? B #=> true * b.instance_of? C #=> false * b.instance_of? M #=> false * b.kind_of? A #=> true * b.kind_of? B #=> true * b.kind_of? C #=> false * b.kind_of? M #=> true */ MRB_API mrb_bool mrb_obj_is_kind_of(mrb_state *mrb, mrb_value obj, struct RClass *c) { struct RClass *cl = mrb_class(mrb, obj); switch (c->tt) { case MRB_TT_MODULE: case MRB_TT_CLASS: case MRB_TT_ICLASS: case MRB_TT_SCLASS: break; default: mrb_raise(mrb, E_TYPE_ERROR, "class or module required"); } MRB_CLASS_ORIGIN(c); while (cl) { if (cl == c || cl->mt == c->mt) return TRUE; cl = cl->super; } return FALSE; } MRB_API mrb_value mrb_to_int(mrb_state *mrb, mrb_value val) { if (!mrb_fixnum_p(val)) { mrb_value type; #ifndef MRB_WITHOUT_FLOAT if (mrb_float_p(val)) { return mrb_flo_to_fixnum(mrb, val); } #endif type = inspect_type(mrb, val); mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S to Integer", type); } return val; } MRB_API mrb_value mrb_convert_to_integer(mrb_state *mrb, mrb_value val, mrb_int base) { mrb_value tmp; if (mrb_nil_p(val)) { if (base != 0) goto arg_error; mrb_raise(mrb, E_TYPE_ERROR, "can't convert nil into Integer"); } switch (mrb_type(val)) { #ifndef MRB_WITHOUT_FLOAT case MRB_TT_FLOAT: if (base != 0) goto arg_error; return mrb_flo_to_fixnum(mrb, val); #endif case MRB_TT_FIXNUM: if (base != 0) goto arg_error; return val; case MRB_TT_STRING: string_conv: return mrb_str_to_inum(mrb, val, base, TRUE); default: break; } if (base != 0) { tmp = mrb_check_string_type(mrb, val); if (!mrb_nil_p(tmp)) { val = tmp; goto string_conv; } arg_error: mrb_raise(mrb, E_ARGUMENT_ERROR, "base specified for non string value"); } /* to raise TypeError */ return mrb_to_int(mrb, val); } MRB_API mrb_value mrb_Integer(mrb_state *mrb, mrb_value val) { return mrb_convert_to_integer(mrb, val, 0); } #ifndef MRB_WITHOUT_FLOAT MRB_API mrb_value mrb_Float(mrb_state *mrb, mrb_value val) { if (mrb_nil_p(val)) { mrb_raise(mrb, E_TYPE_ERROR, "can't convert nil into Float"); } switch (mrb_type(val)) { case MRB_TT_FIXNUM: return mrb_float_value(mrb, (mrb_float)mrb_fixnum(val)); case MRB_TT_FLOAT: return val; case MRB_TT_STRING: return mrb_float_value(mrb, mrb_str_to_dbl(mrb, val, TRUE)); default: return mrb_convert_type(mrb, val, MRB_TT_FLOAT, "Float", "to_f"); } } #endif MRB_API mrb_value mrb_to_str(mrb_state *mrb, mrb_value val) { if (!mrb_string_p(val)) { mrb_value type = inspect_type(mrb, val); mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S to String", type); } return val; } /* obsolete: use mrb_ensure_string_type() instead */ MRB_API mrb_value mrb_string_type(mrb_state *mrb, mrb_value str) { return mrb_ensure_string_type(mrb, str); } MRB_API mrb_value mrb_ensure_string_type(mrb_state *mrb, mrb_value str) { if (!mrb_string_p(str)) { mrb_raisef(mrb, E_TYPE_ERROR, "%S cannot be converted to String", inspect_type(mrb, str)); } return str; } MRB_API mrb_value mrb_check_string_type(mrb_state *mrb, mrb_value str) { if (!mrb_string_p(str)) return mrb_nil_value(); return str; } MRB_API mrb_value mrb_ensure_array_type(mrb_state *mrb, mrb_value ary) { if (!mrb_array_p(ary)) { mrb_raisef(mrb, E_TYPE_ERROR, "%S cannot be converted to Array", inspect_type(mrb, ary)); } return ary; } MRB_API mrb_value mrb_check_array_type(mrb_state *mrb, mrb_value ary) { if (!mrb_array_p(ary)) return mrb_nil_value(); return ary; } MRB_API mrb_value mrb_ensure_hash_type(mrb_state *mrb, mrb_value hash) { if (!mrb_hash_p(hash)) { mrb_raisef(mrb, E_TYPE_ERROR, "%S cannot be converted to Hash", inspect_type(mrb, hash)); } return hash; } MRB_API mrb_value mrb_check_hash_type(mrb_state *mrb, mrb_value hash) { if (!mrb_hash_p(hash)) return mrb_nil_value(); return hash; } MRB_API mrb_value mrb_inspect(mrb_state *mrb, mrb_value obj) { return mrb_obj_as_string(mrb, mrb_funcall(mrb, obj, "inspect", 0)); } MRB_API mrb_bool mrb_eql(mrb_state *mrb, mrb_value obj1, mrb_value obj2) { if (mrb_obj_eq(mrb, obj1, obj2)) return TRUE; return mrb_test(mrb_funcall(mrb, obj1, "eql?", 1, obj2)); } mruby-2.0.0/src/opcode.h000066400000000000000000000001101340361412400150460ustar00rootroot00000000000000/* this header file is to be removed soon. */ #include mruby-2.0.0/src/pool.c000066400000000000000000000077011340361412400145560ustar00rootroot00000000000000/* ** pool.c - memory pool ** ** See Copyright Notice in mruby.h */ #include #include #include #include /* configuration section */ /* allocated memory address should be multiple of POOL_ALIGNMENT */ /* or undef it if alignment does not matter */ #ifndef POOL_ALIGNMENT #if INTPTR_MAX == INT64_MAX #define POOL_ALIGNMENT 8 #else #define POOL_ALIGNMENT 4 #endif #endif /* page size of memory pool */ #ifndef POOL_PAGE_SIZE #define POOL_PAGE_SIZE 16000 #endif /* end of configuration section */ /* Disable MSVC warning "C4200: nonstandard extension used: zero-sized array * in struct/union" when in C++ mode */ #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4200) #endif struct mrb_pool_page { struct mrb_pool_page *next; size_t offset; size_t len; void *last; char page[]; }; #ifdef _MSC_VER #pragma warning(pop) #endif struct mrb_pool { mrb_state *mrb; struct mrb_pool_page *pages; }; #undef TEST_POOL #ifdef TEST_POOL #define mrb_malloc_simple(m,s) malloc(s) #define mrb_free(m,p) free(p) #endif #ifdef POOL_ALIGNMENT # define ALIGN_PADDING(x) ((SIZE_MAX - (x) + 1) & (POOL_ALIGNMENT - 1)) #else # define ALIGN_PADDING(x) (0) #endif MRB_API mrb_pool* mrb_pool_open(mrb_state *mrb) { mrb_pool *pool = (mrb_pool *)mrb_malloc_simple(mrb, sizeof(mrb_pool)); if (pool) { pool->mrb = mrb; pool->pages = NULL; } return pool; } MRB_API void mrb_pool_close(mrb_pool *pool) { struct mrb_pool_page *page, *tmp; if (!pool) return; page = pool->pages; while (page) { tmp = page; page = page->next; mrb_free(pool->mrb, tmp); } mrb_free(pool->mrb, pool); } static struct mrb_pool_page* page_alloc(mrb_pool *pool, size_t len) { struct mrb_pool_page *page; if (len < POOL_PAGE_SIZE) len = POOL_PAGE_SIZE; page = (struct mrb_pool_page *)mrb_malloc_simple(pool->mrb, sizeof(struct mrb_pool_page)+len); if (page) { page->offset = 0; page->len = len; } return page; } MRB_API void* mrb_pool_alloc(mrb_pool *pool, size_t len) { struct mrb_pool_page *page; size_t n; if (!pool) return NULL; len += ALIGN_PADDING(len); page = pool->pages; while (page) { if (page->offset + len <= page->len) { n = page->offset; page->offset += len; page->last = (char*)page->page+n; return page->last; } page = page->next; } page = page_alloc(pool, len); if (!page) return NULL; page->offset = len; page->next = pool->pages; pool->pages = page; page->last = (void*)page->page; return page->last; } MRB_API mrb_bool mrb_pool_can_realloc(mrb_pool *pool, void *p, size_t len) { struct mrb_pool_page *page; if (!pool) return FALSE; len += ALIGN_PADDING(len); page = pool->pages; while (page) { if (page->last == p) { size_t beg; beg = (char*)p - page->page; if (beg + len > page->len) return FALSE; return TRUE; } page = page->next; } return FALSE; } MRB_API void* mrb_pool_realloc(mrb_pool *pool, void *p, size_t oldlen, size_t newlen) { struct mrb_pool_page *page; void *np; if (!pool) return NULL; oldlen += ALIGN_PADDING(oldlen); newlen += ALIGN_PADDING(newlen); page = pool->pages; while (page) { if (page->last == p) { size_t beg; beg = (char*)p - page->page; if (beg + oldlen != page->offset) break; if (beg + newlen > page->len) { page->offset = beg; break; } page->offset = beg + newlen; return p; } page = page->next; } np = mrb_pool_alloc(pool, newlen); if (np == NULL) { return NULL; } memcpy(np, p, oldlen); return np; } #ifdef TEST_POOL int main(void) { int i, len = 250; mrb_pool *pool; void *p; pool = mrb_pool_open(NULL); p = mrb_pool_alloc(pool, len); for (i=1; i<20; i++) { printf("%p (len=%d) %ud\n", p, len, mrb_pool_can_realloc(pool, p, len*2)); p = mrb_pool_realloc(pool, p, len, len*2); len *= 2; } mrb_pool_close(pool); return 0; } #endif mruby-2.0.0/src/print.c000066400000000000000000000017171340361412400147420ustar00rootroot00000000000000/* ** print.c - Kernel.#p ** ** See Copyright Notice in mruby.h */ #include #include #include #ifndef MRB_DISABLE_STDIO static void printstr(mrb_value obj, FILE *stream) { if (mrb_string_p(obj)) { fwrite(RSTRING_PTR(obj), RSTRING_LEN(obj), 1, stream); putc('\n', stream); } } #else # define printstr(obj, stream) (void)0 #endif MRB_API void mrb_p(mrb_state *mrb, mrb_value obj) { printstr(mrb_inspect(mrb, obj), stdout); } MRB_API void mrb_print_error(mrb_state *mrb) { mrb_print_backtrace(mrb); printstr(mrb_funcall(mrb, mrb_obj_value(mrb->exc), "inspect", 0), stderr); } MRB_API void mrb_show_version(mrb_state *mrb) { printstr(mrb_const_get(mrb, mrb_obj_value(mrb->object_class), mrb_intern_lit(mrb, "MRUBY_DESCRIPTION")), stdout); } MRB_API void mrb_show_copyright(mrb_state *mrb) { printstr(mrb_const_get(mrb, mrb_obj_value(mrb->object_class), mrb_intern_lit(mrb, "MRUBY_COPYRIGHT")), stdout); } mruby-2.0.0/src/proc.c000066400000000000000000000167211340361412400145520ustar00rootroot00000000000000/* ** proc.c - Proc class ** ** See Copyright Notice in mruby.h */ #include #include #include #include static mrb_code call_iseq[] = { OP_CALL, }; struct RProc* mrb_proc_new(mrb_state *mrb, mrb_irep *irep) { struct RProc *p; mrb_callinfo *ci = mrb->c->ci; p = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb->proc_class); if (ci) { struct RClass *tc = NULL; if (ci->proc) { tc = MRB_PROC_TARGET_CLASS(ci->proc); } if (tc == NULL) { tc = ci->target_class; } p->upper = ci->proc; p->e.target_class = tc; } p->body.irep = irep; mrb_irep_incref(mrb, irep); return p; } static struct REnv* env_new(mrb_state *mrb, mrb_int nlocals) { struct REnv *e; mrb_callinfo *ci = mrb->c->ci; int bidx; e = (struct REnv*)mrb_obj_alloc(mrb, MRB_TT_ENV, NULL); MRB_ENV_SET_STACK_LEN(e, nlocals); bidx = ci->argc; if (ci->argc < 0) bidx = 2; else bidx += 1; MRB_ENV_SET_BIDX(e, bidx); e->mid = ci->mid; e->stack = mrb->c->stack; e->cxt = mrb->c; return e; } static void closure_setup(mrb_state *mrb, struct RProc *p) { mrb_callinfo *ci = mrb->c->ci; struct RProc *up = p->upper; struct REnv *e = NULL; if (ci && ci->env) { e = ci->env; } else if (up) { struct RClass *tc = MRB_PROC_TARGET_CLASS(p); e = env_new(mrb, up->body.irep->nlocals); ci->env = e; if (tc) { e->c = tc; mrb_field_write_barrier(mrb, (struct RBasic*)e, (struct RBasic*)tc); } } if (e) { p->e.env = e; p->flags |= MRB_PROC_ENVSET; mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)e); } } struct RProc* mrb_closure_new(mrb_state *mrb, mrb_irep *irep) { struct RProc *p = mrb_proc_new(mrb, irep); closure_setup(mrb, p); return p; } MRB_API struct RProc* mrb_proc_new_cfunc(mrb_state *mrb, mrb_func_t func) { struct RProc *p; p = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb->proc_class); p->body.func = func; p->flags |= MRB_PROC_CFUNC_FL; p->upper = 0; p->e.target_class = 0; return p; } MRB_API struct RProc* mrb_proc_new_cfunc_with_env(mrb_state *mrb, mrb_func_t func, mrb_int argc, const mrb_value *argv) { struct RProc *p = mrb_proc_new_cfunc(mrb, func); struct REnv *e; int i; p->e.env = e = env_new(mrb, argc); p->flags |= MRB_PROC_ENVSET; mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)e); MRB_ENV_UNSHARE_STACK(e); e->stack = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value) * argc); if (argv) { for (i = 0; i < argc; ++i) { e->stack[i] = argv[i]; } } else { for (i = 0; i < argc; ++i) { SET_NIL_VALUE(e->stack[i]); } } return p; } MRB_API struct RProc* mrb_closure_new_cfunc(mrb_state *mrb, mrb_func_t func, int nlocals) { return mrb_proc_new_cfunc_with_env(mrb, func, nlocals, NULL); } MRB_API mrb_value mrb_proc_cfunc_env_get(mrb_state *mrb, mrb_int idx) { struct RProc *p = mrb->c->ci->proc; struct REnv *e; if (!p || !MRB_PROC_CFUNC_P(p)) { mrb_raise(mrb, E_TYPE_ERROR, "Can't get cfunc env from non-cfunc proc."); } e = MRB_PROC_ENV(p); if (!e) { mrb_raise(mrb, E_TYPE_ERROR, "Can't get cfunc env from cfunc Proc without REnv."); } if (idx < 0 || MRB_ENV_STACK_LEN(e) <= idx) { mrb_raisef(mrb, E_INDEX_ERROR, "Env index out of range: %S (expected: 0 <= index < %S)", mrb_fixnum_value(idx), mrb_fixnum_value(MRB_ENV_STACK_LEN(e))); } return e->stack[idx]; } void mrb_proc_copy(struct RProc *a, struct RProc *b) { if (a->body.irep) { /* already initialized proc */ return; } a->flags = b->flags; a->body = b->body; if (!MRB_PROC_CFUNC_P(a) && a->body.irep) { a->body.irep->refcnt++; } a->upper = b->upper; a->e.env = b->e.env; /* a->e.target_class = a->e.target_class; */ } static mrb_value mrb_proc_s_new(mrb_state *mrb, mrb_value proc_class) { mrb_value blk; mrb_value proc; struct RProc *p; mrb_get_args(mrb, "&", &blk); if (mrb_nil_p(blk)) { /* Calling Proc.new without a block is not implemented yet */ mrb_raise(mrb, E_ARGUMENT_ERROR, "tried to create Proc object without a block"); } p = (struct RProc *)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb_class_ptr(proc_class)); mrb_proc_copy(p, mrb_proc_ptr(blk)); proc = mrb_obj_value(p); mrb_funcall_with_block(mrb, proc, mrb_intern_lit(mrb, "initialize"), 0, NULL, proc); if (!MRB_PROC_STRICT_P(p) && mrb->c->ci > mrb->c->cibase && MRB_PROC_ENV(p) == mrb->c->ci[-1].env) { p->flags |= MRB_PROC_ORPHAN; } return proc; } static mrb_value mrb_proc_init_copy(mrb_state *mrb, mrb_value self) { mrb_value proc; mrb_get_args(mrb, "o", &proc); if (mrb_type(proc) != MRB_TT_PROC) { mrb_raise(mrb, E_ARGUMENT_ERROR, "not a proc"); } mrb_proc_copy(mrb_proc_ptr(self), mrb_proc_ptr(proc)); return self; } int mrb_proc_cfunc_p(struct RProc *p) { return MRB_PROC_CFUNC_P(p); } /* 15.2.17.4.2 */ static mrb_value mrb_proc_arity(mrb_state *mrb, mrb_value self) { struct RProc *p = mrb_proc_ptr(self); struct mrb_irep *irep; mrb_code *pc; mrb_aspec aspec; int ma, op, ra, pa, arity; if (MRB_PROC_CFUNC_P(p)) { /* TODO cfunc aspec not implemented yet */ return mrb_fixnum_value(-1); } irep = p->body.irep; if (!irep) { return mrb_fixnum_value(0); } pc = irep->iseq; /* arity is depend on OP_ENTER */ if (*pc != OP_ENTER) { return mrb_fixnum_value(0); } aspec = PEEK_W(pc+1); 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) && op) ? -(ma + pa + 1) : ma + pa; return mrb_fixnum_value(arity); } /* 15.3.1.2.6 */ /* 15.3.1.3.27 */ /* * call-seq: * lambda { |...| block } -> a_proc * * Equivalent to Proc.new, except the resulting Proc objects * check the number of parameters passed when called. */ static mrb_value proc_lambda(mrb_state *mrb, mrb_value self) { mrb_value blk; struct RProc *p; mrb_get_args(mrb, "&", &blk); if (mrb_nil_p(blk)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "tried to create Proc object without a block"); } if (mrb_type(blk) != MRB_TT_PROC) { mrb_raise(mrb, E_ARGUMENT_ERROR, "not a proc"); } p = mrb_proc_ptr(blk); if (!MRB_PROC_STRICT_P(p)) { struct RProc *p2 = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, p->c); mrb_proc_copy(p2, p); p2->flags |= MRB_PROC_STRICT; return mrb_obj_value(p2); } return blk; } void mrb_init_proc(mrb_state *mrb) { struct RProc *p; mrb_method_t m; mrb_irep *call_irep = (mrb_irep *)mrb_malloc(mrb, sizeof(mrb_irep)); static const mrb_irep mrb_irep_zero = { 0 }; *call_irep = mrb_irep_zero; call_irep->flags = MRB_ISEQ_NO_FREE; call_irep->iseq = call_iseq; call_irep->ilen = 1; call_irep->nregs = 2; /* receiver and block */ mrb_define_class_method(mrb, mrb->proc_class, "new", mrb_proc_s_new, MRB_ARGS_ANY()); mrb_define_method(mrb, mrb->proc_class, "initialize_copy", mrb_proc_init_copy, MRB_ARGS_REQ(1)); mrb_define_method(mrb, mrb->proc_class, "arity", mrb_proc_arity, MRB_ARGS_NONE()); p = mrb_proc_new(mrb, call_irep); MRB_METHOD_FROM_PROC(m, p); mrb_define_method_raw(mrb, mrb->proc_class, mrb_intern_lit(mrb, "call"), m); mrb_define_method_raw(mrb, mrb->proc_class, mrb_intern_lit(mrb, "[]"), m); mrb_define_class_method(mrb, mrb->kernel_module, "lambda", proc_lambda, MRB_ARGS_NONE()); /* 15.3.1.2.6 */ mrb_define_method(mrb, mrb->kernel_module, "lambda", proc_lambda, MRB_ARGS_NONE()); /* 15.3.1.3.27 */ } mruby-2.0.0/src/range.c000066400000000000000000000270551340361412400147050ustar00rootroot00000000000000/* ** range.c - Range class ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include MRB_API struct RRange* mrb_range_ptr(mrb_state *mrb, mrb_value v) { struct RRange *r = (struct RRange*)mrb_ptr(v); if (r->edges == NULL) { mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized range"); } return r; } static void range_check(mrb_state *mrb, mrb_value a, mrb_value b) { mrb_value ans; enum mrb_vtype ta; enum mrb_vtype tb; ta = mrb_type(a); tb = mrb_type(b); #ifdef MRB_WITHOUT_FLOAT if (ta == MRB_TT_FIXNUM && tb == MRB_TT_FIXNUM ) { #else if ((ta == MRB_TT_FIXNUM || ta == MRB_TT_FLOAT) && (tb == MRB_TT_FIXNUM || tb == MRB_TT_FLOAT)) { #endif return; } ans = mrb_funcall(mrb, a, "<=>", 1, b); if (mrb_nil_p(ans)) { /* can not be compared */ mrb_raise(mrb, E_ARGUMENT_ERROR, "bad value for range"); } } MRB_API mrb_value mrb_range_new(mrb_state *mrb, mrb_value beg, mrb_value end, mrb_bool excl) { struct RRange *r; range_check(mrb, beg, end); r = (struct RRange*)mrb_obj_alloc(mrb, MRB_TT_RANGE, mrb->range_class); r->edges = (mrb_range_edges *)mrb_malloc(mrb, sizeof(mrb_range_edges)); r->edges->beg = beg; r->edges->end = end; r->excl = excl; return mrb_range_value(r); } /* * call-seq: * rng.first => obj * rng.begin => obj * * Returns the first object in rng. */ mrb_value mrb_range_beg(mrb_state *mrb, mrb_value range) { struct RRange *r = mrb_range_ptr(mrb, range); return r->edges->beg; } /* * call-seq: * rng.end => obj * rng.last => obj * * Returns the object that defines the end of rng. * * (1..10).end #=> 10 * (1...10).end #=> 10 */ mrb_value mrb_range_end(mrb_state *mrb, mrb_value range) { struct RRange *r = mrb_range_ptr(mrb, range); return r->edges->end; } /* * call-seq: * range.exclude_end? => true or false * * Returns true if range excludes its end value. */ mrb_value mrb_range_excl(mrb_state *mrb, mrb_value range) { struct RRange *r = mrb_range_ptr(mrb, range); return mrb_bool_value(r->excl); } static void range_init(mrb_state *mrb, mrb_value range, mrb_value beg, mrb_value end, mrb_bool exclude_end) { struct RRange *r = mrb_range_raw_ptr(range); range_check(mrb, beg, end); r->excl = exclude_end; if (!r->edges) { r->edges = (mrb_range_edges *)mrb_malloc(mrb, sizeof(mrb_range_edges)); } r->edges->beg = beg; r->edges->end = end; mrb_write_barrier(mrb, (struct RBasic*)r); } /* * call-seq: * Range.new(start, end, exclusive=false) => range * * Constructs a range using the given start and end. If the third * parameter is omitted or is false, the range will include * the end object; otherwise, it will be excluded. */ mrb_value mrb_range_initialize(mrb_state *mrb, mrb_value range) { mrb_value beg, end; mrb_bool exclusive; mrb_int n; n = mrb_get_args(mrb, "oo|b", &beg, &end, &exclusive); if (n != 3) { exclusive = FALSE; } /* Ranges are immutable, so that they should be initialized only once. */ if (mrb_range_raw_ptr(range)->edges) { mrb_name_error(mrb, mrb_intern_lit(mrb, "initialize"), "`initialize' called twice"); } range_init(mrb, range, beg, end, exclusive); return range; } /* * call-seq: * range == obj => true or false * * Returns true only if * 1) obj is a Range, * 2) obj has equivalent beginning and end items (by comparing them with ==), * 3) obj has the same #exclude_end? setting as rng. * * (0..2) == (0..2) #=> true * (0..2) == Range.new(0,2) #=> true * (0..2) == (0...2) #=> false * */ mrb_value mrb_range_eq(mrb_state *mrb, mrb_value range) { struct RRange *rr; struct RRange *ro; mrb_value obj, v1, v2; mrb_get_args(mrb, "o", &obj); if (mrb_obj_equal(mrb, range, obj)) return mrb_true_value(); if (!mrb_obj_is_instance_of(mrb, obj, mrb_obj_class(mrb, range))) { /* same class? */ return mrb_false_value(); } rr = mrb_range_ptr(mrb, range); ro = mrb_range_ptr(mrb, obj); v1 = mrb_funcall(mrb, rr->edges->beg, "==", 1, ro->edges->beg); v2 = mrb_funcall(mrb, rr->edges->end, "==", 1, ro->edges->end); if (!mrb_bool(v1) || !mrb_bool(v2) || rr->excl != ro->excl) { return mrb_false_value(); } return mrb_true_value(); } static mrb_bool r_le(mrb_state *mrb, mrb_value a, mrb_value b) { mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b); /* compare result */ /* output :a < b => -1, a = b => 0, a > b => +1 */ if (mrb_fixnum_p(r)) { mrb_int c = mrb_fixnum(r); if (c == 0 || c == -1) return TRUE; } return FALSE; } static mrb_bool r_gt(mrb_state *mrb, mrb_value a, mrb_value b) { mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b); /* output :a < b => -1, a = b => 0, a > b => +1 */ return mrb_fixnum_p(r) && mrb_fixnum(r) == 1; } static mrb_bool r_ge(mrb_state *mrb, mrb_value a, mrb_value b) { mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b); /* compare result */ /* output :a < b => -1, a = b => 0, a > b => +1 */ if (mrb_fixnum_p(r)) { mrb_int c = mrb_fixnum(r); if (c == 0 || c == 1) return TRUE; } return FALSE; } /* * call-seq: * range === obj => true or false * range.member?(val) => true or false * range.include?(val) => true or false * */ mrb_value mrb_range_include(mrb_state *mrb, mrb_value range) { mrb_value val; struct RRange *r = mrb_range_ptr(mrb, range); mrb_value beg, end; mrb_bool include_p; mrb_get_args(mrb, "o", &val); beg = r->edges->beg; end = r->edges->end; include_p = r_le(mrb, beg, val) && /* beg <= val */ (r->excl ? r_gt(mrb, end, val) /* end > val */ : r_ge(mrb, end, val)); /* end >= val */ return mrb_bool_value(include_p); } MRB_API mrb_int mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len, mrb_bool trunc) { mrb_int beg, end; struct RRange *r; if (mrb_type(range) != MRB_TT_RANGE) return 0; r = mrb_range_ptr(mrb, range); beg = mrb_int(mrb, r->edges->beg); end = mrb_int(mrb, r->edges->end); if (beg < 0) { beg += len; if (beg < 0) return 2; } if (trunc) { if (beg > len) return 2; if (end > len) end = len; } if (end < 0) end += len; if (!r->excl && (!trunc || end < len)) end++; /* include end point */ len = end - beg; if (len < 0) len = 0; *begp = beg; *lenp = len; return 1; } /* 15.2.14.4.12(x) */ /* * call-seq: * rng.to_s -> string * * Convert this range object to a printable form. */ static mrb_value range_to_s(mrb_state *mrb, mrb_value range) { mrb_value str, str2; struct RRange *r = mrb_range_ptr(mrb, range); str = mrb_obj_as_string(mrb, r->edges->beg); 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_cat_str(mrb, str, str2); return str; } /* 15.2.14.4.13(x) */ /* * call-seq: * rng.inspect -> string * * Convert this range object to a printable form (using * inspect to convert the start and end * objects). */ static mrb_value range_inspect(mrb_state *mrb, mrb_value range) { mrb_value str, str2; struct RRange *r = mrb_range_ptr(mrb, range); str = mrb_inspect(mrb, r->edges->beg); str2 = mrb_inspect(mrb, r->edges->end); str = mrb_str_dup(mrb, str); mrb_str_cat(mrb, str, "...", r->excl ? 3 : 2); mrb_str_cat_str(mrb, str, str2); return str; } /* 15.2.14.4.14(x) */ /* * call-seq: * rng.eql?(obj) -> true or false * * Returns true only if obj is a Range, has equivalent * beginning and end items (by comparing them with #eql?), and has the same * #exclude_end? setting as rng. * * (0..2).eql?(0..2) #=> true * (0..2).eql?(Range.new(0,2)) #=> true * (0..2).eql?(0...2) #=> false * */ static mrb_value range_eql(mrb_state *mrb, mrb_value range) { mrb_value obj; struct RRange *r, *o; mrb_get_args(mrb, "o", &obj); if (mrb_obj_equal(mrb, range, obj)) return mrb_true_value(); if (!mrb_obj_is_kind_of(mrb, obj, mrb->range_class)) { return mrb_false_value(); } if (mrb_type(obj) != MRB_TT_RANGE) return mrb_false_value(); r = mrb_range_ptr(mrb, range); o = mrb_range_ptr(mrb, obj); if (!mrb_eql(mrb, r->edges->beg, o->edges->beg) || !mrb_eql(mrb, r->edges->end, o->edges->end) || (r->excl != o->excl)) { return mrb_false_value(); } return mrb_true_value(); } /* 15.2.14.4.15(x) */ static mrb_value range_initialize_copy(mrb_state *mrb, mrb_value copy) { mrb_value src; struct RRange *r; mrb_get_args(mrb, "o", &src); if (mrb_obj_equal(mrb, copy, src)) return copy; if (!mrb_obj_is_instance_of(mrb, src, mrb_obj_class(mrb, copy))) { mrb_raise(mrb, E_TYPE_ERROR, "wrong argument class"); } r = mrb_range_ptr(mrb, src); range_init(mrb, copy, r->edges->beg, r->edges->end, r->excl); return copy; } mrb_value mrb_get_values_at(mrb_state *mrb, mrb_value obj, mrb_int olen, mrb_int argc, const mrb_value *argv, mrb_value (*func)(mrb_state*, mrb_value, mrb_int)) { mrb_int i, j, beg, len; mrb_value result; result = mrb_ary_new(mrb); for (i = 0; i < argc; ++i) { if (mrb_fixnum_p(argv[i])) { mrb_ary_push(mrb, result, func(mrb, obj, mrb_fixnum(argv[i]))); } else if (mrb_range_beg_len(mrb, argv[i], &beg, &len, olen, FALSE) == 1) { mrb_int const end = olen < beg + len ? olen : beg + len; for (j = beg; j < end; ++j) { mrb_ary_push(mrb, result, func(mrb, obj, j)); } for (; j < beg + len; ++j) { mrb_ary_push(mrb, result, mrb_nil_value()); } } else { mrb_raisef(mrb, E_TYPE_ERROR, "invalid values selector: %S", argv[i]); } } return result; } void mrb_init_range(mrb_state *mrb) { struct RClass *r; r = mrb_define_class(mrb, "Range", mrb->object_class); /* 15.2.14 */ mrb->range_class = r; MRB_SET_INSTANCE_TT(r, MRB_TT_RANGE); mrb_define_method(mrb, r, "begin", mrb_range_beg, MRB_ARGS_NONE()); /* 15.2.14.4.3 */ mrb_define_method(mrb, r, "end", mrb_range_end, MRB_ARGS_NONE()); /* 15.2.14.4.5 */ mrb_define_method(mrb, r, "==", mrb_range_eq, MRB_ARGS_REQ(1)); /* 15.2.14.4.1 */ mrb_define_method(mrb, r, "===", mrb_range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.2 */ mrb_define_method(mrb, r, "exclude_end?", mrb_range_excl, MRB_ARGS_NONE()); /* 15.2.14.4.6 */ mrb_define_method(mrb, r, "first", mrb_range_beg, MRB_ARGS_NONE()); /* 15.2.14.4.7 */ mrb_define_method(mrb, r, "include?", mrb_range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.8 */ mrb_define_method(mrb, r, "initialize", mrb_range_initialize, MRB_ARGS_ANY()); /* 15.2.14.4.9 */ mrb_define_method(mrb, r, "last", mrb_range_end, MRB_ARGS_NONE()); /* 15.2.14.4.10 */ mrb_define_method(mrb, r, "member?", mrb_range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.11 */ mrb_define_method(mrb, r, "to_s", range_to_s, MRB_ARGS_NONE()); /* 15.2.14.4.12(x) */ mrb_define_method(mrb, r, "inspect", range_inspect, MRB_ARGS_NONE()); /* 15.2.14.4.13(x) */ mrb_define_method(mrb, r, "eql?", range_eql, MRB_ARGS_REQ(1)); /* 15.2.14.4.14(x) */ mrb_define_method(mrb, r, "initialize_copy", range_initialize_copy, MRB_ARGS_REQ(1)); /* 15.2.14.4.15(x) */ } mruby-2.0.0/src/state.c000066400000000000000000000141101340361412400147150ustar00rootroot00000000000000/* ** state.c - mrb_state open/close functions ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #include void mrb_init_core(mrb_state*); void mrb_init_mrbgems(mrb_state*); void mrb_gc_init(mrb_state*, mrb_gc *gc); void mrb_gc_destroy(mrb_state*, mrb_gc *gc); MRB_API mrb_state* mrb_open_core(mrb_allocf f, void *ud) { static const mrb_state mrb_state_zero = { 0 }; static const struct mrb_context mrb_context_zero = { 0 }; mrb_state *mrb; mrb = (mrb_state *)(f)(NULL, NULL, sizeof(mrb_state), ud); if (mrb == NULL) return NULL; *mrb = mrb_state_zero; mrb->allocf_ud = ud; mrb->allocf = f; mrb->atexit_stack_len = 0; mrb_gc_init(mrb, &mrb->gc); mrb->c = (struct mrb_context*)mrb_malloc(mrb, sizeof(struct mrb_context)); *mrb->c = mrb_context_zero; mrb->root_c = mrb->c; mrb_init_core(mrb); return mrb; } void* mrb_default_allocf(mrb_state *mrb, void *p, size_t size, void *ud) { if (size == 0) { free(p); return NULL; } else { return realloc(p, size); } } struct alloca_header { struct alloca_header *next; char buf[1]; }; MRB_API void* mrb_alloca(mrb_state *mrb, size_t size) { struct alloca_header *p; p = (struct alloca_header*) mrb_malloc(mrb, sizeof(struct alloca_header)+size); p->next = mrb->mems; mrb->mems = p; return (void*)p->buf; } static void mrb_alloca_free(mrb_state *mrb) { struct alloca_header *p; struct alloca_header *tmp; if (mrb == NULL) return; p = mrb->mems; while (p) { tmp = p; p = p->next; mrb_free(mrb, tmp); } } MRB_API mrb_state* mrb_open(void) { mrb_state *mrb = mrb_open_allocf(mrb_default_allocf, NULL); return mrb; } MRB_API mrb_state* mrb_open_allocf(mrb_allocf f, void *ud) { mrb_state *mrb = mrb_open_core(f, ud); if (mrb == NULL) { return NULL; } #ifndef DISABLE_GEMS mrb_init_mrbgems(mrb); mrb_gc_arena_restore(mrb, 0); #endif return mrb; } void mrb_free_symtbl(mrb_state *mrb); void mrb_irep_incref(mrb_state *mrb, mrb_irep *irep) { irep->refcnt++; } void mrb_irep_decref(mrb_state *mrb, mrb_irep *irep) { irep->refcnt--; if (irep->refcnt == 0) { mrb_irep_free(mrb, irep); } } void mrb_irep_cutref(mrb_state *mrb, mrb_irep *irep) { mrb_irep *tmp; int i; for (i=0; irlen; i++) { tmp = irep->reps[i]; irep->reps[i] = NULL; if (tmp) mrb_irep_decref(mrb, tmp); } } void mrb_irep_free(mrb_state *mrb, mrb_irep *irep) { int i; if (!(irep->flags & MRB_ISEQ_NO_FREE)) mrb_free(mrb, irep->iseq); if (irep->pool) for (i=0; iplen; i++) { if (mrb_type(irep->pool[i]) == MRB_TT_STRING) { mrb_gc_free_str(mrb, RSTRING(irep->pool[i])); mrb_free(mrb, mrb_obj_ptr(irep->pool[i])); } #if defined(MRB_WORD_BOXING) && !defined(MRB_WITHOUT_FLOAT) else if (mrb_type(irep->pool[i]) == MRB_TT_FLOAT) { mrb_free(mrb, mrb_obj_ptr(irep->pool[i])); } #endif } mrb_free(mrb, irep->pool); mrb_free(mrb, irep->syms); for (i=0; irlen; i++) { if (irep->reps[i]) mrb_irep_decref(mrb, irep->reps[i]); } mrb_free(mrb, irep->reps); mrb_free(mrb, irep->lv); mrb_debug_info_free(mrb, irep->debug_info); mrb_free(mrb, irep); } mrb_value mrb_str_pool(mrb_state *mrb, mrb_value str) { struct RString *s = mrb_str_ptr(str); struct RString *ns; char *ptr; mrb_int len; ns = (struct RString *)mrb_malloc(mrb, sizeof(struct RString)); ns->tt = MRB_TT_STRING; ns->c = mrb->string_class; if (RSTR_NOFREE_P(s)) { ns->flags = MRB_STR_NOFREE; ns->as.heap.ptr = s->as.heap.ptr; ns->as.heap.len = s->as.heap.len; ns->as.heap.aux.capa = 0; } else { ns->flags = 0; if (RSTR_EMBED_P(s)) { ptr = s->as.ary; len = RSTR_EMBED_LEN(s); } else { ptr = s->as.heap.ptr; len = s->as.heap.len; } if (len < RSTRING_EMBED_LEN_MAX) { RSTR_SET_EMBED_FLAG(ns); RSTR_SET_EMBED_LEN(ns, len); if (ptr) { memcpy(ns->as.ary, ptr, len); } ns->as.ary[len] = '\0'; } else { ns->as.heap.ptr = (char *)mrb_malloc(mrb, (size_t)len+1); ns->as.heap.len = len; ns->as.heap.aux.capa = len; if (ptr) { memcpy(ns->as.heap.ptr, ptr, len); } ns->as.heap.ptr[len] = '\0'; } } RSTR_SET_POOL_FLAG(ns); MRB_SET_FROZEN_FLAG(ns); return mrb_obj_value(ns); } void mrb_free_backtrace(mrb_state *mrb); MRB_API void mrb_free_context(mrb_state *mrb, struct mrb_context *c) { if (!c) return; mrb_free(mrb, c->stbase); mrb_free(mrb, c->cibase); mrb_free(mrb, c->rescue); mrb_free(mrb, c->ensure); mrb_free(mrb, 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) { mrb->atexit_stack[i - 1](mrb); } #ifndef MRB_FIXED_STATE_ATEXIT_STACK mrb_free(mrb, mrb->atexit_stack); #endif } /* free */ mrb_gc_free_gv(mrb); mrb_free_context(mrb, mrb->root_c); mrb_free_symtbl(mrb); mrb_alloca_free(mrb); mrb_gc_destroy(mrb, &mrb->gc); mrb_free(mrb, mrb); } MRB_API mrb_irep* mrb_add_irep(mrb_state *mrb) { static const mrb_irep mrb_irep_zero = { 0 }; mrb_irep *irep; irep = (mrb_irep *)mrb_malloc(mrb, sizeof(mrb_irep)); *irep = mrb_irep_zero; irep->refcnt = 1; return irep; } MRB_API mrb_value mrb_top_self(mrb_state *mrb) { return mrb_obj_value(mrb->top_self); } MRB_API void mrb_state_atexit(mrb_state *mrb, mrb_atexit_func f) { #ifdef MRB_FIXED_STATE_ATEXIT_STACK if (mrb->atexit_stack_len + 1 > MRB_FIXED_STATE_ATEXIT_STACK_SIZE) { mrb_raise(mrb, E_RUNTIME_ERROR, "exceeded fixed state atexit stack limit"); } #else size_t stack_size; stack_size = sizeof(mrb_atexit_func) * (mrb->atexit_stack_len + 1); if (mrb->atexit_stack_len == 0) { mrb->atexit_stack = (mrb_atexit_func*)mrb_malloc(mrb, stack_size); } else { mrb->atexit_stack = (mrb_atexit_func*)mrb_realloc(mrb, mrb->atexit_stack, stack_size); } #endif mrb->atexit_stack[mrb->atexit_stack_len++] = f; } mruby-2.0.0/src/string.c000066400000000000000000002244671340361412400151250ustar00rootroot00000000000000/* ** string.c - String class ** ** See Copyright Notice in mruby.h */ #ifdef _MSC_VER # define _CRT_NONSTDC_NO_DEPRECATE #endif #ifndef MRB_WITHOUT_FLOAT #include #endif #include #include #include #include #include #include #include #include #include #include typedef struct mrb_shared_string { mrb_bool nofree : 1; int refcnt; char *ptr; mrb_int len; } mrb_shared_string; const char mrb_digitmap[] = "0123456789abcdefghijklmnopqrstuvwxyz"; #define mrb_obj_alloc_string(mrb) ((struct RString*)mrb_obj_alloc((mrb), MRB_TT_STRING, (mrb)->string_class)) static struct RString* str_new_static(mrb_state *mrb, const char *p, size_t len) { struct RString *s; if (len >= MRB_INT_MAX) { mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big"); } s = mrb_obj_alloc_string(mrb); s->as.heap.len = (mrb_int)len; s->as.heap.aux.capa = 0; /* nofree */ s->as.heap.ptr = (char *)p; s->flags = MRB_STR_NOFREE; return s; } static struct RString* str_new(mrb_state *mrb, const char *p, size_t len) { struct RString *s; if (p && mrb_ro_data_p(p)) { return str_new_static(mrb, p, len); } s = mrb_obj_alloc_string(mrb); if (len <= RSTRING_EMBED_LEN_MAX) { RSTR_SET_EMBED_FLAG(s); RSTR_SET_EMBED_LEN(s, len); if (p) { memcpy(s->as.ary, p, len); } } else { if (len >= MRB_INT_MAX) { mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big"); } s->as.heap.ptr = (char *)mrb_malloc(mrb, len+1); s->as.heap.len = (mrb_int)len; s->as.heap.aux.capa = (mrb_int)len; if (p) { memcpy(s->as.heap.ptr, p, len); } } RSTR_PTR(s)[len] = '\0'; return s; } static inline void str_with_class(mrb_state *mrb, struct RString *s, mrb_value obj) { s->c = mrb_str_ptr(obj)->c; } static mrb_value mrb_str_new_empty(mrb_state *mrb, mrb_value str) { struct RString *s = str_new(mrb, 0, 0); str_with_class(mrb, s, str); return mrb_obj_value(s); } MRB_API mrb_value mrb_str_new_capa(mrb_state *mrb, size_t capa) { struct RString *s; s = mrb_obj_alloc_string(mrb); if (capa >= MRB_INT_MAX) { mrb_raise(mrb, E_ARGUMENT_ERROR, "string capacity size too big"); } s->as.heap.len = 0; s->as.heap.aux.capa = (mrb_int)capa; s->as.heap.ptr = (char *)mrb_malloc(mrb, capa+1); RSTR_PTR(s)[0] = '\0'; return mrb_obj_value(s); } #ifndef MRB_STR_BUF_MIN_SIZE # define MRB_STR_BUF_MIN_SIZE 128 #endif MRB_API mrb_value mrb_str_buf_new(mrb_state *mrb, size_t capa) { if (capa < MRB_STR_BUF_MIN_SIZE) { capa = MRB_STR_BUF_MIN_SIZE; } return mrb_str_new_capa(mrb, capa); } static void resize_capa(mrb_state *mrb, struct RString *s, size_t capacity) { #if SIZE_MAX > MRB_INT_MAX mrb_assert(capacity < MRB_INT_MAX); #endif if (RSTR_EMBED_P(s)) { if (RSTRING_EMBED_LEN_MAX < capacity) { char *const tmp = (char *)mrb_malloc(mrb, capacity+1); const mrb_int len = RSTR_EMBED_LEN(s); memcpy(tmp, s->as.ary, len); RSTR_UNSET_EMBED_FLAG(s); s->as.heap.ptr = tmp; s->as.heap.len = len; s->as.heap.aux.capa = (mrb_int)capacity; } } else { s->as.heap.ptr = (char*)mrb_realloc(mrb, RSTR_PTR(s), capacity+1); s->as.heap.aux.capa = (mrb_int)capacity; } } MRB_API mrb_value mrb_str_new(mrb_state *mrb, const char *p, size_t len) { return mrb_obj_value(str_new(mrb, p, len)); } /* * call-seq: (Caution! NULL string) * String.new(str="") => new_str * * Returns a new string object containing a copy of str. */ MRB_API mrb_value mrb_str_new_cstr(mrb_state *mrb, const char *p) { struct RString *s; size_t len; if (p) { len = strlen(p); } else { len = 0; } s = str_new(mrb, p, len); return mrb_obj_value(s); } MRB_API mrb_value mrb_str_new_static(mrb_state *mrb, const char *p, size_t len) { struct RString *s = str_new_static(mrb, p, len); return mrb_obj_value(s); } static void str_decref(mrb_state *mrb, mrb_shared_string *shared) { shared->refcnt--; if (shared->refcnt == 0) { if (!shared->nofree) { mrb_free(mrb, shared->ptr); } mrb_free(mrb, shared); } } void mrb_gc_free_str(mrb_state *mrb, struct RString *str) { if (RSTR_EMBED_P(str)) /* no code */; else if (RSTR_SHARED_P(str)) str_decref(mrb, str->as.heap.aux.shared); else if (!RSTR_NOFREE_P(str) && !RSTR_FSHARED_P(str)) mrb_free(mrb, str->as.heap.ptr); } #ifdef MRB_UTF8_STRING static const char utf8len_codepage[256] = { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,1,1,1,1,1,1,1,1,1,1,1, }; static mrb_int utf8len(const char* p, const char* e) { mrb_int len; mrb_int i; len = utf8len_codepage[(unsigned char)*p]; if (p + len > e) return 1; for (i = 1; i < len; ++i) if ((p[i] & 0xc0) != 0x80) return 1; return len; } static mrb_int utf8_strlen(mrb_value str, mrb_int len) { mrb_int total = 0; char* p = RSTRING_PTR(str); char* e = p; if (RSTRING(str)->flags & MRB_STR_NO_UTF) { return RSTRING_LEN(str); } e += len < 0 ? RSTRING_LEN(str) : len; while (pflags |= MRB_STR_NO_UTF; } return total; } #define RSTRING_CHAR_LEN(s) utf8_strlen(s, -1) /* map character index to byte offset index */ static mrb_int chars2bytes(mrb_value s, mrb_int off, mrb_int idx) { mrb_int i, b, n; const char *p = RSTRING_PTR(s) + off; const char *e = RSTRING_END(s); for (b=i=0; p n) return -1; else if (m == n) { return memcmp(x0, y0, m) == 0 ? 0 : -1; } else if (m < 1) { return 0; } else if (m == 1) { const unsigned char *ys = (const unsigned char *)memchr(y, *x, n); if (ys) return (mrb_int)(ys - y); else return -1; } return mrb_memsearch_qs((const unsigned char *)x0, m, (const unsigned char *)y0, n); } static void str_make_shared(mrb_state *mrb, struct RString *orig, struct RString *s) { mrb_shared_string *shared; mrb_int len = RSTR_LEN(orig); mrb_assert(!RSTR_EMBED_P(orig)); if (RSTR_SHARED_P(orig)) { shared = orig->as.heap.aux.shared; shared->refcnt++; s->as.heap.ptr = orig->as.heap.ptr; s->as.heap.len = len; s->as.heap.aux.shared = shared; RSTR_SET_SHARED_FLAG(s); RSTR_UNSET_EMBED_FLAG(s); } else if (RSTR_FSHARED_P(orig)) { struct RString *fs; fs = orig->as.heap.aux.fshared; s->as.heap.ptr = orig->as.heap.ptr; s->as.heap.len = len; s->as.heap.aux.fshared = fs; RSTR_SET_FSHARED_FLAG(s); RSTR_UNSET_EMBED_FLAG(s); } else if (MRB_FROZEN_P(orig) && !RSTR_POOL_P(orig)) { s->as.heap.ptr = orig->as.heap.ptr; s->as.heap.len = len; s->as.heap.aux.fshared = orig; RSTR_SET_FSHARED_FLAG(s); RSTR_UNSET_EMBED_FLAG(s); } else { shared = (mrb_shared_string *)mrb_malloc(mrb, sizeof(mrb_shared_string)); shared->refcnt = 2; shared->nofree = !!RSTR_NOFREE_P(orig); if (!shared->nofree && orig->as.heap.aux.capa > orig->as.heap.len) { shared->ptr = (char *)mrb_realloc(mrb, orig->as.heap.ptr, len+1); orig->as.heap.ptr = shared->ptr; } else { shared->ptr = orig->as.heap.ptr; } orig->as.heap.aux.shared = shared; RSTR_SET_SHARED_FLAG(orig); shared->len = len; s->as.heap.aux.shared = shared; s->as.heap.ptr = shared->ptr; s->as.heap.len = len; RSTR_SET_SHARED_FLAG(s); RSTR_UNSET_EMBED_FLAG(s); } } static mrb_value byte_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len) { struct RString *orig, *s; orig = mrb_str_ptr(str); if (RSTR_EMBED_P(orig) || RSTR_LEN(orig) == 0 || len <= RSTRING_EMBED_LEN_MAX) { s = str_new(mrb, RSTR_PTR(orig)+beg, len); } else { s = mrb_obj_alloc_string(mrb); str_make_shared(mrb, orig, s); s->as.heap.ptr += beg; s->as.heap.len = len; } return mrb_obj_value(s); } #ifdef MRB_UTF8_STRING static inline mrb_value str_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len) { beg = chars2bytes(str, 0, beg); len = chars2bytes(str, beg, len); return byte_subseq(mrb, str, beg, len); } #else #define str_subseq(mrb, str, beg, len) byte_subseq(mrb, str, beg, len) #endif static mrb_value str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len) { mrb_int clen = RSTRING_CHAR_LEN(str); if (len < 0) return mrb_nil_value(); if (clen == 0) { len = 0; } else if (beg < 0) { beg = clen + beg; } if (beg > clen) return mrb_nil_value(); if (beg < 0) { beg += clen; if (beg < 0) return mrb_nil_value(); } if (len > clen - beg) len = clen - beg; if (len <= 0) { len = 0; } return str_subseq(mrb, str, beg, len); } MRB_API mrb_int mrb_str_index(mrb_state *mrb, mrb_value str, const char *sptr, mrb_int slen, mrb_int offset) { mrb_int pos; char *s; mrb_int len; len = RSTRING_LEN(str); if (offset < 0) { offset += len; if (offset < 0) return -1; } if (len - offset < slen) return -1; s = RSTRING_PTR(str); if (offset) { s += offset; } if (slen == 0) return offset; /* need proceed one character at a time */ len = RSTRING_LEN(str) - offset; pos = mrb_memsearch(sptr, slen, s, len); if (pos < 0) return pos; return pos + offset; } static mrb_int str_index_str(mrb_state *mrb, mrb_value str, mrb_value str2, mrb_int offset) { const char *ptr; mrb_int len; ptr = RSTRING_PTR(str2); len = RSTRING_LEN(str2); return mrb_str_index(mrb, str, ptr, len, offset); } static void check_frozen(mrb_state *mrb, struct RString *s) { if (MRB_FROZEN_P(s)) { mrb_raise(mrb, E_FROZEN_ERROR, "can't modify frozen string"); } } static mrb_value str_replace(mrb_state *mrb, struct RString *s1, struct RString *s2) { mrb_int len; check_frozen(mrb, s1); if (s1 == s2) return mrb_obj_value(s1); s1->flags &= ~MRB_STR_NO_UTF; s1->flags |= s2->flags&MRB_STR_NO_UTF; len = RSTR_LEN(s2); if (RSTR_SHARED_P(s1)) { str_decref(mrb, s1->as.heap.aux.shared); RSTR_UNSET_SHARED_FLAG(s1); } else if (!RSTR_EMBED_P(s1) && !RSTR_NOFREE_P(s1) && !RSTR_FSHARED_P(s1) && s1->as.heap.ptr) { mrb_free(mrb, s1->as.heap.ptr); } RSTR_UNSET_FSHARED_FLAG(s1); RSTR_UNSET_NOFREE_FLAG(s1); if (len <= RSTRING_EMBED_LEN_MAX) { RSTR_UNSET_SHARED_FLAG(s1); RSTR_UNSET_FSHARED_FLAG(s1); RSTR_SET_EMBED_FLAG(s1); memcpy(s1->as.ary, RSTR_PTR(s2), len); RSTR_SET_EMBED_LEN(s1, len); } else { str_make_shared(mrb, s2, s1); } return mrb_obj_value(s1); } static mrb_int str_rindex(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int pos) { char *s, *sbeg, *t; struct RString *ps = mrb_str_ptr(str); mrb_int len = RSTRING_LEN(sub); /* substring longer than string */ if (RSTR_LEN(ps) < len) return -1; if (RSTR_LEN(ps) - pos < len) { pos = RSTR_LEN(ps) - len; } sbeg = RSTR_PTR(ps); s = RSTR_PTR(ps) + pos; t = RSTRING_PTR(sub); if (len) { while (sbeg <= s) { if (memcmp(s, t, len) == 0) { return (mrb_int)(s - RSTR_PTR(ps)); } s--; } return -1; } else { return pos; } } MRB_API mrb_int mrb_str_strlen(mrb_state *mrb, struct RString *s) { mrb_int i, max = RSTR_LEN(s); char *p = RSTR_PTR(s); if (!p) return 0; for (i=0; i char* mrb_utf8_from_locale(const char *str, int len) { wchar_t* wcsp; char* mbsp; int mbssize, wcssize; if (len == 0) return strdup(""); if (len == -1) len = (int)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, int len) { wchar_t* wcsp; char* mbsp; int mbssize, wcssize; if (len == 0) return strdup(""); if (len == -1) len = (int)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 MRB_API void mrb_str_modify(mrb_state *mrb, struct RString *s) { check_frozen(mrb, s); s->flags &= ~MRB_STR_NO_UTF; if (RSTR_SHARED_P(s)) { mrb_shared_string *shared = s->as.heap.aux.shared; if (shared->nofree == 0 && shared->refcnt == 1 && s->as.heap.ptr == shared->ptr) { s->as.heap.ptr = shared->ptr; s->as.heap.aux.capa = shared->len; RSTR_PTR(s)[s->as.heap.len] = '\0'; mrb_free(mrb, shared); } else { char *ptr, *p; mrb_int len; p = RSTR_PTR(s); len = s->as.heap.len; if (len < RSTRING_EMBED_LEN_MAX) { RSTR_SET_EMBED_FLAG(s); RSTR_SET_EMBED_LEN(s, len); ptr = RSTR_PTR(s); } else { ptr = (char *)mrb_malloc(mrb, (size_t)len + 1); s->as.heap.ptr = ptr; s->as.heap.aux.capa = len; } if (p) { memcpy(ptr, p, len); } ptr[len] = '\0'; str_decref(mrb, shared); } RSTR_UNSET_SHARED_FLAG(s); return; } if (RSTR_NOFREE_P(s) || RSTR_FSHARED_P(s)) { char *p = s->as.heap.ptr; mrb_int len = s->as.heap.len; RSTR_UNSET_FSHARED_FLAG(s); RSTR_UNSET_NOFREE_FLAG(s); RSTR_UNSET_FSHARED_FLAG(s); if (len < RSTRING_EMBED_LEN_MAX) { RSTR_SET_EMBED_FLAG(s); RSTR_SET_EMBED_LEN(s, len); } else { s->as.heap.ptr = (char *)mrb_malloc(mrb, (size_t)len+1); s->as.heap.aux.capa = len; } if (p) { memcpy(RSTR_PTR(s), p, len); } RSTR_PTR(s)[len] = '\0'; return; } } MRB_API mrb_value mrb_str_resize(mrb_state *mrb, mrb_value str, mrb_int len) { mrb_int slen; struct RString *s = mrb_str_ptr(str); if (len < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "negative (or overflowed) string size"); } mrb_str_modify(mrb, s); slen = RSTR_LEN(s); if (len != slen) { if (slen < len || slen - len > 256) { resize_capa(mrb, s, len); } RSTR_SET_LEN(s, len); RSTR_PTR(s)[len] = '\0'; /* sentinel */ } return str; } MRB_API char* mrb_str_to_cstr(mrb_state *mrb, mrb_value str0) { struct RString *s; if (!mrb_string_p(str0)) { mrb_raise(mrb, E_TYPE_ERROR, "expected String"); } s = str_new(mrb, RSTRING_PTR(str0), RSTRING_LEN(str0)); if ((strlen(RSTR_PTR(s)) ^ RSTR_LEN(s)) != 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "string contains null byte"); } return RSTR_PTR(s); } /* * call-seq: (Caution! String("abcd") change) * String("abcdefg") = String("abcd") + String("efg") * * Returns a new string object containing a copy of str. */ MRB_API void mrb_str_concat(mrb_state *mrb, mrb_value self, mrb_value other) { other = mrb_str_to_str(mrb, other); mrb_str_cat_str(mrb, self, other); } /* * call-seq: (Caution! String("abcd") remain) * String("abcdefg") = String("abcd") + String("efg") * * Returns a new string object containing a copy of str. */ MRB_API mrb_value mrb_str_plus(mrb_state *mrb, mrb_value a, mrb_value b) { struct RString *s = mrb_str_ptr(a); struct RString *s2 = mrb_str_ptr(b); struct RString *t; t = str_new(mrb, 0, RSTR_LEN(s) + RSTR_LEN(s2)); memcpy(RSTR_PTR(t), RSTR_PTR(s), RSTR_LEN(s)); memcpy(RSTR_PTR(t) + RSTR_LEN(s), RSTR_PTR(s2), RSTR_LEN(s2)); return mrb_obj_value(t); } /* 15.2.10.5.2 */ /* * call-seq: (Caution! String("abcd") remain) for stack_argument * String("abcdefg") = String("abcd") + String("efg") * * Returns a new string object containing a copy of str. */ static mrb_value mrb_str_plus_m(mrb_state *mrb, mrb_value self) { mrb_value str; mrb_get_args(mrb, "S", &str); return mrb_str_plus(mrb, self, str); } /* 15.2.10.5.26 */ /* 15.2.10.5.33 */ /* * call-seq: * "abcd".size => int * * Returns the length of string. */ static mrb_value mrb_str_size(mrb_state *mrb, mrb_value self) { mrb_int len = RSTRING_CHAR_LEN(self); return mrb_fixnum_value(len); } static mrb_value mrb_str_bytesize(mrb_state *mrb, mrb_value self) { mrb_int len = RSTRING_LEN(self); return mrb_fixnum_value(len); } /* 15.2.10.5.1 */ /* * call-seq: * str * integer => new_str * * Copy---Returns a new String containing integer copies of * the receiver. * * "Ho! " * 3 #=> "Ho! Ho! Ho! " */ static mrb_value mrb_str_times(mrb_state *mrb, mrb_value self) { mrb_int n,len,times; struct RString *str2; char *p; mrb_get_args(mrb, "i", ×); if (times < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "negative argument"); } if (times && MRB_INT_MAX / times < RSTRING_LEN(self)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "argument too big"); } len = RSTRING_LEN(self)*times; str2 = str_new(mrb, 0, len); str_with_class(mrb, str2, self); p = RSTR_PTR(str2); if (len > 0) { n = RSTRING_LEN(self); memcpy(p, RSTRING_PTR(self), n); while (n <= len/2) { memcpy(p + n, p, n); n *= 2; } memcpy(p + n, p, len-n); } p[RSTR_LEN(str2)] = '\0'; return mrb_obj_value(str2); } /* -------------------------------------------------------------- */ #define lesser(a,b) (((a)>(b))?(b):(a)) /* ---------------------------*/ /* * call-seq: * mrb_value str1 <=> mrb_value str2 => int * > 1 * = 0 * < -1 */ MRB_API int mrb_str_cmp(mrb_state *mrb, mrb_value str1, mrb_value str2) { mrb_int len; mrb_int retval; struct RString *s1 = mrb_str_ptr(str1); struct RString *s2 = mrb_str_ptr(str2); len = lesser(RSTR_LEN(s1), RSTR_LEN(s2)); retval = memcmp(RSTR_PTR(s1), RSTR_PTR(s2), len); if (retval == 0) { if (RSTR_LEN(s1) == RSTR_LEN(s2)) return 0; if (RSTR_LEN(s1) > RSTR_LEN(s2)) return 1; return -1; } if (retval > 0) return 1; return -1; } /* 15.2.10.5.3 */ /* * call-seq: * str <=> other_str => -1, 0, +1 * * Comparison---Returns -1 if other_str is less than, 0 if * other_str is equal to, and +1 if other_str is greater than * str. If the strings are of different lengths, and the strings are * equal when compared up to the shortest length, then the longer string is * considered greater than the shorter one. If the variable $= is * false, the comparison is based on comparing the binary values * of each character in the string. In older versions of Ruby, setting * $= allowed case-insensitive comparisons; this is now deprecated * in favor of using String#casecmp. * * <=> is the basis for the methods <, * <=, >, >=, and between?, * included from module Comparable. The method * String#== does not use Comparable#==. * * "abcdef" <=> "abcde" #=> 1 * "abcdef" <=> "abcdef" #=> 0 * "abcdef" <=> "abcdefg" #=> -1 * "abcdef" <=> "ABCDEF" #=> 1 */ static mrb_value mrb_str_cmp_m(mrb_state *mrb, mrb_value str1) { mrb_value str2; mrb_int result; mrb_get_args(mrb, "o", &str2); if (!mrb_string_p(str2)) { if (!mrb_respond_to(mrb, str2, mrb_intern_lit(mrb, "to_s"))) { return mrb_nil_value(); } else if (!mrb_respond_to(mrb, str2, mrb_intern_lit(mrb, "<=>"))) { return mrb_nil_value(); } else { mrb_value tmp = mrb_funcall(mrb, str2, "<=>", 1, str1); if (mrb_nil_p(tmp)) return mrb_nil_value(); if (!mrb_fixnum_p(tmp)) { return mrb_funcall(mrb, mrb_fixnum_value(0), "-", 1, tmp); } result = -mrb_fixnum(tmp); } } else { result = mrb_str_cmp(mrb, str1, str2); } return mrb_fixnum_value(result); } static mrb_bool str_eql(mrb_state *mrb, const mrb_value str1, const mrb_value str2) { const mrb_int len = RSTRING_LEN(str1); if (len != RSTRING_LEN(str2)) return FALSE; if (memcmp(RSTRING_PTR(str1), RSTRING_PTR(str2), (size_t)len) == 0) return TRUE; return FALSE; } MRB_API mrb_bool mrb_str_equal(mrb_state *mrb, mrb_value str1, mrb_value str2) { if (!mrb_string_p(str2)) return FALSE; return str_eql(mrb, str1, str2); } /* 15.2.10.5.4 */ /* * call-seq: * str == obj => true or false * * Equality--- * If obj is not a String, returns false. * Otherwise, returns false or true * * caution:if str <=> obj returns zero. */ static mrb_value mrb_str_equal_m(mrb_state *mrb, mrb_value str1) { mrb_value str2; mrb_get_args(mrb, "o", &str2); return mrb_bool_value(mrb_str_equal(mrb, str1, str2)); } /* ---------------------------------- */ MRB_API mrb_value mrb_str_to_str(mrb_state *mrb, mrb_value str) { if (!mrb_string_p(str)) { return mrb_convert_type(mrb, str, MRB_TT_STRING, "String", "to_s"); } return str; } MRB_API const char* mrb_string_value_ptr(mrb_state *mrb, mrb_value str) { str = mrb_str_to_str(mrb, str); return RSTRING_PTR(str); } MRB_API mrb_int mrb_string_value_len(mrb_state *mrb, mrb_value ptr) { mrb_to_str(mrb, ptr); return RSTRING_LEN(ptr); } void mrb_noregexp(mrb_state *mrb, mrb_value self) { mrb_raise(mrb, E_NOTIMP_ERROR, "Regexp class not implemented"); } void mrb_regexp_check(mrb_state *mrb, mrb_value obj) { if (mrb_regexp_p(mrb, obj)) { mrb_noregexp(mrb, obj); } } MRB_API mrb_value mrb_str_dup(mrb_state *mrb, mrb_value str) { struct RString *s = mrb_str_ptr(str); struct RString *dup = str_new(mrb, 0, 0); str_with_class(mrb, dup, str); return str_replace(mrb, dup, s); } static mrb_value mrb_str_aref(mrb_state *mrb, mrb_value str, mrb_value indx) { mrb_int idx; mrb_regexp_check(mrb, indx); switch (mrb_type(indx)) { case MRB_TT_FIXNUM: idx = mrb_fixnum(indx); num_index: str = str_substr(mrb, str, idx, 1); if (!mrb_nil_p(str) && RSTRING_LEN(str) == 0) return mrb_nil_value(); return str; case MRB_TT_STRING: if (str_index_str(mrb, str, indx, 0) != -1) return mrb_str_dup(mrb, indx); return mrb_nil_value(); case MRB_TT_RANGE: goto range_arg; default: indx = mrb_Integer(mrb, indx); if (mrb_nil_p(indx)) { range_arg: { mrb_int beg, len; len = RSTRING_CHAR_LEN(str); switch (mrb_range_beg_len(mrb, indx, &beg, &len, len, TRUE)) { case 1: return str_subseq(mrb, str, beg, len); case 2: return mrb_nil_value(); default: break; } } mrb_raise(mrb, E_TYPE_ERROR, "can't convert to Fixnum"); } idx = mrb_fixnum(indx); goto num_index; } return mrb_nil_value(); /* not reached */ } /* 15.2.10.5.6 */ /* 15.2.10.5.34 */ /* * call-seq: * str[fixnum] => fixnum or nil * str[fixnum, fixnum] => new_str or nil * str[range] => new_str or nil * str[regexp] => new_str or nil * str[regexp, fixnum] => new_str or nil * str[other_str] => new_str or nil * str.slice(fixnum) => fixnum or nil * str.slice(fixnum, fixnum) => new_str or nil * str.slice(range) => new_str or nil * str.slice(other_str) => new_str or nil * * Element Reference---If passed a single Fixnum, returns the code * of the character at that position. If passed two Fixnum * objects, returns a substring starting at the offset given by the first, and * a length given by the second. If given a range, a substring containing * characters at offsets given by the range is returned. In all three cases, if * an offset is negative, it is counted from the end of str. Returns * nil if the initial offset falls outside the string, the length * is negative, or the beginning of the range is greater than the end. * * If a String is given, that string is returned if it occurs in * str. In both cases, nil is returned if there is no * match. * * a = "hello there" * a[1] #=> 101(1.8.7) "e"(1.9.2) * a[1.1] #=> "e"(1.9.2) * a[1,3] #=> "ell" * a[1..3] #=> "ell" * a[-3,2] #=> "er" * a[-4..-2] #=> "her" * a[12..-1] #=> nil * a[-2..-4] #=> "" * a["lo"] #=> "lo" * a["bye"] #=> nil */ static mrb_value mrb_str_aref_m(mrb_state *mrb, mrb_value str) { mrb_value a1, a2; mrb_int argc; argc = mrb_get_args(mrb, "o|o", &a1, &a2); if (argc == 2) { mrb_int n1, n2; mrb_regexp_check(mrb, a1); mrb_get_args(mrb, "ii", &n1, &n2); return str_substr(mrb, str, n1, n2); } if (argc != 1) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%S for 1)", mrb_fixnum_value(argc)); } return mrb_str_aref(mrb, str, a1); } /* 15.2.10.5.8 */ /* * call-seq: * str.capitalize! => str or nil * * Modifies str by converting the first character to uppercase and the * remainder to lowercase. Returns nil if no changes are made. * * a = "hello" * a.capitalize! #=> "Hello" * a #=> "Hello" * a.capitalize! #=> nil */ static mrb_value mrb_str_capitalize_bang(mrb_state *mrb, mrb_value str) { char *p, *pend; mrb_bool modify = FALSE; struct RString *s = mrb_str_ptr(str); mrb_str_modify(mrb, s); if (RSTR_LEN(s) == 0 || !RSTR_PTR(s)) return mrb_nil_value(); p = RSTR_PTR(s); pend = RSTR_PTR(s) + RSTR_LEN(s); if (ISLOWER(*p)) { *p = TOUPPER(*p); modify = TRUE; } while (++p < pend) { if (ISUPPER(*p)) { *p = TOLOWER(*p); modify = TRUE; } } if (modify) return str; return mrb_nil_value(); } /* 15.2.10.5.7 */ /* * call-seq: * str.capitalize => new_str * * Returns a copy of str with the first character converted to uppercase * and the remainder to lowercase. * * "hello".capitalize #=> "Hello" * "HELLO".capitalize #=> "Hello" * "123ABC".capitalize #=> "123abc" */ static mrb_value mrb_str_capitalize(mrb_state *mrb, mrb_value self) { mrb_value str; str = mrb_str_dup(mrb, self); mrb_str_capitalize_bang(mrb, str); return str; } /* 15.2.10.5.10 */ /* * call-seq: * str.chomp!(separator="\n") => str or nil * * Modifies str in place as described for String#chomp, * returning str, or nil if no modifications were made. */ static mrb_value mrb_str_chomp_bang(mrb_state *mrb, mrb_value str) { mrb_value rs; mrb_int newline; char *p, *pp; mrb_int rslen; mrb_int len; mrb_int argc; struct RString *s = mrb_str_ptr(str); argc = mrb_get_args(mrb, "|S", &rs); mrb_str_modify(mrb, s); len = RSTR_LEN(s); if (argc == 0) { if (len == 0) return mrb_nil_value(); smart_chomp: if (RSTR_PTR(s)[len-1] == '\n') { RSTR_SET_LEN(s, RSTR_LEN(s) - 1); if (RSTR_LEN(s) > 0 && RSTR_PTR(s)[RSTR_LEN(s)-1] == '\r') { RSTR_SET_LEN(s, RSTR_LEN(s) - 1); } } else if (RSTR_PTR(s)[len-1] == '\r') { RSTR_SET_LEN(s, RSTR_LEN(s) - 1); } else { return mrb_nil_value(); } RSTR_PTR(s)[RSTR_LEN(s)] = '\0'; return str; } if (len == 0 || mrb_nil_p(rs)) return mrb_nil_value(); p = RSTR_PTR(s); rslen = RSTRING_LEN(rs); if (rslen == 0) { while (len>0 && p[len-1] == '\n') { len--; if (len>0 && p[len-1] == '\r') len--; } if (len < RSTR_LEN(s)) { RSTR_SET_LEN(s, len); p[len] = '\0'; return str; } return mrb_nil_value(); } if (rslen > len) return mrb_nil_value(); newline = RSTRING_PTR(rs)[rslen-1]; if (rslen == 1 && newline == '\n') newline = RSTRING_PTR(rs)[rslen-1]; if (rslen == 1 && newline == '\n') goto smart_chomp; pp = p + len - rslen; if (p[len-1] == newline && (rslen <= 1 || memcmp(RSTRING_PTR(rs), pp, rslen) == 0)) { RSTR_SET_LEN(s, len - rslen); p[RSTR_LEN(s)] = '\0'; return str; } return mrb_nil_value(); } /* 15.2.10.5.9 */ /* * call-seq: * str.chomp(separator="\n") => new_str * * Returns a new String with the given record separator removed * from the end of str (if present). If $/ has not been * changed from the default Ruby record separator, then chomp also * removes carriage return characters (that is it will remove \n, * \r, and \r\n). * * "hello".chomp #=> "hello" * "hello\n".chomp #=> "hello" * "hello\r\n".chomp #=> "hello" * "hello\n\r".chomp #=> "hello\n" * "hello\r".chomp #=> "hello" * "hello \n there".chomp #=> "hello \n there" * "hello".chomp("llo") #=> "he" */ static mrb_value mrb_str_chomp(mrb_state *mrb, mrb_value self) { mrb_value str; str = mrb_str_dup(mrb, self); mrb_str_chomp_bang(mrb, str); return str; } /* 15.2.10.5.12 */ /* * call-seq: * str.chop! => str or nil * * Processes str as for String#chop, returning str, * or nil if str is the empty string. See also * String#chomp!. */ static mrb_value mrb_str_chop_bang(mrb_state *mrb, mrb_value str) { struct RString *s = mrb_str_ptr(str); mrb_str_modify(mrb, s); if (RSTR_LEN(s) > 0) { mrb_int len; #ifdef MRB_UTF8_STRING const char* t = RSTR_PTR(s), *p = t; const char* e = p + RSTR_LEN(s); while (p=e) break; p += clen; } len = p - t; #else len = RSTR_LEN(s) - 1; #endif if (RSTR_PTR(s)[len] == '\n') { if (len > 0 && RSTR_PTR(s)[len-1] == '\r') { len--; } } RSTR_SET_LEN(s, len); RSTR_PTR(s)[len] = '\0'; return str; } return mrb_nil_value(); } /* 15.2.10.5.11 */ /* * call-seq: * str.chop => new_str * * Returns a new String with the last character removed. If the * string ends with \r\n, both characters are removed. Applying * chop to an empty string returns an empty * string. String#chomp is often a safer alternative, as it leaves * the string unchanged if it doesn't end in a record separator. * * "string\r\n".chop #=> "string" * "string\n\r".chop #=> "string\n" * "string\n".chop #=> "string" * "string".chop #=> "strin" * "x".chop #=> "" */ static mrb_value mrb_str_chop(mrb_state *mrb, mrb_value self) { mrb_value str; str = mrb_str_dup(mrb, self); mrb_str_chop_bang(mrb, str); return str; } /* 15.2.10.5.14 */ /* * call-seq: * str.downcase! => str or nil * * Downcases the contents of str, returning nil if no * changes were made. */ static mrb_value mrb_str_downcase_bang(mrb_state *mrb, mrb_value str) { char *p, *pend; mrb_bool modify = FALSE; struct RString *s = mrb_str_ptr(str); mrb_str_modify(mrb, s); p = RSTR_PTR(s); pend = RSTR_PTR(s) + RSTR_LEN(s); while (p < pend) { if (ISUPPER(*p)) { *p = TOLOWER(*p); modify = TRUE; } p++; } if (modify) return str; return mrb_nil_value(); } /* 15.2.10.5.13 */ /* * call-seq: * str.downcase => new_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. * * "hEllO".downcase #=> "hello" */ static mrb_value mrb_str_downcase(mrb_state *mrb, mrb_value self) { mrb_value str; str = mrb_str_dup(mrb, self); mrb_str_downcase_bang(mrb, str); return str; } /* 15.2.10.5.16 */ /* * call-seq: * str.empty? => true or false * * Returns true if str has a length of zero. * * "hello".empty? #=> false * "".empty? #=> true */ static mrb_value mrb_str_empty_p(mrb_state *mrb, mrb_value self) { struct RString *s = mrb_str_ptr(self); return mrb_bool_value(RSTR_LEN(s) == 0); } /* 15.2.10.5.17 */ /* * call-seq: * str.eql?(other) => true or false * * Two strings are equal if the have the same length and content. */ static mrb_value mrb_str_eql(mrb_state *mrb, mrb_value self) { mrb_value str2; mrb_bool eql_p; mrb_get_args(mrb, "o", &str2); eql_p = (mrb_type(str2) == MRB_TT_STRING) && str_eql(mrb, self, str2); return mrb_bool_value(eql_p); } MRB_API mrb_value mrb_str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len) { return str_substr(mrb, str, beg, len); } uint32_t mrb_str_hash(mrb_state *mrb, mrb_value str) { /* 1-8-7 */ struct RString *s = mrb_str_ptr(str); mrb_int len = RSTR_LEN(s); char *p = RSTR_PTR(s); uint64_t key = 0; while (len--) { key = key*65599 + *p; p++; } return (uint32_t)(key + (key>>5)); } /* 15.2.10.5.20 */ /* * call-seq: * str.hash => fixnum * * Return a hash based on the string's length and content. */ static mrb_value mrb_str_hash_m(mrb_state *mrb, mrb_value self) { mrb_int key = mrb_str_hash(mrb, self); return mrb_fixnum_value(key); } /* 15.2.10.5.21 */ /* * call-seq: * str.include? other_str => true or false * str.include? fixnum => true or false * * Returns true if str contains the given string or * character. * * "hello".include? "lo" #=> true * "hello".include? "ol" #=> false * "hello".include? ?h #=> true */ static mrb_value mrb_str_include(mrb_state *mrb, mrb_value self) { mrb_value str2; mrb_get_args(mrb, "S", &str2); if (str_index_str(mrb, self, str2, 0) < 0) return mrb_bool_value(FALSE); return mrb_bool_value(TRUE); } /* 15.2.10.5.22 */ /* * call-seq: * str.index(substring [, offset]) => fixnum or nil * str.index(fixnum [, offset]) => fixnum or nil * str.index(regexp [, offset]) => fixnum or nil * * Returns the index of the first occurrence of the given * substring, * character (fixnum), or pattern (regexp) in str. * Returns * nil if not found. * If the second parameter is present, it * specifies the position in the string to begin the search. * * "hello".index('e') #=> 1 * "hello".index('lo') #=> 3 * "hello".index('a') #=> nil * "hello".index(101) #=> 1(101=0x65='e') * "hello".index(/[aeiou]/, -3) #=> 4 */ static mrb_value mrb_str_index_m(mrb_state *mrb, mrb_value str) { mrb_value *argv; mrb_int argc; mrb_value sub; mrb_int pos, clen; mrb_get_args(mrb, "*!", &argv, &argc); if (argc == 2) { mrb_get_args(mrb, "oi", &sub, &pos); } else { pos = 0; if (argc > 0) sub = argv[0]; else sub = mrb_nil_value(); } mrb_regexp_check(mrb, sub); clen = RSTRING_CHAR_LEN(str); if (pos < 0) { pos += clen; if (pos < 0) { return mrb_nil_value(); } } if (pos > clen) return mrb_nil_value(); pos = chars2bytes(str, 0, pos); switch (mrb_type(sub)) { default: { mrb_value tmp; tmp = mrb_check_string_type(mrb, sub); if (mrb_nil_p(tmp)) { mrb_raisef(mrb, E_TYPE_ERROR, "type mismatch: %S given", sub); } sub = tmp; } /* fall through */ case MRB_TT_STRING: pos = str_index_str(mrb, str, sub, pos); break; } if (pos == -1) return mrb_nil_value(); pos = bytes2chars(RSTRING_PTR(str), pos); BYTES_ALIGN_CHECK(pos); return mrb_fixnum_value(pos); } #define STR_REPLACE_SHARED_MIN 10 /* 15.2.10.5.24 */ /* 15.2.10.5.28 */ /* * call-seq: * str.replace(other_str) => str * * s = "hello" #=> "hello" * s.replace "world" #=> "world" */ static mrb_value mrb_str_replace(mrb_state *mrb, mrb_value str) { mrb_value str2; mrb_get_args(mrb, "S", &str2); return str_replace(mrb, mrb_str_ptr(str), mrb_str_ptr(str2)); } /* 15.2.10.5.23 */ /* * call-seq: * String.new(str="") => new_str * * Returns a new string object containing a copy of str. */ static mrb_value mrb_str_init(mrb_state *mrb, mrb_value self) { mrb_value str2; if (mrb_get_args(mrb, "|S", &str2) == 0) { struct RString *s = str_new(mrb, 0, 0); str2 = mrb_obj_value(s); } str_replace(mrb, mrb_str_ptr(self), mrb_str_ptr(str2)); return self; } /* 15.2.10.5.25 */ /* 15.2.10.5.41 */ /* * call-seq: * str.intern => symbol * str.to_sym => symbol * * Returns the Symbol corresponding to str, creating the * symbol if it did not previously exist. See Symbol#id2name. * * "Koala".intern #=> :Koala * s = 'cat'.to_sym #=> :cat * s == :cat #=> true * s = '@cat'.to_sym #=> :@cat * s == :@cat #=> true * * This can also be used to create symbols that cannot be represented using the * :xxx notation. * * 'cat and dog'.to_sym #=> :"cat and dog" */ MRB_API mrb_value mrb_str_intern(mrb_state *mrb, mrb_value self) { return mrb_symbol_value(mrb_intern_str(mrb, self)); } /* ---------------------------------- */ MRB_API mrb_value mrb_obj_as_string(mrb_state *mrb, mrb_value obj) { mrb_value str; if (mrb_string_p(obj)) { return obj; } str = mrb_funcall(mrb, obj, "to_s", 0); if (!mrb_string_p(str)) return mrb_any_to_s(mrb, obj); return str; } MRB_API mrb_value mrb_ptr_to_str(mrb_state *mrb, void *p) { struct RString *p_str; char *p1; char *p2; uintptr_t n = (uintptr_t)p; p_str = str_new(mrb, NULL, 2 + sizeof(uintptr_t) * CHAR_BIT / 4); p1 = RSTR_PTR(p_str); *p1++ = '0'; *p1++ = 'x'; p2 = p1; do { *p2++ = mrb_digitmap[n % 16]; n /= 16; } while (n > 0); *p2 = '\0'; RSTR_SET_LEN(p_str, (mrb_int)(p2 - RSTR_PTR(p_str))); while (p1 < p2) { const char c = *p1; *p1++ = *--p2; *p2 = c; } return mrb_obj_value(p_str); } /* 15.2.10.5.30 */ /* * call-seq: * str.reverse! => str * * Reverses str in place. */ static mrb_value mrb_str_reverse_bang(mrb_state *mrb, mrb_value str) { #ifdef MRB_UTF8_STRING mrb_int utf8_len = RSTRING_CHAR_LEN(str); mrb_int len = RSTRING_LEN(str); if (utf8_len == len) goto bytes; if (utf8_len > 1) { char *buf; char *p, *e, *r; mrb_str_modify(mrb, mrb_str_ptr(str)); len = RSTRING_LEN(str); buf = (char*)mrb_malloc(mrb, (size_t)len); p = buf; e = buf + len; memcpy(buf, RSTRING_PTR(str), len); r = RSTRING_PTR(str) + len; while (p 1) { p = RSTR_PTR(s); e = p + RSTR_LEN(s) - 1; while (p < e) { c = *p; *p++ = *e; *e-- = c; } } return str; } } /* ---------------------------------- */ /* 15.2.10.5.29 */ /* * call-seq: * str.reverse => new_str * * Returns a new string with the characters from str in reverse order. * * "stressed".reverse #=> "desserts" */ static mrb_value mrb_str_reverse(mrb_state *mrb, mrb_value str) { mrb_value str2 = mrb_str_dup(mrb, str); mrb_str_reverse_bang(mrb, str2); return str2; } /* 15.2.10.5.31 */ /* * call-seq: * str.rindex(substring [, fixnum]) => fixnum or nil * str.rindex(fixnum [, fixnum]) => fixnum or nil * str.rindex(regexp [, fixnum]) => fixnum or nil * * Returns the index of the last occurrence of the given substring, * character (fixnum), or pattern (regexp) in str. Returns * nil if not found. If the second parameter is present, it * specifies the position in the string to end the search---characters beyond * this point will not be considered. * * "hello".rindex('e') #=> 1 * "hello".rindex('l') #=> 3 * "hello".rindex('a') #=> nil * "hello".rindex(101) #=> 1 * "hello".rindex(/[aeiou]/, -2) #=> 1 */ static mrb_value mrb_str_rindex(mrb_state *mrb, mrb_value str) { mrb_value *argv; mrb_int argc; mrb_value sub; mrb_int pos, len = RSTRING_CHAR_LEN(str); mrb_get_args(mrb, "*!", &argv, &argc); if (argc == 2) { mrb_get_args(mrb, "oi", &sub, &pos); if (pos < 0) { pos += len; if (pos < 0) { mrb_regexp_check(mrb, sub); return mrb_nil_value(); } } if (pos > len) pos = len; } else { pos = len; if (argc > 0) sub = argv[0]; else sub = mrb_nil_value(); } pos = chars2bytes(str, 0, pos); mrb_regexp_check(mrb, sub); switch (mrb_type(sub)) { default: { mrb_value tmp; tmp = mrb_check_string_type(mrb, sub); if (mrb_nil_p(tmp)) { mrb_raisef(mrb, E_TYPE_ERROR, "type mismatch: %S given", sub); } sub = tmp; } /* fall through */ case MRB_TT_STRING: pos = str_rindex(mrb, str, sub, pos); if (pos >= 0) { pos = bytes2chars(RSTRING_PTR(str), pos); BYTES_ALIGN_CHECK(pos); return mrb_fixnum_value(pos); } break; } /* end of switch (TYPE(sub)) */ return mrb_nil_value(); } /* 15.2.10.5.35 */ /* * call-seq: * str.split(pattern="\n", [limit]) => anArray * * Divides str into substrings based on a delimiter, returning an array * of these substrings. * * If pattern is a String, then its contents are used as * the delimiter when splitting str. If pattern is a single * space, str is split on whitespace, with leading whitespace and runs * of contiguous whitespace characters ignored. * * If pattern is a Regexp, str is divided where the * pattern matches. Whenever the pattern matches a zero-length string, * str is split into individual characters. * * 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. * * If the limit parameter is omitted, trailing null fields are * suppressed. If limit is a positive number, at most that number of * fields will be returned (if limit is 1, the entire * string is returned as the only entry in an array). If negative, there is no * limit to the number of fields returned, and trailing null fields are not * suppressed. * * " 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"] * "hello".split(//) #=> ["h", "e", "l", "l", "o"] * "hello".split(//, 3) #=> ["h", "e", "llo"] * * "mellow yellow".split("ello") #=> ["m", "w y", "w"] * "1,2,,3,4,,".split(',') #=> ["1", "2", "", "3", "4"] * "1,2,,3,4,,".split(',', 4) #=> ["1", "2", "", "3,4,,"] * "1,2,,3,4,,".split(',', -4) #=> ["1", "2", "", "3", "4", "", ""] */ static mrb_value mrb_str_split_m(mrb_state *mrb, mrb_value str) { mrb_int argc; mrb_value spat = mrb_nil_value(); enum {awk, string, regexp} split_type = string; mrb_int i = 0; mrb_int beg; mrb_int end; mrb_int lim = 0; mrb_bool lim_p; mrb_value result, tmp; argc = mrb_get_args(mrb, "|oi", &spat, &lim); lim_p = (lim > 0 && argc == 2); if (argc == 2) { if (lim == 1) { if (RSTRING_LEN(str) == 0) return mrb_ary_new_capa(mrb, 0); return mrb_ary_new_from_values(mrb, 1, &str); } i = 1; } if (argc == 0 || mrb_nil_p(spat)) { split_type = awk; } else { if (mrb_string_p(spat)) { split_type = string; if (RSTRING_LEN(spat) == 1 && RSTRING_PTR(spat)[0] == ' ') { split_type = awk; } } else { mrb_noregexp(mrb, str); } } result = mrb_ary_new(mrb); beg = 0; if (split_type == awk) { mrb_bool skip = TRUE; mrb_int idx = 0; mrb_int str_len = RSTRING_LEN(str); unsigned int c; int ai = mrb_gc_arena_save(mrb); idx = end = beg; while (idx < str_len) { c = (unsigned char)RSTRING_PTR(str)[idx++]; if (skip) { if (ISSPACE(c)) { beg = idx; } else { end = idx; skip = FALSE; if (lim_p && lim <= i) break; } } else if (ISSPACE(c)) { mrb_ary_push(mrb, result, byte_subseq(mrb, str, beg, end-beg)); mrb_gc_arena_restore(mrb, ai); skip = TRUE; beg = idx; if (lim_p) ++i; } else { end = idx; } } } else if (split_type == string) { mrb_int str_len = RSTRING_LEN(str); mrb_int pat_len = RSTRING_LEN(spat); mrb_int idx = 0; int ai = mrb_gc_arena_save(mrb); while (idx < str_len) { if (pat_len > 0) { end = mrb_memsearch(RSTRING_PTR(spat), pat_len, RSTRING_PTR(str)+idx, str_len - idx); if (end < 0) break; } else { end = chars2bytes(str, idx, 1); } mrb_ary_push(mrb, result, byte_subseq(mrb, str, idx, end)); mrb_gc_arena_restore(mrb, ai); idx += end + pat_len; if (lim_p && lim <= ++i) break; } beg = idx; } else { mrb_noregexp(mrb, str); } if (RSTRING_LEN(str) > 0 && (lim_p || RSTRING_LEN(str) > beg || lim < 0)) { if (RSTRING_LEN(str) == beg) { tmp = mrb_str_new_empty(mrb, str); } else { tmp = byte_subseq(mrb, str, beg, RSTRING_LEN(str)-beg); } mrb_ary_push(mrb, result, tmp); } if (!lim_p && lim == 0) { mrb_int len; while ((len = RARRAY_LEN(result)) > 0 && (tmp = RARRAY_PTR(result)[len-1], RSTRING_LEN(tmp) == 0)) mrb_ary_pop(mrb, result); } return result; } MRB_API mrb_value mrb_str_len_to_inum(mrb_state *mrb, const char *str, mrb_int len, mrb_int base, int badcheck) { const char *p = str; const char *pend = str + len; char sign = 1; int c; uint64_t n = 0; mrb_int val; #define conv_digit(c) \ (ISDIGIT(c) ? ((c) - '0') : \ ISLOWER(c) ? ((c) - 'a' + 10) : \ ISUPPER(c) ? ((c) - 'A' + 10) : \ -1) if (!p) { if (badcheck) goto bad; return mrb_fixnum_value(0); } while (p=pend) { if (badcheck) goto bad; return mrb_fixnum_value(0); } if (*p == '0') { /* squeeze preceding 0s */ p++; while (p= base) { break; } n *= base; n += c; if (n > (uint64_t)MRB_INT_MAX + (sign ? 0 : 1)) { #ifndef MRB_WITHOUT_FLOAT if (base == 10) { return mrb_float_value(mrb, mrb_str_to_dbl(mrb, mrb_str_new(mrb, str, len), badcheck)); } else #endif { mrb_raisef(mrb, E_ARGUMENT_ERROR, "string (%S) too big for integer", mrb_str_new(mrb, str, pend-str)); } } } val = (mrb_int)n; if (badcheck) { if (p == str) goto bad; /* no number */ while (p integer * * Returns the result of interpreting leading characters in str as an * integer base base (between 2 and 36). Extraneous characters past the * end of a valid number are ignored. If there is not a valid number at the * start of str, 0 is returned. This method never raises an * exception. * * "12345".to_i #=> 12345 * "99 red balloons".to_i #=> 99 * "0a".to_i #=> 0 * "0a".to_i(16) #=> 10 * "hello".to_i #=> 0 * "1100101".to_i(2) #=> 101 * "1100101".to_i(8) #=> 294977 * "1100101".to_i(10) #=> 1100101 * "1100101".to_i(16) #=> 17826049 */ static mrb_value mrb_str_to_i(mrb_state *mrb, mrb_value self) { mrb_int base = 10; mrb_get_args(mrb, "|i", &base); if (base < 0) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal radix %S", mrb_fixnum_value(base)); } return mrb_str_to_inum(mrb, self, base, FALSE); } #ifndef MRB_WITHOUT_FLOAT 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}; if (!p) return 0.0; while (ISSPACE(*p)) p++; if (!badcheck && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) { return 0.0; } d = mrb_float_read(p, &end); if (p == end) { if (badcheck) { bad: mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid string for float(%S)", mrb_str_new_cstr(mrb, p)); /* not reached */ } return d; } if (*end) { char *n = buf; char *e = buf + sizeof(buf) - 1; char prev = 0; while (p < end && n < e) prev = *n++ = *p++; while (*p) { if (*p == '_') { /* remove underscores between digits */ if (badcheck) { if (n == buf || !ISDIGIT(prev)) goto bad; ++p; if (!ISDIGIT(*p)) goto bad; } else { while (*++p == '_'); continue; } } prev = *p++; if (n < e) *n++ = prev; } *n = '\0'; p = buf; if (!badcheck && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) { return 0.0; } d = mrb_float_read(p, &end); if (badcheck) { if (!end || p == end) goto bad; while (*end && ISSPACE(*end)) end++; if (*end) goto bad; } } return d; } MRB_API double mrb_str_to_dbl(mrb_state *mrb, mrb_value str, mrb_bool badcheck) { char *s; mrb_int len; mrb_to_str(mrb, str); s = RSTRING_PTR(str); len = RSTRING_LEN(str); if (s) { if (badcheck && memchr(s, '\0', len)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "string for Float contains null byte"); } if (s[len]) { /* no sentinel somehow */ struct RString *temp_str = str_new(mrb, s, len); s = RSTR_PTR(temp_str); } } return mrb_cstr_to_dbl(mrb, s, badcheck); } /* 15.2.10.5.39 */ /* * call-seq: * str.to_f => float * * Returns the result of interpreting leading characters in str as a * floating point number. Extraneous characters past the end of a valid number * are ignored. If there is not a valid number at the start of str, * 0.0 is returned. This method never raises an exception. * * "123.45e1".to_f #=> 1234.5 * "45.67 degrees".to_f #=> 45.67 * "thx1138".to_f #=> 0.0 */ static mrb_value mrb_str_to_f(mrb_state *mrb, mrb_value self) { return mrb_float_value(mrb, mrb_str_to_dbl(mrb, self, FALSE)); } #endif /* 15.2.10.5.40 */ /* * call-seq: * str.to_s => str * * Returns the receiver. */ static mrb_value mrb_str_to_s(mrb_state *mrb, mrb_value self) { if (mrb_obj_class(mrb, self) != mrb->string_class) { return mrb_str_dup(mrb, self); } return self; } /* 15.2.10.5.43 */ /* * call-seq: * str.upcase! => str or nil * * Upcases the contents of str, returning nil if no changes * were made. */ static mrb_value mrb_str_upcase_bang(mrb_state *mrb, mrb_value str) { struct RString *s = mrb_str_ptr(str); char *p, *pend; mrb_bool modify = FALSE; mrb_str_modify(mrb, s); p = RSTRING_PTR(str); pend = RSTRING_END(str); while (p < pend) { if (ISLOWER(*p)) { *p = TOUPPER(*p); modify = TRUE; } p++; } if (modify) return str; return mrb_nil_value(); } /* 15.2.10.5.42 */ /* * call-seq: * str.upcase => new_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. * * "hEllO".upcase #=> "HELLO" */ static mrb_value mrb_str_upcase(mrb_state *mrb, mrb_value self) { mrb_value str; str = mrb_str_dup(mrb, self); mrb_str_upcase_bang(mrb, str); return str; } #define IS_EVSTR(p,e) ((p) < (e) && (*(p) == '$' || *(p) == '@' || *(p) == '{')) /* * call-seq: * str.dump -> new_str * * Produces a version of str with all nonprinting characters replaced by * \nnn notation and all special characters escaped. */ mrb_value mrb_str_dump(mrb_state *mrb, mrb_value str) { mrb_int len; const char *p, *pend; char *q; struct RString *result; len = 2; /* "" */ p = RSTRING_PTR(str); pend = p + RSTRING_LEN(str); while (p < pend) { unsigned char c = *p++; switch (c) { case '"': case '\\': case '\n': case '\r': case '\t': case '\f': case '\013': case '\010': case '\007': case '\033': len += 2; break; case '#': len += IS_EVSTR(p, pend) ? 2 : 1; break; default: if (ISPRINT(c)) { len++; } else { len += 4; /* \NNN */ } break; } } result = str_new(mrb, 0, len); str_with_class(mrb, result, str); p = RSTRING_PTR(str); pend = p + RSTRING_LEN(str); q = RSTR_PTR(result); *q++ = '"'; while (p < pend) { unsigned char c = *p++; switch (c) { case '"': case '\\': *q++ = '\\'; *q++ = c; break; case '\n': *q++ = '\\'; *q++ = 'n'; break; case '\r': *q++ = '\\'; *q++ = 'r'; break; case '\t': *q++ = '\\'; *q++ = 't'; break; case '\f': *q++ = '\\'; *q++ = 'f'; break; case '\013': *q++ = '\\'; *q++ = 'v'; break; case '\010': *q++ = '\\'; *q++ = 'b'; break; case '\007': *q++ = '\\'; *q++ = 'a'; break; case '\033': *q++ = '\\'; *q++ = 'e'; break; case '#': if (IS_EVSTR(p, pend)) *q++ = '\\'; *q++ = '#'; break; default: if (ISPRINT(c)) { *q++ = c; } else { *q++ = '\\'; *q++ = 'x'; q[1] = mrb_digitmap[c % 16]; c /= 16; q[0] = mrb_digitmap[c % 16]; q += 2; } } } *q = '"'; return mrb_obj_value(result); } MRB_API mrb_value mrb_str_cat(mrb_state *mrb, mrb_value str, const char *ptr, size_t len) { struct RString *s = mrb_str_ptr(str); size_t capa; size_t total; ptrdiff_t off = -1; if (len == 0) return str; mrb_str_modify(mrb, s); if (ptr >= RSTR_PTR(s) && ptr <= RSTR_PTR(s) + (size_t)RSTR_LEN(s)) { off = ptr - RSTR_PTR(s); } capa = RSTR_CAPA(s); total = RSTR_LEN(s)+len; if (total >= MRB_INT_MAX) { size_error: mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big"); } if (capa <= total) { if (capa == 0) capa = 1; while (capa <= total) { if (capa <= MRB_INT_MAX / 2) { capa *= 2; } else { capa = total+1; } } if (capa <= total || capa > MRB_INT_MAX) { goto size_error; } resize_capa(mrb, s, capa); } if (off != -1) { ptr = RSTR_PTR(s) + off; } memcpy(RSTR_PTR(s) + RSTR_LEN(s), ptr, len); mrb_assert_int_fit(size_t, total, mrb_int, MRB_INT_MAX); RSTR_SET_LEN(s, total); RSTR_PTR(s)[total] = '\0'; /* sentinel */ return str; } MRB_API mrb_value mrb_str_cat_cstr(mrb_state *mrb, mrb_value str, const char *ptr) { return mrb_str_cat(mrb, str, ptr, strlen(ptr)); } MRB_API mrb_value mrb_str_cat_str(mrb_state *mrb, mrb_value str, mrb_value str2) { if (mrb_str_ptr(str) == mrb_str_ptr(str2)) { mrb_str_modify(mrb, mrb_str_ptr(str)); } return mrb_str_cat(mrb, str, RSTRING_PTR(str2), RSTRING_LEN(str2)); } MRB_API mrb_value mrb_str_append(mrb_state *mrb, mrb_value str1, mrb_value str2) { mrb_to_str(mrb, str2); return mrb_str_cat_str(mrb, str1, str2); } #define CHAR_ESC_LEN 13 /* sizeof(\x{ hex of 32bit unsigned int } \0) */ /* * call-seq: * str.inspect -> string * * Returns a printable version of _str_, surrounded by quote marks, * with special characters escaped. * * str = "hello" * str[3] = "\b" * str.inspect #=> "\"hel\\bo\"" */ mrb_value mrb_str_inspect(mrb_state *mrb, mrb_value str) { const char *p, *pend; char buf[CHAR_ESC_LEN + 1]; mrb_value result = mrb_str_new_lit(mrb, "\""); p = RSTRING_PTR(str); pend = RSTRING_END(str); for (;p < pend; p++) { unsigned char c, cc; #ifdef MRB_UTF8_STRING mrb_int clen; clen = utf8len(p, pend); if (clen > 1) { mrb_int i; for (i=0; i array of fixnums * * Returns an array of bytes in _str_. * * str = "hello" * str.bytes #=> [104, 101, 108, 108, 111] */ static mrb_value mrb_str_bytes(mrb_state *mrb, mrb_value str) { struct RString *s = mrb_str_ptr(str); mrb_value a = mrb_ary_new_capa(mrb, RSTR_LEN(s)); unsigned char *p = (unsigned char *)(RSTR_PTR(s)), *pend = p + RSTR_LEN(s); while (p < pend) { mrb_ary_push(mrb, a, mrb_fixnum_value(p[0])); p++; } return a; } /* ---------------------------*/ void mrb_init_string(mrb_state *mrb) { struct RClass *s; mrb_static_assert(RSTRING_EMBED_LEN_MAX < (1 << 5), "pointer size too big for embedded string"); mrb->string_class = s = mrb_define_class(mrb, "String", mrb->object_class); /* 15.2.10 */ MRB_SET_INSTANCE_TT(s, MRB_TT_STRING); mrb_define_method(mrb, s, "bytesize", mrb_str_bytesize, MRB_ARGS_NONE()); mrb_define_method(mrb, s, "<=>", mrb_str_cmp_m, MRB_ARGS_REQ(1)); /* 15.2.10.5.1 */ mrb_define_method(mrb, s, "==", mrb_str_equal_m, MRB_ARGS_REQ(1)); /* 15.2.10.5.2 */ mrb_define_method(mrb, s, "+", mrb_str_plus_m, MRB_ARGS_REQ(1)); /* 15.2.10.5.4 */ mrb_define_method(mrb, s, "*", mrb_str_times, MRB_ARGS_REQ(1)); /* 15.2.10.5.5 */ mrb_define_method(mrb, s, "[]", mrb_str_aref_m, MRB_ARGS_ANY()); /* 15.2.10.5.6 */ mrb_define_method(mrb, s, "capitalize", mrb_str_capitalize, MRB_ARGS_NONE()); /* 15.2.10.5.7 */ mrb_define_method(mrb, s, "capitalize!", mrb_str_capitalize_bang, MRB_ARGS_NONE()); /* 15.2.10.5.8 */ mrb_define_method(mrb, s, "chomp", mrb_str_chomp, MRB_ARGS_ANY()); /* 15.2.10.5.9 */ mrb_define_method(mrb, s, "chomp!", mrb_str_chomp_bang, MRB_ARGS_ANY()); /* 15.2.10.5.10 */ mrb_define_method(mrb, s, "chop", mrb_str_chop, MRB_ARGS_NONE()); /* 15.2.10.5.11 */ mrb_define_method(mrb, s, "chop!", mrb_str_chop_bang, MRB_ARGS_NONE()); /* 15.2.10.5.12 */ mrb_define_method(mrb, s, "downcase", mrb_str_downcase, MRB_ARGS_NONE()); /* 15.2.10.5.13 */ mrb_define_method(mrb, s, "downcase!", mrb_str_downcase_bang, MRB_ARGS_NONE()); /* 15.2.10.5.14 */ mrb_define_method(mrb, s, "empty?", mrb_str_empty_p, MRB_ARGS_NONE()); /* 15.2.10.5.16 */ mrb_define_method(mrb, s, "eql?", mrb_str_eql, MRB_ARGS_REQ(1)); /* 15.2.10.5.17 */ mrb_define_method(mrb, s, "hash", mrb_str_hash_m, MRB_ARGS_NONE()); /* 15.2.10.5.20 */ mrb_define_method(mrb, s, "include?", mrb_str_include, MRB_ARGS_REQ(1)); /* 15.2.10.5.21 */ mrb_define_method(mrb, s, "index", mrb_str_index_m, MRB_ARGS_ANY()); /* 15.2.10.5.22 */ mrb_define_method(mrb, s, "initialize", mrb_str_init, MRB_ARGS_REQ(1)); /* 15.2.10.5.23 */ mrb_define_method(mrb, s, "initialize_copy", mrb_str_replace, MRB_ARGS_REQ(1)); /* 15.2.10.5.24 */ mrb_define_method(mrb, s, "intern", mrb_str_intern, MRB_ARGS_NONE()); /* 15.2.10.5.25 */ mrb_define_method(mrb, s, "length", mrb_str_size, MRB_ARGS_NONE()); /* 15.2.10.5.26 */ mrb_define_method(mrb, s, "replace", mrb_str_replace, MRB_ARGS_REQ(1)); /* 15.2.10.5.28 */ mrb_define_method(mrb, s, "reverse", mrb_str_reverse, MRB_ARGS_NONE()); /* 15.2.10.5.29 */ mrb_define_method(mrb, s, "reverse!", mrb_str_reverse_bang, MRB_ARGS_NONE()); /* 15.2.10.5.30 */ mrb_define_method(mrb, s, "rindex", mrb_str_rindex, MRB_ARGS_ANY()); /* 15.2.10.5.31 */ mrb_define_method(mrb, s, "size", mrb_str_size, MRB_ARGS_NONE()); /* 15.2.10.5.33 */ mrb_define_method(mrb, s, "slice", mrb_str_aref_m, MRB_ARGS_ANY()); /* 15.2.10.5.34 */ mrb_define_method(mrb, s, "split", mrb_str_split_m, MRB_ARGS_ANY()); /* 15.2.10.5.35 */ #ifndef MRB_WITHOUT_FLOAT mrb_define_method(mrb, s, "to_f", mrb_str_to_f, MRB_ARGS_NONE()); /* 15.2.10.5.38 */ #endif mrb_define_method(mrb, s, "to_i", mrb_str_to_i, MRB_ARGS_ANY()); /* 15.2.10.5.39 */ mrb_define_method(mrb, s, "to_s", mrb_str_to_s, MRB_ARGS_NONE()); /* 15.2.10.5.40 */ mrb_define_method(mrb, s, "to_sym", mrb_str_intern, MRB_ARGS_NONE()); /* 15.2.10.5.41 */ mrb_define_method(mrb, s, "upcase", mrb_str_upcase, MRB_ARGS_NONE()); /* 15.2.10.5.42 */ 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()); } #ifndef MRB_WITHOUT_FLOAT /* * Source code for the "strtod" library procedure. * * Copyright (c) 1988-1993 The Regents of the University of California. * Copyright (c) 1994 Sun Microsystems, Inc. * * Permission to use, copy, modify, and distribute this * software and its documentation for any purpose and without * fee is hereby granted, provided that the above copyright * notice appear in all copies. The University of California * makes no representations about the suitability of this * software for any purpose. It is provided "as is" without * express or implied warranty. * * RCS: @(#) $Id: strtod.c 11708 2007-02-12 23:01:19Z shyouhei $ */ #include #include static const int maxExponent = 511; /* Largest possible base 10 exponent. Any * exponent larger than this will already * produce underflow or overflow, so there's * no need to worry about additional digits. */ static const double powersOf10[] = {/* Table giving binary powers of 10. Entry */ 10., /* is 10^2^i. Used to convert decimal */ 100., /* exponents into floating-point numbers. */ 1.0e4, 1.0e8, 1.0e16, 1.0e32, 1.0e64, 1.0e128, 1.0e256 }; MRB_API double mrb_float_read(const char *string, char **endPtr) /* const char *string; A decimal ASCII floating-point number, * optionally preceded by white space. * Must have form "-I.FE-X", where I is the * integer part of the mantissa, F is the * fractional part of the mantissa, and X * is the exponent. Either of the signs * may be "+", "-", or omitted. Either I * or F may be omitted, or both. The decimal * point isn't necessary unless F is present. * The "E" may actually be an "e". E and X * may both be omitted (but not just one). */ /* char **endPtr; If non-NULL, store terminating character's * address here. */ { int sign, expSign = FALSE; double fraction, dblExp; const double *d; const char *p; int c; int exp = 0; /* Exponent read from "EX" field. */ int fracExp = 0; /* Exponent that derives from the fractional * part. Under normal circumstatnces, it is * the negative of the number of digits in F. * However, if I is very long, the last digits * of I get dropped (otherwise a long I with a * large negative exponent could cause an * unnecessary overflow on I alone). In this * case, fracExp is incremented one for each * dropped digit. */ int mantSize; /* Number of digits in mantissa. */ int decPt; /* Number of mantissa digits BEFORE decimal * point. */ const char *pExp; /* Temporarily holds location of exponent * in string. */ /* * Strip off leading blanks and check for a sign. */ p = string; while (isspace(*p)) { p += 1; } if (*p == '-') { sign = TRUE; p += 1; } else { if (*p == '+') { p += 1; } sign = FALSE; } /* * Count the number of digits in the mantissa (including the decimal * point), and also locate the decimal point. */ decPt = -1; for (mantSize = 0; ; mantSize += 1) { c = *p; if (!isdigit(c)) { if ((c != '.') || (decPt >= 0)) { break; } decPt = mantSize; } p += 1; } /* * Now suck up the digits in the mantissa. Use two integers to * collect 9 digits each (this is faster than using floating-point). * If the mantissa has more than 18 digits, ignore the extras, since * they can't affect the value anyway. */ pExp = p; p -= mantSize; if (decPt < 0) { decPt = mantSize; } else { mantSize -= 1; /* One of the digits was the point. */ } if (mantSize > 18) { if (decPt - 18 > 29999) { fracExp = 29999; } else { fracExp = decPt - 18; } mantSize = 18; } else { fracExp = decPt - mantSize; } if (mantSize == 0) { fraction = 0.0; p = string; goto done; } else { int frac1, frac2; frac1 = 0; for ( ; mantSize > 9; mantSize -= 1) { c = *p; p += 1; if (c == '.') { c = *p; p += 1; } frac1 = 10*frac1 + (c - '0'); } frac2 = 0; for (; mantSize > 0; mantSize -= 1) { c = *p; p += 1; if (c == '.') { c = *p; p += 1; } frac2 = 10*frac2 + (c - '0'); } fraction = (1.0e9 * frac1) + frac2; } /* * Skim off the exponent. */ p = pExp; if ((*p == 'E') || (*p == 'e')) { p += 1; if (*p == '-') { expSign = TRUE; p += 1; } else { if (*p == '+') { p += 1; } expSign = FALSE; } while (isdigit(*p)) { exp = exp * 10 + (*p - '0'); if (exp > 19999) { exp = 19999; } p += 1; } } if (expSign) { exp = fracExp - exp; } else { exp = fracExp + exp; } /* * Generate a floating-point number that represents the exponent. * Do this by processing the exponent one bit at a time to combine * many powers of 2 of 10. Then combine the exponent with the * fraction. */ if (exp < 0) { expSign = TRUE; exp = -exp; } else { expSign = FALSE; } if (exp > maxExponent) { exp = maxExponent; errno = ERANGE; } dblExp = 1.0; for (d = powersOf10; exp != 0; exp >>= 1, d += 1) { if (exp & 01) { dblExp *= *d; } } if (expSign) { fraction /= dblExp; } else { fraction *= dblExp; } done: if (endPtr != NULL) { *endPtr = (char *) p; } if (sign) { return -fraction; } return fraction; } #endif mruby-2.0.0/src/symbol.c000066400000000000000000000265701340361412400151170ustar00rootroot00000000000000/* ** symbol.c - Symbol class ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include /* ------------------------------------------------------ */ typedef struct symbol_name { mrb_bool lit : 1; uint16_t len; const char *name; } symbol_name; static inline khint_t sym_hash_func(mrb_state *mrb, mrb_sym s) { khint_t h = 0; size_t i, len = mrb->symtbl[s].len; const char *p = mrb->symtbl[s].name; for (i=0; isymtbl[a].len == mrb->symtbl[b].len && memcmp(mrb->symtbl[a].name, mrb->symtbl[b].name, mrb->symtbl[a].len) == 0) KHASH_DECLARE(n2s, mrb_sym, mrb_sym, FALSE) KHASH_DEFINE (n2s, mrb_sym, mrb_sym, FALSE, sym_hash_func, sym_hash_equal) /* ------------------------------------------------------ */ static void sym_validate_len(mrb_state *mrb, size_t len) { if (len >= RITE_LV_NULL_MARK) { mrb_raise(mrb, E_ARGUMENT_ERROR, "symbol length too long"); } } static mrb_sym sym_intern(mrb_state *mrb, const char *name, size_t len, mrb_bool lit) { khash_t(n2s) *h = mrb->name2sym; symbol_name *sname = mrb->symtbl; /* symtbl[0] for working memory */ khiter_t k; mrb_sym sym; char *p; sym_validate_len(mrb, len); if (sname) { sname->lit = lit; sname->len = (uint16_t)len; sname->name = name; k = kh_get(n2s, mrb, h, 0); if (k != kh_end(h)) return kh_key(h, k); } /* registering a new symbol */ sym = ++mrb->symidx; if (mrb->symcapa < sym) { if (mrb->symcapa == 0) mrb->symcapa = 100; else mrb->symcapa = (size_t)(mrb->symcapa * 6 / 5); mrb->symtbl = (symbol_name*)mrb_realloc(mrb, mrb->symtbl, sizeof(symbol_name)*(mrb->symcapa+1)); } sname = &mrb->symtbl[sym]; sname->len = (uint16_t)len; if (lit || mrb_ro_data_p(name)) { sname->name = name; sname->lit = TRUE; } else { p = (char *)mrb_malloc(mrb, len+1); memcpy(p, name, len); p[len] = 0; sname->name = (const char*)p; sname->lit = FALSE; } kh_put(n2s, mrb, h, sym); return sym; } MRB_API mrb_sym mrb_intern(mrb_state *mrb, const char *name, size_t len) { return sym_intern(mrb, name, len, FALSE); } MRB_API mrb_sym mrb_intern_static(mrb_state *mrb, const char *name, size_t len) { return sym_intern(mrb, name, len, TRUE); } MRB_API mrb_sym mrb_intern_cstr(mrb_state *mrb, const char *name) { return mrb_intern(mrb, name, strlen(name)); } MRB_API mrb_sym mrb_intern_str(mrb_state *mrb, mrb_value str) { return mrb_intern(mrb, RSTRING_PTR(str), RSTRING_LEN(str)); } MRB_API mrb_value mrb_check_intern(mrb_state *mrb, const char *name, size_t len) { khash_t(n2s) *h = mrb->name2sym; symbol_name *sname = mrb->symtbl; khiter_t k; sym_validate_len(mrb, len); sname->len = (uint16_t)len; sname->name = name; k = kh_get(n2s, mrb, h, 0); if (k != kh_end(h)) { return mrb_symbol_value(kh_key(h, k)); } return mrb_nil_value(); } MRB_API mrb_value mrb_check_intern_cstr(mrb_state *mrb, const char *name) { return mrb_check_intern(mrb, name, (mrb_int)strlen(name)); } MRB_API mrb_value mrb_check_intern_str(mrb_state *mrb, mrb_value str) { return mrb_check_intern(mrb, RSTRING_PTR(str), RSTRING_LEN(str)); } /* lenp must be a pointer to a size_t variable */ MRB_API const char* mrb_sym2name_len(mrb_state *mrb, mrb_sym sym, mrb_int *lenp) { if (sym == 0 || mrb->symidx < sym) { if (lenp) *lenp = 0; return NULL; } if (lenp) *lenp = mrb->symtbl[sym].len; return mrb->symtbl[sym].name; } void mrb_free_symtbl(mrb_state *mrb) { mrb_sym i, lim; for (i=1, lim=mrb->symidx+1; isymtbl[i].lit) { mrb_free(mrb, (char*)mrb->symtbl[i].name); } } mrb_free(mrb, mrb->symtbl); kh_destroy(n2s, mrb, mrb->name2sym); } void mrb_init_symtbl(mrb_state *mrb) { mrb->name2sym = kh_init(n2s, mrb); } /********************************************************************** * Document-class: Symbol * * Symbol objects represent names and some strings * inside the Ruby * interpreter. They are generated using the :name and * :"string" literals * syntax, and by the various to_sym methods. The same * Symbol object will be created for a given name or string * for the duration of a program's execution, regardless of the context * or meaning of that name. Thus if Fred is a constant in * one context, a method in another, and a class in a third, the * Symbol :Fred will be the same object in * all three contexts. * * module One * class Fred * end * $f1 = :Fred * end * module Two * Fred = 1 * $f2 = :Fred * end * def Fred() * end * $f3 = :Fred * $f1.object_id #=> 2514190 * $f2.object_id #=> 2514190 * $f3.object_id #=> 2514190 * */ /* 15.2.11.3.1 */ /* * call-seq: * sym == obj -> true or false * * Equality---If sym and obj are exactly the same * symbol, returns true. */ static mrb_value sym_equal(mrb_state *mrb, mrb_value sym1) { mrb_value sym2; mrb_get_args(mrb, "o", &sym2); return mrb_bool_value(mrb_obj_equal(mrb, sym1, sym2)); } /* 15.2.11.3.2 */ /* 15.2.11.3.3 */ /* * call-seq: * sym.id2name -> string * sym.to_s -> string * * Returns the name or string corresponding to sym. * * :fred.id2name #=> "fred" */ static mrb_value mrb_sym_to_s(mrb_state *mrb, mrb_value sym) { mrb_sym id = mrb_symbol(sym); const char *p; mrb_int len; p = mrb_sym2name_len(mrb, id, &len); return mrb_str_new_static(mrb, p, len); } /* 15.2.11.3.4 */ /* * call-seq: * sym.to_sym -> sym * sym.intern -> sym * * In general, to_sym returns the Symbol corresponding * to an object. As sym is already a symbol, self is returned * in this case. */ static mrb_value sym_to_sym(mrb_state *mrb, mrb_value sym) { return sym; } /* 15.2.11.3.5(x) */ /* * call-seq: * sym.inspect -> string * * Returns the representation of sym as a symbol literal. * * :fred.inspect #=> ":fred" */ #if __STDC__ # define SIGN_EXTEND_CHAR(c) ((signed char)(c)) #else /* not __STDC__ */ /* As in Harbison and Steele. */ # define SIGN_EXTEND_CHAR(c) ((((unsigned char)(c)) ^ 128) - 128) #endif #define is_identchar(c) (SIGN_EXTEND_CHAR(c)!=-1&&(ISALNUM(c) || (c) == '_')) static mrb_bool is_special_global_name(const char* m) { switch (*m) { case '~': case '*': case '$': case '?': case '!': case '@': case '/': case '\\': case ';': case ',': case '.': case '=': case ':': case '<': case '>': case '\"': case '&': case '`': case '\'': case '+': case '0': ++m; break; case '-': ++m; if (is_identchar(*m)) m += 1; break; default: if (!ISDIGIT(*m)) return FALSE; do ++m; while (ISDIGIT(*m)); break; } return !*m; } static mrb_bool symname_p(const char *name) { const char *m = name; mrb_bool localid = FALSE; if (!m) return FALSE; switch (*m) { case '\0': return FALSE; case '$': if (is_special_global_name(++m)) return TRUE; goto id; case '@': if (*++m == '@') ++m; goto id; case '<': switch (*++m) { case '<': ++m; break; case '=': if (*++m == '>') ++m; break; default: break; } break; case '>': switch (*++m) { case '>': case '=': ++m; break; default: break; } break; case '=': switch (*++m) { case '~': ++m; break; case '=': if (*++m == '=') ++m; break; default: return FALSE; } break; case '*': if (*++m == '*') ++m; break; case '!': switch (*++m) { case '=': case '~': ++m; } break; case '+': case '-': if (*++m == '@') ++m; break; case '|': if (*++m == '|') ++m; break; case '&': if (*++m == '&') ++m; break; case '^': case '/': case '%': case '~': case '`': ++m; break; case '[': if (*++m != ']') return FALSE; if (*++m == '=') ++m; break; default: localid = !ISUPPER(*m); id: if (*m != '_' && !ISALPHA(*m)) return FALSE; while (is_identchar(*m)) m += 1; if (localid) { switch (*m) { case '!': case '?': case '=': ++m; default: break; } } break; } return *m ? FALSE : TRUE; } static mrb_value sym_inspect(mrb_state *mrb, mrb_value sym) { mrb_value str; const char *name; mrb_int len; mrb_sym id = mrb_symbol(sym); char *sp; name = mrb_sym2name_len(mrb, id, &len); str = mrb_str_new(mrb, 0, len+1); sp = RSTRING_PTR(str); RSTRING_PTR(str)[0] = ':'; memcpy(sp+1, name, len); mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX); if (!symname_p(name) || strlen(name) != (size_t)len) { str = mrb_str_dump(mrb, str); sp = RSTRING_PTR(str); sp[0] = ':'; sp[1] = '"'; } return str; } MRB_API mrb_value mrb_sym2str(mrb_state *mrb, mrb_sym sym) { mrb_int len; const char *name = mrb_sym2name_len(mrb, sym, &len); if (!name) return mrb_undef_value(); /* can't happen */ return mrb_str_new_static(mrb, name, len); } MRB_API const char* mrb_sym2name(mrb_state *mrb, mrb_sym sym) { mrb_int len; const char *name = mrb_sym2name_len(mrb, sym, &len); if (!name) return NULL; if (symname_p(name) && strlen(name) == (size_t)len) { return name; } else { mrb_value str = mrb_str_dump(mrb, mrb_str_new_static(mrb, name, len)); return RSTRING_PTR(str); } } #define lesser(a,b) (((a)>(b))?(b):(a)) static mrb_value sym_cmp(mrb_state *mrb, mrb_value s1) { mrb_value s2; mrb_sym sym1, sym2; mrb_get_args(mrb, "o", &s2); if (mrb_type(s2) != MRB_TT_SYMBOL) return mrb_nil_value(); sym1 = mrb_symbol(s1); sym2 = mrb_symbol(s2); if (sym1 == sym2) return mrb_fixnum_value(0); else { const char *p1, *p2; int retval; mrb_int len, len1, len2; p1 = mrb_sym2name_len(mrb, sym1, &len1); p2 = mrb_sym2name_len(mrb, sym2, &len2); len = lesser(len1, len2); retval = memcmp(p1, p2, len); if (retval == 0) { if (len1 == len2) return mrb_fixnum_value(0); if (len1 > len2) return mrb_fixnum_value(1); return mrb_fixnum_value(-1); } if (retval > 0) return mrb_fixnum_value(1); return mrb_fixnum_value(-1); } } void mrb_init_symbol(mrb_state *mrb) { struct RClass *sym; mrb->symbol_class = sym = mrb_define_class(mrb, "Symbol", mrb->object_class); /* 15.2.11 */ MRB_SET_INSTANCE_TT(sym, MRB_TT_SYMBOL); mrb_undef_class_method(mrb, sym, "new"); mrb_define_method(mrb, sym, "===", sym_equal, MRB_ARGS_REQ(1)); /* 15.2.11.3.1 */ mrb_define_method(mrb, sym, "id2name", mrb_sym_to_s, MRB_ARGS_NONE()); /* 15.2.11.3.2 */ mrb_define_method(mrb, sym, "to_s", mrb_sym_to_s, MRB_ARGS_NONE()); /* 15.2.11.3.3 */ mrb_define_method(mrb, sym, "to_sym", sym_to_sym, MRB_ARGS_NONE()); /* 15.2.11.3.4 */ mrb_define_method(mrb, sym, "inspect", sym_inspect, MRB_ARGS_NONE()); /* 15.2.11.3.5(x) */ mrb_define_method(mrb, sym, "<=>", sym_cmp, MRB_ARGS_REQ(1)); } mruby-2.0.0/src/value_array.h000066400000000000000000000006421340361412400161210ustar00rootroot00000000000000#ifndef MRB_VALUE_ARRAY_H__ #define MRB_VALUE_ARRAY_H__ #include static inline void value_move(mrb_value *s1, const mrb_value *s2, size_t n) { if (s1 > s2 && s1 < s2 + n) { s1 += n; s2 += n; while (n-- > 0) { *--s1 = *--s2; } } else if (s1 != s2) { while (n-- > 0) { *s1++ = *s2++; } } else { /* nothing to do. */ } } #endif /* MRB_VALUE_ARRAY_H__ */ mruby-2.0.0/src/variable.c000066400000000000000000000554361340361412400154020ustar00rootroot00000000000000/* ** variable.c - mruby variables ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include typedef int (iv_foreach_func)(mrb_state*,mrb_sym,mrb_value,void*); #ifndef MRB_IV_SEGMENT_SIZE #define MRB_IV_SEGMENT_SIZE 4 #endif typedef struct segment { mrb_sym key[MRB_IV_SEGMENT_SIZE]; mrb_value val[MRB_IV_SEGMENT_SIZE]; struct segment *next; } segment; /* Instance variable table structure */ typedef struct iv_tbl { segment *rootseg; size_t size; size_t last_len; } iv_tbl; /* Creates the instance variable table. */ static iv_tbl* iv_new(mrb_state *mrb) { iv_tbl *t; t = (iv_tbl*)mrb_malloc(mrb, sizeof(iv_tbl)); t->size = 0; t->rootseg = NULL; t->last_len = 0; return t; } /* Set the value for the symbol in the instance variable table. */ static void iv_put(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value val) { segment *seg; segment *prev = NULL; segment *matched_seg = NULL; size_t matched_idx = 0; size_t i; if (t == NULL) return; seg = t->rootseg; while (seg) { for (i=0; ikey[i]; /* Found room in last segment after last_len */ if (!seg->next && i >= t->last_len) { seg->key[i] = sym; seg->val[i] = val; t->last_len = i+1; t->size++; return; } if (!matched_seg && key == 0) { matched_seg = seg; matched_idx = i; } else if (key == sym) { seg->val[i] = val; return; } } prev = seg; seg = seg->next; } /* Not found */ t->size++; if (matched_seg) { matched_seg->key[matched_idx] = sym; matched_seg->val[matched_idx] = val; return; } seg = (segment*)mrb_malloc(mrb, sizeof(segment)); if (!seg) return; seg->next = NULL; seg->key[0] = sym; seg->val[0] = val; t->last_len = 1; if (prev) { prev->next = seg; } else { t->rootseg = seg; } } /* Get a value for a symbol from the instance variable table. */ static mrb_bool iv_get(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp) { segment *seg; size_t i; if (t == NULL) return FALSE; seg = t->rootseg; while (seg) { for (i=0; ikey[i]; if (!seg->next && i >= t->last_len) { return FALSE; } if (key == sym) { if (vp) *vp = seg->val[i]; return TRUE; } } seg = seg->next; } return FALSE; } /* Deletes the value for the symbol from the instance variable table. */ static mrb_bool iv_del(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp) { segment *seg; size_t i; if (t == NULL) return FALSE; seg = t->rootseg; while (seg) { for (i=0; ikey[i]; if (!seg->next && i >= t->last_len) { return FALSE; } if (key == sym) { t->size--; seg->key[i] = 0; if (vp) *vp = seg->val[i]; return TRUE; } } seg = seg->next; } return FALSE; } /* Iterates over the instance variable table. */ static mrb_bool iv_foreach(mrb_state *mrb, iv_tbl *t, iv_foreach_func *func, void *p) { segment *seg; size_t i; int n; if (t == NULL) return TRUE; seg = t->rootseg; while (seg) { for (i=0; ikey[i]; /* no value in last segment after last_len */ if (!seg->next && i >= t->last_len) { return FALSE; } if (key != 0) { n =(*func)(mrb, key, seg->val[i], p); if (n > 0) return FALSE; if (n < 0) { t->size--; seg->key[i] = 0; } } } seg = seg->next; } return TRUE; } /* Get the size of the instance variable table. */ static size_t iv_size(mrb_state *mrb, iv_tbl *t) { segment *seg; size_t size = 0; if (t == NULL) return 0; if (t->size > 0) return t->size; seg = t->rootseg; while (seg) { if (seg->next == NULL) { size += t->last_len; return size; } seg = seg->next; size += MRB_IV_SEGMENT_SIZE; } /* empty iv_tbl */ return 0; } /* Copy the instance variable table. */ static iv_tbl* iv_copy(mrb_state *mrb, iv_tbl *t) { segment *seg; iv_tbl *t2; size_t i; seg = t->rootseg; t2 = iv_new(mrb); while (seg != NULL) { for (i=0; ikey[i]; mrb_value val = seg->val[i]; if ((seg->next == NULL) && (i >= t->last_len)) { return t2; } iv_put(mrb, t2, key, val); } seg = seg->next; } return t2; } /* Free memory of the instance variable table. */ static void iv_free(mrb_state *mrb, iv_tbl *t) { segment *seg; seg = t->rootseg; while (seg) { segment *p = seg; seg = seg->next; mrb_free(mrb, p); } mrb_free(mrb, t); } static int iv_mark_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p) { mrb_gc_mark_value(mrb, v); return 0; } static void mark_tbl(mrb_state *mrb, iv_tbl *t) { iv_foreach(mrb, t, iv_mark_i, 0); } void mrb_gc_mark_gv(mrb_state *mrb) { mark_tbl(mrb, mrb->globals); } void mrb_gc_free_gv(mrb_state *mrb) { if (mrb->globals) iv_free(mrb, mrb->globals); } void mrb_gc_mark_iv(mrb_state *mrb, struct RObject *obj) { mark_tbl(mrb, obj->iv); } size_t mrb_gc_mark_iv_size(mrb_state *mrb, struct RObject *obj) { return iv_size(mrb, obj->iv); } void mrb_gc_free_iv(mrb_state *mrb, struct RObject *obj) { if (obj->iv) { iv_free(mrb, obj->iv); } } mrb_value mrb_vm_special_get(mrb_state *mrb, mrb_sym i) { return mrb_fixnum_value(0); } void mrb_vm_special_set(mrb_state *mrb, mrb_sym i, mrb_value v) { } static mrb_bool obj_iv_p(mrb_value obj) { switch (mrb_type(obj)) { case MRB_TT_OBJECT: case MRB_TT_CLASS: case MRB_TT_MODULE: case MRB_TT_SCLASS: case MRB_TT_HASH: case MRB_TT_DATA: case MRB_TT_EXCEPTION: return TRUE; default: return FALSE; } } MRB_API mrb_value mrb_obj_iv_get(mrb_state *mrb, struct RObject *obj, mrb_sym sym) { mrb_value v; if (obj->iv && iv_get(mrb, obj->iv, sym, &v)) return v; return mrb_nil_value(); } MRB_API mrb_value mrb_iv_get(mrb_state *mrb, mrb_value obj, mrb_sym sym) { if (obj_iv_p(obj)) { return mrb_obj_iv_get(mrb, mrb_obj_ptr(obj), sym); } return mrb_nil_value(); } static inline void assign_class_name(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v); MRB_API void mrb_obj_iv_set(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v) { iv_tbl *t; if (MRB_FROZEN_P(obj)) { mrb_raisef(mrb, E_FROZEN_ERROR, "can't modify frozen %S", mrb_obj_value(obj)); } assign_class_name(mrb, obj, sym, v); if (!obj->iv) { obj->iv = iv_new(mrb); } t = obj->iv; iv_put(mrb, t, sym, v); mrb_write_barrier(mrb, (struct RBasic*)obj); } static inline mrb_bool namespace_p(enum mrb_vtype tt) { return tt == MRB_TT_CLASS || tt == MRB_TT_MODULE ? TRUE : FALSE; } static inline void assign_class_name(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v) { if (namespace_p(obj->tt) && namespace_p(mrb_type(v))) { struct RObject *c = mrb_obj_ptr(v); if (obj != c && ISUPPER(mrb_sym2name(mrb, sym)[0])) { mrb_sym id_classname = mrb_intern_lit(mrb, "__classname__"); mrb_value o = mrb_obj_iv_get(mrb, c, id_classname); if (mrb_nil_p(o)) { mrb_sym id_outer = mrb_intern_lit(mrb, "__outer__"); o = mrb_obj_iv_get(mrb, c, id_outer); if (mrb_nil_p(o)) { if ((struct RClass *)obj == mrb->object_class) { mrb_obj_iv_set(mrb, c, id_classname, mrb_symbol_value(sym)); } else { mrb_obj_iv_set(mrb, c, id_outer, mrb_obj_value(obj)); } } } } } } MRB_API void mrb_iv_set(mrb_state *mrb, mrb_value obj, mrb_sym sym, mrb_value v) { if (obj_iv_p(obj)) { mrb_obj_iv_set(mrb, mrb_obj_ptr(obj), sym, v); } else { mrb_raise(mrb, E_ARGUMENT_ERROR, "cannot set instance variable"); } } MRB_API mrb_bool mrb_obj_iv_defined(mrb_state *mrb, struct RObject *obj, mrb_sym sym) { iv_tbl *t; t = obj->iv; if (t) { return iv_get(mrb, t, sym, NULL); } return FALSE; } MRB_API mrb_bool mrb_iv_defined(mrb_state *mrb, mrb_value obj, mrb_sym sym) { if (!obj_iv_p(obj)) return FALSE; return mrb_obj_iv_defined(mrb, mrb_obj_ptr(obj), sym); } #define identchar(c) (ISALNUM(c) || (c) == '_' || !ISASCII(c)) MRB_API mrb_bool mrb_iv_name_sym_p(mrb_state *mrb, mrb_sym iv_name) { const char *s; mrb_int i, len; s = mrb_sym2name_len(mrb, iv_name, &len); if (len < 2) return FALSE; if (s[0] != '@') return FALSE; if (s[1] == '@') return FALSE; for (i=1; iiv) { iv_free(mrb, d->iv); d->iv = 0; } if (s->iv) { mrb_write_barrier(mrb, (struct RBasic*)d); d->iv = iv_copy(mrb, s->iv); } } static int inspect_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p) { mrb_value str = *(mrb_value*)p; const char *s; mrb_int len; mrb_value ins; char *sp = RSTRING_PTR(str); /* need not to show internal data */ if (sp[0] == '-') { /* first element */ sp[0] = '#'; mrb_str_cat_lit(mrb, str, " "); } else { mrb_str_cat_lit(mrb, str, ", "); } s = mrb_sym2name_len(mrb, sym, &len); mrb_str_cat(mrb, str, s, len); mrb_str_cat_lit(mrb, str, "="); if (mrb_type(v) == MRB_TT_OBJECT) { ins = mrb_any_to_s(mrb, v); } else { ins = mrb_inspect(mrb, v); } mrb_str_cat_str(mrb, str, ins); return 0; } mrb_value mrb_obj_iv_inspect(mrb_state *mrb, struct RObject *obj) { iv_tbl *t = obj->iv; size_t len = iv_size(mrb, t); if (len > 0) { const char *cn = mrb_obj_classname(mrb, mrb_obj_value(obj)); mrb_value str = mrb_str_new_capa(mrb, 30); mrb_str_cat_lit(mrb, str, "-<"); mrb_str_cat_cstr(mrb, str, cn); mrb_str_cat_lit(mrb, str, ":"); mrb_str_concat(mrb, str, mrb_ptr_to_str(mrb, obj)); iv_foreach(mrb, t, inspect_i, &str); mrb_str_cat_lit(mrb, str, ">"); return str; } return mrb_any_to_s(mrb, mrb_obj_value(obj)); } MRB_API mrb_value mrb_iv_remove(mrb_state *mrb, mrb_value obj, mrb_sym sym) { if (obj_iv_p(obj)) { iv_tbl *t = mrb_obj_ptr(obj)->iv; mrb_value val; if (iv_del(mrb, t, sym, &val)) { return val; } } return mrb_undef_value(); } static int iv_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p) { mrb_value ary; const char* s; mrb_int len; ary = *(mrb_value*)p; s = mrb_sym2name_len(mrb, sym, &len); if (len > 1 && s[0] == '@' && s[1] != '@') { mrb_ary_push(mrb, ary, mrb_symbol_value(sym)); } return 0; } /* 15.3.1.3.23 */ /* * call-seq: * obj.instance_variables -> array * * Returns an array of instance variable names for the receiver. Note * that simply defining an accessor does not create the corresponding * instance variable. * * class Fred * attr_accessor :a1 * def initialize * @iv = 3 * end * end * Fred.new.instance_variables #=> [:@iv] */ mrb_value mrb_obj_instance_variables(mrb_state *mrb, mrb_value self) { mrb_value ary; ary = mrb_ary_new(mrb); if (obj_iv_p(self)) { iv_foreach(mrb, mrb_obj_ptr(self)->iv, iv_i, &ary); } return ary; } static int cv_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p) { mrb_value ary; const char* s; mrb_int len; ary = *(mrb_value*)p; s = mrb_sym2name_len(mrb, sym, &len); if (len > 2 && s[0] == '@' && s[1] == '@') { mrb_ary_push(mrb, ary, mrb_symbol_value(sym)); } return 0; } /* 15.2.2.4.19 */ /* * call-seq: * mod.class_variables -> array * * Returns an array of the names of class variables in mod. * * class One * @@var1 = 1 * end * class Two < One * @@var2 = 2 * end * One.class_variables #=> [:@@var1] * Two.class_variables #=> [:@@var2] */ mrb_value mrb_mod_class_variables(mrb_state *mrb, mrb_value mod) { mrb_value ary; struct RClass *c; ary = mrb_ary_new(mrb); c = mrb_class_ptr(mod); while (c) { iv_foreach(mrb, c->iv, cv_i, &ary); c = c->super; } return ary; } MRB_API mrb_value mrb_mod_cv_get(mrb_state *mrb, struct RClass *c, mrb_sym sym) { struct RClass * cls = c; mrb_value v; int given = FALSE; while (c) { if (c->iv && iv_get(mrb, c->iv, sym, &v)) { given = TRUE; } c = c->super; } if (given) return v; if (cls && cls->tt == MRB_TT_SCLASS) { mrb_value klass; klass = mrb_obj_iv_get(mrb, (struct RObject *)cls, mrb_intern_lit(mrb, "__attached__")); c = mrb_class_ptr(klass); if (c->tt == MRB_TT_CLASS || c->tt == MRB_TT_MODULE) { given = FALSE; while (c) { if (c->iv && iv_get(mrb, c->iv, sym, &v)) { given = TRUE; } c = c->super; } if (given) return v; } } mrb_name_error(mrb, sym, "uninitialized class variable %S in %S", mrb_sym2str(mrb, sym), mrb_obj_value(cls)); /* not reached */ return mrb_nil_value(); } MRB_API mrb_value mrb_cv_get(mrb_state *mrb, mrb_value mod, mrb_sym sym) { return mrb_mod_cv_get(mrb, mrb_class_ptr(mod), sym); } MRB_API void mrb_mod_cv_set(mrb_state *mrb, struct RClass *c, mrb_sym sym, mrb_value v) { struct RClass * cls = c; while (c) { iv_tbl *t = c->iv; if (iv_get(mrb, t, sym, NULL)) { iv_put(mrb, t, sym, v); mrb_write_barrier(mrb, (struct RBasic*)c); return; } c = c->super; } if (cls && cls->tt == MRB_TT_SCLASS) { mrb_value klass; klass = mrb_obj_iv_get(mrb, (struct RObject*)cls, mrb_intern_lit(mrb, "__attached__")); switch (mrb_type(klass)) { case MRB_TT_CLASS: case MRB_TT_MODULE: case MRB_TT_SCLASS: c = mrb_class_ptr(klass); break; default: c = cls; break; } } else{ c = cls; } if (!c->iv) { c->iv = iv_new(mrb); } iv_put(mrb, c->iv, sym, v); mrb_write_barrier(mrb, (struct RBasic*)c); } MRB_API void mrb_cv_set(mrb_state *mrb, mrb_value mod, mrb_sym sym, mrb_value v) { mrb_mod_cv_set(mrb, mrb_class_ptr(mod), sym, v); } MRB_API mrb_bool mrb_mod_cv_defined(mrb_state *mrb, struct RClass * c, mrb_sym sym) { while (c) { iv_tbl *t = c->iv; if (iv_get(mrb, t, sym, NULL)) return TRUE; c = c->super; } return FALSE; } MRB_API mrb_bool mrb_cv_defined(mrb_state *mrb, mrb_value mod, mrb_sym sym) { return mrb_mod_cv_defined(mrb, mrb_class_ptr(mod), sym); } mrb_value mrb_vm_cv_get(mrb_state *mrb, mrb_sym sym) { struct RClass *c; c = MRB_PROC_TARGET_CLASS(mrb->c->ci->proc); return mrb_mod_cv_get(mrb, c, sym); } void mrb_vm_cv_set(mrb_state *mrb, mrb_sym sym, mrb_value v) { struct RClass *c; c = MRB_PROC_TARGET_CLASS(mrb->c->ci->proc); mrb_mod_cv_set(mrb, c, sym, v); } static void mod_const_check(mrb_state *mrb, mrb_value mod) { switch (mrb_type(mod)) { case MRB_TT_CLASS: case MRB_TT_MODULE: case MRB_TT_SCLASS: break; default: mrb_raise(mrb, E_TYPE_ERROR, "constant look-up for non class/module"); break; } } static mrb_value const_get(mrb_state *mrb, struct RClass *base, mrb_sym sym) { struct RClass *c = base; mrb_value v; mrb_bool retry = FALSE; mrb_value name; L_RETRY: while (c) { if (c->iv) { if (iv_get(mrb, c->iv, sym, &v)) return v; } c = c->super; } if (!retry && base->tt == MRB_TT_MODULE) { c = mrb->object_class; retry = TRUE; goto L_RETRY; } name = mrb_symbol_value(sym); return mrb_funcall_argv(mrb, mrb_obj_value(base), mrb_intern_lit(mrb, "const_missing"), 1, &name); } MRB_API mrb_value mrb_const_get(mrb_state *mrb, mrb_value mod, mrb_sym sym) { mod_const_check(mrb, mod); return const_get(mrb, mrb_class_ptr(mod), sym); } mrb_value mrb_vm_const_get(mrb_state *mrb, mrb_sym sym) { struct RClass *c; struct RClass *c2; mrb_value v; struct RProc *proc; c = MRB_PROC_TARGET_CLASS(mrb->c->ci->proc); if (iv_get(mrb, c->iv, sym, &v)) { return v; } c2 = c; while (c2 && c2->tt == MRB_TT_SCLASS) { mrb_value klass; if (!iv_get(mrb, c2->iv, mrb_intern_lit(mrb, "__attached__"), &klass)) { c2 = NULL; break; } c2 = mrb_class_ptr(klass); } if (c2 && (c2->tt == MRB_TT_CLASS || c2->tt == MRB_TT_MODULE)) c = c2; mrb_assert(!MRB_PROC_CFUNC_P(mrb->c->ci->proc)); proc = mrb->c->ci->proc; while (proc) { c2 = MRB_PROC_TARGET_CLASS(proc); if (c2 && iv_get(mrb, c2->iv, sym, &v)) { return v; } proc = proc->upper; } return const_get(mrb, c, sym); } MRB_API void mrb_const_set(mrb_state *mrb, mrb_value mod, mrb_sym sym, mrb_value v) { mod_const_check(mrb, mod); if (mrb_type(v) == MRB_TT_CLASS || mrb_type(v) == MRB_TT_MODULE) { mrb_class_name_class(mrb, mrb_class_ptr(mod), mrb_class_ptr(v), sym); } mrb_iv_set(mrb, mod, sym, v); } void mrb_vm_const_set(mrb_state *mrb, mrb_sym sym, mrb_value v) { struct RClass *c; c = MRB_PROC_TARGET_CLASS(mrb->c->ci->proc); mrb_obj_iv_set(mrb, (struct RObject*)c, sym, v); } MRB_API void mrb_const_remove(mrb_state *mrb, mrb_value mod, mrb_sym sym) { mod_const_check(mrb, mod); mrb_iv_remove(mrb, mod, sym); } MRB_API void mrb_define_const(mrb_state *mrb, struct RClass *mod, const char *name, mrb_value v) { mrb_obj_iv_set(mrb, (struct RObject*)mod, mrb_intern_cstr(mrb, name), v); } MRB_API void mrb_define_global_const(mrb_state *mrb, const char *name, mrb_value val) { mrb_define_const(mrb, mrb->object_class, name, val); } static int const_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p) { mrb_value ary; const char* s; mrb_int len; ary = *(mrb_value*)p; s = mrb_sym2name_len(mrb, sym, &len); if (len >= 1 && ISUPPER(s[0])) { mrb_ary_push(mrb, ary, mrb_symbol_value(sym)); } return 0; } /* 15.2.2.4.24 */ /* * call-seq: * mod.constants -> array * * Returns an array of all names of contants defined in the receiver. */ mrb_value mrb_mod_constants(mrb_state *mrb, mrb_value mod) { mrb_value ary; mrb_bool inherit = TRUE; struct RClass *c = mrb_class_ptr(mod); mrb_get_args(mrb, "|b", &inherit); ary = mrb_ary_new(mrb); while (c) { iv_foreach(mrb, c->iv, const_i, &ary); if (!inherit) break; c = c->super; if (c == mrb->object_class) break; } return ary; } MRB_API mrb_value mrb_gv_get(mrb_state *mrb, mrb_sym sym) { mrb_value v; if (iv_get(mrb, mrb->globals, sym, &v)) return v; return mrb_nil_value(); } MRB_API void mrb_gv_set(mrb_state *mrb, mrb_sym sym, mrb_value v) { iv_tbl *t; if (!mrb->globals) { mrb->globals = iv_new(mrb); } t = mrb->globals; iv_put(mrb, t, sym, v); } MRB_API void mrb_gv_remove(mrb_state *mrb, mrb_sym sym) { iv_del(mrb, mrb->globals, sym, NULL); } static int gv_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p) { mrb_value ary; ary = *(mrb_value*)p; mrb_ary_push(mrb, ary, mrb_symbol_value(sym)); return 0; } /* 15.3.1.2.4 */ /* 15.3.1.3.14 */ /* * call-seq: * global_variables -> array * * Returns an array of the names of global variables. * * global_variables.grep /std/ #=> [:$stdin, :$stdout, :$stderr] */ mrb_value mrb_f_global_variables(mrb_state *mrb, mrb_value self) { iv_tbl *t = mrb->globals; mrb_value ary = mrb_ary_new(mrb); size_t i; char buf[3]; iv_foreach(mrb, t, gv_i, &ary); buf[0] = '$'; buf[2] = 0; for (i = 1; i <= 9; ++i) { buf[1] = (char)(i + '0'); mrb_ary_push(mrb, ary, mrb_symbol_value(mrb_intern(mrb, buf, 2))); } return ary; } static mrb_bool mrb_const_defined_0(mrb_state *mrb, mrb_value mod, mrb_sym id, mrb_bool exclude, mrb_bool recurse) { struct RClass *klass = mrb_class_ptr(mod); struct RClass *tmp; mrb_bool mod_retry = FALSE; tmp = klass; retry: while (tmp) { if (iv_get(mrb, tmp->iv, id, NULL)) { return TRUE; } if (!recurse && (klass != mrb->object_class)) break; tmp = tmp->super; } if (!exclude && !mod_retry && (klass->tt == MRB_TT_MODULE)) { mod_retry = TRUE; tmp = mrb->object_class; goto retry; } return FALSE; } MRB_API mrb_bool mrb_const_defined(mrb_state *mrb, mrb_value mod, mrb_sym id) { return mrb_const_defined_0(mrb, mod, id, TRUE, TRUE); } MRB_API mrb_bool mrb_const_defined_at(mrb_state *mrb, mrb_value mod, mrb_sym id) { return mrb_const_defined_0(mrb, mod, id, TRUE, FALSE); } MRB_API mrb_value mrb_attr_get(mrb_state *mrb, mrb_value obj, mrb_sym id) { return mrb_iv_get(mrb, obj, id); } struct csym_arg { struct RClass *c; mrb_sym sym; }; static int csym_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p) { struct csym_arg *a = (struct csym_arg*)p; struct RClass *c = a->c; if (mrb_type(v) == c->tt && mrb_class_ptr(v) == c) { a->sym = sym; return 1; /* stop iteration */ } return 0; } static mrb_sym find_class_sym(mrb_state *mrb, struct RClass *outer, struct RClass *c) { struct csym_arg arg; if (!outer) return 0; if (outer == c) return 0; arg.c = c; arg.sym = 0; iv_foreach(mrb, outer->iv, csym_i, &arg); return arg.sym; } static struct RClass* outer_class(mrb_state *mrb, struct RClass *c) { mrb_value ov; ov = mrb_obj_iv_get(mrb, (struct RObject*)c, mrb_intern_lit(mrb, "__outer__")); if (mrb_nil_p(ov)) return NULL; switch (mrb_type(ov)) { case MRB_TT_CLASS: case MRB_TT_MODULE: return mrb_class_ptr(ov); default: break; } return NULL; } static mrb_bool detect_outer_loop(mrb_state *mrb, struct RClass *c) { struct RClass *t = c; /* tortoise */ struct RClass *h = c; /* hare */ for (;;) { if (h == NULL) return FALSE; h = outer_class(mrb, h); if (h == NULL) return FALSE; h = outer_class(mrb, h); t = outer_class(mrb, t); if (t == h) return TRUE; } } mrb_value mrb_class_find_path(mrb_state *mrb, struct RClass *c) { struct RClass *outer; mrb_value path; mrb_sym name; const char *str; mrb_int len; if (detect_outer_loop(mrb, c)) return mrb_nil_value(); outer = outer_class(mrb, c); if (outer == NULL) return mrb_nil_value(); name = find_class_sym(mrb, outer, c); if (name == 0) return mrb_nil_value(); str = mrb_class_name(mrb, outer); path = mrb_str_new_capa(mrb, 40); mrb_str_cat_cstr(mrb, path, str); mrb_str_cat_cstr(mrb, path, "::"); str = mrb_sym2name_len(mrb, name, &len); mrb_str_cat(mrb, path, str, len); if (RSTRING_PTR(path)[0] != '#') { iv_del(mrb, c->iv, mrb_intern_lit(mrb, "__outer__"), NULL); iv_put(mrb, c->iv, mrb_intern_lit(mrb, "__classname__"), path); mrb_field_write_barrier_value(mrb, (struct RBasic*)c, path); } return path; } mruby-2.0.0/src/version.c000066400000000000000000000015041340361412400152650ustar00rootroot00000000000000#include #include void mrb_init_version(mrb_state* mrb) { mrb_value mruby_version = mrb_str_new_lit(mrb, MRUBY_VERSION); 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, "RUBY_ENGINE_VERSION", mruby_version); mrb_define_global_const(mrb, "MRUBY_VERSION", 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)); } mruby-2.0.0/src/vm.c000066400000000000000000002257431340361412400142370ustar00rootroot00000000000000/* ** vm.c - virtual machine for mruby ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "value_array.h" #include #ifdef MRB_DISABLE_STDIO #if defined(__cplusplus) extern "C" { #endif void abort(void); #if defined(__cplusplus) } /* extern "C" { */ #endif #endif #define STACK_INIT_SIZE 128 #define CALLINFO_INIT_SIZE 32 #ifndef ENSURE_STACK_INIT_SIZE #define ENSURE_STACK_INIT_SIZE 16 #endif #ifndef RESCUE_STACK_INIT_SIZE #define RESCUE_STACK_INIT_SIZE 16 #endif /* Define amount of linear stack growth. */ #ifndef MRB_STACK_GROWTH #define MRB_STACK_GROWTH 128 #endif /* Maximum mrb_funcall() depth. Should be set lower on memory constrained systems. */ #ifndef MRB_FUNCALL_DEPTH_MAX #define MRB_FUNCALL_DEPTH_MAX 512 #endif /* Maximum depth of ecall() recursion. */ #ifndef MRB_ECALL_DEPTH_MAX #define MRB_ECALL_DEPTH_MAX 512 #endif /* Maximum stack depth. Should be set lower on memory constrained systems. The value below allows about 60000 recursive calls in the simplest case. */ #ifndef MRB_STACK_MAX #define MRB_STACK_MAX (0x40000 - MRB_STACK_GROWTH) #endif #ifdef VM_DEBUG # define DEBUG(x) (x) #else # define DEBUG(x) #endif #ifndef MRB_GC_FIXED_ARENA static void mrb_gc_arena_shrink(mrb_state *mrb, int idx) { mrb_gc *gc = &mrb->gc; int capa = gc->arena_capa; if (idx < capa / 4) { capa >>= 2; if (capa < MRB_GC_ARENA_SIZE) { capa = MRB_GC_ARENA_SIZE; } if (capa != gc->arena_capa) { gc->arena = (struct RBasic**)mrb_realloc(mrb, gc->arena, sizeof(struct RBasic*)*capa); gc->arena_capa = capa; } } } #else #define mrb_gc_arena_shrink(mrb,idx) #endif #define CALL_MAXARGS 127 void mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args); static inline void stack_clear(mrb_value *from, size_t count) { #ifndef MRB_NAN_BOXING const mrb_value mrb_value_zero = { 0 }; while (count-- > 0) { *from++ = mrb_value_zero; } #else while (count-- > 0) { SET_NIL_VALUE(*from); from++; } #endif } static inline void stack_copy(mrb_value *dst, const mrb_value *src, size_t size) { while (size-- > 0) { *dst++ = *src++; } } static void stack_init(mrb_state *mrb) { struct mrb_context *c = mrb->c; /* mrb_assert(mrb->stack == NULL); */ c->stbase = (mrb_value *)mrb_calloc(mrb, STACK_INIT_SIZE, sizeof(mrb_value)); c->stend = c->stbase + STACK_INIT_SIZE; c->stack = c->stbase; /* mrb_assert(ci == NULL); */ c->cibase = (mrb_callinfo *)mrb_calloc(mrb, CALLINFO_INIT_SIZE, sizeof(mrb_callinfo)); c->ciend = c->cibase + CALLINFO_INIT_SIZE; c->ci = c->cibase; c->ci->target_class = mrb->object_class; c->ci->stackent = c->stack; } static inline void envadjust(mrb_state *mrb, mrb_value *oldbase, mrb_value *newbase, size_t oldsize) { mrb_callinfo *ci = mrb->c->cibase; if (newbase == oldbase) return; while (ci <= mrb->c->ci) { struct REnv *e = ci->env; mrb_value *st; if (e && MRB_ENV_STACK_SHARED_P(e) && (st = e->stack) && oldbase <= st && st < oldbase+oldsize) { ptrdiff_t off = e->stack - oldbase; e->stack = newbase + off; } if (ci->proc && MRB_PROC_ENV_P(ci->proc) && ci->env != MRB_PROC_ENV(ci->proc)) { e = MRB_PROC_ENV(ci->proc); if (e && MRB_ENV_STACK_SHARED_P(e) && (st = e->stack) && oldbase <= st && st < oldbase+oldsize) { ptrdiff_t off = e->stack - oldbase; e->stack = newbase + off; } } ci->stackent = newbase + (ci->stackent - oldbase); ci++; } } /** def rec ; $deep =+ 1 ; if $deep > 1000 ; return 0 ; end ; rec ; end */ static void stack_extend_alloc(mrb_state *mrb, mrb_int room) { mrb_value *oldbase = mrb->c->stbase; mrb_value *newstack; size_t oldsize = mrb->c->stend - mrb->c->stbase; size_t size = oldsize; size_t off = mrb->c->stack - mrb->c->stbase; if (off > size) size = off; #ifdef MRB_STACK_EXTEND_DOUBLING if ((size_t)room <= size) size *= 2; else size += room; #else /* Use linear stack growth. It is slightly slower than doubling the stack space, but it saves memory on small devices. */ if (room <= MRB_STACK_GROWTH) size += MRB_STACK_GROWTH; else size += room; #endif newstack = (mrb_value *)mrb_realloc(mrb, mrb->c->stbase, sizeof(mrb_value) * size); if (newstack == NULL) { mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err)); } stack_clear(&(newstack[oldsize]), size - oldsize); envadjust(mrb, oldbase, newstack, oldsize); mrb->c->stbase = newstack; mrb->c->stack = mrb->c->stbase + off; mrb->c->stend = mrb->c->stbase + size; /* Raise an exception if the new stack size will be too large, to prevent infinite recursion. However, do this only after resizing the stack, so mrb_raise has stack space to work with. */ if (size > MRB_STACK_MAX) { mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err)); } } MRB_API void mrb_stack_extend(mrb_state *mrb, mrb_int room) { if (mrb->c->stack + room >= mrb->c->stend) { stack_extend_alloc(mrb, room); } } static inline struct REnv* uvenv(mrb_state *mrb, int up) { struct RProc *proc = mrb->c->ci->proc; struct REnv *e; while (up--) { proc = proc->upper; if (!proc) return NULL; } e = MRB_PROC_ENV(proc); if (e) return e; /* proc has enclosed env */ else { mrb_callinfo *ci = mrb->c->ci; mrb_callinfo *cb = mrb->c->cibase; while (cb <= ci) { if (ci->proc == proc) { return ci->env; } ci--; } } return NULL; } static inline struct RProc* top_proc(mrb_state *mrb, struct RProc *proc) { while (proc->upper) { if (MRB_PROC_SCOPE_P(proc) || MRB_PROC_STRICT_P(proc)) return proc; proc = proc->upper; } return proc; } #define CI_ACC_SKIP -1 #define CI_ACC_DIRECT -2 #define CI_ACC_RESUMED -3 static inline mrb_callinfo* cipush(mrb_state *mrb) { struct mrb_context *c = mrb->c; static const mrb_callinfo ci_zero = { 0 }; mrb_callinfo *ci = c->ci; int ridx = ci->ridx; if (ci + 1 == c->ciend) { ptrdiff_t size = ci - c->cibase; c->cibase = (mrb_callinfo *)mrb_realloc(mrb, c->cibase, sizeof(mrb_callinfo)*size*2); c->ci = c->cibase + size; c->ciend = c->cibase + size * 2; } ci = ++c->ci; *ci = ci_zero; ci->epos = mrb->c->eidx; ci->ridx = ridx; return ci; } void mrb_env_unshare(mrb_state *mrb, struct REnv *e) { if (e == NULL) return; else { size_t len = (size_t)MRB_ENV_STACK_LEN(e); mrb_value *p; if (!MRB_ENV_STACK_SHARED_P(e)) return; if (e->cxt != mrb->c) return; if (e == mrb->c->cibase->env) return; /* for mirb */ p = (mrb_value *)mrb_malloc(mrb, sizeof(mrb_value)*len); if (len > 0) { stack_copy(p, e->stack, len); } e->stack = p; MRB_ENV_UNSHARE_STACK(e); mrb_write_barrier(mrb, (struct RBasic *)e); } } static inline void cipop(mrb_state *mrb) { struct mrb_context *c = mrb->c; struct REnv *env = c->ci->env; c->ci--; if (env) mrb_env_unshare(mrb, env); } void mrb_exc_set(mrb_state *mrb, mrb_value exc); static void ecall(mrb_state *mrb) { struct RProc *p; struct mrb_context *c = mrb->c; mrb_callinfo *ci = c->ci; struct RObject *exc; struct REnv *env; ptrdiff_t cioff; int ai = mrb_gc_arena_save(mrb); uint16_t i = --c->eidx; int nregs; if (i<0) return; /* restrict total call depth of ecall() */ if (++mrb->ecall_nest > MRB_ECALL_DEPTH_MAX) { mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err)); } p = c->ensure[i]; if (!p) return; mrb_assert(!MRB_PROC_CFUNC_P(p)); c->ensure[i] = NULL; nregs = p->upper->body.irep->nregs; if (ci->proc && !MRB_PROC_CFUNC_P(ci->proc) && ci->proc->body.irep->nregs > nregs) { nregs = ci->proc->body.irep->nregs; } cioff = ci - c->cibase; ci = cipush(mrb); ci->stackent = mrb->c->stack; ci->mid = ci[-1].mid; ci->acc = CI_ACC_SKIP; ci->argc = 0; ci->proc = p; ci->target_class = MRB_PROC_TARGET_CLASS(p); env = MRB_PROC_ENV(p); mrb_assert(env); c->stack += nregs; exc = mrb->exc; mrb->exc = 0; if (exc) { mrb_gc_protect(mrb, mrb_obj_value(exc)); } if (mrb->c->fib) { mrb_gc_protect(mrb, mrb_obj_value(mrb->c->fib)); } mrb_run(mrb, p, env->stack[0]); mrb->c = c; c->ci = c->cibase + cioff; if (!mrb->exc) mrb->exc = exc; mrb_gc_arena_restore(mrb, ai); mrb->ecall_nest--; } #ifndef MRB_FUNCALL_ARGC_MAX #define MRB_FUNCALL_ARGC_MAX 16 #endif MRB_API mrb_value mrb_funcall(mrb_state *mrb, mrb_value self, const char *name, mrb_int argc, ...) { mrb_value argv[MRB_FUNCALL_ARGC_MAX]; va_list ap; mrb_int i; mrb_sym mid = mrb_intern_cstr(mrb, name); if (argc > MRB_FUNCALL_ARGC_MAX) { mrb_raise(mrb, E_ARGUMENT_ERROR, "Too long arguments. (limit=" MRB_STRINGIZE(MRB_FUNCALL_ARGC_MAX) ")"); } va_start(ap, argc); for (i = 0; i < argc; i++) { argv[i] = va_arg(ap, mrb_value); } va_end(ap); return mrb_funcall_argv(mrb, self, mid, argc, argv); } static int ci_nregs(mrb_callinfo *ci) { struct RProc *p; int n = 0; if (!ci) return 3; p = ci->proc; if (!p) { if (ci->argc < 0) return 3; return ci->argc+2; } if (!MRB_PROC_CFUNC_P(p) && p->body.irep) { n = p->body.irep->nregs; } if (ci->argc < 0) { if (n < 3) n = 3; /* self + args + blk */ } if (ci->argc > n) { n = ci->argc + 2; /* self + blk */ } return n; } MRB_API mrb_value mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc, const mrb_value *argv, mrb_value blk) { mrb_value val; if (!mrb->jmp) { struct mrb_jmpbuf c_jmp; ptrdiff_t nth_ci = mrb->c->ci - mrb->c->cibase; MRB_TRY(&c_jmp) { mrb->jmp = &c_jmp; /* recursive call */ val = mrb_funcall_with_block(mrb, self, mid, argc, argv, blk); mrb->jmp = 0; } MRB_CATCH(&c_jmp) { /* error */ while (nth_ci < (mrb->c->ci - mrb->c->cibase)) { mrb->c->stack = mrb->c->ci->stackent; cipop(mrb); } mrb->jmp = 0; val = mrb_obj_value(mrb->exc); } MRB_END_EXC(&c_jmp); mrb->jmp = 0; } else { mrb_method_t m; struct RClass *c; mrb_callinfo *ci; int n = ci_nregs(mrb->c->ci); ptrdiff_t voff = -1; if (!mrb->c->stack) { stack_init(mrb); } if (argc < 0) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative argc for funcall (%S)", mrb_fixnum_value(argc)); } c = mrb_class(mrb, self); m = mrb_method_search_vm(mrb, &c, mid); if (MRB_METHOD_UNDEF_P(m)) { mrb_sym missing = mrb_intern_lit(mrb, "method_missing"); mrb_value args = mrb_ary_new_from_values(mrb, argc, argv); m = mrb_method_search_vm(mrb, &c, missing); if (MRB_METHOD_UNDEF_P(m)) { mrb_method_missing(mrb, mid, self, args); } mrb_ary_unshift(mrb, args, mrb_symbol_value(mid)); mrb_stack_extend(mrb, n+2); mrb->c->stack[n+1] = args; argc = -1; } if (mrb->c->ci - mrb->c->cibase > MRB_FUNCALL_DEPTH_MAX) { mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err)); } ci = cipush(mrb); ci->mid = mid; ci->stackent = mrb->c->stack; ci->argc = (int)argc; ci->target_class = c; mrb->c->stack = mrb->c->stack + n; if (mrb->c->stbase <= argv && argv < mrb->c->stend) { voff = argv - mrb->c->stbase; } if (MRB_METHOD_CFUNC_P(m)) { mrb_stack_extend(mrb, argc + 2); } else if (argc >= CALL_MAXARGS) { mrb_value args = mrb_ary_new_from_values(mrb, argc, argv); mrb_stack_extend(mrb, 3); mrb->c->stack[1] = args; ci->argc = -1; argc = 1; } else { struct RProc *p = MRB_METHOD_PROC(m); ci->proc = p; if (argc < 0) argc = 1; mrb_stack_extend(mrb, p->body.irep->nregs + argc); } if (voff >= 0) { argv = mrb->c->stbase + voff; } mrb->c->stack[0] = self; if (ci->argc > 0) { stack_copy(mrb->c->stack+1, argv, argc); } mrb->c->stack[argc+1] = blk; if (MRB_METHOD_CFUNC_P(m)) { int ai = mrb_gc_arena_save(mrb); ci->acc = CI_ACC_DIRECT; if (MRB_METHOD_PROC_P(m)) { ci->proc = MRB_METHOD_PROC(m); } val = MRB_METHOD_CFUNC(m)(mrb, self); mrb->c->stack = mrb->c->ci->stackent; cipop(mrb); mrb_gc_arena_restore(mrb, ai); } else { ci->acc = CI_ACC_SKIP; val = mrb_run(mrb, MRB_METHOD_PROC(m), self); } } mrb_gc_protect(mrb, val); return val; } MRB_API mrb_value mrb_funcall_argv(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc, const mrb_value *argv) { return mrb_funcall_with_block(mrb, self, mid, argc, argv, mrb_nil_value()); } mrb_value mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p) { mrb_callinfo *ci = mrb->c->ci; int keep, nregs; mrb->c->stack[0] = self; ci->proc = p; if (MRB_PROC_CFUNC_P(p)) { return MRB_PROC_CFUNC(p)(mrb, self); } nregs = p->body.irep->nregs; if (ci->argc < 0) keep = 3; else keep = ci->argc + 2; if (nregs < keep) { mrb_stack_extend(mrb, keep); } else { mrb_stack_extend(mrb, nregs); stack_clear(mrb->c->stack+keep, nregs-keep); } ci = cipush(mrb); ci->target_class = 0; ci->pc = p->body.irep->iseq; ci->stackent = mrb->c->stack; ci->acc = 0; return self; } /* 15.3.1.3.4 */ /* 15.3.1.3.44 */ /* * call-seq: * obj.send(symbol [, args...]) -> obj * obj.__send__(symbol [, args...]) -> obj * * Invokes the method identified by _symbol_, passing it any * arguments specified. You can use __send__ if the name * +send+ clashes with an existing method in _obj_. * * class Klass * def hello(*args) * "Hello " + args.join(' ') * end * end * k = Klass.new * k.send :hello, "gentle", "readers" #=> "Hello gentle readers" */ MRB_API mrb_value mrb_f_send(mrb_state *mrb, mrb_value self) { mrb_sym name; mrb_value block, *argv, *regs; mrb_int argc, i, len; mrb_method_t m; struct RClass *c; mrb_callinfo *ci; mrb_get_args(mrb, "n*&", &name, &argv, &argc, &block); ci = mrb->c->ci; if (ci->acc < 0) { funcall: return mrb_funcall_with_block(mrb, self, name, argc, argv, block); } c = mrb_class(mrb, self); m = mrb_method_search_vm(mrb, &c, name); if (MRB_METHOD_UNDEF_P(m)) { /* call method_mising */ goto funcall; } ci->mid = name; ci->target_class = c; regs = mrb->c->stack+1; /* remove first symbol from arguments */ if (ci->argc >= 0) { for (i=0,len=ci->argc; iargc--; } else { /* variable length arguments */ mrb_ary_shift(mrb, regs[0]); } if (MRB_METHOD_CFUNC_P(m)) { if (MRB_METHOD_PROC_P(m)) { ci->proc = MRB_METHOD_PROC(m); } return MRB_METHOD_CFUNC(m)(mrb, self); } return mrb_exec_irep(mrb, self, MRB_METHOD_PROC(m)); } static mrb_value eval_under(mrb_state *mrb, mrb_value self, mrb_value blk, struct RClass *c) { struct RProc *p; mrb_callinfo *ci; int nregs; if (mrb_nil_p(blk)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given"); } ci = mrb->c->ci; if (ci->acc == CI_ACC_DIRECT) { ci->target_class = c; return mrb_yield_cont(mrb, blk, self, 1, &self); } ci->target_class = c; p = mrb_proc_ptr(blk); ci->proc = p; ci->argc = 1; ci->mid = ci[-1].mid; if (MRB_PROC_CFUNC_P(p)) { mrb_stack_extend(mrb, 3); mrb->c->stack[0] = self; mrb->c->stack[1] = self; mrb->c->stack[2] = mrb_nil_value(); return MRB_PROC_CFUNC(p)(mrb, self); } nregs = p->body.irep->nregs; mrb_stack_extend(mrb, (nregs < 3) ? 3 : nregs); mrb->c->stack[0] = self; mrb->c->stack[1] = self; mrb->c->stack[2] = mrb_nil_value(); ci = cipush(mrb); ci->target_class = 0; ci->pc = p->body.irep->iseq; ci->stackent = mrb->c->stack; ci->acc = 0; return self; } /* 15.2.2.4.35 */ /* * call-seq: * mod.class_eval {| | block } -> obj * mod.module_eval {| | block } -> obj * * Evaluates block in the context of _mod_. This can * be used to add methods to a class. module_eval returns * the result of evaluating its argument. */ mrb_value mrb_mod_module_eval(mrb_state *mrb, mrb_value mod) { mrb_value a, b; if (mrb_get_args(mrb, "|S&", &a, &b) == 1) { mrb_raise(mrb, E_NOTIMP_ERROR, "module_eval/class_eval with string not implemented"); } return eval_under(mrb, mod, b, mrb_class_ptr(mod)); } /* 15.3.1.3.18 */ /* * call-seq: * obj.instance_eval {| | block } -> obj * * Evaluates the given block,within the context of the receiver (_obj_). * In order to set the context, the variable +self+ is set to _obj_ while * the code is executing, giving the code access to _obj_'s * instance variables. In the version of instance_eval * that takes a +String+, the optional second and third * parameters supply a filename and starting line number that are used * when reporting compilation errors. * * class KlassWithSecret * def initialize * @secret = 99 * end * end * k = KlassWithSecret.new * k.instance_eval { @secret } #=> 99 */ mrb_value mrb_obj_instance_eval(mrb_state *mrb, mrb_value self) { mrb_value a, b; mrb_value cv; struct RClass *c; if (mrb_get_args(mrb, "|S&", &a, &b) == 1) { mrb_raise(mrb, E_NOTIMP_ERROR, "instance_eval with string not implemented"); } switch (mrb_type(self)) { case MRB_TT_SYMBOL: case MRB_TT_FIXNUM: #ifndef MRB_WITHOUT_FLOAT case MRB_TT_FLOAT: #endif c = 0; break; default: cv = mrb_singleton_class(mrb, self); c = mrb_class_ptr(cv); break; } return eval_under(mrb, self, b, c); } MRB_API mrb_value mrb_yield_with_class(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value *argv, mrb_value self, struct RClass *c) { struct RProc *p; mrb_sym mid = mrb->c->ci->mid; mrb_callinfo *ci; mrb_value val; int n; if (mrb_nil_p(b)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given"); } ci = mrb->c->ci; n = ci_nregs(ci); if (ci - mrb->c->cibase > MRB_FUNCALL_DEPTH_MAX) { mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err)); } p = mrb_proc_ptr(b); ci = cipush(mrb); ci->mid = mid; ci->proc = p; ci->stackent = mrb->c->stack; ci->argc = (int)argc; ci->target_class = c; ci->acc = CI_ACC_SKIP; n = MRB_PROC_CFUNC_P(p) ? (int)(argc+2) : p->body.irep->nregs; mrb->c->stack = mrb->c->stack + n; mrb_stack_extend(mrb, n); mrb->c->stack[0] = self; if (argc > 0) { stack_copy(mrb->c->stack+1, argv, argc); } mrb->c->stack[argc+1] = mrb_nil_value(); if (MRB_PROC_CFUNC_P(p)) { val = MRB_PROC_CFUNC(p)(mrb, self); mrb->c->stack = mrb->c->ci->stackent; cipop(mrb); } else { val = mrb_run(mrb, p, self); } return val; } MRB_API mrb_value mrb_yield_argv(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value *argv) { struct RProc *p = mrb_proc_ptr(b); return mrb_yield_with_class(mrb, b, argc, argv, MRB_PROC_ENV(p)->stack[0], MRB_PROC_TARGET_CLASS(p)); } MRB_API mrb_value mrb_yield(mrb_state *mrb, mrb_value b, mrb_value arg) { struct RProc *p = mrb_proc_ptr(b); return mrb_yield_with_class(mrb, b, 1, &arg, MRB_PROC_ENV(p)->stack[0], MRB_PROC_TARGET_CLASS(p)); } mrb_value mrb_yield_cont(mrb_state *mrb, mrb_value b, mrb_value self, mrb_int argc, const mrb_value *argv) { struct RProc *p; mrb_callinfo *ci; if (mrb_nil_p(b)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given"); } if (mrb_type(b) != MRB_TT_PROC) { mrb_raise(mrb, E_TYPE_ERROR, "not a block"); } p = mrb_proc_ptr(b); ci = mrb->c->ci; mrb_stack_extend(mrb, 3); mrb->c->stack[1] = mrb_ary_new_from_values(mrb, argc, argv); mrb->c->stack[2] = mrb_nil_value(); ci->argc = -1; return mrb_exec_irep(mrb, self, p); } mrb_value mrb_mod_s_nesting(mrb_state *mrb, mrb_value mod) { struct RProc *proc; mrb_value ary; struct RClass *c = NULL; mrb_get_args(mrb, ""); ary = mrb_ary_new(mrb); proc = mrb->c->ci[-1].proc; /* callee proc */ mrb_assert(!MRB_PROC_CFUNC_P(proc)); while (proc) { if (MRB_PROC_SCOPE_P(proc)) { struct RClass *c2 = MRB_PROC_TARGET_CLASS(proc); if (c2 != c) { c = c2; mrb_ary_push(mrb, ary, mrb_obj_value(c)); } } proc = proc->upper; } return ary; } static struct RBreak* break_new(mrb_state *mrb, struct RProc *p, mrb_value val) { struct RBreak *brk; brk = (struct RBreak*)mrb_obj_alloc(mrb, MRB_TT_BREAK, NULL); brk->proc = p; brk->val = val; return brk; } typedef enum { LOCALJUMP_ERROR_RETURN = 0, LOCALJUMP_ERROR_BREAK = 1, LOCALJUMP_ERROR_YIELD = 2 } localjump_error_kind; static void localjump_error(mrb_state *mrb, localjump_error_kind kind) { char kind_str[3][7] = { "return", "break", "yield" }; char kind_str_len[] = { 6, 5, 5 }; static const char lead[] = "unexpected "; mrb_value msg; mrb_value exc; msg = mrb_str_new_capa(mrb, sizeof(lead) + 7); mrb_str_cat(mrb, msg, lead, sizeof(lead) - 1); mrb_str_cat(mrb, msg, kind_str[kind], kind_str_len[kind]); exc = mrb_exc_new_str(mrb, E_LOCALJUMP_ERROR, msg); mrb_exc_set(mrb, exc); } static void argnum_error(mrb_state *mrb, mrb_int num) { mrb_value exc; mrb_value str; mrb_int argc = mrb->c->ci->argc; if (argc < 0) { mrb_value args = mrb->c->stack[1]; if (mrb_array_p(args)) { argc = RARRAY_LEN(args); } } if (mrb->c->ci->mid) { str = mrb_format(mrb, "'%S': wrong number of arguments (%S for %S)", mrb_sym2str(mrb, mrb->c->ci->mid), mrb_fixnum_value(argc), mrb_fixnum_value(num)); } else { str = mrb_format(mrb, "wrong number of arguments (%S for %S)", mrb_fixnum_value(argc), mrb_fixnum_value(num)); } exc = mrb_exc_new_str(mrb, E_ARGUMENT_ERROR, str); mrb_exc_set(mrb, exc); } #define ERR_PC_SET(mrb) mrb->c->ci->err = pc0; #define ERR_PC_CLR(mrb) mrb->c->ci->err = 0; #ifdef MRB_ENABLE_DEBUG_HOOK #define CODE_FETCH_HOOK(mrb, irep, pc, regs) if ((mrb)->code_fetch_hook) (mrb)->code_fetch_hook((mrb), (irep), (pc), (regs)); #else #define CODE_FETCH_HOOK(mrb, irep, pc, regs) #endif #ifdef MRB_BYTECODE_DECODE_OPTION #define BYTECODE_DECODER(x) ((mrb)->bytecode_decoder)?(mrb)->bytecode_decoder((mrb), (x)):(x) #else #define BYTECODE_DECODER(x) (x) #endif #ifndef MRB_DISABLE_DIRECT_THREADING #if defined __GNUC__ || defined __clang__ || defined __INTEL_COMPILER #define DIRECT_THREADED #endif #endif /* ifndef MRB_DISABLE_DIRECT_THREADING */ #ifndef DIRECT_THREADED #define INIT_DISPATCH for (;;) { insn = BYTECODE_DECODER(*pc); CODE_FETCH_HOOK(mrb, irep, pc, regs); switch (insn) { #define CASE(insn,ops) case insn: pc0=pc++; FETCH_ ## ops ();; L_ ## insn ## _BODY: #define NEXT break #define JUMP NEXT #define END_DISPATCH }} #else #define INIT_DISPATCH JUMP; return mrb_nil_value(); #define CASE(insn,ops) L_ ## insn: pc0=pc++; FETCH_ ## ops (); L_ ## insn ## _BODY: #define NEXT insn=BYTECODE_DECODER(*pc); CODE_FETCH_HOOK(mrb, irep, pc, regs); goto *optable[insn] #define JUMP NEXT #define END_DISPATCH #endif MRB_API mrb_value mrb_vm_run(mrb_state *mrb, struct RProc *proc, mrb_value self, unsigned int stack_keep) { mrb_irep *irep = proc->body.irep; mrb_value result; struct mrb_context *c = mrb->c; ptrdiff_t cioff = c->ci - c->cibase; unsigned int nregs = irep->nregs; if (!c->stack) { stack_init(mrb); } if (stack_keep > nregs) nregs = stack_keep; mrb_stack_extend(mrb, nregs); stack_clear(c->stack + stack_keep, nregs - stack_keep); c->stack[0] = self; result = mrb_vm_exec(mrb, proc, irep->iseq); if (mrb->c != c) { if (mrb->c->fib) { mrb_write_barrier(mrb, (struct RBasic*)mrb->c->fib); } mrb->c = c; } else if (c->ci - c->cibase > cioff) { c->ci = c->cibase + cioff; } return result; } static mrb_bool check_target_class(mrb_state *mrb) { if (!mrb->c->ci->target_class) { mrb_value exc = mrb_exc_new_str_lit(mrb, E_TYPE_ERROR, "no target class or module"); mrb_exc_set(mrb, exc); return FALSE; } return TRUE; } void mrb_hash_check_kdict(mrb_state *mrb, mrb_value self); MRB_API mrb_value mrb_vm_exec(mrb_state *mrb, struct RProc *proc, mrb_code *pc) { /* mrb_assert(mrb_proc_cfunc_p(proc)) */ mrb_code *pc0 = pc; mrb_irep *irep = proc->body.irep; mrb_value *pool = irep->pool; mrb_sym *syms = irep->syms; mrb_code insn; int ai = mrb_gc_arena_save(mrb); struct mrb_jmpbuf *prev_jmp = mrb->jmp; struct mrb_jmpbuf c_jmp; uint32_t a; uint16_t b; uint8_t c; mrb_sym mid; #ifdef DIRECT_THREADED static void *optable[] = { #define OPCODE(x,_) &&L_OP_ ## x, #include "mruby/ops.h" #undef OPCODE }; #endif mrb_bool exc_catched = FALSE; RETRY_TRY_BLOCK: MRB_TRY(&c_jmp) { if (exc_catched) { exc_catched = FALSE; mrb_gc_arena_restore(mrb, ai); if (mrb->exc && mrb->exc->tt == MRB_TT_BREAK) goto L_BREAK; goto L_RAISE; } mrb->jmp = &c_jmp; mrb->c->ci->proc = proc; #define regs (mrb->c->stack) INIT_DISPATCH { CASE(OP_NOP, Z) { /* do nothing */ NEXT; } CASE(OP_MOVE, BB) { regs[a] = regs[b]; NEXT; } CASE(OP_LOADL, BB) { #ifdef MRB_WORD_BOXING mrb_value val = pool[b]; #ifndef MRB_WITHOUT_FLOAT if (mrb_float_p(val)) { val = mrb_float_value(mrb, mrb_float(val)); } #endif regs[a] = val; #else regs[a] = pool[b]; #endif NEXT; } CASE(OP_LOADI, BB) { SET_INT_VALUE(regs[a], b); NEXT; } CASE(OP_LOADINEG, BB) { SET_INT_VALUE(regs[a], -b); NEXT; } CASE(OP_LOADI__1,B) goto L_LOADI; CASE(OP_LOADI_0,B) goto L_LOADI; CASE(OP_LOADI_1,B) goto L_LOADI; CASE(OP_LOADI_2,B) goto L_LOADI; CASE(OP_LOADI_3,B) goto L_LOADI; CASE(OP_LOADI_4,B) goto L_LOADI; CASE(OP_LOADI_5,B) goto L_LOADI; CASE(OP_LOADI_6,B) goto L_LOADI; CASE(OP_LOADI_7, B) { L_LOADI: SET_INT_VALUE(regs[a], (mrb_int)insn - (mrb_int)OP_LOADI_0); NEXT; } CASE(OP_LOADSYM, BB) { SET_SYM_VALUE(regs[a], syms[b]); NEXT; } CASE(OP_LOADNIL, B) { SET_NIL_VALUE(regs[a]); NEXT; } CASE(OP_LOADSELF, B) { regs[a] = regs[0]; NEXT; } CASE(OP_LOADT, B) { SET_TRUE_VALUE(regs[a]); NEXT; } CASE(OP_LOADF, B) { SET_FALSE_VALUE(regs[a]); NEXT; } CASE(OP_GETGV, BB) { mrb_value val = mrb_gv_get(mrb, syms[b]); regs[a] = val; NEXT; } CASE(OP_SETGV, BB) { mrb_gv_set(mrb, syms[b], regs[a]); NEXT; } CASE(OP_GETSV, BB) { mrb_value val = mrb_vm_special_get(mrb, b); regs[a] = val; NEXT; } CASE(OP_SETSV, BB) { mrb_vm_special_set(mrb, b, regs[a]); NEXT; } CASE(OP_GETIV, BB) { regs[a] = mrb_iv_get(mrb, regs[0], syms[b]); NEXT; } CASE(OP_SETIV, BB) { mrb_iv_set(mrb, regs[0], syms[b], regs[a]); NEXT; } CASE(OP_GETCV, BB) { mrb_value val; ERR_PC_SET(mrb); val = mrb_vm_cv_get(mrb, syms[b]); ERR_PC_CLR(mrb); regs[a] = val; NEXT; } CASE(OP_SETCV, BB) { mrb_vm_cv_set(mrb, syms[b], regs[a]); NEXT; } CASE(OP_GETCONST, BB) { mrb_value val; mrb_sym sym = syms[b]; ERR_PC_SET(mrb); val = mrb_vm_const_get(mrb, sym); ERR_PC_CLR(mrb); regs[a] = val; NEXT; } CASE(OP_SETCONST, BB) { mrb_vm_const_set(mrb, syms[b], regs[a]); NEXT; } CASE(OP_GETMCNST, BB) { mrb_value val; ERR_PC_SET(mrb); val = mrb_const_get(mrb, regs[a], syms[b]); ERR_PC_CLR(mrb); regs[a] = val; NEXT; } CASE(OP_SETMCNST, BB) { mrb_const_set(mrb, regs[a+1], syms[b], regs[a]); NEXT; } CASE(OP_GETUPVAR, BBB) { mrb_value *regs_a = regs + a; struct REnv *e = uvenv(mrb, c); if (e && b < MRB_ENV_STACK_LEN(e)) { *regs_a = e->stack[b]; } else { *regs_a = mrb_nil_value(); } NEXT; } CASE(OP_SETUPVAR, BBB) { struct REnv *e = uvenv(mrb, c); if (e) { mrb_value *regs_a = regs + a; if (b < MRB_ENV_STACK_LEN(e)) { e->stack[b] = *regs_a; mrb_write_barrier(mrb, (struct RBasic*)e); } } NEXT; } CASE(OP_JMP, S) { pc = irep->iseq+a; JUMP; } CASE(OP_JMPIF, BS) { if (mrb_test(regs[a])) { pc = irep->iseq+b; JUMP; } NEXT; } CASE(OP_JMPNOT, BS) { if (!mrb_test(regs[a])) { pc = irep->iseq+b; JUMP; } NEXT; } CASE(OP_JMPNIL, BS) { if (mrb_nil_p(regs[a])) { pc = irep->iseq+b; JUMP; } NEXT; } CASE(OP_ONERR, S) { /* check rescue stack */ if (mrb->c->ci->ridx == UINT16_MAX-1) { mrb_value exc = mrb_exc_new_str_lit(mrb, E_RUNTIME_ERROR, "too many nested rescues"); mrb_exc_set(mrb, exc); goto L_RAISE; } /* expand rescue stack */ if (mrb->c->rsize <= mrb->c->ci->ridx) { if (mrb->c->rsize == 0) mrb->c->rsize = RESCUE_STACK_INIT_SIZE; else { mrb->c->rsize *= 2; if (mrb->c->rsize <= mrb->c->ci->ridx) { mrb->c->rsize = UINT16_MAX; } } mrb->c->rescue = (uint16_t*)mrb_realloc(mrb, mrb->c->rescue, sizeof(uint16_t)*mrb->c->rsize); } /* push rescue stack */ mrb->c->rescue[mrb->c->ci->ridx++] = a; NEXT; } CASE(OP_EXCEPT, B) { mrb_value exc = mrb_obj_value(mrb->exc); mrb->exc = 0; regs[a] = exc; NEXT; } CASE(OP_RESCUE, BB) { mrb_value exc = regs[a]; /* exc on stack */ mrb_value e = regs[b]; struct RClass *ec; switch (mrb_type(e)) { case MRB_TT_CLASS: case MRB_TT_MODULE: break; default: { mrb_value exc; exc = mrb_exc_new_str_lit(mrb, E_TYPE_ERROR, "class or module required for rescue clause"); mrb_exc_set(mrb, exc); goto L_RAISE; } } ec = mrb_class_ptr(e); regs[b] = mrb_bool_value(mrb_obj_is_kind_of(mrb, exc, ec)); NEXT; } CASE(OP_POPERR, B) { mrb->c->ci->ridx -= a; NEXT; } CASE(OP_RAISE, B) { mrb_exc_set(mrb, regs[a]); goto L_RAISE; } CASE(OP_EPUSH, B) { struct RProc *p; p = mrb_closure_new(mrb, irep->reps[a]); /* check ensure stack */ if (mrb->c->eidx == UINT16_MAX-1) { mrb_value exc = mrb_exc_new_str_lit(mrb, E_RUNTIME_ERROR, "too many nested ensures"); mrb_exc_set(mrb, exc); goto L_RAISE; } /* expand ensure stack */ if (mrb->c->esize <= mrb->c->eidx+1) { if (mrb->c->esize == 0) mrb->c->esize = ENSURE_STACK_INIT_SIZE; else { mrb->c->esize *= 2; if (mrb->c->esize <= mrb->c->eidx) { mrb->c->esize = UINT16_MAX; } } mrb->c->ensure = (struct RProc**)mrb_realloc(mrb, mrb->c->ensure, sizeof(struct RProc*)*mrb->c->esize); } /* push ensure stack */ mrb->c->ensure[mrb->c->eidx++] = p; mrb->c->ensure[mrb->c->eidx] = NULL; mrb_gc_arena_restore(mrb, ai); NEXT; } CASE(OP_EPOP, B) { mrb_callinfo *ci = mrb->c->ci; unsigned int n, epos = ci->epos; mrb_value self = regs[0]; struct RClass *target_class = ci->target_class; if (mrb->c->eidx <= epos) { NEXT; } if (a > (int)mrb->c->eidx - epos) a = mrb->c->eidx - epos; for (n=0; nnregs; proc = mrb->c->ensure[epos+n]; mrb->c->ensure[epos+n] = NULL; if (proc == NULL) continue; irep = proc->body.irep; ci = cipush(mrb); ci->mid = ci[-1].mid; ci->argc = 0; ci->proc = proc; ci->stackent = mrb->c->stack; ci->target_class = target_class; ci->pc = pc; ci->acc = nregs; mrb->c->stack += ci->acc; mrb_stack_extend(mrb, irep->nregs); regs[0] = self; pc = irep->iseq; } pool = irep->pool; syms = irep->syms; mrb->c->eidx = epos; JUMP; } CASE(OP_SENDV, BB) { c = CALL_MAXARGS; goto L_SEND; }; CASE(OP_SENDVB, BB) { c = CALL_MAXARGS; goto L_SENDB; }; CASE(OP_SEND, BBB) L_SEND: { /* push nil after arguments */ int bidx = (c == CALL_MAXARGS) ? a+2 : a+c+1; SET_NIL_VALUE(regs[bidx]); goto L_SENDB; }; L_SEND_SYM: { /* push nil after arguments */ int bidx = (c == CALL_MAXARGS) ? a+2 : a+c+1; SET_NIL_VALUE(regs[bidx]); goto L_SENDB_SYM; }; CASE(OP_SENDB, BBB) L_SENDB: mid = syms[b]; L_SENDB_SYM: { int argc = (c == CALL_MAXARGS) ? -1 : c; int bidx = (argc < 0) ? a+2 : a+c+1; mrb_method_t m; struct RClass *cls; mrb_callinfo *ci = mrb->c->ci; mrb_value recv, blk; mrb_assert(bidx < irep->nregs); recv = regs[a]; blk = regs[bidx]; if (!mrb_nil_p(blk) && mrb_type(blk) != MRB_TT_PROC) { blk = mrb_convert_type(mrb, blk, MRB_TT_PROC, "Proc", "to_proc"); /* The stack might have been reallocated during mrb_convert_type(), see #3622 */ regs[bidx] = blk; } cls = mrb_class(mrb, recv); m = mrb_method_search_vm(mrb, &cls, mid); if (MRB_METHOD_UNDEF_P(m)) { mrb_sym missing = mrb_intern_lit(mrb, "method_missing"); m = mrb_method_search_vm(mrb, &cls, missing); if (MRB_METHOD_UNDEF_P(m) || (missing == mrb->c->ci->mid && mrb_obj_eq(mrb, regs[0], recv))) { mrb_value args = (argc < 0) ? regs[a+1] : mrb_ary_new_from_values(mrb, c, regs+a+1); ERR_PC_SET(mrb); mrb_method_missing(mrb, mid, recv, args); } if (argc >= 0) { if (a+2 >= irep->nregs) { mrb_stack_extend(mrb, a+3); } regs[a+1] = mrb_ary_new_from_values(mrb, c, regs+a+1); regs[a+2] = blk; argc = -1; } mrb_ary_unshift(mrb, regs[a+1], mrb_symbol_value(mid)); mid = missing; } /* push callinfo */ ci = cipush(mrb); ci->mid = mid; ci->stackent = mrb->c->stack; ci->target_class = cls; ci->argc = argc; ci->pc = pc; ci->acc = a; /* prepare stack */ mrb->c->stack += a; if (MRB_METHOD_CFUNC_P(m)) { if (MRB_METHOD_PROC_P(m)) { struct RProc *p = MRB_METHOD_PROC(m); ci->proc = p; recv = p->body.func(mrb, recv); } else { recv = MRB_METHOD_FUNC(m)(mrb, recv); } mrb_gc_arena_restore(mrb, ai); mrb_gc_arena_shrink(mrb, ai); if (mrb->exc) goto L_RAISE; ci = mrb->c->ci; if (mrb_type(blk) == MRB_TT_PROC) { struct RProc *p = mrb_proc_ptr(blk); if (p && !MRB_PROC_STRICT_P(p) && MRB_PROC_ENV(p) == ci[-1].env) { p->flags |= MRB_PROC_ORPHAN; } } if (!ci->target_class) { /* return from context modifying method (resume/yield) */ if (ci->acc == CI_ACC_RESUMED) { mrb->jmp = prev_jmp; return recv; } else { mrb_assert(!MRB_PROC_CFUNC_P(ci[-1].proc)); proc = ci[-1].proc; irep = proc->body.irep; pool = irep->pool; syms = irep->syms; } } mrb->c->stack[0] = recv; /* pop stackpos */ mrb->c->stack = ci->stackent; pc = ci->pc; cipop(mrb); JUMP; } else { /* setup environment for calling method */ proc = ci->proc = MRB_METHOD_PROC(m); irep = proc->body.irep; pool = irep->pool; syms = irep->syms; mrb_stack_extend(mrb, (argc < 0 && irep->nregs < 3) ? 3 : irep->nregs); pc = irep->iseq; JUMP; } } CASE(OP_CALL, Z) { mrb_callinfo *ci; mrb_value recv = mrb->c->stack[0]; struct RProc *m = mrb_proc_ptr(recv); /* replace callinfo */ ci = mrb->c->ci; ci->target_class = MRB_PROC_TARGET_CLASS(m); ci->proc = m; if (MRB_PROC_ENV_P(m)) { mrb_sym mid; struct REnv *e = MRB_PROC_ENV(m); mid = e->mid; if (mid) ci->mid = mid; if (!e->stack) { e->stack = mrb->c->stack; } } /* prepare stack */ if (MRB_PROC_CFUNC_P(m)) { recv = MRB_PROC_CFUNC(m)(mrb, recv); mrb_gc_arena_restore(mrb, ai); mrb_gc_arena_shrink(mrb, ai); if (mrb->exc) goto L_RAISE; /* pop stackpos */ ci = mrb->c->ci; mrb->c->stack = ci->stackent; regs[ci->acc] = recv; pc = ci->pc; cipop(mrb); irep = mrb->c->ci->proc->body.irep; pool = irep->pool; syms = irep->syms; JUMP; } else { /* setup environment for calling method */ proc = m; irep = m->body.irep; if (!irep) { mrb->c->stack[0] = mrb_nil_value(); a = 0; c = OP_R_NORMAL; goto L_OP_RETURN_BODY; } pool = irep->pool; syms = irep->syms; mrb_stack_extend(mrb, irep->nregs); if (ci->argc < 0) { if (irep->nregs > 3) { stack_clear(regs+3, irep->nregs-3); } } else if (ci->argc+2 < irep->nregs) { stack_clear(regs+ci->argc+2, irep->nregs-ci->argc-2); } if (MRB_PROC_ENV_P(m)) { regs[0] = MRB_PROC_ENV(m)->stack[0]; } pc = irep->iseq; JUMP; } } CASE(OP_SUPER, BB) { int argc = (b == CALL_MAXARGS) ? -1 : b; int bidx = (argc < 0) ? a+2 : a+b+1; mrb_method_t m; struct RClass *cls; mrb_callinfo *ci = mrb->c->ci; mrb_value recv, blk; mrb_sym mid = ci->mid; struct RClass* target_class = MRB_PROC_TARGET_CLASS(ci->proc); mrb_assert(bidx < irep->nregs); if (mid == 0 || !target_class) { mrb_value exc = mrb_exc_new_str_lit(mrb, E_NOMETHOD_ERROR, "super called outside of method"); mrb_exc_set(mrb, exc); goto L_RAISE; } if (target_class->tt == MRB_TT_MODULE) { target_class = ci->target_class; if (target_class->tt != MRB_TT_ICLASS) { mrb_value exc = mrb_exc_new_str_lit(mrb, E_RUNTIME_ERROR, "superclass info lost [mruby limitations]"); mrb_exc_set(mrb, exc); goto L_RAISE; } } recv = regs[0]; if (!mrb_obj_is_kind_of(mrb, recv, target_class)) { mrb_value exc = mrb_exc_new_str_lit(mrb, E_TYPE_ERROR, "self has wrong type to call super in this context"); mrb_exc_set(mrb, exc); goto L_RAISE; } blk = regs[bidx]; if (!mrb_nil_p(blk) && mrb_type(blk) != MRB_TT_PROC) { blk = mrb_convert_type(mrb, blk, MRB_TT_PROC, "Proc", "to_proc"); /* The stack or ci stack might have been reallocated during mrb_convert_type(), see #3622 and #3784 */ regs[bidx] = blk; ci = mrb->c->ci; } cls = target_class->super; m = mrb_method_search_vm(mrb, &cls, mid); if (MRB_METHOD_UNDEF_P(m)) { mrb_sym missing = mrb_intern_lit(mrb, "method_missing"); if (mid != missing) { cls = mrb_class(mrb, recv); } m = mrb_method_search_vm(mrb, &cls, missing); if (MRB_METHOD_UNDEF_P(m)) { mrb_value args = (argc < 0) ? regs[a+1] : mrb_ary_new_from_values(mrb, b, regs+a+1); ERR_PC_SET(mrb); mrb_method_missing(mrb, mid, recv, args); } mid = missing; if (argc >= 0) { if (a+2 >= irep->nregs) { mrb_stack_extend(mrb, a+3); } regs[a+1] = mrb_ary_new_from_values(mrb, b, regs+a+1); regs[a+2] = blk; argc = -1; } mrb_ary_unshift(mrb, regs[a+1], mrb_symbol_value(ci->mid)); } /* push callinfo */ ci = cipush(mrb); ci->mid = mid; ci->stackent = mrb->c->stack; ci->target_class = cls; ci->pc = pc; ci->argc = argc; /* prepare stack */ mrb->c->stack += a; mrb->c->stack[0] = recv; if (MRB_METHOD_CFUNC_P(m)) { mrb_value v; if (MRB_METHOD_PROC_P(m)) { ci->proc = MRB_METHOD_PROC(m); } v = MRB_METHOD_CFUNC(m)(mrb, recv); mrb_gc_arena_restore(mrb, ai); if (mrb->exc) goto L_RAISE; ci = mrb->c->ci; if (!ci->target_class) { /* return from context modifying method (resume/yield) */ if (ci->acc == CI_ACC_RESUMED) { mrb->jmp = prev_jmp; return v; } else { mrb_assert(!MRB_PROC_CFUNC_P(ci[-1].proc)); proc = ci[-1].proc; irep = proc->body.irep; pool = irep->pool; syms = irep->syms; } } mrb->c->stack[0] = v; /* pop stackpos */ mrb->c->stack = ci->stackent; pc = ci->pc; cipop(mrb); JUMP; } else { /* fill callinfo */ ci->acc = a; /* setup environment for calling method */ proc = ci->proc = MRB_METHOD_PROC(m); irep = proc->body.irep; pool = irep->pool; syms = irep->syms; mrb_stack_extend(mrb, (argc < 0 && irep->nregs < 3) ? 3 : irep->nregs); pc = irep->iseq; JUMP; } } CASE(OP_ARGARY, BS) { int m1 = (b>>11)&0x3f; int r = (b>>10)&0x1; int m2 = (b>>5)&0x1f; int kd = (b>>4)&0x1; int lv = (b>>0)&0xf; mrb_value *stack; if (mrb->c->ci->mid == 0 || mrb->c->ci->target_class == NULL) { mrb_value exc; L_NOSUPER: exc = mrb_exc_new_str_lit(mrb, E_NOMETHOD_ERROR, "super called outside of method"); mrb_exc_set(mrb, exc); goto L_RAISE; } if (lv == 0) stack = regs + 1; else { struct REnv *e = uvenv(mrb, lv-1); if (!e) goto L_NOSUPER; if (MRB_ENV_STACK_LEN(e) <= m1+r+m2+kd+1) goto L_NOSUPER; stack = e->stack + 1; } if (r == 0) { regs[a] = mrb_ary_new_from_values(mrb, m1+m2+kd, stack); } else { mrb_value *pp = NULL; struct RArray *rest; int len = 0; if (mrb_array_p(stack[m1])) { struct RArray *ary = mrb_ary_ptr(stack[m1]); pp = ARY_PTR(ary); len = (int)ARY_LEN(ary); } regs[a] = mrb_ary_new_capa(mrb, m1+len+m2+kd); rest = mrb_ary_ptr(regs[a]); if (m1 > 0) { stack_copy(ARY_PTR(rest), stack, m1); } if (len > 0) { stack_copy(ARY_PTR(rest)+m1, pp, len); } if (m2 > 0) { stack_copy(ARY_PTR(rest)+m1+len, stack+m1+1, m2); } if (kd) { stack_copy(ARY_PTR(rest)+m1+len+m2, stack+m1+m2+1, kd); } ARY_SET_LEN(rest, m1+len+m2+kd); } regs[a+1] = stack[m1+r+m2]; mrb_gc_arena_restore(mrb, ai); NEXT; } CASE(OP_ENTER, W) { int m1 = MRB_ASPEC_REQ(a); int o = MRB_ASPEC_OPT(a); int r = MRB_ASPEC_REST(a); int m2 = MRB_ASPEC_POST(a); int kd = (MRB_ASPEC_KEY(a) > 0 || MRB_ASPEC_KDICT(a))? 1 : 0; /* unused int b = MRB_ASPEC_BLOCK(a); */ int argc = mrb->c->ci->argc; mrb_value *argv = regs+1; mrb_value * const argv0 = argv; int const len = m1 + o + r + m2; int const blk_pos = len + kd + 1; mrb_value *blk = &argv[argc < 0 ? 1 : argc]; mrb_value kdict; int kargs = kd; /* arguments is passed with Array */ if (argc < 0) { struct RArray *ary = mrb_ary_ptr(regs[1]); argv = ARY_PTR(ary); argc = (int)ARY_LEN(ary); mrb_gc_protect(mrb, regs[1]); } /* strict argument check */ if (mrb->c->ci->proc && MRB_PROC_STRICT_P(mrb->c->ci->proc)) { if (argc < m1 + m2 || (r == 0 && argc > len + kd)) { argnum_error(mrb, m1+m2); goto L_RAISE; } } /* extract first argument array to arguments */ else if (len > 1 && argc == 1 && mrb_array_p(argv[0])) { mrb_gc_protect(mrb, argv[0]); argc = (int)RARRAY_LEN(argv[0]); argv = RARRAY_PTR(argv[0]); } if (kd) { /* check last arguments is hash if method takes keyword arguments */ if (argc == m1+m2) { kdict = mrb_hash_new(mrb); kargs = 0; } else { if (argv && argc > 0 && mrb_hash_p(argv[argc-1])) { kdict = argv[argc-1]; mrb_hash_check_kdict(mrb, kdict); } else if (r || argc <= m1+m2+o) { kdict = mrb_hash_new(mrb); kargs = 0; } else { argnum_error(mrb, m1+m2); goto L_RAISE; } if (MRB_ASPEC_KEY(a) > 0) { kdict = mrb_hash_dup(mrb, kdict); } } } /* no rest arguments */ if (argc-kargs < len) { int mlen = m2; if (argc < m1+m2) { mlen = m1 < argc ? argc - m1 : 0; } regs[blk_pos] = *blk; /* move block */ if (kd) regs[len + 1] = kdict; /* copy mandatory and optional arguments */ if (argv0 != argv) { value_move(®s[1], argv, argc-mlen); /* m1 + o */ } if (argc < m1) { stack_clear(®s[argc+1], m1-argc); } /* copy post mandatory arguments */ if (mlen) { value_move(®s[len-m2+1], &argv[argc-mlen], mlen); } if (mlen < m2) { stack_clear(®s[len-m2+mlen+1], m2-mlen); } /* initalize rest arguments with empty Array */ if (r) { regs[m1+o+1] = mrb_ary_new_capa(mrb, 0); } /* skip initailizer of passed arguments */ if (o > 0 && argc-kargs > m1+m2) pc += (argc - kargs - m1 - m2)*3; } else { int rnum = 0; if (argv0 != argv) { regs[blk_pos] = *blk; /* move block */ if (kd) regs[len + 1] = kdict; value_move(®s[1], argv, m1+o); } if (r) { mrb_value ary; rnum = argc-m1-o-m2-kargs; ary = mrb_ary_new_from_values(mrb, rnum, argv+m1+o); regs[m1+o+1] = ary; } if (m2) { if (argc-m2 > m1) { value_move(®s[m1+o+r+1], &argv[m1+o+rnum], m2); } } if (argv0 == argv) { regs[blk_pos] = *blk; /* move block */ if (kd) regs[len + 1] = kdict; } pc += o*3; } /* format arguments for generated code */ mrb->c->ci->argc = len + kd; /* clear local (but non-argument) variables */ if (irep->nlocals-blk_pos-1 > 0) { stack_clear(®s[blk_pos+1], irep->nlocals-blk_pos-1); } JUMP; } CASE(OP_KARG, BB) { mrb_value k = mrb_symbol_value(syms[b]); mrb_value kdict = regs[mrb->c->ci->argc]; if (!mrb_hash_p(kdict) || !mrb_hash_key_p(mrb, kdict, k)) { mrb_value str = mrb_format(mrb, "missing keyword: %S", k); mrb_exc_set(mrb, mrb_exc_new_str(mrb, E_ARGUMENT_ERROR, str)); goto L_RAISE; } regs[a] = mrb_hash_get(mrb, kdict, k); mrb_hash_delete_key(mrb, kdict, k); NEXT; } CASE(OP_KEY_P, BB) { mrb_value k = mrb_symbol_value(syms[b]); mrb_value kdict = regs[mrb->c->ci->argc]; mrb_bool key_p = FALSE; if (mrb_hash_p(kdict)) { key_p = mrb_hash_key_p(mrb, kdict, k); } regs[a] = mrb_bool_value(key_p); NEXT; } CASE(OP_KEYEND, Z) { mrb_value kdict = regs[mrb->c->ci->argc]; if (mrb_hash_p(kdict) && !mrb_hash_empty_p(mrb, kdict)) { mrb_value keys = mrb_hash_keys(mrb, kdict); mrb_value key1 = RARRAY_PTR(keys)[0]; mrb_value str = mrb_format(mrb, "unknown keyword: %S", key1); mrb_exc_set(mrb, mrb_exc_new_str(mrb, E_ARGUMENT_ERROR, str)); goto L_RAISE; } NEXT; } CASE(OP_BREAK, B) { c = OP_R_BREAK; goto L_RETURN; } CASE(OP_RETURN_BLK, B) { c = OP_R_RETURN; goto L_RETURN; } CASE(OP_RETURN, B) c = OP_R_NORMAL; L_RETURN: { mrb_callinfo *ci; #define ecall_adjust() do {\ ptrdiff_t cioff = ci - mrb->c->cibase;\ ecall(mrb);\ ci = mrb->c->cibase + cioff;\ } while (0) ci = mrb->c->ci; if (ci->mid) { mrb_value blk; if (ci->argc < 0) { blk = regs[2]; } else { blk = regs[ci->argc+1]; } if (mrb_type(blk) == MRB_TT_PROC) { struct RProc *p = mrb_proc_ptr(blk); if (!MRB_PROC_STRICT_P(p) && ci > mrb->c->cibase && MRB_PROC_ENV(p) == ci[-1].env) { p->flags |= MRB_PROC_ORPHAN; } } } if (mrb->exc) { mrb_callinfo *ci0; L_RAISE: ci0 = ci = mrb->c->ci; if (ci == mrb->c->cibase) { if (ci->ridx == 0) goto L_FTOP; goto L_RESCUE; } while (ci[0].ridx == ci[-1].ridx) { cipop(mrb); mrb->c->stack = ci->stackent; if (ci->acc == CI_ACC_SKIP && prev_jmp) { mrb->jmp = prev_jmp; MRB_THROW(prev_jmp); } ci = mrb->c->ci; if (ci == mrb->c->cibase) { if (ci->ridx == 0) { L_FTOP: /* fiber top */ if (mrb->c == mrb->root_c) { mrb->c->stack = mrb->c->stbase; goto L_STOP; } else { struct mrb_context *c = mrb->c; while (c->eidx > ci->epos) { ecall_adjust(); } c->status = MRB_FIBER_TERMINATED; mrb->c = c->prev; c->prev = NULL; goto L_RAISE; } } break; } /* call ensure only when we skip this callinfo */ if (ci[0].ridx == ci[-1].ridx) { while (mrb->c->eidx > ci->epos) { ecall_adjust(); } } } L_RESCUE: if (ci->ridx == 0) goto L_STOP; proc = ci->proc; irep = proc->body.irep; pool = irep->pool; syms = irep->syms; if (ci < ci0) { mrb->c->stack = ci[1].stackent; } mrb_stack_extend(mrb, irep->nregs); pc = irep->iseq+mrb->c->rescue[--ci->ridx]; } else { int acc; mrb_value v; struct RProc *dst; ci = mrb->c->ci; v = regs[a]; mrb_gc_protect(mrb, v); switch (c) { case OP_R_RETURN: /* Fall through to OP_R_NORMAL otherwise */ if (ci->acc >=0 && MRB_PROC_ENV_P(proc) && !MRB_PROC_STRICT_P(proc)) { mrb_callinfo *cibase = mrb->c->cibase; dst = top_proc(mrb, proc); if (MRB_PROC_ENV_P(dst)) { struct REnv *e = MRB_PROC_ENV(dst); if (!MRB_ENV_STACK_SHARED_P(e) || e->cxt != mrb->c) { localjump_error(mrb, LOCALJUMP_ERROR_RETURN); goto L_RAISE; } } while (cibase <= ci && ci->proc != dst) { if (ci->acc < 0) { localjump_error(mrb, LOCALJUMP_ERROR_RETURN); goto L_RAISE; } ci--; } if (ci <= cibase) { localjump_error(mrb, LOCALJUMP_ERROR_RETURN); goto L_RAISE; } break; } /* fallthrough */ case OP_R_NORMAL: NORMAL_RETURN: if (ci == mrb->c->cibase) { struct mrb_context *c = mrb->c; if (!c->prev) { /* toplevel return */ regs[irep->nlocals] = v; goto L_STOP; } if (c->prev->ci == c->prev->cibase) { mrb_value exc = mrb_exc_new_str_lit(mrb, E_FIBER_ERROR, "double resume"); mrb_exc_set(mrb, exc); goto L_RAISE; } while (c->eidx > 0) { ecall(mrb); } /* automatic yield at the end */ c->status = MRB_FIBER_TERMINATED; mrb->c = c->prev; c->prev = NULL; mrb->c->status = MRB_FIBER_RUNNING; ci = mrb->c->ci; } break; case OP_R_BREAK: if (MRB_PROC_STRICT_P(proc)) goto NORMAL_RETURN; if (MRB_PROC_ORPHAN_P(proc)) { mrb_value exc; L_BREAK_ERROR: exc = mrb_exc_new_str_lit(mrb, E_LOCALJUMP_ERROR, "break from proc-closure"); mrb_exc_set(mrb, exc); goto L_RAISE; } if (!MRB_PROC_ENV_P(proc) || !MRB_ENV_STACK_SHARED_P(MRB_PROC_ENV(proc))) { goto L_BREAK_ERROR; } else { struct REnv *e = MRB_PROC_ENV(proc); if (e->cxt != mrb->c) { goto L_BREAK_ERROR; } } while (mrb->c->eidx > mrb->c->ci->epos) { ecall_adjust(); } /* break from fiber block */ if (ci == mrb->c->cibase && ci->pc) { struct mrb_context *c = mrb->c; mrb->c = c->prev; c->prev = NULL; ci = mrb->c->ci; } if (ci->acc < 0) { mrb_gc_arena_restore(mrb, ai); mrb->c->vmexec = FALSE; mrb->exc = (struct RObject*)break_new(mrb, proc, v); mrb->jmp = prev_jmp; MRB_THROW(prev_jmp); } if (FALSE) { L_BREAK: v = ((struct RBreak*)mrb->exc)->val; proc = ((struct RBreak*)mrb->exc)->proc; mrb->exc = NULL; ci = mrb->c->ci; } mrb->c->stack = ci->stackent; proc = proc->upper; while (mrb->c->cibase < ci && ci[-1].proc != proc) { if (ci[-1].acc == CI_ACC_SKIP) { while (ci < mrb->c->ci) { cipop(mrb); } goto L_BREAK_ERROR; } ci--; } if (ci == mrb->c->cibase) { goto L_BREAK_ERROR; } break; default: /* cannot happen */ break; } while (ci < mrb->c->ci) { cipop(mrb); } ci[0].ridx = ci[-1].ridx; while (mrb->c->eidx > ci->epos) { ecall_adjust(); } if (mrb->c->vmexec && !ci->target_class) { mrb_gc_arena_restore(mrb, ai); mrb->c->vmexec = FALSE; mrb->jmp = prev_jmp; return v; } acc = ci->acc; mrb->c->stack = ci->stackent; cipop(mrb); if (acc == CI_ACC_SKIP || acc == CI_ACC_DIRECT) { mrb_gc_arena_restore(mrb, ai); mrb->jmp = prev_jmp; return v; } pc = ci->pc; ci = mrb->c->ci; DEBUG(fprintf(stderr, "from :%s\n", mrb_sym2name(mrb, ci->mid))); proc = mrb->c->ci->proc; irep = proc->body.irep; pool = irep->pool; syms = irep->syms; regs[acc] = v; mrb_gc_arena_restore(mrb, ai); } JUMP; } CASE(OP_BLKPUSH, BS) { int m1 = (b>>11)&0x3f; int r = (b>>10)&0x1; int m2 = (b>>5)&0x1f; int kd = (b>>4)&0x1; int lv = (b>>0)&0xf; mrb_value *stack; if (lv == 0) stack = regs + 1; else { struct REnv *e = uvenv(mrb, lv-1); if (!e || (!MRB_ENV_STACK_SHARED_P(e) && e->mid == 0) || MRB_ENV_STACK_LEN(e) <= m1+r+m2+1) { localjump_error(mrb, LOCALJUMP_ERROR_YIELD); goto L_RAISE; } stack = e->stack + 1; } if (mrb_nil_p(stack[m1+r+m2])) { localjump_error(mrb, LOCALJUMP_ERROR_YIELD); goto L_RAISE; } regs[a] = stack[m1+r+m2+kd]; NEXT; } #define TYPES2(a,b) ((((uint16_t)(a))<<8)|(((uint16_t)(b))&0xff)) #define OP_MATH_BODY(op,v1,v2) do {\ v1(regs[a]) = v1(regs[a]) op v2(regs[a+1]);\ } while(0) CASE(OP_ADD, B) { /* need to check if op is overridden */ switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) { case TYPES2(MRB_TT_FIXNUM,MRB_TT_FIXNUM): { mrb_int x, y, z; mrb_value *regs_a = regs + a; x = mrb_fixnum(regs_a[0]); y = mrb_fixnum(regs_a[1]); if (mrb_int_add_overflow(x, y, &z)) { #ifndef MRB_WITHOUT_FLOAT SET_FLOAT_VALUE(mrb, regs_a[0], (mrb_float)x + (mrb_float)y); break; #endif } SET_INT_VALUE(regs[a], z); } break; #ifndef MRB_WITHOUT_FLOAT case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT): { mrb_int x = mrb_fixnum(regs[a]); mrb_float y = mrb_float(regs[a+1]); SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x + y); } break; case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM): #ifdef MRB_WORD_BOXING { mrb_float x = mrb_float(regs[a]); mrb_int y = mrb_fixnum(regs[a+1]); SET_FLOAT_VALUE(mrb, regs[a], x + y); } #else OP_MATH_BODY(+,mrb_float,mrb_fixnum); #endif break; case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT): #ifdef MRB_WORD_BOXING { mrb_float x = mrb_float(regs[a]); mrb_float y = mrb_float(regs[a+1]); SET_FLOAT_VALUE(mrb, regs[a], x + y); } #else OP_MATH_BODY(+,mrb_float,mrb_float); #endif break; #endif case TYPES2(MRB_TT_STRING,MRB_TT_STRING): regs[a] = mrb_str_plus(mrb, regs[a], regs[a+1]); break; default: c = 1; mid = mrb_intern_lit(mrb, "+"); goto L_SEND_SYM; } mrb_gc_arena_restore(mrb, ai); NEXT; } CASE(OP_SUB, B) { /* need to check if op is overridden */ switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) { case TYPES2(MRB_TT_FIXNUM,MRB_TT_FIXNUM): { mrb_int x, y, z; x = mrb_fixnum(regs[a]); y = mrb_fixnum(regs[a+1]); if (mrb_int_sub_overflow(x, y, &z)) { #ifndef MRB_WITHOUT_FLOAT SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x - (mrb_float)y); break; #endif } SET_INT_VALUE(regs[a], z); } break; #ifndef MRB_WITHOUT_FLOAT case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT): { mrb_int x = mrb_fixnum(regs[a]); mrb_float y = mrb_float(regs[a+1]); SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x - y); } break; case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM): #ifdef MRB_WORD_BOXING { mrb_float x = mrb_float(regs[a]); mrb_int y = mrb_fixnum(regs[a+1]); SET_FLOAT_VALUE(mrb, regs[a], x - y); } #else OP_MATH_BODY(-,mrb_float,mrb_fixnum); #endif break; case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT): #ifdef MRB_WORD_BOXING { mrb_float x = mrb_float(regs[a]); mrb_float y = mrb_float(regs[a+1]); SET_FLOAT_VALUE(mrb, regs[a], x - y); } #else OP_MATH_BODY(-,mrb_float,mrb_float); #endif break; #endif default: c = 1; mid = mrb_intern_lit(mrb, "-"); goto L_SEND_SYM; } NEXT; } CASE(OP_MUL, B) { /* need to check if op is overridden */ switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) { case TYPES2(MRB_TT_FIXNUM,MRB_TT_FIXNUM): { mrb_int x, y, z; x = mrb_fixnum(regs[a]); y = mrb_fixnum(regs[a+1]); if (mrb_int_mul_overflow(x, y, &z)) { #ifndef MRB_WITHOUT_FLOAT SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x * (mrb_float)y); break; #endif } SET_INT_VALUE(regs[a], z); } break; #ifndef MRB_WITHOUT_FLOAT case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT): { mrb_int x = mrb_fixnum(regs[a]); mrb_float y = mrb_float(regs[a+1]); SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x * y); } break; case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM): #ifdef MRB_WORD_BOXING { mrb_float x = mrb_float(regs[a]); mrb_int y = mrb_fixnum(regs[a+1]); SET_FLOAT_VALUE(mrb, regs[a], x * y); } #else OP_MATH_BODY(*,mrb_float,mrb_fixnum); #endif break; case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT): #ifdef MRB_WORD_BOXING { mrb_float x = mrb_float(regs[a]); mrb_float y = mrb_float(regs[a+1]); SET_FLOAT_VALUE(mrb, regs[a], x * y); } #else OP_MATH_BODY(*,mrb_float,mrb_float); #endif break; #endif default: c = 1; mid = mrb_intern_lit(mrb, "*"); goto L_SEND_SYM; } NEXT; } CASE(OP_DIV, B) { #ifndef MRB_WITHOUT_FLOAT double x, y, f; #endif /* need to check if op is overridden */ switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) { case TYPES2(MRB_TT_FIXNUM,MRB_TT_FIXNUM): #ifdef MRB_WITHOUT_FLOAT { mrb_int x = mrb_fixnum(regs[a]); mrb_int y = mrb_fixnum(regs[a+1]); SET_INT_VALUE(regs[a], y ? x / y : 0); } break; #else x = (mrb_float)mrb_fixnum(regs[a]); y = (mrb_float)mrb_fixnum(regs[a+1]); break; case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT): x = (mrb_float)mrb_fixnum(regs[a]); y = mrb_float(regs[a+1]); break; case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM): x = mrb_float(regs[a]); y = (mrb_float)mrb_fixnum(regs[a+1]); break; case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT): x = mrb_float(regs[a]); y = mrb_float(regs[a+1]); break; #endif default: c = 1; mid = mrb_intern_lit(mrb, "/"); goto L_SEND_SYM; } #ifndef MRB_WITHOUT_FLOAT if (y == 0) { if (x > 0) f = INFINITY; else if (x < 0) f = -INFINITY; else /* if (x == 0) */ f = NAN; } else { f = x / y; } SET_FLOAT_VALUE(mrb, regs[a], f); #endif NEXT; } CASE(OP_ADDI, BB) { /* need to check if + is overridden */ switch (mrb_type(regs[a])) { case MRB_TT_FIXNUM: { mrb_int x = mrb_fixnum(regs[a]); mrb_int y = (mrb_int)b; mrb_int z; if (mrb_int_add_overflow(x, y, &z)) { #ifndef MRB_WITHOUT_FLOAT SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x + (mrb_float)y); break; #endif } SET_INT_VALUE(regs[a], z); } break; #ifndef MRB_WITHOUT_FLOAT case MRB_TT_FLOAT: #ifdef MRB_WORD_BOXING { mrb_float x = mrb_float(regs[a]); SET_FLOAT_VALUE(mrb, regs[a], x + b); } #else mrb_float(regs[a]) += b; #endif break; #endif default: SET_INT_VALUE(regs[a+1], b); c = 1; mid = mrb_intern_lit(mrb, "+"); goto L_SEND_SYM; } NEXT; } CASE(OP_SUBI, BB) { mrb_value *regs_a = regs + a; /* need to check if + is overridden */ switch (mrb_type(regs_a[0])) { case MRB_TT_FIXNUM: { mrb_int x = mrb_fixnum(regs_a[0]); mrb_int y = (mrb_int)b; mrb_int z; if (mrb_int_sub_overflow(x, y, &z)) { #ifndef MRB_WITHOUT_FLOAT SET_FLOAT_VALUE(mrb, regs_a[0], (mrb_float)x - (mrb_float)y); break; #endif } SET_INT_VALUE(regs_a[0], z); } break; #ifndef MRB_WITHOUT_FLOAT case MRB_TT_FLOAT: #ifdef MRB_WORD_BOXING { mrb_float x = mrb_float(regs[a]); SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x - (mrb_float)b); } #else mrb_float(regs_a[0]) -= b; #endif break; #endif default: SET_INT_VALUE(regs_a[1], b); c = 1; mid = mrb_intern_lit(mrb, "-"); goto L_SEND_SYM; } NEXT; } #define OP_CMP_BODY(op,v1,v2) (v1(regs[a]) op v2(regs[a+1])) #ifdef MRB_WITHOUT_FLOAT #define OP_CMP(op) do {\ int result;\ /* need to check if - is overridden */\ switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) {\ case TYPES2(MRB_TT_FIXNUM,MRB_TT_FIXNUM):\ result = OP_CMP_BODY(op,mrb_fixnum,mrb_fixnum);\ break;\ default:\ c = 1;\ mid = mrb_intern_lit(mrb, # op);\ goto L_SEND_SYM;\ }\ if (result) {\ SET_TRUE_VALUE(regs[a]);\ }\ else {\ SET_FALSE_VALUE(regs[a]);\ }\ } while(0) #else #define OP_CMP(op) do {\ int result;\ /* need to check if - is overridden */\ switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) {\ case TYPES2(MRB_TT_FIXNUM,MRB_TT_FIXNUM):\ result = OP_CMP_BODY(op,mrb_fixnum,mrb_fixnum);\ break;\ case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT):\ result = OP_CMP_BODY(op,mrb_fixnum,mrb_float);\ break;\ case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM):\ result = OP_CMP_BODY(op,mrb_float,mrb_fixnum);\ break;\ case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT):\ result = OP_CMP_BODY(op,mrb_float,mrb_float);\ break;\ default:\ c = 1;\ mid = mrb_intern_lit(mrb, # op);\ goto L_SEND_SYM;\ }\ if (result) {\ SET_TRUE_VALUE(regs[a]);\ }\ else {\ SET_FALSE_VALUE(regs[a]);\ }\ } while(0) #endif CASE(OP_EQ, B) { if (mrb_obj_eq(mrb, regs[a], regs[a+1])) { SET_TRUE_VALUE(regs[a]); } else { OP_CMP(==); } NEXT; } CASE(OP_LT, B) { OP_CMP(<); NEXT; } CASE(OP_LE, B) { OP_CMP(<=); NEXT; } CASE(OP_GT, B) { OP_CMP(>); NEXT; } CASE(OP_GE, B) { OP_CMP(>=); NEXT; } CASE(OP_ARRAY, BB) { mrb_value v = mrb_ary_new_from_values(mrb, b, ®s[a]); regs[a] = v; mrb_gc_arena_restore(mrb, ai); NEXT; } CASE(OP_ARRAY2, BBB) { mrb_value v = mrb_ary_new_from_values(mrb, c, ®s[b]); regs[a] = v; mrb_gc_arena_restore(mrb, ai); NEXT; } CASE(OP_ARYCAT, B) { mrb_value splat = mrb_ary_splat(mrb, regs[a+1]); mrb_ary_concat(mrb, regs[a], splat); mrb_gc_arena_restore(mrb, ai); NEXT; } CASE(OP_ARYPUSH, B) { mrb_ary_push(mrb, regs[a], regs[a+1]); NEXT; } CASE(OP_ARYDUP, B) { mrb_value ary = regs[a]; if (mrb_array_p(ary)) { ary = mrb_ary_new_from_values(mrb, RARRAY_LEN(ary), RARRAY_PTR(ary)); } else { ary = mrb_ary_new_from_values(mrb, 1, &ary); } regs[a] = ary; NEXT; } CASE(OP_AREF, BBB) { mrb_value v = regs[b]; if (!mrb_array_p(v)) { if (c == 0) { regs[a] = v; } else { SET_NIL_VALUE(regs[a]); } } else { v = mrb_ary_ref(mrb, v, c); regs[a] = v; } NEXT; } CASE(OP_ASET, BBB) { mrb_ary_set(mrb, regs[b], c, regs[a]); NEXT; } CASE(OP_APOST, BBB) { mrb_value v = regs[a]; int pre = b; int post = c; struct RArray *ary; int len, idx; if (!mrb_array_p(v)) { v = mrb_ary_new_from_values(mrb, 1, ®s[a]); } ary = mrb_ary_ptr(v); len = (int)ARY_LEN(ary); if (len > pre + post) { v = mrb_ary_new_from_values(mrb, len - pre - post, ARY_PTR(ary)+pre); regs[a++] = v; while (post--) { regs[a++] = ARY_PTR(ary)[len-post-1]; } } else { v = mrb_ary_new_capa(mrb, 0); regs[a++] = v; for (idx=0; idx+prereps[b]; if (c & OP_L_CAPTURE) { p = mrb_closure_new(mrb, nirep); } else { p = mrb_proc_new(mrb, nirep); p->flags |= MRB_PROC_SCOPE; } if (c & OP_L_STRICT) p->flags |= MRB_PROC_STRICT; regs[a] = mrb_obj_value(p); mrb_gc_arena_restore(mrb, ai); NEXT; } CASE(OP_BLOCK, BB) { c = OP_L_BLOCK; goto L_MAKE_LAMBDA; } CASE(OP_METHOD, BB) { c = OP_L_METHOD; goto L_MAKE_LAMBDA; } CASE(OP_RANGE_INC, B) { mrb_value val = mrb_range_new(mrb, regs[a], regs[a+1], FALSE); regs[a] = val; mrb_gc_arena_restore(mrb, ai); NEXT; } CASE(OP_RANGE_EXC, B) { mrb_value val = mrb_range_new(mrb, regs[a], regs[a+1], TRUE); regs[a] = val; mrb_gc_arena_restore(mrb, ai); NEXT; } CASE(OP_OCLASS, B) { regs[a] = mrb_obj_value(mrb->object_class); NEXT; } CASE(OP_CLASS, BB) { struct RClass *c = 0, *baseclass; mrb_value base, super; mrb_sym id = syms[b]; base = regs[a]; super = regs[a+1]; if (mrb_nil_p(base)) { baseclass = MRB_PROC_TARGET_CLASS(mrb->c->ci->proc); base = mrb_obj_value(baseclass); } c = mrb_vm_define_class(mrb, base, super, id); regs[a] = mrb_obj_value(c); mrb_gc_arena_restore(mrb, ai); NEXT; } CASE(OP_MODULE, BB) { struct RClass *cls = 0, *baseclass; mrb_value base; mrb_sym id = syms[b]; base = regs[a]; if (mrb_nil_p(base)) { baseclass = MRB_PROC_TARGET_CLASS(mrb->c->ci->proc); base = mrb_obj_value(baseclass); } cls = mrb_vm_define_module(mrb, base, id); regs[a] = mrb_obj_value(cls); mrb_gc_arena_restore(mrb, ai); NEXT; } CASE(OP_EXEC, BB) { mrb_callinfo *ci; mrb_value recv = regs[a]; struct RProc *p; mrb_irep *nirep = irep->reps[b]; /* prepare closure */ p = mrb_proc_new(mrb, nirep); p->c = NULL; mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)proc); MRB_PROC_SET_TARGET_CLASS(p, mrb_class_ptr(recv)); p->flags |= MRB_PROC_SCOPE; /* prepare call stack */ ci = cipush(mrb); ci->pc = pc; ci->acc = a; ci->mid = 0; ci->stackent = mrb->c->stack; ci->argc = 0; ci->target_class = mrb_class_ptr(recv); /* prepare stack */ mrb->c->stack += a; /* setup block to call */ ci->proc = p; irep = p->body.irep; pool = irep->pool; syms = irep->syms; mrb_stack_extend(mrb, irep->nregs); stack_clear(regs+1, irep->nregs-1); pc = irep->iseq; JUMP; } CASE(OP_DEF, BB) { struct RClass *target = mrb_class_ptr(regs[a]); struct RProc *p = mrb_proc_ptr(regs[a+1]); mrb_method_t m; MRB_METHOD_FROM_PROC(m, p); mrb_define_method_raw(mrb, target, syms[b], m); mrb_gc_arena_restore(mrb, ai); NEXT; } CASE(OP_SCLASS, B) { regs[a] = mrb_singleton_class(mrb, regs[a]); mrb_gc_arena_restore(mrb, ai); NEXT; } CASE(OP_TCLASS, B) { if (!check_target_class(mrb)) goto L_RAISE; regs[a] = mrb_obj_value(mrb->c->ci->target_class); NEXT; } CASE(OP_ALIAS, BB) { struct RClass *target; if (!check_target_class(mrb)) goto L_RAISE; target = mrb->c->ci->target_class; mrb_alias_method(mrb, target, syms[a], syms[b]); NEXT; } CASE(OP_UNDEF, B) { struct RClass *target; if (!check_target_class(mrb)) goto L_RAISE; target = mrb->c->ci->target_class; mrb_undef_method_id(mrb, target, syms[a]); NEXT; } CASE(OP_DEBUG, Z) { FETCH_BBB(); #ifdef MRB_ENABLE_DEBUG_HOOK mrb->debug_op_hook(mrb, irep, pc, regs); #else #ifndef MRB_DISABLE_STDIO printf("OP_DEBUG %d %d %d\n", a, b, c); #else abort(); #endif #endif NEXT; } CASE(OP_ERR, B) { mrb_value msg = mrb_str_dup(mrb, pool[a]); mrb_value exc; exc = mrb_exc_new_str(mrb, E_LOCALJUMP_ERROR, msg); ERR_PC_SET(mrb); mrb_exc_set(mrb, exc); goto L_RAISE; } CASE(OP_EXT1, Z) { insn = READ_B(); switch (insn) { #define OPCODE(insn,ops) case OP_ ## insn: FETCH_ ## ops ## _1(); goto L_OP_ ## insn ## _BODY; #include "mruby/ops.h" #undef OPCODE } pc--; NEXT; } CASE(OP_EXT2, Z) { insn = READ_B(); switch (insn) { #define OPCODE(insn,ops) case OP_ ## insn: FETCH_ ## ops ## _2(); goto L_OP_ ## insn ## _BODY; #include "mruby/ops.h" #undef OPCODE } pc--; NEXT; } CASE(OP_EXT3, Z) { uint8_t insn = READ_B(); switch (insn) { #define OPCODE(insn,ops) case OP_ ## insn: FETCH_ ## ops ## _3(); goto L_OP_ ## insn ## _BODY; #include "mruby/ops.h" #undef OPCODE } pc--; NEXT; } CASE(OP_STOP, Z) { /* stop VM */ L_STOP: while (mrb->c->eidx > 0) { ecall(mrb); } mrb->c->cibase->ridx = 0; ERR_PC_CLR(mrb); mrb->jmp = prev_jmp; if (mrb->exc) { return mrb_obj_value(mrb->exc); } return regs[irep->nlocals]; } } END_DISPATCH; #undef regs } MRB_CATCH(&c_jmp) { exc_catched = TRUE; goto RETRY_TRY_BLOCK; } MRB_END_EXC(&c_jmp); } MRB_API mrb_value mrb_run(mrb_state *mrb, struct RProc *proc, mrb_value self) { if (mrb->c->ci->argc < 0) { return mrb_vm_run(mrb, proc, self, 3); /* receiver, args and block) */ } else { return mrb_vm_run(mrb, proc, self, mrb->c->ci->argc + 2); /* argc + 2 (receiver and block) */ } } MRB_API mrb_value mrb_top_run(mrb_state *mrb, struct RProc *proc, mrb_value self, unsigned int stack_keep) { mrb_callinfo *ci; mrb_value v; if (!mrb->c->cibase) { return mrb_vm_run(mrb, proc, self, stack_keep); } if (mrb->c->ci == mrb->c->cibase) { return mrb_vm_run(mrb, proc, self, stack_keep); } ci = cipush(mrb); ci->mid = 0; ci->acc = CI_ACC_SKIP; ci->target_class = mrb->object_class; v = mrb_vm_run(mrb, proc, self, stack_keep); cipop(mrb); return v; } #if defined(MRB_ENABLE_CXX_EXCEPTION) && defined(__cplusplus) # if !defined(MRB_ENABLE_CXX_ABI) } /* end of extern "C" */ # endif mrb_int mrb_jmpbuf::jmpbuf_id = 0; # if !defined(MRB_ENABLE_CXX_ABI) extern "C" { # endif #endif mruby-2.0.0/tasks/000077500000000000000000000000001340361412400137725ustar00rootroot00000000000000mruby-2.0.0/tasks/benchmark.rake000066400000000000000000000037341340361412400165770ustar00rootroot00000000000000module MRuby BENCHMARK_REPEAT = 4 end $dat_files = [] def bm_files Dir.glob("#{MRUBY_ROOT}/benchmark/bm_*.rb") end def build_config_name if ENV['MRUBY_CONFIG'] File.basename(ENV['MRUBY_CONFIG'], '.rb').gsub('build_config_', '') else "build" end end def plot_file File.join(MRUBY_ROOT, 'benchmark', "#{build_config_name}.png") end def plot opts_file = "#{MRUBY_ROOT}/benchmark/plot.gpl" opts = File.read(opts_file).each_line.to_a.map(&:strip).join(';') dat_files = $dat_files.group_by {|f| File.dirname(f).split(File::SEPARATOR)[-1]} opts += ";set output '#{plot_file}'" opts += ';plot ' opts += dat_files.keys.map do |data_file| %Q['-' u 2:3:4:xtic(1) w hist title columnheader(1)] end.join(',') opts += ';' cmd = %Q{gnuplot -p -e "#{opts}"} IO.popen(cmd, 'w') do |p| dat_files.each do |target_name, bm_files| p.puts target_name.gsub('_', '-') bm_files.each do |bm_file| p.write File.read(bm_file) end p.puts "e" end end end MRuby.each_target do |target| next if target.name == 'host' mruby_bin = "#{target.build_dir}/bin/mruby" bm_files.each do |bm_file| bm_name = File.basename bm_file, ".rb" dat_dir = File.join('benchmark', build_config_name, target.name) dat_file = File.join(dat_dir, "#{bm_name}.dat") $dat_files << dat_file directory dat_dir file dat_file => [bm_file, dat_dir, mruby_bin] do |task| print bm_name puts "..." data = (0...MRuby::BENCHMARK_REPEAT).map do |n| str = %x{(time -f "%e %S %U" #{mruby_bin} #{bm_file}) 2>&1 >/dev/null} str.split(' ').map(&:to_f) end File.open(task.name, "w") do |f| data = data.map {|_,r,s| (r + s) / 2.0} min = data.min max = data.max avg = data.inject(&:+) / data.size f.puts "#{bm_name.gsub('_', '-')} #{avg} #{min} #{max}" end end end end file plot_file => $dat_files do plot end task :benchmark => plot_file do plot end mruby-2.0.0/tasks/gitlab.rake000066400000000000000000000101021340361412400160720ustar00rootroot00000000000000CI_VERSION = '0.7'.freeze CI_BASE = 'ubuntu:16.10'.freeze CI_COMPILERS = ['gcc-4.7', 'gcc-4.8', 'gcc-4.9', 'gcc-5', 'gcc-6', 'clang-3.5', 'clang-3.6', 'clang-3.7', 'clang-3.8', 'clang-3.9'].freeze def ci_image_tag(compiler) compiler.tr('+', 'c').delete('-').delete('.') end def ci_docker_tag(compiler) tag = ci_image_tag(compiler) "registry.gitlab.com/dabroz/mruby:#{tag}_#{CI_VERSION}" end def run_cmd(cmd) puts cmd raise 'error' unless system cmd end desc 'recreate docker images for GitLab builds' task :gitlab_dockers do CI_COMPILERS.each do |compiler| tag = ci_image_tag(compiler) filename = "Dockerfile.#{tag}" File.open(filename, 'wb') do |f| f << "# #{compiler} - #{tag}\n" f << "FROM #{CI_BASE}\n" f << "RUN apt-get update && apt-get install -y git ruby2.3 ruby2.3-dev bison\n" f << "RUN apt-get update && apt-get install -y binutils manpages\n" f << "RUN apt-get update && apt-get install -y #{compiler}\n" if compiler['gcc'] f << "RUN apt-get update && apt-get install -y libx32#{compiler}-dev\n" f << "RUN apt-get update && apt-get install --no-install-recommends -y #{compiler}-multilib\n" end f << "RUN dpkg --add-architecture i386\n" f << "RUN apt-get update && apt-get install -y linux-libc-dev:i386\n" if compiler['clang'] f << "RUN apt-get update && apt-get install --no-install-recommends -y libc6-dev-i386\n" f << "RUN apt-get update && apt-get install -y gcc gcc-multilib\n" end end docker_tag = ci_docker_tag(compiler) cmd1 = "docker build -t #{docker_tag} -f #{filename} ." cmd2 = "docker push #{docker_tag}" run_cmd cmd1 run_cmd cmd2 File.delete(filename) end end desc 'create build configurations and update .gitlab-ci.yml' task :gitlab_config do require 'yaml' configs = [] [true, false].each do |mode_32| ['', 'MRB_USE_FLOAT'].each do |float_conf| ['', 'MRB_INT16', 'MRB_INT64'].each do |int_conf| ['', 'MRB_NAN_BOXING', 'MRB_WORD_BOXING'].each do |boxing_conf| ['', 'MRB_UTF8_STRING'].each do |utf8_conf| next if (float_conf == 'MRB_USE_FLOAT') && (boxing_conf == 'MRB_NAN_BOXING') next if (int_conf == 'MRB_INT64') && (boxing_conf == 'MRB_NAN_BOXING') next if (int_conf == 'MRB_INT16') && (boxing_conf == 'MRB_WORD_BOXING') next if (int_conf == 'MRB_INT64') && (boxing_conf == 'MRB_WORD_BOXING') && mode_32 env = [float_conf, int_conf, boxing_conf, utf8_conf].map do |conf| conf == '' ? nil : "-D#{conf}=1" end.compact.join(' ') bit = mode_32 ? '-m32 ' : '' _info = '' _info += mode_32 ? '32bit ' : '64bit ' _info += float_conf['USE'] ? 'float ' : '' _info += int_conf['16'] ? 'int16 ' : '' _info += int_conf['64'] ? 'int64 ' : '' _info += boxing_conf['NAN'] ? 'nan ' : '' _info += boxing_conf['word'] ? 'word ' : '' _info += utf8_conf['UTF8'] ? 'utf8 ' : '' _info = _info.gsub(/ +/, ' ').strip.tr(' ', '_') configs << { '_info' => _info, 'CFLAGS' => "#{bit}#{env}", 'LDFLAGS' => bit.strip.to_s } end end end end end path = './.gitlab-ci.yml' data = YAML.load_file(path) data.keys.select do |key| key.start_with? 'Test' end.each do |key| data.delete(key) end CI_COMPILERS.each do |compiler| configs.each do |config| name = "Test #{compiler} #{config['_info']}" hash = { 'CC' => compiler, 'CXX' => compiler.gsub('gcc', 'g++').gsub('clang', 'clang++'), 'LD' => compiler } hash = hash.merge(config) hash.delete('_info') data[name] = { 'stage' => 'test', 'image' => ci_docker_tag(compiler), 'variables' => hash, 'script' => 'env; ./minirake --verbose all test' } end end File.open(path, 'w') { |f| YAML.dump(data, f) } end mruby-2.0.0/tasks/libmruby.rake000066400000000000000000000020571340361412400164670ustar00rootroot00000000000000MRuby.each_target do file libmruby_static => libmruby_objs.flatten do |t| archiver.run t.name, t.prerequisites end file "#{build_dir}/lib/libmruby.flags.mak" => [__FILE__, libmruby_static] do |t| FileUtils.mkdir_p File.dirname t.name open(t.name, 'w') do |f| f.puts "MRUBY_CFLAGS = #{cc.all_flags}" gem_flags = gems.map { |g| g.linker.flags } gem_library_paths = gems.map { |g| g.linker.library_paths } f.puts "MRUBY_LDFLAGS = #{linker.all_flags(gem_library_paths, gem_flags)} #{linker.option_library_path % "#{build_dir}/lib"}" gem_flags_before_libraries = gems.map { |g| g.linker.flags_before_libraries } f.puts "MRUBY_LDFLAGS_BEFORE_LIBS = #{[linker.flags_before_libraries, gem_flags_before_libraries].flatten.join(' ')}" gem_libraries = gems.map { |g| g.linker.libraries } f.puts "MRUBY_LIBS = #{linker.option_library % 'mruby'} #{linker.library_flags(gem_libraries)}" f.puts "MRUBY_LIBMRUBY_PATH = #{libmruby_static}" end end task :all => "#{build_dir}/lib/libmruby.flags.mak" end mruby-2.0.0/tasks/mrbgems.rake000066400000000000000000000072761340361412400163060ustar00rootroot00000000000000MRuby.each_target do if enable_gems? # set up all gems gems.each(&:setup) gems.check self # loader all gems self.libmruby_objs << objfile("#{build_dir}/mrbgems/gem_init") file objfile("#{build_dir}/mrbgems/gem_init") => ["#{build_dir}/mrbgems/gem_init.c", "#{build_dir}/LEGAL"] file "#{build_dir}/mrbgems/gem_init.c" => [MRUBY_CONFIG, __FILE__] do |t| FileUtils.mkdir_p "#{build_dir}/mrbgems" open(t.name, 'w') do |f| gem_func_gems = gems.select { |g| g.generate_functions } gem_func_decls = gem_func_gems.each_with_object('') do |g, s| s << "void GENERATED_TMP_mrb_#{g.funcname}_gem_init(mrb_state*);\n" \ "void GENERATED_TMP_mrb_#{g.funcname}_gem_final(mrb_state*);\n" end gem_init_calls = gem_func_gems.each_with_object('') do |g, s| s << " GENERATED_TMP_mrb_#{g.funcname}_gem_init(mrb);\n" end gem_final_calls = gem_func_gems.reverse_each.with_object('') do |g, s| s << " GENERATED_TMP_mrb_#{g.funcname}_gem_final(mrb);\n" end f.puts %Q[/*] f.puts %Q[ * This file contains a list of all] f.puts %Q[ * initializing methods which are] f.puts %Q[ * necessary to bootstrap all gems.] f.puts %Q[ *] f.puts %Q[ * IMPORTANT:] f.puts %Q[ * This file was generated!] f.puts %Q[ * All manual changes will get lost.] f.puts %Q[ */] f.puts %Q[] f.puts %Q[#include ] f.puts %Q[] f.write gem_func_decls unless gem_final_calls.empty? f.puts %Q[] f.puts %Q[static void] f.puts %Q[mrb_final_mrbgems(mrb_state *mrb) {] f.write gem_final_calls f.puts %Q[}] end f.puts %Q[] f.puts %Q[void] f.puts %Q[mrb_init_mrbgems(mrb_state *mrb) {] f.write gem_init_calls f.puts %Q[ mrb_state_atexit(mrb, mrb_final_mrbgems);] unless gem_final_calls.empty? f.puts %Q[}] end end end # legal documents file "#{build_dir}/LEGAL" => [MRUBY_CONFIG, __FILE__] do |t| FileUtils.mkdir_p File.dirname t.name open(t.name, 'w+') do |f| f.puts < @platform ? platform : @platform end } if @platform === nil raise(PlatformDirNotFound) else @platform = "android-#{@platform}" end end end if Integer(@platform.rpartition('-')[2]) < 21 case arch when /arm64-v8a/, /x86_64/, /mips64/ raise NotImplementedError, "Platform (#{@platform}) has no implementation for architecture (#{arch})" end end @platform end def armeabi_v7a_mfpu @armeabi_v7a_mfpu ||= (params[:mfpu] || 'vfpv3-d16').to_s end def armeabi_v7a_mfloat_abi @armeabi_v7a_mfloat_abi ||= (params[:mfloat_abi] || 'softfp').to_s end def no_warn_mismatch if %W(soft softfp).include? armeabi_v7a_mfloat_abi '' else ',--no-warn-mismatch' end end def cc case toolchain when :gcc then bin_gcc('gcc') when :clang then bin('clang') end end def ar case toolchain when :gcc then bin_gcc('ar') when :clang then bin_gcc('ar') end end def ctarget flags = [] case toolchain when :gcc case arch when /armeabi-v7a/ then flags += %W(-march=armv7-a) when /armeabi/ then flags += %W(-march=armv5te) when /arm64-v8a/ then flags += %W(-march=armv8-a) when /x86_64/ then flags += %W(-march=x86-64) when /x86/ then flags += %W(-march=i686) when /mips64/ then flags += %W(-march=mips64r6) when /mips/ then flags += %W(-march=mips32) end when :clang case arch when /armeabi-v7a/ then flags += %W(-target armv7-none-linux-androideabi) when /armeabi/ then flags += %W(-target armv5te-none-linux-androideabi) when /arm64-v8a/ then flags += %W(-target aarch64-none-linux-android) when /x86_64/ then flags += %W(-target x86_64-none-linux-android) when /x86/ then flags += %W(-target i686-none-linux-android) when /mips64/ then flags += %W(-target mips64el-none-linux-android) when /mips/ then flags += %W(-target mipsel-none-linux-android) end end case arch when /armeabi-v7a/ then flags += %W(-mfpu=#{armeabi_v7a_mfpu} -mfloat-abi=#{armeabi_v7a_mfloat_abi}) when /armeabi/ then flags += %W(-mtune=xscale -msoft-float) when /arm64-v8a/ then flags += %W() when /x86_64/ then flags += %W() when /x86/ then flags += %W() when /mips64/ then flags += %W(-fmessage-length=0) when /mips/ then flags += %W(-fmessage-length=0) end flags end def cflags flags = [] flags += %W(-MMD -MP -D__android__ -DANDROID --sysroot="#{sysroot}") flags += ctarget case toolchain when :gcc when :clang flags += %W(-gcc-toolchain "#{gcc_toolchain_path}" -Wno-invalid-command-line-argument -Wno-unused-command-line-argument) end flags += %W(-fpic -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes) flags end def ldflags flags = [] flags += %W(--sysroot="#{sysroot}") flags end def ldflags_before_libraries flags = [] case toolchain when :gcc case arch when /armeabi-v7a/ then flags += %W(-Wl#{no_warn_mismatch}) end when :clang flags += %W(-gcc-toolchain "#{gcc_toolchain_path.to_s}") case arch when /armeabi-v7a/ then flags += %W(-target armv7-none-linux-androideabi -Wl,--fix-cortex-a8#{no_warn_mismatch}) when /armeabi/ then flags += %W(-target armv5te-none-linux-androideabi) when /arm64-v8a/ then flags += %W(-target aarch64-none-linux-android) when /x86_64/ then flags += %W(-target x86_64-none-linux-android) when /x86/ then flags += %W(-target i686-none-linux-android) when /mips64/ then flags += %W(-target mips64el-none-linux-android) when /mips/ then flags += %W(-target mipsel-none-linux-android) end end flags += %W(-no-canonical-prefixes) flags end end MRuby::Toolchain.new(:android) do |conf, params| android = MRuby::Toolchain::Android.new(params) toolchain android.toolchain [conf.cc, conf.cxx, conf.objc, conf.asm].each do |cc| cc.command = android.cc cc.flags = android.cflags end conf.archiver.command = android.ar conf.linker.command = android.cc conf.linker.flags = android.ldflags conf.linker.flags_before_libraries = android.ldflags_before_libraries end mruby-2.0.0/tasks/toolchains/clang.rake000066400000000000000000000003721340361412400200670ustar00rootroot00000000000000MRuby::Toolchain.new(:clang) do |conf, _params| toolchain :gcc [conf.cc, conf.objc, conf.asm].each do |cc| cc.command = ENV['CC'] || 'clang' end conf.cxx.command = ENV['CXX'] || 'clang++' conf.linker.command = ENV['LD'] || 'clang' end mruby-2.0.0/tasks/toolchains/gcc.rake000066400000000000000000000047471340361412400175510ustar00rootroot00000000000000MRuby::Toolchain.new(:gcc) do |conf, _params| [conf.cc, conf.objc, conf.asm].each do |cc| cc.command = ENV['CC'] || 'gcc' cc.flags = [ENV['CFLAGS'] || %w(-g -std=gnu99 -O3 -Wall -Werror-implicit-function-declaration -Wdeclaration-after-statement -Wwrite-strings)] cc.defines = %w(DISABLE_GEMS) cc.option_include_path = '-I%s' cc.option_define = '-D%s' cc.compile_options = '%{flags} -MMD -o %{outfile} -c %{infile}' cc.cxx_compile_flag = '-x c++ -std=c++03' cc.cxx_exception_flag = '-fexceptions' end [conf.cxx].each do |cxx| cxx.command = ENV['CXX'] || 'g++' cxx.flags = [ENV['CXXFLAGS'] || ENV['CFLAGS'] || %w(-g -O3 -Wall -Werror-implicit-function-declaration)] cxx.defines = %w(DISABLE_GEMS) cxx.option_include_path = '-I%s' cxx.option_define = '-D%s' cxx.compile_options = '%{flags} -MMD -o %{outfile} -c %{infile}' cxx.cxx_compile_flag = '-x c++ -std=c++03' cxx.cxx_exception_flag = '-fexceptions' end conf.linker do |linker| linker.command = ENV['LD'] || 'gcc' linker.flags = [ENV['LDFLAGS'] || %w()] linker.libraries = %w(m) linker.library_paths = [] linker.option_library = '-l%s' linker.option_library_path = '-L%s' linker.link_options = '%{flags} -o %{outfile} %{objs} %{flags_before_libraries} %{libs} %{flags_after_libraries}' end [[conf.cc, 'c'], [conf.cxx, 'c++']].each do |cc, lang| cc.instance_variable_set :@header_search_language, lang def cc.header_search_paths if @header_search_command != command result = `echo | #{build.filename command} -x#{@header_search_language} -Wp,-v - -fsyntax-only 2>&1` result = `echo | #{command} -x#{@header_search_language} -Wp,-v - -fsyntax-only 2>&1` if $?.exitstatus != 0 return include_paths if $?.exitstatus != 0 @frameworks = [] @header_search_paths = result.lines.map { |v| framework = v.match(/^ (.*)(?: \(framework directory\))$/) if framework @frameworks << framework[1] next nil end v.match(/^ (.*)$/) }.compact.map { |v| v[1] }.select { |v| File.directory? v } @header_search_paths += include_paths @header_search_command = command end @header_search_paths end end def conf.enable_sanitizer(*opts) fail 'sanitizer already set' if @sanitizer_list @sanitizer_list = opts flg = "-fsanitize=#{opts.join ','}" [self.cc, self.cxx, self.linker].each{|cmd| cmd.flags << flg } end end mruby-2.0.0/tasks/toolchains/openwrt.rake000066400000000000000000000024151340361412400205010ustar00rootroot00000000000000# usage of environmental variables to set the # cross compiling toolchain proper MRuby::Toolchain.new(:openwrt) do |conf| [conf.cc, conf.objc, conf.asm].each do |cc| cc.command = ENV['TARGET_CC'] cc.flags = ENV['TARGET_CFLAGS'] cc.include_paths = ["#{MRUBY_ROOT}/include"] cc.defines = %w(DISABLE_GEMS) cc.option_include_path = '-I%s' cc.option_define = '-D%s' cc.compile_options = '%{flags} -MMD -o %{outfile} -c %{infile}' end [conf.cxx].each do |cxx| cxx.command = ENV['TARGET_CXX'] cxx.flags = ENV['TARGET_CXXFLAGS'] cxx.include_paths = ["#{MRUBY_ROOT}/include"] cxx.defines = %w(DISABLE_GEMS) cxx.option_include_path = '-I%s' cxx.option_define = '-D%s' cxx.compile_options = '%{flags} -MMD -o %{outfile} -c %{infile}' end conf.linker do |linker| linker.command = ENV['TARGET_CC'] linker.flags = ENV['TARGET_LDFLAGS'] linker.libraries = %w(m) linker.library_paths = [] linker.option_library = '-l%s' linker.option_library_path = '-L%s' linker.link_options = '%{flags} -o %{outfile} %{objs} %{flags_before_libraries} %{libs} %{flags_after_libraries}' end conf.archiver do |archiver| archiver.command = ENV['TARGET_AR'] archiver.archive_options = 'rs %{outfile} %{objs}' end end mruby-2.0.0/tasks/toolchains/visualcpp.rake000066400000000000000000000045401340361412400210120ustar00rootroot00000000000000MRuby::Toolchain.new(:visualcpp) do |conf, _params| conf.cc do |cc| cc.command = ENV['CC'] || 'cl.exe' # C4013: implicit function declaration cc.flags = [ENV['CFLAGS'] || %w(/c /nologo /W3 /we4013 /Zi /MD /O2 /D_CRT_SECURE_NO_WARNINGS)] cc.defines = %w(DISABLE_GEMS MRB_STACK_EXTEND_DOUBLING) cc.option_include_path = '/I%s' cc.option_define = '/D%s' cc.compile_options = "%{flags} /Fo%{outfile} %{infile}" cc.cxx_compile_flag = '/TP' cc.cxx_exception_flag = '/EHs' end conf.cxx do |cxx| cxx.command = ENV['CXX'] || 'cl.exe' cxx.flags = [ENV['CXXFLAGS'] || ENV['CFLAGS'] || %w(/c /nologo /W3 /Zi /MD /O2 /EHs /D_CRT_SECURE_NO_WARNINGS)] cxx.defines = %w(DISABLE_GEMS MRB_STACK_EXTEND_DOUBLING) cxx.option_include_path = '/I%s' cxx.option_define = '/D%s' cxx.compile_options = "%{flags} /Fo%{outfile} %{infile}" cxx.cxx_compile_flag = '/TP' cxx.cxx_exception_flag = '/EHs' end conf.linker do |linker| linker.command = ENV['LD'] || 'link.exe' linker.flags = [ENV['LDFLAGS'] || %w(/NOLOGO /DEBUG /INCREMENTAL:NO /OPT:ICF /OPT:REF)] linker.libraries = %w() linker.library_paths = %w() linker.option_library = '%s.lib' linker.option_library_path = '/LIBPATH:%s' linker.link_options = "%{flags} /OUT:%{outfile} %{objs} %{flags_before_libraries} %{libs} %{flags_after_libraries}" end conf.archiver do |archiver| archiver.command = ENV['AR'] || 'lib.exe' archiver.archive_options = '/nologo /OUT:%{outfile} %{objs}' end conf.yacc do |yacc| yacc.command = ENV['YACC'] || 'bison.exe' yacc.compile_options = '-o %{outfile} %{infile}' end conf.gperf do |gperf| gperf.command = 'gperf.exe' gperf.compile_options = '-L ANSI-C -C -p -j1 -i 1 -g -o -t -N mrb_reserved_word -k"1,3,$" %{infile} > %{outfile}' end conf.exts do |exts| exts.object = '.obj' exts.executable = '.exe' exts.library = '.lib' end conf.file_separator = '\\' # Unreliable detection and will result in invalid encoding errors for localized versions of Visual C++ # if require 'open3' # Open3.popen3 conf.cc.command do |_, _, e, _| # if /Version (\d{2})\.\d{2}\.\d{5}/ =~ e.gets && $1.to_i <= 17 # m = "# VS2010/2012 support will be dropped after the next release! #" # h = "#" * m.length # puts h, m, h # end # end # end end mruby-2.0.0/test/000077500000000000000000000000001340361412400136245ustar00rootroot00000000000000mruby-2.0.0/test/assert.rb000066400000000000000000000142061340361412400154550ustar00rootroot00000000000000$ok_test = 0 $ko_test = 0 $kill_test = 0 $asserts = [] $test_start = Time.now if Object.const_defined?(:Time) # Implementation of print due to the reason that there might be no print def t_print(*args) i = 0 len = args.size while i < len str = args[i].to_s __t_printstr__ str rescue print str i += 1 end end ## # Create the assertion in a readable way def assertion_string(err, str, iso=nil, e=nil, bt=nil) msg = "#{err}#{str}" msg += " [#{iso}]" if iso && iso != '' msg += " => #{e.cause}" if e && e.respond_to?(:cause) msg += " => #{e.message}" if e && !e.respond_to?(:cause) msg += " (mrbgems: #{GEMNAME})" if Object.const_defined?(:GEMNAME) if $mrbtest_assert && $mrbtest_assert.size > 0 $mrbtest_assert.each do |idx, assert_msg, diff| msg += "\n - Assertion[#{idx}] Failed: #{assert_msg}\n#{diff}" end end msg += "\nbacktrace:\n\t#{bt.join("\n\t")}" if bt msg end ## # Verify a code block. # # str : A remark which will be printed in case # this assertion fails # iso : The ISO reference code of the feature # which will be tested by this # assertion def assert(str = 'Assertion failed', iso = '') t_print(str, (iso != '' ? " [#{iso}]" : ''), ' : ') if $mrbtest_verbose begin $mrbtest_assert = [] $mrbtest_assert_idx = 0 yield if($mrbtest_assert.size > 0) $asserts.push(assertion_string('Fail: ', str, iso, nil)) $ko_test += 1 t_print('F') else $ok_test += 1 t_print('.') end rescue Exception => e bt = e.backtrace if $mrbtest_verbose if e.class.to_s == 'MRubyTestSkip' $asserts.push(assertion_string('Skip: ', str, iso, e, nil)) t_print('?') else $asserts.push(assertion_string("#{e.class}: ", str, iso, e, bt)) $kill_test += 1 t_print('X') end ensure $mrbtest_assert = nil end t_print("\n") if $mrbtest_verbose end def assertion_diff(exp, act) " Expected: #{exp.inspect}\n" + " Actual: #{act.inspect}" end def assert_true(ret, msg = nil, diff = nil) if $mrbtest_assert $mrbtest_assert_idx += 1 unless ret msg = "Expected #{ret.inspect} to be true" unless msg diff = assertion_diff(true, ret) unless diff $mrbtest_assert.push([$mrbtest_assert_idx, msg, diff]) end end ret end def assert_false(ret, msg = nil, diff = nil) if $mrbtest_assert $mrbtest_assert_idx += 1 if ret msg = "Expected #{ret.inspect} to be false" unless msg diff = assertion_diff(false, ret) unless diff $mrbtest_assert.push([$mrbtest_assert_idx, msg, diff]) end end !ret end def assert_equal(arg1, arg2 = nil, arg3 = nil) if block_given? exp, act, msg = arg1, yield, arg2 else exp, act, msg = arg1, arg2, arg3 end msg = "Expected to be equal" unless msg diff = assertion_diff(exp, act) assert_true(exp == act, msg, diff) end def assert_not_equal(arg1, arg2 = nil, arg3 = nil) if block_given? exp, act, msg = arg1, yield, arg2 else exp, act, msg = arg1, arg2, arg3 end msg = "Expected to be not equal" unless msg diff = assertion_diff(exp, act) assert_false(exp == act, msg, diff) end def assert_nil(obj, msg = nil) msg = "Expected #{obj.inspect} to be nil" unless msg diff = assertion_diff(nil, obj) assert_true(obj.nil?, msg, diff) end def assert_include(collection, obj, msg = nil) msg = "Expected #{collection.inspect} to include #{obj.inspect}" unless msg diff = " Collection: #{collection.inspect}\n" + " Object: #{obj.inspect}" assert_true(collection.include?(obj), msg, diff) end def assert_not_include(collection, obj, msg = nil) msg = "Expected #{collection.inspect} to not include #{obj.inspect}" unless msg diff = " Collection: #{collection.inspect}\n" + " Object: #{obj.inspect}" assert_false(collection.include?(obj), msg, diff) end def assert_raise(*exc) return true unless $mrbtest_assert $mrbtest_assert_idx += 1 msg = (exc.last.is_a? String) ? exc.pop : nil begin yield msg ||= "Expected to raise #{exc} but nothing was raised." diff = nil $mrbtest_assert.push [$mrbtest_assert_idx, msg, diff] false rescue *exc true rescue Exception => e msg ||= "Expected to raise #{exc}, not" diff = " Class: <#{e.class}>\n" + " Message: #{e.message}" $mrbtest_assert.push [$mrbtest_assert_idx, msg, diff] false end end def assert_nothing_raised(msg = nil) return true unless $mrbtest_assert $mrbtest_assert_idx += 1 begin yield true rescue Exception => e msg ||= "Expected not to raise #{exc.join(', ')} but it raised" diff = " Class: <#{e.class}>\n" + " Message: #{e.message}" $mrbtest_assert.push [$mrbtest_assert_idx, msg, diff] false end end ## # Fails unless +obj+ is a kind of +cls+. def assert_kind_of(cls, obj, msg = nil) msg = "Expected #{obj.inspect} to be a kind of #{cls}, not #{obj.class}" unless msg diff = assertion_diff(cls, obj.class) assert_true(obj.kind_of?(cls), msg, diff) end ## # Fails unless +exp+ is equal to +act+ in terms of a Float def assert_float(exp, act, msg = nil) msg = "Float #{exp} expected to be equal to float #{act}" unless msg diff = assertion_diff(exp, act) assert_true check_float(exp, act), msg, diff end ## # Report the test result and print all assertions # which were reported broken. def report() t_print("\n") $asserts.each do |msg| t_print "#{msg}\n" end $total_test = $ok_test+$ko_test+$kill_test t_print("Total: #{$total_test}\n") t_print(" OK: #{$ok_test}\n") t_print(" KO: #{$ko_test}\n") t_print("Crash: #{$kill_test}\n") if Object.const_defined?(:Time) t_time = Time.now - $test_start t_print(" Time: #{t_time.round(2)} seconds\n") end end ## # Performs fuzzy check for equality on methods returning floats def check_float(a, b) tolerance = Mrbtest::FLOAT_TOLERANCE a = a.to_f b = b.to_f if a.finite? and b.finite? (a-b).abs < tolerance else true end end ## # Skip the test class MRubyTestSkip < NotImplementedError attr_accessor :cause def initialize(cause) @cause = cause end end def skip(cause = "") raise MRubyTestSkip.new(cause) end mruby-2.0.0/test/bintest.rb000066400000000000000000000011701340361412400156200ustar00rootroot00000000000000$:.unshift File.dirname(File.dirname(File.expand_path(__FILE__))) require 'test/assert.rb' def cmd(s) case RbConfig::CONFIG['host_os'] when /mswin(?!ce)|mingw|bccwin/ "bin\\#{s}.exe" else "bin/#{s}" end end def shellquote(s) case RbConfig::CONFIG['host_os'] when /mswin(?!ce)|mingw|bccwin/ "\"#{s}\"" else "'#{s}'" end end ARGV.each do |gem| case gem when '-v'; $mrbtest_verbose = true end case RbConfig::CONFIG['host_os'] when /mswin(?!ce)|mingw|bccwin/ gem = gem.gsub('\\', '/') end Dir["#{gem}/bintest/**/*.rb"].each do |file| load file end end load 'test/report.rb' mruby-2.0.0/test/report.rb000066400000000000000000000001541340361412400154640ustar00rootroot00000000000000report if $ko_test > 0 or $kill_test > 0 raise "mrbtest failed (KO:#{$ko_test}, Crash:#{$kill_test})" end mruby-2.0.0/test/t/000077500000000000000000000000001340361412400140675ustar00rootroot00000000000000mruby-2.0.0/test/t/argumenterror.rb000066400000000000000000000004361340361412400173130ustar00rootroot00000000000000## # ArgumentError ISO Test assert('ArgumentError', '15.2.24') do e2 = nil a = [] begin # this will cause an exception due to the wrong arguments a[] rescue => e1 e2 = e1 end assert_equal(Class, ArgumentError.class) assert_equal(ArgumentError, e2.class) end mruby-2.0.0/test/t/array.rb000066400000000000000000000240441340361412400155360ustar00rootroot00000000000000## # Array ISO Test assert('Array', '15.2.12') do assert_equal(Class, Array.class) end assert('Array inclueded modules', '15.2.12.3') do assert_true(Array.include?(Enumerable)) end assert('Array.[]', '15.2.12.4.1') do assert_equal([1, 2, 3], Array.[](1,2,3)) end class SubArray < Array end assert('SubArray.[]') do a = SubArray[1, 2, 3] assert_equal(SubArray, a.class) end assert('Array#+', '15.2.12.5.1') do assert_equal([1, 1], [1].+([1])) end assert('Array#*', '15.2.12.5.2') do assert_raise(ArgumentError) do # this will cause an exception due to the wrong argument [1].*(-1) end assert_equal([1, 1, 1], [1].*(3)) assert_equal([], [1].*(0)) end assert('Array#<<', '15.2.12.5.3') do assert_equal([1, 1], [1].<<(1)) end assert('Array#[]', '15.2.12.5.4') do a = Array.new assert_raise(ArgumentError) do # this will cause an exception due to the wrong arguments a.[]() end assert_raise(ArgumentError) do # this will cause an exception due to the wrong arguments a.[](1,2,3) end assert_equal(2, [1,2,3].[](1)) assert_equal(nil, [1,2,3].[](4)) assert_equal(3, [1,2,3].[](-1)) assert_equal(nil, [1,2,3].[](-4)) a = [ "a", "b", "c", "d", "e" ] assert_equal("b", a[1.1]) if class_defined?("Float") assert_equal(["b", "c"], a[1,2]) assert_equal(["b", "c", "d"], a[1..-2]) end assert('Array#[]=', '15.2.12.5.5') do a = Array.new assert_raise(ArgumentError) do # this will cause an exception due to the wrong arguments a.[]=() end assert_raise(ArgumentError) do # this will cause an exception due to the wrong arguments a.[]=(1,2,3,4) end assert_raise(IndexError) do # this will cause an exception due to the wrong arguments a = [1,2,3,4,5] a[1, -1] = 10 end assert_equal(4, [1,2,3].[]=(1,4)) assert_equal(3, [1,2,3].[]=(1,2,3)) a = [1,2,3,4,5] a[3..-1] = 6 assert_equal([1,2,3,6], a) a = [1,2,3,4,5] a[3..-1] = [] assert_equal([1,2,3], a) a = [1,2,3,4,5] a[2...4] = 6 assert_equal([1,2,6,5], a) # passing self (#3274) a = [1,2,3] a[1,0] = a assert_equal([1,1,2,3,2,3], a) a = [1,2,3] a[-1,0] = a assert_equal([1,2,1,2,3,3], a) end assert('Array#clear', '15.2.12.5.6') do a = [1] a.clear assert_equal([], a) end assert('Array#collect!', '15.2.12.5.7') do a = [1,2,3] a.collect! { |i| i + i } assert_equal([2,4,6], a) end assert('Array#concat', '15.2.12.5.8') do assert_equal([1,2,3,4], [1, 2].concat([3, 4])) # passing self (#3302) a = [1,2,3] a.concat(a) assert_equal([1,2,3,1,2,3], a) end assert('Array#delete_at', '15.2.12.5.9') do a = [1,2,3] assert_equal(2, a.delete_at(1)) assert_equal([1,3], a) assert_equal(nil, a.delete_at(3)) assert_equal([1,3], a) assert_equal(nil, a.delete_at(-3)) assert_equal([1,3], a) assert_equal(3, a.delete_at(-1)) assert_equal([1], a) end assert('Array#each', '15.2.12.5.10') do a = [1,2,3] b = 0 a.each {|i| b += i} assert_equal(6, b) end assert('Array#each_index', '15.2.12.5.11') do a = [1] b = nil a.each_index {|i| b = i} assert_equal(0, b) end assert('Array#empty?', '15.2.12.5.12') do a = [] b = [b] assert_true([].empty?) assert_false([1].empty?) end assert('Array#first', '15.2.12.5.13') do assert_raise(ArgumentError) do # this will cause an exception due to the wrong argument [1,2,3].first(-1) end assert_raise(ArgumentError) do # this will cause an exception due to the wrong argument [1,2,3].first(1,2) end assert_nil([].first) b = [1,2,3] assert_equal(1, b.first) assert_equal([], b.first(0)) assert_equal([1], b.first(1)) assert_equal([1,2,3], b.first(4)) end assert('Array#index', '15.2.12.5.14') do a = [1,2,3] assert_equal(1, a.index(2)) assert_equal(nil, a.index(0)) end assert('Array#initialize', '15.2.12.5.15') do a = [].initialize(1) b = [].initialize(2) c = [].initialize(2, 1) d = [].initialize(2) {|i| i} assert_equal([nil], a) assert_equal([nil,nil], b) assert_equal([1,1], c) assert_equal([0,1], d) end assert('Array#initialize_copy', '15.2.12.5.16') do a = [1,2,3] b = [].initialize_copy(a) assert_equal([1,2,3], b) end assert('Array#join', '15.2.12.5.17') do a = [1,2,3].join b = [1,2,3].join(',') assert_equal('123', a) assert_equal('1,2,3', b) end assert('Array#last', '15.2.12.5.18') do assert_raise(ArgumentError) do # this will cause an exception due to the wrong argument [1,2,3].last(-1) end a = [1,2,3] assert_equal(3, a.last) assert_nil([].last) end assert('Array#length', '15.2.12.5.19') do a = [1,2,3] assert_equal(3, a.length) end assert('Array#map!', '15.2.12.5.20') do a = [1,2,3] a.map! { |i| i + i } assert_equal([2,4,6], a) end assert('Array#pop', '15.2.12.5.21') do a = [1,2,3] b = a.pop assert_nil([].pop) assert_equal([1,2], a) assert_equal(3, b) assert_raise(RuntimeError) { [].freeze.pop } end assert('Array#push', '15.2.12.5.22') do a = [1,2,3] b = a.push(4) assert_equal([1,2,3,4], a) assert_equal([1,2,3,4], b) end assert('Array#replace', '15.2.12.5.23') do a = [1,2,3] b = [].replace(a) assert_equal([1,2,3], b) end assert('Array#reverse', '15.2.12.5.24') do a = [1,2,3] b = a.reverse assert_equal([1,2,3], a) assert_equal([3,2,1], b) end assert('Array#reverse!', '15.2.12.5.25') do a = [1,2,3] b = a.reverse! assert_equal([3,2,1], a) assert_equal([3,2,1], b) end assert('Array#rindex', '15.2.12.5.26') do a = [1,2,3] assert_equal(1, a.rindex(2)) assert_equal(nil, a.rindex(0)) end assert('Array#shift', '15.2.12.5.27') do a = [1,2,3] b = a.shift assert_nil([].shift) assert_equal([2,3], a) assert_equal(1, b) assert_raise(RuntimeError) { [].freeze.shift } end assert('Array#size', '15.2.12.5.28') do a = [1,2,3] assert_equal(3, a.size) end assert('Array#slice', '15.2.12.5.29') do a = "12345".slice(1, 3) b = a.slice(0) assert_equal("2:", "#{b}:") assert_equal(2, [1,2,3].[](1)) end assert('Array#unshift', '15.2.12.5.30') do a = [2,3] b = a.unshift(1) c = [2,3] d = c.unshift(0, 1) assert_equal([1,2,3], a) assert_equal([1,2,3], b) assert_equal([0,1,2,3], c) assert_equal([0,1,2,3], d) end assert('Array#to_s', '15.2.12.5.31 / 15.2.12.5.32') do a = [2, 3, 4, 5] r1 = a.to_s r2 = a.inspect assert_equal(r2, r1) assert_equal("[2, 3, 4, 5]", r1) end assert('Array#==', '15.2.12.5.33') do assert_false(["a", "c"] == ["a", "c", 7]) assert_true(["a", "c", 7] == ["a", "c", 7]) assert_false(["a", "c", 7] == ["a", "d", "f"]) end assert('Array#eql?', '15.2.12.5.34') do a1 = [ 1, 2, 3 ] a2 = [ 1, 2, 3 ] a3 = [ 1.0, 2.0, 3.0 ] assert_true(a1.eql? a2) assert_false(a1.eql? a3) end assert('Array#hash', '15.2.12.5.35') do a = [ 1, 2, 3 ] #assert_true(a.hash.is_a? Integer) assert_true(a.hash.is_a? Integral) # mruby special assert_equal([1,2].hash, [1,2].hash) end assert('Array#<=>', '15.2.12.5.36') do r1 = [ "a", "a", "c" ] <=> [ "a", "b", "c" ] #=> -1 r2 = [ 1, 2, 3, 4, 5, 6 ] <=> [ 1, 2 ] #=> +1 r3 = [ "a", "b", "c" ] <=> [ "a", "b", "c" ] #=> 0 assert_equal(-1, r1) assert_equal(+1, r2) assert_equal(0, r3) end # Not ISO specified assert("Array (Shared Array Corruption)") do a = [ "a", "b", "c", "d", "e", "f" ] b = a.slice(1, 3) a.clear b.clear end assert("Array (Longish inline array)") do ary = [[0, 0], [1, 1], [2, 2], [3, 3], [4, 4], [5, 5], [6, 6], [7, 7], [8, 8], [9, 9], [10, 10], [11, 11], [12, 12], [13, 13], [14, 14], [15, 15], [16, 16], [17, 17], [18, 18], [19, 19], [20, 20], [21, 21], [22, 22], [23, 23], [24, 24], [25, 25], [26, 26], [27, 27], [28, 28], [29, 29], [30, 30], [31, 31], [32, 32], [33, 33], [34, 34], [35, 35], [36, 36], [37, 37], [38, 38], [39, 39], [40, 40], [41, 41], [42, 42], [43, 43], [44, 44], [45, 45], [46, 46], [47, 47], [48, 48], [49, 49], [50, 50], [51, 51], [52, 52], [53, 53], [54, 54], [55, 55], [56, 56], [57, 57], [58, 58], [59, 59], [60, 60], [61, 61], [62, 62], [63, 63], [64, 64], [65, 65], [66, 66], [67, 67], [68, 68], [69, 69], [70, 70], [71, 71], [72, 72], [73, 73], [74, 74], [75, 75], [76, 76], [77, 77], [78, 78], [79, 79], [80, 80], [81, 81], [82, 82], [83, 83], [84, 84], [85, 85], [86, 86], [87, 87], [88, 88], [89, 89], [90, 90], [91, 91], [92, 92], [93, 93], [94, 94], [95, 95], [96, 96], [97, 97], [98, 98], [99, 99], [100, 100], [101, 101], [102, 102], [103, 103], [104, 104], [105, 105], [106, 106], [107, 107], [108, 108], [109, 109], [110, 110], [111, 111], [112, 112], [113, 113], [114, 114], [115, 115], [116, 116], [117, 117], [118, 118], [119, 119], [120, 120], [121, 121], [122, 122], [123, 123], [124, 124], [125, 125], [126, 126], [127, 127], [128, 128], [129, 129], [130, 130], [131, 131], [132, 132], [133, 133], [134, 134], [135, 135], [136, 136], [137, 137], [138, 138], [139, 139], [140, 140], [141, 141], [142, 142], [143, 143], [144, 144], [145, 145], [146, 146], [147, 147], [148, 148], [149, 149], [150, 150], [151, 151], [152, 152], [153, 153], [154, 154], [155, 155], [156, 156], [157, 157], [158, 158], [159, 159], [160, 160], [161, 161], [162, 162], [163, 163], [164, 164], [165, 165], [166, 166], [167, 167], [168, 168], [169, 169], [170, 170], [171, 171], [172, 172], [173, 173], [174, 174], [175, 175], [176, 176], [177, 177], [178, 178], [179, 179], [180, 180], [181, 181], [182, 182], [183, 183], [184, 184], [185, 185], [186, 186], [187, 187], [188, 188], [189, 189], [190, 190], [191, 191], [192, 192], [193, 193], [194, 194], [195, 195], [196, 196], [197, 197], [198, 198], [199, 199]] h = Hash.new(0) ary.each {|p| h[p.class] += 1} assert_equal({Array=>200}, h) end assert("Array#rindex") do class Sneaky def ==(*) $a.clear $a.replace([1]) false end end $a = [2, 3, 4, 5, 6, 7, 8, 9, 10, Sneaky.new] assert_equal 0, $a.rindex(1) end assert('Array#sort!') do a = [3, 2, 1] assert_equal a, a.sort! # sort! returns self. assert_equal [1, 2, 3], a # it is sorted. end assert('Array#freeze') do a = [].freeze assert_raise(RuntimeError) do a[0] = 1 end end assert('shared array replace') do a = [0] * 40 b = [0, 1, 2] b.replace a[1, 20].dup assert_equal 20, b.size end mruby-2.0.0/test/t/basicobject.rb000066400000000000000000000002471340361412400166670ustar00rootroot00000000000000## # BasicObject assert('BasicObject') do assert_equal(Class, BasicObject.class) end assert('BasicObject superclass') do assert_nil(BasicObject.superclass) end mruby-2.0.0/test/t/bs_block.rb000066400000000000000000000151371340361412400162010ustar00rootroot00000000000000## # Bootstrap tests for blocks assert('BS Block 1') do assert_equal(1) do 1.times{ begin a = 1 ensure foo = nil end } end end assert('BS Block 2') do assert_equal 2, [1,2,3].find{|x| x == 2} end assert('BS Block 3') do class E include Enumerable def each(&block) [1, 2, 3].each(&block) end end assert_equal 2, E.new.find {|x| x == 2 } end assert('BS Block 3') do sum = 0 for x in [1, 2, 3] sum += x end assert_equal 6, sum end assert('BS Block 4') do sum = 0 for x in (1..5) sum += x end assert_equal 15, sum end assert('BS Block 5') do sum = 0 for x in [] sum += x end assert_equal 0, sum end assert('BS Block 6') do ans = [] assert_equal(1) do 1.times{ for n in 1..3 a = n ans << a end } end end assert('BS Block 7') do ans = [] assert_equal((1..3)) do for m in 1..3 for n in 2..4 a = [m, n] ans << a end end end end assert('BS Block 8') do assert_equal [1, 2, 3], (1..3).to_a end assert('BS Block 9') do assert_equal([4, 8, 12]) do (1..3).map{|e| e * 4 } end end assert('BS Block 10') do def m yield end def n yield end assert_equal(100) do m{ n{ 100 } } end end assert('BS Block 11') do def m yield 1 end assert_equal(20) do m{|ib| m{|jb| i = 20 } } end end assert('BS Block 12') do def m yield 1 end assert_equal(2) do m{|ib| m{|jb| ib = 20 kb = 2 } } end end assert('BS Block 13') do def iter1 iter2{ yield } end def iter2 yield end assert_equal(3) do iter1{ jb = 2 iter1{ jb = 3 } jb } end end assert('BS Block 14') do def iter1 iter2{ yield } end def iter2 yield end assert_equal(2) do iter1{ jb = 2 iter1{ jb } jb } end end assert('BS Block 15') do def m yield 1 end assert_equal(2) do m{|ib| ib*2 } end end assert('BS Block 16') do def m yield 12345, 67890 end assert_equal(92580) do m{|ib,jb| ib*2+jb } end end assert('BS Block 17') do def iter yield 10 end a = nil assert_equal [10, nil] do [iter{|a| a }, a] end end assert('BS Block 18') do def iter yield 10 end assert_equal(21) do iter{|a| iter{|a| a + 1 } + a } end end assert('BS Block 19') do def iter yield 10, 20, 30, 40 end a = b = c = d = nil assert_equal([10, 20, 30, 40, nil, nil, nil, nil]) do iter{|a, b, c, d| [a, b, c, d] } + [a, b, c, d] end end assert('BS Block 20') do def iter yield 10, 20, 30, 40 end a = b = nil assert_equal([10, 20, 30, 40, nil, nil]) do iter{|a, b, c, d| [a, b, c, d] } + [a, b] end end assert('BS Block 21') do def iter yield 1, 2 end assert_equal([1, [2]]) do iter{|a, *b| [a, b] } end end assert('BS Block 22') do def iter yield 1, 2 end assert_equal([[1, 2]]) do iter{|*a| [a] } end end assert('BS Block 23') do def iter yield 1, 2 end assert_equal([1, 2, []]) do iter{|a, b, *c| [a, b, c] } end end assert('BS Block 24') do def m yield end assert_equal(1) do m{ 1 } end end assert('BS Block 25') do def m yield 123 end assert_equal(15129) do m{|ib| m{|jb| ib*jb } } end end assert('BS Block 26') do def m a yield a end assert_equal(2) do m(1){|ib| m(2){|jb| ib*jb } } end end assert('BS Block 27') do sum = 0 3.times{|ib| 2.times{|jb| sum += ib + jb }} assert_equal sum, 9 end assert('BS Block 28') do assert_equal(10) do 3.times{ break 10 } end end assert('BS Block 29') do def iter yield 1,2,3 end assert_equal([1, 2]) do iter{|i, j| [i, j] } end end assert('BS Block 30') do def iter yield 1 end assert_equal([1, nil]) do iter{|i, j| [i, j] } end end assert('BS Block [ruby-dev:31147]') do def m yield end assert_nil m{|&b| b} end assert('BS Block [ruby-dev:31160]') do def m() yield end assert_nil m {|(v,(*))|} end assert('BS Block [issue #750]') do def m(a, *b) yield end args = [1, 2, 3] assert_equal m(*args){ 1 }, 1 end assert('BS Block 31') do def m() yield end assert_nil m {|((*))|} end assert('BS Block [ruby-dev:31440]') do def m yield [0] end assert_equal m{|v, &b| v}, [0] end assert('BS Block 32') do r = false; 1.times{|&b| r = b} assert_equal NilClass, r.class end assert('BS Block [ruby-core:14395]') do class Controller def respond_to(&block) responder = Responder.new block.call(responder) responder.respond end def test_for_bug respond_to{|format| format.js{ "in test" render{|obj| obj } } } end def render(&block) "in render" end end class Responder def method_missing(symbol, &block) "enter method_missing" @response = Proc.new{ 'in method missing' block.call } "leave method_missing" end def respond @response.call end end t = Controller.new assert_true t.test_for_bug end assert("BS Block 33") do module TestReturnFromNestedBlock def self.test 1.times do 1.times do return :ok end end :bad end end assert_equal :ok, TestReturnFromNestedBlock.test end assert("BS Block 34") do module TestReturnFromNestedBlock_BSBlock34 def self.test 1.times do while true return :ok end end :bad end end assert_equal :ok, TestReturnFromNestedBlock_BSBlock34.test end assert("BS Block 35") do module TestReturnFromNestedBlock_BSBlock35 def self.test 1.times do until false return :ok end end :bad end end assert_equal :ok, TestReturnFromNestedBlock_BSBlock35.test end assert('BS Block 36') do def iter yield 1, 2, 3, 4, 5 end assert_equal([1, 2, [3, 4], 5]) do iter{|a, b, *c, d| [a, b, c, d] } end end assert('BS Block 37') do def iter yield 1, 2, 3 end assert_equal([1, 2, [], 3]) do iter{|a, b, *c, d| [a, b, c, d] } end end assert('BS Block 38') do def iter yield 1,2,3,4,5,6 end assert_equal [1,2,3,4,5], iter{|a,b,c=:c,d,e| [a,b,c,d,e]} end mruby-2.0.0/test/t/bs_literal.rb000066400000000000000000000011171340361412400165340ustar00rootroot00000000000000## # Bootstrap test for literals assert('BS Literal 1') do assert_true true end assert('BS Literal 2') do assert_equal TrueClass, true.class end assert('BS Literal 3') do assert_false false end assert('BS Literal 4') do assert_equal FalseClass, false.class end assert('BS Literal 5') do assert_equal 'nil', nil.inspect end assert('BS Literal 6') do assert_equal NilClass, nil.class end assert('BS Literal 7') do assert_equal Symbol, :sym.class end assert('BS Literal 8') do assert_equal 1234, 1234 end assert('BS Literal 9') do assert_equal Fixnum, 1234.class end mruby-2.0.0/test/t/class.rb000066400000000000000000000172711340361412400155310ustar00rootroot00000000000000## # Class ISO Test assert('Class', '15.2.3') do assert_equal(Class, Class.class) end assert('Class#initialize', '15.2.3.3.1') do c = Class.new do def test :test end end.new assert_equal(c.test, :test) end assert('Class#initialize_copy', '15.2.3.3.2') do class TestClass attr_accessor :n def initialize(n) @n = n end def initialize_copy(obj) @n = n end end c1 = TestClass.new('Foo') c2 = c1.dup c3 = TestClass.new('Bar') assert_equal(c1.n, c2.n) assert_not_equal(c1.n, c3.n) end assert('Class#new', '15.2.3.3.3') do assert_raise(TypeError, 'Singleton should raise TypeError') do (class <<"a"; self; end).new end class TestClass def initialize args, &block @result = if not args.nil? and block.nil? # only arguments :only_args elsif not args.nil? and not block.nil? # args and block is given :args_and_block else # this should never happen :broken end end def result; @result; end end assert_equal(:only_args, TestClass.new(:arg).result) # with block doesn't work yet end assert('Class#superclass', '15.2.3.3.4') do class SubClass < String; end assert_equal(String, SubClass.superclass) end # Not ISO specified assert('Class 1') do class C1; end assert_equal(Class, C1.class) end assert('Class 2') do class C2; end assert_equal(C2, C2.new.class) end assert('Class 3') do class C3; end assert_equal(Class, C3.new.class.class) end assert('Class 4') do class C4_A; end class C4 < C4_A; end assert_equal(Class, C4.class) end assert('Class 5') do class C5_A; end class C5 < C5_A; end assert_equal(C5, C5.new.class) end assert('Class 6') do class C6_A; end class C6 < C6_A; end assert_equal(Class, C6.new.class.class) end assert('Class 7') do class C7_A; end class C7_B; end class C7 < C7_A; end assert_raise(TypeError) do # Different superclass. class C7 < C7_B; end end end assert('Class 8') do class C8_A; end class C8; end # superclass is Object assert_raise(TypeError) do # Different superclass. class C8 < C8_A; end end end assert('Class 9') do Class9Const = "a" assert_raise(TypeError) do class Class9Const; end end end assert('Class Module 1') do module M; end assert_equal(Module, M.class) end assert('Class Module 2') do module M; end class C; include M; end assert_equal(C, C.new.class) end # nested class assert('Class Nested 1') do class A; end class A::B; end assert_equal(A::B, A::B) end assert('Class Nested 2') do class A; end class A::B; end assert_equal(A::B, A::B.new.class) end assert('Class Nested 3') do class A; end class A::B; end assert_equal(Class, A::B.new.class.class) end assert('Class Nested 4') do class A; end class A::B; end class A::B::C; end assert_equal(A::B::C, A::B::C) end assert('Class Nested 5') do class A; end class A::B; end class A::B::C; end assert_equal(Class, A::B::C.class) end assert('Class Nested 6') do class A; end class A::B; end class A::B::C; end assert_equal(A::B::C, A::B::C.new.class) end assert('Class Nested 7') do class A; end class A::B; end class A::B2 < A::B; end assert_equal(A::B2, A::B2) end assert('Class Nested 8') do class A; end class A::B; end class A::B2 < A::B; end assert_equal(Class, A::B2.class) end assert('Class Colon 1') do class A; end A::C = 1 assert_equal(1, A::C) end assert('Class Colon 2') do class A; class ::C; end end assert_equal(C, C) end assert('Class Colon 3') do class A; class ::C; end end assert_equal(Class, C.class) end assert('Class Dup 1') do class C; end assert_equal(Class, C.dup.class) end assert('Class Dup 2') do module M; end assert_equal(Module, M.dup.class) end assert('Class.new') do assert_equal(Class, Class.new.class) a = [] klass = Class.new do |c| a << c end assert_equal([klass], a) end assert('class to return the last value') do m = class C; :m end assert_equal(m, :m) end assert('raise when superclass is not a class') do module FirstModule; end assert_raise(TypeError, 'should raise TypeError') do class FirstClass < FirstModule; end end class SecondClass; end assert_raise(TypeError, 'should raise TypeError') do class SecondClass < false; end end class ThirdClass; end assert_raise(TypeError, 'should raise TypeError') do class ThirdClass < ThirdClass; end end end assert('Class#inherited') do class Foo @@subclass_name = nil def self.inherited(subclass) @@subclass_name = subclass end def self.subclass_name @@subclass_name end end assert_equal(nil, Foo.subclass_name) class Bar < Foo end assert_equal(Bar, Foo.subclass_name) class Baz < Bar end assert_equal(Baz, Foo.subclass_name) end assert('singleton tests') do module FooMod def run_foo_mod 100 end end bar = String.new baz = class << bar extend FooMod def self.run_baz 200 end end assert_equal :run_baz, baz assert_raise(NoMethodError, 'should raise NoMethodError') do bar.run_foo_mod end assert_raise(NoMethodError, 'should raise NoMethodError') do bar.run_baz end baz = class << bar extend FooMod def self.run_baz 300 end self end assert_true baz.respond_to? :run_baz assert_true baz.respond_to? :run_foo_mod assert_equal 100, baz.run_foo_mod assert_equal 300, baz.run_baz assert_raise(NoMethodError, 'should raise NoMethodError') do bar.run_foo_mod end assert_raise(NoMethodError, 'should raise NoMethodError') do bar.run_baz end fv = false class << fv def self.run_false 5 end end nv = nil class << nv def self.run_nil 6 end end tv = true class << tv def self.run_nil 7 end end assert_raise(TypeError, 'should raise TypeError') do num = 1.0 class << num def self.run_nil 7 end end end if class_defined?("Float") end assert('clone Class') do class Foo def func true end end Foo.clone.new.func end assert('class variable and class << self style class method') do class ClassVariableTest @@class_variable = "value" class << self def class_variable @@class_variable end end end assert_equal("value", ClassVariableTest.class_variable) end assert('class variable definition in singleton_class') do class ClassVariableDefinitionInSingletonTest class << self @@class_variable = "value" end def class_variable @@class_variable end end assert_equal("value", ClassVariableDefinitionInSingletonTest.new.class_variable) end assert('class variable in module and class << self style class method') do module ClassVariableInModuleTest @@class_variable = "value" class << self def class_variable @@class_variable end end end assert_equal("value", ClassVariableInModuleTest.class_variable) end assert('child class/module defined in singleton class get parent constant') do actual = module GetParentConstantTest EXPECT = "value" class << self class CHILD class << self EXPECT end end end end assert_equal("value", actual) end assert('overriding class variable with a module (#3235)') do module ModuleWithCVar @@class_variable = 1 end class CVarOverrideTest @@class_variable = 2 include ModuleWithCVar assert_equal(1, @@class_variable) end 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 mruby-2.0.0/test/t/codegen.rb000066400000000000000000000120231340361412400160160ustar00rootroot00000000000000## # Codegen tests assert('peephole optimization does not eliminate move whose result is reused') do assert_raise LocalJumpError do def method yield end method(&a &&= 0) end end assert('empty condition in ternary expression parses correctly') do assert_equal(() ? 1 : 2, 2) end assert('method call with exactly 127 arguments') do def args_to_ary(*args) args end assert_equal [0]*127, args_to_ary( 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, \ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, \ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, \ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, \ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, \ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ) end assert('nested empty heredoc') do _, a = nil, <0, 1=>1, 2=>2, 3=>3, 4=>4, 5=>5, 6=>6, 7=>7, 8=>8, 9=>9, 10=>10, 11=>11, 12=>12, 13=>13, 14=>14, 15=>15, 16=>16, 17=>17, 18=>18, 19=>19, 20=>20, 21=>21, 22=>22, 23=>23, 24=>24, 25=>25, 26=>26, 27=>27, 28=>28, 29=>29, 30=>30, 31=>31, 32=>32, 33=>33, 34=>34, 35=>35, 36=>36, 37=>37, 38=>38, 39=>39, 40=>40, 41=>41, 42=>42, 43=>43, 44=>44, 45=>45, 46=>46, 47=>47, 48=>48, 49=>49, 50=>50, 51=>51, 52=>52, 53=>53, 54=>54, 55=>55, 56=>56, 57=>57, 58=>58, 59=>59, 60=>60, 61=>61, 62=>62, 63=>63, 64=>64, 65=>65, 66=>66, 67=>67, 68=>68, 69=>69, 70=>70, 71=>71, 72=>72, 73=>73, 74=>74, 75=>75, 76=>76, 77=>77, 78=>78, 79=>79, 80=>80, 81=>81, 82=>82, 83=>83, 84=>84, 85=>85, 86=>86, 87=>87, 88=>88, 89=>89, 90=>90, 91=>91, 92=>92, 93=>93, 94=>94, 95=>95, 96=>96, 97=>97, 98=>98, 99=>99, 100=>100, 101=>101, 102=>102, 103=>103, 104=>104, 105=>105, 106=>106, 107=>107, 108=>108, 109=>109, 110=>110, 111=>111, 112=>112, 113=>113, 114=>114, 115=>115, 116=>116, 117=>117, 118=>118, 119=>119, 120=>120, 121=>121, 122=>122, 123=>123, 124=>124, 125=>125, 126=>126) end # NODE_OP_ASGN o = Object.new class << o attr_accessor :a end o.a = 1 assert_nothing_raised{ o.a += 1 } o.a = 1 assert_nothing_raised{ o.a <<= 1 } o.a = 1 assert_nothing_raised{ o.a &&= 1 } o = { k: 1 } assert_nothing_raised{ o[:k] += 1 } o = { k: 1 } assert_nothing_raised{ o[:k] <<= 1 } o = { k: 1 } assert_nothing_raised{ o[:k] &&= 1 } o = { k: 1 } assert_nothing_raised{ o[*[:k]] += 1 } o = { k: 1 } assert_nothing_raised{ o[*[:k]] <<= 1 } o = { k: 1 } assert_nothing_raised{ o[*[:k]] &&= 1 } # NODE_YIELD def check_node_yield yield end assert_nothing_raised do check_node_yield{} end # NODE_DXSTR assert_raise(NotImplementedError){ `#{:dynamic}` } # NODE_XSTR assert_raise(NotImplementedError){ `static` } # NODE_DREGX class Regexp; end assert_raise(NoMethodError){ /#{'dynamic'}tail/ } assert_raise(NoMethodError){ /#{'dynamic'}tail/iu } # NODE_REGX assert_raise(NoMethodError){ /static/ } assert_raise(NoMethodError){ /static/iu } Object.remove_const :Regexp # NODE_UNDEF assert_nothing_raised do class << Object.new undef inspect end end # NODE_ALIAS assert_nothing_raised do class << Object.new alias inspect2 inspect end end end mruby-2.0.0/test/t/comparable.rb000066400000000000000000000026701340361412400165260ustar00rootroot00000000000000 assert('Comparable#<', '15.3.3.2.1') do class Foo include Comparable def <=>(x) x end end assert_false(Foo.new < 0) assert_false(Foo.new < 1) assert_true(Foo.new < -1) assert_raise(ArgumentError){ Foo.new < nil } end assert('Comparable#<=', '15.3.3.2.2') do class Foo include Comparable def <=>(x) x end end assert_true(Foo.new <= 0) assert_false(Foo.new <= 1) assert_true(Foo.new <= -1) assert_raise(ArgumentError){ Foo.new <= nil } end assert('Comparable#==', '15.3.3.2.3') do class Foo include Comparable def <=>(x) 0 end end assert_true(Foo.new == Foo.new) end assert('Comparable#>', '15.3.3.2.4') do class Foo include Comparable def <=>(x) x end end assert_false(Foo.new > 0) assert_true(Foo.new > 1) assert_false(Foo.new > -1) assert_raise(ArgumentError){ Foo.new > nil } end assert('Comparable#>=', '15.3.3.2.5') do class Foo include Comparable def <=>(x) x end end assert_true(Foo.new >= 0) assert_true(Foo.new >= 1) assert_false(Foo.new >= -1) assert_raise(ArgumentError){ Foo.new >= nil } end assert('Comparable#between?', '15.3.3.2.6') do class Foo include Comparable def <=>(x) x end end c = Foo.new assert_false(c.between?(-1, 1)) assert_false(c.between?(-1, -1)) assert_false(c.between?( 1, 1)) assert_true(c.between?( 1, -1)) assert_true(c.between?(0, 0)) end mruby-2.0.0/test/t/ensure.rb000066400000000000000000000016461340361412400157240ustar00rootroot00000000000000## # ensure Test assert('ensure - context - yield') do class EnsureYieldBreak attr_reader :ensure_context def try yield ensure @ensure_context = self end end yielder = EnsureYieldBreak.new yielder.try do end assert_equal yielder, yielder.ensure_context end assert('ensure - context - yield and break') do class EnsureYieldBreak attr_reader :ensure_context def try yield ensure @ensure_context = self end end yielder = EnsureYieldBreak.new yielder.try do break end assert_equal yielder, yielder.ensure_context end assert('ensure - context - yield and return') do class EnsureYieldBreak attr_reader :ensure_context def try yield ensure @ensure_context = self end end yielder = EnsureYieldBreak.new lambda do yielder.try do return end end.call assert_equal yielder, yielder.ensure_context end mruby-2.0.0/test/t/enumerable.rb000066400000000000000000000060141340361412400165340ustar00rootroot00000000000000## # Enumerable ISO Test assert('Enumerable', '15.3.2') do assert_equal(Module, Enumerable.class) end assert('Enumerable#all?', '15.3.2.2.1') do assert_true([1,2,3].all?) assert_false([1,false,3].all?) a = [2,4,6] all = a.all? do |e| e % 2 == 0 end assert_true(all) a = [2,4,7] all = a.all? do |e| e % 2 == 0 end assert_false(all) end assert('Enumerable#any?', '15.3.2.2.2') do assert_true([false,true,false].any?) assert_false([false,false,false].any?) a = [1,3,6] any = a.any? do |e| e % 2 == 0 end assert_true(any) a = [1,3,5] any = a.any? do |e| e % 2 == 0 end assert_false(any) end assert('Enumerable#collect', '15.3.2.2.3') do assert_true [1,2,3].collect { |i| i + i } == [2,4,6] end assert('Enumerable#detect', '15.3.2.2.4') do assert_equal 1, [1,2,3].detect() { true } assert_equal 'a', [1,2,3].detect("a") { false } end assert('Array#each_with_index', '15.3.2.2.5') do a = nil b = nil [1].each_with_index {|e,i| a = e; b = i} assert_equal(1, a) assert_equal(0, b) end assert('Enumerable#entries', '15.3.2.2.6') do assert_equal([1], [1].entries) end assert('Enumerable#find', '15.3.2.2.7') do assert_equal 1, [1,2,3].find() { true } assert_equal 'a', [1,2,3].find("a") { false } end assert('Enumerable#find_all', '15.3.2.2.8') do assert_true [1,2,3,4,5,6,7,8,9].find_all() {|i| i%2 == 0}, [2,4,6,8] end assert('Enumerable#grep', '15.3.2.2.9') do assert_equal [4,5,6], [1,2,3,4,5,6,7,8,9].grep(4..6) end assert('Enumerable#include?', '15.3.2.2.10') do assert_true [1,2,3,4,5,6,7,8,9].include?(5) assert_false [1,2,3,4,5,6,7,8,9].include?(0) end assert('Enumerable#inject', '15.3.2.2.11') do assert_equal 21, [1,2,3,4,5,6].inject() {|s, n| s + n} assert_equal 22, [1,2,3,4,5,6].inject(1) {|s, n| s + n} end assert('Enumerable#map', '15.3.2.2.12') do assert_equal [2,4,6], [1,2,3].map { |i| i + i } end assert('Enumerable#max', '15.3.2.2.13') do a = ['aaa', 'bb', 'c'] assert_equal 'c', a.max assert_equal 'aaa', a.max {|i1,i2| i1.length <=> i2.length} end assert('Enumerable#min', '15.3.2.2.14') do a = ['aaa', 'bb', 'c'] assert_equal 'aaa', a.min assert_equal 'c', a.min {|i1,i2| i1.length <=> i2.length} end assert('Enumerable#member?', '15.3.2.2.15') do assert_true [1,2,3,4,5,6,7,8,9].member?(5) assert_false [1,2,3,4,5,6,7,8,9].member?(0) end assert('Enumerable#partition', '15.3.2.2.16') do partition = [0,1,2,3,4,5,6,7,8,9].partition do |i| i % 2 == 0 end assert_equal [[0,2,4,6,8], [1,3,5,7,9]], partition end assert('Enumerable#reject', '15.3.2.2.17') do reject = [0,1,2,3,4,5,6,7,8,9].reject do |i| i % 2 == 0 end assert_equal [1,3,5,7,9], reject end assert('Enumerable#select', '15.3.2.2.18') do assert_equal [2,4,6,8], [1,2,3,4,5,6,7,8,9].select() {|i| i%2 == 0} end assert('Enumerable#sort', '15.3.2.2.19') do assert_equal [1,2,3,4,6,7], [7,3,1,2,6,4].sort assert_equal [7,6,4,3,2,1], [7,3,1,2,6,4].sort {|e1,e2|e2<=>e1} end assert('Enumerable#to_a', '15.3.2.2.20') do assert_equal [1], [1].to_a end mruby-2.0.0/test/t/exception.rb000066400000000000000000000133631340361412400164200ustar00rootroot00000000000000## # Exception ISO Test assert('Exception', '15.2.22') do assert_equal Class, Exception.class end assert('Exception.exception', '15.2.22.4.1') do e = Exception.exception('a') assert_equal Exception, e.class end assert('Exception#exception', '15.2.22.5.1') do e = Exception.new re = RuntimeError.new assert_equal e, e.exception assert_equal e, e.exception(e) assert_equal re, re.exception(re) changed_re = re.exception('message has changed') assert_not_equal re, changed_re assert_equal 'message has changed', changed_re.message end assert('Exception#message', '15.2.22.5.2') do e = Exception.exception('a') assert_equal 'a', e.message end assert('Exception#to_s', '15.2.22.5.3') do e = Exception.exception('a') assert_equal 'a', e.to_s end assert('Exception.exception', '15.2.22.4.1') do e = Exception.exception() e.initialize('a') assert_equal 'a', e.message end assert('NameError', '15.2.31') do assert_raise(NameError) do raise NameError.new end e = NameError.new "msg", "name" assert_equal "msg", e.message assert_equal "name", e.name end assert('ScriptError', '15.2.37') do assert_raise(ScriptError) do raise ScriptError.new end end assert('SyntaxError', '15.2.38') do assert_raise(SyntaxError) do raise SyntaxError.new end end # Not ISO specified assert('Exception 1') do r=begin 1+1 ensure 2+2 end assert_equal 2, r end assert('Exception 2') do r=begin 1+1 begin 2+2 ensure 3+3 end ensure 4+4 end assert_equal 4, r end assert('Exception 3') do r=begin 1+1 begin 2+2 ensure 3+3 end ensure 4+4 begin 5+5 ensure 6+6 end end assert_equal 4, r end assert('Exception 4') do a = nil 1.times{|e| begin rescue => err end a = err.class } assert_equal NilClass, a end assert('Exception 5') do $ans = [] def m $! end def m2 1.times{ begin return ensure $ans << m end } end m2 assert_equal [nil], $ans end assert('Exception 6') do $i = 0 def m iter{ begin $i += 1 begin $i += 2 break ensure end ensure $i += 4 end $i = 0 } end def iter yield end m assert_equal 7, $i end assert('Exception 7') do $i = 0 def m begin $i += 1 begin $i += 2 return ensure $i += 3 end ensure $i += 4 end p :end end m assert_equal 10, $i end assert('Exception 8') do r=begin 1 rescue 2 else 3 end assert_equal 3, r end assert('Exception 9') do r=begin 1+1 rescue 2+2 else 3+3 ensure 4+4 end assert_equal 6, r end assert('Exception 10') do r=begin 1+1 begin 2+2 rescue 3+3 else 4+4 end rescue 5+5 else 6+6 ensure 7+7 end assert_equal 12, r end assert('Exception 11') do a = :ok begin begin raise Exception rescue a = :ng end rescue Exception end assert_equal :ok, a end assert('Exception 12') do a = :ok begin raise Exception rescue a = :ng rescue Exception end assert_equal :ok, a end assert('Exception 13') do a = :ng begin raise StandardError rescue TypeError, ArgumentError a = :ng rescue a = :ok else a = :ng end assert_equal :ok, a end assert('Exception 14') do def exception_test14; UnknownConstant; end a = :ng begin send(:exception_test14) rescue a = :ok end assert_equal :ok, a end assert('Exception 15') do a = begin :ok rescue :ko end assert_equal :ok, a end assert('Exception 16') do begin raise "foo" false rescue => e assert_equal "foo", e.message end end assert('Exception 17') do r=begin raise "a" # RuntimeError rescue ArgumentError 1 rescue StandardError 2 else 3 ensure 4 end assert_equal 2, r end assert('Exception 18') do r=begin 0 rescue ArgumentError 1 rescue StandardError 2 else 3 ensure 4 end assert_equal 3, r end assert('Exception 19') do class Class4Exception19 def a r = @e = false begin b rescue TypeError r = self.z end [ r, @e ] end def b begin 1 * "b" ensure @e = self.zz end end def zz true end def z true end end assert_equal [true, true], Class4Exception19.new.a end assert('Exception#inspect without message') do assert_equal "Exception", Exception.new.inspect end assert('Exception#backtrace') do assert_nothing_raised do begin raise "get backtrace" rescue => e e.backtrace end end end assert('Raise in ensure') do assert_raise(ArgumentError) do begin raise "" # RuntimeError ensure raise ArgumentError end end end def backtrace_available? begin raise "XXX" rescue => exception not exception.backtrace.empty? end end assert('GC in rescue') do skip "backtrace isn't available" unless backtrace_available? line = nil begin [1].each do [2].each do [3].each do line = __LINE__; raise "XXX" end end end rescue => exception GC.start assert_equal("#{__FILE__}:#{line}:in call", exception.backtrace.first) end end assert('Method call in rescue') do skip "backtrace isn't available" unless backtrace_available? line = nil begin [1].each do [2].each do line = __LINE__; raise "XXX" end end rescue => exception [3].each do end assert_equal("#{__FILE__}:#{line}:in call", exception.backtrace.first) end end mruby-2.0.0/test/t/false.rb000066400000000000000000000012121340361412400155020ustar00rootroot00000000000000## # FalseClass ISO Test assert('FalseClass', '15.2.6') do assert_equal Class, FalseClass.class end assert('FalseClass false', '15.2.6.1') do assert_false false assert_equal FalseClass, false.class assert_false FalseClass.method_defined? :new end assert('FalseClass#&', '15.2.6.3.1') do assert_false false.&(true) assert_false false.&(false) end assert('FalseClass#^', '15.2.6.3.2') do assert_true false.^(true) assert_false false.^(false) end assert('FalseClass#to_s', '15.2.6.3.3') do assert_equal 'false', false.to_s end assert('FalseClass#|', '15.2.6.3.4') do assert_true false.|(true) assert_false false.|(false) end mruby-2.0.0/test/t/float.rb000066400000000000000000000106151340361412400155240ustar00rootroot00000000000000## # Float ISO Test if class_defined?("Float") assert('Float', '15.2.9') do assert_equal Class, Float.class end assert('Float#+', '15.2.9.3.1') do a = 3.123456788 + 0.000000001 b = 3.123456789 + 1 assert_float(3.123456789, a) assert_float(4.123456789, b) assert_raise(TypeError){ 0.0+nil } assert_raise(TypeError){ 1.0+nil } end assert('Float#-', '15.2.9.3.2') do a = 3.123456790 - 0.000000001 b = 5.123456789 - 1 assert_float(3.123456789, a) assert_float(4.123456789, b) end assert('Float#*', '15.2.9.3.3') do a = 3.125 * 3.125 b = 3.125 * 1 assert_float(9.765625, a) assert_float(3.125 , b) end assert('Float#/', '15.2.9.3.4') do a = 3.123456789 / 3.123456789 b = 3.123456789 / 1 assert_float(1.0 , a) assert_float(3.123456789, b) end assert('Float#%', '15.2.9.3.5') do a = 3.125 % 3.125 b = 3.125 % 1 assert_float(0.0 , a) assert_float(0.125, b) end assert('Float#<=>', '15.2.9.3.6') do a = 3.125 <=> 3.123 b = 3.125 <=> 3.125 c = 3.125 <=> 3.126 a2 = 3.125 <=> 3 c2 = 3.125 <=> 4 assert_equal( 1, a) assert_equal( 0, b) assert_equal(-1, c) assert_equal( 1, a2) assert_equal(-1, c2) end assert('Float#==', '15.2.9.3.7') do assert_true 3.1 == 3.1 assert_false 3.1 == 3.2 end assert('Float#ceil', '15.2.9.3.8') do a = 3.123456789.ceil b = 3.0.ceil c = -3.123456789.ceil d = -3.0.ceil assert_equal( 4, a) assert_equal( 3, b) assert_equal(-3, c) assert_equal(-3, d) end assert('Float#finite?', '15.2.9.3.9') do assert_true 3.123456789.finite? assert_false (1.0 / 0.0).finite? end assert('Float#floor', '15.2.9.3.10') do a = 3.123456789.floor b = 3.0.floor c = -3.123456789.floor d = -3.0.floor assert_equal( 3, a) assert_equal( 3, b) assert_equal(-4, c) assert_equal(-3, d) end assert('Float#infinite?', '15.2.9.3.11') do a = 3.123456789.infinite? b = (1.0 / 0.0).infinite? c = (-1.0 / 0.0).infinite? assert_nil a assert_equal( 1, b) assert_equal(-1, c) end assert('Float#round', '15.2.9.3.12') do a = 3.123456789.round b = 3.5.round c = 3.4999.round d = (-3.123456789).round e = (-3.5).round f = 12345.67.round(-1) g = 3.423456789.round(0) h = 3.423456789.round(1) i = 3.423456789.round(3) assert_equal( 3, a) assert_equal( 4, b) assert_equal( 3, c) assert_equal( -3, d) assert_equal( -4, e) assert_equal(12350, f) assert_equal( 3, g) assert_float( 3.4, h) assert_float(3.423, i) assert_equal(42.0, 42.0.round(307)) assert_equal(1.0e307, 1.0e307.round(2)) inf = 1.0/0.0 assert_raise(FloatDomainError){ inf.round } assert_raise(FloatDomainError){ inf.round(-1) } assert_equal(inf, inf.round(1)) nan = 0.0/0.0 assert_raise(FloatDomainError){ nan.round } assert_raise(FloatDomainError){ nan.round(-1) } assert_true(nan.round(1).nan?) end assert('Float#to_f', '15.2.9.3.13') do a = 3.123456789 assert_float(a, a.to_f) end assert('Float#to_i', '15.2.9.3.14') do assert_equal(3, 3.123456789.to_i) assert_raise(FloatDomainError) { Float::INFINITY.to_i } assert_raise(FloatDomainError) { (-Float::INFINITY).to_i } assert_raise(FloatDomainError) { Float::NAN.to_i } end assert('Float#truncate', '15.2.9.3.15') do assert_equal( 3, 3.123456789.truncate) assert_equal(-3, -3.1.truncate) end assert('Float#divmod') do def check_floats exp, act assert_float exp[0], act[0] assert_float exp[1], act[1] end # Note: quotients are Float because mruby does not have Bignum. check_floats [ 0, 0.0], 0.0.divmod(1) check_floats [ 0, 1.1], 1.1.divmod(3) check_floats [ 3, 0.2], 3.2.divmod(1) check_floats [ 2, 6.3], 20.3.divmod(7) check_floats [-1, 1.6], -3.4.divmod(5) check_floats [-2, -0.5], 25.5.divmod(-13) check_floats [ 1, -6.6], -13.6.divmod(-7) check_floats [ 3, 0.2], 9.8.divmod(3.2) end assert('Float#nan?') do assert_true (0.0/0.0).nan? assert_false 0.0.nan? assert_false (1.0/0.0).nan? assert_false (-1.0/0.0).nan? end assert('Float#<<') do # Left Shift by one assert_equal 46, 23.0 << 1 # Left Shift by a negative is Right Shift assert_equal 23, 46.0 << -1 end assert('Float#>>') do # Right Shift by one assert_equal 23, 46.0 >> 1 # Right Shift by a negative is Left Shift assert_equal 46, 23.0 >> -1 # Don't raise on large Right Shift assert_equal 0, 23.0 >> 128 # Don't raise on large Right Shift assert_equal(-1, -23.0 >> 128) end end # class_defined?("Float") mruby-2.0.0/test/t/gc.rb000066400000000000000000000015011340361412400150020ustar00rootroot00000000000000# Not ISO specified assert('GC.enable') do assert_false GC.disable assert_true GC.enable assert_false GC.enable end assert('GC.disable') do begin assert_false GC.disable assert_true GC.disable ensure GC.enable end end assert('GC.interval_ratio=') do origin = GC.interval_ratio begin assert_equal 150, (GC.interval_ratio = 150) ensure GC.interval_ratio = origin end end assert('GC.step_ratio=') do origin = GC.step_ratio begin assert_equal 150, (GC.step_ratio = 150) ensure GC.step_ratio = origin end end assert('GC.generational_mode=') do origin = GC.generational_mode begin assert_false (GC.generational_mode = false) assert_true (GC.generational_mode = true) assert_true (GC.generational_mode = true) ensure GC.generational_mode = origin end end mruby-2.0.0/test/t/hash.rb000066400000000000000000000167741340361412400153560ustar00rootroot00000000000000## # Hash ISO Test assert('Hash', '15.2.13') do assert_equal Class, Hash.class end assert('Hash#==', '15.2.13.4.1') do assert_true({ 'abc' => 'abc' } == { 'abc' => 'abc' }) assert_false({ 'abc' => 'abc' } == { 'cba' => 'cba' }) assert_true({ :equal => 1 } == { :equal => 1.0 }) if class_defined?("Float") assert_false({ :a => 1 } == true) end assert('Hash#[]', '15.2.13.4.2') do a = { 'abc' => 'abc' } assert_equal 'abc', a['abc'] # Hash#[] should call #default (#3272) hash = {} def hash.default(k); self[k] = 1; end hash[:foo] += 1 assert_equal 2, hash[:foo] end assert('Hash#[]=', '15.2.13.4.3') do a = Hash.new a['abc'] = 'abc' assert_equal 'abc', a['abc'] end assert('Hash#clear', '15.2.13.4.4') do a = { 'abc' => 'abc' } a.clear assert_equal({ }, a) end assert('Hash#dup') do a = { 'a' => 1 } b = a.dup a['a'] = 2 assert_equal({'a' => 1}, b) c = Hash.new { |h, k| h[k] = k.upcase } d = c.dup assert_equal("FOO", d["foo"]) end assert('Hash#default', '15.2.13.4.5') do a = Hash.new b = Hash.new('abc') c = Hash.new {|s,k| s[k] = k} assert_nil a.default assert_equal 'abc', b.default assert_nil c.default assert_equal 'abc', c.default('abc') end assert('Hash#default=', '15.2.13.4.6') do a = { 'abc' => 'abc' } a.default = 'cba' assert_equal 'abc', a['abc'] assert_equal 'cba', a['notexist'] end assert('Hash#default_proc', '15.2.13.4.7') do a = Hash.new b = Hash.new {|s,k| s[k] = k + k} c = b[2] d = b['cat'] assert_nil a.default_proc assert_equal Proc, b.default_proc.class assert_equal 4, c assert_equal 'catcat', d end assert('Hash#delete', '15.2.13.4.8') do a = { 'abc' => 'ABC' } b = { 'abc' => 'ABC' } b_tmp_1 = false b_tmp_2 = false assert_equal 'ABC', a.delete('abc') b.delete('abc') do |k| b_tmp_1 = true end b.delete('abc') do |k| b_tmp_2 = true end assert_nil a.delete('cba') assert_false a.has_key?('abc') assert_false b_tmp_1 assert_true b_tmp_2 end assert('Hash#each', '15.2.13.4.9') do a = { 'abc_key' => 'abc_value' } key = nil value = nil a.each do |k,v| key = k value = v end assert_equal 'abc_key', key assert_equal 'abc_value', value end assert('Hash#each_key', '15.2.13.4.10') do a = { 'abc_key' => 'abc_value' } key = nil a.each_key do |k| key = k end assert_equal 'abc_key', key end assert('Hash#each_value', '15.2.13.4.11') do a = { 'abc_key' => 'abc_value' } value = nil a.each_value do |v| value = v end assert_equal 'abc_value', value end assert('Hash#empty?', '15.2.13.4.12') do a = { 'abc_key' => 'abc_value' } b = Hash.new assert_false a.empty? assert_true b.empty? end assert('Hash#has_key?', '15.2.13.4.13') do a = { 'abc_key' => 'abc_value' } b = Hash.new assert_true a.has_key?('abc_key') assert_false b.has_key?('cba') end assert('Hash#has_value?', '15.2.13.4.14') do a = { 'abc_key' => 'abc_value' } b = Hash.new assert_true a.has_value?('abc_value') assert_false b.has_value?('cba') end assert('Hash#include?', '15.2.13.4.15') do a = { 'abc_key' => 'abc_value' } b = Hash.new assert_true a.include?('abc_key') assert_false b.include?('cba') end assert('Hash#initialize', '15.2.13.4.16') do # Testing initialize by new. h = Hash.new h2 = Hash.new(:not_found) assert_true h.is_a? Hash assert_equal({ }, h) assert_nil h["hello"] assert_equal :not_found, h2["hello"] end assert('Hash#initialize_copy', '15.2.13.4.17') do a = { 'abc_key' => 'abc_value' } b = Hash.new.initialize_copy(a) assert_equal({ 'abc_key' => 'abc_value' }, b) end assert('Hash#key?', '15.2.13.4.18') do a = { 'abc_key' => 'abc_value' } b = Hash.new assert_true a.key?('abc_key') assert_false b.key?('cba') end assert('Hash#keys', '15.2.13.4.19') do a = { 'abc_key' => 'abc_value' } assert_equal ['abc_key'], a.keys end assert('Hash#length', '15.2.13.4.20') do a = { 'abc_key' => 'abc_value' } b = Hash.new assert_equal 1, a.length assert_equal 0, b.length end assert('Hash#member?', '15.2.13.4.21') do a = { 'abc_key' => 'abc_value' } b = Hash.new assert_true a.member?('abc_key') assert_false b.member?('cba') end assert('Hash#merge', '15.2.13.4.22') do a = { 'abc_key' => 'abc_value', 'cba_key' => 'cba_value' } b = { 'cba_key' => 'XXX', 'xyz_key' => 'xyz_value' } result_1 = a.merge b result_2 = a.merge(b) do |key, original, new| original end assert_equal({'abc_key' => 'abc_value', 'cba_key' => 'XXX', 'xyz_key' => 'xyz_value' }, result_1) assert_equal({'abc_key' => 'abc_value', 'cba_key' => 'cba_value', 'xyz_key' => 'xyz_value' }, result_2) assert_raise(TypeError) do { 'abc_key' => 'abc_value' }.merge "a" end end assert('Hash#replace', '15.2.13.4.23') do a = { 'abc_key' => 'abc_value' } b = Hash.new.replace(a) assert_equal({ 'abc_key' => 'abc_value' }, b) a = Hash.new(42) b = {} b.replace(a) assert_equal(42, b[1]) a = Hash.new{|h,x| x} b.replace(a) assert_equal(127, b[127]) assert_raise(TypeError) do { 'abc_key' => 'abc_value' }.replace "a" end end assert('Hash#shift', '15.2.13.4.24') do a = { 'abc_key' => 'abc_value', 'cba_key' => 'cba_value' } b = a.shift assert_equal Array, b.class assert_equal 2, b.size assert_equal 1, a.size b = a.shift assert_equal Array, b.class assert_equal 2, b.size assert_equal 0, a.size end assert('Hash#size', '15.2.13.4.25') do a = { 'abc_key' => 'abc_value' } b = Hash.new assert_equal 1, a.size assert_equal 0, b.size end assert('Hash#store', '15.2.13.4.26') do a = Hash.new a.store('abc', 'abc') assert_equal 'abc', a['abc'] end assert('Hash#value?', '15.2.13.4.27') do a = { 'abc_key' => 'abc_value' } b = Hash.new assert_true a.value?('abc_value') assert_false b.value?('cba') end assert('Hash#values', '15.2.13.4.28') do a = { 'abc_key' => 'abc_value' } assert_equal ['abc_value'], a.values end # Not ISO specified assert('Hash#eql?') do a = { 'a' => 1, 'b' => 2, 'c' => 3 } b = { 'a' => 1, 'b' => 2, 'c' => 3 } c = { 'a' => 1.0, 'b' => 2, 'c' => 3 } assert_true(a.eql?(b)) assert_false(a.eql?(c)) assert_false(a.eql?(true)) end assert('Hash#reject') do h = {:one => 1, :two => 2, :three => 3, :four => 4} ret = h.reject do |k,v| v % 2 == 0 end assert_equal({:one => 1, :three => 3}, ret) assert_equal({:one => 1, :two => 2, :three => 3, :four => 4}, h) end assert('Hash#reject!') do h = {:one => 1, :two => 2, :three => 3, :four => 4} ret = h.reject! do |k,v| v % 2 == 0 end assert_equal({:one => 1, :three => 3}, ret) assert_equal({:one => 1, :three => 3}, h) end assert('Hash#select') do h = {:one => 1, :two => 2, :three => 3, :four => 4} ret = h.select do |k,v| v % 2 == 0 end assert_equal({:two => 2, :four => 4}, ret) assert_equal({:one => 1, :two => 2, :three => 3, :four => 4}, h) end assert('Hash#select!') do h = {:one => 1, :two => 2, :three => 3, :four => 4} ret = h.select! do |k,v| v % 2 == 0 end assert_equal({:two => 2, :four => 4}, ret) assert_equal({:two => 2, :four => 4}, h) end # Not ISO specified assert('Hash#inspect') do h = { "c" => 300, "a" => 100, "d" => 400, "c" => 300 } ret = h.to_s assert_include ret, '"c"=>300' assert_include ret, '"a"=>100' assert_include ret, '"d"=>400' end assert('Hash#rehash') do h = {[:a] => "b"} # hash key modified h.keys[0][0] = :b # h[[:b]] => nil h.rehash assert_equal("b", h[[:b]]) end assert('Hash#freeze') do h = {}.freeze assert_raise(RuntimeError) do h[:a] = 'b' end end mruby-2.0.0/test/t/indexerror.rb000066400000000000000000000001501340361412400165710ustar00rootroot00000000000000## # IndexError ISO Test assert('IndexError', '15.2.33') do assert_equal Class, IndexError.class end mruby-2.0.0/test/t/integer.rb000066400000000000000000000117041340361412400160540ustar00rootroot00000000000000## # Integer ISO Test assert('Integer', '15.2.8') do assert_equal Class, Integer.class end assert('Integer#+', '15.2.8.3.1') do a = 1+1 b = 1+1.0 if class_defined?("Float") assert_equal 2, a assert_equal 2.0, b if class_defined?("Float") assert_raise(TypeError){ 0+nil } assert_raise(TypeError){ 1+nil } c = Mrbtest::FIXNUM_MAX + 1 d = Mrbtest::FIXNUM_MAX.__send__(:+, 1) if class_defined?("Float") e = Mrbtest::FIXNUM_MAX + 1.0 assert_equal Float, c.class assert_equal Float, d.class assert_float e, c assert_float e, d end end assert('Integer#-', '15.2.8.3.2') do a = 2-1 b = 2-1.0 if class_defined?("Float") assert_equal 1, a assert_equal 1.0, b if class_defined?("Float") c = Mrbtest::FIXNUM_MIN - 1 d = Mrbtest::FIXNUM_MIN.__send__(:-, 1) if class_defined?("Float") e = Mrbtest::FIXNUM_MIN - 1.0 assert_equal Float, c.class assert_equal Float, d.class assert_float e, c assert_float e, d end end assert('Integer#*', '15.2.8.3.3') do a = 1*1 b = 1*1.0 if class_defined?("Float") assert_equal 1, a assert_equal 1.0, b if class_defined?("Float") assert_raise(TypeError){ 0*nil } assert_raise(TypeError){ 1*nil } c = Mrbtest::FIXNUM_MAX * 2 d = Mrbtest::FIXNUM_MAX.__send__(:*, 2) if class_defined?("Float") e = Mrbtest::FIXNUM_MAX * 2.0 assert_equal Float, c.class assert_equal Float, d.class assert_float e, c assert_float e, d end end assert('Integer#/', '15.2.8.3.4') do a = 2/1 b = 2/1.0 assert_equal 2, a assert_equal 2.0, b end assert('Integer#%', '15.2.8.3.5') do a = 1%1 b = 1%1.0 c = 2%4 d = 2%5 e = 2%-5 f = -2%5 g = -2%-5 h = 2%-2 i = -2%2 j = -2%-2 assert_equal 0, a assert_equal 0.0, b assert_equal 2, c assert_equal 2, d assert_equal(-3, e) assert_equal 3, f assert_equal(-2, g) assert_equal 0, h assert_equal 0, i assert_equal 0, j end assert('Integer#<=>', '15.2.9.3.6') do a = 1<=>0 b = 1<=>1 c = 1<=>2 assert_equal 1, a assert_equal 0, b assert_equal(-1, c) end assert('Integer#==', '15.2.8.3.7') do a = 1==0 b = 1==1 assert_false a assert_true b end assert('Integer#~', '15.2.8.3.8') do # Complement assert_equal(-1, ~0) assert_equal(-3, ~2) end assert('Integer#&', '15.2.8.3.9') do # Bitwise AND # 0101 (5) # & 0011 (3) # = 0001 (1) assert_equal 1, 5 & 3 end assert('Integer#|', '15.2.8.3.10') do # Bitwise OR # 0101 (5) # | 0011 (3) # = 0111 (7) assert_equal 7, 5 | 3 end assert('Integer#^', '15.2.8.3.11') do # Bitwise XOR # 0101 (5) # ^ 0011 (3) # = 0110 (6) assert_equal 6, 5 ^ 3 end assert('Integer#<<', '15.2.8.3.12') do # Left Shift by one # 00010111 (23) # = 00101110 (46) assert_equal 46, 23 << 1 # Left Shift by a negative is Right Shift assert_equal 23, 46 << -1 # Left Shift by 31 is bitShift overflow to SignedInt assert_equal 2147483648, 1 << 31 # -3 Left Shift by 30 is bitShift overflow to SignedInt assert_equal(-3221225472, -3 << 30) end assert('Integer#>>', '15.2.8.3.13') do # Right Shift by one # 00101110 (46) # = 00010111 (23) assert_equal 23, 46 >> 1 # Right Shift by a negative is Left Shift assert_equal 46, 23 >> -1 # Don't raise on large Right Shift assert_equal 0, 23 >> 128 end assert('Integer#ceil', '15.2.8.3.14') do assert_equal 10, 10.ceil end assert('Integer#downto', '15.2.8.3.15') do a = 0 3.downto(1) do |i| a += i end assert_equal 6, a end assert('Integer#eql?', '15.2.8.3.16') do a = 1.eql?(1) b = 1.eql?(2) c = 1.eql?(nil) assert_true a assert_false b assert_false c end assert('Integer#floor', '15.2.8.3.17') do a = 1.floor assert_equal 1, a end assert('Integer#next', '15.2.8.3.19') do assert_equal 2, 1.next end assert('Integer#round', '15.2.8.3.20') do assert_equal 1, 1.round end assert('Integer#succ', '15.2.8.3.21') do assert_equal 2, 1.succ end assert('Integer#times', '15.2.8.3.22') do a = 0 3.times do a += 1 end assert_equal 3, a end assert('Integer#to_f', '15.2.8.3.23') do assert_equal 1.0, 1.to_f end if class_defined?("Float") assert('Integer#to_i', '15.2.8.3.24') do assert_equal 1, 1.to_i end assert('Integer#to_s', '15.2.8.3.25') do assert_equal '1', 1.to_s assert_equal("-1", -1.to_s) end assert('Integer#truncate', '15.2.8.3.26') do assert_equal 1, 1.truncate end assert('Integer#upto', '15.2.8.3.27') do a = 0 1.upto(3) do |i| a += i end assert_equal 6, a end assert('Integer#divmod', '15.2.8.3.30') do assert_equal [ 0, 0], 0.divmod(1) assert_equal [ 0, 1], 1.divmod(3) assert_equal [ 3, 0], 3.divmod(1) assert_equal [ 2, 6], 20.divmod(7) assert_equal [-1, 2], -3.divmod(5) assert_equal [-2, -1], 25.divmod(-13) assert_equal [ 1, -6], -13.divmod(-7) end # Not ISO specified assert('Integer#step') do a = [] b = [] 1.step(3) do |i| a << i end 1.step(6, 2) do |i| b << i end assert_equal [1, 2, 3], a assert_equal [1, 3, 5], b end mruby-2.0.0/test/t/iterations.rb000066400000000000000000000016721340361412400166030ustar00rootroot00000000000000assert('while expression', '11.5.2.3.2') do idx = 10 all = [] res = while idx > 0 all << idx idx -= 1 end assert_equal nil, res assert_equal [10,9,8,7,6,5,4,3,2,1], all end assert('until expression', '11.5.2.3.3') do idx = 10 all = [] res = until idx == 0 all << idx idx -= 1 end assert_equal nil, res assert_equal [10,9,8,7,6,5,4,3,2,1], all end assert('break expression', '11.5.2.4.3') do assert_equal :result do while true break :result end end assert_equal :result do until false break :result end end end assert('next expression', '11.5.2.4.4') do assert_equal [8,6,4,2,0] do all = [] idx = 10 while idx > 0 idx -= 1 next if (idx % 2) == 1 all << idx end all end assert_equal [8,6,4,2,0] do all = [] idx = 10 until idx == 0 idx -= 1 next if (idx % 2) == 1 all << idx end all end endmruby-2.0.0/test/t/kernel.rb000066400000000000000000000221121340361412400156720ustar00rootroot00000000000000## # Kernel ISO Test assert('Kernel', '15.3.1') do assert_equal Module, Kernel.class end assert('Kernel.block_given?', '15.3.1.2.2') do def bg_try(&b) if Kernel.block_given? yield else "no block" end end assert_false Kernel.block_given? # test without block assert_equal "no block", bg_try # test with block assert_equal "block" do bg_try { "block" } end # test with block assert_equal "block" do bg_try do "block" end end end # Kernel.eval is provided by the mruby-gem mrbgem. '15.3.1.2.3' assert('Kernel.global_variables', '15.3.1.2.4') do assert_equal Array, Kernel.global_variables.class end assert('Kernel.iterator?', '15.3.1.2.5') do assert_false Kernel.iterator? end assert('Kernel.lambda', '15.3.1.2.6') do l = Kernel.lambda do true end m = Kernel.lambda(&l) assert_true l.call assert_equal Proc, l.class assert_true m.call assert_equal Proc, m.class end assert('Kernel.loop', '15.3.1.2.8') do i = 0 Kernel.loop do i += 1 break if i == 100 end assert_equal 100, i end assert('Kernel.p', '15.3.1.2.9') do # TODO search for a way to test p to stdio assert_true true end assert('Kernel.print', '15.3.1.2.10') do # TODO search for a way to test print to stdio assert_true true end assert('Kernel.puts', '15.3.1.2.11') do # TODO search for a way to test puts to stdio assert_true true end assert('Kernel.raise', '15.3.1.2.12') do assert_raise RuntimeError do Kernel.raise end assert_raise RuntimeError do Kernel.raise RuntimeError.new end end assert('Kernel#__id__', '15.3.1.3.3') do assert_equal Fixnum, __id__.class end assert('Kernel#block_given?', '15.3.1.3.6') do def bg_try(&b) if block_given? yield else "no block" end end assert_false block_given? assert_equal "no block", bg_try assert_equal "block" do bg_try { "block" } end assert_equal "block" do bg_try do "block" end end end assert('Kernel#class', '15.3.1.3.7') do assert_equal Module, Kernel.class end assert('Kernel#clone', '15.3.1.3.8') do class KernelCloneTest def initialize @v = 0 end def get @v end def set(v) @v = v end end a = KernelCloneTest.new a.set(1) b = a.clone def a.test end a.set(2) c = a.clone immutables = [ 1, :foo, true, false, nil ] error_count = 0 immutables.each do |i| begin i.clone rescue TypeError error_count += 1 end end assert_equal 2, a.get assert_equal 1, b.get assert_equal 2, c.get assert_true a.respond_to?(:test) assert_false b.respond_to?(:test) assert_true c.respond_to?(:test) a.freeze d = a.clone assert_true d.frozen? end assert('Kernel#dup', '15.3.1.3.9') do class KernelDupTest def initialize @v = 0 end def get @v end def set(v) @v = v end end a = KernelDupTest.new a.set(1) b = a.dup def a.test end a.set(2) c = a.dup immutables = [ 1, :foo, true, false, nil ] error_count = 0 immutables.each do |i| begin i.dup rescue TypeError error_count += 1 end end assert_equal immutables.size, error_count assert_equal 2, a.get assert_equal 1, b.get assert_equal 2, c.get assert_true a.respond_to?(:test) assert_false b.respond_to?(:test) assert_false c.respond_to?(:test) end assert('Kernel#dup class') do assert_nothing_raised do Array.dup.new(200) Range.dup.new(2, 3) String.dup.new("a"*50) end end # Kernel#eval is provided by mruby-eval mrbgem '15.3.1.3.12' assert('Kernel#extend', '15.3.1.3.13') do class Test4ExtendClass end module Test4ExtendModule def test_method; end end a = Test4ExtendClass.new a.extend(Test4ExtendModule) b = Test4ExtendClass.new assert_true a.respond_to?(:test_method) assert_false b.respond_to?(:test_method) end assert('Kernel#extend works on toplevel', '15.3.1.3.13') do module Test4ExtendModule def test_method; end end # This would crash... extend(Test4ExtendModule) assert_true respond_to?(:test_method) end assert('Kernel#freeze') do obj = Object.new assert_equal obj, obj.freeze assert_equal 0, 0.freeze assert_equal :a, :a.freeze end assert('Kernel#global_variables', '15.3.1.3.14') do assert_equal Array, global_variables.class end assert('Kernel#hash', '15.3.1.3.15') do assert_equal hash, hash end assert('Kernel#inspect', '15.3.1.3.17') do s = inspect assert_equal String, s.class assert_equal "main", s end assert('Kernel#is_a?', '15.3.1.3.24') do assert_true is_a?(Kernel) assert_false is_a?(Array) assert_raise TypeError do 42.is_a?(42) end end assert('Kernel#iterator?', '15.3.1.3.25') do assert_false iterator? end assert('Kernel#kind_of?', '15.3.1.3.26') do assert_true kind_of?(Kernel) assert_false kind_of?(Array) end assert('Kernel#lambda', '15.3.1.3.27') do l = lambda do true end m = lambda(&l) assert_true l.call assert_equal Proc, l.class assert_true m.call assert_equal Proc, m.class end assert('Kernel#loop', '15.3.1.3.29') do i = 0 loop do i += 1 break if i == 100 end assert_equal i, 100 end assert('Kernel#method_missing', '15.3.1.3.30') do class MMTestClass def method_missing(sym) "A call to #{sym}" end end mm_test = MMTestClass.new assert_equal 'A call to no_method_named_this', mm_test.no_method_named_this class SuperMMTestClass < MMTestClass def no_super_method_named_this super end end super_mm_test = SuperMMTestClass.new assert_equal 'A call to no_super_method_named_this', super_mm_test.no_super_method_named_this class NoSuperMethodTestClass def no_super_method_named_this super end end no_super_test = NoSuperMethodTestClass.new begin no_super_test.no_super_method_named_this rescue NoMethodError => e assert_equal "undefined method 'no_super_method_named_this'", e.message end a = String.new begin a.no_method_named_this rescue NoMethodError => e assert_equal "undefined method 'no_method_named_this'", e.message end end assert('Kernel#nil?', '15.3.1.3.32') do assert_false nil? end assert('Kernel#object_id', '15.3.1.3.33') do a = "" b = "" assert_not_equal a.object_id, b.object_id assert_kind_of Numeric, object_id assert_kind_of Numeric, "".object_id assert_kind_of Numeric, true.object_id assert_kind_of Numeric, false.object_id assert_kind_of Numeric, nil.object_id assert_kind_of Numeric, :no.object_id assert_kind_of Numeric, 1.object_id assert_kind_of Numeric, 1.0.object_id end # Kernel#p is defined in mruby-print mrbgem. '15.3.1.3.34' # Kernel#print is defined in mruby-print mrbgem. '15.3.1.3.35' # Kernel#puts is defined in mruby-print mrbgem. '15.3.1.3.39' assert('Kernel#raise', '15.3.1.3.40') do assert_raise RuntimeError do raise end assert_raise RuntimeError do raise RuntimeError.new end end assert('Kernel#remove_instance_variable', '15.3.1.3.41') do class Test4RemoveInstanceVar attr_reader :var def initialize @var = 99 end def remove remove_instance_variable(:@var) end end tri = Test4RemoveInstanceVar.new assert_equal 99, tri.var tri.remove assert_equal nil, tri.var assert_raise NameError do tri.remove end end # Kernel#require is defined in mruby-require. '15.3.1.3.42' assert('Kernel#respond_to?', '15.3.1.3.43') do class Test4RespondTo def valid_method; end def test_method; end undef test_method end assert_raise TypeError do Test4RespondTo.new.respond_to?(1) end assert_raise ArgumentError do Test4RespondTo.new.respond_to? end assert_raise ArgumentError do Test4RespondTo.new.respond_to? :a, true, :aa end assert_true respond_to?(:nil?) assert_true Test4RespondTo.new.respond_to?(:valid_method) assert_true Test4RespondTo.new.respond_to?('valid_method') assert_false Test4RespondTo.new.respond_to?(:test_method) end assert('Kernel#to_s', '15.3.1.3.46') do assert_equal to_s.class, String end assert('Kernel#!=') do str1 = "hello" str2 = str1 str3 = "world" assert_false (str1[1] != 'e') assert_true (str1 != str3) assert_false (str2 != str1) end # operator "!~" is defined in ISO Ruby 11.4.4. assert('Kernel#!~') do x = "x" def x.=~(other) other == "x" end assert_false x !~ "x" assert_true x !~ "z" y = "y" def y.=~(other) other == "y" end def y.!~(other) other == "not y" end assert_false y !~ "y" assert_false y !~ "z" assert_true y !~ "not y" end assert('Kernel#respond_to_missing?') do class Test4RespondToMissing def respond_to_missing?(method_name, include_private = false) method_name == :a_method end end assert_true Test4RespondToMissing.new.respond_to?(:a_method) assert_false Test4RespondToMissing.new.respond_to?(:no_method) end assert('Kernel#global_variables') do variables = global_variables 1.upto(9) do |i| assert_equal variables.include?(:"$#{i}"), true end end assert('stack extend') do def recurse(count, stop) return count if count > stop recurse(count+1, stop) end assert_equal 6, recurse(0, 5) end mruby-2.0.0/test/t/lang.rb000077500000000000000000000026541340361412400153470ustar00rootroot00000000000000# The aim of these tests is to detect pitfall for optimized VM. # Test for or/and # # You may think instruction fusion(OP_EQ and OP_JMPIF) for avoiding # generate intermediate boolean value. # But and/or is pitfall for this fusioning. # # For example, the following mruby code: # # if i > 0 and i < 10 # # compiles to the following byte code: # # 1 000 OP_LOADI R1 0 ; R1:i # 2 001 OP_MOVE R2 R1 ; R1:i # 2 002 OP_LOADI R3 0 # 2 003 OP_GT R2 :> 1 # 2 004 OP_JMPNOT R2 008 # 2 005 OP_MOVE R2 R1 ; R1:i # 2 006 OP_LOADI R3 10 # 2 007 OP_LT R2 :< 1 # 2 008 OP_JMPNOT R2 (The address of end of then part) # # When the instruction fusion the OP_GT and OP_JMPNOT you fell into the pitfalls. # The deleted intermediate boolean value is used in OP_JMPNOT (address 008). assert('and', '11.2.3') do a = 1 if a > 0 and a < 10 b = 1 else b = 0 end assert_equal 1, b if a < 0 and a < 10 b = 1 else b = 0 end assert_equal 0, b if a < 0 and a > 10 b = 1 else b = 0 end assert_equal 0, b end assert('or','11.2.4') do a = 1 if a > 0 or a < 10 b = 1 else b = 0 end assert_equal 1, b if a < 0 or a < 10 b = 1 else b = 0 end assert_equal 1, b if a < 0 or a > 10 b = 1 else b = 0 end assert_equal 0, b end mruby-2.0.0/test/t/literals.rb000066400000000000000000000136271340361412400162440ustar00rootroot00000000000000## # Literals ISO Test assert('Literals Numerical', '8.7.6.2') do # signed and unsigned integer assert_equal 1, 1 assert_equal(-1, -1) assert_equal(+1, +1) # signed and unsigned float assert_equal 1.0, 1.0 assert_equal(-1.0, -1.0) # binary assert_equal 128, 0b10000000 assert_equal 128, 0B10000000 # octal assert_equal 8, 0o10 assert_equal 8, 0O10 assert_equal 8, 0_10 # hex assert_equal 255, 0xff assert_equal 255, 0Xff # decimal assert_equal 999, 0d999 assert_equal 999, 0D999 # decimal separator assert_equal 10000000, 10_000_000 assert_equal 10, 1_0 # integer with exponent assert_equal 10.0, 1e1 assert_equal(0.1, 1e-1) assert_equal 10.0, 1e+1 # float with exponent assert_equal 10.0, 1.0e1 assert_equal(0.1, 1.0e-1) assert_equal 10.0, 1.0e+1 end assert('Literals Strings Single Quoted', '8.7.6.3.2') do assert_equal 'abc', 'abc' assert_equal '\'', '\'' assert_equal '\\', '\\' end assert('Literals Strings Double Quoted', '8.7.6.3.3') do a = "abc" assert_equal "abc", "abc" assert_equal "\"", "\"" assert_equal "\\", "\\" assert_equal "abc", "#{a}" end assert('Literals Strings Quoted Non-Expanded', '8.7.6.3.4') do a = %q{abc} b = %q(abc) c = %q[abc] d = %q e = %q/abc/ f = %q/ab\/c/ g = %q{#{a}} assert_equal 'abc', a assert_equal 'abc', b assert_equal 'abc', c assert_equal 'abc', d assert_equal 'abc', e assert_equal 'ab/c', f assert_equal '#{a}', g end assert('Literals Strings Quoted Expanded', '8.7.6.3.5') do a = %Q{abc} b = %Q(abc) c = %Q[abc] d = %Q e = %Q/abc/ f = %Q/ab\/c/ g = %Q{#{a}} assert_equal 'abc', a assert_equal 'abc', b assert_equal 'abc', c assert_equal 'abc', d assert_equal 'abc', e assert_equal 'ab/c', f assert_equal 'abc', g end assert('Literals Strings Here documents', '8.7.6.3.6') do a = <\"mm3\\n\"}y\nmm1\n", "mm2\n"], m assert_equal ({:x=>"mm3\n"}), m2 assert_equal [1, "nn1\n", 3, 4], n assert_equal "a $ q\n $ c $ d", q1 assert_equal "l $ mqq\nn $ o", q2 assert_equal ["1", "www\n", "3", "4", "5"], w assert_equal [1, "foo 222 333\n 444\n5\n bar\n6\n", 9], x assert_equal "", z end assert('Literals Array', '8.7.6.4') do a = %W{abc#{1+2}def \}g} b = %W(abc #{2+3} def \(g) c = %W[#{3+4}] d = %W< #{4+5} > e = %W// f = %W[[ab cd][ef]] g = %W{ ab #{-1}1 2#{2} } h = %W(a\nb test\ abc c\ d x\y x\\y x\\\y) assert_equal ['abc3def', '}g'], a assert_equal ['abc', '5', 'def', '(g'], b assert_equal ['7'],c assert_equal ['9'], d assert_equal [], e assert_equal ['[ab', 'cd][ef]'], f assert_equal ['ab', '-11', '22'], g assert_equal ["a\nb", 'test abc', "c\nd", "xy", "x\\y", "x\\y"], h a = %w{abc#{1+2}def \}g} b = %w(abc #{2+3} def \(g) c = %w[#{3+4}] d = %w< #{4+5} > e = %w// f = %w[[ab cd][ef]] g = %w{ ab #{-1}1 2#{2} } h = %w(a\nb test\ abc c\ d x\y x\\y x\\\y) assert_equal ['abc#{1+2}def', '}g'], a assert_equal ['abc', '#{2+3}', 'def', '(g'], b assert_equal ['#{3+4}'], c assert_equal ['#{4+5}'], d assert_equal [], e assert_equal ['[ab', 'cd][ef]'], f assert_equal ['ab', '#{-1}1', '2#{2}'], g assert_equal ["a\\nb", "test abc", "c\nd", "x\\y", "x\\y", "x\\\\y"], h end assert('Literals Array of symbols') do a = %I{abc#{1+2}def \}g} b = %I(abc #{2+3} def \(g) c = %I[#{3+4}] d = %I< #{4+5} > e = %I// f = %I[[ab cd][ef]] g = %I{ ab #{-1}1 2#{2} } assert_equal [:'abc3def', :'}g'], a assert_equal [:'abc', :'5', :'def', :'(g'], b assert_equal [:'7'],c assert_equal [:'9'], d assert_equal [], e assert_equal [:'[ab', :'cd][ef]'], f assert_equal [:'ab', :'-11', :'22'], g a = %i{abc#{1+2}def \}g} b = %i(abc #{2+3} def \(g) c = %i[#{3+4}] d = %i< #{4+5} > e = %i// f = %i[[ab cd][ef]] g = %i{ ab #{-1}1 2#{2} } assert_equal [:'abc#{1+2}def', :'}g'], a assert_equal [:'abc', :'#{2+3}', :'def', :'(g'], b assert_equal [:'#{3+4}'], c assert_equal [:'#{4+5}'], d assert_equal [] ,e assert_equal [:'[ab', :'cd][ef]'], f assert_equal [:'ab', :'#{-1}1', :'2#{2}'], g end assert('Literals Symbol', '8.7.6.6') do # do not compile error :$asd :@asd :@@asd :asd= :asd! :asd? :+ :+@ :if :BEGIN a = :"asd qwe" b = :'foo bar' c = :"a#{1+2}b" d = %s(asd) e = %s( foo \)) f = %s[asd \[ qwe] g = %s/foo#{1+2}bar/ h = %s{{foo bar}} assert_equal :'asd qwe', a assert_equal :"foo bar", b assert_equal :a3b, c assert_equal :asd, d assert_equal :' foo )', e assert_equal :"asd [\nqwe", f assert_equal :'foo#{1+2}bar', g assert_equal :'{foo bar}', h end # Not Implemented ATM assert('Literals Regular expression', '8.7.6.5') do mruby-2.0.0/test/t/localjumperror.rb000066400000000000000000000004751340361412400174620ustar00rootroot00000000000000## # LocalJumpError ISO Test assert('LocalJumpError', '15.2.25') do assert_equal Class, LocalJumpError.class # assert_raise LocalJumpError do # # this will cause an exception due to the wrong location # retry # end end # TODO 15.2.25.2.1 LocalJumpError#exit_value # TODO 15.2.25.2.2 LocalJumpError#reason mruby-2.0.0/test/t/methods.rb000066400000000000000000000054241340361412400160640ustar00rootroot00000000000000## # Chapter 13.3 "Methods" ISO Test assert('The alias statement', '13.3.6 a) 4)') do # check aliasing in all possible ways def alias_test_method_original; true; end alias alias_test_method_a alias_test_method_original alias :alias_test_method_b :alias_test_method_original assert_true(alias_test_method_original) assert_true(alias_test_method_a) assert_true(alias_test_method_b) end assert('The alias statement (overwrite original)', '13.3.6 a) 4)') do # check that an aliased method can be overwritten # without side effect def alias_test_method_original; true; end alias alias_test_method_a alias_test_method_original alias :alias_test_method_b :alias_test_method_original assert_true(alias_test_method_original) def alias_test_method_original; false; end assert_false(alias_test_method_original) assert_true(alias_test_method_a) assert_true(alias_test_method_b) end assert('The alias statement', '13.3.6 a) 5)') do # check that alias is raising NameError if # non-existing method should be undefined assert_raise(NameError) do alias new_name_a non_existing_method end assert_raise(NameError) do alias :new_name_b :non_existing_method end end assert('The undef statement', '13.3.7 a) 4)') do # check that undef is undefining method # based on the method name def existing_method_a; true; end def existing_method_b; true; end def existing_method_c; true; end def existing_method_d; true; end def existing_method_e; true; end def existing_method_f; true; end # check that methods are defined assert_true(existing_method_a, 'Method should be defined') assert_true(existing_method_b, 'Method should be defined') assert_true(existing_method_c, 'Method should be defined') assert_true(existing_method_d, 'Method should be defined') assert_true(existing_method_e, 'Method should be defined') assert_true(existing_method_f, 'Method should be defined') # undefine in all possible ways and check that method # is undefined undef existing_method_a assert_raise(NoMethodError) do existing_method_a end undef :existing_method_b assert_raise(NoMethodError) do existing_method_b end undef existing_method_c, existing_method_d assert_raise(NoMethodError) do existing_method_c end assert_raise(NoMethodError) do existing_method_d end undef :existing_method_e, :existing_method_f assert_raise(NoMethodError) do existing_method_e end assert_raise(NoMethodError) do existing_method_f end end assert('The undef statement (method undefined)', '13.3.7 a) 5)') do # check that undef is raising NameError if # non-existing method should be undefined assert_raise(NameError) do undef non_existing_method end assert_raise(NameError) do undef :non_existing_method end end mruby-2.0.0/test/t/module.rb000066400000000000000000000426471340361412400157160ustar00rootroot00000000000000## # Module ISO Test def labeled_module(name, &block) Module.new do (class < e $test_dummy_result = e.name end assert_equal :bar, $test_dummy_result end assert('NameError#initialize', '15.2.31.2.2') do e = NameError.new('a', :foo) assert_equal NameError, e.class assert_equal 'a', e.message assert_equal :foo, e.name end mruby-2.0.0/test/t/nil.rb000066400000000000000000000013351340361412400152000ustar00rootroot00000000000000## # NilClass ISO Test assert('NilClass', '15.2.4') do assert_equal Class, NilClass.class end assert('NilClass', '15.2.4.1') do assert_equal NilClass, nil.class assert_false NilClass.method_defined? :new end assert('NilClass#&', '15.2.4.3.1') do assert_false nil.&(true) assert_false nil.&(nil) end assert('NilClass#^', '15.2.4.3.2') do assert_true nil.^(true) assert_false nil.^(false) end assert('NilClass#|', '15.2.4.3.3') do assert_true nil.|(true) assert_false nil.|(false) end assert('NilClass#nil?', '15.2.4.3.4') do assert_true nil.nil? end assert('NilClass#to_s', '15.2.4.3.5') do assert_equal '', nil.to_s end assert('safe navigation') do assert_nil nil&.size assert_equal 0, []&.size end mruby-2.0.0/test/t/nomethoderror.rb000066400000000000000000000007431340361412400173070ustar00rootroot00000000000000## # NoMethodError ISO Test assert('NoMethodError', '15.2.32') do NoMethodError.class == Class assert_raise NoMethodError do doesNotExistAsAMethodNameForVerySure("") end end assert('NoMethodError#args', '15.2.32.2.1') do a = NoMethodError.new 'test', :test, [1, 2] assert_equal [1, 2], a.args assert_nothing_raised do begin doesNotExistAsAMethodNameForVerySure 3, 1, 4 rescue NoMethodError => e assert_equal [3, 1, 4], e.args end end end mruby-2.0.0/test/t/numeric.rb000066400000000000000000000015041340361412400160560ustar00rootroot00000000000000## # Numeric ISO Test assert('Numeric', '15.2.7') do assert_equal Class, Numeric.class end assert('Numeric#+@', '15.2.7.4.1') do assert_equal(+1, +1) end assert('Numeric#-@', '15.2.7.4.2') do assert_equal(-1, -1) end assert('Numeric#abs', '15.2.7.4.3') do assert_equal(1, 1.abs) assert_equal(1.0, -1.abs) if class_defined?("Float") end assert('Numeric#pow') do assert_equal(8, 2 ** 3) assert_equal(-8, -2 ** 3) assert_equal(1, 2 ** 0) assert_equal(1, 2.2 ** 0) assert_equal(0.5, 2 ** -1) end assert('Numeric#/', '15.2.8.3.4') do n = Class.new(Numeric){ def /(x); 15.1;end }.new assert_equal(2, 10/5) assert_equal(0.0625, 1/16) assert_equal(15.1, n/10) assert_raise(TypeError){ 1/n } assert_raise(TypeError){ 1/nil } end # Not ISO specified assert('Numeric#**') do assert_equal 8.0, 2.0**3 end mruby-2.0.0/test/t/object.rb000066400000000000000000000002721340361412400156630ustar00rootroot00000000000000## # Object ISO Test assert('Object', '15.2.1') do assert_equal Class, Object.class end assert('Object superclass', '15.2.1.2') do assert_equal BasicObject, Object.superclass end mruby-2.0.0/test/t/proc.rb000066400000000000000000000101521340361412400153560ustar00rootroot00000000000000## # Proc ISO Test assert('Proc', '15.2.17') do assert_equal Class, Proc.class end assert('Proc.new', '15.2.17.3.1') do assert_raise ArgumentError do Proc.new end assert_equal (Proc.new {}).class, Proc assert_raise LocalJumpError do Proc.new{ break }.call end end assert('Proc#[]', '15.2.17.4.1') do a = 0 b = Proc.new { a += 1 } b.[] a2 = 0 b2 = Proc.new { |i| a2 += i } b2.[](5) assert_equal 1, a assert_equal 5, a2 end assert('Proc#arity', '15.2.17.4.2') do a = Proc.new {|x, y|}.arity b = Proc.new {|x, *y, z|}.arity c = Proc.new {|x=0, y|}.arity d = Proc.new {|(x, y), z=0|}.arity assert_equal 2, a 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 a = 0 b = Proc.new { a += 1 } b.call a2 = 0 b2 = Proc.new { |i| a2 += i } b2.call(5) assert_equal 1, a assert_equal 5, a2 end assert('Proc#call proc args pos block') do pr = Proc.new {|a,b,&c| [a, b, c.class, c&&c.call(:x)] } assert_equal [nil, nil, Proc, :proc], (pr.call(){ :proc }) assert_equal [1, nil, Proc, :proc], (pr.call(1){ :proc }) assert_equal [1, 2, Proc, :proc], (pr.call(1, 2){ :proc }) assert_equal [1, 2, Proc, :proc], (pr.call(1, 2, 3){ :proc }) assert_equal [1, 2, Proc, :proc], (pr.call(1, 2, 3, 4){ :proc }) assert_equal [nil, nil, Proc, :x], (pr.call(){|x| x}) assert_equal [1, nil, Proc, :x], (pr.call(1){|x| x}) assert_equal [1, 2, Proc, :x], (pr.call(1, 2){|x| x}) assert_equal [1, 2, Proc, :x], (pr.call(1, 2, 3){|x| x}) assert_equal [1, 2, Proc, :x], (pr.call(1, 2, 3, 4){|x| x}) end assert('Proc#call proc args pos rest post') do pr = Proc.new {|a,b,*c,d,e| [a,b,c,d,e] } assert_equal [nil, nil, [], nil, nil], pr.call() assert_equal [1, nil, [], nil, nil], pr.call(1) assert_equal [1, 2, [], nil, nil], pr.call(1,2) assert_equal [1, 2, [], 3, nil], pr.call(1,2,3) assert_equal [1, 2, [], 3, 4], pr.call(1,2,3,4) assert_equal [1, 2, [3], 4, 5], pr.call(1,2,3,4,5) assert_equal [1, 2, [3, 4], 5, 6], pr.call(1,2,3,4,5,6) assert_equal [1, 2, [3, 4, 5], 6,7], pr.call(1,2,3,4,5,6,7) assert_equal [nil, nil, [], nil, nil], pr.call([]) assert_equal [1, nil, [], nil, nil], pr.call([1]) assert_equal [1, 2, [], nil, nil], pr.call([1,2]) assert_equal [1, 2, [], 3, nil], pr.call([1,2,3]) assert_equal [1, 2, [], 3, 4], pr.call([1,2,3,4]) assert_equal [1, 2, [3], 4, 5], pr.call([1,2,3,4,5]) assert_equal [1, 2, [3, 4], 5, 6], pr.call([1,2,3,4,5,6]) assert_equal [1, 2, [3, 4, 5], 6,7], pr.call([1,2,3,4,5,6,7]) end assert('Proc#return_does_not_break_self') do class TestClass attr_accessor :block def initialize end def return_array @block = Proc.new { self } return [] end def return_instance_variable @block = Proc.new { self } return @block end def return_const_fixnum @block = Proc.new { self } return 123 end def return_nil @block = Proc.new { self } return nil end end c = TestClass.new assert_equal [], c.return_array assert_equal c, c.block.call c.return_instance_variable assert_equal c, c.block.call assert_equal 123, c.return_const_fixnum assert_equal c, c.block.call assert_equal nil, c.return_nil assert_equal c, c.block.call end assert('call Proc#initialize if defined') do a = [] c = Class.new(Proc) do define_method(:initialize) do a << :ok end end assert_kind_of c, c.new{} assert_equal [:ok], a end assert('&obj call to_proc if defined') do pr = Proc.new{} def mock(&b) b end assert_equal pr.object_id, mock(&pr).object_id assert_equal pr, mock(&pr) obj = Object.new def obj.to_proc Proc.new{ :from_to_proc } end assert_equal :from_to_proc, mock(&obj).call assert_raise(TypeError){ mock(&(Object.new)) } end assert('Creation of a proc through the block of a method') do def m(&b) b end assert_equal m{}.class, Proc assert_raise LocalJumpError do m{ break }.call end end mruby-2.0.0/test/t/range.rb000066400000000000000000000041301340361412400155060ustar00rootroot00000000000000## # Range ISO Test assert('Range', '15.2.14') do assert_equal Class, Range.class end assert('Range#==', '15.2.14.4.1') do assert_true (1..10) == (1..10) assert_false (1..10) == (1..100) assert_true (1..10) == Range.new(1.0, 10.0) if class_defined?("Float") end assert('Range#===', '15.2.14.4.2') do a = (1..10) assert_true a === 5 assert_false a === 20 end assert('Range#begin', '15.2.14.4.3') do assert_equal 1, (1..10).begin end assert('Range#each', '15.2.14.4.4') do a = (1..3) b = 0 a.each {|i| b += i} assert_equal 6, b end assert('Range#end', '15.2.14.4.5') do assert_equal 10, (1..10).end end assert('Range#exclude_end?', '15.2.14.4.6') do assert_true (1...10).exclude_end? assert_false (1..10).exclude_end? end assert('Range#first', '15.2.14.4.7') do assert_equal 1, (1..10).first end assert('Range#include?', '15.2.14.4.8') do assert_true (1..10).include?(10) assert_false (1..10).include?(11) assert_true (1...10).include?(9) assert_false (1...10).include?(10) end assert('Range#initialize', '15.2.14.4.9') do a = Range.new(1, 10, true) b = Range.new(1, 10, false) assert_equal (1...10), a assert_true a.exclude_end? assert_equal (1..10), b assert_false b.exclude_end? assert_raise(NameError) { (0..1).send(:initialize, 1, 3) } end assert('Range#last', '15.2.14.4.10') do assert_equal 10, (1..10).last end assert('Range#member?', '15.2.14.4.11') do a = (1..10) assert_true a.member?(5) assert_false a.member?(20) end assert('Range#to_s', '15.2.14.4.12') do assert_equal "0..1", (0..1).to_s assert_equal "0...1", (0...1).to_s assert_equal "a..b", ("a".."b").to_s assert_equal "a...b", ("a"..."b").to_s end assert('Range#inspect', '15.2.14.4.13') do assert_equal "0..1", (0..1).inspect assert_equal "0...1", (0...1).inspect assert_equal "\"a\"..\"b\"", ("a".."b").inspect assert_equal "\"a\"...\"b\"", ("a"..."b").inspect end assert('Range#eql?', '15.2.14.4.14') do assert_true (1..10).eql? (1..10) assert_false (1..10).eql? (1..100) assert_false (1..10).eql? (Range.new(1.0, 10.0)) assert_false (1..10).eql? "1..10" end mruby-2.0.0/test/t/rangeerror.rb000066400000000000000000000001501340361412400165560ustar00rootroot00000000000000## # RangeError ISO Test assert('RangeError', '15.2.26') do assert_equal Class, RangeError.class end mruby-2.0.0/test/t/regexperror.rb000066400000000000000000000001211340361412400167520ustar00rootroot00000000000000## # RegexpError ISO Test # TODO broken ATM assert('RegexpError', '15.2.27') do mruby-2.0.0/test/t/runtimeerror.rb000066400000000000000000000001561340361412400171530ustar00rootroot00000000000000## # RuntimeError ISO Test assert('RuntimeError', '15.2.28') do assert_equal Class, RuntimeError.class end mruby-2.0.0/test/t/standarderror.rb000066400000000000000000000001611340361412400172640ustar00rootroot00000000000000## # StandardError ISO Test assert('StandardError', '15.2.23') do assert_equal Class, StandardError.class end mruby-2.0.0/test/t/string.rb000066400000000000000000000356771340361412400157440ustar00rootroot00000000000000# coding: utf-8 ## # String ISO Test UTF8STRING = ("\343\201\202".size == 1) assert('String', '15.2.10') do assert_equal Class, String.class end assert('String#<=>', '15.2.10.5.1') do a = '' <=> '' b = '' <=> 'not empty' c = 'not empty' <=> '' d = 'abc' <=> 'cba' e = 'cba' <=> 'abc' assert_equal 0, a assert_equal(-1, b) assert_equal 1, c assert_equal(-1, d) assert_equal 1, e assert_nil 'a' <=> 1024 end assert('String#==', '15.2.10.5.2') do assert_equal 'abc', 'abc' assert_not_equal 'abc', 'cba' end # 'String#=~', '15.2.10.5.3' will be tested in mrbgems. assert('String#+', '15.2.10.5.4') do assert_equal 'ab', 'a' + 'b' end assert('String#*', '15.2.10.5.5') do assert_equal 'aaaaa', 'a' * 5 assert_equal '', 'a' * 0 assert_raise(ArgumentError) do 'a' * -1 end end assert('String#[]', '15.2.10.5.6') do # length of args is 1 a = 'abc'[0] b = 'abc'[-1] c = 'abc'[10] d = 'abc'[-10] e = 'abc'[1.1] # length of args is 2 a1 = 'abc'[0, -1] b1 = 'abc'[10, 0] c1 = 'abc'[-10, 0] d1 = 'abc'[0, 0] e1 = 'abc'[1, 2] # args is RegExp # It will be tested in mrbgems. # args is String a3 = 'abc'['bc'] b3 = 'abc'['XX'] assert_equal 'a', 'a' # assert_equal 'c', b # assert_nil c # assert_nil d # assert_equal 'b', e # assert_nil a1 # assert_nil b1 # assert_nil c1 # assert_equal '', d1 # assert_equal 'bc', e1 # assert_equal 'bc', a3 # assert_nil b3 # assert_raise(TypeError) do # a[nil] # end end assert('String#[](UTF-8)', '15.2.10.5.6') do assert_equal "ち", "こんにちは世界"[3] assert_equal nil, "こんにちは世界"[20] assert_equal "世", "こんにちは世界"[-2] assert_equal "世界", "こんにちは世界"[-2..-1] assert_equal "んに", "こんにちは世界"[1,2] assert_equal "世", "こんにちは世界"["世"] end if UTF8STRING assert('String#[] with Range') do a1 = 'abc'[1..0] b1 = 'abc'[1..1] c1 = 'abc'[1..2] d1 = 'abc'[1..3] e1 = 'abc'[1..4] f1 = 'abc'[0..-2] g1 = 'abc'[-2..3] h1 = 'abc'[3..4] i1 = 'abc'[4..5] j1 = 'abcdefghijklmnopqrstuvwxyz'[1..3] a2 = 'abc'[1...0] b2 = 'abc'[1...1] c2 = 'abc'[1...2] d2 = 'abc'[1...3] e2 = 'abc'[1...4] f2 = 'abc'[0...-2] g2 = 'abc'[-2...3] h2 = 'abc'[3...4] i2 = 'abc'[4...5] j2 = 'abcdefghijklmnopqrstuvwxyz'[1...3] assert_equal '', a1 assert_equal 'b', b1 assert_equal 'bc', c1 assert_equal 'bc', d1 assert_equal 'bc', e1 assert_equal 'ab', f1 assert_equal 'bc', g1 assert_equal '', h1 assert_nil i2 assert_equal 'bcd', j1 assert_equal '', a2 assert_equal '', b2 assert_equal 'b', c2 assert_equal 'bc', d2 assert_equal 'bc', e2 assert_equal 'a', f2 assert_equal 'bc', g2 assert_equal '', h2 assert_nil i2 assert_equal 'bc', j2 end assert('String#[]=') do # length of args is 1 a = 'abc' a[0] = 'X' assert_equal 'Xbc', a b = 'abc' b[-1] = 'X' assert_equal 'abX', b c = 'abc' assert_raise(IndexError) do c[10] = 'X' end d = 'abc' assert_raise(IndexError) do d[-10] = 'X' end if class_defined?("Float") e = 'abc' e[1.1] = 'X' assert_equal 'aXc', e end # length of args is 2 a1 = 'abc' assert_raise(IndexError) do a1[0, -1] = 'X' end b1 = 'abc' assert_raise(IndexError) do b1[10, 0] = 'X' end c1 = 'abc' assert_raise(IndexError) do c1[-10, 0] = 'X' end d1 = 'abc' d1[0, 0] = 'X' assert_equal 'Xabc', d1 e1 = 'abc' e1[1, 3] = 'X' assert_equal 'aX', e1 # args is RegExp # It will be tested in mrbgems. # args is String a3 = 'abc' a3['bc'] = 'X' assert_equal a3, 'aX' b3 = 'abc' assert_raise(IndexError) do b3['XX'] = 'Y' end end assert('String#capitalize', '15.2.10.5.7') do a = 'abc' a.capitalize assert_equal 'abc', a assert_equal 'Abc', 'abc'.capitalize end assert('String#capitalize!', '15.2.10.5.8') do a = 'abc' a.capitalize! assert_equal 'Abc', a assert_equal nil, 'Abc'.capitalize! end assert('String#chomp', '15.2.10.5.9') do a = 'abc'.chomp b = ''.chomp c = "abc\n".chomp d = "abc\n\n".chomp e = "abc\t".chomp("\t") f = "abc\n" f.chomp assert_equal 'abc', a assert_equal '', b assert_equal 'abc', c assert_equal "abc\n", d assert_equal 'abc', e assert_equal "abc\n", f end assert('String#chomp!', '15.2.10.5.10') do a = 'abc' b = '' c = "abc\n" d = "abc\n\n" e = "abc\t" a.chomp! b.chomp! c.chomp! d.chomp! e.chomp!("\t") assert_equal 'abc', a assert_equal '', b assert_equal 'abc', c assert_equal "abc\n", d assert_equal 'abc', e end assert('String#chop', '15.2.10.5.11') do a = ''.chop b = 'abc'.chop c = 'abc' c.chop assert_equal '', a assert_equal 'ab', b assert_equal 'abc', c end assert('String#chop(UTF-8)', '15.2.10.5.11') do a = ''.chop b = 'あいう'.chop c = "あ\nい".chop.chop assert_equal '', a assert_equal 'あい', b assert_equal 'あ', c end if UTF8STRING assert('String#chop!', '15.2.10.5.12') do a = '' b = 'abc' a.chop! b.chop! assert_equal a, '' assert_equal b, 'ab' end assert('String#chop!(UTF-8)', '15.2.10.5.12') do a = '' b = "あいうえ\n" c = "あいうえ\n" a.chop! b.chop! c.chop! c.chop! assert_equal a, '' assert_equal b, 'あいうえ' assert_equal c, 'あいう' end if UTF8STRING assert('String#downcase', '15.2.10.5.13') do a = 'ABC'.downcase b = 'ABC' b.downcase assert_equal 'abc', a assert_equal 'ABC', b end assert('String#downcase!', '15.2.10.5.14') do a = 'ABC' a.downcase! assert_equal 'abc', a assert_equal nil, 'abc'.downcase! end assert('String#each_line', '15.2.10.5.15') do a = "first line\nsecond line\nthird line" list = ["first line\n", "second line\n", "third line"] n_list = [] a.each_line do |line| n_list << line end assert_equal list, n_list n_list.clear a.each_line("li") do |line| n_list << line end assert_equal ["first li", "ne\nsecond li", "ne\nthird li", "ne"], n_list end assert('String#empty?', '15.2.10.5.16') do a = '' b = 'not empty' assert_true a.empty? assert_false b.empty? end assert('String#eql?', '15.2.10.5.17') do assert_true 'abc'.eql?('abc') assert_false 'abc'.eql?('cba') end assert('String#gsub', '15.2.10.5.18') do assert_equal('aBcaBc', 'abcabc'.gsub('b', 'B'), 'gsub without block') assert_equal('aBcaBc', 'abcabc'.gsub('b'){|w| w.capitalize }, 'gsub with block') assert_equal('$a$a$', '#a#a#'.gsub('#', '$'), 'mruby/mruby#847') assert_equal('$a$a$', '#a#a#'.gsub('#'){|_w| '$' }, 'mruby/mruby#847 with block') assert_equal('$$a$$', '##a##'.gsub('##', '$$'), 'mruby/mruby#847 another case') assert_equal('$$a$$', '##a##'.gsub('##'){|_w| '$$' }, 'mruby/mruby#847 another case with block') assert_equal('A', 'a'.gsub('a', 'A')) assert_equal('A', 'a'.gsub('a'){|w| w.capitalize }) assert_equal("<><>", 'a'.gsub('a', '<\0><\1><\2>')) assert_equal(".h.e.l.l.o.", "hello".gsub("", ".")) a = [] assert_equal(".h.e.l.l.o.", "hello".gsub("") { |i| a << i; "." }) assert_equal(["", "", "", "", "", ""], a) assert_raise(ArgumentError) { "".gsub } assert_raise(ArgumentError) { "".gsub("", "", "") } end assert('String#gsub with backslash') do s = 'abXcdXef' assert_equal 'ab<\\>cd<\\>ef', s.gsub('X', '<\\\\>') assert_equal 'abcdef', s.gsub('X', '<\\&>') assert_equal 'abcdef', s.gsub('X', '<\\0>') assert_equal 'abcdef', s.gsub('X', '<\\`>') assert_equal 'abcdef', s.gsub('X', '<\\\'>') end assert('String#gsub!', '15.2.10.5.19') do a = 'abcabc' a.gsub!('b', 'B') b = 'abcabc' b.gsub!('b') { |w| w.capitalize } assert_equal 'aBcaBc', a assert_equal 'aBcaBc', b end assert('String#hash', '15.2.10.5.20') do a = 'abc' assert_equal 'abc'.hash, a.hash end assert('String#include?', '15.2.10.5.21') do assert_true 'abc'.include?('a') assert_false 'abc'.include?('d') end assert('String#index', '15.2.10.5.22') do assert_equal 0, 'abc'.index('a') assert_nil 'abc'.index('d') assert_equal 3, 'abcabc'.index('a', 1) assert_equal 5, "hello".index("", 5) assert_equal nil, "hello".index("", 6) end assert('String#initialize', '15.2.10.5.23') do a = '' a.initialize('abc') assert_equal 'abc', a a.initialize('abcdefghijklmnopqrstuvwxyz') assert_equal 'abcdefghijklmnopqrstuvwxyz', a end assert('String#initialize_copy', '15.2.10.5.24') do a = '' a.initialize_copy('abc') assert_equal 'abc', a end assert('String#intern', '15.2.10.5.25') do assert_equal :abc, 'abc'.intern end assert('String#length', '15.2.10.5.26') do assert_equal 3, 'abc'.length end # 'String#match', '15.2.10.5.27' will be tested in mrbgems. assert('String#replace', '15.2.10.5.28') do a = '' a.replace('abc') assert_equal 'abc', a assert_equal 'abc', 'cba'.replace(a) b = 'abc' * 10 c = ('cba' * 10).dup b.replace(c); c.replace(b); assert_equal c, b # shared string s = "foo" * 100 a = s[10, 90] # create shared string assert_equal("", s.replace("")) # clear assert_equal("", s) # s is cleared assert_not_equal("", a) # a should not be affected end assert('String#reverse', '15.2.10.5.29') do a = 'abc' a.reverse assert_equal 'abc', a assert_equal 'cba', 'abc'.reverse end assert('String#reverse(UTF-8)', '15.2.10.5.29') do assert_equal "ち", "こんにちは世界"[3] assert_equal nil, "こんにちは世界"[20] assert_equal "世", "こんにちは世界"[-2] assert_equal "世界", "こんにちは世界"[-2..-1] assert_equal "んに", "こんにちは世界"[1,2] assert_equal "世", "こんにちは世界"["世"] end if UTF8STRING assert('String#reverse!', '15.2.10.5.30') do a = 'abc' a.reverse! assert_equal 'cba', a assert_equal 'cba', 'abc'.reverse! end assert('String#reverse!(UTF-8)', '15.2.10.5.30') do a = 'こんにちは世界!' a.reverse! assert_equal '!界世はちにんこ', a assert_equal '!界世はちにんこ', 'こんにちは世界!'.reverse! end if UTF8STRING assert('String#rindex', '15.2.10.5.31') do assert_equal 0, 'abc'.rindex('a') assert_nil 'abc'.rindex('d') assert_equal 0, 'abcabc'.rindex('a', 1) assert_equal 3, 'abcabc'.rindex('a', 4) end assert('String#rindex(UTF-8)', '15.2.10.5.31') do str = "こんにちは世界!\nこんにちは世界!" assert_nil str.index('さ') assert_equal 3, str.index('ち') assert_equal 12, str.index('ち', 10) assert_equal nil, str.index("さ") end if UTF8STRING # 'String#scan', '15.2.10.5.32' will be tested in mrbgems. assert('String#size', '15.2.10.5.33') do assert_equal 3, 'abc'.size end assert('String#size(UTF-8)', '15.2.10.5.33') do str = 'こんにちは世界!' assert_equal 8, str.size assert_not_equal str.bytesize, str.size assert_equal 2, str[1, 2].size end if UTF8STRING assert('String#slice', '15.2.10.5.34') do # length of args is 1 a = 'abc'.slice(0) b = 'abc'.slice(-1) c = 'abc'.slice(10) d = 'abc'.slice(-10) # length of args is 2 a1 = 'abc'.slice(0, -1) b1 = 'abc'.slice(10, 0) c1 = 'abc'.slice(-10, 0) d1 = 'abc'.slice(0, 0) e1 = 'abc'.slice(1, 2) # slice of shared string e11 = e1.slice(0) # args is RegExp # It will be tested in mrbgems. # args is String a3 = 'abc'.slice('bc') b3 = 'abc'.slice('XX') assert_equal 'a', a assert_equal 'c', b assert_nil c assert_nil d assert_nil a1 assert_nil b1 assert_nil c1 assert_equal '', d1 assert_equal 'bc', e1 assert_equal 'b', e11 assert_equal 'bc', a3 assert_nil b3 end # TODO Broken ATM assert('String#split', '15.2.10.5.35') do # without RegExp behavior is actually unspecified assert_equal ['abc', 'abc', 'abc'], 'abc abc abc'.split assert_equal ["a", "b", "c", "", "d"], 'a,b,c,,d'.split(',') assert_equal ['abc', 'abc', 'abc'], 'abc abc abc'.split(nil) assert_equal ['a', 'b', 'c'], 'abc'.split("") end assert('String#split(UTF-8)', '15.2.10.5.35') do got = "こんにちは世界!".split('') assert_equal ['こ', 'ん', 'に', 'ち', 'は', '世', '界', '!'], got got = "こんにちは世界!".split('に') assert_equal ['こん', 'ちは世界!'], got end if UTF8STRING assert('String#sub', '15.2.10.5.36') do assert_equal 'aBcabc', 'abcabc'.sub('b', 'B') assert_equal 'aBcabc', 'abcabc'.sub('b') { |w| w.capitalize } assert_equal 'aa$', 'aa#'.sub('#', '$') assert_equal '.abc', "abc".sub("", ".") str = "abc" miss = str.sub("X", "Z") assert_equal str, miss assert_not_equal str.object_id, miss.object_id a = [] assert_equal '.abc', "abc".sub("") { |i| a << i; "." } assert_equal [""], a end assert('String#sub with backslash') do s = 'abXcdXef' assert_equal 'ab<\\>cdXef', s.sub('X', '<\\\\>') assert_equal 'abcdXef', s.sub('X', '<\\&>') assert_equal 'abcdXef', s.sub('X', '<\\0>') assert_equal 'abcdXef', s.sub('X', '<\\`>') assert_equal 'abcdXef', s.sub('X', '<\\\'>') end assert('String#sub!', '15.2.10.5.37') do a = 'abcabc' a.sub!('b', 'B') b = 'abcabc' b.sub!('b') { |w| w.capitalize } assert_equal 'aBcabc', a assert_equal 'aBcabc', b end assert('String#to_f', '15.2.10.5.38') do a = ''.to_f b = '123456789'.to_f c = '12345.6789'.to_f d = '1e-2147483648'.to_f e = '1e2147483648'.to_f assert_float(0.0, a) assert_float(123456789.0, b) assert_float(12345.6789, c) assert_float(0, d) assert_float(Float::INFINITY, e) end if class_defined?("Float") assert('String#to_i', '15.2.10.5.39') do a = ''.to_i b = '32143'.to_i c = 'a'.to_i(16) d = '100'.to_i(2) e = '1_000'.to_i assert_equal 0, a assert_equal 32143, b assert_equal 10, c assert_equal 4, d assert_equal 1_000, e end assert('String#to_s', '15.2.10.5.40') do assert_equal 'abc', 'abc'.to_s end assert('String#to_sym', '15.2.10.5.41') do assert_equal :abc, 'abc'.to_sym end assert('String#upcase', '15.2.10.5.42') do a = 'abc'.upcase b = 'abc' b.upcase assert_equal 'ABC', a assert_equal 'abc', b end assert('String#upcase!', '15.2.10.5.43') do a = 'abc' a.upcase! assert_equal 'ABC', a assert_equal nil, 'ABC'.upcase! a = 'abcdefghijklmnopqrstuvwxyz' b = a.dup a.upcase! b.upcase! assert_equal 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', b end assert('String#inspect', '15.2.10.5.46') do # should not raise an exception - regress #1210 assert_nothing_raised do ("\1" * 100).inspect end assert_equal "\"\\x00\"", "\0".inspect end # Not ISO specified assert('String interpolation (mrb_str_concat for shared strings)') do a = "A" * 32 assert_equal "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA:", "#{a}:" end assert('Check the usage of a NUL character') do "qqq\0ppp" end assert('String#bytes') do str1 = "hello" bytes1 = [104, 101, 108, 108, 111] str2 = "\xFF" bytes2 = [0xFF] assert_equal bytes1, str1.bytes assert_equal bytes2, str2.bytes end assert('String#each_byte') do str1 = "hello" bytes1 = [104, 101, 108, 108, 111] bytes2 = [] str1.each_byte {|b| bytes2 << b } assert_equal bytes1, bytes2 end assert('String#freeze') do str = "hello" str.freeze assert_raise(RuntimeError) { str.upcase! } end mruby-2.0.0/test/t/superclass.rb000066400000000000000000000036261340361412400166070ustar00rootroot00000000000000[ # [:Object, :implementation_defined_value, '15.2.2.1'], [:Module, :Object, '15.2.2.2'], [:Class, :Module, '15.2.3.2'], [:NilClass, :Object, '15.2.4.2'], [:TrueClass, :Object, '15.2.5.2'], [:FalseClass, :Object, '15.2.6.2'], [:Numeric, :Object, '15.2.7.2'], [:Integer, :Numeric, '15.2.8.2'], [:Float, :Numeric, '15.2.9.2'], [:String, :Object, '15.2.10.2'], [:Symbol, :Object, '15.2.11.2'], [:Array, :Object, '15.2.12.2'], [:Hash, :Object, '15.2.13.2'], [:Range, :Object, '15.2.14.2'], # [:Regexp, :Object, '15.2.15.2'], #No Regexp in mruby core # [:MatchData, :Object, '15.2.16.2'], [:Proc, :Object, '15.2.17.2'], # [:Struct, :Object, '15.2.18.2'], # [:Time, :Object, '15.2.19.2'], # [:IO, :Object, '15.2.20.2'], # [:File, :IO, '15.2.21.2'], [:Exception, :Object, '15.2.22.2'], [:StandardError, :Exception, '15.2.23.2'], [:ArgumentError, :StandardError, '15.2.24.2'], # [:LocalJumpError, :StandardError, '15.2.25.2'], [:LocalJumpError, :ScriptError, '15.2.25.2'], # mruby specific [:RangeError, :StandardError, '12.2.26.2'], [:RegexpError, :StandardError, '12.2.27.2'], [:RuntimeError, :StandardError, '12.2.28.2'], [:TypeError, :StandardError, '12.2.29.2'], # [:ZeroDivisionError, :StandardError, '12.2.30.2'], # No ZeroDivisionError in mruby [:NameError, :StandardError, '15.2.31.2'], [:NoMethodError, :NameError, '15.2.32.2'], [:IndexError, :StandardError, '15.2.33.2'], # [:IOError, :StandardError, '12.2.34.2'], # [:EOFError, :IOError, '12.2.35.2'], # [:SystemCallError, :StandardError, '15.2.36.2'], [:ScriptError, :Exception, '12.2.37.2'], [:SyntaxError, :ScriptError, '12.2.38.2'], # [:LoadError, :ScriptError, '12.2.39,2'], ].each do |cls, super_cls, iso| assert "Direct superclass of #{cls}", iso do skip "#{cls} isn't defined" unless Object.const_defined? cls assert_equal Object.const_get(super_cls), Object.const_get(cls).superclass end end mruby-2.0.0/test/t/symbol.rb000066400000000000000000000011751340361412400157250ustar00rootroot00000000000000## # Symbol ISO Test assert('Symbol') do assert_equal :"a", :a assert_equal :"a#{1}", :a1 assert_equal :'a', :a assert_equal :'a#{1}', :"a\#{1}" end assert('Symbol', '15.2.11') do assert_equal Class, Symbol.class end assert('Symbol#===', '15.2.11.3.1') do assert_true :abc == :abc assert_false :abc == :cba end assert('Symbol#id2name', '15.2.11.3.2') do assert_equal 'abc', :abc.id2name end assert('Symbol#to_s', '15.2.11.3.3') do assert_equal 'abc', :abc.to_s end assert('Symbol#to_sym', '15.2.11.3.4') do assert_equal :abc, :abc.to_sym end assert('Symbol#to_proc') do assert_equal 5, :abs.to_proc[-5] end mruby-2.0.0/test/t/syntax.rb000066400000000000000000000322501340361412400157440ustar00rootroot00000000000000assert('__FILE__') do file = __FILE__[-9, 9] assert_equal 'syntax.rb', file end assert('__LINE__') do assert_equal 7, __LINE__ end assert('super', '11.3.4') do assert_raise NoMethodError do super end class SuperFoo def foo true end def bar(*a) a end end class SuperBar < SuperFoo def foo super end def bar(*a) super(*a) end end bar = SuperBar.new assert_true bar.foo assert_equal [1,2,3], bar.bar(1,2,3) end assert('yield', '11.3.5') do assert_raise LocalJumpError do yield end assert_raise LocalJumpError do o = Object.new def o.foo yield end o.foo end end assert('redo in a for loop (#3275)') do sum = 0 for i in 1..10 sum += i i -= 1 if i > 0 redo end end assert_equal 220, sum end assert('Abbreviated variable assignment', '11.4.2.3.2') do a ||= 1 b &&= 1 c = 1 c += 2 assert_equal 1, a assert_nil b assert_equal 3, c end assert('case expression', '11.5.2.2.4') do # case-expression-with-expression, one when-clause x = 0 case "a" when "a" x = 1 end assert_equal 1, x # case-expression-with-expression, multiple when-clauses x = 0 case "b" when "a" x = 1 when "b" x = 2 end assert_equal 2, x # no matching when-clause x = 0 case "c" when "a" x = 1 when "b" x = 2 end assert_equal 0, x # case-expression-with-expression, one when-clause and one else-clause a = 0 case "c" when "a" x = 1 else x = 3 end assert_equal 3, x # case-expression-without-expression, one when-clause x = 0 case when true x = 1 end assert_equal 1, x # case-expression-without-expression, multiple when-clauses x = 0 case when 0 == 1 x = 1 when 1 == 1 x = 2 end assert_equal 2, x # case-expression-without-expression, one when-clause and one else-clause x = 0 case when 0 == 1 x = 1 else x = 3 end assert_equal 3, x # multiple when-arguments x = 0 case 4 when 1, 3, 5 x = 1 when 2, 4, 6 x = 2 end assert_equal 2, x # when-argument with splatting argument x = :integer odds = [ 1, 3, 5, 7, 9 ] evens = [ 2, 4, 6, 8 ] case 5 when *odds x = :odd when *evens x = :even end assert_equal :odd, x true end assert('Nested const reference') do module Syntax4Const CONST1 = "hello world" class Const2 def const1 CONST1 end end end assert_equal "hello world", Syntax4Const::CONST1 assert_equal "hello world", Syntax4Const::Const2.new.const1 end assert('Abbreviated variable assignment as returns') do module Syntax4AbbrVarAsgnAsReturns class A def b @c ||= 1 end end end assert_equal 1, Syntax4AbbrVarAsgnAsReturns::A.new.b end assert('Splat and multiple assignment') do *a = *[1,2,3] b, *c = *[7,8,9] assert_equal [1,2,3], a assert_equal 7, b assert_equal [8,9], c (a, b), c = [1,2],3 assert_equal [1,2,3], [a,b,c] (a, b), c = 1,2,3 assert_equal [1,nil,2], [a,b,c] end assert('Splat and multiple assignment from variable') do a = [1, 2, 3] b, *c = a assert_equal 1, b assert_equal [2, 3], c end assert('Splat and multiple assignment from variables') do a = [1, 2, 3] b = [4, 5, 6, 7] c, d, *e, f, g = *a, *b assert_equal 1, c assert_equal 2, d assert_equal [3, 4, 5], e assert_equal 6, f assert_equal 7, g end assert('Splat and multiple assignment in for') do a = [1, 2, 3, 4, 5, 6, 7] for b, c, *d, e, f in [a] do end assert_equal 1, b assert_equal 2, c assert_equal [3, 4, 5], d assert_equal 6, e assert_equal 7, f end assert('Splat without assignment') do * = [0] a, * = [1, 2] assert_equal 1, a end assert('multiple assignment (rest)') do *a = 0 assert_equal [0], a end assert('multiple assignment (rest+post)') do *a, b = 0, 1, 2 *c, d = 3 assert_equal [0, 1], a assert_equal 2, b assert_equal [], c 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('multiple assignment (empty array rhs #3236, #3239)') do a,b,*c = []; assert_equal [nil, nil, []], [a, b, c] a,b,*c = [1]; assert_equal [1, nil, []], [a, b, c] a,b,*c = [nil]; assert_equal [nil,nil, []], [a, b, c] a,b,*c = [[]]; assert_equal [[], nil, []], [a, b, c] end assert('Return values of case statements') do a = [] << case 1 when 3 then 2 when 2 then 2 when 1 then 2 end b = [] << case 1 when 2 then 2 else end def fb n = 0 Proc.new do n += 1 case when n % 15 == 0 else n end end end assert_equal [2], a assert_equal [nil], b assert_equal 1, fb.call end assert('Return values of if and case statements') do true_clause_value = if true 1 else case 2 when 3 end 4 end assert_equal 1, true_clause_value end assert('Return values of no expression case statement') do when_value = case when true 1 end assert_equal 1, when_value end assert('splat object in assignment') do o = Object.new def o.to_a nil end assert_equal [o], (a = *o) def o.to_a 1 end assert_raise(TypeError) { a = *o } def o.to_a [2] end assert_equal [2], (a = *o) end assert('splat object in case statement') do o = Object.new def o.to_a nil end a = case o when *o 1 end assert_equal 1, a end assert('splat in case statement') do values = [3,5,1,7,8] testa = [1,2,7] testb = [5,6] resulta = [] resultb = [] resultc = [] values.each do |value| case value when *testa resulta << value when *testb resultb << value else resultc << value end end assert_equal [1,7], resulta assert_equal [5], resultb assert_equal [3,8], resultc end assert('External command execution.') do module Kernel sym = '`'.to_sym alias_method :old_cmd, sym results = [] define_method(sym) do |str| results.push str str end `test` # NOVAL NODE_XSTR `test dynamic #{sym}` # NOVAL NODE_DXSTR assert_equal ['test', 'test dynamic `'], results t = `test` # VAL NODE_XSTR assert_equal 'test', t assert_equal ['test', 'test dynamic `', 'test'], results t = `test dynamic #{sym}` # VAL NODE_DXSTR assert_equal 'test dynamic `', t assert_equal ['test', 'test dynamic `', 'test', 'test dynamic `'], results results = [] assert_equal 'test sym test sym test', `test #{:sym} test #{:sym} test` alias_method sym, :old_cmd end true end assert('parenthesed do-block in cmdarg') do class ParenDoBlockCmdArg def test(block) block.call end end x = ParenDoBlockCmdArg.new result = x.test (Proc.new do :ok; end) assert_equal :ok, result end assert('method definition in cmdarg') do if false bar def foo; self.each do end end end true end assert('optional argument in the rhs default expressions') do class OptArgInRHS def foo "method called" end def t(foo = foo) foo end def t2(foo = foo()) foo end end o = OptArgInRHS.new assert_nil(o.t) assert_equal("method called", o.t2) end assert('optional block argument in the rhs default expressions') do assert_nil(Proc.new {|foo = foo| foo}.call) end assert('multiline comments work correctly') do =begin this is a comment with nothing after begin and end =end =begin this is a comment this is a comment with extra after =begin =end =begin this is a comment that has =end with spaces after it =end =begin this is a comment this is a comment that has extra after =begin and =end with spaces after it =end line = __LINE__ =begin this is a comment this is a comment that has extra after =begin and =end with tabs after it =end xxxxxxxxxxxxxxxxxxxxxxxxxx assert_equal(line + 4, __LINE__) end assert 'keyword arguments' do def m(a, b:1) [a, b] end assert_equal [1, 1], m(1) assert_equal [1, 2], m(1, b: 2) def m(a, b:) [a, b] end assert_equal [1, 2], m(1, b: 2) assert_raise(ArgumentError) { m b: 1 } assert_raise(ArgumentError) { m 1 } def m(a:) a end assert_equal 1, m(a: 1) assert_raise(ArgumentError) { m } assert_raise(ArgumentError) { m 'a' => 1, a: 1 } h = { a: 1 } assert_equal 1, m(h) assert_equal({ a: 1 }, h) def m(a: 1) a end assert_equal 1, m assert_equal 2, m(a: 2) assert_raise(ArgumentError) { m 1 } def m(**) end assert_nil m assert_nil m a: 1, b: 2 assert_raise(ArgumentError) { m 2 } def m(a, **) a end assert_equal 1, m(1) assert_equal 1, m(1, a: 2, b: 3) assert_equal({ 'a' => 1, b: 2 }, m('a' => 1, b: 2)) def m(a, **k) [a, k] end assert_equal [1, {}], m(1) assert_equal [1, {a: 2, b: 3}], m(1, a: 2, b: 3) assert_equal [{'a' => 1, b: 2}, {}], m('a' => 1, b: 2) def m(a=1, **) a end assert_equal 1, m assert_equal 2, m(2, a: 1, b: 0) assert_raise(ArgumentError) { m('a' => 1, a: 2) } def m(a=1, **k) [a, k] end assert_equal [1, {}], m assert_equal [1, {a: 1}], m(a: 1) assert_equal [2, {a: 1, b: 2}], m(2, a: 1, b: 2) assert_equal [{a: 1}, {b: 2}], m({a: 1}, {b: 2}) def m(*, a:) a end assert_equal 1, m(a: 1) assert_equal 3, m(1, 2, a: 3) assert_raise(ArgumentError) { m('a' => 1, a: 2) } def m(*a, b:) [a, b] end assert_equal [[], 1], m(b: 1) assert_equal [[1, 2], 3], m(1, 2, b: 3) assert_raise(ArgumentError) { m('a' => 1, b: 2) } def m(*a, b: 1) [a, b] end assert_equal [[], 1], m assert_equal [[1, 2, 3], 4], m(1, 2, 3, b: 4) assert_raise(ArgumentError) { m('a' => 1, b: 2) } def m(*, **) end assert_nil m() assert_nil m(a: 1, b: 2) assert_nil m(1, 2, 3, a: 4, b: 5) def m(*a, **) a end assert_equal [], m() assert_equal [1, 2, 3], m(1, 2, 3, a: 4, b: 5) assert_raise(ArgumentError) { m("a" => 1, a: 1) } assert_equal [1], m(1, **{a: 2}) def m(*, **k) k end assert_equal({}, m()) assert_equal({a: 4, b: 5}, m(1, 2, 3, a: 4, b: 5)) assert_raise(ArgumentError) { m("a" => 1, a: 1) } def m(a = nil, b = nil, **k) [a, k] end assert_equal [nil, {}], m() assert_equal([nil, {a: 1}], m(a: 1)) assert_raise(ArgumentError) { m("a" => 1, a: 1) } assert_equal([{"a" => 1}, {a: 1}], m({ "a" => 1 }, a: 1)) assert_equal([{a: 1}, {}], m({a: 1}, {})) assert_equal([nil, {}], m({})) def m(*a, **k) [a, k] end assert_equal([[], {}], m()) assert_equal([[1], {}], m(1)) assert_equal([[], {a: 1, b: 2}], m(a: 1, b: 2)) assert_equal([[1, 2, 3], {a: 2}], m(1, 2, 3, a: 2)) assert_raise(ArgumentError) { m("a" => 1, a: 1) } assert_raise(ArgumentError) { m("a" => 1) } assert_equal([[], {a: 1}], m(a: 1)) assert_raise(ArgumentError) { m("a" => 1, a: 1) } assert_equal([[{"a" => 1}], {a: 1}], m({ "a" => 1 }, a: 1)) assert_equal([[{a: 1}], {}], m({a: 1}, {})) assert_raise(ArgumentError) { m({a: 1}, {"a" => 1}) } def m(a:, b:) [a, b] end assert_equal([1, 2], m(a: 1, b: 2)) assert_raise(ArgumentError) { m("a" => 1, a: 1, b: 2) } def m(a:, b: 1) [a, b] end assert_equal([1, 1], m(a: 1)) assert_equal([1, 2], m(a: 1, b: 2)) assert_raise(ArgumentError) { m("a" => 1, a: 1, b: 2) } def m(a:, **) a end assert_equal(1, m(a: 1)) assert_equal(1, m(a: 1, b: 2)) assert_raise(ArgumentError) { m("a" => 1, a: 1, b: 2) } def m(a:, **k) [a, k] end assert_equal([1, {}], m(a: 1)) assert_equal([1, {b: 2, c: 3}], m(a: 1, b: 2, c: 3)) assert_raise(ArgumentError) { m("a" => 1, a: 1, b: 2) } =begin def m(a:, &b) [a, b] end assert_equal([1, nil], m(a: 1)) assert_equal([1, l], m(a: 1, &(l = ->{}))) =end def m(a: 1, b:) [a, b] end assert_equal([1, 0], m(b: 0)) assert_equal([3, 2], m(b: 2, a: 3)) assert_raise(ArgumentError) { m a: 1 } def m(a: def m(a: 1) a end, b:) [a, b] end assert_equal([2, 3], m(a: 2, b: 3)) assert_equal([:m, 1], m(b: 1)) # Note the default value of a: in the original method. assert_equal(1, m()) def m(a: 1, b: 2) [a, b] end assert_equal([1, 2], m()) assert_equal([4, 3], m(b: 3, a: 4)) def m(a: 1, **) a end assert_equal(1, m()) assert_equal(2, m(a: 2, b: 1)) def m(a: 1, **k) [a, k] end assert_equal([1, {b: 2, c: 3}], m(b: 2, c: 3)) def m(a:, **) yield end assert_raise(ArgumentError) { m { :blk } } assert_equal :blk, m(a: 1){ :blk } def m(a:, **k, &b) [b.call, k] end assert_raise(ArgumentError) { m { :blk } } assert_equal [:blk, {b: 2}], m(a: 1, b: 2){ :blk } def m(**k, &b) [k, b] end assert_equal([{ a: 1, b: 2}, nil], m(a: 1, b: 2)) assert_equal :blk, m{ :blk }[1].call def m(hsh = {}) hsh end assert_equal({ a: 1, b: 2 }, m(a: 1, b: 2)) assert_equal({ a: 1, 'b' => 2 }, m(a: 1, 'b' => 2)) def m(hsh) hsh end assert_equal({ a: 1, b: 2 }, m(a: 1, b: 2)) assert_equal({ a: 1, 'b' => 2 }, m(a: 1, 'b' => 2)) =begin def m(a, b=1, *c, (*d, (e)), f: 2, g:, h:, **k, &l) [a, b, c, d, e, f, g, h, k, l] end result = m(9, 8, 7, 6, f: 5, g: 4, h: 3, &(l = ->{})) assert_equal([9, 8, [7], [], 6, 5, 4, 3, {}, l], result) def m a, b=1, *c, d, e:, f: 2, g:, **k, &l [a, b, c, d, e, f, g, k, l] end result = m(1, 2, e: 3, g: 4, h: 5, i: 6, &(l = ->{})) assert_equal([1, 1, [], 2, 3, 2, 4, { h: 5, i: 6 }, l], result) =end end mruby-2.0.0/test/t/true.rb000066400000000000000000000011621340361412400153730ustar00rootroot00000000000000## # TrueClass ISO Test assert('TrueClass', '15.2.5') do assert_equal Class, TrueClass.class end assert('TrueClass true', '15.2.5.1') do assert_true true assert_equal TrueClass, true.class assert_false TrueClass.method_defined? :new end assert('TrueClass#&', '15.2.5.3.1') do assert_true true.&(true) assert_false true.&(false) end assert('TrueClass#^', '15.2.5.3.2') do assert_false true.^(true) assert_true true.^(false) end assert('TrueClass#to_s', '15.2.5.3.3') do assert_equal 'true', true.to_s end assert('TrueClass#|', '15.2.5.3.4') do assert_true true.|(true) assert_true true.|(false) end mruby-2.0.0/test/t/typeerror.rb000066400000000000000000000001451340361412400164470ustar00rootroot00000000000000## # TypeError ISO Test assert('TypeError', '15.2.29') do assert_equal Class, TypeError.class end mruby-2.0.0/test/t/unicode.rb000066400000000000000000000021361340361412400160440ustar00rootroot00000000000000# Test of the \u notation assert('bare \u notation test') do # Mininum and maximum one byte characters assert_equal("\x00", "\u0000") assert_equal("\x7F", "\u007F") # Mininum and maximum two byte characters assert_equal("\xC2\x80", "\u0080") assert_equal("\xDF\xBF", "\u07FF") # Mininum and maximum three byte characters assert_equal("\xE0\xA0\x80", "\u0800") assert_equal("\xEF\xBF\xBF", "\uFFFF") # Four byte characters require the \U notation end assert('braced \u notation test') do # Mininum and maximum one byte characters assert_equal("\x00", "\u{0000}") assert_equal("\x7F", "\u{007F}") # Mininum and maximum two byte characters assert_equal("\xC2\x80", "\u{0080}") assert_equal("\xDF\xBF", "\u{07FF}") # Mininum and maximum three byte characters assert_equal("\xE0\xA0\x80", "\u{0800}") assert_equal("\xEF\xBF\xBF", "\u{FFFF}") # Mininum and maximum four byte characters assert_equal("\xF0\x90\x80\x80", "\u{10000}") assert_equal("\xF4\x8F\xBF\xBF", "\u{10FFFF}") end assert('braced multiple \u notation test') do assert_equal("ABC", "\u{41 42 43}") end mruby-2.0.0/travis_config.rb000066400000000000000000000021071340361412400160270ustar00rootroot00000000000000MRuby::Build.new('debug') do |conf| toolchain :gcc enable_debug # include all core GEMs conf.gembox 'full-core' conf.cc.flags += %w(-Werror=declaration-after-statement) conf.compilers.each do |c| c.defines += %w(MRB_GC_STRESS MRB_GC_FIXED_ARENA MRB_METHOD_CACHE) end build_mrbc_exec end MRuby::Build.new('full-debug') do |conf| toolchain :gcc enable_debug # include all core GEMs conf.gembox 'full-core' conf.cc.defines = %w(MRB_ENABLE_DEBUG_HOOK) conf.enable_test end MRuby::Build.new do |conf| toolchain :gcc # include all core GEMs conf.gembox 'full-core' conf.cc.flags += %w(-Werror=declaration-after-statement) conf.compilers.each do |c| c.defines += %w(MRB_GC_FIXED_ARENA) end conf.enable_bintest conf.enable_test end MRuby::Build.new('cxx_abi') do |conf| toolchain :gcc conf.gembox 'full-core' conf.cc.flags += %w(-Werror=declaration-after-statement -fpermissive) conf.compilers.each do |c| c.defines += %w(MRB_GC_FIXED_ARENA) end conf.enable_bintest conf.enable_test enable_cxx_abi build_mrbc_exec end